Temporary variables are clutter but worse they help habituate one to write in small phrasings. I have been trying out writing with fewer temporary variables and finding it beneficial. Avoiding them pushes one to grasp more complex phrasings.

That is the prime point of this post. I'll demonstrate it once and wander on to a few adjacent thoughts.

Where I might have written something like

my $low_key = (keys %h)[0]; for my $k ( keys %h ) { $low_key = $k if ($k < $low_key ); } my $val_l_k = $h{$low_key}; %h=(); $h{$low_key}=$val_l_k;
(Six lines of one to three chunks.)

now I am more apt to write

%h = ( map { $_, $h{$_} } sort { $a<=>$b } keys %h)[0,1];
(One line of five or seven chunks.)

I don't consider myself facile with Perl, I don't code enough, but I have started to feel how extraneous parentheses impede the quick grasp of code.

In the second bit of code there is a lot of contextual info to tell one to jump to, or just expect more after, the matching parenthesis. But I don't tend to do that, I keep reading to the right and find the subscripting a little bit of a surprise. So I find this is about my limit in reading code; reading as opposed to puzzling things out.

I ask those who are more Perl literate: What do you do when you get to an opening parenthesis?

If the map and sort code blocks were larger, how would you read that code, would you grasp the structure first and come back to read the code blocks?

For myself I think a construction like the below could be helpful. Did K&R C allow subscripts to precede their expressions?

%h = PRE_SUB_OP[0,1]( map { $_, $h{$_} } sort { $a<=>$b } keys %h);
Having the the short clause first seems to add clarity but perhaps I am just missing a trick, or some experience.

Be well.

Replies are listed 'Best First'.
Re: small steps toward Perl literacy, temp vars and parentheses
by hardburn (Abbot) on Jun 16, 2004 at 21:17 UTC

    In the second bit of code there is a lot of contextual info to tell one to jump to

    I've learned to stop worrying about this and enjoy writing LISP in Perl :)

    Removing my tounge from my cheek, I think people shouldn't expect Perl to just be interpreted C. People who have a strong C background may have problems reading chained operations like that, but it's standard operating procedure in LISP and other functional languages. So when somebody tells you that the code you shown above is sloppy, you'll know which community they come from.

    Now, I think you are correct to say that the index isn't a very pleasing way to end the statement. Maybe this will do it (untested):

    %h = @{ shift ( map {[ $_, $h{$_} ]} sort { $a<=>$b } keys %h)};

    Of course, now you have to explain to The Complainer the difference between lists and arrays.

    ----
    send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.

      I don't believe the OP's issue came from reading chained operations, but more from the switch between left-chaining and right-chaining in the revised code.

      His one-liner:

      %h = ( map { $_, $h{$_} } sort { $a<=>$b } keys %h)[0,1];

      was structured like:

       %h = ( op3 op2 op1 %h) op4;

      where %h was acted upon by four operators in turn (keys, sort, map, and an array slice). But the order of operation was not linear. It flowed to the left, then sharply cut back to the right. In pseudo-scheme, that would have been:

      (set! 'h (hash-new (array-slice (1 2) (flatten-map (lambda (x) (list x (hash-get h x))) (sort stringcomp (hash-keys h))))))
      and the dataflow would have been clear. A while ago on the Perl 6 language lists there was discussion of 'gozinta' operators which would have allowed you to adjust the order of things. I don't remember the exact syntax right now (and it may have been squashed, I forget), but his code could have been done something like so:
      %h ==> keys ==> sort { $a<==>$b } ==> map { $_, %h{$_} } ==> [0,1] ==> + %h;
      or...
      %h <== [0,1] <== map { $_, %h{$_} } <== sort { $a<==>$b } <== keys <== + %h;
      Either way would have worked, and the dataflow is obvious without having to switch left/right more often.

        If we wanted a statement that could be read from right to left completely we could use

        %h = ( $_, $h{$_}) for ($_) = sort {$a <=> $b} keys %h;
        "Take the keys of %h, sort them numericaly, take the first one and set the %h hash to the key and its value."

        But I think this one is even more confusing. Maybe this

        %h = ( $_, $h{$_}) for ($_,) = sort {$a <=> $b} keys %h;
        would be better, the comma giving a hint that we do expect the sort to return several values, but we use just the first one.

        Anyway in this case I would use some temp variable. Even though I did work with some functional languages and liked it :-)

        Jenda
        Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
           -- Rick Osborne

        Edit by castaway: Closed small tag in signature

        Yes, Perl6 will let you say which direction you want the result to go. So, you can have your chains read left-to-right or right-to-left, at your discretion.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: small steps toward Perl literacy, temp vars and parentheses
by BrowserUk (Patriarch) on Jun 16, 2004 at 22:27 UTC

    I'd code that as

    use List::Util qw[ min ]; %h = map{ $_, $h{ $_ } } min keys %h;

    Which I think clarifies things a lot as well as saving a little runtime.

    I also favour breaking chains over several lines as I think it makes it easier to pick out what is going on.

    If I needed to keep the lowest 2 or more elements then

    %h = map{ $_, $h{ $_ } } ( sort{ $a <=> $b } keys %h )[ 0 .. 2 ];

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
      Hmm, your solution for keeping the lowest 2 or more elements still puts the indexing at the end, but it changes the op structure to  %h = op4 (op2 op1 %h) op3 which from a jumping back and forth from beginning to end issue is worse. But it does change the structure in another way that suggests:
      %h = map { $_, $h{ $_ } } shift sort { $a <==> $b } keys %h;
      (that, by the way, is completely untested. Hmm, testing says it doesn't work, type of arg 1 of shift must be array, not sort. i wasn't aware that sort was a type. I wonder how to do what I want...)

        I guess we read the code in different ways. I don't think of the code as a list of operations applied to %h, but as assigning the results of map to %h.

        map takes a list and manipulates it. The fact that the input to map is also derived from %h is effectively moot. It could equally well be a completely different hash.

        I look at

        %h = map{ $_, $h{ $_ } } ( sort{ $a <=> $b } keys %h )[ 0 .. 2 ];

        as: Take the first 3 from a list of sorted keys and feed them to map. Map those keys to key/value pairs and assign them to %h.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
Re: small steps toward Perl literacy, temp vars and parentheses
by dragonchild (Archbishop) on Jun 17, 2004 at 03:06 UTC
    Personally, I think you're trying to do too much at once. The line doesn't have an easy flow to it. I'd break it up into two parts.
    my @temp = map { $_ => $h{$_} } sort { $a <=> $b } keys %h; %h = @temp[0,1];

    This tells me I'm doing something with %h, then I'm reassigning that result back to %h; Of course, I could use a do statement, as so:

    %h = do { my @temp = map { $_ => $h{$_} } sort { $a <=> $b } keys %h; @temp[0,1]; };

    That's nicer, but I don't like having my chained operators so far from the left margin. While I limit myself to 80 columns, I like to keep it, if at all possible, in under 40.

    %h = do { my @temp = map { $_ => $h{$_} } sort { $a <=> $b } keys %h; @temp[0,1] };

    That's how I write my LisPerl, with the chains listed vertically instead of horizontally.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: small steps toward Perl literacy, temp vars and parentheses
by parv (Parson) on Jun 17, 2004 at 06:00 UTC

    Whenever i notice map, i generally read the LIST part first followed by reading (BLOCK|EXPR) body, followed by anything before the map.

    If any part of the map structure has more than 1 thing going on, i split it over multiple lines. Your example would be shaped like (excessive in this particular case)...

    %h = ( map { $_ , $h{$_}; } sort { $a <=> $b } keys %h )[0,1] ;

    I am not bothered by the array slice at the end. But as somebody else has/have noted, there is no need to squeeze every little bit of temporary variables at the cost of clarity.

    As to reading code w/ parentheses ... my expectation is not to find nested parentheses; otherwise i have to work very hard to keep track of them. In the second case, i tend to indent and split the text on each opening parenthesis before doing anything else.

Re: small steps toward Perl literacy, temp vars and parentheses
by rir (Vicar) on Jun 17, 2004 at 01:49 UTC
    I am pleased with the first five responses in this thread. I was trying to start an broad discussion.

    I'll re-emphasize my first point: I was not expecting this small practice to improve my Perling so much, so quickly. I wanted to advertise this to others. I thank Merlyn again for his post.

    In the second part of my post I was trying to expose my cognitive state in dealing with the syntax of Perl. I hoped to elicit from my Perl betters some explanation of their mental process in parsing or reading code.

    I feel that I mentally gloss over opening parentheses, braces and such, and that the bad habit comes from reading simple code. I wondered if the more fluent readers of Perl just do what I do, but better, or if there is some redirect or quirk at a parenthesis whose partner is not in sight.

    Be well.

Re: small steps toward Perl literacy, temp vars and parentheses
by Anonymous Monk on Jun 17, 2004 at 04:35 UTC
    What is the advantage to using the denser version? It seems that you lose clarity; what is gained thereby?
      It is clear why you focus on the writing, but I was trying to focus on the reading of code.

      I see it much like the difference between

      See spot. Spot is a dog. See the dish. See the food. The food is in the dish. Spot runs to the dish. Spot eats the food.
      and
      The dog, Spot, runs to the dish and eats the food.
      What is gained?

      Time. I can read the single line faster; if I have the ability to read it there is no loss of clarity. There is an increase in clarity through expressiveness: Dick and Jane may be readable in three word sentences but I would not want to read White Fang that way. Concise expressions allow larger thoughts to cohere by remaining nearby.

      Be well.

        If I was reading White Fang for the pleasure of it I agree.

        but I'm reading White Fang to figure out why there is a slight logicial inconsistency dealing with a Lantern somehere in pages 36-98 or page 14 or pages 19-22, which in turn creates a slight problem in the plot timeline on page 132, I'll take the Dick and Jane Version.

        ditto for your code.
Re: small steps toward Perl literacy, temp vars and parentheses
by Roy Johnson (Monsignor) on Jun 17, 2004 at 17:55 UTC
    I offer another right-to-leftification of your example:
    %h = map { $_->[0], $h{$_->[0]} } [ sort { $a<=>$b } keys %h ];
    Do you find it more readable? Personally, I found the line breaks and indentation that other posters provided was more helpful in adding readability than making the direction consistent. But if you're going to do it on one line, it is helpful to have things go one direction.

    Update: is this better or worse?

    %h = map { $_, $h{$_} } map { $_->[0] } [ sort { $a<=>$b } keys %h ];
    Take the keys, sort them, take the first one, and make the hash from the hash entry.

    We're not really tightening our belts, it just feels that way because we're getting fatter.
      I find your version about the same. BlaisePascal's snippet and yours move me more toward:
      %h = map { $_, $h{$_} } ( sort { $a <=> $b } keys %h )[0];
      But I don't see this as that important. My post was not about re-writing a bit of code so I could find it an easier read. My post was about improving code reading skills so all code is easier to read.

      How much complexity one can absorb on one line seems to me to be a good thing to increase. Contrarily, perhaps I should try to derive more info from the indentation of code.

      Be well.

Re: small steps toward Perl literacy, temp vars and parentheses
by Roy Johnson (Monsignor) on Jun 17, 2004 at 20:55 UTC
    Having dinked around with the example for a bit, I think that the chief reason it is awkward to read is that it's kludgy. You're getting the minimum key value by sorting and indexing, instead of by using a min function.
    use List::Util; my $min_key = min(keys %h); %h = ($min_key, $h{$min_key});
    That's perfectly clear, efficient, and doesn't involve a lot of slinging data around. If you particularly hate the temp variable, you could obviate it with map (although I find that rather dogmatic):
    %h = map { $_, $h{$_} } min(keys %h);
    In the same way that variables should have purposeful names, chunks of code should look like what you're trying to accomplish. If there were no min() function already written, you could (and should) easily write your own, rather than inlining everything.

    Update: if the issue is improving your ability to read code, well, bad code is always going to be hard to read. You could certainly practice at the Perl Golf site. I think that recognizing why good code is good and why bad code is bad is helpful to your literacy.


    We're not really tightening our belts, it just feels that way because we're getting fatter.