I needed a two-dimensional matrix, and what emerged from my sleep-deprived fingertips was:

my $matrix = [([(0) x $cols]) x $rows];

See the problem? I didn't, at first, until my code started behaving in wacky ways.

Data::Dumper revealed:
$VAR1 = [ [ 0, 0 ], $VAR1->[0] ];
The second row is a copy of the first, which is exactly what the x is supposed to do, but isn't what I'd expected. D'oh.

Since my first few test cases used a 1x1 matrix (start small, take small steps), I didn't notice the problem until later, when tests that stored data into a 2x2 matrix started acting wierd.

Having Data::Dumper around when doing test-driven development can be very handy.

Replies are listed 'Best First'.
Re: Using x to build data structures considered harmful
by japhy (Canon) on Jul 17, 2004 at 17:58 UTC
    I've been bitten by that before. And that's not all 'x' does that can be evil:
    my $x = 10; foo(($x) x 2); print "x = $x\n"; sub foo { $_[0]++; $_[1]++; print "@_\n"; }
    You might not expect the results you get.
    _____________________________________________________
    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

      To amplify a bit: The original used x twice. Only one of them was evil. Note that the best WTDI may be:

      my $matrix; { my @row = (0) x $width; $matrix = [map {[@row]} 1..$height]; }

      OTOH, the most perlish way to do it is probably just my $matrix;, and letting perl autovivify the rest when it's created. I assume that dws had a reason to not do that, though.


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      I admit. I don't get it. :-)

        foo(($x) x 2)

      is supposed to be a lazy way to do

        foo($x, $x)

      right? How is that evil? They will both give the same results (i.e. that $x is 12 after the call). What might one expect instead? Is my translation incorrect?

      The thing you get bitten by here is just the aliasing, not the x operator as I understand it.

      Update: Ah, got it. My translation was incorrect (because it actually was a correct translation). Seems like Perl does good dwim in this case, with the i meaning ihb. :-)   ($x) x $n in list context could be believed to be equivalent to

      map $x => 1 .. $n # or sub { ($x) x $n } -> ()
      which both return copies of the values and the output would then be
      11 11 10
      Good thinking, japhy.

      ihb

        It's the fact that it's not making copies, it is making aliases. The subroutine's aliasing is another layer, but the fact that 'x' doesn't make copies is the primary thing here.
        _____________________________________________________
        Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
        How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: Using x to build data structures considered harmful
by BrowserUk (Patriarch) on Jul 17, 2004 at 18:26 UTC

    Similarly:

    my @testData = ( int rand( 1000 ) ) x 1_000_000;

    isn't as useful as it first appears.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon

      It turns out that the best way I can think of to do that (in the general case) is my @testData = map {int rand(1000)} 0..1_000_000;, which isn't as readable (but does have the advantage of working).

      This makes me wonder if I should bring it up on perl6-language, as I know of no better way to do it there.


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

        For p6, they already moved one step ahead with separation of $thing x $count being a scalar-repeat(*) and $thing xx $count being a list-repeat(*).

        * My terms, probably incorrect.

        Looking at the output from -MO=concise for various uses of x, it looks like the optree has enough information in it to allow the repeat operator (or maybe the peep-hole optimiser) to determine whether the list being repeated contains a single 'active' element. Where 'active' is basically anything that isn't a constant or simple scalar: an srefgen; a function call etc. and have a special case for calling that active element iterator wise. The problem is it would the require some way to indicate when you really did want the list-wise replication of a single actively generated list item, and would break all existing uses.

        So, I guess the question is: Is there any syntactically nice way of indicating the difference between my @data = rand xx 1000; meaning I want a thousand different random numbers and my @data = rand xx 1000; I want a thousand copies of the same random number.

        I toyed with my @data = rand xxx 1000; for the former, but that would probably get every site displaying perl code using it blocked under "18 USC 2257", which might not be a good idea :)

        Another possibility is that my @a = { ... } xx 1000; would call the anon. block (or subref) 1000 times, but whether that is sufficiently clearer than my @a = map{ ... } 1 .. 1000; to be worth the cruft is the kind of decision that LW gets right whilst the rest of us are still uming annd ahhing.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon

        I was thinking that "hey, what's wrong with map" but then I reread your code and found an answer: you are producing a list of 1 000 001 elements. Having an operator that just uses a count operand instead of a range with equally many elements can indeed have its use.

        Though, I think introducing another operator or somesuch that would be a "reevaluating x operator" would border to operator bloating (as we have map which isn't unreadable if you know map). That could be said about a lot of the features and operators we love in Perl today (e.g. for (LIST) compared to C-style for (;;)), so I shouldn't say too much. But just because "we" are designing a language almost from scratch doesn't mean we should tuck in every feature we want. I think cleanlyness is good in a language, but so is DWIM and adaption for common tasks. These and other factors should be weighted against each other or we might end up with any of the extremes we dislike today.

        But of course, it's always good to ponder and discuss issues like this if the question isn't "how should we tuck it in" but rather "does this provide real value and how would it fit".

        Just my thought,
        ihb

Re: Using x to build data structures considered harmful
by jryan (Vicar) on Jul 17, 2004 at 20:12 UTC

    Sure, but x can be useful too. I've used this many times before:

    @keys = ... @hash{@keys} = (1) x @keys