Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Fun with multi-dimension arrays

10 views
Skip to first unread message

Dave Whipp

unread,
Apr 26, 2003, 8:49:09 PM4/26/03
to perl6-l...@perl.org
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.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" }

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

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";
}

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

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

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]);

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

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 ] );


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

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?

Having to write

@cell[$point[0]][$point[1]][$point[2]];

could get stale somewhat quick.


So lets assume it works. Hopefully I can now sum my neighborhood by doing:

my $sum = @neighbors
==> map { @cells[$^coordinate }
==> 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);

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

method next_state(int @point) returns bit;
{
my $offsets is const = [ (-1|0|1) x 2 ] & none [0,0];
my $sum = ( $offsets >>+<< @point ).states )
==> map { @.grid[$^coord] }
==> sum;

@.grid[[@point]] ?? $sum == 2|3 :: $sum == 3;
}
}

It would be really cool if all this could really work. But I'm pretty
sure it falls flat on its face somewhere along the road.


Dave.

Luke Palmer

unread,
Apr 26, 2003, 9:31:27 PM4/26/03
to da...@whipp.name, perl6-l...@perl.org
> 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.e. does a loop variable maintain a connection to its iterator.

No. First, that would need to be:

print "$v.key() = $v\n"

And second, that would be calling the key() method on whatever element
$v was referring to. You can do the .kv way above, or:

for zip(@x, 0...) -> $k,$v {...}

> 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" }
>
> Presumably a keyed value could also have .prev and .next methods.
> Perhaps C<keyed> is spelt C<iterator>.

I'd rather not. It's clear enough one of the other two ways.

I don't like delegation on the language level. It can cause some
really counterintuitive results (consider an array of pairs).

> 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";
> }
>
> The issue here is whether the lasily flattened array remembers its
> original index, rather than its flattenned one.

Er, what?

How about:

for @AofA.kv -> $x, @A {
for @A.kv -> $y, $cell {


print "cell value at ($x,$y) is $cell\n";
}
}

> If we've got this far, what about 3-d arrays. Is It possible to iterate

> a 3-d grid without using nested loops?

Don't think so.

Not as if it would be hard to write a multidim() function that could
behave like so:

for multidim(@a1, @a2, @a3) -> $x,$y,$x { ... }

Especially with coroutines.

> 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]);
>
> Will this work. Will the junction reach outward to make a junction of
> array-refs?

Hmmm. I don't know. I think it would, because we don't want
junctions popping up where people don't expect them, because their
logic is so different. So probably, unless it was declared as an
array of Junction.

> 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 ] );

No, but the idea is right.

my $offsets_1d = -1|0|1;

my $offsets = [ ($offsets_1d) xx $N ] & none( [ (0) xx $N ] );

I don't know whether the parens need to be there on the left of xx,
but I know it won't hurt if they are.

> 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

Wow. Well done.

> 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?
>
> Having to write
>
> @cell[$point[0]][$point[1]][$point[2]];
>
> could get stale somewhat quick.

True. I think C<print @cell[$point]> would actually give you the 4th
element of @cell, though.

Maybe we need a Key data type that will let you do that:

my Key $point = [10, 3, 9];

Or some such thing.

> So lets assume it works.

Spoken like a true computer scientist :)

> Hopefully I can now sum my neighborhood by doing:
>
> my $sum = @neighbors
> ==> map { @cells[$^coordinate }
> ==> sum;

Provided C<sum> is a listop synonym for C<reduce { $^a + $^b }>.

> 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);

That C<is default(0)> needn't be there, I presume.

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

Huh? That's a syntax error. Are you trying to copy it, or specify
that the type is the same as @.grid's type? I don't think we have a
way of doing the latter, but I would assume:

my @new_grid is typeof(@.grid);

Or something like it would work.

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

This calls .next_state on the current cell. You need to say

$cell = $self.next_state($cell.key)

It's a shame, too.

> }
> @.grid = @new_grid;
> }

Hmm, something tells me this could be done a little more elegantly
with hypotheticals.

> method next_state(int @point) returns bit;
> {
> my $offsets is const = [ (-1|0|1) x 2 ] & none [0,0];

my $offsets is constant = ...

> my $sum = ( $offsets >>+<< @point ).states )
> ==> map { @.grid[$^coord] }
> ==> sum;
>
> @.grid[[@point]] ?? $sum == 2|3 :: $sum == 3;
> }
> }
>
> It would be really cool if all this could really work. But I'm pretty
> sure it falls flat on its face somewhere along the road.

Likely, but it's pretty close in any case.

Cool!

Luke

Dave Whipp

unread,
Apr 26, 2003, 10:58:45 PM4/26/03
to perl6-l...@perl.org
Luke Palmer relied:

> And second, that would be calling the key() method on whatever element
> $v was referring to. You can do the .kv way above, or:
>
> for zip(@x, 0...) -> $k,$v {...}

I'll admit I hadn't thought of using zip of that: but I don't think it
solves the general case: an N dimensional space with irregular boundary...

>> for @x -> $v is keyed { print "$v.key = $v\n" }
>>
>>Presumably a keyed value could also have .prev and .next methods.
>>Perhaps C<keyed> is spelt C<iterator>.
>
> I'd rather not. It's clear enough one of the other two ways.
>
> I don't like delegation on the language level. It can cause some
> really counterintuitive results (consider an array of pairs).

Yeah, you're right. Even with an explicit trait, its somewhat ungainly.

>>If we've got this far, what about 3-d arrays. Is It possible to iterate
>>a 3-d grid without using nested loops?
>
> Don't think so.
>
> Not as if it would be hard to write a multidim() function that could
> behave like so:
>
> for multidim(@a1, @a2, @a3) -> $x,$y,$x { ... }
>
> Especially with coroutines.

Yes, though perhaps not quite like that. Perhaps

for @array_11d.fully_flat_iterator -> iter
{
my (@coord, $value) = iter.key, iter.value
}

Where @cood would be an array of the 11 ordinates. This would also solve
the irregular boundary case.

>>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
>
> Wow. Well done.

Thank you.

>>Having to write
>>
>> @cell[$point[0]][$point[1]][$point[2]];
>>
>>could get stale somewhat quick.
>
> True. I think C<print @cell[$point]> would actually give you the 4th
> element of @cell, though.
>
> Maybe we need a Key data type that will let you do that:
>
> my Key $point = [10, 3, 9];
>
> Or some such thing.

Thinking about it, a simple @array.value_at(@coord) would solve the
problem: no magic needed.

>>So lets assume it works.
> Spoken like a true computer scientist :)

Damn, my reputation as an engineer is shot ;).

>> my @new_grid is like @.grid;
>
> Huh? That's a syntax error. Are you trying to copy it, or specify
> that the type is the same as @.grid's type? I don't think we have a
> way of doing the latter, but I would assume:
>
> my @new_grid is typeof(@.grid);

What's a few parentheses amonst friends?

>> for *@new_grid -> $cell is rw is keyed
>> {
>> $cell = .next_state($cell.key)
>
> This calls .next_state on the current cell. You need to say
>
> $cell = $self.next_state($cell.key)
>
> It's a shame, too.

I can see that being a really common error. And not just for newbies :(.

>> }
>> @.grid = @new_grid;
> Hmm, something tells me this could be done a little more elegantly
> with hypotheticals.

I'd like to see that. Can I temporise something just within the current
routine, without the methods that I call seeing it? if so, then my GOL
becomes

method update
{
locally_hypothesise @.grid; # what keyword?

for @.grid.fully_flat_iterator -> $iter
{
.value = $self.next_state(.key)
}
}

and next state will use the old values for @.grid:

method next_state(@point)
{
[ -1|0|1 xx 2 ] & ! [0 xx 2]
==> states
==> map { @.grid.at($^coord >>+<< @point) }
==> sum
==> infix:== @.grid.at(@point) ?? 2|3 :: 3;
}

And this code would be almost identical in a N-dimensional game-of-life:
just a few constants would change. Very elegant. The only problem is
that my life world would never grow: its boundary is fixed. I guess I
just need the .sloppy_fully_flat_iterator(boundary_width=>1) operator.
Yes, these continuation things should be very handy.

Dave.

Austin Hastings

unread,
Apr 26, 2003, 11:48:10 PM4/26/03
to Dave Whipp, perl6-l...@perl.org

> -----Original Message-----
> From: Dave Whipp [mailto:da...@whipp.name]
> Sent: Saturday, April 26, 2003 7:49 PM
> To: perl6-l...@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;
}
> }

Austin Hastings

unread,
Apr 26, 2003, 11:48:13 PM4/26/03
to Luke Palmer, perl6-l...@perl.org

> -----Original Message-----
> From: Luke Palmer [mailto:fibo...@babylonia.flatirons.org]
> Sent: Saturday, April 26, 2003 8:31 PM
> To: da...@whipp.name
> Cc: perl6-l...@perl.org
> Subject: Re: 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.e. does a loop variable maintain a connection to its iterator.
>
> No. First, that would need to be:
>
> print "$v.key() = $v\n"
>
> And second, that would be calling the key() method on whatever element
> $v was referring to. You can do the .kv way above, or:
>
> for zip(@x, 0...) -> $k,$v {...}

Backwards, ain'a? $v, $k

(I'm in Wisconsin right now...)

>
> > 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" }
> >
> > Presumably a keyed value could also have .prev and .next methods.
> > Perhaps C<keyed> is spelt C<iterator>.
>
> I'd rather not. It's clear enough one of the other two ways.

Not quite - the .next and .prev thing has some real value, IMO. It's part of
a bundle.

> I don't like delegation on the language level. It can cause some
> really counterintuitive results (consider an array of pairs).

Yeah - that's why it should be C<is keyed> -- to tell the user that you're
overlaying an implicitly typed iteration-variable-class atop the data nodes.

for @AofPairs -> $x { print $x.key }
vs
for @AofPairs -> $x is keyed { print $x.key }

> > 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";
> > }
> >
> > The issue here is whether the lasily flattened array remembers its
> > original index, rather than its flattenned one.
>
> Er, what?
>
> How about:
>
> for @AofA.kv -> $x, @A {
> for @A.kv -> $y, $cell {
> print "cell value at ($x,$y) is $cell\n";
> }
> }
>

Works, but doesn't reduce the workload.

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

> Not as if it would be hard to write a multidim() function that could
> behave like so:
>
> for multidim(@a1, @a2, @a3) -> $x,$y,$x { ... }
>
> Especially with coroutines.

Ehh? You want

sub multidim(*@dims) {
my @iter;
my @ranges;

for @dims {
my $min = 0;
my $max = 0;

when Iter { $min = $_.first; $max = $_.last; }
when Pair { $min = $_.key; $max = $_.value; }
default { $max = $_; }

@iter.unshift $min;
@ranges.unshift $min => $max;
}

loop {
resume *@iter;

my $ci = 0;

while @iter[$ci]++ == @ranges[$ci][1] {
@iter[$ci] = @ranges[$ci][0];
if ++$ci == @ranges {
fail;
}
}
}
}

> > 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]);
> >
> > Will this work. Will the junction reach outward to make a junction of
> > array-refs?
>
> Hmmm. I don't know. I think it would, because we don't want
> junctions popping up where people don't expect them, because their
> logic is so different. So probably, unless it was declared as an
> array of Junction.

> > 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 ] );
>
> No, but the idea is right.
>
> my $offsets_1d = -1|0|1;
> my $offsets = [ ($offsets_1d) xx $N ] & none( [ (0) xx $N ] );
>
> I don't know whether the parens need to be there on the left of xx,
> but I know it won't hurt if they are.

Is this real? I understand the :: -> xx logic, but where did it come from?

> > 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
>
> Wow. Well done.
>
> > 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?
> >
> > Having to write
> >
> > @cell[$point[0]][$point[1]][$point[2]];
> >
> > could get stale somewhat quick.
>
> True. I think C<print @cell[$point]> would actually give you the 4th
> element of @cell, though.

Why? Of all the answers possible, 4 wasn't on my list.

(I.e., 9th or 2nd)

> Maybe we need a Key data type that will let you do that:
>
> my Key $point = [10, 3, 9];
>
> Or some such thing.

Again, why? The only risk is that [] should be () -- create a listref, and
hand it to @array.INDEX() and stand back.

>
> > So lets assume it works.
>
> Spoken like a true computer scientist :)

We imagine a spherical cow of uniform density...

> > Hopefully I can now sum my neighborhood by doing:
> >
> > my $sum = @neighbors
> > ==> map { @cells[$^coordinate }
> > ==> sum;
>
> Provided C<sum> is a listop synonym for C<reduce { $^a + $^b }>.

Agree. It's pretty obvious, but also pretty easy to write..

> > 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);
>
> That C<is default(0)> needn't be there, I presume.

Since C<bit> has no "undef", it's a reasonable thing to say, but since the
closest thing to undef is 0, you're probably right -- not needed.

> > method update
> > {
> > my @new_grid is like @.grid;
>
> Huh? That's a syntax error. Are you trying to copy it, or specify
> that the type is the same as @.grid's type? I don't think we have a
> way of doing the latter, but I would assume:
>
> my @new_grid is typeof(@.grid);
>
> Or something like it would work.

This is another possible use for C<as>, as well.

my @new_grid is as @.grid;

But now we're just giving english lessons. typeof works just as well,
although in this case I think that there's some question about, for example,
the return value of typeof -- should it be "array of array of bit" or
"array(20, 20) of bit". IOW, does the dimensionality get included? If so,
would it BE included if it weren't specified on the vardecl line? Should
there be a way to copy that info, too?

Hence, C<as> could be defined as an operator which MAKES a subtype out of
environmental context.

Beware.

> > for *@new_grid -> $cell is rw is keyed
> > {
> > $cell = .next_state($cell.key)
>
> This calls .next_state on the current cell. You need to say

Calls .next_state on current LIFE, not key -- $self is a game.

> $cell = $self.next_state($cell.key)
>
> It's a shame, too.
>
> > }
> > @.grid = @new_grid;
> > }
>
> Hmm, something tells me this could be done a little more elegantly
> with hypotheticals.

Hypotheticals would be visible in the current namespace - you don't want it
to be visible until you're totally done.

It's a shame, too. I was looking for something like that.

=Austin

Dave Whipp

unread,
Apr 26, 2003, 11:48:22 PM4/26/03
to perl6-l...@perl.org
Following up my own post, I wrote::

> method update
> {
> locally_hypothesise @.grid; # what keyword?
>
> for @.grid.fully_flat_iterator -> $iter
> {
> .value = $self.next_state(.key)
> }
> }
>
> and next state will use the old values for @.grid:

The solution is really quite simple, and needs no extra suff in the
core. All we have to do is redefine the infix:= operator in the local scope:

method update
{
my @assigs is keep { $_[0]=$_[1] for @assigns }
my sub infix:=($lhs is rw, $rhs) { push @assigns, [$lhs, $rhs] };

for @.grid.fully_flat_iterator
{
.value = $self.next_state(.key)
}
}

Wrap that up in a C<delayed_assignments> module, et voila, were're done:
transaction style updates.


Dave.

Austin Hastings

unread,
Apr 26, 2003, 11:59:50 PM4/26/03
to Austin Hastings, Luke Palmer, perl6-l...@perl.org

> -----Original Message-----
> From: Austin Hastings [mailto:Austin_...@Yahoo.com]
> > -----Original Message-----
> > From: Luke Palmer [mailto:fibo...@babylonia.flatirons.org]

> > No, but the idea is right.
> >
> > my $offsets_1d = -1|0|1;
> > my $offsets = [ ($offsets_1d) xx $N ] & none( [ (0) xx $N ] );
> >
> > I don't know whether the parens need to be there on the left of xx,
> > but I know it won't hurt if they are.
>
> Is this real? I understand the :: -> xx logic, but where did it come from?

Google sez it's real, from the horse's mouth itself.

http://groups.google.com/groups?q=g:thl499681241d&dq=&hl=en&lr=&ie=UTF-8&oe=
UTF-8&selm=Pine.LNX.4.44.0210251155240.17544-100000%40london.wall.org

: binary operators:
: + - * / % ** x ~ << >>
: += -= *= /= %= **= x= ~= <<= >>=

We could distinguish an xx operator (along with xx=) that does list
replication, rather than requiring parens around the left argument.

I stand informed.

Austin Hastings

unread,
Apr 26, 2003, 11:59:52 PM4/26/03
to Dave Whipp, perl6-l...@perl.org

> -----Original Message-----
> From: Dave Whipp [mailto:da...@whipp.name]


And somehow you think this is SIMPLER than creating @new_grid?

=Austin

Dave Whipp

unread,
Apr 27, 2003, 4:27:05 PM4/27/03
to perl6-l...@perl.org
Luke Palmer wrote:
>> for *@new_grid -> $cell is rw
>> {
>> $cell = .next_state($cell)
>
> This calls .next_state on the cell. You need to say
>
> $cell = $self.next_state($cell)

>
> It's a shame, too.

OK, I've had a night to sleep on this. It simply doesn't need to be this
way. If we follow A6 on signatures, then we would expect

for %foo.kv -> $k, $v { ... }

to be interpretted as two required-positional arguments. If I wanted $k
to be the invocant, then I'd write

for %foo.kv -> $k: $v {...}

The same would apply to a single arg: if you don't follow it with a
colon, then it's a required-positional, not an invocant/topic.

The only remaining issue (I think) would be the zero-arg case. If we
assume a default signature of "-> $_:", then that would work as expected.

So no magic required. Just obey the rules from A6. In fact A6 explicitly
states, when discussing pointy-blocks, that "you may still put traits or
zone markers on each individual formal parameter".


Dave.

Luke Palmer

unread,
Apr 27, 2003, 5:13:48 PM4/27/03
to da...@whipp.name, perl6-l...@perl.org
> Luke Palmer wrote:
> >> for *@new_grid -> $cell is rw
> >> {
> >> $cell = .next_state($cell)
> >
> > This calls .next_state on the cell. You need to say
> >
> > $cell = $self.next_state($cell)

By the way, I messed up. That should be:

$cell = self.next_state($cell)

No $.

> > It's a shame, too.
>
> OK, I've had a night to sleep on this. It simply doesn't need to be this
> way. If we follow A6 on signatures, then we would expect
>
> for %foo.kv -> $k, $v { ... }
>
> to be interpretted as two required-positional arguments. If I wanted $k
> to be the invocant, then I'd write
>
> for %foo.kv -> $k: $v {...}
>
> The same would apply to a single arg: if you don't follow it with a
> colon, then it's a required-positional, not an invocant/topic.

You're confusing invocant and topic. The invocant doesn't I<just> set
the topic. It's also used for dispatching and is always passed
implicitly by a method call. Anonymous blocks should not have
invocants, because they aren't really part of a class (nor are they a
multimethod).

All routines that accpet arguments topicalize their first argument
(considering the invocant an argument on methods), IIRC. By resorting
to using the invocant here, you're nullifying all the decisions made
about -> in A4, which most of us agree were good decisions. At least
I think they were.

Plus, a little colon shouldn't make the difference between calling one
of your own methods and calling one on the thing being iterated.
That's different things (in similar contexts) looking similar. Not
Good.

If you want to call it on yourself, just put C<self>. It's not a big
deal, really. People will get used to it pretty quickly, I think.

Luke

Dave Whipp

unread,
Apr 27, 2003, 5:50:48 PM4/27/03
to perl6-l...@perl.org
Luke Palmer wrote:
> All routines that accpet arguments topicalize their first argument
> (considering the invocant an argument on methods), IIRC. By resorting
> to using the invocant here, you're nullifying all the decisions made
> about -> in A4, which most of us agree were good decisions. At least
> I think they were.

<quote souce="A4">

A when is the only defaulting construct that pays attention to the
current topicalizer regardless of which variable it is associated
with. All other defaulting constructs pay attention to a fixed
variable, typically $_. So be careful what you're matching against
if the given is aliased to something other than $_:

$_ = "foo";
given "bar" -> $f {
if /foo/ { ... } # true, matches against $_
when /bar/ { ... } # true, matches against $f
}

</quote>

If I read this carefully, then it implies:

method foo
{
$_ := self # redundant, is bound automatically
for @.bar -> $f
{
if .foo { ... } # calls self.foo
when .foo {....} # calls $f.foo
}
}

Right? So my original example would have worked correctly !?


Dave.

0 new messages