การเขียนโปรแกรม D - คู่มือฉบับย่อ
ภาษาการเขียนโปรแกรม D เป็นภาษาการเขียนโปรแกรมระบบหลายกระบวนทัศน์เชิงวัตถุที่พัฒนาโดย Walter Bright แห่ง Digital Mars การพัฒนาเริ่มต้นในปี 2542 และเปิดตัวครั้งแรกในปี 2544 เวอร์ชันหลักของ D (1.0) เปิดตัวในปี 2550 ปัจจุบันเรามี D เวอร์ชัน D2
D คือภาษาที่มีไวยากรณ์เป็นรูปแบบ C และใช้การพิมพ์แบบคงที่ มีคุณสมบัติมากมายของ C และ C ++ ใน D แต่ยังมีคุณสมบัติบางอย่างจากภาษาเหล่านี้ที่ไม่รวมอยู่ในส่วนของ D ส่วนเพิ่มเติมที่โดดเด่นบางอย่างของ D ได้แก่
- การทดสอบหน่วย
- โมดูลที่แท้จริง
- เก็บขยะ
- อาร์เรย์ชั้นหนึ่ง
- ฟรีและเปิดกว้าง
- อาร์เรย์เชื่อมโยง
- อาร์เรย์แบบไดนามิก
- ชั้นเรียนภายใน
- Closures
- ฟังก์ชันที่ไม่ระบุชื่อ
- ขี้เกียจประเมิน
- Closures
หลายกระบวนทัศน์
D เป็นภาษาการเขียนโปรแกรมกระบวนทัศน์หลายภาษา หลายกระบวนทัศน์รวมถึง
- Imperative
- เชิงวัตถุ
- การเขียนโปรแกรม Meta
- Functional
- Concurrent
ตัวอย่าง
import std.stdio;
void main(string[] args) {
writeln("Hello World!");
}
การเรียนรู้ง
สิ่งสำคัญที่สุดที่ต้องทำเมื่อเรียนรู้ D คือเน้นแนวคิดและอย่าหลงรายละเอียดทางเทคนิคภาษา
จุดประสงค์ของการเรียนรู้ภาษาโปรแกรมคือการเป็นโปรแกรมเมอร์ที่ดีขึ้น นั่นคือจะมีประสิทธิภาพมากขึ้นในการออกแบบและใช้ระบบใหม่และการดูแลระบบเก่า
ขอบเขตของ D
การเขียนโปรแกรม D มีคุณสมบัติที่น่าสนใจและไซต์การเขียนโปรแกรม D อย่างเป็นทางการอ้างว่า D นั้นมีความสะดวกมีประสิทธิภาพและมีประสิทธิภาพ การเขียนโปรแกรม D เพิ่มคุณสมบัติมากมายในภาษาหลักซึ่งภาษา C ได้จัดเตรียมไว้ให้ในรูปแบบของไลบรารีมาตรฐานเช่นอาร์เรย์ที่ปรับขนาดได้และฟังก์ชันสตริง D สร้างภาษาที่สองที่ยอดเยี่ยมสำหรับโปรแกรมเมอร์ระดับกลางถึงขั้นสูง D ดีกว่าในการจัดการหน่วยความจำและการจัดการพอยน์เตอร์ที่มักทำให้เกิดปัญหาใน C ++
การเขียนโปรแกรม D มีจุดมุ่งหมายหลักในโปรแกรมใหม่ที่แปลงโปรแกรมที่มีอยู่ ให้การทดสอบและการตรวจสอบในตัวเหมาะสำหรับโครงการใหม่ขนาดใหญ่ที่จะเขียนด้วยโค้ดนับล้านบรรทัดโดยทีมงานขนาดใหญ่
การตั้งค่าสภาพแวดล้อมท้องถิ่นสำหรับ D
หากคุณยังคงเต็มใจที่จะตั้งค่าสภาพแวดล้อมของคุณสำหรับภาษาการเขียนโปรแกรม D คุณต้องมีโปรแกรมสองโปรแกรมต่อไปนี้ที่มีอยู่ในคอมพิวเตอร์ของคุณ (a) Text Editor, (b) D Compiler
โปรแกรมแก้ไขข้อความสำหรับการเขียนโปรแกรม D
สิ่งนี้จะใช้ในการพิมพ์โปรแกรมของคุณ ตัวอย่างของตัวแก้ไขบางตัว ได้แก่ Windows Notepad, OS Edit command, Brief, Epsilon, EMACS และ vim หรือ vi
ชื่อและเวอร์ชันของโปรแกรมแก้ไขข้อความอาจแตกต่างกันไปตามระบบปฏิบัติการต่างๆ ตัวอย่างเช่น Notepad จะใช้กับ Windows และสามารถใช้ vim หรือ vi บน windows ได้เช่นเดียวกับ Linux หรือ UNIX
ไฟล์ที่คุณสร้างด้วยโปรแกรมแก้ไขเรียกว่าไฟล์ต้นฉบับและมีซอร์สโค้ดของโปรแกรม ไฟล์ต้นฉบับสำหรับโปรแกรม D ถูกตั้งชื่อด้วยนามสกุล ".d".
ก่อนเริ่มการเขียนโปรแกรมตรวจสอบให้แน่ใจว่าคุณมีโปรแกรมแก้ไขข้อความหนึ่งตัวและคุณมีประสบการณ์เพียงพอในการเขียนโปรแกรมคอมพิวเตอร์บันทึกลงในไฟล์สร้างและดำเนินการในที่สุด
คอมไพเลอร์ D
การใช้งาน D ในปัจจุบันส่วนใหญ่จะรวบรวมโดยตรงในรหัสเครื่องเพื่อการดำเนินการที่มีประสิทธิภาพ
เรามีคอมไพเลอร์ D หลายตัวและมีดังต่อไปนี้
DMD - คอมไพเลอร์ Digital Mars D เป็นคอมไพเลอร์ D อย่างเป็นทางการโดย Walter Bright
GDC - ส่วนหน้าสำหรับส่วนหลังของ GCC สร้างขึ้นโดยใช้ซอร์สโค้ดคอมไพเลอร์ DMD แบบเปิด
LDC - คอมไพเลอร์ที่ใช้ DMD front-end ที่ใช้ LLVM เป็นคอมไพเลอร์แบ็คเอนด์
คุณสามารถดาวน์โหลดคอมไพเลอร์ต่างๆข้างต้นได้จากการดาวน์โหลด D
เราจะใช้ D เวอร์ชัน 2 และเราไม่แนะนำให้ดาวน์โหลด D1
ให้มีโปรแกรม helloWorld.d ดังนี้ เราจะใช้โปรแกรมนี้เป็นโปรแกรมแรกที่เราทำงานบนแพลตฟอร์มที่คุณเลือก
import std.stdio;
void main(string[] args) {
writeln("Hello World!");
}
เราสามารถดูผลลัพธ์ต่อไปนี้
$ hello world
การติดตั้ง D บน Windows
ดาวน์โหลด Windows ติดตั้ง
เรียกใช้ไฟล์ปฏิบัติการที่ดาวน์โหลดมาเพื่อติดตั้ง D ซึ่งสามารถทำได้โดยทำตามคำแนะนำบนหน้าจอ
ตอนนี้เราสามารถสร้างและเรียกใช้ไฟล์โฆษณากล่าวว่า helloWorld.d โดยเปลี่ยนไปใช้โฟลเดอร์ที่มีไฟล์โดยใช้ cd จากนั้นทำตามขั้นตอนต่อไปนี้ -
C:\DProgramming> DMD helloWorld.d
C:\DProgramming> helloWorld
เราสามารถดูผลลัพธ์ต่อไปนี้
hello world
C: \ DProgramming คือโฟลเดอร์ที่ฉันใช้บันทึกตัวอย่าง คุณสามารถเปลี่ยนเป็นโฟลเดอร์ที่คุณบันทึกโปรแกรม D ไว้
การติดตั้ง D บน Ubuntu / Debian
ดาวน์โหลดเดเบียนติดตั้ง
เรียกใช้ไฟล์ปฏิบัติการที่ดาวน์โหลดมาเพื่อติดตั้ง D ซึ่งสามารถทำได้โดยทำตามคำแนะนำบนหน้าจอ
ตอนนี้เราสามารถสร้างและเรียกใช้ไฟล์โฆษณากล่าวว่า helloWorld.d โดยเปลี่ยนไปใช้โฟลเดอร์ที่มีไฟล์โดยใช้ cd จากนั้นทำตามขั้นตอนต่อไปนี้ -
$ dmd helloWorld.d
$ ./helloWorld
เราสามารถดูผลลัพธ์ต่อไปนี้
$ hello world
การติดตั้ง D บน Mac OS X
ดาวน์โหลด Mac ติดตั้ง
เรียกใช้ไฟล์ปฏิบัติการที่ดาวน์โหลดมาเพื่อติดตั้ง D ซึ่งสามารถทำได้โดยทำตามคำแนะนำบนหน้าจอ
ตอนนี้เราสามารถสร้างและเรียกใช้ไฟล์โฆษณากล่าวว่า helloWorld.d โดยเปลี่ยนไปใช้โฟลเดอร์ที่มีไฟล์โดยใช้ cd จากนั้นทำตามขั้นตอนต่อไปนี้ -
$ dmd helloWorld.d $ ./helloWorld
เราสามารถดูผลลัพธ์ต่อไปนี้
$ hello world
การติดตั้ง D บน Fedora
ดาวน์โหลดฟางติดตั้ง
เรียกใช้ไฟล์ปฏิบัติการที่ดาวน์โหลดมาเพื่อติดตั้ง D ซึ่งสามารถทำได้โดยทำตามคำแนะนำบนหน้าจอ
ตอนนี้เราสามารถสร้างและเรียกใช้ไฟล์โฆษณากล่าวว่า helloWorld.d โดยเปลี่ยนไปใช้โฟลเดอร์ที่มีไฟล์โดยใช้ cd จากนั้นทำตามขั้นตอนต่อไปนี้ -
$ dmd helloWorld.d
$ ./helloWorld
เราสามารถดูผลลัพธ์ต่อไปนี้
$ hello world
การติดตั้ง D บน OpenSUSE
ดาวน์โหลด OpenSUSE ติดตั้ง
เรียกใช้ไฟล์ปฏิบัติการที่ดาวน์โหลดมาเพื่อติดตั้ง D ซึ่งสามารถทำได้โดยทำตามคำแนะนำบนหน้าจอ
ตอนนี้เราสามารถสร้างและเรียกใช้ไฟล์โฆษณากล่าวว่า helloWorld.d โดยเปลี่ยนไปใช้โฟลเดอร์ที่มีไฟล์โดยใช้ cd จากนั้นทำตามขั้นตอนต่อไปนี้ -
$ dmd helloWorld.d $ ./helloWorld
เราสามารถดูผลลัพธ์ต่อไปนี้
$ hello world
D IDE
เรามีการสนับสนุน IDE สำหรับ D ในรูปแบบของปลั๊กอินในกรณีส่วนใหญ่ ซึ่งรวมถึง
ปลั๊กอิน Visual Dเป็นปลั๊กอินสำหรับ Visual Studio 2005-13
DDTเป็นปลั๊กอิน eclipse ที่จัดเตรียมการเติมโค้ดให้เสร็จสมบูรณ์การดีบักด้วย GDB
การเติมโค้ดMono-Dการปรับโครงสร้างใหม่ด้วยการรองรับ dmd / ldc / gdc เป็นส่วนหนึ่งของ GSoC 2012
Code Blocksเป็น IDE หลายแพลตฟอร์มที่รองรับการสร้างโปรเจ็กต์ D การไฮไลต์และการดีบัก
D นั้นค่อนข้างง่ายในการเรียนรู้และมาเริ่มสร้างโปรแกรม D ตัวแรกของเรากันเถอะ!
โปรแกรม D แรก
ให้เราเขียนโปรแกรม D ง่ายๆ ไฟล์ D ทั้งหมดจะมีนามสกุล. d. ดังนั้นใส่ซอร์สโค้ดต่อไปนี้ในไฟล์ test.d
import std.stdio;
/* My first program in D */
void main(string[] args) {
writeln("test!");
}
สมมติว่าสภาพแวดล้อม D ถูกตั้งค่าอย่างถูกต้องให้รันการเขียนโปรแกรมโดยใช้ -
$ dmd test.d
$ ./test
เราสามารถดูผลลัพธ์ต่อไปนี้
test
ตอนนี้ให้เราดูโครงสร้างพื้นฐานของโปรแกรม D เพื่อให้คุณเข้าใจโครงสร้างพื้นฐานของภาษาโปรแกรม D ได้ง่าย
นำเข้าใน D
ไลบรารีที่เป็นคอลเลกชันของชิ้นส่วนโปรแกรมที่ใช้ซ้ำได้สามารถทำให้พร้อมใช้งานสำหรับโครงการของเราด้วยความช่วยเหลือของการนำเข้า ที่นี่เรานำเข้าไลบรารี io มาตรฐานซึ่งมีการดำเนินการ I / O พื้นฐาน Wreln ซึ่งใช้ในโปรแกรมข้างต้นเป็นฟังก์ชันในไลบรารีมาตรฐานของ D ใช้สำหรับพิมพ์บรรทัดข้อความ เนื้อหาไลบรารีใน D ถูกจัดกลุ่มเป็นโมดูลซึ่งขึ้นอยู่กับประเภทของงานที่ตั้งใจจะดำเนินการ โมดูลเดียวที่โปรแกรมนี้ใช้คือ std.stdio ซึ่งจัดการอินพุตและเอาต์พุตข้อมูล
ฟังก์ชั่นหลัก
ฟังก์ชันหลักคือการเริ่มต้นของโปรแกรมและกำหนดลำดับของการดำเนินการและวิธีดำเนินการในส่วนอื่น ๆ ของโปรแกรม
โทเค็นใน D
โปรแกรม AD ประกอบด้วยโทเค็นต่างๆและโทเค็นเป็นทั้งคีย์เวิร์ดตัวระบุค่าคงที่สตริงลิเทอรัลหรือสัญลักษณ์ ตัวอย่างเช่นคำสั่ง D ต่อไปนี้ประกอบด้วยสี่โทเค็น -
writeln("test!");
โทเค็นแต่ละรายการคือ -
writeln (
"test!"
)
;
ความคิดเห็น
ความคิดเห็นเป็นเหมือนข้อความสนับสนุนในโปรแกรม D ของคุณและคอมไพเลอร์จะเพิกเฉย ความคิดเห็นหลายบรรทัดเริ่มต้นด้วย / * และสิ้นสุดด้วยอักขระ * / ดังที่แสดงด้านล่าง -
/* My first program in D */
ความคิดเห็นเดียวเขียนโดยใช้ // ในส่วนเริ่มต้นของความคิดเห็น
// my first program in D
ตัวระบุ
AD identifier คือชื่อที่ใช้เพื่อระบุตัวแปรฟังก์ชันหรือไอเท็มที่ผู้ใช้กำหนด ตัวระบุเริ่มต้นด้วยตัวอักษร A ถึง Z หรือ a ถึง z หรือขีดล่าง _ ตามด้วยตัวอักษรศูนย์หรือมากกว่าขีดล่างและตัวเลข (0 ถึง 9)
D ไม่อนุญาตให้ใช้อักขระเครื่องหมายวรรคตอนเช่น @, $ และ% ภายในตัวระบุ D คือcase sensitiveภาษาโปรแกรม ดังนั้นกำลังคนและกำลังคนจึงเป็นตัวบ่งชี้สองตัวที่แตกต่างกันใน D นี่คือตัวอย่างบางส่วนของตัวระบุที่ยอมรับได้ -
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal
คำหลัก
รายการต่อไปนี้แสดงคำสงวนบางคำใน D. คำสงวนเหล่านี้ไม่สามารถใช้เป็นค่าคงที่หรือตัวแปรหรือชื่อตัวระบุอื่น ๆ
บทคัดย่อ | นามแฝง | จัดตำแหน่ง | asm |
ยืนยัน | อัตโนมัติ | ร่างกาย | บูล |
ไบต์ | กรณี | นักแสดง | จับ |
ถ่าน | ชั้นเรียน | const | ดำเนินการต่อ |
dchar | แก้จุดบกพร่อง | ค่าเริ่มต้น | มอบหมาย |
เลิกใช้แล้ว | ทำ | สองเท่า | อื่น |
enum | ส่งออก | ภายนอก | เท็จ |
สุดท้าย | ในที่สุด | ลอย | สำหรับ |
แต่ละ | ฟังก์ชัน | ไปที่ | ถ้า |
นำเข้า | ใน | inout | int |
อินเตอร์เฟซ | ไม่แปรผัน | คือ | ยาว |
มาโคร | มิกซ์อิน | โมดูล | ใหม่ |
โมฆะ | ออก | แทนที่ | แพ็คเกจ |
pragma | เอกชน | มีการป้องกัน | สาธารณะ |
จริง | อ้างอิง | กลับ | ขอบเขต |
สั้น | คงที่ | โครงสร้าง | สุดยอด |
สวิตซ์ | ซิงโครไนซ์ | แม่แบบ | นี้ |
โยน | จริง | ลอง | typeid |
ประเภทของ | ubyte | uint | อูหลง |
สหภาพแรงงาน | Unittest | ushort | รุ่น |
เป็นโมฆะ | wchar | ในขณะที่ | ด้วย |
ช่องว่างใน D
บรรทัดที่มีเฉพาะช่องว่างอาจมีความคิดเห็นเรียกว่าบรรทัดว่างและคอมไพเลอร์ D จะละเว้นมันโดยสิ้นเชิง
Whitespace เป็นคำที่ใช้ใน D เพื่ออธิบายช่องว่างแท็บอักขระขึ้นบรรทัดใหม่และข้อคิดเห็น ช่องว่างแยกส่วนหนึ่งของคำสั่งออกจากอีกส่วนหนึ่งและช่วยให้ล่ามระบุตำแหน่งที่องค์ประกอบหนึ่งในคำสั่งเช่น int สิ้นสุดลงและองค์ประกอบถัดไปเริ่มต้น ดังนั้นในข้อความต่อไปนี้ -
local age
ต้องมีอักขระช่องว่างอย่างน้อยหนึ่งตัว (โดยปกติจะเป็นช่องว่าง) ระหว่างท้องถิ่นและอายุเพื่อให้ล่ามสามารถแยกแยะได้ ในทางกลับกันในข้อความต่อไปนี้
int fruit = apples + oranges //get the total fruits
ไม่จำเป็นต้องใช้อักขระเว้นวรรคระหว่างผลไม้และ = หรือระหว่าง = และแอปเปิ้ลแม้ว่าคุณจะมีอิสระที่จะใส่อักขระบางตัวหากต้องการเพื่อจุดประสงค์ในการอ่าน
ตัวแปรไม่ใช่อะไรนอกจากชื่อที่กำหนดให้กับพื้นที่จัดเก็บที่โปรแกรมของเราสามารถจัดการได้ ตัวแปรแต่ละตัวใน D มีประเภทเฉพาะซึ่งกำหนดขนาดและรูปแบบของหน่วยความจำของตัวแปร ช่วงของค่าที่สามารถเก็บไว้ในหน่วยความจำนั้น และชุดของการดำเนินการที่สามารถนำไปใช้กับตัวแปร
ชื่อของตัวแปรสามารถประกอบด้วยตัวอักษรตัวเลขและอักขระขีดล่าง ต้องขึ้นต้นด้วยตัวอักษรหรือขีดล่าง อักษรตัวพิมพ์ใหญ่และตัวพิมพ์เล็กมีความแตกต่างกันเนื่องจาก D เป็นตัวพิมพ์เล็กและใหญ่ ตามประเภทพื้นฐานที่อธิบายไว้ในบทที่แล้วจะมีประเภทตัวแปรพื้นฐานดังต่อไปนี้ -
ซีเนียร์ | ประเภทและคำอธิบาย |
---|---|
1 | char โดยทั่วไปเป็นอ็อกเต็ตเดี่ยว (หนึ่งไบต์) นี่คือประเภทจำนวนเต็ม |
2 | int ขนาดของจำนวนเต็มที่เป็นธรรมชาติที่สุดสำหรับเครื่อง |
3 | float ค่าทศนิยมที่มีความแม่นยำเดียว |
4 | double ค่าทศนิยมที่มีความแม่นยำสองเท่า |
5 | void แสดงถึงการไม่มีประเภท |
ภาษาโปรแกรม D ยังอนุญาตให้กำหนดตัวแปรประเภทอื่น ๆ เช่น Enumeration, Pointer, Array, Structure, Union เป็นต้นซึ่งเราจะกล่าวถึงในบทต่อ ๆ ไป สำหรับบทนี้ให้เราศึกษาประเภทตัวแปรพื้นฐานเท่านั้น
นิยามตัวแปรในง
นิยามตัวแปรจะบอกคอมไพลเลอร์ว่าจะสร้างพื้นที่สำหรับตัวแปรได้ที่ไหนและเท่าใด นิยามตัวแปรระบุชนิดข้อมูลและมีรายการตัวแปรประเภทนั้นอย่างน้อยหนึ่งรายการดังนี้ -
type variable_list;
ที่นี่ type ต้องเป็นประเภทข้อมูล D ที่ถูกต้องรวมถึง char, wchar, int, float, double, bool หรือวัตถุที่ผู้ใช้กำหนดเองเป็นต้นและ variable_listอาจประกอบด้วยชื่อตัวบ่งชี้อย่างน้อยหนึ่งชื่อโดยคั่นด้วยเครื่องหมายจุลภาค การประกาศที่ถูกต้องบางส่วนแสดงไว้ที่นี่ -
int i, j, k;
char c, ch;
float f, salary;
double d;
เส้น int i, j, k;ทั้งประกาศและกำหนดตัวแปร i, j และ k; ซึ่งสั่งให้คอมไพเลอร์สร้างตัวแปรชื่อ i, j และ k ประเภท int
ตัวแปรสามารถเริ่มต้นได้ (กำหนดค่าเริ่มต้น) ในการประกาศ initializer ประกอบด้วยเครื่องหมายเท่ากับตามด้วยนิพจน์คงที่ดังนี้ -
type variable_name = value;
ตัวอย่าง
extern int d = 3, f = 5; // declaration of d and f.
int d = 3, f = 5; // definition and initializing d and f.
byte z = 22; // definition and initializes z.
char x = 'x'; // the variable x has the value 'x'.
เมื่อมีการประกาศตัวแปรใน D ตัวแปรจะถูกตั้งค่าเป็น 'default initializer' เสมอซึ่งสามารถเข้าถึงได้ด้วยตนเองเป็น T.init ที่ไหน T คือประเภท (เช่น int.init). ค่าเริ่มต้นเริ่มต้นสำหรับชนิดจำนวนเต็มคือ 0 สำหรับ Booleans false และสำหรับตัวเลขทศนิยม NaN
การประกาศตัวแปรในง
การประกาศตัวแปรให้การรับรองกับคอมไพลเลอร์ว่ามีตัวแปรหนึ่งตัวที่มีอยู่ในประเภทและชื่อที่กำหนดเพื่อให้คอมไพลเลอร์ดำเนินการรวบรวมต่อไปโดยไม่ต้องการรายละเอียดทั้งหมดเกี่ยวกับตัวแปร การประกาศตัวแปรมีความหมายในช่วงเวลาของการคอมไพเลอร์เท่านั้นคอมไพเลอร์ต้องการการประกาศตัวแปรจริงในขณะที่เชื่อมโยงโปรแกรม
ตัวอย่าง
ลองใช้ตัวอย่างต่อไปนี้ซึ่งมีการประกาศตัวแปรเมื่อเริ่มต้นโปรแกรม แต่มีการกำหนดและเริ่มต้นภายในฟังก์ชันหลัก -
import std.stdio;
int a = 10, b = 10;
int c;
float f;
int main () {
writeln("Value of a is : ", a);
/* variable re definition: */
int a, b;
int c;
float f;
/* Initialization */
a = 30;
b = 40;
writeln("Value of a is : ", a);
c = a + b;
writeln("Value of c is : ", c);
f = 70.0/3.0;
writeln("Value of f is : ", f);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Value of a is : 10
Value of a is : 30
Value of c is : 70
Value of f is : 23.3333
Lvalues และ Rvalues ใน D
มีสองประเภทของนิพจน์ใน D -
lvalue - นิพจน์ที่เป็น lvalue อาจปรากฏเป็นด้านซ้ายหรือด้านขวาของงาน
rvalue - นิพจน์ที่เป็นค่า r อาจปรากฏทางด้านขวา แต่ไม่ใช่ด้านซ้ายมือของงาน
ตัวแปรคือ lvalues ดังนั้นจึงอาจปรากฏทางด้านซ้ายมือของงาน ตัวอักษรตัวเลขเป็นค่า r จึงไม่สามารถกำหนดได้และไม่สามารถปรากฏทางด้านซ้ายมือ ข้อความต่อไปนี้ใช้ได้ -
int g = 20;
แต่สิ่งต่อไปนี้ไม่ใช่คำสั่งที่ถูกต้องและจะสร้างข้อผิดพลาดเวลาคอมไพล์ -
10 = 20;
ในภาษาโปรแกรม D ประเภทข้อมูลหมายถึงระบบที่กว้างขวางที่ใช้สำหรับการประกาศตัวแปรหรือฟังก์ชันประเภทต่างๆ ประเภทของตัวแปรจะกำหนดพื้นที่ที่ใช้ในหน่วยเก็บข้อมูลและวิธีการตีความรูปแบบบิตที่จัดเก็บ
ประเภทใน D สามารถจำแนกได้ดังนี้ -
ซีเนียร์ | ประเภทและคำอธิบาย |
---|---|
1 | Basic Types เป็นประเภทเลขคณิตและประกอบด้วยสามประเภท: (a) จำนวนเต็ม (b) ทศนิยมและอักขระ (c) |
2 | Enumerated types เป็นประเภทเลขคณิตอีกครั้ง ใช้เพื่อกำหนดตัวแปรที่สามารถกำหนดได้เฉพาะค่าจำนวนเต็มไม่ต่อเนื่องบางค่าตลอดทั้งโปรแกรม |
3 | The type void ตัวระบุชนิดเป็นโมฆะระบุว่าไม่มีค่า |
4 | Derived types ซึ่งรวมถึง (a) ประเภทตัวชี้ (b) ประเภทอาร์เรย์ (c) ประเภทโครงสร้าง (ง) ประเภทสหภาพและ (จ) ประเภทฟังก์ชัน |
ประเภทอาร์เรย์และประเภทโครงสร้างเรียกรวมกันว่าประเภทการรวม ประเภทของฟังก์ชันระบุชนิดของค่าที่ส่งกลับของฟังก์ชัน เราจะเห็นประเภทพื้นฐานในส่วนต่อไปนี้ในขณะที่ประเภทอื่น ๆ จะกล่าวถึงในบทต่อ ๆ ไป
ประเภทจำนวนเต็ม
ตารางต่อไปนี้แสดงรายการประเภทจำนวนเต็มมาตรฐานพร้อมขนาดพื้นที่จัดเก็บและช่วงค่า -
ประเภท | ขนาดการจัดเก็บ | ช่วงค่า |
---|---|---|
บูล | 1 ไบต์ | เท็จหรือจริง |
ไบต์ | 1 ไบต์ | -128 ถึง 127 |
ubyte | 1 ไบต์ | 0 ถึง 255 |
int | 4 ไบต์ | -2,147,483,648 ถึง 2,147,483,647 |
uint | 4 ไบต์ | 0 ถึง 4,294,967,295 |
สั้น | 2 ไบต์ | -32,768 ถึง 32,767 |
ushort | 2 ไบต์ | 0 ถึง 65,535 |
ยาว | 8 ไบต์ | -9223372036854775808 ถึง 9223372036854775807 |
อูหลง | 8 ไบต์ | 0 ถึง 18446744073709551615 |
เพื่อให้ได้ขนาดที่แน่นอนของประเภทหรือตัวแปรคุณสามารถใช้ไฟล์ sizeofตัวดำเนินการ ประเภทนิพจน์(sizeof)ให้ขนาดหน่วยเก็บข้อมูลของอ็อบเจ็กต์หรือพิมพ์เป็นไบต์ ตัวอย่างต่อไปนี้ได้รับขนาดของประเภท int บนเครื่องใด ๆ -
import std.stdio;
int main() {
writeln("Length in bytes: ", ulong.sizeof);
return 0;
}
เมื่อคุณคอมไพล์และรันโปรแกรมข้างต้นโปรแกรมจะให้ผลลัพธ์ดังต่อไปนี้ -
Length in bytes: 8
ประเภทจุดลอยตัว
ตารางต่อไปนี้กล่าวถึงประเภทจุดลอยตัวมาตรฐานที่มีขนาดพื้นที่จัดเก็บช่วงค่าและวัตถุประสงค์ -
ประเภท | ขนาดการจัดเก็บ | ช่วงค่า | วัตถุประสงค์ |
---|---|---|---|
ลอย | 4 ไบต์ | 1.17549e-38 ถึง 3.40282e + 38 | ทศนิยม 6 ตำแหน่ง |
สองเท่า | 8 ไบต์ | 2.22507e-308 ถึง 1.79769e + 308 | ทศนิยม 15 ตำแหน่ง |
จริง | 10 ไบต์ | 3.3621e-4932 ถึง 1.18973e + 4932 | ประเภทจุดลอยตัวที่ใหญ่ที่สุดที่ฮาร์ดแวร์รองรับหรือสองเท่า แล้วแต่จำนวนใดจะใหญ่กว่า |
ifloat | 4 ไบต์ | 1.17549e-38i ถึง 3.40282e + 38i | ประเภทค่าจินตภาพของการลอย |
ไอดับเบิ้ล | 8 ไบต์ | 2.22507e-308i ถึง 1.79769e + 308i | ประเภทมูลค่าจินตภาพของคู่ |
ireal | 10 ไบต์ | 3.3621e-4932 ถึง 1.18973e + 4932 | ประเภทมูลค่าจินตภาพของจริง |
cfloat | 8 ไบต์ | 1.17549e-38 + 1.17549e-38i ถึง 3.40282e + 38 + 3.40282e + 38i | ประเภทจำนวนเชิงซ้อนที่ทำจากสองโฟลต |
cdouble | 16 ไบต์ | 2.22507e-308 + 2.22507e-308i ถึง 1.79769e + 308 + 1.79769e + 308i | ประเภทจำนวนเชิงซ้อนที่ทำจากสองคู่ |
Creal | 20 ไบต์ | 3.3621e-4932 + 3.3621e-4932i ถึง 1.18973e + 4932 + 1.18973e + 4932i | ประเภทจำนวนเชิงซ้อนที่ทำจากสองเรียล |
ตัวอย่างต่อไปนี้จะพิมพ์พื้นที่เก็บข้อมูลที่ถ่ายโดยประเภทโฟลตและค่าช่วง -
import std.stdio;
int main() {
writeln("Length in bytes: ", float.sizeof);
return 0;
}
เมื่อคุณคอมไพล์และรันโปรแกรมข้างต้นโปรแกรมจะสร้างผลลัพธ์ต่อไปนี้บน Linux -
Length in bytes: 4
ประเภทตัวละคร
ตารางต่อไปนี้แสดงประเภทอักขระมาตรฐานพร้อมขนาดพื้นที่จัดเก็บและวัตถุประสงค์
ประเภท | ขนาดการจัดเก็บ | วัตถุประสงค์ |
---|---|---|
ถ่าน | 1 ไบต์ | หน่วยรหัส UTF-8 |
wchar | 2 ไบต์ | หน่วยรหัส UTF-16 |
dchar | 4 ไบต์ | หน่วยรหัส UTF-32 และจุดรหัส Unicode |
ตัวอย่างต่อไปนี้จะพิมพ์พื้นที่จัดเก็บที่ใช้โดยประเภทถ่าน
import std.stdio;
int main() {
writeln("Length in bytes: ", char.sizeof);
return 0;
}
เมื่อคุณคอมไพล์และรันโปรแกรมข้างต้นโปรแกรมจะให้ผลลัพธ์ดังต่อไปนี้ -
Length in bytes: 1
ประเภทโมฆะ
ประเภทโมฆะระบุว่าไม่มีค่า ใช้ในสองสถานการณ์ -
ซีเนียร์ | ประเภทและคำอธิบาย |
---|---|
1 | Function returns as void มีฟังก์ชั่นต่างๆใน D ที่ไม่คืนค่าหรือคุณสามารถพูดได้ว่ามันคืนค่าเป็นโมฆะ ฟังก์ชันที่ไม่มีค่าส่งคืนจะมีประเภทการส่งคืนเป็นโมฆะ ตัวอย่างเช่น,void exit (int status); |
2 | Function arguments as void มีฟังก์ชันต่างๆใน D ซึ่งไม่ยอมรับพารามิเตอร์ใด ๆ ฟังก์ชันที่ไม่มีพารามิเตอร์สามารถยอมรับว่าเป็นโมฆะ ตัวอย่างเช่น,int rand(void); |
ในตอนนี้คุณอาจไม่เข้าใจประเภทโมฆะดังนั้นให้เราดำเนินการต่อและเราจะกล่าวถึงแนวคิดเหล่านี้ในบทต่อ ๆ ไป
การแจงนับใช้สำหรับกำหนดค่าคงที่ที่ระบุชื่อ ประเภทที่แจกแจงถูกประกาศโดยใช้enum คำสำคัญ.
enumไวยากรณ์
รูปแบบที่ง่ายที่สุดของคำจำกัดความ enum มีดังต่อไปนี้ -
enum enum_name {
enumeration list
}
ที่ไหน
enum_nameระบุชื่อประเภทการแจงนับ
รายการแจงนับเป็นรายการที่คั่นด้วยเครื่องหมายจุลภาคของตัวระบุ
สัญลักษณ์แต่ละตัวในรายการการแจงนับย่อมาจากค่าจำนวนเต็มซึ่งมากกว่าสัญลักษณ์ที่นำหน้า ตามค่าเริ่มต้นค่าของสัญลักษณ์การแจงนับแรกคือ 0 ตัวอย่างเช่น -
enum Days { sun, mon, tue, wed, thu, fri, sat };
ตัวอย่าง
ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการใช้ตัวแปร enum -
import std.stdio;
enum Days { sun, mon, tue, wed, thu, fri, sat };
int main(string[] args) {
Days day;
day = Days.mon;
writefln("Current Day: %d", day);
writefln("Friday : %d", Days.fri);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Current Day: 1
Friday : 5
ในโปรแกรมข้างต้นเราสามารถดูว่าสามารถใช้การแจงนับได้อย่างไร เริ่มแรกเราสร้างตัวแปรชื่อวันของวันแจงนับที่ผู้ใช้กำหนด จากนั้นเราตั้งค่าเป็นจันทร์โดยใช้ตัวดำเนินการจุด เราจำเป็นต้องใช้เมธอด writefln เพื่อพิมพ์ค่าของ mon ที่ถูกเก็บไว้ คุณต้องระบุประเภทด้วย มันเป็นประเภทจำนวนเต็มดังนั้นเราจึงใช้% d สำหรับการพิมพ์
ชื่อ Enums Properties
ตัวอย่างข้างต้นใช้ชื่อ Days สำหรับการแจงนับและเรียกว่าชื่อ enums enums ที่มีชื่อเหล่านี้มีคุณสมบัติดังต่อไปนี้ -
Init - เริ่มต้นค่าแรกในการแจงนับ
min - ส่งกลับค่าที่น้อยที่สุดของการแจงนับ
max - ส่งกลับค่าที่ใหญ่ที่สุดของการแจงนับ
sizeof - ส่งคืนขนาดของการจัดเก็บสำหรับการแจงนับ
ให้เราแก้ไขตัวอย่างก่อนหน้านี้เพื่อใช้ประโยชน์จากคุณสมบัติ
import std.stdio;
// Initialized sun with value 1
enum Days { sun = 1, mon, tue, wed, thu, fri, sat };
int main(string[] args) {
writefln("Min : %d", Days.min);
writefln("Max : %d", Days.max);
writefln("Size of: %d", Days.sizeof);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Min : 1
Max : 7
Size of: 4
Enum นิรนาม
การแจงนับที่ไม่มีชื่อเรียกว่า anonymous enum ตัวอย่างสำหรับanonymous enum ได้รับด้านล่าง
import std.stdio;
// Initialized sun with value 1
enum { sun , mon, tue, wed, thu, fri, sat };
int main(string[] args) {
writefln("Sunday : %d", sun);
writefln("Monday : %d", mon);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Sunday : 0
Monday : 1
Anonymous enums ทำงานในลักษณะเดียวกับ enums ที่ตั้งชื่อ แต่ไม่มีคุณสมบัติ max, min และ sizeof
Enum ที่มีไวยากรณ์ประเภทพื้นฐาน
ไวยากรณ์สำหรับการแจงนับด้วยประเภทฐานแสดงไว้ด้านล่าง
enum :baseType {
enumeration list
}
ฐานบางประเภทประกอบด้วย long, int และ string ตัวอย่างการใช้ long แสดงไว้ด้านล่าง
import std.stdio;
enum : string {
A = "hello",
B = "world",
}
int main(string[] args) {
writefln("A : %s", A);
writefln("B : %s", B);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
A : hello
B : world
คุณสมบัติเพิ่มเติม
การแจงนับใน D มีคุณสมบัติเช่นการเริ่มต้นของค่าหลายค่าในการแจงนับที่มีหลายประเภท ตัวอย่างแสดงด้านล่าง
import std.stdio;
enum {
A = 1.2f, // A is 1.2f of type float
B, // B is 2.2f of type float
int C = 3, // C is 3 of type int
D // D is 4 of type int
}
int main(string[] args) {
writefln("A : %f", A);
writefln("B : %f", B);
writefln("C : %d", C);
writefln("D : %d", D);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
A : 1.200000
B : 2.200000
C : 3
D : 4
ค่าคงที่ที่พิมพ์ในโปรแกรมเป็นส่วนหนึ่งของซอร์สโค้ดจะถูกเรียก literals.
ตัวอักษรสามารถเป็นข้อมูลพื้นฐานประเภทใดก็ได้และสามารถแบ่งออกเป็นเลขจำนวนเต็มเลขทศนิยมอักขระสตริงและค่าบูลีน
อีกครั้งตัวอักษรได้รับการปฏิบัติเช่นเดียวกับตัวแปรทั่วไปยกเว้นว่าไม่สามารถแก้ไขค่าได้หลังจากนิยามแล้ว
ตัวอักษรจำนวนเต็ม
ลิเทอรัลจำนวนเต็มสามารถเป็นประเภทต่อไปนี้ -
Decimal ใช้การรีเฟรชหมายเลขปกติโดยตัวเลขหลักแรกต้องไม่เป็น 0 เนื่องจากตัวเลขนั้นสงวนไว้สำหรับระบุระบบฐานแปดซึ่งไม่รวม 0 ด้วยตัวมันเอง: 0 คือศูนย์
Octal ใช้ 0 เป็นคำนำหน้าตัวเลข
Binary ใช้ 0b หรือ 0B เป็นคำนำหน้า
Hexadecimal ใช้ 0x หรือ 0X เป็นคำนำหน้า
ลิเทอรัลจำนวนเต็มยังสามารถมีส่วนต่อท้ายที่รวมกันของ U และ L สำหรับไม่ได้ลงนามและยาวตามลำดับ คำต่อท้ายสามารถเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กและสามารถเรียงลำดับอย่างไรก็ได้
เมื่อคุณไม่ใช้คำต่อท้ายคอมไพเลอร์เองจะเลือกระหว่าง int, uint, long และ ulong ตามขนาดของค่า
นี่คือตัวอย่างบางส่วนของตัวอักษรจำนวนเต็ม -
212 // Legal
215u // Legal
0xFeeL // Legal
078 // Illegal: 8 is not an octal digit
032UU // Illegal: cannot repeat a suffix
ต่อไปนี้เป็นตัวอย่างอื่น ๆ ของตัวอักษรจำนวนเต็มประเภทต่างๆ -
85 // decimal
0213 // octal
0x4b // hexadecimal
30 // int
30u // unsigned int
30l // long
30ul // unsigned long
0b001 // binary
Floating Point Literals
ลิเทอรัลจุดลอยตัวสามารถระบุได้ทั้งในระบบทศนิยมใน 1.568 หรือในระบบเลขฐานสิบหกเช่นเดียวกับ 0x91.bc
ในระบบเลขฐานสิบสามารถแสดงเลขชี้กำลังได้โดยการเพิ่มอักขระ e หรือ E และตัวเลขหลังจากนั้น ตัวอย่างเช่น 2.3e4 หมายถึง "2.3 คูณ 10 ยกกำลัง 4" อาจมีการระบุอักขระ“ +” ก่อนค่าของเลขชี้กำลัง แต่ไม่มีผลใด ๆ ตัวอย่างเช่น 2.3e4 และ 2.3e + 4 จะเหมือนกัน
อักขระ“ -” ที่เพิ่มก่อนค่าของเลขชี้กำลังจะเปลี่ยนความหมายเป็น "หารด้วย 10 ยกกำลัง" ตัวอย่างเช่น 2.3e-2 หมายถึง "2.3 หารด้วย 10 ยกกำลัง 2"
ในระบบเลขฐานสิบหกค่าจะเริ่มต้นด้วย 0x หรือ 0X เลขชี้กำลังถูกระบุโดย p หรือ P แทน e หรือ E เลขชี้กำลังไม่ได้หมายถึง "10 ยกกำลัง" แต่เป็น "2 ยกกำลังของ" ตัวอย่างเช่น P4 ใน 0xabc.defP4 หมายถึง "abc.de คูณ 2 ยกกำลัง 4"
นี่คือตัวอย่างบางส่วนของตัวอักษรทศนิยม -
3.14159 // Legal
314159E-5L // Legal
510E // Illegal: incomplete exponent
210f // Illegal: no decimal or exponent
.e55 // Illegal: missing integer or fraction
0xabc.defP4 // Legal Hexa decimal with exponent
0xabc.defe4 // Legal Hexa decimal without exponent.
ตามค่าเริ่มต้นประเภทของลิเทอรัลจุดลอยตัวจะเป็นสองเท่า ค่าเฉลี่ยค่า f และ F และตัวระบุ L หมายถึงของจริง
ตัวอักษรบูลีน
มีตัวอักษรบูลีนสองตัวและเป็นส่วนหนึ่งของคำหลัก D มาตรฐาน -
ค่าของ true เป็นตัวแทนของความจริง
ค่าของ false เป็นตัวแทนของเท็จ
คุณไม่ควรพิจารณาค่าของจริงเท่ากับ 1 และค่าของเท็จเท่ากับ 0
ตัวอักษร
ตามตัวอักษรอยู่ในเครื่องหมายคำพูดเดี่ยว
อักขระลิเทอรัลสามารถเป็นอักขระธรรมดา (เช่น 'x'), ลำดับหลีก (เช่น '\ t'), อักขระ ASCII (เช่น '\ x21'), อักขระ Unicode (เช่น '\ u011e') หรือ เป็นอักขระที่มีชื่อ (เช่น '\ ©', '\ ♥', '\ €')
มีอักขระบางตัวใน D เมื่อนำหน้าด้วยแบ็กสแลชซึ่งจะมีความหมายพิเศษและใช้แทนเช่น newline (\ n) หรือ tab (\ t) ที่นี่คุณมีรายการรหัสลำดับการหลบหนีดังกล่าว -
ลำดับการหลบหนี | ความหมาย |
---|---|
\\ | \ อักขระ |
\ ' | 'ตัวละคร |
\ " | "ตัวละคร |
\? | เหรอ? ตัวละคร |
\ ก | แจ้งเตือนหรือกระดิ่ง |
\ b | Backspace |
\ ฉ | ฟีดรูปแบบ |
\ n | ขึ้นบรรทัดใหม่ |
\ r | การกลับรถ |
\ t | แท็บแนวนอน |
\ v | แท็บแนวตั้ง |
ตัวอย่างต่อไปนี้แสดงอักขระลำดับ Escape ไม่กี่ตัว -
import std.stdio;
int main(string[] args) {
writefln("Hello\tWorld%c\n",'\x21');
writefln("Have a good day%c",'\x21');
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Hello World!
Have a good day!
ตัวอักษรสตริง
ตัวอักษรสตริงอยู่ในเครื่องหมายคำพูดคู่ สตริงประกอบด้วยอักขระที่คล้ายกับตัวอักษรอักขระ: อักขระธรรมดาลำดับการหลีกและอักขระสากล
คุณสามารถแบ่งบรรทัดยาวออกเป็นหลายบรรทัดโดยใช้ตัวอักษรสตริงและแยกออกโดยใช้ช่องว่าง
นี่คือตัวอย่างบางส่วนของตัวอักษรสตริง -
import std.stdio;
int main(string[] args) {
writeln(q"MY_DELIMITER
Hello World
Have a good day
MY_DELIMITER");
writefln("Have a good day%c",'\x21');
auto str = q{int value = 20; ++value;};
writeln(str);
}
ในตัวอย่างข้างต้นคุณสามารถใช้ q "MY_DELIMITER MY_DELIMITER" เพื่อแสดงอักขระหลายบรรทัด นอกจากนี้คุณสามารถดู q {} เพื่อแทนคำสั่งภาษา D ได้
ตัวดำเนินการคือสัญลักษณ์ที่บอกให้คอมไพเลอร์ดำเนินการจัดการทางคณิตศาสตร์หรือตรรกะเฉพาะ ภาษา D อุดมไปด้วยตัวดำเนินการในตัวและมีตัวดำเนินการประเภทต่อไปนี้ -
- ตัวดำเนินการเลขคณิต
- ตัวดำเนินการเชิงสัมพันธ์
- ตัวดำเนินการทางตรรกะ
- ตัวดำเนินการ Bitwise
- ผู้ดำเนินการมอบหมาย
- ตัวดำเนินการอื่น ๆ
บทนี้จะอธิบายเกี่ยวกับการคำนวณเชิงสัมพันธ์ตรรกะบิตการกำหนดและตัวดำเนินการอื่น ๆ ทีละตัว
ตัวดำเนินการเลขคณิต
ตารางต่อไปนี้แสดงตัวดำเนินการเลขคณิตทั้งหมดที่ภาษา D รองรับ สมมติตัวแปรA ถือ 10 และตัวแปร B ถือ 20 แล้ว -
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
+ | จะเพิ่มตัวถูกดำเนินการสองตัว | A + B ให้ 30 |
- | มันจะลบตัวถูกดำเนินการที่สองจากตัวแรก | A - B ให้ -10 |
* | มันคูณตัวถูกดำเนินการทั้งสอง | A * B ให้ 200 |
/ | มันหารตัวเศษด้วยตัวหาร | B / A ให้ 2 |
% | ส่งคืนส่วนที่เหลือของการหารจำนวนเต็ม | B% A ให้ 0 |
++ | ตัวดำเนินการส่วนเพิ่มจะเพิ่มค่าจำนวนเต็มทีละหนึ่ง | A ++ ให้ 11 |
- | ตัวดำเนินการลดลงจะลดค่าจำนวนเต็มทีละหนึ่ง | A - ให้ 9 |
ตัวดำเนินการเชิงสัมพันธ์
ตารางต่อไปนี้แสดงตัวดำเนินการเชิงสัมพันธ์ทั้งหมดที่สนับสนุนโดยภาษา D สมมติตัวแปรA ถือ 10 และตัวแปร B ถือ 20 แล้ว -
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
== | ตรวจสอบว่าค่าของตัวถูกดำเนินการสองค่าเท่ากันหรือไม่ถ้าใช่เงื่อนไขจะกลายเป็นจริง | (A == B) ไม่เป็นความจริง |
! = | ตรวจสอบว่าค่าของตัวถูกดำเนินการสองค่าเท่ากันหรือไม่หากค่าไม่เท่ากันเงื่อนไขจะกลายเป็นจริง | (A! = B) เป็นจริง |
> | ตรวจสอบว่าค่าของตัวถูกดำเนินการด้านซ้ายมากกว่าค่าของตัวถูกดำเนินการด้านขวาหรือไม่ถ้าใช่เงื่อนไขจะกลายเป็นจริง | (A> B) ไม่เป็นความจริง |
< | ตรวจสอบว่าค่าของตัวถูกดำเนินการด้านซ้ายน้อยกว่าค่าของตัวถูกดำเนินการด้านขวาหรือไม่ถ้าใช่เงื่อนไขจะกลายเป็นจริง | (A <B) เป็นจริง |
> = | ตรวจสอบว่าค่าของตัวถูกดำเนินการด้านซ้ายมากกว่าหรือเท่ากับค่าของตัวถูกดำเนินการด้านขวาหรือไม่ถ้าใช่เงื่อนไขจะกลายเป็นจริง | (A> = B) ไม่เป็นความจริง |
<= | ตรวจสอบว่าค่าของตัวถูกดำเนินการด้านซ้ายน้อยกว่าหรือเท่ากับค่าของตัวถูกดำเนินการด้านขวาหรือไม่ถ้าใช่เงื่อนไขจะกลายเป็นจริง | (A <= B) เป็นจริง |
ตัวดำเนินการทางตรรกะ
ตารางต่อไปนี้แสดงตัวดำเนินการทางตรรกะทั้งหมดที่ภาษา D รองรับ สมมติตัวแปรA ถือ 1 และตัวแปร B ถือ 0 แล้ว -
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
&& | เรียกว่า Logical AND operator หากตัวถูกดำเนินการทั้งสองไม่ใช่ศูนย์เงื่อนไขจะกลายเป็นจริง | (A && B) เป็นเท็จ |
|| | เรียกว่า Logical OR Operator หากตัวถูกดำเนินการสองตัวใดตัวหนึ่งไม่เป็นศูนย์เงื่อนไขจะกลายเป็นจริง | (A || B) เป็นจริง |
! | เรียกว่า Logical NOT Operator ใช้เพื่อย้อนกลับสถานะตรรกะของตัวถูกดำเนินการ หากเงื่อนไขเป็นจริงตัวดำเนินการ Logical NOT จะสร้างเท็จ | ! (A && B) เป็นเรื่องจริง |
ตัวดำเนินการ Bitwise
ตัวดำเนินการ Bitwise ทำงานบนบิตและดำเนินการแบบบิตต่อบิต ตารางความจริงสำหรับ &, | และ ^ มีดังนี้ -
น | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
สมมติว่า A = 60; และ B = 13 ในรูปแบบไบนารีจะเป็นดังนี้ -
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
ก | B = 0011 1101
ก ^ B = 0011 0001
~ A = 1100 0011
ตัวดำเนินการ Bitwise ที่รองรับโดยภาษา D แสดงอยู่ในตารางต่อไปนี้ สมมติตัวแปร A ถือ 60 และตัวแปร B ถือ 13 จากนั้น -
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
& | ไบนารี AND Operator จะคัดลอกบิตไปยังผลลัพธ์หากมีอยู่ในตัวถูกดำเนินการทั้งสอง | (A & B) จะให้ 12 หมายถึง 0000 1100 |
| | ไบนารีหรือตัวดำเนินการจะคัดลอกบิตหากมีอยู่ในตัวถูกดำเนินการอย่างใดอย่างหนึ่ง | (A | B) ให้ 61. หมายถึง 0011 1101 |
^ | ตัวดำเนินการ XOR ไบนารีจะคัดลอกบิตหากตั้งค่าไว้ในตัวถูกดำเนินการเดียว แต่ไม่ใช่ทั้งสองอย่าง | (A ^ B) ให้ 49. หมายถึง 0011 0001 |
~ | Binary Ones Complement Operator เป็นเอกภาพและมีผลของบิต 'พลิก' | (~ A) ให้ -61 หมายถึง 1100 0011 ในแบบฟอร์มส่วนเสริม 2 |
<< | ตัวดำเนินการกะซ้ายแบบไบนารี ค่าตัวถูกดำเนินการด้านซ้ายจะถูกย้ายไปทางซ้ายตามจำนวนบิตที่ระบุโดยตัวถูกดำเนินการด้านขวา | A << 2 ให้ 240 หมายถึง 1111 0000 |
>> | ตัวดำเนินการกะไบนารีขวา ค่าตัวถูกดำเนินการด้านซ้ายจะถูกย้ายไปทางขวาตามจำนวนบิตที่ระบุโดยตัวถูกดำเนินการด้านขวา | ก >> 2 ให้ 15 หมายถึง 0000 1111. |
ผู้ดำเนินการมอบหมาย
ตัวดำเนินการกำหนดต่อไปนี้รองรับภาษา D -
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
= | มันเป็นตัวดำเนินการกำหนดง่าย กำหนดค่าจากตัวถูกดำเนินการด้านขวาไปยังตัวถูกดำเนินการด้านซ้าย | C = A + B กำหนดค่า A + B เป็น C |
+ = | เป็นตัวดำเนินการเพิ่มและกำหนด เพิ่มตัวถูกดำเนินการด้านขวาให้กับตัวถูกดำเนินการด้านซ้ายและกำหนดผลลัพธ์ให้กับตัวถูกดำเนินการด้านซ้าย | C + = A เทียบเท่ากับ C = C + A |
- = | เป็นตัวดำเนินการลบและกำหนด มันจะลบตัวถูกดำเนินการด้านขวาออกจากตัวถูกดำเนินการด้านซ้ายและกำหนดผลลัพธ์ให้กับตัวถูกดำเนินการด้านซ้าย | C - = A เทียบเท่ากับ C = C - A |
* = | เป็นตัวดำเนินการคูณและกำหนด มันจะคูณตัวถูกดำเนินการด้านขวากับตัวถูกดำเนินการด้านซ้ายและกำหนดผลลัพธ์ให้กับตัวถูกดำเนินการด้านซ้าย | C * = A เทียบเท่ากับ C = C * A |
/ = | เป็นตัวดำเนินการแบ่งและกำหนด มันแบ่งตัวถูกดำเนินการด้านซ้ายกับตัวถูกดำเนินการด้านขวาและกำหนดผลลัพธ์ให้กับตัวถูกดำเนินการด้านซ้าย | C / = A เทียบเท่ากับ C = C / A |
% = | มันคือโมดูลัสและตัวดำเนินการกำหนด ใช้โมดูลัสโดยใช้ตัวถูกดำเนินการสองตัวและกำหนดผลลัพธ์ให้กับตัวถูกดำเนินการด้านซ้าย | C% = A เทียบเท่ากับ C = C% A |
<< = | เป็นตัวดำเนินการเลื่อนซ้ายและตัวดำเนินการกำหนด | C << = 2 เหมือนกับ C = C << 2 |
>> = | เป็นตัวดำเนินการกะและกำหนดสิทธิ์ | C >> = 2 เหมือนกับ C = C >> 2 |
& = | เป็นตัวดำเนินการกำหนดค่าบิตและ | C & = 2 เหมือนกับ C = C & 2 |
^ = | เป็นบิตพิเศษ OR และตัวดำเนินการกำหนด | C ^ = 2 เหมือนกับ C = C ^ 2 |
| = | เป็นบิตรวม OR และตัวดำเนินการกำหนด | C | = 2 เหมือนกับ C = C | 2 |
ตัวดำเนินการที่ผิดปกติ - Sizeof และ Ternary
มีตัวดำเนินการที่สำคัญอื่น ๆ รวมถึง sizeof และ ? : รองรับโดย D Language
แสดงตัวอย่าง
ตัวดำเนินการ | คำอธิบาย | ตัวอย่าง |
---|---|---|
ขนาดของ() | ส่งคืนขนาดของตัวแปร | sizeof (a) โดยที่ a เป็นจำนวนเต็มจะส่งกลับ 4 |
& | ส่งกลับที่อยู่ของตัวแปร | & a; ให้ที่อยู่จริงของตัวแปร |
* | ชี้ไปที่ตัวแปร | * ก; ให้ตัวชี้ไปยังตัวแปร |
เหรอ? : | นิพจน์เงื่อนไข | ถ้าเงื่อนไขเป็นจริงค่า X: มิฉะนั้นค่า Y |
ลำดับความสำคัญของตัวดำเนินการใน D
ลำดับความสำคัญของตัวดำเนินการกำหนดการจัดกลุ่มคำศัพท์ในนิพจน์ สิ่งนี้มีผลต่อวิธีการประเมินนิพจน์ ตัวดำเนินการบางตัวมีความสำคัญเหนือกว่าผู้อื่น
ตัวอย่างเช่นตัวดำเนินการคูณมีลำดับความสำคัญสูงกว่าตัวดำเนินการบวก
ให้เราพิจารณานิพจน์
x = 7 + 3 * 2
ที่นี่ x ถูกกำหนด 13 ไม่ใช่ 20 เหตุผลง่ายๆคือตัวดำเนินการ * มีลำดับความสำคัญสูงกว่า + ดังนั้นจึงคำนวณ 3 * 2 ก่อนจากนั้นจึงเพิ่มผลลัพธ์เป็น 7
ที่นี่ตัวดำเนินการที่มีลำดับความสำคัญสูงสุดจะปรากฏที่ด้านบนสุดของตารางตัวดำเนินการที่มีค่าต่ำสุดจะปรากฏที่ด้านล่าง ภายในนิพจน์ตัวดำเนินการที่มีลำดับความสำคัญสูงกว่าจะได้รับการประเมินก่อน
แสดงตัวอย่าง
ประเภท | ตัวดำเนินการ | ความสัมพันธ์ |
---|---|---|
Postfix | () [] ->. ++ - - | จากซ้ายไปขวา |
ยูนารี | + -! ~ ++ - - (type) * & sizeof | จากขวาไปซ้าย |
หลายหลาก | * /% | จากซ้ายไปขวา |
สารเติมแต่ง | + - | จากซ้ายไปขวา |
กะ | << >> | จากซ้ายไปขวา |
เชิงสัมพันธ์ | <<=>> = | จากซ้ายไปขวา |
ความเท่าเทียมกัน | ==! = | จากซ้ายไปขวา |
Bitwise AND | & | จากซ้ายไปขวา |
Bitwise XOR | ^ | จากซ้ายไปขวา |
Bitwise หรือ | | | จากซ้ายไปขวา |
ตรรกะ AND | && | จากซ้ายไปขวา |
ตรรกะหรือ | || | จากซ้ายไปขวา |
เงื่อนไข | ?: | จากขวาไปซ้าย |
การมอบหมายงาน | = + = - = * = / =% = >> = << = & = ^ = | = | จากขวาไปซ้าย |
จุลภาค | , | จากซ้ายไปขวา |
อาจมีสถานการณ์เมื่อคุณต้องดำเนินการบล็อกรหัสหลาย ๆ ครั้ง โดยทั่วไปคำสั่งจะดำเนินการตามลำดับ: คำสั่งแรกในฟังก์ชันจะถูกเรียกใช้งานก่อนตามด้วยคำสั่งที่สองและอื่น ๆ
ภาษาโปรแกรมจัดเตรียมโครงสร้างการควบคุมต่างๆที่ช่วยให้เส้นทางการดำเนินการซับซ้อนมากขึ้น
คำสั่งวนซ้ำเรียกใช้คำสั่งหรือกลุ่มของคำสั่งหลาย ๆ ครั้ง รูปแบบทั่วไปของคำสั่งลูปต่อไปนี้ซึ่งส่วนใหญ่ใช้ในภาษาโปรแกรม -
ภาษาการเขียนโปรแกรม D จัดเตรียมประเภทของลูปต่อไปนี้เพื่อจัดการกับข้อกำหนดการวนซ้ำ คลิกลิงก์ต่อไปนี้เพื่อตรวจสอบรายละเอียด
ซีเนียร์ | ประเภทห่วงและคำอธิบาย |
---|---|
1 | ในขณะที่วนซ้ำ มันทำซ้ำคำสั่งหรือกลุ่มของคำสั่งในขณะที่เงื่อนไขที่กำหนดเป็นจริง จะทดสอบเงื่อนไขก่อนที่จะดำเนินการร่างกายลูป |
2 | สำหรับห่วง มันรันลำดับของคำสั่งหลาย ๆ ครั้งและย่อโค้ดที่จัดการตัวแปรลูป |
3 | ทำ ... ในขณะที่วนซ้ำ เช่นเดียวกับคำสั่ง while ยกเว้นว่าจะทดสอบเงื่อนไขที่ส่วนท้ายของตัวห่วง |
4 | ลูปที่ซ้อนกัน คุณสามารถใช้ลูปหนึ่งหรือหลายวงในอีกอันหนึ่งสำหรับหรือทำ .. ในขณะที่ลูป |
คำสั่งควบคุมลูป
คำสั่งควบคุมแบบวนซ้ำเปลี่ยนการดำเนินการจากลำดับปกติ เมื่อการดำเนินการออกจากขอบเขตอ็อบเจ็กต์อัตโนมัติทั้งหมดที่สร้างขึ้นในขอบเขตนั้นจะถูกทำลาย
D สนับสนุนคำสั่งควบคุมต่อไปนี้ -
ซีเนียร์ | คำชี้แจงและคำอธิบายการควบคุม |
---|---|
1 | คำสั่งทำลาย ยุติคำสั่งลูปหรือสวิตช์และโอนการดำเนินการไปยังคำสั่งทันทีตามลูปหรือสวิตช์ |
2 | ดำเนินการต่อ ทำให้ลูปข้ามส่วนที่เหลือของร่างกายและทดสอบสภาพของมันใหม่ทันทีก่อนที่จะย้ำอีกครั้ง |
ห่วงไม่มีที่สิ้นสุด
ลูปจะกลายเป็นลูปไม่มีที่สิ้นสุดหากเงื่อนไขไม่เคยเป็นเท็จ forloop ถูกใช้เพื่อจุดประสงค์นี้ เนื่องจากไม่จำเป็นต้องใช้นิพจน์สามนิพจน์ที่สร้าง for loop คุณจึงสามารถสร้างการวนซ้ำแบบไม่มีที่สิ้นสุดได้โดยปล่อยให้นิพจน์เงื่อนไขว่างเปล่า
import std.stdio;
int main () {
for( ; ; ) {
writefln("This loop will run forever.");
}
return 0;
}
เมื่อไม่มีนิพจน์เงื่อนไขจะถือว่าเป็นจริง คุณอาจมีนิพจน์การเริ่มต้นและการเพิ่มขึ้น แต่โปรแกรมเมอร์ D มักใช้โครงสร้าง for (;;) เพื่อแสดงถึงการวนซ้ำที่ไม่มีที่สิ้นสุด
NOTE - คุณสามารถยุติการวนซ้ำที่ไม่มีที่สิ้นสุดได้โดยกดปุ่ม Ctrl + C
โครงสร้างการตัดสินใจประกอบด้วยเงื่อนไขที่ต้องประเมินพร้อมกับชุดคำสั่งสองชุดที่จะดำเนินการ ชุดคำสั่งหนึ่งชุดจะถูกดำเนินการหากเงื่อนไขเป็นจริงและอีกชุดของคำสั่งถูกดำเนินการหากเงื่อนไขเป็นเท็จ
ต่อไปนี้เป็นรูปแบบทั่วไปของโครงสร้างการตัดสินใจทั่วไปที่พบในภาษาโปรแกรมส่วนใหญ่ -
ภาษาโปรแกรม D ถือว่าใด ๆ non-zero และ non-null ค่าเป็น trueและถ้าเป็นอย่างใดอย่างหนึ่ง zero หรือ nullจากนั้นจะถือว่าเป็น false มูลค่า.
ภาษาการเขียนโปรแกรม D มีข้อความประกอบการตัดสินใจประเภทต่อไปนี้
ซีเนียร์ | คำชี้แจงและคำอธิบาย |
---|---|
1 | ถ้าคำสั่ง อัน if statement ประกอบด้วยนิพจน์บูลีนตามด้วยหนึ่งคำสั่งขึ้นไป |
2 | if ... else คำสั่ง อัน if statement ตามด้วยตัวเลือกก็ได้ else statementซึ่งดำเนินการเมื่อนิพจน์บูลีนเป็นเท็จ |
3 | คำสั่ง if ซ้อนกัน คุณสามารถใช้ if หรือ else if คำสั่งภายในอื่น if หรือ else if คำสั่ง (s) |
4 | สลับคำสั่ง ก switch คำสั่งอนุญาตให้ทดสอบตัวแปรเพื่อความเท่าเทียมกับรายการค่า |
5 | คำสั่งสวิตช์ที่ซ้อนกัน คุณสามารถใช้ switch คำสั่งภายในอื่น switch คำสั่ง (s) |
เดอะ? : ตัวดำเนินการใน D
เราได้ครอบคลุม conditional operator ? : ในบทก่อนหน้าซึ่งสามารถใช้เพื่อแทนที่ if...elseงบ มีรูปแบบทั่วไปดังต่อไปนี้
Exp1 ? Exp2 : Exp3;
โดยที่ Exp1, Exp2 และ Exp3 เป็นนิพจน์ สังเกตการใช้และตำแหน่งของลำไส้ใหญ่
ค่าของ? นิพจน์ถูกกำหนดดังนี้ -
Exp1 ได้รับการประเมิน ถ้าเป็นจริง Exp2 จะถูกประเมินและกลายเป็นมูลค่าของทั้งหมด? นิพจน์.
ถ้า Exp1 เป็นเท็จระบบจะประเมิน Exp3 และค่าของมันจะกลายเป็นค่าของนิพจน์
บทนี้อธิบายถึงฟังก์ชันที่ใช้ในการเขียนโปรแกรม D
นิยามฟังก์ชันใน D
นิยามฟังก์ชันพื้นฐานประกอบด้วยส่วนหัวของฟังก์ชันและเนื้อหาของฟังก์ชัน
ไวยากรณ์
return_type function_name( parameter list ) {
body of the function
}
นี่คือส่วนทั้งหมดของฟังก์ชัน -
Return Type- ฟังก์ชันอาจส่งคืนค่า return_typeคือชนิดข้อมูลของค่าที่ฟังก์ชันส่งกลับ ฟังก์ชันบางอย่างดำเนินการตามที่ต้องการโดยไม่ส่งคืนค่า ในกรณีนี้ return_type คือคีย์เวิร์ดvoid.
Function Name- นี่คือชื่อจริงของฟังก์ชัน ชื่อฟังก์ชันและรายการพารามิเตอร์ประกอบกันเป็นลายเซ็นฟังก์ชัน
Parameters- พารามิเตอร์เปรียบเสมือนตัวยึด เมื่อเรียกใช้ฟังก์ชันคุณจะส่งค่าไปยังพารามิเตอร์ ค่านี้เรียกว่าพารามิเตอร์หรืออาร์กิวเมนต์จริง รายการพารามิเตอร์หมายถึงประเภทลำดับและจำนวนของพารามิเตอร์ของฟังก์ชัน พารามิเตอร์เป็นทางเลือก นั่นคือฟังก์ชันอาจไม่มีพารามิเตอร์
Function Body - เนื้อความของฟังก์ชันประกอบด้วยชุดของคำสั่งที่กำหนดสิ่งที่ฟังก์ชันทำ
เรียกใช้ฟังก์ชัน
คุณสามารถเรียกใช้ฟังก์ชันได้ดังนี้ -
function_name(parameter_values)
ประเภทฟังก์ชันใน D
การเขียนโปรแกรม D รองรับฟังก์ชันที่หลากหลายและแสดงไว้ด้านล่าง
- ฟังก์ชั่นที่แท้จริง
- ฟังก์ชัน Nothrow
- ฟังก์ชั่นอ้างอิง
- ฟังก์ชั่นอัตโนมัติ
- ฟังก์ชัน Variadic
- ฟังก์ชัน Inout
- ฟังก์ชั่นคุณสมบัติ
ฟังก์ชั่นต่างๆได้อธิบายไว้ด้านล่าง
ฟังก์ชั่นที่แท้จริง
ฟังก์ชั่นบริสุทธิ์คือฟังก์ชันที่ไม่สามารถเข้าถึงโกลบอลหรือสแตติกสถานะที่เปลี่ยนแปลงได้บันทึกผ่านอาร์กิวเมนต์ สิ่งนี้สามารถเปิดใช้งานการเพิ่มประสิทธิภาพตามข้อเท็จจริงที่ว่าฟังก์ชันบริสุทธิ์ได้รับการรับรองว่าจะไม่มีการกลายพันธุ์สิ่งใดที่ไม่ได้ส่งผ่านไปและในกรณีที่คอมไพเลอร์สามารถรับประกันได้ว่าฟังก์ชันบริสุทธิ์ไม่สามารถเปลี่ยนแปลงอาร์กิวเมนต์ได้ก็สามารถเปิดใช้งานความบริสุทธิ์ที่สมบูรณ์และใช้งานได้ คือการรับประกันว่าฟังก์ชันจะส่งคืนผลลัพธ์เดียวกันสำหรับอาร์กิวเมนต์เดียวกันเสมอ)
import std.stdio;
int x = 10;
immutable int y = 30;
const int* p;
pure int purefunc(int i,const char* q,immutable int* s) {
//writeln("Simple print"); //cannot call impure function 'writeln'
debug writeln("in foo()"); // ok, impure code allowed in debug statement
// x = i; // error, modifying global state
// i = x; // error, reading mutable global state
// i = *p; // error, reading const global state
i = y; // ok, reading immutable global state
auto myvar = new int; // Can use the new expression:
return i;
}
void main() {
writeln("Value returned from pure function : ",purefunc(x,null,null));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Value returned from pure function : 30
ฟังก์ชัน Nothrow
ฟังก์ชัน Nothrow ไม่ทิ้งข้อยกเว้นใด ๆ ที่ได้มาจาก Class Exception ฟังก์ชั่น Nothrow นั้นเป็นมิตรกับคนขว้างปา
Nothrow รับประกันว่าฟังก์ชันจะไม่มีข้อยกเว้นใด ๆ
import std.stdio;
int add(int a, int b) nothrow {
//writeln("adding"); This will fail because writeln may throw
int result;
try {
writeln("adding"); // compiles
result = a + b;
} catch (Exception error) { // catches all exceptions
}
return result;
}
void main() {
writeln("Added value is ", add(10,20));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
adding
Added value is 30
ฟังก์ชั่นอ้างอิง
ฟังก์ชัน Ref อนุญาตให้ฟังก์ชันส่งคืนโดยการอ้างอิง สิ่งนี้คล้ายคลึงกับพารามิเตอร์ฟังก์ชัน ref
import std.stdio;
ref int greater(ref int first, ref int second) {
return (first > second) ? first : second;
}
void main() {
int a = 1;
int b = 2;
greater(a, b) += 10;
writefln("a: %s, b: %s", a, b);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
a: 1, b: 12
ฟังก์ชั่นอัตโนมัติ
ฟังก์ชันอัตโนมัติสามารถส่งคืนค่าประเภทใดก็ได้ ไม่มีข้อ จำกัด ว่าจะส่งคืนประเภทใด ตัวอย่างง่ายๆสำหรับฟังก์ชั่นประเภทอัตโนมัติได้รับด้านล่าง
import std.stdio;
auto add(int first, double second) {
double result = first + second;
return result;
}
void main() {
int a = 1;
double b = 2.5;
writeln("add(a,b) = ", add(a, b));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
add(a,b) = 3.5
ฟังก์ชัน Variadic
ฟังก์ชัน Variadiac คือฟังก์ชันที่กำหนดจำนวนพารามิเตอร์สำหรับฟังก์ชันในรันไทม์ ใน C มีข้อ จำกัด ในการมีพารามิเตอร์อย่างน้อยหนึ่งตัว แต่ในการเขียนโปรแกรม D ไม่มีข้อ จำกัด ดังกล่าว ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
import std.stdio;
import core.vararg;
void printargs(int x, ...) {
for (int i = 0; i < _arguments.length; i++) {
write(_arguments[i]);
if (_arguments[i] == typeid(int)) {
int j = va_arg!(int)(_argptr);
writefln("\t%d", j);
} else if (_arguments[i] == typeid(long)) {
long j = va_arg!(long)(_argptr);
writefln("\t%d", j);
} else if (_arguments[i] == typeid(double)) {
double d = va_arg!(double)(_argptr);
writefln("\t%g", d);
}
}
}
void main() {
printargs(1, 2, 3L, 4.5);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
int 2
long 3
double 4.5
ฟังก์ชัน Inout
inout สามารถใช้ได้ทั้งสำหรับพารามิเตอร์และชนิดของฟังก์ชันที่ส่งคืน มันเป็นเหมือนแม่แบบสำหรับรูปแบบผันแปร const และไม่เปลี่ยนรูป แอตทริบิวต์ความไม่แน่นอนถูกอนุมานจากพารามิเตอร์ หมายความว่า inout จะโอนแอตทริบิวต์ความผันแปรที่อนุมานได้ไปยังประเภทการส่งคืน ตัวอย่างง่ายๆที่แสดงให้เห็นว่าความสามารถในการเปลี่ยนแปลงเปลี่ยนแปลงได้อย่างไรแสดงอยู่ด้านล่าง
import std.stdio;
inout(char)[] qoutedWord(inout(char)[] phrase) {
return '"' ~ phrase ~ '"';
}
void main() {
char[] a = "test a".dup;
a = qoutedWord(a);
writeln(typeof(qoutedWord(a)).stringof," ", a);
const(char)[] b = "test b";
b = qoutedWord(b);
writeln(typeof(qoutedWord(b)).stringof," ", b);
immutable(char)[] c = "test c";
c = qoutedWord(c);
writeln(typeof(qoutedWord(c)).stringof," ", c);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
char[] "test a"
const(char)[] "test b"
string "test c"
ฟังก์ชั่นคุณสมบัติ
คุณสมบัติอนุญาตให้ใช้ฟังก์ชันสมาชิกเช่นตัวแปรสมาชิก ใช้คีย์เวิร์ด @property คุณสมบัติถูกเชื่อมโยงกับฟังก์ชันที่เกี่ยวข้องซึ่งส่งคืนค่าตามความต้องการ ตัวอย่างง่ายๆสำหรับคุณสมบัติแสดงอยู่ด้านล่าง
import std.stdio;
struct Rectangle {
double width;
double height;
double area() const @property {
return width*height;
}
void area(double newArea) @property {
auto multiplier = newArea / area;
width *= multiplier;
writeln("Value set!");
}
}
void main() {
auto rectangle = Rectangle(20,10);
writeln("The area is ", rectangle.area);
rectangle.area(300);
writeln("Modified width is ", rectangle.width);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
The area is 200
Value set!
Modified width is 30
อักขระเป็นส่วนประกอบของสตริง สัญลักษณ์ของระบบการเขียนใด ๆ เรียกว่าอักขระ: ตัวอักษรของตัวอักษรตัวเลขเครื่องหมายวรรคตอนอักขระเว้นวรรค ฯลฯ สร้างความสับสนให้กับตัวอักษรเช่นกัน
ค่าจำนวนเต็มของตัวพิมพ์เล็ก a คือ 97 และค่าจำนวนเต็มของตัวเลข 1 คือ 49 ค่าเหล่านี้ได้รับการกำหนดโดยแบบแผนเท่านั้นเมื่อตาราง ASCII ได้รับการออกแบบ
ตารางต่อไปนี้กล่าวถึงประเภทอักขระมาตรฐานพร้อมขนาดและวัตถุประสงค์ในการจัดเก็บ
อักขระจะแสดงด้วยประเภทถ่านซึ่งสามารถเก็บค่าที่แตกต่างกันได้เพียง 256 ค่า หากคุณคุ้นเคยกับประเภทถ่านจากภาษาอื่นคุณอาจทราบแล้วว่ามันไม่ใหญ่พอที่จะรองรับสัญลักษณ์ของระบบการเขียนจำนวนมาก
ประเภท | ขนาดการจัดเก็บ | วัตถุประสงค์ |
---|---|---|
ถ่าน | 1 ไบต์ | หน่วยรหัส UTF-8 |
wchar | 2 ไบต์ | หน่วยรหัส UTF-16 |
dchar | 4 ไบต์ | หน่วยรหัส UTF-32 และจุดรหัส Unicode |
ฟังก์ชั่นอักขระที่มีประโยชน์บางอย่างแสดงอยู่ด้านล่าง -
isLower - กำหนดว่าอักขระตัวพิมพ์เล็ก?
isUpper - กำหนดว่าอักขระตัวพิมพ์ใหญ่?
isAlpha - กำหนดว่าอักขระที่เป็นตัวเลขและตัวอักษร Unicode (โดยทั่วไปคือตัวอักษรหรือตัวเลข)?
isWhite - กำหนดว่าอักขระเว้นวรรคหรือไม่?
toLower - สร้างตัวพิมพ์เล็กของอักขระที่กำหนด
toUpper - สร้างตัวพิมพ์ใหญ่ของอักขระที่กำหนด
import std.stdio;
import std.uni;
void main() {
writeln("Is ğ lowercase? ", isLower('ğ'));
writeln("Is Ş lowercase? ", isLower('Ş'));
writeln("Is İ uppercase? ", isUpper('İ'));
writeln("Is ç uppercase? ", isUpper('ç'));
writeln("Is z alphanumeric? ", isAlpha('z'));
writeln("Is new-line whitespace? ", isWhite('\n'));
writeln("Is underline whitespace? ", isWhite('_'));
writeln("The lowercase of Ğ: ", toLower('Ğ'));
writeln("The lowercase of İ: ", toLower('İ'));
writeln("The uppercase of ş: ", toUpper('ş'));
writeln("The uppercase of ı: ", toUpper('ı'));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Is ğ lowercase? true
Is Ş lowercase? false
Is İ uppercase? true
Is ç uppercase? false
Is z alphanumeric? true
Is new-line whitespace? true
Is underline whitespace? false
The lowercase of Ğ: ğ
The lowercase of İ: i
The uppercase of ş: Ş
The uppercase of ı: I
การอ่านอักขระใน D
เราสามารถอ่านอักขระโดยใช้readfดังภาพด้านล่าง
readf(" %s", &letter);
เนื่องจากการเขียนโปรแกรม D สนับสนุน Unicode ในการอ่านอักขระ Unicode เราจึงต้องอ่านสองครั้งและเขียนสองครั้งเพื่อให้ได้ผลลัพธ์ที่คาดหวัง สิ่งนี้ใช้ไม่ได้กับคอมไพเลอร์ออนไลน์ ตัวอย่างดังแสดงด้านล่าง
import std.stdio;
void main() {
char firstCode;
char secondCode;
write("Please enter a letter: ");
readf(" %s", &firstCode);
readf(" %s", &secondCode);
writeln("The letter that has been read: ", firstCode, secondCode);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Please enter a letter: ğ
The letter that has been read: ğ
D แสดงการแสดงสตริงสองประเภทดังต่อไปนี้ -
- อาร์เรย์อักขระ
- สตริงภาษาหลัก
อาร์เรย์อักขระ
เราสามารถแสดงอาร์เรย์อักขระในหนึ่งในสองรูปแบบดังที่แสดงด้านล่าง รูปแบบแรกระบุขนาดโดยตรงและรูปแบบที่สองใช้วิธีการ dup ซึ่งสร้างสำเนาที่เขียนได้ของสตริง "สวัสดีตอนเช้า"
char[9] greeting1 = "Hello all";
char[] greeting2 = "Good morning".dup;
ตัวอย่าง
นี่คือตัวอย่างง่ายๆโดยใช้รูปแบบอาร์เรย์อักขระง่ายๆข้างต้น
import std.stdio;
void main(string[] args) {
char[9] greeting1 = "Hello all";
writefln("%s",greeting1);
char[] greeting2 = "Good morning".dup;
writefln("%s",greeting2);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Hello all
Good morning
สตริงภาษาหลัก
สตริงถูกสร้างขึ้นในภาษาหลักของ D สตริงเหล่านี้ทำงานร่วมกันได้กับอาร์เรย์อักขระที่แสดงด้านบน ตัวอย่างต่อไปนี้แสดงการแสดงสตริงอย่างง่าย
string greeting1 = "Hello all";
ตัวอย่าง
import std.stdio;
void main(string[] args) {
string greeting1 = "Hello all";
writefln("%s",greeting1);
char[] greeting2 = "Good morning".dup;
writefln("%s",greeting2);
string greeting3 = greeting1;
writefln("%s",greeting3);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Hello all
Good morning
Hello all
การต่อสตริง
การต่อสายอักขระในการเขียนโปรแกรม D ใช้สัญลักษณ์ tilde (~)
ตัวอย่าง
import std.stdio;
void main(string[] args) {
string greeting1 = "Good";
char[] greeting2 = "morning".dup;
char[] greeting3 = greeting1~" "~greeting2;
writefln("%s",greeting3);
string greeting4 = "morning";
string greeting5 = greeting1~" "~greeting4;
writefln("%s",greeting5);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Good morning
Good morning
ความยาวของสตริง
ความยาวของสตริงในหน่วยไบต์สามารถดึงข้อมูลได้ด้วยความช่วยเหลือของความยาว fuction
ตัวอย่าง
import std.stdio;
void main(string[] args) {
string greeting1 = "Good";
writefln("Length of string greeting1 is %d",greeting1.length);
char[] greeting2 = "morning".dup;
writefln("Length of string greeting2 is %d",greeting2.length);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Length of string greeting1 is 4
Length of string greeting2 is 7
การเปรียบเทียบสตริง
การเปรียบเทียบสตริงนั้นค่อนข้างง่ายในการเขียนโปรแกรม D คุณสามารถใช้ตัวดำเนินการ ==, <และ> สำหรับการเปรียบเทียบสตริง
ตัวอย่าง
import std.stdio;
void main() {
string s1 = "Hello";
string s2 = "World";
string s3 = "World";
if (s2 == s3) {
writeln("s2: ",s2," and S3: ",s3, " are the same!");
}
if (s1 < s2) {
writeln("'", s1, "' comes before '", s2, "'.");
} else {
writeln("'", s2, "' comes before '", s1, "'.");
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
s2: World and S3: World are the same!
'Hello' comes before 'World'.
การเปลี่ยนสตริง
เราสามารถแทนที่สตริงโดยใช้สตริง []
ตัวอย่าง
import std.stdio;
import std.string;
void main() {
char[] s1 = "hello world ".dup;
char[] s2 = "sample".dup;
s1[6..12] = s2[0..6];
writeln(s1);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
hello sample
วิธีการจัดทำดัชนี
วิธีการทำดัชนีสำหรับตำแหน่งของสตริงย่อยในสตริงรวมทั้ง indexOf และ lastIndexOf จะอธิบายในตัวอย่างต่อไปนี้
ตัวอย่าง
import std.stdio;
import std.string;
void main() {
char[] s1 = "hello World ".dup;
writeln("indexOf of llo in hello is ",std.string.indexOf(s1,"llo"));
writeln(s1);
writeln("lastIndexOf of O in hello is " ,std.string.lastIndexOf(s1,"O",CaseSensitive.no));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
indexOf.of llo in hello is 2
hello World
lastIndexOf of O in hello is 7
การจัดการกรณี
วิธีการที่ใช้ในการเปลี่ยนเคสจะแสดงในตัวอย่างต่อไปนี้
ตัวอย่าง
import std.stdio;
import std.string;
void main() {
char[] s1 = "hello World ".dup;
writeln("Capitalized string of s1 is ",capitalize(s1));
writeln("Uppercase string of s1 is ",toUpper(s1));
writeln("Lowercase string of s1 is ",toLower(s1));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Capitalized string of s1 is Hello world
Uppercase string of s1 is HELLO WORLD
Lowercase string of s1 is hello world
การ จำกัด อักขระ
อักขระการวางตัวในสตริงแสดงในตัวอย่างต่อไปนี้
ตัวอย่าง
import std.stdio;
import std.string;
void main() {
string s = "H123Hello1";
string result = munch(s, "0123456789H");
writeln("Restrict trailing characters:",result);
result = squeeze(s, "0123456789H");
writeln("Restrict leading characters:",result);
s = " Hello World ";
writeln("Stripping leading and trailing whitespace:",strip(s));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Restrict trailing characters:H123H
Restrict leading characters:ello1
Stripping leading and trailing whitespace:Hello World
ภาษาโปรแกรม D จัดเตรียมโครงสร้างข้อมูลชื่อ arraysซึ่งเก็บคอลเลกชันตามลำดับขนาดคงที่ขององค์ประกอบประเภทเดียวกัน อาร์เรย์ใช้ในการจัดเก็บชุดข้อมูล มักจะมีประโยชน์มากกว่าที่จะคิดว่าอาร์เรย์เป็นชุดของตัวแปรประเภทเดียวกัน
แทนที่จะประกาศตัวแปรเดี่ยวเช่น number0, number1, ... และ number99 คุณประกาศตัวแปรอาร์เรย์หนึ่งตัวเช่นตัวเลขและใช้ตัวเลข [0], ตัวเลข [1] และ ... , ตัวเลข [99] เพื่อแสดง ตัวแปรแต่ละตัว องค์ประกอบเฉพาะในอาร์เรย์ถูกเข้าถึงโดยดัชนี
อาร์เรย์ทั้งหมดประกอบด้วยตำแหน่งหน่วยความจำที่อยู่ติดกัน ที่อยู่ต่ำสุดสอดคล้องกับองค์ประกอบแรกและที่อยู่สูงสุดขององค์ประกอบสุดท้าย
การประกาศอาร์เรย์
ในการประกาศอาร์เรย์ในภาษาโปรแกรม D โปรแกรมเมอร์จะระบุประเภทขององค์ประกอบและจำนวนองค์ประกอบที่อาร์เรย์ต้องการดังนี้ -
type arrayName [ arraySize ];
สิ่งนี้เรียกว่าอาร์เรย์มิติเดียว arraySizeต้องเป็นจำนวนเต็มมากขึ้นอย่างต่อเนื่องกว่าศูนย์และประเภทสามารถใด ๆ ที่ถูกต้องเขียนโปรแกรม D ชนิดข้อมูลภาษา ตัวอย่างเช่นในการประกาศอาร์เรย์ 10 องค์ประกอบที่เรียกว่าbalance of type double ให้ใช้คำสั่งนี้ -
double balance[10];
การเริ่มต้นอาร์เรย์
คุณสามารถเริ่มต้นอิลิเมนต์อาร์เรย์ภาษาการเขียนโปรแกรม D ทีละรายการหรือใช้คำสั่งเดียวดังต่อไปนี้
double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
จำนวนค่าระหว่างวงเล็บเหลี่ยม [] ทางด้านขวาต้องไม่เกินจำนวนองค์ประกอบที่คุณประกาศสำหรับอาร์เรย์ระหว่างวงเล็บเหลี่ยม [] ตัวอย่างต่อไปนี้กำหนดองค์ประกอบเดียวของอาร์เรย์ -
หากคุณละเว้นขนาดของอาร์เรย์อาร์เรย์ที่ใหญ่พอที่จะรองรับการเริ่มต้นจะถูกสร้างขึ้น เพราะฉะนั้นถ้าคุณเขียน
double balance[] = [1000.0, 2.0, 3.4, 17.0, 50.0];
จากนั้นคุณจะสร้างอาร์เรย์เดียวกันกับที่คุณทำในตัวอย่างก่อนหน้านี้
balance[4] = 50.0;
คำสั่งข้างต้นกำหนดองค์ประกอบหมายเลข 5 ในอาร์เรย์เป็นค่า 50.0 อาร์เรย์ที่มีดัชนีที่ 4 จะเป็น 5 เช่นองค์ประกอบสุดท้ายเนื่องจากอาร์เรย์ทั้งหมดมี 0 เป็นดัชนีขององค์ประกอบแรกซึ่งเรียกอีกอย่างว่าดัชนีฐาน การแสดงภาพต่อไปนี้แสดงอาร์เรย์เดียวกันกับที่เรากล่าวถึงข้างต้น -
การเข้าถึงองค์ประกอบอาร์เรย์
องค์ประกอบถูกเข้าถึงโดยการสร้างดัชนีชื่ออาร์เรย์ ทำได้โดยการวางดัชนีขององค์ประกอบไว้ในวงเล็บเหลี่ยมหลังชื่อของอาร์เรย์ ตัวอย่างเช่น -
double salary = balance[9];
ข้อความข้างต้นใช้เวลา 10 ปีบริบูรณ์องค์ประกอบจาก array และกำหนดค่าให้ตัวแปรเงินเดือน ตัวอย่างต่อไปนี้ใช้การประกาศการกำหนดและการเข้าถึงอาร์เรย์ -
import std.stdio;
void main() {
int n[ 10 ]; // n is an array of 10 integers
// initialize elements of array n to 0
for ( int i = 0; i < 10; i++ ) {
n[ i ] = i + 100; // set element at location i to i + 100
}
writeln("Element \t Value");
// output each array element's value
for ( int j = 0; j < 10; j++ ) {
writeln(j," \t ",n[j]);
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Element Value
0 100
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
อาร์เรย์แบบคงที่เทียบกับอาร์เรย์แบบไดนามิก
หากระบุความยาวของอาร์เรย์ขณะเขียนโปรแกรมอาร์เรย์นั้นจะเป็นอาร์เรย์แบบคงที่ เมื่อความยาวสามารถเปลี่ยนแปลงได้ในระหว่างการทำงานของโปรแกรมอาร์เรย์นั้นจะเป็นอาร์เรย์แบบไดนามิก
การกำหนดอาร์เรย์แบบไดนามิกนั้นง่ายกว่าการกำหนดอาร์เรย์ที่มีความยาวคงที่เนื่องจากการละเว้นความยาวจะทำให้อาร์เรย์แบบไดนามิก -
int[] dynamicArray;
คุณสมบัติอาร์เรย์
นี่คือคุณสมบัติของอาร์เรย์ -
ซีเนียร์ | คุณสมบัติและคำอธิบาย |
---|---|
1 | .init อาร์เรย์แบบคงที่จะส่งคืนลิเทอรัลอาร์เรย์โดยแต่ละองค์ประกอบของลิเทอรัลเป็นคุณสมบัติ. init ของชนิดองค์ประกอบอาร์เรย์ |
2 | .sizeof อาร์เรย์แบบคงที่จะส่งกลับความยาวอาร์เรย์คูณด้วยจำนวนไบต์ต่อองค์ประกอบอาร์เรย์ในขณะที่อาร์เรย์แบบไดนามิกส่งกลับขนาดของการอ้างอิงอาร์เรย์แบบไดนามิกซึ่งเป็น 8 ในการสร้าง 32 บิตและ 16 ในการสร้าง 64 บิต |
3 | .length อาร์เรย์แบบคงที่จะส่งคืนจำนวนองค์ประกอบในอาร์เรย์ในขณะที่อาร์เรย์แบบไดนามิกใช้เพื่อรับ / กำหนดจำนวนองค์ประกอบในอาร์เรย์ ความยาวเป็นประเภท size_t |
4 | .ptr ส่งกลับตัวชี้ไปยังองค์ประกอบแรกของอาร์เรย์ |
5 | .dup สร้างอาร์เรย์แบบไดนามิกที่มีขนาดเท่ากันและคัดลอกเนื้อหาของอาร์เรย์ลงในนั้น |
6 | .idup สร้างอาร์เรย์แบบไดนามิกที่มีขนาดเท่ากันและคัดลอกเนื้อหาของอาร์เรย์ลงในนั้น สำเนาถูกพิมพ์ว่าไม่เปลี่ยนรูป |
7 | .reverse ย้อนกลับตามลำดับขององค์ประกอบในอาร์เรย์ ส่งคืนอาร์เรย์ |
8 | .sort จัดเรียงตามลำดับขององค์ประกอบในอาร์เรย์ ส่งคืนอาร์เรย์ |
ตัวอย่าง
ตัวอย่างต่อไปนี้อธิบายคุณสมบัติต่างๆของอาร์เรย์ -
import std.stdio;
void main() {
int n[ 5 ]; // n is an array of 5 integers
// initialize elements of array n to 0
for ( int i = 0; i < 5; i++ ) {
n[ i ] = i + 100; // set element at location i to i + 100
}
writeln("Initialized value:",n.init);
writeln("Length: ",n.length);
writeln("Size of: ",n.sizeof);
writeln("Pointer:",n.ptr);
writeln("Duplicate Array: ",n.dup);
writeln("iDuplicate Array: ",n.idup);
n = n.reverse.dup;
writeln("Reversed Array: ",n);
writeln("Sorted Array: ",n.sort);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Initialized value:[0, 0, 0, 0, 0]
Length: 5
Size of: 20
Pointer:7FFF5A373920
Duplicate Array: [100, 101, 102, 103, 104]
iDuplicate Array: [100, 101, 102, 103, 104]
Reversed Array: [104, 103, 102, 101, 100]
Sorted Array: [100, 101, 102, 103, 104]
อาร์เรย์หลายมิติใน D
การเขียนโปรแกรม D ทำให้อาร์เรย์หลายมิติ นี่คือรูปแบบทั่วไปของการประกาศอาร์เรย์หลายมิติ -
type name[size1][size2]...[sizeN];
ตัวอย่าง
คำประกาศต่อไปนี้สร้างสามมิติ 5 10. อาร์เรย์จำนวนเต็ม 4 ตัว -
int threedim[5][10][4];
อาร์เรย์สองมิติใน D
รูปแบบที่ง่ายที่สุดของอาร์เรย์หลายมิติคืออาร์เรย์สองมิติ โดยพื้นฐานแล้วอาร์เรย์สองมิติคือรายการของอาร์เรย์หนึ่งมิติ ในการประกาศอาร์เรย์จำนวนเต็มสองมิติขนาด [x, y] คุณจะต้องเขียนไวยากรณ์ดังนี้ -
type arrayName [ x ][ y ];
ที่ไหน type สามารถเป็นประเภทข้อมูลการเขียนโปรแกรม D ที่ถูกต้องและ arrayName จะเป็นตัวระบุการเขียนโปรแกรม D ที่ถูกต้อง
โดยที่typeสามารถเป็นชนิดข้อมูลการเขียนโปรแกรม D ที่ถูกต้องและarrayNameคือตัวระบุการเขียนโปรแกรม D ที่ถูกต้อง
อาร์เรย์สองมิติสามารถคิดเป็นตารางซึ่งมี x จำนวนแถวและจำนวนคอลัมน์ y อาร์เรย์สองมิติa ที่มีสามแถวและสี่คอลัมน์สามารถแสดงได้ดังต่อไปนี้ -
ดังนั้นทุกองค์ประกอบในอาร์เรย์ a ถูกระบุโดยองค์ประกอบเป็น a[ i ][ j ], ที่ไหน a คือชื่อของอาร์เรย์และ i และ j คือตัวห้อยที่ระบุแต่ละองค์ประกอบโดยไม่ซ้ำกันในไฟล์.
การเริ่มต้นอาร์เรย์สองมิติ
อาร์เรย์แบบหลายมิติอาจเริ่มต้นได้โดยการระบุค่าวงเล็บสำหรับแต่ละแถว อาร์เรย์ต่อไปนี้มี 3 แถวและแต่ละแถวมี 4 คอลัมน์
int a[3][4] = [
[0, 1, 2, 3] , /* initializers for row indexed by 0 */
[4, 5, 6, 7] , /* initializers for row indexed by 1 */
[8, 9, 10, 11] /* initializers for row indexed by 2 */
];
วงเล็บปีกกาที่ซ้อนกันซึ่งระบุแถวที่ต้องการเป็นทางเลือก การเริ่มต้นต่อไปนี้เทียบเท่ากับตัวอย่างก่อนหน้านี้ -
int a[3][4] = [0,1,2,3,4,5,6,7,8,9,10,11];
การเข้าถึงองค์ประกอบอาร์เรย์สองมิติ
องค์ประกอบในอาร์เรย์ 2 มิติเข้าถึงได้โดยใช้ตัวห้อยหมายถึงดัชนีแถวและดัชนีคอลัมน์ของอาร์เรย์ ตัวอย่างเช่น
int val = a[2][3];
คำสั่งดังกล่าวนำองค์ประกอบที่ 4 จากแถวที่ 3 ของอาร์เรย์ คุณสามารถตรวจสอบได้ใน digram ด้านบน
import std.stdio;
void main () {
// an array with 5 rows and 2 columns.
int a[5][2] = [ [0,0], [1,2], [2,4], [3,6],[4,8]];
// output each array element's value
for ( int i = 0; i < 5; i++ ) for ( int j = 0; j < 2; j++ ) {
writeln( "a[" , i , "][" , j , "]: ",a[i][j]);
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
a[0][0]: 0
a[0][1]: 0
a[1][0]: 1
a[1][1]: 2
a[2][0]: 2
a[2][1]: 4
a[3][0]: 3
a[3][1]: 6
a[4][0]: 4
a[4][1]: 8
การดำเนินการอาร์เรย์ทั่วไปใน D
นี่คือการดำเนินการต่างๆที่ดำเนินการในอาร์เรย์ -
การแบ่งอาร์เรย์
เรามักใช้ส่วนหนึ่งของอาร์เรย์และการแบ่งส่วนอาร์เรย์มักมีประโยชน์มาก ตัวอย่างง่ายๆสำหรับการแบ่งส่วนอาร์เรย์แสดงไว้ด้านล่าง
import std.stdio;
void main () {
// an array with 5 elements.
double a[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
double[] b;
b = a[1..3];
writeln(b);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
[2, 3.4]
การคัดลอกอาร์เรย์
เรายังใช้การคัดลอกอาร์เรย์ ตัวอย่างง่ายๆสำหรับการคัดลอกอาร์เรย์แสดงไว้ด้านล่าง
import std.stdio;
void main () {
// an array with 5 elements.
double a[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
double b[5];
writeln("Array a:",a);
writeln("Array b:",b);
b[] = a; // the 5 elements of a[5] are copied into b[5]
writeln("Array b:",b);
b[] = a[]; // the 5 elements of a[3] are copied into b[5]
writeln("Array b:",b);
b[1..2] = a[0..1]; // same as b[1] = a[0]
writeln("Array b:",b);
b[0..2] = a[1..3]; // same as b[0] = a[1], b[1] = a[2]
writeln("Array b:",b);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Array a:[1000, 2, 3.4, 17, 50]
Array b:[nan, nan, nan, nan, nan]
Array b:[1000, 2, 3.4, 17, 50]
Array b:[1000, 2, 3.4, 17, 50]
Array b:[1000, 1000, 3.4, 17, 50]
Array b:[2, 3.4, 3.4, 17, 50]
การตั้งค่าอาร์เรย์
ตัวอย่างง่ายๆสำหรับการตั้งค่าในอาร์เรย์แสดงไว้ด้านล่าง
import std.stdio;
void main () {
// an array with 5 elements.
double a[5];
a[] = 5;
writeln("Array a:",a);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Array a:[5, 5, 5, 5, 5]
การต่ออาร์เรย์
ตัวอย่างง่ายๆสำหรับการต่ออาร์เรย์สองอาร์เรย์แสดงไว้ด้านล่าง
import std.stdio;
void main () {
// an array with 5 elements.
double a[5] = 5;
double b[5] = 10;
double [] c;
c = a~b;
writeln("Array c: ",c);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Array c: [5, 5, 5, 5, 5, 10, 10, 10, 10, 10]
Associative arrays มีดัชนีที่ไม่จำเป็นต้องเป็นจำนวนเต็มและอาจมีข้อมูลเบาบาง ดัชนีสำหรับอาร์เรย์ที่เชื่อมโยงเรียกว่าKeyและประเภทของมันเรียกว่า KeyType.
Associative arrays ถูกประกาศโดยการวาง KeyType ไว้ใน [] ของการประกาศอาร์เรย์ ตัวอย่างง่ายๆสำหรับการเชื่อมโยงอาร์เรย์แสดงไว้ด้านล่าง
import std.stdio;
void main () {
int[string] e; // associative array b of ints that are
e["test"] = 3;
writeln(e["test"]);
string[string] f;
f["test"] = "Tuts";
writeln(f["test"]);
writeln(f);
f.remove("test");
writeln(f);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
3
Tuts
["test":"Tuts"]
[]
การเริ่มต้น Associative Array
การเริ่มต้นอย่างง่ายของอาร์เรย์เชื่อมโยงแสดงอยู่ด้านล่าง
import std.stdio;
void main () {
int[string] days =
[ "Monday" : 0,
"Tuesday" : 1,
"Wednesday" : 2,
"Thursday" : 3,
"Friday" : 4,
"Saturday" : 5,
"Sunday" : 6 ];
writeln(days["Tuesday"]);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
1
คุณสมบัติของ Associative Array
นี่คือคุณสมบัติของอาร์เรย์ที่เชื่อมโยง -
ซีเนียร์ | คุณสมบัติและคำอธิบาย |
---|---|
1 | .sizeof ส่งคืนขนาดของการอ้างอิงไปยังอาร์เรย์ที่เชื่อมโยง เป็นแบบ 4 ใน 32 บิตและ 8 ในรุ่น 64 บิต |
2 | .length ส่งคืนจำนวนค่าในอาร์เรย์ที่เชื่อมโยง ไม่เหมือนกับอาร์เรย์แบบไดนามิกคืออ่านอย่างเดียว |
3 | .dup สร้างอาร์เรย์เชื่อมโยงใหม่ที่มีขนาดเท่ากันและคัดลอกเนื้อหาของอาร์เรย์ที่เชื่อมโยงลงไป |
4 | .keys ส่งคืนอาร์เรย์แบบไดนามิกซึ่งเป็นองค์ประกอบที่เป็นคีย์ในอาร์เรย์ที่เชื่อมโยงกัน |
5 | .values ส่งคืนอาร์เรย์แบบไดนามิกซึ่งเป็นองค์ประกอบที่เป็นค่าในอาร์เรย์ที่เชื่อมโยงกัน |
6 | .rehash จัดเรียงอาร์เรย์ที่เชื่อมโยงใหม่เพื่อให้การค้นหามีประสิทธิภาพมากขึ้น การ rehash จะมีผลเมื่อโปรแกรมโหลดตารางสัญลักษณ์เสร็จแล้วและตอนนี้ต้องการการค้นหาอย่างรวดเร็ว ส่งคืนการอ้างอิงไปยังอาร์เรย์ที่จัดระเบียบใหม่ |
7 | .byKey() ส่งคืนผู้รับมอบสิทธิ์ที่เหมาะสำหรับใช้เป็น Aggregate เป็น ForeachStatement ซึ่งจะวนซ้ำบนคีย์ของอาร์เรย์ที่เชื่อมโยง |
8 | .byValue() ส่งคืนผู้รับมอบสิทธิ์ที่เหมาะสมสำหรับใช้เป็น Aggregate ไปยัง ForeachStatement ซึ่งจะวนซ้ำค่าของอาร์เรย์ที่เชื่อมโยง |
9 | .get(Key key, lazy Value defVal) ค้นหาคีย์; หากมีอยู่จะส่งคืนค่าที่สอดคล้องกันอื่น ๆ จะประเมินและส่งกลับ defVal |
10 | .remove(Key key) ลบวัตถุสำหรับคีย์ |
ตัวอย่าง
ตัวอย่างการใช้คุณสมบัติข้างต้นแสดงไว้ด้านล่าง
import std.stdio;
void main () {
int[string] array1;
array1["test"] = 3;
array1["test2"] = 20;
writeln("sizeof: ",array1.sizeof);
writeln("length: ",array1.length);
writeln("dup: ",array1.dup);
array1.rehash;
writeln("rehashed: ",array1);
writeln("keys: ",array1.keys);
writeln("values: ",array1.values);
foreach (key; array1.byKey) {
writeln("by key: ",key);
}
foreach (value; array1.byValue) {
writeln("by value ",value);
}
writeln("get value for key test: ",array1.get("test",10));
writeln("get value for key test3: ",array1.get("test3",10));
array1.remove("test");
writeln(array1);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
sizeof: 8
length: 2
dup: ["test":3, "test2":20]
rehashed: ["test":3, "test2":20]
keys: ["test", "test2"]
values: [3, 20]
by key: test
by key: test2
by value 3
by value 20
get value for key test: 3
get value for key test3: 10
["test2":20]
ตัวชี้การเขียนโปรแกรม D นั้นง่ายและสนุกในการเรียนรู้ งานการเขียนโปรแกรม D บางอย่างดำเนินการได้ง่ายขึ้นโดยใช้พอยน์เตอร์และงานการเขียนโปรแกรม D อื่น ๆ เช่นการจัดสรรหน่วยความจำแบบไดนามิกจะไม่สามารถทำได้หากไม่มีสิ่งเหล่านี้ ตัวชี้ง่ายๆแสดงอยู่ด้านล่าง
แทนที่จะชี้ไปที่ตัวแปรโดยตรงตัวชี้จะชี้ไปที่ที่อยู่ของตัวแปร ดังที่คุณทราบทุกตัวแปรคือตำแหน่งหน่วยความจำและตำแหน่งหน่วยความจำทุกแห่งจะมีที่อยู่ที่กำหนดไว้ซึ่งสามารถเข้าถึงได้โดยใช้ตัวดำเนินการเครื่องหมายและ (&) ซึ่งหมายถึงที่อยู่ในหน่วยความจำ พิจารณาสิ่งต่อไปนี้ซึ่งพิมพ์ที่อยู่ของตัวแปรที่กำหนดไว้ -
import std.stdio;
void main () {
int var1;
writeln("Address of var1 variable: ",&var1);
char var2[10];
writeln("Address of var2 variable: ",&var2);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Address of var1 variable: 7FFF52691928
Address of var2 variable: 7FFF52691930
ตัวชี้คืออะไร?
ก pointerเป็นตัวแปรที่มีค่าเป็นที่อยู่ของตัวแปรอื่น เช่นเดียวกับตัวแปรหรือค่าคงที่คุณต้องประกาศตัวชี้ก่อนจึงจะสามารถใช้งานได้ รูปแบบทั่วไปของการประกาศตัวแปรพอยน์เตอร์คือ -
type *var-name;
ที่นี่ typeเป็นประเภทฐานของตัวชี้ ต้องเป็นประเภทการเขียนโปรแกรมที่ถูกต้องและvar-nameคือชื่อของตัวแปรพอยน์เตอร์ เครื่องหมายดอกจันที่คุณใช้เพื่อประกาศตัวชี้เป็นเครื่องหมายดอกจันเดียวกับที่คุณใช้ในการคูณ อย่างไรก็ตาม; ในคำสั่งนี้เครื่องหมายดอกจันถูกใช้เพื่อกำหนดตัวแปรเป็นตัวชี้ ต่อไปนี้เป็นการประกาศตัวชี้ที่ถูกต้อง -
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
ชนิดข้อมูลจริงของค่าของพอยน์เตอร์ทั้งหมดไม่ว่าจะเป็นจำนวนเต็มจำนวนทศนิยมอักขระหรืออื่น ๆ จะเหมือนกันเป็นเลขฐานสิบหกแบบยาวที่แสดงแอดเดรสหน่วยความจำ ความแตกต่างเพียงอย่างเดียวระหว่างพอยน์เตอร์ของชนิดข้อมูลที่แตกต่างกันคือชนิดข้อมูลของตัวแปรหรือค่าคงที่ที่ตัวชี้ชี้ไป
การใช้พอยน์เตอร์ในการเขียนโปรแกรม D
มีการดำเนินการที่สำคัญไม่กี่อย่างเมื่อเราใช้คำแนะนำบ่อยมาก
เรากำหนดตัวแปรตัวชี้
กำหนดที่อยู่ของตัวแปรให้กับตัวชี้
ในที่สุดก็เข้าถึงค่าตามที่อยู่ที่มีอยู่ในตัวแปรตัวชี้
สิ่งนี้ทำได้โดยใช้ตัวดำเนินการยูนารี *ที่ส่งคืนค่าของตัวแปรที่อยู่ตามที่อยู่ที่ระบุโดยตัวถูกดำเนินการ ตัวอย่างต่อไปนี้ใช้ประโยชน์จากการดำเนินการเหล่านี้ -
import std.stdio;
void main () {
int var = 20; // actual variable declaration.
int *ip; // pointer variable
ip = &var; // store address of var in pointer variable
writeln("Value of var variable: ",var);
writeln("Address stored in ip variable: ",ip);
writeln("Value of *ip variable: ",*ip);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Value of var variable: 20
Address stored in ip variable: 7FFF5FB7E930
Value of *ip variable: 20
ตัวชี้ Null
เป็นแนวทางปฏิบัติที่ดีเสมอในการกำหนดค่า NULL ให้กับตัวแปรตัวชี้ในกรณีที่คุณไม่มีที่อยู่ที่แน่นอนที่จะกำหนด สิ่งนี้ทำได้ในช่วงเวลาของการประกาศตัวแปร ตัวชี้ที่กำหนดค่าว่างเรียกว่า anull ตัวชี้
ตัวชี้โมฆะคือค่าคงที่ที่มีค่าเป็นศูนย์ที่กำหนดไว้ในไลบรารีมาตรฐานต่างๆรวมถึง iostream พิจารณาโปรแกรมต่อไปนี้ -
import std.stdio;
void main () {
int *ptr = null;
writeln("The value of ptr is " , ptr) ;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
The value of ptr is null
ในระบบปฏิบัติการส่วนใหญ่โปรแกรมไม่ได้รับอนุญาตให้เข้าถึงหน่วยความจำที่อยู่ 0 เนื่องจากหน่วยความจำนั้นสงวนไว้โดยระบบปฏิบัติการ อย่างไรก็ตาม; ที่อยู่หน่วยความจำ 0 มีความสำคัญเป็นพิเศษ เป็นการส่งสัญญาณว่าตัวชี้ไม่ได้ตั้งใจให้ชี้ไปยังตำแหน่งหน่วยความจำที่สามารถเข้าถึงได้
ตามแบบแผนถ้าตัวชี้มีค่า null (ศูนย์) จะถือว่าชี้ไปที่ความว่างเปล่า ในการตรวจสอบตัวชี้ค่าว่างคุณสามารถใช้คำสั่ง if ได้ดังนี้ -
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
ดังนั้นหากพอยน์เตอร์ที่ไม่ได้ใช้ทั้งหมดได้รับค่า null และคุณหลีกเลี่ยงการใช้พอยน์เตอร์ว่างคุณสามารถหลีกเลี่ยงการใช้พอยน์เตอร์ที่ไม่ได้กำหนดค่าเริ่มต้นในทางที่ผิดโดยไม่ได้ตั้งใจ หลายครั้งตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นจะมีค่าขยะบางอย่างและยากที่จะแก้ไขข้อบกพร่องของโปรแกรม
ตัวชี้เลขคณิต
มีตัวดำเนินการเลขคณิตสี่ตัวที่สามารถใช้กับพอยน์เตอร์ได้: ++, -, + และ -
เพื่อทำความเข้าใจเลขคณิตของตัวชี้ให้เราพิจารณาตัวชี้จำนวนเต็มที่มีชื่อว่า ptrซึ่งชี้ไปที่แอดเดรส 1000 สมมติว่าเป็นจำนวนเต็ม 32 บิตให้เราดำเนินการทางคณิตศาสตร์ต่อไปนี้บนตัวชี้ -
ptr++
จากนั้น ptrจะชี้ไปที่ตำแหน่ง 1004 เนื่องจากแต่ละครั้งที่เพิ่ม ptr มันจะชี้ไปที่จำนวนเต็มถัดไป การดำเนินการนี้จะย้ายตัวชี้ไปยังตำแหน่งหน่วยความจำถัดไปโดยไม่ส่งผลกระทบต่อค่าจริงที่ตำแหน่งหน่วยความจำ
ถ้า ptr ชี้ไปที่อักขระที่มีแอดเดรสคือ 1,000 จากนั้นการดำเนินการข้างต้นจะชี้ไปที่ตำแหน่ง 1001 เนื่องจากอักขระถัดไปจะพร้อมใช้งานที่ 1001
การเพิ่มตัวชี้
เราชอบใช้ตัวชี้ในโปรแกรมของเราแทนอาร์เรย์เนื่องจากตัวชี้ตัวแปรสามารถเพิ่มขึ้นได้ซึ่งแตกต่างจากชื่ออาร์เรย์ที่ไม่สามารถเพิ่มขึ้นได้เนื่องจากเป็นตัวชี้คงที่ โปรแกรมต่อไปนี้จะเพิ่มตัวชี้ตัวแปรเพื่อเข้าถึงแต่ละองค์ประกอบที่สำเร็จของอาร์เรย์ -
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Address of var[0] = 18FDBC
Value of var[0] = 10
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 200
พอยน์เตอร์เทียบกับอาร์เรย์
ตัวชี้และอาร์เรย์มีความสัมพันธ์กันอย่างมาก อย่างไรก็ตามพอยน์เตอร์และอาร์เรย์ไม่สามารถใช้แทนกันได้อย่างสมบูรณ์ ตัวอย่างเช่นพิจารณาโปรแกรมต่อไปนี้ -
import std.stdio;
const int MAX = 3;
void main () {
int var[MAX] = [10, 100, 200];
int *ptr = &var[0];
var.ptr[2] = 290;
ptr[0] = 220;
for (int i = 0; i < MAX; i++, ptr++) {
writeln("Address of var[" , i , "] = ",ptr);
writeln("Value of var[" , i , "] = ",*ptr);
}
}
ในโปรแกรมด้านบนคุณสามารถดู var.ptr [2] เพื่อตั้งค่าองค์ประกอบที่สองและ ptr [0] ซึ่งใช้ในการตั้งค่าองค์ประกอบซีรอ ธ ตัวดำเนินการเพิ่มสามารถใช้ได้กับ ptr แต่ใช้กับ var ไม่ได้
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Address of var[0] = 18FDBC
Value of var[0] = 220
Address of var[1] = 18FDC0
Value of var[1] = 100
Address of var[2] = 18FDC4
Value of var[2] = 290
ชี้ไปที่ตัวชี้
ตัวชี้ไปยังตัวชี้คือรูปแบบของการเหนี่ยวนำหลายทิศทางหรือโซ่ของพอยน์เตอร์ โดยปกติตัวชี้จะมีที่อยู่ของตัวแปร เมื่อเรากำหนดตัวชี้ไปยังตัวชี้ตัวชี้ตัวแรกจะมีที่อยู่ของตัวชี้ตัวที่สองซึ่งชี้ไปยังตำแหน่งที่มีค่าจริงตามที่แสดงด้านล่าง
ตัวแปรที่เป็นตัวชี้ไปยังตัวชี้จะต้องถูกประกาศเช่นนี้ ทำได้โดยใส่เครื่องหมายดอกจันเพิ่มเติมไว้ข้างหน้าชื่อ ตัวอย่างเช่นต่อไปนี้เป็นไวยากรณ์เพื่อประกาศตัวชี้เป็นตัวชี้ประเภท int -
int **var;
เมื่อค่าเป้าหมายถูกชี้ทางอ้อมโดยตัวชี้ไปยังตัวชี้การเข้าถึงค่านั้นจะต้องใช้ตัวดำเนินการเครื่องหมายดอกจันสองครั้งดังแสดงด้านล่างในตัวอย่าง -
import std.stdio;
const int MAX = 3;
void main () {
int var = 3000;
writeln("Value of var :" , var);
int *ptr = &var;
writeln("Value available at *ptr :" ,*ptr);
int **pptr = &ptr;
writeln("Value available at **pptr :",**pptr);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Value of var :3000
Value available at *ptr :3000
Value available at **pptr :3000
การส่งตัวชี้ไปยังฟังก์ชัน
D ช่วยให้คุณส่งตัวชี้ไปยังฟังก์ชัน ในการดำเนินการดังกล่าวเพียงแค่ประกาศพารามิเตอร์ฟังก์ชันเป็นชนิดตัวชี้
ตัวอย่างง่ายๆต่อไปนี้ส่งตัวชี้ไปยังฟังก์ชัน
import std.stdio;
void main () {
// an int array with 5 elements.
int balance[5] = [1000, 2, 3, 17, 50];
double avg;
avg = getAverage( &balance[0], 5 ) ;
writeln("Average is :" , avg);
}
double getAverage(int *arr, int size) {
int i;
double avg, sum = 0;
for (i = 0; i < size; ++i) {
sum += arr[i];
}
avg = sum/size;
return avg;
}
เมื่อโค้ดด้านบนถูกคอมไพล์เข้าด้วยกันและเรียกใช้งานโค้ดจะได้ผลลัพธ์ดังนี้
Average is :214.4
กลับตัวชี้จากฟังก์ชัน
พิจารณาฟังก์ชันต่อไปนี้ซึ่งส่งคืนตัวเลข 10 ตัวโดยใช้ตัวชี้หมายถึงที่อยู่ขององค์ประกอบอาร์เรย์แรก
import std.stdio;
void main () {
int *p = getNumber();
for ( int i = 0; i < 10; i++ ) {
writeln("*(p + " , i , ") : ",*(p + i));
}
}
int * getNumber( ) {
static int r [10];
for (int i = 0; i < 10; ++i) {
r[i] = i;
}
return &r[0];
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
*(p + 0) : 0
*(p + 1) : 1
*(p + 2) : 2
*(p + 3) : 3
*(p + 4) : 4
*(p + 5) : 5
*(p + 6) : 6
*(p + 7) : 7
*(p + 8) : 8
*(p + 9) : 9
ชี้ไปที่อาร์เรย์
ชื่ออาร์เรย์เป็นตัวชี้ค่าคงที่ขององค์ประกอบแรกของอาร์เรย์ ดังนั้นในการประกาศ -
double balance[50];
balanceเป็นตัวชี้ไปที่ & สมดุล [0] ซึ่งเป็นที่อยู่ขององค์ประกอบแรกของสมดุลอาร์เรย์ ดังนั้นส่วนของโปรแกรมต่อไปนี้จึงกำหนดp ที่อยู่ขององค์ประกอบแรกของ balance -
double *p;
double balance[10];
p = balance;
ถูกกฎหมายที่จะใช้ชื่ออาร์เรย์เป็นตัวชี้ค่าคงที่และในทางกลับกัน ดังนั้น * (balance + 4) จึงเป็นวิธีที่ถูกต้องในการเข้าถึงข้อมูลอย่างสมดุล [4]
เมื่อคุณจัดเก็บที่อยู่ขององค์ประกอบแรกใน p คุณสามารถเข้าถึงองค์ประกอบอาร์เรย์โดยใช้ * p, * (p + 1), * (p + 2) และอื่น ๆ ตัวอย่างต่อไปนี้แสดงแนวคิดทั้งหมดที่กล่าวถึงข้างต้น -
import std.stdio;
void main () {
// an array with 5 elements.
double balance[5] = [1000.0, 2.0, 3.4, 17.0, 50.0];
double *p;
p = &balance[0];
// output each array element's value
writeln("Array values using pointer " );
for ( int i = 0; i < 5; i++ ) {
writeln( "*(p + ", i, ") : ", *(p + i));
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Array values using pointer
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50
Tuples ใช้สำหรับการรวมหลายค่าเป็นอ็อบเจ็กต์เดียว Tuples มีลำดับขององค์ประกอบ องค์ประกอบสามารถเป็นประเภทนิพจน์หรือนามแฝง จำนวนและองค์ประกอบของทูเปิลได้รับการแก้ไขในเวลาคอมไพล์และไม่สามารถเปลี่ยนแปลงได้ในขณะรัน
สิ่งทอมีลักษณะของโครงสร้างและอาร์เรย์ องค์ประกอบทูเปิลอาจมีหลายประเภทเช่นโครงสร้าง องค์ประกอบสามารถเข้าถึงได้ผ่านการสร้างดัชนีเช่นอาร์เรย์ พวกเขาถูกนำไปใช้เป็นคุณลักษณะไลบรารีโดยเทมเพลต Tuple จากโมดูล std.typecons Tuple ใช้ TypeTuple จากโมดูล std.typetuple สำหรับการดำเนินการบางอย่าง
Tuple โดยใช้ tuple ()
สิ่งทอสามารถสร้างได้โดยฟังก์ชันทูเปิล () สมาชิกของทูเปิลถูกเข้าถึงโดยค่าดัชนี ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
import std.typecons;
void main() {
auto myTuple = tuple(1, "Tuts");
writeln(myTuple);
writeln(myTuple[0]);
writeln(myTuple[1]);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Tuple!(int, string)(1, "Tuts")
1
Tuts
Tuple โดยใช้ Tuple Template
นอกจากนี้ยังสามารถสร้าง Tuple ได้โดยตรงโดยใช้เทมเพลต Tuple แทนฟังก์ชัน tuple () ประเภทและชื่อของสมาชิกแต่ละคนถูกระบุเป็นพารามิเตอร์เทมเพลตสองรายการติดต่อกัน เป็นไปได้ที่จะเข้าถึงสมาชิกตามคุณสมบัติเมื่อสร้างโดยใช้เทมเพลต
import std.stdio;
import std.typecons;
void main() {
auto myTuple = Tuple!(int, "id",string, "value")(1, "Tuts");
writeln(myTuple);
writeln("by index 0 : ", myTuple[0]);
writeln("by .id : ", myTuple.id);
writeln("by index 1 : ", myTuple[1]);
writeln("by .value ", myTuple.value);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังต่อไปนี้
Tuple!(int, "id", string, "value")(1, "Tuts")
by index 0 : 1
by .id : 1
by index 1 : Tuts
by .value Tuts
การขยายพารามิเตอร์คุณสมบัติและฟังก์ชัน
สมาชิกของ Tuple สามารถขยายได้โดยใช้คุณสมบัติ. ขยายหรือโดยการแบ่งส่วน ค่าที่ขยาย / แบ่งส่วนนี้สามารถส่งผ่านเป็นรายการอาร์กิวเมนต์ของฟังก์ชัน ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
import std.typecons;
void method1(int a, string b, float c, char d) {
writeln("method 1 ",a,"\t",b,"\t",c,"\t",d);
}
void method2(int a, float b, char c) {
writeln("method 2 ",a,"\t",b,"\t",c);
}
void main() {
auto myTuple = tuple(5, "my string", 3.3, 'r');
writeln("method1 call 1");
method1(myTuple[]);
writeln("method1 call 2");
method1(myTuple.expand);
writeln("method2 call 1");
method2(myTuple[0], myTuple[$-2..$]);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
method1 call 1
method 1 5 my string 3.3 r
method1 call 2
method 1 5 my string 3.3 r
method2 call 1
method 2 5 3.3 r
TypeTuple
TypeTuple ถูกกำหนดไว้ในโมดูล std.typetuple รายการค่าและประเภทที่คั่นด้วยจุลภาค ตัวอย่างง่ายๆโดยใช้ TypeTuple แสดงไว้ด้านล่าง TypeTuple ใช้เพื่อสร้างรายการอาร์กิวเมนต์รายการเทมเพลตและรายการลิเทอรัลอาร์เรย์
import std.stdio;
import std.typecons;
import std.typetuple;
alias TypeTuple!(int, long) TL;
void method1(int a, string b, float c, char d) {
writeln("method 1 ",a,"\t",b,"\t",c,"\t",d);
}
void method2(TL tl) {
writeln(tl[0],"\t", tl[1] );
}
void main() {
auto arguments = TypeTuple!(5, "my string", 3.3,'r');
method1(arguments);
method2(5, 6L);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
method 1 5 my string 3.3 r
5 6
structure เป็นอีกประเภทข้อมูลที่ผู้ใช้กำหนดซึ่งมีอยู่ในการเขียนโปรแกรม D ซึ่งช่วยให้คุณสามารถรวมรายการข้อมูลประเภทต่างๆ
โครงสร้างใช้เพื่อแสดงบันทึก สมมติว่าคุณต้องการติดตามหนังสือของคุณในห้องสมุด คุณอาจต้องการติดตามคุณลักษณะต่อไปนี้เกี่ยวกับหนังสือแต่ละเล่ม -
- Title
- Author
- Subject
- รหัสหนังสือ
การกำหนดโครงสร้าง
ในการกำหนดโครงสร้างคุณต้องใช้ไฟล์ structคำให้การ. คำสั่ง struct กำหนดชนิดข้อมูลใหม่โดยมีสมาชิกมากกว่าหนึ่งคนสำหรับโปรแกรมของคุณ รูปแบบของคำสั่ง struct คือ -
struct [structure tag] {
member definition;
member definition;
...
member definition;
} [one or more structure variables];
structure tagเป็นทางเลือกและนิยามสมาชิกแต่ละตัวเป็นนิยามตัวแปรปกติเช่น int i; หรือลอยฉ; หรือนิยามตัวแปรอื่น ๆ ที่ถูกต้อง ในตอนท้ายของนิยามของโครงสร้างก่อนอัฒภาคคุณสามารถระบุตัวแปรโครงสร้างอย่างน้อยหนึ่งตัวแปรซึ่งเป็นทางเลือก นี่คือวิธีที่คุณจะประกาศโครงสร้างหนังสือ -
struct Books {
char [] title;
char [] author;
char [] subject;
int book_id;
};
การเข้าถึงสมาชิกโครงสร้าง
ในการเข้าถึงสมาชิกของโครงสร้างใด ๆ คุณใช้ไฟล์ member access operator (.). ตัวดำเนินการเข้าถึงสมาชิกถูกเข้ารหัสเป็นช่วงเวลาระหว่างชื่อตัวแปรโครงสร้างและสมาชิกโครงสร้างที่เราต้องการเข้าถึง คุณจะใช้structคำหลักในการกำหนดตัวแปรประเภทโครงสร้าง ตัวอย่างต่อไปนี้อธิบายการใช้โครงสร้าง -
import std.stdio;
struct Books {
char [] title;
char [] author;
char [] subject;
int book_id;
};
void main( ) {
Books Book1; /* Declare Book1 of type Book */
Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "D Programming".dup;
Book1.author = "Raj".dup;
Book1.subject = "D Programming Tutorial".dup;
Book1.book_id = 6495407;
/* book 2 specification */
Book2.title = "D Programming".dup;
Book2.author = "Raj".dup;
Book2.subject = "D Programming Tutorial".dup;
Book2.book_id = 6495700;
/* print Book1 info */
writeln( "Book 1 title : ", Book1.title);
writeln( "Book 1 author : ", Book1.author);
writeln( "Book 1 subject : ", Book1.subject);
writeln( "Book 1 book_id : ", Book1.book_id);
/* print Book2 info */
writeln( "Book 2 title : ", Book2.title);
writeln( "Book 2 author : ", Book2.author);
writeln( "Book 2 subject : ", Book2.subject);
writeln( "Book 2 book_id : ", Book2.book_id);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Book 1 title : D Programming
Book 1 author : Raj
Book 1 subject : D Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : D Programming
Book 2 author : Raj
Book 2 subject : D Programming Tutorial
Book 2 book_id : 6495700
โครงสร้างเป็นอาร์กิวเมนต์ของฟังก์ชัน
คุณสามารถส่งผ่านโครงสร้างเป็นอาร์กิวเมนต์ของฟังก์ชันในลักษณะที่คล้ายกันมากเมื่อคุณส่งผ่านตัวแปรหรือตัวชี้อื่น ๆ คุณจะเข้าถึงตัวแปรโครงสร้างในลักษณะเดียวกับที่คุณเข้าถึงในตัวอย่างข้างต้น -
import std.stdio;
struct Books {
char [] title;
char [] author;
char [] subject;
int book_id;
};
void main( ) {
Books Book1; /* Declare Book1 of type Book */
Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "D Programming".dup;
Book1.author = "Raj".dup;
Book1.subject = "D Programming Tutorial".dup;
Book1.book_id = 6495407;
/* book 2 specification */
Book2.title = "D Programming".dup;
Book2.author = "Raj".dup;
Book2.subject = "D Programming Tutorial".dup;
Book2.book_id = 6495700;
/* print Book1 info */
printBook( Book1 );
/* Print Book2 info */
printBook( Book2 );
}
void printBook( Books book ) {
writeln( "Book title : ", book.title);
writeln( "Book author : ", book.author);
writeln( "Book subject : ", book.subject);
writeln( "Book book_id : ", book.book_id);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495700
การเริ่มต้นโครงสร้าง
โครงสร้างสามารถเริ่มต้นได้ในสองรูปแบบหนึ่งโดยใช้ construtor และอื่น ๆ โดยใช้รูปแบบ {} ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
struct Books {
char [] title;
char [] subject = "Empty".dup;
int book_id = -1;
char [] author = "Raj".dup;
};
void main( ) {
Books Book1 = Books("D Programming".dup, "D Programming Tutorial".dup, 6495407 );
printBook( Book1 );
Books Book2 = Books("D Programming".dup,
"D Programming Tutorial".dup, 6495407,"Raj".dup );
printBook( Book2 );
Books Book3 = {title:"Obj C programming".dup, book_id : 1001};
printBook( Book3 );
}
void printBook( Books book ) {
writeln( "Book title : ", book.title);
writeln( "Book author : ", book.author);
writeln( "Book subject : ", book.subject);
writeln( "Book book_id : ", book.book_id);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 6495407
Book title : Obj C programming
Book author : Raj
Book subject : Empty
Book book_id : 1001
สมาชิกแบบคงที่
ตัวแปรคงเริ่มต้นเพียงครั้งเดียว ตัวอย่างเช่นหากต้องการมีรหัสเฉพาะสำหรับหนังสือเราสามารถทำให้ book_id เป็นแบบคงที่และเพิ่มรหัสหนังสือได้ ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
struct Books {
char [] title;
char [] subject = "Empty".dup;
int book_id;
char [] author = "Raj".dup;
static int id = 1000;
};
void main( ) {
Books Book1 = Books("D Programming".dup, "D Programming Tutorial".dup,++Books.id );
printBook( Book1 );
Books Book2 = Books("D Programming".dup, "D Programming Tutorial".dup,++Books.id);
printBook( Book2 );
Books Book3 = {title:"Obj C programming".dup, book_id:++Books.id};
printBook( Book3 );
}
void printBook( Books book ) {
writeln( "Book title : ", book.title);
writeln( "Book author : ", book.author);
writeln( "Book subject : ", book.subject);
writeln( "Book book_id : ", book.book_id);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 1001
Book title : D Programming
Book author : Raj
Book subject : D Programming Tutorial
Book book_id : 1002
Book title : Obj C programming
Book author : Raj
Book subject : Empty
Book book_id : 1003
ก unionเป็นชนิดข้อมูลพิเศษที่มีอยู่ใน D ที่ช่วยให้คุณสามารถจัดเก็บข้อมูลประเภทต่างๆในตำแหน่งหน่วยความจำเดียวกัน คุณสามารถกำหนดสหภาพที่มีสมาชิกหลายคน แต่มีสมาชิกเพียงคนเดียวเท่านั้นที่สามารถมีค่าในช่วงเวลาใดก็ได้ สหภาพแรงงานเป็นวิธีที่มีประสิทธิภาพในการใช้ตำแหน่งหน่วยความจำเดียวกันเพื่อวัตถุประสงค์หลายประการ
การกำหนดสหภาพใน D.
ในการกำหนดสหภาพคุณต้องใช้คำสั่งสหภาพในลักษณะเดียวกันกับที่คุณทำในขณะที่กำหนดโครงสร้าง คำสั่งสหภาพกำหนดชนิดข้อมูลใหม่โดยมีสมาชิกมากกว่าหนึ่งคนสำหรับโปรแกรมของคุณ รูปแบบของแถลงการณ์สหภาพมีดังนี้ -
union [union tag] {
member definition;
member definition;
...
member definition;
} [one or more union variables];
union tagเป็นทางเลือกและนิยามสมาชิกแต่ละตัวเป็นนิยามตัวแปรปกติเช่น int i; หรือลอยฉ; หรือนิยามตัวแปรอื่น ๆ ที่ถูกต้อง ในตอนท้ายของนิยามของสหภาพก่อนเครื่องหมายอัฒภาคสุดท้ายคุณสามารถระบุตัวแปรยูเนี่ยนได้ตั้งแต่หนึ่งตัวขึ้นไป แต่เป็นทางเลือก นี่คือวิธีที่คุณจะกำหนดประเภทสหภาพที่ชื่อ Data ซึ่งมีสมาชิกสามคนi, fและ str -
union Data {
int i;
float f;
char str[20];
} data;
ตัวแปรของ Datatype สามารถจัดเก็บจำนวนเต็มตัวเลขทศนิยมหรือสตริงของอักขระ ซึ่งหมายความว่าตัวแปรเดียว (ตำแหน่งหน่วยความจำเดียวกัน) สามารถใช้เพื่อจัดเก็บข้อมูลหลายประเภท คุณสามารถใช้ชนิดข้อมูลในตัวหรือที่ผู้ใช้กำหนดภายในสหภาพตามความต้องการของคุณ
หน่วยความจำที่สหภาพแรงงานครอบครองจะมีขนาดใหญ่พอที่จะรองรับสมาชิกที่ใหญ่ที่สุดของสหภาพได้ ตัวอย่างเช่นในตัวอย่างข้างต้นชนิดข้อมูลจะใช้พื้นที่หน่วยความจำ 20 ไบต์เนื่องจากเป็นพื้นที่สูงสุดที่สตริงอักขระสามารถครอบครองได้ ตัวอย่างต่อไปนี้แสดงขนาดหน่วยความจำทั้งหมดที่ครอบครองโดยสหภาพข้างต้น -
import std.stdio;
union Data {
int i;
float f;
char str[20];
};
int main( ) {
Data data;
writeln( "Memory size occupied by data : ", data.sizeof);
return 0;
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Memory size occupied by data : 20
การเข้าถึงสมาชิกสหภาพ
ในการเข้าถึงสมาชิกของสหภาพใด ๆ เราใช้ไฟล์ member access operator (.). ตัวดำเนินการเข้าถึงสมาชิกถูกเข้ารหัสเป็นช่วงเวลาระหว่างชื่อตัวแปรสหภาพและสมาชิกสหภาพแรงงานที่เราต้องการเข้าถึง คุณจะใช้คีย์เวิร์ดยูเนี่ยนเพื่อกำหนดตัวแปรประเภทยูเนี่ยน
ตัวอย่าง
ตัวอย่างต่อไปนี้อธิบายการใช้สหภาพ -
import std.stdio;
union Data {
int i;
float f;
char str[13];
};
void main( ) {
Data data;
data.i = 10;
data.f = 220.5;
data.str = "D Programming".dup;
writeln( "size of : ", data.sizeof);
writeln( "data.i : ", data.i);
writeln( "data.f : ", data.f);
writeln( "data.str : ", data.str);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
size of : 16
data.i : 1917853764
data.f : 4.12236e+30
data.str : D Programming
ที่นี่คุณจะเห็นค่าของ i และ f สมาชิกของสหภาพได้รับความเสียหายเนื่องจากค่าสุดท้ายที่กำหนดให้กับตัวแปรได้ครอบครองตำแหน่งหน่วยความจำและนี่คือเหตุผลที่ค่าของ str สมาชิกได้รับการพิมพ์เป็นอย่างดี
ตอนนี้ให้เราดูตัวอย่างเดียวกันอีกครั้งโดยที่เราจะใช้ตัวแปรทีละตัวซึ่งเป็นจุดประสงค์หลักของการมีสหภาพ -
ตัวอย่างที่แก้ไข
import std.stdio;
union Data {
int i;
float f;
char str[13];
};
void main( ) {
Data data;
writeln( "size of : ", data.sizeof);
data.i = 10;
writeln( "data.i : ", data.i);
data.f = 220.5;
writeln( "data.f : ", data.f);
data.str = "D Programming".dup;
writeln( "data.str : ", data.str);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
size of : 16
data.i : 10
data.f : 220.5
data.str : D Programming
ที่นี่สมาชิกทุกคนได้รับการพิมพ์เป็นอย่างดีเนื่องจากมีการใช้สมาชิกครั้งละหนึ่งคน
ช่วงเป็นนามธรรมของการเข้าถึงองค์ประกอบ สิ่งที่เป็นนามธรรมนี้ทำให้สามารถใช้อัลกอริทึมจำนวนมากในคอนเทนเนอร์หลายประเภทได้ ช่วงจะเน้นถึงวิธีการเข้าถึงองค์ประกอบคอนเทนเนอร์ซึ่งต่างจากวิธีการใช้งานคอนเทนเนอร์ 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"]
นามแฝงเนื่องจากชื่ออ้างถึงให้ชื่ออื่นสำหรับชื่อที่มีอยู่ ไวยากรณ์ของนามแฝงแสดงอยู่ด้านล่าง
alias new_name = existing_name;
ต่อไปนี้เป็นไวยากรณ์ที่เก่ากว่าในกรณีที่คุณอ้างอิงตัวอย่างรูปแบบที่เก่ากว่า ไม่ควรใช้อย่างยิ่งในการใช้สิ่งนี้
alias existing_name new_name;
นอกจากนี้ยังมีไวยากรณ์อื่นที่ใช้กับนิพจน์และมีการระบุไว้ด้านล่างซึ่งเราสามารถใช้ชื่อนามแฝงแทนนิพจน์ได้โดยตรง
alias expression alias_name ;
ดังที่คุณทราบแล้ว typedef จะเพิ่มความสามารถในการสร้างประเภทใหม่ นามแฝงสามารถทำงานของ typedef และอื่น ๆ ได้อีกมากมาย ตัวอย่างง่ายๆสำหรับการใช้นามแฝงแสดงอยู่ด้านล่างซึ่งใช้ส่วนหัว std.conv ซึ่งให้ความสามารถในการแปลงประเภท
import std.stdio;
import std.conv:to;
alias to!(string) toString;
void main() {
int a = 10;
string s = "Test"~toString(a);
writeln(s);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Test10
ในตัวอย่างข้างต้นแทนที่จะใช้ to! string (a) เรากำหนดให้เป็นนามแฝง name toString ทำให้สะดวกและเข้าใจง่ายขึ้น
นามแฝงสำหรับ Tuple
ให้เราดูตัวอย่างอื่นที่เราสามารถตั้งชื่อนามแฝงสำหรับทูเพิล
import std.stdio;
import std.typetuple;
alias TypeTuple!(int, long) TL;
void method1(TL tl) {
writeln(tl[0],"\t", tl[1] );
}
void main() {
method1(5, 6L);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
5 6
ในตัวอย่างข้างต้นประเภททูเปิลถูกกำหนดให้กับตัวแปรนามแฝงและทำให้นิยามวิธีการและการเข้าถึงตัวแปรง่ายขึ้น การเข้าถึงแบบนี้จะมีประโยชน์มากยิ่งขึ้นเมื่อเราพยายามนำสิ่งเหล่านี้มาใช้ซ้ำ
นามแฝงสำหรับประเภทข้อมูล
หลายครั้งเราอาจกำหนดประเภทข้อมูลทั่วไปที่ต้องใช้ในแอปพลิเคชัน เมื่อโปรแกรมเมอร์หลายคนเขียนโค้ดแอปพลิเคชันอาจเป็นกรณีที่บุคคลหนึ่งใช้ int อีกคู่หนึ่งและอื่น ๆ เพื่อหลีกเลี่ยงความขัดแย้งดังกล่าวเรามักใช้ชนิดสำหรับประเภทข้อมูล ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
alias int myAppNumber;
alias string myAppString;
void main() {
myAppNumber i = 10;
myAppString s = "TestString";
writeln(i,s);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
10TestString
นามแฝงสำหรับตัวแปรคลาส
มักจะมีข้อกำหนดที่เราจำเป็นต้องเข้าถึงตัวแปรสมาชิกของซูเปอร์คลาสในคลาสย่อยซึ่งสามารถทำได้โดยใช้นามแฝงซึ่งอาจอยู่ภายใต้ชื่ออื่น
ในกรณีที่คุณยังใหม่กับแนวคิดเรื่องคลาสและการสืบทอดโปรดดูบทช่วยสอนเกี่ยวกับคลาสและการสืบทอดก่อนที่จะเริ่มในส่วนนี้
ตัวอย่าง
ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
import std.stdio;
class Shape {
int area;
}
class Square : Shape {
string name() const @property {
return "Square";
}
alias Shape.area squareArea;
}
void main() {
auto square = new Square;
square.squareArea = 42;
writeln(square.name);
writeln(square.squareArea);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Square
42
นามแฝงนี้
นามแฝงนี้ให้ความสามารถในการแปลงประเภทอัตโนมัติของประเภทที่ผู้ใช้กำหนดเอง ไวยากรณ์แสดงอยู่ด้านล่างซึ่งมีการเขียนนามแฝงของคำหลักไว้ที่ด้านใดด้านหนึ่งของตัวแปรสมาชิกหรือฟังก์ชันสมาชิก
alias member_variable_or_member_function this;
ตัวอย่าง
ตัวอย่างแสดงไว้ด้านล่างเพื่อแสดงพลังของนามแฝงนี้
import std.stdio;
struct Rectangle {
long length;
long breadth;
double value() const @property {
return cast(double) length * breadth;
}
alias value this;
}
double volume(double rectangle, double height) {
return rectangle * height;
}
void main() {
auto rectangle = Rectangle(2, 3);
writeln(volume(rectangle, 5));
}
ในตัวอย่างข้างต้นคุณจะเห็นว่ารูปสี่เหลี่ยมผืนผ้าโครงสร้างถูกแปลงเป็นค่าสองเท่าด้วยความช่วยเหลือของนามแฝงวิธีนี้
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
30
Mixins เป็นโครงสร้างที่อนุญาตให้ผสมรหัสที่สร้างขึ้นลงในซอร์สโค้ด Mixins สามารถเป็นประเภทต่อไปนี้ -
- สตริงมิกซ์
- เทมเพลตมิกซ์อิน
- ช่องว่างชื่อมิกซ์
สตริงมิกซ์
D มีความสามารถในการแทรกโค้ดเป็นสตริงตราบเท่าที่สตริงนั้นเป็นที่รู้จักในเวลาคอมไพล์ ไวยากรณ์ของสตริงผสมแสดงไว้ด้านล่าง -
mixin (compile_time_generated_string)
ตัวอย่าง
ตัวอย่างง่ายๆสำหรับสตริงมิกซ์แสดงอยู่ด้านล่าง
import std.stdio;
void main() {
mixin(`writeln("Hello World!");`);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Hello World!
นี่คืออีกตัวอย่างหนึ่งที่เราสามารถส่งสตริงในเวลาคอมไพล์เพื่อให้มิกซ์อินสามารถใช้ฟังก์ชันเพื่อใช้โค้ดซ้ำได้ ดังแสดงด้านล่าง
import std.stdio;
string print(string s) {
return `writeln("` ~ s ~ `");`;
}
void main() {
mixin (print("str1"));
mixin (print("str2"));
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
str1
str2
เทมเพลตมิกซ์อิน
เทมเพลต D กำหนดรูปแบบโค้ดทั่วไปเพื่อให้คอมไพลเลอร์สร้างอินสแตนซ์จริงจากรูปแบบนั้น เทมเพลตสามารถสร้างฟังก์ชันโครงสร้างสหภาพคลาสอินเทอร์เฟซและรหัส D ทางกฎหมายอื่น ๆ ไวยากรณ์ของเทมเพลตมิกซ์อินดังที่แสดงด้านล่าง
mixin a_template!(template_parameters)
ตัวอย่างง่ายๆสำหรับการผสมสตริงแสดงอยู่ด้านล่างซึ่งเราสร้างเทมเพลตด้วยแผนกคลาสและมิกซ์อินอินสแตนซ์เทมเพลตและด้วยเหตุนี้จึงทำให้ฟังก์ชัน setName และ printNames พร้อมใช้งานสำหรับวิทยาลัยโครงสร้าง
ตัวอย่าง
import std.stdio;
template Department(T, size_t count) {
T[count] names;
void setName(size_t index, T name) {
names[index] = name;
}
void printNames() {
writeln("The names");
foreach (i, name; names) {
writeln(i," : ", name);
}
}
}
struct College {
mixin Department!(string, 2);
}
void main() {
auto college = College();
college.setName(0, "name1");
college.setName(1, "name2");
college.printNames();
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
The names
0 : name1
1 : name2
Mixin Name Spaces
ช่องว่างชื่อ Mixin ใช้เพื่อหลีกเลี่ยงความคลุมเครือในเทมเพลตมิกซ์อิน ตัวอย่างเช่นอาจมีตัวแปร 2 ตัวตัวหนึ่งกำหนดไว้อย่างชัดเจนใน main และอีกตัวผสมกันเมื่อชื่อแบบผสมเหมือนกับชื่อที่อยู่ในขอบเขตโดยรอบชื่อที่อยู่ในขอบเขตรอบข้างจะได้รับ ใช้แล้ว ตัวอย่างนี้แสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
template Person() {
string name;
void print() {
writeln(name);
}
}
void main() {
string name;
mixin Person a;
name = "name 1";
writeln(name);
a.name = "name 2";
print();
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
name 1
name 2
โมดูลเป็นส่วนประกอบพื้นฐานของ D. พวกเขาตั้งอยู่บนแนวคิดง่ายๆ ไฟล์ต้นฉบับทุกไฟล์เป็นโมดูล ดังนั้นไฟล์เดียวที่เราเขียนโปรแกรมจึงเป็นโมดูลแต่ละโมดูล ตามค่าเริ่มต้นชื่อของโมดูลจะเหมือนกับชื่อไฟล์ที่ไม่มีนามสกุล. d
เมื่อระบุอย่างชัดเจนชื่อของโมดูลจะถูกกำหนดโดยคีย์เวิร์ดของโมดูลซึ่งจะต้องปรากฏเป็นบรรทัดแรกที่ไม่ใช่ความคิดเห็นในไฟล์ต้นฉบับ ตัวอย่างเช่นสมมติว่าชื่อของไฟล์ต้นฉบับคือ "พนักงาน d" จากนั้นชื่อของโมดูลระบุโดยโมดูลคำหลักตามด้วยการทำงานของพนักงาน ดังที่แสดงด้านล่าง
module employee;
class Employee {
// Class definition goes here.
}
สายโมดูลเป็นทางเลือก เมื่อไม่ได้ระบุจะเหมือนกับชื่อไฟล์ที่ไม่มีนามสกุล. d
ชื่อไฟล์และโมดูล
D สนับสนุน Unicode ในซอร์สโค้ดและชื่อโมดูล อย่างไรก็ตามการสนับสนุน Unicode ของระบบไฟล์แตกต่างกันไป ตัวอย่างเช่นแม้ว่าระบบไฟล์ Linux ส่วนใหญ่จะรองรับ Unicode แต่ชื่อไฟล์ในระบบไฟล์ Windows อาจไม่แยกความแตกต่างระหว่างอักษรตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ นอกจากนี้ระบบไฟล์ส่วนใหญ่ จำกัด อักขระที่สามารถใช้ในชื่อไฟล์และไดเร็กทอรี ด้วยเหตุผลด้านความสามารถในการพกพาฉันขอแนะนำให้คุณใช้อักษร ASCII ตัวพิมพ์เล็กในชื่อไฟล์เท่านั้น ตัวอย่างเช่น "staff.d" จะเป็นชื่อไฟล์ที่เหมาะสมสำหรับคลาสชื่อพนักงาน
ดังนั้นชื่อของโมดูลจะประกอบด้วยตัวอักษร ASCII เช่นกัน -
module employee; // Module name consisting of ASCII letters
class eëmployëë { }
D แพ็คเกจ
การรวมกันของโมดูลที่เกี่ยวข้องเรียกว่าแพ็คเกจ แพ็กเกจ D เป็นแนวคิดง่ายๆเช่นกัน: ซอร์สไฟล์ที่อยู่ในไดเร็กทอรีเดียวกันจะถือว่าอยู่ในแพ็กเกจเดียวกัน ชื่อของไดเร็กทอรีกลายเป็นชื่อของแพ็กเกจซึ่งต้องระบุเป็นส่วนแรกของชื่อโมดูลด้วย
ตัวอย่างเช่นถ้า "staff.d" และ "office.d" อยู่ในไดเร็กทอรี "company" การระบุชื่อไดเร็กทอรีพร้อมกับชื่อโมดูลจะทำให้เป็นส่วนหนึ่งของแพ็กเกจเดียวกัน -
module company.employee;
class Employee { }
ในทำนองเดียวกันสำหรับโมดูลสำนักงาน -
module company.office;
class Office { }
เนื่องจากชื่อแพ็กเกจสอดคล้องกับชื่อไดเร็กทอรีชื่อแพ็กเกจของโมดูลที่ลึกกว่าระดับไดเร็กทอรีหนึ่งต้องแสดงถึงลำดับชั้นดังกล่าว ตัวอย่างเช่นหากไดเร็กทอรี "company" มีไดเร็กทอรี "branch" ชื่อของโมดูลภายในไดเร็กทอรีนั้นจะรวมสาขาด้วย
module company.branch.employee;
การใช้โมดูลในโปรแกรม
คีย์เวิร์ดนำเข้าซึ่งเราใช้ในเกือบทุกโปรแกรมเพื่อแนะนำโมดูลให้กับโมดูลปัจจุบัน -
import std.stdio;
ชื่อโมดูลอาจมีชื่อแพ็กเกจด้วย ตัวอย่างเช่นมาตรฐาน ส่วนด้านบนระบุว่า stdio เป็นโมดูลที่เป็นส่วนหนึ่งของแพ็คเกจ std
ตำแหน่งของโมดูล
คอมไพเลอร์ค้นหาไฟล์โมดูลโดยการแปลงชื่อแพ็กเกจและโมดูลเป็นชื่อไดเร็กทอรีและไฟล์โดยตรง
ตัวอย่างเช่นพนักงานและสำนักงานทั้งสองโมดูลจะอยู่ในชื่อ "บริษัท / พนักงาน d" และ "animal / office.d" ตามลำดับ (หรือ "company \ workers.d" และ "company \ office.d" ขึ้นอยู่กับ ระบบไฟล์) สำหรับ บริษัท พนักงานและ บริษัท สำนักงาน
ชื่อโมดูลแบบยาวและแบบสั้น
ชื่อที่ใช้ในโปรแกรมอาจสะกดด้วยชื่อโมดูลและชื่อแพ็กเกจดังที่แสดงด้านล่าง
import company.employee;
auto employee0 = Employee();
auto employee1 = company.employee.Employee();
โดยปกติไม่จำเป็นต้องใช้ชื่อยาว แต่บางครั้งก็มีความขัดแย้งของชื่อ ตัวอย่างเช่นเมื่ออ้างถึงชื่อที่ปรากฏในโมดูลมากกว่าหนึ่งโมดูลคอมไพลเลอร์ไม่สามารถตัดสินใจได้ว่าหมายถึงอันใด โปรแกรมต่อไปนี้กำลังสะกดชื่อยาว ๆ เพื่อแยกความแตกต่างระหว่างโครงสร้างพนักงานสองส่วนที่แยกจากกันซึ่งกำหนดไว้ในโมดูลแยกกันสองโมดูล: บริษัทและวิทยาลัย .
โมดูลพนักงานแรกใน บริษัท โฟลเดอร์มีดังนี้
module company.employee;
import std.stdio;
class Employee {
public:
string str;
void print() {
writeln("Company Employee: ",str);
}
}
โมดูลพนักงานที่สองในโฟลเดอร์คอลเลจมีดังนี้
module college.employee;
import std.stdio;
class Employee {
public:
string str;
void print() {
writeln("College Employee: ",str);
}
}
โมดูลหลักใน hello.d ควรบันทึกไว้ในโฟลเดอร์ที่มีโฟลเดอร์ของวิทยาลัยและ บริษัท เป็นดังนี้.
import company.employee;
import college.employee;
import std.stdio;
void main() {
auto myemployee1 = new company.employee.Employee();
myemployee1.str = "emp1";
myemployee1.print();
auto myemployee2 = new college.employee.Employee();
myemployee2.str = "emp2";
myemployee2.print();
}
คีย์เวิร์ดนำเข้าไม่เพียงพอที่จะทำให้โมดูลกลายเป็นส่วนหนึ่งของโปรแกรม เพียงแค่ทำให้คุณสมบัติของโมดูลภายในโมดูลปัจจุบันพร้อมใช้งาน สิ่งนั้นจำเป็นสำหรับการคอมไพล์โค้ดเท่านั้น
สำหรับโปรแกรมข้างต้นที่จะสร้างขึ้นจะต้องระบุ "บริษัท / พนักงาน d" และ "วิทยาลัย / พนักงาน d" ในบรรทัดการรวบรวมด้วย
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
$ dmd hello.d company/employee.d college/employee.d -ofhello.amx
$ ./hello.amx
Company Employee: emp1
College Employee: emp2
เทมเพลตเป็นรากฐานของการเขียนโปรแกรมทั่วไปซึ่งเกี่ยวข้องกับการเขียนโค้ดในลักษณะที่ไม่ขึ้นกับประเภทใดประเภทหนึ่ง
เทมเพลตคือพิมพ์เขียวหรือสูตรสำหรับสร้างคลาสทั่วไปหรือฟังก์ชัน
เทมเพลตเป็นคุณลักษณะที่ช่วยให้สามารถอธิบายโค้ดเป็นรูปแบบเพื่อให้คอมไพลเลอร์สร้างโค้ดโปรแกรมโดยอัตโนมัติ บางส่วนของซอร์สโค้ดอาจเหลือให้คอมไพเลอร์กรอกจนกว่าส่วนนั้นจะถูกใช้ในโปรแกรมจริง คอมไพเลอร์เติมเต็มในส่วนที่ขาดหายไป
เทมเพลตฟังก์ชัน
การกำหนดฟังก์ชันเป็นเทมเพลตจะทำให้ประเภทหนึ่งหรือมากกว่านั้นใช้เป็นประเภทที่ไม่ได้ระบุไว้เพื่อให้คอมไพลเลอร์อนุมานได้ในภายหลัง ประเภทที่ถูกปล่อยทิ้งไว้ไม่ได้ระบุถูกกำหนดไว้ภายในรายการพารามิเตอร์เทมเพลตซึ่งอยู่ระหว่างชื่อของฟังก์ชันและรายการพารามิเตอร์ของฟังก์ชัน ด้วยเหตุนี้เทมเพลตฟังก์ชันจึงมีรายการพารามิเตอร์สองรายการ -
- รายการพารามิเตอร์เทมเพลต
- รายการพารามิเตอร์ฟังก์ชัน
import std.stdio;
void print(T)(T value) {
writefln("%s", value);
}
void main() {
print(42);
print(1.2);
print("test");
}
หากเรารวบรวมและเรียกใช้โค้ดด้านบนสิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -
42
1.2
test
เทมเพลตฟังก์ชันที่มีพารามิเตอร์หลายประเภท
สามารถมีพารามิเตอร์ได้หลายประเภท ซึ่งแสดงในตัวอย่างต่อไปนี้
import std.stdio;
void print(T1, T2)(T1 value1, T2 value2) {
writefln(" %s %s", value1, value2);
}
void main() {
print(42, "Test");
print(1.2, 33);
}
หากเรารวบรวมและเรียกใช้โค้ดด้านบนสิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -
42 Test
1.2 33
เทมเพลตคลาส
เช่นเดียวกับที่เราสามารถกำหนดเทมเพลตฟังก์ชันได้แล้วเรายังสามารถกำหนดเทมเพลตคลาสได้อีกด้วย ตัวอย่างต่อไปนี้กำหนดคลาส Stack และใช้เมธอดทั่วไปในการพุชและป๊อปองค์ประกอบจากสแต็ก
import std.stdio;
import std.string;
class Stack(T) {
private:
T[] elements;
public:
void push(T element) {
elements ~= element;
}
void pop() {
--elements.length;
}
T top() const @property {
return elements[$ - 1];
}
size_t length() const @property {
return elements.length;
}
}
void main() {
auto stack = new Stack!string;
stack.push("Test1");
stack.push("Test2");
writeln(stack.top);
writeln(stack.length);
stack.pop;
writeln(stack.top);
writeln(stack.length);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Test2
2
Test1
1
เรามักใช้ตัวแปรที่เปลี่ยนแปลงได้ แต่อาจมีหลายครั้งที่ไม่จำเป็น ตัวแปรที่ไม่เปลี่ยนรูปสามารถใช้ได้ในกรณีเช่นนี้ มีตัวอย่างบางส่วนด้านล่างซึ่งสามารถใช้ตัวแปรที่ไม่เปลี่ยนรูปได้
ในกรณีของค่าคงที่ทางคณิตศาสตร์เช่นpiที่ไม่เคยเปลี่ยนแปลง
ในกรณีของอาร์เรย์ที่เราต้องการคงค่าไว้และไม่ใช่ข้อกำหนดของการกลายพันธุ์
ความไม่เปลี่ยนรูปทำให้สามารถเข้าใจได้ว่าตัวแปรไม่เปลี่ยนรูปหรือเปลี่ยนแปลงได้หรือไม่โดยรับประกันว่าการดำเนินการบางอย่างจะไม่เปลี่ยนแปลงตัวแปรบางตัว นอกจากนี้ยังช่วยลดความเสี่ยงจากข้อผิดพลาดของโปรแกรมบางประเภท แนวคิดการไม่เปลี่ยนรูปของ D แสดงโดยคำหลัก const และไม่เปลี่ยนรูป แม้ว่าทั้งสองคำจะมีความหมายใกล้เคียงกัน แต่ความรับผิดชอบในโปรแกรมจะแตกต่างกันและบางครั้งก็เข้ากันไม่ได้
แนวคิดการไม่เปลี่ยนรูปของ D แสดงโดยคำหลัก const และไม่เปลี่ยนรูป แม้ว่าทั้งสองคำจะมีความหมายใกล้เคียงกัน แต่ความรับผิดชอบในโปรแกรมจะแตกต่างกันและบางครั้งก็เข้ากันไม่ได้
ประเภทของตัวแปรที่ไม่เปลี่ยนรูปใน D
ตัวแปรกำหนดมีสามประเภทที่ไม่มีวันกลายพันธุ์
- ค่าคงที่ของ enum
- ตัวแปรที่ไม่เปลี่ยนรูป
- ตัวแปร const
enumคงที่ใน D
ค่าคงที่ของ enum ทำให้สามารถเชื่อมโยงค่าคงที่กับชื่อที่มีความหมายได้ ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
enum Day{
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
void main() {
Day day;
day = Day.Sunday;
if (day == Day.Sunday) {
writeln("The day is Sunday");
}
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
The day is Sunday
ตัวแปรที่ไม่เปลี่ยนรูปใน D
ตัวแปรที่ไม่เปลี่ยนรูปสามารถกำหนดได้ในระหว่างการทำงานของโปรแกรม เพียงแค่สั่งคอมไพเลอร์ว่าหลังจากการเริ่มต้นแล้วมันจะไม่เปลี่ยนรูป ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
import std.random;
void main() {
int min = 1;
int max = 10;
immutable number = uniform(min, max + 1);
// cannot modify immutable expression number
// number = 34;
typeof(number) value = 100;
writeln(typeof(number).stringof, number);
writeln(typeof(value).stringof, value);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
immutable(int)4
immutable(int)100
คุณสามารถดูในตัวอย่างด้านบนว่าเป็นไปได้อย่างไรในการถ่ายโอนประเภทข้อมูลไปยังตัวแปรอื่นและใช้สตริงของขณะพิมพ์
ตัวแปรConstใน D
ไม่สามารถแก้ไขตัวแปร Const ที่คล้ายกับไม่เปลี่ยนรูปได้ ตัวแปรที่ไม่เปลี่ยนรูปสามารถส่งผ่านไปยังฟังก์ชันเป็นพารามิเตอร์ที่ไม่เปลี่ยนรูปได้ดังนั้นจึงขอแนะนำให้ใช้ไม่เปลี่ยนรูปบน const ตัวอย่างเดียวกันกับที่ใช้ก่อนหน้านี้ถูกแก้ไขสำหรับ const ดังที่แสดงด้านล่าง
ตัวอย่าง
import std.stdio;
import std.random;
void main() {
int min = 1;
int max = 10;
const number = uniform(min, max + 1);
// cannot modify const expression number|
// number = 34;
typeof(number) value = 100;
writeln(typeof(number).stringof, number);
writeln(typeof(value).stringof, value);
}
หากเรารวบรวมและเรียกใช้โค้ดด้านบนสิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -
const(int)7
const(int)100
พารามิเตอร์ที่ไม่เปลี่ยนรูปใน D
const จะลบข้อมูลเกี่ยวกับว่าตัวแปรดั้งเดิมนั้นเปลี่ยนแปลงได้หรือไม่เปลี่ยนรูปและด้วยเหตุนี้การใช้ไม่เปลี่ยนรูปจึงทำให้มันส่งผ่านฟังก์ชันอื่น ๆ โดยที่ประเภทดั้งเดิมยังคงอยู่ ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
void print(immutable int[] array) {
foreach (i, element; array) {
writefln("%s: %s", i, element);
}
}
void main() {
immutable int[] array = [ 1, 2 ];
print(array);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
0: 1
1: 2
ไฟล์จะแสดงโดยโครงสร้างไฟล์ของโมดูล std.stdio ไฟล์แสดงลำดับของไบต์ไม่สำคัญว่าจะเป็นไฟล์ข้อความหรือไฟล์ไบนารี
ภาษาการเขียนโปรแกรม D ช่วยให้สามารถเข้าถึงฟังก์ชันระดับสูงและระดับต่ำ (ระดับ OS) เพื่อจัดการไฟล์บนอุปกรณ์จัดเก็บข้อมูล
การเปิดไฟล์ใน D
สตรีมอินพุตและเอาต์พุตมาตรฐาน stdin และ stdout เปิดอยู่แล้วเมื่อโปรแกรมเริ่มทำงาน พวกเขาพร้อมที่จะใช้ ในทางกลับกันต้องเปิดไฟล์ก่อนโดยระบุชื่อไฟล์และสิทธิ์การเข้าถึงที่จำเป็น
File file = File(filepath, "mode");
ที่นี่ filename เป็นสตริงลิเทอรัลซึ่งคุณใช้ตั้งชื่อไฟล์และเข้าถึง mode สามารถมีค่าใดค่าหนึ่งต่อไปนี้ -
ซีเนียร์ | โหมดและคำอธิบาย |
---|---|
1 | r เปิดไฟล์ข้อความที่มีอยู่เพื่อจุดประสงค์ในการอ่าน |
2 | w เปิดไฟล์ข้อความสำหรับการเขียนหากไม่มีอยู่ไฟล์ใหม่จะถูกสร้างขึ้น ที่นี่โปรแกรมของคุณจะเริ่มเขียนเนื้อหาจากจุดเริ่มต้นของไฟล์ |
3 | a เปิดไฟล์ข้อความสำหรับการเขียนในโหมดต่อท้ายหากไม่มีอยู่ไฟล์ใหม่จะถูกสร้างขึ้น ที่นี่โปรแกรมของคุณจะเริ่มต่อท้ายเนื้อหาในเนื้อหาไฟล์ที่มีอยู่ |
4 | r+ เปิดไฟล์ข้อความสำหรับอ่านและเขียนทั้งสองอย่าง |
5 | w+ เปิดไฟล์ข้อความสำหรับอ่านและเขียนทั้งสองอย่าง ขั้นแรกให้ตัดทอนไฟล์ให้มีความยาวเป็นศูนย์หากมีอยู่ให้สร้างไฟล์หากไม่มีอยู่ |
6 | a+ เปิดไฟล์ข้อความสำหรับอ่านและเขียนทั้งสองอย่าง จะสร้างไฟล์หากไม่มีอยู่ การอ่านจะเริ่มต้นจากจุดเริ่มต้น แต่สามารถเขียนต่อท้ายได้เท่านั้น |
การปิดไฟล์ใน D
ในการปิดไฟล์ให้ใช้ฟังก์ชัน file.close () ที่ไฟล์มีการอ้างอิงไฟล์ ต้นแบบของฟังก์ชันนี้คือ -
file.close();
ไฟล์ใด ๆ ที่ถูกเปิดโดยโปรแกรมจะต้องปิดเมื่อโปรแกรมเสร็จสิ้นโดยใช้ไฟล์นั้น ในกรณีส่วนใหญ่ไม่จำเป็นต้องปิดไฟล์อย่างชัดเจน พวกเขาจะปิดโดยอัตโนมัติเมื่ออ็อบเจ็กต์ไฟล์ถูกยกเลิก
การเขียนไฟล์ใน D
file.writeln ใช้เพื่อเขียนไปยังไฟล์ที่เปิดอยู่
file.writeln("hello");
import std.stdio;
import std.file;
void main() {
File file = File("test.txt", "w");
file.writeln("hello");
file.close();
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานโค้ดจะสร้างไฟล์ใหม่ test.txt ในไดเร็กทอรีที่เริ่มต้นภายใต้ (ในไดเร็กทอรีการทำงานของโปรแกรม)
การอ่านไฟล์ใน D
วิธีการต่อไปนี้อ่านบรรทัดเดียวจากไฟล์ -
string s = file.readln();
ตัวอย่างการอ่านและเขียนที่สมบูรณ์แสดงอยู่ด้านล่าง
import std.stdio;
import std.file;
void main() {
File file = File("test.txt", "w");
file.writeln("hello");
file.close();
file = File("test.txt", "r");
string s = file.readln();
writeln(s);
file.close();
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
hello
นี่คืออีกตัวอย่างหนึ่งสำหรับการอ่านไฟล์จนจบไฟล์
import std.stdio;
import std.string;
void main() {
File file = File("test.txt", "w");
file.writeln("hello");
file.writeln("world");
file.close();
file = File("test.txt", "r");
while (!file.eof()) {
string line = chomp(file.readln());
writeln("line -", line);
}
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
line -hello
line -world
line -
คุณสามารถดูในตัวอย่างข้างต้นว่าบรรทัดที่สามว่างเปล่าเนื่องจากการเขียนจะนำไปสู่บรรทัดถัดไปเมื่อดำเนินการแล้ว
การทำงานพร้อมกันทำให้โปรแกรมทำงานบนเธรดหลายชุดพร้อมกัน ตัวอย่างของโปรแกรมที่ทำงานพร้อมกันคือเว็บเซิร์ฟเวอร์ที่ตอบสนองไคลเอนต์จำนวนมากในเวลาเดียวกัน การใช้งานพร้อมกันนั้นทำได้ง่ายด้วยการส่งผ่านข้อความ แต่เขียนได้ยากมากหากใช้ข้อมูลร่วมกัน
ข้อมูลที่ส่งผ่านระหว่างเธรดเรียกว่าข้อความ ข้อความอาจประกอบด้วยประเภทใดก็ได้และตัวแปรจำนวนเท่าใดก็ได้ ทุกเธรดมีรหัสซึ่งใช้สำหรับระบุผู้รับข้อความ เธรดใดที่เริ่มเธรดอื่นเรียกว่าเจ้าของกระทู้ใหม่
การเริ่มต้นเธรดใน D
ฟังก์ชัน spawn () รับตัวชี้เป็นพารามิเตอร์และเริ่มเธรดใหม่จากฟังก์ชันนั้น การดำเนินการใด ๆ ที่ดำเนินการโดยฟังก์ชันนั้นรวมถึงฟังก์ชันอื่น ๆ ที่อาจเรียกใช้จะถูกดำเนินการบนเธรดใหม่ เจ้าของและผู้ปฏิบัติงานทั้งสองเริ่มดำเนินการแยกกันราวกับว่าเป็นโปรแกรมอิสระ
ตัวอย่าง
import std.stdio;
import std.stdio;
import std.concurrency;
import core.thread;
void worker(int a) {
foreach (i; 0 .. 4) {
Thread.sleep(1);
writeln("Worker Thread ",a + i);
}
}
void main() {
foreach (i; 1 .. 4) {
Thread.sleep(2);
writeln("Main Thread ",i);
spawn(≈worker, i * 5);
}
writeln("main is done.");
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
Main Thread 1
Worker Thread 5
Main Thread 2
Worker Thread 6
Worker Thread 10
Main Thread 3
main is done.
Worker Thread 7
Worker Thread 11
Worker Thread 15
Worker Thread 8
Worker Thread 12
Worker Thread 16
Worker Thread 13
Worker Thread 17
Worker Thread 18
ตัวระบุเธรดใน D
thisTidตัวแปรทั่วโลกที่มีอยู่ในระดับโมดูลอยู่เสมอ ID ของด้ายปัจจุบัน นอกจากนี้คุณยังสามารถรับ threadId ได้เมื่อเรียก spawn ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
import std.concurrency;
void printTid(string tag) {
writefln("%s: %s, address: %s", tag, thisTid, &thisTid);
}
void worker() {
printTid("Worker");
}
void main() {
Tid myWorker = spawn(&worker);
printTid("Owner ");
writeln(myWorker);
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
Owner : Tid(std.concurrency.MessageBox), address: 10C71A59C
Worker: Tid(std.concurrency.MessageBox), address: 10C71A59C
Tid(std.concurrency.MessageBox)
ข้อความผ่านใน D
ฟังก์ชั่น send () ส่งข้อความและฟังก์ชัน earnOnly () รอข้อความประเภทใดประเภทหนึ่ง มีฟังก์ชั่นอื่น ๆ ที่ชื่อ prioritySend (), รับ () และรับเวลาเอาท์ () ซึ่งจะอธิบายในภายหลัง
เจ้าของในโปรแกรมต่อไปนี้ส่งข้อความประเภท int ให้ผู้ปฏิบัติงานและรอข้อความจากผู้ปฏิบัติงานประเภท double เธรดยังคงส่งข้อความกลับไปกลับมาจนกว่าเจ้าของจะส่ง int เชิงลบ ตัวอย่างแสดงด้านล่าง
ตัวอย่าง
import std.stdio;
import std.concurrency;
import core.thread;
import std.conv;
void workerFunc(Tid tid) {
int value = 0;
while (value >= 0) {
value = receiveOnly!int();
auto result = to!double(value) * 5; tid.send(result);
}
}
void main() {
Tid worker = spawn(&workerFunc,thisTid);
foreach (value; 5 .. 10) {
worker.send(value);
auto result = receiveOnly!double();
writefln("sent: %s, received: %s", value, result);
}
worker.send(-1);
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
sent: 5, received: 25
sent: 6, received: 30
sent: 7, received: 35
sent: 8, received: 40
sent: 9, received: 45
ข้อความผ่านด้วยการรอใน D
ตัวอย่างง่ายๆที่มีข้อความผ่านด้วยการรอแสดงอยู่ด้านล่าง
import std.stdio;
import std.concurrency;
import core.thread;
import std.conv;
void workerFunc(Tid tid) {
Thread.sleep(dur!("msecs")( 500 ),);
tid.send("hello");
}
void main() {
spawn(&workerFunc,thisTid);
writeln("Waiting for a message");
bool received = false;
while (!received) {
received = receiveTimeout(dur!("msecs")( 100 ), (string message) {
writeln("received: ", message);
});
if (!received) {
writeln("... no message yet");
}
}
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
Waiting for a message
... no message yet
... no message yet
... no message yet
... no message yet
received: hello
ข้อยกเว้นคือปัญหาที่เกิดขึ้นระหว่างการทำงานของโปรแกรม ข้อยกเว้น AD คือการตอบสนองต่อสถานการณ์พิเศษที่เกิดขึ้นในขณะที่โปรแกรมกำลังทำงานเช่นความพยายามที่จะหารด้วยศูนย์
ข้อยกเว้นเป็นวิธีการถ่ายโอนการควบคุมจากส่วนหนึ่งของโปรแกรมไปยังอีกส่วนหนึ่ง การจัดการข้อยกเว้น D ถูกสร้างขึ้นจากคำหลักสามคำtry, catchและ throw.
throw- โปรแกรมแสดงข้อยกเว้นเมื่อปัญหาปรากฏขึ้น ทำได้โดยใช้ไฟล์throw คำสำคัญ.
catch- โปรแกรมตรวจจับข้อยกเว้นด้วยตัวจัดการข้อยกเว้นที่ตำแหน่งในโปรแกรมที่คุณต้องการจัดการปัญหา catch คีย์เวิร์ดบ่งบอกถึงการจับข้อยกเว้น
try - ก tryบล็อกระบุบล็อกของรหัสที่เปิดใช้งานข้อยกเว้นเฉพาะ ตามด้วยบล็อกจับอย่างน้อยหนึ่งบล็อก
สมมติว่าบล็อกจะเพิ่มข้อยกเว้นเมธอดจะจับข้อยกเว้นโดยใช้การรวมกันของ try และ catchคำหลัก บล็อก try / catch ถูกวางไว้รอบ ๆ โค้ดที่อาจสร้างข้อยกเว้น โค้ดภายในบล็อก try / catch เรียกว่าโค้ดที่มีการป้องกันและไวยากรณ์สำหรับการใช้ try / catch มีลักษณะดังนี้ -
try {
// protected code
}
catch( ExceptionName e1 ) {
// catch block
}
catch( ExceptionName e2 ) {
// catch block
}
catch( ExceptionName eN ) {
// catch block
}
คุณสามารถลงรายการได้หลายรายการ catch เพื่อตรวจจับข้อยกเว้นประเภทต่างๆในกรณีที่ไฟล์ try บล็อกมีข้อยกเว้นมากกว่าหนึ่งข้อในสถานการณ์ต่างๆ
การโยนข้อยกเว้นใน D
สามารถโยนข้อยกเว้นที่ใดก็ได้ภายในบล็อกรหัสโดยใช้ throwงบ ตัวถูกดำเนินการของคำสั่ง throw จะกำหนดประเภทของข้อยกเว้นและอาจเป็นนิพจน์ใดก็ได้และประเภทของผลลัพธ์ของนิพจน์จะกำหนดประเภทของข้อยกเว้นที่ถูกโยน
ตัวอย่างต่อไปนี้แสดงข้อยกเว้นเมื่อหารด้วยเงื่อนไขเป็นศูนย์เกิดขึ้น -
ตัวอย่าง
double division(int a, int b) {
if( b == 0 ) {
throw new Exception("Division by zero condition!");
}
return (a/b);
}
การจับข้อยกเว้นใน D
catch บล็อกตาม tryบล็อกจับข้อยกเว้นใด ๆ คุณสามารถระบุประเภทของข้อยกเว้นที่คุณต้องการตรวจจับและสิ่งนี้ถูกกำหนดโดยการประกาศข้อยกเว้นที่ปรากฏในวงเล็บหลังจากที่จับคีย์เวิร์ด
try {
// protected code
}
catch( ExceptionName e ) {
// code to handle ExceptionName exception
}
รหัสข้างต้นมีข้อยกเว้น ExceptionNameประเภท. หากคุณต้องการระบุว่า catch block ควรจัดการกับข้อยกเว้นประเภทใด ๆ ที่เกิดขึ้นใน try block คุณต้องใส่จุดไข่ปลา, ... , ระหว่างวงเล็บที่ล้อมรอบการประกาศข้อยกเว้นดังนี้ -
try {
// protected code
}
catch(...) {
// code to handle any exception
}
ตัวอย่างต่อไปนี้แสดงการหารด้วยข้อยกเว้นเป็นศูนย์ มันติดอยู่ในบล็อกจับ
import std.stdio;
import std.string;
string division(int a, int b) {
string result = "";
try {
if( b == 0 ) {
throw new Exception("Cannot divide by zero!");
} else {
result = format("%s",a/b);
}
} catch (Exception e) {
result = e.msg;
}
return result;
}
void main () {
int x = 50;
int y = 0;
writeln(division(x, y));
y = 10;
writeln(division(x, y));
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
Cannot divide by zero!
5
การเขียนโปรแกรมตามสัญญาในการเขียนโปรแกรม D มุ่งเน้นไปที่การจัดหาวิธีการจัดการข้อผิดพลาดที่ง่ายและเข้าใจได้ การเขียนโปรแกรมสัญญาใน D ดำเนินการโดยบล็อกรหัสสามประเภท -
- บล็อกร่างกาย
- ในบล็อก
- ออกจากบล็อก
บอดี้บล็อคใน D
บล็อกเนื้อหาประกอบด้วยรหัสการทำงานที่แท้จริงของการดำเนินการ บล็อกเข้าและออกเป็นทางเลือกในขณะที่บังคับบล็อกตัวถัง ไวยากรณ์ง่ายๆแสดงอยู่ด้านล่าง
return_type function_name(function_params)
in {
// in block
}
out (result) {
// in block
}
body {
// actual function block
}
อยู่ในบล็อกสำหรับเงื่อนไขเบื้องต้นใน D
ในบล็อกมีไว้สำหรับเงื่อนไขก่อนง่ายๆที่ตรวจสอบว่าพารามิเตอร์อินพุตเป็นที่ยอมรับและอยู่ในช่วงที่โค้ดสามารถจัดการได้หรือไม่ ประโยชน์ของการบล็อกคือเงื่อนไขการเข้าทั้งหมดสามารถเก็บไว้ด้วยกันและแยกออกจากเนื้อความจริงของฟังก์ชัน เงื่อนไขเบื้องต้นง่ายๆสำหรับการตรวจสอบรหัสผ่านสำหรับความยาวขั้นต่ำแสดงอยู่ด้านล่าง
import std.stdio;
import std.string;
bool isValid(string password)
in {
assert(password.length>=5);
}
body {
// other conditions
return true;
}
void main() {
writeln(isValid("password"));
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
true
บล็อกสำหรับเงื่อนไขการโพสต์ใน D
out block จะดูแลค่าที่ส่งคืนจากฟังก์ชัน ตรวจสอบว่าค่าที่ส่งคืนอยู่ในช่วงที่คาดไว้ ตัวอย่างง่ายๆที่มีทั้งเข้าและออกแสดงอยู่ด้านล่างซึ่งแปลงเดือนปีเป็นรูปแบบอายุทศนิยมรวมกัน
import std.stdio;
import std.string;
double getAge(double months,double years)
in {
assert(months >= 0);
assert(months <= 12);
}
out (result) {
assert(result>=years);
}
body {
return years + months/12;
}
void main () {
writeln(getAge(10,12));
}
เมื่อรวบรวมและดำเนินการโค้ดด้านบนจะอ่านไฟล์ที่สร้างในส่วนก่อนหน้าและสร้างผลลัพธ์ต่อไปนี้ -
12.8333
การคอมไพล์ตามเงื่อนไขเป็นกระบวนการในการเลือกโค้ดที่จะคอมไพล์และโค้ดใดที่จะไม่คอมไพล์คล้ายกับ #if / #else / #endif ใน C และ C ++ คำสั่งใด ๆ ที่ยังไม่ได้รวบรวมจะต้องถูกต้องตามหลักไวยากรณ์
การคอมไพล์ตามเงื่อนไขเกี่ยวข้องกับการตรวจสอบเงื่อนไขที่ประเมินได้ในขณะคอมไพล์ คำสั่งเงื่อนไขรันไทม์เช่น if, for, while ไม่ใช่คุณสมบัติการคอมไพล์ตามเงื่อนไข คุณสมบัติต่อไปนี้ของ D มีไว้สำหรับการคอมไพล์ตามเงื่อนไข -
- debug
- version
- คงที่ถ้า
คำชี้แจงการแก้ไขข้อบกพร่องใน D
การดีบักมีประโยชน์ในระหว่างการพัฒนาโปรแกรม นิพจน์และคำสั่งที่ทำเครื่องหมายว่าดีบักจะถูกคอมไพล์ลงในโปรแกรมเฉพาะเมื่อเปิดใช้งานสวิตช์คอมไพเลอร์ -debug
debug a_conditionally_compiled_expression;
debug {
// ... conditionally compiled code ...
} else {
// ... code that is compiled otherwise ...
}
ส่วนคำสั่งอื่นเป็นทางเลือก ทั้งนิพจน์เดียวและบล็อกโค้ดด้านบนจะคอมไพล์ก็ต่อเมื่อเปิดใช้งานสวิตช์คอมไพเลอร์ -debug
แทนที่จะถูกลบออกทั้งหมดคุณสามารถทำเครื่องหมายบรรทัดเป็น debug แทนได้
debug writefln("%s debug only statement", value);
บรรทัดดังกล่าวจะรวมอยู่ในโปรแกรมเฉพาะเมื่อเปิดใช้งานสวิตช์คอมไพเลอร์ -debug
dmd test.d -oftest -w -debug
คำสั่ง Debug (tag) ใน D
คำสั่งดีบักสามารถกำหนดชื่อ (แท็ก) ที่จะรวมไว้ในโปรแกรมได้โดยเลือก
debug(mytag) writefln("%s not found", value);
บรรทัดดังกล่าวจะรวมอยู่ในโปรแกรมเฉพาะเมื่อเปิดใช้งานสวิตช์คอมไพเลอร์ -debug
dmd test.d -oftest -w -debug = mytag
บล็อกการแก้ไขข้อบกพร่องสามารถมีแท็กได้เช่นกัน
debug(mytag) {
//
}
เป็นไปได้ที่จะเปิดใช้งานแท็กดีบักได้มากกว่าหนึ่งแท็กในแต่ละครั้ง
dmd test.d -oftest -w -debug = mytag1 -debug = mytag2
คำสั่ง Debug (ระดับ) ใน D
บางครั้งการเชื่อมโยงคำสั่งการดีบักตามระดับตัวเลขจะมีประโยชน์มากกว่า การเพิ่มระดับสามารถให้ข้อมูลที่ละเอียดมากขึ้น
import std.stdio;
void myFunction() {
debug(1) writeln("debug1");
debug(2) writeln("debug2");
}
void main() {
myFunction();
}
นิพจน์การดีบักและบล็อกที่ต่ำกว่าหรือเท่ากับระดับที่ระบุจะถูกคอมไพล์
$ dmd test.d -oftest -w -debug = 1 $ ./test
debug1
เวอร์ชัน (แท็ก) และเวอร์ชัน (ระดับ) คำสั่งในง
เวอร์ชันคล้ายกับการดีบักและใช้ในลักษณะเดียวกัน ส่วนคำสั่งอื่นเป็นทางเลือก แม้ว่าเวอร์ชันจะทำงานเหมือนกับการดีบั๊ก แต่การมีคีย์เวิร์ดแยกกันจะช่วยแยกแยะการใช้งานที่ไม่เกี่ยวข้องกันได้ เช่นเดียวกับการแก้จุดบกพร่องสามารถเปิดใช้งานได้มากกว่าหนึ่งเวอร์ชัน
import std.stdio;
void myFunction() {
version(1) writeln("version1");
version(2) writeln("version2");
}
void main() {
myFunction();
}
นิพจน์การดีบักและบล็อกที่ต่ำกว่าหรือเท่ากับระดับที่ระบุจะถูกคอมไพล์
$ dmd test.d -oftest -w -version = 1 $ ./test
version1
คงที่ถ้า
คงที่ if เป็นเวลาคอมไพล์เทียบเท่ากับคำสั่ง if เช่นเดียวกับคำสั่ง if, static if ใช้นิพจน์เชิงตรรกะและประเมินค่า ไม่เหมือนคำสั่ง if คือ static if ไม่เกี่ยวกับขั้นตอนการดำเนินการ แต่จะเป็นตัวกำหนดว่าควรรวมโค้ดไว้ในโปรแกรมหรือไม่
นิพจน์ if ไม่เกี่ยวข้องกับตัวดำเนินการ is ที่เราเห็นก่อนหน้านี้ทั้งในเชิงวากยสัมพันธ์และความหมาย มีการประเมินในเวลาคอมไพล์ มันสร้างค่า int ทั้ง 0 หรือ 1; ขึ้นอยู่กับนิพจน์ที่ระบุในวงเล็บ แม้ว่านิพจน์ที่ใช้จะไม่ใช่นิพจน์เชิงตรรกะ แต่นิพจน์ is เองก็ถูกใช้เป็นนิพจน์ตรรกะเวลาคอมไพล์ มีประโยชน์อย่างยิ่งในการกำหนดเงื่อนไขแบบคงที่และข้อ จำกัด เทมเพลต
import std.stdio;
enum Days {
sun,
mon,
tue,
wed,
thu,
fri,
sat
};
void myFunction(T)(T mytemplate) {
static if (is (T == class)) {
writeln("This is a class type");
} else static if (is (T == enum)) {
writeln("This is an enum type");
}
}
void main() {
Days day;
myFunction(day);
}
เมื่อเราคอมไพล์และรันเราจะได้ผลลัพธ์ดังนี้
This is an enum type
คลาสเป็นคุณสมบัติหลักของการเขียนโปรแกรม D ที่สนับสนุนการเขียนโปรแกรมเชิงวัตถุและมักเรียกว่าประเภทที่ผู้ใช้กำหนดเอง
คลาสถูกใช้เพื่อระบุรูปแบบของอ็อบเจ็กต์และรวมการแทนข้อมูลและวิธีการจัดการข้อมูลนั้นเป็นแพ็กเกจเดียว ข้อมูลและฟังก์ชันภายในคลาสเรียกว่าสมาชิกของคลาส
นิยามคลาส D
เมื่อคุณกำหนดคลาสคุณจะกำหนดพิมพ์เขียวสำหรับชนิดข้อมูล สิ่งนี้ไม่ได้กำหนดข้อมูลใด ๆ แต่เป็นการกำหนดความหมายของชื่อคลาสนั่นคืออ็อบเจ็กต์ของคลาสจะประกอบด้วยอะไรและสามารถดำเนินการกับอ็อบเจ็กต์ดังกล่าวได้อย่างไร
นิยามคลาสเริ่มต้นด้วยคีย์เวิร์ด classตามด้วยชื่อชั้น; และตัวคลาสล้อมรอบด้วยวงเล็บปีกกาคู่หนึ่ง คำจำกัดความของคลาสจะต้องตามด้วยอัฒภาคหรือรายการของการประกาศ ตัวอย่างเช่นเรากำหนดประเภทข้อมูล Box โดยใช้คำสำคัญclass ดังต่อไปนี้ -
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
}
คำหลัก publicกำหนดคุณสมบัติการเข้าถึงของสมาชิกของคลาสที่ตามมา สมาชิกสาธารณะสามารถเข้าถึงได้จากภายนอกชั้นเรียนที่ใดก็ได้ภายในขอบเขตของวัตถุคลาส คุณยังสามารถระบุสมาชิกของชั้นเรียนเป็นprivate หรือ protected ซึ่งเราจะพูดถึงในส่วนย่อย
การกำหนดวัตถุ D
คลาสจัดเตรียมพิมพ์เขียวสำหรับอ็อบเจ็กต์ดังนั้นโดยพื้นฐานแล้วอ็อบเจ็กต์จะถูกสร้างขึ้นจากคลาส คุณประกาศออบเจ็กต์ของคลาสด้วยการประกาศประเภทเดียวกับที่คุณประกาศตัวแปรประเภทพื้นฐาน คำสั่งต่อไปนี้ประกาศสองวัตถุของคลาสกล่อง -
Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
ทั้งสองอ็อบเจ็กต์ Box1 และ Box2 มีสำเนาข้อมูลของตัวเอง
การเข้าถึงสมาชิกข้อมูล
สมาชิกข้อมูลสาธารณะของออบเจ็กต์ของคลาสสามารถเข้าถึงได้โดยใช้ตัวดำเนินการเข้าถึงสมาชิกโดยตรง (.) ให้เราลองใช้ตัวอย่างต่อไปนี้เพื่อทำให้สิ่งต่างๆชัดเจน -
import std.stdio;
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
}
void main() {
Box box1 = new Box(); // Declare Box1 of type Box
Box box2 = new Box(); // Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
box1.height = 5.0;
box1.length = 6.0;
box1.breadth = 7.0;
// box 2 specification
box2.height = 10.0;
box2.length = 12.0;
box2.breadth = 13.0;
// volume of box 1
volume = box1.height * box1.length * box1.breadth;
writeln("Volume of Box1 : ",volume);
// volume of box 2
volume = box2.height * box2.length * box2.breadth;
writeln("Volume of Box2 : ", volume);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Volume of Box1 : 210
Volume of Box2 : 1560
สิ่งสำคัญคือต้องทราบว่าสมาชิกส่วนตัวและที่ได้รับการป้องกันไม่สามารถเข้าถึงได้โดยตรงโดยใช้ตัวดำเนินการเข้าถึงสมาชิกโดยตรง (.) ไม่นานคุณจะได้เรียนรู้ว่าสมาชิกส่วนตัวและได้รับการป้องกันสามารถเข้าถึงได้อย่างไร
คลาสและวัตถุใน D
จนถึงตอนนี้คุณมีความคิดพื้นฐานเกี่ยวกับคลาส D และวัตถุ มีแนวคิดที่น่าสนใจเพิ่มเติมเกี่ยวกับคลาส D และออบเจ็กต์ซึ่งเราจะพูดถึงในส่วนย่อยต่างๆที่แสดงด้านล่าง -
ซีเนียร์ | แนวคิดและคำอธิบาย |
---|---|
1 | ฟังก์ชันสมาชิกในชั้นเรียน ฟังก์ชันสมาชิกของคลาสคือฟังก์ชันที่มีนิยามหรือต้นแบบภายในนิยามคลาสเหมือนกับตัวแปรอื่น ๆ |
2 | ตัวปรับการเข้าถึงคลาส สมาชิกชั้นเรียนสามารถกำหนดเป็นสาธารณะส่วนตัวหรือได้รับการคุ้มครอง โดยค่าเริ่มต้นสมาชิกจะถือว่าเป็นส่วนตัว |
3 | ตัวสร้างและตัวทำลาย ตัวสร้างคลาสคือฟังก์ชันพิเศษในคลาสที่ถูกเรียกใช้เมื่อสร้างอ็อบเจ็กต์ใหม่ของคลาส ตัวทำลายยังเป็นฟังก์ชันพิเศษซึ่งถูกเรียกใช้เมื่ออ็อบเจ็กต์ที่สร้างขึ้นถูกลบ |
4 | ตัวชี้นี้ใน D ทุกวัตถุมีตัวชี้พิเศษ this ซึ่งชี้ไปที่วัตถุนั้นเอง |
5 | ชี้ไปที่คลาส D ตัวชี้ไปยังชั้นเรียนจะทำในลักษณะเดียวกับที่ตัวชี้ไปยังโครงสร้าง ในความเป็นจริงคลาสเป็นเพียงโครงสร้างที่มีฟังก์ชันอยู่ในนั้น |
6 | สมาชิกคงที่ของคลาส ทั้งสมาชิกข้อมูลและสมาชิกฟังก์ชันของคลาสสามารถประกาศเป็นแบบคงที่ได้ |
แนวคิดที่สำคัญที่สุดอย่างหนึ่งในการเขียนโปรแกรมเชิงวัตถุคือการสืบทอด การสืบทอดอนุญาตให้กำหนดคลาสในรูปแบบของคลาสอื่นซึ่งทำให้ง่ายต่อการสร้างและดูแลแอปพลิเคชัน นอกจากนี้ยังให้โอกาสในการนำฟังก์ชันโค้ดกลับมาใช้ซ้ำและใช้งานได้รวดเร็ว
เมื่อสร้างคลาสแทนที่จะเขียนสมาชิกข้อมูลและฟังก์ชันสมาชิกใหม่ทั้งหมดโปรแกรมเมอร์สามารถกำหนดได้ว่าคลาสใหม่ควรสืบทอดสมาชิกของคลาสที่มีอยู่ คลาสที่มีอยู่นี้เรียกว่าbase คลาสและคลาสใหม่เรียกว่า derived ชั้นเรียน.
ความคิดเกี่ยวกับการถ่ายทอดทางพันธุกรรมทำให้เกิดความสัมพันธ์ ตัวอย่างเช่นสัตว์เลี้ยงลูกด้วยนม IS-A สุนัข IS-A เลี้ยงลูกด้วยนมดังนั้นสุนัข IS-A เช่นกันและอื่น ๆ
คลาสพื้นฐานและคลาสที่ได้รับใน D
คลาสสามารถได้มาจากมากกว่าหนึ่งคลาสซึ่งหมายความว่าสามารถสืบทอดข้อมูลและฟังก์ชันจากคลาสพื้นฐานได้หลายคลาส ในการกำหนดคลาสที่ได้รับเราใช้รายการที่มาของคลาสเพื่อระบุคลาสฐาน (es) รายการที่มาของคลาสจะตั้งชื่อคลาสพื้นฐานตั้งแต่หนึ่งคลาสขึ้นไปและมีรูปแบบ -
class derived-class: base-class
พิจารณาคลาสพื้นฐาน Shape และคลาสที่ได้รับมา Rectangle ดังต่อไปนี้ -
import std.stdio;
// Base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
}
// Derived class
class Rectangle: Shape {
public:
int getArea() {
return (width * height);
}
}
void main() {
Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
writeln("Total area: ", Rect.getArea());
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Total area: 35
การควบคุมการเข้าถึงและการสืบทอด
คลาสที่ได้รับสามารถเข้าถึงสมาชิกที่ไม่ใช่ส่วนตัวทั้งหมดของคลาสพื้นฐานได้ ดังนั้นสมาชิกระดับฐานที่ไม่ควรเข้าถึงฟังก์ชันสมาชิกของคลาสที่ได้รับมาจึงควรประกาศเป็นส่วนตัวในคลาสพื้นฐาน
คลาสที่ได้รับสืบทอดเมธอดคลาสพื้นฐานทั้งหมดโดยมีข้อยกเว้นต่อไปนี้ -
- ตัวสร้างตัวทำลายและตัวสร้างสำเนาของคลาสพื้นฐาน
- ตัวดำเนินการที่มากเกินไปของคลาสพื้นฐาน
มรดกหลายระดับ
การสืบทอดอาจมีได้หลายระดับและจะแสดงในตัวอย่างต่อไปนี้
import std.stdio;
// Base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
}
// Derived class
class Rectangle: Shape {
public:
int getArea() {
return (width * height);
}
}
class Square: Rectangle {
this(int side) {
this.setWidth(side);
this.setHeight(side);
}
}
void main() {
Square square = new Square(13);
// Print the area of the object.
writeln("Total area: ", square.getArea());
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Total area: 169
D ช่วยให้คุณระบุคำจำกัดความได้มากกว่าหนึ่งคำจำกัดความสำหรับไฟล์ function ชื่อหรือ operator ในขอบเขตเดียวกันซึ่งเรียกว่า function overloading และ operator overloading ตามลำดับ
การประกาศที่มากเกินไปคือการประกาศที่ได้รับการประกาศโดยใช้ชื่อเดียวกันกับการประกาศครั้งก่อนในขอบเขตเดียวกันยกเว้นว่าการประกาศทั้งสองมีข้อโต้แย้งที่แตกต่างกันและความหมายที่แตกต่างกันอย่างเห็นได้ชัด
เมื่อคุณโทรเกิน function หรือ operatorคอมไพลเลอร์กำหนดนิยามที่เหมาะสมที่สุดที่จะใช้โดยการเปรียบเทียบประเภทอาร์กิวเมนต์ที่คุณใช้เรียกฟังก์ชันหรือตัวดำเนินการกับประเภทพารามิเตอร์ที่ระบุในนิยาม เรียกกระบวนการเลือกฟังก์ชันโอเวอร์โหลดที่เหมาะสมที่สุดหรือตัวดำเนินการoverload resolution..
ฟังก์ชันโอเวอร์โหลด
คุณสามารถกำหนดได้หลายคำจำกัดความสำหรับชื่อฟังก์ชันเดียวกันในขอบเขตเดียวกัน คำจำกัดความของฟังก์ชันต้องแตกต่างกันตามประเภทและ / หรือจำนวนอาร์กิวเมนต์ในรายการอาร์กิวเมนต์ คุณไม่สามารถโอเวอร์โหลดการประกาศฟังก์ชันที่แตกต่างกันตามประเภทการส่งคืนเท่านั้น
ตัวอย่าง
ตัวอย่างต่อไปนี้ใช้ฟังก์ชันเดียวกัน print() เพื่อพิมพ์ข้อมูลประเภทต่างๆ -
import std.stdio;
import std.string;
class printData {
public:
void print(int i) {
writeln("Printing int: ",i);
}
void print(double f) {
writeln("Printing float: ",f );
}
void print(string s) {
writeln("Printing string: ",s);
}
};
void main() {
printData pd = new printData();
// Call print to print integer
pd.print(5);
// Call print to print float
pd.print(500.263);
// Call print to print character
pd.print("Hello D");
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Printing int: 5
Printing float: 500.263
Printing string: Hello D
ตัวดำเนินการมากเกินไป
คุณสามารถกำหนดใหม่หรือโอเวอร์โหลดตัวดำเนินการในตัวส่วนใหญ่ที่มีอยู่ใน D. ดังนั้นโปรแกรมเมอร์จึงสามารถใช้ตัวดำเนินการกับประเภทที่ผู้ใช้กำหนดได้เช่นกัน
ตัวดำเนินการสามารถโอเวอร์โหลดได้โดยใช้ string op ตามด้วย Add, Sub และอื่น ๆ ตามตัวดำเนินการที่มีการโอเวอร์โหลด เราสามารถโอเวอร์โหลดตัวดำเนินการ + เพื่อเพิ่มสองกล่องดังที่แสดงด้านล่าง
Box opAdd(Box b) {
Box box = new Box();
box.length = this.length + b.length;
box.breadth = this.breadth + b.breadth;
box.height = this.height + b.height;
return box;
}
ตัวอย่างต่อไปนี้แสดงแนวคิดของตัวดำเนินการมากเกินไปโดยใช้ฟังก์ชันสมาชิก ที่นี่วัตถุจะถูกส่งผ่านเป็นอาร์กิวเมนต์ที่มีการเข้าถึงคุณสมบัติโดยใช้วัตถุนี้ วัตถุที่เรียกตัวดำเนินการนี้สามารถเข้าถึงได้โดยใช้this ตามคำอธิบายด้านล่าง -
import std.stdio;
class Box {
public:
double getVolume() {
return length * breadth * height;
}
void setLength( double len ) {
length = len;
}
void setBreadth( double bre ) {
breadth = bre;
}
void setHeight( double hei ) {
height = hei;
}
Box opAdd(Box b) {
Box box = new Box();
box.length = this.length + b.length;
box.breadth = this.breadth + b.breadth;
box.height = this.height + b.height;
return box;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Main function for the program
void main( ) {
Box box1 = new Box(); // Declare box1 of type Box
Box box2 = new Box(); // Declare box2 of type Box
Box box3 = new Box(); // Declare box3 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
box1.setLength(6.0);
box1.setBreadth(7.0);
box1.setHeight(5.0);
// box 2 specification
box2.setLength(12.0);
box2.setBreadth(13.0);
box2.setHeight(10.0);
// volume of box 1
volume = box1.getVolume();
writeln("Volume of Box1 : ", volume);
// volume of box 2
volume = box2.getVolume();
writeln("Volume of Box2 : ", volume);
// Add two object as follows:
box3 = box1 + box2;
// volume of box 3
volume = box3.getVolume();
writeln("Volume of Box3 : ", volume);
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
ประเภทการโอเวอร์โหลดของตัวดำเนินการ
โดยทั่วไปมีตัวดำเนินการมากเกินไปสามประเภทตามรายการด้านล่าง
ซีเนียร์ | ประเภทการโอเวอร์โหลด |
---|---|
1 | Unary Operators โอเวอร์โหลด |
2 | ตัวดำเนินการไบนารีโอเวอร์โหลด |
3 | ตัวดำเนินการเปรียบเทียบโอเวอร์โหลด |
โปรแกรม D ทั้งหมดประกอบด้วยองค์ประกอบพื้นฐานสองประการดังต่อไปนี้ -
Program statements (code) - นี่คือส่วนหนึ่งของโปรแกรมที่ดำเนินการและเรียกว่าฟังก์ชัน
Program data - เป็นข้อมูลของโปรแกรมที่ได้รับผลกระทบจากการทำงานของโปรแกรม
การห่อหุ้มเป็นแนวคิดการเขียนโปรแกรมเชิงวัตถุที่เชื่อมโยงข้อมูลและฟังก์ชันที่จัดการข้อมูลเข้าด้วยกันและช่วยให้ทั้งสองปลอดภัยจากการรบกวนภายนอกและการใช้งานในทางที่ผิด การห่อหุ้มข้อมูลนำไปสู่แนวคิด OOP ที่สำคัญของdata hiding.
Data encapsulation เป็นกลไกในการรวมข้อมูลและฟังก์ชันที่ใช้และ data abstraction เป็นกลไกในการเปิดเผยเฉพาะอินเทอร์เฟซและซ่อนรายละเอียดการใช้งานจากผู้ใช้
D สนับสนุนคุณสมบัติของการห่อหุ้มและการซ่อนข้อมูลผ่านการสร้างชนิดที่ผู้ใช้กำหนดเองเรียกว่า classes. เราได้ศึกษาแล้วว่าชั้นเรียนสามารถมีได้private, การป้องกันและ publicสมาชิก. โดยค่าเริ่มต้นรายการทั้งหมดที่กำหนดในคลาสเป็นส่วนตัว ตัวอย่างเช่น -
class Box {
public:
double getVolume() {
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
ตัวแปรความยาวความกว้างและความสูงคือ private. ซึ่งหมายความว่าสมาชิกคนอื่น ๆ ในคลาส Box สามารถเข้าถึงได้เท่านั้นและไม่สามารถเข้าถึงได้โดยส่วนอื่น ๆ ของโปรแกรมของคุณ นี่เป็นวิธีการห่อหุ้มวิธีหนึ่งที่ทำได้
เพื่อสร้างส่วนต่างๆของชั้นเรียน public (กล่าวคือสามารถเข้าถึงส่วนอื่น ๆ ของโปรแกรมของคุณได้) คุณต้องประกาศหลังจากส่วน publicคำสำคัญ. ตัวแปรหรือฟังก์ชันทั้งหมดที่กำหนดหลังจากตัวระบุสาธารณะสามารถเข้าถึงได้โดยฟังก์ชันอื่น ๆ ทั้งหมดในโปรแกรมของคุณ
การทำให้ชั้นเรียนเป็นเพื่อนกับอีกคนหนึ่งจะทำให้รายละเอียดการนำไปใช้งานและลดการห่อหุ้ม เหมาะอย่างยิ่งที่จะเก็บรายละเอียดของแต่ละคลาสให้ซ่อนจากคลาสอื่น ๆ ให้ได้มากที่สุด
การห่อหุ้มข้อมูลใน D
โปรแกรม D ใด ๆ ที่คุณใช้คลาสกับสมาชิกภาครัฐและเอกชนเป็นตัวอย่างของการห่อหุ้มข้อมูลและการแยกข้อมูล ลองพิจารณาตัวอย่างต่อไปนี้ -
ตัวอย่าง
import std.stdio;
class Adder {
public:
// constructor
this(int i = 0) {
total = i;
}
// interface to outside world
void addNum(int number) {
total += number;
}
// interface to outside world
int getTotal() {
return total;
};
private:
// hidden data from outside world
int total;
}
void main( ) {
Adder a = new Adder();
a.addNum(10);
a.addNum(20);
a.addNum(30);
writeln("Total ",a.getTotal());
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Total 60
ชั้นเรียนด้านบนจะเพิ่มตัวเลขเข้าด้วยกันและส่งกลับผลรวม สมาชิกสาธารณะaddNum และ getTotalเป็นส่วนต่อประสานกับโลกภายนอกและผู้ใช้จำเป็นต้องรู้จักพวกเขาเพื่อใช้คลาส จำนวนสมาชิกส่วนตัวทั้งหมดเป็นสิ่งที่ถูกซ่อนจากโลกภายนอก แต่จำเป็นสำหรับชั้นเรียนเพื่อให้ทำงานได้อย่างถูกต้อง
กลยุทธ์การออกแบบคลาสใน D
พวกเราส่วนใหญ่ได้เรียนรู้จากประสบการณ์อันขมขื่นเพื่อให้สมาชิกชั้นเรียนเป็นส่วนตัวโดยปริยายเว้นแต่เราจำเป็นต้องเปิดเผยพวกเขาจริงๆ แค่นั้นก็ดีแล้วencapsulation.
ภูมิปัญญานี้ถูกนำไปใช้กับสมาชิกข้อมูลบ่อยที่สุด แต่ใช้กับสมาชิกทุกคนอย่างเท่าเทียมกันรวมถึงฟังก์ชันเสมือนด้วย
อินเทอร์เฟซเป็นวิธีการบังคับให้คลาสที่สืบทอดมาจากอินเทอร์เฟซต้องใช้ฟังก์ชันหรือตัวแปรบางอย่าง ต้องไม่นำฟังก์ชันไปใช้ในอินเทอร์เฟซเนื่องจากจะใช้งานในคลาสที่สืบทอดมาจากอินเทอร์เฟซเสมอ
อินเทอร์เฟซถูกสร้างขึ้นโดยใช้คีย์เวิร์ดอินเทอร์เฟซแทนคีย์เวิร์ดคลาสแม้ว่าทั้งสองจะมีความคล้ายคลึงกันในหลาย ๆ ด้าน เมื่อคุณต้องการสืบทอดจากอินเทอร์เฟซและคลาสนั้นสืบทอดมาจากคลาสอื่นแล้วคุณต้องแยกชื่อคลาสและชื่อของอินเทอร์เฟซด้วยลูกน้ำ
ให้เราดูตัวอย่างง่ายๆที่อธิบายการใช้อินเทอร์เฟซ
ตัวอย่าง
import std.stdio;
// Base class
interface Shape {
public:
void setWidth(int w);
void setHeight(int h);
}
// Derived class
class Rectangle: Shape {
int width;
int height;
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
int getArea() {
return (width * height);
}
}
void main() {
Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
writeln("Total area: ", Rect.getArea());
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Total area: 35
การเชื่อมต่อกับฟังก์ชันสุดท้ายและคงที่ใน D
อินเทอร์เฟซสามารถมีวิธีการขั้นสุดท้ายและแบบคงที่ซึ่งคำจำกัดความควรรวมอยู่ในอินเทอร์เฟซเอง ฟังก์ชันเหล่านี้ไม่สามารถลบล้างได้โดยคลาสที่ได้รับ ตัวอย่างง่ายๆแสดงไว้ด้านล่าง
ตัวอย่าง
import std.stdio;
// Base class
interface Shape {
public:
void setWidth(int w);
void setHeight(int h);
static void myfunction1() {
writeln("This is a static method");
}
final void myfunction2() {
writeln("This is a final method");
}
}
// Derived class
class Rectangle: Shape {
int width;
int height;
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
int getArea() {
return (width * height);
}
}
void main() {
Rectangle rect = new Rectangle();
rect.setWidth(5);
rect.setHeight(7);
// Print the area of the object.
writeln("Total area: ", rect.getArea());
rect.myfunction1();
rect.myfunction2();
}
เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -
Total area: 35
This is a static method
This is a final method
Abstraction หมายถึงความสามารถในการสร้างคลาสนามธรรมใน OOP คลาสนามธรรมคือคลาสที่ไม่สามารถสร้างอินสแตนซ์ได้ ฟังก์ชันอื่น ๆ ทั้งหมดของคลาสยังคงมีอยู่และฟิลด์เมธอดและคอนสตรัคเตอร์ทั้งหมดสามารถเข้าถึงได้ในลักษณะเดียวกัน คุณไม่สามารถสร้างอินสแตนซ์ของคลาสนามธรรมได้
หากคลาสเป็นนามธรรมและไม่สามารถสร้างอินสแตนซ์ได้คลาสจะไม่มีประโยชน์มากนักเว้นแต่จะเป็นคลาสย่อย โดยทั่วไปแล้วคลาสนามธรรมเกิดขึ้นระหว่างขั้นตอนการออกแบบ คลาสพาเรนต์มีฟังก์ชันการทำงานทั่วไปของคอลเลกชันของคลาสย่อย แต่คลาสพาเรนต์นั้นเป็นนามธรรมเกินไปที่จะใช้ด้วยตัวมันเอง
การใช้คลาสนามธรรมในง
ใช้ abstractคีย์เวิร์ดเพื่อประกาศคลาสบทคัดย่อ คีย์เวิร์ดจะปรากฏในการประกาศคลาสก่อนคีย์เวิร์ดคลาส ต่อไปนี้แสดงตัวอย่างของวิธีการสืบทอดและใช้คลาสนามธรรม
ตัวอย่าง
import std.stdio;
import std.string;
import std.datetime;
abstract class Person {
int birthYear, birthDay, birthMonth;
string name;
int getAge() {
SysTime sysTime = Clock.currTime();
return sysTime.year - birthYear;
}
}
class Employee : Person {
int empID;
}
void main() {
Employee emp = new Employee();
emp.empID = 101;
emp.birthYear = 1980;
emp.birthDay = 10;
emp.birthMonth = 10;
emp.name = "Emp1";
writeln(emp.name);
writeln(emp.getAge);
}
เมื่อเราคอมไพล์และรันโปรแกรมข้างต้นเราจะได้ผลลัพธ์ดังต่อไปนี้
Emp1
37
ฟังก์ชันนามธรรม
เช่นเดียวกับฟังก์ชันคลาสสามารถเป็นนามธรรมได้เช่นกัน การใช้ฟังก์ชันดังกล่าวไม่ได้กำหนดไว้ในคลาส แต่ควรจัดให้อยู่ในคลาสที่สืบทอดคลาสด้วยฟังก์ชันนามธรรม ตัวอย่างข้างต้นได้รับการอัปเดตด้วยฟังก์ชันนามธรรม
ตัวอย่าง
import std.stdio;
import std.string;
import std.datetime;
abstract class Person {
int birthYear, birthDay, birthMonth;
string name;
int getAge() {
SysTime sysTime = Clock.currTime();
return sysTime.year - birthYear;
}
abstract void print();
}
class Employee : Person {
int empID;
override void print() {
writeln("The employee details are as follows:");
writeln("Emp ID: ", this.empID);
writeln("Emp Name: ", this.name);
writeln("Age: ",this.getAge);
}
}
void main() {
Employee emp = new Employee();
emp.empID = 101;
emp.birthYear = 1980;
emp.birthDay = 10;
emp.birthMonth = 10;
emp.name = "Emp1";
emp.print();
}
เมื่อเราคอมไพล์และรันโปรแกรมข้างต้นเราจะได้ผลลัพธ์ดังต่อไปนี้
The employee details are as follows:
Emp ID: 101
Emp Name: Emp1
Age: 37