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

I'm still a beginner with perl. I wrote this little script, but perl keeps saying I've got a syntax error at line 6: near "$count{"
#!/usr/bin/perl use warnings; use strict; chomp(my @words = <STDIN>); foreach my $word (@words) { my $count{$word} += 1; } foreach my $key (sort keys %count) { print "$key was found $count{$key} times\n"; }
What's wrong? I found out, if you define %count outside the foreach loop with my %count; and omit "my" within the loop, it's working. But "my" within the loop results in the mentioned warning.

Replies are listed 'Best First'.
Re: Syntax error - "my" within foreach loop
by FunkyMonk (Bishop) on Apr 13, 2008 at 19:16 UTC
    What's wrong? I found out, if you define %count outside the foreach loop with my %count; and omit "my" within the loop, it's working. But "my" within the loop results in the mentioned warning.
    Nothing! That's exactly how you do it
    #!/usr/bin/perl use warnings; use strict; chomp(my @words = <STDIN>); my %count; foreach my $word (@words) { $count{$word} += 1; } foreach my $key (sort keys %count) { print "$key was found $count{$key} times\n"; }

      Thanks FunkyMonk. As far as I understand it, it's just wrong to use "my" within the foreach loop in this context.
        It's not wrong because it's in a loop, but because you're accessing a hash element.

        my declares a new, lexical variable. But you don't want to declare a new variable, you're accessing an item in an existing data structure.

        I believe the point of "my" declarations is to simplify finding the "root" of a scoped variable. Variable scoping is an effect of command nesting; in your example the "$count{$word}" declaration is nested in the first foreach loop, which is within the scope of the "my $word" declaration and the "my %count" declaration (necessarily added by FunkyMonk). The variables have this scope because they are outside the loop. That means the variables are also available to the next foreach loop. Since the only one used is %count, you could write: foreach (@words) { my $word = $_; and the scope of $word would be limited to the nest where it is actually used.

        Unfortunately, niether your nor FunkyMonk's code actually works properly because of "@words=<STDIN>". I am sure what you wanted to do was this:

        #!/usr/bin/perl use warnings; use strict; chomp(my $input = <STDIN>); my @words = split / /,$input; my %count; foreach my $word (@words) { $count{$word} += 1; } foreach my $key (sort keys %count) { print "$key was found $count{$key} times\n"; }
        Scoping and the purpose of the "my" declaration will become much clearer when you are writing longer, more complicated and more heavily nested scripts with subroutines, etc. Do stick with "use strict"
        Actually, what it seems like to me is, your original code was trying to access an hash that hadn't been created, so it, the perl interpreter, may have thought it was a scalar with a set of braces at the end of it. Just a thought. Anyone else have an idea?
Re: Syntax error - "my" within foreach loop
by GrandFather (Saint) on Apr 13, 2008 at 20:06 UTC

    You already have good answers to your immediate question, but there are some slightly deeper issues to consider too. But first, it is great to see that you are using strictures (strict and warnings) in the first place - many Perl beginners don't.

    Some languages (C and Pascal for example) require that all variables be declared in a block before any other statements. That often means the declaration is a long way from the first use and it can be hard to be sure where the first use is. Perl on the other hand allows you to declare variables pretty much anywhere you want to. That means that you can mostly declare a variable the first place it is used and assign a value to it as part of the declaration. This is a Good Thing&tm; because it makes it easier to see where and how the variable is used.

    A more subtle thing is that inside a block (a loop body for example) you get a new "instance" (fresh sheet of paper to write the value on) every time the program flow enters the block and the variable in most cases can't be accessed outside the block.

    There are many other things that you will learn about variables in Perl over time including closures and that the loop variable for a for loop is an alias and doesn't persist outside the loop. Enjoy your new adventure. ;)


    Perl is environmentally friendly - it saves trees
      I like being able to declare variables anywhere, especially if they are unimportant accumulators or iterators. However I will often predeclare varibles early in the code or block where they are needed so that I can group them and describe them in a comment block. YMMV.

        A comment on a variable indicates in one place what the variable is for. An appropriately named variable tells the reader every place it's used what it is for.

        A comment that explains a variables role in following code should be placed with the code, not where a collection of variable declarations may huddle together at the top of a block or subroutine.

        A key element of good commenting is to place the comment as close to the real context for the comment as possible and to only to explain non-obvious elements of the code (there may be some disagreement over what is 'non-obvious').

        There is a lot of art in naming variables and writing good comments. There is much less art required in figuring out where to declare variables - code structure and the "late as possible" mantra dictates that.

        Something related to think about: a useful guide is that the length of an identifier should reflect the scope over which it is used - short identifiers for small scopes and longer identifiers for larger scopes. $tl is ok for a variable in a small scope, but $topLeft is better in a bigger scope, and $mainWndTopLeft may be appropriate in a large scope.


        Perl is environmentally friendly - it saves trees
Re: Syntax error - "my" within foreach loop
by ww (Archbishop) on Apr 13, 2008 at 19:54 UTC

    And just in case your next question is something on the order of "Why doesn't this DWIM?, consider that you've provided no mechanism for your script to know what marks the end of your input.

    chomp(my @words = <STDIN>);

    ...has no control mechanism to terminate the input and thus, as it stands, it runs out of control on the CLI or under debug.

    As this smells like more homework (label it as such, if so), the solution of that (very basic) bit of logic is left to the student.

      has no control mechanism to terminate the input
      ^D worked for me :)

        True, but better for the student to learn the importance of prompting the user clearly, and explicitly... AND to learn first to code using explicit constructs, rather than relying on implicit (and OS-dependant) ones.

        For example, lusers could still screw this up, but at least the program's author would have tried:

        ... print "Type a series of words, separated by spaces, ending with a retu +rn:\n"; my $words = <STDIN>; chomp $words; my @words = split (/ /, $words); ...
Re: Syntax error - "my" within foreach loop
by Klammer (Acolyte) on Apr 14, 2008 at 00:51 UTC

    Thanks alot to everyone.

    I think I understand now, why it's wrong to use "my" within the foreach loop.

    Just for your information: I'm currently trying to learn Perl by reading the Llama book (Learning Perl). The question is about my solution to one of the exercises within the book. Don't wonder about my @words = <STDIN> because including the current chapter the author always works in this way. End of input is reached by hitting Ctrl+D. This is until we reach a future chapter where he probably tells us how to solve it in a better way. I assume it might be this "split" solution which I have already seen.

    btw. concerning use diagnostics;. I already knew about it, but it wasn't very helpful in this case.

Re: Syntax error - "my" within foreach loop
by rudder (Scribe) on Apr 13, 2008 at 20:47 UTC

    If you have "my" inside the loop, it makes perl create a new lexical every time around -- and throws out the previous one.

    Try turning on "use diagnostics;" to see more info about the problem.

      Which diagnostic do you have in mind?

        I've never used diagnostics with more sophistication than simply putting a "use diagnostics;" at the top of a script that's failing with a message that I don't understand.

        Though, I probably should've tried it with the OP's code before submitting my comment here, as it doesn't seem to reveal anything pithy that's not already in the orginal error message.

        In my defense, Klammer says he's a beginner, and it seemed like a good idea to recommend "use diagnostics;" in general, in case he was unaware of it.