D編程 范圍(Ranges)

2021-09-01 10:36 更新

范圍range是元素訪問的抽象,范圍強調(diào)如何訪問容器元素,而不是如何實現(xiàn)容器。

Number ranges

數(shù)字范圍是非常常用,這些數(shù)字范圍是int類型的,下面是一些數(shù)字范圍的示例-

//Example 1 
foreach (value; 3..7)  

//Example 2 
int[] slice=array[5..10];

Phobos Ranges

與結(jié)構(gòu)和類接口有關(guān)的范圍是phobos ranges, Phobos是D語言編譯器隨附的官方運行時和標準庫。

有多種類型的范圍,包括-

  • InputRange
  • ForwardRange
  • BidirectionalRange
  • RandomAccessRange
  • OutputRange

InputRange

最簡單的范圍是輸入范圍,其他范圍對它們所基于的范圍提出了更高的要求,InputRange需要三個函數(shù)-

  • 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); 
}

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

[Raj(1), John(2), Ram(3)] 
3 
Raj(1) 
false 
[John(2), Ram(3)]

ForwardRange

ForwardRange另外還需要InputRange的其他三個函數(shù)中的save函數(shù),并在調(diào)用save函數(shù)時返回范圍range的副本。

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); 
}

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

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

除了ForwardRange函數(shù)外,BidirectionalRange還提供了兩個函數(shù),popBack函數(shù)類似于popFront函數(shù),它從范圍中刪除最后一個元素。

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])); 
} 

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

[3, 2, 1]

Infinite RandomAccessRange

與ForwardRange相比,還需要opIndex(),同樣,在編譯時將空函數(shù)的值稱為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); 
}

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

25 
100 
25 
[100, 144, 400, 900, 1444, 1600, 2500]

Finite RandomAccessRange

與Bidirectional range相比,還需要opIndex()和length。這將在使用斐波那契數(shù)列和先前使用的Squares Range示例進行解釋。

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); 
}

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16]

OutputRange

OutputRange表示流元素輸出,類似于將字符發(fā)送到stdout,OutputRange需要支持put(range,element)操作,put()是在std.range模塊中定義的函數(shù)。它在編譯時確定范圍和元素的函數(shù),并使用最合適的方法來輸出元素,一個簡單的例子如下所示。

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("\n", "output_0", "output_1"); 
   copy([ 1, 2, 3], output);  
   copy([ "red", "blue", "green" ], output); 
} 

編譯并執(zhí)行上述代碼后,將產(chǎn)生以下輸出-

[1, 2, 3] 
["red", "blue", "green"]


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號