我正在尝试使用Scala进行一些实验.我想重复这个实验(随机)直到预期结果出来并得到结果.如果我使用while或do-while循环执行此操作,那么我需要编写(假设'body'代表实验,'cond'表示是否预期):
do {
val result = body
} while(!cond(result))
Run Code Online (Sandbox Code Playgroud)
但是,它不起作用,因为最后一个条件不能引用循环体中的局部变量.我们需要像这样修改这个控件抽象:
def repeat[A](body: => A)(cond: A => Boolean): A = {
val result = body
if (cond(result)) result else repeat(body)(cond)
}
Run Code Online (Sandbox Code Playgroud)
它以某种方式工作,但对我来说并不完美,因为我需要通过传递两个参数来调用此方法,例如:
val result = repeat(body)(a => ...)
Run Code Online (Sandbox Code Playgroud)
我想知道是否有更有效和自然的方法来做到这一点,使它看起来更像一个内置的结构:
val result = do { body } until (a => ...)
Run Code Online (Sandbox Code Playgroud)
在这篇文章中找到了一个没有返回值的身体的优秀解决方案:如何在重复直到?中进行Scala控制抽象?,最后一个单行答案.它body
在该答案中的部分不返回值,因此它until
可以是新AnyRef
对象的方法,但是这个技巧在这里不适用,因为我们想要返回A
而不是AnyRef
.有没有办法实现这个目标?谢谢.
你正在混合编程风格并因此而陷入困境.
您的循环仅适用于加热处理器,除非您在其中执行某种副作用.
do {
val result = bodyThatPrintsOrSomething
} until (!cond(result))
Run Code Online (Sandbox Code Playgroud)
所以,如果你要使用副作用代码,只需将条件放入var:
var result: Whatever = _
do {
result = bodyThatPrintsOrSomething
} until (!cond(result))
Run Code Online (Sandbox Code Playgroud)
或等效的:
var result = bodyThatPrintsOrSomething
while (!cond(result)) result = bodyThatPrintsOrSomething
Run Code Online (Sandbox Code Playgroud)
或者,如果采用函数方法,则无论如何都必须返回计算结果.然后使用类似的东西:
Iterator.continually{ bodyThatGivesAResult }.takeWhile(cond)
Run Code Online (Sandbox Code Playgroud)
(有一个已知的烦恼是Iterator
没有做好所有好的加上列表中的第一个坏的).
或者你可以使用你的repeat
方法,这是尾递归.如果您不相信它,请检查字节码(带javap -c
),添加@annotation.tailrec
注释,以便编译器在不是尾递归时抛出错误,或者使用以下var
方法将其写为while循环:
def repeat[A](body: => A)(cond: A => Boolean): A = {
var a = body
while (cond(a)) { a = body }
a
}
Run Code Online (Sandbox Code Playgroud)
通过微小的修改,您可以使用一种非常流畅的API来转换当前的方法,从而产生接近您想要的语法:
class run[A](body: => A) {
def until(cond: A => Boolean): A = {
val result = body
if (cond(result)) result else until(cond)
}
}
object run {
def apply[A](body: => A) = new run(body)
}
Run Code Online (Sandbox Code Playgroud)
既然do
是保留字,我们必须配合run
.结果现在看起来像这样:
run {
// body with a result type A
} until (a => ...)
Run Code Online (Sandbox Code Playgroud)
编辑:
我刚才意识到我几乎彻底改变了链接问题中已提出的内容.一种可能延长该方法返回一个类型A
,而不是Unit
将是:
def repeat[A](body: => A) = new {
def until(condition: A => Boolean): A = {
var a = body
while (!condition(a)) { a = body }
a
}
}
Run Code Online (Sandbox Code Playgroud)