无法在另一个命名空间 (Raku) 中插入变量
Can't Interpolate Variable In Another Namespace (Raku)
我正在尝试使用插值在另一个命名空间中创建一个变量,但在咨询 https://docs.raku.org/language/packages#index-entry-::() 后无法让它工作。在测试不同的东西时,我发现了一些让我感到困惑的东西。这有效:
#!/usr/bin/env raku
$Foo::bar = 'foobar';
say $Foo::bar;
$ ./package.raku
foobar
但这不是:
#!/usr/bin/env raku
my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar;
$ ./package-interpolate.raku
No such symbol '$bar'
in block <unit> at ./package-interpolate.raku line 4
我错过了什么吗?谢谢!
TL;DR 对于某个特定包(名称空间)中保存的变量(符号)的引用,存在三种不同的语法。您尝试了两个,但看起来您可能需要第三个。这里也有一些皱纹,所以这个答案很长。
这里是三种语法的总结,从 可能 是您想要的(部分)语法开始,然后是您尝试过的两种语法:
Foo::Bar::Baz::<$qux>
静态指定包,动态指定变量
如果嵌套包规范无法解析为相应的一组现有包,则构造会产生错误。如果包规范解析,则创建变量(如果它已分配或绑定到并且尚不存在)。
$Foo::Bar::Baz::qux
静态指定包和变量
如果嵌套包规范中的任何包无法解析为现有包,则会自动创建该包。如果变量被赋值或绑定并且尚不存在,也会创建该变量。
::('$Foo::Bar::Baz::qux')
动态指定包和变量
如果嵌套包规范无法解析为相应的一组现有包,或者如果变量不存在,则构造会产生错误。
此答案的其余部分是三个部分,其中包含与上述内容相对应的更多详细信息。
Foo::Bar::Baz::<$qux>
Staticically specified packages, dynamically specified variable
如果所有包都已经存在,构造将起作用,如果变量不存在则创建变量。否则构造将产生一个错误,一个合理的 compile-time 错误(如果只指定了一个包)或 run-time 的一个 LTA 错误(如果指定了嵌套包)。
变量名可以通过 expression/variable 间接指定,其值在 run-time.
处求值
引用本节标题中链接的文档:
To do direct lookup in a package's symbol table without scanning, treat the package name as a hash
这是我们在 non-nested non-existent 包中引用 non-existent 变量的第一个示例:
my $bar = '$baz';
say Foo::{$bar};
产生编译时间错误:
Undeclared name:
Foo used at line 2
接下来,一组嵌套non-existent包中的一个non-existent变量:
my $bar = '$baz';
try say Foo::Bar::Baz::{$bar};
say $!; # Could not find symbol '&Baz' in 'GLOBAL::Foo::Bar'
say GLOBAL::Foo::Bar; # (Bar)
错误消息($!
中的异常)不仅仅是 LTA,而是反映了疯狂。构造创建了包 GLOBAL::Foo::Bar
。更多疯狂!
现在举一个例子,其中我们引用了一个 确实 存在的包和其中一个 不 最初存在的变量:
package Foo {}
my $bar = '$qux';
say Foo::{$bar}; # (Any)
say Foo::{$bar}:exists; # False
Foo::{$bar} = 99;
say Foo::{$bar}:exists; # True
say Foo::{$bar}; # 99
say Foo.WHO.keys; # ($qux)
say $Foo::qux; # 99
因此,如果指定的变量不存在,Foo::{...}
语法(或 Foo::<...>
,但不是 ::(...)
)会 not 出错,但仅当 软件包 不存在时才有效。
此语法中对不存在的变量的引用(在存在的包中)return是 (Any)
。如果 (Any)
被赋值,它会创建变量。
(除非包是 MY
或未指定,这在此语法中意味着相同的事情。如果在 MY
中查找的变量丢失,查找将 return 值Nil
而不是 (Any)
,并且不能分配或绑定到。)
$Foo::Bar::Baz::qux
Statically specified packages and variable
如果你提到一个使用这种语法的包,即使是non-nested一个包,如果它不存在你也会创建它。
如果您 assign/bind 一个变量,如果它不存在,您将创建它。
您必须静态说明您要指定的(嵌套)包和变量名。您不能间接声明(例如通过变量)此语法的任何部分。
引用本节标题中链接的文档:
Ordinary package-qualified names look like this: $Foo::Bar::quux
, which would be the $quux
variable in package Foo::Bar
这是您问题中的第一个代码片段(有效,@user0721090601 感到惊讶)附加了一些行。三行代码块显示了 Foo
包的着陆点(并提供了对该答案和文档中涵盖的三种语法的先睹为快)。最后一行确认 $bar
变量已添加到 Foo
包中:
$Foo::bar = 'foobar';
say $Foo::bar; # foobar
say GLOBAL::Foo; # (Foo) Package qualified name yields type object `(Foo)`
say GLOBAL::<Foo>; # (Foo) Package qualified lookup (yields same object)
say ::('Foo'); # (Foo) Package scanning lookup (yields same object)
say GLOBAL::Foo.WHO.keys; # ($bar) `.WHO` returns `(Foo)`'s package named `Foo`
GLOBAL
是包含“Interpreter-wide 包符号的 "pseudo" package,实际上是 UNIT::GLOBAL
”。
如果你去掉上面代码中的第一行($Foo::bar = 'foobar';
):
say $Foo::bar;
行的结果是 (Any)
;
添加的三行代码块继续显示(Foo)
,表明仅提及句法形式的变量[=44] =] 足以创建包 Foo
;
最后一行显示()
,表明 $bar
未 添加到 Foo
包中,尽管 say $Foo::bar;
行显示 (Any)
而不是Nil
.
::('$Foo::Bar::Baz::qux')
Dynamically specified packages and variable
如果您希望在现有包中读取或写入现有变量,而不会有意外创建它们的风险如果它们不存在,请使用此语法。
相反,如果您希望自动创建包 and/or 变量,请使用其他两种语法之一或其他方法。
您可以在此语法的 (...)
部分使用复杂的表达式,它们将被动态插入,但随后被视为静态源代码。因此,您可以执行一些操作,例如在 (...)
中包含文字字符串 '$Foo::Bar::Baz::qux'
或计算结果为此类字符串的变量,编译器会将其插入到整体引用中,然后用于指导查找,将 ::
之间的每个组件视为不同的嵌套包。
引用本节标题中链接的文档:
using ::($expr)
where you'd ordinarily put a package or variable name ... the indirect name is looked up ... with priority given first to leading pseudo-package names, then to names in the lexical scope (searching scopes outwards, ending at CORE). The current package is searched last.
请注意,查找将扫描到许多个包。
您问题中的 second 代码片段(not 按您的预期工作)使用此扫描逻辑进行变量查找,可能不仅仅查看一个明确指定的包,而是查看许多包。
这是相同的代码,但在开头插入了另一行:
package Foo { our $bar } # <-- This line added
my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar; # foobar
现在可以了。
这表明您的 $Foo::($bar) = 'foobar';
行:
在包 Foo
中查找 变量 $bar
。 (所以“插入名称”确实 像文档所宣传的那样工作。)
但是没有创建包。
并且没有创建变量。
但是确实进行了赋值,因为查找成功找到了匹配查找的包和变量。
在 Raku 中使用插值在其他命名空间中创建变量似乎是不可能的。
如果它们事先在词法范围内,则可以通过这种方式访问它们。
如果您在代码之前添加此部分,它会起作用:
class Foo {
our $bar = 'our $bar';
}
为了实现你想做的事情,我建议你查看 rosettacode 的这一部分:
https://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#Raku
它提出了几种不同的方法来解决 Raku 的这个问题。即使在他们的代码中“命名空间”的概念被简化为 类.
我正在尝试使用插值在另一个命名空间中创建一个变量,但在咨询 https://docs.raku.org/language/packages#index-entry-::() 后无法让它工作。在测试不同的东西时,我发现了一些让我感到困惑的东西。这有效:
#!/usr/bin/env raku
$Foo::bar = 'foobar';
say $Foo::bar;
$ ./package.raku
foobar
但这不是:
#!/usr/bin/env raku
my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar;
$ ./package-interpolate.raku
No such symbol '$bar'
in block <unit> at ./package-interpolate.raku line 4
我错过了什么吗?谢谢!
TL;DR 对于某个特定包(名称空间)中保存的变量(符号)的引用,存在三种不同的语法。您尝试了两个,但看起来您可能需要第三个。这里也有一些皱纹,所以这个答案很长。
这里是三种语法的总结,从 可能 是您想要的(部分)语法开始,然后是您尝试过的两种语法:
Foo::Bar::Baz::<$qux>
静态指定包,动态指定变量
如果嵌套包规范无法解析为相应的一组现有包,则构造会产生错误。如果包规范解析,则创建变量(如果它已分配或绑定到并且尚不存在)。
$Foo::Bar::Baz::qux
静态指定包和变量
如果嵌套包规范中的任何包无法解析为现有包,则会自动创建该包。如果变量被赋值或绑定并且尚不存在,也会创建该变量。
::('$Foo::Bar::Baz::qux')
动态指定包和变量
如果嵌套包规范无法解析为相应的一组现有包,或者如果变量不存在,则构造会产生错误。
此答案的其余部分是三个部分,其中包含与上述内容相对应的更多详细信息。
Foo::Bar::Baz::<$qux>
Staticically specified packages, dynamically specified variable
如果所有包都已经存在,构造将起作用,如果变量不存在则创建变量。否则构造将产生一个错误,一个合理的 compile-time 错误(如果只指定了一个包)或 run-time 的一个 LTA 错误(如果指定了嵌套包)。
变量名可以通过 expression/variable 间接指定,其值在 run-time.
处求值
引用本节标题中链接的文档:
To do direct lookup in a package's symbol table without scanning, treat the package name as a hash
这是我们在 non-nested non-existent 包中引用 non-existent 变量的第一个示例:
my $bar = '$baz';
say Foo::{$bar};
产生编译时间错误:
Undeclared name:
Foo used at line 2
接下来,一组嵌套non-existent包中的一个non-existent变量:
my $bar = '$baz';
try say Foo::Bar::Baz::{$bar};
say $!; # Could not find symbol '&Baz' in 'GLOBAL::Foo::Bar'
say GLOBAL::Foo::Bar; # (Bar)
错误消息($!
中的异常)不仅仅是 LTA,而是反映了疯狂。构造创建了包 GLOBAL::Foo::Bar
。更多疯狂!
现在举一个例子,其中我们引用了一个 确实 存在的包和其中一个 不 最初存在的变量:
package Foo {}
my $bar = '$qux';
say Foo::{$bar}; # (Any)
say Foo::{$bar}:exists; # False
Foo::{$bar} = 99;
say Foo::{$bar}:exists; # True
say Foo::{$bar}; # 99
say Foo.WHO.keys; # ($qux)
say $Foo::qux; # 99
因此,如果指定的变量不存在,Foo::{...}
语法(或 Foo::<...>
,但不是 ::(...)
)会 not 出错,但仅当 软件包 不存在时才有效。
此语法中对不存在的变量的引用(在存在的包中)return是 (Any)
。如果 (Any)
被赋值,它会创建变量。
(除非包是 MY
或未指定,这在此语法中意味着相同的事情。如果在 MY
中查找的变量丢失,查找将 return 值Nil
而不是 (Any)
,并且不能分配或绑定到。)
$Foo::Bar::Baz::qux
Statically specified packages and variable
如果你提到一个使用这种语法的包,即使是non-nested一个包,如果它不存在你也会创建它。
如果您 assign/bind 一个变量,如果它不存在,您将创建它。
您必须静态说明您要指定的(嵌套)包和变量名。您不能间接声明(例如通过变量)此语法的任何部分。
引用本节标题中链接的文档:
Ordinary package-qualified names look like this:
$Foo::Bar::quux
, which would be the$quux
variable in packageFoo::Bar
这是您问题中的第一个代码片段(有效,@user0721090601 感到惊讶)附加了一些行。三行代码块显示了 Foo
包的着陆点(并提供了对该答案和文档中涵盖的三种语法的先睹为快)。最后一行确认 $bar
变量已添加到 Foo
包中:
$Foo::bar = 'foobar';
say $Foo::bar; # foobar
say GLOBAL::Foo; # (Foo) Package qualified name yields type object `(Foo)`
say GLOBAL::<Foo>; # (Foo) Package qualified lookup (yields same object)
say ::('Foo'); # (Foo) Package scanning lookup (yields same object)
say GLOBAL::Foo.WHO.keys; # ($bar) `.WHO` returns `(Foo)`'s package named `Foo`
GLOBAL
是包含“Interpreter-wide 包符号的 "pseudo" package,实际上是 UNIT::GLOBAL
”。
如果你去掉上面代码中的第一行($Foo::bar = 'foobar';
):
say $Foo::bar;
行的结果是(Any)
;添加的三行代码块继续显示
(Foo)
,表明仅提及句法形式的变量[=44] =] 足以创建包Foo
;最后一行显示
()
,表明$bar
未 添加到Foo
包中,尽管say $Foo::bar;
行显示(Any)
而不是Nil
.
::('$Foo::Bar::Baz::qux')
Dynamically specified packages and variable
如果您希望在现有包中读取或写入现有变量,而不会有意外创建它们的风险如果它们不存在,请使用此语法。
相反,如果您希望自动创建包 and/or 变量,请使用其他两种语法之一或其他方法。
您可以在此语法的
(...)
部分使用复杂的表达式,它们将被动态插入,但随后被视为静态源代码。因此,您可以执行一些操作,例如在(...)
中包含文字字符串'$Foo::Bar::Baz::qux'
或计算结果为此类字符串的变量,编译器会将其插入到整体引用中,然后用于指导查找,将::
之间的每个组件视为不同的嵌套包。
引用本节标题中链接的文档:
using
::($expr)
where you'd ordinarily put a package or variable name ... the indirect name is looked up ... with priority given first to leading pseudo-package names, then to names in the lexical scope (searching scopes outwards, ending at CORE). The current package is searched last.
请注意,查找将扫描到许多个包。
您问题中的 second 代码片段(not 按您的预期工作)使用此扫描逻辑进行变量查找,可能不仅仅查看一个明确指定的包,而是查看许多包。
这是相同的代码,但在开头插入了另一行:
package Foo { our $bar } # <-- This line added
my $bar = 'bar';
$Foo::($bar) = 'foobar';
say $Foo::bar; # foobar
现在可以了。
这表明您的 $Foo::($bar) = 'foobar';
行:
在包
Foo
中查找 变量$bar
。 (所以“插入名称”确实 像文档所宣传的那样工作。)但是没有创建包。
并且没有创建变量。
但是确实进行了赋值,因为查找成功找到了匹配查找的包和变量。
在 Raku 中使用插值在其他命名空间中创建变量似乎是不可能的。 如果它们事先在词法范围内,则可以通过这种方式访问它们。 如果您在代码之前添加此部分,它会起作用:
class Foo {
our $bar = 'our $bar';
}
为了实现你想做的事情,我建议你查看 rosettacode 的这一部分: https://rosettacode.org/wiki/Add_a_variable_to_a_class_instance_at_runtime#Raku
它提出了几种不同的方法来解决 Raku 的这个问题。即使在他们的代码中“命名空间”的概念被简化为 类.