Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Possible useless use of map

by andreas1234567 (Vicar)
on Mar 18, 2010 at 09:50 UTC ( [id://829360]=perlquestion: print w/replies, xml ) Need Help??

andreas1234567 has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I recently discovered i bug in my code. My intention was to create a hash reference with a key for each value of an array reference.

use strict; use warnings; use diagnostics; my $arr = [1,2,3]; my $hr = undef; map { $hr->{$_} } @{$arr};
The last line should have been written as
map { $hr->{$_} = 1 } @{$arr};
Could this bug have been picked up by some sibling of use warnings ?
--
No matter how great and destructive your problems may seem now, remember, you've probably only seen the tip of them. [1]

Replies are listed 'Best First'.
Re: Possible useless use of map
by Anonymous Monk on Mar 18, 2010 at 10:13 UTC
    Could this bug have been picked up by some sibling of use warnings ?

    Based on what? Lets see

    • you're using map in void context, but that has been silenced since 5.9.0
    • you're treating $hr as a hashref, but you're discarding the value, hmmm, lets check
      $ perl -Mstrict -Mwarnings -le " my $hr; $hr->{stuff}" Useless use of hash element in void context at -e line 1. $ perl -Mstrict -Mwarnings -le " my $hr; map { $hr->{stuff} } 1" $ perl -Mstrict -Mwarnings -le " my $hr; $hr->{stuff} for 1" Useless use of hash element in void context at -e line 1.
    Congratulations andreas1234567, you have found a bug: map in void context silences "Useless use of hash element in void context".

    Please use perlbug and report this bug :)

    A similar bug is #68592: 'Useless use of not in void context' should appear in more cases

      Actually, I'm a bit surprised that I do get the same warning with a slightly modified variant:

      $ perl -Mstrict -Mwarnings -MData::Dumper -le 'my $hr; $hr->{stuff}{fo +o} for 1; print Dumper $hr' Useless use of hash element in void context at -e line 1. $VAR1 = { 'stuff' => {} };

      As it autovivifies, it's not "useless", strictly speaking...  Also, what about (intended) side effects with tied hashes?

        As it autovivifies, it's not "useless", strictly speaking..

        By that logic, the following shouldn't warn since calling f() isn't useless:
        $ perl -c -we'f()+123' Useless use of addition (+) in void context at -e line 1. -e syntax OK

        You're mistaken about what gives the warning. The op that does the autovivification is not the one issuing the warning. It's the access of the foo element that issues the warning, and it's indeed useless to access the foo element.

        $ perl -we'my $hr; $hr->{stuff}{foo}' Useless use of hash element in void context at -e line 1.

        is basically equivalent to

        $ perl -we'my $hr; $_ = \%{ $hr }; $_ = \%{ $_->{stuff} }; $_->{foo};' Useless use of hash element in void context at -e line 1.

        The last bit is clearly useless.

        If you prefer a diagram,

        ___________________ scalar context pad fetch. autovivifies / ________________ deref | / ____________ scalar context hash fetch. autovivifies | | / _______ deref | | | / ____ void context hash fetch. warns. | | | | / |--|-|------|-|----| $hr->{stuff}->{foo};

        Similar, without useless bits:

        $hr->{stuff} //= {};

        Also, what about (intended) side effects with tied hashes?

        Warnings aren't always accurate. That's why you can turn them off.

        Update: Additions for the sake of clarity.

        Well the first version also auotovivifies.... i'm not sure what you mean about tied hashes.
      What bug? In:
      map {$hr->{stuff}} 1
      there is no hash element in void context. It's in list context.
        perl590delta says map in void context is no longer expensive. map is now context aware, and will not construct a list if called in void context.

        if there is no list, why is there list context?

Re: Possible useless use of map
by Marshall (Canon) on Mar 18, 2010 at 10:26 UTC
    I personally think that "bare maps...maps that don't use the output of the map" are not a good idea although other Monks would disagree. Modern Perl versions don't have the older memory penalty of a anon array that cannot be deleted, but the style issue persists.

    I think your code could have been written as:

    foreach my $num (@$arr) { $hr->{$num} =1; }
    If you don't have =1, it is easier to see this in a foreach loop. And it means the same thing as your map{}.
    #!/usr/bin/perl -w use strict; use strict; use warnings; use diagnostics; use Data::Dumper; my $arr = [1,2,3]; my $hr = undef; foreach my $num (@$arr) { $hr->{$num} =1; } print Dumper $hr; __END__ prints: $VAR1 = { '1' => 1, '3' => 1, '2' => 1 };
      $hr->{$_} = 1 for @$arr;

      Though I'd probably write just

      @{$hr}{ @$arr } = ();
      and change the code to test for the existence of the key, not the value.

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.

        This idea will certainly work!

        I personally try to avoid using undef as an intentional value because:
        a) this adds a complication about exists(), defined() later in the code,
        b)If you have to export something with an "undef" value to something else other than Perl what do you use? or for that matter even it its Perl, why not 0 or 1 which is easier to parse? undef throws an extra third case into mix. Sometimes this makes sense like SQL's NULL, but why throw that in there if you don't need to?
        c) it doesn't save any memory or execution time.

        We are getting a bit off topic, but this is a hash slice. To get this work with the =1 idea, I think something like the below is required. There isn't foreach, but there is still a loop (except in the enumerated first case).

        I personally like the $hr->{$_} = 1; syntax because it makes clear that we are de-referencing a hash reference.

        A short thing about hash slice is at hash slice. My foreach loop can be replaced with this..

        #foreach my $num (@$arr) #{ #any one of these does the same thing... #pick one... @$hr{@$arr} = (1,1,1); @$hr{@$arr} = map{1}@$arr; @{$hr}{@$arr} = map{1}@$arr; #}
        Oh, the map{1} generates an "@something" that has the same number of "1's" as there are elements in the input @$arr although here it is "anon", having no explicit name. Here that output of the map is used (ie, it is not a "bare" map). In this case, there is still a loop and we've just made the situation more confusing. Clarity of code is important and just because some implementation uses fewer lines doesn't mean that it is faster.
      "Modern Perl versions don't have the older memory penalty of a anon array that cannot be deleted."

      Has there ever been an array involved? I think it's always been a list of values on the stack.

        I think it's always been a list of values on the stack.

        I think they (c/c++ programmers) call that an array :)

      I personally think that "bare maps...maps that don't use the output of the map" are not a good idea although other Monks would disagree.
      Uhm, Perl is context driven. Whether operations "output" 0, 1 or many things isn't up to them. It's up to the context. void context means an operator doesn't return any values.
        I guess a few things got mixed together in this thread.
        @output = map{some code}@input;
        In older versions of Perl the @output will be created whether you give it a name like: @output or not. In other words there will be some @some_anon_thing for which you don't know the "name". I understand this has been fixed as of 5.10. So a version like Perl 5.6 has a "penalty" for a bare map.

        On the style issue, I use map{} for short transformations where I use the output of the map{}, maybe @$aref or some such thing. If it gets hairy, I use a foreach(). I personally don't use "bare" map{}'s as a shorthand foreach(). Now doing so is completely legal and so certainly doesn't rise to the level of "hey that's wrong"! I prefer foreach in those situations partly because I have the thing we are looping over right there at the start of the statement and also that since I use map in consistent style, I can see immediately that we are going some kind of transformation, deg F to deg C or whatever. Also Perl is terse enough that I don't see the need to save a few characters by "bare" map vs foreach. Now sometimes the situation arises where I use a map{} within a foreach, like foreach (map{...}@input), but that is using the output of the map and fits with the short transformation idea.

        I am sure that others have different opinions. I've tried to explain what I do and why - I hope that was understandable and appears rational and consistent. I'm not the "code police".

        Coding has a lot of "art" to it. And there are all kind of exceptions for every "rule".

Re: Possible useless use of map
by happy.barney (Friar) on Mar 18, 2010 at 09:56 UTC
Re: Possible useless use of map
by amir_e_a (Hermit) on Mar 18, 2010 at 20:12 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://829360]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (9)
As of 2024-04-19 16:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found