Collection type generated by for with yield - scala

When I evaluate a for in Scala, I get an immutable IndexedSeq (a collection with array-like performance characteristics, such as efficient random access):
scala> val s = for (i <- 0 to 9) yield math.random + i
s: scala.collection.immutable.IndexedSeq[Double] = Vector(0.6127056766832756, 1.7137598183155291, ...
Does a for with a yield always return an IndexedSeq, or can it also return some other type of collection class (a LinearSeq, for example)? If it can also return something else, then what determines the return type, and how can I influence it?
I'm using Scala 2.8.0.RC3.

Thanks michael.kebe for your comment.
This explains how for is translated to operations with map, flatMap, filter and foreach. So my example:
val s = for (i <- 0 to 9) yield math.random + i
is translated to something like this (I'm not sure if it's translated to map or flatMap in this case):
val s = (0 to 9) map { math.random + _ }
The result type of operations like map on collections depends on the collection you call it on. The type of 0 to 9 is a Range.Inclusive:
scala> val d = 0 to 9
d: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
The result of the map operation on that is an IndexedSeq (because of the builder stuff inside the collections library).
So, to answer my question: the result of a for (...) yield ... depends on what type is inside the parantheses. If I want a List as the result, I could do this:
scala> val s = for (i <- List.range(0, 9)) yield math.random + i
s: List[Double] = List(0.05778968639862214, 1.6758775042995566, ...

You can always transform a range to a list using toList:
> val s = for (i <- (0 to 9).toList) yield math.random + i
> s : List[Double]

Related

Storing each operation of fold operation in List

To sum using fold I use :
val l = List(1, 2, 3)
val sum = l.fold(0) { (a, b) => a + b }
How can I capture the result of each add operation in a list? So for above the following is stored :
List(0,1,3,6)
Only option I can think of is instead of fold iterate over the list and store the result of each operation in an mutable list.
This is what scanLeft does!
scala> val sum = l.scanLeft(0) { (a, b) => a + b }
sum: List[Int] = List(0, 1, 3, 6)
It's exactly what you're looking for—a version of fold that holds on to the result of each operation.

Scala: Indexed Seq instead of List in for loop

The following code shows a type mismatch error :
def f(arr:List[Int]): List[Int] =
for(num <- 0 to arr.length-1; if num % 2 == 1) yield arr(num)
It is says that it found an IndexedSeq instead of a List. The following works :
def f(arr:List[Int]): List[Int] =
for(num <- (0 to arr.length-1).toList; if num % 2 == 1) yield arr(num)
I have used i <- a to b in a for loop before but haven't seen this error before. Can someone please explain why the format i <- a to b cannot be used here ?
because 0 to arr.length-1 return type is: IndexedSeq[Int], so when execute for yield it also will yield result with IndexedSeq[Int] type.
The correct function define:
def f(arr:List[Int]):IndexedSeq[Int] = for( num <- 0 to arr.length-1 if num%2==1) yield arr(num)
And
for( num <- 0 to arr.length-1 if num%2==1) yield arr(num)
will translate to:
scala> def f(arr:List[Int]) = (0 to arr.length-1).filter(i => i%2==1).map(i => arr(i))
f: (arr: List[Int])scala.collection.immutable.IndexedSeq[Int]
So we can see the return type is decided by 0 to arr.length-1 type.
and (0 to arr.length-1).toList is changing the return IndexedSeq[int] type to List[Int] type, so for yield will generate result with type of List[Int].
In Scala, for each iteration of your for loop, yield generates a value which will be remembered. The type of the collection that is returned is the same type that you were iterating over, so a List yields a List, a IndexedSeq yields a IndexedSeq, and so on.
The type of (0 to arr.length-1) is scala.collection.immutable.Range, it's Inherited from scala.collection.immutable.IndexedSeq[Int]. So, in the first case, the result is IndexedSeq[Int], but the return type of function f is List[Int], obviously it doesn't work. In the second case, a List yields a List, and the return type of f is List[Int].
You can also write function f as follow:
def f(arr: List[Int]): IndexedSeq[Int] = for( a <- 1 to arr.length-1; if a % 2 == 1) yield arr(a)
Another example:
scala> for (i <- 1 to 5) yield i
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5)
scala> for (e <- Array(1, 2, 3, 4, 5)) yield e
res1: Array[Int] = Array(1, 2, 3, 4, 5)
In scala for is a syntax sugar, where:
for (i <- a to b) yield func(i)
translate to:
RichInt(a).to(b).map({ i => func(i) })
RichInt.to returns a Range
Range.map returns a IndexedSeq

How to sum adjacent elements in scala

I want to sum adjacent elements in scala and I'm not sure how to deal with the last element.
So I have a list:
val x = List(1,2,3,4)
And I want to sum adjacent elements using indices and map:
val size = x.indices.size
val y = x.indices.map(i =>
if (i < size - 1)
x(i) + x(i+1))
The problem is that this approach creates an AnyVal elemnt at the end:
res1: scala.collection.immutable.IndexedSeq[AnyVal] = Vector(3, 5, 7, ())
and if I try to sum the elements or another numeric method of the collection, it doesn't work:
error: could not find implicit value for parameter num: Numeric[AnyVal]
I tried to filter out the element using:
y diff List(Unit) or y diff List(AnyVal)
but it doesn't work.
Is there a better approach in scala to do this type of adjacent sum without using a foor loop?
For a more functional solution, you can use sliding to group the elements together in twos (or any number of them), then map to their sum.
scala> List(1, 2, 3, 4).sliding(2).map(_.sum).toList
res80: List[Int] = List(3, 5, 7)
What sliding(2) will do is create an intermediate iterator of lists like this:
Iterator(
List(1, 2),
List(2, 3),
List(3, 4)
)
So when we chain map(_.sum), we will map each inner List to it's own sum. toList will convert the Iterator back into a List.
You can try pattern matching and tail recursion also.
import scala.annotation.tailrec
#tailrec
def f(l:List[Int],r :List[Int]=Nil):List[Int] = {
l match {
case x :: xs :: xss =>
f(l.tail, r :+ (x + xs))
case _ => r
}
}
scala> f(List(1,2,3,4))
res4: List[Int] = List(3, 5, 7)
With a for comprehension by zipping two lists, the second with the first item dropped,
for ( (a,b) <- x zip x.drop(1) ) yield a+b
which results in
List(3, 5, 7)

Can you define a value (in a if) in a for comprehension in Scala for use in yield

Is it possible to define a value (in a if) in a for comprehension in Scala for use in yield.
I want to do this to avoid a potential expensive evaluation two times.
An example to illustrate.
for {
bar <- bars if expensive(bar) > 5
} yield (bar, expensive(bar))
How about this:
for {
bar <- bars
exp = expensive(bar)
if exp > 5
} yield (bar, exp)
Yes, you can:
scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> for(n <- res0; val b = n % 2; if b==1) yield b
res2: List[Int] = List(1, 1, 1)

What's the best way to create a dynamically growing array in Scala?

I wanted to add items dynamically into an array. But it seems Scala arrays and lists don't provide any methods for adding items dynamically due to the immutable nature.
So I decided to use List data type to make use of this :: method to achieve this. My code look like this
var outList = List(Nil)
val strArray = Array("ram","sam","bam")
for (str<-strArray)
outList = str :: outList
Though it works in some way, the problem is the new strings are pre-appended into the list. But the ideal requirement is order of the data. Yeah, I know what you are thinking, you can reverse the final result list to get the original order. But the problem is it's a huge array. And I believe it's not a solution though it solves the problem. I believe there should be a simple way to solve this...
And my reason for hacking Scala is to learn the functional way of coding. Having var (mutable type) and populating the list on the fly seems to me is not a functional way of solving things.
How can I do it?
Ideally, I want to achieve something like this in Scala (below the C# code)
List<int> ls = new List<int>();
for (int i = 0; i < 100; i++)
ls.Add(i);
But it seems Scala Arrays & Lists doesn't provide any methods for adding items dynamically due to the immutable nature.
Well, no. Scala Arrays are just Java arrays, so they are mutable:
val arr = Array(1,2)
arr(0) = 3 // arr == Array(3, 2)
But just as Java (and C/C++/C#/etc.) arrays, you can't change the size of an array.
So you need another collection, which is backed by an array, but does allow resizing. A suitable collection in Scala is scala.collection.mutable.ArrayBuffer, java.util.ArrayList in Java, etc.
If you want to get a List instead of an Array in the end, use scala.collection.mutable.ListBuffer instead.
Going after retronym's answer:
If you still want to use list there are a few ways to prepend an item to the list. What you can do is (yes the top part is still wrong):
scala> var outList : List[String] = Nil
outList: List[String] = List()
scala> val strArray = Array("a","b","c")
strArray: Array[java.lang.String] = Array(a, b, c)
scala> for(s <- strArray)
| outList = outList :+ s
scala> outList
res2: List[String] = List(a, b, c)
Note the :+ operator. If you rather append, you'd use s +: outList.
Now who says programming in Scala isn't fun? ;)
P.S. Maybe the reason why you'd want to make them immutable is the speed. Handling large data will be more efficient with immutable data types. Am I right?
If you want to use a mutable Buffer, as retronym mentioned. It looks like this:
scala> var outList = scala.collection.mutable.Buffer[String]()
outList: scala.collection.mutable.Buffer[String] = ArrayBuffer()
scala> for(str<-strArray) outList += str
scala> outList
res10: scala.collection.mutable.ListBuffer[String] = ListBuffer(ram, sam, bam)
Anyway, perhaps it is better to directly do the things you want to do with the strArray. E.g:
strArray map(_.toUpperCase) foreach(println)
If you want to work with immutable structures, you can use the ++ method:
scala> val orgList = List(1,2,3)
orgList: List[Int] = List(1, 2, 3)
scala> val list2Add = List(4,5,6)
list2Add: List[Int] = List(4, 5, 6)
scala> val newList = orgList ++ list2Add
newList: List[Int] = List(1, 2, 3, 4, 5, 6)
If you want to do more work on the elements than just adding them you can use higher order functions:
val newList = orgList ++ list2Add.map(_ * 2)
newList: List[Int] = List(1, 2, 3, 8, 10, 12)
Or with a for loop:
val newList = orgList ++ {for(x <- list2Add) yield 2*x}
Or you could create some recursive loop:
def addAll(toList: List[Int], fromList: List[Int]): List[Int] =
fromList match {
case x :: tail => addAll(2*x :: toList, tail)
case Nil => toList
}
val newList = addAll(orgList, list2Add )
but in this case the ordering of the added elements will be in reversed:
List(12, 10, 8, 1, 2, 3)
If you want performance when working with lists, it is better to reverse the result than try to add new elements in the end. Adding elements in the end to a list is nooot good :-)
Okay, there are a few things to clear up.
This is wrong, you're make a one element list, containing an empty list:
scala> var outList = List(Nil)
outList: List[object Nil] = List(List())
Nil is the empty list:
scala> var outList: List[String] = Nil
outList: List[String] = List()
Or, if you prefer:
scala> var outList = List[String]()
outList: List[String] = List()
Without more context, it's hard to know what you mean by 'dynamically'. Your example code would be better written as:
scala> val strArray = Array("ram","sam","bam")
strArray: Array[java.lang.String] = Array(ram, sam, bam)
scala> strArray toList
res0: List[java.lang.String] = List(ram, sam, bam)
If you want a mutable collection that can grow and efficiently handle prepend, append, and insert operations, you could use scala.mutable.Buffer.
If you are looking to create a new collection, you can use the yield key word:
val outlist = for(i <- 0 to 100) yield i
Or:
val arrList = "Some" :: "Input" :: "List" :: Nil
val outlist = for ( i <- arrList ) yield i
Technically, outlist is a Seq in both of the above examples so you may need to call the toList method on it if you need some of List's methods.
We can use ArrayBuffer as suggested.
However, i have created a simple program which takes no of parameters for an Array of int and it will let you enter the numbers and finally we are displaying them.
val arraySize = scala.io.StdIn.readLine().toInt
val arr = new ArrayBuffer[Int]() ++ (1 to arraySize).map{
i =>
scala.io.StdIn.readLine().toInt
}
println(arr.mkString(","))