且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-02-14 16:02:28

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

但是,如果您并没有真正对这些方法中的对象进行某些操作,为什么还要使用 method 呢?那么它们也可能是 sub s,您可以将其作为参数传递.

也许这是***的例子:

class list_filter {有@.my_list = 1..20;# 不需要括号sub filter($ --> True) { } # 不需要代码,签名就够了#过滤子sub filter_lt_10($l) { not $l >10 }#过滤子sub filter_gt_10($l) { 不是 $l <10 }# 私人的方法 !get_filtered_list(&filter_sub) {@.my_list.grep(&filter_sub);}# 期望 (1..10) 的列表是这里的输出方法 get_filtered_list_lt_10() {self!get_filtered_list(&filter_lt_10);}}我的 $listobj = list_filter.new();我的@outlist = $listobj.get_filtered_list_lt_10();说@outlist;# [1 2 3 4 5 6 7 8 9 10]

第一个 sub filter,它只返回一个常量值(在本例中为 True),可以更容易地在签名中用空体表示.

filter_lt_10filter_gt_10 子项只需要否定条件,因此使用了 not.

get_filtered_list 方法应该是私有的,因此通过前缀 ! 使其成为私有方法.

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

更改 get_filtered_list 以使用内置的 grep 方法:这需要一个 Callable 块,该块采用单个参数并且应该返回一些 True 包含它所处理的列表的值.由于带有单个参数的sub一个Callable,我们可以直接在那里指定sub.

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

一些通用的编程评论:我觉得 subs 的命名令人困惑:我觉得它们应该被称为 filter_le_10filter_ge_10,因为那是在我看来,他们的所作所为确实如此.此外,如果您真的不想要任何临时过滤,而只想从一组特定的预定义过滤器中进行过滤,那么***使用常量或 enum 创建调度表,并使用它来指示您想要哪种过滤器,而不是以另一种要制作和维护的方法的名称编码此信息.

希望这会有所帮助.

I have a script like the below. Intent is to have different filter methods to filter a list.

Here is the code.

  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;

Expecting [1..10] to be the output here. But getting following error.

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

What am I doing wrong here?

Passing a method as a parameter in Perl 6 either requires you to use MOP (Meta-Object Protocol) methods, or pass the method by name (which would then do the lookup for you at runtime).

But why use methods if you're not really doing something with the object in those methods? They might as well be subs then, which you can pass as a parameter.

Perhaps this is best by example:

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]

The first sub filter, which only returns a constant value (in this case True), can be represented much more easily in the signature with an empty body.

The filter_lt_10 and filter_gt_10 subs only need the condition negated, hence the use of the not.

The get_filtered_list method is supposed to be private, so make it a private method by prefixing !.

In the get_filtered_list_lt_10 you now need to call get_filtered_list with a ! instead of a .. And you pass the filter_lt_10 sub as a parameter by prefixing the & (otherwise it would be considered a call to the sub without any parameters, which would fail).

Change the get_filtered_listto use the built-in grep method: this takes a Callable block that takes a single parameter and which should return something True to include the value of the list it works upon. Since a sub taking a single parameter is a Callable, we can just specify the sub there directly.

Hope this made sense. I tried to stay as close as possible to the intended semantics.

Some general programming remarks: it feels to me that the naming of the subs is confusing: it feels to me that they should be called filter_le_10 and filter_ge_10, because that's really what they do it appears to me. Also, if you really don't want any ad-hoc filtering, but only filtering from a specific set of predefined filters, you would probably be better of by creating a dispatch table using constants or enums, and use that to indicate which filter you want, rather than encoding this information in the name of yet another method to make and maintain.

Hope this helps.