Использование многих экземпляров "gsub" в Ruby
Я делаю базовый переводчик в Ruby (1.9.3). Я вытаскиваю из локального тестового файла ('a.txt') и использование gsub для замены определенных соответствий, чтобы имитировать перевод с современного английского на средний / ранний современный английский. Я столкнулся с проблемой читаемости:
Как я могу сделать большое количество использования gsub легче читать? Я попытался использовать несколько строк, начиная с
def translate
@text.gsub(/my/, 'mine')
@text.gsub(/siss/, ' be ')
end
но это только печатает окончательный gsub. Я могу только предположить, что второй запрос перезаписывает первое. Я хотел бы избежать создания гигантской линии запросов gsub, и я не могу найти подходящий ответ.
вот пример моей текущей программы:
lines = File.readlines('a.txt')
@text = lines.join
def translate
@text.gsub(/my/, 'mine').gsub(/siss/, ' be ').gsub(/ys/, 'ye ').gsub(/ts/, 'te ').gsub(/t,/, 'te,').gsub(/t./, 'te.')
end
puts translate
Я заранее извиняюсь, если этот запрос кажется полностью основным. Ура!
5 ответов
второй вызов не переопределяет первый. Первый вызов возвращает копию @text
С замены, но вы ничего не делаете с возвращаемым значением. Если вы хотите изменить @text
, вы должны использовать . Если вы не хотите изменять @text
, тогда вам нужно цепи gsub
вместо звонков. Например, если у вас есть хэш сопоставления из ответ сливу, это вернет переведенный текст без фактического изменения @text
переменная экземпляра:
def translate
RegexMap.inject(@text) do |string, mapping|
string.gsub(*mapping)
end
end
блок перешел в inject
вызывается один раз за сопоставление (пара ключ / значение в RegexMap
). В первый раз, string
- значение, переданное в inject
а именно -@text
. После этого каждый последующий вызов получает возвращаемое значение предыдущего вызова, переданного как его string
значение. Так что это как если бы вы сделали это, но с набором отображений более легко настраивается:
@text.gsub(/my/,'mine').gsub(/\sis\s/, ' be ').gsub(/y\s/,'ye ').gsub....
на String#gsub
метод возвращает новую копию строки с заменой сделано, оставляя исходную строку без изменений. Обе замены в вашем первом примере выполнены, но результат первого отбрасывается, потому что он ничему не назначен. Результат второго возвращается как результат метода.
если вы вместо этого используете #gsub!
метод, который изменит исходную строку с результатами подстановки, что позволит упростить способ сделайте несколько замен.
def translate
@text.gsub!(/my/, 'mine')
@text.gsub!(/\sis\s/, ' be ')
@text
end
если вы не хотите изменять атрибут объекта, вы можете запустить метод с помощью text = @text.dup
, а затем использовать text
переменная вместо @text
атрибут для остальной части метода.
более читабельным?
вы бы тогда подумали о том, чтобы построить карту и использовать цикл на ней?
RegexMap = {
/my/ => 'mine',
/\sis\s/ => ' be ',
/y\s/ => 'ye ',
/t\s/ => 'te ',
/t\,/ => 'te,',
/t\./ => 'te.',
}
text = '123 my 456 is 123y 456t 123t, 456t.'
RegexMap.each_pair {|f,t| text = text.gsub(f, t)}
puts text
#=> 123 mine 456 be 123ye 456te 123te, 456te.
обновление: как Марк предложил, используя gsub!
позволит избежать избыточных операций копирования / назначения:
RegexMap.each_pair {|f,t| text.gsub! f, t}
вот рабочая демо
построение идеи сливу, вот Perl-подобная альтернатива:
@text = '123 my 456 is 123y 456t 123t, 456t.'
def s(regex, string)
@text.gsub!(regex, string)
end
s /my/, 'mine'
s /\sis\s/, ' be '
s /y\s/, 'ye '
s /t\s/, 'te '
s /t\,/, 'te,'
s /t\./, 'te.'
puts @text
Если вы всегда преобразуете определенный шаблон, а именно слова, то вы можете иметь простой шаблон соответствия, а затем заменить в зависимости от слова только одним запуском gsub
в соответствующий узор.
def translate
@text
.gsub(/[ \t]+/, " ")
.gsub(/\w+/,
"my" => "mine",
"is" => "be",
"y" => "ye",
"t" => "te"
)
end
Это было бы намного быстрее, чем повторять несколько раз для gsub
.