【问题标题】:Simplify the invocation of a common method简化通用方法的调用
【发布时间】:2019-01-31 02:27:55
【问题描述】:

我有这样的课:

class Foo {
    method some-method(Str $name) { ... }
}

简单用法:

my $foo = Foo.new;

$foo.some-method("peter");

由于“some-method”会被非常频繁地调用,我想做一些事情来让用户像下面这样使用它:

$foo.peter;

我知道 FALLBACK 可以完成这项工作,但它已用于另一种方法。 我试图定义一个中缀运算符:

sub infix:<%>(Foo $foo, $name) {
    $foo.some-method($name);
}

下面的代码可以工作,但是双引号很烦人。

$foo % "peter";

那么有什么办法可以避免引号呢?或者有什么方法可以简化调用?

【问题讨论】:

  • 我为此使用了 FALLBACK,但由于您的 arg 是一个简单的 Str,您可以将其设为关联,只需使用方法 AT-KEY 并使用 $foo&lt;peter&gt; 调用它。
  • FALLBACK supports multi methods and subsignatures。只要您可以区分不同的调用(例如,通过检查方法名称或其参数),您就应该能够将它用于任意数量的方法。
  • 这可行吗?必须运行并且尚未测试,但可能使用 MOP 生成和导出适当约束的后缀?类似(无 MOPing)class c { module { sub postfix:&lt;.foo&gt; (c $_) is export { .say } } }; import c; c.foo; # (c).
  • @raiph 使用前需要定义运算符吗?
  • @lovetomato 是的,你做到了。总是。否则 Perl 6 将不知道如何解析它。

标签: methods operator-keyword raku


【解决方案1】:

正如 Curt Tilmes 已经指出的,您可以让您的 Foo 对象充当 Associative(或 Hash):

class Foo {
    method some-method(Str $name) { ... }
    method AT-KEY(Str $name) { self.some-method($name) }
}
my $foo = Foo.new;
say $foo<peter>;   # same as $foo.some-method("peter")

当然,AT-KEY 方法可以是一个 multi,所以你也可以用它玩各种花样。

class Foo {
    method some-method(Str $name) { "$name is ok" }
    multi method AT-KEY("peter")   { "peter is special" }
    multi method AT-KEY(Str $name) { self.some-method($name) }
}
my $foo = Foo.new;
say $foo<peter>;   # "peter is special"
say $foo<joe>;     # "joe is ok"

【讨论】:

    【解决方案2】:

    有一种方法可以将FALLBACK 用于多个操作,前提是它们在某些方面有所不同。

    • 通过检查对象的某些属性:

      class Foo {
        # this could be set as part of `new`/`BUILD`
        has %!special = ( "peter" => 42 );
      
        multi method FALLBACK ( $name where (%!special{$name}:exists) ) {
          %!special{$name}
        }
      
        multi method FALLBACK ( $other ) {
          $other.tc
        }
      }
      
      with Foo.new {
        say .paul; # Paul
        say .peter; # 42
      }
      

      这有潜在的远距离动作问题。

    • 使用不同数量或类型的参数:

      class Bar {
        multi method FALLBACK ( Str:D $name ) {
          $name.tc
        }
        multi method FALLBACK ( Str:D $name, Real:D $number ) {
          $name.tc, 1 / $number
        }
        multi method FALLBACK ( Str:D $name, Str:D $other ) {
          $name.tc, $other.uc
        }
      }
      
      with Bar.new {
        say .paul;          # Paul
        say .peter(42);     # Peter, 0.02381
        say .peter('Paul'); # Peter, PAUL
      }
      

    您可以使用 .[…] 作为 Int 参数。

    class Baz {
      method AT-POS ( $arg ) { say "Baz[$arg]" }
    }
    Baz.new[42,32]; # Baz[42]
                    # Baz[32]
    

    内置的 postcircumfix:« [ ] » 将参数强制转换为 Int,但您可以添加一个新参数。
    (这样做有很多注意事项。)

    multi sub postcircumfix:<[ ]> ( Baz:D $b, $a ) is export {
      # $b.AT-POS( $a )
    
      $b.some-method( $a )
    }
    

    您可以将.&lt;…&gt; 用于空格分隔的Strs 或.{…} 用于任意值。

    class Other {
      multi method AT-KEY ( Str:D $name ){
        $name.tc
      }
      multi method AT-KEY ( Real:D $number ){
        1 / $number
      }
    }
    
    with Other.new {
      say $_<peter>;     # Peter
      say $_.<paul>;     # Paul
      say .<peter paul>; # Peter Paul
      # note that AT-Key got called twice
    
      say $_{42,'peter'}; # 0.02381, Peter
      # note that AT-Key got called twice
    }
    

    你可以让你的对象是可调用的。

    class Fubar {
      multi method CALL-ME ( Str:D $name ){
        $name.tc
      }
      multi method CALL-ME ( Real:D $number ){
        1 / $number
      }
      multi method CALL-ME ( +@args ){
        @args.map: {self.CALL-ME($_)}
      }
    }
    
    with Fubar.new {
      say $_('peter');   # Peter
      say $_(42);        # 0.02381
    
      # this calls the +@args one
      say $_('paul',32); # Paul, 0.03125
    }
    

    在执行任何这些操作之前,您应该认真考虑您的 API。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-19
      • 2015-10-15
      相关资源
      最近更新 更多