Re: (almost) foldl
by Fox (Pilgrim) on Jun 08, 2011 at 14:25 UTC
|
No recursion ?
sub sum { (shift()//0) + (@_ ? sum(@_) : 0) }; | [reply] [d/l] |
|
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.
| [reply] [d/l] [select] |
|
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.
| [reply] [d/l] [select] |
|
|
|
|
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 )
| [reply] |
|
|
|
|
This method is extremely elegant, and therefore doubleplus unsuitable for the "Obfuscated code" section.
Just kidding, sorry if it sounds harsh :-)
| [reply] |
|
| [reply] |
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;
| [reply] [d/l] [select] |
|
| [reply] |
|
Are core modules like List::Util "extra" or not?
| [reply] |
|
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.
| [reply] |
|
|
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 "", '. | [reply] [d/l] |
|
my @list = ( 1..6 );
print 0+map{(1)x$_}@list;
| [reply] [d/l] |
|
| [reply] |
|
my @list = ( -4 .. 6 );
print map((1)x$_,@list) - map((1)x-$_,@list);
| [reply] [d/l] |
|
|
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. | [reply] [d/l] |
Re: (almost) foldl
by ikegami (Patriarch) on Jun 07, 2011 at 22:50 UTC
|
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)
| [reply] [d/l] [select] |
|
| [reply] |
|
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?
| [reply] [d/l] [select] |
|
|
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.
| [reply] [d/l] |
Re: (almost) foldl
by BrowserUk (Patriarch) on Jun 11, 2011 at 07:29 UTC
|
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.
| [reply] [d/l] |
Re: (almost) foldl
by ambrus (Abbot) on Oct 29, 2011 at 14:49 UTC
|
| [reply] |