Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Assignment to a value only if it is defined

by voj (Acolyte)
on Jan 20, 2010 at 10:34 UTC ( #818418=perlquestion: print w/ replies, xml ) Need Help??
voj has asked for the wisdom of the Perl Monks concerning the following question:

Hi! found myself coding a lot of ugly statements like this:

$foo = $bar if defined $bar;

This is fine unless $bar is a complex statement or a function that I don't want to call twice. Of course I could create a temporary variable which makes the code even uglier:

$tmp = bar(); $foo = $tmp if defined $tmp;

What I would like is a short and simple syntax where I only have to type each part of the conditional assignment once:

$foo =ifdef $bar

Any ideas? The code should be short and readable. Bonus points if $foo can also be a list.

Comment on Assignment to a value only if it is defined
Select or Download Code
Re: Assignment to a value only if it is defined
by Corion (Pope) on Jan 20, 2010 at 10:40 UTC

    If you're using Perl 5.10, you have the defined-or operator //:

    $foo //= $bar; # equivalent to $foo = $bar if defined $bar;

      not quite — $foo //= $bar would test if $foo is defined, not $bar (it's equivalent to $foo = $bar unless defined $foo).

      Wrong. That's equivalent to
      $foo = $bar unless defined $foo;
      What the OP wants is:
      $foo = $bar // $foo;
      which is equivalent to
      $foo = $bar if defined $bar;
      assuming the absence of ties and overloading.
Re: Assignment to a value only if it is defined
by jettero (Monsignor) on Jan 20, 2010 at 12:01 UTC
    If bar() is a complex function, then perhaps it should be the one returning the default value...

    If that's not reasonable, then perhaps you need a default wrapper or something... Here's two ideas:

    sub mybar { my $t = bar(); return "default" unless defined $t; return $t; } sub default { my $to_call = shift; my $default = shift; my $t = to_call->(@_); return $default unless defined $t; return $t; }

    Or just do what the others said above (5.10+ only): $x = bar() // "default";

    -Paul

      The "default" action is not to do a assignement at all. But using the current value of $foo will do it as JavaFan suggested:

      $foo = $bar // $foo;

      However this is not much better then

      $foo = $bar if defined $bar;

      Because the other part of the statement ($foo) is duplicated - if I have a long variable like $myhash{mykey}->{mykey2} instead of $foo it only gets uglier. I was looking for a simple and short syntax like:

      $foo =// $bar

      But appereantly such operator does not exist in Perl. The best solution I found so far (also ugly but not getting more complex if $foo and $bar are long expressions) is:

      {local $_ = $bar; $foo = $_ if defined $_;}
        Because the other part of the statement ($foo) is duplicated - if I have a long variable like $myhash{mykey}->{mykey2} instead of $foo it only gets uglier

        Then try:

        $_ = $bar // $_ for $myhash->{mykey}{mykey2};

        {local $_ = $bar; $foo = $_ if defined $_;}

        $foo = $_ for grep defined, expensive();
Re: Assignment to a value only if it is defined
by girarde (Friar) on Jan 20, 2010 at 18:27 UTC
    Your temporary assignment approach looks pretty short to me, unless this is golf.
Re: Assignment to a value only if it is defined
by ikegami (Pope) on Jan 20, 2010 at 19:01 UTC
    my $bar = expensive(); $foo = $bar if defined $bar;
    if (defined(my $bar = expensive())) { $foo = $bar; }
    $foo = $_ for grep defined, expensive();
    sub assign_ifdef { $_[0] = $_[1] if defined($_[1]) } assign_ifdef($foo, expensive());

    The preceeding solution is very similar to what you asked (=ifdef) and very simple (single op).

    Bonus points if $foo can also be a list.

    I'm not sure if you mean

    my $bar = expensive(); ($i1, $i2) = $bar if defined $bar;
    or
    if (defined(my $bar = expensive()) { $_ = $bar for $i1, $i2; }

    Update: Added second and third.

      Thanks! That perfectly fits to assign to a scalar:

      sub setifdef { $_[0] = $_[1] if defined($_[1]) } setifdef $foo, $bar;

      And in case of a list I'll use the (slightly less readable) 'for grep defined,' psuedo-inline operator. It only makes sense with list references (if you treat (undef) as valid list). Defined scalar values which are transformed to a list can also be handled:

      @list = @{$_} for grep defined, $listref_or_undef; @list = split(",",$_) for grep defined, $splitme_if_defined;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://818418]
Approved by marto
Front-paged by MadraghRua
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (15)
As of 2014-10-01 17:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (30 votes), past polls