配列の初期化

配列を初期化するさまざまな方法について説明します。

以下のように、配列を初期化できます。

外部

配列は外部データによって初期化できます。その場合、宣言の形式は次のようになります。


int a[1..2] [1..3] = ...; 

また、実際の初期化は、データ・ソースで提供されます。IBM ILOG OPL では、データ・ソースは個別の .dat ファイルになります。

  • 値をリストする方法

    これまでに示したほとんどの例で、配列はこの方法で初期化されています。実際、OPL の多次元配列は配列の配列であり、それに応じて初期化する必要があります。例えば、次の宣言では、

    
    /* .mod file */
    int a[1..2][1..3] = ...;
    /* .dat file */
    a = [
    [10, 20, 30],
    [40, 50, 60]
    ];
    

    最初の次元の 1 次元配列の初期化を行うことで、2 次元配列を初期化します。この初期化を任意の次元数に一般化する方法は簡単です。

  • ペアを指定する方法

    次の宣言のように、ペア (添字、値) を指定して配列を初期化することもできます。

    
    /* .mod file */
    int a[Days] = ...;
    /* .dat file */
    a = #[
    “Monday”: 1,
    ”Tuesday”: 2,
    ”Wednesday”: 3,
    ”Thursday”: 4,
    ”Friday”: 5,
    ”Saturday”: 6,
    ”Sunday”: 7
    ]#;
    
    注:
    1. (添字、値) ペアによって初期化を指定する場合、区切り文字には、[ および ] の代わりに #[ および ]# を使用する必要があります。

    2. ペアの順序は任意です。

    これらの 2 つの形式の初期化は、以下のように任意に組み合わせることができます。

    
    /* .mod file */
    int a[1..2][1..3] = ...;
    /* .dat file */
    a = #[
    2: [40, 50, 60],
    1: [10, 20, 30]
    ]#; 
    

内部

.dat ファイルと同じ構文を使用して、配列を内部で (つまり、.mod ファイルで) 初期化できます。ここで、配列項目は、初期化中に評価される式にできます。ペア #[]# の構文は、内部初期化では使用できません。

前処理命令

以下のように、前処理命令で配列を初期化することもできます。

range R = 1..8;
int a[R];
execute {
  for(var i in R) {
    a[i] = i + 1;
  }}

この場合、a[1] = 2, a[2] = 3 などのように配列を初期化します。データの前処理を参照してください。

汎用配列

OPL は、汎用配列 (式によって項目が初期化される配列) もサポートします。これらの汎用配列により、アプリケーションのモデル化が大幅に簡素化されます。 以下の宣言では、

int a[i in 1..10] = i+1; 

a[i] の値が i+1になるような 10 個のエレメントの配列を宣言しています。 汎用配列は、当然ながら次のように多次元にできます。

int m[i in 0..10][j in 0..10] = 10*i + j; 

この場合、エレメント m[i][j]10*i + j に初期化されます。汎用配列は、いくつかの単純な変換を実行する際に有用です。例えば、汎用配列を使用して、以下のように単純な方法で行列を置き換えることができます。

int m[Dim1][Dim2] = ...; 
int t[j in Dim2][i in Dim1] = m[i][j]; 

一般的には、汎用配列を使用して、単純な方法で配列の索引の順序を変えることができます。

添字付きの汎用配列

汎用的な方法で配列を初期化する際の柔軟性を高めるために、上記の『汎用配列』で説明されているように、OPL では項目値に加えて添字値も制御できます。構文を説明するために、同じ例を次のように表すことができます。

int a[1..10] = [ i-1 : i | i in 2..11 ]; 
int m[0..10][0..10] = [ i : [ j : 10*i+j ] | i,j in 0..10 ]; 

この構文は、上記の『ペアの指定』で説明されているように、#[ および ]# で区切られた索引によって .dat ファイル内の配列を初期化するために使用される構文と類似しています。この構文を使用すると、データの添字付けに使用される配列を効率的に初期化できます。

oilDB.mod の例には、初期化を実行する execute ブロックが含まれています。 以下の代わりに、

GasType gas[Gasolines];
execute {
  for(var g in gasData) {
    gas[g.name] = g
  }
}

同じ内容を以下のように添字付きの汎用配列の構文を使用して表すことができます。

GasType gas[Gasolines] = [ g.name : g | g in gasData ];

同様に、transp4.mod に含まれている以下の構文は、

float Cost[Routes];
execute INITIALIZE {
  for( var t in TableRoutes ) {
      Cost[Routes.get(t.p,Connections.get(t.o,t.d))] = t.cost;
   }
}

上記のように表され、これは以下と同等です。


float Cost[Routes] = [ <t.p,<t.o,t.d>>:t.cost | t in TableRoutes ];
注:
  1. モデルがより明確かつ理解しやすくなるため、可能な場合には、汎用配列または添字付きの汎用配列を使用することをお勧めします。

  2. 添字が複数回一致する場合でも、警告は発行されず、この添字に対して設定された最新の値が保持されます。

    例:

    int n=5;
    
    {int} s= {1,3,4,2,5};
    sorted {int} s2=asSet(1..n);;
    reversed {int} s3=asSet(1..n);;
    
    int x[1..n]=[maxl(n-i,i): i | i in s];
    int x2[1..n]=[maxl(n-i,i): i | i in s2];
    int x3[1..n]=[maxl(n-i,i): i | i in s3];
    
    execute
    {
     writeln(x);
     writeln(x2);
     writeln(x3);
    } 

    の結果は、以下のようになります。

    [0 0 2 4 5]
    [0 0 3 4 5]
    [0 0 2 1 5]