【问题标题】:Rubyish way to invert a regular expressionRubyish 反转正则表达式的方法
【发布时间】:2013-07-15 16:54:13
【问题描述】:

假设一个正则表达式来自当前上下文之外的调用代码,然后被传递给在当前项目之外实现的另一个调用:

["1", "2"].grep(/1/)        #=> ["1"]

在进行调用时,是否有一种简单的 Rubyish 方式来实现以下行为?

["1", "2"].grep(/1/.negate) #=> ["2"]

此行为类似于将=~ 运算符与!~ 运算符切换。当然,可以使用#select#reject,或者打开或继承Regexp。但我很好奇Ruby中是否已经有一种方法可以以上述方式否定正则表达式返回的匹配项。另外,我不在乎 falseniltrue 或匹配的位置是否参与实现此效果。

有一个相关的theoretical question,但超出了这里的简单考虑范围。


编辑:我知道迭代器是 Ruby 中过滤列表的一般方法,但人们忽略了问题的约束。另外,我认为正则表达式被反转的方式有一些很好的功能。我不认为它过度劳累或过于聪明。它是一种古老的面向对象编程,也是 Ruby 擅长做的事情。

【问题讨论】:

  • 虽然在语义上并不明显,但如果您考虑一下,将正则表达式传递给split“匹配”所有不匹配的子字符串。我对 Ruby 不够熟悉,无法根据此写出答案,但我相信其他人可以。
  • 您的问题有点矛盾,因为当您可以简单地更改迭代器时,尝试以这种方式修改正则表达式并不是 Rubyish。我想说你想要达到的目标是 Perlish 而不是 Rubyish。
  • @sawa - 我原则上不反对这一点。但是 Ruby 允许以多种方式解决问题。在这种情况下,我想继续使用我无法控制的方法(不一定是#grep),例如,它可能不接受块参数。

标签: ruby regex


【解决方案1】:
["1", "2"].reject { |e| /1/ === e }

【讨论】:

  • 我喜欢这个怪异之处,但你使用的是#reject,我希望继续使用#grep(或我无法控制的其他方法)。
  • @tadman:我试图在将正则表达式从我的控制中传递给我无法控制的方法(不一定是#grep)的约束下工作,仅此而已。我不是想变得困难。
  • reject 的参数可以通过编程方式指定,其中/1/ 可以是变量,也可以像这样包装:def grep_reject(regex); reject { |e| regex === e }; end
  • 如果您正在追求一个超级通用的解决方案,听起来您遇到了一个架构问题,在不同的关注点之间存在过多的信息泄露。您应该有某种类型的过滤器对象,给定正则表达式可以满足您的需求。任何公开正确方法的对象都是合适的替代品。 “Ruby 方式”是传入定义过滤行为的块,因此在这种情况下,{ |e| !regex.match(e) } 将封装您正在寻找的逻辑。
  • @tadman - 没有评论在这种特殊情况下泄漏关注点的可能性,Ruby 提供了另一种在实例化正则表达式后操纵它们的方法,即Regexp.union。我不认为假设的#negate 方法在精神上完全不同。
【解决方案2】:

你可以这样做:

class NegatedRegex < Regexp
  def ===(other)
    !super
  end
end

class Regexp
  def negate
    NegatedRegex.new self
  end
end

可能还有其他方法可以重新实现,但是对于 grep 这已经足够了:

["1", "2"].grep(/1/.negate) #=> ["2"]

【讨论】:

  • 沿着这些思路的东西可能是做到这一点的唯一方法。如果事实证明 Ruby 中没有内置支持,我会接受这个答案(我的印象是没有)。
  • String#scan 例如,直接在 MRI 上调用 rb_reg_search。如果不重新实现每个可能采用正则表达式的方法,就无法改变这种行为。
  • 虽然这很聪明,但它可能聪明了,那种聪明的,多年后你会想建造一台时光机,让自己回到过去,永远阻止自己首先这样做。
  • 也许它太聪明了,但我认为它很有趣,值得尝试看看它的发展方向。
  • 我喜欢这种方法,但不继承 Regexp 会更安全(因为你实际上并没有否定它的所有行为)而是有一个只定义 === 的类。
【解决方案3】:

你可以一次性完成它们:

re = /1/
matches, non_matches = ["1", "2", "1", "3"].partition { |el| re =~ el }

p matches       #=> ["1", "1"]
p non_matches   #=> ["2", "3"]

【讨论】:

    【解决方案4】:

    Grep 有一个黑暗的兄弟,他做任何事情都与 grep 相同,反之亦然。

    ["1", "2"].grep(/1/)   #=> ["1"]
    

    ["1", "2"].grep_v(/1/) #=> ["2"]
    

    【讨论】:

      【解决方案5】:
      arr=["1","2"]
      arr-arr.grep("1")  # ["2"]
      

      :)

      【讨论】:

        【解决方案6】:

        这可能是一种方法

        ["1", "2", 3].select {|i| i !~ /1/ }
         => ["2", 3] 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-30
          相关资源
          最近更新 更多