A tad of meta-programming needed

R

RichardOnRails

I've got the following hash set up and it works fine. But this took
more typing than programming.

h = {:name=>$1, :n_shares=>$2, :eek:pened_MDY=>$3, :closed_MDY=>$4,
:proceeds=>$5, :cost=> $6, :gain_Loss=>$7
}

So I'd like to get the following to work:

field_names =%w<Name Shares Opened_MDY Closed_MDY Proceeds Cost
GainLoss>
h = {}
(1..field_names.size).each { |i| h[field_names] = eval($ +
i.to_s) }

But concatenating the character "$" with the value of i converted to a
string, followed be evaluating that as a local variable isn't working
as I coded it. Can it be easily corrected?

Thanks in Advance,
Richard
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

On Sun, Jan 16, 2011 at 11:30 PM, RichardOnRails <
I've got the following hash set up and it works fine. But this took
more typing than programming.

h = {:name=>$1, :n_shares=>$2, :eek:pened_MDY=>$3, :closed_MDY=>$4,
:proceeds=>$5, :cost=> $6, :gain_Loss=>$7
}

So I'd like to get the following to work:

field_names =%w<Name Shares Opened_MDY Closed_MDY Proceeds Cost
GainLoss>
h = {}
(1..field_names.size).each { |i| h[field_names] = eval($ +
i.to_s) }

But concatenating the character "$" with the value of i converted to a
string, followed be evaluating that as a local variable isn't working
as I coded it. Can it be easily corrected?

Thanks in Advance,
Richard


The matchdata object implements the same inteface as a hash. If that is all
you need, on 1.9 you could just name the capture groups, then use the
MatchData object and not have to do any assignment/creation at all.
(Assuming this data is based on the pastie you posted earlier, this code
would work)


RUBY_VERSION # => "1.9.2"

line = "Apple 1.1234 1/2/3 4/5/6 $1,234.56 $7 ($5)"

regex = /^(?<name> .+ )\s
(?<n_shares> \d+\.\d{4} )\s+
(?<opened_MDY> \d\d?\/ \d\d?\/ \d\d? )\s+
(?<closed_MDY> \d\d?\/ \d\d?\/ \d\d? )\s+
(?<proceeds> [\$\d,\.]+ )\s+
(?<cost> [\$\d,\.]+ )\s+
(?<gain_Loss> [\(\)\$,\d \.]+ )\s*
$/x

result = regex.match(line)
result # => #<MatchData "Apple 1.1234 1/2/3 4/5/6
$1,234.56 $7 ($5)" name:"Apple" n_shares:"1.1234" opened_MDY:"1/2/3"
closed_MDY:"4/5/6" proceeds:"$1,234.56" cost:"$7" gain_Loss:"($5)">
result[ :name ] # => "Apple"
result[ :n_shares ] # => "1.1234"
result[ :eek:pened_MDY ] # => "1/2/3"
result[ :closed_MDY ] # => "4/5/6"
result[ :proceeds ] # => "$1,234.56"
result[ :cost ] # => "$7"
result[ :gain_Loss ] # => "($5)"
 
B

botp

On Sun, Jan 16, 2011 at 11:30 PM, RichardOnRails <
The matchdata object implements the same inteface as a hash. If that is a= ll
you need, on 1.9 you could just name the capture groups, then use the
MatchData object and not have to do any assignment/creation at all.
(Assuming this data is based on the pastie you posted earlier, this code
would work)
RUBY_VERSION # =3D> "1.9.2"
line =3D "Apple 1.1234 1/2/3 4/5/6 $1,234.56 $7 ($5)"
regex =3D /^(?<name> =A0 =A0 =A0 .+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 )\s
=A0 =A0 =A0 =A0 =A0(?<n_shares> =A0 \d+\.\d{4} =A0 =A0 =A0 =A0 =A0 =A0 = =A0 )\s+
=A0 =A0 =A0 =A0 =A0(?<opened_MDY> \d\d?\/ =A0\d\d?\/ =A0\d\d? =A0)\s+
=A0 =A0 =A0 =A0 =A0(?<closed_MDY> \d\d?\/ =A0\d\d?\/ =A0\d\d? =A0)\s+
=A0 =A0 =A0 =A0 =A0(?<proceeds> =A0 [\$\d,\.]+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 )\s+
=A0 =A0 =A0 =A0 =A0(?<cost> =A0 =A0 =A0 [\$\d,\.]+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 )\s+
=A0 =A0 =A0 =A0 =A0(?<gain_Loss> =A0[\(\)\$,\d \.]+ =A0 =A0 =A0 =A0 =A0)\= s*
=A0 =A0 =A0 =A0$/x
result =3D regex.match(line)

or for older reg engines, they can do parallel assignments w array or splat

something like

_,:name,:n_shares,:eek:pened =3D regex.match(line).to_a

or

_,:name,:n_shares,:eek:pened =3D *regex.match(line)

best regards -botp
 
R

RichardOnRails

[Note:  parts of this message were removed to make it a legal post.]

I've got the following hash set up and it works fine.  But this took
more typing than programming.
       h = {:name=>$1,  :n_shares=>$2,  :eek:pened_MDY=>$3,  :closed_MDY=>$4,
               :proceeds=>$5,  :cost=> $6,   :gain_Loss=>$7
             }
So I'd like to get the following to work:
       field_names =%w<Name Shares Opened_MDY Closed_MDY Proceeds Cost
GainLoss>
       h = {}
       (1..field_names.size).each { |i| h[field_names] = eval($ +
i.to_s) }

But concatenating the character "$" with the value of i converted to a
string, followed be evaluating that as a local variable isn't working
as I coded it.  Can it be easily corrected?
Thanks in Advance,
Richard

The matchdata object implements the same inteface as a hash. If that is all
you need, on 1.9 you could just name the capture groups, then use the
MatchData object and not have to do any assignment/creation at all.
(Assuming this data is based on the pastie you posted earlier, this code
would work)

RUBY_VERSION # => "1.9.2"

line = "Apple 1.1234 1/2/3 4/5/6 $1,234.56 $7 ($5)"

regex = /^(?<name>       .+                       )\s
          (?<n_shares>   \d+\.\d{4}               )\s+
          (?<opened_MDY> \d\d?\/  \d\d?\/  \d\d?  )\s+
          (?<closed_MDY> \d\d?\/  \d\d?\/  \d\d?  )\s+
          (?<proceeds>   [\$\d,\.]+               )\s+
          (?<cost>       [\$\d,\.]+               )\s+
          (?<gain_Loss>  [\(\)\$,\d \.]+          )\s*
        $/x

result = regex.match(line)
result                      # => #<MatchData "Apple 1.1234 1/2/3 4/5/6
$1,234.56 $7 ($5)" name:"Apple" n_shares:"1.1234" opened_MDY:"1/2/3"
closed_MDY:"4/5/6" proceeds:"$1,234.56" cost:"$7" gain_Loss:"($5)">
result[ :name       ]       # => "Apple"
result[ :n_shares   ]       # => "1.1234"
result[ :eek:pened_MDY ]       # => "1/2/3"
result[ :closed_MDY ]       # => "4/5/6"
result[ :proceeds   ]       # => "$1,234.56"
result[ :cost       ]       # => "$7"
result[ :gain_Loss  ]       # => "($5)"


Hi Josh,

Wow! I posted this question ~ midnight and shutdown for the night,
except for re-reading the Reflections chapter in Pragmatic's
"Programming Ruby". 2nd ed.

Got up at 9, expecting to tackle this problem again, but first opened
this thread hopefully, and was rewarded with a tutorial on part of
MatchData applicable to my goal. That's too good to be true.
The matchdata object implements the same inteface as a hash. If that is all
you need ...
That is precisely all I need.
on 1.9 ...
I've been running 1.8.6 for a good while. I've been tempted to
advance to to 1.9.x in get Look Ahead or Look Behind in regex's,
whichever in missing in 1.8.6. But now I've got a definite purpose to
upgrade!

Thank you very much for your excellent solution. I'm burdened this
problem because the IRS wants tax reports for past years when I lost a
little money as a day trader in the stock market. You might be
surprised at how many trades you can amass in a year if you're at it
almost every weekday each year. The IRS has instituted a new rule
which took effect this year: brokerages must report not merely
proceeds from sales, but also the related cost and hence the gain or
loss. Duh! Hopefully, the new rule will preclude the recurrence of
this issue.

Best wishes,
Richard
An innocent citizen :)

I won't need to beg for any more help for a good while, I hope.

Best wish
 
R

RichardOnRails

eval('$' + i.to_s)

or simply:

eval("$#{i}")

Hi Su Zhang,

That failure was annoying me greatly. Thanks for restoring my sanity.

I'm sticking with option 1. It's easier to interpret at a glance
IMHO. But thanks for pointing out the more succinct alternative.

Best wishes,
Richard
 
R

RichardOnRails

On Sun, Jan 16, 2011 at 11:30 PM, RichardOnRails <
The matchdata object implements the same inteface as a hash. If that isall
you need, on 1.9 you could just name the capture groups, then use the
MatchData object and not have to do any assignment/creation at all.
(Assuming this data is based on the pastie you posted earlier, this code
would work)
RUBY_VERSION # => "1.9.2"
line = "Apple 1.1234 1/2/3 4/5/6 $1,234.56 $7 ($5)"
regex = /^(?<name>       .+                      )\s
         (?<n_shares>   \d+\.\d{4}               )\s+
         (?<opened_MDY> \d\d?\/  \d\d?\/  \d\d?  )\s+
         (?<closed_MDY> \d\d?\/  \d\d?\/  \d\d?  )\s+
         (?<proceeds>   [\$\d,\.]+               )\s+
         (?<cost>       [\$\d,\.]+               )\s+
         (?<gain_Loss>  [\(\)\$,\d \.]+          )\s*
       $/x
result = regex.match(line)

or for older reg engines, they can do parallel assignments w array or splat

something like

_,:name,:n_shares,:eek:pened = regex.match(line).to_a

or

_,:name,:n_shares,:eek:pened = *regex.match(line)

best regards -botp

Hi botp,

Thanks for that very interesting approach. It's beautifully
succinct. I tried running it on my WinXP-Pro/SP3 machine running Ruby
1.8.6 and was unsuccessful in running your example. I probably
overlooked some detail, for which I apologize.

Here's what I ran:
_, :name, :shares, :eek:pened_MDY, :closed_MDY, :proceeds, :cost, :gainLoss
= regex.match(line).to_a

I got:
ProcessTaxData.rb:116: syntax error, unexpected ',', expecting tCOLON2
or '[' or '.'

_, :name, :shares, :eek:pened_MDY, :closed_MDY, :proceeds, :cost, :gainLoss
= regex.match(line).to_a
^

I ran this in SciTE 1.74, where the output is rendered in monotype
font, I believe. So SciTE actually displayed the "^" under the "m" of
the ":name" symbol, despite the rendering above.

While the symbols didn't work, using local variables did in this
simple test:
string = "abc 123x45"
regex = /(\w+)\s*(\d+)/

puts "Test 1"
_, @x, @y = regex.match(string).to_a
puts @x, @y # => abc and 123 on successive lines

Do you have a working example with symbols you could provide me? And
what version of Ruby ran it?

Again, thanks for helping with my Ruby education.

Best wishes,
Richard
 
R

Robert Klemme

Do you have a working example with symbols you could provide me? =A0And
what version of Ruby ran it?

Basically you need a mapping from symbols to group indexes to make
named groups work in 1.8. There are several ways you can do that

1. manual

names =3D [:foo, :bar, :baz]
...
md =3D rx.match str
puts md[names.index:)foo) + 1]

2. semi automatic

names =3D [:foo, :bar, :baz]

def names.get(md, name)
md[names.index:)foo) + 1]
end
...
md =3D rx.match str
puts names.get(md, :foo)

3. or even

class MatchData
attr_accessor :names

def get(name)
self[names.index(name) + 1]
end
end
...
md =3D rx.match str
md.names =3D names
puts md.get:)foo)

Further variants are possible. But I'd switch to 1.9 soon - because
it's faster and has a better regexp engine with named captures.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
R

RichardOnRails

Do you have a working example with symbols you could provide me?  And
what version of Ruby ran it?

Basically you need a mapping from symbols to group indexes to make
named groups work in 1.8.  There are several ways you can do that

1. manual

names = [:foo, :bar, :baz]
..
md = rx.match str
puts md[names.index:)foo) + 1]

2. semi automatic

names = [:foo, :bar, :baz]

def names.get(md, name)
  md[names.index:)foo) + 1]
end
..
md = rx.match str
puts names.get(md, :foo)

3. or even

class MatchData
  attr_accessor :names

  def get(name)
    self[names.index(name) + 1]
  end
end
..
md = rx.match str
md.names = names
puts md.get:)foo)

Further variants are possible.  But I'd switch to 1.9 soon - because
it's faster and has a better regexp engine with named captures.

Kind regards

robert

Hi Robert,

Thanks for your response. As usual, it seems to me, your response
was thorough.
Further variants are possible. But I'd switch to 1.9 soon - because
it's faster and has a better regexp engine with named captures.

Another responder pointed out the advantage of upgrading to 1.9,
which I will do in a couple of weeks.

My original wish was to use the values from a regex match ($1, $2 ...)
without explicitly listing those symbols in an expression. Rather I
wanted to extract the values from them in a loop. Having done some
Ruby meta-programming in past, but suffering from an increasingly
poor memory in my dotage, I jotted down "eval($ + i.to_s)", which
failed.

Su Zhang on this thread corrected my error: "put the & in quotes",
along with alternatives. Problem solved.

Happily, I received a number of other approaches, which I saved
because I love to employ programmatic ways to avoid repetitive program
code. And now I have the good fortune of getting a bunch of clever
alternatives from you, which I will store with my other gifts.

Since my original problem has been solved, I'm going to keep on
pushing my project forward. But I treasure these gifts and promise to
employ them in my continuation education in art of Ruby programming.

Best wishes,
Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top