7

在阅读了 Raku 文档后,我发现这个只是为了取消定义一个变量。我相信在 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>⏏&lt;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

那么赋值和绑定有什么区别呢?
如何取消定义变量?

4

1 回答 1

10

这里有很多不同的问题要讨论。

> my $k := 5;
> $k := Nil;
Cannot use bind operator

第一个问题是 Raku REPL。cf你的最后一个SO。您是否尝试过CommaIDErepl.it

您的代码完全有效:

my $k := 5;
$k := Nil;
say $k; # Nil

继续:

my $k := 5;
$k = Nil;
Cannot assign to an immutable value

这是不同的。绑定5到之后$k$k = Nil代码会尝试将值分配给数字。只有容器[1]支持assignment。数字不是容器,因此您无法分配给它。


澄清您提到但未明确涵盖的一些案例:

my @foo;
@foo := Nil;
Type check failed in binding; expected Positional...

虽然标量变量(带有$印记的变量)将绑定到任何值或容器,但@印记变量只会绑定到Positional容器,例如Array. (同样是 a%到一个Associative诸如 a Hash。)

不仅如此,这些容器总是被定义的。因此True.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;语句的第一个元素赋值。将值分配给多元素容器的不存在元素意味着新容器弹出存在并在分配继续之前绑定到该缺失元素。然后,因为我们正在分配 a ,并且因为 a表示没有值,所以将的默认值 ( ) 复制到而不是.Nil@fooScalarNilNilArray(Any)ScalarNil


进入sub案件...

sub foo {}
&foo = {}  # Cannot modify an immutable Sub (&foo)
&foo := {} # Cannot use bind operator ...

虽然sub foo声明生成&foo标识符,但它故意既不可分配也不可绑定。如果你想要一个Callable变量,你必须使用普通的变量声明来声明一个。


取消绑定或取消定义变量

不能取消绑定变量,因为它们根本不受任何约束。(换句话说,Raku 避免了十亿美元的错误。)在某些情况下,您可以重新绑定变量。

在某些情况下,您可以将未定义的值绑定或分配给变量。如果它不是标量变量,那么就像@上面提到的变量示例一样。接下来考虑标量情况。

绑定案例的示例:

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它绑定到 a Scalar,它是一个容器,对于“已定义”的某些定义,它最肯定是“定义的”,尽管在该say $foo.defined;行中出现相反的情况。

在这say $foo.WHAT;条线上,Scalar遗体被隐藏起来。相反,我们看到一个(Any). 但是绑定到的容器保存的(Any)的类型。Scalar$foo

在下一行中,我们已经开始通过调用来揭开.VAR.WHAT面纱$foo.VAR得到它来Scalar显示自己,而不是产生它包含的值。所以我们看到了 type Scalar

但是,如果您调用.defined它,Scalar仍然坚持隐藏!:

my $foo;
say $foo.VAR.defined;  # False
say $foo.VAR.DEFINITE; # True

(迫使 Raku 告诉你关于其确定性观点的最终真相的唯一方法是调用确定性的最终仲裁者,.DEFINITE.)

那么规则是什么?

如果根据原始变量声明这样做是有效的,编译器将允许您将给定的新值或容器分配或绑定到变量。

分配或绑定未定义的值遵循相同的规则。

捆绑

所有变量都必须在其声明的末尾绑定。

如果变量的声明允许将未定义的绑定/分配给该变量,则该变量在该意义上可以是未定义的。但在所有其他情况和意义下,变量本身永远不会是“未绑定”或“未定义”的。

绑定是关于使变量对应于某个容器

  • 变量 with@%sigils 必须绑定到一个容器(默认ArrayHash分别)。

  • 带有 sigil 的变量$必须绑定到容器(默认值Scalar)或值。

  • 带有&sigil 的变量必须绑定到一个Callable值或Scalar约束为包含一个Callable值。(&由于声明 a 而可见的 sigil'd 变量sub不允许重新绑定或赋值。)

任务

赋值意味着将值复制容器中。

如果一个变量绑定到一个容器,那么你可以分配给它,前提是编译器根据原始变量声明允许分配。

如果变量绑定到容器,则编译器将拒绝对它的赋值。

标量变量

如果您使用标量容器,就好像它是一个一样,那么您将获得容器保存的值。

将值(已定义或未定义)绑定到标量变量将意味着它将停止充当容器。如果您然后尝试分配给该变量,它将不起作用。您需要将其重新绑定回容器。

脚注

[1]在这个答案中,我使用了“容器”这个词来指代任何可以用作包含其他值的容器的值。例如,ArrayHashScalar

于 2020-10-22T17:22:43.070 回答