我想在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这样的近似匹配库来做同样的事情,这样你就可以设置一个匹配值(比如2),当你有一个=时仍然会得到匹配Unicorn“或a =”uncorn“?
此外,是否有一种更清晰,更易于维护的方法来通过YAML文件播种/比较所有可能的匹配来编写所有这些,而不是写出大量的case语句?我是YAML的新手,但希望我能够只加载一个文件:
?
-"gnome"
-"dwarf"
-"hobbit"
:
-"dwarf"
?
-"dragon"
:
-puts "run away"
?
-"centaur"
-"unicorn"
:
-magical_equine_function(a)
然后只对加载的文件进行近似匹配。有没有办法做到这一点,不会杀死性能?
答案 0 :(得分:1)
有几种方法可以给这只猫上皮。一个是lambdas:
MATCHERS = [
[/^gnome|dwarf|hobbit$/, lambda { |a| 'dwarf' }],
['dragon', lambda { |a| puts 'run away!' }],
[/^centaur|unicorn$/, lambda { |a| magical_equine_function(a) }],
[//, lambda { |a| false }],
]
它将模式(可以是字符串或正则表达式)与lambdas相关联。最后一个匹配器是特殊的:它是一个匹配任何东西的哨兵。它代表示例中的 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.
在匹配方法中,添加该前缀:
def match(a)
@matchers.each do |pattern, method|
return send("matched_#{method}", a) if pattern === a
end
end
现在只有匹配函数可以调用前缀为“matched_”的方法。
在评论中回答后续问题:
模式的YAML是一个字符串数组,如下所示:
---
- - - gnome
- dwarf
- hobbit
- :small_person
- - - dragon
- :dragon