Частичное совпадение слов в Ruby

Мне нужна помощь в поиске частичного совпадения слов. Он должен возвращать слова, которые соответствуют трем последовательным буквам цели. Например:

WORDS = ["born", "port" ,"cort", "mort"]
find_match("corn", WORDS)  =>  returns  ["born", "cort"]

должны найти частичные совпадения для "corn". И "b orn ", и " cor t" совпадают.

Регулярные выражения могут быть не лучшим решением такой проблемы. Если у вас есть другие идеи, не стесняйтесь поделиться.


person hken27    schedule 20.10.2013    source источник


Ответы (2)


Вы можете использовать each_cons для создания массива подстрок:

'corn'.chars.each_cons(3).map(&:join)
# ['cor', 'orn']

Затем Regexp.union для преобразования массива в одно регулярное выражение:

re = Regexp.union('corn'.chars.each_cons(3).map(&:join))

Затем вы можете сопоставить re с элементами массива:

WORDS.select { |w| w =~ re }

Обобщение:

def find_match(word, words)
    re = Regexp.union(word.chars.each_cons(3).map(&:join))
    words.select { |w| w =~ re }
end

Я уверен, что есть много вариаций на эту общую тему. Например, вы можете использовать форму match_str для String#[] вместо регулярного выражения, и я уверен, что есть много разных способов вытащить все подстроки длины 3.

person mu is too short    schedule 20.10.2013
comment
@Стефан: Хорошо. Я часто забываю о grep. - person mu is too short; 20.10.2013
comment
Спасибо, мю. Здесь так много нового для меня, плюс напоминание @Stefan о grep. Я не знал ни о each_con, ни о Regexp.union. Очень полезно. Мне нужно более тщательно изучить методы Regexp. - person Cary Swoveland; 23.10.2013
comment
@CarySwoveland: документы Enumerable (ruby-doc.org/core-2.0.0 /Enumerable.html) необходимо прочитать всем, кто работает с Ruby. Затем вам придется какое-то время переключаться между чтением интерфейса Enumerable и мышлением в стиле shell-pipeline/functional, и в конечном итоге это станет второй натурой. Консультации с документами часто также помогают вам быть в курсе того, что доступно (как и ответы на вопросы здесь :). Похоже, вы тоже почти сосед. - person mu is too short; 23.10.2013

Решение без регулярных выражений:

WORDS = ["born", "port" ,"cort", "mort"]

def find_match(w)
  threes = (0..w.size-3).reduce([]) {|arr, i| arr << w[i,3]}
  WORDS.select {|w| threes.select {|s| w.include?(s)}.any?}
end

find_match("corn")   # => ["born", "cort"] 
find_match("cavort") # => ["port", "cort", "mort"]   
find_match("heart")  # => []
  • Сначала вычислите threes, массив всех подстрок w длины три. Если w = snort, это будет ['sno', 'nor', 'ort'], где w[0,3] = 'sno', w[1,3] = 'nor' и w[2,3] = 'ort'.
  • Затем выберите слова в WORDS, подстрока которых соответствует хотя бы одной из строк в threes.

Конечно, есть много вариантов этого, например:

threes = []; (threes << w[0,3]; w.slice!(0)) while w.size > 2

Для второй строки выше я сначала попытался

threes.reduce([]) {|arr1, s| arr1 += WORDS.select {|w| w.include?(s)}}

но это было проблематично, потому что слово в WORDS могло соответствовать более чем одной трехсимвольной подстроке w, и в этом случае оно включалось в arr1 один раз для каждого совпадения.

person Cary Swoveland    schedule 20.10.2013