红宝石正则表达式不一致

时间:2016-09-04 02:42:19

标签: ruby regex

我正在尝试使用带有后引用的gsub转换为罗马数字,我发现了一个奇怪的不一致。

$ ruby -v 
ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-linux]

$ irb
irb(main):001:0>   BASES = {
irb(main):002:1*     1000 => 'M',
irb(main):003:1*     500 => 'D',
irb(main):004:1*     100 => 'C',
irb(main):005:1*     50 => 'L',
irb(main):006:1*     10 => 'X',
irb(main):007:1*     5 => 'V',
irb(main):008:1*     1 => 'I'
irb(main):009:1>   }
=> {1000=>"M", 500=>"D", 100=>"C", 50=>"L", 10=>"X", 5=>"V", 1=>"I"}
irb(main):010:0>   BASE_KEYS = BASES.keys
=> [1000, 500, 100, 50, 10, 5, 1]
irb(main):011:0> rom = 'IIII'
=> "IIII"

以上是设置

下面我试图识别任何重复4次的字符, 并将其替换为该角色中的1个和下一个BASE角色。

例如:IIII => IV

irb(main):012:0>     rom.gsub(/((.)\2{3})/,
irb(main):013:1*       "#{
irb(main):014:0>         BASES[BASE_KEYS.select.with_index{ |bk, i|
irb(main):015:2>           BASES[BASE_KEYS[i]] == $2
irb(main):016:2>         }.first]
irb(main):017:0>        }
irb(main):018:1"        #{BASE_KEYS.select.with_index{ |bk, i|
irb(main):019:1>           BASES[BASE_KEYS[i]] == $2
irb(main):020:1>         }.first}
irb(main):021:1"        #{
irb(main):022:0>         BASES[BASE_KEYS.select.with_index{|bk, i|
irb(main):023:2>           BASES[BASE_KEYS[i+1]] == $2
irb(main):024:2>         }.first]
irb(main):025:0>        }
irb(main):026:1"        #{BASE_KEYS.select.with_index{ |bk, i|
irb(main):027:1>           BASES[BASE_KEYS[i+1]] == $2
irb(main):028:1>         }.first}
irb(main):029:1"       "
irb(main):030:1>     )
=> "\n       \n       I\n       1\n      "

所以我得到错误的答案..(有更多见解的调试信息)

irb(main):031:0>     rom.gsub(/((.)\2{3})/,
irb(main):032:1*       "#{
irb(main):033:0>         BASES[BASE_KEYS.select.with_index{ |bk, i|
irb(main):034:2>           BASES[BASE_KEYS[i]] == $2
irb(main):035:2>         }.first]
irb(main):036:0>        }
irb(main):037:1"        #{BASE_KEYS.select.with_index{ |bk, i|
irb(main):038:1>           BASES[BASE_KEYS[i]] == $2
irb(main):039:1>         }.first}
irb(main):040:1"        #{
irb(main):041:0>         BASES[BASE_KEYS.select.with_index{|bk, i|
irb(main):042:2>           BASES[BASE_KEYS[i+1]] == $2
irb(main):043:2>         }.first]
irb(main):044:0>        }
irb(main):045:1"        #{BASE_KEYS.select.with_index{ |bk, i|
irb(main):046:1>           BASES[BASE_KEYS[i+1]] == $2
irb(main):047:1>         }.first}
irb(main):048:1"       "
irb(main):049:1>     )
=> "I\n       1\n       V\n       5\n      "
irb(main):050:0> 

诚然,我的正则表达式代码几乎无法理解,但为什么在第二次调用相同代码时会得到不同的结果?

irb(main):050:0> rom
=> "IIII"

注意rom没有改变......

1 个答案:

答案 0 :(得分:3)

在评估正则表达式之前,您的代码使用$2。运行它后第一次设置$2并且代码按预期工作。考虑使用块而不是字符串,因为在匹配发生之前插入了字符串。

  

在块形式中,当前匹配字符串作为参数传入,并且将适当地设置诸如$ 1,$ 2,$`,$&和$'的变量。块返回的值将替换每次调用的匹配。

这是一致的:

rom.gsub(/((.)\2{3})/) { |s|
  "#{
     BASES[BASE_KEYS.select.with_index{ |bk, i|
       BASES[BASE_KEYS[i]] == $2
     }.first]
    }
    #{BASE_KEYS.select.with_index{ |bk, i|
       BASES[BASE_KEYS[i]] == $2
     }.first}
    #{
     BASES[BASE_KEYS.select.with_index{|bk, i|
       BASES[BASE_KEYS[i+1]] == $2
     }.first]
    }
    #{BASE_KEYS.select.with_index{ |bk, i|
       BASES[BASE_KEYS[i+1]] == $2
     }.first}
   "
}
# => "I\n    1\n    V\n    5\n   "