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

OP::Array - Array object class

DESCRIPTION

Extends OP::Object to handle Perl ARRAY refs as OP Objects. Provides constructor, getters, setters, "Ruby-esque" collection, and other methods which one might expect an Array object to respond to.

INHERITANCE

This class inherits additional class and object methods from the following packages:

OP::Class > OP::Object > OP::Array

SYNOPSIS

  use OP::Array;

  my $emptyList = OP::Array->new();

  my $arrayFromList = OP::Array->new(@list); # Makes new ref

  my $arrayFromRef = OP::Array->new($ref);   # Keeps orig ref

METHODS

Public Class Methods

  • $class->new(@list)

    Instantiate a new OP::Array. Accepts an optional array or array reference as a prototype object

    Usage is cited in the SYNOPSIS section of this document.

  • $class->assert(OP::Type $memberType, *@rules)

    Return a new OP::Type::Array instance.

    To permit multiple values of a given type, just wrap an Array() assertion around any other assertion.

    Each element in the stored array lives in a dynamically subclassed linked table, with foreign key constraints against the parent table.

      #
      # File: Example.pm
      #
    
      use OP;
    
      create "OP::Example" => {
        #
        # An array of strings:
        #
        someArr => OP::Array->assert(
          OP::Str->assert()
        ),
    
        ...
      };

    In Caller:

      #!/bin/env perl
      #
      # File: somecaller.pl
      #
    
      use strict;
      use warnings;
    
      use OP::Example;
    
      my $exa = OP::Example->spawn("Array Example");
    
      $exa->setSomeArr("Foo", "Bar", "Rebar", "D-bar");
    
      $exa->save();

    Nested Arrays: Array() can be wrapped around other Array() assertions, for cases where multi-dimensional arrays are needed. Each assertion has independent rules:

      #
      # File: Example.pm
      #
    
      use OP;
    
      create "OP::Example" => {
        #
        # Fancy... a 3x3 matrix of integers with per-element enforcement of
        # min and max values:
        #
        matrix => OP::Array->assert(
          OP::Array->assert(
            OP::Int->assert( subtype(
              min => 0,
              max => 255
            ) ),
            subtype(
              size => 3,
            ),
          ),
          subtype(
            size => 3
          )
        ),
    
        # ...
      };

    In caller:

      #!/bin/env perl
      #
      # File: somecaller.pl
      #
    
      use strict;
      use warnings;
    
      use OP::Example;
    
      my $example = OP::Example->spawn("Matrix Example");
    
      #
      # Data looks like this:
      #
      $example->setMatrix(
        [255, 127, 63],
        [69,  69,  69]
        [42,  23,  5]
      );
    
      $example->save();

Public Instance Methods

  • $array->get($index)

    Get the received array index. Functionally the same as $ref->[$index].

      my $array = OP::Array->new( qw| foo bar | );
    
      my $foo = $array->get(0);
      my $bar = $array->get(1);
  • $array->set($index, $value)

    Set the received array index to the received value. Functionally the same as $ref->[$index] = $value.

      my $array = OP::Array->new( qw| foo bar | );
    
      $array->set(1, "rebar"); # was "bar", now is "rebar"
  • $array->push(@list)

    Object wrapper for Perl's built-in push() function. Functionally the same as push(@$ref, @list).

      my $array = OP::Array->new();
    
      $array->push($something);
    
      $array->push( qw| foo bar | );
  • $array->size()

    Object wrapper for Perl's built-in scalar() function. Functionally the same as scalar(@$ref).

      my $array = OP::Array->new( qw| foo bar | );
    
      my $size = $array->size(); # returns 2
  • $array->collect($sub)

  • $array->collectWithIndex($sub)

  • yield(item, [item, ...]), emit(item, [item, ...]), return, break

    List iterator method. collect returns a new array with the results of running the received CODE block once for every element in the original. Returns the yielded/emitted results in a new OP::Array instance.

    This Perl implementation of collect is borrowed from Ruby. OP employs several functions which may finely control the flow of execution.

    The collector pattern is used throughout OP. The following pseudocode illustrates its possible usage.

      my $array = OP::Array->new( ... );
    
      my $sub = sub {
        my $item = shift;
    
        # ...
    
        return if $something;       # Equivalent to next()
    
        break() if $somethingElse;  # Equivalent to last()
    
        emit($thing1, [$thing2, ...]);  # Upstreams $things,
                                        # and continues current iteration
    
        # ...
    
        yield($thing1, [$thing2, ...]); # Upstreams $things,
                                        # and skips to next iteration
      };
     
      my $results = $array->collect($sub);

    A simple working example - return a new array containing capitalized versions of each element in the original. Collection is performed using collect; each may be used when there is code to run with no return values.

      my $array = OP::Array->new( qw|
        foo bar baz whiskey tango foxtrot
      | );
    
      my $capped = $array->collect( sub {
        my $item = shift;
    
        yield uc($item)
      } );
    
      $capped->each( sub {
        my $item = shift;
    
        print "Capitalized array contains item: $item\n";
      } );

    Collector Control Flow

    The flow of the collect sub may be controlled using return, yield, emit, and break.

    break invokes Perl's last, breaking execution of the collect loop.

    In the context of the collect sub, return is like Perl's next or Javascript's continue- that is, it stops execution of the sub in progress, and continues on to the next iteration. Because Perl subs always end with an implicit return, using return to reap yielded elements is not workable, so we use yield for this instead. Any arguments to return in this context are ignored.

    Like return, yield stops execution of the sub in progress, but items passed as arguments to yield are added to the new array returned by collect.

    emit adds items to the returned array, but does so without returning from the sub in progress. Emitted items are added to the array returned by the collect sub, just like yield, but you may call emit as many times as needed per iteration, without breaking execution.

    If nothing is yielded or emitted by the collect sub in an iteration, nothing will be added to the returned array for that item. To yield nothing for an iteration, don't yield(undef), just don't yield-- Rather, use return instead, to avoid undefined elements in the returned array.

    If yielding multiple items at a time, they are added to the array returned by collect in a "flat" manner-- that is, no array nesting will occur unless the yielded data is explicitly structured as such.

    Recap: Return vs Yield vs Emit

    yield adds items to the array returned by the collect sub, in addition to causing Perl to jump ahead to the next iteration, like next in a for loop would. Remember, return just returns without adding anything to the return array-- use it in cases where you just want to skip ahead without yielding items (ie next).

      #
      # Create a new Array ($quoted) containing quoted elements
      # from the original, omitting items which aren't wanted.
      #
      my $quoted = $array->collect( sub {
        my $item = shift;
    
        print "Have item: $item\n";
    
        return if $item =~ /donotwant/;
    
        yield( $myClass->quote($item) );
    
        print "You will never get here.\n";
      } );
    
      #
      # The above was roughly equivalent to:
      #
      my $quoted = OP::Array->new();
    
      for my $item ( @{ $array } ) {
        print "Have item: $item\n";
    
        next if $item =~ /donotwant/;
    
        $quoted->push( $myClass->quote($item) );
    
        next;
    
        print "You will never get here.\n";
      }

    emit adds items to the array returned by the collect sub, but does so without returning (that is, execution of the sub in progress will continue uninterrupted). It's just like pushing to an array from inside a for loop, because that's exactly what it does.

    The only functional difference between the preceding example for yield and the below example for emit is that using emit lets the interpreter get to that final print statement.

      #
      # For example, create a new Array ($quoted) containing quoted elements
      # from the original, omitting items which aren't wanted.
      #
      my $quoted = $array->collect( sub {
        my $item = shift;
    
        print "Have item: $item\n";
    
        return if $item =~ /donotwant/;
    
        emit( $myClass->quote($item) );
    
        print "You will *always* get here!\n";
      } );
    
      #
      # The above was a more compact way of doing this:
      #
      my $quoted = OP::Array->new();
    
      for ( @{ $array } ) {
        print "Have item: $_\n";
    
        next if $_ =~ /donotwant/;
    
        $quoted->push( $myClass->quote($_) );
    
        print "You will *always* get here!\n";
      }

    The indexed version of collect is collectWithIndex. It provides the index integer as a second argument to the received CODE block.

      my $new = $array->collectWithIndex( sub {
        my $item = shift;
        my $index = shift;
    
        print "Working on item $index: $item\n";
      } );
  • $array->each($sub)

  • $array->eachWithIndex($sub)

  • return, break

    List iterator method. Runs $sub for each element in self; returns true on success.

    Just as in collect, return skips to the next iteration, and break breaks the loop. Array elements are accessed in the same manner as collect.

      my $arr = OP::Array->new( qw| foo bar rebar | );
    
      $arr->each( sub {
        my $item = shift;
    
        print "Have item: $item\n";
      } );
    
      #
      # Expected output:
      #
      # Have item: foo
      # Have item: bar
      # Have item: rebar
      #

    The indexed version of each is eachWithIndex. It provides the index integer as a second argument to the received CODE block.

      $array->eachWithIndex( sub {
        my $item = shift;
        my $index = shift;
    
        print "Have item $index: $item\n";
      } );
    
      #
      # Expected output:
      #
      # Have item 0: foo
      # Have item 1: bar
      # Have item 2: rebar
      #
  • $array->join($joinStr)

    Object wrapper for Perl's built-in join() function. Functionally the same as join($joinStr, @{ $self }).

      my $array = OP::Array->new( qw| foo bar | );
    
      my $string = $array->join(','); # returns "foo,bar"
  • $array->compact()

    Removes any undefined elements from self.

      my $array = OP::Array->new( 'foo', undef, 'bar' );
    
      $array->compact(); # Array becomes ('foo', 'bar')
  • $array->unshift()

    Object wrapper for Perl's built-in unshift() function. Functionally the same as unshift(@{ $self }).

      my $array = OP::Array->new('bar');
    
      $array->unshift('foo'); # Array becomes ('foo', 'bar')
  • $array->rand()

    Returns a pseudo-random array element.

      my $array = OP::Array->new( qw| heads tails | );
    
      my $flip = $array->rand(); # Returns 'heads' or 'tails' randomly
  • $array->isEmpty()

    Returns a true value if self contains no values, otherwise false.

      my $array = OP::Array->new();
    
      if ( $array->isEmpty() ) {
        print "Foo\n";
      }
    
      $array->push('anything');
    
      if ( $array->isEmpty() ) {
        print "Bar\n";
      }
    
      #
      # Expected Output:
      #
      # Foo
      #
  • $array->includes($value)

    Returns a true value if self includes the received value, otherwise false.

      my $array = OP::Array->new( qw| foo bar | );
    
      for my $key ( qw| foo bar rebar | ) {
        next if $array->includes($key);
    
        print "$key does not belong here.\n";
      }
    
      #
      # Expected output:
      #
      # rebar does not belong here.
      #
  • $array->grep($expr, [$mod])

    Returns an array of scalar matches if the expression has any hits in the array. Wrapper for Perl's built-in grep() function.

    The second argument is an optional regex modifier string (e.g. "i", "gs", "gse", etc).

      my $array = OP::Array->new( qw| Jimbo Jimbob chucky | );
    
      for ( @{ $array->grep(qr/^jim/, "i") } ) {
        print "Matched $_\n";
      };
    
      #
      # Expected output:
      #
      # Matched Jimbo
      # Matched Jimbob
      #
  • $array->clear()

    Removes all items, leaving self with zero array elements.

      my $array = OP::Array->new( qw| foo bar | );
    
      my $two = $array->size(); # 2
    
      $array->clear();
    
      my $zero = $array->size(); # 0
  • $array->purge()

    Explicitly purges each item in self, leaving self with zero array elements.

    Useful in cases of arrays of CODE references, which are not otherwise cleaned up by Perl's GC.

  • $array->average()

    Returns the average of all items in self.

      my $arr = OP::Array->new( qw|
       54343 645564 89890 32 342 564564
      | );
    
      my $average = $arr->average();
    
      print "Avg: $average\n"; # Avg: 225789.166666667
  • $array->median()

    Returns the median value of all items in self.

      my $arr = OP::Array->new( qw|
       54343 645564 89890 32 342 564564
      | );
    
      my $median = $arr->median();
    
      print "Med: $median\n"; # Med: 89890
  • $array->max()

    Returns the highest value of all items in self.

      my $arr = OP::Array->new( qw|
       54343 645564 89890 32 342 564564
      | );
    
      my $max = $arr->max();
    
      print "Max: $max\n"; # Max: 645564
  • $array->min()

    Returns the lowest value of all items in self.

      my $arr = OP::Array->new( qw|
       54343 645564 89890 32 342 564564
      | );
       
      my $min = $arr->min();
    
      print "Min: $min\n"; # Min: 32  
  • $array->sum()

    Returns the sum of all items in self.

      my $arr = OP::Array->new( qw|
       54343 645564 89890 32 342 564564
      | );
    
      my $sum = $arr->sum();
    
      print "Sum: $sum\n"; # Sum: 1354735
  • $array->stddev()

    Return the standard deviation of the current set.

      my $array = OP::Array->new(2, 4, 4, 4, 5, 5, 7, 9);
    
      my $stddev = $array->stddev;
      # 2
  • $array->sort([$function])

    Wrapper to Perl's built-in sort function.

    Accepts an optional argument, a sort function to be used. Sort function should take $a and $b as arguments.

      my $alphaSort = $array->sort();
    
      my $numSort = $array->sort(sub{ shift() <=> shift() });
      
  • $array->first()

    Returns the first item in the array. Same as $array->[0].

      my $array = OP::Array->new( qw| alpha larry omega | );
    
      print $array->first();
      print "\n";
    
      # Prints "alpha\n"
  • $array->last()

    Returns the final item in the array. Same as $array->[-1].

      my $array = OP::Array->new( qw| alpha larry omega | );
    
      print $array->last();
      print "\n";
    
      # Prints "omega\n"
  • $array->uniq();

    Returns a copy of self with duplicate elements removed.

  • $array->reversed();

    Returns a copy of self with elements in reverse order

  • $array->pop()

    Object wrapper for Perl's built-in pop() function. Functionally the same as pop(@{ $self }).

      my $array = OP::Array->new( qw| foo bar rebar | );
    
      while ( my $element = $array->pop() ) {
        print "Popped $element\n";
      }
    
      if ( $array->isEmpty() ) { print "Now it's empty!\n"; }
    
      #
      # Expected output (note reversed order):
      #
      # Popped rebar
      # Popped bar
      # Popped foo
      # Now it's empty!
      #
  • $array->shift()

    Object wrapper for Perl's built-in shift() function. Functionally the same as shift(@{ $self }).

      my $array = OP::Array->new( qw| foo bar | );
    
      while( my $element = $array->shift() ) {
        print "Shifted $element\n";
      }
    
      if ( $array->isEmpty() ) {
        print "Now it's empty!\n";
      }
    
      #
      # Expected output:
      #
      # Shifted foo
      # Shifted bar
      # Now it's empty!
      #

SEE ALSO

perlfunc, Math::VecStat

This file is part of OP.