Is there any difference between this code:
for(term <- term_array) {
val list = hashmap.get(term)
...
}
and:
for(term <- term_array; val list = hashmap.get(term)) {
...
}
Inside the loop I'm changing the hashmap with something like this
hashmap.put(term, string :: list)
While checking for the head of list it seems to be outdated somehow when using the second code snippet.
The difference between the two is, that the first one is a definition which is created by pattern matching and the second one is a value inside a function literal. See Programming in Scala, Section 23.1 For Expressions:
for {
p <- persons // a generator
n = p.name // a definition
if (n startsWith "To") // a filter
} yield n
You see the real difference when you compile sources with scalac -Xprint:typer <filename>.scala:
object X {
val x1 = for (i <- (1 to 5); x = i*2) yield x
val x2 = for (i <- (1 to 5)) yield { val x = i*2; x }
}
After code transforming by the compiler you will get something like this:
private[this] val x1: scala.collection.immutable.IndexedSeq[Int] =
scala.this.Predef.intWrapper(1).to(5).map[(Int, Int), scala.collection.immutable.IndexedSeq[(Int, Int)]](((i: Int) => {
val x: Int = i.*(2);
scala.Tuple2.apply[Int, Int](i, x)
}))(immutable.this.IndexedSeq.canBuildFrom[(Int, Int)]).map[Int, scala.collection.immutable.IndexedSeq[Int]]((
(x$1: (Int, Int)) => (x$1: (Int, Int) #unchecked) match {
case (_1: Int, _2: Int)(Int, Int)((i # _), (x # _)) => x
}))(immutable.this.IndexedSeq.canBuildFrom[Int]);
private[this] val x2: scala.collection.immutable.IndexedSeq[Int] =
scala.this.Predef.intWrapper(1).to(5).map[Int, scala.collection.immutable.IndexedSeq[Int]](((i: Int) => {
val x: Int = i.*(2);
x
}))(immutable.this.IndexedSeq.canBuildFrom[Int]);
This can be simplified to:
val x1 = (1 to 5).map {i =>
val x: Int = i * 2
(i, x)
}.map {
case (i, x) => x
}
val x2 = (1 to 5).map {i =>
val x = i * 2
x
}
Instantiating variables inside for loops makes sense if you want to use that variable the for statement, like:
for (i <- is; a = something; if (a)) {
...
}
And the reason why your list is outdated, is that this translates to a foreach call, such as:
term_array.foreach {
term => val list= hashmap.get(term)
} foreach {
...
}
So when you reach ..., your hashmap has already been changed. The other example translates to:
term_array.foreach {
term => val list= hashmap.get(term)
...
}
Related
I have the following issue
Given this list in input , I want to concateneate integers for each line having the same title,
val listIn= List("TitleB,Int,11,0",
"TitleB,Int,1,0",
"TitleB,Int,1,0",
"TitleB,Int,3,0",
"TitleA,STR,3,0",
"TitleC,STR,4,5")
I wrote the following function
def sumB(list: List[String]): List[String] = {
val itemPattern = raw"(.*)(\d+),(\d+)\s*".r
list.foldLeft(ListMap.empty[String, (Int,Int)].withDefaultValue((0,0))) {
case (line, stri) =>
val itemPattern(k,i,j) = stri
val (a, b) = line(k)
line.updated(k, (i.toInt + a, j.toInt + b))
}.toList.map { case (k, (i, j)) => s"$k$i,$j" }
}
Expected output would be:
List("TitleB,Int,16,0",
"TitleA,STR,3,0",
"TitleC,STR,4,5")
Since you are looking to preserve the order of the titles as they appear in the input data, I would suggest you to use LinkedHashMap with foldLeft as below
val finalResult = listIn.foldLeft(new mutable.LinkedHashMap[String, (String, String, Int, Int)]){ (x, y) => {
val splitted = y.split(",")
if(x.keySet.contains(Try(splitted(0)).getOrElse(""))){
val oldTuple = x(Try(splitted(0)).getOrElse(""))
x.update(Try(splitted(0)).getOrElse(""), (Try(splitted(0)).getOrElse(""), Try(splitted(1)).getOrElse(""), oldTuple._3+Try(splitted(2).toInt).getOrElse(0), oldTuple._4+Try(splitted(3).toInt).getOrElse(0)))
x
}
else {
x.put(Try(splitted(0)).getOrElse(""), (Try(splitted(0)).getOrElse(""), Try(splitted(1)).getOrElse(""), Try(splitted(2).toInt).getOrElse(0), Try(splitted(3).toInt).getOrElse(0)))
x
}
}}.mapValues(iter => iter._1+","+iter._2+","+iter._3+","+iter._4).values.toList
finalResult should be your desired output
List("TitleB,Int,16,0", "TitleA,STR,3,0", "TitleC,STR,4,5")
when I try to run the following code:
val anonFunc = (x: Pos): List[Pos] => {for(i <- 1 to 10){ println(i)}
I get the following error:
illegal start of declaration
and I have no idea why.
This code is used inside a function
(1) There two ways to ensure correct return type of anonymous function that I know:
val f = (x: Int) => { x + 1 }: Double
val f: Int => Double = x => x + 1
(2) Your for comprehension will return Unit as it's written, not List[Pos]. To return something real you need to use yield:
val anonFunc = (x: Pos) => { for(i <- 1 to 10) yield(x) }: List[Pos]
but as you do this you will notice that compiler doesn't like List as a return type because what for returns is IndexedSeq, not List. So you need to convert it explicitly:
val anonFunc = (x: Pos) => { for(i <- 1 to 10) yield(x) }.toList: List[Pos]
I'm getting this error when i run the below code -
type mismatch, found : scala.collection.immutable.IndexedSeq[Int] required: Range
Where I'm going wrong ?
Functions -
def calcRange(i: Int, r: List[Range]):List[Range] = r match {
case List() => List(new Range(i,i+1,1))
case r1::rs =>
if (r1.start-1==i) {new Range(i,r1.end,1):: rs; }
else if(r1.end==i){new Range(r1.start,r1.end+1,1)::rs}
else {r1::calcRange(i,rs)}
}
def recurseForRanges(l: Iterator[Int]):List[Range] = {
var ans=List[Range]()
while(l.hasNext){
val cur=l.next;
ans=calcRange(cur,ans)
}
ans
}
def rangify(l: Iterator[Int]):Iterator[Range] = recurseForRanges(l).toIterator
Driver code
def main(args: Array[String]) {
val x=rangify( List(1,2,3,6,7,8).toIterator ).reduce( (x,y) => x ++ y)
/** This line gives the error -type mismatch,
found : scala.collection.immutable.IndexedSeq[Int] required: Range */
}
You can check docs:
++[B](that: GenTraversableOnce[B]): IndexedSeq[B]
++ returns IndexedSeq, not another Range, Range cannot have "holes" in them.
One way to fix it is to change Ranges to IndexedSeqs before reducing. This upcasts the Range so that reduce could take function
(IndexedSeq[Int], IndexedSeq[Int]) => IndexedSeq[Int]
because now it takes
(Range, Range) => Range
But ++ actually returns IndexedSeq[Int] instead of Range hence the type error.
val x = rangify(List(1, 2, 3, 6, 7, 8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
You can as well do this kind of cast by annotating type:
val it: Iterator[IndexedSeq[Int]] = rangify(List(1,2,3,6,7,8).iterator)
val x = it.reduce(_ ++ _)
Note that your code can be simplified, without vars
def calcRange(r: List[Range], i: Int): List[Range] = r match {
case Nil =>
Range(i, i + 1) :: Nil
case r1 :: rs =>
if (r1.start - 1 == i)
Range(i, r1.end) :: rs
else if (r1.end == i)
Range(r1.start, r1.end + 1) :: rs
else
r1 :: calcRange(rs, i)
}
def recurseForRanges(l: Iterator[Int]): List[Range] = {
l.foldLeft(List.empty[Range])(calcRange)
}
def rangify(l: Iterator[Int]): Iterator[Range] = recurseForRanges(l).iterator
val x = rangify(List(1,2,3,6,7,8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
To explain what I've done with it:
Range has a factory method, you don't need new keyword, you don't need to specify by value because 1 is default.
You need no semicolons as well.
What you are doing in recurseForRanges is basically what foldLeft does, I just swapped arguments in calcRange it could be passed directly to foldLeft.
I had a simple task to find combination which occurs most often when we drop 4 cubic dices an remove one with least points.
So, the question is: are there any Scala core classes to generate streams of cartesian products in Scala? When not - how to implement it in the most simple and effective way?
Here is the code and comparison with naive implementation in Scala:
object D extends App {
def dropLowest(a: List[Int]) = {
a diff List(a.min)
}
def cartesian(to: Int, times: Int): Stream[List[Int]] = {
def stream(x: List[Int]): Stream[List[Int]] = {
if (hasNext(x)) x #:: stream(next(x)) else Stream(x)
}
def hasNext(x: List[Int]) = x.exists(n => n < to)
def next(x: List[Int]) = {
def add(current: List[Int]): List[Int] = {
if (current.head == to) 1 :: add(current.tail) else current.head + 1 :: current.tail // here is a possible bug when we get maximal value, don't reuse this method
}
add(x.reverse).reverse
}
stream(Range(0, times).map(t => 1).toList)
}
def getResult(list: Stream[List[Int]]) = {
list.map(t => dropLowest(t).sum).groupBy(t => t).map(t => (t._1, t._2.size)).toMap
}
val list1 = cartesian(6, 4)
val list = for (i <- Range(1, 7); j <- Range(1,7); k <- Range(1, 7); l <- Range(1, 7)) yield List(i, j, k, l)
println(getResult(list1))
println(getResult(list.toStream) equals getResult(list1))
}
Thanks in advance
I think you can simplify your code by using flatMap :
val stream = (1 to 6).toStream
def cartesian(times: Int): Stream[Seq[Int]] = {
if (times == 0) {
Stream(Seq())
} else {
stream.flatMap { i => cartesian(times - 1).map(i +: _) }
}
}
Maybe a little bit more efficient (memory-wise) would be using Iterators instead:
val pool = (1 to 6)
def cartesian(times: Int): Iterator[Seq[Int]] = {
if (times == 0) {
Iterator(Seq())
} else {
pool.iterator.flatMap { i => cartesian(times - 1).map(i +: _) }
}
}
or even more concise by replacing the recursive calls by a fold :
def cartesian[A](list: Seq[Seq[A]]): Iterator[Seq[A]] =
list.foldLeft(Iterator(Seq[A]())) {
case (acc, l) => acc.flatMap(i => l.map(_ +: i))
}
and then:
cartesian(Seq.fill(4)(1 to 6)).map(dropLowest).toSeq.groupBy(i => i.sorted).mapValues(_.size).toSeq.sortBy(_._2).foreach(println)
(Note that you cannot use groupBy on Iterators, so Streams or even Lists are the way to go whatever to be; above code still valid since toSeq on an Iterator actually returns a lazy Stream).
If you are considering stats on the sums of dice instead of combinations, you can update the dropLowest fonction :
def dropLowest(l: Seq[Int]) = l.sum - l.min
I can use an = in a scala for-comprehension (as specified in section 6.19 of the SLS) as follows:
Option
Suppose I have some function String => Option[Int]:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
Then I can use it thus
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
It was my understanding that this was essentially equivalent to:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
That is, the embedded generator was a way of injecting a map into a sequence of flatMap calls. So far so good.
Either.RightProjection
What I actually want to do: use a similar for-comprehension as the previous example using the Either monad.
However, if we use it in a similar chain, but this time using the Either.RightProjection monad/functor, it doesn't work:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
Then use:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
The issue has something to do with the function that a right-projection expects as an argument to its flatMap method (i.e. it expects an R => Either[L, R]). But modifying to not call right on the second generator, it still won't compile.
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
Mega-Confusion
But now I get doubly confused. The following works just fine:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
But this does not:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
I thought these were equivalent
What is going on?
How can I embed an = generator in a for comprehension across an Either?
The fact that you cannot embed the = in the for-comprehension is related to this issue reported by Jason Zaugg; the solution is to Right-bias Either (or create a new data type isomorphic to it).
For your mega-confusion, you expanded the for sugar incorrectly. The desugaring of
for {
b <- x(a)
c <- y(b)
} yield z(c)
is
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
and not
x(a) flatMap { b => y(b)} map { c => z(c) }
Hence you should have done this:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
More fun about desugaring (the `j = i + 10` issue)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
is desugared into
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
So in your case, y(b) has result type Either which doesn't have map defined.