【问题标题】:Determine type of a variable in Tcl确定 Tcl 中变量的类型
【发布时间】:2011-09-15 08:36:48
【问题描述】:

我正在寻找一种在 Tcl 中查找变量类型的方法。例如,如果我有变量 $a 并且我想知道它是否是整数。

到目前为止,我一直在使用以下内容:

    if {[string is boolean $a]} {
    #do something
    }

这似乎适用于以下类型:
alnum, alpha, ascii, boolean, control, digit, double, false, graph, integer, lower, print, punct, space, true, upper, wordchar, xdigit

但是它无法告诉我我的变量是数组、列表还是字典。有谁知道一种方法来判断一个变量是否是这三个中的任何一个?

【问题讨论】:

  • 你需要什么版本的TCL?
  • 你能解释一下为什么你首先需要这个吗?我的意思是,由于 Tcl 本质上是一种无类型语言,因此您所要求的看起来像是自找麻烦。
  • @Kostix,当然。我需要这个的原因是创建一个将字典解析为 JSON 的过程。例如 JSON 中的字符串由 "" 包围,而整数则不是。此外,如果字典要包含另一个字典,则该字典应该在 JSON 对象中获取自己的 JSON 对象。
  • @Tom,然后我会选择另一种方法,并要求您的包的用户使用显式类型标签为序列化程序提供(人工)注释结构。比如serialize [object $mykey1 [int $value] $mykey2 [float $bar]]等等。试图推断无类型值的类型很容易出错,无法修复。更不用说当我希望字符串“0123”真正序列化为字符串而不被解释为 123 或 83 时的明显情况(有关有趣的东西,请参阅 wiki.tcl.tk/498
  • 我很困惑,如果你有 8.5 你应该有 string is list available 但它不在你提供的类型列表中

标签: variables tcl object-type


【解决方案1】:

Tcl 的变量没有类型(除了它们是否真的是变量的关联数组——即使用$foo(bar) 语法——你使用array exists),但Tcl 的值有。嗯,有点。 Tcl 可以在它认为合适的时候改变不同类型之间的值,并且不会暴露这些信息[*];你真正能做的就是检查一个值是否符合特定的类型。

此类一致性检查是使用 string is 完成的(由于丑陋的历史原因,您需要 -strict 选项):

if {[string is integer -strict $foo]} {
    puts "$foo is an integer!"
}

if {[string is list $foo]} {    # Only [string is] where -strict has no effect
    puts "$foo is a list! (length: [llength $foo])"
    if {[llength $foo]&1 == 0} {
        # All dictionaries conform to lists with even length
        puts "$foo is a dictionary! (entries: [dict size $foo])"
    }
}

注意所有值都符合字符串的类型; Tcl 的值是总是可序列化的。

[来自 cmets 的编辑]:对于 JSON 序列化,可以使用肮脏的 hack 来产生“正确”的序列化(严格来说,从 Tcl 的角度来看,将所有内容放在字符串中是正确的,但这对其他语言并没有帮助) Tcl 8.6。执行此操作的代码,最初发布在Rosetta Code 上是:

package require Tcl 8.6

proc tcl2json value {
    # Guess the type of the value; deep *UNSUPPORTED* magic!
    regexp {^value is a (.*?) with a refcount} \
        [::tcl::unsupported::representation $value] -> type

    switch $type {
        string {
            # Skip to the mapping code at the bottom
        }
        dict {
            set result "{"
            set pfx ""
            dict for {k v} $value {
                append result $pfx [tcl2json $k] ": " [tcl2json $v]
                set pfx ", "
            }
            return [append result "}"]
        }
        list {
            set result "\["
            set pfx ""
            foreach v $value {
                append result $pfx [tcl2json $v]
                set pfx ", "
            }
            return [append result "\]"]
        }
        int - double {
            return [expr {$value}]
        }
        booleanString {
            return [expr {$value ? "true" : "false"}]
        }
        default {
            # Some other type; do some guessing...
            if {$value eq "null"} {
                # Tcl has *no* null value at all; empty strings are semantically
                # different and absent variables aren't values. So cheat!
                return $value
            } elseif {[string is integer -strict $value]} {
                return [expr {$value}]
            } elseif {[string is double -strict $value]} {
                return [expr {$value}]
            } elseif {[string is boolean -strict $value]} {
                return [expr {$value ? "true" : "false"}]
            }
        }
    }

    # For simplicity, all "bad" characters are mapped to \u... substitutions
    set mapped [subst -novariables [regsub -all {[][\u0000-\u001f\\""]} \
        $value {[format "\\\\u%04x" [scan {& } %c]]}]]
    return "\"$mapped\""
}

警告: 不支持上述代码。 这取决于脏黑客。它很容易在没有警告的情况下破裂。 (但它确实工作。移植到 Tcl 8.5 需要一个很小的 ​​C 扩展来读取类型注释。)


[*] 严格来说,它确实提供了一个不受支持的接口,用于发现 8.6 中值的当前类型注释——作为 ::tcl::unsupported::representation 的一部分——但该信息是故意采用人类可读的形式,并且可能会在没有通知的情况下更改.它用于调试,而不是代码。此外,Tcl 在内部使用了很多不同的类型(例如,缓存的命令和变量名),在正常情况下您不会想探查这些类型;引擎盖下的事情相当复杂……

【讨论】:

  • 感谢您的反馈,但这似乎行不通。在 if {[string is list $foo]} { 部分。包含多个单词的字符串也被视为列表。此外,如果我有一个包含偶数项的列表,则此代码将始终将其分类为字典。我不确定我正在尝试做的事情是否可能,但您的代码也可能是最好的解决方案。
  • @Tom,这是因为包含多个单词的字符串与使用“list”命令创建的变量没有区别。例如,试试这个: set foo "ab bb dd";索引 $foo 1
  • string is list 8.5 还是 8.6?据我所知,Tom 可能无法使用它?
  • 它在 8.5 中,这是当前的标准生产级版本。使用 8.4(或之前!)将使您有资格被“过时”(或卡在 Java 中)。
  • @Tom:嗯,所有偶数长度列表都是有效的字典,尽管可能有不太理想的表示(例如,重复键)。
【解决方案2】:

其他答案都提供了非常有用的信息,但值得注意的是,很多人一开始似乎并不理解。

在 Tcl 中,值没有类型......他们的问题是它们是否可以用作给定类型。你可以这样想

string is integer $a

你没有问

$a 中的值是整数吗

你问的是

我可以将 $a 中的值用作整数吗

当您按照“这是一个整数”的思路思考时,考虑这两个问题之间的区别很有用。每个整数也是一个有效列表(一个元素)...所以 can be used 作为其中一个,两个 string is 命令都将返回 true(其他几个整数也是如此)。

【讨论】:

    【解决方案3】:

    如果您想处理 JSON,那么我强烈建议您阅读 Tcl wiki 上的 JSON 页面:http://wiki.tcl.tk/json

    在那个页面上,我发布了一个简单的函数,它将 Tcl 值编译为给定格式描述符的 JSON 字符串。我还发现该页面上的讨论内容非常丰富。

    【讨论】:

      【解决方案4】:

      对于你想要的数组array exists 对于你想要的字典dict exists

      对于我认为在 8.5 之前没有内置方式的列表,这是来自http://wiki.tcl.tk/440

      proc isalist {string} {
        return [expr {0 == [catch {llength $string}]}]
      }
      

      【讨论】:

      • 谢谢 :) 你能解释一下在这种情况下你将如何使用存在的 dict 吗?我试过这样使用它: if {[dict exists $testData2 nullval]} { #do something } 但如果变量不是字典,它将返回错误。
      • @Tom,在这种情况下,我会将您的 if 语句包装在“catch”中,这样​​您就可以处理变量不是 dict 的情况。
      【解决方案5】:

      判断一个变量是否为数组:

      proc is_array {var} {
          upvar 1 $var value
          if {[catch {array names $value} errmsg]} { return 1 } 
          return 0
      }   
      
      # How to use it
      array set ar {}
      set x {1 2 3}
      puts "ar is array? [is_array ar]"; # ar is array? 1
      puts "x is array? [is_array x]";   # x is array? 0
      

      【讨论】:

      • 你错过了[array exists varname] -- tcl.tk/man/tcl8.5/TclCmd/array.htm
      • jk 已经提到数组存在,我只是想提供一种不同的做事方式。
      • 我明白了。生成数组名称列表(只是为了扔掉它们)很昂贵:您可能需要array size
      【解决方案6】:

      对于判断一个值是否可用作字典的特定情况,tcllib 的dicttool package 有一个dict is_dict <value> 命令,如果<value> 可以作为一个值,则返回一个真值。

      【讨论】:

      • 回答比较迟,但另一个问题刚刚作为这个问题的副本关闭,所以看起来很合适。
      猜你喜欢
      • 1970-01-01
      • 2016-02-29
      • 2011-02-08
      • 2021-03-04
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 2013-05-29
      相关资源
      最近更新 更多