0

大家好,我需要一些关于 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;
    }

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

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

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

有什么建议么???

4

1 回答 1

5

您在这里将数组与列表混淆了。

在 tcl 中,您不能将数组传递给函数或从函数中返回它们。此外,tcl 使用术语“数组”来表示 C++ 所称的“映射”或 Perl 和 Ruby 所称的“哈希”或 Javascript 所称的“对象”。该术语来自这样一个事实,即所有这些东西在计算机科学中通常被称为“关联数组”。所以 tcl 中的“数组”是键值对,而不是数据序列。

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

array set tcArrayName {$fileName $tcSpec};
return $tcArrayName;

生成语法错误:

can't read "tcArrayName": variable is array

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

return [array get tcArrayName]

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

"$fileName $tcSpec"

这也恰好是一个字符串。这就是实际的字符串:字符“$”后跟字符“f”、“i”、“l”、“e”、“N”、“a”、“m”、“e”等。文件名以及连接的项目名称和项目 ID。文字字符串“$fileName $tcSpec”。

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

array set tcArrayName {$fileName $tcSpec}

在 tcl 中,{xxx}其行为方式'xxx'与在 Perl 中相同。基本上它是一个不会被替换的文字字符串。如果您希望 tcl 进行$替换,那么您需要使用""分组:

array set tcArrayName "$fileName $tcSpec"

但这很脆弱,因为如果fileName包含空格,那么它会破坏代码。一种更强大的方法是:

array set tcArrayName [list $fileName $tcSpec]

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

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

proc B {fileID} {
  set fileName [::getFileName $fileID];  # getFileName returns filename from fileid
  set tcName "[set ItemName]_[set ItemId]"
  return [list $fileName $tcSpec]
}

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

can't read "ItemName": no such variable

这是因为 tcl 默认情况下不会将全局变量导入函数。因此变量ItemNameItemId不存在于B. 您要么需要通过以下global命令显式导入它:

proc B {fileID} {
  global ItemName
  global ItemId

  # ...
}

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

set tcName "[set ::ItemName]_[set ::ItemId]"

所以B应该是这样的:

proc B {fileID} {
  set fileName [::getFileName $fileID];  # getFileName returns filename from fileid
  set tcName "[set ::ItemName]_[set ::ItemId]"
  return [list $fileName $tcSpec]
}

这已经解决B了,但A也有一些问题:

can't read "file1": no such variable

我们需要对其进行一些修改以访问变量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;
}

这应该可以按您的预期工作。

如果您想在使用键值列表时稍微加快速度,那么您真的应该阅读dicts. 照原样,dicts 不会影响上面给出的代码,但会影响代码调用A。如果您打算使用 dicts,那么您可以将lappend上面的调用替换为dict append. 但是旧版本的 tcl 没有字典。

于 2013-07-03T06:16:32.343 回答