novacik
novacik

Reputation: 1507

Only one of two arguments should be defined

What is the more elegant way to write the next?

sub depend {
    my($x,$y) = @_;
    die "only one allowed" if( defined($x) && defined($y) );
    die "one must be defined" unless ( defined($x) || defined($y) );
    if( defined($x) ) {
         $y = somefunc($x);
    } else {
         $x = somefunc($y);
    }
    return($x,$y);
 }

The function should get exactly only one argument. If get defined both = error, if defined none = error. And the undefined arg is calculated based on the defined one.

Upvotes: 2

Views: 135

Answers (4)

chepner
chepner

Reputation: 530960

I might define the subroutine to take two arguments, but treat them as a key-value pair. To use the width/height example from your comment:

sub depend {
    my $key = shift;
    my $value = shift;
    die "One parameter only" if @_;
    return ($value, calc_height($value)) if $key eq "width";
    return (calc_width($value), $value) if $key eq "height";
    die "Must specify either height or width, not $key";
}

my ($w1, $h1) = depend( width => 5 );
my ($w2, $h2) = depend( height => 10 );
my ($w3, $h3) = depend();  # ERROR Must specify either height or width
my ($w4, $h4) = depend( other=>3 );  # ERROR Must specify either height or width, not other
my ($w5, $h5) = depend( foo => bar, 7); # ERROR, One parameter only

Upvotes: 1

mpapec
mpapec

Reputation: 50637

Using xor might not be intuitive, and solution below could easily be extended to more input arguments,

 sub depend {
    my($x,$y) = @_;

    die "There should be only one!" if (grep defined, $x,$y) != 1;

    if( defined($x) ) {
         $y = somefunc($x);
    }
    else {
         $x = somefunc($y);
    }
    return($x,$y);
}

Upvotes: -1

choroba
choroba

Reputation: 241808

Use xor, i.e. the "exclusive or":

sub depend {
    my ($x, $y) = @_;
    die "Exactly one must be defined.\n" unless defined $x xor defined $y;

    if (defined $x) {
         $y = somefunc($x);
    } else {
         $x = somefunc($y);
    }
    return($x, $y);
 }

Update: You can shorten the rest of the sub, too. Instead of the if part, just put

return ($x // somefunc($y), $y // somefunc($x));

Upvotes: 10

mogul
mogul

Reputation: 4553

Try this:

sub f 
{
  my ($x, $y) = @_; 
  die "BOOM" if (defined $x ? 1 : 0) + 
                (defined $y ? 1 : 0) != 1;
}

Upvotes: 0

Related Questions