Scala 部分函数的逆';s提升法
Scala 部分函数的逆';s提升法,scala,Scala,PartialFunction的lift方法将PartialFunction转换为函数返回选项结果 是否存在与此相反的操作,将Function1[a,Option[B]]转换为PartialFunction[a,B]?不在库中,但很容易构建。然而,isDefinedAt必须对函数进行全面评估,使其比通过模式匹配构建的部分函数的典型成本更高,并且还可能导致不必要的副作用 scala> def unlift[A, B](f : (A => Option[B])) = new Partia
PartialFunction
的lift
方法将PartialFunction转换为函数
返回选项
结果
是否存在与此相反的操作,将
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....
scala>def unlift[A,B](f:(A=>Option[B])=newpartialfunction[A,B]{
|def isDefinedAt(x:A)=f(x)。isDefined
|def应用(x:A)=f(x)。获取
| }
unlift:[A,B](f:(A)=>Option[B])java.lang.Object与PartialFunction[A,B]
scala>def(x:Int)=如果(x==1)一些(1)其他无
f:(x:Int)选项[Int]
scala>val g=unlift(f)
g:java.lang.Object与PartialFunction[Int,Int]=
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
在scala.None$.get(Option.scala:262)
在scala.None$.get(Option.scala:260)
在$anon$1.应用(:7)
在scala.Function1$class.apply$mcII$sp处(Function1.scala:39)
在$anon$1。应用$mcII$sp(:5)
在。(:9)
在
请求时结果$。(:9)
请求时结果$()
请求时结果$scala_repl_result()
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处
位于sun.reflect.NativeMethodAccessorImpl.invoke(未知源)
在sun.reflect.DelegatingMethodAccessorImpl.invoke处(未知源)
位于java.lang.reflect.Method.invoke(未知源)
在scala.tools.nsc.transparer$Request$$anonfun$loadand run$1$$anonfun$apply$17.apply(transparer.scala:988)
在scala。工具。。。。
纯粹主义者也可以用try/catch块包装isDefinedAt,以便在异常时返回false。以James的答案为基础 还可以缓存在
isDefinedAt
中生成的结果,然后在调用apply
时返回该结果,从而避免重复执行
然而,Scala并没有强制执行纯函数,因此很容易找到现实生活中的例子,在这些例子中,任何一种解压策略都会产生出人意料的结果。因此,通常不建议将某些内容“取消”到
部分函数中,以James的答案为基础,使用一个更复杂的示例,我的Scala库中有以下代码,这些代码被Scala库遗忘了(或不信任你):
大部分代码用于确保缓存函数求值(只要apply正好在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)
scala>valf=(x:Int)=>如果(x>=0)一些(x)其他没有
f:(Int)=>选项[Int]=
scala>数组(-2,-1,0,1,2).收集(f.drop)
res0:Array[Int]=数组(0,1,2)
缓存有助于加快速度并避免双重副作用问题(至少当在应用之前立即使用isDefinedAt
时,以及当函数返回None
时忽略副作用时)。很难从一系列scala灯具中找到所有这些好的答案,但是,如果您想了解标准库中的一个,它位于scala.Function companion对象中。(在2.9中)
我们应该始终使用部分函数文本来构建PartialFunction
,因为对于PartialFunction
来说,正确实现applyOrElse
太简单了
因此,正确的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 }
}
你可以使用函数。unlift
,。你是说返回部分函数[a,B]
的东西,不是吗?可能重复部分函数的lift
正是我要找的。谢谢!:)谢谢,双重评估的需要解释了为什么它从图书馆里消失了。哇,这看起来很吓人。保证它不是线程安全的。它可能有它的用途,但是。。。“保重!”乔纳奇里斯托弗萨恩瓦尔德说,“的确如此。和大多数使用缓存的东西一样,它不是线程安全的。我喜欢这个答案。我看到了两种潜在的线程安全解决方案,请回顾一下:一种是将tested
、arg
和ans
封装在元组或case类中,然后DroppedFunction
将始终具有内部一致的状态。另一种方法是完全避免这个问题,并确保在任何多线程上下文中,dropped函数
是在作用域的本地新创建的。@samthebest-使用case类可以提供一致性,但不能阻止同一函数的多个应用程序。在synchronized
块中包装isDefinedAt
和apply
稍微好一点,但这不会阻止用户在一个线程中调用isDefinedAt
,然后在另一个线程中调用另一个线程,然后在第一个线程中使用apply
,并从第二个线程中获取结果。。最好不要在多线程上下文中使用它,因为您无法确保人们使用applyOrElse
(这可能是安全的)。您没有实现getOrElse
。看有趣的。这是否避免了当前(29-2.11)库实现中出现的双重执行?如果是,为什么库实现没有以这种方式实现它?
/** 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
}
// 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 }
}