位置:首頁 > 高級語言 > D語言教學 > 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"]