D言語入門記事 ~配列(2)~

アバター
tk-xleader
記事: 158
登録日時: 14年前
連絡を取る:

D言語入門記事 ~配列(2)~

投稿記事 by tk-xleader » 13年前

前の記事に引き続いて、今日も配列についてやっていこうかと思います。

[1] 配列のスライス

 配列は、その一部分を取り出して、別の配列として扱うことが出来ます。これは、スライスと呼ばれる演算です。スライスの結果は、動的配列型になります。例えば、a[3]~a[7]のスライスを取り出したい場合、次のように記述します。

a[3..8];

a[3..7]ではないことに注意してください。(最後の要素a[8]は含まれない。)

CODE:

import std.stdio;

void main(){
	int a[15] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
	
	write("a[] = [");
	foreach(c;a){
		writef("%d,",c);
	}
	writeln("]");
	
	int b[] = a[3..8]; //スライス演算
	
	write("b[] = [");
	foreach(c;b){
		writef("%d,",c);
	}
	writeln("]");
}
実行結果
a[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,]
b[] = [4,5,6,7,8,]


 ちなみに、配列aと配列bは、同じ領域を共有します。つまり、スライス演算はコピーを作らないのです。それと、配列の全要素をスライスするには、a[]と記述します。だから、次の二つは同じ意味を持ちます。

a[0..a.length];
a[]
► スポイラーを表示
► スポイラーを表示
[2] スライスに代入する。

 配列スライスが代入式の左辺値として使われた場合、スライスに対する代入演算として扱われます。右辺値が要素型の値である場合、スライス全体にその値が代入されます。

CODE:

import std.stdio;

void main(){
	int a[15] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
	
	write("a[] = [");
	foreach(c;a){
		writef("%d,",c);
	}
	writeln("]");
	
	a[3..8] = 32; //ここでスライスに代入している。
	
	write("a[] = [");
	foreach(c;a){
		writef("%d,",c);
	}
	writeln("]");
}
実行結果
a[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,]
a[] = [1,2,3,32,32,32,32,32,9,10,11,12,13,14,15,]


 このように、a[3]~a[7]の要素に対して、32という値が代入されているわけです。もちろん、配列全体に代入したい場合、a[]=32; のように記述すればいいのです。
► スポイラーを表示
 また、代入式の右辺が配列型の場合は、配列要素のコピー演算になります。この場合、左辺と右辺で要素数が異なっていたり、両辺に領域の重複がある場合、エラーになります。

CODE:

import std.stdio;

void main(){
	int a[15] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
	int b[15];
	
	write("a[] = [");
	foreach(c;a){
		writef("%d,",c);
	}
	writeln("]");
	
	b[] = a; //aの全要素を 配列bにコピーする
	
	write("b[] = [");
	foreach(c;b){
		writef("%d,",c);
	}
	writeln("]");
	
	b[2..5] = a[6..9]; //b[2..5]に、a[6..9]をコピーする。
	
	write("b[] = [");
	foreach(c;b){
		writef("%d,",c);
	}
	writeln("]");
}
実行結果
a[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,]
b[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,]
b[] = [1,2,7,8,9,6,7,8,9,10,11,12,13,14,15,]


 これらの演算は、並列命令としてコンパイルされる場合がありますから、要素番号の順にコピーされるという仮定は出来ません。それを保証したい場合は、残念ながら、forループを使うほかにないでしょう。

[3] 配列の連結

 要素型が同じ2つの配列は連結することが出来ます。連結する為には、~演算子、もしくは~=演算子を用います。~演算子を用いた場合、何があろうと、必ず新しい配列オブジェクトが生成されます。~=演算子を用いた場合、左辺の配列オブジェクトが、新しいバッファを確保することなく連結できるときは、新しい配列オブジェクトを生成することはありません。
 当然ですが、~演算子の結果は、元の配列と同じ要素型の動的配列型になります。

CODE:

import std.stdio;

void main(){
	int[]a  = [1,2,3,4,5,6,7,8,9,10]; //動的配列
	int[5]b = [11,12,13,14,15]; //静的配列
	
	auto d = a ~ b; //配列を連結する。
	
	//これは等しいよね?
	writefln("%s ? %s",(d == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]).stringof,d == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
	
	a ~= b; //aにbを追加する。
	
	write("a[] = [");
	foreach(c;a){
		writef("%d,",c);
	}
	writeln("]");
	
	writefln("%s ? %s",(a == d).stringof,a == d);
}
実行結果
d == [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] ? true
a[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,]
a == d ? true


 書き忘れていましたが、D言語では==演算子や!=演算子で、配列が同じかどうかを判定することが出来ます。また、2つの動的配列型の変数a,bが同じ配列オブジェクトを指しているかを判定するには、中置二項演算子isが使えます。さらに、

!(a is b)

という式は、!=と同じような要領で、!is二項演算子を用いて、

a !is b

と書き換えることが出来ます。次の例では、bはaのコピーを、cはaそのものを指していることに注意してください。

CODE:

import std.stdio;

void array_out(int[] array){
	write("[");
	foreach(c;array){
		writef("%d,",c);
	}
	writeln("]");
}

void main(){
	int[]a = [1,2,3,4,5,6,7,8,9,10]; //動的配列
	int[]b = a.dup ,c = a;
	
	write("a[] == ");
	array_out(a);
	
	write("b[] == ");
	array_out(b);
	
	write("c[] == ");
	array_out(c);

	writefln("%s ? %s",(a == b).stringof,a == b);
	
	writefln("%s ? %s",(a is b).stringof,a is b);
	
	writefln("%s ? %s",(a != c).stringof,a != c);
	
	writefln("%s ? %s",(a is c).stringof,a is c);
	
	writefln("%s ? %s",(b == c).stringof,b == c);
	
	writefln("%s ? %s",(b !is c).stringof,b !is c);
}
実行結果
a[] == [1,2,3,4,5,6,7,8,9,10,]
b[] == [1,2,3,4,5,6,7,8,9,10,]
c[] == [1,2,3,4,5,6,7,8,9,10,]
a == b ? true
a is b ? false
a != c ? false
a is c ? true
b == c ? true
b !is c ? true

► スポイラーを表示
前の記事「配列(1)」 ←→ 次の記事「Const,Immutable(1)
最後に編集したユーザー tk-xleader on 2012年3月30日(金) 23:37 [ 編集 3 回目 ]

コメントはまだありません。