Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

open annoyance

by tlm (Prior)
on Sep 29, 2005 at 13:17 UTC ( [id://496091]=perlquestion: print w/replies, xml ) Need Help??

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

The following script tests several variants of a function that opens and returns a lexical filehandle. The versions differ only in the way arguments are passed to open, and they are tested under two conditions: the file to be written to does not already exist, or it does. I show the results of running the script immediately after the code:

use strict; use warnings FATAL => 'all'; my @open = ( q(open( my $fh, @_ )), q(open( my $fh, @_[ 0..$#_ ] )), q(open( my $fh, shift, @_ )), ); my $filename = 'hello_world'; my $test_string = "Hello, world!\n"; for my $prepare ( \&remove, \&truncate ) { for my $o ( @open ) { my $sub = eval "sub { $o or die \$!; return \$fh }" or die 'eval failed'; $prepare->( $filename ); # remove or truncate eval { my $fh = $sub->( '>', $filename ); print $fh $test_string; close $fh; $test_string eq `cat $filename` or die "incorrect contents\n"; }; printf "%-33s: %s", $o, $@ ? $@ : "OK\n"; } print "\n"; } sub remove { unlink shift; } sub truncate { open my $fh, '>', shift or die $!; return; } __END__ open( my $fh, @_ ) : No such file or directory at (eval +1) line 1. open( my $fh, @_[ 0..$#_ ] ) : No such file or directory at (eval +2) line 1. open( my $fh, shift, @_ ) : OK open( my $fh, @_ ) : No such file or directory at (eval +5) line 1. open( my $fh, @_[ 0..$#_ ] ) : Filehandle $fh opened only for inpu +t at pita.pl line 25. open( my $fh, shift, @_ ) : OK

That the first version fails did not surprise me too much, since I know some functions distinguish between arrays and lists in their input, but the failure of the second version does surprise me, especially because the particular nature of the failure depends on whether the file already exists or not.

Incidentally, the second error for the second version ("Filehandle $fh opened only for input...") is actually only a warning; I have rendered it fatal for the purpose of this analysis.

I found the working version by trial and error, but this is annoying. Programming something this simple should not be an experimental science. Besides, without testing all sorts of combinations of inputs, I can't tell whether it will work in general.

Is there any principle that I could invoke to short-circuit this process? Or is it "just one of those things" that one has to live with?

the lowliest monk

Replies are listed 'Best First'.
Re: open annoyance
by japhy (Canon) on Sep 29, 2005 at 13:30 UTC
    The reason, of course, is Perl's prototyping behavior. Example:
    sub proto ($@) { print "$_[0]; $_[1..$#_]\n"; } my @args = qw( a b c d e f ); proto($args[0], @args[1..$#args]); proto(@args[0..$#args]);
    Perl applies scalar() to the first "argument" you pass proto() at compile-time. In this case, that's $args[0] and @args[0..$#args] -- in the former, there is no change, but in the latter, we end up getting the last element in the array! When you pass an array as the first element, it gets transmuted into its size. (But you probably knew most of this already.)

    I've been in a similar situation. The simplest work-around I can think of is the one you've shown, where you separate the first element of the array from the others. Whether you do that destructively (as with shift) or with slices is a personal choice.


    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

      Thanks. I could have saved myself a lot of hassle if I had just looked at the output of prototype( 'CORE::open' ). I'd mistakenly looked at prototype( 'open' ), which returned nothing, so I figured open was one of the builtin functions that doesn't have a prototype... :-/

      the lowliest monk

Re: open annoyance
by thundergnat (Deacon) on Sep 29, 2005 at 14:09 UTC

    Interestingly enough, if you change where you are specifying the IO parameters to the open command, it seems to work for all three styles of open commands.

    Update: Whoops, edit to restore truncate function, still works though.

    Note this was tested under Windows so I changed the cat command, but it is otherwise the same.

    use strict; use warnings FATAL => 'all'; my @open = ( q(open( my $fh, '>', @_ )), q(open( my $fh, '>', @_[ 0..$#_ ] )), q(open( my $fh, '>', shift, @_ )), ); my $filename = 'hello_world'; my $test_string = "Hello, world!\n"; for my $prepare ( \&remove, \&truncate ) { for my $o ( @open ) { my $sub = eval "sub { $o or die \$!; return \$fh }" or die 'eval failed'; $prepare->( $filename ); # remove or truncate eval { my $fh = $sub->($filename ); print $fh $test_string; close $fh; my $op = ($^O =~/MSWin/) ? 'type' : 'cat'; $test_string eq `$op $filename` or die "incorrect contents\n"; }; printf "%-33s: %s", $o, $@ ? $@ : "OK\n"; } print "\n"; } sub remove { unlink shift; } sub truncate { open my $fh, '>', shift or die $!; return; } __END__ open( my $fh, '>', @_ ) : OK open( my $fh, '>', @_[ 0..$#_ ] ): OK open( my $fh, '>', shift, @_ ) : OK open( my $fh, '>', @_ ) : OK open( my $fh, '>', @_[ 0..$#_ ] ): OK open( my $fh, '>', shift, @_ ) : OK
Re: open annoyance
by diotalevi (Canon) on Sep 29, 2005 at 15:03 UTC

    There's an additional problem. The result of evaluating sub { func( shift, @_ ) } is undefined because you're modifying and using the same variable more than once in an expression. You cannot know whether func() is going to receive a copy of everything in @_ preceded by an additional copy of $_[0] or whether it will give you $_[0] followed by everything remaining in @_.

    func( shift @ary, @ary ); # Is one of the following but which one is not defined: func( $ary[0], @ary ); shift @ary; func( @ary ); shift @ary;

    As a related note, you can't say = ( shift() . shift() ) because you can't know which shift() is going to happen first.

Re: open annoyance
by Tanktalus (Canon) on Sep 29, 2005 at 21:49 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2024-04-16 20:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found