2017-01-24 196 views
3

替代的返回數組我想借此串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 /'。 –

回答

1

這是很容易與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

0

我想借此串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"] 

有一些解決方案會使用較少的內存,但是這種方法似乎很方便。

1

我不認爲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