9

我经常遇到一个模式,所以我想知道 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
}

这已经在标准库中实现了吗?

一个使用示例可能是保持当前位置的列表(拉链)。通过调用next,None如果当前位置之后没有元素或Option同一个列表没有元素,则返回,但当前位置递增。通过使用上述方法,end可以构造一个将列表查找到末尾的方法。

4

4 回答 4

2

你正在做的看起来像是一种非常特殊的蹦床。更一般的 case 使用包装在 case 类中的函数而不是 an Option,并支持不同的参数和返回类型。

正如 Calin-Andrei 指出的,蹦床在标准库中可以使用TailCalls 对象

从第一个链接:

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
于 2013-07-29T14:51:58.387 回答
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因为我们不能以 a 开头None(否则可能出现无限循环)。

于 2013-07-29T16:32:18.500 回答
1

在选美比赛的情况下,您可以构建一个函数,通过使用柯里化将现有的怪物转换为您正在谈论的怪物。

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你得到预期的行为。

于 2013-04-07T20:45:31.297 回答
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
于 2013-04-19T02:49:12.220 回答