Quick Tip #29: Flip-flopping junctions

The flip flop operator isn’t just something for politicians and nervous interface designers. It’s a feature designed to turn on and off to set a window of processing.

Suppose I want to see all the words between two words in /usr/share/dict/words (your distro may place it elsewhere). I want to extract a window of lines from a big text.

The ff operator is False until its lefthand expression is true, and then True until its righthand expression is True. After that, it’s false. So, it turns on when the left side becomes true and turns off when the right side does.

Here’s a program that reads lines from a file and prints the ones between the two words I specify:

sub MAIN (
	Str :$file  = '/usr/share/dict/words',
	Str :$first!,
	Str :$last!,
	) {
	my $fh = open( $file );

	for $fh.lines -> $line {
		say $line if $line ~~ $first ff $line ~~ $last; # flip-flop
		}
	}

It’s up to me to ensure that the first value is less than the last one, and I’m going to ignore matters of case for this example:

$ perl6 words.p6 --first=fox --last=hound
fox
foxbane
foxberry
foxchop
...
Houghton
hounce
hound

I can specify the same word for the start and end. When the particular word shows up, the flip-flop evaluates the lefthand side. Since it’s the same word, the flip-flop turns on.

$ perl6 words.p6 --first=fox --last=fox
fox

I still have to read all the lines in this example, but there it is.

The flip-flop has inclusive and exclusive versions, just like the ranges and sequences I wrote about in Quick Tip #3: Inclusive and Exclusive. I can exclude either endpoint with a ^:

sub MAIN (
	Str :$file  = '/usr/share/dict/words',
	Str :$first!,
	Str :$last!,
	) {
	my $fh = open( $file );

	for $fh.lines -> $line {
		say $line if $line ~~ $first ^ff^ $line ~~ $last; # changed!
		}
	}

Now I don’t see “fox” or “hound” in the output:

$ perl6 words.p6 --first=fox --last=hound
foxbane
foxberry
foxchop
...
Houghton
hounce

In that example I excluded both endpoints with ^ff^, but I can exclude either side on their own with ^ff or ff^.

But, I don’t have to be satisfied with one window. I can put Junctions into $first and $last to make multiple windows. Any of the values in $first can turn it on and any of the values in $last can turn it off.

sub MAIN (
	Str :$file  = '/usr/share/dict/words',
	) {
	my $fh = open( $file );

	my $first = any( <cat fox> );
	my $last  = any( <dog hound> );

	for $fh.lines -> $line {
		say $line if $line ~~ $first ff $line ~~ $last;
		}
	}

Now I see that it turns off at “dog” but turns on again at “fox”:

cat
catabaptist
catabases
...
doffer
doftberry
dog
fox
foxbane
foxberry
...
houghmagandy
Houghton
hounce

If you’re used to the sed version of flip-flopping, there’s also fff. This one doesn’t evaluate both sides for the same element.

Leave a Reply

Your email address will not be published. Required fields are marked *