Scala使用'byname'参数和非''name'参数进行curry

时间:2011-03-14 12:13:32

标签: scala

我有一个看似错误的假设。我过去的'主要'语言是C ++,在C ++中,我可以在伪中执行函数参数绑定:

// declaration
void f(int param1, int param2);

// usage
func<void, int> boundfunc = bind(f, 1, _1)
func(2) // equivalent to f(1, 2)

有趣的是,如果我用函数调用替换1定义中的boundfunc,那么在绑定站点调用该函数调用,而不是调用func

所以,当我试图巩固我头脑中的currying概念时,我将它与C ++中的绑定进行比较,并且我假设(错误地)使用非''name'参数进行讨论将等同于同样的想法 - 在'绑定站点'调用。当然,这不是正在发生的事情,我很好奇为什么下面的fg表现相同。我显然错过了一些东西......

import scala.util.Random

class X { println("Constructing"); val r = new Random }

def f(limit: Int)(x: X) = x.r.nextInt(limit)
def g(limit: Int)(x: => X) = x.r.nextInt(limit)

def r = f(100)(new X) // Doesn't print "Constructing"
def s = g(100)(new X) // Doesn't print "Constructing"

r // prints "Constructing"
r // prints "Constructing"

s // also prints "Constructing"
s // also prints "Constructing"

我希望s的定义能够做它正在做的事情(即不要调用'new X')但我确实希望r的定义可以调用它,因为{{1}有一个f参数的基本定义(即非''name')。

现在,正如我所说,显然我缺少一些基本的FP概念,我希望尽快纠正,但我也想知道是否有办法获得等效的参数绑定以便{{ 1}}不会重复调用。

(从barjak回答后编辑)

事实上,问题源于我在创建xnew X时滥用def - 我应该使用r来调用{s 1}}发生在'绑定站点'。

最后一件令人惊讶的事情是val中的'byname'参数只在我们创建new X时解析一次,而不是在我们明确评估g时。 (即“构建”仅在我们定义ss时打印,而不是在我们定义r和我们评估s时)。

现在,我的假设是Scala正在执行正确定义的事情,并将r绑定到第二个参数的结果,因此无关紧要,在s或{{1 } x是否是'byname'。

我想,如果我想反复评估s,我需要的不仅仅是'byname'参数,而是需要一个显式的函数参数,在评估r时会反复评估。

2 个答案:

答案 0 :(得分:6)

使用关键字r声明

def,这实际上意味着“重新评估每个调用的表达式”。我想您想使用val关键字,该关键字仅针对X的每个实例计算一次表达式。

val r = f(100)(new X)
val s = g(100)(new X)

否则,我认为您的解释是正确的。

答案 1 :(得分:1)

据我所知,名称参数

中的fg之间没有太大区别
def f(limit: Int)(x: X) = x.r.nextInt(limit)
def g(limit: Int)(x: => X) = x.r.nextInt(limit) 

因为仅在身体定义中评估x一次。

但是如果你有一个循环,那么内部定义x可以被评估多个......然后在fnew X只会被评估一次(在方法之前)但是在gnew X的评估频率与循环一样频繁。

/* using x in body twice (for demonstration only) */
def f(limit: Int)(x: X) = { x.r.nextInt(limit); x.r.nextInt(limit) }
def g(limit: Int)(x: => X) = { x.r.nextInt(limit); x.r.nextInt(limit) }
val r = f(100)(new X) /* prints "Constructing" only one time */
val s = g(100)(new X) /* prints "Constructing" twice */
r /* Doesn't print "Constructing" */
r /* Doesn't print "Constructing" */
s /* Doesn't print "Constructing" */
s /* Doesn't print "Constructing" */

最后要将 currying 付诸实践,或许你想将new X绑定到x,你可以在函数定义中交换参数列表。然后你可以做

def f(x: X)(limit: Int) = x.r.nextInt(limit)
val r = f(new X)_
val s = f(new X)_
r(100)
r(200)
s(100)
s(200)

其中rs是使用cur f构建的两个不同(独立)随机流(不需要名称参数)。