使用可选参数调用TCL过程

时间:2014-04-08 08:42:49

标签: tcl

有一个TCL脚本,它有多个过程定义,在不同的命名空间中具有相似的名称func。程序如下:

proc func {a} {
    puts $a
}

所有这类程序只有一个参数a。所有这类程序都是从整个脚本中的一行调用的:

func $a

我需要在其他命名空间中创建另一个具有类似名称func的过程定义。但该程序将有两个参数。该过程也需要从具有相同名称的其他过程的同一行调用。程序如下所示:

proc func {a b} {
    puts $a
    puts $b
}

我现在需要修改调用所有过程func $a的行,以便它可以使用一个参数和具有两个参数的新过程调用所有过程。但是不能更改带有一个参数的过程定义。调用所有这些程序func $a的行应该是什么样的?

3 个答案:

答案 0 :(得分:4)

如果你想要一个可选参数,并且你知道如果没有提供可选值,你可以这样做:

proc func {a {b "the default"}} {
    puts "a is $a"
    puts "b is $b"
}

如果需要在运行时计算默认值,最简单的技术是一个神奇的哨兵值,在实际输入中不太可能发生。例如两个ASCII NUL字符(== Unicode U + 000000):

proc func {a {b "\u0000\u0000"}} {
    if {$b eq "\u0000\u0000"} {
        set b "default:$a"
    }
    puts "a is $a"
    puts "b is $b"
}

否则,您可以使用魔术args值来获取完整的参数列表,并“手动”完成所有工作:

proc func {a args} {
    if {[llength $args] == 0} {
        set b "the default..."
    } elseif {[llength $args] == 1} {
        set b [lindex $args 0]
    } else {
        error "bad number of arguments!"
    }
    puts "a is $a"
    puts "b is $b"
}

如果你这样做,info level内省探测器可以提供帮助,但事情会变得复杂......

答案 1 :(得分:2)

在Tcl代码中,根据参数的数量调用命令的两个实现之一是相当不寻常的。 可以执行此操作,前提是该命令的实现都不在全局命名空间中,并且在从包含相关实现的命名空间调用时,您不希望切换行为。

您所做的是在全局命名空间中创建一个过程(每个其他命名空间将在中查找命令,如果不存在于本地),然后显式链接到所需的实现。启用此功能需要的主要方法是在特定情况下计算出所需的实现(例如查看参数列表的长度)。

对于Tcl 8.6,您可以使用tailcall进行链接以获得最高效率:

proc ::func args {
    if {[llength $args] == 1} {
        tailcall ::impl1::func {*}$args
    } else {
        tailcall ::impl2::func {*}$args
    }
}

在Tcl 8.5中,你要写这个(这是解释器中的优化案例):

proc ::func args {
    if {[llength $args] == 1} {
        return [uplevel 1 [list ::impl1::func {*}$args]]
    } else {
        return [uplevel 1 [list ::impl2::func {*}$args]]
    }
}

在较旧的Tcl版本中,您使用类似的东西(速度较慢):

proc ::func args {
    if {[llength $args] == 1} {
        return [uplevel 1 ::impl1::func $args]
    } else {
        return [uplevel 1 ::impl2::func $args]
    }
}

当你使用完全错误的参数数量调用时,这对于处理获取正确类型的错误消息是完美的,尤其是如果两个实现都没有正式的可选参数。自动确定 可能完全不切实际!您最终必须编写额外的样板代码(这很明显,并且可以直接在所有版本的Tcl中使用):

proc ::func args {
    if {[llength $args] == 1} {
        tailcall ::impl1::func {*}$args
    } elseif {[llength $args] == 2} {
        tailcall ::impl2::func {*}$args
    } else {
        # Using the -errorcode is optional really
        return -code error -errorcode {TCL WRONGARGS} \
                "wrong # args: should be \"func a ?b?\""
    }
}

答案 2 :(得分:0)

我找到了答案的解决方案:https://stackoverflow.com/a/22933188/1601703。我们可以得到程序接受的参数个数,并制作将使用相应程序调用的相应if个参数:

set num [llength [info args func]]

    if {$num == 1} {
        func $a
    } elseif {$num == 2} {
        func $a $b
    }