pushappend 的表现不同, push 一次只添加单个参数到列表末端, append 一次可以添加多个参数。

  1. use v6;
  2. my @d = ( [ 1 .. 3 ] );
  3. @d.push( [ 4 .. 6 ] );
  4. @d.push( [ 7 .. 9 ] );
  5. for @d -> $r {
  6. say "$r[]";
  7. }
  8. # 1
  9. # 2
  10. # 3
  11. # 4 5 6
  12. # 7 8 9
  13. for @d -> $r { say $r.WHAT() }
  14. # (Int)
  15. # (Int)
  16. # (Int)
  17. # (Array) 整个数组作为单个参数
  18. # (Array)
  19. say @d.perl;
  20. # [1, 2, 3, [4, 5, 6], [7, 8, 9]]

使用 append 一次能追加多个元素:

  1. use v6;
  2. my @d = ( [ 1 .. 3 ] );
  3. @d.append( [ 4 .. 6 ] );
  4. @d.append( [ 7 .. 9 ] );
  5. for @d -> $item {
  6. say "$item[]";
  7. }
  8. # 打印 1n2n3n4n5n6n7n8n9
  9. # [1, 2, 3, 4, 5, 6, 7, 8, 9]

这跟The single argument rule有关。

设计大纲


Perl 6 提供了很多跟列表相关的特性, 包括 eager, lazy, 并行计算, 还有紧凑型阵列和多维数组存储。

Sequences vs. Lists


在 Perl 6 中, 我们使用项 sequence 来来指某个东西, 当需要的时候产生一个值的序列。即序列是惰性的。注意, 只能要求生成一次。我们使用项list来指能保存值的东西。

  1. (1, 2, 3) # 列表, 最简单的 list
  2. [1, 2, 3] # 数组, Scalar 容器的列表
  3. |(1, 2) # a Slip, 一个展开到周围列表中的列表
  4. $*IN.lines # a Seq, 一个可以被连续处理的序列
  5. (^1000).race # a HyperSeq, 一个可以并行处理的序列

The single argument rule


在 Perl 中 @ 符号标示着 “这些”(these), 而 $符号标示着 “这个”(the)。这种复数/单数的明显差别出现在语言中的各种地方, Perl 中的很多便捷就是来源于这个特点。展平(Flattening)就是 @-like 的东西会在特定上下文中自动把它的值并入周围的列表中。之前这在 Perl 中既功能强大又很有迷惑性。在直截了当的发起知名的”单个参数规则”之前, Perl 6 在发展中通过了几次跟 flattening 有关的模仿。

对单个参数规则最好的理解是通过 for循环迭代的次数。对于 for循环, 要迭代的东西总是被当作单个参数。因此有了单个参数规则这个名字。

  1. for 1, 2, 3 { } # 含有 3 个元素的列表, 3 次迭代
  2. for (1, 2, 3) { } # 含有 3 个元素的列表, 3 次迭代
  3. for [1, 2, 3] { } # 含有 3 个元素的数组(存放在 Scalar 中), 3次迭代
  4. for @a, @b { } # 含有 2 个元素的列表, 2 次迭代
  5. for (@a,) { } # 含有 1 个元素的列表, 1 次迭代
  6. for (@a) { } # 含有 @a.elems 个元素的列表, @a.elems 次迭代
  7. for @a { } # 含有 @a.elems 个元素的列表, @a.elems 次迭代

前两个是相同的, 因为圆括号事实上不构建列表, 而只是分组。是中缀操作符 infix:<,>组成的列表。第三个也执行了 3 次迭代, 因为在 Perl 6 中 [...] 构建了一个数组但是没有把它包裹进 Scalar 容器中。第四个会执行 2 次迭代, 因为参数是一个含有两个元素的列表, 而那两个元素恰好是数组, 它俩都有 @ 符号, 但是都没有导致展开。第 五 个同样, infix:<,> 很高兴地组成了只含一个元素的列表。

单个参数规则也考虑了 Scalar 容器。因此:

  1. for $(1, 2, 3) { } # Scalar 容器中的一个列表, 1 次迭代
  2. for $[1, 2, 3] { } # Scalar 容器中的一个数组, 1 次迭代
  3. for $@a { } # Scalar 容器中的一个数组, 1 次迭代
  1. > for $(1, 2, 3) -> $i { say $i.elems }
  2. 3
  3. > for $(1, 2, 3) -> $i { say $i }
  4. (1 2 3)
  5. > for $(1, 2, 3) -> $i { say $i.WHAT }
  6. (List)
  7. > for $(1, 2, 3) -> $i { say $i.perl }
  8. $(1, 2, 3)
  9. > for $[1, 2, 3] -> $i { say $i }
  10. [1 2 3]
  11. > for $[1, 2, 3] -> $i { say $i.perl }
  12. $[1, 2, 3]
  13. > for $[1, 2, 3] -> $i { say $i.WHAT }
  14. (Array)
  15. > for $[1, 2, 3] -> $i { say $i.elems }
  16. 3
  17. > my @a = 1,2,3
  18. [1 2 3]
  19. > for $@a -> $a { say $a.perl }
  20. $[1, 2, 3]

贯穿 Perl 6 语言, 单个参数规则(Single argument rule) 始终如一地被实现了。例如, 我们看 push 方法:

  1. @a.push: 1, 2, 3; # pushes 3 values to @a
  2. @a.push: [1, 2, 3]; # pushes 1 Array to @a
  3. @a.push: @b; # pushes 1 Array to @a
  4. @a.push: @b,; # same, trailing comma doesn't make > 1 argument
  5. @a.push: $(1, 2, 3); # pushes 1 value (a List) to @a
  6. @a.push: $[1, 2, 3]; # pushes 1 value (an Array) to @a

此外, 列表构造器(例如 infix:<,> 操作符) 和数组构造器([…]环缀)也遵守这个规则:

  1. [1, 2, 3] # Array of 3 elements
  2. [@a, @b] # Array of 2 elements
  3. [@a, 1..10] # Array of 2 elements
  4. [@a] # Array with the elements of @a copied into it
  5. [1..10] # Array with 10 elements
  6. [$@a] # Array with 1 element (@a)
  7. [@a,] # Array with 1 element (@a)
  8. [[1]] # Same as [1]
  9. [[1],] # Array with a single element that is [1]
  10. [$[1]] # Array with a single element that is [1]

所以, 要让最开始的那个例子工作, 使用:

  1. my @d = ( [ 1 .. 3 ], ); # [[1 2 3]]
  2. @d.push: [ 4 .. 6 ];
  3. @d.push: [ 7 .. 9 ];
  4. # [[1 2 3] [4 5 6] [7 8 9]]

或者

  1. my @d = ( $[ 1 .. 3 ]);
  2. @d.push: [ 4 ..6 ];
  3. @d.push: [ 7 ..9 ];

User-level Types


List


List 是不可变的, 可能是无限的, 值的列表。组成 List 最简单的一种方法是使用 infix:<,> 操作符:

  1. 1, 2, 3

List 可以被索引, 并且, 假设它是有限的, 也能询问列表中元素的个数:

  1. say (1, 2, 3)[1]; # 2
  2. say (1, 2, 3).elems; # 3

因为List是不可变的, 对它进行 push、pop、shift、unshift 或 splice 是不可能的。 reverserotate 操作会返回新的 Lists

虽然List自身是不可变的, 但是它包含的元素可以是可变的, 包括 Scalar 容器:

  1. my $a = 2;
  2. my $b = 4;
  3. ($a, $b)[0]++;
  4. ($a, $b)[1] *= 2;
  5. say $a; # 3
  6. say $b; # 8

List 中尝试给不可变值赋值会导致错误:

  1. (1, 2, 3)[0]++; # Dies: 不能给不可变值赋值

Slip


Slip 类型是 List 的一个子类。Slip 会把它的值并入周围的 List大专栏  S07strong> 中。

  1. (1, (2, 3), 4).elems # 3
  2. (1, slip(2, 3), 4).elems # 4

List 强转为 Slip 是可能的, 所以上面的也能写为:

  1. (1, (2, 3).Slip, 4).elems # 4

在不发生 flattening 的地方使用 Slip 是一种常见的获取 flattening 的方式:

  1. my @a = 1, 2, 3;
  2. my @b = 4, 5;
  3. .say for @a.Slip, @b.Slip; # 5 次迭代

这有点啰嗦, 使用 prefix:<|>来做 Slip 强转:

  1. my @a = 1, 2, 3;
  2. my @b = 4, 5;
  3. .say for |@a, |@b; # 5 次迭代

|在如下形式中也很有用:

  1. my @prefixed-values = 0, |@values;

这儿, 单个参数规则会使 @prefixed-values 拥有两个元素, 即 0 和 @values。

Slip 类型也可以用在 mapgather/take、和 lazy循环中。下面是一种 map能把多个值放进它的结果流里面的方法:

  1. my @a = 1, 2;
  2. say @a.map({ $_ xx 2 }).elems; # 2
  3. say @a.map({ |($_ xx 2) }).elems; # 4

因为 $_ xx 2 产生一个含有两个元素的列表(List)。

Array


ArrayList 的一个子类, 把赋值给数组的值放进 Scalar 容器中, 这意味着数组中的值可以被改变。Array 是 @-sigil 变量得到的默认类型。

  1. my @a = 1, 2, 3;
  2. say @a.WHAT; # (Array)
  3. @a[1]++; # Scalar 容器中的值可变
  4. say @a; # 1 3 3

如果没有 shape 属性, 数组会自动增长:

  1. my @a;
  2. @a[5] = 42;
  3. say @a.elems; # 6

Array支持 pushpopshiftunshiftsplice

给数组赋值默认是迫切的(eager), 并创建一组新的 Scalar 容器:

  1. my @a = 1, 2, 3;
  2. my @b = @a;
  3. @a[1]++;
  4. say @b; # 1, 2, 3

注意, [...] 数组构造器等价于创建然后再赋值给一个匿名数组。

Seq


Seq 是单次值生产者。大部分列表处理操作返回 Seq

  1. say (1, 2, 3).map(* + 1).^name; # Seq
  2. say (1, 2 Z 'a', 'b').^name; # Seq
  3. say (1, 1, * + * ... *).^name; # Seq
  4. say $*IN.lines.^name; # Seq

因为 Seq 默认不会记住它的值(values), 所以 Seq 只能被使用一次。例如, 如果存储了一个 Seq:

  1. my seq = (1, 2, 3).map(* + 1);

只有第一次迭代会有效, 之后再尝试迭代就会死, 因为值已经被用完了:

  1. for seq { .say } # 2n3n4n
  2. for seq { .say } # Dies: This Seq has already been iterated

这意味着你可以确信 for 循环迭代了文件的行:

  1. for open('data').lines {
  2. .say if /beer/;
  3. }

这不会把文件中的行保持在内存中。此外设立不会把所有行保持在内存中的处理管道也会很容易:

  1. my lines = open('products').lines;
  2. my beer = lines.grep(/beer/);
  3. my excited = beer.map(&uc);
  4. .say for excited;

然而, 任何重用 linesbeer、或excited 的尝试都会导致错误。这段程序在性能上等价于:

  1. .say for open('products').lines.grep(/beer/).map(&uc);

但是提供了一个给阶段命名的机会。注意使用 Scalar 变量代替也是可以的, 但是单个参数规则需要最终的循环必须为:

  1. .say for |$excited;

只要序列没有被标记为 lazy, 把 Seq 赋值给数组就会迫切的执行操作并把结果存到数组中。因此, 任何人这样写就不惊讶了:

  1. my @lines = open('products').lines;
  2. my @beer = @lines.grep(/beer/);
  3. my @excited = @beer.map(&uc);
  4. .say for @excited;

重用这些数组中的任何一个都没问题。当然, 该程序的内存表现完全不同, 并且它会较慢, 因为它创建了所有的额外的 Scalar 容器(导致额外的垃圾回收)和糟糕的位置引用。(我们不得不在程序的生命周期中多次谈论同一个字符串)。

偶尔, 要求 Seq 缓存自身也有用。这可以通过在Seq 身上调用 cache方法完成, 这从 Seq 得到一个惰性列表并返回它。之后再调用 cache方法会返回同样的惰性列表。注意, 第一次调用 cache方法会被算作消费了Seq, 所以如果之前已经发生了迭代它就不再有效, 而且之后任何在调用完 cache的迭代尝试都会失败。只有 .cache方法能被调用多于1 次。

Seq 不像 List 那样遵守 Positional role。 因此, Seq 不能被绑定给含有 @ 符号的变量:

  1. my @lines := $*IN.lines; # Dies

这样做的一个后果就是, 原生地, 你不能传递 Seq 作为绑定给@符号的参数:

  1. sub process(@data) {
  2. }
  3. process($*IN.lines);

这会极不方便。因此, 签名 binder(它实际使用 ::= 赋值语义而非 :=)会 spot 失败来绑定 @符号参数, 并检查参数是否遵守了 Positional role。 如果遵守了, 那么它会在参数上调用 cache 方法并绑定它的结果代替。

Iterable


SeqList 这俩, 还有 Perl 6 中的各种其它类型, 遵守 Iterable role。这个 role 的主要意图是获得一个 iterator方法。中级 Perl 6 用户很少会关心 iterator方法和它返回什么。

Iterable 的第二个目的是为了标记出会被按需展开的东西, 使用 flat方法或用在它们身上的函数。

  1. my @a = 1, 2, 3;
  2. my @b = 4, 5;
  3. for flat @a, @b { } # 5 次迭代
  4. say [flat @a, @b].elems; # 5 次迭代

flat 的另一用途是展开嵌套的列表结构。例如, Z(zip)操作符产生一个列表的列表:

  1. say (1, 2 Z 'a', 'b').perl; # ((1, "a"), (2, "b")).Seq

flat 能用于展开它们, 这在和使用带有多个参数的尖块 for 循环一块使用时很有用:

  1. for flat 1, 2 Z 'a', 'b' -> $num, $letter { }

注意 flat 也涉及 Scalar 容器, 所以:

  1. for flat $(1, 2) { }

将只会迭代一次。记住数组把所有东西都存放在 Scalar 容器中, 在数组身上调用 flat 总是和迭代数组自身相同。实际上, 在数组上调用 flat 返回的同一性。

S07的更多相关文章

  1. scrapy爬虫结果插入mysql数据库

    1.通过工具创建数据库scrapy

  2. PHP+ajaxfileupload与jcrop插件结合 完成头像上传

    昨天花了点时间整合了一下头像插件 东拼西凑的成果 先来看下效果

  3. PostgreSQL系列一:PostgreSQL简介与安装

    一.PostgreSQL简介     1.1 PostgreSQL概述             PostgreSQL数据库是目前功能最强大的开源数据库,支持丰富的数据类型(如JSON和JSONB类型. ...

  4. Tomcat双向Https验证搭建,亲自实现与主流浏览器、Android/iOS移动客户端超安全通信

    紧接着<Tomcat单向Https验证搭建,亲自实现与主流浏览器.Android/iOS移动客户端安全通信>,此处演示下更安全的双向Https认证的通信机制,为了清晰明了,以下进行单独描述 ...

  5. Tomcat单向Https验证搭建,亲自实现与主流浏览器、Android/iOS移动客户端安全通信

    众所周知,iOS9已经开始在联网方面默认强制使用Https替换原来的Http请求了,虽然Http和Https各有各的优势,但是总得来说,到了现在这个安全的信息时代,开发者已经离不开Https了. 网上 ...

  6. 数据库逆向框架代码生成工具:MyBatis Generator的使用

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration ...

  7. UGUI之Toggle使用

    Toggle对象是一个开关.一般用于单选,可以用Toggle制作背包的选项卡 在场景中创建Toggle按钮.看看他有Toggle组件

  8. groovy

    1.加载和卸载(每次都新建一个GroovyClassLoader 实例,然后使用新建的classloader去加载) try { GroovyClassLoader groovyClassLoader ...

  9. OAF_EO系列6 - Delete详解和实现(案例)

    2014-06-14 Created By BaoXinjian

随机推荐

  1. 吴裕雄--天生自然 pythonTensorFlow图形数据处理:将MNIST手写图片数据写入TFRecord文件

    import numpy as np import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_dat ...

  2. mysql SQL优化琐记之索引

    equal最好了,其次in,最后是range !=  <>  这类非操作尽量不用,它会转换为range.>都是范围查询 复合索引有左匹配原则,(clo_a,clo_b)相当建立了两个 ...

  3. 解决ubuntu16.04启动时长时间陷入紫屏

    今天我的ubuntu系统进不去,一启动就陷入紫屏的死循环中,重装了两遍系统还是一样进不去,后来上网查找了各种解决办法,网上都说是显卡的问题,我也不懂什么意思.试了几种方法,终于解决了这个问题,在这里记 ...

  4. flash插件的安装——网页视频无法播放

    1.从官网下载Adobe flash player 安装包.官方网址:https://get.adobe.com/cn/flashplayer/ 或者从我的网盘下载:链接:https://pan.ba ...

  5. 国内外主流的三维GIS软件

    我国GIS经过三十多年的发展,理论和技术日趋成熟,在传统二维GIS已不能满足应用需求的情况下,三维GIS应运而生,并成为GIS的重要发展方向之一.上世纪八十年代末以来,空间信息三维可视化技术成为业界研 ...

  6. jQ给下拉框绑定事件,为什么要绑定在框(select标签)上,而不是绑定在选项(option标签)上

    这是我在学习锋利的 jquery 书中 5.1.4 的代码时遇到的一个小问题,源代码如下: <head> <style type="text/css"> * ...

  7. shell制作bin文件

    #!/bin/bash curdir=`pwd` tardir=tardir if [ -e $tardir ];then echo $tardir is exist.... false! exit ...

  8. 史无前例的KDD 2014大会记

    2014大会记" title="史无前例的KDD 2014大会记"> 作者:蒋朦 微软亚洲研究院实习生 创造多项纪录的KDD 2014 ACM SIGKDD 国际会 ...

  9. C语言学习笔记之动态分配数组空间

    本文为原创文章,转载请标明出处 高级语言写多了,再拿起C语言的时候,自己已经傻了... C语言中数组大小不能为变量,即使这个变量已经被赋过值了,应该使用malloc方法进行数组空间动态分配. 如下: ...

  10. Nginx笔记总结四:Nginx连接PHP5.4

    location ~ .*\.(php)?${ expires -ls; try_file $uri=404; fastcgi_split_path_info ~(.+\.php)(/.+)$; in ...