[英]Alter how arguments are processed before they're passed to sub MAIN
鑒於文檔和對較早問題的評論,根據要求,我制作了一個最小的可重現示例,展示了這兩個語句之間的區別:
my %*SUB-MAIN-OPTS = :named-anywhere;
PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True;
給定一個只有這個的腳本文件:
#!/usr/bin/env raku
use MyApp::Tools::CLI;
以及 MyApp/Tools 中名為 CLI.pm6 的模塊文件:
#PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True;
my %*SUB-MAIN-OPTS = :named-anywhere;
proto MAIN(|) is export {*}
multi MAIN( 'add', :h( :$hostnames ) ) {
for @$hostnames -> $host {
say $host;
}
}
multi MAIN( 'remove', *@hostnames ) {
for @hostnames -> $host {
say $host;
}
}
從命令行進行的以下調用不會產生可識別的子例程,但會顯示用法:
mre.raku add -h=localhost -h=test1
切換my %*SUB-MAIN-OPTS =:named-anywhere;
對於PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True;
如預期的那樣,將使用提供的兩個主機名打印兩行。
但是,如果這是在一個文件中完成的,如下所示,兩者的工作方式相同:
#!/usr/bin/env raku
#PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True;
my %*SUB-MAIN-OPTS = :named-anywhere;
proto MAIN(|) is export {*}
multi MAIN( 'add', :h( :$hostnames )) {
for @$hostnames -> $host {
say $host;
}
}
multi MAIN( 'remove', *@hostnames ) {
for @hostnames -> $host {
say $host;
}
}
我覺得這很難理解。 重現此內容時,請注意必須如何調用每個命令。
mre.raku remove localhost test1
mre.raku add -h=localhost -h=test1
因此,當在單獨的文件中使用my %*SUB-MAIN-OPTS =:named-anywhere;
. 而PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True;
總是有效。 對於 slurpy 數組,兩者在兩種情況下的工作方式相同。
問題是它在腳本和模塊中都不是同一個變量。
當然他們有相同的名字,但這並不意味着什么。
my \A = anon class Foo {}
my \B = anon class Foo {}
A ~~ B; # False
B ~~ A; # False
A === B; # False
這兩個類具有相同的名稱,但是是獨立的實體。
如果您查看其他內置動態變量的代碼,您會看到如下內容:
Rakudo::Internals.REGISTER-DYNAMIC: '$*EXECUTABLE-NAME', {
PROCESS::<$EXECUTABLE-NAME> := $*EXECUTABLE.basename;
}
這可以確保變量安裝到正確的位置,以便它適用於每個編譯單元。
如果你尋找%*SUB-MAIN-OPTS
,你唯一能找到的是這一行:
my %sub-main-opts := %*SUB-MAIN-OPTS // {};
這會在主編譯單元中查找變量。 如果沒有找到它,它會創建並使用一個空的 Hash。
因此,當您嘗試在主編譯單元以外的 scope 中執行此操作時,它不在該行可以找到的位置。
要測試添加是否可以解決問題,您可以將其添加到主編譯單元的頂部。 (加載模塊的腳本。)
BEGIN Rakudo::Internals.REGISTER-DYNAMIC: '%*SUB-MAIN-OPTS', {
PROCESS::<%SUB-MAIN-OPTS> := {}
}
然后在模塊中,這樣寫:
%*SUB-MAIN-OPTS = :named-anywhere;
或者更好的是:
%*SUB-MAIN-OPTS<named-anywhere> = True;
在嘗試了這個之后,它似乎工作得很好。
問題是, 那里曾經有過類似的東西。
考慮到它會減慢每個 Raku 程序的速度,它被刪除了。
盡管我認為它導致的任何減速仍然是一個問題,因為仍然存在的行必須查看是否存在該名稱的動態變量。
(給出的理由更多,坦率地說,我不同意所有這些理由。)
願一杯茶給未來思考事物意義的SO讀者帶來啟迪。 [1]
我認為Liz 對 SO 問一個類似問題的回答可能是一個很好的讀物,可以很好地解釋為什么模塊主線中的my
(就像一個較小our
)不起作用,或者至少確認核心開發人員知道它。
她后來對另一個 SO 的回答解釋了如何通過將my
放在RUN-MAIN
中來使用它。
關於事情為何如此的一個豐富資源是聲明 S06 的 MAIN 子程序部分(子程序概要) [2] 。
關鍵摘錄:
像往常一樣,開關被假定為第一個,並且第一個非開關之后的所有內容,或 - 之后的任何開關都被視為位置或 go 進入 slurpy 陣列(即使它們看起來像開關)。
所以看起來這是默認行為的來源,其中命名不能go 來自任何地方; 似乎@Larry [3]聲稱“通常的” shell 約定如所描述,並暗示這應該表明默認行為是原樣的。
自從 Raku 正式發布以來, RFC: Allow subcommands in MAIN讓我們走上了通往今天的:named-anywhere
選項的道路。 RFC 提出了一個非常強大的 1-2 拳——一個無可挑剔的兩行黑客散文/數據論點,很快就導致了粗略的共識,並帶有一個帶有此提交消息的工作代碼 PR:
允許 --named-switches 在命令行中的任何位置。
Raku 類似於 GNU,因為它具有“--double-dashes”,並且在遇到“--”時停止解釋命名參數,但與類似 GNU 的解析不同,它在遇到任何位置參數時也停止解釋命名參數。 此補丁通過允許在位置后命名為 arguments 以准備允許子命令,使其更像 GNU。
在 S06 的上述鏈接部分中, @Larry
還寫道:
通常,頂級 Raku“腳本”只是評估其匿名主線代碼並退出。 在主線代碼中,程序的 arguments 以原始形式從
@*ARGS
數組中獲得。
這里的重點是您可以在將@*ARGS
傳遞給MAIN
之前對其進行預處理。
繼續:
然而,在主線代碼的末尾,將使用
@*ARGS
中保留的任何命令行 arguments 調用MAIN
子例程。
請注意,正如 Liz 所解釋的,Raku 現在有一個在調用MAIN
之前調用的RUN-MAIN
例程。
然后是標准參數處理(可通過使用標准選項進行更改,其中目前只有:named-anywhere
一個,或諸如SuperMAIN
類的用戶區模塊,它添加了各種其他功能)。
最后@Larry
指出:
通過顯式調用
MAIN
可以輕松引入其他 [命令行解析] 策略。 例如,您可以使用語法解析您的 arguments 並將生成的Match
object 作為Capture
傳遞給MAIN
。
昨天您寫了一條評論,建議進行文檔修復。
我現在看到我們(集體)知道編碼問題。 那么為什么文檔是這樣的呢? 我認為您的 SO 和以前的 SO 的組合提供了足夠的軼事來支持至少考慮提出相反的文檔問題。 再一次,Liz 在其中一個 SO 中暗示可能會出現修復,至少對於our
的 s。 SO 本身可以說是 doc。 所以也許等待更好? 我會踢球,讓你決定。 如果您決定提交文檔問題,至少您現在有幾個 SO 要引用。
[1]我想明確一點,如果有人認為發布此 SO 有任何錯誤,那么他們是對的,錯誤完全是我的。 我向@acw 提到我已經進行了搜索,因此他們可以相當合理地得出結論,他們也沒有必要進行搜索。 所以,mea culpa,包括糟糕的咖啡啟發雙關語。 (糟糕的雙關語,不錯的咖啡。)
[2]當您了解 Raku 時,Imo 這些古老的歷史推測性設計文檔值得一讀再讀,盡管它們在某些方面已經過時。
[3] @Larry
出現在 Raku 文化中,作為 Larry Wall 等人(由 Larry 領導的 Raku 語言團隊)的一種有趣且方便的簡寫。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.