比较Tcl中列表的'n'个列表

时间:2019-03-07 07:40:33

标签: tcl

我有以下列表:

set w {1 2 3 4 5 6 7}
set x {1 2 3 4 5 8 9}
set y {1 2 3 4 0 9 1}
set z {1 2 3 4 5 6 7}

我想比较所有列表-有相应的索引-找出公共元素,并将这些公共元素附加到新列表中。如果比较上面的列表,我会发现1 2 3 4在所有列表中都是公用的,并且具有相同的索引,因此我的输出应该是:

{1 2 3 4}

如果没有公共元素(即使在第0个索引处),我的新列表也将为空。

我首先创建一个列表列表:

set l1 [list $w $x $y $z]

然后,我创建一个嵌套循环以比较列表并提取我的常见元素,我将使用列表'x'作为参考列表:

for {set j 0} {$j < [llength $x]} {incr j} {
    for {set i 1} {$i < [llength $l1]} {incr i} {

         set a [lindex [lindex $l1 $i] $j]

         if {$a == [lindex $x $j] && [lindex $l2 $j] == {}} {
                lappend l2 $a
         } else {
           break
         }
    }

}

我得到的是:

1 2 3 4 5

2 个答案:

答案 0 :(得分:2)

相似的实现,但是使用数组存储切片的唯一元素

set lists [list $w $x $y $z]
set common [list]

for {set i 0} {$i < [llength $w]} {incr i} {
    array unset elements
    foreach list $lists {
        set elements([lindex $list $i]) dummyvalue
    }
    set unique [array names elements]
    if {[llength $unique] == 1} {
        lappend common $unique
    }
}

puts $common  ;# => 1 2 3 4

答案 1 :(得分:1)

您实际上仅是将列表x与列表x进行比较,并且上面代码的实际输出(假设列表l2最初为空)实际上是:

1 2 3 4 5 8 9

您可能会问:

  

为什么比较列表x与列表x

您的内部循环从索引1(set i 1)开始,该索引在x中的列表l1中。

您可能还会问:

  

为什么不比较其他列表?

一旦您向l2添加了一些内容,lindex $l2 $j的下一个列表就永远不会为空,因此内部循环将中断。


那么,怎么做?

我可能会使用这样的东西:

set w {1 2 3 4 5 6 7}
set x {1 2 3 4 5 8 9}
set y {1 2 3 4 0 9 1}
set z {1 2 3 4 5 6 7}

set l1 [list $w $x $y $z]
set l2 [list]

set num [llength $x]

for {set i 0} {$i < $num} {incr i} {
  # This variable will tell us how many matched. 0 indicating none.
  set same 0

  for {set j 0} {$j < [llength $l1]} {incr j} {
    # lindex accepts multiple indices, see the manual
    # using x as reference, if the current list's ith element is the same as x's ith element...
    if {[lindex $l1 $j $i] == [lindex $x $i]} {
      incr same
    }
  }

  # if same reached 4, means 4 matched
  if {$same == 4} {
    lappend l2 [lindex $x $i]
  }
}

结果:

1 2 3 4

如果元素不匹配,则可以使内部循环中断,因此不会不必要地循环。

或者您可以检查内部循环是否中断,而不是计算匹配次数:

for {set i 0} {$i < $num} {incr i} {
  set broke 0

  for {set j 0} {$j < [llength $l1]} {incr j} {
    if {[lindex $l1 $j $i] != [lindex $x $i]} {
      set broke 1
      break
    }
  }

  # if it did not break, then we know all matched
  if {$broke == 0} {
    lappend l2 [lindex $x $i]
  }
}