Определить тип переменной в Tcl - PullRequest
8 голосов
/ 15 сентября 2011

Я ищу способ найти тип переменной в Tcl.Например, если у меня есть переменная $ a, и я хочу знать, является ли она целым числом.

До сих пор я использовал следующее:

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

, и это, кажется, прекрасно работаетдля следующих типов:
alnum, alpha, ascii, boolean, control, digit, double, false, граф, целое число, lower, print, пункт, пробел, true, upper, wordchar, xdigit

он не способен сказать мне, может ли моя переменная быть массивом, списком или словарем.Кто-нибудь знает способ сказать, является ли переменная одним из этих трех?

Ответы [ 5 ]

12 голосов
/ 15 сентября 2011

Переменные 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 всегда сериализуемые.

[РЕДАКТИРОВАТЬ из комментариев]: Для сериализации JSON можно использовать грязные хаки для создания «правильной» сериализации (строго говоря, если поместить все в строку,быть правильным с точки зрения 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\""
}

Предупреждение: Указанный выше код не поддерживается. Itзависит от грязных хаков.Это может сломаться без предупреждения.(Но он работает . Портирование на Tcl 8.5 потребует небольшого расширения C для считывания аннотаций типов.)


[*] Строго говоря, он предоставляет неподдерживаемый интерфейсдля обнаружения текущей аннотации типа значения в 8.6 - как часть ::tcl::unsupported::representation - но эта информация находится в сознательно понятной для человека форме и может быть изменена без объявления.Это для отладки, а не кода.Кроме того, Tcl использует довольно много различных типов внутри себя (например, кэшированные имена команд и переменных), которые вы не захотите проверять в обычных условиях;под капотом все довольно сложно…

4 голосов
/ 07 октября 2011

Все остальные ответы дают очень полезную информацию, но стоит отметить кое-что, что многие люди, кажется, не пускают поначалу.

В Tcl значения не имеют типа ... они задаются вопросом, могут ли они использоваться в качестве данного типа. Вы можете думать об этом так

string is integer $a

Вы не спрашиваете

Является ли значение в $ a целым числом

То, что вы спрашиваете, это

Могу ли я использовать значение в $ a как целое число

Полезно учитывать разницу между этими двумя вопросами, когда вы думаете по типу «это целое число». Каждое целое число также является допустимым списком (из одного элемента) ... так что can be used как и любая другая, и обе команды string is вернут true (как и несколько других для целого числа).

1 голос
/ 21 сентября 2011

Если вы хотите иметь дело с JSON, я настоятельно рекомендую вам прочитать страницу JSON на вики Tcl: http://wiki.tcl.tk/json.

На этой странице я разместил простую функцию, которая компилирует значения Tcl в строку JSON с учетом дескриптора форматирования. Я также считаю обсуждение на этой странице очень информативным.

0 голосов
/ 15 сентября 2011

Чтобы определить, является ли переменная массивом:

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
0 голосов
/ 15 сентября 2011

Для массивов, которые вы хотите array exists для диктов, которые вы хотите dict exists

для списка. Я не думаю, что есть встроенный способ до 8.5 ?, это от http://wiki.tcl.tk/440

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