Perl 6 中的 Whatever

作者: 焉知非鱼 | 来源:发表于2016-09-25 17:14 被阅读54次

    Whatever 是什么?

    Placeholder for unspecified value/parameter - 未指定的值/参数的占位符。

    * 字面量在 「term」 位置上创建 「Whatever」 对象。
    * 的大部分魔法来自于 「Whatever 柯里化」. 当 * 作为 item 与很多操作符组合使用时, 编译器会把表达式转换为 「WhateverCode」 类型的闭包.

    my $c = * + 2;          # same as   -> $x { $x + 2 };
    say $c(4);              # 6
    

    如果一个表达式中有 N 个 *, 则会产生一个含有 N 个参数的闭包:

    my $c = * + *;          # same as   -> $x, $y { $x + $y }
    

    在复杂的表达式中使用 * 也会产生闭包:

    my $c = 4 * * + 5;      # same as   -> $x { 4 * $x + 5 }
    

    * 号身上调用方法也会产生闭包:

    <a b c>.map: *.uc;      # same as <a b c>.map: -> $char { $char.uc }
    

    前面提到, 不是所有的操作符和语法都会把 * 柯里化为 「WhateverCode」。
    下面这几种情况, * 仍旧是 「Whatever 对象」。

    例外               Example    What it does
    
    逗号               1,*,2      用一个 * 元素生成一个 Parcel
    范围操作符         1..*       Range.new(:from(1), :to(*));
    序列操作符         1 ... *    无限列表
    智能匹配           1 ~~ *     返回 True
    赋值               $x = *     把 * 赋值给 $x
    绑定               $x := *    把 * 绑定给 $x
    列表重复           1 xx *     生成无限列表
    

    范围操作符被特殊处理. 它们不使用 Whatever-Stars 柯里化, 但是它们使用 「WhateverCode」 进行柯里化.

    say (1..*).WHAT;        # Range
    say (1..*-1).WHAT;      # WhateverCode
    

    上面的 *-1 是作为参数传递了。

    下面这些也能使用:

    .say for 1..*;          # infinite loop
    my @a = 1..4;
    say @a[0..*];           # 1 2 3 4
    say @a[0..*-2];         # 1 2 3
    

    因为 Whatever-currying 是纯粹的语法编译器转换, 这不会在运行时把存储的 Whatever-stars 柯里化为 WhateverCodes.

    my $x = *;
    $x + 2;                 # not a closure, dies because
                            # it can't coerce $x to Numeric
    

    存储 Whatever-stars 的使用案例像上面提到的那样, 但要把柯里化异常的情况也包含进去. 例如, 如果你想要一个默认的无限序列:

    my $max    = potential-upper-limit() // *;
    my $series = known-lower-limit() ... $max;
    

    一个存储后的 * 会在智能匹配的特殊情况下生成 WhateverCode. 注意, 正被柯里化的并非真正储存的 *, 而是在 LHS 上的 *

    my $constraint = find-constraint() // *;
    my $maybe-always-matcher = * ~~ $constraint;
    

    如果这个假定的 find-constraint 没有找到约束, 则 maybe-always-matcher 会对任何东西都返回 True.

    $maybe-always-matcher(555);      # True
    $maybe-always-matcher(Any);      # True
    

    Whatever Star

    当作为一个「项」使用时, 我们把 * 叫做 "Whatever"。当不是实际值时,它用作占位符。例如, 1, 2, 3 ... *,意思是没有终结点的自然数序列。

    Whatever 闭包

    Whatever 最强大的用处是 「Whatever」 闭包。

    对于 Whatever 没有特殊意义的普通操作符:把 Whatever 当作参数传递时就创建了一个闭包! 所以,举个例子:

    * + 1 # 等价于 -> $a { $a + 1 }
    * + * # 等价于 -> $a, $b { $a + $b }
    

    一个星号占一个坑儿。

    @list.grep(* > 10)                  # 返回 @list 数组中所有大于 10 的元素
    @list.grep( -> $ele { $ele > 10 } ) # 同上, 使用显式的闭包
    @list.grep: -> $ele { $ele > 10 }   # 同上, 使用冒号调用方式
    @list.grep: * > 10                  # 同上
    @list.grep: { $_ > 10 }             # 同上
    
    
    @list.map(* + *)                    # 返回 @list 数组中每两个元素的和
    @list.map( -> $a, $b { $a+$b } )    # 同上, 使用显式的闭包
    

    如果给 @a[ ] 的方括号里面传递一个闭包, 它会把 @a 数组的元素个数作为参数传递并计算!

    数组的最后一个元素

    my @a =  1,22,33,11;
    say @a[*-1];
    say @a[->$a {$a-1}]; # $a  即为数组@a 的元素个数
    

    数组的倒数第二个元素

    say @a[*-2];
    say @a[->$a {$a-2}];
    

    所以 @a[*/2]@a 数组的中间元素, @a[1..*-2]@a 中不包含首尾元素的其它元素。
    1, 1, * + * ... * 是一个无限列表, * + * 是后来值的生成规则, 最后一个 * 表示没有终结测试。

    把闭包存储在标量中

    my $a = -> $b { $b + 1 }
    $a(3) # 4
    
    my @add_by_one = @list.map($a); # 对 @list 中的每个元素加 1
    

    Perl 6 的列表求值是惰性的,只要你不要求最后一个元素, 无限列表是没问题的。使用绑定 (:=) 操作符把列表赋值给变量:

    my @fib := 1, 1, * + * ... *
    

    如果我稍后要 @fib[40] 的值, 会生成足够多的元素以获取数组的第 41 个元素,那些生成的元素会被记忆。尽管未来, 如果列表未绑定给变量, 之前的值会被忘记, 大部分 Perl 6 列表函数能作用并生成惰性列表。

    @a.map@a.grep 都生成「惰性列表」, 即使 @a 不是惰性的。
    @fib.grep(* %% 2) 是一个偶数惰性列表,例如 @fib Z @a 生成一个惰性列表: @fib[0], @a[0], @fib[1], @a[1] ...
    给 for 循环传递一个无限列表是没问题的, 它会循环直到停止。

    但是要注意不能使用嵌套的闭包:

    my @a = 1 .. 10;
    my @b = @a.map: { * ** 2 }
    ===SORRY!=== Error while compiling:
    Malformed double closure; WhateverCode is already a closure without curlies, so either remove the curlies or use valid parameter syntax instead of * at line 2 ------> <BOL><HERE><EOL>
    

    注意上面的错误信息, 说的已经很明显了, WhateverCode 已经是一个不带花括号的闭包了, 所以要么移除花括号, 要么使用合法的参数语法代替 * 号, 提示信息足够清楚了。所以, 按照提示:

    方法一:使用 $_ 代替 *

    > my @b = @a.map: { $_ ** 2 }
    [1 4 9 16 25 36 49 64 81 100]
    

    方法二:

    > my @b = @a.map:  * ** 2
    [1 4 9 16 25 36 49 64 81 100]
    

    方法三, 显式的使用 closure:

    > my @b = @a.map: -> $item { $item ** 2 }
    [1 4 9 16 25 36 49 64 81 100]
    

    相关文章

      网友评论

        本文标题:Perl 6 中的 Whatever

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