2017-10-07 93 views
1

我想重新定义TCL的foreach,因为foreach的多个应用程序打破了一些代码。重新定义TCL foreach

详细的问题描述

在码符发生在Vivado,使用TCL脚本编写的程序的实际情况。他们提供了一个指定get_<someObject>方法的API,该方法返回对象列表(实际上其行为与列表类似,但由xilinx实现,用于缓存/打印有点不同)。要处理这些列表,我使用foreach。当我第一次使用列表中的foreach时,我在循环变量中得到<objects>

但是,如果我在一个对象上应用foreach,则该对象将转换为表示其名称的字符串。问题是,API函数get_property需要一个对象。

在应用foreach两次时,似乎没有道理,如果您编写两个函数来获取对象列表并对其进行操作,则可能会发生这种情况。

proc a {obj} { 
    puts "logging [llength $obj] objects:" 
    foreach o $obj { 
     puts "$o has column index [get_property COLUMN_INDEX $o]" 
    } 
} 


proc b {obj} { 
    foreach o $obj { 
     a $o 
     puts "working on $o" 
     get_property COLUMN_INDEX $o 
    } 
} 

如果我们现在调用这些函数如下

a [get_clock_regions X0Y0] # ok (the list is turned into single objects in foreach) 
b [get_clock_regions X0Y0] # crashes inside a (the list from get_clock_regions is 
          # turned into objects by the foreach in b 
          # a is then called with a single object, 
          # the foreach now turns the object into a string 
          # representing the name, but get_property 
          # does not work on strings => error 

我问,如果行为可以被固定在这个question on the Xilinx Forums,但是我正在寻找的同时解决方法。

我所试图做的

我想落实的同时解决方法。虽然我可以做

a [list $o] 

内B,我认为这将是更好,如果我可以重新定义foreach不会打破上述情况。这样,如果赛灵思可以解决这个问题,我可以简单地放弃我对foreach的重新定义。 (我期望foreach工作相同,无论我有一个元素或单个元素的列表,据我的理解,这是在TCL相同)。

我与重新定义的foreach问题是:

  1. 我怎么知道,如果foreach被称为
    • foreach {varA varB} {valueList} {...}
    • foreach {varA varB} {valueList1 valueList2} {...}
  2. 有没有一种方法来测试,如果我有一个对象或一个包含一个对象的列表?想法是两个检测,如果它只是一个对象,如果这样包装成一个列表,然后可以打开回正常的foreach对象,但我不知道如何检测这种情况。

大纲代码我喜欢写:

proc safeForeach {varnames valueLists body} { 
    if { thereAreMultiple valueLists } {   # this issue no 1 
     foreach valueList $valueLists { 
      if {wouldDecayToString $valueLists} { # this is issue no 2 
       set valueLists [list $valueLists] 
      } 
     } 
    } else { 
     if {wouldDecayToString $valueLists} {  # this is issue no 2 again 
      set valueLists [list $valueLists] 
     } 
    } 
    #the next line should be wraped in `uplevel` 
    foreach $varnames $valueLists $body 
} 

回答

3

问题的根本原因是(用你的例子)调用PROC a,这需要一个列表,只有一个标量值当它在proc b中被调用时。您的解决方法是将a as,a [list $o]作为“解决方法”。它将单个值转换为一个元素的列表。包含一个元素的列表与单个值不同。由于Tcl中的列表只是特殊格式的字符串,如果proc a的参数包含空格,它将被视为一个列表,分解为空白分隔的组件。虽然Tcl足够灵活,可以让你从根本上重新定义语言,但我认为这是一个“仅仅因为你可以,并不意味着你应该”的例子。我不认为这个案例足够引人注目,因为一些代码重构会使问题消失。

+0

虽然我认为你是部分正确的,但你必须明白,我不能改变对象或其行为(阻止它衰减成字符串)。这让我改变了另一端的代码:虽然你声称一个对象和一个对象的列表是不同的东西,但我要求部分区别。在没有空格或数字的字符串的情况下,我看不出有什么不同。 ('if {1 == [list 1]} {puts {where is the difference}}'),我查看带有空格的字符串作为* multiple *字符串的列表,可以通过'foreach'拆除。 ... – ted

+0

...然而,无论你经常通过'foreach'('foreach a“string”{foreach b $ a {foreach c $ b {puts $ c} }}''仍然会'puts'字符串''。如果它们实际上不同,我会很高兴能够学会一种方法来检测差异。我很抱歉在这方面有点头疼,但是我发现找到了这个问题相当令人沮丧,因为'puts'并没有真正的帮助(我仍然看到相同的输出),并且希望找到一种解决方法,这并不需要将来的程序员不需要关于这个细节。 – ted

+1

我明白你的沮丧。我们所有人都遇到过一些意想不到的结果,但这是由于嵌入式空白字符串作为列表处理时,与不含内嵌空白字符串的字符串转换的结果不同,这只是一个“细节“的列表是特殊格式的字符串和列表元素包含空格需要谨慎处理。我通常的解决方案是谨慎对待简单的价值观和名单之间的区别,以及对后面的人进行大量有用的评论。 –

2

最终,问题在于您不想将简单值视为列表。处理这个问题的方法之一的确使用[list $a]来列出不应该被虐待的值,但另一种方法是将a过程更改为采用多个参数,以便您可以将它们作为内部列表对待同时具有引用自动应用:

# The args argument variable is special 
proc a {args} { 
    puts "logging [llength $args] objects:" 
    foreach o $args { 
     puts "$o has column index [get_property COLUMN_INDEX $o]" 
    } 
} 

然后,你可以这样调用:

a $o 

当在一个列表传递给这样的过程中,要使用扩展语法:

a {*}[get_clock_regions X0Y0] 

该领先的{*}是Tcl中的一个伪操作符,意思是将其余的参数解释为列表,并将列表中的单词作为自己的参数传递。

+0

虽然我喜欢带* * variadic参数列表*(+1)的aprach,但如果必须传递两个不同的列表或单个对象,则无法工作,因为无法知道一个列表的结束位置,其他开始。实际上我想实现的是'foreach var $ singleObject'等于'set var $ singleObject'(以防$'singleObject'不是列表),而不是'set var [getStringRepresentation $ singleObject]'。一个问题可能是,我不知道软件供应商如何在tcl中实现''。 – ted