如何在 Raku 中动态访问对象中的属性

Kon*_*ele 7 attributes object raku

我想知道如何在运行时通过名称动态访问对象的属性。代替:StrRaku

#!/usr/bin/rakudo                                                                                                                                                                    
                                                                                                                                                                                    
class c0 {                                                                                                                                                                          
    has $!a0 = 1;                                                                                                                                                                      
    has $!a1 = 2;                                                                                                                                                                      
    method access(Str $m) {
        if ($m eq "a0") { return $!a0; }                                                                                                                                                
        if ($m eq "a1") { return $!a1; }                                                                                                                                                
    }
    method set(Str $m, $v) {
        if ($m eq "a0") { $!a0 = $v; }                                                                                                                                                
        if ($m eq "a1") { $!a1 = $v; }                                                                                                                                                
    }                                                                                                                                                                               
}                                                                                                                                                                                   
                                                                                                                                                                                    
my $c = c0.new();                                                                                                                                                                   
$c.set("a0", 3);
$c.set("a1", 4);
say $c.access("a0");                                                                                                                                                                    
say $c.access("a1");       
Run Code Online (Sandbox Code Playgroud)

我想使用一些看起来像伪代码的东西:

class c0 { 
    ...
    method access(Str $m) {
        return self.$m;   
    }                                                                                                                                                                               
    method set(Str $m, $v) {
        self.$m = $v;   
    }                                                                                                                                                                               
}                                                                                                                                                             
              
Run Code Online (Sandbox Code Playgroud)

这可能Raku吗?我需要使用哪种构造?

作为一名背景人员,我正在考虑如何实现一个向类添加关联功能的角色,以透明地访问成员。属性名称将被参数化:如果我有一个类class ports { has @!ports_; ... }和一个实例,my $p = ports.new() 那么我希望能够使用下标语法通过 $p[...] 访问 @ports_ 。我揣摩天气我可以定义role acc [ Str $member] does Associative[Cool,Str] { ... },然后定义通过端口class ports does acc["ports_"] { ... } ,其中AT-KEYEXISTS-KEYrole acc使用动态属性访问(如果可能的话)来实现。我不想使用“EVAL”。

Tyi*_*yil 5

通过对属性进行一些内省,这是可能的。但是,我想指出私有属性的确切意图是私有。创建一个变通方法来处理它们作为公共属性是一种反模式,并引入了不必要的复杂性。

class c0 {
    has $.a0 = 1;
    has $.a1 = 2;

    method access (Str $m) {
        my $attribute = self.^attributes.first({ ~$_ eq '$!' ~ $m });

        return unless $attribute;

        $attribute.get_value(self); # 1
    }
}

my $c = c0.new;

say $c.access('a0');
Run Code Online (Sandbox Code Playgroud)

要设置值,您可以使用.set_value属性上的方法。

method set (Str $m, $v) {
    ...

    $attribute.set_value(self, $v);
}
Run Code Online (Sandbox Code Playgroud)

旧答案留在这里是出于历史目的。

是的,这样的事情在 Raku 中是可能的。您甚至不需要显式定义该access方法。

class c0 {
    has $.a0 = 1;
    has $a.1 = 2;
}

my $c = $c0.new;

say $c.'a0'(); # 1
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为 Raku 为您的类的公共变量创建了一个访问器方法,当您使用.'a0'(). 该()所必需的使用方法引用名。


Bra*_*ert 4

\n

您更改了帖子以添加有关如何执行以下操作的问题:

\n
role acc [ Str $member] does Associative[Cool,Str] { ... }\n\nclass ports does acc["ports_"] { has @!ports_; ... }\n
Run Code Online (Sandbox Code Playgroud)\n

答案当然是,不要这样做。
\n我的意思是你可以,但你真的不应该。
\n我的意思是你真的不应该\n

\n

此外,您还表明您要用于[]索引。\n问题是,这Positional不是Associative

\n

(我忽略了这样一个事实:没有必要添加_到属性名称的末尾。通常在 Perl 或 Python 中添加 indicates _private但我们不需要在 Raku 中这样做。)

\n
\n

正确的方法是将数组放在角色内部。

\n
role Array::Access [::OF = Cool] does Positional[OF] {\n  has OF @!array-access handles < AT-POS >;\n}\n\nclass Ports does Array::Access {\n  # allows you to access it as self!ports inside of this class\n  method !ports () is raw { @!array-access }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这表明添加一个角色来做到这一点可能有点矫枉过正。

\n
class Ports does Positional[Cool] {\n  has Cool @!ports handles < AT-POS >;\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

如果你真的非常想按照你要求的方式去做,那么下面的方法是有效的。

\n
role Inner::Array::Access [ Str:D \\name, ::OF = Cool ] does Positional[OF] {\n  # a way to quickly access the attribute\n  # (hopefully no-one tries to add an attribute of this name to their class)\n  has $!inner-array handles < AT-POS >;\n\n  # set $!inner-array\n  submethod TWEAK (){\n    $!inner-array := self.^attributes.first(name).get_value(self);\n  }\n}\n\nclass Ports does Inner::Array::Access[\'@!ports\'] {\n  has @!ports;\n\n  # a quick way to add a way to set @!ports for our test\n  submethod BUILD( :@!ports ){}\n}\n\nmy Ports $v = ports => [0,10,20,30];\n\nsay $v[2]; # 20\n
Run Code Online (Sandbox Code Playgroud)\n

可能您正在考虑将其嵌入self.^attributesAT\xe2\x80\x8d-\xe2\x80\x8dPOS.

\n
role Inner::Array::Access [ Str:D \\name, ::OF = Cool ] does Positional[OF] {\n  method AT-POS ( \\index ) is raw {\n    self.^attributes.first(name).get_value(self).AT-POS(index);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这会很慢,因为每次访问单个元素时它都必须执行所有这些查找。

\n