50用d编程区间2

容器,算法,区间都是模板
std.range包含许多按模板限制static if的模板示例.
区间类型模板.
isInputRange isForwardRange isBidirectionalRange isRandomAccessRange isOutputRange

void print(T)(T range)
        if (isInputRange!T) {//要求是输入区间
    // ...
}
void foo(T)(T range)
        if (isOutputRange!(T, double)) {//两个参数
//1,区间类型,2,元素类型.
    // ...
}
//与`static if`连用,可以决定用户定义区间的能力
struct Negative(T)
        if (isInputRange!T) {
    T range;

    @property bool empty() {
        return range.empty;
    }

    @property auto front() {
        return -range.front;
    }//负

    void popFront() {
        range.popFront();
    }
}

如是前向区间,就可用save函数.
返回类型可指定为ElementType!T
方便函数:

Negative!T negative(T)(T range) {
    return Negative!T(range);
}

示例:

struct FibonacciSeries {
    int current = 0;int next = 1;
    enum empty = false;
    @property int front() const {
        return current;
    }
    void popFront() {
        const nextNext = current + next;
        current = next;
        next = nextNext;
    }

    @property FibonacciSeries save() const {
        return this;
    }
}

// ...

    writeln(FibonacciSeries().take(5).negative);

这样是不行的

    writeln(FibonacciSeries()
            .take(5)
            .negative
            .cycle        // 编译错误,前向区间
            .take(10));

加上下面,就可编译了.

    static  if(isForwardRange!T){//如果T为前向区间
        @property Negative save(){
            return Negative(range.save);
        }//赋予保存,自身也是前向区间了.
    }

还可以加上双向,随机区间:

    static  if(isBidirectionalRange!T){
        @property auto back(){
             return -range.back;
        }

        void popBack(){
            range.popBack();
        }
    }

    static  if(isRandomAccessRange!T){
         auto opIndex(size_t index){
             return -range [index];
        }
    }

可用

    auto d = [ 1.5, 2.75 ];
    auto n = d.negative;
    writeln(n[1]);

访问了.
元素类型与元素编码类型,类似c++的值类型

void foo(I1, I2, O)(I1 input1, I2 input2, O output)
        if (isInputRange!I1 &&
            isForwardRange!I2 &&
            isOutputRange!(O, ElementType!I1)) {
            //O为输出区间类型,接受I1的元素类型
    // ...
}

因而ElementType!string 和 ElementType!wstring是dchar.
因为串是自动解码为dchar的.串当作数组时是dchar的区间.串的实际编码类型可由ElementEncodingType取得.
更多区间模板:
isInfinite,是无穷,
hasLength,有长度属性
hasSlicing,有切片,即是否支持a[x…y]
hasAssignableElements,当前(front)的返回类型是否可赋值
hasSwappableElements,区间元素是否可交换(算法里面)
hasMobileElements,是否可移动.有这moveFront(), moveBack(),或moveAt()三个函数
hasLvalueElements,区间元素是左值.(既不是实际元素的拷贝,也不是栈上的临时对象)
hasLvalueElements!FibonacciSeries为假,因为其不是系列元素值,只是单个front值.hasLvalueElements!(Negative!(int[]))也是假,因为切片不是拥有实际值,里面的模板参数只是返回实际切片元素的负号.而hasLvalueElements!(int[])是真,因为确实提供访问元素值.
//这些搞得很难懂.

struct Negative(T)
        if (isInputRange!T) {
// ...

    static if (isInfinite!T) {
        // Negative!T 也是无穷
        enum empty = false;

    } else {
        @property bool empty() {
            return range.empty;
        }
    }

// ...
}

static assert( isInfinite!(Negative!FibonacciSeries));
static assert(!isInfinite!(int[]));

模板实现的区间,展示了编译时多态.
编译时多态要处理不同的类型,的实例化.
take模板的返回类型与原来的区间有关.

   writeln(typeof([11, 22].negative.take(1)).stringof);
    writeln(typeof(FibonacciSeries().take(1)).stringof);
//输出:

Take!(Negative!(int[]))
Take!(FibonacciSeries)

因而不同类型不能相互赋值,

    auto range = [11, 22].negative;
    // ... 稍后 ...
    range = FibonacciSeries();//编译错误,不能转换

d标准库提供了inputRangeObject()和outputRangeObject().这种输入区间对象能支持4种区间(除了输出区间的那4种)(InputRange, ForwardRange, BidirectionalRange, 和 RandomAccessRange)

    InputRange!int range = [11, 22].negative.inputRangeObject;
//必须明确指定返回类型,不能用`auto`定义.
    // ... 稍后...
    range=FibonacciSeries().inputRangeObject;
    //编译

取其输入区间对象,感觉还是折腾
再如.

    ForwardRange!int range = [11, 22].negative.inputRangeObject;

    auto copy = range.save;

    range = FibonacciSeries().inputRangeObject;
    writeln(range.save.take(10));

调用save,输出区间对象与输出区间搭配.允许输出区间的再操作们作为相同类型.

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

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/104590121