【问题标题】:Ruby gsub match concatenationRuby gsub 匹配连接
【发布时间】:2016-05-20 22:41:51
【问题描述】:

给定一串数字,我试图在奇数之间插入'-',在偶数之间插入'*'。解决方法如下:

def DashInsertII(num)
  num = num.chars.map(&:to_i)
  groups = num.slice_when {|x,y| x.odd? && y.even? || x.even? && y.odd?}.to_a
  puts groups.to_s
    groups.map! do |array|
      if array[0].odd?
      array.join(" ").gsub(" ", "-")
      else
      array.join(" ").gsub(" ", "*")
    end
  end
  d = %w{- *}
  puts groups.join.chars.to_s
  groups = groups.join.chars

 # Have to account for 0 because Coderbyte thinks 0 is neither even nor odd, which is false.

  groups.each_with_index do |char,index|
    if d.include? char
        if (groups[index-1] == "0" || groups[index+1] == "0")
            groups.delete_at(index)
        end
    end
  end
  groups.join
end

非常复杂,我想知道我是否可以这样做:

"99946".gsub(/[13579][13579]/) {|s,x| s+"-"+x}

其中s 是第一个奇数,x 第二个。通常当我替换时,我会替换匹配的术语,但这里我想保留匹配的术语并在模式之间插入一个字符。这将使这个问题变得更简单。

【问题讨论】:

  • 你读过docs for String#gsub吗?他们清楚地解释了如何在替换中使用捕获的字符串。

标签: ruby regex substitution


【解决方案1】:

这对你有用:

"99946".gsub(/[13579]+/) {|s| s.split("").join("-") }
# => "9-9-946"

这与您尝试的大致相似。它捕获多个连续的奇数,并使用 gsub 块将它们拆分然后连接它们,并用“-”分隔。

这将包括两个解决方案一起工作:

"99946".gsub(/[13579]+/) {|s| s.split("").join("-") }.gsub(/[02468]+/) {|s| s.split("").join("*") }
# => "9-9-94*6"

【讨论】:

  • 这里的 $ 是什么意思?为什么我们在提到捕获的组时需要它? @Michael_Gaskill
  • 是的,$1 是“第一个捕获的组”,$2 是第二个。 $0 将是整个匹配的表达式。
【解决方案2】:

接受的答案很好地说明了解决问题所需的逻辑。但是,我想建议在生产代码中对其进行一些简化,以便于阅读和理解。

特别是,我们用不同的参数做了两次相同的事情,所以通过编写一个都使用调用的方法或 lambda 来让读者明白这一点会很有帮助。例如:

do_pair = ->(string, regex, delimiter) do
  string.gsub(regex) { |s| s.chars.join(delimiter) }
end

那么,可以这样称呼它:

do_pair.(do_pair.('999434432', /[13579]+/, '-'), /['02468']+/, '*')

这可以进一步简化:

do_pair = ->(string, odd_or_even) do
  regex     = (odd_or_even == :odd) ? /[13579]+/ : /['02468']+/
  delimiter = (odd_or_even == :odd) ?    '-'     :     '*'
  string.gsub(regex) { |s| s.chars.join(delimiter) }
end

这种方法的一个优点是,它使我们处理两种情况(奇数和偶数)这一事实以及我们用于这两种情况的值都变得很明显。然后可以这样调用:

do_pair.(do_pair.('999434432', :odd), :even)

当然,这也可以通过方法来完成,这很好。我建议使用 lambda 的原因是它的逻辑非常简单,并且仅在单个方法中的一个(尽管是复合)表达式中使用。

这无疑更冗长,但将读者的逻辑分解成更容易理解的部分,从而降低理解它的认知成本。

【讨论】:

    【解决方案3】:

    通常的做法是:

    "99946"
    .gsub(/(?<=[13579])(?=[13579])/, "-")
    .gsub(/(?<=[2468])(?=[2468])/, "*")
    # => "9-9-94*6"
    

    "99946".gsub(/(?<=[13579])()(?=[13579])|(?<=[2468])()(?=[2468])/){$1 ? "-" : "*"}
    # => "9-9-94*6"
    

    【讨论】:

      【解决方案4】:
      "2899946".each_char.chunk { |c| c.to_i.even? }.map { |even, arr|
        arr.join(even ? '*' : '-') }.join
        #=> "2*89-9-94*6"
      

      步骤:

      enum0 = "2899946".each_char
        #=> #<Enumerator: "2899946":each_char>
      

      我们可以将enum0 转换为数组来查看它会生成的元素:

      enum0.to_a
        #=> ["2", "8", "9", "9", "9", "4", "6"]
      

      继续,

      enum1 = enum0.chunk { |c| c.to_i.even? }
        #=> #<Enumerator: #<Enumerator::Generator:0x007fa733024b58>:each> 
      enum1.to_a
        #=> [[true, ["2", "8"]], [false, ["9", "9", "9"]], [true, ["4", "6"]]] 
      a = enum1.map { |even, arr| arr.join(even ? '*' : '-') }
        #=> ["2*8", "9-9-9", "4*6"] 
      a.join
        #=> "2*89-9-94*6" 
      

      【讨论】:

        猜你喜欢
        • 2013-03-27
        • 2012-02-24
        • 2013-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-10
        • 2010-12-31
        • 1970-01-01
        相关资源
        最近更新 更多