使用命名空间时,从函数返回表达式会有所不同。大家好,
从函数返回表达式时,我遇到了一些奇怪的行为,并且已经找不到任何关于此的讨论。 我习惯于几乎所有事情都使用名称空间。 我发现,当函数返回表达式时,不会针对无作用域函数计算该表达式,而是在使用命名空间时计算该表达式。 有谁知道这是怎么回事?
此脚本应显示变量 x 在调用第一个函数时不会递增,但会在第二个函数调用时递增。 我使用的是 JMP v17.0.0,因此这可能已得到修复,但我现在无法更新。
NewNamespace("varNamespace");
varNamespace:x = 0;<BR />
myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
myFunc();
Show(varNamespace:x);
NewNamespace("functionTest");
functionTest:myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
functionTest:myFunc();
show(varNamespace:x);自动化和脚本编写回复:使用命名空间时,从函数返回表达式会有所不同。我已更新到 17.2.0,并且可以确认问题仍然存在。
这是一个简化的示例,希望更容易阅读
x = 0;
myFunc = Function({}, {DEFAULTLOCAL},
Expr(::x++);
);
myFunc(); // Returns unevaluated expression: Expr(::x++)
Show(::x); // ::x=0;
NewNamespace("funcNS");
funcNS:myFunc = Function({},{DEFAULTLOCAL},
Expr(::x++);
);
funcNS:myFunc(); // Returns evaluated expression;
show(::x); // ::x=1;回复:使用命名空间时,从函数返回表达式会有所不同。很确定您已经发现了一个错误。 这是在命名空间中使用函数的两种方法的更简化的演示:
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(1+2)
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) );
类型(funcNS:f1()) = "表达式";
类型(funcNS:f2()) = "数字";
f1 返回正确答案; f2 错误地评估了答案。 我更喜欢 f2 方式(除了这个 bug),因为它更快:
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
1
)
);
funcNS:f2 = Function( {},
1
);
nf1 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf1++,
funcNS:f1()
);
Show( nf1 ); // 180K calls
nf2 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf2++,
funcNS:f2()
);
Show( nf2 ); // 260K calls
nf1=182239;
nf2=258929;
这两个问题可能是相关的。 命名空间函数调用内指定的项在使用之前不会被构造(它们保留为表达式)。这意味着创建命名空间时不会编译 f1,但每次使用时都会编译 f1,因此速度较慢。因此可能有一些专门的 C++ 代码来重新编译 f1 然后对其进行评估。 我的猜测是,f2 触发了相同的代码,但不会重新编译,因为 f2 已经编译了,然后错误地评估了 f2 的结果,大多数时候它是一个原始值,没有人注意到。 返回的 expr() 值确实计算为一个更简单的值,引起您的注意。
查看更多...
@朱利安- 很确定这是一个错误。 我认为不会有很多命名空间用户依赖于错误的行为。
回复:使用命名空间时,从函数返回的表达式有所不同。此事有任何更新吗?
据我所知,这种行为在 jmp 19 中仍然存在。
看起来返回值在 ns:f2 定义中被评估了两次:
ns = New Namespace("fns",
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
);
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Function({},
x = Expr(1+3);
return(Name Expr(Name Expr(x)));
);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = x;
如果其他人遇到这个问题,快速解决方法就像示例建议的那样;将返回值包装在“Name Expr”中,直到得到所需的结果
这行不通。我之前运行的时候已经在全局/此处作用域中定义了 ax 。似乎没有办法从像 ns:f2 这样定义的函数返回表达式。
这绝对看起来像是一个错误,我认为没有人会期望 ns:f2 返回与 ns:f1 或 f1 不同的值;函数代码完全相同。
回复:使用命名空间时,从函数返回的表达式有所不同。我会通过支持团队提交报告。
TS-00235798
回复:使用命名空间时,从函数返回的表达式有所不同。我还提交了一份报告:TS-00235515
支持人员的回应是,即使这种行为是意外的,也不认为这是一个错误。
根本原因正如您所描述的;基本上 ns:f2 函数被评估了两次。
这似乎也会影响初始命名空间创建之外定义的所有内容,如下面的更新示例所示。
建议的解决方案是将所有内容包装在一个表达式中 - ns:f2 = Expr(Function(....))
Names Default to Here(1);
delete symbols();
ns = New Namespace("fns",
{
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
),
e1 = Name Expr(expr(1 + a))
}
);
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Expr(Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
));
ns:e2 = Name Expr(expr(1 + a));
ns:e3 = Expr(Name Expr(expr(1 + a)));
show(ns:e1);
show(try(ns:e2, exception_msg));
show(ns:e3);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
ns:e1 = 1 + a;
Try(ns:e2,exception_msg) = {“名称未解析:a”(1,2,“a”,a /*******/)};
ns:e3 = 1 + a;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
回复:使用命名空间时,从函数返回的表达式有所不同。这种解决方法……很不幸。幸运的是,大多数用户并不想返回表达式,也不需要它。
回复:使用命名空间时,从函数返回的表达式有所不同。此外,大多数不幸的情况可以通过返回来缓解
表达式(表达式(1 + 1))
而不是让整个函数
表达式(函数(...
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(expr(1+2)) // <<<< extra expr wrapper
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) ); // expr, expr
回复:使用命名空间时,从函数返回的表达式有所不同。该解决方案确实更优雅,但它似乎只有当您可以直接编写要返回的表达式时才有效。
如果表达式存储在变量中,并且我们将其包装在 Expr() 中,则返回的值是具有变量名的表达式
在这种情况下,似乎唯一的解决方案似乎是将返回值包装在 Name Expr() 中,并将整个函数包装在 expr 中
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( funcNs:f4() ); // 1+2
这样做的一个很大的危险是,第二次评估似乎发生在函数的局部范围之外。
全局变量可能会遮蔽返回值,但不会引发错误。
Names Default to Here(1);
Delete Symbols(); // Only use if you dont have variables you need
funcNS = New Namespace("funcNS");
funcNS:f1 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
);
funcNS:f2 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(Expr(x));
);
funcNS:f3 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
x;
));
funcNS:f4 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
));
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( Try(funcNs:f1(), exception_msg) ); // Name Unresolved
Show(funcNs:f2()); // X
Show(funcNs:f3()); // 3
Show(funcNs:f4()); // x
Show(funcNs:f5()); // 1+2
// Second Evaluation happens outside Function scope
x = Expr(Expr(1+10));
Show(funcNs:f1()); // 1+10
回复:使用命名空间时,从函数返回的表达式有所不同。哇!
添加到Jmp 和 JSL 中的 Tiny Traps
回复:使用命名空间时,从函数返回的表达式有所不同。 Names Default to Here(1);
x= Expr("Hello");
funcNS = New Namespace("funcNS");
funcNS:f = Function( {}, {Default Local},
x = Expr(1+2);
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))
);
Show( funcNS:f() , Type( funcNS:f() ) ); // expr, expr
- 给函数一个在函数内部查找x的机会:命名为 Expr()而不是Expr()。
- 为了防止命名空间“两次”评估返回值,必须使用表达式()保护返回值。
- 这使得 Name Expr() 变得困难
Expr(名称 Expr())将阻止名称表达式()完成它的工作
- 在 99% 的此类情况下,可以使用Eval Expr()。
但这里很困难,因为Expression()在 JMP 中有两个用途:
- 评估表达式()中的“它在这里()”
-我保护你() - 作为命名空间问题的解决方案
不幸的是, 评估表达式()会对第一个Expression()感到满意并对其进行评估。
因此, Eval Expr(Expr(Expr(名称 Expr(x))))不会按照以下精神发挥作用:
Eval Expr(我保护你(它在这里(Name Expr(x))))
相反,它将其解释为
Eval Expr(它在这里(我保护你(Name Expr(x))))
因此它将返回
Expr(名称 Expr(x))
...然后 JMP 将在函数范围之外搜索x
- 所有表达式问题的最终解决方案是Substitute()
回复:JMP 中的表达式处理:Tipps 和 Trapdoors
- is there an easier solution than
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))