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 }
}