2010-01-17 57 views
2

下面的代碼定義了包含正則表達式(鍵)和替換(值)的哈希。然後它遍歷散列並相應地替換字符串。使用反向引用和哈希時的Ruby gsub問題

簡單的字符串替換效果很好,但是當我需要計算resut在替換它之前(幾年到幾天的變化),它不會。事先定義哈希鍵是關鍵。

我錯過了什麼?任何幫助將非常感激。

a = "After 45 years we cannot use this thing." 

hash = { 
    /(\d+) years/ => "#{$1.to_f*2}" + ' days', 
    /cannot/  => 'of course we CAN' 
} 

hash.each {|k,v| 

    a.gsub!(k) { v } 
} 

puts a 

謝謝!

回答

7

String#gsub!有兩種形式,一是您在其中傳遞了一個字符串作爲第二個參數,其中像$1$2變量引用被替換爲相應的子表達式匹配,並且您在其中傳入一個塊,該塊通過傳入子表達式匹配的參數調用。調用gsub!時使用塊形式,但散列中的字符串正試圖使用傳入字符串的格式。

此外,字符串中的變量插值發生在之前的匹配;變量插值在字符串被計算後立即發生,這就是在構建哈希時,爲此,您需要在發生子表達替換後發生變化插值(這絕不是這種情況;變量插值將會發生首先,結果字符串將被傳遞到gsub!,gsub!以替代$1的子表達式匹配,但$1已經被計算出來,因爲內插已經發生,所以不再在字符串中。

現在,如何解決這個問題?那麼,你可能希望直接將塊存儲在散列中(以便在構造散列時不會解釋這些字符串,而是當gsub!調用該塊時),並且具有對應於該匹配的參數,並且$1,$2,等綁定到適當的子表達式匹配。爲了將塊轉換爲可以存儲並稍後檢索的值,您需要將lambda添加到該值;那麼您可以在再次與&前綴它把它作爲一個塊:

hash = { 
    /(\d+) years/ => lambda { "#{$1.to_f*2} days" }, 
    /cannot/  => lambda { 'of course we CAN' } 
} 

hash.each {|k,v| 
    a.gsub!(k, &v) 
} 
+0

請注意,塊中年的值實際上是「45年」,而不是「45」。儘管如此,自「45年」以來,無所謂.to_f == 45.0'。 – sepp2k 2010-01-17 21:20:13

+0

糟糕。你是對的;爲了避免混淆,我現在修復了使用'$ 1'的問題。 – 2010-01-17 23:39:52

2

表達式"$1.to_f*2" + ' days'將在您創建哈希時執行並存儲在哈希中,而不是在您訪問哈希時。因爲那時$ 1還沒有價值,所以不起作用。

您可以通過在哈希存儲lambda表達式是這樣解決這個問題:

hash = {/(\d+) years/ => lambda {|_| "#{$1.to_f * 2} days"}, 
    /cannot/  => lambda {|_| 'of course we CAN'} } 

hash.each {|k,v| 
    a.gsub!(k, &v) 
} 
+0

不,這是行不通的。該塊內未定義「$ 1」;當調用'gsub!'的塊形式時,子表達式匹配被傳遞到塊中,在你的例子中,塊簡單地放棄它的參數。 – 2010-01-17 20:59:06

+0

它現在可以工作(將lambda作爲一個塊傳遞給它們,而不是調用.call - 這樣它就可以訪問'$ 1'(這可以從傳遞給gsub的塊中獲得))。而且,是的,我無意中忽略了對該塊的爭論。該塊的參數是整個比賽,而不僅僅是捕捉中的部分。所以我們不需要它,我們想要1美元。 – sepp2k 2010-01-17 21:18:10

+0

是的,你是對的。我不記得'gsub'是如何工作的。 – 2010-01-17 23:40:44