Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

(almost) foldl

by dk (Chaplain)
on Jun 07, 2011 at 21:16 UTC ( #908568=obfuscated: print w/replies, xml ) Need Help??

There runs now a topic on moscow.pm list on how to calculate a sum of numbers in a list, basically this:
my $sum = 0; $sum += $_ for @list;
but without using extra vars and modules (nurture your inner purist, yes :) Now I believe I found a line that wasn't discovered before:

print sub { (map {splice @_, 0, 2, $_[0] + ($_[1] // 0)} @_)[-1] }-> ( +1,2,3,4,5);

Hopefully you'll find it amusing :)

Replies are listed 'Best First'.
Re: (almost) foldl
by Fox (Pilgrim) on Jun 08, 2011 at 14:25 UTC
    No recursion ?

    sub sum { (shift()//0) + (@_ ? sum(@_) : 0) };

      I came up with a similar, and arguably neater implementation:

      sub sum{ ( shift()//return 0 ) + &sum } print sum( 1,2,3);; 6 print sum( 1 .. 100 );; Deep recursion on subroutine "main::sum" at 5050

      but rejected it because 'sum' is a (glob) variable.

      In theory, it is possible to avoid the naming of the sub, thereby achieving "anonymous recursion", by using the Y-combinator. And the Y-combinator has been achieved in Perl by a former regular here.

      Putting it together you get:

      print Y( sub{my$rec=shift; sub{(shift()//return 0) + &$rec }})->(1 .. +100);; 5050

      But whilst that achieves the Y-combinators goal of recursion without adding the sub to the permanent namespace, it still requires the naming of the pesky closure $rec.

      And of course, requires you to add the Y-combinator to the permanent namespace first:

      sub Y { my ( $curried_rec ) = @_; sub { my ( $f1 ) = @_; $curried_rec->( sub { $f1->( $f1 )->( @_ ) } ) }->( sub { my ( $f2 ) = @_; $curried_rec->( sub { $f2->( $f2 )->( @_ ) } ) } ) }

      And that's already more obfuscation as I want to wrap my brain around, even in an obfuscation section post!

      My final thought is that the simplest mechanism that fits the OPs breif is just:

      C:\test>perl -Mstrict -wE"say eval join'+',()" C:\test>perl -Mstrict -wE"say eval join'+',0" 0 C:\test>perl -Mstrict -wE"say eval join'+',-1" -1 C:\test>perl -Mstrict -wE"say eval join'+',-1..+3" 5

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        Nice! But print eval join "+", ("1e", 6) yields 1000000, whereas my aforementioned solution: print sub {eval 'pop(@_)+' x ($#_ + 1) . '0';}->("1e", 6); prints the expected result, 7. Ok, ok, I'm quibbling.
        Great! There was once a YAPC auction selling off a built-in name in a future perl, at the buyer's choice. IIRC it has never actualized, but now I wish it was Y )
      This method is extremely elegant, and therefore doubleplus unsuitable for the "Obfuscated code" section. Just kidding, sorry if it sounds harsh :-)
      cool! ++
Re: (almost) foldl
by ikegami (Patriarch) on Jun 07, 2011 at 22:14 UTC

    First, it's not equivalent since you don't handle the empty list.

    my $sum = sub { (map {splice @_, 0, 2, $_[0] + ($_[1] // 0)} @_)[-1] } +->(@list);

    should be

    my $sum = sub { (map {splice @_, 0, 2, $_[0] + ($_[1] // 0)} @_)[-1] } +->(0, @list);

    A general form of this exists as reduce in List::Util. It's is easier to use (since handling one-element lists is easier) and easier to read (since it uses $a and $b instead of $_[0] and $_[1] and uses less extraneous code).

    my $sum = reduce { $a + $b } 0, @list;
      no extra modules! )
        Are core modules like List::Util "extra" or not?

        Your reply doesn't make sense. I pointed out that someone already wrote your code, and wrote it way better. The choice isn't between using a module and using no module, the choice is between using your code and using List::Util's.

Re: (almost) foldl
by Grimy (Pilgrim) on Jun 07, 2011 at 22:57 UTC
    Well, in unary, summing is as simple as concatenating. And Perl has a built-in decimal-to-unary converter (the x operator), as well as a built-in unary-to-decimal converter (the length function). Putting all this together:
    my @list = (1, 2, 3, 4, 5); print length join "", map{"1" x $_} @list; #prints 15
    But your implementation sure is fun :-) EDIT: Look at tye's code instead. This one is stupid. Don't know what I had in mind when I wrote 'length join "", '.
      my @list = ( 1..6 ); print 0+map{(1)x$_}@list;

      - tye        

      negative numbers?
        my @list = ( -4 .. 6 ); print map((1)x$_,@list) - map((1)x-$_,@list);

        - tye        

        sub sum { eval 'pop(@_)+' x ($#_ + 1) . '0'; } @list = (-4, 15, -5, 2.5); print sum @list; # 8.5, as expected print sum -4, 15, -5, 2.5; # Works the same print sum; # 0 (not undef)
        EDIT: replaced $#list with $#_ , so that it works with lists of any length. Silly me.
Re: (almost) foldl
by ikegami (Patriarch) on Jun 07, 2011 at 22:50 UTC

    One can simplify your implementation

    sub { ( map { splice @_, 0, 2, $_[0] + ($_[1] // 0) } @_ )[-1] }->(0, +@list)

    such that one is left with a single splice and without the extra loop pass that necessitates // 0.

    sub { ( splice(@_, 0, 0, 0), ( map $_[0] += $_, @_) )[-1] }->(@list)

    Of course, that's really just the same as

    sub { splice(@_, 0, 0, 0); map $_[0] += $_, @_; $_[0] }->(@list)

    At which point, you a can give a better name to the variables you do create (despite claims to the contrary).

    sub { my $acc = 0; map $acc += $_, @_; $acc }->(@list)

    Finally, canonise.

    sub { my $acc = 0; $acc += $_ for @_; $acc }->(@list)
      and no extra vars ))))
        Are you saying that creating vars named $_[0] (like you did) is better than creating a var named $acc, or did you just not read my post?
Re: (almost) foldl
by BrowserUk (Patriarch) on Jun 11, 2011 at 08:41 UTC

    Not as elegant, but waaaaaaay more efficient:

    print sub{ unshift@_,0; $_[0]+= pop while @_>1; return @_ }->( 1..1e6) +;; 500000500000

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: (almost) foldl
by BrowserUk (Patriarch) on Jun 11, 2011 at 07:29 UTC

    He he :)

    print $_->(1,2,3) for sub{ (shift//return 0) + &$_ }; 6

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: (almost) foldl
by ambrus (Abbot) on Oct 29, 2011 at 14:49 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: obfuscated [id://908568]
Approved by ikegami
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2023-09-24 07:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?