foreach
支持:切片,數組,關聯數組,區間,庫類型(特定),文件(行).
對自定義類型,可自定義每一
.
1,定義區間成員函數,允許與其他區間算法連用.
2,定義一個或多個opApply
.
opApply
優先.但一般區間成員函數
就足夠了,更簡單,更常用.
當對象有集合
概念時,可以每一
.否則,沒必要
foreach (element; myObject) {
// ... expressions ...
}
重寫爲
for(;!myObject.empty();myObject.popFront()) {
auto element = myObject.front();
// ... expressions ...
}
用戶定義類型,要實現每一
,就要提供三個函數empty, popFront, 和 front
,然後編譯器再重寫.
空的
,判斷是否結束.
前
,當前元素
彈前
,丟了.
示例:
struct NumberRange {
int begin;int end;
invariant() {
//不變量
assert(begin <= end);
}
bool empty() const {//相等時爲空
return begin == end;
}
void popFront() {//彈前
++begin;
}//可對這兩個函數進行額外檢查,保證爲空時不調用
int front() const {// 當前元素
return begin;
}
}
就可以這樣調用:
foreach (element; NumberRange(3, 7)) {
write(element, ' ');
}
std.range
包含許多區間算法.
反向迭代.std.range.retro
.
需要兩個成員函數popBack
與back
.
名叫後彈
與後
.與前面的前彈
與前
沒啥區別.
爲了安全,最好再加上個save()
函數.返回這個對象的副本.
現在再定義
struct NumberRange {
// ...
void popBack() {//跳過尾巴
--end;
}
int back() const {
//尾,訪問的就是它
return end - 1;
}
NumberRange save() const @property {
//爲了安全,保存一份
return this;
}
}
//現在你可以
import std.range;
// ...
foreach (element; NumberRange(3, 7).retro) {
write(element, ' ');
}
每一
及每一逆
,還可以支持自定義:
用opApply 和 opApplyReverse
:
opApply和 opApplyReverse
是一樣的,opApply
能幹的事opApplyReverse
也能幹.
這樣,允許我們把對象
當區間使用
,特別在只有一種迭代方法時,更適用.
有時,希望按不同方式迭代對象.如關聯數據
,既可以只訪問值
,也可訪問鍵與值
.
string[string] dictionary; // 英轉土
// ...
foreach (inTurkish; dictionary) {
// ... 僅值...
}
foreach (inEnglish, inTurkish; dictionary) {
// ... 鍵和值...
}
opApply
允許每一
按不同的
甚至很複雜
的方式使用自定義類型.
程序在每一塊
和opApply塊
中交替執行,先調用opApply
成員函數,然後再顯式調用每一
塊,直到循環結束.
foreach (/*循環變量*/; myObject) {
// ... 式們
}
如有opApply
,則每一塊
作爲閉包傳遞給opApply
,幕後代碼如下:
myObject.opApply(delegate int(/*循環變量*/) {
// ... 每一塊的式們
return hasBeenTerminated;//結束了
});
即給opApply
傳遞一個閉包.
每一
變成了每一閉包
,然後傳給opApply
,要求:
1,每次迭代,opApply
都要調用這個閉包
.
2,循環變量成爲閉包參數
,opApply
必須按引用
定義這些參數.
3,閉包
返回值爲整
,編譯器再在閉包尾注入
一個返回語句
(用break/return)來判斷是否結束.返回0
則繼續迭代,否則停止迭代.
實際迭代動作發生在opApply
裏面.即opApply
負責迭代,動作發生在迭代裏面.
4,opApply
必須與閉包
返回的值一樣.
示例:
struct NumberRange {
int begin;int end;
int opApply(int delegate(ref int) operations) const {
int result = 0;
for (int number = begin; number != end; ++number) { // (4)
result = operations(number);// (1)
if (result) {//是否中斷,或停止呢?
break;// (3)
}
}
return result;// (5)
}
}
//這樣使用
foreach (element; NumberRange(3, 7)) {
write(element, ' ');
}
其實就是兩個函數聯合
起來完成一個任務,一個負責迭代,一個負責具體工作.
可以按不同方式迭代,即重載時用不同的閉包.
示例:
foreach (first, second; NumberRange(0, 15)) {
writef("%s,%s ", first, second);
}//1,2
如上,類似關聯數組的迭代.可以這樣:
int opApply(int delegate(ref int, ref int) dg) const {//兩個引用的閉包參數
int result = 0;
for (int i=begin; (i + 1) < end; i += 5) {
int first = i;int second = i + 1;
result = dg(first, second);
if (result) {break;}
}
return result;
}
可以有儘量多重載.還可以通過顯式指定循環變量類型
來給提示重載哪個,
class School {
int opApply(int delegate(ref Student) dg) const {
// ...
}
int opApply(int delegate(ref Teacher) dg) const {
// ...
}
}
//這樣調用
foreach (Student student; school) {// ...
}
foreach (Teacher teacher; school) {// ...
}
可以這樣獲取迭代計數:
import std.range;
// ...
foreach (i, element; NumberRange(42, 47).enumerate) {//枚舉
writefln("%s: %s", i, element);
}//用區間成員函數
用opApply
,區間計數必須定義爲一個單獨的閉包參數:
import std.stdio;
enum Color { blue, green, red }
struct Point {
int x;
int y;
}
struct Polygon {
Color color;
Point[] points;
int opApply(int delegate(ref const(Point)) dg) const {//兩個常,禁止修改,爲了允許修改,則都要去掉
int result = 0;
foreach (point; points) {
result = dg(point);
if (result) {
break;
}
}
return result;
}
}
void main() {
auto polygon = Polygon(Color.blue,
[ Point(0, 0), Point(1, 1) ] );
foreach (point; polygon) {
writeln(point);
}
}
//爲了編譯通過
foreach (i, point; polygon) { //編譯錯誤
writefln("%s: %s", i, point);
}
//還得重載一個
int opApply(int delegate(ref size_t,ref const(Point)) dg) const {//加了個`size_t`參數
int result = 0;
foreach (i, point; points) {//利用了.
result = dg(i, point);//借用的
if (result) {break;}
}//所以每一循環裏面不能修改i變量
return result;
}
//上面就可以編譯了
這樣:
int opApply(int delegate(ref size_t,
ref const(Point)) dg) const {
int result = 0;
bool isDone = false;
size_t counter = 0;
while (!isDone) {
// ...
result = dg(counter, nextElement);
if (result) {
break;
}
++counter;
}
return result;
}
在迭代集合時,集合本身不能變.不能增刪
新元素,允許改變元素.