I thought I'd share an enlightenment I had recently with the community, hoping someone else can have a similar breakthrough. Until now, I've always shied away from map and grep. I understood how they worked (on the surface) but never seemed to find a situation where they "fit". I didn't fully understand their power. It was like having a tool in my toolbox without taking it out of the wrapper.

The other day I was playing around with a two little OO modules, I'll call one Item and one Collection for sake of argument.

Item has two main attributes:
VAL :a scalar value. and
USABLE :a flag that denotes the items availability.

Collection is just that: a collection of Item objects with added methods to do stuff with the collections. It's main attribute is ITEMS which is a referance to an array of Item objects.

Now that I've set the scene and bored half the audience to tears I come to the problem that led to enlightenment. I wanted a method in Collection to return a scalar that was a concatenation of the values (sorted numerically) of all the objects in the collection whose USABLE flag was true. I ended up with this:

#method in Collection object stringify { my $self = shift; return join("",sort {$a <=> $b} map {$_->{VAL}} grep {$_->{USABLE}} @$self->{ITEMS}); }

The great part is, once I wrote it, it seemed 'natural'. It just fit. I used to read this kind of code and go "uhhhh, Ok that's cool but I'll never write something like that let alone be able to decipher it". I feel I've crossed some sort of learning plateau, or passed an important rite of passage.
That's it.. on to the next level

Replies are listed 'Best First'.
RE: A minor epiphany with grep and map
by mwp (Hermit) on Jul 25, 2000 at 08:11 UTC

    This is handy too. I've often felt that DBI lacks a fetchall_hashref method. :)

    my $sth = $dbh->prepare(q{ SELECT Table_PK, Name ... }); $sth->execute; my %lookup = map { $_->[0], $_->[1] } @{ $sth->fetchall_arrayref };

    Here's one of my favorites:

    $sth->bind_columns( map { undef ${$_}; \${$_}; } @{ $sth->{NAME_lc} } ); while($sth->fetch) { print("$table_pk\t$name\n"); }

    It's good to be lazy.

    I am the lurker that spams in the night.

RE: A minor epiphany with grep and map
by chip (Curate) on Jul 27, 2000 at 03:16 UTC
    By the way, I'm intersted in an examination of @$x->{y}. Examining the code generated with -Dx, I think this runs only by accident. The left side of -> is supposed to be a reference, but in that code, the left hand side is an array, namely, @$x. And it's not a syntax error to have one of those on the left side of ->{y} because of pseudohashes.

    Or so it seems.

    --Chip Salzenberg, Free-Floating Agent of Chaos

      @$self->{ITEMS} @$x->{y}

      $self->{ITEMS} (or $x->{y}) returns a reference or pointer to an array of values. The @-operator dereferences it to be used by the map and grep functions.

      This could have been much more clearly written as:

      @{ $x->{y} }

      But if wishes were fishes... :) My point is that there's nothing magical about this, and doesn't really require an "examination."

      Hope this helps, or at least makes sense with all that chaos out there.

      I am the lurker that spams in the night.

      (UPDATE: I understand what chip is saying, and I'm not trying to be a smartass. I think he's reading into the code a little too deeply. Just wanted to clarify things for some of the less experienced readers. :P)

        I'm sorry, Alakaboo, you didn't understand what I wrote at all.

        Given the precedence rules, @$x->{y} is not the same as @{$x->{y}}. Rather, it's the same as (@$x)->{y}. Look at the output of -Dx and -Dts if you don't believe me.

        In fact, now that I try a simple test, the original code can't work:

        $ perl -le '$x={y=>[1]}; print @$x->{y}' Not an ARRAY reference at -e line 1. $ perl -le '$x={y=>[1]}; print @{$x->{y}}' 1

        So let's see the full code to the original, OK?

            -- Chip Salzenberg, Free-Floating Agent of Chaos

      Hmmmm.... I see your point. @{$x->{y}} _is_ probably a better way to clarify this to the parser(and to the poor soul who has to maintain the code somewhere down the line- probably me.)

      Looking at perlop, I'm a little confused as to _why_ it works myself. Quoting from the Docs: (with my comments not in <code> tags)

      The Arrow Operator ``->'' is an infix dereference operator, just as it is in C and C++. If the right side is either a [...], {...}, or a (...) subscript,
      ..It seems this describes the situation..
      then the left side must be either a hard or symbolic reference to an array, a hash, or a subroutine respectively. (Or technically speaking, alocation capable of holding a hard reference, if it's an array or hash reference being used for assignment.) See the perlreftut manpage and the perlref manpage. Otherwise, the right side is a method name or a simple scalar variable containing either the method name or a subroutine reference, and the left side must be either an object (a blessed reference) or a class name (that is, a package name). See the perlobj manpage.
      It seems somehow the -> is binding before the @ in @$x, contrary to what I see in perlop
      I guess I should consider it a _feature_.
      Thank you, chip for noticing something that I completely glossed over. Good brain food.
RE: A minor epiphany with grep and map
by eduardo (Curate) on Jul 24, 2000 at 23:51 UTC
    hehehehe, welcome to the darkside... we're glad to have you.