【问题标题】:How do you add a method to an existing class in Perl 6?如何在 Perl 6 中向现有类添加方法?
【发布时间】:2015-12-29 05:18:12
【问题描述】:

Int class 有一个方法 is_prime,所以我想,只是为了咯咯笑,我想在 Int 中添加一些其他方法,用于我的一些做数论的业余项目。

我想我可以这样做:

class Int {
    method is-even (Int:D $number ) returns Bool:D {
        return False if $number % 2;
        return True;
        }
    }

say 137.is-even;

但这不起作用:

===SORRY!===
P6opaque: must compose before allocating

我不知道这是否意味着我不能这样做,或者我做错了。

我可以轻松地创建一个继承自 Int 的新类,但这不是我感兴趣的:

class MyInt is Int {
    method is-even () returns Bool:D {
        return False if self % 2;
        return True;
        }
    }

my $n = MyInt.new(138);
say $n.is-even;

我不是在寻找解决方法或替代解决方案。

【问题讨论】:

    标签: raku


    【解决方案1】:

    这有语法糖 - augment:

    use MONKEY-TYPING;
    
    augment class Int {
        method is-even() returns Bool:D {
            return False if self % 2;
            return True;
        }
    }
    

    增加一个类被认为是危险的,原因有两个:首先,远距离操作,其次,因为(据我所知),存在未定义行为 去优化的可能性 因为它可能会使各种方法缓存处于无效状态。

    因此,在您被允许使用之前提供MONKEY-TYPING pragma 的要求。

    顺便说一句,请注意is-even 可以更简洁地写成self %% 2

    【讨论】:

    • @briandfoy:请注意,我将措辞从 undefined behavior 更改为 deoptimization - 默认情况下,类应该是打开的;据我所知,没有实现类终结
    【解决方案2】:

    嗯,这行得通,我以为我以前尝试过,而且我比我在问题中提出的更喜欢。

    Int.^add_method( 'is-even', method () returns Bool:D {
        return False if self % 2;
        return True;
        } );
    
    say 137.is-even;
    

    不过,我不确定这是否可行。 add_method docs 说我们应该只在类型组合之前这样做。如果我打电话给Int.^methodsis-even 不会出现。尽管如此,它似乎是可调用的并且做正确的事情。

    词法方法

    玩多了,我想我可以创建一个不附加到任何类的方法并在对象上调用它:

    my &is-even = method (Int:D :) returns Bool:D { self %% 2 };
    

    这构造了一个Callable(查看&is-even.WHAT)。在签名中,我将它限制为一个明确的 Int 值 (Int:D),但没有给它命名。我在类型约束后添加冒号以注意第一个参数是调用者。现在我可以将该方法应用于我喜欢的任何对象:

    say 137.&is-even;
    say 138.&is-even;
    say "foo".&is-even;  # works, although inside is-even blow up
    

    这在不同的维度上很好,因为它是词法的,但不好的是错误类型的对象可能会调用它。在它认为它有方法可以调度之后,错误就会出现。

    【讨论】:

    • Int 上额外调用.^compose(或至少.^publish_method_cache)可能是一个好主意,也可能不是一个好主意
    • 这是在哪里记录的?
    • 我认为它没有被记录在案,我什至不能 100% 确定我的建议是否合理——只是 Perl6::Metamodel::MethodContainer.add_method 使用参数 0 执行 nqp::setmethcacheauth,而 Perl6::Metamodel::ClassHOW.compose 调用Perl6::Metamodel::MROBasedMethodDispatch.publish_method_cache 再次设置nqp::setmethcacheauth
    • 重新编辑,你的 Rakudo 版本是多少?我最近的一个在"foo"上调用&is-even时死于Type check failed in binding <anon>; expected Int but got Str
    • 我得到了同样的错误。我宁愿得到类似“没有这种方法”的东西。我对%% 的评论来自我的测试中发生的其他事情。
    【解决方案3】:

    您也可以通过包含& sygil 来简单地调用子方法:

    sub is-even(Int $n) { $n %% 2 }
    say 4.&is-even;  # True
    

    当然,这只是语法糖,但我已经做了几次,它看起来比简单地调用 sub 更具可读性。

    (我知道您“不是在寻找变通办法或替代解决方案”,但您自己发布了一些,所以我想,为什么不呢?)

    【讨论】:

    • 我在第一个回答中注意到了这一点。 :)
    • 但是你使用了一个有趣的方法赋值,而不是简单地声明一个子。
    【解决方案4】:

    如果您只需要类的某些实例,还有另一种有趣的方法可以做到这一点。您可以使用角色来装饰对象:

     my $decorated = $object but role { ... }
    

    【讨论】:

    • 这就是我一直在寻找的:向实例添加方法。对象的类必须能够clone 我猜是因为我得到了:No such method 'clone' for invocant of type 'Perl6::Grammar+{Nogil::NogilGrammar}'
    • 我得到了,它:我正在寻找的是混合
    猜你喜欢
    • 2018-08-14
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多