[英]How does Ruby Array #count handle multiple block arguments
當我執行以下命令時:
[[1,1], [2,2], [3,4]].count {|a,b| a != b} # => 1
塊參數a
, b
分別分配給每個內部數組的第一個和第二個值。 我不知道這是如何實現的。
文檔中給出的帶有塊的Array#count
和Enumerable#count
的唯一示例使用單個塊參數:
ary.count {|x| x % 2 == 0} # => 3
就像任務一樣,有一個(不是這樣)秘密的快捷方式。 如果右側是一個數組,而左側有多個變量,則該數組將被展開,因此以下兩行相同:
a, b, c = [1, 2, 3]
a, b, c = *[1, 2, 3]
當產生的值是數組且有多個參數時,塊雖然有不同的含義,但它們具有相同的含義。 因此,當您yield [1, 2, 3]
時,這兩個塊的作用相同:
do |a, b, c|
...
end
do |(a, b, c)|
...
end
因此,在您的情況下,該值被解構,就像您編寫此代碼一樣:
[[1,1], [2,2], [3,4]].count {|(a,b)| a != b} # => 1
如果您要隨數組傳遞另一個值,則必須顯式指定結構,因為數組的解構不會以我們想要的方式自動進行:
[[1,1], [2,2], [3,4]].each.with_index.count {|e,i| i + 1 == e[1] }
# automatic deconstruction of [[1,1],0]:
# e=[1,1]; i=0
[[1,1], [2,2], [3,4]].each.with_index.count {|(a,b),i| i + 1 == b }
# automatic deconstruction of [[1,1],0], explicit deconstruction of [1,1]:
# a=1; b=1; i=0
[[1,1], [2,2], [3,4]].each.with_index.count {|a,b,i| i + 1 == b }
# automatic deconstruction of [[1,1],0]
# a=[1,1]; b=0; i=nil
# NOT what we want
我看過Array.count和Enumerable.count的文檔,給出的帶有塊的唯一示例使用單個塊參數...
像幾乎所有主流編程語言一樣,Ruby也不允許用戶代碼更改語言的基本語義。 換句話說,您不會在Array#count
的文檔中找到任何有關塊形式參數綁定語義的信息,因為塊形式參數綁定語義是由Ruby Language Specification指定的,而Array#count
不可能對此進行更改。
我不明白這是如何實現的。
這與Array#count
無關。 這只是塊形式參數的標准塊形式參數綁定語義。
塊形式參數的形式參數綁定語義不同於方法形式參數的形式參數綁定語義。 特別是,它們在處理形式參數和實際參數之間的不匹配方面更加靈活。
yield
多個塊實際參數,則該塊形式參數將綁定到包含該塊實際參數的Array
。 yield
一個塊實際參數,並且一個實際參數是一個Array
,則該塊形式參數將綁定到Array
的各個元素。 (這就是您在示例中看到的。) yield
更塊的實際參數比塊有形式參數,多余的實際參數會被忽略。 nil
(就像已定義但統一的局部變量一樣)。 如果仔細觀察,您會發現塊形式參數的形式參數綁定語義更接近賦值語義,即您可以想象一個賦值,其賦值運算符的左側是塊形式參數,而塊實際右邊的論點。
如果您具有如下定義的塊:
{|a, b, c|}
並像這樣yield
:
yield 1, 2, 3, 4
您幾乎可以想象到塊形式參數綁定是這樣工作的:
a, b, c = 1, 2, 3, 4
並且,如果您的問題中有這種情況,那么您具有如下定義的塊:
{|a, b|}
並像這樣yield
:
yield [1, 2]
您幾乎可以想象到塊形式參數綁定是這樣工作的:
a, b = [1, 2]
如您所知,哪個當然會產生以下結果:
a #=> 1
b #=> 2
有趣的事實:高達1.8紅寶石,阻斷形式參數是用實際分配結合! 你可以,例如,定義一個常數,實例變量,類變量,全局變量,甚至一個屬性的作家(!!!)作為一個正式的參數,當你yield
編到該塊,紅寶石會從字面上進行分配:
class Foo
def bar=(value)
puts "`#{__method__}` called with `#{value.inspect}`"
@bar = value
end
attr_reader :bar
end
def set_foo
yield 42
end
foo = Foo.new
set_foo {|foo.bar|}
# `bar=` called with `42`
foo.bar
#=> 42
瘋了吧?
這些塊形式參數綁定語義的最廣泛應用是使用Hash#each
(或使用Hash
實例作為接收方的任何Enumerable
方法)時。 的Hash#each
方法yield
SA單個的兩元件Array
含有鍵和值作為實際參數傳遞到塊,但我們幾乎總是把它當作好像它是yield
荷蘭國際集團的鍵和值作為單獨的實際參數。 通常,我們更喜歡寫作
hsh.each do |k, v|
puts "The key is #{k} and the value is #{v}"
end
過度
hsh.each do |key_value_pair|
k, v = key_value_pair
puts "The key is #{k} and the value is #{v}"
end
這完全等同於您在問題中看到的內容。 我敢打賭,您從來沒有問過自己,為什么即使只yield
一個Array
為什么也可以將帶有兩個塊形式參數的塊傳遞給Hash#each
? 好吧,這種情況是完全一樣的 。 您正在將具有兩個塊形式參數的塊傳遞給一個方法,該方法每次迭代yield
一個Array
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.