在tcl中创建和附加现有数组

Pun*_*tal 0 arrays tcl

大家好我需要一些关于tcl数组的帮助.我在tcl中有两个程序,如下所示:

    proc A {} {
      set lst_A [list];
      if {true} {
        lappend lst_A [::B $file1];
      } else {
        foreach id $allId {
          lappend lst_A [::B $id];
        }
      }
      return $lst_A;
    }

    proc B {fileID} {
      set fileName [::getFileName $fileID];  # getFileName returns filename from fileid
      set tcName "[set ItemName]_[set ItemId]";
      array set tcArrayName {$fileName $tcSpec};
      return $tcArrayName;
    }
Run Code Online (Sandbox Code Playgroud)

现在我想创建一个数组,它将是一个键值对,其中key是某种文件id,value是与该id相关联的名称.现在问题是在proc A中,如果条件为真,我想创建只有一个键值对的数组,然后将该数组附加到lst_A,在这种情况下,它只包含一个项,即返回的数组.但是如果条件是假的,那么我遍历一些id并且对于每个id,我调用proc B并创建数组,然后将其附加到lst_A,在这种情况下,它将包含多个键值配对数组.

所以我编写了上面两个程序,并在阅读了tcl教程中的数组后创建了数组.但不确定这是正确的方式还是最优化的方式.

我的最终目标是创建一个lst_A或者我会说应该是一个数组,如果条件为真,它将只包含一个键值对,否则将是一个具有多个键值对的数组.因为我在proc B中创建数组,所以我只能想到将proc B中的键值对作为数组返回,然后将该数组附加到列表中,即proc A中的lst_A.

有什么建议???

sle*_*man 5

你在这里混淆数组和列表.

在tcl中,您无法将数组传递到函数中或从函数返回它们.此外,tcl使用术语"数组"来表示C++调用"maps"或Perl和Ruby调用"哈希"或Javascript称之为"对象".该术语来自于所有这些事物在计算机科学中通常被称为"关联阵列"的事实.所以tcl中的"数组"是键值对,而不是数据序列.

数组通常不会退化为值(或经验丰富的tcl程序员称之为"字符串").因此:

array set tcArrayName {$fileName $tcSpec};
return $tcArrayName;
Run Code Online (Sandbox Code Playgroud)

生成语法错误:

can't read "tcArrayName": variable is array
Run Code Online (Sandbox Code Playgroud)

实际上,您可以将数组的内容简化为可以返回的值.但您必须通过以下[array get]命令手动执行此操作:

return [array get tcArrayName]
Run Code Online (Sandbox Code Playgroud)

上面的命令将以tcArrayName两个元素列表的形式返回数组的内容:

"$fileName $tcSpec"
Run Code Online (Sandbox Code Playgroud)

这也恰好是一个字符串.这就是实际的字符串:字符'$'后跟字符'f','i','l','e','N','a','m','e'等.不是文件名和连接的项目名称和项目ID.文字字符串"$ fileName $ tcSpec".

那是因为你{}在这行代码中使用了分组:

array set tcArrayName {$fileName $tcSpec}
Run Code Online (Sandbox Code Playgroud)

在tcl中,{xxx}行为与'xxx'Perl中的行为相同.基本上它是一个不被替换的文字字符串.如果你想要tcl做$替换,那么你需要使用""分组:

array set tcArrayName "$fileName $tcSpec"
Run Code Online (Sandbox Code Playgroud)

但这很脆弱,因为如果fileName包含空格则会破坏代码.更强大的方法是:

array set tcArrayName [list $fileName $tcSpec]
Run Code Online (Sandbox Code Playgroud)

但是array在这种情况下使用a有点多余,因为您所做的只是使用键值列表初始化它(也称为dict更现代版本的tcl.BTW,您使用的是什么版本的tcl?)然后立即丢弃它并将其内容作为键值列表返回.

为什么不简单地返回键值列表:

proc B {fileID} {
  set fileName [::getFileName $fileID];  # getFileName returns filename from fileid
  set tcName "[set ItemName]_[set ItemId]"
  return [list $fileName $tcSpec]
}
Run Code Online (Sandbox Code Playgroud)

但我们仍然不清楚.如果您尝试执行,B您将看到此错误:

can't read "ItemName": no such variable
Run Code Online (Sandbox Code Playgroud)

那是因为tcl默认情况下不会将全局变量导入函数.这样的变量ItemName,并ItemId在不存在B.您需要通过以下global命令显式导入它:

proc B {fileID} {
  global ItemName
  global ItemId

  # ...
}
Run Code Online (Sandbox Code Playgroud)

或使用完全限定的变量名称通过全局命名空间访问它:

set tcName "[set ::ItemName]_[set ::ItemId]"
Run Code Online (Sandbox Code Playgroud)

所以B应该是这样的:

proc B {fileID} {
  set fileName [::getFileName $fileID];  # getFileName returns filename from fileid
  set tcName "[set ::ItemName]_[set ::ItemId]"
  return [list $fileName $tcSpec]
}
Run Code Online (Sandbox Code Playgroud)

这是B照顾,但A也有一些问题:

can't read "file1": no such variable
Run Code Online (Sandbox Code Playgroud)

我们需要修改它以访问变量file1:

proc A {} {
  set lst_A [list];
  if {true} { ;# assuming that there will be a valid condition here
    lappend lst_A [::B $::file1];
  } else {
    foreach id $allId {
      lappend lst_A [::B $id];
    }
  }
  return $lst_A;
}
Run Code Online (Sandbox Code Playgroud)

这应该按照您的预期工作.

如果你想在使用键值列表时加快一点速度,那么你应该真正阅读dicts.因为,dicts不会影响上面给出的代码,但它确实会影响代码调用A.如果您打算使用dicts,那么您可以用lappend上面的调用替换dict append.但旧版本的tcl没有dicts.