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