与您的代码的主要问题是,你复制操作的行和列。你想写“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]]
等等。
是数字的数字? –