如何将类方法作为参数传递给perl 6中的另一个类方法

时间:2018-01-03 15:28:02

标签: perl6 function-object

我有一个如下的脚本。意图是使用不同的过滤方法来过滤列表。

这是代码。

  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

我在这里做错了什么?

5 个答案:

答案 0 :(得分:4)

在Perl 6中将方法作为参数传递要么需要使用MOP(元对象协议)方法,要么按名称传递方法(然后在运行时为您进行查找)。

但是,如果您在这些方法中没有真正使用对象,那么为什么要使用saveTapped?它们也可能是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),可以在签名中更容易地表示为空体。

Truefilter_lt_10潜点只需要否定条件,因此使用filter_gt_10

not方法应该是私有的,因此通过为get_filtered_list加前缀来使其成为私有方法。

!中,您现在需要使用get_filtered_list_lt_10而不是get_filtered_list来呼叫!。并且通过为.加前缀来传递filter_lt_10子作为参数(否则它将被视为对没有任何参数的子调用,这将失败)。

更改&以使用内置的get_filtered_list方法:这会占用一个grep块,该块需要一个参数,哪个块应返回Callable以包含它所依赖的列表的价值。由于True采用单个参数 sub,我们可以直接指定子参数。

希望这是有道理的。我试图尽可能接近预期的语义。

一些一般的编程评论:我觉得潜艇的命名令人困惑:我觉得它们应该被称为Callablefilter_le_10,因为那真的是什么他们这样做对我来说。此外,如果您真的不想要任何特别过滤,但只是从一组特定的预定义过滤器中过滤,那么通过使用常量或filter_ge_10 s创建一个调度表,您可能会更好。用它来表示你想要的过滤器,而不是用另一种方法来制作和维护这些信息。

希望这有帮助。

答案 1 :(得分:3)

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}中,当您使用参数A (即调用参数)提供.filter方法调用时,不要提供参数B ,即位置参数( filter之后,例如&{self.filter(42)})。

因此Too few positionals passed; expected 2 arguments but got 1

答案 2 :(得分:2)

找到它。这对我有用。

  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]

答案 3 :(得分:0)

&{self.method}语法对我来说是新的,所以谢谢你。不幸的是,如果需要参数,它不起作用。您可以使用sub作为提到的其他海报,但如果您需要使用方法,您可以通过调用self.^lookup来获取方法,这是使用Elizabeth提到的元对象协议。 (&#39; ^&#39;意味着你并没有调用那个类的一部分方法,而是包含主类&#34; shadow&#34;类的一部分39;内容/实施细节。)

要获取方法,请使用run obj.^lookup(method name),并通过传入对象本身(通常&#34; self&#34;)作为第一个参数,然后传递其他参数来调用它。要将对象绑定到函数,以便每次都不需要显式添加,请使用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

答案 4 :(得分:0)

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