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

Re^5: Write code diferently

by BrowserUk (Pope)
on Aug 24, 2013 at 11:17 UTC ( #1050804=note: print w/ replies, xml ) Need Help??


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

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.


Comment on Re^5: Write code diferently
Download Code
Re^6: Write code diferently
by Laurent_R (Vicar) on Aug 24, 2013 at 14:24 UTC

    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.

      The relevant section is about programming style, not efficiency or performance.

      Oh, its 'style advice'. It's perfectly fine to cargo-cult that(*).

      Does the book have any opinion on whether I should be wearing a full beard with my knee-crutched jeans or just 3-day stubble?

      (*)My point being, that you are still passing on information you have read, as advice or preference or law, without having examined the reasoning behind it, or having applied your own reasoning to it; or concluded that it is good advice, preference or law on the basis of your own experience.

      I occasionally suggest or recommend the use of strict and/or warnings. I do so, not because I've read that they should be used, nor even because someone told me it is a good idea; but because I personally have found them useful to me. And when I do so; I demonstrate what benefit the OP would have accrued from doing so.

      But nowhere have you even attempted to reason (beyond "looks a bit clunky"; and you read it in a book) why you think my use of map is suboptimal. That's cargo-cult.


      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.

        You are misrepresenting what I have said, and it starts to look deliberate.

        My point being, that you are still passing on information you have read, as advice or preference or low, without having examined reasoning behind it, or having applied your own reasoning to it; or concluded that it is good advice, preference or law on the basis of your own experience.

        This is not so. I only made a two-line post just trying to understand why the OP wanted to change the map in the code presented, and saying that, in my personal opinion, a for or foreach construct would be clearer in that case. Not that I read it somewhere or someone told me, my own experience tells me that this is the case.

        In my next post, I said this:

        I was just expressing a matter of personal taste that it may not be the best style. Others may have different opinions on that.
        Still not repeating something I have read, stating clearly that it is a personal opinion of mine.

        I am forced to quote the official Perldoc map documentation and the Camel Book only because you are tweaking the map functionality by saying something like:

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

        There, you are basically making the claim that the return list is a secondary effect of map. This claim is just preposterous and you know better than that. BTW, another quote from the official Perldoc documentation on map:

        Map always returns a list.
        But quoting the official documentation is probably just repeating cargo cult, isn't it?

        And the only reason I said that the quote from the Camel Book was in a programming Style section was just to set the matter straight that it was not previous section on efficiency and performance.

        I am sorry that this discussion turned this way, I certainly did not want to have this type of argument with you. Again, I respect very much your opinions, but that does not mean that I have to agree will all of them. Agreeing with everything you said might indeed be repeating cargo-cult, I have enough experience to have my own opinion. I do not think that I want to continue arguing. You may have the last word.

Re^6: Write code diferently
by Laurent_R (Vicar) on Aug 26, 2013 at 20:09 UTC

    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.

      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.

      Wrong! All you've done is show how little you understand about Perl.

      • You didn't replicate my benchmark correctly. I posted this:
        cmpthese -3,{a=>q[@a = map $_*1, @a],b=>q[map $_*=1, @a],c=>q[$_*=1 fo +r @a] };;

        You used this:

        timethese (10000000,{a=>q{map {1} @a},b=>q[map $_*=1, @a],c=>q[$_*=1 f +or @a] });

        Spot your idiocies:.

        1. a=>q{map {1} @a}

          Where did your get that from? Cos it wasn't my benchmark.

        2. Now the biggy: my @a = 1..1000000;.

          How d'you expect code that is eval'd from a string, to see a lexical array?

          Since you seem incapable of understanding stuff that requires a little knowledge; try downloading this and running it on your laptop(and post the results if you dare!):

      Multiplying a value by 1 is not terribly useful, and part of your code is probably optimized away.

      Again, you show your lack of knowledge. Multiplying by one does not get optimised away.

      Now, let's do a real benchmark:

      You're joking right? Because your benchmark is crap.

      You are timing how long it takes:

      1. to allocate an array;
      2. and then populate it;
      3. and the time taken to call a subroutine (the one you wrote) from within a subroutine (the one Benchmark wraps around the code ref (or string) you give to it.)
      4. in addition to the time taken to iterate it..

      Let's start with your benchmark as posted (minus the eclectic formatting), run on my machine:

      timethese 10000, { "idiomatic" => sub { my @array = 1..1000; $_ +=2 for @array; +}, "map" => sub { my @array = 1..1000; map{ $_ +=2;} @array; +}, }; __END__ C:\test>IdiotsBench.pl Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 2 wallclock secs ( 1.52 usr + 0.00 sys = 1.52 CPU) @ 65 +96.31/s (n=10000) map: 3 wallclock secs ( 3.06 usr + 0.00 sys = 3.06 CPU) @ 32 +64.77/s (n=10000)

      Now let's remove those array allocations and populations from the timing:

      C:\test>IdiotsBench.pl my @array = 1..1000; timethese 10000, { "idiomatic" => sub { $_ +=2 for @array; }, "map" => sub { map{ $_ +=2 } @array; }, }; __END__ C:\test>IdiotsBench.pl Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 1 wallclock secs ( 0.92 usr + 0.00 sys = 0.92 CPU) @ 10 +845.99/s (n=10000) map: 2 wallclock secs ( 2.44 usr + 0.00 sys = 2.44 CPU) @ 41 +01.72/s (n=10000)

      Now let's remove the expensive and redundant block scope from the map test:

      C:\test>IdiotsBench.pl timethese 10000, { "idiomatic" => sub { $_ +=2 for @array; }, "map" => sub { map $_ +=2, @array; }, }; __END__ Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 1 wallclock secs ( 0.95 usr + 0.00 sys = 0.95 CPU) @ 10 +493.18/s (n=10000) map: 1 wallclock secs ( 0.95 usr + 0.00 sys = 0.95 CPU) @ 10 +493.18/s (n=10000)

      And finally, the double function call:

      our @array2 = 1 .. 1000; timethese 10000, { "idiomatic" => q[ $_ +=2 for @array2; ], "map" => q[ map $_ +=2, @array2; ], }; __END__ Benchmark: timing 10000 iterations of idiomatic, map... idiomatic: 1 wallclock secs ( 0.88 usr + 0.00 sys = 0.88 CPU) @ 11 +428.57/s (n=10000) map: 1 wallclock secs ( 0.84 usr + 0.00 sys = 0.84 CPU) @ 11 +848.34/s (n=10000)

      And waddayaknow; now we are benchmarking just the disputed code and not conflating it with a bunch other random stuff, map is faster than for. Just like I said.

      You want to withdraw your accusation of phony results now?


      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.

        Alright, I must apologize, I made an error while trying to use your benchmark. I am sorry about that and about what I said about your tests.

        try downloading this and running it on your laptop(and post the results if you dare!)

        No need to be very daring, I can recognize a mistake of mine when that occurs. I admit that I was wrong on this point.

        How d'you expect code that is eval'd from a string, to see a lexical array?

        Of course, code that is eval'd from a string can see a lexical array. Just an example:

        use strict; use warnings; my $c = "foo"; my $d = "print q{$c}, q{\n}"; eval ($d); # print "foo" my @f = 1..10; process_f(); sub process_f { my $g = 'print "$_ " for @f; print "\n";'; eval ($g); } my $h = 'my $i = sub { print map {$_ *= 2; "$_ ";} @f; print "\n" }; $ +i->();'; eval ($h);

        Here, the problem occurs only because the string is passed to a module function in a separate file, so that the lexical is neither passed as an argument, nor seen as a global. Which is by the way the reason why I don't like using the string version for the Benchmark module and almost never use it. And this is why I made the silly mistake, almost never using this form.

        map is faster than for. Just like I said.

        I don't think this is true. I've tried your code a dozen times on three different version of Perl, for was almost always faster than map by a very thin margin. As a final test, I increased the number of iterations to 50,000 on Perl 5.18/Windows 7:

        use strict; use warnings; use Benchmark; our @array2 = 1 .. 1000; timethese 50000, { "idiomatic" => q[ $_ +=2 for @array2; ], "map" => q[ map $_ +=2, @array2; ], };
        Results for 5 runs:
        Benchmark: timing 50000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 2.87 usr + 0.00 sys = 2.87 CPU) @ 17 +421.60/s ( n=50000) map: 3 wallclock secs ( 3.04 usr + 0.00 sys = 3.04 CPU) @ 16 +436.55/s ( n=50000) Benchmark: timing 50000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 2.87 usr + 0.00 sys = 2.87 CPU) @ 17 +421.60/s ( n=50000) map: 3 wallclock secs ( 3.04 usr + 0.00 sys = 3.04 CPU) @ 16 +436.55/s ( n=50000) Benchmark: timing 50000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 2.86 usr + 0.00 sys = 2.86 CPU) @ 17 +513.13/s ( n=50000) map: 3 wallclock secs ( 3.04 usr + 0.00 sys = 3.04 CPU) @ 16 +436.55/s ( n=50000) Benchmark: timing 50000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 2.87 usr + 0.00 sys = 2.87 CPU) @ 17 +415.53/s ( n=50000) map: 3 wallclock secs ( 3.04 usr + 0.00 sys = 3.04 CPU) @ 16 +436.55/s ( n=50000) Benchmark: timing 50000 iterations of idiomatic, map... idiomatic: 3 wallclock secs ( 2.87 usr + 0.00 sys = 2.87 CPU) @ 17 +421.60/s ( n=50000) map: 3 wallclock secs ( 3.04 usr + 0.00 sys = 3.04 CPU) @ 16 +436.55/s ( n=50000)

        The results are very consistent throughout and it certainly cannot be said that map is faster than for. At best, they are more or less equivalent. But my initial point has never been about performance anyway, but only to suggest a possible explanation for the OP's original question.

        Again, I did not want to have a debate with you on this subject in the first place, because I think we were agreeing on most things. And I want to reiterate that I am sorry that I made an error leading me to believe and to say wrongly that your test was wrong.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (6)
As of 2014-08-22 04:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (147 votes), past polls