perl6正确地将例程传递给对象变量

Ric*_*rth 7 class perl6

我需要将一些代码从外部程序传递到一个类中.

在我使用的通用模块中(为简单起见,减少了愚蠢)

class A {
   has &.hl;
   submethod BUILD( :&!hl ) {}
}
Run Code Online (Sandbox Code Playgroud)

在程序的其他地方,我有

use A;
my &hl = -> $st { 
   my $p = shell "hl $st", :in,:out;
   $p.out.slurp
};
my $statement = 'my $perl6-variable = "Helloooo";'
my $first = &hl($statement);
my A $a .= new(:&hl);
my $second = $a.hl( $statement );
Run Code Online (Sandbox Code Playgroud)

$first 将被处理并将包含预期的结果.

$second,我将得到一个运行时错误

Too many positionals passed; expected 1 argument but got 2
Run Code Online (Sandbox Code Playgroud)

很明显,类中的例程正在提供调用者和参数$s.

重写类以提供自定义访问器:

class A {
   has &!hl;
   submethod BUILD( :&!hl ) {}
   method process-it( Str $s --> Str ) { &!hl( $s ) }
}
# elsewhere
my $second = $a.process-it( $statement );
Run Code Online (Sandbox Code Playgroud)

然后,两个$first$second运行没有错误,将含有相同的结果.

hl类中访问时,不会添加任何调用,但如果未将其声明为,&.hl则在类外部不可见.

因此,我的问题是:是否有另一种方法来创建一个公共对象代码变量,它不会自动将调用者作为变量添加到代码中?除了创建单独的访问器方法.

这是一个简短的bash脚本,hl用于说明

#! /bin/bash
echo '<div class="statement">'$1'</div>'
Run Code Online (Sandbox Code Playgroud)

这是一个完整的Perl 6程序

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.perl;';
    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($_) }
}
Run Code Online (Sandbox Code Playgroud)

rai*_*iph 8

TL; DR无法直接访问声明它的类的源代码之外的属性.提供访问权限的唯一方法是通过单独的公共访问器方法.这个答案希望能够澄清对此的困惑.其他答案列出了您的选择.

为什么会收到Too many positionals passed;错误消息

该代码has &!hl; 声明属性,&!hl.

代码has &.hl;执行相同但生成一个方法,.hl方法是具有相同名称的属性的公共访问器.像所有这样生成的访问器一样,它需要一个参数,即invocant,而不是其他参数.

my $second = $a.hl( $statement )
Run Code Online (Sandbox Code Playgroud)

此代码调用方法 hl.P6将dot($a)左边的值作为第一个参数传递- 调用者.但你也加了一个$statement论点.所以它也通过了.

因此错误消息:

Too many positionals passed; expected 1 argument but got 2
Run Code Online (Sandbox Code Playgroud)

hl类中访问时,不添加任何调用

这不是因为它是在课堂内访问的.这是因为你不把它称为方法:

method process-it( Str $s --> Str ) { &!hl( $s ) }
Run Code Online (Sandbox Code Playgroud)

&!hl( $s )代码是一个在举行例行的风格调用 &!hl属性.它有一个参数,$s.

是否有另一种方法来创建一个公共对象代码变量,该变量不会自动将调用者作为变量添加到代码中?

问题不在于P6是自动添加一个调用者.

除了创建单独的访问器方法.

无法直接访问声明它的类的源代码之外的属性.提供访问权限的唯一方法是通过单独的公共访问器方法.这个答案希望能够澄清对此的困惑.其他答案列出了您的选择.


Bra*_*ert 8

has $!hl声明私有属性.has $.hl声明一个公共属性.

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

class A {
   has &.hl;
}
Run Code Online (Sandbox Code Playgroud)

这实际上与:

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 )
  }
}
Run Code Online (Sandbox Code Playgroud)

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


您可以通过几种方式处理此问题.

  1. 只需称它为"两次".

    $a.hl()(42)
    $a.hl().(42)
    $a.hl.(42)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 有一个使用它的其他方法.

    method call-it ( |C ){
      &!hl( |C )
    }
    
    Run Code Online (Sandbox Code Playgroud)
    $a.call-it( 42 )
    my &hl = $a.hl;
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,我曾经|C避免完全处理签名.
    你有可能签名并像你一样处理它.

  3. 通过自己添加来覆盖自动生成的方法.

    method hl ( |C ){
      &!hl( |C )
    }
    
    Run Code Online (Sandbox Code Playgroud)

    $a.hl( 42 )
    
    Run Code Online (Sandbox Code Playgroud)

    通过覆盖它,所有其他使其成为公共属性的更改仍然为您完成.
    所以没有必要创建一个BUILD子方法.


当你覆盖它时,这意味着is rw没有效果.这也意味着外部代码无法检索代码对象本身.

如果需要,有办法解决这个问题.
如果你不需要返回值,&!hl那就把它留在上面.

  1. 如果永远不会使用零位置参数调用代码对象.

    multi method hl (){ &!hl }
    multi method hl ( |C ){
      &!hl( |C )
    }
    
    Run Code Online (Sandbox Code Playgroud)
    $a.hl;        # returns the value in $!hl
    $a.hl();      # returns the value in $!hl
    
    $a.hl( 42 );  # calls &!hl(42)
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,方法无法区分.hl.hl().

  2. 您还可以使用命名参数.

    multi method hl ( :code($)! ){ &!hl }
    multi method hl ( |C ){
      &hl( |C )
    }
    
    Run Code Online (Sandbox Code Playgroud)
    $a.hl(:code); # returns the value in &!hl
    
    $a.hl;        # calls &!hl()
    $a.hl();      # calls &!hl()
    $a.hl( 42 );  # calls &!hl(42)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 您无法轻松获取代码对象,只需让它们使用子签名解析来获取属性即可.
    (这就是Capture为您创建方法的原因)

    class A {
      has &.hl;
    
      method hl ( |C ){
        &!hl( |C )
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
    sub get-hl ( A $ ( :&hl ) ){ &hl }
    
    my &hl = get-hl($a);
    
    
    my &hl = -> A $ ( :&hl ){ &hl }( $a );
    
    my &hl = $a.Capture{'hl'};
    
    Run Code Online (Sandbox Code Playgroud)


Eli*_*sen 6

问题是访问者返回的属性恰好是一个Callable.只有那么你要调用与参数访问者的返回值.这基本上就是你通过创建自己的访问器而做的事情.

您不必实际创建自己的访问者.只需添加一个额外的括号(表示您在没有任何额外参数的情况下调用访问器),然后添加实际想要传递的值的括号:

class A {
    has &.a = *.say;  # quick way to make a Callable: { .say }
}
A.new.a()(42);        # 42
Run Code Online (Sandbox Code Playgroud)

或者,如果你不喜欢括号,请考虑方法调用语法,如timotimo所指出:

A.new.a.(42);         # 42
Run Code Online (Sandbox Code Playgroud)

  • 您也可以将它写成`A.new.a.(42)`而不是两对括号,它通过语法清楚地表明您使用参数42而不是方法本身调用a的结果 (4认同)