class Junction is Mu { }

交点是零个或多个值的无序复合值。交点在许多操作中自动线程化,这意味着该操作对每个交点元素(也称为本征态)执行,结果是所有这些运算符的返回值的交点。

交点在布尔上下文中折叠成单个值,因此在条件、否定或通过so?前缀运算符明确强制转换为 Bool 时使用。这种折叠的语义取决于交点类型,该类型可以是allanyonenone

类型构造函数运算符如果...为真
allall&没有值评估为 False
anyany|至少有一个值评估为 True
oneone^恰好有一个值评估为 True
nonenone没有值评估为 True

如表格所示,为了创建交点,您使用表示Junction类型的命令后跟任何对象,或者在对象上调用.all.none.one

say so 3 == (1..30).one;         # OUTPUT: «True␤» 
say so ("a" ^ "b" ^ "c"eq "a"# OUTPUT: «True␤»

交点是非常特殊的对象。它们不属于Any层次结构,只是作为任何其他对象,是Mu的子类。这为大多数方法启用了一个特性:自动线程化。当交点绑定到不接受类型为Junction的值的代码对象的某个参数时,就会发生自动线程化。签名绑定会针对交点的每个值重复,而不是产生错误。

示例

my $j = 1|2;
if 3 == $j + 1 {
    say 'yes';
}

首先自动线程化infix:<+>运算符,生成交点2|3。下一个自动线程化步骤是infix:<==>,它生成False|Trueif条件在布尔上下文中评估交点,将其折叠为True。因此,代码打印yes\n

Junction的类型不会影响自动线程化后结果Junction中的项目数。例如,在Hash键查找期间使用oneJunction,仍然会导致具有多个项目的Junction。只有在布尔上下文中,Junction的类型才会发挥作用

my %h = :42foo, :70bar;
say    %h{one <foo meow>}:exists# OUTPUT: «one(True, False)␤» 
say so %h{one <foo meow>}:exists# OUTPUT: «True␤» 
say    %h{one <foo  bar>}:exists# OUTPUT: «one(True, True)␤» 
say so %h{one <foo  bar>}:exists# OUTPUT: «False␤»

请注意,编译器允许(但不要求)并行化自动线程化(以及一般的交点行为),因此通常错误地将交点自动线程化到具有副作用的代码上。

自动线程化意味着自动线程化的函数还将返回它通常会返回的值的交点。

(1..3).head2|3 ).say# OUTPUT: «any((1 2), (1 2 3))␤»

由于.head返回一个列表,因此自动线程化版本返回一个列表的Junction

'walking on sunshine'.contains'king'&'sun' ).say# OUTPUT: «all(True, True)␤»

同样,.contains返回一个布尔值;因此,自动线程化版本返回一个布尔值的Junction。一般来说,所有采用类型为T的参数并返回类型为TT的方法和例程也将接受T的交点,返回TT的交点。

允许实现对 Junction 进行短路。例如,如果条件的结果已经从已执行的例程调用中完全确定(仅一个真返回值足以知道整个 Junction 为真),则以下代码中的一个或多个例程调用(a()b()c())可能根本不会执行

if a() | b() | c() {
    say "At least one of the routines was called and returned a truthy value"
}

Junction 旨在用作布尔上下文的匹配器;不支持对 Junction 进行自省。如果您感到有必要自省 Junction,请改用 Set 或相关类型。

用法示例

my @list = <1 2 "Great">;
@list.append(True).append(False);
my @bool_or_int = grep Bool|Int@list;
 
sub is_prime(Int $xreturns Bool {
    # 'so' is for Boolean context 
    so $x %% none(2..$x.sqrt);
}
my @primes_ending_in_1 = grep &is_prime & / 1$ /2..100;
say @primes_ending_in_1;        # OUTPUT: «[11 31 41 61 71]␤» 
 
my @exclude = <~ .git>;
for dir("."{ say .Str if .Str.ends-with(none @exclude}

在对可能产生空列表的参数使用 all 时应特别小心

my @a = ();
say so all(@a# True, because there are 0 Falses

要表示“全部,但至少一个”,可以使用 @a && all(@a)

my @a = ();
say so @a && all(@a);   # OUTPUT: «False␤»

在自动线程处理方面,否定运算符是特殊情况。$a !op $b 在内部被重写为 !($a op $b)。外部否定会折叠所有 Junction,因此返回值始终是一个普通的 Bool

my $word = 'yes';
my @negations = <no none never>;
if $word !eq any @negations {
    say '"yes" is not a negation';
}

请注意,如果没有此特殊情况,则对于一侧为非平凡列表的表达式,如 $word ne any @words,始终会评估为 True

为此,infix:<ne> 算作 infix:<eq> 的否定。

通常,使用正比较运算符和否定 Junction 更具可读性

my $word = 'yes';
my @negations = <no none never>;
if $word eq none @negations {
    say '"yes" is not a negation';
}

故障和异常§

就 Junction 而言,Failure 只是与其他值一样的值

my $j = +any "not a number""42""2.1";
my @list = gather for $j -> $e {
    take $e if $e.defined;
}
@list.say# OUTPUT: «[42 2.1]␤»

在上面,我们对 Junction 使用了前缀 + 运算符,将其中的字符串强制转换为 Numeric。由于当不包含数字的 Str 被强制转换为 Numeric 时,该运算符会返回 Failure,因此 Junction 中的元素之一是 Failure。只有在使用或下沉之前,Failure 才会变成异常,但我们可以检查已定义性以避免这种情况。这就是我们在循环中对 Junction 的元素进行遍历时所做的,仅在已定义这些元素时才将它们添加到列表中。

如果您尝试将 Failure 用作值,则会引发异常——就像此 Failure 是独立的,不属于 Junction 一样

my $j = +any "not a number""42""2.1";
try say $j == 42;
$! and say "Got exception: $!.^name()";
# OUTPUT: «Got exception: X::Str::Numeric␤» 

请注意,如果在计算 Junction 中的任何值时引发异常,则会引发异常,就像在独立计算有问题的值且不使用 Junction 一样;您不能只计算有效的值而忽略异常

sub calc ($_{ die when 13 }
my $j = any 1..42;
say try calc $j# OUTPUT: «Nil␤»

上面只有一个值会导致异常,但 try 的结果仍然是 Nil。一种可能的解决方法是作弊,单独评估 Junction 的值,然后根据结果重新创建 Junction

sub calc ($_{ die when 13 }
my $j = any 1..42;
$j = any (gather $j».take).grep: {Nil !=== try calc $_};
say so $j == 42# OUTPUT: «True␤»

智能匹配§

请注意,在 ~~ 的右侧使用 Junction 的方式与在其他运算符中使用 Junction 的方式略有不同。

考虑此示例

say 25 == (25 | 42);    # OUTPUT: «any(True, False)␤» – Junction 
say 25 ~~ (25 | 42);    # OUTPUT: «True␤»             – Bool

原因是 ==(以及大多数其他运算符)都受自动线程处理的影响,因此您将获得一个交点作为结果。另一方面,~~ 将在右侧(在这种情况下是在交点上)调用 .ACCEPTS,结果将是 Bool

方法§

方法 new§

multi method new(Junction: \valuesStr :$type!)
multi method new(Junction: Str:D \type, \values)

这些构造函数从定义它的类型和一组值构建一个新的交点。

my $j = Junction.new(<Þor Oðinn Loki>type => "all");
my $n = Junction.new"one"1..6 )

两个多值之间的主要区别在于 Junction 的类型如何作为参数传递;要么作为第一个参数按位置传递,要么使用 type 作为命名参数传递。

方法 defined§

multi method defined(Junction:D:)

检查是否已定义,而不是检查布尔值。

say ( 3 | Str).defined ;   # OUTPUT: «True␤» 
say (one 3Str).defined;  # OUTPUT: «True␤» 
say (none 3Str).defined# OUTPUT: «False␤»

Failure 也被认为是未定义的

my $foo=Failure.new;
say (one 3$foo).defined# OUTPUT: «True␤»

从 6.d 开始,此方法将自动进行线程处理。

方法 Bool§

multi method Bool(Junction:D:)

折叠 Junction 并根据类型和它所包含的值返回一个布尔值。每个元素都转换为 Bool

my $n = Junction.new"one"1..6 );
say $n.Bool;                         # OUTPUT: «False␤» 

在这种情况下,所有元素都转换为 True,因此断言其中只有一个元素是错误的。

my $n = Junction.new"one", <0 1> );
say $n.Bool;                         # OUTPUT: «True␤» 

在这种情况下,其中只有一个元素为真,即 1,因此对 Bool 的强制转换返回 True

方法 Str§

multi method Str(Junction:D:)

自动对 .Str 方法进行线程处理,使其元素返回结果为 Junction。使用 .Str 方法(printput)的输出方法是自动线程处理交点的特殊情况,尽管它们能够接受 Mu 类型。

方法 iterator§

multi method iterator(Junction:D:)

返回一个迭代器,该迭代器对转换为 ListJunction 进行迭代。

方法 gist§

multi method gist(Junction:D:)

折叠 Junction 并返回一个 Str,该字符串由交点的类型及其组件的 要点 组成

<a 42 c>.all.say# OUTPUT: «all(a, 42, c)␤»

方法 raku§

multi method raku(Junction:D:)

折叠 Junction 并返回一个 Str,该字符串由其组件的 raku 组成,这些组件 求值 为具有等效组件的等效 Junction

<a 42 c>.all.raku.put# OUTPUT: «all("a", IntStr.new(42, "42"), "c")␤»

中缀 ~§

multi infix:<~>(Str:D $aJunction:D $b)
multi infix:<~>(Junction:D $aStr:D $b)
multi infix:<~>(Junction:D \aJunction:D \b)

中缀 ~ 连接可用于将交点合并为一个交点或将交点与字符串合并。结果交点将合并所有元素,就好像它们被连接到一个嵌套循环中一样

my $odd  = 1|3|5;
my $even = 2|4|6;
 
my $merged = $odd ~ $even;
say $merged# OUTPUT: «any(12, 14, 16, 32, 34, 36, 52, 54, 56)␤» 
 
say "Found 34!" if 34 == $merged# OUTPUT: «Found 34!␤» 
my $prefixed = "0" ~ $odd;
say "Found 03" if "03" == $prefixed# OUTPUT: «Found 03!␤» 
 
my $postfixed = $odd ~ "1";
say "Found 11" if 11 == $postfixed# OUTPUT: «Found 11!␤» 

另一方面,将字符串用作一个参数的 ~ 版本只会将字符串与 Junction 的每个成员连接起来,从而创建一个具有相同数量元素的另一个 Junction。

另请参阅§

类型图§

Junction 的类型关系
raku-type-graph Junction Junction Mu Mu Junction->Mu

展开上面的图表