| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Array::APX; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =pod | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 NAME | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | Array::APX - Array Programming eXtensions | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | =head1 VERSION | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | This document refers to version 0.4 of Array::APX | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | use strict; | 
| 16 |  |  |  |  |  |  | use warnings; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | # Create two vectors [0 1 2] and [3 4 5]: | 
| 21 |  |  |  |  |  |  | my $x = iota(3); | 
| 22 |  |  |  |  |  |  | my $y = iota(3) + 3; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | print "The first vector is  $x"; | 
| 25 |  |  |  |  |  |  | print "The second vector is $y\n"; | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # Add these vectors and print the result: | 
| 28 |  |  |  |  |  |  | print 'The sum of these two vectors is ', $x + $y, "\n"; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # Create a function to multiply two values: | 
| 31 |  |  |  |  |  |  | my $f = sub { $_[0] * $_[1] }; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # Create an outer product and print it: | 
| 34 |  |  |  |  |  |  | print "The outer product of these two vectors is\n", $x |$f| $y; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | yields | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | The first vector is  [    0    1    2 ] | 
| 39 |  |  |  |  |  |  | The second vector is [    3    4    5 ] | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | The sum of these two vectors is [    3    5    7 ] | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | The outer product of these two vectors is | 
| 44 |  |  |  |  |  |  | [ | 
| 45 |  |  |  |  |  |  | [    0    0    0 ] | 
| 46 |  |  |  |  |  |  | [    3    4    5 ] | 
| 47 |  |  |  |  |  |  | [    6    8   10 ] | 
| 48 |  |  |  |  |  |  | ] | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | This module extends Perl-5 with some basic functionality commonly found in | 
| 53 |  |  |  |  |  |  | array programming languages like APL, Lang5 etc. It is basically a wrapper | 
| 54 |  |  |  |  |  |  | of Array::Deeputils and overloads quite some basic Perl operators in a way | 
| 55 |  |  |  |  |  |  | that allows easy manipulation of nested data structures. These data | 
| 56 |  |  |  |  |  |  | structures are basically blessed n-dimensional arrays that can be handled | 
| 57 |  |  |  |  |  |  | in a way similar to APL or Lang5. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | A nice example is the computation of a list of prime numbers using an | 
| 60 |  |  |  |  |  |  | archetypical APL solution. The basic idea is this: Create an outer product | 
| 61 |  |  |  |  |  |  | of two vectors [2 3 4 ... ]. The resulting matrix does not contain any | 
| 62 |  |  |  |  |  |  | primes since every number is the product of at least two integers. Then | 
| 63 |  |  |  |  |  |  | check for every number in the original vector [2 3 4 ... ] if it is a | 
| 64 |  |  |  |  |  |  | member of this matrix. If not, it must be a prime number. The set | 
| 65 |  |  |  |  |  |  | theoretic method 'in' returns a selection vector consisting of 0 and 1 | 
| 66 |  |  |  |  |  |  | values which can be used in a second step to select only the prime values | 
| 67 |  |  |  |  |  |  | from the original vector. Using Array::APX this can be written in Perl | 
| 68 |  |  |  |  |  |  | like this: | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | use strict; | 
| 71 |  |  |  |  |  |  | use warnings; | 
| 72 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | my $f = sub { $_[0] * $_[1] }; # We need an outer product | 
| 75 |  |  |  |  |  |  | my $x; | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | print $x->select(!($x = iota(199) + 2)->in($x |$f| $x)); | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | How does this work? First a vector [2 3 4 ... 100] is created: | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | $x = iota(99) + 2 | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | This vector is then used to create an outer product (basically a multiplication | 
| 84 |  |  |  |  |  |  | table without the 1-column/row: | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | my $f = sub { $_[0] * $_[1] }; # We need an outer product | 
| 87 |  |  |  |  |  |  | ... $x |$f| $x ... | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | The |-operator is used here as the generalized outer-'product'-operator | 
| 90 |  |  |  |  |  |  | (if applied to two APX data structures it would act as the bitwise binary or) | 
| 91 |  |  |  |  |  |  | - it expects a | 
| 92 |  |  |  |  |  |  | function reference like $f in the example above. Thus it is possible to | 
| 93 |  |  |  |  |  |  | create any outer 'products' - not necessarily based on multiplication only. | 
| 94 |  |  |  |  |  |  | Using the vector stored in $x and this two dimensional matrix, the | 
| 95 |  |  |  |  |  |  | in-method is used to derive a boolean vector that contains a 1 at every | 
| 96 |  |  |  |  |  |  | place corresponding to an element on the left hand operand that is contained | 
| 97 |  |  |  |  |  |  | in the right hand operand. This boolean vector is then inverted using the | 
| 98 |  |  |  |  |  |  | overloaded !-operator: | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | !($x = iota(99) + 2)->in($x |$f| $x) | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | Using the select-method this boolean vector is used to select the elements | 
| 103 |  |  |  |  |  |  | corresponding to places marked with 1 from the original vector $x thus | 
| 104 |  |  |  |  |  |  | yielding a vector of prime numbers between 2 and 100: | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | print $x->select(!($x = iota(199) + 2)->in($x |$f| $x)); | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =cut | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 1 |  |  | 1 |  | 33626 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 111 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 101 |  | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | require Exporter; | 
| 114 |  |  |  |  |  |  | our @ISA         = qw(Exporter); | 
| 115 |  |  |  |  |  |  | our @EXPORT_OK   = qw(dress iota); | 
| 116 |  |  |  |  |  |  | our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ] ); | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | our $VERSION = 0.4; | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 1 |  |  | 1 |  | 5 | use Data::Dumper; | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 57 |  | 
| 121 |  |  |  |  |  |  | #use Array::DeepUtils qw(:all); | 
| 122 | 1 |  |  | 1 |  | 1165 | use Array::DeepUtils; | 
|  | 1 |  |  |  |  | 9849 |  | 
|  | 1 |  |  |  |  | 48 |  | 
| 123 | 1 |  |  | 1 |  | 10 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 191 |  | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | # The following operators will be generated automatically: | 
| 126 |  |  |  |  |  |  | my %binary_operators = ( | 
| 127 |  |  |  |  |  |  | '+'  => 'add', | 
| 128 |  |  |  |  |  |  | '*'  => 'multiply', | 
| 129 |  |  |  |  |  |  | '-'  => 'subtract', | 
| 130 |  |  |  |  |  |  | '%'  => 'mod', | 
| 131 |  |  |  |  |  |  | '**' => 'power', | 
| 132 |  |  |  |  |  |  | '&'  => 'bitwise_and', | 
| 133 |  |  |  |  |  |  | '^'  => 'bitwise_xor', | 
| 134 |  |  |  |  |  |  | ); | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # Overload everything defined in %binary_operators: | 
| 137 |  |  |  |  |  |  | eval "use overload '$_' => '$binary_operators{$_}';" | 
| 138 | 1 |  |  | 1 |  | 5 | for keys(%binary_operators); | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 1 |  | 5 |  | 
|  | 1 |  |  | 1 |  | 86 |  | 
|  | 1 |  |  | 1 |  | 1 |  | 
|  | 1 |  |  | 1 |  | 6 |  | 
|  | 1 |  |  | 1 |  | 89 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 110 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 80 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 18 |  | 
|  | 1 |  |  |  |  | 68 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 71 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # All other overloads are here: | 
| 141 |  |  |  |  |  |  | use overload ( | 
| 142 |  |  |  |  |  |  | # Unary operators: | 
| 143 | 1 |  |  |  |  | 9 | '!' => 'not', | 
| 144 |  |  |  |  |  |  | # Binary operators with trick (0 instead of '' or undef): | 
| 145 |  |  |  |  |  |  | '==' => 'numeric_equal', | 
| 146 |  |  |  |  |  |  | '!=' => 'numeric_not_equal', | 
| 147 |  |  |  |  |  |  | # Non-standard operators: | 
| 148 |  |  |  |  |  |  | '|'  => 'outer',      # This also implements the bitwise binary 'or'! | 
| 149 |  |  |  |  |  |  | '/'  => 'reduce',     # This also implements the binary division operator! | 
| 150 |  |  |  |  |  |  | 'x'  => 'scan', | 
| 151 |  |  |  |  |  |  | '""' => '_stringify', | 
| 152 | 1 |  |  | 1 |  | 5 | ); | 
|  | 1 |  |  |  |  | 3 |  | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | ############################################################################### | 
| 155 |  |  |  |  |  |  | # Overloading unary operators: | 
| 156 |  |  |  |  |  |  | ############################################################################### | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =head1 Overloaded unary operators | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | Overloaded unary operators are automatically applied to all elements of | 
| 161 |  |  |  |  |  |  | a (nested) APX data structure. The following operators are currently | 
| 162 |  |  |  |  |  |  | available: ! | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | =cut | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub not # Not, mapped to '!'. | 
| 167 |  |  |  |  |  |  | { | 
| 168 | 2 |  |  | 2 | 0 | 3 | my $data = [@{$_[0]}]; | 
|  | 2 |  |  |  |  | 6 |  | 
| 169 | 2 |  |  | 8 |  | 11 | Array::DeepUtils::unary($data, sub { return 0+ !$_[0] }); | 
|  | 8 |  |  |  |  | 473 |  | 
| 170 | 2 |  |  |  |  | 71 | return bless $data; | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | ############################################################################### | 
| 174 |  |  |  |  |  |  | # Overloading binary operators: | 
| 175 |  |  |  |  |  |  | ############################################################################### | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =head1 Overloaded binary operators | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | In general all overloaded binary operators are automatically applied in an | 
| 180 |  |  |  |  |  |  | element wise fashion to all (corresponding) elements of APX data structures. | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | The following operators are currently available and do what one would | 
| 183 |  |  |  |  |  |  | expect: | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =head2 +, -, *, /, %, **, |, &, ^, ==, != | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | These operators implement addition, subtraction, multiplication, division, | 
| 188 |  |  |  |  |  |  | modulus, power, bitwise or / and /xor, numerical equal/not equal | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =cut | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # Overload basic binary operators: | 
| 193 |  |  |  |  |  |  | eval (' | 
| 194 |  |  |  |  |  |  | sub ' . $binary_operators{$_} . ' | 
| 195 |  |  |  |  |  |  | { | 
| 196 |  |  |  |  |  |  | my ($self, $other, $swap) = @_; | 
| 197 |  |  |  |  |  |  | my $result = ref($other) ? [@$other] : [$other]; | 
| 198 |  |  |  |  |  |  | ($self, $result) = ($result, [@$self]) if $swap; | 
| 199 |  |  |  |  |  |  | _binary([@$self], $result, sub { $_[0] ' . $_ . ' $_[1] }, 1); | 
| 200 |  |  |  |  |  |  | return bless $result; | 
| 201 |  |  |  |  |  |  | } | 
| 202 | 28 | 100 |  | 28 | 0 | 4639 | ') for keys(%binary_operators); | 
|  | 13 | 100 |  | 17 | 0 | 4006 |  | 
|  | 22 | 100 |  | 4 | 0 | 5645 |  | 
|  | 174 | 100 |  | 4 | 0 | 36726 |  | 
|  | 28 | 100 |  | 4 | 0 | 5003 |  | 
|  | 22 | 100 |  | 4 | 0 | 4818 |  | 
|  | 23 | 100 |  | 4 | 0 | 5814 |  | 
|  | 17 | 100 |  | 6 |  | 57 |  | 
|  | 17 | 100 |  |  |  | 51 |  | 
|  | 17 | 100 |  |  |  | 45 |  | 
|  | 17 | 100 |  |  |  | 107 |  | 
|  | 17 | 100 |  |  |  | 178 |  | 
|  | 4 | 100 |  |  |  | 9 |  | 
|  | 4 | 100 |  |  |  | 14 |  | 
|  | 4 |  |  |  |  | 11 |  | 
|  | 4 |  |  |  |  | 27 |  | 
|  | 4 |  |  |  |  | 22 |  | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 12 |  | 
|  | 4 |  |  |  |  | 11 |  | 
|  | 4 |  |  |  |  | 26 |  | 
|  | 4 |  |  |  |  | 18 |  | 
|  | 4 |  |  |  |  | 9 |  | 
|  | 4 |  |  |  |  | 18 |  | 
|  | 4 |  |  |  |  | 13 |  | 
|  | 4 |  |  |  |  | 27 |  | 
|  | 4 |  |  |  |  | 22 |  | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 15 |  | 
|  | 4 |  |  |  |  | 14 |  | 
|  | 4 |  |  |  |  | 21 |  | 
|  | 4 |  |  |  |  | 18 |  | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 17 |  | 
|  | 4 |  |  |  |  | 13 |  | 
|  | 4 |  |  |  |  | 24 |  | 
|  | 4 |  |  |  |  | 24 |  | 
|  | 6 |  |  |  |  | 15 |  | 
|  | 6 |  |  |  |  | 22 |  | 
|  | 6 |  |  |  |  | 24 |  | 
|  | 6 |  |  |  |  | 41 |  | 
|  | 6 |  |  |  |  | 33 |  | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub numeric_equal | 
| 205 |  |  |  |  |  |  | { | 
| 206 | 4 |  |  | 4 | 0 | 8 | my ($self, $other, $swap) = @_; | 
| 207 | 4 | 100 |  |  |  | 10 | my $result = ref($other) ? [@$other] : [$other]; | 
| 208 | 4 |  |  | 16 |  | 18 | _binary([@$self], $result, sub { 0+ ($_[0] == $_[1]) }, 1); | 
|  | 16 |  |  |  |  | 2647 |  | 
| 209 | 4 |  |  |  |  | 16 | return bless $result; | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | sub numeric_not_equal | 
| 213 |  |  |  |  |  |  | { | 
| 214 | 4 |  |  | 4 | 0 | 8 | my ($self, $other, $swap) = @_; | 
| 215 | 4 | 100 |  |  |  | 14 | my $result = ref($other) ? [@$other] : [$other]; | 
| 216 | 4 |  |  | 16 |  | 21 | _binary([@$self], $result, sub { 0+ ($_[0] != $_[1]) }, 1); | 
|  | 16 |  |  |  |  | 2785 |  | 
| 217 | 4 |  |  |  |  | 17 | return bless $result; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | =head2 Generalized outer products | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | A basic function in every array programming language is an operator to create | 
| 223 |  |  |  |  |  |  | generalized outer products of two vectors. This generalized outer product | 
| 224 |  |  |  |  |  |  | operator consists of a function pointer that is enclosed in two '|' (cf. the | 
| 225 |  |  |  |  |  |  | prime number example at the beginning of this documentation). Given two | 
| 226 |  |  |  |  |  |  | APX vectors a traditional outer product can be created like this: | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | use strict; | 
| 229 |  |  |  |  |  |  | use warnings; | 
| 230 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | my $f = sub { $_[0] * $_[1] }; | 
| 233 |  |  |  |  |  |  | my $x = iota(10) + 1; | 
| 234 |  |  |  |  |  |  | print $x |$f| $x; | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | This short program yields the following output: | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | [ | 
| 239 |  |  |  |  |  |  | [    1    2    3    4    5    6    7    8    9   10 ] | 
| 240 |  |  |  |  |  |  | [    2    4    6    8   10   12   14   16   18   20 ] | 
| 241 |  |  |  |  |  |  | [    3    6    9   12   15   18   21   24   27   30 ] | 
| 242 |  |  |  |  |  |  | [    4    8   12   16   20   24   28   32   36   40 ] | 
| 243 |  |  |  |  |  |  | [    5   10   15   20   25   30   35   40   45   50 ] | 
| 244 |  |  |  |  |  |  | [    6   12   18   24   30   36   42   48   54   60 ] | 
| 245 |  |  |  |  |  |  | [    7   14   21   28   35   42   49   56   63   70 ] | 
| 246 |  |  |  |  |  |  | [    8   16   24   32   40   48   56   64   72   80 ] | 
| 247 |  |  |  |  |  |  | [    9   18   27   36   45   54   63   72   81   90 ] | 
| 248 |  |  |  |  |  |  | [   10   20   30   40   50   60   70   80   90  100 ] | 
| 249 |  |  |  |  |  |  | ] | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | =cut | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # Create a generalized outer 'product' based on a function reference. | 
| 254 |  |  |  |  |  |  | # In addition to that the |-operator which is overloaded here can also act | 
| 255 |  |  |  |  |  |  | # as binary 'or' if applied to two APX structures. | 
| 256 |  |  |  |  |  |  | my @_outer_stack; | 
| 257 |  |  |  |  |  |  | sub outer | 
| 258 |  |  |  |  |  |  | { | 
| 259 | 6 |  |  | 6 | 1 | 10 | my ($left, $right) = @_; | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 6 | 100 | 66 |  |  | 87 | if ((ref($left) eq __PACKAGE__ and ref($right) eq __PACKAGE__) or | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 262 |  |  |  |  |  |  | (ref($left) eq __PACKAGE__ and defined($right) and !ref($right)) | 
| 263 |  |  |  |  |  |  | ) # Binary or | 
| 264 |  |  |  |  |  |  | { | 
| 265 | 4 |  |  |  |  | 7 | my ($self, $other) = @_; | 
| 266 | 4 | 100 |  |  |  | 11 | my $result = ref($right) ? [@$right] : [$right]; | 
| 267 | 4 |  |  | 28 |  | 36 | Array::DeepUtils::binary([@$left], $result, sub { $_[0] | $_[1] }, 1); | 
|  | 28 |  |  |  |  | 4550 |  | 
| 268 | 4 |  |  |  |  | 88 | return bless $result; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  | # If the right side argument is a reference to a subroutine we are at | 
| 271 |  |  |  |  |  |  | # the initial stage of a |...|-operator and have to rememeber the | 
| 272 |  |  |  |  |  |  | # function to be used as well as the left hand operator: | 
| 273 |  |  |  |  |  |  | elsif (ref($left) eq __PACKAGE__ and ref($right) eq 'CODE') | 
| 274 |  |  |  |  |  |  | { | 
| 275 | 1 |  |  |  |  | 2 | my %outer; | 
| 276 | 1 |  |  |  |  | 3 | $outer{left}     = $left;  # APX object | 
| 277 | 1 |  |  |  |  | 2 | $outer{operator} = $right; # Reference to a subroutine | 
| 278 | 1 |  |  |  |  | 4 | push @_outer_stack, \%outer; | 
| 279 | 1 |  |  |  |  | 5 | return; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | elsif (ref($left) eq __PACKAGE__ and !defined($right)) | 
| 282 |  |  |  |  |  |  | {   # Second phase of applying the |...|-operator: | 
| 283 | 1 |  |  |  |  | 4 | my $info = pop @_outer_stack; | 
| 284 | 1 |  |  |  |  | 1 | my ($a1, $a2) = ([@{$info->{left}}], [@{$left}]); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 285 | 1 |  |  |  |  | 1 | my @result; | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 1 |  |  |  |  | 3 | for my $i ( 0 .. @$a1 - 1 ) | 
| 288 |  |  |  |  |  |  | { | 
| 289 | 3 |  |  |  |  | 4 | for my $j ( 0 .. @$a2 - 1 ) | 
| 290 |  |  |  |  |  |  | { | 
| 291 | 9 |  |  |  |  | 11 | my $value = $a2->[$j]; | 
| 292 | 9 |  |  |  |  | 18 | _binary($a1->[$i], $value, $info->{operator}); | 
| 293 | 9 |  |  |  |  | 16 | $result[$i][$j] = $value; | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 1 |  |  |  |  | 4 | return bless \@result; | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 0 |  |  |  |  | 0 | croak 'outer: Strange parametertypes: >>', ref($left), | 
| 301 |  |  |  |  |  |  | '<< and >>', ref($right), '<<'; | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | =head2 The reduce operator / | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | The operator / acts as the reduce operator if applied to a reference to a | 
| 307 |  |  |  |  |  |  | subroutine as its left argument and an APX structure as its right element: | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | use strict; | 
| 310 |  |  |  |  |  |  | use warnings; | 
| 311 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | my $x = iota(100) + 1; | 
| 314 |  |  |  |  |  |  | my $f = sub { $_[0] + $_[1] }; | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | print $f/ $x, "\n"; | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | calculates the sum of all integers between 1 and 100 (without using Gauss' | 
| 319 |  |  |  |  |  |  | summation formula just by repeated addition). The combined operator | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | $f/ | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | applies the function referenced by $f between each two successive elements | 
| 324 |  |  |  |  |  |  | of the APX structure on the right hand side of the operator. | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =cut | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | sub reduce | 
| 329 |  |  |  |  |  |  | { | 
| 330 | 5 |  |  | 5 | 1 | 11 | my ($left, $right, $swap) = @_; | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 5 | 100 | 66 |  |  | 59 | if (ref($left) eq __PACKAGE__ and ref($right) ne 'CODE') # Binary division | 
|  |  | 50 | 33 |  |  |  |  | 
| 333 |  |  |  |  |  |  | { | 
| 334 | 4 | 100 |  |  |  | 16 | my $result = ref($right) ? [@$right] : [$right]; | 
| 335 | 4 | 100 |  |  |  | 11 | ($left, $result) = ($result, [@$left]) if $swap; | 
| 336 | 4 |  |  | 13 |  | 23 | _binary([@$left], $result, sub { $_[0] / $_[1] }, 1); | 
|  | 13 |  |  |  |  | 3350 |  | 
| 337 | 4 |  |  |  |  | 22 | return bless $result; | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  | elsif (ref($_[0]) eq __PACKAGE__ and ref($_[1]) eq 'CODE') # reduce operator | 
| 340 |  |  |  |  |  |  | { | 
| 341 | 1 |  |  |  |  | 3 | my $result = shift @$left; | 
| 342 | 1 |  |  |  |  | 3 | for my $element (@$left) | 
| 343 |  |  |  |  |  |  | { | 
| 344 | 99 |  |  |  |  | 81 | eval { _binary($element, $result, $right); }; | 
|  | 99 |  |  |  |  | 137 |  | 
| 345 | 99 | 50 |  |  |  | 169 | croak "reduce: Error while applying reduce: $@\n" if $@; | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  |  | 
| 348 | 1 |  |  |  |  | 9 | return $result; | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 0 |  |  |  |  | 0 | croak 'outer: Strange parametertypes: ', ref($_[0]), ' and ', ref($_[0]); | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | =head2 The scan operator x | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | The scan-operator works like the \-operator in APL - it applies a binary | 
| 357 |  |  |  |  |  |  | function to all successive elements of an array but accumulates the results | 
| 358 |  |  |  |  |  |  | gathered along the way. The following example creates a vector of the | 
| 359 |  |  |  |  |  |  | partial sums of 0, 0 and 1, 0 and 1 and 2, 0 and 1 and 2 and 3 etc.: | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | $f = sub { $_[0] + $_[1] }; | 
| 362 |  |  |  |  |  |  | $x = $f x iota(10); | 
| 363 |  |  |  |  |  |  | print $x; | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | This code snippet yields the following result: | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | [    0    1    3    6   10   15   21   28   36   45 ] | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | =cut | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | sub scan | 
| 372 |  |  |  |  |  |  | { | 
| 373 | 1 |  |  | 1 | 1 | 3 | my ($argument, $function, $swap) = @_; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 1 | 50 |  |  |  | 4 | croak "scan operator: Wrong sequence of function and argument!\n" | 
| 376 |  |  |  |  |  |  | unless $swap; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 1 | 50 |  |  |  | 4 | croak "scan operator: No function reference found!\n" | 
| 379 |  |  |  |  |  |  | if ref($function) ne 'CODE'; | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 1 |  |  |  |  | 2 | my @result; | 
| 382 | 1 |  |  |  |  | 2 | push @result, (my $last_value = shift @$argument); | 
| 383 | 1 |  |  |  |  | 2 | for my $element (@$argument) | 
| 384 |  |  |  |  |  |  | { | 
| 385 | 9 |  |  |  |  | 16 | _binary($element, $last_value, $function); | 
| 386 | 9 |  |  |  |  | 14 | push @result, $last_value; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 1 |  |  |  |  | 5 | return bless \@result; | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | ############################################################################### | 
| 393 |  |  |  |  |  |  | # Exported functions: | 
| 394 |  |  |  |  |  |  | ############################################################################### | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | =head1 Exported functions | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =head2 dress | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | This function expects an array reference and converts it into an APX objects. | 
| 401 |  |  |  |  |  |  | This is useful if nested data structures that have been created outside of | 
| 402 |  |  |  |  |  |  | the APX framework are to be processed using the APX array processing | 
| 403 |  |  |  |  |  |  | capabilities. | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | use strict; | 
| 406 |  |  |  |  |  |  | use warnings; | 
| 407 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | my $array = [[1, 2], [3, 4]]; | 
| 410 |  |  |  |  |  |  | my $x = dress($array); | 
| 411 |  |  |  |  |  |  | print "Structure:\n$x"; | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | yields the following output: | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | Structure: | 
| 416 |  |  |  |  |  |  | [ | 
| 417 |  |  |  |  |  |  | [    1    2 ] | 
| 418 |  |  |  |  |  |  | [    3    4 ] | 
| 419 |  |  |  |  |  |  | ] | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | =cut | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | sub dress # Transform a plain vanilla Perl array into an APX object. | 
| 424 |  |  |  |  |  |  | { | 
| 425 | 49 |  |  | 49 | 1 | 14471 | my ($value) = @_; | 
| 426 | 49 | 50 |  |  |  | 142 | croak "Can't dress a non-reference!" if ref($value) ne 'ARRAY'; | 
| 427 | 49 |  |  |  |  | 502 | return bless $value; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | =head2 iota | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | This function expects a positive integer value as its argument and returns | 
| 433 |  |  |  |  |  |  | an APX vector with unit stride, starting with 0 and containing as many | 
| 434 |  |  |  |  |  |  | elements as specified by the argument: | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | use strict; | 
| 437 |  |  |  |  |  |  | use warnings; | 
| 438 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | my $x = iota(10); | 
| 441 |  |  |  |  |  |  | print "Structure:\n$x"; | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | yields | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | Structure: | 
| 446 |  |  |  |  |  |  | [    0    1    2    3    4    5    6    7    8    9 ] | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | =cut | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # Create a unit stride vector starting at 0: | 
| 451 |  |  |  |  |  |  | sub iota | 
| 452 |  |  |  |  |  |  | { | 
| 453 | 52 |  |  | 52 | 1 | 38974 | my ($argument) = @_; | 
| 454 |  |  |  |  |  |  |  | 
| 455 | 52 | 50 |  |  |  | 300 | croak "iota: Argument is not a positive integer >>$argument<<\n" | 
| 456 |  |  |  |  |  |  | if $argument !~ /^[+]?\d+$/; | 
| 457 |  |  |  |  |  |  |  | 
| 458 | 52 |  |  |  |  | 1012 | return bless [ 0 .. $_[0] - 1 ]; | 
| 459 |  |  |  |  |  |  | } | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | ############################################################################### | 
| 462 |  |  |  |  |  |  | # APX-methods: | 
| 463 |  |  |  |  |  |  | ############################################################################### | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | =head1 APX-methods | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | =head2 collapse | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | To convert an n-dimensional APX-structure into a one dimensional structure, | 
| 470 |  |  |  |  |  |  | the collapse-method is used: | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | use strict; | 
| 473 |  |  |  |  |  |  | use warnings; | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | print dress([[1, 2], [3, 4]])->collapse(); | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | yields | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | [    1    2    3    4 ] | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | =cut | 
| 484 |  |  |  |  |  |  |  | 
| 485 | 1 |  |  | 1 | 1 | 1092 | sub collapse { return bless Array::DeepUtils::collapse([@{$_[0]}]); } | 
|  | 1 |  |  |  |  | 5 |  | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | =head2 grade | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | The grade-method returns an index vector that can be used to sort the elements | 
| 490 |  |  |  |  |  |  | of the object, grade was applied to. For example | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | print dress([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5])->grade(); | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | yields | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | [    3    1    6    9    0    2    8    4   10    7    5 ] | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | So to sort the elements of the original object, the subscript-method could | 
| 499 |  |  |  |  |  |  | be applied with this vector as its argument. | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | =cut | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | sub grade | 
| 504 |  |  |  |  |  |  | { | 
| 505 | 1 |  |  | 1 | 1 | 32 | my ($data) = @_; | 
| 506 |  |  |  |  |  |  |  | 
| 507 | 1 |  |  |  |  | 4 | my %h = map { $_ => $data->[$_] } 0 .. @$data - 1; | 
|  | 7 |  |  |  |  | 16 |  | 
| 508 |  |  |  |  |  |  |  | 
| 509 | 1 |  |  |  |  | 14 | return bless [ sort { $h{$a} <=> $h{$b} } keys %h ]; | 
|  | 10 |  |  |  |  | 18 |  | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | =head2 in | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | This implements the set theoretic 'in'-function. It checks which elements of | 
| 515 |  |  |  |  |  |  | its left operand data structure are elements of the right hand data structure | 
| 516 |  |  |  |  |  |  | and returns a boolean vector that contains a 1 at corresponding locations | 
| 517 |  |  |  |  |  |  | of the left side operand that are elements of the right side operand. | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | use strict; | 
| 520 |  |  |  |  |  |  | use warnings; | 
| 521 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | my $x = iota(10); | 
| 524 |  |  |  |  |  |  | my $y = dress([5, 11, 3, 17, 2]); | 
| 525 |  |  |  |  |  |  | print "Boolean vector:\n", $y->in($x); | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | yields | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | Boolean vector: | 
| 530 |  |  |  |  |  |  | [    1    0    1    0    1 ] | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | Please note that the in-method operates on a one dimensional APX-object while | 
| 533 |  |  |  |  |  |  | its argument can be of any dimension >= 1. | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | =cut | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | # Set function 'in': | 
| 538 |  |  |  |  |  |  | sub in | 
| 539 |  |  |  |  |  |  | { | 
| 540 | 0 |  |  | 0 | 1 | 0 | my ($what, $where) = @_; | 
| 541 |  |  |  |  |  |  |  | 
| 542 | 0 | 0 |  |  |  | 0 | croak 'in: argument is not an APX-object: ', ref($where), "\n" | 
| 543 |  |  |  |  |  |  | unless ref($where) eq __PACKAGE__; | 
| 544 |  |  |  |  |  |  |  | 
| 545 | 0 |  |  |  |  | 0 | my @result; | 
| 546 | 0 |  |  |  |  | 0 | push (@result, _is_in($_, $where)) for (@$what); | 
| 547 | 0 |  |  |  |  | 0 | return bless \@result; | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | sub int | 
| 551 |  |  |  |  |  |  | { | 
| 552 | 1 |  |  | 1 | 0 | 2 | my $data = [@{$_[0]}]; | 
|  | 1 |  |  |  |  | 3 |  | 
| 553 | 1 |  |  | 3 |  | 8 | Array::DeepUtils::unary($data, sub { return int($_[0]) }); | 
|  | 3 |  |  |  |  | 166 |  | 
| 554 | 1 |  |  |  |  | 25 | return bless $data; | 
| 555 |  |  |  |  |  |  | } | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | =head2 index | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | The index-method returns an index vector containing the indices of the elements | 
| 560 |  |  |  |  |  |  | of the object it was applied to with respect to its argument which must be an | 
| 561 |  |  |  |  |  |  | APX-object, too. Thus | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | print dress([[1, 3], [4, 5]])->index(dress([[1, 2, 3], [4, 5, 6], [7, 8, 9]])); | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | yields | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | [ | 
| 568 |  |  |  |  |  |  | [ | 
| 569 |  |  |  |  |  |  | [    0    0 ] | 
| 570 |  |  |  |  |  |  | [    0    2 ] | 
| 571 |  |  |  |  |  |  | ] | 
| 572 |  |  |  |  |  |  | [ | 
| 573 |  |  |  |  |  |  | [    1    0 ] | 
| 574 |  |  |  |  |  |  | [    1    1 ] | 
| 575 |  |  |  |  |  |  | ] | 
| 576 |  |  |  |  |  |  | ] | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | =cut | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | sub index | 
| 582 |  |  |  |  |  |  | { | 
| 583 | 1 |  |  | 1 | 1 | 2 | my ($a, $b) = @_; | 
| 584 |  |  |  |  |  |  |  | 
| 585 | 1 | 50 |  |  |  | 5 | croak 'index: argument is not an APX-object: ', ref($b), "\n" | 
| 586 |  |  |  |  |  |  | unless ref($b) eq __PACKAGE__; | 
| 587 |  |  |  |  |  |  |  | 
| 588 | 1 |  |  |  |  | 6 | return bless Array::DeepUtils::idx([@$a], [@$b]); | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | =head2 remove | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | The remove-method removes elements from an APX-object controlled by an index | 
| 594 |  |  |  |  |  |  | vector supplied as its argument (which must be an APX-object, too): | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | print iota(10)->remove(dress([1, 3, 5])); | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | yields | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | [    0    2    4    6    7    8    9 ] | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | =cut | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | sub remove | 
| 605 |  |  |  |  |  |  | { | 
| 606 | 1 |  |  | 1 | 1 | 2 | my ($a, $b) = @_; | 
| 607 |  |  |  |  |  |  |  | 
| 608 | 1 | 50 |  |  |  | 5 | croak 'remove: argument is not an APX-object: ', ref($b), "\n" | 
| 609 |  |  |  |  |  |  | unless ref($b) eq __PACKAGE__; | 
| 610 |  |  |  |  |  |  |  | 
| 611 | 1 |  |  |  |  | 2 | $a = [@$a]; | 
| 612 | 1 |  |  |  |  | 5 | Array::DeepUtils::remove($a, [@$b]); | 
| 613 | 1 |  |  |  |  | 120 | return bless $a; | 
| 614 |  |  |  |  |  |  | } | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | =head2 reverse | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | The reverse-method reverses the sequence of elements in an APX-object, thus | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | print iota(5)->reverse(); | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | yields | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | [    4    3    2    1    0 ] | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | =cut | 
| 627 |  |  |  |  |  |  |  | 
| 628 | 1 |  |  | 1 | 1 | 1 | sub reverse { return bless [reverse(@{$_[0]})]; } | 
|  | 1 |  |  |  |  | 5 |  | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | =head2 rho | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | The reshape-method has fulfills a twofold function: If called without any | 
| 633 |  |  |  |  |  |  | argument it returns an APX-object describing the structure of the object it | 
| 634 |  |  |  |  |  |  | was applied to. If called with an APX-object as its parameter, the | 
| 635 |  |  |  |  |  |  | rho-method restructures the object it was applied to according to the | 
| 636 |  |  |  |  |  |  | dimension values specified in the parameter (please note that rho will | 
| 637 |  |  |  |  |  |  | reread values from the object it was applied to if there are not enough to | 
| 638 |  |  |  |  |  |  | fill the destination structure). The following code example | 
| 639 |  |  |  |  |  |  | shows both usages of rho: | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | use strict; | 
| 642 |  |  |  |  |  |  | use warnings; | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | my $x = iota(9); | 
| 647 |  |  |  |  |  |  | my $y = dress([3, 3]); | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  | print "Data rearranged as 3-times-3-matrix:\n", my $z = $x->rho($y); | 
| 650 |  |  |  |  |  |  | print 'Dimensionvector of this result: ', $z->rho(); | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | This test program yields the following output: | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | Data rearranged as 3-times-3-matrix: | 
| 655 |  |  |  |  |  |  | [ | 
| 656 |  |  |  |  |  |  | [    0    1    2 ] | 
| 657 |  |  |  |  |  |  | [    3    4    5 ] | 
| 658 |  |  |  |  |  |  | [    6    7    8 ] | 
| 659 |  |  |  |  |  |  | ] | 
| 660 |  |  |  |  |  |  | Dimensionvector of this result: [    3    3 ] | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | =cut | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | sub rho | 
| 665 |  |  |  |  |  |  | { | 
| 666 | 6 |  |  | 6 | 1 | 1422 | my ($data, $control) = @_; | 
| 667 |  |  |  |  |  |  |  | 
| 668 | 6 | 100 |  |  |  | 12 | if (!defined($control)) # Return a structure object | 
| 669 |  |  |  |  |  |  | { | 
| 670 | 1 |  |  |  |  | 6 | return bless Array::DeepUtils::shape([@$data]); | 
| 671 |  |  |  |  |  |  | } | 
| 672 |  |  |  |  |  |  | else | 
| 673 |  |  |  |  |  |  | { | 
| 674 | 5 | 50 |  |  |  | 15 | croak "rho: Control structure is not an APX-object!" | 
| 675 |  |  |  |  |  |  | if ref($control) ne __PACKAGE__; | 
| 676 |  |  |  |  |  |  |  | 
| 677 | 5 |  |  |  |  | 22 | return bless Array::DeepUtils::reshape([@$data], [@$control]); | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  | } | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | =head2 rotate | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | rotate rotates an APX-structure along several axes. The following example shows | 
| 684 |  |  |  |  |  |  | the rotation of a two dimensional data structure along its x- and y-axes by | 
| 685 |  |  |  |  |  |  | +1 and -1 positions respecitively: | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | print dress([[1, 2, 3], [4, 5, 6], [7, 8, 9]])->rotate(dress([1, -1])); | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  | The result of this rotation is thus | 
| 690 |  |  |  |  |  |  |  | 
| 691 |  |  |  |  |  |  | [ | 
| 692 |  |  |  |  |  |  | [    8    9    7 ] | 
| 693 |  |  |  |  |  |  | [    2    3    1 ] | 
| 694 |  |  |  |  |  |  | [    5    6    4 ] | 
| 695 |  |  |  |  |  |  | ] | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | =cut | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | sub rotate | 
| 700 |  |  |  |  |  |  | { | 
| 701 | 1 |  |  | 1 | 1 | 2 | my ($a, $b) = @_; | 
| 702 |  |  |  |  |  |  |  | 
| 703 | 1 | 50 |  |  |  | 4 | croak 'rotate: argument is not an APX-object: ', ref($b), "\n" | 
| 704 |  |  |  |  |  |  | unless ref($b) eq __PACKAGE__; | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 1 |  |  |  |  | 6 | return bless Array::DeepUtils::rotate([@$a], [@$b]); | 
| 707 |  |  |  |  |  |  | } | 
| 708 |  |  |  |  |  |  |  | 
| 709 |  |  |  |  |  |  | =head2 scatter | 
| 710 |  |  |  |  |  |  |  | 
| 711 |  |  |  |  |  |  | The scatter-method is the inverse of subscript. While subscript selects | 
| 712 |  |  |  |  |  |  | values from an APX-object, controlled by an index vector, scatter creates | 
| 713 |  |  |  |  |  |  | a new data structure with elements read from the APX-object it was applied | 
| 714 |  |  |  |  |  |  | to and their positions controlled by an index vector. The following example | 
| 715 |  |  |  |  |  |  | shows the use of scatter: | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | print (iota(7) + 1)->scatter(dress([[0, ,0], [0, 1], [1, 0], [1, 1]])); | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | yields | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | [ | 
| 722 |  |  |  |  |  |  | [    1    2 ] | 
| 723 |  |  |  |  |  |  | [    3    4 ] | 
| 724 |  |  |  |  |  |  | ] | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | =cut | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | sub scatter | 
| 729 |  |  |  |  |  |  | { | 
| 730 | 1 |  |  | 1 | 1 | 2 | my ($a, $b) = @_; | 
| 731 |  |  |  |  |  |  |  | 
| 732 | 1 | 50 |  |  |  | 5 | croak 'scatter: argument is not an APX-object: ', ref($b), "\n" | 
| 733 |  |  |  |  |  |  | unless ref($b) eq __PACKAGE__; | 
| 734 |  |  |  |  |  |  |  | 
| 735 | 1 |  |  |  |  | 4 | return bless Array::DeepUtils::scatter([@$a], [@$b]); | 
| 736 |  |  |  |  |  |  | } | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | =head2 select | 
| 739 |  |  |  |  |  |  |  | 
| 740 |  |  |  |  |  |  | The select-method is applied to a boolean vector and selects those elements | 
| 741 |  |  |  |  |  |  | from its argument vector that correspond to places containing a true value | 
| 742 |  |  |  |  |  |  | in the boolean vector. Thus | 
| 743 |  |  |  |  |  |  |  | 
| 744 |  |  |  |  |  |  | use strict; | 
| 745 |  |  |  |  |  |  | use warnings; | 
| 746 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 747 |  |  |  |  |  |  |  | 
| 748 |  |  |  |  |  |  | my $x = iota(10) + 1; | 
| 749 |  |  |  |  |  |  | my $s = dress([0, 1, 1, 0, 1, 0, 1]); | 
| 750 |  |  |  |  |  |  |  | 
| 751 |  |  |  |  |  |  | print $x->select($s); | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | yields | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | [    2    3    5    7 ] | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | Please note that select works along the first dimension of the APX-object it is | 
| 758 |  |  |  |  |  |  | applied to and expects a one dimensional APX-objects as its argument. | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | =cut | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  | sub select | 
| 763 |  |  |  |  |  |  | { | 
| 764 | 0 |  |  | 0 | 1 | 0 | my ($data, $control) = @_; | 
| 765 |  |  |  |  |  |  |  | 
| 766 | 0 | 0 |  |  |  | 0 | croak 'select: argument is not an APX-object: ', ref($control), "\n" | 
| 767 |  |  |  |  |  |  | unless ref($control) eq __PACKAGE__; | 
| 768 |  |  |  |  |  |  |  | 
| 769 | 0 |  |  |  |  | 0 | my @result; | 
| 770 | 0 |  |  |  |  | 0 | for my $i ( 0 .. @$control - 1 ) | 
| 771 |  |  |  |  |  |  | { | 
| 772 | 0 | 0 |  |  |  | 0 | push (@result, $data->[$i]) if $control->[$i]; | 
| 773 |  |  |  |  |  |  | } | 
| 774 |  |  |  |  |  |  |  | 
| 775 | 0 |  |  |  |  | 0 | return bless \@result; | 
| 776 |  |  |  |  |  |  | } | 
| 777 |  |  |  |  |  |  |  | 
| 778 |  |  |  |  |  |  | =head2 slice | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | slice extracts part of a nested data structure controlled by a coordinate | 
| 781 |  |  |  |  |  |  | vector as the following example shows: | 
| 782 |  |  |  |  |  |  |  | 
| 783 |  |  |  |  |  |  | print (iota(9) + 1)->rho(dress([3, 3]))->slice(dress([[1, 0], [2, 1]])); | 
| 784 |  |  |  |  |  |  |  | 
| 785 |  |  |  |  |  |  | yields | 
| 786 |  |  |  |  |  |  |  | 
| 787 |  |  |  |  |  |  | [ | 
| 788 |  |  |  |  |  |  | [    4    5 ] | 
| 789 |  |  |  |  |  |  | [    7    8 ] | 
| 790 |  |  |  |  |  |  | ] | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | =cut | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | sub slice | 
| 795 |  |  |  |  |  |  | { | 
| 796 | 1 |  |  | 1 | 1 | 3 | my ($data, $control) = @_; | 
| 797 |  |  |  |  |  |  |  | 
| 798 | 1 | 50 |  |  |  | 4 | croak 'slice: argument is not an APX-object: ', ref($control), "\n" | 
| 799 |  |  |  |  |  |  | unless ref($control) eq __PACKAGE__; | 
| 800 |  |  |  |  |  |  |  | 
| 801 | 1 |  |  |  |  | 7 | return bless Array::DeepUtils::dcopy([@$data], [@$control]); | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | =head2 strip | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | strip is the inverse function to dress() - it is applied to an APX data | 
| 807 |  |  |  |  |  |  | structure and returns a plain vanilla Perl array: | 
| 808 |  |  |  |  |  |  |  | 
| 809 |  |  |  |  |  |  | use strict; | 
| 810 |  |  |  |  |  |  | use warnings; | 
| 811 |  |  |  |  |  |  | use Array::APX qw(:all); | 
| 812 |  |  |  |  |  |  | use Data::Dumper; | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | my $x = iota(3); | 
| 815 |  |  |  |  |  |  | print Dumper($x->strip); | 
| 816 |  |  |  |  |  |  |  | 
| 817 |  |  |  |  |  |  | yields | 
| 818 |  |  |  |  |  |  |  | 
| 819 |  |  |  |  |  |  | $VAR1 = [ | 
| 820 |  |  |  |  |  |  | 0, | 
| 821 |  |  |  |  |  |  | 1, | 
| 822 |  |  |  |  |  |  | 2 | 
| 823 |  |  |  |  |  |  | ]; | 
| 824 |  |  |  |  |  |  |  | 
| 825 |  |  |  |  |  |  | =cut | 
| 826 |  |  |  |  |  |  |  | 
| 827 | 1 |  |  | 1 | 1 | 2 | sub strip { return [@{$_[0]}]; } | 
|  | 1 |  |  |  |  | 4 |  | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | =head2 subscript | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | The subscript-method retrieves values from a nested APX-data structure | 
| 832 |  |  |  |  |  |  | controlled by an index vector (an APX-object, too) as the following simple | 
| 833 |  |  |  |  |  |  | example shows: | 
| 834 |  |  |  |  |  |  |  | 
| 835 |  |  |  |  |  |  | print (iota(9) + 1)->rho(dress([3, 3]))->subscript(dress([1])); | 
| 836 |  |  |  |  |  |  |  | 
| 837 |  |  |  |  |  |  | returns the element with the index 1 from a two dimensional data structure | 
| 838 |  |  |  |  |  |  | that contains the values 1 to 9 yielding: | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | [ | 
| 841 |  |  |  |  |  |  | [    4    5    6 ] | 
| 842 |  |  |  |  |  |  | ] | 
| 843 |  |  |  |  |  |  |  | 
| 844 |  |  |  |  |  |  | =cut | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | sub subscript | 
| 847 |  |  |  |  |  |  | { | 
| 848 | 1 |  |  | 1 | 1 | 3 | my ($data, $control) = @_; | 
| 849 |  |  |  |  |  |  |  | 
| 850 | 1 | 50 |  |  |  | 5 | croak 'subscript: argument is not an APX-object: ', ref($control), "\n" | 
| 851 |  |  |  |  |  |  | unless ref($control) eq __PACKAGE__; | 
| 852 |  |  |  |  |  |  |  | 
| 853 | 1 |  |  |  |  | 7 | return bless Array::DeepUtils::subscript([@$data], [@$control]); | 
| 854 |  |  |  |  |  |  | } | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | =head2 transpose | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  | transpose is used to transpose a nested APX-structure along any of its axes. | 
| 859 |  |  |  |  |  |  | In the easiest two dimensional case this corresponds to the traditional | 
| 860 |  |  |  |  |  |  | matrix transposition, thus | 
| 861 |  |  |  |  |  |  |  | 
| 862 |  |  |  |  |  |  | print (iota(9) + 1)->rho(dress([3, 3]))->transpose(1); | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | yields | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | [ | 
| 867 |  |  |  |  |  |  | [    1    4    7 ] | 
| 868 |  |  |  |  |  |  | [    2    5    8 ] | 
| 869 |  |  |  |  |  |  | [    3    6    9 ] | 
| 870 |  |  |  |  |  |  | ] | 
| 871 |  |  |  |  |  |  |  | 
| 872 |  |  |  |  |  |  | =cut | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | sub transpose | 
| 875 |  |  |  |  |  |  | { | 
| 876 | 1 |  |  | 1 | 1 | 467 | my ($data, $control) = @_; | 
| 877 |  |  |  |  |  |  |  | 
| 878 | 1 | 50 |  |  |  | 7 | croak "transpose: argument is not an integer: >>$control<<\n" | 
| 879 |  |  |  |  |  |  | if $control !~ /^[+-]?\d+/; | 
| 880 |  |  |  |  |  |  |  | 
| 881 | 1 |  |  |  |  | 8 | return bless Array::DeepUtils::transpose([@$data], $control); | 
| 882 |  |  |  |  |  |  | } | 
| 883 |  |  |  |  |  |  |  | 
| 884 |  |  |  |  |  |  | ############################################################################### | 
| 885 |  |  |  |  |  |  | # Support functions - not to be exported (these are mostly lend from Lang5). | 
| 886 |  |  |  |  |  |  | ############################################################################### | 
| 887 |  |  |  |  |  |  |  | 
| 888 |  |  |  |  |  |  | # Apply a binary word to a nested data structure. | 
| 889 |  |  |  |  |  |  | sub _binary { | 
| 890 | 172 |  |  | 172 |  | 191 | my $func = $_[2]; | 
| 891 |  |  |  |  |  |  |  | 
| 892 |  |  |  |  |  |  | # both operands not array refs -> exec and early return | 
| 893 | 172 | 100 | 66 |  |  | 577 | if ( ref($_[0]) ne 'ARRAY' and ref($_[1]) ne 'ARRAY' ) { | 
| 894 | 117 |  |  |  |  | 218 | $_[1] = $func->($_[0], $_[1]); | 
| 895 | 117 |  |  |  |  | 279 | return 1; | 
| 896 |  |  |  |  |  |  | } | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | # no eval because _binary will be called in an eval {} | 
| 899 | 55 |  |  |  |  | 170 | Array::DeepUtils::binary($_[0], $_[1], $func); | 
| 900 |  |  |  |  |  |  |  | 
| 901 | 55 |  |  |  |  | 1981 | return 1; | 
| 902 |  |  |  |  |  |  | } | 
| 903 |  |  |  |  |  |  |  | 
| 904 |  |  |  |  |  |  | # Implements '.'; dump a scalar or structure to text. | 
| 905 |  |  |  |  |  |  | sub _stringify { | 
| 906 | 2 |  |  | 2 |  | 8 | my($element) = @_; | 
| 907 | 2 |  |  |  |  | 5 | $element = [@$element]; | 
| 908 |  |  |  |  |  |  |  | 
| 909 |  |  |  |  |  |  | # shortcut for simple scalars | 
| 910 | 2 | 50 | 33 |  |  | 17 | if ( !ref($element) or ref($element) eq 'Lang5::String' ) { | 
| 911 | 0 | 0 |  |  |  | 0 | $element = 'undef' unless defined $element; | 
| 912 | 0 | 0 |  |  |  | 0 | $element .= "\n" | 
| 913 |  |  |  |  |  |  | if $element =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/; | 
| 914 | 0 |  |  |  |  | 0 | return $element; | 
| 915 |  |  |  |  |  |  | } | 
| 916 |  |  |  |  |  |  |  | 
| 917 | 2 |  |  |  |  | 3 | my $indent = 2; | 
| 918 | 2 |  |  |  |  | 3 | my @estack = ( $element ); | 
| 919 | 2 |  |  |  |  | 3 | my @istack = ( 0 ); | 
| 920 |  |  |  |  |  |  |  | 
| 921 | 2 |  |  |  |  | 4 | my $txt = ''; | 
| 922 |  |  |  |  |  |  |  | 
| 923 | 2 |  |  |  |  | 5 | while ( @estack ) { | 
| 924 |  |  |  |  |  |  |  | 
| 925 | 6 |  |  |  |  | 8 | my $e = $estack[-1]; | 
| 926 | 6 |  |  |  |  | 5 | my $i = $istack[-1]; | 
| 927 |  |  |  |  |  |  |  | 
| 928 |  |  |  |  |  |  | # new array: output opening bracket | 
| 929 | 6 | 100 |  |  |  | 12 | if ( $i == 0 ) { | 
| 930 | 2 | 50 |  |  |  | 4 | if ( $txt ) { | 
| 931 | 0 |  |  |  |  | 0 | $txt .= "\n"; | 
| 932 | 0 |  |  |  |  | 0 | $txt .= ' ' x ( $indent * ( @istack - 1 ) ); | 
| 933 |  |  |  |  |  |  | } | 
| 934 | 2 |  |  |  |  | 4 | $txt .= '['; | 
| 935 |  |  |  |  |  |  | } | 
| 936 |  |  |  |  |  |  |  | 
| 937 | 6 | 50 |  |  |  | 13 | if ( $i <= $#$e  ) { | 
| 938 |  |  |  |  |  |  | # push next reference and a new index onto stacks | 
| 939 | 6 | 50 | 33 |  |  | 14 | if ( ref($e->[$i]) and ref($e->[$i]) ne 'Lang5::String' ) { | 
| 940 | 0 |  |  |  |  | 0 | push @estack, $e->[$i]; | 
| 941 | 0 |  |  |  |  | 0 | push @istack, 0; | 
| 942 | 0 |  |  |  |  | 0 | next; | 
| 943 |  |  |  |  |  |  | } | 
| 944 |  |  |  |  |  |  |  | 
| 945 |  |  |  |  |  |  | # output element | 
| 946 | 6 | 50 |  |  |  | 12 | if ( $txt =~ /\]$/ ) { | 
| 947 | 0 |  |  |  |  | 0 | $txt .= "\n"; | 
| 948 | 0 |  |  |  |  | 0 | $txt .= ' ' x ( $indent * @istack ); | 
| 949 |  |  |  |  |  |  | } else { | 
| 950 | 6 |  |  |  |  | 7 | $txt .= ' '; | 
| 951 |  |  |  |  |  |  | } | 
| 952 | 6 | 50 |  |  |  | 17 | $txt .= defined($e->[$i]) ? sprintf("%4s", $e->[$i]) : 'undef'; | 
| 953 |  |  |  |  |  |  | } | 
| 954 |  |  |  |  |  |  |  | 
| 955 |  |  |  |  |  |  | # after last item, close arrays | 
| 956 |  |  |  |  |  |  | # on an own line and indent next line | 
| 957 | 6 | 100 |  |  |  | 13 | if ( $i >= $#$e ) { | 
| 958 |  |  |  |  |  |  |  | 
| 959 | 2 |  |  |  |  | 10 | my($ltxt) = $txt =~ /(?:\A|\n)([^\n]*?)$/; | 
| 960 |  |  |  |  |  |  |  | 
| 961 |  |  |  |  |  |  | #  The current text should not end in a closing bracket as it | 
| 962 |  |  |  |  |  |  | # would if we had typed an array and it should not end in a | 
| 963 |  |  |  |  |  |  | # parenthesis as it would if we typed an array with an object | 
| 964 |  |  |  |  |  |  | # type . | 
| 965 | 2 | 50 | 33 |  |  | 14 | if ( $ltxt =~ /\[/ and $ltxt !~ /\]|\)$/ ) { | 
| 966 | 2 |  |  |  |  | 5 | $txt .= ' '; | 
| 967 |  |  |  |  |  |  | } else { | 
| 968 | 0 |  |  |  |  | 0 | $txt .= "\n"; | 
| 969 | 0 |  |  |  |  | 0 | $txt .= ' ' x ( $indent * ( @istack - 1 ) ); | 
| 970 |  |  |  |  |  |  | } | 
| 971 | 2 |  |  |  |  | 2 | $txt .= ']'; | 
| 972 |  |  |  |  |  |  |  | 
| 973 |  |  |  |  |  |  | # Did we print an element that had an object type set? | 
| 974 | 2 |  |  |  |  | 4 | my $last_type = ref(pop @estack); | 
| 975 | 2 | 50 | 33 |  |  | 12 | $txt .= "($last_type)" | 
|  |  |  | 33 |  |  |  |  | 
| 976 |  |  |  |  |  |  | if $last_type | 
| 977 |  |  |  |  |  |  | and | 
| 978 |  |  |  |  |  |  | $last_type ne 'ARRAY' | 
| 979 |  |  |  |  |  |  | and | 
| 980 |  |  |  |  |  |  | $last_type ne 'Lang5::String'; | 
| 981 | 2 |  |  |  |  | 3 | pop @istack; | 
| 982 |  |  |  |  |  |  | } | 
| 983 |  |  |  |  |  |  |  | 
| 984 | 6 | 100 |  |  |  | 19 | $istack[-1]++ | 
| 985 |  |  |  |  |  |  | if @istack; | 
| 986 |  |  |  |  |  |  | } | 
| 987 |  |  |  |  |  |  |  | 
| 988 | 2 | 50 |  |  |  | 6 | $txt .= "\n" unless $txt =~ /\n$/; | 
| 989 |  |  |  |  |  |  |  | 
| 990 | 2 |  |  |  |  | 10 | return $txt; | 
| 991 |  |  |  |  |  |  | } | 
| 992 |  |  |  |  |  |  |  | 
| 993 |  |  |  |  |  |  | # Return 1 if a scalar element is found in a structure (set operation in). | 
| 994 |  |  |  |  |  |  | sub _is_in | 
| 995 |  |  |  |  |  |  | { | 
| 996 | 0 |  |  | 0 |  |  | my($el, $data) = @_; | 
| 997 |  |  |  |  |  |  |  | 
| 998 | 0 |  |  |  |  |  | for my $d ( @$data ) | 
| 999 |  |  |  |  |  |  | { | 
| 1000 | 0 | 0 |  |  |  |  | if ( ref($d) eq 'ARRAY' ) | 
| 1001 |  |  |  |  |  |  | { | 
| 1002 | 0 | 0 |  |  |  |  | return 1 if _is_in($el, $d); | 
| 1003 |  |  |  |  |  |  | } | 
| 1004 |  |  |  |  |  |  |  | 
| 1005 | 0 | 0 |  |  |  |  | return 1 if $el eq $d; | 
| 1006 |  |  |  |  |  |  | } | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 | 0 |  |  |  |  |  | return 0; | 
| 1009 |  |  |  |  |  |  | } | 
| 1010 |  |  |  |  |  |  |  | 
| 1011 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 |  |  |  |  |  |  | Array::APX relies mainly on Array::Deeputils which, in turn, was developed | 
| 1014 |  |  |  |  |  |  | for the interpreter of the array programming language Lang5. The source of | 
| 1015 |  |  |  |  |  |  | Array::Deeputils is maintained in the source repository of Lang. In addition | 
| 1016 |  |  |  |  |  |  | to that Array::APX borrows some basic functions of the Lang5 interpreter | 
| 1017 |  |  |  |  |  |  | itself, too. | 
| 1018 |  |  |  |  |  |  |  | 
| 1019 |  |  |  |  |  |  | =head2 Links | 
| 1020 |  |  |  |  |  |  |  | 
| 1021 |  |  |  |  |  |  | =over | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 |  |  |  |  |  |  | =item * | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | L. | 
| 1026 |  |  |  |  |  |  |  | 
| 1027 |  |  |  |  |  |  | =back | 
| 1028 |  |  |  |  |  |  |  | 
| 1029 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 |  |  |  |  |  |  | Bernd Ulmann Eulmann@vaxman.deE | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | Thomas Kratz Etomk@cpan.orgE | 
| 1034 |  |  |  |  |  |  |  | 
| 1035 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 1036 |  |  |  |  |  |  |  | 
| 1037 |  |  |  |  |  |  | Copyright (C) 2012 by Bernd Ulmann, Thomas Kratz | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or | 
| 1040 |  |  |  |  |  |  | modify it under the same terms as Perl itself, either Perl version | 
| 1041 |  |  |  |  |  |  | 5.8.8 or, at your option, any later version of Perl 5 you may | 
| 1042 |  |  |  |  |  |  | have available. | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 |  |  |  |  |  |  | =cut | 
| 1045 |  |  |  |  |  |  |  | 
| 1046 |  |  |  |  |  |  | 1; |