我经常遇到一个模式,所以我想知道Scala库中是否有任何方便的方法。
让它成为一个函数f: A => Option[B]
。我想从f
开始x
,f(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}}方法来搜索列表到最后。
答案 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