2017-03-17 81 views
4

下面發生的事情對我來說似乎有點奇怪。關鍵字參數在Ruby中解壓縮(splat)

def f(a, b) 
    puts "#{a} :: #{b}" 
end 

f(*[1, 2], **{}) # prints "1 :: 2" 

hash = {} 
f(*[1, 2], **hash) 
ArgumentError: wrong number of arguments (3 for 2) 

f(*[1, 2], **Hash.new) 
ArgumentError: wrong number of arguments (3 for 2) 

這是編譯器優化功能嗎?

+0

好問題!我學到了很多關於關鍵字的論點。 –

回答

3

這是一個多次報告的Ruby錯誤(例如我的here),但尚未修復。

我猜想,由於引入了關鍵字參數功能,雙重splat語法變得模糊,這就是這個錯誤的間接原因。我聽說Matz正在考慮在未來的Ruby版本中引入新的語法來區分散列和關鍵字參數。

+0

包裝和包裝正是我想要遵循的模式,Sawa。我很驚訝這種非常有用的轉發參數模式尚未完善。我想,風格問題。 –

1

我有一種感覺,你正在絆倒與該空的散列結構相關的摔跤運算符的特性。看起來,一個空的內聯哈希引起它消失,但其他任何東西都會擴展爲某種類型的參數。

這實際上可能是Ruby中的一個錯誤,雖然這是一個古怪的邊緣案例,我並不感到驚訝。

您的f函數不接受任何類型的關鍵字參數,所以如果有足夠有力的嘗試來提供它們,就會失敗。最後兩個例子似乎試圖強制使用空散列作爲文字參數。

+0

這不是空的問題。所討論的對象是空的哈希文字還是指向空哈希的變量。沒有理由這樣做。 – sawa

+1

@sawa我同意不應該有任何區別,但是有一個,這是非常令人困惑的。在Ruby中看到這種事情是非常罕見的,它趨向於一致性。我認爲這是一個錯誤,如果繼續下去,最終可能會成爲Ruby Spec的測試。 – tadman

1

[編輯:我看到@ sawa完成我的回答。我是對的:這是一個bug!]

當字面空的散列是雙重splatted並且空的散列是變量的值是雙重splatted時,得到不同的結果,在我看來似乎是prima facia證明這是由於Ruby中的錯誤。爲了理解錯誤存在的原因,首先考慮將雙重散列哈希傳遞給方法的原因。

假設我們定義一個方法與一些關鍵字參數:

def my_method(x, a: 'cat', b: 'dog') 
    [x, a, b] 
end 

my_method(1) 
    #=> [1, "cat", "dog"] 

的默認值適用於這兩個關鍵字參數。現在嘗試:

my_method(1, a: 2) 
    #=> [1, 2, "dog"] 

現在讓我們使用雙摔散列哈希。

h = { a: 2, b: 3 } 

my_method(1, **h) 
#=> [1, 2, 3] 

這與所需的關鍵字參數(Ruby 2.1+)相同。

def my_method(x, a:, b:) 
    [x, a, b] 
end 

my_method(1, **h) 
    #=> [1, 2, 3] 

然而,要使用的雙splatted散列作爲參數,散列不能包含未列出作爲在方法定義參數密鑰。

def my_method(x, a:) 
    [x, a] 
end 

h = { a: 2, b: 3 } 

my_method(1, **h) 
    #=> ArgumentError: unknown keyword: b 

因此,問題出現了:可以雙splatted空哈希被作爲參數傳遞,考慮到所有的哈希的鍵(無)被包括在方法定義的參數(它本來這種情況下,沒有任何影響)?我們來試試吧。

def my_method(x) 
    [x] 
end 

my_method(1, **{}) 
    #=> [1] 

是的!

h = {} 
my_method(1, **h) 
    #=> ArgumentError: wrong number of arguments (given 2, expected 1) 

不!

這沒有任何意義。所以假設這是一個錯誤,它怎麼會出現?正如OP建議的那樣,我懷疑它可能與Ruby的優化有關。它的空散列是一個文字,在Ruby的代碼中可以比之前處理變量的值更早處理它。我猜測,無論誰寫了早期的代碼,對上面提出的問題都回答「是」,而編寫後者代碼的人回答「否」,或者沒有考慮當時的空散列情況。

如果這個錯誤理論沒有被擊落,那麼OP或其他人應該報告它。