Robert Klemme
12/7/2006 6:51:00 PM
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