On Ranges as conditions

  • Thread starter Rafael Cunha de Almeida
  • Start date
R

Rafael Cunha de Almeida

Reading a few tutorials I found this piece of code:

while input = gets
input = input.chomp
puts input + " triggered" if input =~ /start/ .. input =~ /end/
end

I understand what it does. If I type start, end or anything in between
it prints
"triggered" after the input. Now, if I'm out of the start .. end block,
the if
fails.

What I don't understand is how it does it. For instance, if I use the
interpreter to do something like this:

irb(main):038:0> input = 'fooo'
=> "fooo"
irb(main):039:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
=> nil
irb(main):040:0> input = 'start'
=> "start"
irb(main):041:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
start triggered
=> nil
irb(main):042:0> input = 'bar'
=> "bar"
irb(main):043:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
=> nil

it doesn't work as I'd expect. I thought the if clause there would
somehow set a
global to say "the first regex was matched" or something like that. But it
doesn't seem to be the way it works. It only seems to work inside of a while
loop. I thought it might have something to do with the $_ global. But I
made a
test in the interpreter which ruled out that theory:

irb(main):045:0> $_ = 'foo'
=> "foo"
irb(main):046:0> input = 'foo'
=> "foo"
irb(main):047:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
=> nil
irb(main):048:0> $_ = input = 'start'
=> "start"
irb(main):049:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
start triggered
=> nil
irb(main):050:0> $_ = input = 'foo'
=> "foo"
irb(main):051:0> puts input + " triggered" if input =~ /start/ .. input
=~ /end/
=> nil
irb(main):052:0>

Can anyone help me find out what's going on?
 
R

Rick DeNatale

Reading a few tutorials I found this piece of code:

=A0 =A0 =A0 =A0while input =3D gets
=A0 =A0 =A0 =A0 =A0 =A0input =3D input.chomp
=A0 =A0 =A0 =A0 =A0 =A0puts input + " triggered" if input =3D~ /start/ ..= input =3D~ /end/
=A0 =A0 =A0 =A0end

I understand what it does. If I type start, end or anything in between
it prints
"triggered" after the input. Now, if I'm out of the start .. end block,
the if
fails.

What I don't understand is how it does it. For instance, if I use the
interpreter to do something like this:

=A0 =A0 =A0 =A0irb(main):038:0> input =3D 'fooo'
=A0 =A0 =A0 =A0=3D> "fooo"
=A0 =A0 =A0 =A0irb(main):039:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0=3D> nil
=A0 =A0 =A0 =A0irb(main):040:0> input =3D 'start'
=A0 =A0 =A0 =A0=3D> "start"
=A0 =A0 =A0 =A0irb(main):041:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0start triggered
=A0 =A0 =A0 =A0=3D> nil
=A0 =A0 =A0 =A0irb(main):042:0> input =3D 'bar'
=A0 =A0 =A0 =A0=3D> "bar"
=A0 =A0 =A0 =A0irb(main):043:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0=3D> nil

it doesn't work as I'd expect. I thought the if clause there would
somehow set a
global to say "the first regex was matched" or something like that. But i= t
doesn't seem to be the way it works. It only seems to work inside of a wh= ile
loop. I thought it might have something to do with the $_ global. But I
made a
test in the interpreter which ruled out that theory:

=A0 =A0 =A0 =A0irb(main):045:0> $_ =3D 'foo'
=A0 =A0 =A0 =A0=3D> "foo"
=A0 =A0 =A0 =A0irb(main):046:0> input =3D 'foo'
=A0 =A0 =A0 =A0=3D> "foo"
=A0 =A0 =A0 =A0irb(main):047:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0=3D> nil
=A0 =A0 =A0 =A0irb(main):048:0> $_ =3D input =3D 'start'
=A0 =A0 =A0 =A0=3D> "start"
=A0 =A0 =A0 =A0irb(main):049:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0start triggered
=A0 =A0 =A0 =A0=3D> nil
=A0 =A0 =A0 =A0irb(main):050:0> $_ =3D input =3D 'foo'
=A0 =A0 =A0 =A0=3D> "foo"
=A0 =A0 =A0 =A0irb(main):051:0> puts input + " triggered" if input =3D~ /= start/ .. input
=3D~ /end/
=A0 =A0 =A0 =A0=3D> nil
=A0 =A0 =A0 =A0irb(main):052:0>

Can anyone help me find out what's going on?

As far as I can tell, from reading the 1.8 parse.c code, the problem
is that conditional ranges really aren't Range instances but are
really a kind of syntactic sugar. Every time you retype the source
line into irb, you get an entirely new parse, and a new conditional
range in the initial state.


--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
M

Markus Roberts

It isn't a range, it's a "flip-flop" which happens to use the same
operator ('..') as range construction.

While useful in some sed/awk like scripts it's not a widely used
feature.

-- MarkusQ
 
J

Josh Cheek

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

Reading a few tutorials I found this piece of code:

while input = gets
input = input.chomp
puts input + " triggered" if input =~ /start/ .. input =~ /end/
end

I understand what it does. If I type start, end or anything in between
it prints
"triggered" after the input. Now, if I'm out of the start .. end block,
the if
fails.

What I don't understand is how it does it. For instance, if I use the
interpreter to do something like this:

irb(main):038:0> input = 'fooo'
=> "fooo"
irb(main):039:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
=> nil
irb(main):040:0> input = 'start'
=> "start"
irb(main):041:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
start triggered
=> nil
irb(main):042:0> input = 'bar'
=> "bar"
irb(main):043:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
=> nil

it doesn't work as I'd expect. I thought the if clause there would
somehow set a
global to say "the first regex was matched" or something like that. But it
doesn't seem to be the way it works. It only seems to work inside of a
while
loop. I thought it might have something to do with the $_ global. But I
made a
test in the interpreter which ruled out that theory:

irb(main):045:0> $_ = 'foo'
=> "foo"
irb(main):046:0> input = 'foo'
=> "foo"
irb(main):047:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
=> nil
irb(main):048:0> $_ = input = 'start'
=> "start"
irb(main):049:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
start triggered
=> nil
irb(main):050:0> $_ = input = 'foo'
=> "foo"
irb(main):051:0> puts input + " triggered" if input =~ /start/ ..
input
=~ /end/
=> nil
irb(main):052:0>

Can anyone help me find out what's going on?
# (this entire response can be directly placed into a script -- not the
above quote, though)
#
# The first condition turns it on (causes it to evaluate to true)
# the second turns it off (causes it to evaluate back to false).
# In this example, you would get the output
#
# start triggered
# instruction2 triggered
# instruction3 triggered
# end triggered
# start triggered
# instruction6 triggered
# instruction7 triggered
# instruction8 triggered
# end triggered
#
# Give it a try

$stdin = DATA

while input = gets
input = input.chomp
puts input + " triggered" if input =~ /start/ .. input =~ /end/
end

__END__
instruction0
instruction1
start
instruction2
instruction3
end
instruction4
instruction5
start
instruction6
instruction7
instruction8
end
instruction9
 
X

Xavier Noria

It isn't a range, it's a "flip-flop" which happens to use the same operator ('..') as range construction.

While useful in some sed/awk like scripts it's not a widely used feature.

Well, it is used as often as you find the problem it solves, no matter
the language.
 
K

Ken Bloom

As far as I can tell, from reading the 1.8 parse.c code, the problem is
that conditional ranges really aren't Range instances but are really a
kind of syntactic sugar. Every time you retype the source line into
irb, you get an entirely new parse, and a new conditional range in the
initial state.

Pretty cool! Thanks for pointing this syntax out! I don't recall seeing
it in Pickaxe.

One question:

irb(main):013:0> a=%w{foo start bar end baz start foo bar end baz}
=> ["foo", "start", "bar", "end", "baz", "start", "foo", "bar", "end",
"baz"]
irb(main):014:0> a.each {|i| puts i + (if i =~ /start/ .. i =~ /end/ then
" triggered" else "" end) }
foo
start triggered
bar triggered
end triggered
baz
start triggered
foo triggered
bar triggered
end triggered
baz
=> ["foo", "start", "bar", "end", "baz", "start", "foo", "bar", "end",
"baz"]
irb(main):015:0> a.each {|i| puts i + (if i =~ /start/ ... i =~ /end/
then " triggered" else "" end) }
foo
start triggered
bar triggered
end triggered
baz
start triggered
foo triggered
bar triggered
end triggered
baz

Shouldn't the last version (using ... instead of ..) logically omit the
end line from the triggered section?

--Ken
 
C

Colin Bartlett

Pretty cool! Thanks for pointing this syntax out! I don't recall seeing
it in Pickaxe.
It's definitely in the first (2001) edition as "Ranges as conditions":
_A range used in a boolean expression acts as a flip-flop.
_ It has two states, set and unset, and is initially unset.
_ On each call, the range cycles through the state machine ...
_ The range returns true if it is in the set state at the end of the call,
_ and false otherwise.
_The two-dot form of a range behaves slightly differently than the
three-dot form.
_ When the two-dot form first makes the transition from unset to set,
_it immediately evaluates the end condition and makes the transition
accordingly.
_This means that if expr1 and expr2 both evaluate to true on the same call,
_the two-dot form will finish the call in the unset state.
_However, it still returns true for this call.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top