我需要将一些代码从外部程序传递到类中。
在通用模块中,我拥有(为简单起见,简化为愚蠢的)

class A {
   has &.hl;
   submethod BUILD( :&!hl ) {}
}
在程序的其他地方,我有
use A;
my &hl = -> $st {
   my $p = shell "hl $st", :in,:out;
   $p.out.slurp
};
my $statement = 'my $raku-variable = "Helloooo";'
my $first = &hl($statement);
my A $a .= new(:&hl);
my $second = $a.hl( $statement );
$first将被处理,并将包含预期的结果。
$second,我会收到一个运行时错误
Too many positionals passed; expected 1 argument but got 2
显然,该类中的例程同时被提供了invocant和参数$s
重写类以提供自定义访问器:
class A {
   has &!hl;
   submethod BUILD( :&!hl ) {}
   method process-it( Str $s --> Str ) { &!hl( $s ) }
}
# elsewhere
my $second = $a.process-it( $statement );
然后,$first$second都将运行且没有错误,并且将包含相同的结果。
当在类内部访问hl时,不添加任何发起人,但是如果未将其声明为&.hl,则在类外部不可见。
因此,我的问题是:是否存在另一种创建公共(public)对象代码变量的方法,该变量不会自动将倡导者作为变量添加到代码中?除了创建单独的访问器方法外。
这是简短的bash脚本hl以供说明
#! /bin/bash
echo '<div class="statement">'$1'</div>'
这是完整的Raku程序
use v6.c;

class A {
    has &!highlighter; # also tried with has &highlighter
    submethod BUILD( :&!highlighter ) {}
    method process-it( Str $s --> Str ) {
       &!highlighter( $s )
    }
}

sub MAIN() {
    my @strings = 'my $v = "Hello World";', 'my $w = $v.raku;';
    my $proc;
    my $proc-supply;
    my &highlighter = -> $s {
        my $p = shell "./hl '$s' ", :in,:out;
        $p.out.slurp
    }

    for @strings {
        say .&highlighter
    }
    my A $a .= new(:&highlighter);
    for @strings { say $a.highlighter($_) }
    # own accessor
    for @strings { say $a.process-it($_) }
}

最佳答案

has $!hl声明一个私有(private)属性。 has $.hl声明一个公共(public)属性。

公开的意思是,它创建了一个同名的方法并将其返回,并将其添加到BUILD/gist/perl/Capture [sub]方法中。

class A {
   has &.hl;
}

这实际上与以下内容相同:

class A {
  has &!hl;

  submethod BUILD ( :&!hl ){}

  method hl (){ &!hl } # return the code object

  method perl (){
    "A.new(hl => $!hl.perl())"
  }
  method gist (){ self.perl }

  method Capture () {
    \( :&!hl )
  }
}

因此,当您调用A.hl时,它将返回存储在&!hl中的代码对象。

您可以通过几种方式来处理。
  • 只需将其称为“两次”即可。

    $a.hl()(42)
    $a.hl().(42)
    $a.hl.(42)
    
  • 有使用它的其他方法。

    method call-it ( |C ){
      &!hl( |C )
    }
    

    $a.call-it( 42 )
    my &hl = $a.hl;
    

    请注意,我使用|C来避免完全处理签名。
    对于您来说,签名并像对待签名一样处理可能是有意义的。
  • 通过自己添加覆盖自动生成的方法。

    method hl ( |C ){
      &!hl( |C )
    }
    

    $a.hl( 42 )
    

    通过覆盖它,使它成为公共(public)属性的所有其他更改仍将为您完成。
    因此,无需创建BUILD子方法。


  • 覆盖它时,这意味着is rw不起作用。这也意味着
    外部代码无法检索代码对象本身。

    如果需要,有一些方法可以解决。
    如果您不需要返回&!hl中的值,则只需像上面一样保留它即可。
  • 如果从未使用零位置参数调用代码对象。

    multi method hl (){ &!hl }
    multi method hl ( |C ){
      &!hl( |C )
    }
    

    $a.hl;        # returns the value in $!hl
    $a.hl();      # returns the value in $!hl
    
    $a.hl( 42 );  # calls &!hl(42)
    

    注意,没有方法可以区分.hl.hl()
  • 您也可以使用命名参数。

    multi method hl ( :code($)! ){ &!hl }
    multi method hl ( |C ){
      &hl( |C )
    }
    

    $a.hl(:code); # returns the value in &!hl
    
    $a.hl;        # calls &!hl()
    $a.hl();      # calls &!hl()
    $a.hl( 42 );  # calls &!hl(42)
    
  • 您无法采取任何措施使获取代码对象变得更加容易,只是让他们使用子签名解析来获取属性。
    (这就是为您创建Capture方法的原因)

    class A {
      has &.hl;
    
      method hl ( |C ){
        &!hl( |C )
      }
    }
    

    sub get-hl ( A $ ( :&hl ) ){ &hl }
    
    my &hl = get-hl($a);
    
    
    my &hl = -> A $ ( :&hl ){ &hl }( $a );
    
    my &hl = $a.Capture{'hl'};
    
  • 10-08 19:51