如何在 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
但是
- 语法有点笨拙(可能我没有按预期那样做)
- 如果我不希望它与
hyper
一起使用怎么办?
那么正确的做法是什么?
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
示例则朝另一个方向发展,朝着不变性迈进。 List
s 是不可变的 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 })
}
例如我有一个 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
但是
- 语法有点笨拙(可能我没有按预期那样做)
- 如果我不希望它与
hyper
一起使用怎么办?
那么正确的做法是什么?
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
示例则朝另一个方向发展,朝着不变性迈进。 List
s 是不可变的 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 })
}