Scala - 闭包状态/匿名函数



我试图在满足特定谓词时从列表中获取元素。但是,谓词取决于最后一个元素。这是说明问题和我的解决方案的一些代码

val list = List(1,2,3,1,4)
list.takeWhile {
   // state inside the closure?!
   var curr = 0
   // actual function application
   i => 
   val test = i > curr
   // update state
   curr = i
   test
}

预期的结果是

List(1,2,3)

但是我不确定这是偶然的还是偶然的斯卡拉有意为之。有没有更好的方法

谢谢穆基

takeWhile协定中实际上没有任何内容指定应用操作的顺序。

以下是不依赖突变的原因:

scala> def f(list: collection.GenSeq[Int]) = list.takeWhile {
     |    // state inside the closure?!
     |    var curr = 0
     | 
     |    // actual function application
     |    i => 
     |    val test = i > curr
     |    // update state
     |    curr = i
     |    test
     | }
f: (list: scala.collection.GenSeq[Int])scala.collection.GenSeq[Int]
scala> f(List(1,2,3,1,4,8,9,10).par)
res22: scala.collection.GenSeq[Int] = ParVector()
scala> f(List(1,2,3,1,4,8,9,10).par)
res23: scala.collection.GenSeq[Int] = ParVector(1, 2)

这是一个你可以表示为折叠的函数,但你通常会把它编码为命令式循环并将其命名为takeMonotonically

但作为练习:

scala> def f(xs: collection.GenSeq[Int]) = xs.foldLeft(List[Int](),false) {
     | case ((Nil,_),i) => (i::Nil,false)
     | case ((acc,done),i) if !done && acc.head < i => (i::acc,false)
     | case ((acc,_),_) => (acc,true)
     | }
f: (xs: scala.collection.GenSeq[Int])(List[Int], Boolean)
scala> f(List(1,2,3,1,4,8,9,10))
res24: (List[Int], Boolean) = (List(3, 2, 1),true)
scala> f(List(1,2,3,1,4,8,9,10).par)
res25: (List[Int], Boolean) = (List(3, 2, 1),true)
scala> f(List(1,2,3,1,4,8,9,10).par)
res26: (List[Int], Boolean) = (List(3, 2, 1),true)
scala> def g(xs: collection.GenSeq[Int]) = f(xs)._1.reverse
g: (xs: scala.collection.GenSeq[Int])List[Int]
scala> g(List(1,2,3,1,4,8,9,10).par)
res27: List[Int] = List(1, 2, 3)

作为进一步的练习:

object Test extends App {
  def takeMonotonically[R](xs: collection.GenTraversableLike[Int,R]) = {
    val it = xs.toIterator
    if (it.isEmpty) Nil
    else {
      var last = it.next
      val b = collection.mutable.ListBuffer[Int]()
      b append last
      var done = false
      while (!done && it.hasNext) {
        val cur = it.next
        done = cur <= last
        if (!done) b append cur
      }
      b.result
    }
  }
  implicit class `gentrav take mono`[R](private val xs: collection.GenTraversableLike[Int,R]) extends AnyVal {
    def takeMonotonically[R] = Test.takeMonotonically(xs)
  }
  Console println takeMonotonically(List(1,2,3,1,4,8,9,10))
  Console println List(1,2,3,1,4,8,9,10).takeMonotonically
  Console println takeMonotonically(List(1,2,3,1,4,8,9,10).par)
  Console println takeMonotonically(List(1,2,3,1,4,8,9,10).par)
}

或者想一想:

scala> List(1,2,3,4,5,6,1,4,8,9,10).par.iterator.sliding(2).takeWhile(vs => vs(0) < vs(1)).toList
res0: List[Seq[Int]] = List(List(1, 2), List(2, 3), List(3, 4), List(4, 5), List(5, 6))
scala> val end = res0.last(1)
end: Int = 6
scala> (res0 map (_(0))) :+ end
res1: List[Int] = List(1, 2, 3, 4, 5, 6)

curr不是"在闭包内"。这个概念被称为"闭包",因为匿名函数

i => 
  val test = i > curr
  curr = i
  test

是一个开放表达式(它有一个自由变量curr),它通过将curr绑定到函数外部声明的var闭合

括号中的块不是传递给takeWhile的函数 – 它是计算函数的表达式。

最新更新