Повторяющийся вызов функции до ее возвращения никто
я часто сталкиваюсь с шаблоном, поэтому мне было интересно, есть ли какой-либо удобный метод в библиотеке 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
}
это уже реализовано в стандартной библиотеке?
использование пример для этого может быть для списка (молнии), который сохраняет текущую позицию. Позвонив next
, None
возвращается, если нет элементов после текущей позиции или Option
для того же списка, но с увеличением текущей позиции. Используя вышеуказанный метод, an end
метод может быть построен, который ищет список до конца.
4 ответов
то, что вы делаете, похоже на очень специфический тип батут. Более общий случай использует функции, обернутые в классы case вместо Option
и поддерживает различные типы аргументов и возвращаемых типов.
как отмечает Калин-Андрей, батуты доступны в стандартной библиотеке с помощью 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
как насчет:
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
(в противном случае возможен бесконечный цикл).
в случае конкурса красоты вы можете построить функцию, которая преобразует существующую в монстра, о котором вы говорили, с помощью карринга.
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
вы получаете предполагаемое поведение.
Если вы хотите, вы можете создать steam из своих опций, а затем использовать функции потока, чтобы получить последний определенный элемент. (Или, скорее, последний определенный элемент перед неопределенным элементом)
def options[B](f: B => Option[B], initialValue: Option[B]): Stream[Option[B]] = {
initialValue #:: options(f, initialValue.map(f(_)).flatten)
}
options.takeWhile(_.isDefined).last