【问题标题】:Loop through all combinations of arrays (variable size)循环遍历数组的所有组合(可变大小)
【发布时间】:2014-03-18 08:44:03
【问题描述】:

在 tcl 中,我需要为未知数量的变量值的每个可能组合执行一个脚本。

用文字描述:

A 从 a0 -> a1 开始,步长为“da”

B 从 b0 -> b1 开始,步长为“db”

C 从 c0 -> c1 开始,步长为“dc”

....

变量的数量可以变化。注意:变量的名称事先不知道,“A”也可以称为“Ape”或其他任何名称。其他变量也是如此。

到目前为止我所拥有的是:

array set min_vals {A $a0 B $b0 C $c0 ...} ;# --> These are user-defined
array set max_vals {A $a1 B $b1 C $c1 ...} ;# --> These are user-defined
array set step_vals {A $da B $db C $dc ...} ;# --> These are user-defined

# First I determine the number of variables and the number of values they can have
set nr_vars [array size min_vals] ;# Determine nr of variables
set nr_vals [list] ;# --> Set empty list for nr of values for each variable

foreach var_name [array names min_vals] {
    set nr [expr {round( ( $max_vals(${var_name})-$min_vals(${var_name}) ) / $step_vals(${var_names}) )}]
    set nr_vals [concat $nr_vals $nr]
}

现在我需要以某种方式遍历每个可能的组合:

[A=a0, B=b0, C=c0]
[A=a0+da, B=b0, C=c0]
[A=a0+2*da, B=b0, C=c0]
...
...
[A=a1, B=b0, C=c0]
[A=a0, B=b0+db, C=c0]
[A=a0+da, B=b0+db, C=c0]
...
...
[A=a1, B=b1, C=c1]

我希望有一种简单的方法可以做到这一点。我能想到的唯一方法是使用包含所有组合的迭代次数的单个循环,并让每个迭代次数对应于特定组合。但我相信一定有一种不那么繁琐的方法。

_

编辑:

也许我并不清楚我到底想要什么。我不在乎实际输出。我的目标是将每个变量设置为正确的值并使用这些变量运行另一个脚本:

set A $a0
set B $b0
set C $c0
source run/some/script.tcl

并对ABC 的每个可能的值组合重复此操作。

【问题讨论】:

  • This 可以帮助您获得一些想法。

标签: tcl combinations permutation


【解决方案1】:

使用嵌套的 for 循环

for {set a $min_vals(A)} {$a <= $max_vals(A)} {incr a $step_vals(A)} {
    for {set b $min_vals(B)} {$b <= $max_vals(B)} {incr b $step_vals(B)} {
        for {set c $min_vals(C)} {$c <= $max_vals(C)} {incr c $step_vals(C)} {

            do something with [list $a $b $c]

        }
    }
}

啊,需要更有活力。嗯,

set variables {A B C}
array set min_vals  {A 1 B 10 C 100}
array set max_vals  {A 3 B 30 C 300}
array set step_vals {A 1 B 10 C 100}

proc build_loops {} {
    global variables 

    # create the "seed" code: what to do with the generated tuple
    set code "do_something_with \[list "
    foreach var $variables { 
        append code "\$[loop_var $var] " 
    }
    append code "]"

    # and wrap layers of for loops around the seed
    foreach var [lreverse $variables] {
        set loop_var [loop_var $var]
        set code [format {for {set %s $min_vals(%s)} {$%s <= $max_vals(%s)} {incr %s $step_vals(%s)} {%s}} \
            $loop_var $var \
            $loop_var $var \
            $loop_var $var \
            $code \
        ]
    }

    return $code
}

proc loop_var {varname} {
    return "loop_[string tolower $varname]"
}

proc do_something_with {args} {
    puts $args
}

set code [build_loops]
puts $code
eval $code
for {set loop_a $min_vals(A)} {$loop_a <= $max_vals(A)} {incr loop_a $step_vals(A)} {for {set loop_b $min_vals(B)} {$loop_b <= $max_vals(B)} {incr loop_b $step_vals(B)} {for {set loop_c $min_vals(C)} {$loop_c <= $max_vals(C)} {incr loop_c $step_vals(C)} {do_something_with [list $loop_a $loop_b $loop_c ]}}}
{1 10 100}
{1 10 200}
{1 10 300}
{1 20 100}
{1 20 200}
{1 20 300}
{1 30 100}
{1 30 200}
{1 30 300}
{2 10 100}
{2 10 200}
{2 10 300}
{2 20 100}
{2 20 200}
{2 20 300}
{2 30 100}
{2 30 200}
{2 30 300}
{3 10 100}
{3 10 200}
{3 10 300}
{3 20 100}
{3 20 200}
{3 20 300}
{3 30 100}
{3 30 200}
{3 30 300}

我保留了一个单独的变量名称列表:[array names a] 返回一个 无序 名称列表,并且(我假设)知道赋予 @987654326 的元组的顺序很重要@过程

【讨论】:

  • 感谢格伦,这似乎将我推向了正确的方向。我知道我需要一个嵌套循环,但我不确定如何实现它。但是,如果我正确阅读了您的脚本,您首先确定所需的循环数,然后将其放入字符串 $code 中,然后执行 $code,就好像它是一个单独的脚本一样。 %s$::max_vals 有什么作用?第 16 行的 `123 \n ` 是什么?
  • 我将代码更新为更简单。 %sformat command 的占位符(format 是 Tcl 的 sprintf
  • 这是一个很好的方法。然而,我不得不改变一些东西,让它按照我想要的方式工作。我需要使用非整数,因此我用while 替换了for,并使用set [expr {$%s + [lindex $step_vals %s]}] 在循环中添加了增量。我还将初始输入值更改为列表set min_vals {1 10 10}
猜你喜欢
  • 1970-01-01
  • 2014-07-14
  • 2015-02-22
  • 2015-04-29
  • 1970-01-01
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多