49用d编程区间
把区间与容器/算法结合起来的是模板(非常重要).
 stl.拉开序幕.
 d的区间,RandomAccessRange,最强大,随机访问,就是类似数组.
import std.stdio;
import std.algorithm;
void main(){
     int [] values = [1,20,7,11];
    writeln(values.filter!(value => value> 10));
//返回的是区间
}
 int[] chosen = values.filter!(value => value > 10); //编译错误,
//不能把FilterResult!(__lambda2, int[])转换为int[]
//但可以转为数组.
传统的:
struct Node {
    int element;
    Node * next;
}
void print(const(Node) * list) {
    for ( ; list; list = list.next) {
        write(' ', list.element);
    }
}
数据结构知道算法如何实现.类似的
void print(const int[] array) {
    for(int i = 0;i != array.length; ++i) {//长度
        write(' ', array[i]);//[]
    }
}
因而传统N种算法,M种数据结构,
 数组,链表,关联数组,二叉树,堆等必须编写find(), sort(), swap()等算法.折腾.接近N星M的折腾.但无法对关联数组排序.
 但区间远离数据结构.只需要实现N种算法,M种数据结构就可以了.区间就是搞个规范.然后你们去实现吧.
    foreach (value; 3..7) {//数字区间
    int[] slice = array[5..10];  
各种各样的区间输入(empty,front,popFront),前向(前+save),双向(back,popBack),随机([],另一个..)区间.
 随机还分有限/无限随机访问区间.
 还有输出区间.要求put(range, element)操作.
    int[] slice = [ 10, 11, 12 ];
    for (int i = 0; i != slice.length; ++i) {
        write(' ', slice[i]);
    }
    assert(slice.length == 3); 
迭代容器元素,并不改变容器自身.
   for ( ; slice.length; slice = slice[1..$]) {
        write(' ', slice[0]);   // 瞎折腾
    }
另造一个切片来:
   int[] slice = [ 10, 11, 12 ];
    int[] surrogate = slice;
    for ( ; surrogate.length; surrogate = surrogate[1..$]) {
        write(' ', surrogate[0]);
    }
    assert(surrogate.length == 0); //0
    assert(slice.length == 3);     //不变
d标准库也是这样,返回特殊的,原来的切片不变.
 输入区间三函数empty,front,popFront(),前两个可当属性用.
 popFront()有(),表明改变了.
void print(T)(T range) {
    for ( ; !range.empty; range.popFront()) {
        write(' ', range.front);
    }
    writeln();
}
示例:
import std.string;
struct Student {
    string name;
    int number;
    string toString() const {
        return format("%s(%s)", name, number);
    }
}
struct School {
    Student[] students;
}
void main() {
    auto school = School( [ Student("Ebru", 1),
                            Student("Derya", 2) ,
                            Student("Damla", 3) ] );
}
加三成员:
struct School {
    // ...
    @property bool empty() const {
        return students.length == 0;
    }//判断是否为空,好结束判断
    @property ref Student front(){
         return students [0];
    }//当前元素,返回引用
    void popFront() {
        students = students[1 .. $];
    }//修改元素.这里删元素不合理.
}
只导入std.array,就可使最常见的容器类型匹配最有能力的区间类型:切片可按随机访问区间使用.
 std.array可为切片提供empty, front, popFront()和其他函数.因而任意区间函数可使用切片.
import std.array;
// ...
    print([ 1, 2, 3, 4 ]);
如果导入了std.range,就没必要导入std.array了.
void print(T)(T range) {
    for ( ; !range.empty; range.popFront()) {
        write(' ', range.front);
    }//编译错误,定义处
    writeln();
}
void main() {
    int[4] array = [ 1, 2, 3, 4 ];
    print(array);
}
由于切片中的popFront要删元素,因而固定数组不能用作区间(元素不变),
void print(T)(T range)
        if (isInputRange!T) {    //限制模板
    // ...
}
// ...
    print(array);    //报编译错误.调用处
固定数组仍然可以:print(array[]);//编译,复制成区间了.
 尽管切片可用作区间,不是所有区间都可用作数组.
import std.array;
// ...
    // 注意: 利用统一调用
    auto copiesOfStudents = school.array;//不注意,就看不出来.
    writeln(copiesOfStudents);
std.array.array帮助从切片转成数组.其迭代输入区间复制为一个新数组.
 把串自动解码为dchar的区间.其实这是个坏主意.
 当把串用作区间时,自动解码串元素.
void printElements(T)(T str) {
    for (int i = 0; i != str.length; ++i) {
        write(' ', str[i]);
    }
    writeln();
}
// ...
    printElements("abc?deé??"c);
    printElements("abc?deé??"w);
    printElements("abc?deé??"d);
自动解码的值不是实际元素,而是右值.
import std.array;
void main() {
    char[] s = "hello".dup;
    s.front = 'H';//编译错误//右值
}
这样:
import std.array;
import std.string;//represention
void main() {
    char[] s = "hello".dup;
    s.representation.front = 'H';    // 编译
    assert(s == "Hello");
//串被当作`ubyte`数组
}
char, wchar, 和 dchar的表示为ubyte, ushort, 和 uint
 切片的一个优势:不拥有元素,每次调用popFront时计算返回元素,front返回当前元素作为值,其为dchar代表一个统一码
 一些区间没有元素,但能访问其他区间元素,为了保留实际元素,可用特殊区间.
struct School {
    Student[] students;
}
struct StudentRange {
    Student[] students;
    this(School school) {
        this.students = school.students;
    }//这里.则学校不再是区间
    @property bool empty() const {
        return students.length == 0;
    }
    @property ref Student front() {
        return students[0];
    }
    void popFront() {
        students = students[1 .. $];
    }
}
//使用
    auto school = School( [ Student("Ebru", 1),
                            Student("Derya", 2) ,
                            Student("Damla", 3) ] );
    print(StudentRange(school));
    // 原元素,保留了的
    assert(school.students.length == 3);
无限区间enum empty = false;,或static immutable empty = false;
struct FibonacciSeries
{
    int current = 0;
    int next = 1;//但不超过`int.max`
    enum empty = false ;   //无限区间
 
    @property int front()const {
         return current;
    }
    void popFront(){
         const nextNext = current + next;
        current = next;
        next = nextNext;
    }
}
print(FibonacciSeries());不会终止.
 当不需要消耗完区间时有用.
StudentRange studentsOf(ref School school) {
    return StudentRange(school);
}
// ...
    print(school.studentsOf);//注意,利用统一调用
感觉又是个间接层(麻烦),//避免显式记区间类型.
import std.range;
// ...
    auto school = School( [ Student("Ebru", 1),
                            Student("Derya", 2) ,
                            Student("Damla", 3) ] );
    print(school.studentsOf.take(2));
//先要从学生们中建一个学校
writeln(typeof(school.studentsOf.take(2)).stringof);感觉很折腾.得到Take!(StudentRange)
 std.range and std.algorithm有大量区间,结构,类和算法.
import std.algorithm;
// ...
    auto turkishSchool = School( [ Student("Ebru", 1),
                                   Student("Derya", 2) ,
                                   Student("Damla", 3) ] );
    auto americanSchool = School( [ Student("Mary", 10),
                                    Student("Jane", 20) ] );
    swapFront(turkishSchool.studentsOf,
              americanSchool.studentsOf);
//交换区间头
    print(turkishSchool.studentsOf);
    print(americanSchool.studentsOf);
过滤
 print(school.studentsOf.filter!(a => a.number % 2));
import std.array;
// ...
    bool startsWithD(Student student) {
        return student.name.front == 'D';
    }
    print(school.studentsOf.filter!startsWithD);
其实与c++的元编程没啥区别.(过滤!函数)就是过滤<函数>(),只不过c++比较夸张而已.
 注意student.name[0],这是第一个代码单元,不是第一个字母
import std.stdio;
import std.range;
import std.random;
void main() {
    auto diceThrower = generate!(() => uniform(0, 6));
    writeln(diceThrower.take(10));
}
std.range的generate从可调用实体(函数,指针,λ)生成区间.无穷区间,函数返回值作为一个个的元素.
 懒,不到使用时不干活.
 前向区间提供save函数.返回区间副本.
struct FibonacciSeries {
 // ...
    @property FibonacciSeries save()const {
         return  this ;
    }//副本,不是引用.
}
import std.range;
// ...
void report(T)(const dchar[] title, const ref T range) {
    writefln("%40s: %s", title, range.take(5));
}
void main() {
    auto range = FibonacciSeries();
    report("Original range", range);
    range.popFrontN(2);
    report("After removing two elements", range);
    auto theCopy = range.save;
    report("The copy", theCopy);
    range.popFrontN(3);
    report("After removing three more elements", range);
    report("The copy", theCopy);
}
区间是挨个挨个的.
 stdio也可带InputRange对象.
 writeln(FibonacciSeries().take(5).cycle.take(20));
 [0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3]
    auto series                   = FibonacciSeries();
    auto firstPart                = series.take(5);
    auto cycledThrough            = firstPart.cycle;
    auto firstPartOfCycledThrough = cycledThrough.take(20);
//上面为区间
    writeln(firstPartOfCycledThrough);
哪有这么麻烦?
 双向区间:
import std.array;
import std.stdio;
struct Reversed {
    int[] range;
    this(int[] range) {
        this.range = range;
    }
    @property bool empty() const {
        return range.empty;
    }
    @property int front() const {
        return range.back;  // 逆向
    }
    @property int back() const {
        return range.front; // 逆向
    }
    void popFront() {
        range.popBack();    // 逆向
    }
    void popBack() {
        range.popFront();   // 逆向
    }
}
void main() {
    writeln(Reversed([ 1, 2, 3]));
}
随机访问[].
 char,wchar不能随机访问,
 无穷随机编译时empty==false,
 有穷,定义了长度属性
class SquaresRange {
    int first;
    this(int first = 0) {
        this.first = first;
    }
    enum empty = false;
    @property int front() const {
        return opIndex(0);
    }
    void popFront() {
        ++first;
    }
    @property SquaresRange save() const {
        return new SquaresRange(first);
    }
    int opIndex(size_t index) const {
         /* 常数时间*/
        immutable integerValue = first + cast(int)index;
        return integerValue * integerValue;
    }
}
    squares.popFrontN(5);//弹N
    writeln(squares[0]);//头,
//=-=========
 bool are_lastTwoDigitsSame(int value) {
        if (value < 10) {//至少
            return false;
        }
        immutable lastTwoDigits = value % 100;
        return (lastTwoDigits % 11) == 0;
    }//可整除11.
    writeln(squares.take(50).filter!are_lastTwoDigitsSame);
示例:
    auto range = Together([ 1, 2, 3 ], [ 101, 102, 103]);
    writeln(range[4]);
元素不直接复制,只是从原区间访问.
struct Together {
    const(int)[][] slices;
    this(const(int)[][] slices...) {//
        this.slices = slices.dup;
        clearFront();
        clearBack();
    }//去头尾空切片
    private  void clearFront(){
         while(!slices.empty && slices.front.empty){
            slices.popFront();
        }
    }
    private  void clearBack(){
         while(!slices.empty && slices.back.empty){
            slices.popBack();
        }
    }
    @property bool empty() const {
        return slices.empty;
    }
    @property int front() const {
        return slices.front.front;
    }
    void popFront(){
        slices.front.popFront();
        clearFront();//每一次都要校验,
    }
    @property Together save()const {
         return Together(slices.dup);
    }//仍然是区间,不是实体.
    @property int back()const {
         return slices.back.back;
    }
    void popBack(){
        slices.back.popBack();
        clearBack();
    }//尾类似
    @property size_t length()const {
        size_t totalLength = 0;
        foreach(slice;slices){
            totalLength + = slice.length;
        }
        return totalLength;
    }//上下两个一样的.
    @property size_t length()const {
         return slices.fold!((a,b)=> a + b.length)(size_t.init);
    }
    int opIndex(size_t index) const {
        /* Save the index for the error message */
        immutable originalIndex = index;
        foreach (slice; slices) {
            if (slice.length > index) {
                return slice[index];
            } else {
                index -= slice.length;
            }
        }
        throw new Exception(
            format("Invalid index: %s (length: %s)",originalIndex, this.length));
    }//太麻烦了.不能`常数时间`访问
}
其实切片是搞复杂了.每一次都要校验,能不复杂吗?
 看得出来,搞复杂了.
    auto range = Together(FibonacciSeries().take(10).array,
                          [ 777, 888 ],
                          (new SquaresRange()).take(5).array);
    writeln(range.save);
可用,但太长,太麻烦,不推荐.
 输出区间
| 情况 | 方法 | 
|---|---|
| R有成员函数叫put,并取E作参数 | range.put(e); | 
| R有成员函数叫put,并取E[]作参数 | range.put([ e ]); | 
| R是输入区间,e可赋值给range.front | range.front = e;range.popFront(); | 
| E是输入区间,可复制给R | for(;!e.empty;e.popFront())put(range, e.front); | 
| R有E作参数(如R可能是闭包) | range(e); | 
| R有E[]作参数(如R可能是闭包) | range([e]); | 
其中R为区间类型,E为元素类型,range为区间对象,e为区间元素
struct MultiFile {
    string delimiter;
    File[] files;
    this(string delimiter, string[] fileNames...) {
        this.delimiter = delimiter;
        /* 总是包含stdout*/
        this.files ~= stdout;
        /* 每个文件名的文件对象*/
        foreach (fileName; fileNames) {
            this.files ~= File(fileName, "w");
        }
    }
    // 取数组的版本,但不是串
    void put(T)(T slice)
            if (isArray!T && !isSomeString!T) {
        foreach (element; slice) {
            put(element);//调用下面其他版本的put
        }
    }
    // 取非数组和是串的版本
    void put(T)(T value)
            if (!isArray!T || isSomeString!T) {
        foreach (file; files) {
            file.write(value, delimiter);
        }
    }
}
接着
import std.traits;
import std.stdio;
import std.algorithm;
// ...
void main() {
    auto output = MultiFile("\n", "output_0", "output_1");
    copy([ 1, 2, 3], output);
    copy([ "red", "blue", "green" ], output);
}
标准库中std.algorithm.copy使用输出区间
import std.stdio;
import std.range;
void main() {
    int[] slice = [ 1, 2, 3 ];
    int[] slice2 = slice;
    put(slice2, 100);
    writeln(slice2);//只2个元素,丢了一个
    writeln(slice);
}
再:
    int[] slice = [ 1, 2, 3 ];
    int[] slice2 = slice;
    foreach (i; 0 .. 4) {    
        put(slice2, i * 100);
    }
由程序员保证空间足够,输出区间也是一个折腾.
import std.array;
// ...
    auto a = appender([ 1, 2, 3 ]);
    foreach (i; 0 .. 4) {
        a.put(i * 100);
    }
补救措施.
 还可以
    a ~= 1000;writeln(a.data);
c++的一个向量,都搞定了.还有区间模板.
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号