Perl to Ruby: regex captures to assignment

Robert Klemme wrote in post #1089733:

I would choose a completely different approach: I would have a single
expression for matching and decide which assignments to make based on
the value of one of the capturing groups in the conditional branch:

["1 3/4", "5"].each do |s|
  puts s

  if %r{\A(\d+)(?:\s+(\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [1, $1.to_i, 0]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

I also spiced the regexp a bit more to be more restrictive.

I am prompting for input, so I would be parsing individual strings and
not a list of them. Though I will remember that for future static
testing. Also, I and using the "\S+" to handle negative rational
numbers: "-1 3/4".

If you like readability then why are you using Perl in the first place?
:slight_smile:

Because Perl is awesome! That is why I chose Ruby as my next personal
choice of languages to learn. I just completed a quarter of Programming
Language Concepts where I was introduced to Lisp, but was also required
to use some Python. I know Python is a fascinatng language, but I am
not yet a big fan of it. The final assignment was write a class to work
with fractions. It started as a C++ class, then migrated to Perl (for
fun), and now Ruby (more fun).

I attached the two versions, Ruby and Perl. The Perl version uses a
"class", also. Feel free to suggest how to make the Ruby version more
Ruby-ish! My goal is to take more advantage of the OO aspects of Ruby,
and not just have it look like Perl.

Thanks,

Derrick

Attachments:
http://www.ruby-forum.com/attachment/7988/ratnum.rb
http://www.ruby-forum.com/attachment/7989/ratnum.pl

···

--
Posted via http://www.ruby-forum.com/\.

Derrick B. wrote in post #1089954:

I so also understand that the '\' is an escape character

Okay. Now what does this produce:

"\\n"

···

--
Posted via http://www.ruby-forum.com/\.

Maybe I was exaggerating about it being "all of" them. Now that I've
looked I only see a few.

···

On Sat, Dec 22, 2012 at 7:09 PM, Eric Christopherson <echristopherson@gmail.com> wrote:

I just wanted to point out that all of 7stud's messages (as far as I
know) in this thread have gotten truncated by the time they reached my
Gmail inbox.

Derrick B. wrote in post #1089990:

Perl does just fine without the slash:

    $n = ( $w == abs($w) )
        ? $n + $w * $d
        : -1 * ( $n - $w * $d ) if $d;

And you have no idea why that is? The perl parser knows that a
statement has ended when it sees a semi-colon.

···

--
Posted via http://www.ruby-forum.com/\.

Derrick B. wrote in post #1089748:

I am prompting for input, so I would be parsing individual strings and
not a list of them. Though I will remember that for future static
testing. Also, I and using the "\S+" to handle negative rational
numbers: "-1 3/4".

test_data = [
  "3/4",
  "1 3/4",
  "-1 3/4",
  'hello'
]

test_data.each do |str|
  match_data = str.match(
   %r{
        (
          [-]?
          \d+
        )?
        \s*
        (\d+)
        /
        (\d+)
     }xms
  )

  w, n, d = match_data ? match_data.captures: [str, 0, 1]
  puts "%s %s %s" % [w, n, d]

end

--output:--
3 4
1 3 4
-1 3 4
hello 0 1

···

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #1089733:

I would choose a completely different approach: I would have a single
expression for matching and decide which assignments to make based on
the value of one of the capturing groups in the conditional branch:

["1 3/4", "5"].each do |s|
  puts s

  if %r{\A(\d+)(?:\s+(\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [1, $1.to_i, 0]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

I also spiced the regexp a bit more to be more restrictive.

I am prompting for input, so I would be parsing individual strings and
not a list of them.

That was just for demonstration purposes.

Though I will remember that for future static
testing. Also, I and using the "\S+" to handle negative rational
numbers: "-1 3/4".

There's still a better way to do that then just match everything non
whitespace. How would you parse ":s9d2++*3h43" as a number?

["1 3/4", "5", '-23', '-23 -4/4'].each do |s|
  puts s

  if %r{\A([-+]?\d+)(?:\s+([-+]?\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [1, $1.to_i, 0]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

Btw, did I mention that I find your variable assignment weird? I'd rather do

["1 3/4", "5", '-23', '-23 -4/4'].each do |s|
  puts s

  if %r{\A([-+]?\d+)(?:\s+([-+]?\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [$1.to_i, 0, 1]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

If you like readability then why are you using Perl in the first place?
:slight_smile:

Because Perl is awesome!

Hm... It's been a while that I thought that (but I did actually
once). IMHO Perl has gone over the edge of usefulness long ago.

That is why I chose Ruby as my next personal
choice of languages to learn.

Good choice!

I just completed a quarter of Programming
Language Concepts where I was introduced to Lisp, but was also required
to use some Python. I know Python is a fascinatng language, but I am
not yet a big fan of it. The final assignment was write a class to work
with fractions. It started as a C++ class, then migrated to Perl (for
fun), and now Ruby (more fun).

:slight_smile:

I attached the two versions, Ruby and Perl. The Perl version uses a
"class", also. Feel free to suggest how to make the Ruby version more
Ruby-ish! My goal is to take more advantage of the OO aspects of Ruby,
and not just have it look like Perl.

I don't have the time right now but you should definitively use class
Rational. Also your parsing of the command line could be better:

do
  print "Fraction 1: "
  input = gets or break
  f1 = parse_fraction(input)

  print "Fraction 2: "
  input = gets or break
  f2 = parse_fraction(input)

  print "Command: "
  input = gets or break

  case input
  when "A"
    puts f1 + f2
  when "S"
    puts f1 - f2
...
else
   abort "Invalid command: #{input}"
end

end until cmd == "Q"

puts "Quitting"

You should also not implement #to_string but rather #to_s and then use
string interpolation for constructing your outputs.

Kind regards

robert

···

On Thu, Dec 20, 2012 at 7:23 PM, Derrick B. <lists@ruby-forum.com> wrote:

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

7stud -- wrote in post #1090043:

And you have no idea why that is? The perl parser knows that a
statement has ended when it sees a semi-colon. How does the ruby parser
know when a statement has ended?

Ruby is similar to Python in that it uses line termination, unless you
indicate an implied line continuation.

a = b +
c

implies continuation, whereas

a = b
+ c

does not.

You can also use a semicolon in Ruby to end a statement, but usually
that is when you put multiple statements on one line.

I cannot wait to take the Compiler Design class, because these details
will be exactly what I need to take into consideration when creating my
own language parser.

···

--
Posted via http://www.ruby-forum.com/\.

7stud -- wrote in post #1089755:

   %r{
        (
          [-]?
          \d+
        )?
        \s*
        (\d+)
        /
        (\d+)
     }xms
  )

That works, but it needs another "[-]?" in the second group to handle
negative numerators, or whole numbers:

%r#(?:([-]?\d+) )??([-]?\d+)/(\d+)#xms

...but I should probably add the "\s*" and not expect a single space. I
got the "??" from Perl, but it seems to not be a problem in Ruby.

Lastly, my code I posted did not convert terms to improper fractions,
correctly. Here is the code that works:

Ruby:
    n = ( w == w.abs ) ?
        n + w * d :
        -1 * ( n - w * d ) if d != 0 # to negative improper fraction

Perl:
    $n = ( $w == abs($w) )
        ? $n + $w * $d # to positive improper fraction
        : -1 * ( $n - $w * $d ) if $d; # to negative improper fraction

...which leads me to another question:

Why is it that when I try to add
a comment after a line continuation backslash, Ruby says:

./ratnum.rb:14: syntax error, unexpected $undefined
    n = ( w == w.abs ) ? \ #
                          ^
./ratnum.rb:15: syntax error, unexpected ':', expecting keyword_end

Ruby should respect my need for comments! :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #1089807:

That was just for demonstration purposes.

Understood.

There's still a better way to do that then just match everything non
whitespace. How would you parse ":s9d2++*3h43" as a number?

["1 3/4", "5", '-23', '-23 -4/4'].each do |s|
  puts s

  if %r{\A([-+]?\d+)(?:\s+([-+]?\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [1, $1.to_i, 0]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

I like that, because it introduces to_i during capture, which was what I
was also trying to figure out. I have already modified my regex to
something similar to yours.

Btw, did I mention that I find your variable assignment weird? I'd
rather do

["1 3/4", "5", '-23', '-23 -4/4'].each do |s|
  puts s

  if %r{\A([-+]?\d+)(?:\s+([-+]?\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [$1.to_i, 0, 1]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

I will have to search for "weird" in recent posts. haha! I do not
doubt it, but for my C++ to Perl to Ruby translation, it was
seamless.

I don't have the time right now but you should definitively use class
Rational. Also your parsing of the command line could be better:

do
  print "Fraction 1: "
  input = gets or break
  f1 = parse_fraction(input)

  print "Fraction 2: "
  input = gets or break
  f2 = parse_fraction(input)

  print "Command: "
  input = gets or break

  case input
  when "A"
    puts f1 + f2
  when "S"
    puts f1 - f2
...
else
   abort "Invalid command: #{input}"
end

end until cmd == "Q"

puts "Quitting"

You should also not implement #to_string but rather #to_s and then use
string interpolation for constructing your outputs.

Kind regards

robert

As I stated as an edit in a previous post, this started as an assignment
to create a C++ class that handles fractions, so I then created a Perl
"class", and now a Ruby class. So, using "require rational", or "use
bigrat" in Perl, would have defeated the purpose. I do plan on learning
more about Ruby, and Perl, add-ons.

Thank you all for the input! I really appreciate being able to post
code and have quality responses. I am transitioning from a career
(nearly 20 years) in IT, to going to school to get my BS in CS, to then
continue as a programmer.

I'll try not to be a stranger as long as my code is to be considered
strange. :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.

fractions,...

not to answer your questions, but, i'd probably go a different route ...

eg,

require 'rational'
["1 3/4", "-2 1/2", "5/8", "-1/3", "5", "-5"].map do |mix|
  mix.split.map{|x| Rational(x)}
end

=> [ [(1/1), (3/4)], [(-2/1), (1/2)], [(5/8)], [(-1/3)], [(5/1)],
[(-5/1)] ]

kind regards -botp

···

On Fri, Dec 21, 2012 at 12:59 PM, Derrick B. <lists@ruby-forum.com wrote:

....Lastly, my code I posted did not convert terms to improper

Derrick B. wrote in post #1089779:

Why is it that when I try to add
a comment after a line continuation backslash, Ruby says:

./ratnum.rb:14: syntax error, unexpected $undefined
    n = ( w == w.abs ) ? \ #
                          ^
./ratnum.rb:15: syntax error, unexpected ':', expecting keyword_end

Ruby should respect my need for comments! :slight_smile:

See if you can make sense of this line:

result = true ? \ n #your comment "true value" : "false value"

···

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #1089807:

Btw, did I mention that I find your variable assignment weird? I'd
rather do

["1 3/4", "5", '-23', '-23 -4/4'].each do |s|
  puts s

  if %r{\A([-+]?\d+)(?:\s+([-+]?\d+)/(\d+))?\z} =~ s
    w, n, d = $2 ? [$1.to_i, $2.to_i, $3.to_i] : [$1.to_i, 0, 1]
    printf "%4d %4d %4d\n", w, n, d
  else
    $stderr.puts "No match #{s}"
  end
end

I will have to search for "weird" in recent posts. haha! I do not
doubt it, but for my C++ to Perl to Ruby translation, it was
seamless.

:slight_smile: What I meant by weird was that I would have the first variable
always store the integer number, the second the numerator and the
third the denominator. With your numbering scheme you need to
evaluate the third value against 0 to detect what kind of number you
have. With my scheme you can treat all parsed input equally and you
won't have a division by zero (unless the user entered 0 as last
number).

As I stated as an edit in a previous post, this started as an assignment
to create a C++ class that handles fractions, so I then created a Perl
"class", and now a Ruby class. So, using "require rational", or "use
bigrat" in Perl, would have defeated the purpose. I do plan on learning
more about Ruby, and Perl, add-ons.

Ah, yes of course that makes a whole lot of sense. I must have
forgotten in between.

Btw, I have a blog posting about creating numerical classes in Ruby:
http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html

You might want to read the previous article first:
http://blog.rubybestpractices.com/posts/rklemme/018-Complete_Class.html

Thank you all for the input! I really appreciate being able to post
code and have quality responses. I am transitioning from a career
(nearly 20 years) in IT, to going to school to get my BS in CS, to then
continue as a programmer.

Good! Please make sure that you do not miss the part about algorithms
and data structures. I consider that one of the more important parts
in CS.

I'll try not to be a stranger as long as my code is to be considered
strange. :slight_smile:

:slight_smile:

Kind regards

robert

···

On Fri, Dec 21, 2012 at 7:19 PM, Derrick B. <lists@ruby-forum.com> wrote:

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

Derrick B. wrote in post #1089888:
(...)

As I stated as an edit in a previous post, this started as an assignment
to create a C++ class that handles fractions, so I then created a Perl
"class", and now a Ruby class. So, using "require rational", or "use
bigrat" in Perl, would have defeated the purpose. I do plan on learning
more about Ruby, and Perl, add-ons.

Just to clear things up, since Ruby 1.9 Rational is not a gem nor in
Standard Lib, it is in Ruby core. For instance, this works without
requiring anything:

puts "1/40".to_r * "2/3".to_r #=> 1/60

and botp's code runs without the require 'rational' on 1.9. Nothing
wrong with reinventing a wheel for learning purposes however.

···

--
Posted via http://www.ruby-forum.com/\.

botp wrote in post #1089780:

···

On Fri, Dec 21, 2012 at 12:59 PM, Derrick B. <lists@ruby-forum.com > wrote:

....Lastly, my code I posted did not convert terms to improper

fractions,...

not to answer your questions, but, i'd probably go a different route
...

eg,

require 'rational'
["1 3/4", "-2 1/2", "5/8", "-1/3", "5", "-5"].map do |mix|
  mix.split.map{|x| Rational(x)}
end

=> [ [(1/1), (3/4)], [(-2/1), (1/2)], [(5/8)], [(-1/3)], [(5/1)],
[(-5/1)] ]

kind regards -botp

Interesting! Coming from Perl, I figured there would be a gem to handle
fractions.

Thanks!

--
Posted via http://www.ruby-forum.com/\.

7stud -- wrote in post #1089800:

See if you can make sense of this line:

result = true ? \ n #your comment "true value" : "false value"

which is equivalent to:

result = true ? \ n

I can see that, but it is still odd.

···

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #1089946:

Good! Please make sure that you do not miss the part about algorithms
and data structures. I consider that one of the more important parts
in CS.

Yes, Data Structure out of the way (B+), and Algorithms next quarter,
along with OOP design, Systems Programming, and 15lbs of stress to
accumulate. :smiley:

···

--
Posted via http://www.ruby-forum.com/\.

Derrick B. wrote in post #1089885:

7stud -- wrote in post #1089800:

See if you can make sense of this line:

result = true ? \ n #your comment "true value" : "false value"

which is equivalent to:

result = true ? \ n

I can see that, but it is still odd.

It is not odd. You just don't understand what escaping a newline does.
In my code that is not a \n --- it's a \ followed by a space followed by
an n. That's essentially what escaping a \n does.

···

--
Posted via http://www.ruby-forum.com/\.