シェルは再帰をサポートしますか?

シェルは再帰をサポートしますか?

シェルスクリプトで再帰関数を作成しようとしています。次のコードを考えてみましょう。

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。 2番目の機能だけが失敗しました。

あああああああああ

recur.sh[5]: 1 * : より多くのトークンが予想されます。

recur.sh[5]: 2 * : より多くのトークンが予想されます。

recur.sh[5]: 3 * : より多くのトークンが予想されます。

recur.sh[5]: 4 * : より多くのトークンが予想されます。

recur.sh[5]: 5 * : より多くのトークンが予想されます。

  • 私は何が間違っていましたか?
  • bashとzshは別の再帰構文をサポートしていますか?
  • なぜ2番目の関数(factorial)が失敗するのですkshか?

ポリスチレン:知っています。再帰は悪く、パフォーマンスも悪いです。代わりに通常のループを使用する必要があります。 bla bla bla.再帰が良いか悪いかを議論するのではなく、通常のシェルがそれをサポートしているかどうかを議論することです。私は単純な反復ループがトリックを実行するときに本番で再帰関数を送信するほど愚かではありません。 :)

答え1

  • 構文エラー:算術演算には引用符は使用されません。
  • 論理エラー: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 $?

どちらのコードもbashksh(もちろん93、88についてはわかりません)、で動作しますzsh。したがって、シェルは再帰をサポートします。

答え2

関数を呼び出すときに内部的に起こっていることとトラップを理解する限り、再帰は悪くありません。

まず、再帰関数がジョブを完了したときに実行される停止条件があることを確認してください。そうしないと、再帰関数ではなくループのない関数があります。

以下は変数と再エントリポイントです。各関数呼び出しは、リエントリポイントのアドレス(関数が返されると次の命令のアドレス)に情報をプッシュします。次に、tge 戻り値型のスペースを予約する必要があります。

以下は範囲の問題です。変数はパラメータとして渡され、ローカル変数は関数で宣言されます。このスペースは、関数が呼び出されるたびにスタックに割り当てられ、呼び出し関数が返されるとポップされるまでそのまま残ります。したがって、最終的にはスタックメモリが不足します(スタックオーバーフロー状況)。

私はDEC Vaxで教えるパスカルコースのための「ハノイタワー」プログラムを作成しました。バーやリングを必要なだけ追加して、プログラムやVMSと競合する条件を作成しようとしています。 3つの柱で1000回の回転を完了しましたが、プログラムは引き続き実行されます。実行に10分かかりましたが、IRが逃げました。

とにかく、あなたの質問に戻ります。ここで何が起こるのかは、変数が誤った範囲にあるようです。ローカル環境ではなく、グローバル環境で呼び出されるようです。したがって、関数による変数の変更は、関数のすべてのインスタンスに反映されます。すべての変更はローカルスコープで行われ、その値は呼び出し関数に返されます。 Bashなどの解釈された言語がローカル変数をサポートし、各変数をすべてのスクリプトロジックに表示できるかどうかはわかりません。

関連情報