有一个TCL脚本,它有多个过程定义,在不同的命名空间中具有相似的名称func
。程序如下:
proc func {a} {
puts $a
}
所有这类程序只有一个参数a
。所有这类程序都是从整个脚本中的一行调用的:
func $a
我需要在其他命名空间中创建另一个具有类似名称func
的过程定义。但该程序将有两个参数。该过程也需要从具有相同名称的其他过程的同一行调用。程序如下所示:
proc func {a b} {
puts $a
puts $b
}
我现在需要修改调用所有过程func $a
的行,以便它可以使用一个参数和具有两个参数的新过程调用所有过程。但是不能更改带有一个参数的过程定义。调用所有这些程序func $a
的行应该是什么样的?
答案 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
}