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;
}
在迭代集合时,集合本身不能变.不能增删
新元素,允许改变元素.