Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl 6: writable multidimensional subscript access with AT-POS

Tags:

raku

You can easily allow subscript access to your own classes using AT-POS:

class Foo
{
    has @.grid;

    method AT-POS($x) is rw { return-rw @!grid[$x] }
    method Str { '<' ~ @!grid.join(' ') ~ '>' }
    method gist { self.Str }
}

my $foo = Foo.new(:grid(<a b c d e>));
say $foo;
say $foo[2];
$foo[3] = 'z';
say $foo;

output:

<a b c d e>
c
<a b c z e>

But I need two-dimensional subscript access. I've figured out how to make this work for reading, but it dies when writing:

class Bar
{
    has @.grid;

    method AT-POS($y, $x) is rw { return-rw @!grid[$y;$x] }
    method Str { '<' ~ @!grid».join(' ').join("\n ") ~ '>' }
    method gist { self.Str }
}

my $bar = Bar.new(:grid(<a b c d e>, <f g h i j>, <k l m n o>));
say $bar;
say $bar[1;2];
$bar[2;3] = 'z';
say $bar;

output:

<a b c d e
 f g h i j
 k l m n o>
h
Too few positionals passed; expected 3 arguments but got 2
  in method AT-POS at ./p6subscript line 25
  in block <unit> at ./p6subscript line 33

Is there any way to make this work?

like image 270
mscha Avatar asked Dec 16 '18 13:12

mscha


2 Answers

Somehow, the AT-POS method is not being called. The documentation mentions the use of ASSIGN-POS instead, so here we go:

class Bar
{
    has @.grid is rw;

    method AT-POS($y, $x) is rw { say "AT-POS $y, $x"; return-rw @!grid[$y;$x] }
    method ASSIGN-POS($y, $x, $new) { say "ASSIGN-POS $y, $x"; @!grid[$y;$x] = $new }
    method Str { '<' ~ @!grid».join(' ').join("\n ") ~ '>' }
    method gist { self.Str }
}

my $bar = Bar.new(:grid(<a b c d e>, <f g h i j>, <k l m n o>));
say $bar;
say $bar[1;2];
$bar[2;3] = 'z';
say $bar;

Which, interestingly, yields another error:

Cannot modify an immutable List ((k l m n o))
  in method ASSIGN-POS at semilist-so.p6 line 8
  in block <unit> at semilist-so.p6 line 16

So the problem is not really the syntax, but the fact that you are working with immutable lists. You should use Arrays, which are mutable, and you'll be able to do that.

class Bar
{
    has @.grid is rw;

    method AT-POS($y, $x) is rw { return-rw @!grid[$y;$x] }
    method ASSIGN-POS($y, $x, $new) { @!grid[$y;$x] = $new }
    method Str { '<' ~ @!grid».join(' ').join("\n ") ~ '>' }
    method gist { self.Str }
}

my $bar = Bar.new(:grid([<a b c d e>], [<f g h i j>], [<k l m n o>]));
say $bar;
say $bar[1;2];
$bar[2;3] = 'z';
say $bar;
like image 67
jjmerelo Avatar answered Oct 20 '22 19:10

jjmerelo


My solution would be (provided we only have 2 dimensions):

class Bar {
    has @.grid;

    method TWEAK() { $_ .= Array for @!grid }
    method AT-POS(|c) is raw { @!grid.AT-POS(|c) }
    method Str { '<' ~ @!grid».join(' ').join("\n ") ~ '>' }
    method gist { self.Str }
}

The TWEAK will convert any lists that were given to arrays if they're not already. The is raw on AT-POS is all that is needed: return-rw is a very roundabout way of doing that.

like image 20
Elizabeth Mattijsen Avatar answered Oct 20 '22 18:10

Elizabeth Mattijsen