與您的代碼的主要問題是,你複製操作的行和列。你想寫「DRY」代碼,代表「不要重複你自己」。
與您的代碼作爲模型開始,可以通過寫這樣的方法來提取你輸入字符串想要的信息幹出來,併爲行調用一次,並曾經爲列:
def doit(s, c)
...
end
這裏s
是輸入字符串,c
是字符串「R」或「C」。在你想要的方法 中提取以c
的值開頭並且後面跟有數字的子字符串。您使用String#scan決定是好的,但你需要一個不同的正則表達式:
def doit(s, c)
s.scan(/#{c}\d+/)
end
我會解釋正則表達式,但是讓我們首先嚐試的方法。假設字符串爲:
s = "R1C4R2C5"
然後
rows = doit(s, "R") #=> ["R1", "R2"]
cols = doit(s, "C") #=> ["C4", "C5"]
這是不是你想要的東西相當,但容易固定。首先,正則表達式。正則表達式首先查找字符#{c}
。 #{c}
將變量c
的值轉換爲文字字符,在這種情況下,它將是「R」或「C」。 \d+
表示字符#{c}
必須後跟一個或多個數字0-9
,其數量與下一個非數字(此處爲「R」或「C」)或字符串末尾之前的數字相同。
現在讓我們來修復方法:
def doit(s, c)
a = s.scan(/#{c}\d+/)
b = a.map {|str| str[1..-1]}
b.map(&:to_i)
end
rows = doit(s, "R") #=> [1, 2]
cols = doit(s, "C") #=> [4, 5]
成功!如前所述,a => ["R1", "R2"]
如果c => "R"
和a =>["C4", "C5"]
如果c => "C"
。 a.map {|str| str[1..-1]}
將a
的每個元素映射爲由所有字符組成的字符串,但第一個(例如,,"R12"[1..-1] => "12"
),所以我們有b => ["1", "2"]
或b =>["4", "5"]
。然後,我們再次應用map
將這些字符串轉換爲它們的Fixnum等價物。表達b.map(&:to_i)
是
b.map {|str| str.to_i}
最後的計算量是由該方法返回的簡寫,因此,如果它是你想要的,因爲它是在這裏,沒有必要在年底return
聲明。
然而,這可以通過幾種方法簡化。首先,我們可以通過刪除最後一個和改變一個上面結合上面的兩個語句:
a.map {|str| str[1..-1].to_i}
也擺脫了局部變量b
的。第二個改進是「鏈」剩下的兩個陳述,這也趕走我們的其他臨時變量:
def doit(s, c)
s.scan(/#{c}\d+/).map { |str| str[1..-1].to_i }
end
這是典型的Ruby代碼。
請注意,通過這樣做,不需要字符串中的行和列引用交替,並且數字值可以具有任意數字的數字。
這裏的另一種方式做同樣的事情,有些人可能看到的是更紅寶石般:
s.scan(/[RC]\d+/).each_with_object([[],[]]) {|n,(r,c)|
(n[0]=='R' ? r : c) << n[1..-1].to_i}
這裏發生了什麼。假設:
s = "R1C4R2C5R32R4C7R18C6C12"
然後
a = s.scan(/[RC]\d+/)
#=> ["R1", "C4", "R2", "C5", "R32", "R4", "C7", "R18", "C6", "C12"]
scan
使用正則表達式/([RC]\d+)/
以提取與「R」或「C」開始後跟一個或多個數字到的下一個字母或端子串。
b = a.each_with_object([[],[]]) {|n,(r,c)|(n[0]=='R' ? r : c) << n[1..-1].to_i}
#=> [[1, 2, 32, 4, 18], [4, 5, 7, 6, 12]]
行值由[1, 2, 32, 4, 18]
給出;列值由[4, 5, 7, 6, 12]
。 (v1.9 +)創建一個由兩個空數組組成的數組,[[],[]]
。第一個子數組將包含行值,第二個列值。這兩個子陣列分別由塊變量r
和c
表示。
a
的第一個元素是「R1」。這在塊中由變量n
表示。由於
"R1"[0] #=> "R"
"R1"[1..-1] #=> "1"
我們執行
r << "1".to_i #=> [1]
所以現在
[r,c] #=> [[1],[]]
的a
下一個元素是 「C4」,因此,我們將執行:
c << "4".to_i #=> [4]
所以現在
[r,c] #=> [[1],[4]]
等等。
是數字的數字? –