Dynamic For Comprehensions - scala

I'm relatively new to Scala so I'm not super confident with the language and I need your help to solve a problem. I know that the for-comprehension is just a syntactic sugar to simplify complex map/flatMap hierarchies.
Now, consider to have 3 different Range intervals, which should be combined in order to create all the possible combinations (respecting the intervals) of values.
Example:
Using the for-comprehension the problem can be solved as:
val intervalX = 1 to 5
val intervalY = 6 to 13
val intervalZ = 20 to 50
for {
x <- intervalX;
y <- intervalY;
z <- intervalZ
} yield (x,y,z)
Which is converted by the Scala compiler as:
intervalX.flatMap{x =>
intervalY.flatMap{y =>
intervalZ.map{z => (x,y,z)}
}
}
However, the problem is harder if you are given in input a variable number d of intervals. Is it possible to perform the same operation, obtaining all the possible d-tuples? I think that it could be solved using the foldLeft operation, but I am not able to write it correctly at the moment. Can you help me?
Thanks

If you can live without tuples as a result then a version using foldLeft and returning lists representing combinations could be:
val intervalX = 1 to 5
val intervalY = 6 to 13
val intervalZ = 20 to 50
val ranges = intervalZ :: intervalY :: intervalX :: Nil
val combos = ranges.foldLeft(Iterable[Seq[Int]](Nil)) { case (c, e) =>
for {
i <- e
j <- c
} yield i +: j
}
combos foreach { println(_) }

Related

How can I use the last result from a scala map as input to the next function?

I'm working through some project euler questions to practice my scala. For problem 7 I have to find the 10001st prime. I have a working solution, but dont feel its as functional as it could be.
def first_n_primes(n: Long) : List[Long] = {
var last_prime = 1L
(1L to n).map(x => {last_prime = get_next_prime(x, last_prime); last_prime}).toList
}
Specifically, I feel there might be a way to get rid of the var last_prime, but I dont know how to use the result of the nth map evaluation as the input to the n+1 evaluation. How can I do this more functionally?
You are looking for scanLeft:
(1l to n).scanLeft(1) { case (x, last) => get_next_prime(x, last) }
Or just (1l to n).scanLeft(1)(get_next_prime)
Note however that this is not a very good algorithm looking for the primes, because there is a lot of repetitive work that could be saved (to find the next prime, you need to re-discover all the previous ones).
This sort of task is better done in scala with recursive streams:
lazy val primes: Stream[Long] = 2 #:: Stream.iterate(3l)(_+1).filter { n =>
val stop = math.sqrt(n)
primes.takeWhile { _ <= stop }.forall { k => n % k != 0 }
}
primes.take(n).toList

Calculating the mean of elements in a list in Scala

I'm trying to write a method that calculates the mean of the elements in a given List in Scala. Here's my code:
def meanElements(list: List[Float]): Float = {
list match {
case x :: tail => (x + meanElements(tail))/(list.length)
case Nil => 0
}
}
When I call meanElements(List(10,12,14))), the result I get is different than 12. Can someone help?
You can simply do it using inbuilt functions:
scala> def mean(list:List[Int]):Int =
| if(list.isEmpty) 0 else list.sum/list.size
mean: (list: List[Int])Int
scala> mean(List(10,12,14))
res1: Int = 12
scala>
The formula is not correct, it should be:
case x :: tail => (x + meanElements(tail) * tail.length) / list.length
But this implementation is performing a lot of divisions and multiplications.
It would be better to split the computation of the mean to two steps,
calculating the sum first,
and then dividing by list.length.
That is, something more like this:
def meanElements(list: List[Float]): Float = sum(list) / list.length
Where sum is a helper function you have to implement.
If you don't want to expose its implementation,
then you can define it in the body of meanElements.
(Or as #ph88 pointed out,
it could be as simple as list.reduce(_ + _).)

Scala for loop multiple counters

I'm new to Scala, and I'm trying to convert this for loop from Java:
for(int x=1, y=2; x<=5; x++, y+=2)
System.out.println(x+y);
I'm trying to zip the values in Scala since I can't find a way to have multiple counters which are non-nested:
val a = Seq(1 to 5)
val b = Seq(2 to 10 by 2)
for((x,y) <- a.zip(b))
println(x+y)
But the above code is giving this error:
type mismatch; found: scala.collection.immutable.Range required: String
Does anyone know how to fix this? I would prefer to do with for loop only, not while loop.
Try this, no need to wrap the Range in a Seq:
val a = 1 to 5
val b = 2 to 10 by 2
for(
(x,y) <- a.zip(b)
)
println(x+y)
You might try . . .
((1 to 5) zip (2 to 10 by 2)).foreach(x => println(x._1+x._2))
Because Scala for comprehensions are sufficiently different from for() loops in other languages, it's often a good idea for beginners to avoid them until they've gained a sufficient knowledge of map, flatMap, and foreach.
In your example you want x to range from 1 to 5 and y is always 2*x. Using for loops is easy for those coming from Java:
for(x <- 1 to 5; y = x*2) {
println(s"x = $x, y = $y, x+y = ${x+y}")
}
Here is solution to a more generic problem - iterating over elements in a collection using multiple counters (=indices or pointers), like if you want to compare each 2 pairs:
val c = List("a", "b", "c", "d") //or any collection
val end = c.length - 1
for(i <- 0 to end-1; j <- i+1 to end)
//compare or operate with each pair
println(c(i)+c(j))
... prints:
ab
ac
ad
bc
bd
cd

How to use yield with multistatement for?

The code is just for illustrative purposes, i.e. it is an example not a real code.
I tried this:
val results = for(i <- 1 to 20)
{
val x = i+1
println(x)
yield x
}
and this
val results = for {i <- 1 to 20;
val x = i+1;
println(x)
}
yield x
But none of this works -- I need a generator, definition, and a statement -- is this possible to do it with yield? If yes, what is the correct syntax?
Hopefully, this will get you started:
val result = for (i <- 1 to 10 if i%2==0) yield {
println(i);
i
}
which is equivalent to
(1 to 10).filter(_%2==0).map(x => { println(x); x } )
You seem to think that for in Scala is similar to for in imperative languages. It's not! Behind the scenes, it makes use of flatMap. Every expression in the first section of the for/yield syntax must have a certain form. If I'm not mistaken, it must either be an assignment (restricted to val, maybe) or a <- expression. You can hack it to get what you want:
for {
i <- 1 to 20
val x = i + 1
_ <- {println(x); List(1)}
} yield x
But that is pretty horrible. Hacking the yield, as Jamil demonstrated, is also a possibility, though also pretty horrible.
The question is, what exactly are you trying to accomplish? foreach is best used for side-effecting loop code:
(1 to 10) foreach { i =>
val x = i+1
println(x)
}
map is best used for producing a new list of the same length:
(1 to 10) map (i => i + 1)
It is rather unusual, and somewhat ugly, to want to do both at the same time.

What is Scala's yield?

I understand Ruby and Python's yield. What does Scala's yield do?
I think the accepted answer is great, but it seems many people have failed to grasp some fundamental points.
First, Scala's for comprehensions are equivalent to Haskell's do notation, and it is nothing more than a syntactic sugar for composition of multiple monadic operations. As this statement will most likely not help anyone who needs help, let's try again… :-)
Scala's for comprehensions is syntactic sugar for composition of multiple operations with map, flatMap and filter. Or foreach. Scala actually translates a for-expression into calls to those methods, so any class providing them, or a subset of them, can be used with for comprehensions.
First, let's talk about the translations. There are very simple rules:
This
for(x <- c1; y <- c2; z <-c3) {...}
is translated into
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
This
for(x <- c1; y <- c2; z <- c3) yield {...}
is translated into
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
This
for(x <- c; if cond) yield {...}
is translated on Scala 2.7 into
c.filter(x => cond).map(x => {...})
or, on Scala 2.8, into
c.withFilter(x => cond).map(x => {...})
with a fallback into the former if method withFilter is not available but filter is. Please see the section below for more information on this.
This
for(x <- c; y = ...) yield {...}
is translated into
c.map(x => (x, ...)).map((x,y) => {...})
When you look at very simple for comprehensions, the map/foreach alternatives look, indeed, better. Once you start composing them, though, you can easily get lost in parenthesis and nesting levels. When that happens, for comprehensions are usually much clearer.
I'll show one simple example, and intentionally omit any explanation. You can decide which syntax was easier to understand.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
or
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 introduced a method called withFilter, whose main difference is that, instead of returning a new, filtered, collection, it filters on-demand. The filter method has its behavior defined based on the strictness of the collection. To understand this better, let's take a look at some Scala 2.7 with List (strict) and Stream (non-strict):
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
The difference happens because filter is immediately applied with List, returning a list of odds -- since found is false. Only then foreach is executed, but, by this time, changing found is meaningless, as filter has already executed.
In the case of Stream, the condition is not immediatelly applied. Instead, as each element is requested by foreach, filter tests the condition, which enables foreach to influence it through found. Just to make it clear, here is the equivalent for-comprehension code:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
This caused many problems, because people expected the if to be considered on-demand, instead of being applied to the whole collection beforehand.
Scala 2.8 introduced withFilter, which is always non-strict, no matter the strictness of the collection. The following example shows List with both methods on Scala 2.8:
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
This produces the result most people expect, without changing how filter behaves. As a side note, Range was changed from non-strict to strict between Scala 2.7 and Scala 2.8.
It is used in sequence comprehensions (like Python's list-comprehensions and generators, where you may use yield too).
It is applied in combination with for and writes a new element into the resulting sequence.
Simple example (from scala-lang)
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
The corresponding expression in F# would be
[ for a in args -> a.toUpperCase ]
or
from a in args select a.toUpperCase
in Linq.
Ruby's yield has a different effect.
Yes, as Earwicker said, it's pretty much the equivalent to LINQ's select and has very little to do with Ruby's and Python's yield. Basically, where in C# you would write
from ... select ???
in Scala you have instead
for ... yield ???
It's also important to understand that for-comprehensions don't just work with sequences, but with any type which defines certain methods, just like LINQ:
If your type defines just map, it allows for-expressions consisting of a
single generator.
If it defines flatMap as well as map, it allows for-expressions consisting
of several generators.
If it defines foreach, it allows for-loops without yield (both with single and multiple generators).
If it defines filter, it allows for-filter expressions starting with an if
in the for expression.
Unless you get a better answer from a Scala user (which I'm not), here's my understanding.
It only appears as part of an expression beginning with for, which states how to generate a new list from an existing list.
Something like:
var doubled = for (n <- original) yield n * 2
So there's one output item for each input (although I believe there's a way of dropping duplicates).
This is quite different from the "imperative continuations" enabled by yield in other languages, where it provides a way to generate a list of any length, from some imperative code with almost any structure.
(If you're familiar with C#, it's closer to LINQ's select operator than it is to yield return).
Consider the following for-comprehension
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
It may be helpful to read it out loud as follows
"For each integer i, if it is greater than 3, then yield (produce) i and add it to the list A."
In terms of mathematical set-builder notation, the above for-comprehension is analogous to
which may be read as
"For each integer , if it is greater than , then it is a member of the set ."
or alternatively as
" is the set of all integers , such that each is greater than ."
The keyword yield in Scala is simply syntactic sugar which can be easily replaced by a map, as Daniel Sobral already explained in detail.
On the other hand, yield is absolutely misleading if you are looking for generators (or continuations) similar to those in Python. See this SO thread for more information: What is the preferred way to implement 'yield' in Scala?
Yield is similar to for loop which has a buffer that we cannot see and for each increment, it keeps adding next item to the buffer. When the for loop finishes running, it would return the collection of all the yielded values. Yield can be used as simple arithmetic operators or even in combination with arrays.
Here are two simple examples for your better understanding
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))
Hope this helps!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
These two pieces of code are equivalent.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
These two pieces of code are also equivalent.
Map is as flexible as yield and vice-versa.
val doubledNums = for (n <- nums) yield n * 2
val ucNames = for (name <- names) yield name.capitalize
Notice that both of those for-expressions use the yield keyword:
Using yield after for is the “secret sauce” that says, “I want to yield a new collection from the existing collection that I’m iterating over in the for-expression, using the algorithm shown.”
taken from here
According to the Scala documentation, it clearly says "yield a new collection from the existing collection".
Another Scala documentation says, "Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enums) yield e, where enums refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter. "
yield is more flexible than map(), see example below
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
yield will print result like: List(5, 6), which is good
while map() will return result like: List(false, false, true, true, true), which probably is not what you intend.