使用命名空間時,從函數返回表達式會有所不同。大家好,
從函數返回表達式時,我遇到了一些奇怪的行為,並且已經找不到任何關於此的討論。 我習慣於對幾乎所有事情都使用名稱空間。 我發現,當函數返回表達式時,不會針對無作用域函數計算該表達式,而是在使用命名空間時計算該表達式。 有誰知道這是怎麼回事?
此腳本應顯示變量 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()。
但這裡就比較困難了,因為JMP中使用表達式()有兩個目的:
- 評估表達式()中的“它在這裡()”
-我保護你() - 作為命名空間問題的解決方案
不幸的是, 評估表達式()會對第一個表達式()感到滿意並對其進行評估。
因此, 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)))