使用数组或多个参数的Ruby块
今天,我惊奇地发现ruby能够自动找到作为块参数给出的数组的值 例如:使用数组或多个参数的Ruby块,ruby,block,enumerable,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
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,"
我原以为最后两个例子会给出某种错误
Ruby的块机制有一个怪癖,那就是如果你在迭代包含数组的东西,你可以将它们展开成不同的变量:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
当使用Hash#each
时,此模式非常有用,您需要将键
和值
分成两部分:each{k,v |…}
在Ruby代码中非常常见
如果块接受多个参数,并且被迭代的元素是一个数组,那么它将切换参数的解释方式。您始终可以强制展开:
[ %w[ a b ], %w[ c d ] ].each do |(a, b)|
puts 'a=%s b=%s' % [ a, b ]
end
这对于情况更复杂的情况非常有用:
[ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
puts 'a=%s b=%s @ %d' % [ a, b, i ]
end
因为在本例中,它迭代一个数组和附加的另一个元素,所以每个项实际上是一个元组,内部形式为%w[ab],0
,如果块只接受一个参数,它将转换为数组
这与定义变量时可以使用的原理大致相同:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
这实际上为a
和b
分配了独立的值。与之相比:
a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil
Ruby block就是这样古怪的 规则是这样的,如果一个块接受多个参数,并生成一个响应
到_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] }
印刷品
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
[[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
[#<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
鉴于
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] }
印刷品
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
[[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
[#<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
最后要说明的是Ruby中的所有东西都使用duck类型
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] }
印刷品
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
[[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
[#<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
[#]
[#]
[1,2]#数组扩展使它看起来像一个元组
[1,2,3]#数组扩展使其看起来像一个元组
[1,2,3,nil]#数组扩展使它看起来像一个元组
另外,相同的数组扩展行为适用于行为类似于块的proc
闭包,而lambda
闭包的行为类似于方法
我原以为最后两个例子会给出某种错误
如果您从一个方法传递proc
,它实际上就是这样工作的。屈服于这样一个过程要严格得多——它检查其算术性,不尝试将数组参数转换为参数列表:
def m(a, b)
"#{a}-#{b}"
end
['a', 'b', 'c'].zip([0, 1, 2]).map(&method(:m))
#=> wrong number of arguments (given 1, expected 2) (ArgumentError)
这是因为zip
创建了一个数组,而map
只生成每个元素,即
yield ['a', 0]
yield ['b', 1]
yield ['c', 2]
另一方面,每个带有索引的\u工作:
['a', 'b', 'c'].each_with_index.map(&method(:m))
#=> ["a-0", "b-1", "c-2"]
因为它产生两个独立的值,元素和它的索引,即
yield 'a', 0
yield 'b', 1
yield 'c', 2
这类似于(可能与?)数组中变量的并行赋值。因此,这里的正确术语是:“Ruby正在将每个数组扩展到传递给map实例方法的块中的变量a、b或c”@mbigras大多数语言都将其称为“destructuring”,ES6现在有它、LISP()、clojure()等等。在yield[a,b]之间存在差异
和产生a,b
,尽管它们在块只需要一个参数时的行为。元素不是[%w[ab],0]
内部,请参见[%w[ab],%w[cd]]。每个{a | pa}
只打印没有索引的元素。在内部每个带有_索引的_
生成块的元组,而不是数组。所以它使用了等价的yield each,index
而不是yield[each,index]
是的,我明白你的意思了。我喜欢你的答案,但是你应该把输出移到更接近示例的地方,这样就更容易理解了。我还试图用一种更抽象的方式来描述这种行为,而元组和数组之间的微妙之处很难表达。我又更新了措辞,说得好。您还可以使用lambda
来演示这一点<代码>[1,2,3]。每个&lambda{a,b}
都失败,但[1,2,3]。每个&proc{a,b}
都工作。