D 프로그래밍-범위

범위는 요소 액세스의 추상화입니다. 이 추상화를 통해 수많은 컨테이너 유형에 대해 많은 알고리즘을 사용할 수 있습니다. 범위는 컨테이너가 구현되는 방식과 달리 컨테이너 요소에 액세스하는 방식을 강조합니다. 범위는 유형이 특정 멤버 함수 집합을 정의하는지 여부를 기반으로하는 매우 간단한 개념입니다.

범위는 D의 필수 부분입니다. D의 슬라이스는 가장 강력한 범위 RandomAccessRange의 구현이며 Phobos에는 많은 범위 기능이 있습니다. 많은 Phobos 알고리즘은 임시 범위 개체를 반환합니다. 예를 들어 filter ()는 다음 코드에서 10보다 큰 요소를 선택하여 실제로 배열이 아닌 범위 객체를 반환합니다.

숫자 범위

숫자 범위는 매우 일반적으로 사용되며 이러한 숫자 범위는 int 유형입니다. 숫자 범위에 대한 몇 가지 예가 아래에 나와 있습니다.

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

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

포보스 산맥

구조체 및 클래스 인터페이스와 관련된 범위는 phobos 범위입니다. Phobos는 D 언어 컴파일러와 함께 제공되는 공식 런타임 및 표준 라이브러리입니다.

다음을 포함하는 다양한 유형의 범위가 있습니다-

  • InputRange
  • ForwardRange
  • BidirectionalRange
  • RandomAccessRange
  • OutputRange

InputRange

가장 간단한 범위는 입력 범위입니다. 다른 범위는 기준이되는 범위에 더 많은 요구 사항을 가져옵니다. InputRange에 필요한 세 가지 함수가 있습니다.

  • empty− 범위가 비어 있는지 여부를 지정합니다. 범위가 비어있는 것으로 간주되면 true를 리턴해야합니다. 그렇지 않으면 거짓.

  • 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의 다른 세 함수에서 저장 멤버 함수 부분을 추가로 필요로하며 save 함수가 호출 될 때 범위의 복사본을 반환합니다.

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는 ForwardRange의 멤버 함수에 대해 두 개의 멤버 함수를 추가로 제공합니다. 앞쪽과 유사한 뒤쪽 기능은 범위의 마지막 요소에 대한 액세스를 제공합니다. popBack 함수는 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

ForwardRange와 비교할 때 opIndex ()가 추가로 필요합니다. 또한 컴파일 타임에 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 () 및 길이가 추가로 필요합니다. 이것은 이전에 사용 된 피보나치 시리즈와 제곱 범위 예제를 사용하는 자세한 예제의 도움으로 설명됩니다. 이 예제는 일반 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("\n", "output_0", "output_1"); 
   copy([ 1, 2, 3], output);  
   copy([ "red", "blue", "green" ], output); 
}

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

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