Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

||= (poorly documented?)

by live4tech (Sexton)
on Jul 09, 2012 at 05:43 UTC ( [id://980650]=perlquestion: print w/replies, xml ) Need Help??

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

I recently came upon the following line of code:

$FH = $OUTFH{$name} ||= IO::File->new(">g:\\perl_scripts\\$name.log") +or die $!;

I have looked for the ||= operator and found it only in a table in perlop. There is no description of it or examples using it. I have not found it anywhere else.

From the context of the above code line and through some testing I found that the line works like this: $FH will be set to $OUTFH{$name} if $OUTFH{$name} is defined. If $OUTFH{$name} is undefined, $FH is set to the IO::File reference.

Is this the same as //? This is really poorly documented...

Replies are listed 'Best First'.
Re: ||= (poorly documented?)
by frozenwithjoy (Priest) on Jul 09, 2012 at 05:55 UTC

    You are almost correct in understanding how it works. Whatever is on the right side of the ||= operator is used if the left side evaluates to undef, like you said; however, it is also used if the left evaluates to false.

    my $string; $string ||= "Oh noes! Your variable was undef or false!";

    I believe //, on the other hand, only tests for whether the left side is defined (but not for truthiness).

Re: ||= (poorly documented?)
by kcott (Archbishop) on Jul 09, 2012 at 06:02 UTC
Re: ||= (poorly documented?)
by tobyink (Canon) on Jul 09, 2012 at 06:54 UTC

    In general, where X is an existing operator, then:

    $foo X= $bar; # is short for: $foo = $foo X $bar;

    Practical examples:

    $foo += $bar; # is short for: $foo = $foo + $bar; $foo ||= $bar; # is short for: $foo = $foo || $bar; $foo .= $bar; # is short for: $foo = $foo . $bar; $foo *= $bar; # is short for: $foo = $foo * $bar;

    This convention originally comes from Algol 68, but was made popular by C/C++ and is also available in many other languages (Java, Ruby, Python, Javascript, PHP, etc).

    I would suppose that the reason these aren't documented in detail in perlop would be that people are expected to be already familiar with the convention. The Perl documentation probably shouldn't assume familiarity with other programming languages, but (especially for the older parts of the manual) they often seem to.

    By the way, what the code you originally posted does is this: it creates a file handle $FH by opening the file with IO::File, but uses a hash %OUTFH in order to cache the file handles, and avoid opening the same file twice.

    A longer way of writing the same thing would be:

    $OUTFH{$name} = $OUTFH{$name} || IO::File->new(">g:\\perl_scripts\\$na +me.log"); $FH = $OUTFH{$name} or die $!;

    It's actually a fairly common construct. The poor man's memoization...

    my $result = $cache{$input} ||= my_function($input);

    ... and probably ought to be documented in some of those lists of Perl idioms.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: ||= (poorly documented?)
by Marshall (Canon) on Jul 09, 2012 at 07:36 UTC
    Maybe this will help...
    #!/usr/bin/perl -w use strict; ## The ||= operator tests for "truthfulness" ## The //= operator tests for "definedness" my $z = 25; my $x = 550; $z ||= $x; print "$z\n"; #prints 25 because z is already true my $y; # $y is undefined print "y is not defined\n" if !defined $y; $y //= 32; print "y is is defined now as $y\n" if defined $y; my $k; #$k is undefined #An undefined value evaluates to "false" #and the assignment proceeds $k ||= 3842; print "k is $k ...hey I'm defined now!\n"; __END__ 25 y is not defined y is is defined now as 32 k is 3842 ...hey I'm defined now!
    Update:
    There is a rather strange thing that can happen in Perl.
    It is possible for Perl to return a "true", "zero" value.
    It does this by returning the string "0E0": 0 * 10**1 =0 numerically, but that evaluates to "true" in a logical sense. This is used in the Database Interface for example. You might get back an answer "hey I worked, but I didn't modify any rows!". In an language like C or Java, you have to have two variables: Number of rows and did it work or not? In Perl this can be expressed in a single value. "I worked, but didn't modify any rows!". A "0" is logical False while a 0E0 is logical True- pretty cool!

    Update:
    I got off track, but since I'm talking about 0E0, I will show some code for your amusement.

    #!/usr/bin/perl -w #use strict; $|=1; #Turn off STDOUT Buffering # This is wild but Perl has # a special string that will evaluate # to a "true", "zero" value and can be used # in a numeric computation - even with # warnings! # This is so obscure that it must be # depreciated in favor of the 0E0 notation. # I personally wouldn't use this, and I # show it just for amusement. This just # Perl trivia. my $str_zero = "0 but true"; ### special string ### $str_zero += 1; print "new str_zero is: $str_zero\n"; # No warning, this is the same as 0E0 my $bogus_zero = "0 bogus"; $bogus_zero +=1; print "bogus_zero plus one is: $bogus_zero\n"; # "works" albeit with a warning __END__ new str_zero is: 1 Argument "0 bogus" isn't numeric in addition (+) at C:\TEMP\zeroButTrue.pl line 23. bogus_zero plus one is: 1
      There is a rather strange thing that can happen in Perl.
      It is possible for Perl to return a "true", "zero" value.
      It does this by returning the string "0E0": 0 * 10**1 =0 numerically, but that evaluates to "true" in a logical sense.
      This works because there are only two strings which evaluate as false in Perl: "" and "0".

      Any other string which begins with one or more zeroes followed by a non-digit character will by logically true (since it's not one of the two false strings) while still having the value 0 when evaluated as a number. This actually will also work with strings that start with a non-numeric character (true as a boolean, 0 as a number), but, if warnings are enabled (as they generally should be), it will complain that "Argument "..." isn't numeric" if you try to use it as a number.

      Aside from "0E0", the other string I often see used for this purpose (I'm not sure which is more common) is "0 but true".

        I don't see how you and I are in any kind of disagreement.
Re: ||= (poorly documented?)
by DrHyde (Prior) on Jul 09, 2012 at 09:55 UTC
    You're almost right - ||= checks for *truth*, not definedness. Consider the case where $OUTFH{$name} == 0 - that is, it's defined but not true.
Re: ||= (poorly documented?)
by Anonymous Monk on Jul 09, 2012 at 06:21 UTC

    This is really poorly documented...

    Yes, if you only go by perldoc.perl.org/perlop.html, looking only for |=", it is poorly documented.

      To be fair, some things are a pain in the ass to google properly (especially if you aren't familiar with the topic in the first place). Also, documentation always makes more sense when you already know the topic. :P
        This actually is easily googleable... if you're deep enough into the Perl culture. Try searching for it under the name "orcish maneuver", which will explain not only what the operator is but also exactly how it's being used in this case. (Link is to duckduckgo's first result, which just happens to also be on PerlMonks.)

        Also note that, because of the distinction between truthiness and definedness mentioned in previous replies, you're going to want to use //= instead of ||= in the vast majority of cases unless your code needs to support pre-5.10 versions of Perl.

        To be fair ...

        Teach a man how to find answers and you teach him for life.
        Answer his questions for him and you get Perlmonks XP.

Re: ||= (poorly documented?)
by Anonymous Monk on Jul 09, 2012 at 06:36 UTC
    Yep, that is right and it works the way it reads.
    perl -e "my $shoo = 50; $shoo ||= 100; print $shoo;"
    Prints
    50
    Should be the same as,
    perl -e "$shoo=50; $shoo = ($shoo)?50 : 100;print $shoo;"
    If you scroll down in perlop you'll see that style used to describe what // does, so they appear to be equivalent.

    Celebrate Intellectual Diversity

      If you scroll down in perlop you'll see that style used to describe what // does, so they appear to be equivalent.
      || tests for truthiness (and, therefore, definedness), whereas // only tests for definedness.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://980650]
Approved by kcott
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-03-19 10:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found