What is a flip-flop operator?
The flip-flop operator in Perl evaluates to true when the left operand is true, and keeps evaluating to true until the right operand is true. The left and right operand could be any kind of expression, but most often it is used with regexes.
With regexes, it is useful for finding all the lines between two markers. Here is a simple example that shows how it works:
use Modern::Perl;
while (<DATA>)
{
if (/start/ .. /end/)
{
say "flip flop true: $_";
}
else
{
say "flip flop false: $_";
}
}
__DATA__
foo
bar
start
inside
blah
this is the end
baz
The flip flop operator will be true for all lines from start
until this is the end
.
The two dot version of the operator allows first and second regex to both match on the same line. So, if your data looked like this, the above program would only be true for the line start blah end
:
foo
bar
start blah end
inside
blah
this is the end
baz
If you don't want the first and second regexes to match the same line, you can use the three dot version: if (/start/ ... /end/)
.
Note that care should be taken not to confuse the flip-flop operator with the range operator. In list context, ..
has an entirely different function: it returns a list of sequential values. e.g.
my @integers = 1 .. 1000; #makes an array of integers from 1 to 1000.
I'm not familiar with Ruby, but Lee Jarvis's link suggests that it works similarly.
flip-flop-operator/readline-counter question
To quote perlop:
In scalar context, "
..
" returns a
boolean value. The operator is
bistable, like a flip-flop, and
emulates the line-range (comma)
operator of sed, awk, and various
editors. Each "..
" operator maintains
its own boolean state, even across
calls to a subroutine that contains
it. It is false as long as its left
operand is false. Once the left
operand is true, the range operator
stays true until the right operand is
true, AFTER which the range operator
becomes false again. It doesn't become
false till the next time the range
operator is evaluated. It can test the
right operand and become false on the
same evaluation it became true (as in
awk), but it still returns true once.
If you don't want it to test the right
operand until the next evaluation, as
in sed, just use three dots ("...
")
instead of two. In all other regards,
"...
" behaves just like "..
" does.The right operand is not evaluated
while the operator is in the "false"
state, and the left operand is not
evaluated while the operator is in the
"true" state. The precedence is a
little lower than||
and&&
. The value
returned is either the empty string
for false, or a sequence number
(beginning with 1) for true. The
sequence number is reset for each
range encountered. The final sequence
number in a range has the string "E0"
appended to it, which doesn't affect
its numeric value, but gives you
something to search for if you want to
exclude the endpoint. You can exclude
the beginning point by waiting for the
sequence number to be greater than 1.If either operand of scalar "
..
" is a
constant expression, that operand is
considered true if it is equal (==
)
to the current input line number (the
$.
variable).
(emphasis added)
The use of flip-flop operator in Perl 6
You need to specify that you want the matched values to not be included. You do this by adding ^
to the side of the operator you want excluded. In this case it is both sides of the operator.
You also need to collect the values up to have the grouped together. The simplest way in this case is to put that off between matches.
(If you wanted the endpoints included it would require more thought to get it right)
my @codelines = gather {
my @current;
for $excerpt.lines {
if "=begin code" ^ff^ "=end code" {
# collect the values between matches
push @current, $_;
} else {
# take the next value between matches
# don't bother if there wasn't any values matched
if @current {
# you must do something so that you aren't
# returning the same instance of the array
take @current.List;
@current = ();
}
}
}
}
If you need the result to be an array of arrays (mutable).
if @current {
take @current;
@current := []; # bind it to a new array
}
An alternative would be to use do for
with sequences that share the same iterator.
This works because for
is more eager than map
would be.
my $iterator = $excerpt.lines.iterator;
my @codelines = do for Seq.new($iterator) {
when "=begin code" {
do for Seq.new($iterator) {
last when "=end code";
$_<> # make sure it is decontainerized
}
}
# add this because `when` will return False if it doesn't match
default { Empty }
}
map
takes one sequence and turns it into another, but doesn't do anything until you try to get the next value from the sequence.for
starts iterating immediately, only stopping when you tell it to.
So map
would cause race conditions even when running on a single thread, but for
won't.
Perl Flip Flop operator and line numbers
The use of a bare number in the flip-flop is treated as a test against the line count variable, $.
. From perldoc perlop
:
If either operand of scalar
".."
is a constant expression, that
operand is considered true if it is equal (==
) to the current input
line number (the$.
variable).
So
print if 5 .. undef;
is "shorthand" for:
print if $. == 5 .. undef;
The same is not true for a scalar variable as it is not a constant expression. This is why it is not tested against $.
.
Is Perl's flip-flop operator bugged? It has global state, how can I reset it?
Can someone clarify what the issue with the documentation is? It clearly indicates:
Each ".." operator maintains its own boolean state.
There is some vagueness there about what "Each" means, but I don't think the documentation would be well served by a complex explanation.
Note that Perl's other iterators (each
or scalar context glob
) can lead to the same problems. Because the state for each
is bound to a particular hash, not a particular bit of code,each
can be reset by calling (even in void context) keys
on the hash. But for glob
or ..
, there is no reset mechanism available except by calling the iterator until it is reset. A sample glob bug:
sub globme {
print "globbing $_[0]:\n";
print "got: ".glob("{$_[0]}")."\n" for 1..2;
}
globme("a,b,c");
globme("d,e,f");
__END__
globbing a,b,c:
got: a
got: b
globbing d,e,f:
got: c
Use of uninitialized value in concatenation (.) or string at - line 3.
got:
For the overly curious, here are some examples where the same .. in the source is a different .. operator:
Separate closures:
sub make_closure {
my $x;
return sub {
$x if 0; # Look, ma, I'm a closure
scalar( $^O..!$^O ); # handy values of true..false that don't trigger ..'s implicit comparison to $.
}
}
print make_closure()->(), make_closure()->();
__END__
11
Comment out the $x if 0
line to see that non-closures have a single .. operation shared by all "copies", with the output being 12
.
Threads:
use threads;
sub coderef { sub { scalar( $^O..!$^O ) } }
coderef()->();
print threads->create( coderef() )->join(), threads->create( coderef() )->join();
__END__
22
Threaded code starts with whatever the state of the .. had been before thread creation, but changes to its state in the thread are isolated from affecting anything else.
Recursion:
sub flopme {
my $recurse = $_[0];
flopme($recurse-1) if $recurse;
print " "x$recurse, scalar( $^O..!$^O ), "\n";
flopme($recurse-1) if $recurse;
}
flopme(2)
__END__
1
1
2
1
3
2
4
Each depth of recursion is a separate .. operator.
Is there a functional programming concept equivalent to the flip-flop operator in Perl or Ruby?
In a functional language such as haskell, you would pass in the flip and flop conditions as predicates, and filter an input list based on it. For example, the following is a definition of flipflop
in haskell (don't worry about the implementation if you don't know haskell - the key part is how it is used):
flipflop flip flop =
uncurry (++) . second (take 1) . break flop . dropWhile (not . flip)
This is how it can be used:
> flipflop (== 3) (== 5) [1..10]
[3,4,5]
It is an example of making an effectively new language construct just by using higher ordered function.
I don't know if there is a special name for that construct in functional languages.
Perl Flip-flop operator - Is it possible to treat the END of first match as START of next match?
The range operator itself cannot be configured to evaluate the left side immediately. But you can try
if (my $r = /^LS\s*SPID\s*ASP\s*SPID$/ ... (/^LS\s*SPID\s*ASP\s*SPID$/ || /^END$/)) {
print "$. \t $_\n";
redo if $r =~ /E0$/;
}
Related Topics
Zlib in Ruby to Uncompress .Gz
Override Ruby Constant in Subclass So Inherited Methods Use New Constant Instead of the Old
Ruby Backslash to Continue String on a New Line
Ruby: Split String at Character, Counting from the Right Side
Setter Method (Assignment) with Multiple Arguments
Problem with Quantifiers and Look-Behind
Ruby: Intersection Between Two Ranges
Ruby Gem Dependencies on Offline Server
How to Validate Xhtml with Nokogiri
What's the Point of Freezing Your Rails Version/Gems
In Ruby, Is There No Way to Dynamically Define a Local Variable in the Current Context
How to Access Nested Elements of a Hash with a Single String Key
Railstutorial - Chapter 8.4.3 - Test Database Not Clearing After Adding User in Integration Test