【问题标题】:Tcl increasing a value in a procedureTcl 在过程中增加一个值
【发布时间】:2023-04-08 11:11:01
【问题描述】:

我刚开始学习 Tcl。我想写一个简单的程序。 当程序启动时,它会打开一个浏览窗口来浏览文件。 在那里您可以选择要打开的文件。

然后会弹出一个窗口,询问您是否要选择另一个文件。 您选择的每个文件都必须放入一个数组中。

我必须遵循以下代码:

########## Defining the sub procedures ############
proc open_file {} {
    set n 0
    set title "Select a file"
    set types {
      {{GDS files} {.gds} }
      {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]

    set opendFiles($n) $filename
    set n [expr $n + 1]

    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
    if {$answer == yes } {
       open_file
    } else {
       show_files ($opendFiles)
    }
}

proc show_files {} {
    foreach key [array names opendFiles] {
        puts $opendFiles($key)
    }
}

########## Main Program ###########
open_file

我有以下问题。因为我总是记得proc'open_file'变量$n一直设置为0。但是不回忆整个子程序我不知道怎么回忆窗口的打开....

第二个问题是将数组发送到下一个过程。当我发送到 proc 'show_files' 时,我总是收到下一个错误:can't read "opendFiles": variable is array

我似乎找不到两个答案..

【问题讨论】:

  • 在 proc 之外设置/初始化 n。在 proc 中使用 incr ::n 来增加值。 ::n 是一个全局变量。
  • 用于将数组发送到另一个 proc 使用 array get & array set
  • 或者传递变量名并使用upvar
  • 没有人提到incr n 而不是set n [expr $n + 1]。在 Tcl 中支持 expr 的第一个参数也是一种很好的做法:set n [expr {$n + 1}]

标签: tcl


【解决方案1】:

全局变量有时非常有用,但我相信尽可能避免使用它们。在这种情况下,我宁愿在主程序中处理循环和数组,而不是 proc

此外,如果您在其他编程语言中使用数组,通常最好在 Tcl 中使用列表,例如:

proc open_file {} {
    set title "Select a file"
    set types {
        {{GDS files} {.gds} }
        {{All Files} * }
    }

    set filename [tk_getOpenFile -filetypes $types -title $title]
    return $filename
}

proc show_files {files} {
    foreach file $files {
        puts $file
    }
}

set openedFiles [list]
set answer yes

while {$answer == yes}
    lappend openedFiles [open_file]
    set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
}
show_files $openedFiles

如果你想简洁,可以写 show_files

proc show_files {files} {
    puts [join $files \n]
}

而且,既然它这么短,你可以把它放在一条直线上,而不是有另一个 proc。

最后,您是否考虑过如果用户在tk_getOpenFile 中按下取消,您想要做什么?在这种情况下,文件名将设置为空(零长度)字符串。你也可以

  • 忽略这些;或
  • 摆脱tk_messageBox 调用,让用户在输入任意数量的文件后按取消。

如果您只想忽略用户按下取消的那些时间,您可以这样做

set filename [open_file]
if {[string length $filename] > 0} {
    # The user entered a new filesname - add it to the list
    lappend openedFiles $filesname
} else {
    # The user pressed cancel - just ignore the filename
}

如果你想使用取消来跳出循环,那么主程序变成这样:

set openedFiles [list]
set filename dummy
while {[string length $filename] > 0} {
    set filename [open_file]
    if {[string length $filename] > 0} {
        lappend openedFiles $filename
    }
}
show_files $openedFiles

在这种情况下,您可能希望在主程序的开头放置一个消息框,告诉用户发生了什么。

【讨论】:

    【解决方案2】:

    要使变量的状态在对过程的调用之间保持不变,您需要使该变量在过程之外存在。最简单的方法是使用全局变量:

    # Initialize it...
    set n 0
    proc open_file {} {
        # Import it...
        global n
        ...
        # Use it...
        set openedFiles($n) $filename
        incr n
        ...
    }
    

    数组不是值,因此不能直接传递给另一个过程。您可以通过传入名称并使用upvar 1 将本地别名链接到调用堆栈帧中的变量来处理此问题:

    proc show_files {varName} {
        upvar 1 $varName ary
        foreach key [array names ary] {
            puts $ary($key)
        }
    }
    

    使用数组的名称调用,所以没有$:

    show_files openedFiles
    

    (您也可以使用array get openedFiles 传递数组的序列化以进行序列化并使用array set ary $serialization 进行反序列化,但这会带来一些开销。)

    您可能应该将openedFiles 变量添加到global 行,以便它在open_file 的所有调用中保持不变。

    【讨论】:

      【解决方案3】:

      为此,您需要全局变量。这对我有用:

      ########## Defining the sub procedures ############
      set n 0
      array set openedFiles {}
      
      proc open_file {} {
          set title "Select a file"
          set types {
              {{GDS files} {.gds} }
              {{All Files} * }
          }
      
          set filename [tk_getOpenFile -filetypes $types -title $title]
      
          set ::openedFiles($::n) $filename
          incr ::n
      
          set answer [tk_messageBox -message "Load another GDS file?" -type yesno -icon question]
          if {$answer == yes } {
              open_file
          } else {
              show_files
          }
      }
      
      proc show_files {} {
          foreach key [array names ::openedFiles] {
              puts $::openedFiles($key)
          }
      }
      
      ########## Main Program ###########
      open_file
      

      数组问题

      在 Tcl 中,您不能将数组发送到 procs。您需要使用 array get 将它们转换为列表,然后将此列表发送到 proc,然后使用 array set 再次将其转换回数组。

      【讨论】:

      • 希望您没有复制粘贴代码! :-) 如果您想学习它,您需要编写并理解它。 :-P
      • 我将代码输入到我的程序中。我现在犯了错误,我还需要学习很多知识;)
      • 我现在有另一个“问题”。当我重新启动脚本时,数组并不完全为空。它会记住以前的值。我试过这个来清除数组:array unset opensledFiles {} 但它似乎不起作用。任何指示或提示?谢谢 edit 找到了解决方案!忘记了数组名称后面的“*”。
      • show_files 中,您可以致电unset ::openedFiles; array set ::openedFiles {};set ::n 0;。当然是在循环之后。
      猜你喜欢
      • 1970-01-01
      • 2012-05-01
      • 2012-06-18
      • 2016-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多