2017-10-07 28 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