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.
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 [http://grenekatz.org/cgi-bin/MarkovBot?id=hardburn|Markov Hardburn].
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
------
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
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 ];
%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.
"Think for yourself!" - [Abigail-II|Abigail]
"Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - [tachyon]
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.
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
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.
Yes! There is a point. Who reads code? And when? And why?
My counter is that White Fang is 271 pages and that White Fang, the Dick and Jane Version (DJV) will run over 1300 pages.
Another counter: Would you really prefer DJV over White Fang for pleasure? Something that is pleasurable is apt to be done well.
I suspect that the DJV will have more instances of the word $lantern to lead me astray when I look for the problem.
I picked that
sample to express the limit of some cognitive ability skill of
mine. The slicing puts it up there right on or just past
my
limit. ( Without the slicing it is a no-op.)
I do understand how code needs to be readable by its audience/maintainers. If I were running a crew of programmers, I would consider this a subtle issue.
If I can understand White Fang as well as I can understand DJV, then I don't see any point in your argument. I understand you to be arguing for simplicity. I am pushing for literacy or fluency. I don't see them as opposed. I see this as a skill-level issue, greater fluency makes more things simple.
Be well.
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.
%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.
%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.
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 [http://terje2.perlgolf.org|the Perl Golf site]. I think that recognizing why good code is good and why bad code is bad is helpful to your literacy.
perlmonks.org content © perlmonks.org and Anonymous Monk, BlaisePascal, BrowserUk, chance, dragonchild, hardburn, Jenda, parv, rir, Roy Johnson, ysth
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03