D语言范围 - D语言教程

范围是存取元素的抽象。这种抽象使得在容器类型的大量使用算法大量出现。范围强调如何容器元素的访问,而不是如何在容器中实现。范围是一个是基于是否一个类型定义某组的成员函数非常简单的概念。

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: 指定的范围内是否为空;当范围被认为是空的,它必须返回true,否则返回false

  • front: 提供对元素的范围的开始

  • popFront(): 通过去除所述第一元件从一开始就缩短了范围

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("%40s: %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"]