Scala扩展while循环到do-until表达式

Yan*_*ang 9 scala

我正在尝试使用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.有没有办法实现这个目标?谢谢.

Rex*_*err 6

你正在混合编程风格并因此而陷入困境.

您的循环仅适用于加热处理器,除非您在其中执行某种副作用.

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)


blu*_*e10 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)
}
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)