Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Real World 1, Great Expectations 0

by hsmyers (Canon)
on Oct 18, 2001 at 02:22 UTC ( [id://119576]=perlquestion: print w/replies, xml ) Need Help??

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

I've lately been playing around with the IO:: modules, File, Scalar and ScalarArray. I've noticed some behavior that leaves me a little puzzled- the old feature versus bug thing. Here's what I'm talking about, starting with filehandles. No matter which style you use, open FILE, name or $fh = IO::File->new(name), old and new styles are pretty much transparent, i.e. code written one way can be changed to the other with minimal bother. There is one thing though, this:
#!/perl/bin/perl # # test.pl -- IO:: test code... use strict; use warnings; use diagnostics; use IO::ScalarArray; use IO::File; use IO::Scalar; my @list = qw(one.tmp two.tmp three.tmp); my @handles; foreach (@list) { push @handles,new IO::File ">$_"; } for (my $i = 0;$i < $#handles ;$i++) { print $handles[$i] ,"Hi how the hell are ya!"; close $handles[$i]; }
doesn't work worth a damn. You get an error message like:
String found where operator expected at test.pl line 31, near "] "Hi h +ow the hell are ya!"" (#1) (S) The Perl lexer knows whether to expect a term or an operator. + If it sees what it knows to be a term when it was expecting to see an operator, it gives you this warning. Usually it indicates that an operator or delimiter was omitted, such as a semicolon. (Missing operator before "Hi how the hell are ya!"?) syntax error at test.pl line 31, near "] "Hi how the hell are ya!"" Execution of test.pl aborted due to compilation errors (#2) Uncaught exception from user code: syntax error at test.pl line 31, near "] "Hi how the hell are +ya!"" Execution of test.pl aborted due to compilation errors. C:\Perl\Perl_Dev\regex>perl test.pl Undefined subroutine &main::mprint called at test.pl line 31 (#1) (F) The subroutine indicated hasn't been defined, or if it was, it + has since been undefined. Uncaught exception from user code: Undefined subroutine &main::mprint called at test.pl line 31.
It doesn't work any better on similar code using old style typeglobs:
open ONE, ">one.tmp"; push @handles, *ONE; open TWO, ">two.tmp"; push @handles, *TWO; open THREE, ">three.tmp"; push @handles, *THREE;

Dies in the same place and manner. The code works just fine if you create a temporary simple scalar with a copy of the contents of the array position. Like '$fh = $handles[$i]; print $fh yadda, yadda, yadda'. So somehow perl doesn't evaluate 'array position containing scalar' to 'scalar contained'. Which last, is certainly what I had expected. Note that any reduction to simple scalar works fine.

Another peculiarity, lies in the difference between filehandles created by IO::File versus IO::Scalar and IO::ScalarArray. Simply put, in the first case $fh = IO::File->new whatever, <$fh> works fine, pretty much keeping up the aforementioned transparency. But, if $fh comes from either IO::Scalar or IO::ScalarArray, the shorthand, <$fh>, dies the usual nasty death.

The work-around is to use either $fh->getline() or $fh->getlines() as needed (wantarray versus scalar context.) Since both inherit from IO::Handles and since the source for handle.pm suggests that:

"The getline method returns a single line from the filehandle, just like the <$fh> operator when used in a scalar context. the getlines method returns a list of lines in a manner identical to the <$fh> operator in a list context. The getlines method will croak if called in a scalar context."
Note that it doesn't say <$fh> for IO::Scalar and IO::ScalarArray dies...just says this stuff about 'just like the <$fh>...'.

It occurs to me that these two things are just different sides of the same coin. For whatever reason, perl does not evaluate the thing in question as a filehandle and complains accordingly.

So having said all of this, would someone point out to me what I've gotten wrong, misunderstood or the like. I am entirely too aware of my own blind spots, particularly when things don't appear to work as I had expected (that shouldn't be too surprising, great expectations are usually replaced by reality sooner or later…).

hsm

Replies are listed 'Best First'.
Re: Real World 1, Great Expectations 0
by wog (Curate) on Oct 18, 2001 at 03:56 UTC
    Your error message indicates that you had print $handles[$i] "Hi how the hell are ya!";, not what you copied here, so I am going to assume that is what you meant to copy. (And copying-and-pasting in the future will prevent transcription mistakes like this.)

    Your problem is caused by an odd ambiguity of the "indirect object" syntax that print uses. That is, when you say:

    something $array[$i]
    would it mean
    something {$array} [$i]
    or
    something {$array[$i]}
    (where the {} can be used to set off the actual "object" you want to act on)? To resolve this ambiguity without incuring too much lookahead, perl treats it like:
    something {$array} [$i]
    You do have arguments after it, so concievably perl could disambiguite:
    something $array[$i] $something_else
    into:
    something {$array[$i]} $something_else
    but it doesn't try to look that far ahead, it just treats it like:
    something {$array} [$i] $something_else
    which is a syntax error because it has no comma (update: after the [$i])

    Thus, there are two ways to make that take $handles[$i] be the first argument:

    • Use {}s: print {$handles[$i]} "...";
    • Use the IO::Handle module and use the -> syntax: use IO::Handle; $handles[$i]->print("...");

    update: see also perlobj which discusses this ambiguity under the heading "WARNING".

    (update: minor grammatical edit(s) above)

      Yes! and the winner is… Thank you for precisely the information I was looking for. I had tried something like your first answer, but the perldoc said parens or plus sign, not curley braces (what are they called anyway?), so I didn't get far. As for answer #2, certainly this works (although as I said, I hadn't gotten around to IO::Handle yet) but then so do a lot of other techniques as well. What I was looking for was what you explained— why it didn't work, not workarounds. Thanks again,

      hsm
        ...not curley braces (what are they called anyway?)

        I seem to remember reading this in a previous life:
            () are braces
            {} are brackets

        Update: I stand corrected. See pjf's response below.

        mr greywolf
Re: Real World 1, Great Expectations 0
by tadman (Prior) on Oct 18, 2001 at 02:57 UTC
    If you're going to use IO::Handle, use it, don't just stick it in there and then ignore it. Code which makes use of a few methods from IO::Handle, but otherwise uses the regular functions looks schizophrenic. It's not unlike driving on the right hand side of the road most of the time, but sometimes, switching to the left side for no clear reason. Confusing, at best, especially when you're adding code somewhere in the middle. Which method do you use then?

    IO::Handle would have you do it like so:
    #!/usr/bin/perl -w # # test.pl -- IO:: test code... use strict; use warnings; use diagnostics; use IO::File; my @list = qw[ one.tmp two.tmp three.tmp ]; my @handles = map { IO::File->new(">$_") } @list; foreach my $handle (@handles) { $handle->print("Hi how the hell are ya!"); $handle->close(); }
    A few notes:

    I'm using map to convert your @list list into the @handles list. map is great at performing list conversions, so I try to use it as much as possible. Stuff in, stuff out. No mess.

    When you're calling functions, try and put brackets on them to make it clear what you're doing. Sometimes the interpreter can get a little confused about what is the function and what is the object. Throwing brackets in makes it very clear, even to the reader.

    If you open with IO::Handle, close with it too. That is, don't use the internal close method on an object. Only use that on something created with the internal open method. Make everything match up, because sometimes the object does something important that you are skipping.
      A few things... First I'm not using IO::Handle, second, I'm fully aware of a variety of workarounds— that is not the point, the question is why the syntax used doesn't work! A question that was well answered in a later reply. Now for the good news, I hadn't gotten around to using IO::Handle, your comments suggest I should investigate post haste, and I will. Thanks

      hsm
        IO::File inherits from IO::Handle, so the parent methods are also available to you. (You might have better luck with the ambiguous syntax if you use a Perl-style loop. I have not tested that, however.)

        Update: PSI::ESP will only be available when the already-complex Perl grammar is complete enough to DWIMHM (might have meant). :)

        Anyone who can figure out how to allow post-expression for clauses while retaining the standard procedural loop and adding support for finding missing semicolons in the first case, should feel free to send me a message. We could fix a parser bug. *shiver*

        Yeah well, so I'm not using it directly, IO::Handle that is. Insert sheepish grin here[ ____ ]. In defense of self, this whole exercise arose from someone else's code—which same I reduced to a test case, so that we could look at the array reference problem. In real life, I don't mix approaches like that— no really I don't. Nope, not me, my evil twin (who fires from the hip enough to get hip burns) might, but no, not me!

        hsm

        p.s. thanks again...

Re: Real World 1, Great Expectations 0
by premchai21 (Curate) on Oct 18, 2001 at 02:34 UTC

    A style tip: use foreach instead of for. Also, you have an extraneous comma before the string.

    foreach (@handles) { print $_ "Blah blah blah\n"; close $_; }
      Thanks for the tip...however, that has nothing to do with the problem I am illustrating. As I said any technique (and there are a whole host of them) that reduces the reference to a simple scalar makes the problem go away. Work arounds are of course our friend, but that still doesn't explain why what should work doesn't. See prize winning answer below... Thanks

      hsm

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (8)
As of 2024-04-18 07:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found