views:

106

answers:

3

Let's say, I want to separate certain combinations of elements from an array. For example

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"]]

The code have done the correct extraction, but it missed the elements just after the extracted sets. So if my data array is

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

then

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

Why does it happen? How to get those missed elements inside #each_with_index? Or may be there is a better solution for this problem assuming that there are much more sets to extract?

+1  A: 

Because you are manipulating data in place.

When you hit rgb the next element in the loop would be 255, but you are deleting those elements so now between is in the place that rgb was, so the next element is hex

Something like this may work better for you:

when 'rgb' then rgb  = data.slice! i+1,3
when 'hex' then hex  = data.slice! i+1,1
gnibbler
I see. How would you do such extraction then? (here, `rgb` and `hex` are just to illustrate the results. I use `color` in my code in both cases)
Andrey
Aha,.. no I need to remove 'rgb', 'hex' and others from `data` (case-insensitive)
Andrey
+1  A: 
Jörg W Mittag
Thanks for the nice example! I need to parse a not very complicated syntax, which is mostly a chain of space-separated words/numbers (mixed sets), although with comments and quoted strings. I am quite a newbie in this problem, so may be you can advise me good libraries/tutorials/examples for making such parsers in Ruby?
Andrey
Sweeet!.. Thank you very much for your time, Jörg! Really appreciate your help. The parser looks very nice and clear, but I need some time to think how to add other types of my input. I will probably show up on SO again with another question about the parser.
Andrey
@Andrei: if you are a paying member or the ACM or you are a student or employee of a company/school/college/university that is a paying member, you should definitely check out the paper *An object oriented approach to constructing recursive descent parsers* by Matthew S. Davis (http://Portal.ACM.Org/citation.cfm?id=345105.345113). It uses Smalltalk for the examples, but they are mostly directly applicable to Ruby as well.
Jörg W Mittag
@Andrei: also, I found it helpful to have the grammar at least informally written down and put it side-by-side with the code.
Jörg W Mittag
@Jörg: Thank you for the nice reference and advices! I have got the article and tried to read it. Pretty new language for me and quite a number of references to more nice articles. I still have difficulties to write Greibach forms for my grammar. May be you can have a look on it, if you have time? http://stackoverflow.com/questions/3363227/parser-in-ruby-dealing-with-sticky-comments-and-quotes
Andrey
A: 

Here is a bit nicer solution

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"]
Andrey