美文网首页Raku Programming Language
Perl 6 - stackoverflowWeekly1014

Perl 6 - stackoverflowWeekly1014

作者: 焉知非鱼 | 来源:发表于2016-10-14 18:49 被阅读13次

    Perl 6 List Concatenation without Slip

    Perl 6 List Concatenation without Slip
    在 Perl 5 中 , 操作符用来连接列表; 然而在 Perl 6 中需要使用 | 操作符, 即 slip 操作符。

    my @a = <a b c>;
    my @b = <d e f>;
    my @ab = |@a, |@b;
    

    有比这短点的写法吗?

    • 答案

    你可以使用 「flat」sub:

    my @a  = <a b c>;
    my @b  = <d e f>;
    my @ab = flat @a, @b;
    say @ab.perl; #> ["a", "b", "c", "d", "e", "f"]
    my @abf = (@a, @b).flat;
    say @abf.perl; #> ["a", "b", "c", "d", "e", "f"]
    

    注意, flat 可以移除多层嵌套, 如果该层中的值不是标量的话。

    # 使用无符号变量
    my \list = (1,2,(3,4,(5,6)));
    say flat list; # (1 2 3 4 5 6)
    

    但是使用 |() 只能脱去一层:

    my \list = (1,2,|( (3,4,(5,6)) ));
    say list;  # (1 2 3 4 (5 6))
    

    在 Perl 6 中有多少种方法写 Fibonacci 序列?

    How many ways are there to describe the Fibonacci sequence in Perl 6?

    my @fibs = (0, 1, -> $a, $b { $a + $b } ... *);
    my @fibs = (0, 1, { $^a + $^b } ... *);  
    my @fibs = (0, 1, *+* ... *);
    
    # 迭代
    sub fib (Int $n --> Int) {
        $n > 1 or return $n;
        my ($prev, $this) = 0, 1;
        ($prev, $this) = $this, $this + $prev for 1 ..^ $n;
        return $this;
    }
    
    # 递归
    use experimental :cached;
    
     # 定义一个原型, 接收 Int 类型的参数并返回 Int 类型的结果
    proto fib (Int $n --> Int) is cached {*}
    multi fib (0)  { 0 }
    multi fib (1)  { 1 }
    multi fib ($n) { fib($n - 1) + fib($n - 2) }
    
    # 解析
    sub fib (Int $n --> Int) {
        constant φ1 = 1 / constant φ = (1 + sqrt 5)/2;
        constant invsqrt5 = 1 / sqrt 5;
     
        floor invsqrt5 * (φ**$n + φ1**$n);
    }
    

    Perl 6 的性能现在怎么样了?

    What performance increases can we expect as the Perl 6 implementations mature?

    5 年之前是非常慢的, 现在已经没有那么慢了:

    $ perl6 -e 'say [+] 1..10**1000; say now - INIT now'
    5000000000000000000000000000000000000000000000 ...
    0.007447
    
    $ perl6 -e 'say [+] (1..100000).list; say now - INIT now'
    5000050000
    0.13052975
    

    像 Int(Cool) 这样的类型强制有什么用?

    What is the point of coercions like Int(Cool)?

    类型强制能让你在子例程中拥有一个特殊的类型, 但是接收更广的输入。当该子例程被调用时, 参数会被自动转换为更窄的类型。

    sub double(Int(Cool) $x) {
        2 * $x
    }
    
    say double '21';    # 42
    say double Any;     # Type check failed in binding $x; expected 'Cool' but got 'Any'
    

    这里, Int 是参数将被强制的目标类型, 而 Cool 类型是子例程的输入参数的类型。

    但是这个子例程有什么用呢? $x 不就是 Int 类型吗? 为什么你还约束调用者来为参数实现 Cool

    这个子例程所做的就是接收一个 Cool 的子类型的值, 然后尝试把这个值转换为 Int。那个时候它就是一个 Int 类型的值了, 不管它之前是什么类型的值。

    所以:

    sub double ( Int(Cool) $n ) { $n * 2 }
    

    真的可以被看作(我认为这是它在 Rakudo 中的实现方式)

    # Int 是 Cool 的子类型否则它就是 Any 或 Mu 类型
    proto sub double ( Cool $n ) {*} # 定义一个原型
    
    # 这儿有你写的内在部分
    multi sub double (  Int $n ) { $n * 2 }
    
    # 这儿是编译器为你写的部分
    multi sub double ( Cool $n ) {
        # 调用其它 multi, 因为它现在是 Int 类型
        samewith Int($n);
    }
    

    所以它接受任何 Int, Str, Rat, FatRat, Num, Array, Hash, 等类型. 并在调用 &infix:<*> (带有 $n 和 2)之前尝试把它转换为 Int 类型。

    say double '  5  '; # 10
    say double 2.5;     # 4
    say double [0,0,0]; # 6
    say double { a => 0, b => 0 }; # 4
    

    你可以把它限制为 Cool 而非 Any 类型, 因为所有的 Cool 值本来要为 Int 提供一个强制。

    ( :( Int(Any) $ ) can be shortened to just :( Int() $ ) )
    

    你这样做的原因可能是因为你需要在子例程中让它是 Int 类型因为你正调用其它含有不同类型的代码来做不同的事情:

    sub example ( Int(Cool) $n ) returns Int {
        other-multi( $n ) * $n;
    }
    
    multi sub other-multi ( Int $ ) { 10 }
    multi sub other-multi ( Any $ ) {  1 }
    
    say example 5;   # 50
    say example 4.5; # 40
    

    在这种特殊情况下, 你可以把它写为下面的其中之一。

    sub example ( Cool $n ) returns Int {
        other-multi( Int($n) ) * Int($n);
    }
    
    sub example ( Cool $n ) returns Int {
        my $temp = Int($n);
        other-multi( $temp ) * $temp;
    }
    
    sub example ( Cool $n is copy ) returns Int {
        $n = Int($n);
        other-multi( $n ) * $n;
    }
    

    它们中没有一种比使用签名来为你强制类型更清晰。
    通常对于这样简单的功能你可以使用如下几种方法中的任意一种, 它会很好地满足你想要的。

    my &double = * * 2; # WhateverCode
    my &double = * × 2; # 同上
    
    my &double = { $_ * 2 };       # bare block
    my &double = { $^n * 2 };      # block with positional placeholder
    my &double = -> $n { $n * 2 }; # pointy block
    
    my &double = sub ( $n ) { $n * 2 } # anon sub
    my &double = anon sub double ( $n ) { $n * 2 } # anon sub with name
    
    my &double = &infix:<*>.assuming(*,2); # 柯里化
    my &double = &infix:<*>.assuming(2);
    
    sub double ( $n ) { $n * 2 } # same as :( Any $n )
    

    我不太清楚 proto 是干什么用的。
    答:当你调用一个 multi sub 的时候, 实际上你调用的是一个「原型 sub」(proto sub), 然后这个原型 sub 再调用合适的 multi 候选者。

    相关文章

      网友评论

        本文标题:Perl 6 - stackoverflowWeekly1014

        本文链接:https://www.haomeiwen.com/subject/xjcnyttx.html