Scala for/yield syntax

核能气质少年 提交于 2019-12-24 01:39:34

问题


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.


回答1:


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



回答2:


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)



回答3:


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 )



回答4:


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)



回答5:


Just a new approach:

(for (i<-0 to arr.length-2 by 2) yield Array(arr(i+1), arr(i))).flatten.toArray



回答6:


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



回答7:


var result = for(j<- 0 until (a.length,2);
i<- 1 to 0 by -1; 
if (j+i < a.length) )  
   yield a(j+i)


来源:https://stackoverflow.com/questions/22756814/scala-for-yield-syntax

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!