重新定义TCL foreach
我想重新定义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问题是:
- 我怎么知道,如果
foreach
被称为foreach {varA varB} {valueList} {...}
foreach {varA varB} {valueList1 valueList2} {...}
- 有没有一种方法来测试,如果我有一个对象或一个包含一个对象的列表?想法是两个检测,如果它只是一个对象,如果这样包装成一个列表,然后可以打开回正常的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
}
问题的根本原因是(用你的例子)调用PROC a
,这需要一个列表,只有一个标量值当它在proc b
中被调用时。您的解决方法是将a
as,a [list $o]
作为“解决方法”。它将单个值转换为一个元素的列表。包含一个元素的列表与单个值不同。由于Tcl中的列表只是特殊格式的字符串,如果proc a
的参数包含空格,它将被视为一个列表,分解为空白分隔的组件。虽然Tcl足够灵活,可以让你从根本上重新定义语言,但我认为这是一个“仅仅因为你可以,并不意味着你应该”的例子。我不认为这个案例足够引人注目,因为一些代码重构会使问题消失。
最终,问题在于您不想将简单值视为列表。处理这个问题的方法之一的确使用[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中的一个伪操作符,意思是将其余的参数解释为列表,并将列表中的单词作为自己的参数传递。
虽然我喜欢带* * variadic参数列表*(+1)的aprach,但如果必须传递两个不同的列表或单个对象,则无法工作,因为无法知道一个列表的结束位置,其他开始。实际上我想实现的是'foreach var $ singleObject'等于'set var $ singleObject'(以防$'singleObject'不是列表),而不是'set var [getStringRepresentation $ singleObject]'。一个问题可能是,我不知道软件供应商如何在tcl中实现'
虽然我认为你是部分正确的,但你必须明白,我不能改变对象或其行为(阻止它衰减成字符串)。这让我改变了另一端的代码:虽然你声称一个对象和一个对象的列表是不同的东西,但我要求部分区别。在没有空格或数字的字符串的情况下,我看不出有什么不同。 ('if {1 == [list 1]} {puts {where is the difference}}'),我查看带有空格的字符串作为* multiple *字符串的列表,可以通过'foreach'拆除。 ... – ted
...然而,无论你经常通过'foreach'('foreach a“string”{foreach b $ a {foreach c $ b {puts $ c} }}''仍然会'puts'字符串''。如果它们实际上不同,我会很高兴能够学会一种方法来检测差异。我很抱歉在这方面有点头疼,但是我发现找到了这个问题相当令人沮丧,因为'puts'并没有真正的帮助(我仍然看到相同的输出),并且希望找到一种解决方法,这并不需要将来的程序员不需要关于这个细节。 – ted
我明白你的沮丧。我们所有人都遇到过一些意想不到的结果,但这是由于嵌入式空白字符串作为列表处理时,与不含内嵌空白字符串的字符串转换的结果不同,这只是一个“细节“的列表是特殊格式的字符串和列表元素包含空格需要谨慎处理。我通常的解决方案是谨慎对待简单的价值观和名单之间的区别,以及对后面的人进行大量有用的评论。 –