2

我想在 Ruby 中构建一个函数,该函数接受一个字符串并将其与一个可能性列表进行比较,然后再返回一些值或函数。这是一种(可能是幼稚的)可以写成案例陈述的方式:

a = 'unicorn'
case a
  when "gnome", "dwarf", "hobbit"
    "dwarf"
  when "dragon"
    puts "run away!"
  when "centaur", "unicorn"
    magical_equine_function(a)
  else
    false
end

我的问题是,如何使用像amatch这样的近似匹配库来做同样的事情,这样你就可以设置一个 amatch 值(比如 2),并且当你有 a = "Unicorn" 或 a = "uncorn “?

此外,是否有一种更简洁、更易于维护的方式来编写所有这些内容,方法是通过 YAML 文件播种/比较所有可能的匹配项,而不是写出大量的 case 语句?我对 YAML 完全陌生,但希望我能够加载如下文件:

? 
  -"gnome"
  -"dwarf"
  -"hobbit"
: 
  -"dwarf"
? 
  -"dragon"
: 
  -puts "run away"
?
  -"centaur"
  -"unicorn"
: 
  -magical_equine_function(a)

然后对加载的文件进行近似匹配。有没有办法做到这一点不会影响性能?

4

1 回答 1

2

有几种方法可以给这只猫剥皮。一种是使用 lambda:

MATCHERS = [
  [/^gnome|dwarf|hobbit$/, lambda { |a| 'dwarf' }],
  ['dragon', lambda { |a| puts 'run away!' }],
  [/^centaur|unicorn$/, lambda { |a| magical_equine_function(a) }],
  [//, lambda { |a| false }],
]

这将模式(可以是字符串或正则表达式)与 lambda 相关联。最后一个匹配器很特别:它是一个可以匹配任何东西的哨兵。它代表您示例中的else子句。

这是进行匹配的代码:

def match(a)
  MATCHERS.each do |pattern, f|
    return f[a] if pattern === a
  end
end

我们使用===以便可以使用字符串或正则表达式。

以下是它的使用方法:

p match('gnome')
# => "dwarf"

p match('dragon')
# => "run away!"
# => nil

p match('unicorn')
# => "equine unicorn"

p match('oddball')
# => false

您也可以使用方法来做到这一点:

class Matcher

  def match(a)
    MATCHERS.each do |pattern, method|
      return send(method, a) if pattern === a
    end
  end

  private

  MATCHERS = [
    [/^gnome|dwarf|hobbit$/, :dwarf],
    ['dragon', :dragon],
    [/^centaur|unicorn$/, :equine],
    [//, :default],
  ]

  def dwarf(a)
    "dwarf"
  end

  def dragon(a)
    puts "run away!"
  end

  def equine(a)
    magical_equine_function(a)
  end

  def default(a)
    false
  end

  def magical_equine_function(a)
    "equine #{a}"
  end

end

并在使用中:

matcher = Matcher.new
    p matcher.match('gnome')
# => "dwarf"

# etc.

使用方法将代码与匹配规则分开,如果需要,您可以将匹配规则放入文件中。这是的内容match_rules

---
- - !ruby/regexp /^gnome|dwarf|hobbit$/
  - :dwarf
- - dragon
  - :dragon
- - !ruby/regexp /^centaur|unicorn$/
  - :equine
- - !ruby/regexp //
  - :default

我们不必对Matcher类进行很多更改即可使其正常工作:

require 'yaml'

class Matcher

  def initialize
    @matchers = YAML.load_file('match_rules')
  end

  def match(a)
    @matchers.each do |pattern, method|
      return send(method, a) if pattern === a
    end
  end

  # ...

end

使用send您从文件中获取的数据允许创建该文件的人让您调用任何 Matcher 类的方法。为了防止这种情况,您可以简单地为规则中使用的所有方法添加一个前缀:

def matched_dragon(a)
  puts "run away!"
end

# etc.

在 match 方法中,添加该前缀:

def match(a)
    @matchers.each do |pattern, method|
      return send("matched_#{method}", a) if pattern === a
    end
  end

现在只有以“matched_”为前缀的方法才能被 match 函数调用。


在评论中回答后续问题:

模式是字符串数组的 YAML 如下所示:

---
- - - gnome
    - dwarf
    - hobbit
  - :small_person
- - - dragon
  - :dragon
于 2012-10-20T19:21:49.417 回答