我有一个看似错误的假设。我过去的'主要'语言是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'参数进行讨论将等同于同样的想法 - 在'绑定站点'调用。当然,这不是正在发生的事情,我很好奇为什么下面的f
和g
表现相同。我显然错过了一些东西......
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回答后编辑)
事实上,问题源于我在创建x
和new X
时滥用def
- 我应该使用r
来调用{s
1}}发生在'绑定站点'。
最后一件令人惊讶的事情是val
中的'byname'参数只在我们创建new X
时解析一次,而不是在我们明确评估g
时。 (即“构建”仅在我们定义s
和s
时打印,而不是在我们定义r
和我们评估s
时)。
现在,我的假设是Scala正在执行正确定义的事情,并将r
绑定到第二个参数的结果,因此无关紧要,在s
或{{1 } x
是否是'byname'。
我想,如果我想反复评估s
,我需要的不仅仅是'byname'参数,而是需要一个显式的函数参数,在评估r
时会反复评估。
答案 0 :(得分:6)
r
声明 def
,这实际上意味着“重新评估每个调用的表达式”。我想您想使用val
关键字,该关键字仅针对X
的每个实例计算一次表达式。
val r = f(100)(new X)
val s = g(100)(new X)
否则,我认为您的解释是正确的。
答案 1 :(得分:1)
据我所知,名称参数
中的f
和g
之间没有太大区别
def f(limit: Int)(x: X) = x.r.nextInt(limit)
def g(limit: Int)(x: => X) = x.r.nextInt(limit)
因为仅在身体定义中评估x
一次。
但是如果你有一个循环,那么内部定义x
可以被评估多个......然后在f
中new X
只会被评估一次(在方法之前)但是在g
中new 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)
其中r
和s
是使用cur f
构建的两个不同(独立)随机流(不需要名称参数)。