การเขียนโปรแกรม D - ช่วง
ช่วงเป็นนามธรรมของการเข้าถึงองค์ประกอบ สิ่งที่เป็นนามธรรมนี้ทำให้สามารถใช้อัลกอริทึมจำนวนมากในคอนเทนเนอร์หลายประเภทได้ ช่วงจะเน้นถึงวิธีการเข้าถึงองค์ประกอบคอนเทนเนอร์ซึ่งต่างจากวิธีการใช้งานคอนเทนเนอร์ Ranges เป็นแนวคิดที่เรียบง่ายมากซึ่งขึ้นอยู่กับว่าประเภทกำหนดฟังก์ชันของสมาชิกบางชุดหรือไม่
ช่วงเป็นส่วนหนึ่งของชิ้นส่วนของ D. อัลกอริทึมโฟบอสจำนวนมากส่งคืนวัตถุช่วงชั่วคราว ตัวอย่างเช่น 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- ระบุว่าช่วงว่างหรือไม่ มันจะต้องกลับมาเป็นจริงเมื่อถือว่าช่วงนั้นว่างเปล่า เท็จเป็นอย่างอื่น
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 คล้ายกับฟังก์ชัน 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 นอกจากนี้ค่าของฟังก์ชันว่างที่จะทราบในเวลาคอมไพล์ว่าเป็นเท็จ ตัวอย่างง่ายๆอธิบายด้วยช่วงกำลังสองดังแสดงด้านล่าง
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"]