为什么 "dict get" 命令中没有内置异常处理?
Why isn't exception handling built into the "dict get" command?
当字典首次实现并添加到 Tcl 时,为什么 dict get 命令的实现方式允许在尝试检索字典中不存在的键的值时发生错误?
如果你想确保它是完全安全的,这就要求你每次使用它时都将命令包装在一个catch语句中。在我看来,像这样经常使用的命令总是会内置某种异常处理。
我想这就是为什么我们提供 dict exists
命令的原因。
您可能期望 dict get
到 return 该关键元素的空字符串不存在。但是,如果任何键本身的实际值是一个空字符串,那么像他们这样的实现将导致问题。
% set demo {id {} name Dinesh}
id {} name Dinesh
% dict get $demo id
% dict get $demo age
key "age" not known in dictionary
%
如果您想跳过 catch
,请使用 dict exists
。
dict
命令作为 ensemble 实现。这意味着您可以很容易地自己扩展它来实现这一目标。我喜欢称其为 dict get?
并在密钥不存在时将其设为 return 空值。我们可以按如下方式添加这个新的子命令:
proc ::tcl::dict::get? {dict key} {
if {[dict exists $dict $key]} {
return [dict get $dict $key]
}
return
}
namespace ensemble configure dict \
-map [linsert [namespace ensemble configure dict -map] end get? ::tcl::dict::get?]
如您所见,这简单地用 dict get
调用结束了 dict exists
调用,但由于整体更新,它作为 dict
命令的内置部分呈现。在使用中它看起来像这样:
if {[dict get? $meta x-check-query] eq "yes"} {
... do stuff ...
}
(这可以在 Tcl 测试套件 httpd test server 代码中看到。)
这是 Tcl(以及其他一些语言)中的常见设计选择。当像 dict get
(或者更常见的是 open
)这样的命令失败时,程序必须处理它,这意味着必须以某种方式提醒它失败。
最常见的选项是让失败的命令成为
Return 域外值(例如 null
在具有它的语言中),或
引发异常。
(例如,lsearch
命令要么 return 如果成功则为索引值,如果失败则为 -1(第一个选项)。dict get
命令要么 return 如果成功则为一个值,如果失败则引发异常(第二个选项)。)
第一个选项对于 dict get
命令来说并不实际,因为没有域外值。任何 Tcl 值都可能存储在字典中,因此您无法查看 dict get
的结果并知道它找不到值。空字符串通常在 Tcl 中用作伪空值,但很可能空字符串是字典中的实际值。
所以dict get
在失败时引发异常。也不是那么坏。异常有很多简洁的属性,例如直接将控制权交给最近的封闭处理程序,而不管它必须展开多少堆栈级别。
(实际上不可能处理命令中的所有异常:处理程序必须知道如何处理错误,而 dict get
无法知道。)
无论哪种方式,可能会失败的命令都需要包含在某种检查中。如果 foo
命令用于获取可能不可用的资源并且没有合理的默认值,则调用它的代码必须如下所示:
if {[set x [foo]] ne {BAD_RETURN_VALUE}} {
# use the resource
} else {
# deal with failure
}
或者像这样:
try {
foo
} on ok x {
# use the resource
} on error {} {
# deal with failure
}
或像这样(如果存在预测 foo
是否成功的谓词函数):
if {[foo-will-succeed]} {
set x [foo]
# use the resource
} else {
# deal with failure
}
这在每种情况下都差不多。由于域外值在 Tcl 中很少见,并且错误处理非常通用,因此通常偏爱谓词或异常策略。
patthoyts 已经展示了一种向 dict
集合添加错误抑制 getter 函数的方法。另一个相对轻量级的调用是
set foo [try {dict get $bar xyzzy} on error {} {}]
如果成功,return 是 dict get
调用的结果,如果失败,则为空字符串,并压缩任何引发的错误。
set foo [try {dict get $bar xyzzy} on error {} {return 42}]
此调用设置了一个默认 return 值以在失败时使用。
如果调用还是麻烦,可以做成命令:
proc dictget args {
set default {}
if {[lindex $args 0] eq {-default}} {
set args [lassign $args - default]
}
try {
dict get {*}$args
} on error {} {
set default
}
}
大纲是
dictget ?-default value? ?字典值? ?键 ...?
当字典首次实现并添加到 Tcl 时,为什么 dict get 命令的实现方式允许在尝试检索字典中不存在的键的值时发生错误?
如果你想确保它是完全安全的,这就要求你每次使用它时都将命令包装在一个catch语句中。在我看来,像这样经常使用的命令总是会内置某种异常处理。
我想这就是为什么我们提供 dict exists
命令的原因。
您可能期望 dict get
到 return 该关键元素的空字符串不存在。但是,如果任何键本身的实际值是一个空字符串,那么像他们这样的实现将导致问题。
% set demo {id {} name Dinesh}
id {} name Dinesh
% dict get $demo id
% dict get $demo age
key "age" not known in dictionary
%
如果您想跳过 catch
,请使用 dict exists
。
dict
命令作为 ensemble 实现。这意味着您可以很容易地自己扩展它来实现这一目标。我喜欢称其为 dict get?
并在密钥不存在时将其设为 return 空值。我们可以按如下方式添加这个新的子命令:
proc ::tcl::dict::get? {dict key} {
if {[dict exists $dict $key]} {
return [dict get $dict $key]
}
return
}
namespace ensemble configure dict \
-map [linsert [namespace ensemble configure dict -map] end get? ::tcl::dict::get?]
如您所见,这简单地用 dict get
调用结束了 dict exists
调用,但由于整体更新,它作为 dict
命令的内置部分呈现。在使用中它看起来像这样:
if {[dict get? $meta x-check-query] eq "yes"} {
... do stuff ...
}
(这可以在 Tcl 测试套件 httpd test server 代码中看到。)
这是 Tcl(以及其他一些语言)中的常见设计选择。当像 dict get
(或者更常见的是 open
)这样的命令失败时,程序必须处理它,这意味着必须以某种方式提醒它失败。
最常见的选项是让失败的命令成为
Return 域外值(例如
null
在具有它的语言中),或引发异常。
(例如,lsearch
命令要么 return 如果成功则为索引值,如果失败则为 -1(第一个选项)。dict get
命令要么 return 如果成功则为一个值,如果失败则引发异常(第二个选项)。)
第一个选项对于 dict get
命令来说并不实际,因为没有域外值。任何 Tcl 值都可能存储在字典中,因此您无法查看 dict get
的结果并知道它找不到值。空字符串通常在 Tcl 中用作伪空值,但很可能空字符串是字典中的实际值。
所以dict get
在失败时引发异常。也不是那么坏。异常有很多简洁的属性,例如直接将控制权交给最近的封闭处理程序,而不管它必须展开多少堆栈级别。
(实际上不可能处理命令中的所有异常:处理程序必须知道如何处理错误,而 dict get
无法知道。)
无论哪种方式,可能会失败的命令都需要包含在某种检查中。如果 foo
命令用于获取可能不可用的资源并且没有合理的默认值,则调用它的代码必须如下所示:
if {[set x [foo]] ne {BAD_RETURN_VALUE}} {
# use the resource
} else {
# deal with failure
}
或者像这样:
try {
foo
} on ok x {
# use the resource
} on error {} {
# deal with failure
}
或像这样(如果存在预测 foo
是否成功的谓词函数):
if {[foo-will-succeed]} {
set x [foo]
# use the resource
} else {
# deal with failure
}
这在每种情况下都差不多。由于域外值在 Tcl 中很少见,并且错误处理非常通用,因此通常偏爱谓词或异常策略。
patthoyts 已经展示了一种向 dict
集合添加错误抑制 getter 函数的方法。另一个相对轻量级的调用是
set foo [try {dict get $bar xyzzy} on error {} {}]
如果成功,return 是 dict get
调用的结果,如果失败,则为空字符串,并压缩任何引发的错误。
set foo [try {dict get $bar xyzzy} on error {} {return 42}]
此调用设置了一个默认 return 值以在失败时使用。
如果调用还是麻烦,可以做成命令:
proc dictget args {
set default {}
if {[lindex $args 0] eq {-default}} {
set args [lassign $args - default]
}
try {
dict get {*}$args
} on error {} {
set default
}
}
大纲是
dictget ?-default value? ?字典值? ?键 ...?