kaif has asked for the wisdom of the Perl Monks concerning the following question:

I am looking for list utilities that may aid me in using more functional programming paradigms in Perl. I find myself writing many of these often, so I was wondering if there was already a CPAN module to help me. I tried searching Perlmonks (and CPAN) for related questions, but I was unsuccessful in my attempt.

It seems that many people here swear by List:Util and List::MoreUtils. (There must be more! I'm sorry I was unable to find them.) However, there are still many functions that I would like to use:

At this point, I pause to note that Perl's calling mechanism does not make such a function easy (probably one reason why I can't seem to find such a function). As such, there are a couple approaches to the problem. Suppose we have two lists @a and @b and we wish to create ($a[0]+$b[0], $a[1]+$b[1], ...); then we could (a) use transpose something like map { $$_[0] + $$_[1] } transpose @a, @b or (b) supply list references (my preferred approach) multimap { $_[0] + $_[1] } [1,2,3], [2,3,5]. Notice that List::MoreUtils::pairwise does the two-list version of what I want here. From now on, assume LIST means a list reference.

So you say all of these are easy to implement from scratch or by using List::MoreUtils? Perhaps, but I was looking for an existing implementation, perhaps implemented in XS for efficiency. If there is no such thing, would people be interested in such a CPAN module (unfortunately, I probably wouldn't be able to do an XS version myself)? Thanks for your help!

Remark: I have likely mixed the uses of "array" and "list" in this node. However, I do not think it matters here, really, because I would really like for all of these functions to work on list references. In particular, my preferred example for transpose is "[[1,2,3],[2,3,5]] into [[1,2],[2,3],[4,5]]."

Replies are listed 'Best First'.
Re: More functional programming utilities
by BrowserUk (Patriarch) on Jun 08, 2005 at 08:39 UTC

    Here's your multimap BLOCK LIST, LIST... (with a practical limitation).

    #! perl -slw use strict; sub multimap (&\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) { my $code = shift; map { my $i = $_; $code->( map{ $_->[ $i ] } @_ ) } 0 .. $#{ $_[ 0 ] }; } my @l = 'a' .. 'z'; my @u = 'A' .. 'Z'; my @n = 1 .. 26; print for multimap { join ', ', @_ } @u, @n, @l; __END__ P:\test>464573 A, 1, a B, 2, b C, 3, c D, 4, d E, 5, e F, 6, f G, 7, g H, 8, h I, 9, i J, 10, j K, 11, k L, 12, l M, 13, m N, 14, n O, 15, o P, 16, p Q, 17, q R, 18, r S, 19, s T, 20, t U, 21, u V, 22, v W, 23, w X, 24, x Y, 25, y Z, 26, z

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      I must admit, I frowned at your prototype.. but then i checked, and I found out that the author of List::MoreUtils has implemented a couple of his functions in exactly the same way - though his prototypes are with 25 arrays.. I find it mildly disturbing that he did not bother with documenting this...

        I don't like the prototype in this case either, it heralds too many limitations in use. The practical ones of artificially limiting the number of arrays it can operate on, but more fundementally, the fact that you can only use real arrays.

        With the caveat of requiring the user to pass an AoA ref, the function becomes much more flexible and makes writing transpose LOL trivial:

        #! perl -slw use strict; use Data::Dumper::SLC; sub multimap (&$) { my( $code, $aref ) = @_; return unless ref $aref eq 'ARRAY'; map { my $i = $_; $code->( map{ $_->[ $i ] } @$aref ) } 0 .. $#{ $aref->[ 0 ] }; } my @l = 'a' .. 'h'; my @u = 'A' .. 'H'; my @n = 1 .. 8; print for multimap { join ', ', @_ } [ \( @u, @n, @l ) ]; sub transpose { return multimap{ [ @_ ] } $_[ 0 ]; } my @LoL = ([1,2,3],[2,3,5]); my @transposed = transpose \@LoL; Dump \@transposed; __END__ P:\test>464573 A, 1, a B, 2, b C, 3, c D, 4, d E, 5, e F, 6, f G, 7, g H, 8, h [ [ '1', '2', ], [ '2', '3', ], [ '3', '5', ], ]

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: More functional programming utilities
by Roy Johnson (Monsignor) on Jun 08, 2005 at 11:33 UTC
    Check out Algorithm::Loops 'MapCar*' for parallel processing of lists (which would include transposing):
    use Algorithm::Loops 'MapCar'; my $wide = [[1,2,3],[2,3,5]]; my $tall = [ MapCar {[@_]} @$wide ];
    Your fold_left can be done exactly as you've written it using reduce. fold_right would require only one reverse. mult would be another reduce (as, internally, sum is). You could also do log math on sum. :-)

    Caution: Contents may have been coded under pressure.

      Thank you for finding MapCar. That does a lot of what I need.

      So yeah, I just realized that fold_left can be written by simply calling reduce with the same arguments (so that fold_left's initial becomes the first element of reduce's input). But fold_right does require two reverses, in the following sense: suppose I wanted to compare left- and right-associative subtraction. Then, I have to reverse once to change a call of fold_right into fold_left but then I also have to change the order of arguments into the block.

      Well, I really want sum to be reduce { $a + $b } 0, @input, so that would make multiplication reduce { $a * $b }, 1, @input. Note that sum is not reduce internally. Here's the code:

      sum(...) PROTOTYPE: @ CODE: { SV *sv; int index; if(!items) { XSRETURN_UNDEF; } sv = ST(0); RETVAL = slu_sv_value(sv); for(index = 1 ; index < items ; index++) { sv = ST(index); RETVAL += slu_sv_value(sv); } } OUTPUT: RETVAL
        Within List::Util source, sum is defined:
        sub sum (@) { reduce { $a + $b } @_ }
        Where is the code you quoted from?

        Caution: Contents may have been coded under pressure.
      nah, you can't, you'd also have to keep the sign information and short-circuit on 0s.. unless you promise to only multiply positive numbers..

      other than that, all points are spot on.