views:

50

answers:

1

So, I believe this has something to do with the difference between arrays and lists, but I don't understand what's going on here. Can anyone explain how and why Perl treats an expression like (1..4) differently than (1, 2, 3, 4) and @{[1..4]}?

$ perl -de1

Loading DB routines from perl5db.pl version 1.31
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1): 1
  DB<1> x scalar (1,2,3,4)
0  4
  DB<2> x scalar (1..2,3,4)
0  4
  DB<3> x scalar (1,2..3,4)
0  4
  DB<4> x scalar (1,2,3..4)
0  ''
  DB<5> sub foo { (1..4) } # (the actual problem case, except 4 would be a variable)
  DB<6> x scalar foo()
0  ''
  DB<7> sub bar { @{[1..4]} } # (the workaround)
  DB<8> x scalar bar()
0  4
+11  A: 

The "range operator" that produces a list just doesn't exist in scalar context -- the .. operator when used in scalar context is the "flip-flop operator" instead. The flip-flop operator starts off returning false, continues returning false until its left argument becomes true, then continues returning true until its right argument becomes true, after which it returns false and returns to its initial state. If all of that sounds pretty useless, consider

while (<>) {
  print if /BEGIN/ .. /END/;
}

The .. operator starts out false, so lines from the file aren't printed. But once the regex /BEGIN/ matches, the flip-flop becomes true, and all of the following lines will be printed, until the regex /END/ matches. From the line following "END", the flip-flop will be false again. So the effect is to only print lines between (and including, but you can control that) "BEGIN" and "END".

There's a little more magic with $. if either side is constant, but that's not really important. Read more on the flip-flop operator in perldoc perlop.

hobbs
That operator sure is popular today!
Ether
Whee! I suppose I was expecting the expression to be evaluated in list context and then scalar'd.`perldoc -f scalar` hints at it, I suppose, but is ambiguous at best: *There is no equivalent operator to force an expression to be interpolated in list context because in practice, this is never needed. If you really wanted to do so, however, you could use the construction `@{[ (some expression) ]}`, but usually a simple `(some expression)` suffices.*
fennec
@fennec yeah, what you initially expected simply doesn't happen. List and scalar context are distinct, and nothing is "evaluated in list context and then scalar'd" unless you specifically force that to happen. Many operators will behave *as if* that was the case (having equivalent side-effects in either context, and either returning a list in list context and a count in scalar context, or returning a single value in either context) but some will surprise you by doing something significantly different in one context than another. `m//g` and `split` are other ones to look out for.
hobbs
Yeah, I'm theoretically aware of that on some level, but the original code I was using was actually counting thingies with `scalar $x->thingy_indices` where `sub thingy_indices { +(1, 2) }` or `sub thingy_indices { +(1, 2, 3) }`. and it was getting back values `2` and `3` as expected. This effectively poisoned my mind for the day. :b
fennec
fennec: scalar's doc is not the place to have looked, since each operator has its own idea of what to do in scalar vs list context.
ysth