Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^3: Write code diferently

by BrowserUk (Pope)
on Aug 23, 2013 at 20:56 UTC ( #1050736=note: print w/ replies, xml ) Need Help??


in reply to Re^2: Write code diferently
in thread Write code diferently

to tell the truth, the first map looks a bit clunky to me.

Truth is good; but "looks a bit clunky to me" doesn't cut much ice as a technical objection. It reflects only your own limited expectations.

The map function is really designed to return a list.

Repeating cargo cult does not make it so.

map maps a function over a list; optionally returning a new list. Used in a void context; it does not create an output list.

If you are using it only for its side effects on $_ and therefore on the original list, then I submit that for/foreach is a better choice.

In the abstract, that is sound reasoning; but it concentrates on the minutia rather than the bigger picture.

The purpose of that line of code is to apply a function: f(x) := x / (mutation rate * 100); in place, to every element of a matrix.

The original language, Darwin, has matrix operators that allow this to be written very naturally.

Perl does not have those operators; but does provide the tools to allow them to be written quite concisely. Thus allowing the purpose of the code; the mutation of the elements of the matrix to stand clear of the mechanics of the code required to iterate over the matrix dimensions.

I contend that this:

for my $aref ( @lnPAM ) { $_ /= ( $mutRate * 100 ) for @$aref; }

Distracts from the purpose of the code, by making too great an emphasis of the (purely mechanical) process of iterating the matrix.

Clearer and possibly faster.

Clearer is in the eye of the beholder -- and I disagree with your view.

Faster is a matter of benchmarking, but my guess is that there will be little in it; and it will probably favour the map version:

@a = map[ map int(rand$_), 1 .. 10],1..10;; cmpthese -1,{ a=>q[ map $_ /= 1, @$_ for @a; ], b=>q[ for my $r( @a ){ $_ /= 1 for @$r } ], };; Rate b a b 52726/s -- -25% a 70388/s 33% --

Yup!


The perl code:

while( abs( $mutRate - 0.01) > 1e-16 ) { ++$iter; $k *= $mutRate * 100; map $_ /= ( $mutRate * 100), @$_ for @lnPAM; @matrix = map[ map exp( $_ ), @$_ ], @lnPAM; $mutRate = sum map{ $freq[ $_ ] * ( 1 - $matrix[ $_ ][ $_ ] ) } 0. +.19; printf "After %d iteration(s), k=%f, RateMutation=%f\n", $iter, $k +, $mutRate; }

was written to mirror the original Darwin code:

while abs(RateMutation - 0.01) > DBL_EPSILON do iter := iter + 1; k:= k * RateMutation * 100: lnPAM1 := lnPAM1 / (RateMutation * 100): PAM1 := exp(lnPAM1): RateMutation := sum(Freq[i] * (1 - PAM1[i,i]), i= 1..20); printf('After %d iteration(s), k=%f, RateMutation=%f', iter, k, Ra +teMutation); od:

as closely as possible; which I think it does admirably.

The reason for asking the OP why he wanted it coded a different way; was so as to get some idea of how best to address his concerns when doing so.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
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.


Comment on Re^3: Write code diferently
Select or Download Code
Re^4: Write code diferently
by Laurent_R (Parson) on Aug 24, 2013 at 10:13 UTC

    Truth is good; but "looks a bit clunky to me" doesn't cut much ice as a technical objection.

    It was not a technical objection, I know that using map in void context works fine. I was just expressing a matter of personal taste that it may not be the best style. Others may have different opinions on that. And I was only trying to offer a possible reason why the OP asked the question in the first place.

    It reflects only your own limited expectations.

    Yeah, I am more limited than you, sorry about that.

    Repeating cargo cult does not make it so.

    Labeling others' arguments does not make the label true. Besides, this is just rhetorics, falling short of a proper intellectual argument.

    map maps a function over a list; optionally returning a new list.

    You are sort of rewriting the official documentation to fit your argument. The documentation says:

    Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element) and returns the list value composed of the results of each such evaluation.
    The map function may be used in scalar or void context, but that is definitely not its priority use. The documentation also says that map "can be used to modify the elements" of the input list, but that
    Using a regular foreach loop for this purpose would be clearer in most cases.
    Besides concentrating on the minutia of the Perl syntax is forgetting the other bigger picture that map is used in dozens of different languages and has actually been borrowed from functional programming languages to make it possible to generate a new list from an existing one in a natural fashion. There is nothing wrong using it in a different way, but it would be wrong to deny that creating a new modified list is definitely its main purpose, which you sort of do when you write "optionally returning a new list", as if it was just a secondary possible side effect of map.

    Oh, BTW, just an additional quote from the Camel Book (Programming with Style section, page 703 of the 4th edition):

    Avoid using grep, map and backticks in void context; that is when you just throw away their return values. Those functions have return values, so use them. Otherwise use a foreach loop or the system function.

    For the rest, I basically agree with the rest of your post and I generally have great respect for your opinions and carefully consider them, even when, as in the case in point, I don't entirely share them. And I upvoted your post despite a couple of things I don't like to much.

      Oh, BTW, just an additional quote from the Camel Book

      I've no idea when that edition was revised; but if it was any time in the last 8 or so years, the revision was lacking.

      Neither map nor grep "throw away their return values" when used in a void or scalar context; they simply do not produce that list.

      I don't recall which version this was changed for; but it has been so for long enough that anyone leveling charges of "the inefficiency of map in a void context" can only be be cargo-culted repetition of something you've heard or read; not based upon actual experience.

      Ie. Don't level a charge until you've tested it for yourself; rather than just parroting what you've heard or read others say.

      And I upvoted your post despite a couple of things I don't like to much.

      Don't! If you don't like what I say, don't upvote.

      Now to those things you apparently didn't like:

      And I was only trying to offer a possible reason why the OP asked the question in the first place.

      Do you think that your guess as to his reasoning is likely to be any more definitive than my guess?

      Yeah, I am more limited than you, sorry about that.

      If you set out to be offended; you likely will be; but twisting my words to achieve that happy state is less rhetoric, more ....

      I didn't say you were limited; I said your expectations were. And the latter is in no way a personal attack; just a statement of fact based upon your own expression of those expectations.

      You expect to see map used to produce a new list from a list. This may be because you've come from a background (some other language; possibly a functional one) where that is the only way their (equivalent of) map can be used. Maybe because that is what you've read that it does. Or maybe it just the only way you've ever used it. None of these is an insult. None of these is a personal attack.

      Perl's implementation of many widely used constructs is more flexible than their equivalents in other languages. That is in big part what makes Perl so useful and effective.

      In this case, map embraces rather than decries the ability to modify the iterated list in place -- what you called a "side-effect" -- through the use of aliasing of the origin of the list. That allows Perl to achieve efficiencies that would be difficult (Haskell's unsafe* functions) or impossible (Clean/Miranda/Erlang) in other languages.

      Labeling others' arguments does not make the label true. Besides, this is just rhetorics, falling short of a proper intellectual argument.

      Dealt with above, but to reiterate.

      Performance:

      [0] Perl> cmpthese -3,{a=>q[@a = map $_*1, @a],b=>q[map $_*=1, @a],c=> +q[$_*=1 for @a] };; Rate a c b a 2.88/s -- -67% -67% c 8.64/s 200% -- -2% b 8.86/s 208% 3% --

      If you repeat an idea without having tested it for yourself -- that's cargo-cult.

      When the label applied is correct; that is perfectly good "intellectual argument". It's a fact.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      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.

        I do not want to argue endlessly, especially in view of the fact that we probably mostly agree and that the whole discussion really stems from a quick comment I made on the possible motivation of the OP, nothing fundamental. I just want to set the record straight on a couple of things.

        The Camel Book edition I mentioned was issued in 2012 and the revision probably occurred in 2011. The cover says prominently "Covers version 5.14". So, pretty recent stuff. The relevant section is about programming style, not efficiency or performance.

        "The inefficiency of map in a void context" may be cargo cult, but I did not say anything like that or even remotely similar to that, not even hinted anything in that direction. I vaguely remember having read things about that about 10 years ago, but it is absolutely not my belief, I simply have no opinion on that because I do not know. I ran a test a few weeks ago comparing map and foreach, and, in the specific context of my specific test (quite different from the case in point), foreach turned out to be slightly faster (25%). This is the only reason why I said "possibly faster". I was not repeating some sacred mantras read somewhere, and there is really nothing in what I said that could lead anyone to believe that this is what I was doing.

        I have some interest in the functional programming paradigm, because I think it created or promoted a lot of very interesting and powerful concepts (list processing, higher order functions, closures, lazy evaluation, etc.), but I never used any functional language anywhere beyond making some toy programs to get a feeling of it. My background before Perl was Fortran, Modula, ADA, assembly, C, C++, TCL, Python, awk, shell, PL-SQL and quite a few other languages, none of them functional. But I love the fact that Perl offers me the possibility to use some of the useful concepts it has borrowed from functional programming, because it gives me extra expressive power. But, of course, I have many other reasons to love Perl, some of which are certainly much more significant.

        As I said before, I made some benchmarks a few weeks ago to compare for and map, and for turned out to be faster. The conditions were different, so I did not insist so much when I saw your results. But, still I was a bit surprised. So thinking back to it, I made a quick test, then another, again another, etc., and the results are markedly different from yours.

        The slight problem with your benchmark is that it appears not to do what you think and it reports phony results. Multiplying a value by 1 is not terribly useful, and part of your code is probably optimized away.

        I tried to get some meaningful results with your code, and I had to increase the numbers to incredibly high values to get something seemingly significant. This is my code based on yours:

        use strict; use warnings; use Benchmark; my @a = 1..1000000; timethese (10000000,{a=>q{map {1} @a},b=>q[map $_*=1, @a],c=>q[$_*=1 f +or @a] });

        And the results:

        a: 1 wallclock secs ( 0.75 usr + 0.00 sys = 0.75 CPU) @ 13 +368983.96/s (n=10000000) b: 0 wallclock secs ( 0.75 usr + 0.00 sys = 0.75 CPU) @ 13 +368983.96/s (n=10000000) c: 2 wallclock secs ( 1.70 usr + 0.00 sys = 1.70 CPU) @ 58 +78894.77/s (n=10000000)

        Less than one second to process 10 million times an array with one million elements? My laptop isn't so powerful. More interestingly, the "a" code_ref, which does nothing is just exactly as fast as the "b" code_ref. Your benchmark, on which you base all your demonstration that I am wrong and that you are right, is just hot air and pure baloney. And it is plain wrong.

        Now, let's do a real benchmark:

        use strict; use warnings; use Benchmark; timethese ( 10000, { "idiomatic" => sub { my @array = 1..1000; $_ +=2 for @ +array; }, "map" => sub { my @array = 1..1000; m +ap { $_ +=2;} @array; } } );

        Tested on HP_UX, v5.8.8 :

        Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 3.25 usr + 0.01 sys = 3.26 CPU) @ 306 +7.48/s (n=10000) map: 6 wallclock secs ( 5.91 usr + 0.01 sys = 5.92 CPU) @ 16 +89.19/s (n=10000)

        Tested on VMS - v5.8.6 :

        idiomatic: 2 wallclock secs ( 1.59 usr + 0.00 sys = 1.59 CPU) @ 62 +8.93/s (n=10000) map: 3 wallclock secs ( 2.88 usr + 0.00 sys = 2.88 CPU) @ 34 +7.22/s (n=10000)

        Tested on AIX, v5.10.1 :

        Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 2 wallclock secs ( 0.89 usr + 0.00 sys = 0.89 CPU) @ 112 +35.96/s (n=10000) map: 3 wallclock secs ( 1.70 usr + 0.00 sys = 1.70 CPU) @ 58 +82.35/s (n=10000)

        Tested on Linux, v.5.14.2:

        Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 1 wallclock secs ( 1.25 usr + 0.00 sys = 1.25 CPU) @ 80 +12.82/s (n=10000) map: 3 wallclock secs ( 2.31 usr + 0.00 sys = 2.31 CPU) @ 43 +30.88/s (n=10000)

        Tested on Windows 7, v. 5.18.0 (Strawberry Perl):

        Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 1 wallclock secs ( 1.01 usr + 0.00 sys = 1.01 CPU) @ 98 +61.93/s (n=10000) map: 2 wallclock secs ( 2.08 usr + 0.00 sys = 2.08 CPU) @ 48 +19.28/s (n=10000)

        So, five different platforms, five different operating systems, five versions of Perl, including the most recent 5.18, and every time just about the same result: for is 1.81 to 2.01 faster than map.

        Cargo cult? Just repeating like a parrot what others are saying? No, actual hard experimental results.

      > 1999/11/01 Re: grep/map in void context by Larry Wall

      The argument against using an operator for other than its primary purpose strikes me the same as the old argument that you shouldn't have sex for other than procreational purposes.

      Sometimes side effects are more enjoyable than the originally intended effect.

      This lovely quote unearthed through Think for yourself.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1050736]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (11)
As of 2014-10-24 09:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (131 votes), past polls