Как ограничить количество вызовов блока? - PullRequest
8 голосов
/ 17 мая 2011

В Как ограничить количество замен при использовании gsub? , кто-то предложил следующий способ сделать ограниченное количество замен:

str = 'aaaaaaaaaa'
count = 5
p str.gsub(/a/){if count.zero? then $& else count -= 1; 'x' end}
# => "xxxxxaaaaa"

Это работает, нокод смешивает, сколько раз заменить (5) на то, какой должна быть замена («x», если должна быть замена, $& в противном случае).Можно ли разделить два из них?

(Если в этом сценарии разделить две вещи слишком сложно, но это можно сделать в некоторых других сценариях, опубликуйте это как ответ)

Ответы [ 3 ]

8 голосов
/ 17 мая 2011

Как насчет простого извлечения замены в качестве аргумента и инкапсуляции счетчика с помощью закрытия блока над ним внутри метода?

str = "aaaaaaaaaaaaaaa"

def replacements(replacement, limit)
    count = limit
    lambda { |original| if count.zero? then original else count -= 1; replacement end }
end

p str.gsub(/a/, &replacements("x", 5))

Вы можете сделать его еще более общим, используя блок для замены:

def limit(n, &block)
    count = n
    lambda do |original|
        if count.zero? then original else count -= 1; block.call(original) end
    end
end

Теперь вы можете делать такие вещи, как

p str.gsub(/a/, &limit(5) { "x" })
p str.gsub(/a/, &limit(5, &:upcase))
2 голосов
/ 17 мая 2011

gsub будет вызывать блок точно так же часто, как регулярное выражение соответствует строке.Единственный способ предотвратить это - вызвать break в блоке, однако это также не даст gsub получить значимое возвращаемое значение.

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

1 голос
/ 17 мая 2011

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

Используйте sub вместо:

str = 'aaaaaaaaaa'
count = 5
count.times { str.sub!(/a/, 'x') }
p str
# >> "xxxxxaaaaa"

str = 'mississippi'
2.times { str.sub!(/s/, '5') }
2.times { str.sub!(/s/, 'S') }
2.times { str.sub!(/i/, '1') }
p str
# >> "m1551SSippi"
...