在 raku 中取消绑定或取消定义变量
Unbind or undefine a variable in raku
阅读 Raku 文档后,我只发现 this 用于取消定义变量。我相信在 Raku 中赋值和绑定是有区别的。
定义和取消定义标量很容易。
> my $n
(Any)
> $n.defined
False
> $n = 3
3
> $n.defined
True
> $n = Nil
(Any)
> $n.defined
False
当绑定变量时,这是不可能的。
> my $k := 5
5
> $k := Nil
===SORRY!=== Error while compiling:
Cannot use bind operator with this left-hand side
at line 2
------> <BOL>⏏<EOL>
> $k = Nil
Cannot assign to an immutable value
in block <unit> at <unknown file> line 1
对于数组或散列,我可以清空它,但变量仍然被定义。
对于函数,当您使用 sub
定义函数时,您不能取消定义它,但可以使用匿名函数。
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> sub foo ($n) { $n + 1}
&foo
> &foo.defined
True
> &foo = Nil
Cannot modify an immutable Sub (&foo)
in block <unit> at <unknown file> line 1
那么赋值和绑定有什么区别呢?
如何取消定义变量?
这里有很多不同的问题要讨论。
> my $k := 5;
> $k := Nil;
Cannot use bind operator
第一个问题是 Raku REPL。比照你的最后一个。你试过了吗CommaIDE or repl.it?
您的代码完全有效:
my $k := 5;
$k := Nil;
say $k; # Nil
继续:
my $k := 5;
$k = Nil;
Cannot assign to an immutable value
这是不一样的。在将 5
绑定到 $k
之后,$k = Nil
代码试图将 分配 一个值到 number .只有容器[1]支持赋值。数字不是容器,因此您不能对其进行赋值。
澄清一些你提到但没有明确涵盖的案例:
my @foo;
@foo := Nil;
Type check failed in binding; expected Positional...
虽然标量变量(带有 $
印记的变量)将绑定到任何值或容器,但带有 @
印记的变量只会绑定到 Positional
容器,例如一个 Array
。 (同样 %
到 Associative
例如 Hash
。)
不仅如此,这些容器总是被定义的。所以他们仍然 return True
for .defined
即使他们是空的:
my @foo := Array.new; # An empty array
say @foo.elems; # 0 -- zero elements
say @foo.defined; # True -- despite array being empty
现在将 Nil
分配给一个数组:
my @foo;
@foo = Nil;
say @foo; # [(Any)]
如果 @
标记变量的声明没有将其绑定到某些显式 Positional
类型,而是绑定到 默认 @
变量的选择。这是一个 Array
,默认元素值为 Any
。
上面的@foo = Nil;
语句赋值一个Nil
值到[=40=的第一个元素].将值 赋值 到 multi-element 容器的 non-existing 元素意味着新的 Scalar
容器弹出并绑定到该缺失元素在任务继续之前。然后,因为我们要分配 Nil
,并且因为 Nil
表示 缺少 值,所以 Array
的默认值((Any)
) 被复制到 Scalar
而不是 Nil
。
关于 sub
案例...
sub foo {}
&foo = {} # Cannot modify an immutable Sub (&foo)
&foo := {} # Cannot use bind operator ...
虽然 sub foo
声明会生成 &foo
标识符,但它有意既不可分配也不可绑定。如果你想要一个 Callable
变量,你必须使用普通变量声明来声明一个。
Unbind or undefine a variable
您不能取消绑定变量,让它们完全不绑定任何东西。 (换句话说,Raku 避免了 the billion dollar mistake。)在某些情况下,您可以 重新 绑定变量。
在某些情况下,您可以将未定义的值绑定或分配给变量。如果它不是标量变量,那么就像上面提到的 @
变量示例一样。接下来考虑标量情况。
绑定案例示例:
my $foo := Any;
say $foo.defined; # False
say $foo; # (Any)
say $foo.VAR.WHAT; # (Any)
我们稍后会看到 .VAR
的内容。
作业案例:
my $foo = Any;
say $foo.defined; # False
say $foo.WHAT; # (Any)
say $foo.VAR.WHAT; # (Scalar)
重要的是要理解,在这种情况下,$foo
绑定到 Scalar
,这是一个容器,对于“已定义”的某些定义,它绝对是“已定义”的,尽管在 say $foo.defined;
行中出现了相反的情况。
在 say $foo.WHAT;
行中,Scalar
保持隐藏状态。相反,我们看到 (Any)
。但是 (Any)
是 值 的类型 在 绑定到 $foo
的 Scalar
容器中。
在下一行中,我们通过在 $foo
上调用 .VAR.WHAT
开始揭开面纱。 .VAR
让 Scalar
显示自己,而不是产生它包含的 value。所以我们看到类型 Scalar
.
但是如果你在Scalar
上调用.defined
它仍然坚持隐藏!:
my $foo;
say $foo.VAR.defined; # False
say $foo.VAR.DEFINITE; # True
(迫使 Raku 告诉你关于其确定性观点的最终真相的唯一方法是调用确定性的最终仲裁者,.DEFINITE
。)
那么规则是什么?
如果根据原始变量声明这样做是有效的,编译器将允许您将给定的新值或容器分配或绑定到变量。
分配或绑定未定义的值遵循相同的规则。
绑定
所有变量必须在其声明结束时绑定。
如果一个变量的声明允许一个未定义的值成为那个变量的bound/assigned,那么这个变量可以是未定义的。但在所有其他情况和意义下,变量本身永远不能“未绑定”或“未定义”。
绑定就是让一个变量对应一些容器或值:
具有 @
和 %
符号的变量必须绑定到 容器(默认为 Array
和 Hash
分别)。
具有 $
标志的变量必须绑定到 容器(默认为 Scalar
)或值。
带有 &
印记的变量必须绑定到 Callable
值或 Scalar
必须包含 Callable
值。 (由于声明 sub
而可见的 &
标记变量不允许重新绑定或赋值。)
分配给我t
赋值意味着复制一个值到一个容器中。
如果一个变量绑定到一个容器,那么你可以赋值给它,前提是编译器根据原始变量声明允许赋值。
如果变量未绑定到容器,则编译器将拒绝对其赋值。
标量变量
如果您将标量 容器 当作 值 使用,那么您将获得保存的值 在容器内。
绑定一个值(已定义或未定义)到标量变量将意味着它将停止充当容器。如果您然后尝试分配给该变量,它将不起作用。您需要将其重新绑定回容器。
脚注
[1] 在这个回答中,我使用了“容器”这个词来指代任何可以作为用于包含其他值的容器。例如,Array
、Hash
或 Scalar
.
的实例
阅读 Raku 文档后,我只发现 this 用于取消定义变量。我相信在 Raku 中赋值和绑定是有区别的。
定义和取消定义标量很容易。
> my $n
(Any)
> $n.defined
False
> $n = 3
3
> $n.defined
True
> $n = Nil
(Any)
> $n.defined
False
当绑定变量时,这是不可能的。
> my $k := 5
5
> $k := Nil
===SORRY!=== Error while compiling:
Cannot use bind operator with this left-hand side
at line 2
------> <BOL>⏏<EOL>
> $k = Nil
Cannot assign to an immutable value
in block <unit> at <unknown file> line 1
对于数组或散列,我可以清空它,但变量仍然被定义。
对于函数,当您使用 sub
定义函数时,您不能取消定义它,但可以使用匿名函数。
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> my &pera = -> $n { $n + 2}
-> $n { #`(Block|140640519305672) ... }
> &pera = Nil
(Callable)
> &pera.defined
False
> sub foo ($n) { $n + 1}
&foo
> &foo.defined
True
> &foo = Nil
Cannot modify an immutable Sub (&foo)
in block <unit> at <unknown file> line 1
那么赋值和绑定有什么区别呢?
如何取消定义变量?
这里有很多不同的问题要讨论。
> my $k := 5;
> $k := Nil;
Cannot use bind operator
第一个问题是 Raku REPL。比照你的最后一个。你试过了吗CommaIDE or repl.it?
您的代码完全有效:
my $k := 5;
$k := Nil;
say $k; # Nil
继续:
my $k := 5;
$k = Nil;
Cannot assign to an immutable value
这是不一样的。在将 5
绑定到 $k
之后,$k = Nil
代码试图将 分配 一个值到 number .只有容器[1]支持赋值。数字不是容器,因此您不能对其进行赋值。
澄清一些你提到但没有明确涵盖的案例:
my @foo;
@foo := Nil;
Type check failed in binding; expected Positional...
虽然标量变量(带有 $
印记的变量)将绑定到任何值或容器,但带有 @
印记的变量只会绑定到 Positional
容器,例如一个 Array
。 (同样 %
到 Associative
例如 Hash
。)
不仅如此,这些容器总是被定义的。所以他们仍然 return True
for .defined
即使他们是空的:
my @foo := Array.new; # An empty array
say @foo.elems; # 0 -- zero elements
say @foo.defined; # True -- despite array being empty
现在将 Nil
分配给一个数组:
my @foo;
@foo = Nil;
say @foo; # [(Any)]
如果 @
标记变量的声明没有将其绑定到某些显式 Positional
类型,而是绑定到 默认 @
变量的选择。这是一个 Array
,默认元素值为 Any
。
上面的@foo = Nil;
语句赋值一个Nil
值到[=40=的第一个元素].将值 赋值 到 multi-element 容器的 non-existing 元素意味着新的 Scalar
容器弹出并绑定到该缺失元素在任务继续之前。然后,因为我们要分配 Nil
,并且因为 Nil
表示 缺少 值,所以 Array
的默认值((Any)
) 被复制到 Scalar
而不是 Nil
。
关于 sub
案例...
sub foo {}
&foo = {} # Cannot modify an immutable Sub (&foo)
&foo := {} # Cannot use bind operator ...
虽然 sub foo
声明会生成 &foo
标识符,但它有意既不可分配也不可绑定。如果你想要一个 Callable
变量,你必须使用普通变量声明来声明一个。
Unbind or undefine a variable
您不能取消绑定变量,让它们完全不绑定任何东西。 (换句话说,Raku 避免了 the billion dollar mistake。)在某些情况下,您可以 重新 绑定变量。
在某些情况下,您可以将未定义的值绑定或分配给变量。如果它不是标量变量,那么就像上面提到的 @
变量示例一样。接下来考虑标量情况。
绑定案例示例:
my $foo := Any;
say $foo.defined; # False
say $foo; # (Any)
say $foo.VAR.WHAT; # (Any)
我们稍后会看到 .VAR
的内容。
作业案例:
my $foo = Any;
say $foo.defined; # False
say $foo.WHAT; # (Any)
say $foo.VAR.WHAT; # (Scalar)
重要的是要理解,在这种情况下,$foo
绑定到 Scalar
,这是一个容器,对于“已定义”的某些定义,它绝对是“已定义”的,尽管在 say $foo.defined;
行中出现了相反的情况。
在 say $foo.WHAT;
行中,Scalar
保持隐藏状态。相反,我们看到 (Any)
。但是 (Any)
是 值 的类型 在 绑定到 $foo
的 Scalar
容器中。
在下一行中,我们通过在 $foo
上调用 .VAR.WHAT
开始揭开面纱。 .VAR
让 Scalar
显示自己,而不是产生它包含的 value。所以我们看到类型 Scalar
.
但是如果你在Scalar
上调用.defined
它仍然坚持隐藏!:
my $foo;
say $foo.VAR.defined; # False
say $foo.VAR.DEFINITE; # True
(迫使 Raku 告诉你关于其确定性观点的最终真相的唯一方法是调用确定性的最终仲裁者,.DEFINITE
。)
那么规则是什么?
如果根据原始变量声明这样做是有效的,编译器将允许您将给定的新值或容器分配或绑定到变量。
分配或绑定未定义的值遵循相同的规则。
绑定
所有变量必须在其声明结束时绑定。
如果一个变量的声明允许一个未定义的值成为那个变量的bound/assigned,那么这个变量可以是未定义的。但在所有其他情况和意义下,变量本身永远不能“未绑定”或“未定义”。
绑定就是让一个变量对应一些容器或值:
具有
@
和%
符号的变量必须绑定到 容器(默认为Array
和Hash
分别)。具有
$
标志的变量必须绑定到 容器(默认为Scalar
)或值。带有
&
印记的变量必须绑定到Callable
值或Scalar
必须包含Callable
值。 (由于声明sub
而可见的&
标记变量不允许重新绑定或赋值。)
分配给我t
赋值意味着复制一个值到一个容器中。
如果一个变量绑定到一个容器,那么你可以赋值给它,前提是编译器根据原始变量声明允许赋值。
如果变量未绑定到容器,则编译器将拒绝对其赋值。
标量变量
如果您将标量 容器 当作 值 使用,那么您将获得保存的值 在容器内。
绑定一个值(已定义或未定义)到标量变量将意味着它将停止充当容器。如果您然后尝试分配给该变量,它将不起作用。您需要将其重新绑定回容器。
脚注
[1] 在这个回答中,我使用了“容器”这个词来指代任何可以作为用于包含其他值的容器。例如,Array
、Hash
或 Scalar
.