簡體   English   中英

Ruby塊采用數組或多個參數

[英]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,"

我本以為最后兩個示例會出現某種錯誤。

  1. 這是紅寶石中更一般概念的示例嗎?
  2. 我不認為我在問題開頭的措辭是正確的,我怎么稱呼這里發生的事情?

像這樣的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並想分解成對的keyvalue部分時,此模式非常有用: 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'

這實際上為ab分配了獨立的值。 與:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM