在Scala Odersky的书中,他有一个例子解释第295页的部分功能。它从这个功能开始:
val second: List[Int] => Int = {
case x :: y :: _ => y
}
因此,如果您传递一个三元素列表而不是一个空列表,则上述函数将成功。
second(List(5,6,7))
有效,但不是
second(List())
上面会抛出MatchError: List
这是令我困惑的部分。奥德斯基写道:
如果要检查是否定义了部分函数,必须首先告诉编译器您知道正在使用部分函数。
为什么我要检查是否定义了部分功能。什么是部分功能?它是一个仅适用于某些值的函数吗?
类型List [Int] => Int包括从整数列表到整数列表的所有函数,无论函数是否是部分函数。只包含从整数列表到整数的部分函数的类型写成PartialFunction [List [Int],Int]。
因此上面的函数返回List [Int] =>类型的函数。 Int,我看到了,但为什么我们需要更改此函数以键入PartialFunction[List[Int], Int]
?
这是重新定义的功能:
val second: PartialFunction[List [Int], Int] = {
case x :: y :: _ => y
}
我真的不明白。有什么好处?为什么我们要检查是否定义了部分函数?这甚至意味着什么?
答案 0 :(得分:4)
部分函数是任何函数,只接受一个参数,定义(即有效)仅适用于其参数的某个范围。值。例如,Math.asin
仅针对范围[-1.0, 1.0]
中的参数值定义,并且对于该范围之外的值未定义 - 因此它是部分函数。例如,如果我们调用Math.asin(5.0)
,我们会返回NaN
,这意味着没有为该参数定义函数。
请注意,部分函数不一定要抛出异常;它只需要做一些事情而不是返回一个有效的值。
函数式编程的一个关键原则是引用透明度( RT ),这意味着我们应该能够替换一个表达式(例如具有该表达式值的函数调用),而不改变程序的含义。 (有关此主题的更多信息,我强烈建议您阅读Chiusano和Bjarnason的Functional Programming in Scala。)显然,如果抛出异常或返回无效值,则会发生故障。对于对部分函数的引用是透明透明的,我们只能用它们被定义的参数值来调用它们,或者我们需要优雅地处理未定义的值。那么我们如何判断是否为某个任意参数值定义了部分函数?
在 Scala 中,我们可以将部分函数表达为scala.PartialFunction
的子类,以便我们回答这个问题。
让我们在 Scala REPL 会话中查看您的示例...
$ scala
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala> val second: List[Int] => Int = {
| case x :: y :: _ => y
| }
<console>:11: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
val second: List[Int] => Int = {
^
second: List[Int] => Int = $$Lambda$3181/1894473818@27492c62
那我们刚刚做了什么?我们将second
定义为对带有List[Int]
参数的函数的引用,并返回Int
(列表中的第二个值)。
您会注意到 Scala编译器认识到这不符合所有情况,并警告您这一事实。这是一个部分函数,在某种意义上它会因某些参数而失败,但它不是scala.PartialFunction
的实例,因为我们可以验证如下:
scala> second.isInstanceOf[PartialFunction[List[Int], Int]]
res0: Boolean = false
顺便提一下,List[Int] => Int
类型是scala.Function1[List[Int], Int]
的简写,因此second
类型是该类型的实例:
scala> second.isInstanceOf[Function1[List[Int], Int]]
res1: Boolean = true
调用此版本的函数会产生您指出的结果:
scala> second(List(1, 2, 3))
res1: Int = 2
scala> second(Nil)
scala.MatchError: List() (of class scala.collection.immutable.Nil$)
at .$anonfun$second$1(<console>:11)
at .$anonfun$second$1$adapted(<console>:11)
... 36 elided
问题在于,如果我们只有一些列表值l
,并且不知道该列表中的内容,我们就不知道我们是否会获得例外如果我们将它传递给second
引用的函数。现在,我们可以将调用放在try
块和catch
任何异常中,但这是一个冗长且不太好的函数式编程风格。理想情况下,我们想知道我们是否可以先调用该函数以避免异常。不幸的是,没有办法从Function1
实例中说出来:
scala> second.isDefinedAt(Nil)
<console>:13: error: value isDefinedAt is not a member of List[Int] => Int
second.isDefinedAt(Nil)
^
我们需要声明second
类型PartialFunction[List[Int], Int]
如下:
scala> val second: PartialFunction[List[Int], Int] = {
| case x :: y :: _ => y
| }
second: PartialFunction[List[Int],Int] = <function1>
(顺便说一句,请注意您在此代码的问题中有一个拼写错误 - 以上是如何定义的。)
现在我们没有任何警告!我们告诉编译器这是一个PartialFunction
实例,所以编译器知道它的某些参数未定义,因此警告是多余的。我们现在可以验证这个事实:
scala> second.isInstanceOf[PartialFunction[List[Int], Int]]
res6: Boolean = true
我们现在还可以验证它是否针对特定值进行了定义:
scala> second.isDefinedAt(Nil)
res7: Boolean = false
scala> second.isDefinedAt(List(1, 2))
res9: Boolean = true
等等。 (如本书所述, Scala 编译器能够为我们实现这个神奇的isDefinedAt
函数。)
那么,这是否意味着我们现在应该编写这样的代码:
def getSecondValue(l: List[Int]): Option[Int] = {
// Check if second is defined for this argument. If so, call it and wrap in Some.
if(second.isDefinedAt(l)) Some(second(l))
// Otherwise, we do not have a second value.
else None
}
嗯,这也有点啰嗦。幸运的是,一旦second
是PartialFunction
个实例,我们就可以将上面重写为:
def getSecondValue(l: List[Int]): Option[Int] = second.lift(l)
lift
方法将部分函数转换为完整函数,为每个参数返回一个定义的值:如果定义了second
的参数,那么我们得到一个Some(value)
;否则,我们得到None
。
当你更熟悉函数式编程时,你会发现部分函数的概念,PartialFunction
更有用。如果你现在没有得到它,请不要担心;一切都会变得清晰。
答案 1 :(得分:0)
部分函数是一种函数,它不能为每个可能的输入值提供答案。它仅为可能数据的子集提供答案,并定义它可以处理的数据。在Scala中,还可以查询部分函数以确定它是否可以处理特定值。 举一个简单的例子,想象一下将一个数字除以另一个数字的正常函数:
val divide = (x: Int) => 42 / x
根据定义,当输入参数为零时,此功能会爆炸:
scala> divide(0)
java.lang.ArithmeticException: / by zero
虽然您可以通过捕获和抛出异常来处理这种特殊情况,但Scala允许您将除法函数定义为PartialFunction。执行此操作时,您还明确声明在输入参数不为零时定义函数:
val divide = new PartialFunction[Int, Int] {
def apply(x: Int) = 42 / x
def isDefinedAt(x: Int) = x != 0
}
https://alvinalexander.com/scala/how-to-define-use-partial-functions-in-scala-syntax-examples
您可以参考以上链接。