斯卡拉咖喱与部分应用的功能 [英] Scala currying vs partially applied functions

查看:114
本文介绍了斯卡拉咖喱与部分应用的功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我意识到这里有几个有关 什么 的问题,并且部分应用了功能,但我在问他们有什么不同。作为一个简单的例子,这里是一个用于查找偶数的curry函数:

  def filter(xs:List [Int],p如果(xs.isEmpty)xs 
else if(p(xs.head))xs.head :: filter(xs.tail,p):Int => Boolean):List [Int] =
if )
else filter(xs.tail,p)
$ b $ def modN(n:Int)(x:Int)=((x%n)== 0)

所以你可以写下面的内容来使用它:

  val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums,modN(2))

返回: List(2,4,6,8)。但我发现我可以这样做:

  def modN(n:Int,x:Int) =((x%n)== 0)

val p = modN(2,_:Int)
println(filter(nums,p))

也返回: List(2,4,6,8)



所以我的问题是,这两者之间的主要区别是什么,以及什么时候使用两者之一?这只是为了一个简单的例子来说明为什么一个会被用于另一个?

解决方案

语义差异已被解释得相当不错 Plasty Grove链接的答案



功能,但似乎没有太大的区别。让我们看看一些例子来验证。首先,一个正常的函数:

  scala> def modN(n:Int,x:Int)=((x%n)== 0)
scala> modN(5,_:Int)
res0:Int =>布尔=< function1>

所以我们得到一个部分应用的< function1> 需要一个 Int ,因为我们已经给它第一个整数。到现在为止还挺好。现在来currying:

  scala> def modNCurried(n:Int)(x:Int)=((x%n)== 0)



<使用这种表示法,你会天真地期望以下工作:

  scala> modNCurried(5)
< console>:9:错误:缺少方法modN的参数;
如果您想将其视为部分应用函数,则使用`_'跟随此方法
modNCurried(5)

因此,多参数列表表示法似乎并不是马上创建一个curried函数(假设为避免不必要的开销),而是等待您明确声明您希望它有咖喱味(该符号也有一些其他优点):

 阶> modNCurried(5)_ 
res24:Int =>布尔=< function1>

这与我们之前所得到的完全一样,所以在这里没有区别,除了符号。另一个例子:

  scala> modN _ 
res35:(Int,Int)=>布尔=< function2>

scala> modNCurried _
res36:Int => (Int => Boolean)=< function1>

这说明部分应用正常函数如何产生一个函数,该函数需要所有参数,而部分应用具有多个参数列表的函数会创建一个函数链,每个参数列表一个,它们都会返回一个新函数:

 阶> def foo(a:Int,b:Int)(x:Int)(y:Int)= a * b + x  -  y 
scala> foo _
res42:(Int,Int)=> Int => (Int => Int)=< function2>

scala> res42(5)
< console>:10:error:方法适用的参数不足:(v1:Int,v2:Int)Int => (Int => Int)在特征函数2中。
未指定的值参数v2。

正如您所看到的,因为第一个参数列表 foo 有两个参数,咖喱链中的第一个函数有两个参数。




总之,就功能而言,部分应用函数并不是完全不同的形式。这很容易验证,因为您可以将任何函数转换为curried类型:

  scala> (modN _)。curried 
res45:Int => (Int => Boolean)=< function1

scala> modNCurried _
res46:Int => (Int => Boolean)=< function1>






Post Scriptum



注意:您的示例 println(filter(nums,modN(2))不带下划线后的原因 modN(2)似乎是Scala编译器只是简单地假定下划线是程序员的便利。






加法:正如@asflierl已经正确指出的那样,Scala在部分应用普通函数时似乎无法推断出类型: p>

  scala> modN(5,_)
< console>:9:错误:缺少扩展函数的参数类型(x $ 1)=> modN(5,x $ 1))

使用多个参数列表符号写入的函数:

  scala> modNCurried(5)_ 
res3:Int => Boolean =< function1>

这个答案显示了这可能非常有用。


I realize that there are several questions on here about what currying and partially applied functions are, but I'm asking about how they are different. As a simple example, here is a curried function for finding even numbers:

def filter(xs: List[Int], p: Int => Boolean): List[Int] =
   if (xs.isEmpty) xs
   else if (p(xs.head)) xs.head :: filter(xs.tail, p)
   else filter(xs.tail, p)

def modN(n: Int)(x: Int) = ((x % n) == 0)

So you could write the following to use this:

val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))

which returns: List(2,4,6,8). But I've found that I can do the same thing this way:

def modN(n: Int, x: Int) = ((x % n) == 0)

val p = modN(2, _: Int)
println(filter(nums, p))

which also returns: List(2,4,6,8).

So my question is, what's the main difference between the two, and when would you use one over the other? Is this just too simplistic of an example to show why one would be used over the other?

解决方案

The semantic difference has been explained fairly well in the answer linked to by Plasty Grove.

In terms of functionality, there doesn't seem much of a difference, though. Let's look at some examples to verify that. First, a normal function:

scala> def modN(n: Int, x: Int) = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>

So we get a partially applied <function1> that takes an Int, because we've already given it the first integer. So far so good. Now to currying:

scala> def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

With this notation, you'd naively expect the following to work:

scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
          modNCurried(5)

So the multiple parameter list notation doesn't really seem to create a curried function right away (assumingly to avoid unnecessary overhead) but waits for you to explicitly state that you want it curried (the notation has some other advantages as well):

scala> modNCurried(5) _
res24: Int => Boolean = <function1>

Which is exactly the same thing we got before, so no difference here, except for notation. Another example:

scala> modN _
res35: (Int, Int) => Boolean = <function2>

scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>

This demonstrates how partially applying a "normal" function results in a function that takes all parameters, whereas partially applying a function with multiple parameter lists creates a chain of functions, one per parameter list which, all return a new function:

scala> def foo(a:Int, b:Int)(x:Int)(y:Int) = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>

scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.

As you can see, because the first parameter list of foo has two parameters, the first function in the curried chain has two parameters.


In summary, partially applied functions aren't really different form curried functions in terms of functionality. This is easily verified given that you can convert any function to a curried one:

scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1

scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>


Post Scriptum

Note: The reason that your example println(filter(nums, modN(2)) works without the underscore after modN(2) seems to be that the Scala compiler simply assumes that underscore as a convenience for the programmer.


Addition: As @asflierl has correctly pointed out, Scala doesn't seem to be able to infer the type when partially applying "normal" functions:

scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))

Whereas that information is available for functions written using multiple parameter list notation:

scala> modNCurried(5) _
res3: Int => Boolean = <function1>

This answers shows how this can be very useful.

这篇关于斯卡拉咖喱与部分应用的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆