Основная проблема с вашим кодом является то, что вы повторить операции для строк и столбцов. Вы хотите написать код «СУХОЙ», который означает «не повторяйте себя».
Начиная с кодом в качестве модели, вы можете высушить его, написав метод, как это, чтобы извлечь информацию, которую вы хотите от входной строки, и ссылаться на него один раз для строки и один раз для столбцов:
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]
.
Enumerable#each_with_object (v1.9 +) создает массив, состоящий из двух пустых массивов, [[],[]]
. Первый subarray будет содержать значения строк, второй - значения столбца. Эти два подмассива представлены блочными переменными 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]]
и так далее.
являются числа цифр? –