JavaScript 的事件循环使用消息队列来安排工作,并在开始下一条消息之前将每条消息运行到完成。因此,JavaScript 代码中一个小众但令人惊讶的常见模式是安排一个函数在当前队列中的消息使用setTimeout(fn, 0)
. 例如:
setTimeout(() => {console.log('first')}, 0);
console.log('second');
// OUTPUT: "second\nfirst"
Run Code Online (Sandbox Code Playgroud)
(有关更多详细信息,请参阅MDN 的描述。)
Raku's 是否提供任何类似的方法来在所有当前计划的工作完成后立即安排工作?根据我对 Raku 并发模型的理解(主要来自这篇 6guts 帖子),似乎 Raku 使用了类似的消息队列(尽管如果有误,请纠正我!)。我最初认为这Promise.in(0).then: &fn
是一个直接的等价物:
my $p = Promise.in(0).then: { say 'first' }
say 'second';
await $p;
# OUTPUT: «second\nfirst» # ...usually
Run Code Online (Sandbox Code Playgroud)
但是,在运行上面的代码很多次后,我意识到,这只是建立一个竞争条件和'first'
是有时第一。那么,是否有任何 Raku 代码提供相同的行为?而且,如果是这样,这种行为是否是 Raku/Roast 决定的有意语义的结果,而不是(可能是临时的)实现细节的结果?
与我之前的问题一样,在这个领域我无法判断我对 Raku 语义的理解是否遇到了错误或漏洞。上次竟然是bug,不过怀疑闪电会劈两次!
\n一般来说,我知道我可以使用看起来很像创建 Pair 的语法(例如f :a(42)
)或使用看起来很像展平哈希的语法(例如f |%h
)将命名参数传递给函数。(请参阅文档中的参数解构)。通常,这两者是等效的,即使对于非标量参数也是如此:
sub f(:@a) { dd @a }\nmy %h = a => [4, 2];\nf :a([4,2]); # OUTPUT: \xc2\xabArray element = [4, 2]\xc2\xbb\nf |%h; # OUTPUT: \xc2\xabArray element = [4, 2]\xc2\xbb\n
Run Code Online (Sandbox Code Playgroud)\n然而,当使用默认构造函数构造对象时.new
,这两种形式似乎给出了不同的结果:
class C { has @.a; }\nmy %h = a => [4, 2];\nC.new: :a([4,2]; # OUTPUT: \xc2\xabC.new(a => ([[4, 2]])\xc2\xbb\nC.new: |%h; # OUTPUT: \xc2\xabC.new(a => [[4, 2],])\xc2\xbb\n
Run Code Online (Sandbox Code Playgroud)\n … 我怀疑这个问题很容易回答,答案是否定的。但是,我想确保我没有遗漏任何东西。
考虑以下代码:
sub f(:$a = 'foo') { say $a }
sub g(:$a) { f :$a }
g(); # OUTPUT: «(Any)»
Run Code Online (Sandbox Code Playgroud)
有没有一种好方法可以更改&f
or的签名/正文,&g
以便打印foo
而不是Any
?
我知道有两种方法可以&f
使用 的默认值$a
,但它们都不是很好。
选项1:
sub f(:$a = 'foo') { say $a }
multi g(:$a) { f :$a }
multi g() { f }
g(); # OUTPUT: «foo»
Run Code Online (Sandbox Code Playgroud)
选项 2:
sub f(:$a = 'foo') { say $a }
sub g(:$a) { f |(:$a with $a) }
g(); # OUTPUT: …
Run Code Online (Sandbox Code Playgroud) 如果我有一个变量my $a = True
,那么我从下面的代码中得到这个输出:
say «a list of words foo $a bar baz».raku;
# OUTPUT: ("a", "list", "of", "words", "foo", "True", "bar", "baz")
Run Code Online (Sandbox Code Playgroud)
也就是说,即使结果是 a List
,元素True
在被包含在列表中之前也会被字符串化——列表包含"True"
,而不是True
。有什么方法可以在仍然使用插值的同时避免这种字符串化?
如果$a
是我定义的类(因此可以Str
为其编写方法)而不是 ,那么有没有办法做到这一点Bool
?
(我知道我可以写更详细的("a", "list", "of", "words", "foo", $a, "bar", "baz")
or «a list of words foo».Slip, $a, «bar baz».Slip
,但我问是否有办法仍然使用插值)。
在下面的代码中,正则表达式$r
清楚地“知道”它包含文本bar
——这就是它能够与 Str 匹配的方式bar
。但是.gist
并.raku
报告$r
包含变量$foo
而不说明值$foo
包含什么。有什么办法可以$r
告诉我它的计算值吗?
sub f {
my $foo = 'bar';
g(rx/$foo/);
}
sub g($r) {
say $r.gist;
say 'bar' ~~ rx/$r/;
}
f # OUTPUT: rx/$foo/
# ?bar?
Run Code Online (Sandbox Code Playgroud)
(我知道我可以通过手动解析访问相同的信息$r
,发现所有的变量,然后走&g
的callframe
s中变量的值。但是,这似乎是一个相当脆弱的黑客得到的信息是,正则表达式显然已经知道,至少在某种程度上。)
我最近注意到,在使用赋值的大多数情况下,重新初始化动态变量没有我预期的语义(但是,绑定以我预期的方式工作)。
具体来说,在这段代码中:
sub g {
my $*i = CALLERS::<$*i> // 0;
my $*a1 = CALLERS::<$*a1> // Array.new;
my @*a2 = CALLERS::<@*a2> // Array.new;
$*i++;
$*a1.push: 'v1';
@*a2.push: 'v2';
dd $*i;
dd $*a1;
dd @*a2;
}
sub f {
my $*i = 0;
my $*a1 = Array.new;
my @*a2 = Array.new;
g; g; g;
}
f
Run Code Online (Sandbox Code Playgroud)
我期望输出3
, ["v1", "v1", "v1"]
,["v2", "v2", "v2"]
而不是得到1
, $["v1", "v1", "v1"]
, ["v2"]
。切换到绑定解决了这个问题,所以我没有试图解决问题——但我非常想了解为什么赋值在这里不起作用。我注意到指向 Array 的 Scalar 有效,但指向 …
以下程序无法正确编译:
sub f(Int $a) { my Str $b = $a }
say f 42;
say f 'foo';
Run Code Online (Sandbox Code Playgroud)
具体来说,第 3 行导致编译错误(带有===SORRY!===
错误消息);此错误发生在第 2 行执行之前,因此永远不会达到 &f 中的类型不匹配。
但是,具体是什么时候发生此错误?我以为它发生在 CHECK阶段,但惊讶地发现raku -c
它没有生成编译错误;它报告Syntax OK
。
为了更深入地研究这一点,我在上面的代码片段中添加了日志记录代码:
BEGIN note 'begin';
CHECK note 'check';
INIT note 'init';
END note 'end';
sub f(Int $a) { my Str $b = $a }
say f 42;
say f 'foo';
Run Code Online (Sandbox Code Playgroud)
运行此修改后的代码并raku -c
打印“begin\n check\n Syntax OK”;运行它并raku
打印“begin\n check\n ===SORRY!==="(以及其余的错误消息)。
如果我删除该say f 'foo' …
该文件指出,“运行块地方不能是独立的语句最简单的方法是通过编写do
之前”,并提供了下面的例子:
# This dies half of the time
do { say "Heads I win, tails I die."; Bool.pick } or die; say "I win.";
Run Code Online (Sandbox Code Playgroud)
但是,do
似乎不会导致所有块都运行。特别是,它似乎没有运行带有签名的块:
do -> $a = 42 { say "ran with $a"; 0 } or die; say 'done'; # OUTPUT: «done»
Run Code Online (Sandbox Code Playgroud)
那么将do
块视为有时会导致它运行的表达式会更好吗?还是乐堂在这里的行为不正确?还是我的理解不正确?
文档说fmt
\n\n返回一个字符串,其中列表中的每个元素均已根据
\n$format
[第一个参数] 进行格式化,并且每个元素均由$separator
[第二个参数] 分隔。
根据该描述,我希望能够调用.fmt
列表列表,然后传递一个printf
-style 格式字符串,其中%
包含内部列表中每个元素的指令。但这是行不通的。
如果您告诉我我对 ^^^^ 的看法是错误的,我会期望它.fmt
会自动展平其参数,因此每个参数都会被格式化并由$separator
. 但事实并非如此。
相反,运行此代码
\n\nsay (<a b c>, <1 2 3>, <X Y Z>).fmt(\'\xe2\x86\x92%03s|\', "\\n=================\\n");\n
Run Code Online (Sandbox Code Playgroud)\n产生这个输出:
\n\xe2\x86\x9200a| \xe2\x86\x9200b| \xe2\x86\x9200c|\n=================\n\xe2\x86\x92001| \xe2\x86\x92002| \xe2\x86\x92003|\n=================\n\xe2\x86\x9200X| \xe2\x86\x9200Y| \xe2\x86\x9200Z|\n
Run Code Online (Sandbox Code Playgroud)\n也就是说,格式字符串应用于内部列表中的每个元素,然后对这些列表进行字符串化(不使用格式字符串;注意
每个|
和\xe2\x86\x92
字符之间),然后在每个外部列表之间插入分隔符列表之间插入分隔符。
这给我留下了三个问题:
\nRaku 的状态声明符可用于为子例程或其他块提供其自己的本地状态,该状态在函数的多次调用中持续存在:
\nsub f { state $n++ }\nsay f; # OUTPUT: \xc2\xab0\xc2\xbb\nsay f; # OUTPUT: \xc2\xab1\xc2\xbb\nsay f; # OUTPUT: \xc2\xab2\xc2\xbb\n
Run Code Online (Sandbox Code Playgroud)\n我知道有两种“复制”具有内部状态的函数的方法:首先,我可以使用&
诸如my &f1 = &f
. 这导致&f1
有效地成为别名,&f
并且意味着它们共享状态 \xe2\x80\x93 任何改变其他函数状态&f
或&f1
也将改变其他函数状态的东西。
其次,我可以使用像&f
. my &f2 = &f.clone
这将创建一个独立的函数,其状态被初始化为任何默认值(&f
即上面的代码)。$n
Any
然而,我希望采用第三种复制方法&f
(如选项 1)将保留 \ 状态的当前值&f
,但(如选项 2)将使该状态独立于&f
\ 的状态。换句话说,我希望能够使用下面注释掉的行:
sub f { state $n++ }\nsay f; # …
Run Code Online (Sandbox Code Playgroud) raku ×10
rakudo ×10
arrays ×1
compilation ×1
compile-time ×1
constructor ×1
do-notation ×1
event-loop ×1
expression ×1
formatting ×1
javascript ×1
nested-lists ×1
printf ×1
regex ×1
state ×1
string ×1