在汤姆斯图尔特的书“理解计算”中,有一章致力于通过Procs / Lambdas重建FizzBuzz。他首先讨论了如何通过触发器定义数字,并显示一个“数字”数字'可以用一个proc表示,它对它所代表的数字运行x
次(即1是proc [x],2是proc [proc [x]]等)。那讲得通。但是,下一步他定义了一个to_integer
方法:
def to_integer(proc)
proc[-> n { n + 1 }][0]
end
这打破了我的大脑。我无法理解这意味着什么。我承认在使用rails时,除了范围内的lambda之外,还没有使用过多的触发器。
我可以将其简化为
def to_integer(proc)
proc[Proc.new(n){ n + 1 }][0]
end
...但是就我所能得到的而言。任何人都可以用更容易理解的方式解释或简化这个吗?这里发生了什么?我对proc [x
] [0]样式感到困惑,n
来自哪里。我只是很难理解这一点。
在书中,传递给此方法的过程是这样的:
ZERO = -> p { -> x { x } }
ONE = -> p { -> x { p[x] } }
TWO = -> p { -> x { p[p[x]] } }
答案 0 :(得分:1)
to_integer中的代码使用Ruby' s []简写链接2 proc调用来调用proc。
在IRB中手工完成它是有帮助的。让我们从定义ZERO(lambda)开始:
ZERO = -> p { -> x { x } }
=> #<Proc:0x007ff86a147930@(irb):103 (lambda)>
你会在这里看到不同的数字,但重要的部分是ZERO是一个lambda,这是一个proc。
让我们在to_integer中给匿名lambda一个名字,让它清楚地说明那里发生了什么。使用&#34; stabby lambda&#34;语法:
INCR = -> n { n + 1 }
INCR[2]
=> 3
INCR[3]
=> 4
我们发送一个号码,它返回该号码加1。
然后我们将ZERO传递给to_integer并通过调用lambda ZERO并发送INCR来启动它:
ZERO[INCR]
=> #<Proc:0x007ff86a0decf0@(irb):103 (lambda)>
如您所见,这将返回另一个lambda(在ZERO中定义的lambda,其中p设置为INCR)。我们现在可以通过调用这个lambda并发送0来开始滚动:
ZERO[INCR][0]
=> 0
答案 1 :(得分:1)
proc literal的语法为&#34; - &gt;参数{body}&#34;。用于递增数字的proc文字的具体示例是&#34; - &gt; n {n + 1}&#34;。为了便于阅读,可以将proc文字分配给变量,例如&#34; INCREMENT = - &gt; n {n + 1}&#34;。
可以使用方括号调用proc文字&#34; - &gt;参数{body} [arguments]&#34;。使用前面的示例,这里有如何增加9以给你10&#34; - &gt; n {n + 1} [9]&#34;。传入的论证&#34; 9&#34;由参数&#34; n&#34;表示。在体内,1加入&#34; n&#34;给出结果。同样,为了便于阅读,&#34; INCREMENT [9]&#34;可以用来给出相同的结果。
现在让我们来看看&#34; ZERO = - &gt; p { - &gt; x {x}}&#34;。当&#34; ZERO [INCREMENT] [0]&#34;被执行,&#34; ZERO [INCREMENT]&#34;部分首先执行。结果是&#34; - &gt; x {x}&#34;并且,通过替换,原始陈述变为&#34; - &gt; x {x} [0]&#34;。执行该操作时,它返回0。
现在让我们来看看&#34; ONE = - &gt; p { - &gt; x {p [x]}}&#34;。当&#34; ONE [INCREMENT] [0]&#34;被执行,&#34; ONE [INCREMENT]&#34;部分首先执行。结果是&#34; - &gt; x {p [x]}&#34;其中p是&#34; INCREMENT&#34;。通过替换,原始陈述变为&#34; - &gt; x {INCREMENT [x]} [0]&#34;。当执行该操作时,基本上INCREMENT被调用为0,即&#34; INCREMENT [0]&#34;,结果为1.
最后,让我们来看看: def to_integer(proc) PROC [ - &GT; n {n + 1}] [0] 端
&#34; to_integer(ZERO)&#34;执行&#34; ZERO [ - &gt; n {n + 1}] [0]&#34;。请注意&#34; - &gt; n {n + 1}&#34;与INCREMENT相同,因此执行与ZERO [INCREMENT] [0]相同。如上所述,产生0。
&#34; to_integer(ONE)&#34;执行&#34; ONE [ - &gt; n {n + 1}] [0]&#34;。请注意&#34; - &gt; n {n + 1}&#34;与INCREMENT相同,因此执行与ONE [INCREMENT] [0]相同。如上所述,这会产生1。
同样的过程可以应用于TWO等。
答案 2 :(得分:0)
这里的基本想法实际上来自于构建自然数字,如Peano Axioms(http://en.wikipedia.org/wiki/Peano_axioms)
因此零为0,一个为S(0),两个为S(S(0)),三个为S(S(S(0)))等(其中S为某种函数)。
其次,可以重写此方法,用[]
代替call
:
def to_integer(proc)
proc.call(Proc.new { |n| n + 1 }).call(0)
end
我们现在可以清楚地看到它反向计算 - 当我们试图计算一个(实际上是S(0),或者我们的符号-> p { -> x { p[x] } }
相当于Proc.new { |p| Proc.new { |x| p.call(x) } }
时) ,它存储了S(x)需要的信息,我们不知道的地方(我们的proc的参数)
现在是棘手的部分。这里Ruby调用我们的定义(它是一个Proc,所以它可以调用!)作为参数传递另一个Proc - 这个等同于S函数(n + 1)。所以在最后.call(0)
之前我们有Procs序列 - 调用更深的一个参数增加1(因为我们的整数) - S(x)。通过最后的调用(0),我们设置了自然数S(0)的基础。
考虑一些例子可能会有所帮助:
当我们将to_integer
替换为:
def to_integer(proc)
proc[-> n { n + 1 }][0]
end
(可能有趣的是,这些数字与标准数字一样好。)
如果我们将通话中的基数从0更改为1,会发生什么?