反向打印列表:TCL

Print List in Reverse: TCL

我正在阅读 Don Libes 的 "Exploring Expect: A TCL-Based Toolkit ... "。

向 "Write a procedure to reverse a string. If you wrote an iterative solution, now write a recursive solution or vice versa."

提出的章节结尾问题

读到现在,我决定尝试以下方法:

set list {a b c d e f g}


for {set index [expr [llength $list]-1]} {$index>=0} {incr $index - 1} {

for {set i [expr [llength $list]-1]} {$i>=0} {incr $i - 1} {

    puts [lindex $list $index]

}

}

但是我得到以下错误:

Error(s), warning(s):
wrong # args: should be "incr varName ?increment?"
    while executing
"incr $i - 1"
    ("for" body line 3)
    invoked from within
"for {set index [expr [llength $list]-1]} {$index>=0} {incr $index - 1} {

for {set i [expr [llength $list]-1]} {$i>=0} {incr $i - 1} {

    puts [lind..."
    (file "source_file.tcl" line 4)
g

我发现我没有正确地增加 "index" 变量,尽管我不确定为什么。 另外,这种方法是递归的还是迭代的?

任何建议将不胜感激。


________SOLUTION____________________________________

根据@glenn提供的solution/approach正确代码如下:

set list {a b c d e f g}



for {set i [expr {[llength $list]-1}]} {$i>=0} {incr i -1} {

    puts [lindex $list $i]

}

下面他的 post 显示了许多其他示例。

首先,您要将 3 个参数传递给 incr:$index-1。如错误消息所示,incr 最多接受 2 个参数。将 "minus 1" 指定为 -1,不带空格。

注意错误信息:

wrong # args: should be "incr varName ?increment?"

注意它是怎么说的 varName——当您使用 $index 时,您传递的是变量的 value 而不是 name。删除 $

for {set i [expr {[llength $list]-1}]} {$i>=0} {incr i -1} {
# .................................................. ^ ^^
#                                              varname increment

请注意 expr 参数周围的大括号:这是养成的好习惯。

这里有几个不同的实现 lreverse

  1. 递归

    proc lreverse_rec {list} {
        if {[llength $list] == 0} return
        set procname [lindex [info level 0] 0]
        return [concat [lindex $list end] [$procname [lrange $list 0 end-1]]]
    }
    
  2. 尾递归

    proc lreverse_tail {list {result {}}} {
        if {[llength $list] == 0} {return $result}
        lappend result [lindex $list end]
        set procname [lindex [info level 0] 0]
        tailcall $procname [lrange $list 0 end-1] $result
    }
    
  3. while 循环 a(来自这个主题的 very old discussion

    proc lreverse_while_a {xlist} {
        set rlist $xlist
        for {
            set j 0
            set i [expr {[llength $xlist]-1}]
        } {$i>=0} {
            incr i -1
            incr j
        } {
            set rlist [lreplace $rlist[set rlist {}] $j $j [lindex $xlist $i]]
        }
        set rlist
    }
    
  4. while 循环 b

    proc lreverse_while_b {list} {
        set result {}
        while {[llength $list]} {
            lappend result [lindex $list end]
            set list [lrange $list[set list {}] 0 end-1]
        }
        return $result
    }
    
  5. while 循环,但有一些方便的 list-op procs

    proc pop {listVar} {
        upvar 1 $listVar list
        set result [lindex $list end]
        set list [lrange $list[set list {}] 0 end-1]
        return $result
    }
    proc push {listVar value} {
        upvar 1 $listVar list
        lappend list $value
    }
    proc shift {listVar} {
        upvar 1 $listVar list
        set result [lindex $list 0]
        set list [lrange $list[set list {}] 1 end]
        return $result
    }
    proc unshift {listVar value} {
        upvar 1 $listVar list
        set list [linsert $list[set list {}] 0 $value]
    }
    

    所有这些都导致了这个整洁的解决方案

    proc lreverse_listops {list} {
        set result {}
        while {[llength $list]} {push result [pop list]}
        return $result
    }
    

我无法访问解释 set list [... $list[set list {}] ...] 习语的网页,但它是在改变值时对 Tcl 内部结构的优化。更新:这里是:https://wiki.tcl-lang.org/page/K(感谢 mrcalvin)


以及一些基准测试

## put all the above procedures here ...

proc main {} {
    set list [list]
    for {set i 0} {$i <= 100} {incr i} {lappend list $i}
    foreach proc {
        lreverse_rec
        lreverse_tail
        lreverse_while_a
        lreverse_while_b
        lreverse_listops
    } {
        puts [format "%-20s %s" $proc [time [list $proc $list] 1000]]
    }
}

main

输出类似于

lreverse_rec         271.029 microseconds per iteration
lreverse_tail        293.496 microseconds per iteration
lreverse_while_a     75.541 microseconds per iteration
lreverse_while_b     53.962 microseconds per iteration
lreverse_listops     247.262 microseconds per iteration