mbi*_*ras 7 ruby block enumerable
今天我很惊讶地发现ruby自动找到作为块参数给出的数组的值.
例如:
foo = "foo"
bar = "bar"
p foo.chars.zip(bar.chars).map { |pair| pair }.first #=> ["f", "b"]
p foo.chars.zip(bar.chars).map { |a, b| "#{a},#{b}" }.first #=> "f,b"
p foo.chars.zip(bar.chars).map { |a, b,c| "#{a},#{b},#{c}" }.first #=> "f,b,"
Run Code Online (Sandbox Code Playgroud)
我原以为最后两个例子会出现某种错误.
Ruby块像这样古怪.
规则是这样的,如果一个块接受多个参数并且它产生一个响应的对象,to_ary那么该对象被扩展.这使得产生一个数组而不是产生一个元组似乎对于带有两个或更多参数的块的行为方式相同.
yield [a,b]与yield a,b当块接受一个参数或仅当块采用可变数目的参数做虽然有所不同.
让我证明这两点
def yield_tuple
yield 1, 2, 3
end
yield_tuple { |*a| p a }
yield_tuple { |a| p [a] }
yield_tuple { |a, b| p [a, b] }
yield_tuple { |a, b, c| p [a, b, c] }
yield_tuple { |a, b, c, d| p [a, b, c, d] }
Run Code Online (Sandbox Code Playgroud)
版画
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
Run Code Online (Sandbox Code Playgroud)
而
def yield_array
yield [1,2,3]
end
yield_array { |*a| p a }
yield_array { |a| p [a] }
yield_array { |a, b| p [a, b] }
yield_array { |a, b, c| p [a, b, c] }
yield_array { |a, b, c, d| p [a, b, c, d] }
Run Code Online (Sandbox Code Playgroud)
版画
[[1, 2, 3]]
[[1, 2, 3]]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
Run Code Online (Sandbox Code Playgroud)
最后要表明Ruby中的所有内容都使用duck-typing
class A
def to_ary
[1,2,3]
end
end
def yield_arrayish
yield A.new
end
yield_arrayish { |*a| p a }
yield_arrayish { |a| p [a] }
yield_arrayish { |a, b| p [a, b] }
yield_arrayish { |a, b, c| p [a, b, c] }
yield_arrayish { |a, b, c, d| p [a, b, c, d] }
Run Code Online (Sandbox Code Playgroud)
版画
[#<A:0x007fc3c2969190>]
[#<A:0x007fc3c2969050>]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
Run Code Online (Sandbox Code Playgroud)
PS,相同的数组扩展行为适用于proc行为类似于块的lambda闭包,而闭包的行为类似于方法.
Ruby的块函数对它们有一个怪癖,即如果你在包含数组的东西上进行迭代,你可以将它们扩展为不同的变量:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
Run Code Online (Sandbox Code Playgroud)
使用时,这种模式是非常有用的Hash#each,你想打出来的key,并value在对部分:each { |k,v| ... }是很常见的Ruby代码.
如果你的块有多个参数并且迭代的元素是一个数组,那么它会切换参数的解释方式.你可以随时强制扩展:
[ %w[ a b ], %w[ c d ] ].each do |(a, b)|
puts 'a=%s b=%s' % [ a, b ]
end
Run Code Online (Sandbox Code Playgroud)
这对于事情更复杂的情况很有用:
[ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
puts 'a=%s b=%s @ %d' % [ a, b, i ]
end
Run Code Online (Sandbox Code Playgroud)
因为在这种情况下它迭代一个数组和另一个被添加的元素,所以每个项目实际上是%w[ a b ], 0内部表单的元组,如果你的块只接受一个参数,它将被转换为数组.
这与定义变量时可以使用的原理大致相同:
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
Run Code Online (Sandbox Code Playgroud)
这实际上是为a和分配独立的价值观b.对比:
a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil
Run Code Online (Sandbox Code Playgroud)