[英]Ruby block taking array or multiple parameters
今天,我很驚訝地發現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,"
我本以為最后兩個示例會出現某種錯誤。
像這樣的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] }
版畫
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
而
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, 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
最后證明Ruby中的所有內容都使用鴨式輸入法
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] }
版畫
[#<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
PS,相同的數組擴展行為適用於proc
閉包,其行為類似於塊,而lambda
閉包的行為類似於方法。
Ruby的塊機制對其有一個怪癖,也就是說,如果要遍歷包含數組的內容,則可以將它們擴展為不同的變量:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
當使用Hash#each
並想分解成對的key
和value
部分時,此模式非常有用: each { |k,v| ... }
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[ ab ], 0
內部為%w[ ab ], 0
,如果您的塊僅接受一個參數,則它將轉換為數組。
這與定義變量時可以使用的原理大致相同:
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
這實際上為a
和b
分配了獨立的值。 與:
a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil
我本以為最后兩個示例會出現某種錯誤。
實際上,如果您從方法中傳遞proc
,它確實會以這種方式工作。 產生這樣的proc更為嚴格–它檢查其Arity,並且不嘗試將數組參數轉換為參數列表:
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]
另一方面, each_with_index
起作用:
['a', 'b', 'c'].each_with_index.map(&method(:m))
#=> ["a-0", "b-1", "c-2"]
因為它會產生兩個單獨的值,即元素及其索引,即
yield 'a', 0
yield 'b', 1
yield 'c', 2
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.