develooper Front page | perl.perl6.language | Postings from April 2003

RE: Fun with multi-dimension arrays

Thread Previous | Thread Next
From:
Austin Hastings
Date:
April 26, 2003 20:45
Subject:
RE: Fun with multi-dimension arrays
Message ID:
ICELKKFHGNOHCNCCCBKFGECFCEAA.Austin_Hastings@Yahoo.com


> -----Original Message-----
> From: Dave Whipp [mailto:dave@whipp.name]
> Sent: Saturday, April 26, 2003 7:49 PM
> To: perl6-language@perl.org
> Subject: Fun with multi-dimension arrays
>
>
> I was having fun with a game of life over on perl monks (node_id=253366)
> -- writing "real" Perl6 code -- and I realised there were a few things
> that I wanted arrays/loops to do, that I'm not entirely sure that
> they can.
>
> Lets start with something simple:
>
>    for @x.kv -> $k,$v { print "$k = $v\n" }
>
> That works fine. But can I do
>
>    for @x -> $v { print "$v.key = $v\n" }

I think I remember seeing something about C<.index> a while back, but I
don't recall the discussion. It's a nice idea, but there are some problems
with it, not least of which the "multiple list" case:

    for @x, @y, @z -> $x { # iterate over all lists }

Should $x.index return the overall index, or the local one? (In this
particular context, the overall inclination is to say that C<.index> would
do the global version while C<.key> did the local one. Should there be two?

> I.e. does a loop variable maintain a connection to its iterator. C<Fo>
> being a builtin, the compiler could easily do that behind the scenes.
> For namespace reasons, we might require an explicit trait:
>
>    for @x -> $v is keyed { print "$v.key = $v\n" }

This does make it more explicit, and it does serve the case where the
objects being iterated might have a C<.key> (or C<.index>) method already.
Good call.

> Presumably a keyed value could also have .prev and .next methods.
> Perhaps C<keyed> is spelt C<iterator>.

Actually, I think that having C<.prev> and (more importantly) C<.next> would
be valuable in their own right. There's that certain class of problems that
are really well solved by "peeking":

    for @list -> $l {
      @unique.push $l unless $l == $l.next;
    }

Unless .next is supposed to advance the iterator, in which case what?

> Thats the 1-d case. Now what about 2-d arrays:
>
>    for *@AofA -> $cell is keyed
>    {
>       my ($x, $y) = $cell.key;
>       print "cell value at ($x,$y) is $cell\n";
>    }

Umm, no. You flattened the list, so all you get is the 1-d key. (This is a
job for DWIMmery, but damn!) Consider what would happen if you said:

   my @AofA = ...;
   my $ref := @AofA[2];

   $ref[3].key == ?;   # "3", or "[2, 3]" ?


> The issue here is whether the lasily flattened array remembers its
> original index, rather than its flattenned one.

From the IBM 30090 assembler manual:

  ROM   -  Read Operator's Mind and branch if zero

> If we've got this far, what about 3-d arrays. Is It possible to iterate
> a 3-d grid without using nested loops?

Sure. Is it possible for any of us to comprehend how it's done?

> The next thing that I wanted to do was to get all the sum of the
> neighboring points around a cell (remember, this was Game Of Life).
>
> Going back to 2-d a moment:
>
> First the offsets:
>
>    my $offsets_2d = [ -1|0|1, -1|0|1 ] & none([0,0]);

This is a "pair of junctions" interacting with a junction of (one) pair.

IANAJG, but should that maybe be:

     my $offsets_2d = any([-1..1, -1..1]) & none([0,0]);

Ask Damian - it's what works for me.

> Will this work. Will the junction reach outward to make a junction of
> array-refs?

Pairs. Or you could do it the other way, which would take more typing.

> For the multi-d (dimension $N) case, would this work?
>
>    my $offsets_1d = -1|0|1;
>
>    my $offsets = [ ($offsets_1d) x $N ] & none( [ (0) x $N ] );

Interesting if it did, but this strikes me as a really low-value feature.
See above about converting 1|0|-1 -> -1..1, though. I think the same
reasoning applies.

> OK, having got this far, lets apply out offsets to get coords:
>
>    my @neighbors = ( $offsets_2d >>+<< [$x,$y] ).states;
>
> I think Ive got this right: both sides of the hyper-plus will
> dereference their arrays, and elements will be summed. Because the lhs
> is a junction, the result of the hyper operator will be a junction of
> corrdinates. I then .states that junction to get the neighborhood: an
> array of points

I think this is wrong.

The lhs is singular (admittedly a junction) and the rhs is singular (a
pair). I think if you want a junction as the result, and if you've managed
to get a junction of PAIRS into the lhs, all you need is lhs + rhs --
there's no vectorization required.

I think that if you vectorize a Scalar (junction) and a Pair, you run the
risk of getting the Scalar added to each element of the pair -- not what you
want.

    # or should the [$x, $y] actually be ($x => $y) ?
    my @neighbors = $offsets_2d.states >>+<< [$x, $y];
    my @neighbors = ($offsets_2d + [$x, $y]).states;

> OK, so thats the junctions. What about access? Given @cell[$x][$y][$z],
> how do I pass around an index to a cell?
>
>    $point = [10, 3, 9];
>    print @cell[$point]; # is this an array slice, or a single point?

I think the "indexed-access" method (.INDEX?) probably expects a list
context, so that should work.

> Having to write
>
>    @cell[$point[0]][$point[1]][$point[2]];
>
> could get stale somewhat quick.

Yes. I would also suggest an alternate syntax for the .INDEX method: accept
a hash of Nums or Ints, using the "alphabetically increasing" rule we use
for currying:

    my $point = {x => 1, y => 2, z => 3};
    print @cell[ $point ]

> So lets assume it works. Hopefully I can now sum my neighborhood by doing:
>
>    my $sum = @neighbors
>                 ==> map { @cells[$^coordinate }

Missing ']'

>                 ==> sum;

What's sum?

Maybe

   my $sum = (@cells[ *@neighbors ]).reduce { $^a + $^b; }

I don't recall the syntax for reduce. But you should be able to (1) flatten
@cells/@neighbors into a list; and (2) reduce the list to $sum.

> Putting this all together, my code the the game-of-life update loop
> could be:
>
> class Life
> {
>    has @.grid is array dim(20) of array dim(20) of bit is default(0);

dim(20) ? Should there be an C<is> in there? (This would be nesting
C<is>es...)

>    method update
>    {
>       my @new_grid is like @.grid;

I like this C<like>, but we've got variants laying all over the place. Maybe
C<typeof>, or maybe just C<is @.grid> -- in much the same way that we could
use an object or a class for C<new>. OTOH, what to do when $class contains a
class? Value or reference? Argh.

>       for *@new_grid -> $cell is rw is keyed
>       {
>         $cell = .next_state($cell.key)
>       }
>       @.grid = @new_grid;
>    }

Let Sugalski do it for you:

    class Life {
      has $.cols;
      has $.rows;
      has @.grid is Array of Array of Int;  # Favor speed over size

      state Pair $.coords;

      method INIT {
        $.coords = [ any( 0 .. $rows ) => any( 0 .. $cols ) ];
        @.grid[$.coords] = 0;
      }

      method update {
        my @new_grid is typeof @.grid;

        map { @new_grid[ $_ ] = .next_state( $_ ) } <== $.coords.states;
      }

      method next_state(Pair $cell) {
        # No, THIS won't show up in Perl Poetry... state Pair indeed.
        state Pair $surround is const = any( [ -1 .. 1, -1 .. 1 ] ) &
none( [0, 0] );

        my $sum = (@.grid[ *(($cell + $surround).states) ]).reduce { $^a +
$^b; };
        @.grid[ $cell ] ?? $sum == 2|3 :: $sum == 3;
      }
> }


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About