开发者

How do I limit the number of times a block is called?

In How do I limit the number of replacements when using gsub?, someone suggested the following way to do a limited number of substitutions:

s开发者_Python百科tr = 'aaaaaaaaaa'
count = 5
p str.gsub(/a/){if count.zero? then $& else count -= 1; 'x' end}
# => "xxxxxaaaaa"

It works, but the code mixes up how many times to substitute (5) with what the substitution should be ("x" if there should be a substitution, $& otherwise). Is it possible to seperate the two out?

(If it's too hard to seperate the two things out in this scenario, but it can be done in some other scenarios, post that as an answer)


How about just extracting the replacement as an argument and encapsulating the counter by having the block close over it inside a method?

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))

You can make it even more general by using a block for the replacement:

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

Now you can do stuff like

p str.gsub(/a/, &limit(5) { "x" })
p str.gsub(/a/, &limit(5, &:upcase))


gsub will call the block exactly as often as the regex matches the string. The only way to prevent that is to call break in the block, however that will also keep gsub from producing a meaningful return value.

So no, unless you call break in the block (which prevents any further code in the yielding method from running and thus prevents the method from returning anything), the number of times a method calls a block is solely determined by the method itself. So if you want gsub to yield only 5 times, the only way to do that is to pass in a regex which only matches the given strings five times.


Why are you using gsub()? By its design, gsub is designed to replace all occurrences of something, so, right off the bat you're fighting it.

Use sub instead:

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"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜