2

文字列を受け取り、値または関数を返す前に可能性のリストと比較する関数を Ruby で作成したいと考えています。以下は、case ステートメントとして記述できる (おそらくナイーブな) 方法です。

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 がある場合でも一致するようにする方法です。 "?

また、大量の case ステートメントを書き出すのではなく、YAML ファイルを介してすべての可能な一致をシード/比較することにより、これらすべてを記述するよりクリーンで保守しやすい方法はありますか? 私はYAMLをまったく初めて使用しますが、次のようなファイルをロードできることを望んでいました:

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

そして、ロードされたファイルに対して近似マッチングを行うだけです。パフォーマンスを落とさない方法はありますか?

4

1 に答える 1

2

この猫の皮を剥ぐ方法はいくつかあります。1つはラムダを使用しています:

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

これにより、パターン (文字列または正規表現) がラムダに関連付けられます。最後のマッチャーは特別です。これは、何にでもマッチする歩哨です。これは、例の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

ファイルから on data you toを使用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 に答える