Ruby:扫描字符串以匹配相邻的元音组

时间:2015-10-23 00:40:25

标签: ruby regex string

我正在构建一个脚本来随机生成听起来像英语的单词。我已将大量英文单词分解为VCV groups

  

...其中V代表一个单词中所有相邻的元音,C代表所有相邻的辅音。例如,英文单词“缩样”将成为   “-mi”,“inia”,“iatu”和“ure”。 “学校”将成为“-schoo”和“ool”。   这些小组将与其他小组一起组成   规则是完整的相邻结尾元音必须匹配   附件组的一整套起始元音。

我在以下结构中构造了一个哈希:

pieces = { 
   :starters => { "-sma" => 243, "-roa" => 77, "-si" => 984, ...},
   :middles =>  { "iatu" => 109, "inia" => 863, "aci" => 229, ...},
   :enders =>   { "ar-" => 19, "ouid-" => 6, "ude" => 443,   ...}
}

为了构造生成的单词,“起始”字符串需要以与“中间”字符串相同的元音分组结束。将“中间”字符串与“ender”字符串连接时也是如此。使用上述示例的一个可能结果是“ - sma”+“aba”+“ar - ”以提供“smabar”。另一个是“ - si”+“inia”+“iatu”+“ude”来提供“siniatude”

我的问题是,当我采样任何两件时,我不知道如何确保第一件的结束V组与第二件的开始V组完全匹配。例如,“utua”+“uailo”不会一起使用,因为“ua”“uai”不同。但是,成功的一对将是“utua”+“uado”,因为“ua”=“ua”

3 个答案:

答案 0 :(得分:2)

def match(first, second)
  end_of_first = first[/[aeiou]+$|[^aeiou]+$/]
  start_of_second = second[/^[aeiou]+|^[^aeiou]+/]
  end_of_first == start_of_second
end

match("utua", "uailo")
# => false
match("inia", "iatu")
# => true
编辑:我显然无法阅读,我以为你只想匹配这个组(无论是元音还是辅音)。如果限制为元音组,则更简单:

  end_of_first = first[/[aeiou]+$/]
  start_of_second = second[/^[aeiou]+/]

答案 1 :(得分:0)

由于您已经预处理了字典,我建议您进行一些预处理以使生成更简单。我有两个建议。首先,对于starters和middles,将每个分成一个形式(VC,V)的元组(在Ruby中,我们只使用一个双元素数组),例如, "inia"变为["in", "ia"]

starters = [
  [ "-sm", "a" ],
  [ "-r", "oa" ],
  [ "-s", "i" ],
  # ...
]

我们将启动器存储在数组中,因为我们只需要随机选择一个,我们可以使用Array#sample

starter, middle1_key = starters.sample
puts starter # => "-sm"
puts middle1_key # => "a"

我们希望能够通过他们的初始V组查找middles,所以我们将这些元组放在Hash中,而将其初始V组作为键:

middles = {
  "ia" => [
    [ "iat", "u" ],
    [ "iabl", "e" ],
  ],
  "i" => [
    [ "in", "ia" ],
    # ...
  ],
  "a" => [
    [ "ac", "i" ],
    # ...
  ],
  # ...
}

由于我们将起始器的最终V组存储在上面的middle1_key中,我们现在可以使用它作为键来获取初始V组匹配的中间元组数组,并像上面一样随机选择一个:

possible_middles1 = middles[middle1_key]
middle1, middle2_key = possible_middles1.sample
puts middle1 # => "ac"
puts middle2_key => "i"

只是为了踢,让我们选择第二个中间位置:

middle2, ender_key = middles[middle2_key].sample
puts middle2 # => "in"
puts ender_key # => "ia"

我们的enders我们不需要存储在元组中,因为我们不会像使用middles一样使用它们中的任何部分。我们可以把它们放在哈希中,哈希的键是初始V组,其值是所有具有该初始V组的enders的数组:

enders = {
  "a" => [ "ar-", ... ],
  "oui" => [ "ouid-", ... ],
  "u" => [ "ude-", ... ],
  "ia" => [ "ial-", "iar-", ... ]
  # ...
}

我们将第二个中间人的最终V组存储在上面的ender_key中,我们可以使用它来获取匹配的enders数组:

possible_enders = enders[ender_key]
ender = possible_enders.sample
puts ender # => "iar-"

现在我们有四个部分,我们只是把它们放在一起形成我们的话:

puts starter + middle1 + middle2 + ender
# => -smaciniar-

修改

上面的数据结构省略了相对频率(在我有机会阅读你关于数字的问题的答案之前,我写了上述内容)。显然,将相对频率存储在零件旁边也是微不足道的,但我不知道如何快速地以加权的方式选择零件。希望无论如何,我的答案对你有用。

答案 2 :(得分:0)

您可以使用方法Enumerable#flat_mapString#partitionEnumerable#chunk以及一些更熟悉的方法来实现这一目标:

def combine(arr)
  arr.flat_map { |s| s.partition /[^aeiou-]+/ }.
      chunk { |s| s }.
      map { |_, a| a.first }.
      join.delete('-')
end

combine ["-sma", "aba", "ar-"])            #=> "smabar"
combine ["-si", "inia", "iatu", "ude"]     #=> "siniatude" 
combine ["utua", "uailo", "orsua", "uav-"] #=> "utuauailorsuav"

要了解其工作原理,让我们看一下最后一个例子:

arr = ["utua", "uailo", "orsua", "uav-"]
a = arr.flat_map { |s| s.partition /[^aeiou-]+/ }
  #=> ["u", "t", "ua", "uai", "l", "o", "o", "rs", "ua", "ua", "v", "-"] 
enum = a.chunk { |s| s }
  #=> #<Enumerator: #<Enumerator::Generator:0x007fdd14963888>:each> 

我们可以通过将它转换为数组来查看此枚举器的元素:

enum.to_a
  #=> [["u", ["u"]], ["t", ["t"]], ["ua", ["ua"]], ["uai", ["uai"]],
  #    ["l", ["l"]], ["o", ["o", "o"]], ["rs", ["rs"]], ["ua", ["ua", "ua"]],
  #    ["v", ["v"]], ["-", ["-"]]]

b = enum.map { |_, a| a.first }
  #=> ["u", "t", "ua", "uai", "l", "o", "rs", "ua", "v", "-"] 
s = b.join
  #=> "utuauailorsuav-" 
s.delete('-')
  #=> "utuauailorsuav"