forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy patharrays.dd
1095 lines (872 loc) · 33 KB
/
arrays.dd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Ddoc
$(SPEC_S 配列,
$(P 配列には四種類あります:)
$(TABLE2 配列の種類,
$(TR $(TH 構文) $(TH 説明))
$(TR $(TD $(I type)*) $(TD $(LINK2 #pointers, データへのポインタ)))
$(TR $(TD $(I type)[$(I integer)]) $(TD $(LINK2 #static-arrays, 静的配列)))
$(TR $(TD $(I type)[]) $(TD $(LINK2 #dynamic-arrays, 動的配列)))
$(TR $(TD $(I type)[$(I type)]) $(TD $(DDLINK hash-map, Associative Arrays, 連想配列)))
)
<h3>$(LNAME2 pointers, ポインタ)</h3>
---------
int* p;
---------
$(P Cのポインタと類似の、
データへの単純なポインタがあります。
ポインタは、Cへのインターフェイスと、特定のシステム処理の
用途のために残されています。
ポインタには長さの情報が付随していないため、
コンパイラやランタイムで境界チェックなどをすることができません。
大抵の場合、ポインタは、
動的配列や $(D out) と $(D ref)
パラメタや、参照型で置き換えることが出来ます。
)
<h3>$(LNAME2 static-arrays, 静的配列)</h3>
---------
int[3] s;
---------
$(P Cの配列に似たものもあります。
静的配列はコンパイル時に決まる固定サイズを持ちます。
)
$(P 静的配列の総サイズが16Mbを越えることは出来ません。
そのような巨大配列には、動的配列を使ってください。
)
$(P サイズ0の静的配列を作ることも可能ですが、
そこにはメモリは割り当てられません。
これは主に可変長構造体の最後のメンバとしてや、
テンプレート展開の末端のケースに利用されます。
)
$(V1
$(P 静的配列は値型ですが、C
と同様に、静的配列は関数には参照で渡され、
関数から返すことはできません。
)
)
$(V2
$(P 静的配列は値型です。CやD1の静的配列とは異なり、
関数引数としても静的配列は値渡しされます。
また、関数の返値とすることも可能です。
)
)
<h3>$(LNAME2 dynamic-arrays, 動的配列)</h3>
---------
int[] a;
---------
$(P 動的配列は、サイズ情報と配列データへのポインタを保持しています。
複数の動的配列がデータの一部または全部を共有することも可能です。
)
<h2>配列の宣言</h2>
$(P 配列の宣言には、
前置と後置の二種類あります。
前置形式が、推奨される方式です。
)
<h4>前置配列宣言</h4>
$(P 識別子の前に書く前置形式では、配列宣言は
左から右へと読むことができます。従って:
)
---------
int[] a; // int の動的配列
int[4][3] b; // int の4要素配列 の3要素配列
int[][5] c; // int の動的配列 の5要素配列
int*[]*[3] d; // int へのポインタ の動的配列 へのポインタ の3要素配列
int[]* e; // int の動的配列 へのポインタ
---------
<h4>後置配列宣言</h4>
$(P 識別子の後ろに書く後置形式では、
配列宣言は右から左へと読みます。
それぞれのグループは等価な宣言です:
)
---------
// int 動的配列
int[] a;
int a[];
// int の4要素配列 の3要素配列
int[4][3] b;
int[4] b[3];
int b[3][4];
// int の動的配列 の5要素配列
int[][5] c;
int[] c[5];
int c[5][];
// int へのポインタ の動的配列 へのポインタ の3要素配列
int*[]*[3] d;
int*[]* d[3];
int* (*d[3])[];
// int の動的配列 へのポインタ
int[]* e;
int (*e)[];
---------
$(P $(B 理由:) 後置形式は、
CとC++での配列宣言の形式と一致しています。
このため、この形式をサポートすることでプログラマのDへの移行が簡単になるでしょう。
)
<h2>$(LNAME2 usage, 使い方)</h2>
$(P 配列への操作は広範囲にわたりますが、
大きく二種類に分けることができます。
一つは配列のハンドルへの操作、
一つは配列の内容への操作です。
Cでは配列のハンドルへの操作しかありませんでしたが、
Dでは双方が使用可能です。
)
$(P 配列へのハンドルは、
配列の名前によって指定します:
)
---------
int* p;
int[3] s;
int[] a;
int* q;
int[3] t;
int[] b;
p = q; // p は q と同じものを指す
p = s.ptr; // p は 配列s の先頭要素を指す
p = a.ptr; // p は 配列a の先頭要素を指す
s = ...; // エラー
// s は配列への静的な参照としてコンパイルされている
a = p; // エラー
// pによって指されている配列のサイズが不明
a = s; // a は配列s を指すように初期化
a = b; // a は配列b と同じものを指す
---------
<h2>$(LNAME2 slicing, スライス)</h2>
$(P 配列を $(I スライス) するとは、配列の一部分を取り出すことを言います。
配列のスライスは元のデータのコピーではなく、
単なる別の参照になります。
例えば:
)
---------
int[10] a; // intの10要素配列の宣言
int[] b;
b = a[1..3]; // a[1..3] は、
// a[1] と a[2] からなる2要素配列
foo(b[1]); // foo(0) と同じ
a[2] = 3;
foo(b[1]); // foo(3) と同じ
---------
$(P 単に [] と書くと、配列全体のスライスを表します。
例えば配列bへの代入は:
)
---------
int[10] a;
int[] b;
b = a;
b = a[];
b = a[0 .. a.length];
---------
$(P 全て同じ意味となります。
)
$(P スライス
は他の配列の一部分を参照するのに便利なだけでなく、
ポインタを境界チェック付きの配列に変換するのにも使えます。
)
---------
int* p;
int[] b = p[0..8];
---------
<h2>$(LNAME2 array-copying, 配列のコピー)</h2>
$(P スライス演算子が代入式の左辺に現れると、
配列への参照ではなく、
配列の内容がコピーの対象となることを示します。
配列内容のコピーは、
左辺がスライスで右辺が同じ型の配列かポインタの時に発生します。
)
---------
int[3] s;
int[3] t;
s[] = t; // t[3] の3つの要素が s[3] へコピーされる
s[] = t[]; // t[3] の3つの要素が s[3] へコピーされる
s[1..2] = t[0..1]; // s[1] = t[0] と同じ意味
s[0..2] = t[1..3]; // s[0] = t[1], s[1] = t[2] と同じ意味
s[0..4] = t[0..4]; // エラー。s には 3要素しかない
s[0..2] = t; // エラー。左辺と右辺で要素数が違う
---------
$(P 範囲の重なるコピーはエラーです:)
---------
s[0..2] = s[1..3]; // エラー,範囲重複
s[1..3] = s[0..2]; // エラー,範囲重複
---------
$(P 範囲の重なるコピーを禁止することで、
Cの逐次のセマンティクスを越えた、
より強力なコードの並列最適化が可能になります。
)
<h2>$(LNAME2 array-setting, 配列へのデータセット)</h2>
$(P 代入式の左辺にスライス、
右辺に要素型と同じ型の値が来ると、
左辺の配列の内容が全て
右辺の値にセットされます。
)
---------
int[3] s;
int* p;
s[] = 3; // s[0] = 3, s[1] = 3, s[2] = 3 と同じ意味
p[0..2] = 3; // p[0] = 3, p[1] = 3 と同じ意味
---------
<h2>$(LNAME2 array-concatenation, 配列の結合)</h2>
$(P 二項演算子 ~ は、$(I 連結)演算子です。
配列をつなげるのに使います:
)
---------
int[] a;
int[] b;
int[] c;
a = b ~ c; // bとcをつなげて
// 新しい配列を作る
---------
$(P 多くの言語は + 演算子を連結の意味でオーバーロードしていますが、
これは次の式の出力結果に混乱をもたらします:
)
---------
"10" + 3 + 4
---------
$(P この式は 整数 17 を作るのか、それとも文字列 "1034" あるいは "107" を結果とするのでしょうか?
言語の設計者は、これをあいまいにしないように注意深く規則 -
間違って実装されたり,見過ごされたり忘れられたりする規則 -
を定めなくてはなりません。
それよりは、 + は加算の意味、
と決めてしまって連結には別の演算子を定義する方がベターです。
)
$(P 同様に、~= 演算子は末尾への追加の意味になります:
)
---------
a ~= b; // a は a と b の連結
---------
$(P 配列の連結は、
片方が長さ 0 の配列であっても必ずコピーを伴います:
)
---------
a = b; // a は b を指す
a = b ~ c[0..0]; // a は b のコピーを指す
---------
$(P 配列への追加は必ずコピーを行うとは限りません。詳しくは $(LINK2 #resize,
配列サイズの動的変更) をご覧下さい。
)
<h2>$(LNAME2 array-operations, 配列演算)</h2>
$(P 多くの配列演算(ベクトル演算)を、
ループではなくより高レベルに表現することが可能です。
例えば、以下のループ
)
---
T[] a, b;
...
for (size_t i = 0; i < a.length; i++)
a[i] = b[i] + 4;
---
$(P は $(CODE a) の各要素に $(CODE b)
$(CODE 4) を足した値を代入するものですが、
これは配列演算で以下のように記述できます:
)
---
T[] a, b;
...
a[] = b[] + 4;
---
$(P ベクトル演算は、スライス演算子が
=, +=, -=, *=, /=, %=, ^=, &=, |= 演算子の左辺に来たときに行われます。
その場合の右辺式には、同じ長さと型の配列スライスか、
要素型の値を返す式の、
任意の組み合わせが使用できます。
ベクトル演算に対応している演算子は、
二項演算子 +, -, *, /, %, ^, &, |, と、
単項演算子 -, ~ です。
)
$(P 左辺のスライスと右辺のスライスには重なりがないものとします。
ベクトル代入演算は右から左の順番で評価され、
その他の二項演算子は左から右へ評価されます。
全てのオペランドはちょうど1回ずつ評価されます。
これは長さゼロの配列スライスを扱う場合も同様です。
)
$(P 配列の各要素が計算される順序は実装依存の定義で、
並列実行される可能性もあります。
アプリケーションは演算の順序に依存してはなりません。
)
$(P 実装ノート:
典型的なベクトル演算の多くは、
ターゲットマシンのベクトル演算命令を活用することが期待されます。
)
<h2>$(LNAME2 pointer-arithmetic, ポインタ演算)</h2>
---------
int[3] abc; // intの3要素静的配列
int[] def = [ 1, 2, 3 ]; // intの3要素動的配列
void dibb(int* array) {
array[2]; // *(array + 2) と同義
*(array + 2); // 3番目の要素を得る
}
void diss(int[] array) {
array[2]; // ok
*(array + 2); // エラー。配列はポインタではない
}
void ditt(int[3] array) {
array[2]; // ok
*(array + 2); // エラー。配列はポインタではない
}
---------
<h2>$(LNAME2 rectangular-arrays, "rectangular" 配列)</h2>
$(P 熟練したFORTRANの数値プログラマは、多次元の"長方形型"配列が、
行列演算のようなものを実装するのに、"配列へのポインタの配列"
を使うよりずっと高速なことをご存じでしょう。
例えば D の構文:
)
---------
double[][] matrix;
---------
$(P は、配列へのポインタの配列を宣言します。(動的配列は、配列データへのポインタとして
実装されています。)これは配列のサイズが変わる可能性がある(動的配列なので)ためで、
このような実装はよく "ぎざぎざ" 配列と呼ばれています。最適化を考えるとさらに悪いことに、
しばしば違う行が同じ要素を指すなんてこともあるのです! 幸い、D の静的配列は、
同じ構文でも固定の長方形型のメモリレイアウトを持っています:
)
---------
double[3][3] matrix;
---------
$(P これは3行3列の配列の宣言で、全ての要素がメモリ上に連続に配置されます。
他の言語でいうと、これは多次元配列と呼ばれ、次のように宣言するようです:
)
---------
double matrix[3,3];
---------
<h2>$(LNAME2 array-length, 配列の長さ)</h2>
$(V1
$(P 静的/動的を問わず、配列の [ ] の中では、
変数 $(B length)
が暗黙に宣言され、その配列の長さを持っています。
$(B $) 記号も同じ用途で使えます。
)
)
$(V2
$(P 静的/動的を問わず、配列の [ ] の中では、
$(B $)
という記号でその配列の長さを取得できます。
)
)
---------
int[4] foo;
int[] bar = foo;
int* p = &foo[0];
// 以下の式は全て同じ意味:
bar[]
bar[0 .. 4]
$(V1 bar[0 .. $(B length)]
)bar[0 .. $(B $)]
bar[0 .. bar.length]
$(V1 p[0 .. length] // 'length' は未定義。pは配列ではない
bar[0]+length // 'length' は未定義。[ ] の外
bar[$(B length)-1] // 配列の最後の要素を取得
)
$(V2 p[0 .. $(DOLLAR)] // '$' は未定義。pは配列ではない
bar[0]+$(DOLLAR) // '$' は未定義。[ ] の外
bar[$(B $(DOLLAR))-1] // 配列の最後の要素を取得
)
---------
<h2>$(LNAME2 array-properties, 配列のプロパティ)</h2>
$(P 静的配列のプロパティは:)
$(TABLE2 Static 静的配列のプロパティ,
$(TR $(TH プロパティ) $(TH 説明))
$(TR
$(TD $(B .init))
$(V1 $(TD 要素型のデフォルト初期化子)
)
$(V2 $(TD 各要素に要素型の $(B .init)
が入った配列リテラル
)
)
)
$(TR
$(TD $(B .sizeof))
$(TD 配列の長さと
一要素ごとのバイト数をかけた値
)
)
$(TR
$(TD $(B .length))
$(TD 配列の要素数。
静的配列では、定数。
型は $(B size_t) となる。
)
)
$(TR
$(TD $(B .ptr))
$(TD 配列の先頭要素を指すポインタを返す。
)
)
$(TR
$(TD $(B .dup))
$(TD 同じサイズの動的配列を作り、
そこに要素をコピーして返す。
)
)
$(TR
$(TD $(B .idup))
$(TD 同じサイズの動的配列を作り、
そこに要素をコピーして返す。
コピーの型はimmutableとなる。
$(I D 2.0 のみ)
)
)
$(TR
$(TD $(B .reverse))
$(TD 配列中の要素をin-placeで逆順に並べる。
配列自身を返す。
)
)
$(TR
$(TD $(B .sort))
$(TD 配列中の要素をin-placeでソートする。
配列自身を返す。
)
)
)
$(P 動的配列のプロパティは:)
$(TABLE2 動的配列のプロパティ,
$(TR $(TH プロパティ) $(TH 説明))
$(TR
$(TD $(B .init))
$(TD null)
)
$(TR
$(TD $(B .sizeof))
$(TD 動的配列の参照のサイズを返す。
32bitマシンでは 8、64bitマシンでは16。
)
)
$(TR
$(TD $(B .length))
$(TD 動的配列の長さを取得/設定。
型は $(B size_t) となる。
)
)
$(TR
$(TD $(B .ptr))
$(TD 配列の先頭要素を指すポインタを返す。
)
)
$(TR
$(TD $(B .dup))
$(TD 同じサイズの動的配列を作り、
そこに要素をコピーして返す。
)
)
$(TR
$(TD $(B .idup))
$(TD 同じサイズの動的配列を作り、
そこに要素をコピーして返す。
コピーの型はimmutableとなる。
$(I D 2.0 のみ)
)
)
$(TR
$(TD $(B .reverse))
$(TD 配列中の要素をin-placeで逆順に並べる。
配列自身を返す。
)
)
$(TR
$(TD $(B .sort))
$(TD 配列中の要素をin-placeでソートする。
配列自身を返す。
)
)
)
$(P クラスオブジェクトの配列に対して $(B .sort) プロパティを使うには、
そのクラスに関数
$(D int opCmp(Object)) が定義されている必要があります。この関数は、
並べ替えの順序を決定します。引数の型は
$(D Object) であって、そのクラス自身の型ではないことに注意してください。)
$(P 構造体や共用体の配列に対して $(B .sort)
プロパティを使うには、
その構造体/共用体に関数
$(V1
$(D int opCmp(S)) または
$(D int opCmp(S*)).
)
$(V2
$(D int opCmp(ref const S) const).
)
が定義されている必要があります。 $(D S) はその構造体や共用体自身の型です。
この関数は、並べ替えの順序を決定します。
)
$(P 例:)
---------
int* p;
int[3] s;
int[] a;
p.length; // エラー、ポインタからは長さはわからない
s.length; // cコンパイル時定数、3
a.length; // 実行時の値
p.dup; // エラー、長さ不明
s.dup; // 3要素の配列を作り、
// そこにsの要素をコピー
a.dup; // a.length要素の配列を作り、
// そこにaの要素をコピー
---------
<h3>$(LNAME2 resize, 動的配列のサイズ設定)</h3>
$(P $(B $(D .length)) プロパティは、= 演算子の左辺値にして、
値を設定することが出来ます:
)
---------
array.length = 7;
---------
$(P これによって、配列用のメモリが再確保され、
既存の内容がコピーされます。
新しい長さの方が短ければ、メモリの再確保は行われず、コピーも発生しません。
以下のスライシングと同じ意味となります:)
---------
array = array[0..7];
---------
$(P 新しい長さの方が長けれれば、
残りはデフォルト初期化されます。
)
$(P 実行効率を重視して、ランタイムは、
できる限りその場でバッファをリサイズすることで余計なコピーを避けようとします。
$(V1 サイズの大きくなるケースでは、new演算子や前のリサイズ以外で割り当てられた配列である場合には、
かならずコピーされます。)
$(V2 サイズの大きくなるケースでは、new演算子以外で割り当てられた配列である場合と、
再割り当て無しの拡大が別の有効な配列データを書き潰してしまう場合は、
かならずコピーされます。)
)
$(V1 $(P This means that if there is an array slice immediately following the
array being resized, the resized array could overlap the slice; i.e.:
)
---------
char[] a = new char[20];
char[] b = a[0..10];
char[] c = a[10..20];
b.length = 15; // always resized in place because it is sliced
// from a[] which has enough memory for 15 chars
b[11] = 'x'; // a[11] and c[1] are also affected
a.length = 1;
a.length = 20; // no net change to memory layout
c.length = 12; // always does a copy because c[] is not at the
// start of a gc allocation block
c[5] = 'y'; // does not affect contents of a[] or b[]
a.length = 25; // may or may not do a copy
a[3] = 'z'; // may or may not affect b[3] which still overlaps
// the old a[3]
---------
)
$(V2
例えば:
---------
char[] a = new char[20];
char[] b = a[0..10];
char[] c = a[10..20];
char[] d = a;
b.length = 15; // a[]からのスライスで上書きの危険があるため、
// 常に再割り当てを行う
b[11] = 'x'; // a[11] と c[1] には影響しない
d.length = 1;
d.length = 20; // これも a と c を上書きする危険があるため、
// 再割り当て
c.length = 12; // その場でバッファ拡大を行うかもしれない
// (c の後に割り当てられているものがないので)
c[5] = 'y'; // aの内容には影響するかもしれないが、
// bやdは再割り当て済みなので影響しない
a.length = 25; // c がその場で拡大した場合必ず再割り当て。
// さもないとcを上書きしてしまうため。c
// がその場での再割り当てができなかった場合、
// スペース不足だったということなので、a も結局再割り当てされる。
a[15] = 'z'; // a か c のどちらかは再割り当てされているので c には影響しない。
---------
)
$(P 確実にコピーさせるには、.dup プロパティを使ってリサイズ可能で
ユニークな配列を取得します。 $(V2 さらに、Phobos の
$(D .capacity) pプロパティを使って、
再割り当てなしで後いくつの要素を追加できるか判断することもできます。)
)
$(P この問題は、~= 演算子による連結時にも生じます。
~ 演算子による連結ならば必ずコピーされるので、
この問題はありません。
)
$(P 動的配列をリサイズするのは、比較的コストの高い操作です。そこで、
配列を値で埋める方法は:
)
---------
int[] array;
while (1) {
c = getinput();
if (!c)
break;
$(V2 ++array.length;)$(V1 array.length = array.length + 1;)
array[array.length - 1] = c;
}
---------
$(P これでも動作しますが、非効率的です。
リサイズ回数を最小にするもっと実際的なアプローチは次の通りです:
)
---------
int[] array;
array.length = 100; // 初期推測値
for (i = 0; ; i++) {
c = getinput();
if (!c)
break;
if (i == array.length)
$(V2 array.length *= 2;)$(V1 array.length = array.length * 2;)
array[i] = c;
}
array.length = i;
---------
$(P うまい初期推測値を与えるのはある種のテクニックですが、
たいていの場合は99%の場合をカバーできる上手い値を見つけられます。
例えば、コンソールからのユーザー入力を受け取るには …
普通は80文字を越えないでしょう。
)
$(V2 $(P Phobos のユーティリティ関数 $(D reserve)
を使うと、append のためのメモリをあらかじめ割り当てておくことが可能です。))
<h3>$(LNAME2 func-as-property, 配列のプロパティとしての関数)</h3>
$(P 関数の第一引数が配列だった場合、
その関数はあたかも配列のプロパティであるかのように呼び出せます:
)
---
int[] array;
void foo(int[] a, int x);
foo(array, 3);
array.foo(3); // 同じ意味
---
<h2>$(LNAME2 bounds, 配列の境界チェック)</h2>
$(P 0より小さい値や配列の長さ以上の値をindexとして配列に与えるのは誤りです。
この種のエラーがコンパイル時に見つかればコンパイルエラーになり、
実行時ならば $(V1 an ArrayBoundsError)$(V2 a RangeError) 例外が発生します。
しかし、
境界チェックに頼ったプログラムを書くべきではありません。
例えば次のプログラムは間違っています:
)
---------
try {
for (i = 0; ; i++) {
array[i] = 5;
}
}
catch (RangeError) {
// ループ終了
}
---------
このループを正しく書くと:
---------
for (i = 0; i < array.length; i++) {
array[i] = 5;
}
---------
$(P $(B 実装ノート:) コンパイラは、コンパイル時にも境界エラーを
検出できるようにすべきです。例えば:
)
---------
int[3] foo;
int x = foo[3]; // エラー、indexの範囲外
---------
$(P また、コンパイル時のスイッチによって、
実行時の境界チェックを
ON/OFF できるのが望ましいです。
)
<h2>$(LNAME2 array-initialization, 配列の初期化)</h2>
<h3>$(LNAME2 default-initialization, デフォルトの初期化)</h3>
$(UL
$(LI ポインタは $(B null) で初期化されます)
$(LI 静的配列の各要素は、
要素型のデフォルト初期化で初期化されます)
$(LI 動的配列は要素数0に初期化されます)
$(LI 連想配列は要素数0に初期化されます)
)
<h3>$(LNAME2 void-initialization, void 初期化)</h3>
$(P void 初期化は、配列の $(I Initializer) が
$(B void) の時の処理です。これは、何も初期化が行われない -
つまり、配列の内容が未定義になるという動作です。
これは、実行効率の最適化にもっとも役立ちます。
void初期化は熟練者向けの技術で、
プロファイルによって配列初期化がネックと判明した時にのみ使用すべきです。
)
<h3>$(LNAME2 static-init-static, 静的配列の静的初期化)</h3>
$(P 静的初期化は、配列要素を
[ ] で囲って指定します。値は、
インデックスと : を前につけることもできます。
インデックスが指定されなかった場合は、
先頭要素なら 0、それ以外なら直前プラス1 となります。
)
---------
int[3] a = [ 1:2, 3 ]; // a[0] = 0, a[1] = 2, a[2] = 3
---------
$(P 配列のindexをenumで与えたいとき、この書き方が一番簡単です:)
---------
enum Color { red, blue, green };
int value[Color.max + 1] =
[ Color.blue :6,
Color.green:2,
Color.red :5 ];
---------
$(P これらの配列は、グローバルスコープに現れたときのみstaticです。
そうでない場合は、static配列とするには、 $(B const) か $(B static)
と宣言してください。)
<h2>$(LNAME2 special-array, 特別な配列型)</h2>
<h3>$(LNAME2 strings, 文字列)</h3>
$(P 文字列とは、
文字の配列です。文字列リテラルは、
文字の配列を簡単に記述する単なる手段に過ぎません。
文字列リテラルは immutable (読み取り専用)です。
)
$(V1
---------
char[] str;
char[] str1 = "abc";
str[0] = 'b'; // エラー。"abc" は読み取り専用。クラッシュの危険
---------
$(P 名前 $(CODE string) が $(CODE char[]),
従って、上の宣言は以下のように書き換えることが出来ます:
)
---------
string str;
string str1 = "abc";
---------
)
$(V2
---------
char[] str1 = "abc"; // エラー。"abc" は mutable でない
char[] str2 = "abc".dup; // ok。mutableなコピーを作成
immutable(char)[] str3 = "abc"; // ok
immutable(char)[] str4 = str1; // エラー。str4 は mutable でない
immutable(char)[] str5 = str1.idup; // ok。immutable なコピー
---------
$(P 名前 $(CODE string) が $(CODE immutable(char)[]) の別名として定義されています。
従って、上の宣言は以下のように書き換えることが出来ます:
)
---------
char[] str1 = "abc"; // エラー。"abc" は mutable でない
char[] str2 = "abc".dup; // ok。mutableなコピーを作成
string str3 = "abc"; // ok
string str4 = str1; // エラー。str4 は mutable でない
string str5 = str1.idup; // ok。immutable なコピー
---------
)
$(P $(CODE char[]) 文字列は UTF-8 形式です。
$(CODE wchar[]) 文字列は in UTF-16 形式です。
$(CODE dchar[]) 文字列は in UTF-32 形式です。
)
$(P 文字列は、コピー、比較、結合などの操作が可能です:)
---------
str1 = str2;
if (str1 < str3) ...
func(str3 ~ str4);
str4 ~= str1;
---------
$(P どれもご覧の通りの意味を持っています。途中で生じた一時オブジェクトは、
ガベージコレクタ(あるいは、$(CODE alloca()))が回収します。
それだけでなく、これらの操作は特別な文字列配列だけでなく、
どんな配列に対しても可能です。
)
$(P charへのポインタも作れます:
)
---------
char* p = &str[3]; // 第4要素へのポインタ
char* p = str; // 先頭要素へのポインタ
---------
$(P しかし、Dの文字列は0終端ではないので、
Cの文字列へ変換するには
末尾に0を追加する必要があります:
)
---------
str ~= "\0";
---------
$(P あるいは $(D std.string.toStringz) 関数を使います)
$(P 文字列の型は、
コンパイル時の意味解析の段階で決定します。
型は、char[], wchar[], dchar[] のいずれかで、
暗黙の変換規則によって一つに決定します。
二種類の変換が同じくらい適切であった場合はエラーになります。
この曖昧さをなくすには、
キャストを使うか、接尾辞 $(B c),
$(B w), $(B d) を使うと良いでしょう:
)
---------
$(V1
cast(wchar [])"abc" // これはwchar文字の配列
"abc"w // これも
)
$(V2
cast(immutable(wchar) [])"abc" // これはwchar文字の配列
"abc"w // これも
)
---------
$(P 接尾辞なしで、キャストもされていない文字列リテラルは、
必要に応じて暗黙に
string, wstring, dstring に変換されます。
)
---------
char c;
wchar w;
dchar d;
c = 'b'; // c には 'b' が入る
w = 'b'; // w には wchar文字 'b' が入る
w = 'bc'; // エラー - 1度に1文字ずつ
w = "b"[0]; // w にはwchar文字'b'が入る