суботу, 19 червня 2010 р.

Аналог PHP функції strtr() на Ruby

В ході написання одного скрипта на Ruby, знадобився аналог PHP функції strtr().
Власне, функція повинна повертати рядок, у якому кожне входження будь-якого рядків із перелічених у from, замінюється на відповідний рядок з to. Функція приймає один аргумент, який повинен бути масивом, індекси якого трактуються як рядки пошуку, а відповідні значення - як рядки заміни. В першу чергу замінює більш довші підрядки.

class String
  def strtr(tr)
    sorted_tr = tr.sort{|a, b| b[0] <=> a[0]}
    keys = sorted_tr.map{|k, v| k}
    values = sorted_tr.map{|k, v| v}
    r = /(#{keys.map{|i| Regexp.escape(i)}.join( ')|(' )})/
    self.gsub(r){|match| values[keys.index(match)]}
  end
end

Приклад роботи:
tr1 = {'A' => '1', 'AA' => '2', 'AAA' => '3'}
tr2 = [['A', '1'],['AA', '2'],['AAA', '3']]
s = 'ABAACAAADAAAA'

puts s.strtr(tr1)
puts s.strtr(tr2)

1B2C3D31
1B2C3D31


UPD
Ще один варіант цієї функції. Коротший і зрозуміліший:
class String
  def strtr(tr)
    tr.sort{|a, b| b[0] <=> a[0]}.map.inject(self){|str, pair| str.gsub(pair[0], pair[1])}
  end
end


Але він не відповідає умові, що один і той же рядок пошуку використовується тільки один раз.

При вхідних даних:
s = "ABAACAAADAAAA"
tr = {"A"=>"AA", "AA"=>"AAAA", "AAA"=>"AAAAAA"}
s.strtr(tr)

Для першого варіанту отримаємо:
=> "AABAAAACAAAAAADAAAAAAAA"
а для другого:
=> "AABAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAA"