代码之家  ›  专栏  ›  技术社区  ›  BhaskarS

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

  •  7
  • BhaskarS  · 技术社区  · 8 年前

    我有一个像下面这样的脚本。目的是使用不同的筛选方法来筛选列表。

    这是代码。

      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 回复  |  直到 8 年前
        1
  •  5
  •   Elizabeth Mattijsen    8 年前

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

    但为什么要使用 method 如果在这些方法中没有真正对对象执行操作?他们也可能是 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 ,它只返回一个常量值(在本例中 True ),可以更容易地在带有空正文的签名中表示。

    这个 filter_lt_10 filter_gt_10 Sub只需要条件被否定,因此使用 not .

    这个 get_filtered_list 方法应该是私有的,所以通过前缀将其设置为私有方法 ! .

    get_filtered_list_lt_10 你现在需要打电话 get\u filtered\u列表 用一个 ! 而不是 . . 然后你通过 过滤器\u lt\u 10 通过在 & (否则,它将被视为对sub的调用,没有任何参数,这将失败)。

    更改 get\u filtered\u列表 使用内置 grep 方法:这需要 Callable 获取单个参数并应返回某些内容的块 真的 以包含它所处理的列表的值。自a 附属的 采用单个参数 可调用的 ,我们可以直接在那里指定sub。

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

    一些一般编程备注:我觉得sub的命名很混乱:我觉得应该调用它们 filter_le_10 filter_ge_10 因为在我看来,他们就是这么做的。此外,如果您真的不想进行任何特别过滤,而只想从一组特定的预定义过滤器中进行过滤,那么最好使用常量或 enum s、 并使用它来指示所需的过滤器,而不是以另一个要创建和维护的方法的名称来编码此信息。

    希望这有帮助。

        2
  •  3
  •   raiph    8 年前

    TL;博士 您告诉P6在调用 filter 方法那么,您在调用时未能通过商定的参数。所以P6代表你投诉。要解决此问题,请传递您告诉P6期望的参数,或停止告诉P6期望它们。:)


    消息显示预期 2 ,获得 1 ,而非预期 1. 得到了 0 .

    这是因为 self 隐式传递并添加到此附加的消息详细信息位中的“预期”和“已获得”总计,将两者增加一。(这个细节可能不太好,也就是说,我们应该考虑修复一些东西。)


    当我 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
    

    方法 公告 method filter($l) {...} 第27行告诉P6 每个的参数 .filter 方法 呼叫 :

    • 发票人。(这将绑定到 自己 .) 我们这样说吧 参数A .

    • 位置参数。(这将绑定到 $l 参数)。我们这样说吧 参数B .

    但在 &{self.filter} 在第12行中 .滤器 方法 呼叫 使用 参数A ,即invocant参数,您不提供 参数B ,即位置参数( 之后 滤器 ,例如。 &{self.filter(42)} ).

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

        3
  •  2
  •   BhaskarS    8 年前

    找到了。这对我来说很有用。

      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]
    
        4
  •  2
  •   Elizabeth Mattijsen    6 年前

    这个 &{self.method} 语法对我来说是新的,所以谢谢你。不幸的是,如果需要参数,它就不起作用。您可以使用 sub 正如其他海报所提到的,但是如果需要使用方法,可以通过调用 self.^lookup ,这是Elizabeth提到的元对象协议的使用。(“^”表示您调用的方法不是该类的一部分,而是“shadow”类的一部分,该类包含主类的勇气/实现细节。)

    要获取方法,请使用run 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
    
        5
  •  0
  •   Owen    6 年前

    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