shell 支持遞歸嗎?
我正在嘗試在我的 shell 腳本中編寫遞歸函式。考慮以下程式碼:
function printA { if [[ "$1" = 0 ]]; then return else echo "a$(printA $(("$1" - 1)))" fi } printA 10 function factorial { if [[ "$1" = 0 ]]; then return 1 else return $(( "$1" * $(factorial $(( $1 - 1 )) ) )) fi } echo $(factorial 5)
程式碼失敗:
bash
(3.0)recur.sh:第 5 行:“10” - 1:語法錯誤:預期操作數(錯誤標記為“”10“ - 1”)
一種
recur.sh:第 16 行:“1”:語法錯誤:預期操作數(錯誤標記為“”1“”)
recur.sh:第 16 行:“2”:語法錯誤:預期操作數(錯誤標記為“2”“)
recur.sh:第 16 行:“3”:語法錯誤:預期操作數(錯誤標記為“3”“)
recur.sh:第 16 行:“4”:語法錯誤:預期操作數(錯誤標記為“”4“”)
recur.sh:第 16 行:“5”:語法錯誤:預期操作數(錯誤標記為“5”“)
zsh
(4.2.1)printA:1: 錯誤的數學表達式:非法字元:"
一種
階乘:5:錯誤的數學表達式:非法字元:“
然而,它部分成功地使用了
ksh88
. 只有第二個功能失敗:啊啊啊啊
recur.sh
$$ 5 $$: 1 * : 預計會有更多代幣 recur.sh
$$ 5 $$: 2 * : 預計會有更多代幣 recur.sh
$$ 5 $$: 3 * : 預計會有更多代幣 recur.sh
$$ 5 $$: 4 * : 預計會有更多代幣 recur.sh
$$ 5 $$: 5 * : 預計會有更多代幣
- 我做錯什麼了嗎?
- bash 和 zsh 是否支持另一種遞歸語法?
- 為什麼第二個函式(
factorial
)失敗了ksh
?PS:我知道,遞歸是邪惡的,性能很差,我應該改用正常循環,bla bla bla。我不是在討論遞歸是好是壞,而是常見的 shell 是否支持它。當簡單的迭代循環可以解決問題時,我還沒有愚蠢到在生產中發送遞歸函式:)
- 語法錯誤:在算術評估中不使用引號。
- 邏輯錯誤:您正在混合 STDOUT 和
return
值。將值作為 STDOUT 傳遞:
function factorial { (( $1 )) && echo $(( $1 * $( factorial $(( $1 - 1 )) ) )) || echo 1 } factorial 5
或者
return
他們:function factorial { (( $1 )) || return 1 factorial $(( $1 - 1 )) return $(( $1 * $? )) } factorial 5 echo $?
兩種程式碼都可以在
bash
,ksh
(93 當然,不知道 88)和中工作zsh
,所以我猜是的,shell 確實支持遞歸。
遞歸不是邪惡的;只要您知道呼叫函式時內部發生的情況以及陷阱。
首先,確保有一個停止條件,當遞歸函式完成其任務時將執行該條件。如果你不這樣做,你就沒有遞歸函式,而是一個 wndless 循環。
接下來,變數和重新進入點。對函式的每次呼叫都會將資訊推送到堆棧上;重入點的地址(函式返回時下一條指令的地址)。然後你必須為 tge 返回值類型保留空間。
接下來是范圍問題。變數作為參數傳遞,局部變數在函式中聲明。每次呼叫函式時,都必須在堆棧上分配這個空間,並一直保留在那裡,直到返回呼叫函式時彈出。因此,最終您將耗盡堆棧記憶體(堆棧溢出條件)。
我為在 DEC Vax 上教授的帕斯卡課程編寫了一個“河內塔”程序。我試圖通過能夠添加任意數量的極點和環來創建一個可能導致我的程序或 VMS 崩潰的條件。我在 3 個桿子上打了 1000 圈,程序仍然執行。跑了大約10分鐘,但我跑了。
無論如何,回到你的問題。這裡似乎發生的是您的變數在錯誤的範圍內 - 看起來它們是在全域環境中呼叫的,而不是在本地環境中呼叫的。因此,函式對變數所做的任何更改都會反映在函式的所有實例中。所有更改都需要在本地範圍內進行,然後將值返回給呼叫函式。我不確定像 bash 這樣的插入式語言是否支持局部變數,並且可能使每個變數對所有腳本的邏輯都是可見的。