【问题标题】:Tcl: recursive proc gives "out of stack space"Tcl:递归过程给出“堆栈空间外”
【发布时间】:2012-12-02 01:36:49
【问题描述】:

我是一名 Tcl 新手,为了学习,我正在尝试实现来自 Wikipedia 的归并排序算法伪代码:

function merge_sort(list m)
// if list size is 1, consider it sorted and return it
if length(m) <= 1
    return m
// else list size is > 1, so split the list into two sublists
var list left, right
var integer middle = length(m) / 2
for each x in m before middle
     add x to left
for each x in m after or equal middle
     add x to right
// recursively call merge_sort() to further split each sublist
// until sublist size is 1
left = merge_sort(left)
right = merge_sort(right)
// merge the sublists returned from prior calls to merge_sort()
// and return the resulting merged sublist
return merge(left, right)

在我的 Tcl 脚本中我这样做:

proc merge_sort { lst } {

   if { [llength $lst] <= 1 } {
       return $lst
   }

   set middle [expr {[llength $lst] / 2}]

   set left [lrange $lst 0 $middle]
   set right [lrange $lst [expr {$middle + 1}] [llength $lst]]

   set left [merge_sort $left]
   set right [merge_sort $right]

   return [merge $left $right]
}

根据我的调试尝试,一切正常,直到递归调用 merge_sort proc。

输出显示我“堆栈空间不足(无限循环?)”。老实说,我没有在代码中看到问题。我哪里错了?

【问题讨论】:

  • 根据这篇文章astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/lrange.n.html。命令 lrange 获取列表、第一个和最后一个元素。当列表的长度 == 2 时,中间 = 1,所以左边包含第 0 个元素和第 1 个元素,右边包含 0 个元素。所以你得到了无限递归。调试你的代码

标签: list recursion tcl


【解决方案1】:

一个问题是您的代码使用[lrange $lst 0 $middle] 来获取字符串的左侧部分,但由于lrange 的范围是从零开始的,这意味着对于两个-成员列表{7 8} 你会得到middle 等于1left 将是{7 8}

此外,由于您将[lrange $lst [expr {$middle + 1}] [llength $lst]] 用于right,因此对于同一个列表,它将导致一个空列表,因为[lrange $lst 2 2] 对于两个成员的列表是空的。

我还更改了正确的 lrange 以使用 end 作为最后一个索引,因为这是一种常见的最佳做法,而不是等效的 [expr {[llength $lst] - 1}]

proc merge_sort { lst } {

   if { [llength $lst] <= 1 } {
       return $lst
   }

   set middle [expr {[llength $lst] / 2}]

   #for each x in m *before* middle add x to left
   set left [lrange $lst 0 [expr {$middle - 1}]]

   #for each x in m *after or equal* middle add x to right
   set right [lrange $lst $middle end]

   set left [merge_sort $left]
   set right [merge_sort $right]

   return [merge $left $right]
}

【讨论】:

  • 需要{} 来禁止Tcl 代码的双重插值——一次由Tcl 解释器本身进行,另一次由expr 本身进行。除了使expr 对用户提供的数据的某些滥用具有弹性(此处不适用)之外,这只会使expr 运行得更快,因为{} 之间的内容将由expr 编译一次并缓存在对象中拿着那根绳子。请参阅this wiki page 了解更多信息。
  • 一个很好的答案还包括一些解释性文字......
  • 感谢@kostix 指出这一点。很高兴在使用 Tcl 这么多年后学到新东西。我已将此行还原为 OP 的原始代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
  • 2017-01-20
  • 2018-12-02
  • 2017-09-06
  • 2019-07-08
  • 2015-08-05
  • 2017-09-29
相关资源
最近更新 更多