在 Scala 中使用部分函数 - 它是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8650549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using partial functions in Scala - how does it work?
提问by PlexQ
I'm new to Scala, I'm using 2.9.1, and I'm trying to get my head around how to use partial functions. I have a basic understanding of curried functions, and I know that partial functions are kind of like curried functions where they are only 2nary or some such. As you can tell I'm a bit green at this.
我是 Scala 的新手,我使用的是 2.9.1,我正在尝试了解如何使用部分函数。我对柯里化函数有基本的了解,我知道偏函数有点像柯里化函数,它们只有 2nary 或类似的函数。正如你所知道的,我对此有点不以为然。
It does seem that in certain cases like XML filtering, being able to partial functions would be highly advantageous, so I'm hoping get a better understanding of how to use them.
似乎在某些情况下,例如 XML 过滤,能够使用部分函数将是非常有利的,所以我希望能够更好地了解如何使用它们。
I have a function that uses the RewriteRule structure, but I need it to work with two arguments, whereas the RewriteRule structure only takes one, OR a partial function. I think this is one of the cases I'm thinking about it being helpful.
我有一个使用 RewriteRule 结构的函数,但我需要它处理两个参数,而 RewriteRule 结构只需要一个参数,或者一个部分函数。我认为这是我认为它有帮助的情况之一。
Any advice, links, words of wisdom etc. welcome!
欢迎提供任何建议、链接、智慧之言等!
The answers so far are excellent, and have cleared up a few fundamental misconceptions I have. I think they also explain where I'm struggling - I think maybe posting a new question being a bit more specific will help, so I'll do that too.
到目前为止的答案都非常好,并且澄清了我的一些基本误解。我认为他们也解释了我在哪里挣扎 - 我认为发布一个更具体的新问题会有所帮助,所以我也会这样做。
回答by Rex Kerr
A partial function is a function that is valid for only a subset of values of those types you might pass in to it. For example:
部分函数是仅对您可能传递给它的那些类型的值的子集有效的函数。例如:
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
This is useful when you have something that knows how to check whether a function is defined or not. Collect, for instance:
当您知道如何检查函数是否已定义时,这很有用。收集,例如:
scala> List(0.5, -0.2, 4).collect(root) // List of _only roots which are defined_
res2: List[Double] = List(0.7071067811865476, 2.0)
This is notgoing to help you place two arguments where you really want one.
这不会帮助您将两个参数放在您真正需要的地方。
In contrast, a partially applied functionis a function where some of its arguments have already been filled in.
相比之下,部分应用函数是其中一些参数已经被填充的函数。
def add(i: Int, j: Int) = i + j
val add5 = add(_: Int,5)
Now you only need one argument--the thing to add 5 to--instead of two:
现在你只需要一个参数——添加 5 的东西——而不是两个:
scala> add5(2)
res3: Int = 7
You can see from this example how to use it.
你可以从这个例子中看到如何使用它。
But if you need to specify those two arguments, this stillwon't do it--say you want to use map, for instance, and you need to give it a function of one argument, but you want it to add two different things. Well, then you can
但是如果您需要指定这两个参数,这仍然不会这样做——map例如,假设您想使用,并且您需要给它一个参数的函数,但您希望它添加两个不同的东西。那么你可以
val addTupled = (add _).tupled
which will partially apply the function (really, just create a function out of the method, since nothing has been filled in) and then combine the separate arguments into a tuple. Now you can use this in places that require a single argument (assuming that the type is correct):
这将部分应用该函数(实际上,只需从该方法中创建一个函数,因为尚未填充任何内容),然后将单独的参数组合成一个元组。现在您可以在需要单个参数的地方使用它(假设类型正确):
scala> List((1,2), (4,5), (3,8)).map(addTupled)
res4: List[Int] = List(3, 9, 11)
In contrast, curryingis different yet again; it turns functions of the form (A,B) => Cinto A => B => C. That is, given a function of multiple arguments, it will produce a chain of functions that each take one argument and return a chain one shorter (you can think of it as partially applying one argument at a time).
相比之下,柯里化又有所不同。它将形式的函数(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
回答by Daniel C. Sobral
Rex Kerr's explanation is very good -- and no surprise there either. The question is clearly mixing up partial functionswith partially applied functions. For whatever it is worth, I made the same confusion myself when I learned Scala.
Rex Kerr 的解释非常好——这也不足为奇。问题显然是将部分函数与部分应用函数混为一谈。不管它值多少钱,当我学习 Scala 时,我自己也犯过同样的困惑。
However, since the question does draw attention to partial functions, I'd like to speak of them a bit.
然而,由于这个问题确实引起了对部分函数的关注,我想稍微谈谈它们。
Many people say that partial functions are functions that are not defined for all input, and that's true of mathematics, but not of Scala. In Scala, a function may not be defined for all input either. In fact, since partial function inherit from function, then function includes all partial functions as well, making that inevitable.
很多人说偏函数是没有为所有输入定义的函数,数学是这样,但 Scala 不是。在 Scala 中,也可能不会为所有输入定义一个函数。事实上,既然偏函数继承自函数,那么函数也就包含了所有的偏函数,这就不可避免了。
Others mention the method isDefinedAt, which is, indeed, a difference, but one mostly about implementation. That's so true that Scala 2.10 will probably be released with a "fast partial function", which does not rely on isDefinedAt.
其他人提到了 method isDefinedAt,这确实是一个差异,但主要是关于实现的。确实如此,Scala 2.10 可能会发布一个“快速部分函数”,它不依赖于isDefinedAt.
And a few people even imply that the applymethod for partial functions do something different than the applymethod for functions, like only executing for the input that is defined -- which could not be farther from the truth. The applymethod is exactly the same.
有些人甚至暗示apply部分函数的apply方法与函数的方法有所不同,比如只对定义的输入执行——这与事实相去甚远。该apply方法是完全一样的。
What partial functions really come down to is another method: orElse. That sums up all use cases for partial functions much better than isDefinedAt, because partial functions are really about doing one of the following things:
部分函数真正归结为另一种方法:orElse. 这总结了部分函数的所有用例比 好得多isDefinedAt,因为部分函数实际上是做以下事情之一:
- Chaining partial functions (which is what
orElsedoes), so that an input will be tried on each partial function until one of them matches. - Doing something different if a partial function doesn't match, instead of throwing an exception, which is what would happen if you chain that different thing using
orElse.
- 链接部分函数(这就是这样
orElse做的),以便将在每个部分函数上尝试输入,直到其中一个匹配为止。 - 如果部分函数不匹配,则做一些不同的事情,而不是抛出异常,如果您使用
orElse.
I'm not saying everything can be easilyimplemented in terms of orElse, mind you. I'm just saying that partial functions are about doing something else when an input isn't defined for it.
请注意,我并不是说一切都可以轻松实现orElse。我只是说部分函数是在没有为它定义输入时做其他事情。

