File Coverage

blib/lib/Array/APX.pm
Criterion Covered Total %
statement 226 257 87.9
branch 78 118 66.1
condition 22 42 52.3
subroutine 50 53 94.3
pod 19 30 63.3
total 395 500 79.0


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;