从红宝石

问题描述:

替代的返回数组我想借此串foofoofoo,地图foobar,并将所有个体替换为一个数组 - ['barfoofoo', 'foobarfoo', 'foofoobar']从红宝石

这是最好的我:

require 'pp' 
def replace(string, pattern, replacement) 
    results = [] 
    string.length.times do |idx| 
    match_index = (Regexp.new(pattern) =~ string[idx..-1]) 
    next unless match_index 
    match_index = idx + match_index 
    prefix = '' 
    if match_index > 0 
     prefix = string[0..match_index - 1] 
    end 

    suffix = '' 
    if match_index < string.length - pattern.length - 1 
     suffix = string[match_index + pattern.length..-1] 
    end 

    results << prefix + replacement + suffix 
    end 
    results.uniq 
end 

pp replace("foofoofoo", 'foo', 'bar') 

这起作用(至少对于这个测试用例来说),但是看起来太冗长而且很拗口。我可以做的更好,也许通过使用string#gsub与块或其他?

+0

我不不认为Ruby提供这样的功能离子性。 –

+0

@JaredBeck为了澄清,输入字符串作为示例给出 - 真正的问题是支持任意字符串,并用所提供的替换替换任何索引处的模式匹配。例如,'replace('foofof','f | o | fo','x')'应该产生'['xoofof','xofof','fxofof','fxxfof','foxfof', ]' – Anand

+0

请根据您的最新评论更新您的问题。另外,''f | o | fo''是一个字符串,而不是一个模式。如果你想要一个模式,你应该使用'/ f | o | fo /'。 –

这是很容易与pre_match$`)和post_match$')做:

def replace_matches(str, re, repl) 
     return enum_for(:replace_matches, str, re, repl) unless block_given? 
     str.scan(re) do 
     yield "#$`#{repl}#$'" 
     end 
    end 

    str = "foofoofoo" 

    # block usage 
    replace_matches(str, /foo/, "bar") { |x| puts x } 

    # enum usage 
    puts replace_matches(str, /foo/, "bar").to_a 

编辑:如果你有重叠的比赛,那么它会变得更难,因为正则表达式并没有真正具备处理它的能力。所以,你可以做这样的:

def replace_matches(str, re, repl) 
    return enum_for(:replace_matches, str, re, repl) unless block_given? 
    re = /(?=(?<pattern>#{re}))/ 
    str.scan(re) do 
    pattern_start = $~.begin(0) 
    pattern_end = pattern_start + $~[:pattern].length 
    yield str[0 ... pattern_start] + repl + str[pattern_end .. -1] 
    end 
end 

str = "oooo" 
replace_matches(str, /oo/, "x") { |x| puts x } 

在这里,我们滥用积极先行,这是0宽度,所以我们可以得到重叠的匹配。但是,我们还需要知道我们匹配了多少个字符,而我们现在无法像以前那样匹配0个宽度,因此我们将重新捕获该预测的内容,并计算新的宽度那。

(免责声明:它仍然将只匹配每个字符一次;如果你想在每一个字符考虑多种可能性,就像你/f|o|fo/情况下,复杂的东西还多)

编辑:一个有点调整和我们甚至可以支持适当的GSUB类似的行为:

def replace_matches(str, re, repl) 
    return enum_for(:replace_matches, str, re, repl) unless block_given? 
    new_re = /(?=(?<pattern>#{re}))/ 
    str.scan(new_re) do 
    pattern_start = $~.begin(0) 
    pattern_end = pattern_start + $~[:pattern].length 
    new_repl = str[pattern_start ... pattern_end].gsub(re, repl) 
    yield str[0 ... pattern_start] + new_repl + str[pattern_end .. -1] 
    end 
end 

str = "abcd" 
replace_matches(str, /(?<first>\w)(?<second>\w)/, '\k<second>\k<first>').to_a 
# => ["bacd", "acbd", "abdc"] 

(免责声明:最后一个片段,该模式采用回顾后或先行到比赛区域外的检查无法处理的情况下)

+0

如果我可以的话,我会再次为此编辑upvote :-) – Anand

我想借此串foofoofoo,地图FOO吧,并返回所有单个替换为一个数组 - 如果我们假定[ 'barfoofoo', 'foobarfoo', 'foofoobar']

输入总是正好是“foofoofoo”(三个“foo”),那么问题很简单,所以我们假设有一个或多个“foo”。

def possibilities(input) 
    n = input.length/3 
    n.times.map { |i| 
    (['bar'] + Array.new(n - 1, 'foo')).rotate(-i).join 
    } 
end 

possibilities "foo" 
# ["bar"] 
possibilities "foofoo" 
# ["barfoo", "foobar"] 
possibilities "foofoofoo" 
# ["barfoofoo", "foobarfoo", "foofoobar"] 

有一些解决方案会使用较少的内存,但是这种方法似乎很方便。

我不认为Ruby提供了这种开箱即用的功能。然而,这里是我的两分钱,这可能是更优雅:

def replace(str, pattern, replacement) 
    count = str.scan(pattern).count 
    fragments = str.split(pattern, -1) 

    count.times.map do |occurrence| 
    fragments[0..occurrence].join(pattern) 
     .concat(replacement) 
     .concat(fragments[(occurrence+1)..count].to_a.join(pattern)) 
    end 
end 
+0

这是针对所述问题的一个非常好的方法。在我的问题中看到我的评论 - 我实际上需要支持从任何索引开始的匹配,所以,例如,'replace 'oxo','oox']' – Anand