Perl 6 中的 Whatever

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]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容