Scala returning multi-dimensional arrays - arrays

I am trying to make a simple minesweeper game in Scala and I am trying to make a method that calls a nested method for randomly placing mines into the grid.
I am using the ofDim method on the Array to make it multi-dimensional which works great, until I specify it as the methods return type. The error I get is:
type ofDim is not a member of Object Array
The code is as follows:
class GridBuilder(x:Int, y:Int, mines:Int) {
def generateGrid: Array.ofDim[String](x, y) = {
def placeMines(mineLocations: Array.ofDim[String](x, y)): Array.ofDim[String](x, y) = {
val xcoord = Random.nextInt(x)
val ycoord = Random.nextInt(y)
if (mineLocations.count(_ == "Bomb") == mines)
mineLocations
else if (mineLocations(xcoord) (ycoord) contains "Mine")
placeMines(mineLocations)
else
placeMines(mineLocations(xcoord)(ycoord) = "Mine")
}
placeMines(new Array.ofDim[String](x, y))
}
}
I haven't found anything about returning multi-dimensional arrays anywhere. Is this possible in Scala? What am I missing here?

Array.ofDim is a method, not a type,
If you look at how it's implemented, it's a bunch of overloaded methods, each having different return type. In your case Array[Array[String]].

I threw together a quick little example so you can compare what you end up with. It is by no means perfect, and doesn't cover invalid user input, or game over etc, but here it is anyway.
import scala.annotation.tailrec
import scala.util.Random
sealed trait GridValue
case object Bomb extends GridValue
case object Empty extends GridValue
case object Checked extends GridValue
case class Grid(private var grid: List[List[GridValue]]){
def click(x: Int, y: Int): String = {
grid(x)(y) match {
case Bomb => "BOOM! You lose!"
case Empty =>
grid = grid.updated(x, grid(x).updated(y, Checked))
"No bomb! Good Job"
case Checked => "You've already clicked here!"
}
}
}
object Grid{
def buildGrid(x: Int, y: Int, mines: Int): Grid = {
#tailrec
def placeMines(grid: List[List[GridValue]] = List.fill(x)(List.fill(y)(Empty)), minesRemaining: Int = mines): List[List[GridValue]] = {
if(minesRemaining == 0) {
grid
} else {
val xcoord = Random.nextInt(x)
val ycoord = Random.nextInt(y)
grid(xcoord)(ycoord) match {
case Bomb => placeMines(grid, minesRemaining)
case Empty => placeMines(grid.updated(xcoord,grid(xcoord).updated(ycoord, Bomb)), minesRemaining - 1)
}
}
}
Grid(placeMines())
}
}
//Start game: val game = Grid.buildGrid(100, 100, 5)
//Click: game.click(10, 10)

Related

Search array for a value [swift]

Just started learning swift, spent a few hours trying to figure this out by searching here but no luck so far.
I have an array, created like this:
class ProbabilitiesClass {
var list = [Odds]()
init() {
list.append(Odds(dateInit: -35, oddsTodayInit: "0,01", sevenDaysInit: "0,2"))
list.append(Odds(dateInit: -34, oddsTodayInit: "0,01", sevenDaysInit: "0,3"))
list.append(Odds(dateInit: -33, oddsTodayInit: "0,02", sevenDaysInit: "0,4"))
I want to search first parameter of this array for an integer and return its index.
Tried this
if let i = Odds.firstIndex(where: { $0.hasPrefix(differenceInDays) }) {
print("index is \([i])")
}
It returns error:
Type 'Odds' has no member 'firstIndex'
The end goal is to return second and third parameters of that index.
Update: I defined Odds like this:
import Foundation
class Odds {
let dateText : Int
let oddsToday : String
let odds7Days : String
init(dateInit: Int, oddsTodayInit: String, sevenDaysInit : String) {
dateText = dateInit
oddsToday = oddsTodayInit
odds7Days = sevenDaysInit
}
}
You could do it like so:
let p = ProbabilitiesClass()
let differenceInDays = -34
if let i = p.list.firstIndex(where: { $0.dateText == differenceInDays }) {
print("index is \([i])") //index is [1]
}
Look for the index in the list property of an instance of ProbabilitiesClass. And like the error message says: The class Odds is not an array to use the method firstIndex(where:) on it.
If you want to use the properties of the first element which has its dateInit equal to differenceInDays, then you could do it like so:
if let first = p.list.first(where: { $0.dateText == differenceInDays }) {
print("oddsTodayInit =", first.oddsToday)
print("sevenDaysInit =", first.odds7Days)
}
It uses this function first(where:).

unable to return an Array from a class

Trying to understand 'apply' method. I want to create a class which returns either a List, Array or a Set depending on the argument passed. The code works for List and Set but not for Array. I am not able to understand the issue
class CollectionFactory [A](s:String){}
object CollectionFactory {
def apply[A](s: String): Traversable[A] = {
s match {
case "list" => {
List[A]()
}
//this doesnt work. It seems using [A] is incorrect. How do I specify the type?
/*
case "array" => {
new Array[A](1)
}
*/
case _ => {
Set[A]() }
}
}
}
val c = CollectionFactory[Int]("list")
c: Traversable[Int] = List()
CollectionFactory[String]("list")
res0: Traversable[String] = List()
CollectionFactory[Boolean]("")
res1: Traversable[Boolean] = Set()
You need a ClassTag[A] to instantiate a new Array[A]. This is easy to fix by adding an implicit ct: ClassTag[A] parameter.
object CollectionFactory {
def apply[A: reflect.ClassTag](s: String): Traversable[A] = {
s match {
case "list" => List[A]()
case "array" => new Array[A](1)
case _ => Set[A]()
}
}
}

Mapping swift enum with associated values

Let say we have an enum with associated value types. In the example below the two value types are simple object that hold an image and a url to share.
enum Content {
case Image(ShareableImage)
case Video(ShareableVideo)
}
Now let's have an array of video and image cases.
let media: [Content] = [*a lot of enum cases inside here*]
All the code above so far cannot be changed in any way in the codebase, I need to work with it.
Here starts my problem:
Let's filter the array with media to only image cases
let imageOnlyCases: [Content] = media.filter { item -> Bool in
switch item {
case .Image: return true
default: return false
}
}
Next step, I want to get from array of enum to an array of their associated values
[Content] -> [ShareableImage] by using map.
so I do this
let shareablemages = imageOnlyCases.map { imageCase -> ShareableImage in
switch imageCase {
case .Image(let image): return image
default: return WHAT TO DO HERE?
}
}
You see, I have a problem with return type..I know that the enum cases are all .Image..and I want a simple map. But the swift syntax is not helping me.
Any ideas?
You could return image for case .Image, and nil otherwise, within a .flatMap operation (to "filter" out nil entries):
/* Example */
enum Foo {
case Bar(Int)
case Baz(Int)
}
let foo: [Foo] = [.Bar(1), .Bar(9),. Baz(3), .Bar(39), .Baz(5)]
/* 1. using 'switch' */
let barOnlyValues: [Int] = foo.flatMap {
switch $0 {
case .Bar(let val): return val
case _: return nil
}}
/* 2. alternatively, as pointed out in MartinR:s answer;
as you're only looking for a single case, the alternative
'if case let' clause could be preferred over 'switch': */
let barOnlyValuesAlt: [Int] = foo.flatMap {
if case let .Bar(val) = $0 { return val }
else { return nil }}
print(barOnlyValues) // [1, 9, 39]
Applied to your use case: note that you needn't perform the filtering to create the imageOnlyCases array, as you can apply the above directly on the media array:
/* 1. using switch */
let shareableImages : [ShareableImage] = media.flatMap {
switch $0 {
case .Image(let image): return image
case _: return nil
}}
/* 2. 'if case let' alternative, as per MartinR:s suggestion */
let shareableImagesAlt : [ShareableImage] = media.flatMap {
if case let .Image(image) = $0 { return image }
else { return nil }}
Disclaimer: I cannot verify your specific use case in practice as I don't have access to the ShareableImage class/struct.
(Thanks #MartinR for advice that .map{ ... }.flatMap{ ... } can be simplified to just .flatMap{ ... }).
If it is guaranteed that only the .Image case can occur then
you can call fatalError() in all other cases:
let shareableImages = imageOnlyCases.map { imageCase -> ShareableImage in
if case let .Image(image) = imageCase {
return image
} else {
fatalError("Unexpected content")
}
}
fatalError() causes the program to terminate immediately. It is
only meant for situations that "cannot occur", i.e. to find programming
errors.
It satisfies the compiler because the function is marked as #noreturn.
If you cannot make that guarantee then use flatMap() as suggested
in the other answer.
Note also that you can use if case here with a pattern instead
of switch/case.

Enum with array of string

I'm a newbie in Swift and I found on the internet a utiliy class to handle errors when I use Objective C classes from swift. Here the utility class, which is a enum:
enum Result<A> {
case Success(Box<A>), Error(NSError)
static func success(v: A) -> Result<A> {
return .Success(Box(v))
}
static func error(e: NSError) -> Result<A> {
return .Error(e)
}
}
final class Box<A> {
let value: A
init(_ value: A) {
self.value = value
}
}
I have a function that returns an Array of strings of type result
func getFiles(account:DBAccount, curFolder:String) ->Result<[String]>{ ...}
Now how can I access the results in an easy way. A println on the console gives me (Enum Value).
I tried to get the results using the following:
let dicList = getFiles(account, currFolder)
switch dicList {
case let .Success(aBox): results = aBox.value
case let .Error(err): results = []
}
now the array results contains the data, but is there no easier way to access the results.
thanks
arnold

How to return a type parameter that is a subtype of an Array?

I don't understand why this code is impossible in Scala:
def getColumns[T <: Array[_]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
Compiler says:
Expression of type Array[(String,Array[Double])] doesn't conform to expected type Array[(String, T)]
I have the same error with this code:
def getColumns[T <: Array[Double]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
It's more clear with my complete use case, finally I have chosen an Array[AnyVal]:
class SystemError(message: String, nestedException: Throwable) extends Exception(message, nestedException) {
def this() = this("", null)
def this(message: String) = this(message, null)
def this(nestedException : Throwable) = this("", nestedException)
}
class FileDataSource(path:String) {
val fileCatch = catching(classOf[FileNotFoundException], classOf[IOException]).withApply(e => throw new SystemError(e))
def getStream():Option[BufferedSource]={
fileCatch.opt{Source.fromInputStream(getClass.getResourceAsStream(path))}
}
}
class CSVReader(src:FileDataSource) {
def lines= src.getStream().get
val head = lines.getLines.take(1).toList(0).split(",")
val otherLines = lines.getLines.drop(1).toList
def getColumns():Array[(String,Array[_])] ={
val transposeLines = otherLines.map { l => l.split(",").map {
d => d match {
case String => d
case Int => d.toInt
case Double => d.toDouble
}}.transpose }
head zip transposeLines.map {_.toArray}
}
}
Can you give me some explanation or good links to understand the issues?
Because your function must be able to work with any T (any array type), however, it will always return an Array[(String,Array[Double])].
A simpler working signature would be:
def getColumns[T](): Array[(String,Array[T])]
But in the body of the function you will have to create an array of type T.
Your function signature requires a T in the compound return type, but you give it a Array[Double]. Note that T <: Array[Double], not the other way around. The following code works:
def getColumns[T >: Array[Double]] ():Array[(String,T)] ={
Array(Tuple2("test",Array(1.0,2.0,3.0)))
}
Shouldn't be what you want but just for elaborating the problem.

Resources