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;
    }

在迭代集合时,集合本身不能变.不能增删新元素,允许改变元素.

发布了440 篇原创文章 · 获赞 29 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/104589611
今日推荐