声明
本章翻译仅用于 Raku 学习和研究, 请支持电子版或纸质版。
第十四章. Junctions 和集合
Junctions
A Junction
is a combination of values that is mostly indistinguishable from a single value. They have their roots in the math of quantum mechanics. You may have heard of Schrödinger’s cat, who is both dead and alive at the same time—an analogy that physicist used to show how ridiculous this all is. Well, the joke was on him.
Junction
是一组值,它们与单个值几乎无法区分。它们根植于于量子力学中的数学。你可能听说过薛定谔的猫,同一个时间它既死又活着 - 这个物理学家用来表示这一切是多么荒谬。那个笑话现在应在 Junction 身上了。
any
The first Junction
is the any
. This “any” is lowercase and is not related to the type Any
. It creates a value that can act like, well, any of the ones you gave it:
第一个 Junction
是 any
。这个“any”是小写的,与 Any
类型无关。它会创建一个值,就像你给它的任何一个一样:
my $first-junction = any( 1, 3, 7 );
You can make a Junction
from an Array
or any other Positional
:
您可以从数组或任何其他 Positional
创建一个 Junction
:
my $junction = any( @array ); # Array
my $junction = any( 1 .. 10 ); # Range
my $junction = any( 1 ... 10 ); # Sequence
Now you have a Junction
of three values. It will only ever have three values. You can’t take one away or add one. There’s no interface to extract them or count them. You’re not supposed to know—or even care—which values are in there. In fact, Junction
is the only builtin type that does not inherit from Any
:
现在你有一个三个值的 Junction
。它只会有三个值。你不能拿走一个或添加一个。没有接口可以提取它们或计算它们。你不应该知道 - 甚至关心 - 那里有哪些观。事实上, Junction
是唯一不从 Any
继承的内置类型:
% raku
To exit type 'exit' or '^D'
> my $first-junction = any( 1, 3, 7 );
any(1, 3, 7)
> $first-junction.^name
Junction
> $first-junction.^mro
((Junction) (Mu))
These are quite handy in complex conditions. Consider the annoying code you’ve had to write to test if a value is one of three possible numbers:
这些在复杂条件下非常方便。考虑一下你必须编写的令人讨厌的代码,以测试值是否是三个可能的数字之一:
my $n = any( 1, 3, 7 );
if $n == 1 || $n == 3 || $n == 7 {
put "n is one of those values";
}
Being clever with a Hash
doesn’t actually feel that much more clever:
聪明的 Hash
实际上并没有感觉更聪明:
my Int %hash = map { $_ => True }, (1, 3, 7);
if %hash{$n}:exists {
put "n is one of those values";
}
Not only does the Junction
equal any of those values, but it also equals all of them. This looks like a Block
that would never execute, but it does:
Junction
不仅等于这些值中的任何一个,而且它也等于所有这些值。这看起来像一个永远不会执行的 Block
,但它确实:
if $n == 1 && $n == 3 && $n == 7 {
put "n is all of those values";
}
A Junction
is much closer to how you’d probably describe this in speech:
Junction
更接近你可能在演讲中描述的方式:
if $n == any( 1, 3, 7 ) {
put "n is one of those values";
}
When you operate on a Junction
your code may distribute (autothread) that operation over all of its values to produce a Junction
of intermediate results. The first step might look like this:
当您在 Junction
上操作时,您的代码可以在其所有值上分配(自动读取)该操作,以产生中间结果的 Junction
。第一步可能如下所示:
if any( 1 == $n, 3 == $n, 7 == $n ) {
put "n is one of those values";
}
These evaluate to their Boolean values. If $n
is 3
one of the comparisons is True
:
这些求值为其布尔值。如果 $n
为 3
,则其中一个比较为 True
:
my $n = 3;
if any( False, True, False ) {
put "n is one of those values";
}
Any True
makes the entire junctive expression True
:
任何一个为 True
会使整个正则表达式为 True
:
my $n = 3;
if True {
put "n is one of those values";
}
You don’t have to define the Junction
in the condition. It might already be in a variable and ready for use:
您不必在条件中定义 Junction
。它可能已经在变量中并且可以使用:
my $any = any( 1, 3, 7 );
if $n == $any {
put "n is one of those values";
}
Here’s the beauty of Junction
s—you don’t have to know that you are using one. Here’s an Array
that has some “normal” values and one that is a Junction
:
这是 Junction
的美之所在 - 你不知不觉就在使用它。这是一个具有一些“正常”值的数组
,其中一个元素是 Junction
:
my @array = 5, any( 1, 7 ), 8, 9;
for @array -> $item {
put "$item was odd" unless $item %% 2;
}
The loop works with single “normal” values as well as Junction
s. Notice that the Junction
creates two lines of output. The stringification happened for each value:
在这个循环中,单个“正常”值以及 Junction
都起作用。请注意,Junction
创建了两行输出。每个值都发生了字符串化:
5 was odd
1 was odd
7 was odd
9 was odd
That multiple stringification could change in the future; it wasn’t this way when I started the book and it might change again. The .gist
on $item
prevents that:
多重字符串化可能会在未来发生变化; 当我开始写这本书时它不是这样的,它可能会再次改变。 $item
上的 .gist
可以防止多重字符串化:
my @array = 5, any( 1, 7 ), 8, 9;
for @array -> $item {
put "{$item.gist} was odd" unless $item %% 2;
}
5 was odd
any(1, 7) was odd
9 was odd
EXERCISE 14.1Make an any
Junction
of the prime numbers between 1 and 10 (so, 2, 3, 5, and 7). Use that Junction
to note which numbers from 1 to 10 are prime.
练习14.1使用 1 到 10 之间的素数制作一个 any
Junction
(因此,2,3,5和7)。使用该 Junction
来记录从1到10的哪些数字是素数。
There’s a symbolic notation for any
. The |
between values creates a Junction
. It looks similar to the ||
for the logical OR operator but is not related:
any
有一个象征性的符号。 值之间的 |
创建一个 Junction
。它看起来类似于 ||
对于逻辑OR运算符但不相关:
my $n = 3;
my $any = 1 | 3 | 7;
if $n == $any {
put "n is one of those values";
}
NOTE
Raku uses |
, &
, and ^
to create Junction
s. You might be used to these as numeric bit operators in other languages. You’ll find those now are called +|
, +^
, and +&
. That leading +
denotes the numeric flavor.
Raku 使用 |
,&
和 ^
来创建 Junction
。您可能习惯使用其他语言中的数字位运算符。您会发现现在称为 +|
,+^
和 +&
。前导 +
表示数字风味。
You can change Junction
s by affecting their values. Numerically adding to a Junction
adds to every value in it:
您可以通过影响其值来更改 Junction
。以数字方式添加到 Junction
会增加其中的每个值:
my $junction = any( 1, 3, 7 );
$junction += 1;
if $junction %% 2 {
put "{$junction.gist} is even";
}
The output shows that you were able to add one to each of the values:
输出显示您可以为每个值加一:
any(2, 4, 8) is even
This generally applies to all of the operations, and you can get quite creative with that. What if you add two any
Junction
s? Think about this for a minute before you read ahead to the output:
这通常适用于所有操作,您可以从中获得相当的创意。如果你将两个 any
Junction
相加会怎样?在继续读到输出之前,请考虑一分钟:
my $any-any = any( 6, 7 ) + any( 9, 11 )
put "Result is $any-any";
Now figure out what this means:
现在弄清楚这意味着什么:
Result is any(any(15, 17), any(16, 18))
That’s an any
of any
s! Suppose you wanted to check if that value was less than 17
. This virtual series of steps finds the answer:
这是一个里面含有 any
的 any
。假设您要检查该值是否小于 17
。此虚拟系列步骤可找到答案:
$any-any < 17
any( any(15, 17), any(16, 18) ) < 17
any( any(15, 17) < 17, any(16, 18) < 17 )
any( any(15 < 17, 17 < 17), any(16 < 17, 18 < 17) )
any( any(True,False), any(True, False) )
any( True, True )
True
This has the same effect as any( 15, 16, 17, 18 )
but with more steps involved. That’s a warning. If you aren’t careful you could have an explosion of Junction
s in there.
这与任何 any( 15, 16, 17, 18 )
具有相同的效果,但涉及更多步骤。这是一个警告。如果你不小心,你可能会爆发 Junction
。
all
An all
Junction
requires that each of its values satisfy the condition or method you apply:
all
Junction
要求其每个值满足你应用的条件或方法:
my $all-of-u = all( <Danaus Bicyclus Amauris> );
if $all-of-u.contains: 'u' {
put "Everyone has a u";
}
Perhaps you want to check that all of the values are a particular type. In this example there’s a Str
in @mixed-types
:
也许您想要检查所有值是否为特定类型。在这个例子中,@mixed-types
中有一个 Str
:
my @mixed-types = <1 2/3 4+8i Hello>;
if all(@mixed-types) ~~ Numeric {
put "Every value is a numeric thingy";
}
else {
put "One of these things is not like the others";
}
The Hello
cannot become a number, so a smart match against Numeric
fails. The entire Junction
evaluates to False
because one of its values does.
Hello
无法成为数字,因此与 Numeric
的智能匹配失败。整个 Junction
的计算结果为 False
,因为其值之一是 False
。
The all
is much easier to read than almost anything else that might accomplish the task. Comparing the result of a .grep
to the original number of elements in the source is too much typing:
all
都比其他任何可能完成任务的东西更容易阅读。将 .grep
的结果与源中元素的原始数量进行比较打字太多了:
if @mixed-types.grep( * !~~ Numeric ) == +@mixed-types {
put "One of these things is not a number";
}
You can create an all
Junction
with the &
:
您可以使用 &
创建一个 all
Junction
:
my $all-of-u = 'Danaus' & 'Bicyclus' & 'Amauris';
if $all-of-u.contains: 'u' {
put "Everyone has a u";
}
EXERCISE 14.2Using all
, test if all the numbers you specify on the command line are prime.
练习14.2使用 all
,测试你在命令行中指定的所有数字是否为素数。
one
The one
Junction
allows only one of its values to satisfy its condition. If more than one would make the condition True
the Junction
fails:
one
Junction
只允许其中一个值满足其条件。如果不止一个条件为 True
,则 Junction
失败:
put one( 1, 2, 3 ) %% 2 ?? # True
"Exactly one is even"
!!
"More (or less) than one is even";
If more than one thing in the one
is True
, then the entire Junction
is False
:
如果 one
中的多个是 True
,则整个 Junction
为 False
:
one( True, True, False ).so # False;
You can create a one
Junction
with the ^
:
您可以使用 ^
创建 one
Junction
:
put ( 1 ^ 2 ^ 3 ) %% 2 ?? # True
"Exactly one is even"
!!
"More (or less) than one is even";
none
The none
Junction
requires that all of the values cause its condition to be False
. That means that everything should evaluate to False
. There’s no symbolic operator version for this type:
none
Junction
要求所有值都导致其条件为 False
。这意味着一切都应该计算为 False
。该类型没有符号运算符版本:
put none( 1, 2, 3 ) %% 5 ?? True
"Exactly one is even"
!!
"More (or less) than one is even";
EXERCISE 14.3Use none
to test if no numbers you specify on the command line are prime. Once you’ve done that, use none
to test that some numbers in an Array
are prime.
练习14.3如果您在命令行中指定的数字不是素数,则使用 none
来测试。完成后,使用 none
测试,数组中的某些数字是素数。
Some Junctive Tricks
Junction
s aren’t designed for introspection and you aren’t supposed to care if the value is in a Junction
. This isn’t too hard to work around, though.
Junction
不是为内省而设计的,你不应该关心这个值是否在一个 Junction
中。不过,这并不难解决。
You can apply an operation to each value with a hyperoperator (Chapter 6). This one adds one to each element:
您可以使用超运算符对每个值应用操作(第6章)。这个为每个元素加一:
my $junction = any( 1, -3, 7 );
say $junction »+« 1;
The new Junction
has new values. You still aren’t supposed to know what these new values are, but something must know what they are to add one to them:
新的 Junction
有新的值。你仍然不应该知道这些新值是什么,但肯定知道它们加一之后是什么:
any(2, -2, 8)
The »+«
surrounds the +
because that’s an infix operator and expects arguments on either side of it. You can call a method (a postfix thing) on each item:
»+«
包围着 +
,因为这是一个中缀运算符,并期望它的任何一边都有参数。你可以在每一项上调用一个方法(一个后缀的东西):
$junction>>.is-prime; # any((True), (False), (False))
That method could be .take
, which adds the value to the list that gather
makes. This means that the values can escape the Junction
:
该方法可以是 .take
,它将值添加到 gather
制作的列表中。这意味着值可以避开 Junction
:
my $junction = any( 1, -3, 7 );
my @values = gather $junction».take;
put "Values are @values[]";
Don’t make a habit of this because it’s slightly naughty. You aren’t supposed to know how to do this.
不要养成这个习惯,因为它有点顽皮。你不应该知道如何做到这一点。
A Junction
is handy to allow a combination of types in a type constraint. Use the subset
of the Junction
of both types as the constraint:
Junction
可以方便地在类型约束中组合类型。使用两种类型的 Junction
的 subset
子集作为约束:
subset IntInf where Int | Inf;
sub add ( IntInf $a, IntInf $b ) { $a + $b }
put add( 1, 3 ); # 4
put add( 1, Inf ); # Inf
EXERCISE 14.4Rewrite the number-guessing game from Chapter 2 to have three secret numbers. This time the hints are a bit trickier. If any of the secret numbers are smaller than the guess, tell the person that one or some of them are smaller. Do the same with larger numbers. For a single guess, some numbers may be larger and others smaller. When the person has guessed all of the secret numbers, end the game. Is it easier to use given-when
or if
? Using all of the Junction
types may make this easier.
练习14.4 重写第2章的猜数游戏,得到三个秘密数字。这次提示有点棘手。如果任何一个秘密数字小于猜测的值,请告诉该人其中一个或一些较小。用更大的数字做同样的事情。对于单个猜测,一些数字可能更大而其他数字更小。当该人猜出所有秘密数字时,结束游戏。是否更容易使用 - given-when
或 if
?使用所有 Junction
类型可以使这更容易。
Table 14-1 provides a summary of Junction
s.
Junction | Operator | Description |
---|---|---|
any |
| | Any of the values will work. |
all |
& | All of the values must work. |
one |
^ | Exactly one of the values will work. |
none |
None of the values can work. |
Sets
Set
s are another way to combine values. They aren’t like Junction
s, where several values can act like one value; they combine zero or more values as its own thingy that you can inspect. Each value can be in the Set
only once (although there are weighted Set
s I won’t write about), and once created the Set
is fixed.
Set
是另一种组合值的方法。它们不像 Junction
,其中几个值可以像一个值一样;它们将零个或多个值组合为您可以检查的自己的东西。每个值都只可以在 Set
中一次(虽然有加权集合我不会写),一旦创建了集合就固定了。
注意
A Set
is a type of Associative
, so many of the things you already know about those work on Set
s.
You can create a Set
with a routine or a coercer. Each thingy in the List
is a member of the Set
. These are the same:
您可以使用例程或强制器创建集合
。 列表
中的每个东西都是 集合
成员。这些都是一样的:
set( 1, 2, 3 )
(1, 2, 3).Set
You can store any combination or mixture of thingys, including type objects:
您可以存储任何组合或混合物,包括类型对象:
set( <♠ ♣ ♥ ♦> )
set( Int, 3, Inf, 'Hamadryas', $(1,2,3) )
A Set
stores a thingy only once. It’s either in the Set
or it isn’t, so it doesn’t need duplicates:
集合
只存储一次东西。它可以在集合
中,也可以不在,因此不需要重复:
put set( 1, 2, 3 ).elems; # 3
put set( 1, 2, 2, 3, 3, 3 ).elems; # 3
You can check that a value is in the Set
with the (elem)
operator:
您可以使用(elem)
运算符检查集合
中的值:
my $set = <♠ ♣ ♥ ♦>.Set;
put 'Number is in the set' if '♥' (elem) $set;
There’s also the fancy Unicode ∈
operator that tests if the thingy is in the set (or is a “member” of the set):
还有花哨的 Unicode ∈
运算符,它测试东西是否在集合中(或者是集合的“成员”):
put 'Number is in the set' if '♥' ∈ $set;
These operators know that they need Set
s, so they coerce what you give them:
这些运算符知道他们需要集合
,所以他们强迫你给他们的东西:
put 'Number is in the set' if '♥' ∈ <♠ ♣ ♥ ♦>;
With that operator the Set
is the second operand. The order of operands is reversed for the (cont)
and ∋
operators. Now you test that a Set
contains an element:
使用该运算符,集合
是第二个操作数。对于(cont)
和 ∋
运算符,操作数的顺序是相反的。现在,你测试一个集合
包含一个元素:
put 'Number is in the set' if $set (cont) '♥';
put 'Number is in the set' if $set ∋ '♥';
You can test that a thingy is not a member of a Set
by either prefacing the ASCII operator with a !
or using the Unicode version with the line through it:
你可以通过在 ASCII 操作符前面添加一个 !
来测试一个东西不是一个集合
的成员!或者使用带有直线穿过的Unicode 版本:
put 'Number is not in the set' if '♥' !(elem) $set;
put 'Number is not in the set' if '♥' ∉ $set;
put 'Number is not in the set' if $set !(cont) '♥';
put 'Number is not in the set' if $set ∌ '♥';
You can compare Set
s to other Set
s. Another Set
that contains only some of the members is a subset. A “strict” or “proper” subset is one that is smaller than the Set
and only contains elements of the Set
. Another way to say that is a proper subset is always smaller. The (<)
(or ⊂
) operator does that with the opening of the angle toward the larger Set
. The order of elements does not matter:
您可以将集合
与其他集合
进行比较。另一个仅包含部分成员的集合
是子集。 “严格”或“适当”子集是小于集合
的子集,仅包含集合
的元素。另一种说法是适当的子集总是更小。 (<)
(或 ⊂
)运算符通过朝向较大的集合
开放角度来执行此操作。元素的顺序无关紧要:
set( 1, 3 ) (<) set( 1, 3, 7 ); # True
set( 3, 1, 7 ) (<) set( 1, 3 ); # False (not smaller)
set( 5, 7 ) ⊂ set( 1, 3, 7 ); # False (5 not in set)
A !
in front of the ASCII operator or a line through the Unicode operator negates the condition:
ASCII 运算符前面的 !
或通过带有直线穿过的 Unicode 运算符否定条件:
set( 1, 3 ) !(<) set( 1, 3, 7 ); # False
set( 3, 1, 7 ) !(<) set( 1, 3 ); # True
set( 5, 7 ) ⊄ set( 1, 3, 7 ); # True
Use the (>=)
or ⊆
operators if you want to allow the Set
s to be the same size:
如果要允许集合
具有相同的大小,请使用((>=)
或 ⊆
运算符:
set( 1, 3 ) (<=) set( 1, 3, 7 ); # True
set( 1, 3, 7 ) (<=) set( 1, 3, 7 ); # True
set( 3, 1, 7 ) ⊆ set( 1, 3 ); # False (subset has 7)
Negate those in the same way:
以同样的方式否定这些:
set( 1, 3 ) !(<=) set( 1, 3, 7 ); # False
set( 1, 3, 7 ) !(<=) set( 1, 3, 7 ); # False
set( 3, 1, 7 ) ⊈ set( 1, 3 ); # True
You can also have supersets. That’s just a matter of which one you allow to be the larger Set
. So far you’ve seen examples where you expected the larger Set
to be to the right of the operator. Flip those operators around so you expect the larger Set
to be on the left:
你也可以有超集。这只是一个你允许成为更大的集合
的问题。到目前为止,您已经看到过您希望较大的集合
位于运算符右侧的示例。翻转这些运算符,以便您希望较大的集合
位于左侧:
set( 3, 1, 7 ) (>) set( 1, 3 ); # False (not smaller)
set( 3, 1, 7 ) ⊃ set( 1, 3 ); # False (not smaller)
set( 3, 1, 7 ) !(>) set( 1, 3 ); # True
set( 3, 1, 7 ) ⊅ set( 1, 3 ); # True
Table 14-2 shows the rest of the Set
operations.
Operation | Operator | Code number | Description |
---|---|---|---|
$a (elem) $set |
∈ | U+2208 | $a is a member of $set |
$a !(elem) $set |
∉ | U+2209 | $a is not a member of $set |
$set (cont) $a |
∋ | U+220B | $set contains $a |
$set !(cont) $a |
∌ | U+220C | $set does not contain $a |
$set-a (<) $set-b |
⊂ | U+2282 | $set-a is a proper subset of $set-b |
$set-a !(<) $set-b! |
⊄ | U+2284 | $set-a is not a proper subset of $set-b |
$set-a (<=) $set-b! |
⊆ | U+2286 | $set-a is the same or is a subset of $set-b |
$set-a !(<=) $set-b! |
⊈ | U+2288 | $set-a is not the same and isn’t a subset of $set-b |
$set-a (>) $set-b! |
⊃ | U+2283 | $set-a is a proper superset of $set-b |
$set-a !(>) $set-b! |
⊅ | U+2285 | $set-a is not a proper superset of $set-b |
$set-a (>=) $set-b! |
⊇ | U+2287 | $set-a is the same or is a superset of $set-b |
$set-a !(>=) $set-b! |
⊉ | U+2289 | $set-a is not the same and isn’t a superset of $set-b |
EXERCISE 14.5In Chapter 9 you used a Map
to check allowed values. Do the same thing with a List
. Prompt for some starting colors (perhaps all on one line that you break up into elements). Continue to prompt for colors and report if the color was one of the initial colors. Can you do this ignoring case?
练习14.5 在第9章中,您使用了 Map
来检查允许的值。用列表
做同样的事情。提示一些起始颜色(可能在一行中分解为元素)。继续提示颜色并报告颜色是否为初始颜色之一。你能忽略这个案子吗?
Set Operations
You can operate on two Set
s to create new Set
s. A union is a combination of two Set
s. Each element still shows up only once:
您可以操作两个集合
来创建新集合
。并集是两个集合
的组合。每个元素仍然只显示一次:
set(1,2) (|) set(3,7); # set(1 2 3 7)
set(1,2) ∪ set(3,7); # set(1 2 3 7)
The intersection makes the Set
of the elements they have in common:
交集制作一个它们共有的元素集合
:
set(1,3) (&) set(3,7); # set(3)
set(1,2) ∩ set(3,7); # set()
The set difference creates a Set
made up of the elements from the first Set
that aren’t in the second. The ∖
isn’t the ASCII backslash; it’s (U+2216 SET MINUS):
差集创建一个由第一个集合
中不在第二个集合
中的元素组成的集合
。 ∖
不是 ASCII 反斜杠; 它是(U+2216 SET MINUS):
set( <a b> ) (-) set( <b c> ); # set(a)
set( <A b> ) ∖ set( <x y> ); # set(A b)
The symmetric set difference does a similar thing in both directions. It creates a Set
containing all the elements of either Set
that don’t show up in the other:
对称差集在两个方向上做类似的事情。它创建一个集合
,其中包含任何集合
中未显示在另一个集合
中的所有元素:
set( <a b> ) (^) set( <b c> ); # set(a c)
set( <A b> ) ⊖ set( <x y> ); # set(A b x y)
These operations are summarized in Table 14-3.
Operation | Operator | Code number | Description |
---|---|---|---|
(|) | ∪ | U+222A | Union (combination) |
(&) | ∩ | U+2229 | Intersection (overlap) |
(-) | ∖ | U+2216 | Set difference |
(^) | ⊖ | U+2296 | Symmetric set difference |
EXERCISE 14.6Create two Set
s of 10 numbers between 1 and 50. Find their intersection and union.
练习14.6在1到50之间创建两组10个数字。找到它们的交集和联合。
Summary
Junction
s make several values pretend to be a single value, in such a way that you can’t tell which value it is or how many values there are. You create the Junction
in a way that makes the values all work together or separately. A Set
also combines values but lets you look inside to see what those values are. You can combine Set
s in various ways to create new ones. This is handy to tell what’s in, out, or common.
Junction
使多个值假装成单个值,这样你就无法判断它是哪个值或者有多少个值。您可以通过使值全部一起或单独工作的方式创建 Junction
。 集合
还可以组合值,但是让您查看这些值是什么。您可以通过各种方式组合集合
来创建新的集合
。这可以很方便地分辨出进出的内容。