Scala - 状态闭包/匿名函数
Scala - State in closure / anonymous function
我正在尝试在满足特定谓词的情况下从列表中获取元素。然而,谓词取决于最后一个元素。这是
一些说明问题和我的解决方案的代码
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)
但是我不确定这是偶然发生的还是真的
scala 故意的。有更好的方法吗?
谢谢,
向
curr
不是 "inside the closure"。这个概念被称为 "closure" 因为匿名函数
i =>
val test = i > curr
curr = i
test
是一个开放表达式(它有一个自由变量 curr
),它通过将 curr
绑定到 var
关闭 即在函数的 外部 声明。
括号中的块不是传递给 takeWhile
的函数 – 它是计算函数的表达式。
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)
我正在尝试在满足特定谓词的情况下从列表中获取元素。然而,谓词取决于最后一个元素。这是 一些说明问题和我的解决方案的代码
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)
但是我不确定这是偶然发生的还是真的 scala 故意的。有更好的方法吗?
谢谢, 向
curr
不是 "inside the closure"。这个概念被称为 "closure" 因为匿名函数
i =>
val test = i > curr
curr = i
test
是一个开放表达式(它有一个自由变量 curr
),它通过将 curr
绑定到 var
关闭 即在函数的 外部 声明。
括号中的块不是传递给 takeWhile
的函数 – 它是计算函数的表达式。
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)