我正在尝试使用Scala进行一些实验。我想重复这个实验(随机)直到预期结果出来并得到结果。如果我用while或do-while循环执行此操作,那么我需要编写(假设'body'表示实验,'cond'表示是否预期):
do {
val result = body
} while(!cond(result))
然而,它不起作用,因为最后一个条件不能引用循环体中的局部变量。我们需要像这样修改这个控件抽象:
def repeat[A](body: => A)(cond: A => Boolean): A = {
val result = body
if (cond(result)) result else repeat(body)(cond)
}
它以某种方式工作,但对我来说并不完美,因为我需要通过传递两个参数来调用此方法,例如:
val result = repeat(body)(a => ...)
我想知道是否有一种更有效,更自然的方法来实现这一点,使它看起来更像是内置结构:
val result = do { body } until (a => ...)
在这篇文章中找到了一个没有返回值的身体的优秀解决方案:How Does One Make Scala Control Abstraction in Repeat Until?,最后一个单行答案。该答案中的body
部分不返回值,因此until
可以是新AnyRef
对象的方法,但这个技巧不适用于此,因为我们想要返回A
而不是AnyRef
。有没有办法实现这个目标?感谢。
答案 0 :(得分:6)
你正在混合编程风格并因此而陷入困境。
您的循环仅适用于加热处理器,除非您在其中产生某种副作用。
do {
val result = bodyThatPrintsOrSomething
} until (!cond(result))
所以,如果你要使用副作用代码,只需将条件放入var:
var result: Whatever = _
do {
result = bodyThatPrintsOrSomething
} until (!cond(result))
或同等的:
var result = bodyThatPrintsOrSomething
while (!cond(result)) result = bodyThatPrintsOrSomething
或者,如果采用功能方法,则无论如何都必须返回计算结果。然后使用类似的东西:
Iterator.continually{ bodyThatGivesAResult }.takeWhile(cond)
(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
}
方法:
{{1}}
答案 1 :(得分:5)
通过微小的修改,您可以使用一种非常流畅的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)
}
由于do
是保留字,因此我们必须使用run
。结果现在看起来像这样:
run {
// body with a result type A
} until (a => ...)
修改强>
我刚刚意识到我几乎彻底改变了链接question中已经提出的内容。将该方法扩展为返回类型A
而不是Unit
的一种可能性是:
def repeat[A](body: => A) = new {
def until(condition: A => Boolean): A = {
var a = body
while (!condition(a)) { a = body }
a
}
}
答案 2 :(得分:0)
为了记录前面提出的建议的衍生物,我采用了repeat { ... } until(...)
的尾递归实现,其中还包括迭代次数的限制:
def repeat[A](body: => A) = new {
def until(condition: A => Boolean, attempts: Int = 10): Option[A] = {
if (attempts <= 0) None
else {
val a = body
if (condition(a)) Some(a)
else until(condition, attempts - 1)
}
}
}
这允许循环在attempts
执行身体后纾困:
scala> import java.util.Random
import java.util.Random
scala> val r = new Random()
r: java.util.Random = java.util.Random@cb51256
scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res0: Option[Int] = Some(98)
scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res1: Option[Int] = Some(98)
scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res2: Option[Int] = None
scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res3: Option[Int] = None
scala> repeat { r.nextInt(100) } until(_ > 90, 4)
res4: Option[Int] = Some(94)