49

我是 Scala 的新手,我正在使用 2.9.1,我正在努力了解如何使用部分函数。我对柯里化函数有基本的了解,并且我知道部分函数有点像柯里化函数,它们只是二元或类似的。正如你所知道的那样,我对此有些不满。

似乎在某些情况下,如 XML 过滤,能够使用部分函数会非常有利,所以我希望能更好地理解如何使用它们。

我有一个使用 RewriteRule 结构的函数,但我需要它使用两个参数,而 RewriteRule 结构只需要一个或一个部分函数。我认为这是我认为它有帮助的案例之一。

欢迎任何建议、链接、智慧之言等!

到目前为止的答案非常好,并且已经消除了我的一些基本误解。我认为他们也解释了我在哪里挣扎 - 我认为发布一个更具体的新问题可能会有所帮助,所以我也会这样做。

4

2 回答 2

144

偏函数是仅对您可能传递给它的那些类型的值的子集有效的函数。例如:

val root: PartialFunction[Double,Double] = {
  case d if (d >= 0) => math.sqrt(d)
}

scala> root.isDefinedAt(-1)
res0: Boolean = false

scala> root(3)
res1: Double = 1.7320508075688772

当您知道如何检查函数是否已定义时,这很有用。收集,例如:

scala> List(0.5, -0.2, 4).collect(root)   // List of _only roots which are defined_
res2: List[Double] = List(0.7071067811865476, 2.0)

不会帮助您将两个参数放在您真正想要的位置。

相反,部分应用的函数是其中一些参数已经被填充的函数。

def add(i: Int, j: Int) = i + j
val add5 = add(_: Int,5)

现在你只需要一个参数——添加 5 的东西——而不是两个:

scala> add5(2)
res3: Int = 7

你可以从这个例子中看到如何使用它。

但是,如果您需要指定这两个参数,这仍然不会这样做 - 例如map,您想使用 ,并且您需要给它一个参数的函数,但您希望它添加两个不同的东西。那么你可以

val addTupled = (add _).tupled

这将部分应用该函数(实际上,只需从该方法中创建一个函数,因为没有填充任何内容),然后将单独的参数组合成一个元组。现在您可以在需要单个参数的地方使用它(假设类型正确):

scala> List((1,2), (4,5), (3,8)).map(addTupled)
res4: List[Int] = List(3, 9, 11)

相比之下,currying又不一样了。它将表单的功能(A,B) => C转换为A => B => C. 也就是说,给定一个具有多个参数的函数,它将生成一个函数链,每个函数接受一个参数并返回一个较短的链(您可以将其视为一次部分应用一个参数)。

val addCurried = (add _).curried

scala> List(1,4,3).map(addCurried)
res5: List[Int => Int] = List(<function1>, <function1>, <function1>)

scala> res5.head(2)   // is the first function, should add 1
res6: Int = 3

scala> res5.tail.head(5)   // Second function should add 4
res7: Int = 9

scala> res5.last(8)  // Third function should add 3
res8: Int = 11
于 2011-12-28T00:21:40.267 回答
36

Rex Kerr 的解释非常好——这也不足为奇。问题显然是将部分功能部分应用功能混为一谈。不管它值多少钱,当我学习 Scala 时,我自己也犯了同样的困惑。

但是,由于这个问题确实引起了对部分函数的关注,所以我想稍微谈谈它们。

很多人说偏函数是没有为所有输入定义的函数,这在数学中是正确的,但在 Scala 中则不然。在 Scala 中,也可能不会为所有输入定义一个函数。事实上,既然偏函数继承自函数,那么函数也包括所有偏函数,这是不可避免的。

其他人提到了方法isDefinedAt,这确实是一个区别,但主要是关于实现的。确实如此,Scala 2.10 可能会发布一个“快速偏函数”,它不依赖于isDefinedAt.

一些人甚至暗示apply偏函数的方法与函数的方法做了一些不同的事情apply,比如只为定义的输入执行——这与事实相去甚远。apply方法完全一样

偏函数真正归结为另一种方法:orElse. 这总结了偏函数的所有用例比 更好isDefinedAt,因为偏函数实际上是关于做以下事情之一:

  • 链接部分函数(就是这样orElse做的),以便在每个部分函数上尝试输入,直到其中一个匹配。
  • 如果部分函数不匹配,则做一些不同的事情,而不是抛出异常,如果你使用orElse.

请注意,我并不是说一切都可以轻松实现orElse。我只是说部分函数是在没有为它定义输入时做其他事情。

于 2011-12-28T01:37:25.407 回答