Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Did I get what I expected (an array)?

by bronto (Priest)
on Jul 16, 2002 at 15:41 UTC ( [id://182114]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Fellow monks

I am writing a test script for a module (a class actually), and I'm in doubt on how to check that a method is returning a list of values (not a reference, not a scalar: a plain old list)

Now, the only way to check this that comes into my mind is to assign the return value to both a scalar and a list and see if the assigned values are consistent. For example, this scriptlet works as I expect:

use strict ; use warnings ; sub test { my @x = qw(test) ; return @x } my $test = test() ; my @test = test() ; print ">>> $test\n" ; print map "[$_]\n",@test ; print $test == scalar(@test)? "Yepa!\n" : "Nope!\n" ;

I am wondering if there is a better way (i.e.: more Perlish or more compact) to do the same job.

Please note that I would like to do this without relying on extra modules, unless they are bundled with Perl 5.6.1; I don't want make test to depend on the presence of an optional module (other than those required by my modules themselves).

Thanks for any advice

--bronto

# Another Perl edition of a song:
# The End, by The Beatles
END {
  $you->take($love) eq $you->made($love) ;
}

Replies are listed 'Best First'.
Re: Did I get what I expected (an array)?
by arturo (Vicar) on Jul 16, 2002 at 16:13 UTC

    One of the interesting things about Perl is that what a subroutine returns is determined by the context in which it is called:

    #!/usr/bin/perl use warnings; use strict; sub foo { my @water = qw(heavy normal polluted); @water; } my $bar = foo(); my @baz = foo(); print "Bar : $bar\n"; print "Baz : @baz\n";

    with "bar", the interpreter sees that it's assigning the returned value to a scalar (the call is made in scalar context), so it gives you the size of the list it returned -- as a matter of fact, you'd get the same result with  my $bar = scalar foo();, so your code isn't doing what you expect. With "baz", it sees that it's assigning the returned value to an array (list context), so it assigns the members of the list it's returning to the array. (BTW, this sort of behavior is changing radically in Perl 6). If your sub returns a scalar, and you assign it to an array, what you get is a one member array. So, to a large extent, you get what you ask for from the subroutine. Perl (v. 5, anyway =) just isn't the kind of language that tries to enforce the distinction you're looking to enforce.

    By the way, you can check for the calling context within the subroutine with wantarray. HTH

    I mistrust all systematizers and avoid them. The will to a system shows a lack of integrity -- F. Nietzsche

Re: Did I get what I expected (an array)?
by Abigail-II (Bishop) on Jul 16, 2002 at 15:58 UTC
    I really do not understand what you are trying to do. In your test program, you are getting 1 in scalar context, and ("test") in list context. And that's ok. Could you give an example of what in your eyes in a failure?

    Also, in your title, you suggest that you want the function to return an array, but in the text, you talk about returning a list. A list, and not a scalar. But this is very confusing. First, a function can not return an array. A function returns either a scalar, or a list. What it returns is determined by the context, and the context alone. A scalar will be returned in scalar context, and a list will be returned in list context. The return @x in the test function never returns an array - it returns a scalar in scalar context, and a list (consisting of the elements of @x) in list context.

    BTW, what is your fear of modules?

    Abigail

      Ok, I'll try a better translation from my thoughts in italian to a better english :-)

      Maybe I should restate the question this way:

      is it possible , and how, to distinguish if a sub/method is returning a single scalar (

      return $this
      , which my test script should consider wrong) or a list (return ($this) or return qw(a bunch of values)) or an array (return @this) which my test script should both consider good?

      I'm writing a test script (one of those that make test runs) that checks if a class of mine or a subclass of it behaves like it should.

      My check detects the case return @this but fails on return qw(this list).

      use strict ; use warnings ; sub test { return ('a','test') } my $test = test() ; my @test = test() ; print ">>> $test\n" ; print map "[$_]\n",@test ; print $test == scalar(@test)? "Yepa!\n" : "Nope!\n" ;

      This one fails:

      $ perl test.pl >>> test [a] [test] Argument "test" isn't numeric in numeric eq (==) at test.pl line 11. Nope!

      (even if I consider test's return value ok for my class...)

      I hope I explained now. Do I?

      Ciao!
      --bronto

      # Another Perl edition of a song:
      # The End, by The Beatles
      END {
        $you->take($love) eq $you->made($love) ;
      }

        Yes, you explained what you want. It's also clear you lack some fundamental knowledge about Perl. You need to make yourself more familiar with the concept context. A subroutine will return a list if and only if it was called in list context. A subroutine will return a scalar if and only if it was called in scalar context. A subroutine will return nothing if and only if it was called in void context. The crucial point is that it is the context that determines whether the subroutine returns a list or a scalar - not the subroutine.

        Furthermore, parenthesis do not create lists (except in some very specific syntax constructs). Parenthesis are used for grouping.

        return $this; return ($this); return (($this)); return ((($this)));
        all mean the same. The do not differ just like there is no difference between:
        3 + 4; (3 + 4); (3) + (4); ((3) + (4));
        As for
        return @x;
        that will return the number of elements of @x in scalar context, and a list consisting of the elements of @x in list context. Perl will never return an array. You can check for yourself:
        my @x = qw /foo bar baz/; sub my_func {return @x}; (my_func) [1] .= "hello";
        This gives a compile error:
        Can't modify list slice in concatenation (.) or string
        Note that it says "list slice". A list, not an array.

        Abigail

Re: Did I get what I expected (an array)?
by broquaint (Abbot) on Jul 16, 2002 at 16:08 UTC
    There's is essentially no difference between returning a one element list and a single scalar. However if you're returning an array (not list) and assigning to a scalar you'll get the length of the list (as opposed to the first element when returning a list). This is all do with the context in which the sub is called, so maybe you could take advantage of context e.g
    use strict ; use warnings ; sub test { return wantarray ? qw(test) : scalar @{[qw(test)]}; } my $test = test() ; my @test = test() ; print ">>> $test\n" ; print map "[$_]\n",@test ; print $test == scalar(@test)? "Yepa!\n" : "Nope!\n" ; __output__ >>> 1 [test] Yepa!
    If you ever start using modules for this sort of thing I highly recommend any of the Test modules and for context issues the marvellous Want.
    HTH

    _________
    broquaint

(Russ) Re: Did I get what I expected (an array)?
by Russ (Deacon) on Jul 16, 2002 at 16:11 UTC
    Well, in your example, print $test == scalar(@test)?, you are not testing for what you probably think you are. :-)

    When you return @x into scalar context, you get the number of elements in @x. (Aside: to return the "right thing" in whatever context the user called, use wantarray) So, your test will always work, because you are doing the same thing twice.

    If you don't want a reference, test against ref. If you want to prove you only returned one value (or you returned > 1), assign to an array and test with scalar, as you are.

    Perhaps you should ask, "Why do I care if I have a list/array vs a scalar value?" There is no difference between your example (returning one value) and return 'test' (also one value) (except, of course, your array will not return 'test' into scalar context...).

    Does it really matter, as long as you get one or more correct values?

    Russ
    Brainbench 'Most Valuable Professional' for Perl

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2024-03-29 11:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found