我自己先说几个最常见的。
作为迭代器。貌似不用示例了。来这的人都知道。
传入逻辑,类似于`设计模式'中经典的委托模式. 示例:
# 将 y <=> x (任意两元素)的<=>方法比较结果作为`排序的逻辑', 传入sort方法.
[1, 3, 38, 24, 4, 5].sort {|x, y| y <=> x } # => [38, 24, 6, 4, 3, 1]
使用 lambda, 形成一个闭包,这个我经验比较少,目前还没有什么地方用到。 示例:
def return_a_closure
x = "zw963 say: "
lambda {puts x + yield}
end
a_closure = return_a_closure {"hello world!"} # => "zw963 say: hello world!" a_closure.call another_closure = return_a_closure {"hello ruby!"} # => "zw963 say: hello ruby!" a_closure.call
谁可以告诉我, 具体在那些的实现中, 用到了闭包?
大家接着补充.
在对象的 initalize 中yield self if block_given?
之后
my_object = MyObject.new do |o|
o.foo = "bar"
o.bar = "foo"
end
我想这个是很常用的
写 dsl 的时候可以用到,比如:
class A
def self.help(&block)
a = A.new
a.instance_exec &block
end
def method1
puts "method1"
end
def method2
puts "method2"
end
end
A.help do
method1
method2
end
比如你写一个极简单的 Rack App:
app = Rack::Builder.app do
use Rack::CommonLogger
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end
Rack::Response 内部也使用了 lambda。等等,例子很多。 :> lambda 一个很重要的作用是可以把调用推迟。以前用 Ruby 写过一个 ARM 指令解释器,当时的实作是用 lambda 来对应指令。解释时,遇到指令就执行相应的 lambda。
我来总结下: #5 楼的用途,和我总结的传入一个策略,基本上相似。
#3 楼 #6 楼, 跟 attr_accessor 的用法差不多。虽然不完全相同。 前者上下文空间通过类方法被定义,后者在 Module 类中被定义而已, @allenwei , 的这种定义方式的确在 Ruby 极多,rake, rails 都大量用到. 他们非常相似的地方是:它们都使用 block 创建了一个包含上下文空间的块. (在块中更改了 self 值,就像类定义一样,可以使用一些特殊的方法)
#8 楼, @skandhas, 你说的延后调用,是不是和 RSpec 中的下面代码类似。
...
lambda { do something }.run # 这个run是在那里定义的? 为什么不用call
...
我感觉就像 bash 里面的$(...) 一样。
@allenwei , 我问一下,你的那个 DSL 示例,写成下面这样,是否会存在问题?
class A
def self.help(&block)
self.instance_exec &block
end
def self.method1
puts "method1"
end
def self.method2
puts "method2"
end
end
A.help do
method1
method2
end
我总觉得先创建了 A 的实例对象,再调用实例方法,是否有些多余?
class 人
def self.来一个(&block)
self.instance_exec &block
end
def self.扫地
puts "扫完了"
end
def self.做饭
puts "做好了"
end
end
人.来一个 do
扫地
做饭
end
这样可以,但你说是不是没有前面那么写来得好?这是艺术问题,不是技术问题。
#9 楼 @zw963 关于楼主 block 的用途,我多少有一点体会 block 本质是匿名方法,和 hash loop class recursion 一样就是一个编程的工具,没有的话程序都可以写,就是特别的场合,有特别适用的地方。 那么,block 匿名方法什么时候适用呢?方便理解也举个例子吧, 有两组数据,可能要进行不同处理
def cross_loop(array_a, array_b, process)
array_a.each do |element_a|
array_b.each do |element_b|
process.call(element_a, element_b)
end
end
end
cross_loop([1,2,3], [3,4,5], lambda{|x,y| puts x + y})
cross_loop([1,2,3], [3,4,5], lambda{|x,y| puts x * y})
说不用 lambda 行不行,当然,最基本可以在 cross_loop 里面调用一个函数。只不过重用的时候不好,因为没有 process 的参数。 所以,最基本的需求场合是,定义方法的时候,有些操作当时不能确定,需要调用的时候,动态确定。 当然,也可以不用 lambda 又动态传入 process,写个 send 然后,再写些 process 函数也可以。
def cross_loop(array_a, array_b, process)
array_a.each do |element_a|
array_b.each do |element_b|
self.send(process, element_a, element_b)
end
end
end
def plus(a, b); puts a + b; end
def multiply(a, b); puts a * b; end
cross_loop([1,2,3], [3,4,5], 'plus')
cross_loop([1,2,3], [3,4,5], 'multiply')
只是跟 block 比,各有所长吧
@ken_lv ,嗨~ 我完全明白你的意思。虽然你的代码有多处错误。呵呵。
不过我觉得你说的那个场合,并不是特别需要 lambda 的场合,相反,
你说的这个需求,正是代码块参数
最擅长的地方.
(这里我把代码块当作一个普通方法的一个特殊参数来看待,这样理解很好)
显然,你的代码更好的实现是:
def cross_loop(array_a, array_b, &process)
array_a.each do |element_a|
array_b.each do |element_b|
yield array_a, array_b
end
end
end
cross_loop([1,2,3], [3,4,5]) {|x, y| puts x + y}
cross_loop([1,2,3], [3,4,5]) {|x,y| puts x * y}
很显然,lambda 真正特殊的地方是它的闭包特性,还有就是#8 楼说的,延后执 行,这两个特性,你的代码都没有涉及。
又想起一个 lambda 的实例,是 Programming ruby 1.9 里面的好像。
一个对象 o 在某个安全级别下运行,其内部机制实际上为: 通过一个 Proc 对象,将对象 o 裹 (wrap) 起来,并在代码块内设定需要的安全级别。 例如:
lambda {$SAFE = 3; ... }.call
我不知道该怎么描述这种用法,也许 8 楼所说的延后执行,只是神秘的 lambda 其 众多含义中的一种而已。
puts_hello = lambda{ puts "hello world"}.call
puts_hello # => 输出 "hello world"
其实以上这种简单的实现,使用 proc 又未尝不可呢? 只不过在某些复杂点的场合,使用 lambda 出错的几率小一点。
puts_hello = proc {puts "hello world" }.call
puts_hello # => 输出 "hello world"
我觉得说了这么一堆示例,其核心内容,还是以下两点:
在某些场合,以上两点可能很重要。只是我资历尚浅,闭包没见过实际应用。呵呵。