结合状态和令牌时发生错误。为什么?

7

这个有效

sub test-string( $string )
{
    my token opening-brace { \( };
    my token closing-brace { \) };
    my token balanced-braces { 
        ( <opening-brace>+ ) <closing-brace> ** { $0.chars } 
    };

    so $string ~~ /^ <balanced-braces> $/;
}

This

sub test-string( $string )
{
    state token opening-brace { \( };
    state token closing-brace { \) };
    state token balanced-braces { 
        ( <opening-brace>+ ) <closing-brace> ** { $0.chars } 
    };

    so $string ~~ /^ <balanced-braces> $/;
}    

与...一起死亡

No such method 'opening-brace' for invocant of type 'Match'
  in regex balanced-braces at ch-2.p6 line 13
  in sub test-string at ch-2.p6 line 17
  in block <unit> at ch-2.p6 line 23

我更喜欢第二个版本,因为我认为第一个版本在每次调用函数时设置token时效率非常低。所以如果这是真正的代码而不是挑战条目,我必须将tokens(文件)全局化。为什么会发生这种情况呢?

1
你能在函数内声明语法吗?我相信这会像状态变量一样运作。 - Scimon Proctor
1
匹配分隔符可以直接编码 https://docs.raku.org/language/regexes#Tilde_for_nesting_structures 你不使用它的原因是什么?此外,标记只是函数,并且它们不会在下一次调用时保持状态。我真的看不出在这里使用状态的意义。另外,正如@ScimonProctor所说,语法是更好的选择。 - jjmerelo
@jjmerelo "函数……不会保留从一次调用到下一次的状态",但这恰恰是“状态”声明符的一个使用案例——sub foo { state $foo++; state $bar--; $foo, $bar }; foo; say foo; 显示 (2 -2)。只是“state”不能在程序声明中工作(正如Holli的问题和我在回答中探讨的那样)。这与想要为程序调用使用“state”并不相同。(尽管如此,我认为语法可能是最佳选择。我猜它会比我的回答中的任何方法都更好且执行效果更佳。) - raiph
@raiph 我这里应该表述得更清楚一些。令牌是函数,而那些函数我指的是不会保留状态的函数。如果你将子例程或方法声明为状态,那么情况就与此相同。它们无法在一个调用与下一个调用之间保留状态。 - jjmerelo
2
@jmerelo "Tokens are functions"我知道。 我能够倒背如流地写下 say token {.}.^mro。你不需要更清楚了。"[a sub won't] keep a state from one call to the next." 你难道看不出你措辞的问题吗?你如何解释我之前评论中的结果 (2 -2) 而非 (1 -1)呢? "There's no way". Raku 中的一个更激进的特点是它是建立在作用域 continuation 之上的,因此甚至有一种方式来保留调用帧。这就是动态交叉调用 gather / take 的工作原理。 - raiph
2
尽管如上所述,我同意使用state来声明例程类似于海市蜃楼,因为例程声明无论如何都会声明一个常量。这就是为什么我在我的答案中写下了take 2。我想引导Holli通过精确的实际用例证明他们需要解决的问题,或者意识到事情已经相当优化。我完全希望他们得出后者的结论,但我将take 2放在take 1之后,因为我不想从线索开始,而且我可能误解了他们的说法或事物的运作方式。 - raiph
1个回答

6

简而言之:我喜欢take 0。有一个解决方法(参见take1),但我认为这不值得。我认为使用纯my不会效率低下(参见take2)。我认为在编译时应该拒绝使用带有正则表达式/方法的state(参见take35),或者保留原样(参见take4)。除非你是一个编程天才,愿意说服jnthn让Rakudo大幅增加对continuations的曝光(参见take5)。

这到底是为什么?(take 1)

如果您像这样编写,则不会出现“这”:

sub test-string( $string )
{
    state &opening-brace = token { \( }
    state &closing-brace = token { \) }
    state &balanced-braces = token { 
        ( <&opening-brace>+ ) <&closing-brace> ** { $0.chars } 
    }

    so $string ~~ /^ <&balanced-braces> $/;
}   

(正则表达式调用中需要使用&这个符号,有点让我感到意外。1)

这到底是为什么?(第二篇)

发生了什么事情?

我认为第一个版本在每次调用函数时都必须设置标记,因此效率相当低。

你所说的“认为”、“相当低效”、“设置标记”是什么意思?我期望正则表达式代码只会被编译一次(如果每次都编译,我会感到震惊),但还没有进行性能测试以验证。

这引发了一系列问题:

你的担忧纯粹是每次调用test-string函数时重新创建3个lexpad条目(例如&opening-parens等;更普遍地说,正则表达式的数量)所花费的时间吗?

你是否真正对运行原始代码进行了性能分析,并发现存在显著问题?

你是否真的测量过这一点,并发现它是你实际项目中“关键的三分之一”

为什么会发生这种情况?(第三次尝试)

state声明符对于sub做了一个合理的处理——它会产生编译时错误:

state sub foo {}    # Compile time error: "Cannot use 'state' with sub declaration"
state my sub foo {} # Compile time error: "Type 'my' is not declared"

但是使用一种方法(正则表达式实际上就是这样)进行编译,虽然可以通过,但却没有任何有用的作用:
state method foo {} # Compiles, but I failed to find a way to access `foo`
state regex bar {.}  # Same

我查看了Rakudo的GH问题队列,未能找到讨论上述代码最后两行(与您的token案例基本相同)的问题。也许人们没有注意到这一点,或者至少认为提交错误不会有帮助?

为什么会发生这种情况?(第4次尝试)

因此,您可以发布一个SO文档,说明state regex应该在编译时被拒绝或做一些有用的事情。@Scimon++会记录另一种观点。还有我。

为什么会发生这种情况?(第5次尝试)

<Your Compiler Code Goes Here>

因为 Raku 是我们的大型多人在线角色扮演游戏(MMORPG),如果您希望在例程声明时使用state声明符号有用的功能(假设它应该像当前与sub一起使用时产生编译时错误,或在Raku构建的"scoped continuations"约束下执行一些花哨的连续操作),那么这项工作可能只是一个“smop”(即简单的方法)就可以实现,因为Rakudo编译器大部分是用Raku语言编写的。某个人故意将sub上的state设置为编译时错误,而连续概念将是一个真正庞大的项目,所以我认为,如果有任何适当的措施,未来几年内在方法或规则上使用state也应该成为编译时错误。

或者,更合适的是,现在已经有了SO,有文档化的替代方案(语法)和解决方法(take 1),现在是时候转向下一个层次了...

脚注

1 请参见我对Difference in ... regex scope问题的回答。使用state声明的正则表达式的行为似乎不遵循我在那个回答中引用的设计推测的直观阅读。并且,至少从那个答案中以下部分的叙述也是错误的...

"<bar>如上所述。它优先解析为一个早期绑定的词法(my/our)程序/规则,名称为&bar

...因为在本答案的第一次尝试代码中,正则表达式调用必须以&为前缀才能工作。也许这只是偶然的,它们根本不起作用。


1
感谢您提供这么详细的回答。有时我仍然会受到 Perl 语言的影响。Tokens 就是函数,需要注意。我也认为错误提示信息可以更好些。 - Holli
略微有趣的是思考state sub如何声明一个 continuation。这到底意味着什么?目前我想到的只有同步的,而这已经被动态的gather/take覆盖了;或者异步的,而这已经被await覆盖了。回过头来看,根据你所写的,答案基本上应该是“在 Raku 中,正则表达式并不是字符串”,也许@jmerelo 应该写下这个答案,然后您应该接受那个答案。同时,感谢您用委婉语说出“详细说明”。 :) - raiph
我找不到访问 foo 的方法 -- (state method foo { say 42 })(0),虽然也许这不是你想要的。 - ugexe
@ugexe 是的,两个问题都是。 (我尝试过 state method { say self }(42) 来说服自己它会生成一些合理的代码。我认为如果我插入一个名称它会起作用,但我没有尝试,因为正如你所猜测的那样,我只对通过名称在声明语句之外访问该方法感兴趣。感谢您为其他读者澄清了这一点。) - raiph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接