我需要将一些代码从外部程序传递到一个类中.
在我使用的通用模块中(为简单起见,减少了愚蠢)
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)
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是自动添加一个调用者.
除了创建单独的访问器方法.
无法直接访问声明它的类的源代码之外的属性.提供访问权限的唯一方法是通过单独的公共访问器方法.这个答案希望能够澄清对此的困惑.其他答案列出了您的选择.
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
.
您可以通过几种方式处理此问题.
只需称它为"两次".
$a.hl()(42)
$a.hl().(42)
$a.hl.(42)
Run Code Online (Sandbox Code Playgroud)有一个使用它的其他方法.
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
避免完全处理签名.
你有可能签名并像你一样处理它.
通过自己添加来覆盖自动生成的方法.
method hl ( |C ){
&!hl( |C )
}
Run Code Online (Sandbox Code Playgroud)
$a.hl( 42 )
Run Code Online (Sandbox Code Playgroud)
通过覆盖它,所有其他使其成为公共属性的更改仍然为您完成.
所以没有必要创建一个BUILD
子方法.
当你覆盖它时,这意味着is rw
没有效果.这也意味着外部代码无法检索代码对象本身.
如果需要,有办法解决这个问题.
如果你不需要返回值,&!hl
那就把它留在上面.
如果永远不会使用零位置参数调用代码对象.
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()
.
您还可以使用命名参数.
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)您无法轻松获取代码对象,只需让它们使用子签名解析来获取属性即可.
(这就是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)问题是访问者返回的属性恰好是一个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)