1

たとえば、要素の特定の組み合わせを配列から分離したいとします。例えば

data = %w{ start before rgb 255 255 255 between hex FFFFFF after end }
rgb, hex = [], []
data.each_with_index do |v,i|
  p [i,v]
  case v.downcase
    when 'rgb' then rgb  = data.slice! i,4
    when 'hex' then hex  = data.slice! i,2
  end
end
pp [rgb, hex, data]
# >> [0, "start"]
# >> [1, "before"]
# >> [2, "rgb"]
# >> [3, "hex"]
# >> [4, "end"]
# >> [["rgb", "255", "255", "255"],
# >>  ["hex", "FFFFFF"],
# >>  ["start", "before", "between", "after", "end"]]

コードは正しい抽出を実行しましたが、抽出されたセットの直後の要素が欠落しています。したがって、私のデータ配列が

data = %w{ start before rgb 255 255 255 hex FFFFFF after end }

それから

pp [rgb, hex, data]
# >> [["rgb", "255", "255", "255"],
# >>  [],
# >>  ["start", "before", "hex", "FFFFFF", "after", "end"]]

なぜそれが起こるのですか?それらの見逃した要素を内部に入れる方法は#each_with_index?または、抽出するセットがはるかに多いと仮定して、この問題のより良い解決策があるでしょうか?

4

3 に答える 3

1

あなたがそのdata場で操作しているからです。

rgbループ内の次の要素をヒットするとはになり255ますが、これらの要素を削除しているので、今betweenは以前の場所rgbにあるので、次の要素はhex

このようなものがあなたにとってよりうまくいくかもしれません:

when 'rgb' then rgb  = data.slice! i+1,3
when 'hex' then hex  = data.slice! i+1,1
于 2010-07-27T13:03:52.627 に答える
1

問題は、コレクションを反復処理しているときコレクションを変更していることです。これはおそらく機能しません。(そして私の意見では、そうすべきではありません。この場合、Rubyは、誤った動作を黙って許可するのではなく、例外を発生させる必要があります。これは、他のほとんどすべての命令型言語が行うことです。)

これは、元のスタイルを維持しながら、私が思いつくことができる最高のものです。

require 'pp'

data = %w[start before rgb 255 255 255 hex FFFFFF after end]

rgb_count = hex_count = 0

rgb, hex, rest = data.reduce([[], [], []]) do |acc, el|
  acc.tap do |rgb, hex, rest|
    next (rgb_count = 3  ; rgb << el) if /rgb/i =~ el
    next (rgb_count -= 1 ; rgb << el) if rgb_count > 0
    next (hex_count = 1  ; hex << el) if /hex/i =~ el
    next (hex_count -= 1 ; hex << el) if hex_count > 0
    rest << el
  end
end

data.replace(rest)

pp rgb, hex, data
# ["rgb", "255", "255", "255"]
# ["hex", "FFFFFF"]
# ["start", "before", "after", "end"]

しかし、あなたが抱えているのは構文解析の問題であり、それは実際にはパーサーによって解決されるべきです。単純な手巻きのパーサー/ステートマシンは、おそらく上記よりも少しコードが多くなりますがはるかに読みやすくなります。

これがあなたの問題を解決する単純な再帰下降パーサーです:

class ColorParser
  def initialize(input)
    @input = input.dup
    @rgb, @hex, @data = [], [], []
  end

  def parse
    parse_element until @input.empty?
    return @rgb, @hex, @data
  end

  private

  def parse_element
    parse_color or parse_stop_word
  end

  def parse_color
    parse_rgb or parse_hex
  end

  def parse_rgb
    return unless /rgb/i =~ peek
    @rgb << consume
    parse_rgb_values
  end

再帰下降パーサーは、その構造が文法とほぼ完全に一致しているため、非常に気に入っています。入力が空になるまで要素を解析し続けるだけです。要素とは何ですか?さて、それは色の仕様またはストップワードです。色の仕様とは何ですか?ええと、それはRGBカラー仕様か16進カラー仕様のどちらかです。RGBカラー仕様とは何ですか?まあ、それは正規表現と/rgb/iそれに続くRGB値に一致するものです。RGB値とは何ですか?ええと、それはたった3つの数字です…

  def parse_rgb_values
    3.times do @rgb << consume.to_i end
  end

  def parse_hex
    return unless /hex/i =~ peek
    @hex << consume
    parse_hex_value
  end

  def parse_hex_value
    @hex << consume.to_i(16)
  end

  def parse_stop_word
    @data << consume unless /rgb|hex/i =~ peek
  end

  def consume
    @input.slice!(0)
  end

  def peek
    @input.first
  end
end

そのようにそれを使用してください:

data = %w[start before rgb 255 255 255 hex FFFFFF after end]
rgb, hex, rest = ColorParser.new(data).parse

require 'pp'

pp rgb, hex, rest
# ["rgb", 255, 255, 255]
# ["hex", 16777215]
# ["start", "before", "after", "end"]

比較のために、文法は次のとおりです。

  • S要素*
  • 要素カラー | ワード
  • rgbhex | _
  • rgbrgb rgbvalues
  • rgbvaluesトークン トークン トークン
  • hexhex hexvalue
  • hexvalueトークン
  • 単語トークン
于 2010-07-27T14:08:23.937 に答える
0

これがもう少し良い解決策です

data = %w{ start before rgb 255 255 255 hex FFFFFF hex EEEEEE after end }
rest, rgb, hex = [], [], []
until data.empty?
  case (key = data.shift).downcase
    when 'rgb' then rgb  += [key] + data.shift(3)
    when 'hex' then hex  += [key] + data.shift(1)
    else rest << key
  end
end
p rgb, hex, rest
# >> ["rgb", "255", "255", "255"]
# >> ["hex", "FFFFFF", "hex", "EEEEEE"]
# >> ["start", "before", "after", "end"]
于 2010-08-02T14:04:19.977 に答える