What Is a Flip-Flop Operator

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



Leave a reply



Submit