2017-04-05 9 views
1

Bashがどのように再帰で動作するかを簡単にテストしましたが、結果はわかりません。Bash-4再帰の奇妙な結果

試験:

  1. シェル
  2. にグローバル変数Xを割り当てる割り当てlocal X同じ名前の変数local X
  3. を割り当てる機能f()グローバルXの値を作成local X=$X
  4. すなわち
  5. この関数を数回繰り返し、各再帰でグローバルXまたはを使用するかどうかを確認しますX内すなわち、各再帰で

    • 、以前f()からlocal XX新しい「グローバル」になり、私は2つの結果の一つが予想

機能の前のターンから各再帰が以前のスコープの下に新しいスコープを作成することを示す次のスコープ。

  • 各再帰では、以前のlocal Xの値は忘れられ、新しい各local X=$Xは最初に割り当てたグローバルX。これは、Bashが隣接するスコープを作成することを示します。
  • 私はこれらを取得しませんでした。私は奇妙な何かを得た。以下は、端末からのコピー貼り付けです。

    Iシェルのグローバル変数を割り当てる:

    [email protected]:~$ X=1 
    

    Iはlocal Xを作成するシェルで機能f()を作成し、それをグローバルXの値を割り当てwhileループに入り、に1を加算local X(私はローカルのものと仮定します)、新しいlocal X値を出力し、それ自身を呼び出します。 5または6回繰り返します。

    [email protected]:~$ f() { local X=$X; while [ $X -lt 6 ]; do X=$(($X + 1)); echo $X; sleep 1; f; done; } 
    

    私はf()を呼び出し、出力はうんざりです。

    [email protected]:~$ f 
    2 
    3 
    4 
    5 
    6 
    6 
    5 
    6 
    6 
    4 
    5 
    6 
    6 
    5 
    6 
    6 
    3 
    4 
    5 
    6 
    6 
    5 
    6 
    6 
    4 
    5 
    6 
    6 
    5 
    6 
    6 
    

    この時点で、それ自体は終了しました。予想通り、グローバルXは影響を受けませんでした。

    [email protected]:~$ echo $X 
    1 
    

    ここでは何が起こっているのですか?グローバルX、時にはlocal Xを使用することがありますか?もしあなたがここで何が起こっているのか分かっていれば、私はその細部を忘れないでください。

    最後に、ちょうど楽しみのために、出力のグラフ:

    1 == 
    2 === 
    3 ==== 
    4 ===== 
    5 ====== 
    6 ====== 
    7 ===== 
    8 ====== 
    9 ====== 
    10 ==== 
    11 ===== 
    12 ====== 
    13 ====== 
    14 ===== 
    15 ====== 
    16 ====== 
    17 === 
    18 ==== 
    19 ===== 
    20 ====== 
    21 ====== 
    22 ===== 
    23 ====== 
    24 ====== 
    25 ==== 
    26 ===== 
    27 ====== 
    28 ====== 
    29 ===== 
    30 ====== 
    31 ====== 
    

    仕様:

    • バッシュ-4.4。5(1)疑いで31本の出力ラインがありますが、
    • x86_64版のUbuntuピリッ
    • Linuxカーネル4.10.0-17ジェネリック
    • VMware Workstationの12の仮想マシン

    答えて

    2

    私はのように完全に彼の答えに@chepnerによって説明動的スコープを視覚化するための最善の方法は、わずかにあなたの機能を変更することであると思う:

    function f() { 
        local X="$X" 
        while [ "$X" -lt 6 ]; do 
        X=$((X + 1)) 
        echo "${FUNCNAME[*]}" "$X" # This will print the call stack 
        sleep 1 
        f 
        done 
    } 
    

    と値が増加する方法を参照してください場合出力列に従うと、各レベルで何が起こるかをデバッグすることができます。

    $ f 
    f f f f f 2 
    f f f f f f 3 
    f f f f f f f 4 
    f f f f f f f f 5 
    f f f f f f f f f 6 
    f f f f f f f f 6 
    f f f f f f f 5 
    f f f f f f f f 6 
    f f f f f f f 6 
    f f f f f f 4 
    f f f f f f f 5 
    f f f f f f f f 6 
    ... 
    
    1

    を-release 2 -1と同じです。何が起きるかというと、ループのすべての反復で関数がコピーされ、Xの値はその時点の値と同じになります。

    したがって、関数はすべてのレベルでループの残りの部分を2回完了し、2分木を作成します。視覚的に最も外側の部分は次のようになります。

    4 
    +--5 
    | +--6 
    | +--6 
    +--5 
        +--6 
        +--6 
    

    (これはあなたの最初の提案ではないです、私は正確にはわからない?。)

    これは、同じ結果を生成します(f 1で実行)が、パスを値は下位レベルの引数として明示的に指定されます。

    f() { 
        local X=$1; 
        while [ $X -lt 6 ]; do 
        X=$(($X + 1)); 
        echo $X; 
        f $X; 
        done; 
    } 
    
    +0

    非常に興味深いです。 – someguy54321

    2

    bashない静的(別名字句)スコープ、スコープ動的あります。つまり、local X=$X行を実行すると、グローバルレキシカルスコープで割り当てられた値に基づいて$Xの値は得られませんが、最も近いランタイムスコープにある値、つまりfだったスコープの値と呼ばれます。つまり、ローカル値は関数呼び出しではなく、そこから行われた呼び出しから表示されます。

    これは再帰に特有ではないことに注意してください。

    $ X=3 
    $ foo() { local X=5; bar; } 
    $ bar() { echo $X; } 
    $ bar 
    3 
    $ foo 
    5 
    $ echo $X 
    3 
    
    +0

    それは私の最初の仮説に最も近いですが、それが奇妙な出力をどのように説明するのかは明らかではありません。後で座って@ikkachuとあなたの答えを考える必要があります。 (私のポストを修正してくれてもありがとう) – someguy54321

    1

    機能をもっと冗長にして、制限として4を使用します。
    試すことができます。

    終了を監視する!!

    それが起こる正確に何が表示されます実行
    #!/bin/bash 
    depth=0 
    f() { 
        echo "depth =======$((++depth))" 
        echo "Received  $X" 
        local X=$X; 
        while ((X < 4)); do 
        ((X++)); 
        echo "Calling with $X"; 
        #sleep 1; 
        f; 
        done; 
        echo "exit with $X depth $((depth--))" 
    } 
    
    X=1 
    f 
    echo "final depth is $depth" 
    

    :私は、後に座って、このかかわらず、作業する必要があります、

    $ ./script 
    depth =======1 
    Received  1 
    Calling with 2 
    depth =======2 
    Received  2 
    Calling with 3 
    depth =======3 
    Received  3 
    Calling with 4 
    depth =======4 
    Received  4 
    exit with 4 depth 4 
    exit with 4 depth 3 
    Calling with 4 
    depth =======3 
    Received  4 
    exit with 4 depth 3 
    exit with 4 depth 2 
    Calling with 3 
    depth =======2 
    Received  3 
    Calling with 4 
    depth =======3 
    Received  4 
    exit with 4 depth 3 
    exit with 4 depth 2 
    Calling with 4 
    depth =======2 
    Received  4 
    exit with 4 depth 2 
    exit with 4 depth 1 
    final depth is 0