范围是存取元素的抽象。这种抽象使得在容器类型的大量使用算法大量出现。范围强调如何容器元素的访问,而不是如何在容器中实现。范围是一个是基于是否一个类型定义某组的成员函数非常简单的概念。
D语言范围片恰巧是最强大RandomAccessRange实现不可或缺的一部分,而且有很多的功能使用范围。许多算法返回的临时对象范围。例如filter(),它选择了大于10下面的代码元素,实际上返回一个范围对象,而不是一个数组:
数字范围
数量范围是相当常用的这些数字范围是int类型。对于数量范围的一些例子如下所示
// Example 1 foreach (value 3..7) // Example 2 int[] slice = array[5..10]
福波斯范围
关于结构和类接口的范围是福波斯的范围。 Phobos是正式运行库和标准库自带的D语言编译器。
有多种类型的范围,其中包括,
-
InputRange
-
ForwardRange
-
BidirectionalRange
-
RandomAccessRange
-
OutputRange
InputRange
最简单的范围的输入范围。在其他范围带来他们是基于一系列的顶部更高的需求。有三个函数InputRange需求,
-
empty:%uA0指定的范围内是否为空当范围被认为是空的,它必须返回true,否则返回false
-
front:%uA0提供对元素的范围的开始
-
popFront():%uA0通过去除所述第一元件从一开始就缩短了范围
import std.stdio import std.string struct Student { string name int number string toString() const { return format("%s(%s)", name, number) } } 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 .. $] } } void main(){ auto school = School( [ Student("Raj", 1), Student("John", 2) , Student("Ram", 3) ] ) auto range = StudentRange(school) writeln(range) writeln(school.students.length) writeln(range.front) range.popFront writeln(range.empty) writeln(range) }
当上面的代码被编译并执行,它会产生以下结果:
[Raj(1), John(2), Ram(3)] 3 Raj(1) false [John(2), Ram(3)]
ForwardRange
ForwardRange还需要保存成员函数部分来自其他三个功能InputRange和返回时保存函数被调用的范围内的一个副本。
import std.array import std.stdio import std.string import std.range struct FibonacciSeries { int first = 0 int second = 1 enum empty = false // infinite range @property int front() const { return first } void popFront() { int third = first + second first = second second = third } @property FibonacciSeries save() const { return this } } void report(T)(const dchar[] title, const ref T range) { writefln("%s: %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) }
当上面的代码被编译并执行,它会产生以下结果:
Original range: [0, 1, 1, 2, 3] After removing two elements: [1, 2, 3, 5, 8] The copy: [1, 2, 3, 5, 8] After removing three more elements: [5, 8, 13, 21, 34] The copy: [1, 2, 3, 5, 8]
BidirectionalRange
BidirectionalRange进行附加在ForwardRange的成员函数提供了两个成员函数。回调函数,它类似于front,提供了访问该范围的最后一个元素。 popBack functiom类似于popFront功能状况和它消除了最后一个元素的范围。
import std.array import std.stdio import std.string struct Reversed { int[] range this(int[] range) { this.range = range } @property bool empty() const { return range.empty } @property int front() const { return range.back // reverse } @property int back() const { return range.front // reverse } void popFront() { range.popBack() } void popBack() { range.popFront() } } void main() { writeln(Reversed([ 1, 2, 3])) }
当上面的代码被编译并执行,它会产生以下结果:
[3, 2, 1]
无穷大RandomAccessRange
opIndex()相较于ForwardRange是进行附加是必需的。另外要在编译时empty函数的值为false。一个简单的例子进行说明用的正方形的范围如下所示。
import std.array import std.stdio import std.string import std.range import std.algorithm 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 { /* This function operates at constant time */ immutable integerValue = first + cast(int)index return integerValue * integerValue } } bool are_lastTwoDigitsSame(int value) { /* Must have at least two digits */ if (value < 10) { return false } /* Last two digits must be divisible by 11 */ immutable lastTwoDigits = value % 100 return (lastTwoDigits % 11) == 0 } void main() { auto squares = new SquaresRange() writeln(squares[5]) writeln(squares[10]) squares.popFrontN(5) writeln(squares[0]) writeln(squares.take(50).filter!are_lastTwoDigitsSame) }
让我们编译和运行上面的程序,这将产生以下结果:
25 100 25 [100, 144, 400, 900, 1444, 1600, 2500]
有限RandomAccessRange
opIndex()和length相比双向范围都需要进行附加。这是详细的例子,它使用的斐波那契数列和前面使用正方形范围。这个例子工作原理以及在正常的D编译器,但不会对在线编译工作。
import std.array import std.stdio import std.string import std.range import std.algorithm struct FibonacciSeries { int first = 0 int second = 1 enum empty = false // infinite range @property int front() const { return first } void popFront() { int third = first + second first = second second = third } @property FibonacciSeries save() const { return this } } void report(T)(const dchar[] title, const ref T range) { writefln("@s: %s", title, range.take(5)) } 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 { /* This function operates at constant time */ immutable integerValue = first + cast(int)index return integerValue * integerValue } } bool are_lastTwoDigitsSame(int value) { /* Must have at least two digits */ if (value < 10) { return false } /* Last two digits must be divisible by 11 */ immutable lastTwoDigits = value % 100 return (lastTwoDigits % 11) == 0 } 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 { return reduce!((a, b) => a + b.length)(size_t.init, slices) } 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)) } } void main(){ auto range = Together(FibonacciSeries().take(10).array, [ 777, 888 ], (new SquaresRange()).take(5).array) writeln(range.save) }
让我们编译和运行上面的程序,这将产生以下结果:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16]
OutputRange
OutputRange代表流元件的输出,类似于发送字符到stdout。 OutputRange要求对put(range, element)操作支持。put()是std.range模块中定义的函数。它确定范围和元件,在编译时,并使用最合适的方法,可用来输出的元素。一个简单的例子如下所示。
import std.algorithm import std.stdio struct MultiFile { string delimiter File[] files this(string delimiter, string[] fileNames ...) { this.delimiter = delimiter /* stdout is always included */ this.files ~= stdout /* A File object for each file name */ foreach (fileName fileNames) { this.files ~= File(fileName, "w") } } void put(T)(T element) { foreach (file files) { file.write(element, delimiter) } } } void main(){ auto output = MultiFile(" ", "output_0", "output_1") copy([ 1, 2, 3], output) copy([ "red", "blue", "green" ], output) }
让我们编译和运行上面的程序,这将产生以下结果:
[1, 2, 3] ["red", "blue", "green"]