【问题标题】:tcl add counter to identical list valuestcl 将计数器添加到相同的列表值
【发布时间】:2017-10-16 09:07:01
【问题描述】:

我有一个包含值的列表:

set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]

我想为后续相同的值添加一个计数器,如下所示:

numbered [ 101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106] 

到目前为止,我已经尝试了以下方法:

set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
set numbered [list ]
set previous [lindex $unnumbered 0]
set subcounter 1

foreach current $unnumbered  {

        if { $current eq $previous } {
                lappend numbered $current.$counter
                set previous $current
                incr subcounter
        } else {        
                lappend numbered $current
                set previous $current
                set subcounter 1
                }
}

结果几乎是我需要的。

101.1 101.2 101.3 102 102.1 103 104 105 105.1 105.2 106

对于除第一个值之外的所有值,计数器开始计数到晚。前 102 拉克“.1”

我该如何解决这个问题?

【问题讨论】:

    标签: list tcl counter


    【解决方案1】:

    问题是您的代码在将数字添加到numbered 时没有足够的信息。先获取信息,再应用。

    首先,创建一个列表,其中每个项目都是一个列表,其中包含$unnumbered 中的一个唯一数字和$unnumbered 中出现该数字的索引:

    lmap n [lsort -unique $unnumbered] {
       list $n [lsearch -all $unnumbered $n]
    }
    # => {101 {0 1 2}} {102 {3 4}} {103 5} {104 6} {105 {7 8 9}} {106 10}
    

    对于每个项目,将项目拆分为 n = 数字和 indices = 索引。检查你有多少索引。对于多个索引,添加这样的枚举数字:

    set i 0
    foreach index $indices {
        lappend numbered $n.[incr i]
    }
    

    对于单个索引,只需添加数字:

    lappend numbered $n
    

    整个程序如下所示:

    set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
    set numbered [list]
    
    foreach item [lmap n [lsort -unique $unnumbered] {
        list $n [lsearch -all $unnumbered $n]
    }] {
        lassign $item n indices
        if {[llength $indices] > 1} {
            set i 0
            foreach index $indices {
                lappend numbered $n.[incr i]
            }
        } else {
            lappend numbered $n
        }
    }
    

    文档: > (operator), foreach, if, incr, lappend, lassign, list, llength, lmap (for Tcl 8.5), lmap, lsearch, lsort, set

    如果您没有lmap,请参阅上面的链接。如果您没有lassign,请使用

    foreach {n indices} $item break
    

    改为。

    ETA如果可以放宽“对单例数字无索引”的要求,可以这样做:

    set previous {}
    lmap num $unnumbered {
        if {$num ne $previous} {
            set i 0
        }
        set previous $num
        format %d.%d $num [incr i]
    }
    

    另一个变种。这与 Jerry 的第二个建议非常相似,但老实说,直到我要提交这个,我才看到那个。这个假设$unnumbered 中没有元素是空字符串。

    set numbered [list]
    set rest [lassign $unnumbered current next]
    set i 0
    while 1 {
        if {$current eq $next} {
            lappend numbered $current.[incr i]
        } else {
            if {$i > 0} {
                lappend numbered $current.[incr i]
                set i 0
            } else {
                lappend numbered $current
            }
            set current $next
        }
        if {$next eq {}} break
        set rest [lassign $rest next]
    }
    

    【讨论】:

      【解决方案2】:

      另一种方法:维护一个 dict 以记录您到目前为止所看到的内容

      set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
      set count [dict create]
      set numbered {}
      foreach num $unnumbered {
          dict incr count $num
          lappend numbered "$num.[dict get $count $num]"
      }
      puts $numbered
      
      101.1 101.2 101.3 102.1 102.2 103.1 104.1 105.1 105.2 105.3 106.1
      

      使用数组稍微简单一些:利用incr 返回新计数的事实

      set numbered {}
      array set count {}
      foreach num $unnumbered {lappend numbered "$num.[incr count($num)]"}
      

      好的,我错过了单例条目不应该有后缀的要求。有这个,但它可能重新排列初始列表:

      set count [dict create]
      foreach num $unnumbered {dict incr count $num}
      set numbered {}
      foreach num [dict keys $count] {
          set c [dict get $count $num]
          if {$c == 1} {
              lappend numbered $num
          } else {
              for {set i 1} {$i <= $c} {incr i} {
                  lappend numbered "$num.$i"
              }
          }
      }
      puts $numbered
      
      101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106
      

      或者,这保持原来的顺序

      set count [dict create]
      foreach num $unnumbered {dict incr count $num}
      foreach key [dict keys $count] {
          if {[dict get $count $key] == 1} {
              set count [dict remove $count $key]
          }
      }
      
      set numbered {}
      foreach num [lreverse $unnumbered] {
          if {![dict exists $count $num]} {
              lappend numbered $num
          } else {
              lappend numbered "$num.[dict get $count $num]"
              dict incr count $num -1
          }
      }
      set numbered [lreverse $numbered]
      puts $numbered
      
      101.1 101.2 101.3 102.1 102.2 103 104 105.1 105.2 105.3 106
      

      【讨论】:

      • 很好的解决方案,但如果想要保持单例数字不被索引,则无济于事。
      • 对,错过了这个要求。
      【解决方案3】:

      O(n) 解决方案(单循环),我认为它看起来更像您最初想要实现的方式:

      set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
      set numbered [list]
      set previous ""
      set subcounter 1
      
      foreach current $unnumbered  {
        if {$previous == ""} {
          # First, do nothing except set $current to $previous later below
        } elseif {$previous == $current} {
          lappend numbered $previous.$subcounter
          incr subcounter
        } else {
          if {$subcounter > 1} {
            lappend numbered $previous.$subcounter
          } else {
            lappend numbered $previous
          }
          set subcounter 1
        }
        set previous $current
      }
      
      if {$subcounter > 1} {
        lappend numbered $current.$subcounter
      } else {
        lappend numbered $current
      }
      

      循环基本上将一个数字添加到numbered 列表中,因此最后一个数字需要最后一个if。当然,这只有在您知道 unnumbered 已排序时才有效。


      编辑:实际上这更接近!因为你已经可以得到$previous,你可以从列表的下一个元素开始循环,最后一个元素之后循环一次(注意如果lindex提供了一个超出范围的索引,我们会得到一个空白,这使事情变得更容易)。

      set unnumbered [list 101 101 101 102 102 103 104 105 105 105 106]
      set numbered [list]
      set previous [lindex $unnumbered 0]
      set subcounter 1
      
      for {set i 1} {$i <= [llength $unnumbered]} {incr i} {
        if {$previous == [lindex $unnumbered $i]} {
          lappend numbered $previous.$subcounter
          incr subcounter
        } else {
          if {$subcounter > 1} {
            lappend numbered $previous.$subcounter
          } else {
            lappend numbered $previous
          }
          set subcounter 1
        }
        set previous [lindex $unnumbered $i]
      }
      

      【讨论】:

        猜你喜欢
        • 2019-10-08
        • 2022-01-04
        • 1970-01-01
        • 2014-02-14
        • 1970-01-01
        • 2011-07-06
        • 1970-01-01
        • 1970-01-01
        • 2020-02-25
        相关资源
        最近更新 更多