Considering x,p,r are evaluated from the previous expressions , what is happening underneath , <- and then after =
val a = for{
x <- y
p = q (x)
r <- s (p)
} yield (something(p.something, r.something))
The <- is equivalent (syntactic sugar) to .flatMap call, while = is equivalent to val x = (and yield kind of final .map).
So the code is equivalent to:
val a = y.flatMap { x => // first <-
val p = q (x)
s(p).map { r => // 2nd <- + yield
something(p.something, r.something)
}
}
Related
I have a for comprehension like:
val ao = Option(1)
val bo = Option(2)
val (x,y) = for (a <- ao; b <- bo) yield (a+b, b+a*2)
However this does not work. For comprehension returns Option[(Int,Int)] but cannot be assigned to individual x and y.
If I do:
val Some((x,y)) = for ...
It causes exception when yield None.
How to achieve this goal? I want x and y to be Option[Int]. I hope to find an elegant solution without using like x._1 or x.getOrElse, or match
It should have been unzip, but unfortunately, unzip returns Lists, not Options. Probably the shortest work-around would be:
val pairOpt = for (a <- ao; b <- bo) yield (a+b, b+a*2)
val (x, y) = (pairOpt.map(_._1), pairOpt.map(_._2))
Isn't pattern matching the best way to handle options?
val res = for (a <- ao; b <- bo) yield (a+b, b+a*2)
val (x, y) = res match {
case Some((x, y)) => (Some(x), Some(y))
case None => (None, None)
}
Why would that not be considered 'elegant'?
I want to generate a list of Tuple2 objects. Each tuple (a,b) in the list should meeting the conditions:a and b both are perfect squares,(b/30)<a<b
and a>N and b>N ( N can even be a BigInt)
I am trying to write a scala function to generate the List of Tuples meeting the above requirements?
This is my attempt..it works fine for Ints and Longs..But for BigInt there is sqrt problem I am facing..Here is my approach in coding as below:
scala> def genTups(N:Long) ={
| val x = for(s<- 1L to Math.sqrt(N).toLong) yield s*s;
| val y = x.combinations(2).map{ case Vector(a,b) => (a,b)}.toList
| y.filter(t=> (t._1*30/t._2)>=1)
| }
genTups: (N: Long)List[(Long, Long)]
scala> genTups(30)
res32: List[(Long, Long)] = List((1,4), (1,9), (1,16), (1,25), (4,9), (4,16), (4,25), (9,16), (9,25), (16,25))
Improved this using BigInt square-root algorithm as below:
def genTups(N1:BigInt,N2:BigInt) ={
def sqt(n:BigInt):BigInt = {
var a = BigInt(1)
var b = (n>>5)+BigInt(8)
while((b-a) >= 0) {
var mid:BigInt = (a+b)>>1
if(mid*mid-n> 0) b = mid-1
else a = mid+1
}; a-1 }
val x = for(s<- sqt(N1) to sqt(N2)) yield s*s;
val y = x.combinations(2).map{ case Vector(a,b) => (a,b)}.toList
y.filter(t=> (t._1*30/t._2)>=1)
}
I appreciate any help to improve in my algorithm .
You can avoid sqrt in you algorithm by changing the way you calculate x to this:
val x = (BigInt(1) to N).map(x => x*x).takeWhile(_ <= N)
The final function is then:
def genTups(N: BigInt) = {
val x = (BigInt(1) to N).map(x => x*x).takeWhile(_ <= N)
val y = x.combinations(2).map { case Vector(a, b) if (a < b) => (a, b) }.toList
y.filter(t => (t._1 * 30 / t._2) >= 1)
}
You can also re-write this as a single chain of operations like this:
def genTups(N: BigInt) =
(BigInt(1) to N)
.map(x => x * x)
.takeWhile(_ <= N)
.combinations(2)
.map { case Vector(a, b) if a < b => (a, b) }
.filter(t => (t._1 * 30 / t._2) >= 1)
.toList
In a quest for performance, I came up with this recursive version that appears to be significantly faster
def genTups(N1: BigInt, N2: BigInt) = {
def sqt(n: BigInt): BigInt = {
var a = BigInt(1)
var b = (n >> 5) + BigInt(8)
while ((b - a) >= 0) {
var mid: BigInt = (a + b) >> 1
if (mid * mid - n > 0) {
b = mid - 1
} else {
a = mid + 1
}
}
a - 1
}
#tailrec
def loop(a: BigInt, rem: List[BigInt], res: List[(BigInt, BigInt)]): List[(BigInt, BigInt)] =
rem match {
case Nil => res
case head :: tail =>
val a30 = a * 30
val thisRes = rem.takeWhile(_ <= a30).map(b => (a, b))
loop(head, tail, thisRes.reverse ::: res)
}
val squares = (sqt(N1) to sqt(N2)).map(s => s * s).toList
loop(squares.head, squares.tail, Nil).reverse
}
Each recursion of the loop adds all the matching pairs for a given value of a. The result is built in reverse because adding to the front of a long list is much faster than adding to the tail.
Firstly create a function to check if number if perfect square or not.
def squareRootOfPerfectSquare(a: Int): Option[Int] = {
val sqrt = math.sqrt(a)
if (sqrt % 1 == 0)
Some(sqrt.toInt)
else
None
}
Then, create another func that will calculate this list of tuples according to the conditions mentioned above.
def generateTuples(n1:Int,n2:Int)={
for{
b <- 1 to n2;
a <- 1 to n1 if(b>a && squareRootOfPerfectSquare(b).isDefined && squareRootOfPerfectSquare(a).isDefined)
} yield ( (a,b) )
}
Then on calling the function with parameters generateTuples(5,10)
you will get an output as
res0: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,4), (1,9), (4,9))
Hope that helps !!!
Im trying to pair up two lists in Scala where non matching pairs should be replaced by a default value, this is what I have so far but thy all fall short in some way.
How do I create List((a,a),(b,empty),(c,c))???
case class Test(id: Option[Int] = None)
val empty = Test()
val a = Test(Some(1))
val b = Test(Some(2))
val c = Test(Some(3))
val cache = List(a,b,c)
val delta = List(a,c)
//Trial 1
val newCache1 = cache.zipAll(delta,empty,empty)
//Tial 2
val newCache2 = for {
c <- cache
d <- delta
if c.id == d.id
} yield (c,d)
//Tial 3
val newCache3 = for {
c <- cache
d <- delta
} yield if (c.id == d.id) (c,d) else (c,empty)
Turn your delta into a map, then join them up.
val deltaMap: Map[Int, Test] =
delta.flatMap(x => x.id.map(id => id -> x)).toMap
val newCache: Seq[(Test, Test)] = cache.map { c =>
c -> c.id.flatMap(deltaMap.get).getOrElse(empty)
}
In Scala language, I want to write a function that yields odd numbers within a given range. The function prints some log when iterating even numbers. The first version of the function is:
def getOdds(N: Int): Traversable[Int] = {
val list = new mutable.MutableList[Int]
for (n <- 0 until N) {
if (n % 2 == 1) {
list += n
} else {
println("skip even number " + n)
}
}
return list
}
If I omit printing logs, the implementation become very simple:
def getOddsWithoutPrint(N: Int) =
for (n <- 0 until N if (n % 2 == 1)) yield n
However, I don't want to miss the logging part. How do I rewrite the first version more compactly? It would be great if it can be rewritten similar to this:
def IWantToDoSomethingSimilar(N: Int) =
for (n <- 0 until N) if (n % 2 == 1) yield n else println("skip even number " + n)
def IWantToDoSomethingSimilar(N: Int) =
for {
n <- 0 until N
if n % 2 != 0 || { println("skip even number " + n); false }
} yield n
Using filter instead of a for expression would be slightly simpler though.
I you want to keep the sequentiality of your traitement (processing odds and evens in order, not separately), you can use something like that (edited) :
def IWantToDoSomethingSimilar(N: Int) =
(for (n <- (0 until N)) yield {
if (n % 2 == 1) {
Option(n)
} else {
println("skip even number " + n)
None
}
// Flatten transforms the Seq[Option[Int]] into Seq[Int]
}).flatten
EDIT, following the same concept, a shorter solution :
def IWantToDoSomethingSimilar(N: Int) =
(0 until N) map {
case n if n % 2 == 0 => println("skip even number "+ n)
case n => n
} collect {case i:Int => i}
If you will to dig into a functional approach, something like the following is a good point to start.
First some common definitions:
// use scalaz 7
import scalaz._, Scalaz._
// transforms a function returning either E or B into a
// function returning an optional B and optionally writing a log of type E
def logged[A, E, B, F[_]](f: A => E \/ B)(
implicit FM: Monoid[F[E]], FP: Pointed[F]): (A => Writer[F[E], Option[B]]) =
(a: A) => f(a).fold(
e => Writer(FP.point(e), None),
b => Writer(FM.zero, Some(b)))
// helper for fixing the log storage format to List
def listLogged[A, E, B](f: A => E \/ B) = logged[A, E, B, List](f)
// shorthand for a String logger with List storage
type W[+A] = Writer[List[String], A]
Now all you have to do is write your filtering function:
def keepOdd(n: Int): String \/ Int =
if (n % 2 == 1) \/.right(n) else \/.left(n + " was even")
You can try it instantly:
scala> List(5, 6) map(keepOdd)
res0: List[scalaz.\/[String,Int]] = List(\/-(5), -\/(6 was even))
Then you can use the traverse function to apply your function to a list of inputs, and collect both the logs written and the results:
scala> val x = List(5, 6).traverse[W, Option[Int]](listLogged(keepOdd))
x: W[List[Option[Int]]] = scalaz.WriterTFunctions$$anon$26#503d0400
// unwrap the results
scala> x.run
res11: (List[String], List[Option[Int]]) = (List(6 was even),List(Some(5), None))
// we may even drop the None-s from the output
scala> val (logs, results) = x.map(_.flatten).run
logs: List[String] = List(6 was even)
results: List[Int] = List(5)
I don't think this can be done easily with a for comprehension. But you could use partition.
def getOffs(N:Int) = {
val (evens, odds) = 0 until N partition { x => x % 2 == 0 }
evens foreach { x => println("skipping " + x) }
odds
}
EDIT: To avoid printing the log messages after the partitioning is done, you can change the first line of the method like this:
val (evens, odds) = (0 until N).view.partition { x => x % 2 == 0 }
I have the following (working) code
val superSuperSorts = superSorts.flatMap(relations.get(_)).flatMap(x=>x)
And I give you the types here
val superSorts: Set[Sort]
val relations: Map[Sort, Set[Sort]]
Changing it to this for-comprehension gives me a compile error
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
s <- sss //extract the elements from the Set
) yield s
of this reading
error: type mismatch;
found : scala.collection.immutable.Set[edu.uulm.scbayes.logic.Sort]
required: Option[?]
s <- sss
Please explain why my for-comprehension is wrong.
You can't flatMap an Option. Have a look at its type signature:
def flatMap [B] (f: (A) ⇒ Option[B]): Option[B]
So, flatMap unpacks the Option, but requires a new Option, so you need an alternative. You can use the method getOrElse of Map or method seq of Option:
val superSuperSorts = for {
s <- superSorts
ss <- relations.getOrElse(s, Set.empty)
} yield s
val superSuperSorts = for {
s <- superSorts
ss <- relations.get(s).seq
sss <- ss
} yield sss
Another problem is, that your flatMap code is not equivalent with your for-expression. The expression
for (x <- expr1; y <- expr2) yield expr3
is translated to
expr1.flatMap(x => for (y <- expr2) yield expr3)
and in another step to
expr1.flatMap(x => expr2.map(y => expr3))
But you have:
expr1.flatMap(x => expr2).flatMap(y => expr3)
Let me give an example to make clear where the problem is. Let's say you have:
val superSorts = Set('x)
val relations = Map('x -> Set('a, 'b))
This code:
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
s <- sss //extract the elements from the Set
) yield s
translates to:
superSorts.flatMap(
ss => relations.get(ss).flatMap(
sss => sss.map(
s => s)))
First, note that the last term is a map, not a flatMap. Now, let's consider an iteration with the data above:
ss = 'x
sss = Set('a, 'b)
s = 'a, 'b
And now let's go backward in the code.
// Set('a, 'b) => Set('a, 'b)
sss.map(s => s)
// Some(Set('a, 'b)) => Some('a, 'b)????
relations.get(ss).flatMap(...)
See the problem here? How can an Option[Set[Sort]] be flattened? There's no such a thing as a Some('a, 'b).
So, why does the original code work?
val superSuperSorts = superSorts.flatMap(relations.get(_)).flatMap(x=>x)
If we break it down:
// Set('x) => Set(Set('a, 'b))
superSorts.flatMap(relations.get(_))
// Set(Set('a, 'b')) => Set('a, 'b)
(...).flatMap(x=>x)
See how all the flatMap are being applied over a Set, not an Option? The Option was eliminated by the flatMap and never came into play.
The more-or-less equivalent for-comprehension for your code would be:
val superSuperSorts = for {
x <- (for {
ss <- superSorts
sss <- relations.get(ss)
} yield sss)
s <- x
} yield s
This introduces a couple of identity maps: map(sss => sss) and map(s => s), which get around the fact that the last generator in a for-comprehension is always a map.
It's important to understand that a for comprehension will yield the type of collection fed in. So why your code didn't work is you want to return a single element(s). Try this:
val superSuperSorts =
for(
ss <- superSorts;
sss <- relations.get(ss); //get Option[Set[Sort]] and flatten the option
) yield sss
:/ Hope this helps