问题
I'm attempting to implement a class that 'does' Positional
that also allows me to update its values by assigning to the result returned by the AT-POS
method. Eventually, I was able to concoct the following class that works as intended:
class Test does Positional
{
has $.slot_1 is rw = 12;
has $.slot_2 is rw = 24;
method AT-POS(\position)
{
my $t = self;
return-rw Proxy.new:
FETCH => method ()
{
position % 2 ?? $t.slot_1 !! $t.slot_2
},
STORE => method ($v)
{
if position % 2
{
$t.slot_1 = $v
}
else
{
$t.slot_2 = $v
}
}
}
}
my $test = Test.new;
die unless $test[2] == 24;
die unless $test[5] == 12;
$test[7] = 120;
die unless $test[2] == 24;
die unless $test[5] == 120;
$test[10] = 240;
die unless $test[2] == 240;
die unless $test[5] == 120;
Would it be possible to somehow (and: simply) return the container bound to $!slot_1
(or $!slot_2
) inside the Test
class implementation?
Before I discovered the use of Proxy
instances I attempted to return
(and return-rw
) the result of expression position % 2 ?? $!slot_1.VAR !! $!slot_2.VAR
, because I'm under the impression that the VAR
method gives me access to the underlying container, in the hope that I can simply return
it. That didn't really work, and I do not understand why yet: I suspect it somehow gets coerced back to a value somehow?
So in other words: is it possible to simplify my AT-POS
implementation in this particular situation?
Thanks,
Regards,
Raymond.
回答1:
Assuming you do not want accessors for "slot_1" and "slot_2", and if I understand the question correctly, this would be my implementation. I wouldn't call it a Test
class, as that would interfere with the Test
class that is used for testing.
class Foo {
has @elements = 24, 12;
method AT-POS(Int:D $pos) is raw {
@elements[$pos % 2]
}
}
my $f = Foo.new;
say $f[2]; # 24
say $f[5]; # 12
$f[2] = 666;
say $f[4]; # 666
Note that the defaults in the array have changed order, that's to keep the arithmetic in AT-POS
simple.
Also note the is raw in the definition of the AT-POS
method: it will ensure that no de-containerization will take place when returning a value. This allows you to just assign to whatever $f[2]
returns.
Hope this made sense!
Also: the Array::Agnostic module may be of interest for you, to use directly, or to use as a source of inspiration.
回答2:
First off if you aren't going to use an attribute outside of the object, there isn't a reason to declare them as public, and especially not rw
.
class Foo {
has $!odd = 12;
has $!even = 24;
…
}
You can also directly return a Scalar container from a method. You should declare the method as rw
or raw
. (raw
doesn't guarantee that it is writable.)
class Foo {
has $!odd = 12;
has $!even = 24;
method AT-POS(\position) is rw {
position % 2 ?? $!odd !! $!even
}
}
# we actually get the Scalar container given to us
say Foo.new[10].VAR.name; # $!even
Note that even if you declare the attributes as public they still have a private name. The private attribute is always rw
even if it isn't publicly declared as rw
.
class Foo {
has $.odd = 12;
has $.even = 24;
method AT-POS(\position) is rw {
position % 2 ?? $!odd !! $!even
}
}
If you are going to use a Proxy, I would consider moving the common code outside of it.
class Foo {
has $.odd = 12;
has $.even = 24;
method AT-POS(\position) is rw {
# no need to write this twice
my $alias := (position % 2 ?? $!odd !! $!even);
Proxy.new:
FETCH => method () { $alias },
STORE => method ($new-value) { $alias = $new-value }
}
}
Of course the ?? !!
code is a core feature of this module, so it would make sense to put it into a single method so that you don't end up with duplicate code all over your class. In this case I made it a private method.
class Foo {
has $.odd = 12;
has $.even = 24;
# has to be either `raw` or `rw`
# it is debatable of which is better here
method !attr(\position) is raw {
position % 2 ?? $!odd !! $!even
}
method AT-POS(\position) is rw {
my $alias := self!attr(position);
Proxy.new:
FETCH => -> $ { $alias },
STORE => -> $, $new-value { $alias = $new-value }
}
}
Again, not much reason to use a Proxy.
class Foo {
has $.odd = 12;
has $.even = 24;
method !attr(\position) is raw {
position % 2 ?? $!odd !! $!even
}
method AT-POS(\position) is rw {
self!attr(position);
}
}
Instead of ?? !!
you could use an indexing operation.
method !attr(\position) is raw {
($!even,$!odd)[position % 2]
}
Which would allow for a ternary data structure.
method !attr(\position) is raw {
($!mod0,$!mod1,$!mod2)[position % 3]
}
There was no need to write the if
statement that you did as Raku usually passes Scalar containers around instead of the value.
(position % 2 ?? $t.slot_1 !! $t.slot_2) = $v;
来源:https://stackoverflow.com/questions/62968829/returning-a-raw-scalar-container-from-at-pos-method-rather-than-a-proxy-insta