41用d編程每一

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.
需要兩個成員函數popBackback.
名叫後彈.與前面的前彈沒啥區別.
爲了安全,最好再加上個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;
    }

在迭代集合時,集合本身不能變.不能增刪新元素,允許改變元素.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章