2008-02-18

Scheme的Lambda与Ruby的Block

关键字: scheme ruby
SICP中有一个简单而经典的例子:构造一个通用的求和函数

这里的m, n和函数f都不是预先定好的。在Scheme语言中,利用

可以定义出一个这样的sum函数:
(define (sum m n func)
  (if (> m n)
      0
      (+ (func m)(sum (+ m 1) n func))))

要计算1+2+...+100,先定义一个函数(define (f x)(x)),然后代入到sum中:(sum 1 100 f)。当然如果每换一个通项公式都要定义一个函数的话,程序中就会充斥着大量一次性的小函数。采用Scheme的“匿名函数”(在Scheme中称为Lambda函数)可以解决这个问题。对于1+2+...+100,只要写出(sum 1 100 (lambda (x) x))即可。
同样的,如果计算

只需(sum 1 100 (lambda (x)(/ 1 x)))。
说到Ruby,起初感觉Block和Lambda函数有些相似的地方,但又不能完全对应。一个简单的例子:
def threeTimes
  yield
  yield
  yield
end
threeTimes { puts "Hello" }

将显示三个“Hello”。代码中定义了一个名字为threeTimes的函数。在执行threeTimes这个Block时,会找到与它同名的函数threeTimes,并将函数中的yield替换成Block中的代码。这很像在Scheme语言中的替换机制。这些都不算什么,真正有趣的是带参数的yield。
如果用Ruby写那个通用的求和函数,我想可以这样写:
def sum(m,n)
	s=0
	for i in m..n
		s+=yield(i)
	end
	return s
end

在这段代码中,yield扮演了那个通项函数。接下来的关键是用什么样的Block去“替换”yield。
puts sum(1,100){|x|x} # 1+2+...+100
puts sum(1,10){|n|n*n} # 1^2+2^2+...+10^2
puts sum(1,100){|n|1/n} # 1/1+1/2+...+1/100

这些Block实际上扮演了“匿名函数”的角色,和Scheme的Lambda函数是极其相似的。对比一下Scheme的写法:
(sum 1 100 (lambda (x)(x)))
(sum 1 10 (lambda (n)(* n n)))
(sum 1 100 (lambda (n)(/ 1 n)))

个人感觉,Ruby的Block没有Scheme的Lambda函数灵活。在Scheme中,可以很容易将多个Lambda匿名函数传递给另一个函数,在Ruby中又如何做到呢?
评论
hax 2008-02-18
ruby应该是可以传入多个的,只不过通常最常见的是传一个lambda。
发表评论

您还没有登录,请登录后发表评论

ggggqqqqihc
搜索本博客
存档
最新评论