Использование многих экземпляров "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.