Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re^4: If you believe in Lists in Scalar Context, Clap your Hands

by gone2015 (Deacon)
on Oct 24, 2008 at 11:47 UTC ( [id://719326]=note: print w/replies, xml ) Need Help??


in reply to Re^3: If you believe in Lists in Scalar Context, Clap your Hands
in thread If you believe in Lists in Scalar Context, Clap your Hands

But you had discussed arrays and hashes. Are you implying that array slice make lists and arrays don't?

Yes. I'd say that an array is a variable whose value is a list. Using the name or some other reference to an array yields that value or its length, depending on Context. In that sense, yes, I was implying that arrays do not make lists. A slice operation, on the other hand, takes a list value and makes a list which is a subset. (I'm open minded, though: if there is, in fact and invisible operator that makes lists out of arrays I would not be surprised.)

The omission seems rather "convenient" because it allowed you to cut two exceptions from the "in Scalar Context" list.

I'm sorry you've drawn that conclusion.

The last item in the list you refer to covers the case of arrays, pointing out that lists and arrays are not identical, and notes the effect of Context. For completeness I could have mentioned hashes, but their relationship to lists is, in my opinion, one step removed, and I did try to keep to the main point, namely Lists in a Scalar Context.

Exactly. A list can't be produced in scalar context. So how can a list be put into scalar context?

Just for a moment, let's consider integers and floats as two different sorts of value. Suppose we have the expression:  27 % 4.79. Now, '%' is an integer operation -- one could say it has "Integer Context" -- and the floating point argument is implicitly coerced to an integer, suitable for the '%' operation. At least that is a common understanding of what happens.

You may argue that the thing which I referred to as a floating point value never was a such, because you cannot have a "Float in Integer Context". That would be consistent with the view that, in Scalar Context:  100 + @r[@p] the thing which looks a lot like a list isn't a list because you cannot have a "List in Scalar Context" (QED).

"List in scalar context" is deceptive, misleading and/or confusing.

It is definitely dangerous to assume that all things that return lists in list context will have "List in Scalar Context" semantics. Just as it is would be dangerous to assume that all numeric functions return the same sort of number. So one has to be careful.

However, where those semantics apply, which is not uncommon, I find it a less confusing notion than "it was never a list, no matter what it looks like". Cooercion is a well understood and common notion. I really don't see why it should not apply to lists. (It does to arrays.)

Assignment operators are right-associative. That means that in chain of assignments, the right-most has highest precedence. ** is also right-associative.
>perl -le"print( 2**3**4 )"
2.41785163922926e+024

Thanks for the clarification. The right-hand '**' happens first, giving 81, then the left-hand '**' takes the 2 and the 81, giving a modestly big result. OK.

Following that model, in:  $r = ($a, $b) = 0..11 ; the right-hand assignment:  ($a, $b) = 0..11 happens first, so we get ($a=0, $b=1), then that is assigned to $r.... leaving to one side whether ($a, $b) is or isn't a list (given that for the right-hand assignment there's List Context, and in the left-hand one Scalar Context).... since the result is $r=12, right-associativity doesn't appear to be the whole story.

With thanks to all who pointed me in the right direction here, this is a piece of deeper magic. The right-associativity causes the 0..11 to be evaluated in List Context, and a list assignment is made to ($a, $b). So far, so straightforward. The interesting bit is that the next (left hand), scalar assignment, effectively reaches round the first (right hand) assignment and picks up the length of the list. This is well documented, but the significance of it didn't sink in when I read it however many years ago.

It's obvious to me now, of course. We're all used to using this magic, for example in: while (my($k, $v) = each(%h)). I admit that I had assumed that this was either because the assignment would leave the lvalue with no more entries in it than were available in the rvalue, or because the $v was undefined -- though I never tested either. I live and learn. (Though, sadly, wisdom eludes me.)

Ah well. It has been said that "There is no spoon". But some say it's worse than that (Jim), by extension there's no cutlery, no kitchen utensils, no stove, no kitchen and pretty soon I can expect to starve to death. I think I'll go and do something really dangerous now, I'll take away some brackets and to blazes with the sign.

Replies are listed 'Best First'.
Re^5: If you believe in Lists in Scalar Context, Clap your Hands
by ikegami (Patriarch) on Oct 24, 2008 at 18:54 UTC

    Are you implying that array slice make lists and arrays don't?

    Yes.

    How do you reconcile that with the fact that both the array and the array slice perform exactly the same action in list context: they put their members on the stack. Either they both make a list or neither does.

    Following that model, in: $r = ($a, $b) = 0..11 ; the right-hand assignment: ($a, $b) = 0..11 happens first, so we get ($a=0, $b=1), then that is assigned to $r.... leaving to one side whether ($a, $b) is or isn't a list (given that for the right-hand assignment there's List Context, and in the left-hand one Scalar Context).... since the result is $r=12, right-associativity doesn't appear to be the whole story.

    I bolded the mistake. Just like the right-hand ** doesn't return 3 in 2**3**4, The right-hand assignment doesn't return ($a, $b) in $r = ($a, $b) = 0..11. A list assignment in scalar context returns the number of items it was given to assign. 12, in this case.

    >perl -le"print( scalar( ($a, $b) = 0..11 ) );" 12

    Just for a moment, let's consider integers and floats as two different sorts of value. Now, '%' is an integer operation -- one could say it has "Integer Context" -- and the floating point argument is implicitly coerced to an integer, suitable for the '%' operation. At least that is a common understanding of what happens.

    There are two different sort of value. Three even.
    NVs hold floats (which could technically be integers, but not relevant).
    IVs hold machine signed integers.
    UVs hold machine unsigned integers.

    I wouldn't say it has integer context, because that would imply a parallel with scalar vs list context. Scalar and list context force operands to returns a scalar and a list respectively. Integer coercion doesn't force operands to returns an integer.

    Let's look at an example. Given f() % 3, f() is executed in scalar context and in "integer context".

    Scalar ContextInteger Coercion
    The scalar context will force f() to return a scalar. The integer conversion doesn't force f() to return an integer.
    f() is unable to return a list. f() is free to return undef, a string, a reference, a float, etc.
    % never converts a list to a scalar. The conversion to integer is performed by %.
    The scalar context is forced onto the operands before they are evaluated. The integer coercion is performed on the operands after they are evaluated.

    Your argument relies on the premise of a parallel between scalar context and integer coercion, but it's very clear there isn't one. The premise is false, so the argument is moot.

    This isn't relevant, but I thought you'd be interested in an additional difference: some operators (such |, & and ^) behave differently based on whether they're given a string or an integer. Some operators behave differently based on the length of the list they are given, but you won't find an operator that can accept both a scalar and a list.

    Update: Formatting changes. Minor typo correction.

Re^5: If you believe in Lists in Scalar Context, Clap your Hands
by JavaFan (Canon) on Oct 24, 2008 at 12:26 UTC
    Yes. I'd say that an array is a variable whose value is a list. Using the name or some other reference to an array yields that value or its length, depending on Context. In that sense, yes, I was implying that arrays do not make lists. A slice operation, on the other hand, takes a list value and makes a list which is a subset. (I'm open minded, though: if there is, in fact and invisible operator that makes lists out of arrays I would not be surprised.)
    No.

    An array returns a list in list context, but it isn't a list. Just as localtime() isn't a list, although it will return a list in list context. In fact, every expression will return a list in list context, although that list may be empty or just contain a single value.

    As for the slice operation, yes, it will take a subset of a list. On arrays though, it can return more.

    @a = ()[3, 5, 6]; @b = @c[3, 5, 6]; say 0 + @a; say 0 + @b; __END__ 0 3
    Following that model, in: $r = ($a, $b) = 0..11 ; the right-hand assignment: ($a, $b) = 0..11 happens first, so we get ($a=0, $b=1), then that is assigned to $r.... leaving to one side whether ($a, $b) is or isn't a list (given that for the right-hand assignment there's List Context, and in the left-hand one Scalar Context).... since the result is $r=12, right-associativity doesn't appear to be the whole story.

    With thanks to all who pointed me in the right direction here, this is a piece of deeper magic. The right-associativity causes the 0..11 to be evaluated in List Context, and a list assignment is made to ($a, $b). So far, so straightforward. The interesting bit is that the next (left hand), scalar assignment, effectively reaches round the first (right hand) assignment and picks up the length of the list. This is well documented, but the significance of it didn't sink in when I read it however many years ago.

    You are quite wrong here. The list assignment operator in scalar context DOES NOT RETURN A LIST. It returns the number of elements on its right hand side. It sets $a and $b as side-effects - it doesn't return its left hand side. If you think of operators being shorthands for function calls, you may read
    $r = ($a, $b) = 0 .. 11;
    more or less as
    sassign(\$r, scalar lassign([\$a, \$b], range (0, 11))) sub lassign { my ($rhs, $lhs) = @_; for (my $i = 0; $i < @$rhs; $i ++) { $$rhs[$i] = $$lhs[$i]; } wantarray ? map {$$_} @$lhs : scalar @$rhs; } sub sassign: lvalue { my ($rhs, $lhs) = @_; $$rhs = $lhs; $$rhs; }
    Now the above isn't exactly the same in the your line in detail, because I don't think you can mimic the assignment operator exactly in Perl code, but you get the idea.

    The point is, the assignment is a side-effect. The results are not returned. And also important, there's no magical reaching around by the first (leftmost) assignment. The first assignment operator doesn't look on its right hand side and say "my, my, lets see, I've a list here, I wonder how that list came to be. Oh, I see, in the past this list was created by an assignment. Let me reach even further back in the past, and see how many elements there were on its right hand side". No, by the time the leftmost assignment gets in the driver seat, it'll see two operands, a scalar lvalue, and a scalar value. How that scalar value got there doesn't interest the assignment operator. It could have been the return value of the add operation, or the return value of the lassign operator.

      Yes. I'd say that an array is a variable whose value is a list. Using the name or some other reference to an array yields that value or its length, depending on Context. In that sense, yes, I was implying that arrays do not make lists. A slice operation, on the other hand, takes a list value and makes a list which is a subset. (I'm open minded, though: if there is, in fact and invisible operator that makes lists out of arrays I would not be surprised.)
      No.
      An array returns a list in list context, but it isn't a list. Just as localtime() isn't a list, although it will return a list in list context. In fact, every expression will return a list in list context, although that list may be empty or just contain a single value.

      A flat "No". Goodness.

      I'm puzzled by what you are contradicting here. Is it controversial to say that an array is a variable ? Is it then controversial to say the the value of that variable is a list ? Or to note that in an expression, the part of the value used (the list itself or its length) depends on the Context, is that controversial ?

      As for the slice operation, yes, it will take a subset of a list. On arrays though, it can return more.
      @a = ()[3, 5, 6]; @b = @c[3, 5, 6]; say 0 + @a; say 0 + @b; __END__ 0 3

      Well, that's amusing. I note that it doesn't matter whether @c is undefined or defined but empty. So, slices of lists are different from slices of arrays. Splendid. Thank you.

      (I realise also that subset might be the wrong word, since a slice can also include repeated values, which might not pass as a subset. Also, as you point out, some items in the slice may be undef, which may or may not be considered as part of the original.)

      You are quite wrong here. The list assignment operator in scalar context DOES NOT RETURN A LIST. It returns the number of elements on its right hand side. It sets $a and $b as side-effects - it doesn't return its left hand side.

      SO IT WOULD APPEAR.

      I was trying to say: to fully understand $r = ($a, $b) = 0..11; it is necessary to know what a many-splendored thing '=' is. Understanding the more common-place notion of right-associativity is not enough. I'm sorry if that wasn't as clear as I had hoped.

      I do not know how this is actually implemented. I can see how the documentation describes it. The effect remains as if the scalar assignment reaches round the list assignment, but I am more than happy to take your word for it that it doesn't actually work like that.

      Further, I withdraw unreservedly any implication, imputation or suggestion that this (or any other) mechanism in Perl might, through a glass darkly, or otherwise, appear to be magical to even the slightest degree. I throw myself on the mercy of the court, and hope, as only a true penitent can hope, for a light sentence involving the minimum of pain, and if at all possible, no permanent disfigurement.

        Understanding the more common-place notion of right-associativity is not enough.
        Indeed. You must also know the return value of the operator (= in this case). But that's the same as trying to understand the result of
        2 ** 3 ** 4
        you need to understand the return value of ** beside understanding right-associativity.
        I do not know how this is actually implemented.
        Associativity is a parser/compiler issue. By the time the code is running, associativity no longer plays a role. As for the implementation of assignment, they are actually two different operators, scalar assignment (pp_sassign in pp_hot.c) and list assignment (pp_aassign in pp_hot.c).

        And if you look in pp_hot.c, you see there's check on context to determine what is returned:

        if (gimme == G_VOID) SP = firstrelem - 1; else if (gimme == G_SCALAR) { dTARGET; SP = firstrelem; SETi(lastrelem - firstrelem + 1 - duplicates); } else { if (ary) SP = lastrelem; else if (hash) { if (duplicates) { /* Removes from the stack the entries which ended up a +s * duplicated keys in the hash (fix for [perl #24380]) + */ Move(firsthashrelem + duplicates, firsthashrelem, duplicates, SV**); lastrelem -= duplicates; } SP = lastrelem; } else SP = firstrelem + (lastlelem - firstlelem); lelem = firstlelem + (relem - firstrelem); while (relem <= SP) *relem++ = (lelem <= lastlelem) ? *lelem++ : &PL_sv_undef; } RETURN;
        Now, I don't expect you to understand everything (neither do I), but I do hope you see that in VOID context, nothing is returned, in SCALAR context, a single value is returned, and in LIST context, more things are returned.

        One more piece of evidence that it isn't the scalar assignment that is reaching all the way behind, here we have a list assignment in scalar context where the scalar context isn't given by another assignment:

        $ perl -E 'say scalar(() = ($a, $b, $c))' 3 $
        Further, I withdraw unreservedly any implication, imputation or suggestion that this (or any other) mechanism in Perl might, through a glass darkly, or otherwise, appear to be magical to even the slightest degree.
        Now that's worth an upvote :)

        FWIW, I took the "flat No" as applying to "arrays do not make lists".

      A slight correction. The difference you note between list slices and array slices is in fact only there in some cases:
      @a = ()[0,0,0]; @b = @c[0,0,0]; @c = (1)[0,0,0]; say 0 + @a; say 0 + @b; say 0 + @c; __END__ 0 3 3
      The rule is that a list slice (in list context) produces an empty list if all the indices are out of range. This permits code like this:
      if ( my ($dev,$ino) = (stat(FH))[0,1] ) {
      where the error condition is "passed through" the slice and assignment to affect the if.

      In all other cases, a list slice behaves just like an array slice.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-04-20 03:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found