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"]