探索Perl6中的函数式编程:懒列表、序列运算符与柯里化
立即解锁
发布时间: 2025-08-22 00:57:37 阅读量: 1 订阅数: 4 


Perl 6编程入门与实践
# 探索 Perl 6 中的函数式编程:懒列表、序列运算符与柯里化
## 1 懒列表与序列运算符
### 1.1 序列运算符基础
Perl 中提供了 `...` 序列运算符来构建懒列表。例如:
```perl
my $lazylist := (0, 1 ... 200);
say $lazylist[42]; # -> 42
```
上述代码生成了一个 0 到 200 之间连续整数的懒列表。Perl 6 编译器可能会也可能不会分配部分数字(取决于具体实现),但不需要立即生成完整列表。若程序尝试使用尚未生成的数字,这些数字会在需要时被创建并提供。
若要生成连续整数,可简化懒列表定义:
```perl
my $lazylist := (0 ... 200);
```
### 1.2 序列运算符与数组
若将序列赋值给数组,会立即生成序列的所有值,因为数组赋值是急切的(非懒加载)。不过,在赋值给数组时可使用 `lazy` 内置函数强制实现懒加载:
```perl
my @lazyarray = lazy 1 ... 200; # -> [...]
say @lazyarray.elems; # -> Cannot .elems a lazy list
say @lazyarray[199]; # -> 200
say @lazyarray[200]; # -> (Any)
say @lazyarray.elems; # -> 200
```
这里的 `@lazylist` 数组最初是懒加载的。评估数组最后一个元素之后的一项会强制 Perl 实际生成完整数组(此时数组不再是懒加载的)。之后,无法再生成更多元素,`.elems` 保持为 200(除非实际为第 200 个元素之后的元素赋值)。
### 1.3 不同类型序列的生成
- **连续整数序列**:给定列表的第一个和最后一个整数,序列运算符将生成这两个整数之间的连续整数列表。
- **算术序列**:若提供两个初始项隐式定义步长,将生成算术序列:
```perl
my $odds = (1, 3 ... 15); # (1 3 5 7 9 11 13 15)
my $evens = (0, 2 ... 42); # (0 2 4 6 8 ... 40 42)
```
- **几何序列**:当提供三个呈几何级数的初始数字时,序列运算符将生成几何序列,例如生成 2 的幂:
```perl
say (1, 2, 4 ... 32); # -> (1 2 4 8 16 32)
```
- **非整数序列**:序列运算符也可用于生成非整数数字:
```perl
say (1, 1.1 ... 2);
# (1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2)
```
- **递减序列**:与 `..` 范围运算符不同,序列运算符还可以递减计数:
```perl
say (10 ... 1); # (10 9 8 7 6 5 4 3 2 1)
```
### 1.4 无限列表
懒列表的一大优点是,由于项的评估被推迟,它们可以是无限的,而不会消耗计算机的无限资源:
```perl
my $evens = (0, 2 ... Inf); # (...)
say $evens[18..21]; # -> (36 38 40 42)
```
`Inf` 操作数是 `∞` 无穷符号的所谓“Texas”或 ASCII 等效形式,上述代码也可写成:
```perl
my $evens = (0, 2 ... ∞);
say $evens[21]; # -> 42
```
不过,指示无限懒列表最常见的方法是使用 `*` 任意参数:
```perl
my $evens = (0, 2 ... *);
say $evens[21]; # -> 42
```
### 1.5 使用显式生成器
序列运算符 `...` 是生成懒列表的强大工具。给定一个数字,它会从该数字开始递增计数(除非序列的结束数字较小,此时会递减计数);给定两个数字开始序列,它会将其视为算术序列,通过将前两个数字的差值加到最后生成的数字上来生成下一个数字;给定三个数字,它会检查它们是否代表算术或几何序列的开始,并继续生成序列。
但许多有趣的序列既不是算术序列也不是几何序列。若一个项可以从前面的项推导出来,仍可使用序列运算符生成这些序列。为此,需要显式提供代码块来生成序列中的下一个数字。例如,奇数列表也可使用生成器生成:
```perl
say (1, { $_ + 2 } ... 11); # -> (1 3 5 7 9 11)
```
还可以用另一种方式定义阶乘函数:
```perl
my $a;
my @fact = $a = 1, {$_ * $a++} ... *;
say @fact[0..8]; # -> (1 1 2 6 24 120 720 5040 40320)
```
或者更易读的形式:
```perl
my @fact = 1, { state $a = 1; $_ * $a++} ... *;
say @fact[0..8]; # -> (1 1 2 6 24 120 720 5040 40320)
```
这种方法在重复使用时比之前的方法更高效,因为它会自动在懒数组中缓存之前计算的值。
同样,可以构造斐波那契数列的懒无限列表:
```perl
my @fibo = 0, 1, -> $a, $b { $a + $b } ... *;
say @fibo[0..10]; # -> (0 1 1 2 3 5 8 13 21 34 55)
```
使用 `*` 任意占位符参数可以更简洁地重写:
```perl
my @fibo = 0, 1, * + * ... *;
say @fibo[^10]; # -> (0 1 1 2 3 5 8 13 21 34)
```
与阶乘函数一样,这种实现比之前的实现更高效,因为计算的值会缓存在懒数组中。
### 1.6 显式生成器的注意事项
使用带有显式生成器的序列运算符时,有一个小注意事项:结束值(上限)必须是生成的数字之一,列表才会在该值处停止。否则,将构建一个无限列表:
```perl
my $nums = (0, { $_ + 4 } ... 10);
say $nums[0..5]; # -> (0 4 8 12 16 20)
```
在这种情况下,生成器“跳过了终点”(超过了 10),列表实际上是无限的。这通常在计算机资源方面不是问题,因为它是一个懒无限列表,但如果期望列表不超过 10,这可能是一个错误。
在难以预测终点应该是什么的情况下,可以定义另一个代码块来测试序列是否应该停止或继续。若该代码块返回真值,序列将停止。例如,计算小于 100 的斐波那契数:
```perl
my @fibo = 0, 1, -> $a, $b { $a + $b } ... -> $c { $c > 100}
# [0 1 1 2 3 5 8 13 21 34 55 89 144]
```
这虽然停止了数字序列,但不是我们想要的结果。我们希望在小于 100 的最后一个斐波那契数处停止。可以通过稍微改变语法来实现:
```perl
my @fibo = 0, 1, -> $a, $b { $a + $b } ...^ -> $c
```
0
0
复制全文
相关推荐










