通常,while循环的样板(r
是我想要的结果,p
是预测变量:
var r, p;
while(p()) {
(r, p) = compute(r)
}
我可以将它转换为递归以摆脱var
:
def f(r) = {
val (nr, p) = compute(r)
if(p()) nr
else f(nr)
}
是否有内置的方法来实现这样的逻辑?我知道Iterator.continually
,但似乎仍需要var
来存储副作用。
答案 0 :(得分:4)
def compute(i: Int): (Int, () => Boolean) =
(i - 1) -> { () => i > 1 }
要创建一个不可变的while
,您需要iteration
- 一个接受state
并返回相同类型的新state
加退出条件的函数。
这不是最好的解决方案 - 在我看来这段代码很难读,但是既然你提到了它:
val (r, p) = Iterator.continually(()).
scanLeft( 13 -> { () => true } ){
case ((r, p), _) => compute(r)
}.dropWhile{ case (r, p) => p() }.
next
// r: Int = 0
// p: () => Boolean = <function0>
您可以使用val (r, _) =
,因为您不需要p
。
如果您希望使用Iterator
的解决方案,请this answer与Iterator.iterate
一起使用。
我想这是一个惯用的解决方案。您总是可以使用显式状态类型将while
循环重写为尾递归:
@annotation.tailrec
def lastWhile[T](current: T)(f: T => (T, () => Boolean)): T = {
val (r, p) = f(current)
if (p()) lastWhile(r)(f)
else r
}
lastWhile(13){ compute }
// Int = 0
如果您使用的是scalaz
,则已有此类方法。它产生Stream
,所以你应该得到最后一个元素。
在迭代结束时,您应该生成Option
(None
是一个退出条件),其中包含Pair
流元素(r
)和下一个状态{{ 1}}:
(r, p())
答案 1 :(得分:1)
我不知道这是否真的回答了“内置”的问题。我不相信有一个比你的递归例程更容易实现或理解的解决方案。但这是另一种攻击问题的方法。
您可以使用Iterator.iterate
创建无限迭代器,然后找到第一个未通过谓词的元素。
// Count until we are greater than 5
def compute(r: Int): (Int, () => Boolean) = {
(r + 1, () => (r < 5))
}
// Start at the beginning
val r = 1
val p = () => true
// Create an infinite iterator of computations
val it = Iterator.iterate((r, p))({
case (r, _) => compute(r)
})
// Find (optionally) the first element that fails p. Then get() the result.
val result = it.find({ case (_, p) => !p() })
.map { _._1 }
.get