在 'does' 位置的 class 中从 AT-POS 方法(而不是 Proxy 实例)返回 'raw' 标量容器?

Returning a 'raw' scalar container from AT-POS method (rather than a Proxy instance) in a class that 'does' Positional?

我正在尝试实现一个 class 'does' Positional 也允许我通过分配给结果 return 来更新它的值 returned AT-POS方法。最终,我能够编造出以下按预期工作的 class:

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;

是否有可能以某种方式(并且:简单地)return 容器 绑定到 $!slot_1(或 $!slot_2Test class 实施?

在我发现 Proxy 实例的使用之前,我尝试 return(和 return-rw)表达式 position % 2 ?? $!slot_1.VAR !! $!slot_2.VAR 的结果,因为我印象中VAR 方法让我可以访问底层容器,希望我可以简单地 return 它。那并没有真正起作用,我还不明白为什么:我怀疑它以某种方式被强制恢复为某个值?

换句话说:在这种特殊情况下是否可以简化我的 AT-POS 实施?

谢谢,

此致,

雷蒙.

假设您想要“slot_1”和“slot_2”的访问器,如果我理解正确的话,这将是我的执行。我不会将其称为 Test class,因为那样会干扰用于测试的 Test class。

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

请注意,数组中的默认值已更改顺序,这是为了使 AT-POS 中的算法保持简单。

还要注意 AT-POS 方法定义中的 is raw :它将确保返回值时不会发生去容器化。这允许您只分配给任何 $f[2] returns.

希望这是有道理的!

此外:您可能会对 Array::Agnostic 模块感兴趣,可以直接使用,也可以用作灵感来源。

首先,如果您不打算在对象外部使用属性,则没有理由将它们声明为 public,尤其是 rw.

class Foo {
    has $!odd = 12;
    has $!even = 24;

    …
}

您也可以直接从方法中 return 标量容器。您应该将方法声明为 rwraw。 (raw 不保证它是可写的。)

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

请注意,即使您将属性声明为 public,它们仍然具有私有名称。私有属性始终是 rw,即使未 public 声明为 rw

class Foo {
    has $.odd = 12;
    has $.even = 24;

    method AT-POS(\position) is rw {
        position % 2 ?? $!odd !! $!even
    }
}

如果您要使用代理,我会考虑将公共代码移到它之外。

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

当然 ?? !! 代码是该模块的核心功能,因此将它放入一个方法中是有意义的,这样您就不会在整个 class。在这种情况下,我将其设为私有方法。

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

同样,没有太多理由使用代理。

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

您可以使用索引操作代替 ?? !!

method !attr(\position) is raw {
    ($!even,$!odd)[position % 2]
}

这将允许三元数据结构。

method !attr(\position) is raw {
    ($!mod0,$!mod1,$!mod2)[position % 3]
}

无需编写您所做的 if 语句,因为 Raku 通常传递标量容器而不是值。

(position % 2 ?? $t.slot_1 !! $t.slot_2) = $v;