From: Robert Klemme Date: 2006-12-08T03:55:05+09:00 Subject: Re: variable pointers On 06.12.2006 19:55, xyz wrote: > I have a situation where I'm walking through an unordered sequence of text > (splitting at newlines) looking for particular patterns. If a particular pattern > matches then I perform some processing unique to that pattern and assign the > output to a particular location (the location could be anything, e.g. local > scalar, a particular array element, an instance variable, etc.). > > WARNING: C syntax approaching. Please restore your seats to their upright and > locked positions and apply seat belts. > > Using "&" (take address) and "*" (de-reference pointer) from C, conceptually > what I want to do is: > > template = [ > [ patternRegex, processingProc, &storage ] > ... > ] > textSequence.each { |line| > template.each { |tuple| > *tuple[2] = tuple[1].call(line) if line =~ tuple[0] > } > } > > Obviously this could be achieved with eval() but that is very heavyweight. > > Are there other options? > > It doesn't seem that using Symbol's will work since they're not addresses (and > thus can't be de-referenced) and they're scope-independent, i.e. if I use :foo I > can't distinguish between a local variable foo, a method foo(), etc. even if > they're not lexically visible within the current scope. > > As a kludge I could change tuple[2] to be an integer that indexes into an array > and after the main loop is finished manually scatter the array elements to their > final locations (note the word "kludge" at the beginning). The locations being > stored may be all over the place so collecting them in a single object would be > difficult, no less kludgy, and would obfuscate the code. I can think of several approaches one of them being: class Foo INSTRUCTIONS = { /(\d+)\s*\+\s*(\d+)/ => lambda {|t, a, b| a.to_i + b.to_i}, } def process (enum) result = {} enum.each do |line| INSTRUCTIONS.each do |rx, fun| md = rx.match(line) and result[rx] = fun[*md.to_a] end end result end end irb(main):051:0> Foo.new.process ["1+2", "3"] => {/(\d+)\s*\+\s*(\d+)/=>3} Of course, you could use a symbol as label in INSTRUCTIONS and use that to reference results. And then you might as well store multiple results (if it is possible that a pattern matches multiple times). class Foo INSTRUCTIONS = { :sum => [/(\d+)\s*\+\s*(\d+)/, lambda {|t, a, b| a.to_i + b.to_i}], } def process (enum) result = Hash.new {|h,k| h[k]=[]} enum.each do |line| INSTRUCTIONS.each do |label, (rx, fun)| md = rx.match(line) and result[label] << fun[*md.to_a] end end result end end irb(main):087:0> Foo.new.process ["1+2", "3", " 4 + 4"] => {:sum=>[3, 8]} You can as well hand over the MatchData instance directly to the lambda etc. but I guess you get the picture. Btw, that method also works with an IO instance. Kind regards robert