CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
wantarray returns 1 when function called in void context

 



rpaskudniak
User


Jul 28, 2013, 5:08 PM

Post #1 of 15 (1642 views)
wantarray returns 1 when function called in void context Can't Post

Hi Family.

I was trying to be oh so elegant!

I wrote a subroutine to be callable either passing a floating point number or a reference to an array of such. (I specifically die if caller passes a list.) It can return a scalar if called in a scalar context, or a transform of the original array if called in a list context and passed that reference. What if I want to operate on the passed array itself? Then I call it in a void context, like the way we call chomp.

My function looks something like this:

Code
sub realify 
{
my $thing = $_[0]; # Don't shift, we might need to modify in place
my $rval = 0.0;

die "You may not pass a list to realify(); use array reference"
if ($#_ > 0);
# Real or complex, if absolute value is really tiny, "correct" to 0
#
if (wantarray) # Array context: Assume $thing is an
{ # array reference.
my @rlist = <some stuff based on @$thing>; # Return list
return @rlist;
}
elsif (defined(wantarray)) # Scalar context: assume $thing is a scalar
{ # and caller wants a new value returned
$rval = <Some stuff to set up a scalar return value>
return $rval;
}
else # Void context: Operate on the passed object.
{
<Some stuff to operate directly on $_[0] or the array it references>

# Note: No return statement - void context, after all.
}
}

I have called it with something like the following line:

Code
realify($some_scalar)

So wantarray should be undefined! But NooOOOooo! It returns a 1! Mad This forces my code into logic for which it was not intended. So the bottom line for this post is:
Why is wantarray returning a 1 when I clearly had called the function in void context? It should be undefined.

My workaround it not to be so blessed clever and always return something, even in void context but then I don't learn the correct solution to operating in place or not. (In fact, the earlier, working, version was scalar parameter with scalar context only; I was using the map {} command to operate on the whole array.)

Thanks much for guidance.
--------------------
-- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


(This post was edited by rpaskudniak on Jul 28, 2013, 7:42 PM)


2teez
Novice

Jul 28, 2013, 8:11 PM

Post #2 of 15 (1636 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

Hi,
I don't quite understand or get what you want to use your subroutine for. Though that been said, I think you are not really getting wantarray. Please do perldoc -f wantarray to see how to properly use it.
Suggestions:
1. Pass an array reference to your sub, then check if is an array ref, else the sub dies.
2. then using wantarray, return a list if your subroutine is called in a list context or a 'scalar' of the list generated within your sub, if the subroutine is called in a scalar context.
Like so:

Code
#!/usr/bin/perl -l 
use warnings;
use strict;


print realify( [qw(0.5 tim tolu 900_000)] );

my $an_val = realify( [qw(0.5 tim tolu 900_000)] );
print $an_val;

my @new_arr = qw(apple banana 1234 3.169);
my ( $red, $yellow, $num, $pi ) = realify( \@new_arr );
print $yellow;

my $pi_num = ( realify( \@new_arr ) )[3];
print $pi_num;

## I expect this to die
print realify(@new_arr);

## I also expect this to die
print realify(qw(apple banana 1234 3.169));

## I also expect this to die
print realify( 1 / 10 );

sub realify {
my ($val) = @_; ## or use shift.

die "You can only pass ARRAY reference"
unless ref $val eq 'ARRAY';

wantarray ? return @$val : return scalar @$val;
}


Please NOTE the context in which the subroutine is called.
NOTE: The script above is to illustrate the point mention, you might have to do much more to get what you wanted. Also note how, what is pass into the sub. was checked.
Using '$_[0]' like you did, might not be what you are really looking for.


(This post was edited by 2teez on Jul 28, 2013, 8:15 PM)


rpaskudniak
User


Jul 28, 2013, 10:11 PM

Post #3 of 15 (1631 views)
Re: [2teez] wantarray returns 1 when function called in void context [In reply to] Can't Post

2teez,

Thanks but your reply misses the point of my question by a tad. (Click this YouTube link, http://www.youtube.com/watch?v=Gd6aLnPHqeE, for a more precise definition of a "tad". Sly )

I want to be able to call the function in void context. That means no return value is expected. The function recognizes this call context by noting that "wantarray" is undefined. But that's what blew up in my face; I could see, via the debugger, that wantarray was not only defined but was set to 1, indicating a list context call.

Man, that has *GOT* to be just plain wrong! And it's a pretty good bet that Perl is not at fault. Hence, I'm trying to figure out where my logic has gone haywire.

As intended, once realify() realizes it was called in void context (as indicated by undefined wantarray), it would operate directly on the parameter (passed by reference), with no need to return anything. (Your suggestion always has something being returned. Not my plan.)

In this situation, I do *NOT* want to execute a return(something); I just want to fall back to the caller with no returned value. Your suggestion is concentrating on the parameter that was passed. In the context of my question, it doesn't matter; I care about what the caller expects me to return (or not).

Sorry to be so verbose - it's my affliction anyway - but I want to be perfectly clear about this. (Channeling Tricky Dicky? Wink I need to hit the sack!)
--------------------
-- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


Laurent_R
Veteran / Moderator

Jul 29, 2013, 12:38 AM

Post #4 of 15 (1622 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

If you are passed an array ref, then your function will probably modify the array itself, and you probably don't want to return the array but probably only a boolean return code (success or failure, modified the aray or not, something like this) or even possibly nothing.


Laurent_R
Veteran / Moderator

Jul 29, 2013, 12:49 AM

Post #5 of 15 (1621 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

For me it is working properly. Just one quick test.


Code
use strict; 
use warnings;

print "void context: \n";
test_func();
print "list context :\n";
my @array = test_func();

sub test_func {
my $c = wantarray;
if (defined $c) {
print $c, "\n";
} else {
print "undef\n";
}
}


Execution:

Code
 $ perl test_arr.pl 
void context:
undef
list context :
1



2teez
Novice

Jul 29, 2013, 1:11 AM

Post #6 of 15 (1619 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

Smile Not off the course totally though.
This could be what you wanted:

Code
use warnings; 
use strict;

my $arr_ref = [qw(apple banana 1234 3.169)];
realify($arr_ref);

print realify($arr_ref);

my $n = realify($arr_ref);
print $n;

sub realify {
my ($val) = @_; ## or use shift.

#is ref $val, 'ARRAY', '$val is an ARRAY ref'; ## use for test
die "You can only pass ARRAY reference"
unless ref $val eq 'ARRAY';

if(defined wantarray() and wantarray()){
return @$val;
}
elsif(defined wantarray() and not wantarray()){
return shift @$val;
}
elsif(not defined wantarray()){
print 'void';
}
}



FishMonger
Veteran / Moderator

Jul 29, 2013, 7:57 AM

Post #7 of 15 (1600 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post


Quote
In this situation, I do *NOT* want to execute a return(something); I just want to fall back to the caller with no returned value.

That contradicts your opening problem description and code.

Quote
It can return a scalar if called in a scalar context, or a transform of the original array


So what do you really want it to do?

Even without an explicit return statement, the sub will return the result of the last statement it executed.


(This post was edited by FishMonger on Jul 29, 2013, 7:58 AM)


FishMonger
Veteran / Moderator

Jul 29, 2013, 8:02 AM

Post #8 of 15 (1597 views)
Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post


Quote
I want to be able to call the function in void context. That means no return value is expected.


Then you shouldn't be using wantarray. Since the sub needs to do different things based on the passed in param(s), then you should be using ref instead of wantarray, as others have shown.


rpaskudniak
User


Jul 29, 2013, 11:16 AM

Post #9 of 15 (1588 views)
Re: [FishMonger] wantarray returns 1 when function called in void context [In reply to] Can't Post

Fishmonger et al,

It's time for me to put my cards on the table. (Boy is this gonna be long winded! Crazy) The bottom line, Fishmonger, is that I have divorced the parameter context from the return context.

The module I am writing is for numerical calculations involving real and complex numbers. With the inevitable rounding errors, even in a 64-bit machine, occasionally you get a a number (or component of a complex number) so close to 0 that you just know it would have been 0 in a perfect world.

Motivation: If you feed a real number into the function Math::Complex::root($some_real,3), you would think you get back one one real root; instead your real comes back as a complex with a teeny (but non-zero) Im component. I needed to get rid of that.

The original, working just fine version of realify() accepts a scalar or a reference to a complex number.
  • If the absolute value is very low (based on a global variable, $margin) it returns a 0 (real). This applies to either a real or complex number

  • If the imaginary component is similarly very low, it returns only the real component (cplx->Re). (Hence the name: "realify".)

  • If the real component (->Re) is tiny, it returns a complex number with the same Im component but 0 in the Re component.

  • All without messing with the original parameter. In usage:

    Code
    $smoother = realify($calculated);                      # Make a corrected copy of original 
    $calculated = realify($calculated); # Correct the original in place
    @corrected_list = map {realify($_)} @calculated_list; # Get corrected array without messing up the original.
    @calculated_list = map {realify($_)} @calculated_list; # Correct original list in place

    All without messing up the original parameter, although I could have done so easily just by manipulating $_ in the function. Here's the original (worked just fine) code:

    Code
    sub realify 
    {
    my $thing = shift(@_);

    # Real or complex, if absolute value is really tiny, "correct" to 0
    #
    return ($thing = 0.0) if (abs($thing) < $margin);

    if (ref($thing) eq $class_cplx) # Here we lose components that are so
    { # tiny that we assume them to be errors
    # If imaginary component is really tiny, remove it from the number,
    # producing a real.
    #
    $thing = $thing->Re if (abs($thing->Im) < $margin);

    # Hey, the real component might also be tiny but have a disruptive presence.
    #
    if (ref($thing) eq $class_cplx) # If I have not realified it above then
    { # I am permitted to check its components
    $thing = cplx(0.0, $thing->Im)
    if (abs($thing->Re) < $margin); # Need to keep real part, even if tiny
    }
    }
    # If it was real but tiny, we already took care of that. Nothing left to
    # do but return the transformed parameter
    #
    return $thing;
    }

    By the way, I just noticed a logic bug that I have not encountered in testing. But that is separate from my question.

    Now I wanted to get clever about this, mainly as an exercise in get-your-hands-dirty Perl and decided to make all sorts of options, as I've described in my opening post to this thread. The above code - scalar or complex -> same - is still at the heart of the logic. But anything other scheme - scalar/list/void context or scalar/list-reference parameter, makes recursive calls to itself to reduce the problem to the original scheme. The code is not a role model of efficiency but does (I think) cover all possibilities. Here's that code:

    Code
    sub realify 
    {
    my $thing = $_[0]; # Don't shift, we might need to modify in place
    my $rval = 0.0;

    die "You may not pass a list to realify(); use array reference"
    if ($#_ > 0);

    # Real or complex, if absolute value is really tiny, "correct" to 0
    #
    if (wantarray) # Array context: Assume $thing is an
    { # array reference.
    my @rlist = map {realify($_)} @$thing; # Return list
    return @rlist;
    }
    elsif (defined(wantarray)) # Scalar context: assume $thing is a scalar
    { # and caller wants a new value returned
    return (0.0) if (abs($thing) < $margin); # Just tiny abs value: Quickie

    # If either component of a complex number is really tiny, set it to 0.
    # And if it's the imaginary component, lose it altogether
    #
    if (ref($thing) eq $class_cplx) # If I have not zeroed it above then
    { # I am permitted to check its components
    $rval = $thing; # Preserve original complex number
    if (abs($thing->Re) < $margin) # Need to keep real part, even if tiny
    {
    $rval = cplx(0.0, $thing->Im) # So just zero it out
    }
    if (abs($thing->Im) < $margin) # If imaginary component is tiny
    {
    $rval = $thing->Re; # lose it completely and return a real
    }
    }
    return $rval;
    }
    else # Void context: Operate on the passed object.
    {
    if (ref($thing) eq "ARRAY") # Realifying every element in an array?
    {
    @{$thing} = realify($thing); # Tickle the wantarray code above
    }
    else # Realify only one object
    { # Just work on that one
    $_[0] = realify($_[0]); # Tickle the defined(wantarray) code above
    }
    }
    }

    So now, if I want to operate in place, I would use:

    Code
    realify($calculated);                         # Correct the original in place 
    @corrected_list = realify(\@calculated_list); # Get corrected array without messing up the original.
    realify(\@calculated_list); # Correct original list in place

    The problem: This was called to realify an array (passed as a reference) in place. When I make the second recursive call, which is scalar to scalar (happens to be the number 1), it should, I think, tickle the defined(wantarray) code, as in the comment. Instead it tickles the "if (wantarray)" code and sties to treat the scalar '1' as an array reference. It barfs, of course:

    Quote
    Can't use string ("1") as an ARRAY ref while "strict refs" in use at ... line 602
    ::realify(1) called at ... line 602
    ::realify('ARRAY(0x80a1ad60)') called at ... line 630
    ::realify('ARRAY(0x80a1ad60)') called at ... line 419

    Now, I could go back to the original scheme; I still have the previous code and calling sequences saved. And if I were writing this on a paid project that's exactly what I would do - get it working, don't look for perfection. But I have that luxury right now and I'd really like to mold this to my will, not the other way around.

    So if you have the patience to eyeball all that, please let me know what logic error I have incurred to provoke that mistaken wantarray.

    Thanks. (Whew!)
    --------------------
    -- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


    Laurent_R
    Veteran / Moderator

    Jul 29, 2013, 1:59 PM

    Post #10 of 15 (1585 views)
    Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

    I am not sure I understood everything.


    Quote
    The bottom line, Fishmonger, is that I have divorced the parameter context from the return context.


    You don't need to divorce them, they are two completely different things.

    You can call a function and pass one or several parameters to it. This can easily be verified within the called subroutine by checking the value of @_ in scalar context, which will give you the number of arguments. If 0, void, if 1, scalar, if more than 1, list (even if you pass just one array to the function, the function will see a list or parameters. If the argument is an array ref or a hash ref (or code ref or any other reference), you can check with the ref function.

    The wantarray function has absolutely nothing to do with that and does not care at all about the number of arguments passed to the function in which it is called.

    Used in a subroutine, the wantarray function tells you what the line of code that called that subroutine is expecting as return value(s): it could be void, a single scalar (that includes possibly a reference to something) or a list.

    Maybe you know very well all this, but I had the feeling that there might some confusion in your mind on these two completely different things.


    rpaskudniak
    User


    Jul 29, 2013, 2:37 PM

    Post #11 of 15 (1582 views)
    Re: [Laurent_R] wantarray returns 1 when function called in void context [In reply to] Can't Post


    Quote
    Maybe you know very well all this, but I had the feeling that there might some confusion in your mind on these two completely different things.

    That's funny. Laurent. I went through all that because I perceived there was some confusion on the part of my replyers on that same point. I indeed know this quite well.

    To reiterate the problem: When the function is called in a void context, in which case wantarray should not even be defined, wantarray was 1.

    I will take a break from this thread for a day while I trace carefully through every blessed step of the logic in progress.
    --------------------
    -- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


    Laurent_R
    Veteran / Moderator

    Jul 29, 2013, 2:51 PM

    Post #12 of 15 (1581 views)
    Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

    Well, look again at the test I made on post 005 of this thread. With a void context, wantarray does return an undef value. This is, I think, the standard expected behavior.

    Having said that, I am not at all claiming that you should consult a psychiatrist and ask an admission ticket to the loony bin Wink, I remember having seen in the past cases where I was also somewhat puzzled by the unexpected results of the wantarray function, although this is too long ago for me to remember the details.


    BillKSmith
    Veteran

    Jul 29, 2013, 3:48 PM

    Post #13 of 15 (1578 views)
    Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post

    I would prefer to see complex numbers treated as objects. Derive a domain specific class of complex number which includes this function as a member.
    Good Luck,
    Bill


    rpaskudniak
    User


    Jul 29, 2013, 10:13 PM

    Post #14 of 15 (1573 views)
    Re: [Laurent_R] wantarray returns 1 when function called in void context [In reply to] Can't Post

    Bill, are you reading my mind? The fact that the list includes complex numbers is totally incidental to this whole business but I will be postng another thread about Math::Complex and its documented refusal work with Math::BigFloat. But that's another story altogether.

    Laurent, Thanks for the sympathy. How are you so sure I'm not already in an asylum? Crazy But like so many inmates in such, I have merely been misunderstood. But I got my answer and MAN is it subtle:

    In the above whole function, at some point I use the map {} operator to realify the whole array, internally looping for one scalar context call for each element of the array. To probe this, I wrote the following short program, included here with line numbers:

    Code
      1 #!/usr/bin/perl -w -d 
    2 # Test the realify function for wantarray
    3 #
    4 use strict;
    5
    6 my $numb = 37;
    7 printf ("\nCalling realify with void context\n");
    8 realify($numb); # Call with void context
    9
    10 printf ("\nCalling realify with scalar context\n");
    11 my $numb2 = realify($numb);
    12
    13 printf ("\nCalling realify with list context\n");
    14 my ($s1, $s2, $s3) = realify($numb);
    15
    16 printf ("\nCalling in scalar context via map{} command\n");
    17 my @nums = (31, 42,53);
    18 my @rnums = map {realify($_)} @nums;
    19
    20 sub realify
    21 {
    22 my $thing = $_[0]; # Don't shift, we might need to modify in place
    23 my $rval = 0.0;
    24
    25 printf("Entered realify(%d) with wantarray == <%s>\n", $thing, wantarray);
    26 if (wantarray) # Array context: Assume $thing is an
    27 { # array reference.
    28 printf("Wantarray is <%s>; I was called with list context\n", wantarray);
    29 return (1, 2, 3); # return a list
    30 }
    31 elsif (defined(wantarray)) # Scalar context: assume $thing is a scalar
    32 { # and caller wants a new value returned
    33 printf("Wantarray null but defined; I was called with scalar context\n");
    34 return ("Not-1");
    35 }
    36 else # Void context: Operate on the passed object.
    37 {
    38 print("wantrarray not defined; I was called with void context\n");
    39 }
    40 }

    And here is the output:

    Quote
    Calling realify with void context
    Use of uninitialized value in printf at ./realify.pl line 25.
    at ./realify.pl line 25
    main::realify(37) called at ./realify.pl line 8
    Entered realify(37) with wantarray == <>
    wantrarray not defined; I was called with void context

    Calling realify with scalar context
    Entered realify(37) with wantarray == <>
    Wantarray null but defined; I was called with scalar context

    Calling realify with list context
    Entered realify(37) with wantarray == <1>
    Wantarray is <1>; I was called with list context

    Calling in scalar context via map{} command
    Entered realify(31) with wantarray == <1>
    Wantarray is <1>; I was called with list context
    Entered realify(42) with wantarray == <1>
    Wantarray is <1>; I was called with list context
    Entered realify(53) with wantarray == <1>
    Wantarray is <1>; I was called with list context

    Notice the last set of output lines? Although the map{} operator is surely calling realify() in scalar context for each element of @nums, the called function believes it was called in list context, possibly due to the @array in the LHS of the map{} operator. Oh, come on, Larry! That kind of subtlety is just not fair!

    Back in my code, I converted the map{} usage to a brute force loop and, Jubilation! It loves me again! Cool (I pound on my desk and I howling!)

    Oh, by the way, when I am called in void context, the debugger does indeed report that wantarray has a value of 1. In fact, caller(0)[5] reports that the $wantarray entry is 1. But when I step down the logic, it indeed goes to all the right places. And if I try to print the value of wantarray, I get an "uninitialized value" error. (See the error message from line 25.) So that whole business about wantarray returning a 1 is a nagy piros hering (big red herring, in Hungarian), sold to me by the debugger. (Not by fishmonger? Wink )

    Problem solved, program working again, more learned than I expected. And it really is more elegant now. Though it's still a mystery to me that the debugger reports the 1 instead of undef. Can't win 'em all.

    Thanks for all the efforts, Fishmonger, Laurent, 2teez (Related to fruteez?). As a reward, you have all been warned about this subtlety.
    --------------------
    -- Rasputin Paskudniak (In perpetual pursuit of undomesticated, semi-aquatic avians)


    Laurent_R
    Veteran / Moderator

    Jul 31, 2013, 2:40 PM

    Post #15 of 15 (1542 views)
    Re: [rpaskudniak] wantarray returns 1 when function called in void context [In reply to] Can't Post


    In Reply To
    Although the map{} operator is surely calling realify() in scalar context for each element of @nums, the called function believes it was called in list context, possibly due to the @array in the LHS of the map{} operator.


    That is most probably wrong. Surely the map{} operator is working on one element of the array at a time, but the map block can return two or several values for each input element, so that map is quite obviously imposing a list context.

    As an example; map receives and returns 3 values for each value of the array on the right-hand side in the following Perl one-liner:


    Code
    $ perl -e 'print join " ", map {2*$_, 3*$_, 4*$_;} 5..10' 
    10 15 20 12 18 24 14 21 28 16 24 32 18 27 36 20 30 40


    So it is quite logical to have map giving a list context to whatever is in the map block.


    In Reply To
    Oh, by the way, when I am called in void context, the debugger does indeed report that wantarray has a value of 1. [..]
    Though it's still a mystery to me that the debugger reports the 1 instead of undef.


    That's exactly it, now I remember clearly.

    When I said earlier that I remembered having seen in the past cases where I was also somewhat puzzled by the unexpected results of the wantarray function, that was when trying it under the debugger.

     
     


    Search for (options) Powered by Gossamer Forum v.1.2.0

    Web Applications & Managed Hosting Powered by Gossamer Threads
    Visit our Mailing List Archives