4

こんにちは、私はほぼ 1 年間 tcl スクリプトを使用しており、現在ではほぼ基本的なことを完全に理解しています。しかし、今日、ネストされたプロシージャに出くわしました。これは、使用できなかったので奇妙なものです。

とにかく、ここでネストされた proc について読みましたが、なぜそれが必要なのかについて明確な考えが得られませんでした。

この記事では、proc は名前空間でグローバルであるため、ローカル proc を作成するにはネストされた proc を作成すると述べています。

    proc parent {} {
        proc child {} {
            puts "Inside child proc";
        }
        ...   
    }

今私が考えることができる1つの使用法は次のようなものです

    proc parent {} {
        proc child {intVal} {
            puts "intVal is $intVal";
        }
        puts "[::child 10]";
        ... #some processing
        puts "[::child 20]";
        ... #some processing
        puts "[::child 30]";
        ... #some processing
        puts "[::child 40]";
        ... #some processing
        puts "[::child 50]";
        ... #some processing
    }

そのため、子 proc は親 proc に対してローカルになり、親 proc 内でのみ使用できます。また、私が理解しているように、その親プロシージャ内の複数の場所で同じ処理を行いたい場合にも役立ちます。

今、私の混乱は、これがネストされた proc の唯一の使用ですか、それとも私が理解していない他の何かがあるのでしょうか???. つまり、ネストされた proc は一種のプライベート proc のように見えます。

それで、それに光を当てて、ネストされたprocの使用を理解するのを手伝ってください。

4

2 に答える 2

7

Tcl にはネストされたプロシージャがありません。プロシージャ定義内で呼び出すことができますprocが、それは通常のプロシージャを作成するだけです (作成するプロシージャの名前の解決に使用される名前空間は、によって報告されるように、呼び出し元の現在の名前空間になりますnamespace current)。

procなぜあなたは中に入れるのprocですか?そうする本当の理由は、呼び出されたときにコマンドを作成するために、外部コマンドをfactoryとして機能させたい場合です。作成するコマンドの名前は、呼び出し元によって提供される場合もあれば、内部的に生成される場合もあります (後者の場合、作成されたコマンドの名前を返すのが通常です)。もう 1 つのケースは、外側のコマンドが (実際の) 内側のコマンドのある種のプロキシであり、実際のコマンドの作成を延期できる場合です。その場合、内部プロシージャは実際には同じ名前で作成される傾向があります外側のものとして、それを置き換えます(ただし、実行中のスタック フレームではありません。Tcl はそれについて注意を払っています。そうしないとクレイジーになるからです)。

本当に「内部手続き」が必要な場合は、apply 代わりにラムダ項を使用する方が実際には優れています。これは、ローカル変数に格納できる真の値であり、外部プロシージャが終了すると (またはunset変数を明示的に指定するか、その内容を置き換えると) 自動的に消えるためです。この内部コードは、外部コードの変数にアクセスできませんupvar。変数をバインドしながら値を返したい場合は、コマンド プレフィックスを使用し、変数を事前に指定された引数としてバインドするためのちょっとした工夫を含める必要があります。

proc multipliers {from to} {
    set result {}
    for {set i $from} {$i <= $to} {incr i} {
        lappend result [list apply {{i n} {
            return [expr {$i * $n}]
        }} $i]
    }
    return $result
}

set mults [multipliers 1 5]
foreach m $mults {
    puts [{*}$m 2.5]
}
# Prints (one per line): 2.5  5.0  7.5  10.0  12.5

インナーprocを使ってシミュレートするapply

applyコマンドは実際には内部プロシージャによってシミュレートできることに注意してください。これは、Tcl 8.4 以前で使用されていた手法です。

# Omitting error handling...
proc apply {lambdaTerm args} {
    foreach {arguments body namespace} $lambdaTerm break
    set cmd ${namespace}::___applyLambad
    proc $cmd $arguments $body
    set result [uplevel 1 [linsert 0 $args $cmd]]; # 8.4 syntax for safe expansion!
    rename $cmd ""
    return $result
}

これは、呼び出しのたびに再コンパイルされるため、ややエラーが発生しやすく、非常に低速でした。私たちはもうそれをしません!

于 2013-06-20T19:34:16.847 に答える
1

Tcl にはネストされた proc がありません。procマニュアルページから:

通常、 name は修飾されておらず (含まれている名前空間の名前は含まれません)、新しいプロシージャは現在の namespace に作成されます

(私のものを強調)

デモンストレーションするには:

% namespace eval foo {
    proc parent {} {
        proc child {} {
            puts "the child"
        }
        puts "the parent, which holds [child]"
    }
}
% foo::parent
the child
the parent, which holds 
% foo::child
the child

「内部」プロシージャを直接呼び出すことはできますが、これは外側のプロシージャに対してローカルではありません。

その wiki ページの議論であなたが見逃した項目の 1 つは、proc を真にローカルな proc にするためには、それを囲む proc の最後でそれを削除する必要があるということです:

% namespace eval foo {
    proc parent {} {
        proc child {} {
            puts "the child"
        }
        puts "the parent, which holds [child]"
        # now, destroy the inner proc
        rename child ""
    }
}
% foo::parent
the child
the parent, which holds 
% foo::child
invalid command name "foo::child"

ローカル プロシージャの使用に関しては、現在のプロシージャでのみ役立つ繰り返しタスクをカプセル化することが有益であることに同意します。とはいえ、私はそこにこだわるつもりはありません。明確なドキュメントやコード規約も同様に役に立ちます。

于 2013-06-20T17:32:07.600 に答える