Проблема Ruby Gsub при использовании обратных ссылок и хэшей - PullRequest
2 голосов
/ 17 января 2010

Следующий код определяет хеш с регулярными выражениями (ключами) и заменами (значениями). Затем он перебирает хеш и соответственно заменяет строку.

Простая подстановка строк работает хорошо, но когда мне нужно вычислить результат перед его заменой (случай лет меняется), это не так. И это ключ к тому, что хеш определен заранее.

Чего мне не хватает? Буду очень признателен за любую помощь.

a = "After 45 years we cannot use this thing."

hash = {
  /(\d+) years/ => "#{$1.to_f*2}" + ' days',
  /cannot/      => 'of course we CAN'  
}

hash.each {|k,v| 

  a.gsub!(k) { v }
}

puts a

Спасибо!

Ответы [ 2 ]

7 голосов
/ 17 января 2010

String#gsub! имеет две формы, одну из которых вы передаете в виде строки в качестве второго аргумента, в которой ссылки на переменные, такие как $1 и $2, заменяются соответствующим соответствием подвыражения, а другая - в которой вы передаете блок, который вызывается с аргументами, для которых передаются совпадения подвыражения. Вы используете блочную форму при вызове gsub!, но строка в вашем хэше пытается использовать форму, в которой передается строка.

Кроме того, переменная интерполяция в вашей строке происходит до совпадения; интерполяция переменных происходит, как только строка оценивается, то есть во время создания вашего хэша, в то время как для этого вам понадобится интерполяция переменных, которая произойдет после замены подвыражения (что никогда не происходит; интерполяция переменных произойдет сначала, и результирующая строка будет передана в gsub! для gsub!, чтобы заменить совпадение подвыражения для $1, но $1 уже был бы оценен и больше не был в строке, так как интерполяция уже произошла ).

Теперь, как это исправить? Что ж, вы, вероятно, захотите сохранить свои блоки непосредственно в хэше (чтобы строки не интерпретировались при создании хэша, а вместо этого, когда gsub! вызывает блок), с аргументом, соответствующим совпадению, и $1, $2 и т. Д. Связаны с соответствующими совпадениями подвыражения. Чтобы превратить блок в значение, которое может быть сохранено и впоследствии извлечено, вам необходимо добавить к нему lambda; затем вы можете снова передать его как блок, добавив префикс &:

hash = {
  /(\d+) years/ => lambda { "#{$1.to_f*2} days" },
  /cannot/      => lambda { 'of course we CAN' }
}

hash.each {|k,v|
  a.gsub!(k, &v)
}
2 голосов
/ 17 января 2010

Выражение "$1.to_f*2" + ' days' будет выполнено и сохранено в хэше при создании хэша, а не при обращении к нему.Поскольку в этот момент $ 1 еще не имеет значения, оно не работает.

Вы можете исправить это, храня лямбда-выражения в хэше следующим образом:

hash = {/(\d+) years/ => lambda {|_| "#{$1.to_f * 2} days"},
  /cannot/      => lambda {|_| 'of course we CAN'} }

hash.each {|k,v| 
  a.gsub!(k, &v)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...