我有一个从'数据库'读取'字段'的c函数(dbread)。大多数这些领域都是单一的;但有时他们是多重的。所以我有c代码说
if valcount == 1
return string
else
make list
foreach item in vals
append to list
return list
因为我认为大多数时候人们都想要一个标量。
然而,这样做会导致一些奇怪的解析错误。特别是如果我想添加一个值
set l [dbread x] # get current c value
lappend l "extra value" # add a value
dbwrite x {*}$l # set it back to db
如果x具有单个值且该值包含空格,则lappend会解析错误。我得到一个包含3个项目的列表而不是2.我看到这是因为它传递的不是列表,它将其解析为列表并看到2个项目。
set l "foo bar"
lappend l "next val" # string l is parsed into list -> [list foo bar]
所以我最终得到[list foo bar {next val}]
无论如何,解决方案是让dbread始终返回一个列表 - 即使只有一个项目。我的问题是 - 这有什么缺点吗?是否有人会期待标量的90%的情况下会出现惊喜
替代方法是做我自己的lappend检查llength == 1和特殊情况
答案 0 :(得分:4)
我认为拥有一个始终返回结果列表的API更加清晰,无论是一个结果还是多个结果。然后不需要特殊的套管。
没有下行空间,只有上涨空间。
考虑一下,如果你不再返回单个标量,并且将来会返回一个单个值的情况,该值恰好是一个带有空格的字符串,该怎么办? 。如果您没有构造该单个值的列表,则将其视为两个值(因为Tcl会将字符串闪烁为两个事物的列表)。通过始终构建返回值列表,使用API的所有代码都将正确处理此问题。
仅仅因为Tcl没有严格打字并不意味着在不同时间返回不同类型的风格很好。
答案 1 :(得分:0)
我过去采用的一种方法(当每行的数据可能包含空值或空字符串时)是使用列表列表列表:
{{a b} {c d}} ;# two rows, each with two elements
{{{} b} {c d}} ;# two rows, first element of first row is null
;# llength [lindex [lindex {{{} b} {c d}} 0] 0] -> 0
{ { {{}} b } { c d } }
;# two rows, first element of first row is the empty string
;# llength [lindex [lindex {{{{}} b} {c d}} 0] 0] -> 1
看起来很复杂,但如果您将实际数据项视为不透明的数据结构并添加访问器以使用它,则实际上并非如此:
foreach row $db_result {
foreach element $row {
if {[db_isnull $element]} {
puts "null"
} elseif {![string length [db_value $element]]} {
puts "empty string"
} else {
puts [db_value $element]
}
}
}
不可否认,这比你想要的要复杂得多,但我认为值得一提。