对函数的循环调用,直到它返回None

时间:2013-03-29 15:59:47

标签: scala functional-programming

我经常遇到一个模式,所以我想知道Scala库中是否有任何方便的方法。

让它成为一个函数f: A => Option[B]。我想从f开始xf(f(f(x).get).get...)f,然后None返回None,并保留最后一次非@tailrec def recurrentCallUntilNone[B](f: B => Option[B], x: B): B = f(x) match { case Some(y) => recurrentCallUntilNone(f, y) case None => x } 。 1}}值。

我为此写了一个实现:

next

这是否已在标准库中实现?

此用法示例可以是保持当前位置的列表(Zipper)。通过调用None,如果当前位置之后没有元素,则返回Option,或者同一列表中的end,但当前位置递增。通过使用上述方法,可以构造一个{{1}}方法来搜索列表到最后。

4 个答案:

答案 0 :(得分:2)

您正在做的事情看起来像一种非常特殊的trampoline类型。更一般的情况使用包含在case类而不是Option中的函数,并支持不同的参数和返回类型。

Calin-Andrei指出使用TailCalls object标准库中可以使用蹦床。

从第一个链接:

def even2(n: Int): Bounce[Boolean] = {
  if (n == 0) Done(true)
  else Call(() => odd2(n - 1))
}
def odd2(n: Int): Bounce[Boolean] = {
  if (n == 0) Done(false)
  else Call(() => even2(n - 1))
}
trampoline(even2(9999))

sealed trait Bounce[A]
case class Done[A](result: A) extends Bounce[A]
case class Call[A](thunk: () => Bounce[A]) extends Bounce[A]

def trampoline[A](bounce: Bounce[A]): A = bounce match {
  case Call(thunk) => trampoline(thunk())
  case Done(x) => x
}

现在使用标准库

import scala.util.control.TailCalls._

def even2(n: Int): TailRec[Boolean] = {
  if (n == 0) done(true)
  else tailcall(odd2(n - 1))
}
def odd2(n: Int): TailRec[Boolean] = {
  if (n == 0) done(false)
  else tailcall(even2(n - 1))
}
even2(9999).result

答案 1 :(得分:2)

怎么样:

Stream.iterate(Some(x)) { x => x.flatMap(f _) }.takeWhile { _.isDefined }.last

<强>更新

甚至更整洁的恕我直言(只有一次遍历):

val result = {
  val xs = Stream.iterate(Some(x)) { x => x.flatMap(f _) }
  (xs zip xs.tail) collectFirst {
    case (Some(x), None) => x
  } get
}

请注意,调用collectFirst是安全的,因为我们无法以None开头(否则可能会出现无限循环)。

答案 2 :(得分:1)

如果是选美比赛,你可以建立一个功能,通过使用currying将现有的一个变成你正在谈论的怪物。

def composeUntilTheEnd[B](f: Option[B] => Option[B])(x: Option[B]): Option[B] = 
        if (f(x) != None) composeUntilTheEnd(f)(f(x))
        else x

def ff = composeUntilTheEnd((x:Option[Int]) => x)_

现在调用ff可以获得预期的行为。

答案 3 :(得分:1)

如果需要,可以从选项中创建一个蒸汽,然后在其上使用流函数来获取最后定义的元素。 (或者更确切地说是未定义元素之前的最后定义元素)

def options[B](f: B => Option[B], initialValue: Option[B]): Stream[Option[B]] = {
  initialValue #:: options(f, initialValue.map(f(_)).flatten)
}

options.takeWhile(_.isDefined).last