The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Bit::FlipFlop - Facilitates the maintainance of one bit of state in Perl programs.

SYNOPSIS

  use Bit::FlipFlop;

  my $f = Bit::FlipFlop->new(set   => sub { /start/ },
                             reset => sub { /end/ });

  for (<INPUT>) {
    $f->test;

    print "**leading edge**\n" if $f->lead_edge;
    print "number ", $f->series, ":  $_" if $f->state;
    print "**trailing edge**\n" if $f->trail_edge;
  }

  # -- or --

  #use Bit::FlipFlop

  my $f;
  for (<INPUT>) {
    $f = /start/ .. /end/;  
    
    print "**lead edge**\n" if $f == 1;
    print "number ", +$f, ":  $_" if $f;
    print "**trailing edge**\n" if $f =~ /E/;
  }
   

DESCRIPTION

Maintaining one bit of state in a program can be a very useful thing. A Bit::FlipFlop does just that. The flip flop can be false (represented by the integer 0) or true (1).

Overview

The initial state of a flip flop is false. It has an opportunity to change state during each call to the test method.

Two callbacks are provided at flip flop construction time, set and reset. One or both of these callbacks may be evaluated when the test method is called. The callbacks are evaluated in scalar context, and their return value is interpreted as a boolean.

While false, the flip flop evaluates the set callback at each call to the test method. If the set callback returns a true value, the flip flop flips state to true. While true, the flip flop evaluates the reset callback when test is called. If reset returns a true value, then the flip flop flops back into the false state.

Edge Conditions and More

The Bit::FlipFlop actually maintains a few bits more state than just one.

When the flip flop changes from false to true, that is called the leading edge. Conversely, when its state falls from true to falls, that is known as the trailing edge. Bit::FlipFlop provides methods to test for these "sub states", lead_edge and trail_edge.

Edges are easily seen when the state of a flip flop is plotted over time:

   true        +-------+        +---+      
               |       |        |   |     
   false  -----+       +--------+   +------
               ^       ^ 
   leading edge         trailing edge
               

Which condition will be tested next?

The next_test method is provided to query the flip flop to see which condition (set or reset) will be queried on the next call to the test method.

Bit::FlipFlop methods

The Bit::FlipFlop class provides an object oriented interface to the flip flop.

new
  $f = Bit::FlipFlop->new(set   => sub {$. == 15},
                          reset => sub {$. == 20},
                          simultaneous_edges => 0);

The new method creates a new Bit::FlipFlop object. A newly created flip flop's state is always false.

The set and reset arguments are required. They are sub refs that are used for callbacks to test for setting or resetting the flip flop.

simultaneous_edges is an optional argument that defaults to 1 (true) if unspecified. This argument governs whether the flip flop may look for a leading edge and trailing edge within a single call to the test method.

Normally when a flip flop is in the false state, it tests for the set condition. Should that condition be true, then the flip flop will change states to true. There are two possible behaviors at this point. The flip flop could either evaluate the reset condition to see if it should switch back to the false state *within the same call to test*, or it could postpone looking at the reset condition until the next call to test.

The former behaviour is specified by simultaneous_edges=>1, which is the default behaviour if left unspecified. To obtain a flip flop that will *not* check for both edge conditions within a single call to test, specify simultaneous_edges=>0.

test
  $f->test;

The test method evaluates one or both conditional tests (according to the flip flop's state) and determines if the flip flop's state should be inverted. An alias, clock is provided for circuitry heads.

state
  print "It's ", $f->state ? 'true' : 'false', ".\n";

To query the current state of a flip flop, call its state method, which will return a boolean value (1 for true, 0 for false).

For those just desperate to obsfucate some code, even while using a nice, clean OO interface, the boolean state of a flip flop is also available as $$f. Don't do this. In fact, just forget that you read it.

  # don't you wish that you didn't fire me now?
  # muahahahaha ack plbbf..
  print "It's ", $$f? 'true' : 'false', ".\n";
series

The series method returns an integer that tells how many times the test method has been called since its state has been true.

series will return 1 after the first call to test has flip a flip flop's state to true, and its return value will be incremented by one each time that test is called and the state remains true.

A call to series while the flip flop is false will return 0.

lead_edge
  print "Just passed the leading edge of truth.\n" 
    if $f->lead_edge;

The lead_edge method returns true if the last call to test caused the flip flop to change state from false to true.

trail_edge
  print "About passed from truth to despair.\n"
    if $f->trail_edge;

The trailing edge, when a flip flop is about to change state from true to false, can be detected by calling the trail_edge method.

It is useful to note that when passing the trailing edge, the *reported* state is true, while the internal state is false. That is, during the *next* call to test, the flip flop will check the set conditional to see if it should switch to true.

  # to print out content between the set event and
  # reset event, excluding the edges themselves, 
  # one might:

  use Bit::FlipFlop;

  my $f = Bit::FlipFlop->new(set   => sub { /start/ },
                             reset => sub { /end/ });

  for (<INPUT>) {
    $f->test;

    if ($f->state and not($f->lead_edge or $f->trail_edge)) {
      print;
    }
  }
next_test

The next_test method lets one query the flip flop to see which callback will be tested on the next call to test. It will return a string, either 'set' or 'reset'.

* Note that in the default case, a flip flop tests the output of both call backs during the call to test that represents a leading edge.

Super Stealth Built in Operator Mode

The functionallity that Bit::FlipFlop provides is so useful, that it can be accessed just like a built in Perl operator. This is referred to as "Super Stealth Built in Operator Mode", or SSBOM. SSBOM is the preferred interface, and the author recommends only using the OO interface as "training wheels".

One signals to the Bit::FlipFlop module that the SSBOM interface is desired by commenting out the use statement:

  #use Bit::FlipFlop;

Advantage of SSBOM

Why should you go through the trouble of learning this slightly cryptic SSBOM interface? Well, for one reason is that the Bit::FlipFlop module *need not be installed* on the target system. This alone is a huge advantage. Another reason is that the SSBOM interface has been standard for many years, and is well known by *thousands* of Perl programmers world wide. SSBOM is more readily available, more well known and thus lends to maintainable code.

Of course, since Bit::FlipFlop objects and their methods won't be available, coding style must be adjusted. What follows are code excerpts to accomplish the same things that the OO method calls do.

new

Instead of creating a Bit::FlipFlop object, use the .. or ... operator.

  $result = set_condition() .. reset_condition();

Instead of subrefs, any Perl expression can be places on either side of the operator. The expression on the left side is taken as the set condition, that on the right as the reset condition.

If a flip flop with the attribute of simultaneous_edges=>1 is desired, then use the .. operator. ... has the functionallity of simultaneous_edges=>0.

test

Under the SSBOM interface, the flip flop tests its conditionals to see if it needs to change state each time it is evaluated.

state

When a flip flop is evaluated, it return value, taken in a boolean sense, represents the state of the flip flop. Should one wish to test the state some time after the flip flop has been evaluated, then save the return value into a scalar variable.

This OO example:

  my $f = Bit::FlipFlop->new(set => sub { $_ == 3 },
                             reset => sub { $_ == 7 });
  for (1..10) {
    $f->test;
    if ($f->state) {
      ++$count;
    } 
  }

Under SSBOM becomes:

  for (1..10) {
    if ($_ == 3 .. $_ == 7) {
      ++$count;
    }
  }    

You may have noticed the dual use of the .. operator in that example. This is an illusion. The characters .., when evaluated in list context is the normal range operator that you are used to. Bit::FlipFlop's SSBOM interface only applies to .. or ... in scalar context.

series

To determine how many times a flip flop has been evaluated since being in the true state, numerify its return value. This number includes the evaluation that caused the flip flop to flip states to true. If the flip flop is in the false state, then using this "method" will yield the integer zero (0).

  $r = set_test() ... reset_test();
  # this same $r is present in the examples below

  print 'the flip flop has been true for ', +$r, " iterations.\n";
lead_edge

The leading edge, when a flip flop changes from the false state to true can be detected by testing the series for number 1.

  print "leading edge\n" if $r == 1;
trail_edge

The trailing edge is when the flip flop changes from a state of true to false. This is indicated in the SSBOM interface by appending the string 'E0' to the return value of a flip flop's evaluation. Note that when the return value is interpreted numerically or as a boolean, that the presence or lacking of the 'E0' appendix is irrelevant.

  print "trailing edge\n" if $r =~ /E/;
next_test

A flip flop will test the set condition (left hand expression) when the trailing edge has been crossed, or when its state is false.

  print "will check left term next" if (not $r) or $r =~ /E/;

On the other hand, the flip flop will check the right hand expression (the reset conditional) if the state is true, and the trailing edge has not yet been crossed.

  print "will check right term next" if $r and $r !~ /E/;

BACKGROUND

Digital Logic

Perl's flip flop operator has roots in digital logic circuitry.

           ___                     $a  $b  | $o   
  $a -----|   \                   ---------|----       
          |    |o---- $o            F   F  |  T        
  $b -----|___/                     T   F  |  T        
                                    F   T  |  T        
                                    T   T  |  F    

The NAND gate. Its logical function could be expressed in Perl as $o = not($a and $b);. One diffence in the function of the NAND gate and its Perl equivalent is that the NAND gate's output ceases to retain its logical state when the input has been removed. It sure would be nice to be able to maintain that bit of state.

   _       ___                          _   _   _
  $s -----|   \                    $q  $q  $s  $r  |  $q
          |    |o-+-- $q          -----------------+-----
        +-|___/   |                 F   T   T   T  |   F
        |        /                  F   T   F   T  |   T  
        |      /                    T   F   T   T  |   T
        \__  /___                   T   F   T   F  |   F   
           /     \                                 |
         /        |               . . .            |
        |  ___    |                                |
        +-|   \   |    _            T   F   F   F  |  ?!?
   _      |    |o-+   $q 
  $r -----|___/         

The RS Latch uses two NAND gates to maintain one bit of logical state after the input has been removed. (The output of the top NAND gate is connected to one of the inputs of the lower gate and vice versa, if my ASCII art isn't quite clear.) Its function is along the lines of this Perl: $q = !$s ... !$r;. One difference between the functionallity of the RS Latch and the provided Perl equivalent is that the latch's output is undefined if $s and $r should be logical false at the same time.

This idea is further developed in digital circuitry in the RS Flip Flop and the JK Flip Flop. The code $q = !$s ... !$r; is very close to the action of the JK Flip Flop, if one abstracts the JK's notion of the Clock input being True as equivalent to the moment of execution of the ... operator.

The curious reader can find lots of information about digital logic by googling on some of these keywords.

ed, etc

Perl's concept of the flip flop op was inherited from awk, who got it from sed, which got it from ed and so on.

The ed command (prepend it with a colon, and it's a vi command), /start/,/stop/s/flip/flop/ can be seen in Perl as perl -pi -e's/flip/flop/ if /start/.../stop/'.

HISTORY

0.01

Original version;

AUTHOR

Colin Meyer, <cmeyer@helvella.org>

SEE ALSO

perl. perlop.