Perl to Ruby: regex captures to assignment

Hello all,

I am converting a Perl program to Ruby in order to learn Ruby. There is
an expression that takes a string, ("1 3/4" or "5", for example)
determines what kind of fraction was entered, and assigns variables (w,
n, d), appropriately. If a whole number was entered, then it assigns
the variables differently, of course.

The expression first checks for the presence of a slash ("/") to
determine if it is a fraction or whole number. Then, using the
conditional operator, assigns the variables, appropriately.

Here are the two expressions:

Perl:

    my ($w, $n, $d) = ( $frac_str =~ /\// )
            ? $frac_str =~ /(?:(\S+) )??(\S+)\/(\S+)/
            : ( 1, $frac_str, 0 );

Ruby:

    w, n, d = frac_str.match(/\//) \
        ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
        : 0, frac_str, 1
    puts "w: #{w}, n: #{n}, d: #{d}" # check assignment

I used irb to test the regex and it works just fine:

exp = Regexp.new(/(?:(\S+) )*?(\S+)\/(\S+)/)
=> /(?:(\S+) )*?(\S+)\/(\S+)/

str = "1 3/4"
=> "1 3/4"

n, w, d = str.match(exp).captures
=> ["1", "3", "4"]

w
=> "3"

n
=> "1"

d
=> "4"

But, the above version outputs:

w: 134, n: 1 3/4, d: 1

It appears to recurse over the regex and duplicate the captures. But,
since I am new to this language, I know I can use an 'if..else'
statement, but since I am coming from Perl, I figured that a similar
expression would "just work"! :slight_smile:

Thanks for your time and input,

Derrick

···

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

As it is, the parser is interpreting the ternary operation as the first
item in the list, like:

    tmp = frac_str.match(/\//) \
      ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
      : 0
    w, n, d = tmp, frac_str, d

Subsequently puts(tmp) is calling Array#to_s which concatenates the
elements, so you see "134"

I got it to work by turning the third parameter of the ternary operation
into an explicit array:

    w, n, d = frac_str.match(/\//) \
      ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
      : [0, frac_str, 1] # <= !!!
    puts "w: #{w}, n: #{n}, d: #{d}" # check assignment

Outputs: w: 1, n: 3, d: 4

···

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd

Try this:

w, n, d = frac_str =~ %r{/} \
? frac_str.match(%r{(?:(\S+) )*?(\S+)/(\S+)}).captures \
: [1, frac_str, 0]

I've use %r{} to quote regex, so no need to escape for /

···

On Thu, Dec 20, 2012 at 8:45 AM, Derrick B. <lists@ruby-forum.com> wrote:

Hello all,

I am converting a Perl program to Ruby in order to learn Ruby. There is
an expression that takes a string, ("1 3/4" or "5", for example)
determines what kind of fraction was entered, and assigns variables (w,
n, d), appropriately. If a whole number was entered, then it assigns
the variables differently, of course.

The expression first checks for the presence of a slash ("/") to
determine if it is a fraction or whole number. Then, using the
conditional operator, assigns the variables, appropriately.

Here are the two expressions:

Perl:

    my ($w, $n, $d) = ( $frac_str =~ /\// )
            ? $frac_str =~ /(?:(\S+) )??(\S+)\/(\S+)/
            : ( 1, $frac_str, 0 );

Ruby:

    w, n, d = frac_str.match(/\//) \
        ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
        : 0, frac_str, 1
    puts "w: #{w}, n: #{n}, d: #{d}" # check assignment

I used irb to test the regex and it works just fine:

exp = Regexp.new(/(?:(\S+) )*?(\S+)\/(\S+)/)
=> /(?:(\S+) )*?(\S+)\/(\S+)/

str = "1 3/4"
=> "1 3/4"

n, w, d = str.match(exp).captures
=> ["1", "3", "4"]

w
=> "3"

n
=> "1"

d
=> "4"

But, the above version outputs:

w: 134, n: 1 3/4, d: 1

It appears to recurse over the regex and duplicate the captures. But,
since I am new to this language, I know I can use an 'if..else'
statement, but since I am coming from Perl, I figured that a similar
expression would "just work"! :slight_smile:

Thanks for your time and input,

Derrick

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

--
Regards,
Wei Feng

03 9005 3441 | M 0413 658 250 | windix@gmail.com | http://wei.feng.id.au

frac_str = "1 3/4"

w, n, d = frac_str.match(/\//) \
  ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
  : [0, frac_str, 1]

puts "w: #{w}, n: #{n}, d: #{d}" # check assignment

--output:--
w: 1, n: 3, d: 4

frac_str = "1 3/4"

md = frac_str.match %r{(\d+ )?(\d+)/(\d+)}
w, n, d = md ? md.captures : [frac_str.to_i, 0, 1]

puts "w: #{w}, n: #{n}, d: #{d}" # check assignment

--output:--
w: 1 , n: 3, d: 4

···

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

First of all, thanks for the fast responses!

Secondly, I swear I tried bracketing the false side of the conditional
(pg 98, section 4.5.5.3 - "The Ruby Programming Language" book), but I
remember it assigning the array to the first variable - w.

In Perl, there are times when "those brackety thingies" are not
necessary, but as a programmer, I like readabiliy (yes, still talking
about Perl :slight_smile: ) and will include them when it just makes sense.

Thanks all!

···

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

how about this? use the magic of =~ to assign the local variables

w, n, d = 0, frac_str, 1 unless /(?:(?<w>\S+) )*?(?<n>\S+)\/(?<d>\S+)/
=~ frac_str

···

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

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

Right. I would try to capture it out front so that I wouldn't have to
repeat myself.

···

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

I got the "??" from Perl, but it seems to not be a problem in Ruby.

I don't see how it is pertinent to your regex.

···

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

dbuckhal what about this?

w,n,d = 1,frac,0 unless
%r{(?:(?<w>[-]?\d+)\s*)??(?<n>[-]?\d+)/(?<d>\d+)}xms =~ frac
w,n,d = w.to_i,n.to_i,d.to_i

with that you do not need the $vars

···

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

Also, I am still trying to wrap my head around the line terminating
error I showed you.

Explain in detail what do you think a \ at the end of the line does?

···

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

an official definition of a newline

Inside a ruby program, a newline is defined to be the string: "\n".

···

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

Sorry for replying to myself. I just noticed that the Perl code also had
an explicit array,

     : ( 1, $frac_str, 0 )

Those brackety thingies are important, no matter which language you're
using. :wink:

···

On 20 December 2012 09:07, Matthew Kerwin <matthew@kerwin.net.au> wrote:

As it is, the parser is interpreting the ternary operation as the first
item in the list, like:

    tmp = frac_str.match(/\//) \
      ? frac_str.match(/(?:(\S+) )*?(\S+)\/(\S+)/).captures \
      : 0
    w, n, d = tmp, frac_str, d

Subsequently puts(tmp) is calling Array#to_s which concatenates the
elements, so you see "134"

Matthew Kerwin wrote in post #1089658:

···

On 20 December 2012 09:07, Matthew Kerwin <matthew@kerwin.net.au> wrote:

Sorry for replying to myself. I just noticed that the Perl code also
had
an explicit array,

     : ( 1, $frac_str, 0 )

That isn't an array in perl.

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

First of all, thanks for the fast responses!

Secondly, I swear I tried bracketing the false side of the conditional
(pg 98, section 4.5.5.3 - "The Ruby Programming Language" book), but I
remember it assigning the array to the first variable - w.

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.

In Perl, there are times when "those brackety thingies" are not
necessary, but as a programmer, I like readabiliy (yes, still talking
about Perl :slight_smile: ) and will include them when it just makes sense.

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

Cheers

robert

···

On Thu, Dec 20, 2012 at 1:38 AM, 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 #1089801:

By the way, if you haven't played with Rubular, you
should
give it a try:

http://rubular.com/

I have found that. Good tool to use.

Also, I am still trying to wrap my head around the line terminating
error I showed you. I want to try other scenarios besides conditional
operators to see what happens.

Lastly, I reduced my assignment statement to two lines:

    %r{(?:([-]?\d+)\s*)??([-]?\d+)(/)(\d+)}xms =~ frac
    w, n, d = $3 ? [$1.to_i, $2.to_i, $4.to_i] : [1, frac.to_i, 0]

... which adds the slash check, rather than using a separate regex.
Probably still "weird", but that's ok. :slight_smile:

I think I need to start a new thread with new material. 20+ entries is
probably enough. For now, I'm on chapter nine of "The Ruby Programming
Language", which is an in-depth tour of Ruby syntax. Good stuff.

···

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

It allows one to continue a line that would otherwise be terminated. Contrived example:

a = 5
+ 2
puts a
# => 5

a = 5 \
+ 2
puts a
# => 7

It wasn't necessary in OP's example, but I also think it should work there.

···

On Sat, 22 Dec 2012 08:40:58 +0100, 7stud -- <lists@ruby-forum.com> wrote:

Explain in detail what do you think a \ at the end of the line does?

--
Matma Rex

7stud -- wrote in post #1089925:

Also, I am still trying to wrap my head around the line terminating
error I showed you.

Explain in detail what you think a \ at the end of the line does? Well,
maybe we need to get more basic than that: Do you know what a newline
is?

My understanding, from a C/C++ background, is that the '\' allows for
continuation of the current line. Combine that with my knowledge of
comments (// and /* */ in C/C++) should allow for anything following
that to be "ignored". Perl does not behave as Ruby did when I did the
very same thing. So, maybe I took my assumption of those to personal
ideas and definitions and expected too much from Ruby?

I so also understand that the '\' is an escape character to allow for
various things to happen: newlines, tabs, characters to have different
meanings in strings, etc...

So, please enlighten me as to your definition, or a link to an official
definition of a newline, line continuation indicator, and anything else
that would be useful.

Thanks,

Derrick

···

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

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. I'm not sure if this is a problem with Gmail or
ruby-forum.com or the list.

For instance, this is all I got for 7stud's most recent message:

an official definition of a newline

Inside a ruby program, a newline is defined to be the string: "\n".

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

But this shows up at <Perl to Ruby: regex captures to assignment - Ruby - Ruby-Forum

···

On Sat, Dec 22, 2012 at 4:52 PM, 7stud -- <lists@ruby-forum.com> wrote:

Re: Perl to Ruby: regex captures to assignment.
Posted by 7stud -- (7stud) on 2012-12-22 23:52
> an official definition of a newline

Inside a ruby program, a newline is defined to be the string: "\n".

However, anytime you hit <RETURN> in your text editor an invisible
newline is entered into the text you are typing. All text is just one
long continuous string of characters--where some of the characters may
be spaces, tabs, or newlines. But text editors do something special
when
they display a newline--they skip down to the next line to display the
text that follows a newline.

7stud -- wrote in post #1089965:

an official definition of a newline

Inside a ruby program, a newline is defined to be the string: "\n".

However, anytime you hit <RETURN> in your text editor an invisible
newline is entered into the text you are typing. All text is just one
long continuous string of characters--where some of the characters may
be spaces, tabs, or newlines. But text editors do something special
when
they display a newline--they skip down to the next line to display the
text that follows a newline.

Thanks, but I am no longer trying to wrap my head around it. I tested
it in some C++ and Perl code, did not work, so I'm fine with proven
failed results. I only began this query because I did not understand
why Ruby would not allow a conditional to span the lines as such before
I even introduced comments:

    n = ( w == w.abs ) \
        ? n + w * d
        : -1 * ( n - w * d ) if d != 0

...without putting a durn slash in the first line. Without it, I get:

./ratnum.rb:17: warning: invalid character syntax; use ?\s
./ratnum.rb:17: syntax error, unexpected '?', expecting keyword_end
        ? n + w * d ...
         ^
./ratnum.rb:18: syntax error, unexpected ':', expecting keyword_end
        : -1 * ( n - w * d ) if d != 0...
         ^
I do understand that line termination, or an optional semi-colon,
completes a statement in Ruby.

Perl does just fine without the slash:

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

I appreciate your effort, but am ready to move on to something else to
talk about. heh

···

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

7stud -- wrote in post #1089660:

Matthew Kerwin wrote in post #1089658:

Sorry for replying to myself. I just noticed that the Perl code also
had
an explicit array,

     : ( 1, $frac_str, 0 )

That isn't an array in perl--it's a list. The direct translation from
perl to ruby doesn't work in this case.

Semantically, and in the context, a Perl list is analogous with a ruby
array. Since the languages are syntactically different (i.e. ruby
doesn't allow delimited lists within a statement) and transliteration
is, as you say, impossible, the most direct translation from a Perl list
is to a ruby array.

If I, as a ruby programmer, when talking to a Perl programmer, say
"array" when clearly the syntax includes round parentheses, I'm fairly
confident they should be able to understand my intention.

If my slip was really that offensive, let me retract and rephrase as:

Sorry for replying to myself. I just noticed that the Perl code also
had an explicit /list/,

Irrespective, it is still true that "those brackety thingies are
important, no matter which language you're using."

···

On 20 December 2012 09:07, Matthew Kerwin <matthew@kerwin.net.au> wrote:

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