Lập trình D - Phạm vi

Phạm vi là một sự trừu tượng của quyền truy cập phần tử. Sự trừu tượng này cho phép sử dụng số lượng lớn các thuật toán trên số lượng lớn các loại vùng chứa. Phạm vi nhấn mạnh cách các phần tử vùng chứa được truy cập, trái ngược với cách các vùng chứa được triển khai. Dải là một khái niệm rất đơn giản dựa trên việc một kiểu có xác định các bộ hàm thành viên nhất định hay không.

Phạm vi là một phần không thể thiếu trong các lát cắt của D. D tình cờ là triển khai của phạm vi mạnh nhất RandomAccessRange và có nhiều tính năng phạm vi trong Phobos. Nhiều thuật toán Phobos trả về các đối tượng phạm vi tạm thời. Ví dụ: filter () chọn các phần tử lớn hơn 10 trong đoạn mã sau thực sự trả về một đối tượng phạm vi, không phải một mảng.

Dải số

Dải số được sử dụng khá phổ biến và những dải số này thuộc kiểu int. Dưới đây là một số ví dụ cho các dải số:

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

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

Dãy Phobos

Phạm vi liên quan đến cấu trúc và giao diện lớp là phạm vi phobos. Phobos là thư viện tiêu chuẩn và thời gian chạy chính thức đi kèm với trình biên dịch ngôn ngữ D.

Có nhiều loại phạm vi bao gồm:

  • InputRange
  • ForwardRange
  • BidirectionalRange
  • RandomAccessRange
  • OutputRange

InputRange

Phạm vi đơn giản nhất là phạm vi đầu vào. Các phạm vi khác mang lại nhiều yêu cầu hơn so với phạm vi mà chúng dựa trên. Có ba chức năng mà InputRange yêu cầu:

  • empty- Nó chỉ định xem phạm vi có trống không; nó phải trả về true khi phạm vi được coi là trống; sai khác.

  • front - Nó cung cấp quyền truy cập vào phần tử ở đầu phạm vi.

  • popFront() - Nó rút ngắn phạm vi ngay từ đầu bằng cách loại bỏ phần tử đầu tiên.

Thí dụ

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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

ForwardRange

ForwardRange cũng yêu cầu phần chức năng thành viên lưu từ ba chức năng khác của InputRange và trả về một bản sao của phạm vi khi chức năng lưu được gọi.

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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]

Hai chiềuRange

BidirectionalRange cung cấp thêm hai chức năng thành viên so với các chức năng thành viên của ForwardRange. Hàm back tương tự như front, cung cấp quyền truy cập vào phần tử cuối cùng của phạm vi. Hàm popBack tương tự như hàm popFront và nó loại bỏ phần tử cuối cùng khỏi phạm vi.

Thí dụ

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

[3, 2, 1]

Vô hạn ngẫu nhiênAccessRange

opIndex () cũng được yêu cầu khi so sánh với ForwardRange. Ngoài ra, giá trị của một hàm trống được biết tại thời điểm biên dịch là false. Một ví dụ đơn giản được giải thích với phạm vi hình vuông được hiển thị bên dưới.

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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

Finite RandomAccessRange

opIndex () và độ dài cũng được yêu cầu khi so sánh với phạm vi hai chiều. Điều này được giải thích với sự trợ giúp của ví dụ chi tiết sử dụng chuỗi Fibonacci và ví dụ Phạm vi hình vuông được sử dụng trước đó. Ví dụ này hoạt động tốt trên trình biên dịch D bình thường nhưng không hoạt động trên trình biên dịch trực tuyến.

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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

Phạm vi đầu ra

OutputRange đại diện cho đầu ra phần tử được phát trực tiếp, tương tự như việc gửi các ký tự đến stdout. OutputRange yêu cầu hỗ trợ cho hoạt động đặt (phạm vi, phần tử). put () là một hàm được định nghĩa trong mô-đun std.range. Nó xác định khả năng của phạm vi và phần tử tại thời điểm biên dịch và sử dụng phương pháp thích hợp nhất để sử dụng để xuất các phần tử. Một ví dụ đơn giản được hiển thị bên dưới.

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

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

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