Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

2 newby questions

by Socrates440 (Acolyte)
on Jun 14, 2012 at 01:35 UTC ( [id://976096]=perlquestion: print w/replies, xml ) Need Help??

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

Hey,I am teaching myself to program in perl with a few online books. For the record, I am no longer in school so none of my questions are school assignments. I am just learning for fun. Anyways, I have two questions. First: I am very confused by this code. I am testing a few of my ideas regarding how to complete the next exercise and I expected this to print "key = bbb value = bbat" at the end but instead it's printing "key = aaa value = aaapple." Can anybody explain to me why? Also, I know that I declared an uninitialized variable. When I understand this piece of the code I will put it to use. Thanks!
#!/usr/bin/perl -w use strict; my ($userin, $key, $value, %fred) ; print "Type in a list of words. Type done when finished.\n" ; %fred = ("aaa" =>"aaapple", "bbb" => "bbbat") ; while (($key, $value) = each(%fred)) { print "key = $key value = $value\n" ; } delete $fred{"aaapple"} ; print "This is after the deleted aaaple" ; while (($key, $value) = each(%fred)) { print "key = $key value = $value\n" ; }
Next: I got a bit stuck on my latest perl exercize so I looked up the book's example in the hopes that I could understand it and create my own version. The program is supposed to ask users to enter words and then produce a hash such that the keys and values list which words were entered and how many times they were entered. My question is about line four. It seems as though the statement "$count{$word} = $count{$word} + 1;" translates into add one to the key $count associated with the value $word. I gues what confuses me is that I am used to the ++ statement looking like $count++ does $count{word}++ do the same thing for the key count?
chomp(@words = <STDIN>); # read the words, minus newlines foreach $word (@words) { $count{$word} = $count{$word} + 1; # or $count{$word}++ } foreach $word (keys %count) { print "$word was seen $count{$word} times\n"; }

Replies are listed 'Best First'.
Re: 2 newby questions
by ww (Archbishop) on Jun 14, 2012 at 02:39 UTC
    Re your second question, here's a question (rhetorical as well as didactic, as we'll soon see):
    "Why not try it?"

    Now, that would be terrible advice if your program were invoking rm * or del *.* /FQ or drop tables LIKE *.

    But, it isn't.

    Many, many newcomers to perl seem to fear the empirical method of obtaining answers to such questions as "does $count{word}++ do...?"

    It's OK. But know that your computer won't belch "blue flames" (as one infamous poster asserted Perl had caused his to do) nor will a cautiously written test have you looking at a BSOD or kernel crash...nor will doing so cause your taxes to go up or your dog to run away. So, go ahead and try it.

    PS: the answer is "no."

      "Why not try it?"

      Excellent advice, especially with the caveats given.

      PS: the answer is "no."

      Some clarification may help the OP:

      There are 4 ways to increment a counter by 1:

      (A) $count{$word} = $count{$word} + 1; (B) $count{$word} += 1; (C) $count{$word}++; (D) ++$count{$word};

      First, note that the $ prefix is required in $word here (this was supplied in the OP’s code, but not in the OP’s question). The difference between (C) and (D) is detailed in perldoc.

      The real difference arises when warnings are turned on. (A) produces warnings of the form:

      Use of uninitialized value within %count in addition (+) at ... line ..., <STDIN> line 1.

      whereas (B), (C), and (D) do not. In all 4 cases, Perl creates the hash entry by a process called autovivification (on which there is a useful tutorial by Uri Guttman), which creates a new hash entry with the specified key and assigns it a value of undef. The difference is that in (A), Perl initially sees you accessing this undefined hash entry in the addition on the right of the assignment and issues a warning, whereas in (B), (C), and (D), Perl is smart enough to know that the warning is not needed. But in their effects, (A), (B), (C), and (D) are the same, in that each increments by 1 the value of the %count entry which has $word as its key.

      Update: SuicideJunkie (below) makes an interesting point. A good place to follow-up on it is in the thread begun by First JAPH - Spell perl in two hundred and eighty five thousand and seventy four easy steps, a 10-year-old post which turns up from time to time in Selected Best Nodes.

      HTH,

      Athanasius <°(((><contra mundum

        The effects do vary in some cases, since ++ is smarter than it looks.

        use strict; use warnings; my $index = 'AH'; $index++; print "++ gives $index\n"; $index += 1; print "+= gives $index\n";

        Results in:

        ++ gives AI Argument "AI" isn't numeric in addition (+) at test.pl line 7. += gives 1
Re: 2 newby questions
by toolic (Bishop) on Jun 14, 2012 at 01:58 UTC
    You need to delete a hash key, not a hash value:
    delete $fred{"aaa"} ;
Re: 2 newby questions
by choroba (Cardinal) on Jun 14, 2012 at 10:46 UTC
    You seem to be misunderstanding how hashes work. A value is associated to a key, not vice versa. Therefore, $hash{key}++ increases the value associated with key, not the key itself.
Re: 2 newby questions
by aaron_baugher (Curate) on Jun 14, 2012 at 13:15 UTC

    In addition to the good replies above, this:

    chomp(@words = <STDIN>); foreach $word (@words){ $count{$word}++ } # is better done like this: while( my $word = <STDIN> ){ chomp $word; $count{$word}++; }

    As you can see, the second idiom doesn't require the @words array at all, since it reads and processes the data one line at a time. That doesn't matter much for small amounts of data, but for larger amounts the first method could run into memory constraints. It's best to use the second method by default unless you must use the first for some reason.

    Aaron B.
    Available for small or large Perl jobs; see my home node.

      Wow, I did not expect this much feedback. Thank you all. I took what you posted, what my book recommended, and some other bits and pieces of information and I tried to reconstruct the program in my own way as best I could. Any critiques would be helpful. Particularly, how I could condense the program into fewer lines. Thanks!
      #!/usr/bin/perl -w use strict; my ($counter, $words, %counter) ; print "This program records all of the words you type and counts how o +ften you type them.\nPlease type in a list of words. Type done when +you are finished.\n" ; chomp ($words = <STDIN>) ; while ($words ne "done") { chomp ($words = <STDIN>) ; if ($words eq "done") { last ; } $counter{$words}++ ; } foreach $words (keys%counter) { if ($counter{$words} <= 1) { print "You typed $words $counter{$words} time." } else { print "You typed $words $counter{$words} times." ; } }

        You're kind of repeating yourself inside and outside your while loop, so that can be condensed. Here's a more perlish way to do it. It confines the $word variable to the smallest possible scope, and uses a statement modifier after last to shorten things and make the logic clearer (in my opinion).

        while( my $word = <STDIN> ){ chomp $word; last if $word eq 'done'; $counter{$word}++; }

        On your foreach loop, you can eliminate the if/else with a ternary operator:

        for my $word (keys %counter){ print "You typed $word $counter{$word} time" .($counter{$word}>1 ? ' +s' : ''). ".\n"; # or printf "You typed %s %d time%s.\n", $word, $counter{$word}, ($counte +r{$word}>1?'s':''); }

        Aaron B.
        Available for small or large Perl jobs; see my home node.

        You can shorten your while loop:

        $counter{$_}++ while chomp($_ = <>) && !/^done$/i;

        I threw in !/^done$/i to terminate on "Done", "DONE", etc.

        And note the subtle suggestion of others to change $words to $word, which is clearer.

        What about this one, is this code condensed enough?

        #!/usr/bin/perl -w use strict; use warnings; my %counter; print "This program records all of the words you type and counts how oft +en you type them.\nPlease type in a list of words. Type done when yo +u are finished.\n"; $counter{$_}++ while chomp($_ = <STDIN>) && !/^done$/i; foreach my $line ( sort keys %counter ) { printf "You typed '$line' $counter{$line} time%s.\n", ($counter{$l +ine} == 1) ? "" : "s"; }

        Also note the 'my' in the foreach, always use this idiom!

Re: 2 newby questions
by flexvault (Monsignor) on Jun 14, 2012 at 13:13 UTC

    Welcome Socrates440,

    As a 'newby' you may not be aware of the great command line tool that Perl is. For one liners, I use $h (hash), $ar (array), $s (scalar). For now, just avoid using $a and $b, as these are used by sort. So you could have tested your question immediately from the command line as:

    perl -e ' $h{W}=2; $h{W}++; print "$h{W}\n";' Result: 3
    or if you have a Perl of version 5.10 or later you could use '-E' with 'say'.

    Obviously, you could have typed 'use strict' as you should in your scripts, but this is a throw away one-liner, so why type more.

    It's a fast way to test your question. In the beginning I never used this great capability, but now I keep a xterm open just for this purpose.

    Good Luck

    "Well done is better than well said." - Benjamin Franklin

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (2)
As of 2024-04-26 00:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found