PartialFunction lift метод превращает PartialFunction в Function, возвращая результат Option.
Есть ли обратная операция для этого, которая превращает a Function1[A, Option[B]] в PartialFunction[A, B]?
PartialFunction lift метод превращает PartialFunction в Function, возвращая результат Option.
Есть ли обратная операция для этого, которая превращает a Function1[A, Option[B]] в PartialFunction[A, B]?
Не в библиотеке, но ее легко построить. Однако isDefinedAt должен будет полностью оценить функцию, делающую ее более дорогой, чем это типично для частичных функций, построенных по шаблону, а также, возможно, приводит к нежелательным побочным эффектам.
scala> def unlift[A, B](f : (A => Option[B])) = new PartialFunction[A,B] {
| def isDefinedAt(x : A) = f(x).isDefined
| def apply(x : A) = f(x).get
| }
unlift: [A,B](f: (A) => Option[B])java.lang.Object with PartialFunction[A,B]
scala> def f(x : Int) = if (x == 1) Some(1) else None
f: (x: Int)Option[Int]
scala> val g = unlift(f)
g: java.lang.Object with PartialFunction[Int,Int] = <function1>
scala> g.isDefinedAt(1)
res0: Boolean = true
scala> g.isDefinedAt(2)
res1: Boolean = false
scala> g(1)
res2: Int = 1
scala> g(2)
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:262)
at scala.None$.get(Option.scala:260)
at $anon$1.apply(<console>:7)
at scala.Function1$class.apply$mcII$sp(Function1.scala:39)
at $anon$1.apply$mcII$sp(<console>:5)
at .<init>(<console>:9)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools....
Пурист может также wrap isDefinedAt с блоком try/catch для возврата false в исключения.
Трудно найти все эти прекрасные ответы из диапазона scala светильников, но в случае, если вы хотите узнать о том, что находится в стандартной библиотеке, он находится в объекте scala.Function. (В 2.9.)
/** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. Important note:
* this transformation implies the original function will be called 2 or more
* times on each logical invocation, because the only way to supply an implementation
* of isDefinedAt is to call the function and examine the return value.
*
* @param f a function T => Option[R]
* @return a partial function defined for those inputs where
* f returns Some(_) and undefined where f returns None.
* @see PartialFunction#lift
*/
def unlift[T, R](f: T => Option[R]): PartialFunction[T, R] = new PartialFunction[T, R] {
def apply(x: T): R = f(x).get
def isDefinedAt(x: T): Boolean = f(x).isDefined
override def lift: T => Option[R] = f
}
Чтобы построить ответ Джеймса с более сложным примером, у меня есть следующий код в моей библиотеке вещей-the- Scala -library-forget (или didn't-trust-you-with):
class DroppedFunction[-A,+B](f: A => Option[B]) extends PartialFunction[A,B] {
private[this] var tested = false
private[this] var arg: A = _
private[this] var ans: Option[B] = None
private[this] def cache(a: A) {
if (!tested || a != arg) {
tested = true
arg = a
ans = f(a)
}
}
def isDefinedAt(a: A) = {
cache(a)
ans.isDefined
}
def apply(a: A) = {
cache(a)
ans.get
}
}
class DroppableFunction[A,B](f: A => Option[B]) {
def drop = new DroppedFunction(f)
}
implicit def function_is_droppable[A,B](f: A => Option[B]) = new DroppableFunction(f)
Большая часть кода посвящена проверке кэширования функции (если приложение применяется сразу после isDefinedAt). Пример использования:
scala> val f = (x: Int) => if (x>=0) Some(x) else None
f: (Int) => Option[Int] = <function1>
scala> Array(-2,-1,0,1,2).collect(f.drop)
res0: Array[Int] = Array(0, 1, 2)
Кэширование помогает ускорить работу и избежать проблем с двойным побочным эффектом (по крайней мере, когда isDefinedAt используется непосредственно перед apply и когда функция опускает побочные эффекты, когда возвращает None).
На основе ответа Джеймса...
Также можно кэшировать результат, сгенерированный в isDefinedAt, а затем вернуть это при вызове apply, избегая двойного выполнения.
Однако Scala не обеспечивает выполнение чистых функций, поэтому легко найти примеры реальной жизни, где либо разветвленная стратегия приведет к неожиданным и неожиданным результатам. Из-за этого обычно не рекомендуется "развязывать" что-то в PartialFunction
Вы можете использовать Function.unlift, См. документы.
Мы должны всегда использовать литерал частичной функции для построения PartialFunction, потому что это слишком тривиально, чтобы правильно реализовать applyOrElse для PartialFunction.
Таким образом, правильный unlift должен быть реализован как таковой:
// Use AnyVal to avoid the indirect reference to f
class Extractor[A, B](val f: A => Option[B]) extends AnyVal {
def unapply(a: A) = f(a)
}
def unlift[A, B](f: A => Option[B]): PartialFunction[A, B] = {
val LocalExtractor = new Extractor(f)
// Create the PartialFunction from a partial function literal
{ case LocalExtractor(b) => b }
}