4

任意の深さでネストされたループを作成する必要があります。再帰ループは正しい方法のようですが、ループ変数をループの中で使用する方法がわかりません。たとえば、深さを 3 に指定すると、次のように動作するはずです

count = 1 
for i=1, Nmax-2
    for j=i+1, Nmax-1
        for k=j+1,Nmax
            function(i,j,k,0,0,0,0....) // a function having Nmax arguments
            count += 1
        end
    end
end

ループの深さを引数とするサブルーチンを作りたいです。

アップデート:

Zoltan が提案したスキームを実装しました。簡単にするためにPythonで書きました。

count = 0;

def f(CurrentDepth, ArgSoFar, MaxDepth, Nmax): 
    global count;
    if CurrentDepth > MaxDepth:
        count += 1;
        print count, ArgSoFar;
    else:
        if CurrentDepth == 1:
            for i in range(1, Nmax + 2 - MaxDepth):
                NewArgs = ArgSoFar;
                NewArgs[1-1] = i;
                f(2, NewArgs, MaxDepth, Nmax);
        else:
            for i in range(ArgSoFar[CurrentDepth-1-1] + 1, Nmax + CurrentDepth - MaxDepth +1):
                NewArgs = ArgSoFar;
                NewArgs[CurrentDepth-1] = i;
                f(CurrentDepth + 1, NewArgs, MaxDepth, Nmax);

f(1,[0,0,0,0,0],3,5)

結果は

1 [1, 2, 3, 0, 0]
2 [1, 2, 4, 0, 0]
3 [1, 2, 5, 0, 0]
4 [1, 3, 4, 0, 0]
5 [1, 3, 5, 0, 0]
6 [1, 4, 5, 0, 0]
7 [2, 3, 4, 0, 0]
8 [2, 3, 5, 0, 0]
9 [2, 4, 5, 0, 0]
10 [3, 4, 5, 0, 0] 

これを行うためのより良い方法があるかもしれませんが、今のところこれはうまくいきます。これはFortranで簡単にできそうです。手伝ってくれてどうもありがとう!!!

4

4 に答える 4

2

やりたいことを実現する方法の 1 つを次に示します。これは疑似コードです。コンパイルしてテストするには十分に書いていませんが、全体像はつかめるはずです。

関数を定義し、とりわけ整数配列引数fun1を取る関数を呼び出しましょう。おそらく次のようになります

<type> function fun1(indices, other_arguments)
    integer, dimension(:), intent(in) :: indices
    ...

あなたはこのように呼ぶかもしれません

fun1([4,5,6],...)

これの解釈は、関数が次のように 3 レベルの深さのループ ネストを使用することであるということです。

do ix = 1,4
    do jx = 1,5
        do kx = 1,6
        ...

もちろん、深さが実行時に決定されるループの入れ子を書くことはできません (とにかく Fortran ではそうではありません)。

do ix = 1, product(indices)

ループ内の個々のインデックスの値が必要な場合は、線形化されたインデックスを非平坦化する必要があります。配列インデックスを ND から 1-D に、またはその逆に変換するコードを書いているだけであることに注意してください。これは、コンパイル時に配列のランクを指定できる場合にコンパイラが行うことです。内部ループがインデックスの全範囲で実行されない場合は、より複雑で慎重なコーディングが必要ですが、難しくはないことを行う必要があります。

実際に何をしようとしているのかによって、これは良い方法である場合とそうでない場合があります。関数を作成するときにランクがわからない配列の各要素の値を計算する関数を作成しようとしている場合、前の提案は完全に間違っています。この場合、elemental関数を作成する必要があります。さらに情報が必要な場合は、質問を更新してください。

于 2013-08-16T16:13:03.557 に答える
1

List最初は空である引数を持つように関数を定義できます

void f(int num,List argumentsSoFar){

  // call f() for num+1..Nmax
  for(i = num+1 ; i < Nmax ; i++){
    List newArgs=argumentsSoFar.clone();
    newArgs.add(i);
    f(i,newArgs);
  }
  if (num+1==Nmax){
     // do the work with your argument list...i think you wanted to arrive here ;)
  }

}

Nmax注意: スタックは深度関数呼び出しを処理できる必要があります

于 2013-08-16T15:41:14.610 に答える
1

あなたが望むものを達成するためのさらに別の方法は、ハイパフォーマンスマークによる回答に基づいていますが、より一般的にすることができます:

subroutine nestedLoop(indicesIn)

  ! Input indices, of arbitrary rank
  integer,dimension(:),intent(in) :: indicesIn

  ! Internal indices, here set to length 5 for brevity, but set as many as you'd like
  integer,dimension(5) :: indices = 0

  integer :: i1,i2,i3,i4,i5

  indices(1:size(indicesIn)) = indicesIn

  do i1 = 0,indices(1)
    do i2 = 0,indices(2)
      do i3 = 0,indices(3)
        do i4 = 0,indices(4)
          do i5 = 0,indices(5)

            ! Do calculations here:
            ! myFunc(i1,i2,i3,i4,i5)

          enddo
        enddo
      enddo
    enddo
  enddo

endsubroutine nestedLoop

これで明示的にコード化されたネストされたループができましたが、特に指定しない限り、これらは 1 回のトリップ ループです。ネストされたループの深さに依存するランクの配列を作成する場合は、ランク 7 まで上げることができます。コンパイラがそれをサポートしている場合 (Fortran 2008)、ランク 15 まで上げることができます。あなたは今試すことができます:

call nestedLoop([1])
call nestedLoop([2,3])
call nestedLoop([1,2,3,2,1])

このルーチンを好みや目的の適用性に合わせて変更したり、例外処理を追加したりできます。

于 2013-08-16T19:39:45.297 に答える
0

OOP アプローチから、各ループは「ループ」オブジェクトで表すことができます。このオブジェクトは、それ自体の別のインスタンスを含みながら構築することができます。理論的には、これらを必要なだけ深くネストすることができます。

Loop1 が実行され、Loop2 が実行され、Loop3.. 以降が実行されます。

于 2013-08-16T15:39:31.943 に答える