长度为一的 tcl 列表和标量之间的区别?

发布于 2024-09-25 11:32:08 字数 859 浏览 2 评论 0原文

我有一个从“数据库”读取“字段”的 ac 函数(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 和特殊情况

I have a c function (dbread) that reads 'fields' from a 'database'. Most of those fields are single valued; but sometimes they are multi-valued. So I had c code that said

if valcount == 1
   return string
else
    make list
    foreach item in vals
        append to list
    return list

Because i thought most of the time people want a scalar.

However doing this leads to some odd parsing errors. Specifically if I want to add a value

set l [dbread x]            # get current c value
lappend l "extra value"     # add a value
dbwrite x {*}$l             # set it back to db

If x has single value and that value contains spaces the lappend parses wrong. I get a list with 3 items not 2. I see that this is because it is passed something that is not a list and it parses it to a list and sees 2 items.

set l "foo bar"
lappend l "next val" # string l is parsed into list -> [list foo bar]

so I end up with [list foo bar {next val}]

Anyway, the solution is to make dbread always return a list - even if there is only one item. My question is - is there any downside to this? Are there surprises lurking for the 90% case where people would expect a scalar

The alternative would be to do my own lappend that checks for llength == 1 and special cases it

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

海风掠过北极光 2024-10-02 11:32:08

我认为拥有一个总是返回结果列表的 API 会更简洁,无论是一个结果还是多个结果。这样就不需要特殊的外壳。

没有坏处,只有好处。

想一想,如果您不再返回单个标量,并且将来会返回一个单个值,而该值恰好是一个带有空格的字符串,该怎么办?如果您没有构造该单个值的列表,您会将其视为两个值(因为 Tcl 会将字符串闪烁到两个事物的列表中)。通过始终构建返回值列表,所有使用 API 的代码都将正确处理此问题。

仅仅因为 Tcl 没有严格的类型并不意味着在不同时间返回不同类型是好的风格。

I think it's cleaner to have an API which always returns a list of results, be it one result or many. Then there's no special casing needed.

No downside, only upside.

Think about it, what if you move away from returning a single scalar and have a case in the future where you're returning a single value that happens to be a string with a space in it. If you didn't construct a list of that single value, you'd treat it as two values (because Tcl would shimmer the string into a list of two things). By always constructing a list of return values, all the code using your API will handle this correctly.

Just because Tcl doesn't have strict typing doesn't mean it's good style to return different types at different times.

你爱我像她 2024-10-02 11:32:08

我过去采取的方法之一(当每行的数据可能包含空值或空字符串时)是使用列表列表的列表:

{{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]
        }
    }
}

诚然,比您正在寻找的复杂得多,但我认为值得一提。

One of the approaches I have taken in the past (when the data for each row could contain nulls or empty strings), was to use a list of lists of list:

{{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

It looks complicated, but it's really not if you treat the actual data items as an opaque data structure and add accessors to use it:

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]
        }
    }
}

Admittedly, far more complicated than you're looking for, but I thought it worth mentioning.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文