การเขียนโปรแกรม 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