File Coverage

blib/lib/Sub/Fp.pm
Criterion Covered Total %
statement 115 115 100.0
branch 34 36 94.4
condition 16 19 84.2
subroutine 29 29 100.0
pod 3 3 100.0
total 197 202 97.5


line stmt bran cond sub pod time code
1             package Sub::Fp;
2 2     2   163279 use strict;
  2         10  
  2         59  
3 2     2   10 use warnings;
  2         3  
  2         56  
4 2     2   11 use Carp;
  2         4  
  2         181  
5 2     2   986 use POSIX;
  2         12936  
  2         10  
6 2     2   5681 use List::Util;
  2         4  
  2         154  
7 2     2   1288 use Data::Dumper qw(Dumper);
  2         14518  
  2         146  
8 2     2   16 use Exporter qw(import);
  2         4  
  2         137  
9             our $VERSION = '0.43';
10             our @EXPORT_OK = qw(
11             incr reduces flatten
12             drop_right drop take_right take
13             assoc maps decr chain
14             first end subarray partial
15             __ find filter some
16             none uniq bool spread
17             len to_keys to_vals is_array
18             is_hash every noop identity
19             is_empty is_sub flow eql
20             to_pairs for_each apply get
21             second range pops pushes
22             shifts unshifts once
23             );
24 2     2   11 use constant ARG_PLACE_HOLDER => {};
  2         4  
  2         689  
25              
26             $SIG{__WARN__} = sub {
27             caller eq "Sub::Fp" ? carp $_[0] : warn $_[0];
28             };
29              
30             $SIG{__DIE__} = sub {
31             caller eq "Sub::Fp" ? croak $_[0] : die $_[0];
32             };
33              
34             _wrap_to_use_partials(
35             grep { $_ !~ /flow|flow_right|partial|chain/ } @EXPORT_OK
36             );
37              
38             # -----------------------------------------------------------------------------#
39              
40             sub __ { ARG_PLACE_HOLDER };
41              
42             sub _contains_placeholders {
43 4084     4084   5467 my $arguments = [@_];
44              
45 4084         4914 foreach my $arg (@{ $arguments }) {
  4084         6055  
46 1903 100 66     3834 if (ref($arg) and ref(__) and $arg == __) {
      100        
47 9         26 return 1;
48             }
49             }
50 4075         8498 return 0;
51             }
52              
53             sub _wrap {
54             ##no critic
55 2     2   18 no warnings 'redefine';
  2         4  
  2         96  
56 2     2   29 no strict 'refs';
  2         4  
  2         6982  
57              
58 86     86   138 my ($methodName, $decorator) = @_;
59 86         114 my $module = caller;
60 86         155 my $fullName = "$module"."::"."$methodName";
61 86         90 my $wrappedSub = *{"$fullName"}{CODE};
  86         181  
62              
63 86         341 *{"$fullName"} = List::Util::reduce {
64 86     86   124 my ($wrappedSub, $decor) = ($a, $b);
65 86         132 return $decor->($wrappedSub);
66 86         287 } ($wrappedSub, $decorator);
67             }
68              
69             sub _can_use_placeholders {
70 86     86   104 my $func = shift;
71              
72             return sub {
73 4053 50   4053   396980 return _contains_placeholders(@_) ? partial($func, @_) : $func->(@_);
74             }
75 86         344 }
76              
77             sub _wrap_to_use_partials {
78 2     2   6 foreach my $func_name (@_) {
79 86         210 _wrap($func_name, \&_can_use_placeholders);
80             }
81             }
82              
83             sub noop {
84             ## no critic;
85             return undef;
86             }
87              
88             sub identity {
89             my $args = shift // undef;
90              
91             return $args;
92             }
93              
94             sub once {
95             my $func = shift // \&noop;
96             my $was_called_once = 0;
97             my $result;
98              
99             return sub {
100             if ($was_called_once) {
101             return $result;
102             }
103              
104             $was_called_once++;
105             $result = $func->(@_);
106             return $result;
107             }
108             }
109              
110             # Forgive me below father, for I have sinned.
111             # Seriously, I can't figure a simpler way to do this...
112             # TODO: Please refactor this eventually...
113             sub range {
114             my ($start, $end, $step) = @_;
115              
116             if (!defined $start) {
117             return [];
118             }
119              
120             if (!defined $end) {
121             return range(0, $start, $start < 0 ? -1 : 1);
122             }
123              
124             if (!defined $step) {
125             return range($start, $end, $end < 0 ? -1 : 1);
126             }
127              
128             if (_is_nonsense_range($start, $end, $step)) {
129             return [];
130             }
131              
132             my $loop_count = ceil(abs(($end - $start) / ($step || 1)));
133             my $list = [];
134              
135             while ($loop_count) {
136             push(@{ $list }, $start);
137              
138             $start+=$step;
139             $loop_count-=1;
140             }
141              
142             return $list;
143             }
144              
145             sub _is_nonsense_range {
146 22     22   36 my ($start, $end, $step) = @_;
147              
148 22 100 100     50 if ($start == $end &&
149             $end == $step) {
150 1         4 return 1;
151             }
152              
153 21 100 100     63 if ($start > $end &&
154             $step >= 0 ) {
155 3         9 return 1;
156             }
157              
158 18 100 100     87 if ($start < $end &&
159             $step < 0) {
160 3         9 return 1;
161             }
162             }
163              
164              
165             sub _safe_get {
166 22   50 22   52 my $key = shift // 0;
167 22   50     47 my $coll = shift // [];
168 22         30 my $default = shift;
169              
170 22 100       32 if (is_array($coll)) {
171 12 100       101 return defined $coll->[$key] ? $coll->[$key] : $default;
172             }
173              
174 10 100       16 if (is_hash($coll)) {
175 9 100       67 return defined $coll->{$key} ? $coll->{$key} : $default;
176             }
177              
178 1         4 my $string_coll = [spread($coll)];
179              
180 1 50       14 return defined $string_coll->[$key] ? $string_coll->[$key] : $default;
181             }
182              
183             sub get {
184             my ($path, $coll, $default) = @_;
185              
186             if (!defined $path) {
187             return $default;
188             }
189              
190             my $accessors = [$path =~ /([\w\s-]+)/g];
191              
192             return reduces(sub {
193             my ($value, $accessor) = @_;
194             return _safe_get($accessor, $value, $default);
195             }, $coll, $accessors)
196             }
197              
198             sub apply {
199             my $fn = shift // sub {};
200             my $args = shift // [];
201              
202             return $fn->(@{$args});
203             }
204              
205             sub flow {
206 5     5 1 7242 my $funcs = [@_];
207              
208 5 100       20 if (ref $funcs->[0] ne 'CODE') {
209 1         5 return \&noop;
210             }
211              
212             return sub {
213 3     3   64 my $args = [@_];
214              
215             return chain(
216 3         6 sub { first($funcs)->(spread($args)) },
217 3         17 spread(drop($funcs)),
218             );
219             }
220 4         19 }
221              
222             sub is_sub {
223             my $sub = shift;
224              
225             return bool(eql(ref $sub, 'CODE'));
226             }
227              
228             sub is_array {
229             my $coll = shift;
230             my $extra_args = [@_];
231              
232             if (len($extra_args)) {
233             return 0;
234             }
235              
236             return bool(ref $coll eq 'ARRAY');
237             }
238              
239             sub is_hash {
240             my $coll = shift;
241              
242             return bool(ref $coll eq 'HASH');
243             }
244              
245             sub to_pairs {
246             my $coll = shift // [];
247              
248             if (is_array($coll)) {
249             return maps(sub {
250             my ($val, $idx) = @_;
251             return [$idx, $val]
252             }, $coll)
253             }
254              
255             if (is_hash($coll)) {
256             return maps(sub {
257             my $key = shift;
258             return [$key, $coll->{$key}]
259             }, to_keys($coll))
260             }
261              
262             return maps(sub {
263             my ($char, $idx) = @_;
264             return [$idx, $char];
265             }, to_vals($coll));
266             }
267              
268             sub to_vals {
269             my $coll = shift // [];
270              
271             if (is_array($coll)) {
272             return $coll;
273             }
274              
275             if (is_hash($coll)) {
276             return [values %{ $coll }];
277             }
278              
279             return [spread($coll)];
280             }
281              
282             sub to_keys {
283             my $coll = shift // [];
284              
285             #Backwards compatibility < v5.12
286             if (is_array($coll)) {
287             return maps(sub {
288             my (undef, $idx) = @_;
289             return $idx;
290             }, $coll);
291             }
292              
293             if (is_hash($coll)) {
294             return [keys %{ $coll }];
295             }
296              
297             return maps(sub {
298             my (undef, $idx) = @_;
299             return $idx;
300             }, [spread($coll)])
301             }
302              
303             sub len {
304             my $coll = shift || [];
305              
306             if (ref $coll eq 'ARRAY') {
307             return scalar spread($coll);
308             }
309              
310             if (ref $coll eq 'HASH') {
311             return scalar (keys %{ $coll });
312             }
313              
314             return length($coll);
315             }
316              
317             sub for_each {
318             my ($fn, $coll) = @_;
319             my $idx = 0;
320              
321             foreach my $val (@{ $coll }) {
322             $idx++;
323             $fn->($val, $idx - 1, $coll);
324             }
325              
326             ##no critic;
327             return undef;
328             }
329              
330             sub is_empty {
331             my $coll = shift;
332             return bool(len($coll) == 0);
333             }
334              
335             sub uniq {
336             my $coll = shift;
337              
338             my @vals = do {
339             my %seen;
340             grep { !$seen{$_}++ } @$coll;
341             };
342              
343             return [@vals];
344             }
345              
346             sub find {
347             my $pred = shift;
348             my $coll = shift // [];
349              
350             return List::Util::first {
351             $pred->($_)
352             } @$coll;
353             }
354              
355             sub filter {
356             my $pred = shift;
357             my $coll = shift // [];
358              
359             return [grep { $pred->($_) } @$coll];
360             }
361              
362             sub some {
363             my $pred = shift;
364             my $coll = shift // [];
365              
366             return bool(find($pred, $coll));
367             }
368              
369             sub every {
370             my $pred = shift;
371             my $coll = shift // [];
372              
373             my $bool = List::Util::all {
374             $pred->($_);
375             } @$coll;
376              
377             return bool($bool);
378             }
379              
380             sub none {
381             my $pred = shift;
382             my $coll = shift // [];
383              
384             return some($pred, $coll) ? 0 : 1;
385             }
386              
387             sub incr {
388             my $num = shift;
389             return $num + 1;
390             }
391              
392             sub decr {
393             my $num = shift;
394             return $num - 1;
395             }
396              
397             sub first {
398             my $coll = shift;
399             return @$coll[0];
400             }
401              
402             sub second {
403             my $coll = shift;
404             return @$coll[1];
405             }
406              
407             sub end {
408             my $coll = shift // [];
409             my $len = scalar @$coll;
410              
411             return @$coll[$len - 1 ];
412             }
413              
414             sub flatten {
415             my $coll = shift;
416              
417             return [
418             map {
419             ref $_ ? @{$_} : $_;
420             } @$coll
421             ];
422             }
423              
424             sub pops {
425             my ($array, $val) = @_;
426              
427             return pop @{$array};
428             }
429              
430             sub pushes {
431             my ($array, $val) = @_;
432              
433             return push @{$array}, $val;
434             }
435              
436             sub shifts {
437             my ($array, $val) = @_;
438              
439             return shift @{$array};
440             }
441              
442             sub unshifts {
443             my ($array, $val) = @_;
444              
445             return unshift @{$array}, $val;
446             }
447              
448             sub _prepare_args {
449 65     65   148 my $args = [@_];
450 65 100       118 my $count = len($args) > 1 ? $args->[0] : 1;
451 65 100       114 my $coll = len($args) > 1 ? $args->[1] : $args->[0];
452 65         109 my $coll_len = len($coll);
453              
454 65         167 return ($coll, $count, $coll_len)
455             }
456              
457             sub drop {
458             my ($coll, $count, $coll_len) = _prepare_args(@_);
459              
460             return [@$coll[$count .. $coll_len - 1]];
461             }
462              
463             sub drop_right {
464             my ($coll, $count, $coll_len) = _prepare_args(@_);
465              
466             return [@$coll[0 .. ($coll_len - ($count + 1))]];
467             }
468              
469             sub take {
470             my ($coll, $count, $coll_len) = _prepare_args(@_);
471              
472             if (!$coll_len) {
473             return [];
474             }
475              
476             if ($count >= $coll_len ) {
477             return $coll;
478             }
479              
480             return [@$coll[0 .. $count - 1]];
481             }
482              
483             sub take_right {
484             my ($coll, $count, $coll_len) = _prepare_args(@_);
485              
486             if (!$coll_len) {
487             return [];
488             }
489              
490             if ($count >= $coll_len ) {
491             return $coll;
492             }
493              
494             return [@$coll[($coll_len - $count) .. ($coll_len - 1)]];
495             }
496              
497             sub assoc {
498             my ($obj, $key, $item) = @_;
499              
500             if (!defined $key) {
501             return $obj;
502             }
503              
504             if (ref $obj eq 'ARRAY') {
505             return [
506             @{(take($key, $obj))},
507             $item,
508             @{(drop($key + 1, $obj))},
509             ];
510             }
511              
512             return {
513             %{ $obj },
514             $key => $item,
515             };
516             }
517              
518             sub maps {
519             my $func = shift;
520             my $coll = shift;
521              
522             my $idx = 0;
523              
524             if (!is_sub($func) && defined $coll) {
525             return maps(get($func, __), $coll);
526             }
527              
528             my @vals = map {
529              
530             $idx++;
531             $func->($_, $idx - 1, $coll);
532             } @$coll;
533              
534             return [@vals];
535             }
536              
537              
538             sub reduces {
539             my $func = shift;
540             my ($accum, $coll) = spread(_get_reduces_args([@_]));
541              
542             my $idx = 0;
543              
544             return List::Util::reduce {
545             my ($accum, $val) = ($a, $b);
546             $idx++;
547             $func->($accum, $val, $idx - 1, $coll);
548             } ($accum, @$coll);
549             }
550              
551             sub _get_reduces_args {
552 20     20   26 my $args = shift;
553              
554 20 100       66 if (eql(len($args), 1)) {
555             return chain(
556             $args,
557             \&flatten,
558             sub {
559 1     1   4 return [first($_[0]), drop($_[0])]
560             }
561             )
562 1         10 }
563              
564 19         35 return [first($args), flatten(drop($args))];
565             }
566              
567             sub partial {
568 13     13 1 12089 my $func = shift;
569 13         27 my $oldArgs = [@_];
570              
571 13 100       38 if (ref $func ne 'CODE') {
572 2         256 carp("Expected a function as first argument");
573             }
574              
575             return sub {
576 18     18   84 my $newArgs = [@_];
577 18         36 my $filled_args = _fill_holders([spread($oldArgs)], $newArgs);
578              
579 18         32 return $func->(spread($filled_args));
580             }
581 11         42 }
582              
583              
584             #Once again, forgive me for I have sinned...
585             sub _fill_holders {
586 18     18   31 my ($old_args, $new_args) = @_;
587              
588 18         26 my $filled_args = [];
589 18         28 my $old_args_len = len($old_args);
590              
591 18         41 for (my $idx = 0; $idx < $old_args_len; $idx++) {
592 31         34 my $arg = shift @{ $old_args };
  31         40  
593              
594 31 100       50 if(_contains_placeholders($arg)) {
595 9         11 push @{ $filled_args }, (shift @{ $new_args });
  9         12  
  9         14  
596             } else {
597 22         28 push @{ $filled_args }, $arg;
  22         38  
598             }
599              
600 31 100       64 if ($old_args_len == ($idx + 1)) {
601 18         24 push @{ $filled_args }, @{ $new_args };
  18         21  
  18         39  
602             }
603             }
604              
605 18         45 return $filled_args;
606             }
607              
608             sub subarray {
609             my $coll = shift || [];
610             my $start = shift;
611             my $end = shift // scalar @$coll;
612              
613             if (!$start) {
614             return $coll;
615             }
616              
617             if ($start == $end) {
618             return [];
619             }
620              
621             return [
622             @$coll[$start .. ($end - 1)],
623             ];
624             }
625              
626             sub chain {
627 2     2   20 no warnings 'once';
  2         4  
  2         935  
628 7     7 1 3931 my ($val, @funcs) = @_;
629              
630             return List::Util::reduce {
631 8     8   34 my ($accum, $func) = ($a, $b);
632 8         17 $func->($accum);
633 7 100       47 } (ref($val) eq 'CODE' ? $val->() : $val), @funcs;
634             }
635              
636             sub eql {
637             my $arg1 = shift // '';
638             my $arg2 = shift // '';
639              
640             if (ref $arg1 ne ref $arg2) {
641             return 0;
642             }
643              
644             if (is_array($arg1) && is_array($arg2) ||
645             is_hash($arg1) && is_hash($arg2)) {
646             return bool($arg1 == $arg2);
647             }
648              
649             return bool($arg1 eq $arg2);
650             }
651              
652             sub bool {
653             my ($val) = @_;
654              
655             return $val ? 1 : 0;
656             }
657              
658             sub spread {
659             my $coll = shift // [];
660              
661             if (ref $coll eq 'ARRAY') {
662             return @{ $coll };
663             }
664              
665             if (ref $coll eq 'HASH') {
666             return %{ $coll }
667             }
668              
669             return split('', $coll);
670             }
671              
672             # ------------------------------------------------------------------------------
673              
674             =head1 NAME
675              
676             Sub::Fp - A Clojure / Python Toolz / Lodash inspired Functional Utility Library
677              
678             =cut
679              
680             =head1 SYNOPSIS
681              
682             This library provides numerous functional programming utility methods,
683             as well as functional varients of native in-built methods, to allow for consistent,
684             concise code.
685              
686             =head1 SUBROUTINES/METHODS
687              
688             =head1 EXPORT
689              
690             incr reduces flatten
691             drop_right drop take_right take
692             assoc maps decr chain
693             first end subarray partial
694             __ find filter some
695             none uniq bool spread every
696             len is_array is_hash to_keys to_vals
697             noop identity is_empty flow eql
698             is_sub to_pairs for_each apply
699             get second
700              
701             =cut
702              
703             =head2 incr
704              
705             Increments the supplied number by 1
706              
707             incr(1)
708              
709             # => 2
710              
711             =cut
712              
713             =head2 decr
714              
715             Decrements the supplied number by 1
716              
717             decr(2)
718              
719             # => 1
720              
721             =cut
722              
723             =head2 once
724              
725             Creates a function that is restricted to invoking func once.
726             Repeat calls to the function return the value of the first invocation.
727              
728             my $times_called = 0;
729             my $sub = once(sub {
730             $times_called++;
731             return "I was only called $times_called time"
732             });
733              
734             $sub->(); # "I was only called 1 time"
735             $sub->(); # "I was only called 1 time"
736             $sub->(); # etc
737              
738             =cut
739              
740             =head2 apply
741              
742             Calls the supplied function with the array of arguments, spreading the
743             arguments into the function it invokes
744              
745             my $sum_all_nums = sub {
746             my $num = shift;
747             my $second_num = shift;
748              
749             return $num + $second_num;
750             };
751              
752             apply($sum_all_nums, [100, 200]);
753             # same as $sum_all_nums->(100, 200)
754              
755             # => 300
756              
757             =cut
758              
759             =head2 range
760              
761             Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end.
762             A step of -1 is used if a negative start is specified without an end or step.
763             If end is not specified, it's set to start with start then set to 0.
764              
765             range(10);
766              
767             # [1,2,3,4,5,6,7,8,9]
768              
769              
770             range(1,10);
771              
772             # [1,2,3,4,5,6,7,8,9]
773              
774             range(-1, -10);
775              
776             # [-1, -2, -3, -4, -5, -6 ,-7, -8, -9]
777              
778             range(1, 4, 0);
779              
780             # [1, 1, 1]
781              
782              
783             #Ranges that "dont make sense" will return empty arrays
784              
785             range(-1, -4, 0);
786              
787             # []
788              
789             range(100, 1, 0)
790              
791             # []
792              
793             range(0,0,0)
794              
795             # []
796              
797             range(0, -100, 100)
798              
799             # []
800              
801             range(0, 100, -100)
802              
803             # []
804              
805             #etc...
806              
807             =cut
808              
809             =head2 for_each
810              
811             Iterates over elements of collection and invokes iteratee for each element. The iteratee is invoked with three arguments: (value, index|key, collection).
812              
813              
814             for_each(sub {
815             my $num = shift;
816             print $num;
817             }, [1,2,3]);
818              
819              
820             for_each(sub {
821             my ($num, $idx, $coll) = @_;
822             print $idx;
823             }, [1,2,3])
824              
825             # 0 1 2
826              
827             for_each(sub {
828             my ($num, $idx, $coll) = @_;
829             print Dumper $coll;
830             }, [1,2,3])
831              
832             # [1,2,3],
833             # [1,2,3],
834             # [1,2,3]
835              
836             =cut
837              
838             =head2 maps
839              
840             Creates an array of values by running each element in collection thru iteratee.
841             The iteratee is invoked with three arguments:
842             (value, index|key, collection).
843              
844             maps(sub {
845             my $num = shift;
846             return $num + 1;
847             }, [1,1,1]);
848              
849             # [2,2,2]
850              
851             =cut
852              
853             =head2 reduces
854              
855             Reduces collection to a value which is the accumulated result of running each element in collection thru iteratee,
856             where each successive invocation is supplied the return value of the previous.
857             If accumulator is not given, the first element of collection is used as the initial value.
858             The iteratee is invoked with four arguments:
859             (accumulator, value, index|key, collection).
860              
861             # Implicit Accumulator
862              
863             reduces(sub {
864             my ($sum, $num) = @_;
865              
866             return $sum + $num;
867             }, [1,1,1]);
868              
869             # 3
870              
871              
872             # Explict Accumulator
873              
874             reduces(sub {
875             my ($accum, $num) = @_;
876             return {
877             spread($accum),
878             key => $num,
879             }
880             }, {}, [1,2,3]);
881              
882             # {
883             # key => 1,
884             # key => 2,
885             # key => 3,
886             # }
887             =cut
888              
889             =head2 flatten
890              
891             Flattens array a single level deep.
892              
893             flatten([1,1,1, [2,2,2]]);
894              
895             # [1,1,1,2,2,2];
896              
897             =cut
898              
899             =head2 pop / pushes / shifts / unshifts
900              
901             Works the same as builtin pop / push etc etc, with mutations,
902             except it uses references instead of @ lists.
903              
904             my $array = [1,2,3];
905              
906             pops($array)
907              
908             # 3
909              
910             my $array = [1,2,3];
911              
912             pushes($array, 4);
913              
914             # [1,2,3,4]
915              
916             =cut
917              
918             =head2 drop
919              
920             Creates a slice of array with n elements dropped from the beginning.
921              
922             drop([1,2,3])
923              
924             # [2,3];
925              
926             drop(2, [1,2,3])
927              
928             # [3]
929              
930             drop(5, [1,2,3])
931              
932             # []
933              
934             drop(0, [1,2,3])
935              
936             # [1,2,3]
937             =cut
938              
939              
940              
941             =head2 drop_right
942              
943             Creates a slice of array with n elements dropped from the end.
944              
945             drop_right([1,2,3]);
946              
947             # [1,2]
948              
949             drop_right(2, [1,2,3])
950              
951             # [1]
952              
953             drop_right(5, [1,2,3])
954              
955             # []
956              
957             drop_right(0, [1,2,3])
958              
959             #[1,2,3]
960             =cut
961              
962             =head2 take
963              
964             Creates a slice of array with n elements taken from the beginning.
965              
966             take([1, 2, 3);
967              
968             # [1]
969              
970             take(2, [1, 2, 3]);
971              
972             # [1, 2]
973              
974             take(5, [1, 2, 3]);
975              
976             # [1, 2, 3]
977              
978             take(0, [1, 2, 3]);
979              
980             # []
981              
982             =cut
983              
984             =head2 take_right
985              
986             Creates a slice of array with n elements taken from the end.
987              
988             take_right([1, 2, 3]);
989              
990             # [3]
991              
992             take_right(2, [1, 2, 3]);
993              
994             # [2, 3]
995              
996             take_right(5, [1, 2, 3]);
997              
998             # [1, 2, 3]
999              
1000             take_right(0, [1, 2, 3]);
1001              
1002             # []
1003              
1004             =cut
1005              
1006             =head2 second
1007              
1008             Returns the second item in an array
1009              
1010             second(["I", "am", "a", "string"])
1011              
1012             # "am"
1013              
1014             second([5,4,3,2,1])
1015              
1016             # 4
1017              
1018             =cut
1019              
1020             =head2 first
1021              
1022             Returns the first item in an array
1023              
1024             first(["I", "am", "a", "string"])
1025              
1026             # "I"
1027              
1028             first([5,4,3,2,1])
1029              
1030             # 5
1031              
1032             =cut
1033              
1034             =head2 end
1035              
1036             Returns the end, or last item in an array
1037              
1038             end(["I", "am", "a", "string"])
1039              
1040             # "string"
1041              
1042             end([5,4,3,2,1])
1043              
1044             # 1
1045              
1046             =cut
1047              
1048             =head2 len
1049              
1050             Returns the length of the collection.
1051             If an array, returns the number of items.
1052             If a hash, the number of key-val pairs.
1053             If a string, the number of chars (following built-in split)
1054              
1055             len([1,2,3,4])
1056              
1057             # 4
1058              
1059             len("Hello")
1060              
1061             # 5
1062              
1063             len({ key => 'val', key2 => 'val'})
1064              
1065             #2
1066              
1067             len([])
1068              
1069             # 0
1070              
1071             =cut
1072              
1073             =head2 noop
1074              
1075             A function that does nothing (like our government), and returns undef
1076              
1077             noop()
1078              
1079             # undef
1080              
1081             =cut
1082              
1083             =head2 identity
1084              
1085             A function that returns its first argument
1086              
1087             identity()
1088              
1089             # undef
1090              
1091             identity(1)
1092              
1093             # 1
1094              
1095             identity([1,2,3])
1096              
1097             # [1,2,3]
1098              
1099             =cut
1100              
1101             =head2 eql
1102              
1103             Returns 0 or 1 if the two values have == equality, with convience wrapping
1104             for different types (no need to use eq vs ==). Follows internal perl rules
1105             on equality following strings vs numbers in perl.
1106              
1107             eql([], [])
1108              
1109             # 1
1110              
1111             eql(1,1)
1112              
1113             # 1
1114              
1115              
1116             my $obj = {};
1117              
1118             eql($obj, $obj);
1119              
1120             # 1
1121              
1122              
1123             eql("123", 123)
1124              
1125             # 1 'Following perls internal rules on comparing scalars'
1126              
1127              
1128             eql({ key => 'val' }, {key => 'val'});
1129              
1130             # 0 'Only identity equality'
1131              
1132             =cut
1133              
1134             =head2 is_sub
1135              
1136             Returns 0 or 1 if the argument is a sub ref
1137              
1138             is_sub()
1139              
1140             # 0
1141              
1142             is_sub(sub {})
1143              
1144             # 1
1145              
1146             my $sub = sub {};
1147             is_sub($sub)
1148              
1149             # 1
1150              
1151             =cut
1152              
1153             =head2 is_array
1154              
1155             Returns 0 or 1 if the argument is an array
1156              
1157             is_array()
1158              
1159             # 0
1160              
1161             is_array([1,2,3])
1162              
1163             # 1
1164              
1165             =cut
1166              
1167             =head2 is_hash
1168              
1169             Returns 0 or 1 if the argument is a hash
1170              
1171             is_hash()
1172              
1173             # 0
1174              
1175             is_hash({ key => 'val' })
1176              
1177             # 1
1178              
1179             =cut
1180              
1181             =head2 is_empty
1182              
1183             Returns 1 if the argument is 'empty',
1184             0 if not empty. Used on strings, arrays, hashes.
1185              
1186             is_empty()
1187              
1188             # 1
1189              
1190             is_empty([])
1191              
1192             # 1
1193              
1194             is_empty([1,2,3])
1195              
1196             # 0
1197              
1198             is_empty({ key => 'val' })
1199              
1200             # 0
1201              
1202             is_empty("I am a string")
1203              
1204             # 0
1205              
1206             =cut
1207              
1208             =head2 get
1209              
1210             Returns value from hash, string, array based on key/idx provided.
1211             Returns default value if provided key/idx does not exist on collection.
1212             Only works one level deep;
1213              
1214             my $hash = {
1215             key1 => 'value1',
1216             };
1217              
1218             get('key1', $hash);
1219              
1220             # 'value1'
1221              
1222              
1223             my $array = [100, 200, 300]
1224              
1225             get(1, $array);
1226              
1227             # 200
1228              
1229              
1230             my $string = "Hello";
1231              
1232             get(1, $string);
1233              
1234             # e
1235              
1236              
1237             # Also has the ability to supply default-value when key/idx does not exist
1238              
1239             my $hash = {
1240             key1 => 'value1',
1241             };
1242              
1243             get('key2', $hash, "DEFAULT HERE");
1244              
1245             # 'DEFAULT HERE'
1246              
1247             =cut
1248              
1249             =head2 spread
1250              
1251             Destructures an array / hash into non-ref context.
1252             Destructures a string into an array of chars (following in-built split)
1253              
1254             spread([1,2,3,4])
1255              
1256             # 1,2,3,4
1257              
1258             spread({ key => 'val' })
1259              
1260             # key,'val'
1261              
1262             spread("Hello")
1263              
1264             # 'H','e','l','l','o'
1265              
1266             =cut
1267              
1268             =head2 bool
1269              
1270             Returns 0 or 1 based on truthiness of argument, following
1271             internal perl rules based on ternary coercion
1272              
1273             bool([])
1274              
1275             # 1
1276              
1277             bool("hello!")
1278              
1279             # 1
1280              
1281             bool()
1282              
1283             # 0
1284              
1285             bool(undef)
1286              
1287             # 0
1288              
1289             =cut
1290              
1291             =head2 to_keys
1292              
1293             Creates an array of the key names in a hash,
1294             indicies of an array, or chars in a string
1295              
1296             to_keys([1,2,3])
1297              
1298             # [0,1,2]
1299              
1300             to_keys({ key => 'val', key2 => 'val2' })
1301              
1302             # ['key', 'key2']
1303              
1304             to_keys("Hey")
1305              
1306             # [0, 1, 2];
1307              
1308             =cut
1309              
1310             =head2 to_vals
1311              
1312             Creates an array of the values in a hash, of an array, or string.
1313              
1314             to_vals([1,2,3])
1315              
1316             # [0,1,2]
1317              
1318             to_vals({ key => 'val', key2 => 'val2' })
1319              
1320             # ['val', 'val2']
1321              
1322             to_vals("Hey");
1323              
1324             # ['H','e','y'];
1325              
1326             =cut
1327              
1328             =head2 to_pairs
1329              
1330             Creates an array of key-value, or idx-value pairs from arrays, hashes, and strings.
1331             If used on a hash, key-pair order can not be guaranteed;
1332              
1333             to_pairs("I am a string");
1334              
1335             # [
1336             # [0, "I"],
1337             # [1, "am"],
1338             # [2, "a"],
1339             # [3, "string"]
1340             # ]
1341              
1342             to_pairs([100, 101, 102]);
1343              
1344             # [
1345             # [0, 100],
1346             # [1, 102],
1347             # [2, 103],
1348             # ]
1349              
1350             to_pairs({ key1 => 'value1', key2 => 'value2' });
1351              
1352             # [
1353             # [key1, 'value1'],
1354             # [key2, 'value2']
1355             # ]
1356              
1357             to_pairs({ key1 => 'value1', key2 => { nested => 'nestedValue' }});
1358              
1359             # [
1360             # [key1, 'value1'],
1361             # [key2, { nested => 'nestedValue' }]
1362             # ]
1363              
1364             =cut
1365              
1366             =head2 uniq
1367              
1368             Creates a duplicate-free version of an array,
1369             in which only the first occurrence of each element is kept.
1370             The order of result values is determined by the order they occur in the array.
1371              
1372             uniq([2,1,2])
1373              
1374             # [2,1]
1375              
1376             uniq(["Hi", "Howdy", "Hi"])
1377              
1378             # ["Hi", "Howdy"]
1379              
1380             =cut
1381              
1382             =head2 assoc
1383              
1384             Returns new hash, or array, with the updated value at index / key.
1385             Shallow updates only
1386              
1387             assoc([1,2,3,4,5,6,7], 0, "item")
1388              
1389             # ["item",2,3,4,5,6,7]
1390              
1391             assoc({ name => 'sally', age => 26}, 'name', 'jimmy')
1392              
1393             # { name => 'jimmy', age => 26}
1394              
1395             =cut
1396              
1397             =head2 subarray
1398              
1399             Returns a subset of the original array, based on
1400             start index (inclusive) and end idx (not-inclusive)
1401              
1402             subarray(["first", "second", "third", "fourth"], 0,2)
1403              
1404             # ["first", "second"]
1405              
1406             =cut
1407              
1408             =head2 find
1409              
1410             Iterates over elements of collection, returning the first element predicate returns truthy for.
1411              
1412             my $people = [
1413             {
1414             name => 'john',
1415             age => 25,
1416             },
1417             {
1418             name => 'Sally',
1419             age => 25,
1420             }
1421             ]
1422              
1423             find(sub {
1424             my $person = shift;
1425             return eql($person->{'name'}, 'sally')
1426             }, $people);
1427              
1428             # { name => 'sally', age => 25 }
1429              
1430             =cut
1431              
1432             =head2 filter
1433              
1434             Iterates over elements of collection, returning only elements the predicate returns truthy for.
1435              
1436             my $people = [
1437             {
1438             name => 'john',
1439             age => 25,
1440             },
1441             {
1442             name => 'Sally',
1443             age => 25,
1444             },
1445             {
1446             name => 'Old Greg',
1447             age => 100,
1448             }
1449             ]
1450              
1451             filter(sub {
1452             my $person = shift;
1453             return $person->{'age'} < 30;
1454             }, $people);
1455              
1456             # [
1457             # {
1458             # name => 'john',
1459             # age => 25,
1460             # },
1461             # {
1462             # name => 'Sally',
1463             # age => 25,
1464             # }
1465             # ]
1466              
1467             =cut
1468              
1469             =head2 none
1470              
1471             If one element is found to return truthy for the given predicate, none returns 0
1472              
1473              
1474             my $people = [
1475             {
1476             name => 'john',
1477             age => 25,
1478             },
1479             {
1480             name => 'Sally',
1481             age => 25,
1482             },
1483             {
1484             name => 'Old Greg',
1485             age => 100,
1486             }
1487             ]
1488              
1489             none(sub {
1490             my $person = shift;
1491             return $person->{'age'} > 99;
1492             }, $people);
1493              
1494             # 0
1495              
1496             none(sub {
1497             my $person = shift;
1498             return $person->{'age'} > 101;
1499             }, $people);
1500              
1501             # 1
1502              
1503             =cut
1504              
1505             =head2 every
1506              
1507             Itterates through each element in the collection, and checks if element makes predicate
1508             return truthy. If all elements cause predicate to return truthy, every returns 1;
1509              
1510             every(sub {
1511             my $num = shift;
1512             $num > 0;
1513             }, [1,2,3,4]);
1514              
1515             # 1
1516              
1517             every(sub {
1518             my $num = shift;
1519             $num > 2;
1520             }, [1,2,3,4]);
1521              
1522             # 0
1523              
1524             =cut
1525              
1526             =head2 some
1527              
1528             Checks if predicate returns truthy for any element of collection.
1529             Iteration is stopped once predicate returns truthy.
1530              
1531             some(sub {
1532             my $num = shift;
1533             $num > 0;
1534             }, [1,2,3,4]);
1535              
1536             # 1
1537              
1538             some(sub {
1539             my $num = shift;
1540             $num > 2;
1541             }, [1,2,3,4]);
1542              
1543             # 1
1544              
1545             =cut
1546              
1547             =head2 partial
1548              
1549             Creates a function that invokes func with partials prepended to the arguments it receives.
1550             (funcRef, args)
1551              
1552             my $add_three_nums = sub {
1553             my ($a, $b, $c) = @_;
1554              
1555             return $a + $b + $c;
1556             };
1557              
1558             my $add_two_nums = partial($add_three_nums, 1);
1559              
1560             $add_two_nums->(1,1)
1561              
1562             # 3
1563              
1564              
1565             # Can also use __ to act as a placeholder
1566              
1567             my $add_four_strings = sub {
1568             my ($a, $b, $c, $d) = @_;
1569              
1570             return $a . $b . $c . $d;
1571             };
1572              
1573             my $add_two_strings = partial($add_four_strings, "first ", __, "third ", __);
1574              
1575             $add_two_strings->("second ", "third ")
1576              
1577             # "first second third fourth"
1578              
1579             =cut
1580              
1581             =head2 chain
1582              
1583             Composes functions, left to right, and invokes them, returning
1584             the result. Accepts an expression as the first argument, to be passed
1585             as the first argument to the proceding function
1586              
1587             chain(
1588             [1,2,3, [4,5,6]],
1589             sub {
1590             my $array = shift;
1591             return [spread($array), 7]
1592             },
1593             \&flatten,
1594             );
1595              
1596             # [1,2,3,4,5,6,7]
1597              
1598              
1599             # Invokes first function, and uses that as start value for next func
1600             chain(
1601             sub { [1,2,3, [4,5,6]] },
1602             sub {
1603             my $array = shift;
1604             return [spread($array), 7]
1605             },
1606             \&flatten,
1607             )
1608              
1609             # [1,2,3,4,5,6,7]
1610              
1611             =cut
1612              
1613             =head2 flow
1614              
1615             Creates a function that returns the result of invoking the given functions,
1616             where each successive invocation is supplied the return value of the previous.
1617              
1618             my $addTwo = flow(\&incr, \&incr);
1619              
1620             $addTwo->(1);
1621              
1622             # 3
1623              
1624             =cut
1625              
1626             =head1 AUTHOR
1627              
1628             Kristopher C. Paulsen, C<< >>
1629              
1630             =head1 BUGS
1631              
1632             Please report any bugs or feature requests to C, or through
1633             the web interface at L. I will be notified, and then you'll
1634             automatically be notified of progress on your bug as I make changes.
1635              
1636             =head1 SUPPORT
1637              
1638             You can find documentation for this module with the perldoc command.
1639              
1640             perldoc Sub::Fp
1641              
1642              
1643             You can also look for information at:
1644              
1645             =over 4
1646              
1647             =item * RT: CPAN's request tracker (report bugs here)
1648              
1649             L
1650              
1651             =item * AnnoCPAN: Annotated CPAN documentation
1652              
1653             L
1654              
1655             =item * CPAN Ratings
1656              
1657             L
1658              
1659             =item * Search CPAN
1660              
1661             L
1662              
1663             =back
1664              
1665              
1666             =head1 ACKNOWLEDGEMENTS
1667              
1668              
1669             =head1 LICENSE AND COPYRIGHT
1670              
1671             MIT
1672              
1673             Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
1674              
1675             The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
1676              
1677             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1678              
1679              
1680             =cut
1681              
1682             1;