如何将 class 方法作为参数传递给 perl 6 中 class 的另一个方法
how to pass a class method as argument to another method of the class in perl 6
我有一个如下所示的脚本。目的是使用不同的过滤方法来过滤列表。
这是代码。
2
3 class list_filter {
4 has @.my_list = (1..20);
5
6 method filter($l) { return True; }
7
8 # filter method
9 method filter_lt_10($l) {
10 if ($l > 10) { return False; }
11 return True;
12 }
13
14 # filter method
15 method filter_gt_10($l) {
16 if ($l < 10) { return False; }
17 return True;
18 }
19
20 # expecting a list of (1..10) to be the output here
21 method get_filtered_list_lt_10() {
22 return self.get_filtered_list(&{self.filter_lt_10});
23 }
24
25 # private
26 method get_filtered_list(&filter_method) {
27 my @newlist = ();
28 for @.my_list -> $l {
29 if (&filter_method($l)) { push(@newlist, $l); }
30 }
31 return @newlist;
32 }
33 }
34
35 my $listobj = list_filter.new();
36
37 my @outlist = $listobj.get_filtered_list_lt_10();
38 say @outlist;
期望 [1..10] 是这里的输出。但是出现以下错误。
Too few positionals passed; expected 2 arguments but got 1
in method filter_lt_10 at ./b.pl6 line 9
in method get_filtered_list_lt_10 at ./b.pl6 line 22
in block <unit> at ./b.pl6 line 37
我做错了什么?
在 Perl 6 中将方法作为参数传递需要您使用 MOP(元对象协议)方法,或者按名称传递方法(然后在运行时为您进行查找)。
但是,如果您实际上并没有在这些方法中对对象执行某些操作,为什么还要使用 method
s 呢?它们也可能是 sub
,您 可以 作为参数传递。
也许这是最好的例子:
class list_filter {
has @.my_list = 1..20; # don't need parentheses
sub filter($ --> True) { } # don't need code, signature is enough
# filter sub
sub filter_lt_10($l) { not $l > 10 }
# filter sub
sub filter_gt_10($l) { not $l < 10 }
# private
method !get_filtered_list(&filter_sub) {
@.my_list.grep(&filter_sub);
}
# expecting a list of (1..10) to be the output here
method get_filtered_list_lt_10() {
self!get_filtered_list(&filter_lt_10);
}
}
my $listobj = list_filter.new();
my @outlist = $listobj.get_filtered_list_lt_10();
say @outlist; # [1 2 3 4 5 6 7 8 9 10]
第一个 sub filter
,只有 return 是一个常量值(在本例中 True
),可以更容易地在带有空体的签名中表示。
filter_lt_10
和 filter_gt_10
subs 只需要否定条件,因此使用 not
.
get_filtered_list
方法应该是私有的,因此通过前缀 !
使其成为私有方法。
在 get_filtered_list_lt_10
中,您现在需要使用 !
而不是 .
来调用 get_filtered_list
。并且您通过在 &
前加上前缀 filter_lt_10
sub 作为参数传递(否则它将被视为对没有任何参数的 sub 的调用,这将失败)。
更改 get_filtered_list
以使用内置 grep
方法:这需要一个 Callable
块,该块采用单个参数并且应该 return 一些东西 True
以包括它所处理的列表的值。由于 sub
接受单个参数 是 一个 Callable
,我们可以直接在那里指定 sub。
希望这是有道理的。我试图尽可能接近预期的语义。
一些通用的编程评论:我觉得 sub 的命名很混乱:我觉得它们应该被称为 filter_le_10
和 filter_ge_10
,因为它们确实是这样做的在我看来。另外,如果你真的不想要任何临时过滤,而只是从一组特定的预定义过滤器中过滤,你可能会更好地使用常量或 enum
s 创建调度 table , 并使用它来指示您想要的过滤器,而不是将此信息编码为另一种制作和维护方法的名称。
希望对您有所帮助。
&{self.method}
语法对我来说是新的,非常感谢。不幸的是,如果需要参数,它就不起作用。你可以像其他poster提到的那样使用sub
,但是如果你需要使用方法,你可以通过调用self.^lookup
来获取方法,这就是Elizabeth提到的元对象协议的使用。 ('^' 表示您调用的方法不是 class 的一部分,而是 "shadow" class 的一部分,其中包含 class 的主要内容 /实施细节。)
要获取一个方法,使用运行 obj.^lookup(method name)
,并通过传入对象本身(通常是"self")作为第一个参数来调用它,然后是其他参数。要将对象绑定到函数以便不需要每次都显式添加它,请使用 assuming 函数。
class MyClass {
method log(Str $message) { say now ~ " $message"; }
method get-logger() { return self.^lookup('log').assuming(self); }
}
my &log = MyClass.get-logger();
log('hello'); # output: Instant:1515047449.201730 hello
找到了。这对我有用。
3 class list_filter {
4 has @.my_list = (1..20);
5
6 # will be overriding this in derived classes
7 method filter1($l) { return True; }
8 method filter2($l) { return True; }
9
10 # same print method I will be calling from all derived class objects
11 method print_filtered_list($type) {
12 my @outlist = self.get_filtered_list($type);
13 say @outlist;
14 }
15
16 # private
17 method get_filtered_list($type) {
18 my @newlist = ();
19 for @.my_list -> $l {
20 my $f = "filter$type";
21 if (self."$f"($l)) { push(@newlist, $l); }
22 }
23 return @newlist;
24 }
25 }
26
27 class list_filter_lt_10 is list_filter {
28 method filter1($l) {
29 if ($l > 10) { return False; }
30 return True;
31 }
32 method filter2($l) {
33 if ($l > 10) { return False; }
34 if ($l < 5) { return False; }
35 return True;
36 }
37 }
38
39 class list_filter_gt_10 is list_filter {
40 method filter1($l) {
41 if ($l < 10) { return False; }
42 return True;
43 }
44 method filter2($l) {
45 if ($l < 10) { return False; }
46 if ($l > 15) { return False; }
47 return True;
48 }
49 }
50
51 my $listobj1 = list_filter_lt_10.new();
52 $listobj1.print_filtered_list(1);
53 $listobj1.print_filtered_list(2);
54
55 my $listobj2 = list_filter_gt_10.new();
56 $listobj2.print_filtered_list(1);
57 $listobj2.print_filtered_list(2);
58
输出:
./b.pl6
[1 2 3 4 5 6 7 8 9 10]
[5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[10 11 12 13 14 15]
TL;DR 你告诉 P6 在调用你的 filter
方法时期望的参数。然后你在调用它时没有传递约定的参数。所以P6代你投诉。要解决此问题,要么传递您告诉 P6 期望的参数,要么停止告诉 P6 期望它们。 :)
消息说预期 2
,得到 1
,而不是预期 1
得到 0
。
这是因为 self
被隐式传递并添加到此附加的消息详细信息位中的 "expected" 和 "got" 总计中,将两者加一。 (这个细节可能不太好,即我们应该考虑修复的东西。)
当我 run your code on tio 我得到:
Too few positionals passed; expected 2 arguments but got 1
in method filter at .code.tio line 27
in method print_filtered_list at .code.tio line 12
in block <unit> at .code.tio line 42
第 27 行的方法 声明 method filter($l) {...}
告诉 P6 期望每个 .filter
方法有 两个 个参数呼叫:
祈求者。 (这将绑定到 self
。)我们称该 参数为 A。
位置参数。 (这将绑定到 $l
参数)。我们称该 参数为 B.
但是在第 12 行的 &{self.filter}
中,当您提供 .filter
方法时 call 带有 参数 A,即调用参数,您不提供 参数 B,即位置参数(after filter
,例如 &{self.filter(42)}
).
因此Too few positionals passed; expected 2 arguments but got 1
.
piojo 的回答看起来可行(虽然我没试过)。
将方法转换为变量的另一种方法是使用间接寻址:
class Foo {
method bar($a) {
$a * 2
}
}
sub twice(&f, $x) {
f f $x
}
my $foo = Foo.new();
say twice {$foo.bar: $^a}, 1
我有一个如下所示的脚本。目的是使用不同的过滤方法来过滤列表。
这是代码。
2
3 class list_filter {
4 has @.my_list = (1..20);
5
6 method filter($l) { return True; }
7
8 # filter method
9 method filter_lt_10($l) {
10 if ($l > 10) { return False; }
11 return True;
12 }
13
14 # filter method
15 method filter_gt_10($l) {
16 if ($l < 10) { return False; }
17 return True;
18 }
19
20 # expecting a list of (1..10) to be the output here
21 method get_filtered_list_lt_10() {
22 return self.get_filtered_list(&{self.filter_lt_10});
23 }
24
25 # private
26 method get_filtered_list(&filter_method) {
27 my @newlist = ();
28 for @.my_list -> $l {
29 if (&filter_method($l)) { push(@newlist, $l); }
30 }
31 return @newlist;
32 }
33 }
34
35 my $listobj = list_filter.new();
36
37 my @outlist = $listobj.get_filtered_list_lt_10();
38 say @outlist;
期望 [1..10] 是这里的输出。但是出现以下错误。
Too few positionals passed; expected 2 arguments but got 1
in method filter_lt_10 at ./b.pl6 line 9
in method get_filtered_list_lt_10 at ./b.pl6 line 22
in block <unit> at ./b.pl6 line 37
我做错了什么?
在 Perl 6 中将方法作为参数传递需要您使用 MOP(元对象协议)方法,或者按名称传递方法(然后在运行时为您进行查找)。
但是,如果您实际上并没有在这些方法中对对象执行某些操作,为什么还要使用 method
s 呢?它们也可能是 sub
,您 可以 作为参数传递。
也许这是最好的例子:
class list_filter {
has @.my_list = 1..20; # don't need parentheses
sub filter($ --> True) { } # don't need code, signature is enough
# filter sub
sub filter_lt_10($l) { not $l > 10 }
# filter sub
sub filter_gt_10($l) { not $l < 10 }
# private
method !get_filtered_list(&filter_sub) {
@.my_list.grep(&filter_sub);
}
# expecting a list of (1..10) to be the output here
method get_filtered_list_lt_10() {
self!get_filtered_list(&filter_lt_10);
}
}
my $listobj = list_filter.new();
my @outlist = $listobj.get_filtered_list_lt_10();
say @outlist; # [1 2 3 4 5 6 7 8 9 10]
第一个 sub filter
,只有 return 是一个常量值(在本例中 True
),可以更容易地在带有空体的签名中表示。
filter_lt_10
和 filter_gt_10
subs 只需要否定条件,因此使用 not
.
get_filtered_list
方法应该是私有的,因此通过前缀 !
使其成为私有方法。
在 get_filtered_list_lt_10
中,您现在需要使用 !
而不是 .
来调用 get_filtered_list
。并且您通过在 &
前加上前缀 filter_lt_10
sub 作为参数传递(否则它将被视为对没有任何参数的 sub 的调用,这将失败)。
更改 get_filtered_list
以使用内置 grep
方法:这需要一个 Callable
块,该块采用单个参数并且应该 return 一些东西 True
以包括它所处理的列表的值。由于 sub
接受单个参数 是 一个 Callable
,我们可以直接在那里指定 sub。
希望这是有道理的。我试图尽可能接近预期的语义。
一些通用的编程评论:我觉得 sub 的命名很混乱:我觉得它们应该被称为 filter_le_10
和 filter_ge_10
,因为它们确实是这样做的在我看来。另外,如果你真的不想要任何临时过滤,而只是从一组特定的预定义过滤器中过滤,你可能会更好地使用常量或 enum
s 创建调度 table , 并使用它来指示您想要的过滤器,而不是将此信息编码为另一种制作和维护方法的名称。
希望对您有所帮助。
&{self.method}
语法对我来说是新的,非常感谢。不幸的是,如果需要参数,它就不起作用。你可以像其他poster提到的那样使用sub
,但是如果你需要使用方法,你可以通过调用self.^lookup
来获取方法,这就是Elizabeth提到的元对象协议的使用。 ('^' 表示您调用的方法不是 class 的一部分,而是 "shadow" class 的一部分,其中包含 class 的主要内容 /实施细节。)
要获取一个方法,使用运行 obj.^lookup(method name)
,并通过传入对象本身(通常是"self")作为第一个参数来调用它,然后是其他参数。要将对象绑定到函数以便不需要每次都显式添加它,请使用 assuming 函数。
class MyClass {
method log(Str $message) { say now ~ " $message"; }
method get-logger() { return self.^lookup('log').assuming(self); }
}
my &log = MyClass.get-logger();
log('hello'); # output: Instant:1515047449.201730 hello
找到了。这对我有用。
3 class list_filter {
4 has @.my_list = (1..20);
5
6 # will be overriding this in derived classes
7 method filter1($l) { return True; }
8 method filter2($l) { return True; }
9
10 # same print method I will be calling from all derived class objects
11 method print_filtered_list($type) {
12 my @outlist = self.get_filtered_list($type);
13 say @outlist;
14 }
15
16 # private
17 method get_filtered_list($type) {
18 my @newlist = ();
19 for @.my_list -> $l {
20 my $f = "filter$type";
21 if (self."$f"($l)) { push(@newlist, $l); }
22 }
23 return @newlist;
24 }
25 }
26
27 class list_filter_lt_10 is list_filter {
28 method filter1($l) {
29 if ($l > 10) { return False; }
30 return True;
31 }
32 method filter2($l) {
33 if ($l > 10) { return False; }
34 if ($l < 5) { return False; }
35 return True;
36 }
37 }
38
39 class list_filter_gt_10 is list_filter {
40 method filter1($l) {
41 if ($l < 10) { return False; }
42 return True;
43 }
44 method filter2($l) {
45 if ($l < 10) { return False; }
46 if ($l > 15) { return False; }
47 return True;
48 }
49 }
50
51 my $listobj1 = list_filter_lt_10.new();
52 $listobj1.print_filtered_list(1);
53 $listobj1.print_filtered_list(2);
54
55 my $listobj2 = list_filter_gt_10.new();
56 $listobj2.print_filtered_list(1);
57 $listobj2.print_filtered_list(2);
58
输出:
./b.pl6
[1 2 3 4 5 6 7 8 9 10]
[5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[10 11 12 13 14 15]
TL;DR 你告诉 P6 在调用你的 filter
方法时期望的参数。然后你在调用它时没有传递约定的参数。所以P6代你投诉。要解决此问题,要么传递您告诉 P6 期望的参数,要么停止告诉 P6 期望它们。 :)
消息说预期 2
,得到 1
,而不是预期 1
得到 0
。
这是因为 self
被隐式传递并添加到此附加的消息详细信息位中的 "expected" 和 "got" 总计中,将两者加一。 (这个细节可能不太好,即我们应该考虑修复的东西。)
当我 run your code on tio 我得到:
Too few positionals passed; expected 2 arguments but got 1
in method filter at .code.tio line 27
in method print_filtered_list at .code.tio line 12
in block <unit> at .code.tio line 42
第 27 行的方法 声明 method filter($l) {...}
告诉 P6 期望每个 .filter
方法有 两个 个参数呼叫:
祈求者。 (这将绑定到
self
。)我们称该 参数为 A。位置参数。 (这将绑定到
$l
参数)。我们称该 参数为 B.
但是在第 12 行的 &{self.filter}
中,当您提供 .filter
方法时 call 带有 参数 A,即调用参数,您不提供 参数 B,即位置参数(after filter
,例如 &{self.filter(42)}
).
因此Too few positionals passed; expected 2 arguments but got 1
.
piojo 的回答看起来可行(虽然我没试过)。
将方法转换为变量的另一种方法是使用间接寻址:
class Foo {
method bar($a) {
$a * 2
}
}
sub twice(&f, $x) {
f f $x
}
my $foo = Foo.new();
say twice {$foo.bar: $^a}, 1