Scala for / yield语法

时间:2014-03-31 08:26:02

标签: scala for-loop yield

在我正在研究的书中有一个练习:

  

编写一个循环,用于交换整数数组的相邻元素。例如,Array(1,2,3,4,5)变为Array(2,1,4,3,5)。我的解决方案是:

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
}

这个算法工作正常,但没有完全利用Scala的潜力,它写得好像我用C ++写的。事实上,以下练习要求:

重复前面的赋值,但生成一个包含交换值的新数组。使用 for / yield

现在我尝试了:

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

错误是:“值更新不是scala.collection.immutable.IndexedSeq [Int] 的成员”

我该如何解决这个问题?我知道语法“for / yield”非常强大,但我不知道如何使用它。

7 个答案:

答案 0 :(得分:3)

有一个sliding函数可以完全满足您的需求:

(for {
  i <- Array(1,2,3,4,5).sliding(2,2)
  j <- i.reverse
} yield j).toArray

答案 1 :(得分:1)

我正在通过 Scala for the Impatient 来刷新我的Scala编码技能。鉴于书中这一点引入的概念,我相信作者正在寻找以下内容:

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)

答案 2 :(得分:0)

在您的生成器中,您使用的0 until v.length by 2IndexedSeq。这是您的输入类型,yield将为res生成相同的集合类型。

由于immutable.IndexedSeq是不可变的,因此无法对其进行修改。因此,不允许在{i-1更新项目的res(i-1)=a(i)

因此,一个选择是在继续之前将res转换为可变集合。

通常更可取的选择是在不更新的情况下解决它。这是example使用foldLeft,它会迭代我们的IndexedSeq并构建一个新的扁平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 )

答案 3 :(得分:0)

错误是因为res是不可变序列(Vector),无法就地更新。 Vector确实有一个updated(index: Int, elem: A)方法,它返回一个带有更新元素的新Vector。

我不确定练习的作者是什么想法 - 使用for / yield似乎有点尴尬,但你可以使用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)

没有for / yield的更简洁的方法,也可以使用flatMap

a.grouped(2).toArray.flatMap(_.reverse)                                      
                                   //> res5: Array[Int] = Array(2, 1, 4, 3, 5)

答案 4 :(得分:0)

只是一种新方法:

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

答案 5 :(得分:0)

这是我的解决方案:

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

答案 6 :(得分: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)