为什么我们需要在tcl中嵌套过程

Pun*_*tal 4 nested tcl procedures

嗨,我一直在使用tcl脚本近一年,现在完全了解它的基本知识.但今天我刚刚遇到嵌套程序,这有点奇怪,因为我没有使用它.

无论如何,我在这里阅读有关嵌套proc的内容,但没有明确我们为什么需要它.

文章说,因为proc在命名空间中是全局的,所以要创建一个本地proc,你可以创建嵌套的proc.

    proc parent {} {
        proc child {} {
            puts "Inside child proc";
        }
        ...   
    }
Run Code Online (Sandbox Code Playgroud)

现在,我能想到的一种用法就像

    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
    }
Run Code Online (Sandbox Code Playgroud)

所以现在子proc是父proc的本地,只能在parent proc中使用.而且据我所知,当你想在父proc中的多个地方进行相同的处理时它很有用.

现在我的困惑是,这是嵌套proc的唯一用途还是还有其他我不理解的东西??? 我的意思是嵌套proc似乎是一种私有过程.

所以请详细说明并帮助我理解嵌套过程的使用.

Don*_*ows 7

Tcl没有嵌套程序.您可以proc在过程定义中调用,但这只是创建一个正常的过程(用于解析要创建的过程名称的名称空间将是调用者的当前名称空间,如报告所示namespace current).

为什么你会把proc里面proc?嗯,这样做的真正原因是当你想让外部命令充当工厂时,在调用它时创建命令.有时,要创建的命令的名称将由调用者提供,有时它将在内部生成(在后一种情况下,返回创建的命令的名称是正常的).出现的另一种情况是外部命令是(真实的)内部命令的某种代理,允许推迟创建真实命令,因为它在某种程度上是昂贵的; 如果是这种情况,内部过程将倾向于实际使用相同的名称创建 作为外部的一个,它将替换它(虽然不是执行堆栈框架; Tcl小心那个,因为否则会疯狂).

在你真的需要一个"内部程序"的情况下,实际上最好使用一个可以apply 代替的lambda术语.这是因为它是一个真正的值,可以存储在局部变量中,当外部过程终止时(或者如果你明确地unset表示变量或替换它的内容),它将自动消失.除了via之外,这个内部代码将无法访问外部代码的变量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
Run Code Online (Sandbox Code Playgroud)

使用内部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
}
Run Code Online (Sandbox Code Playgroud)

这有点容易出错并且非常慢,因为它会在每次调用时重新编译; 我们不再这样做了!