Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

List-to-Scalar Function?

by Masem (Monsignor)
on May 09, 2001 at 02:08 UTC ( #78977=perlmeditation: print w/replies, xml ) Need Help??

In recent golfs and other ponderings, it's quite common to run into expression such as:
my $i; for (0..10) { $i += $_; }
That is, you want to apply the same expression over a list of values, and return a value from that. I'll call this a "list-to-scalar" mapping, in much the same that map and grep are both "list-to-list" mappings.

I am wondering if someone has taken the time to write a generic list-to-scalar function that works similar to map. For example, assume this function is called operate (I'm sure the name could be better). Then this:

my $sum = operate { $~ + $_ } (0..10);
would be equivalent to the above. "$~" is a local variable that is initially set to null at the start of the loop, and is set to the value of the code block after that block is executed for each item. (I can't recall of any current use of $~, and IIRC, it is a 'reserved' variable).

I know this might seem trivial, as you're simply removing an explicit my statement. But providing such a function seems to give perl a bit more symmetry when you come it with map and grep. (And as recent discussions have shown, it's better to think of perl as a list-oriented language rather than scalar). This also makes some other functions a bit more obvious:

#Max: my $max = operate { $~ > $_ ? $~ : $_ } @list; #Straight Concat: my $string = operate { $~.$_ } @list; #Count occurences of words in a file: my $count = operate { $~ + /the/ } <FILE>;
Writing such a function shouldn't be impossible, as it's compariable to treemap and other code here at PM. But before I tackle this, I want to verify I'm not reinventing the wheel, nor running into any trouble with the usage I'm suggesting.

Update - As shown below, the functionality I was looking for is sufficiently taken care of by the reduce fuction in List::Util.


Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain

Replies are listed 'Best First'.
Re: List-to-Scalar Function?
by merlyn (Sage) on May 09, 2001 at 02:30 UTC
    use List::Util qw(reduce); # from the CPAN my $max = reduce { $a > $b ? $a : $b } @list; my $string = reduce {$a . $b } @list;
    Your third one is problematic. "null" in what sense? And yes, $~ does already have a meaning. Everything means something. That's why they used up almost all the control-character names and are now started on the multicharacter names in 5.6.1.

    -- Randal L. Schwartz, Perl hacker


    update: That third one is:
    my $count = reduce { $a + $b =~ /the/ } 0, <FILE>;
      You can also implement reduce in native Perl:
      sub reduce (&@) { my $op = shift; no strict 'refs'; my $pkg = caller; local *A = local *{"$pkg\::a"}; $A = shift; local *B = local *{"$pkg\::b"}; foreach $B (@_) { $A = $op->(); } $A; }
      (Stolen shamelessly from here, with full permission of the author. :-)

      UPDATE
      D'oh. I should have read the source-code. They don't present that in the documentation... And I wrote it originally because someone (jcwren I think) was having trouble running my code at Re (tilly) 1: An exercise in "That's not the way I'd do it" (TNTWIDI) because List::Util wasn't compiling.

        Somewhat similar to the pure-Perl implementation in List::Util itself...
        use vars qw($a $b); sub reduce (&@) { my $code = shift; return shift unless @_ > 1; my $caller = caller; local(*{$caller."::a"}) = \my $a; local(*{$caller."::b"}) = \my $b; $a = shift; foreach (@_) { $b = $_; $a = &{$code}(); } $a; }
        Although if the XS compiles and loads, this code is not used.

        -- Randal L. Schwartz, Perl hacker

      One of the old-style variable names is still unused... It's $}
Re (tilly) 1: List-to-Scalar Function?
by tilly (Archbishop) on May 09, 2001 at 04:45 UTC
    Your max implementation will give a wrong answer if your list consists of negative numbers.

    At the cost of (sometimes) passing one extra argument, reduce makes it easy to handle whatever initialization logic may be useful. As the example of max shows, this is not an advantage to be sneezed at.

Re: List-to-Scalar Function?
by perlmonkey (Hermit) on May 09, 2001 at 03:45 UTC
    To implement it using your syntax you could do something like:
    sub operate (&@) { my $sub = shift; local $~; $~ = &$sub($_) for @_; return $~ }


    So for your examples you would get:
    use strict; sub operate (&@) { my $sub = shift; local $~; $~ = &$sub($_) for @_; return $~ } my @list = (4, 6, 2, 67, 123, 645, 23, 54 ); my $max = operate { $~ > $_ ? $~ : $_ } @list; print $max, "\n"; my $string = operate { $~.$_ } @list; print $string, "\n"; open FILE, $0; my $count = operate { $~ + /operate/ } <FILE>; print "Count = $count\n";


    And the results are
    max = 645 string = 462671236452354 count = 4

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://78977]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (2)
As of 2022-10-04 01:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My preferred way to holiday/vacation is:











    Results (15 votes). Check out past polls.

    Notices?