2

In the book that I'm studying there is an exercise:

Write a loop that swaps adjacent elements of an array of integer. For example Array(1,2,3,4,5) becomes Array(2,1,4,3,5). My solution is:

var v = Array(0,1,2,3,4,5,6,7,8,9)
for (i <- 0 until v.length by 2) {
  var temp = 0
  temp = v(i+1); v(i+1) = v(i); v(i) = temp
}

This algorithm works fine but isn't written fully exploiting the potential of Scala, it is written as if I wrote in C++. In fact, the following exercise asks:

Repeat the preceding assignment, but produce a new array with the swapped values. Use for/yield.

Now I tried with:

val a = ArrayBuffer(1,2,3,4,5)
var res = for (i <- 0 until a.length by 2) yield a(i)
for (i <- 1 until a.length by 2) res(i-1)=a(i) <---------eclipse give me an error

The error is: "value update is not a member of scala.collection.immutable.IndexedSeq[Int]"

How can I solve this task? I understand that the syntax "for / yield" is very powerful, but I don't know how to use it.

cmbuckley
  • 40,217
  • 9
  • 77
  • 91
user3464253
  • 71
  • 1
  • 2
  • The error is because `res` is an immutable sequence (a Vector), which cannot be updated in-place. Vector does have an `updated(index: Int, elem: A)` method, however, which returns a new Vector with the updated element. – DNA Mar 31 '14 at 08:30
  • _a_ is an ArrayBuffer, so automatically also _res_ become an ArrayBuffer. Right? – user3464253 Mar 31 '14 at 08:57
  • No, it doesn't - try it and see! It would become an `ArrayBuffer` if you did something like `for (i <- a) yield i` where `a` is the starting collection in the for-comprehension. – DNA Mar 31 '14 at 09:07

7 Answers7

3

There is a sliding function that does exactly what you need:

(for {
  i <- Array(1,2,3,4,5).sliding(2,2)
  j <- i.reverse
} yield j).toArray
almendar
  • 1,823
  • 13
  • 23
  • 1
    Or a one-liner but not for-loop: `Array(1,2,3,4,5).sliding(2,2).flatMap(_.reverse).toArray` – almendar Mar 31 '14 at 11:16
  • Could you explain in detail the code you've written please? I understand the sliding function, but why two index (i and j)? – user3464253 Mar 31 '14 at 13:24
  • `i<- Array(1,2,3,4,5).sliding(2,2)` spawns: `Array(Array(1, 2), Array(3, 4), Array(5))` `i` iterate over inner Arrays. Now you need to reverse them and you need a name for it so you just give it a `j` as name. To better understand for-comprehensions search on google how they are expanded. Those are really series of flatMap, map and filter. Read more here: http://stackoverflow.com/questions/14598990/confused-with-the-for-comprehension-to-flatmap-map-transformation – almendar Mar 31 '14 at 13:33
1

I am working through Scala for the Impatient myself to refresh my Scala coding skills. Given the concepts introduced by this point in the book, I believe the author is looking for the following:

val a = Array(1, 2, 3, 4, 5)
for (i <- 0 until a.length) 
  yield 
    if (i % 2 == 0) 
      if (i == a.length-1) a(i) 
      else a(i+1) 
    else a(i-1)
Dave Swartz
  • 910
  • 6
  • 14
0

In your generator, you use 0 until v.length by 2, which is an IndexedSeq. This being your input type, yield will produce the same collection type for res.

Since immutable.IndexedSeq is immutable, you cannot modify it. Hence res(i-1)=a(i), which would update the item at i-1, is not allowed.

So, one option would be to convert res to a mutable collection before you go on.

An often preferable option would be to solve it without updating. Here an example using foldLeft, which iterates over our IndexedSeq and builds up a new, flattened Array[Int]

val array = Array(1,2,3,4,5)

val result = (
    for ( i <- 0 until array.length by 2) 
    yield 
        if (i < array.length-1) 
            Array(array(i+1), array(i)) 
        else 
            Array(array(i))
).foldLeft (Array[Int]()) ((a,b) => a ++ b )
rompetroll
  • 4,781
  • 2
  • 37
  • 50
  • This code work fine, like other codes posted by other users, but I don't really understand it yet. Can you explain me, please? The structure if()...else() after the yield determine the result value I think. But what about .foldLeft ? – user3464253 Mar 31 '14 at 13:27
  • You might be interested in reading about [folds](https://wiki.haskell.org/Fold) from Haskell. Haskell is a functional language that is similar to Scala in ways. I suggest reading Haskell's description of folds because information on Haskell is easy to find in a nice, understandable format. I have a hard time with the information out there for Scala. This way you might be able to get a general understanding of what it is folds do. – Lucas May 07 '15 at 06:16
0

The error is because res is an immutable sequence (a Vector), which cannot be updated in-place. Vector does have an updated(index: Int, elem: A) method, however, which returns a new Vector with the updated element.

I'm not sure what the writer of the exercise had in mind - using for/yield seems a little awkward here, but you could use grouped():

val a = Array(1,2,3,4,5)            //> a  : Array[Int] = Array(1, 2, 3, 4, 5)

val swapped = (for (i <- a.grouped(2)) yield i.reverse).flatten.toArray            
                              //> swapped  : Array[Int] = Array(2, 1, 4, 3, 5)

A neater way without for/yield, using flatMap is also possible

a.grouped(2).toArray.flatMap(_.reverse)                                      
                                   //> res5: Array[Int] = Array(2, 1, 4, 3, 5)
DNA
  • 42,007
  • 12
  • 107
  • 146
0

Just a new approach:

(for (i<-0 to arr.length-2 by 2) yield Array(arr(i+1), arr(i))).flatten.toArray
Carlos Caldas
  • 806
  • 11
  • 12
0

Here is my solution:

def swap(a: Array[Int]): Array[Int] = {
    for (elem <- 0 until a.length)
      yield {
        if (elem % 2 == 0) {
          if (elem == a.length - 1) a(elem)
          else a(elem + 1)
        }
        else a(elem - 1)
      }
  }.toArray
Artem Vlasenko
  • 115
  • 1
  • 1
  • 7
0
var result = for(j<- 0 until (a.length,2);
i<- 1 to 0 by -1; 
if (j+i < a.length) )  
   yield a(j+i)
Farhad
  • 4,119
  • 8
  • 43
  • 66
HSquared
  • 1
  • 1