Ruby 1.9正则表达式作为哈希键

13
我正在尝试这个示例 myhash = {/(\d+)/ => "hello"},使用的是ruby 1.9.2p136 (2010-12-25) [i386-mingw32]版本。
它没有按照预期工作(编辑:事实证明,它不应该按照我期望的方式工作):

irb(main):004:0> myhash = {/(\d+)/ => "hello"}
=> {/(\d+)/=>"Hello"}
irb(main):005:0> myhash[2222]
=> nil
irb(main):006:0> myhash["2222"]
=> nil

在ruby1.8.7上的Rubular中,这个正则表达式是有效的。
我错过了什么?


@gnab - 发现得不错。但不幸的是,那不是问题所在。 - Mr. L
1
我认为你必须迭代哈希并进行普通匹配。虽然这样做不会很快。 - Michael Koper
你在尝试解决什么问题?使用正则表达式作为哈希表的键值似乎有些奇怪。 - Jonas Elfström
@Jonas - 看起来我只是在把一个简单的任务复杂化了。 - Mr. L
请访问以下网址以了解有关编程的信息:http://rubyworks.github.io/hashery/,特别是http://rubydoc.info/github/rubyworks/hashery/master/Hashery/FuzzyHash。 - Seamus Abshere
5个回答

13

您正在寻找这种行为吗?

myhash = Hash.new{|h,k| h[k] = 'hello' if k =~ /(\d+)/}

p myhash['aaa'] #=> nil
p myhash #=> {}
p myhash['1234'] #=>" hello"
p myhash #=> {"1234"=>"hello"}

10

如果不加一些额外的代码,这段代码是行不通的。因为你正在比较一个正则表达式对象与整数或字符串对象。它们不会被视为值相等或身份相等。虽然它们可以匹配,但这需要对Hash类代码进行更改。

irb(main):001:0> /(\d+)/.class
=> Regexp
irb(main):002:0> 2222.class
=> Fixnum
irb(main):003:0> '2222'.class
=> String
irb(main):004:0> /(\d+)/==2222
=> false
irb(main):007:0> /(\d+)/=='2222'
=> false
irb(main):009:0> /(\d+)/.equal?'2222'
=> false
irb(main):010:0> /(\d+)/.equal?2222
=> false

你需要迭代哈希并在类似以下的地方使用 =~:

 hash.each do |k,v|    
   unless (k=~whatever.to_s).nil?
     puts v   
   end
 end

或者更改Hash类,除了正常匹配条件外,尝试使用=~。 (我认为最后一个选项可能很困难,在mri中,Hash类似乎有很多C代码)


5
您可以将Jean的答案放在default_proc中。
MAP = {
  /1/ => "one",
  /2/ => "two",
  /\d/ => "number"
}

MAP.default_proc = lambda do |hash, lookup|
  hash.each_pair do |key, value|
    return value if key =~ lookup
  end
  return nil
end

p MAP["2"] #=> "two"
p MAP[44] #=> "number"

这个答案似乎比被接受的那个更加通用。 - aclima

1

我从未想过将正则表达式用作哈希键。老实说,我不确定这是否有效,如果有效,它会如何工作。

无论如何,有两个想法:

  1. 你在尝试查找项目时使用了hash,但哈希被命名为myhash
  2. 如果我随便玩一下,得到这些结果:

    hektor ~ ❯❯ irb
    >> myhash = {/(\d+)/ => "hello"}
    => {/(\d+)/=>"hello"} 
    >> myhash['2222']
    => nil 
    >> myhash[2222]
    => nil 
    >> myhash[/(\d+)/]
    => "hello" 
    

这是使用 Ruby 1.9.2-p180。

好的,已经检查过了,以下是可行的内容:

myhash = {/foo/ => "hello"}
myhash[/foo/] # => "hello"

查找是基于关键字进行的,而该关键字是一个正则表达式,而不是该正则表达式的许多潜在匹配项之一。


@Telemachus - 是的,myhash和hash是我的笔误。而且看起来Ruby并没有真正将/(\d+)/视为正则表达式。 - Mr. L
@Telemachus - 我看到了这篇 Stack Overflow 的帖子 http://stackoverflow.com/questions/2082457/ruby-gsub-problem-when-using-backreference-and-hashes。看起来应该是可以工作的。 - Mr. L
你链接的帖子需要一个迭代器和一个调用gsub的操作,这将解释正则表达式。在你的情况下,你正在尝试将一个正则表达式对象分别与整数和字符串进行比较... - Jean
@Jean - 现在我也能看到它了。 - Mr. L
它对我不起作用。"ruby 1.9.2p180 (2011-02-18) [i386-mingw32]" irb(main):002:0> myhash[/222/] => nil - Jonas Elfström
1
@Jonas 这就是关键所在。你不能直接通过匹配来查找它。关键不是匹配,而是正则表达式本身。因此,尝试使用 myhash[/\d+/](例如),它应该返回 "hello"(或者你存储的任何内容)。 - Telemachus

0

现在有一个名为 Hashie 的宝石(gem),它提供了这个功能(以及更多):https://github.com/intridea/hashie#rash

它提供了一种名为 Rash(正则哈希)的数据结构,可以像这样使用

myhash = {/(\d+)/ => "hello"}
rash = Hashie::Rash.new(myhash)

>> rash["2222"]
=> "hello"

它真的会尝试将键与正则表达式匹配,因此数字键不起作用,除非您将它们转换为字符串,您可以通过将Rash继承到自己的类中轻松实现这一点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接