tags:

views:

334

answers:

3

I am a Scala noob. I have decided to write a spider solitaire solver as a first exercise to learn the language and functional programming in general.

I would like to generate a randomly shuffled deck of cards containing 1, 2, or 4 suits. Here is what I came up with:

val numberOfSuits = 1
(List["clubs", "diamonds", "hearts", "spades"].take(numberOfSuits) * 4).take(4)

which should return

List["clubs", "clubs", "clubs", "clubs"]
List["clubs", "diamonds", "clubs", "diamonds"]
List["clubs", "diamonds", "hearts", "spades"]

depending on the value of numberOfSuits, except there is no List "multiply" operation that I can find. Did I miss it? Is there a better way to generate the complete deck before shuffling?

BTW, I plan on using an Enumeration for the suits, but it was easier to type my question with strings. I will take the List generated above and using a for comprehension, iterate over the suits and a similar List of card "ranks" to generate a complete deck.

+3  A: 

Flatten a finite lists of lists:

scala> List.fill(2)(List(1, 2, 3, 4)).flatten
res18: List[Int] = List(1, 2, 3, 4, 1, 2, 3, 4)

Flatten an infinite Stream of lists, take the first N elements:

scala> Stream.continually(List(1, 2, 3, 4)).flatten.take(8).toList
res19: List[Int] = List(1, 2, 3, 4, 1, 2, 3, 4)
retronym
Is this a new method in 2.8.0? I could not get it to work, but this did: val suits = List("clubs", "diamonds", "hearts", "spades"); List.flatten(List.make(4, suits.take(1))), where the "1" is the numberOfSuits value.
Ralph
Yes, some of these are new in 2.8.
retronym
+1  A: 

You can expand a numeric sequence and flatMap instead of multiplying.

scala> (1 to 3).flatMap(_=>List(1,2,3,4).take(2)).take(4)
res1: Seq[Int] = List(1, 2, 1, 2)

This works in 2.7.x also.


Edit: since you're less experienced with Scala, you may not yet have come across the pimp-my-library pattern. If you want to multiply your lists a lot, you can add a custom conversion class:

class MultipliableList[T](l: List[T]) {
  def *(n: Int) = (1 to n).flatMap(_=>l).toList
}
implicit def list2multipliable[T](l: List[T]) = new MultipliableList[T](l)

and now you can

scala> List(1,2,3)*4
res2: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)

(Generally, to reuse such implicits, declare them in an object and then import MyObject._ to get the implicit conversion and corresponding class in scope.)

Rex Kerr
+1  A: 

You should look up the scaladoc for the object List. It has all manners of interesting methods for creation of lists. For instance, the following does exactly what you were trying to:

List.flatten(List.make(4, List("clubs", "diamonds", "hearts", "spades").take(numberOfSuits))).take(4)

A much nicer code, however, would be this (Scala 2.7):

val suits = List("clubs", "diamonds", "hearts", "spades")
List.tabulate(4, i => suits.apply(i % numberOfSuits))

On Scala 2.8 tabulate is curried, so the correct syntax would be:

List.tabulate(4)(i => suits.apply(i % numberOfSuits))
Daniel
I tried your example, but get an error on 2.8.0.Beta1-prerelease. In fact, I get this error on most samples of List.tabulate I can find. What gives?scala> List.tabulate(4, i => suits.apply(i % numberOfSuits)) <console>:5: error: missing parameter type List.tabulate(4, i => suits.apply(i % numberOfSuits))scala> List.tabulate(4, (i:Int) => suits.apply(i % numberOfSuits))<console>:7: error: overloaded method value tabulate with alternatives....cannot be applied to (Int,(Int) => java.lang.String) List.tabulate(4, (i:Int) => suits.apply(i % numberOfSuits)) ^
Adam Rabung
@Adam The syntax changed slightly from 2.7 to 2.8. See the edited answer.
Daniel
Funny you should mention it. I forgot that the singleton docs were at the bottom of the package doc pages. In the comment I wrote above, I came up with the *EXACT* same answer you offered first above.
Ralph