Проблема в том, что вы мутируете коллекцию , в то время как вы итерируете ее. Это не может возможно работать. (И, на мой взгляд, этого не должно быть. В этом случае 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, либо шестнадцатеричная спецификация цвета. Что такое спецификация цвета RGB? Ну, это то, что соответствует регулярному выражению /rgb/i
, за которым следуют значения RGB. Каковы значения RGB? Ну, это всего лишь три числа & hellip;
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 & rarr; элемент
*
- элемент & rarr; цвет
|
слово
- color & rarr; rgb
|
hex
- rgb & rarr; rgb rgbvalues
- rgbvalues & rarr; токен токен токен
- hex & rarr; hex hexvalue
- шестнадцатеричное значение & rarr; лексема
- word & rarr; лексема