老生常谈的答案是,如果评估b条
两次是错误的,那么只应该评估一次。。。
免责声明:我不知道AEC,所以我将编写语法。
是否脱糖
为了回答这个问题,我将AEC的去糖化定义为语义等价的AEC代码的转换。
我想问的第一个问题是你是否应该去糖。
去语法化的优点是可以尽早删除结构,从而可能简化编译器的其余部分,但它也有自己的挑战:诊断应该引用原始源代码,含义不应该改变,有时根本不可能用语言表达目标语义。
因此,如果您由于任何原因无法取消糖化,请不要这样做。请将问题转移到编译管道的稍后部分,这样就不会受到取消糖化所面临的限制。
如果你真的想脱糖,请继续阅读。
变量
去糖化需要引入变量,这绝对不应影响任何现有变量。
作弊是一个可能有用的伎俩。与C和C++一样,为“实现”保留前缀允许使用@1
,@2
,等等……当其他人都不被允许时。否则,您需要一组范围内变量名,并且需要避免使用它们。
使用块表达式
如果语言有块表达式1,您本质上想替换a’<b’<c’
通过以下方式:
{设@1=a';设@2=b';@1<@2&&@2<c'}
(其中a’
和co是表达式的位置所有者)
如果有更多元素,请嵌套:
{设@1=a';设@2=b';@1 < @2 && {设@3=c';@2 < @3 && {设@4=d';@3 < @4}}}
块表达式是最好的,因为即使在Rust这样的语言中,输入和借用都非常严格,它们也可以避免拼写出类型、借用模式等。。。
1 块表达式是将块用作表达式的能力.
有一个封口
在没有块表达式的情况下,最简单的方法是引入闭包:
定义closure_42():@1=a'@2=b'如果不是(@1<@2):return错误@3=c’如果不是(@2<@3):return错误#可能还有其他表达return True
然后用闭包替换替换的表达式。
使用函数
在没有闭包的情况下,您可以模拟它。。。
您实际上创建了一个函数,该函数将表达式中引用的所有变量的并集作为输入进行替换。这在动态类型语言或带有模板的语言中效果良好。
def生成_42(v0',v_1',v_2',v_3'):@1=a'@2=b'如果不是(@1<@2):return错误@3=c’如果不是(@2<@3):return错误#可能还有其他表达return True
其中v_0',v_1',v_2'。。。是定义a'、b'和c'的作用域的变量,由a'、b'和c'引用。请注意,每个引用变量只需传递一次,它们的名称应该与以前相同。
现场
最后也是最起码的解决方案是现场重写整个的条件。
这是我最不喜欢的,因为它需要重写整个表达式,而不仅仅是子表达式a’<b’<c’
,或者至少将表达式的前缀重写为要重写的最后一个子表达式。这使得重写变得不那么局部,因此正确地执行会更加烦人,并且当在单个表达式中重写多个子表达式时,情况会变得更糟。
除此之外,它不能使用短路。
之前:
如果foo()和a'<b'<c'。。。和bar():#原始块。
之后:
@条件=foo()如果@条件:@1=a'@2=b'@条件=@1<@2如果@条件:@3=c’@条件=@2<@3#进一步链接if@condition和bar():#原始块。
我确实注意到了一个不幸的问题:必须重写整个条件,或者至少重写前缀如果你觉得有趣的话。