如何在 Perl 6 中对列表的所有元素进行功能性处理?

How to do something functionally with all the elements of a list in Perl 6?

例如我有一个 Array 数字:

> my @a = ^5
[0 1 2 3 4]

我想打印他们的方块。我可以使用 map,但它也会 return 修改后的 List(这五个 True),我不想要:

> @a.map({ put $_**2 })
0
1
4
9
16
(True True True True True)

我找到的唯一方法是使用 hyper >>:

> @a>>.&{ put $_**2 }
0
1
4
9
16

但是

那么正确的做法是什么?

P.S。当然我可以用map然后put结果:

.put for @a.map: {$_**2 }

但这不是我想要的。

首先,使用 >>. 不是 一个好主意,因为执行操作的 order , 保证按任何特定顺序排列。

您可以使用 WhateverCode:

.say for @a.map: * ** 2

我个人认为最易读。或者,您可以使用占位符变量:

.say for @a.map: { $^value ** 2 }

或者完全写出来:

.say for @a.map: { $_ ** 2 }

但显然你不希望那样。为什么?

这个答案是对其他答案的补充。我会假设您和其他读者对避免纯粹主义的 FPish 方法的某些可能性感兴趣。

my @a = ^5;            # @a bound to an Array
@a.map(* ** 2)  .put;  # 0 1 4 9 16
@a.put;                # 0 1 2 3 4       <- Array unchanged

@a.map(* **= 2) .put;  # 0 1 4 9 16
@a.put;                # 0 1 4 9 16      <- Array changed

@a := @a.List;         # rebind @a to a List
@a.map(* ** 2)  .put;  # 0 1 16 81 256   <- Same as if Array
@a.put;                # 0 1 4 9 16      <- List unchanged

@a.map(* **= 2) .put;  # Cannot assign to an immutable value

Array 示例展示了您如何可以 选择性地改变 en passant。 (这样做是使用 op= 形式的二进制(中缀)操作,而不仅仅是 op,但如果这令人困惑,请注意效果并忽略语法。)

En passant 突变的 "evil" 副作用比单纯的 .put 多得多,因此如果有人调用它,纯粹主义者会有点 惊恐 "functional" 风格。当然不是我。 :)

List 示例则朝另一个方向发展,朝着不变性迈进。 Lists 是不可变的 wannabes.

同样,那些喜欢 FP 的人可能会称赞他们的不变性。纯粹主义者会诋毁崇拜者的一面。 Perl 用户可能会看到这两种方式。

不可变的崇拜者

A List 完全或大部分不可变。

您永远无法压入或弹出 List

它的长度总是不可变的。

基本值列表是完全不可变的:

my $really-immutable-list = (1,2,'foo', 1.5, (3,4,5));

例外情况是,如果列表的元素本身是可变的,那么您可以对其进行变异,从而改变 List:

my $only-shallowly-immutable-list = (1,2,'foo', 1.5, [3,4,5]);
$only-shallowly-immutable-list[4][1] = 9;
say $only-shallowly-immutable-list; # (1 2 foo 1.5 [3 9 5])

更巧妙:

class c { has $.foo; method waldo { $!foo = 42 } }
my $bar  = c.new;
my $list = ($bar, 2);
say $list;   # (c.new(foo => Any) 2)
$bar.waldo; 
say $list;   # (c.new(foo => 42) 2)

使用 map 可以很好地实现此目的,因为在接收器上下文中它不会生成结果列表。在 REPL 中,需要 map 的结果,这就是产生它的原因。但是在这样的情况下:

@a.map({ put $_**2 });
say "That's all, folks";

那么 map 的结果就不需要了,因此不会收集任何结果列表。如果想真正说明这一点,可以这样写:

sink @a.map({ put $_**2 })

请注意例程中的最后一条语句被视为隐式 return 值。使用 --> Nil 足以确保最终的 map 位于接收器上下文中。

sub put-squares(@a --> Nil) {
    @a.map({ put $_**2 })
}