簡體   English   中英

Ruby Array #count如何處理多個塊參數

[英]How does Ruby Array #count handle multiple block arguments

當我執行以下命令時:

[[1,1], [2,2], [3,4]].count {|a,b| a != b} # => 1

塊參數ab分別分配給每個內部數組的第一個和第二個值。 我不知道這是如何實現的。

文檔中給出的帶有塊的Array#countEnumerable#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.

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