D 프로그래밍-퀵 가이드

D 프로그래밍 언어는 Digital Mars의 Walter Bright가 개발 한 객체 지향 다중 패러다임 시스템 프로그래밍 언어입니다. 1999 년에 개발이 시작되어 2001 년에 처음 출시되었습니다. D (1.0)의 메이저 버전은 2007 년에 출시되었습니다. 현재 D2 버전의 D가 있습니다.

D는 구문이 C 스타일 인 언어이며 정적 유형을 사용합니다. D에는 C 및 C ++의 많은 기능이 있지만 D의 일부가 포함되지 않은 이러한 언어의 일부 기능도 있습니다.

  • 단위 테스트
  • 진정한 모듈
  • 가비지 수집
  • 일등석 배열
  • 자유롭고 개방적
  • 연관 배열
  • 동적 배열
  • 내부 수업
  • Closures
  • 익명 기능
  • 게으른 평가
  • Closures

다중 패러다임

D는 다중 패러다임 프로그래밍 언어입니다. 여러 패러다임에는 다음이 포함됩니다.

  • Imperative
  • 객체 지향
  • 메타 프로그래밍
  • Functional
  • Concurrent

import std.stdio; 
 
void main(string[] args) { 
   writeln("Hello World!"); 
}

학습 D

D를 배울 때해야 할 가장 중요한 일은 개념에 집중하고 언어의 기술적 세부 사항에 빠져들지 않는 것입니다.

프로그래밍 언어를 배우는 목적은 더 나은 프로그래머가되는 것입니다. 즉, 새로운 시스템을 설계 및 구현하고 기존 시스템을 유지하는 데 더 효과적입니다.

D의 범위

D 프로그래밍에는 몇 가지 흥미로운 기능이 있으며 공식 D 프로그래밍 사이트에서는 D가 편리하고 강력하며 효율적이라고 주장합니다. D 프로그래밍은 크기 조정이 가능한 배열 및 문자열 함수와 같은 표준 라이브러리의 형태로 C 언어가 제공하는 핵심 언어에 많은 기능을 추가합니다. D는 중급에서 고급 프로그래머에게 훌륭한 제 2 언어를 만듭니다. D는 메모리를 처리하고 C ++에서 종종 문제를 일으키는 포인터를 관리하는 데 더 좋습니다.

D 프로그래밍은 주로 기존 프로그램을 변환하는 새로운 프로그램을 대상으로합니다. 대규모 팀에서 수백만 줄의 코드로 작성 될 대규모 새 프로젝트에 이상적인 내장 테스트 및 검증 기능을 제공합니다.

D를위한 로컬 환경 설정

여전히 D 프로그래밍 언어에 대한 환경을 설정하려면 컴퓨터에서 사용할 수있는 다음 두 소프트웨어, (a) 텍스트 편집기, (b) D 컴파일러가 필요합니다.

D 프로그래밍을위한 텍스트 편집기

이것은 프로그램을 입력하는 데 사용됩니다. 몇 가지 편집기의 예로는 Windows 메모장, OS 편집 명령, Brief, Epsilon, EMACS 및 vim 또는 vi가 있습니다.

텍스트 편집기의 이름과 버전은 운영 체제에 따라 다를 수 있습니다. 예를 들어 메모장은 Windows에서 사용되며 vim 또는 vi는 Linux 또는 UNIX뿐만 아니라 Windows에서도 사용할 수 있습니다.

편집기로 만든 파일을 소스 파일이라고하며 프로그램 소스 코드를 포함합니다. D 프로그램의 소스 파일 이름은 ".d".

프로그래밍을 시작하기 전에 하나의 텍스트 편집기가 있고 컴퓨터 프로그램을 작성하고 파일에 저장하고 빌드하고 마지막으로 실행할 수있는 충분한 경험이 있는지 확인하십시오.

D 컴파일러

대부분의 현재 D 구현은 효율적인 실행을 위해 기계 코드로 직접 컴파일됩니다.

우리는 여러 D 컴파일러를 사용할 수 있으며 다음을 포함합니다.

  • DMD − Digital Mars D 컴파일러는 Walter Bright의 공식 D 컴파일러입니다.

  • GDC − 개방형 DMD 컴파일러 소스 코드를 사용하여 구축 된 GCC 백엔드 용 프런트 엔드.

  • LDC − LLVM을 컴파일러 백엔드로 사용하는 DMD 프런트 엔드 기반 컴파일러.

위의 다른 컴파일러는 D 다운로드 에서 다운로드 할 수 있습니다.

D 버전 2를 사용할 예정이며 D1을 다운로드하지 않는 것이 좋습니다.

다음과 같이 helloWorld.d 프로그램을 만들 수 있습니다. 선택한 플랫폼에서 실행되는 첫 번째 프로그램으로 이것을 사용합니다.

import std.stdio; 
 
void main(string[] args) { 
   writeln("Hello World!"); 
}

다음 출력을 볼 수 있습니다.

$ hello world

Windows에 D 설치

Windows 설치 프로그램을 다운로드합니다 .

다운로드 한 실행 파일을 실행하여 화면의 지시에 따라 D를 설치하십시오.

이제 cd를 사용하여 파일이 포함 된 폴더로 전환 한 후 다음 단계를 사용하여 helloWorld.d라는 광고 파일을 빌드하고 실행할 수 있습니다.

C:\DProgramming> DMD helloWorld.d 
C:\DProgramming> helloWorld

다음 출력을 볼 수 있습니다.

hello world

C : \ DProgramming은 샘플을 저장하는 데 사용하는 폴더입니다. D 프로그램을 저장 한 폴더로 변경할 수 있습니다.

Ubuntu / Debian에 D 설치

데비안 설치 프로그램을 다운로드합니다 .

다운로드 한 실행 파일을 실행하여 화면의 지시에 따라 D를 설치하십시오.

이제 cd를 사용하여 파일이 포함 된 폴더로 전환 한 후 다음 단계를 사용하여 helloWorld.d라는 광고 파일을 빌드하고 실행할 수 있습니다.

$ dmd helloWorld.d 
$ ./helloWorld

다음 출력을 볼 수 있습니다.

$ hello world

Mac OS X에 D 설치

Mac 설치 프로그램을 다운로드합니다 .

다운로드 한 실행 파일을 실행하여 화면의 지시에 따라 D를 설치하십시오.

이제 cd를 사용하여 파일이 포함 된 폴더로 전환 한 후 다음 단계를 사용하여 helloWorld.d라는 광고 파일을 빌드하고 실행할 수 있습니다.

$ dmd helloWorld.d $ ./helloWorld

다음 출력을 볼 수 있습니다.

$ hello world

Fedora에 D 설치

fedora 설치 프로그램을 다운로드합니다 .

다운로드 한 실행 파일을 실행하여 화면의 지시에 따라 D를 설치하십시오.

이제 cd를 사용하여 파일이 포함 된 폴더로 전환 한 후 다음 단계를 사용하여 helloWorld.d라는 광고 파일을 빌드하고 실행할 수 있습니다.

$ dmd helloWorld.d 
$ ./helloWorld

다음 출력을 볼 수 있습니다.

$ hello world

OpenSUSE에 D 설치

OpenSUSE 설치 프로그램을 다운로드합니다 .

다운로드 한 실행 파일을 실행하여 화면의 지시에 따라 D를 설치하십시오.

이제 cd를 사용하여 파일이 포함 된 폴더로 전환 한 후 다음 단계를 사용하여 helloWorld.d라는 광고 파일을 빌드하고 실행할 수 있습니다.

$ dmd helloWorld.d $ ./helloWorld

다음 출력을 볼 수 있습니다.

$ hello world

D IDE

대부분의 경우 플러그인 형태로 D에 대한 IDE 지원이 있습니다. 여기에는 다음이 포함됩니다.

  • Visual D 플러그인 은 Visual Studio 2005-13 용 플러그인 입니다.

  • DDT 는 코드 완성, GDB로 디버깅을 제공하는 Eclipse 플러그인입니다.

  • Mono-D 코드 완성, dmd / ldc / gdc 지원을 통한 리팩토링. GSoC 2012의 일부였습니다.

  • Code Blocks 는 D 프로젝트 생성, 강조 표시 및 디버깅을 지원하는 다중 플랫폼 IDE입니다.

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에서 가져 오기

재사용 가능한 프로그램 부분의 모음 인 라이브러리는 가져 오기를 통해 프로젝트에서 사용할 수 있습니다. 여기서는 기본 I / O 작업을 제공하는 표준 io 라이브러리를 가져옵니다. 위 프로그램에서 사용하는 writeln은 D의 표준 라이브러리에있는 함수입니다. 한 줄의 텍스트를 인쇄하는 데 사용됩니다. D의 라이브러리 내용은 수행하려는 작업 유형에 따라 모듈로 그룹화됩니다. 이 프로그램이 사용하는 유일한 모듈은 데이터 입력 및 출력을 처리하는 std.stdio입니다.

주요 기능

주요 기능은 프로그램의 시작이며 실행 순서와 프로그램의 다른 섹션을 실행하는 방법을 결정합니다.

D의 토큰

AD 프로그램은 다양한 토큰으로 구성되며 토큰은 키워드, 식별자, 상수, 문자열 리터럴 또는 기호입니다. 예를 들어, 다음 D 문은 4 개의 토큰으로 구성됩니다.

writeln("test!");

개별 토큰은 다음과 같습니다.

writeln (
   "test!"
)
;

코멘트

주석은 D 프로그램의 지원 텍스트와 같으며 컴파일러에서 무시됩니다. 여러 줄 주석은 / *로 시작하고 다음과 같이 * / 문자로 끝납니다.

/* My first program in D */

단일 주석은 주석 시작 부분에 //를 사용하여 작성됩니다.

// my first program in D

식별자

AD 식별자는 변수, 함수 또는 기타 사용자 정의 항목을 식별하는 데 사용되는 이름입니다. 식별자는 A에서 Z까지의 문자 또는 a에서 z까지의 문자 또는 밑줄 _로 시작하여 0 개 이상의 문자, 밑줄 및 숫자 (0-9)가 이어집니다.

D는 식별자 내에서 @, $ 및 %와 같은 구두점 문자를 허용하지 않습니다. D는case sensitive프로그래밍 언어. 따라서 인력인력 은 D에서 두 가지 다른 식별자입니다. 다음은 허용되는 식별자의 몇 가지 예입니다.

mohd       zara    abc   move_name  a_123 
myname50   _temp   j     a23b9      retVal

키워드

다음 목록은 D의 예약어 중 일부를 보여줍니다. 이러한 예약어는 상수 나 변수 또는 기타 식별자 이름으로 사용할 수 없습니다.

요약 별명 정렬 asm
주장하다 자동 부울
바이트 케이스 캐스트 잡기
수업 const 계속하다
dchar 디버그 기본 대리자
비추천 하다 더블 그밖에
열거 형 수출 통근자 그릇된
결정적인 드디어 흙손 ...에 대한
각각 함수 이동 만약
수입 안으로 int
상호 작용 불변 이다
매크로 믹스 인 기준 치수 새로운
없는 우세하다 꾸러미
프라 그마 은밀한 보호 공공의
레알 심판 반환 범위
짧은 공전 구조체 감독자
스위치 동기화 됨 주형
던지다 진실 시험 typeid
유형 ubyte 단위 울롱
노동 조합 unittest ushort 버전
wchar 동안

D의 공백

공백 만 포함하는 줄 (가능하면 주석 포함)을 빈 줄이라고하며 D 컴파일러는이를 완전히 무시합니다.

공백은 공백, 탭, 개행 문자 및 주석을 설명하기 위해 D에서 사용되는 용어입니다. 공백은 명령문의 한 부분을 다른 부분과 분리하고 인터프리터가 int와 같은 명령문의 한 요소가 끝나고 다음 요소가 시작되는 위치를 식별 할 수 있도록합니다. 따라서 다음 진술에서-

local age

통역사가 구분할 수 있으려면 지역과 연령 사이에 적어도 하나의 공백 문자 (일반적으로 공백)가 있어야합니다. 반면에 다음 진술에서

int fruit = apples + oranges   //get the total fruits

과일과 = 사이 또는 =와 사과 사이에는 공백 문자가 필요하지 않지만 가독성을 위해 원하는 경우 일부를 포함 할 수 있습니다.

변수는 프로그램이 조작 할 수있는 저장 영역에 주어진 이름 일뿐입니다. D의 각 변수에는 변수 메모리의 크기와 레이아웃을 결정하는 특정 유형이 있습니다. 해당 메모리 내에 저장할 수있는 값의 범위 변수에 적용 할 수있는 연산 집합입니다.

변수 이름은 문자, 숫자 및 밑줄 문자로 구성 될 수 있습니다. 문자 또는 밑줄로 시작해야합니다. D는 대소 문자를 구분하므로 대문자와 소문자는 구별됩니다. 이전 장에서 설명한 기본 유형에 따라 다음과 같은 기본 변수 유형이 있습니다.

Sr. 아니. 유형 및 설명
1

char

일반적으로 단일 옥텟 (1 바이트)입니다. 이것은 정수 유형입니다.

2

int

기계의 가장 자연스러운 정수 크기입니다.

float

단 정밀도 부동 소수점 값입니다.

4

double

배정 밀도 부동 소수점 값입니다.

5

void

유형이 없음을 나타냅니다.

D 프로그래밍 언어는 또한 열거 형, 포인터, 배열, 구조, 유니온 등과 같은 다양한 유형의 변수를 정의 할 수 있습니다. 이에 대해서는 다음 장에서 다룰 것입니다. 이 장에서는 기본 변수 유형 만 살펴 보겠습니다.

D의 변수 정의

변수 정의는 변수에 대해 생성 할 공간과 공간을 컴파일러에 알려줍니다. 변수 정의는 데이터 유형을 지정하고 다음과 같이 해당 유형의 하나 이상의 변수 목록을 포함합니다.

type variable_list;

여기, type char, wchar, int, float, double, bool 또는 사용자 정의 개체 등을 포함하는 유효한 D 데이터 유형이어야합니다. variable_list쉼표로 구분 된 하나 이상의 식별자 이름으로 구성 될 수 있습니다. 몇 가지 유효한 선언이 여기에 표시됩니다.

int    i, j, k; 
char   c, ch; 
float  f, salary; 
double d;

라인 int i, j, k;둘 다 변수 i, j 및 k를 선언하고 정의합니다. 컴파일러에게 int 유형의 i, j 및 k라는 변수를 생성하도록 지시합니다.

변수는 선언에서 초기화 (초기 값 할당) 할 수 있습니다. 이니셜 라이저는 다음과 같이 등호와 상수 표현식으로 구성됩니다.

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에서 선언되면 항상 '기본 이니셜 라이저'로 설정되며 다음과 같이 수동으로 액세스 할 수 있습니다. T.init 어디 T 유형입니다 (예 : int.init). 정수 유형의 기본 이니셜 라이저는 0, 부울 false 및 부동 소수점 숫자 NaN입니다.

D의 변수 선언

변수 선언은 주어진 유형과 이름을 가진 하나의 변수가 존재한다는 것을 컴파일러에 보장하여 컴파일러가 변수에 대한 완전한 세부 사항없이 추가 컴파일을 진행할 수 있도록합니다. 변수 선언은 컴파일시에만 의미가 있으며 컴파일러는 프로그램 연결시 실제 변수 선언이 필요합니다.

프로그램 시작시 변수가 선언되었지만 주 함수 내에서 정의되고 초기화되는 다음 예제를 시도해보십시오.

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

D의 Lvalue와 Rvalue

D에는 두 종류의 표현이 있습니다-

  • lvalue − lvalue 인 표현식은 할당의 왼쪽 또는 오른쪽으로 나타날 수 있습니다.

  • rvalue − rvalue 인 표현식은 할당의 왼쪽이 아닌 오른쪽에 나타날 수 있습니다.

변수는 lvalue이므로 할당의 왼쪽에 나타날 수 있습니다. 숫자 리터럴은 r 값이므로 할당되지 않고 왼쪽에 표시 될 수 없습니다. 다음 진술은 유효합니다-

int g = 20;

그러나 다음은 유효한 문이 아니며 컴파일 시간 오류를 생성합니다.

10 = 20;

D 프로그래밍 언어에서 데이터 유형은 다양한 유형의 변수 또는 함수를 선언하는 데 사용되는 광범위한 시스템을 나타냅니다. 변수 유형은 저장 공간에서 차지하는 공간의 양과 저장된 비트 패턴이 해석되는 방식을 결정합니다.

D의 유형은 다음과 같이 분류 할 수 있습니다.

Sr. 아니. 유형 및 설명
1

Basic Types

이들은 산술 유형이며 (a) 정수, (b) 부동 소수점 및 (c) 문자의 세 가지 유형으로 구성됩니다.

2

Enumerated types

그들은 다시 산술 유형입니다. 프로그램 전체에서 특정 이산 정수 값에만 할당 될 수있는 변수를 정의하는 데 사용됩니다.

The type void

유형 지정자 void 는 사용 가능한 값이 없음을 나타냅니다.

4

Derived types

여기에는 (a) 포인터 유형, (b) 배열 유형, (c) 구조 유형, (d) 공용체 유형 및 (e) 함수 유형이 포함됩니다.

배열 유형 및 구조 유형을 집합 적으로 집계 유형이라고합니다. 함수의 유형은 함수의 반환 값 유형을 지정합니다. 다음 섹션에서 기본 유형을 살펴보고 다른 유형은 다음 장에서 다룰 것입니다.

정수 유형

다음 표는 저장 크기 및 값 범위와 함께 표준 정수 유형을 나열합니다.

유형 저장 크기 값 범위
부울 1 바이트 거짓 또는 참
바이트 1 바이트 -128에서 127
ubyte 1 바이트 0에서 255
int 4 바이트 -2,147,483,648에서 2,147,483,647
단위 4 바이트 0에서 4,294,967,295
짧은 2 바이트 -32,768에서 32,767
ushort 2 바이트 0에서 65,535
8 바이트 -9223372036854775808 ~ 9223372036854775807
울롱 8 바이트 0에서 18446744073709551615

유형 또는 변수의 정확한 크기를 얻으려면 sizeof운영자. type. (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 하드웨어가 지원하는 가장 큰 부동 소수점 유형 또는 double; 더 큰
ifloat 4 바이트 1.17549e-38i에서 3.40282e + 38i 허수 값 유형의 부동 소수점
idouble 8 바이트 2.22507e-308i에서 1.79769e + 308i로 double의 허수 값 유형
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로 두 개의 실수로 구성된 복소수 유형

다음 예제는 float 유형과 범위 값이 차지하는 저장 공간을 인쇄합니다.

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 코드 단위 및 유니 코드 코드 포인트

다음 예제는 char 유형이 차지하는 저장 공간을 인쇄합니다.

import std.stdio;

int main() {
   writeln("Length in bytes: ", char.sizeof);
   
   return 0;
}

위의 프로그램을 컴파일하고 실행하면 다음과 같은 결과가 생성됩니다.

Length in bytes: 1

무효 유형

void 유형은 사용 가능한 값이 없음을 지정합니다. 두 가지 상황에서 사용됩니다.

Sr. 아니. 유형 및 설명
1

Function returns as void

D에는 값을 반환하지 않거나 void를 반환한다고 말할 수있는 다양한 함수가 있습니다. 반환 값이없는 함수는 반환 유형이 void입니다. 예를 들면void exit (int status);

2

Function arguments as void

D에는 매개 변수를 허용하지 않는 다양한 기능이 있습니다. 매개 변수가없는 함수는 void로 받아 들일 수 있습니다. 예를 들면int rand(void);

이 시점에서 void 유형을 이해하지 못할 수 있으므로 계속 진행하고 다음 장에서 이러한 개념을 다룰 것입니다.

열거 형은 명명 된 상수 값을 정의하는 데 사용됩니다. 열거 형은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

위의 프로그램에서 열거가 어떻게 사용되는지 볼 수 있습니다. 처음에는 사용자 정의 열거 일의 day 라는 변수를 생성합니다 . 그런 다음 도트 연산자를 사용하여 mon으로 설정합니다 . 저장된 mon 값을 인쇄하려면 writefln 메소드를 사용해야합니다. 유형도 지정해야합니다. 정수 유형이므로 인쇄에 % d를 사용합니다.

명명 된 열거 형 속성

위의 예에서는 열거 형에 Days라는 이름을 사용하고 열거 형이라고합니다. 이러한 명명 된 열거 형에는 다음과 같은 속성이 있습니다.

  • 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

익명 열거 형

이름없는 열거를 익명 열거라고합니다. 예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

익명 열거 형은 명명 된 열거 형과 거의 동일한 방식으로 작동하지만 max, min 및 sizeof 속성이 없습니다.

기본 유형 구문이있는 열거 형

기본 유형을 사용한 열거 구문은 다음과 같습니다.

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 첫 번째 숫자는 8 진수 시스템을 나타 내기 위해 예약되어 있으므로 0이 될 수없는 일반 숫자 표현을 사용합니다. 여기에는 자체적으로 0이 포함되지 않습니다 : 0은 0입니다.

  • Octal 번호의 접두사로 0을 사용합니다.

  • Binary 0b 또는 0B를 접두사로 사용합니다.

  • Hexadecimal 0x 또는 0X를 접두사로 사용합니다.

정수 리터럴은 각각 unsigned 및 long에 대해 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

부동 소수점 리터럴

부동 소수점 리터럴은 1.568과 같은 10 진수 시스템 또는 0x91.bc와 같은 16 진수 시스템으로 지정할 수 있습니다.

십진법에서 지수는 문자 e 또는 E와 그 뒤에 숫자를 추가하여 나타낼 수 있습니다. 예를 들어 2.3e4는 "2.3 곱하기 10의 4 제곱"을 의미합니다. 지수 값 앞에 "+"문자를 지정할 수 있지만 효과가 없습니다. 예를 들어 2.3e4와 2.3e + 4는 동일합니다.

지수 값 앞에 추가 된 "-"문자는 "10의 거듭 제곱으로 나눈"의미를 변경합니다. 예를 들어 2.3e-2는 "2.3을 10으로 2의 제곱으로 나눈 값"을 의미합니다.

16 진수 시스템에서 값은 0x 또는 0X로 시작합니다. 지수는 e 또는 E 대신 p 또는 P로 지정됩니다. 지수는 "10의 거듭 제곱"이 아니라 "2의 거듭 제곱"을 의미합니다. 예를 들어, 0xabc.defP4의 P4는 "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.

기본적으로 부동 소수점 리터럴의 유형은 double입니다. f와 F는 float를 의미하고 L 지정자는 실수를 의미합니다.

부울 리터럴

두 개의 부울 리터럴이 있으며 표준 D 키워드의 일부입니다.

  • 가치 true 사실을 나타냅니다.

  • 가치 false 거짓을 나타냅니다.

true 값은 1이고 false 값은 0으로 간주해서는 안됩니다.

문자 리터럴

문자 리터럴은 작은 따옴표로 묶입니다.

문자 리터럴은 일반 문자 (예 : 'x'), 이스케이프 시퀀스 (예 : '\ t'), ASCII 문자 (예 : '\ x21'), 유니 코드 문자 (예 : '\ u011e') 또는 이름이 지정된 문자로 (예 : '\ ©', '\ ♥', '\ €').

D에는 백 슬래시가 앞에 오면 특별한 의미를 가지며 개행 문자 (\ n) 또는 탭 (\ t)과 같은 표현에 사용되는 특정 문자가 있습니다. 여기에 이러한 이스케이프 시퀀스 코드 목록이 있습니다.

탈출 시퀀스 의미
\\ \ 캐릭터
\ ' ' 캐릭터
\ " " 캐릭터
\? ? 캐릭터
\ㅏ 경고 또는 벨
\비 역행 키이
\에프 양식 피드
\엔 개행
\아르 자형 캐리지 리턴
\티 수평 탭
\V 수직 탭

다음 예는 이스케이프 시퀀스 문자를 거의 보여줍니다.

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"를 사용하여 여러 줄 문자를 나타낼 수 있습니다. 또한 D 언어 문 자체를 나타내는 q {}를 볼 수 있습니다.

연산자는 컴파일러에게 특정 수학적 또는 논리적 조작을 수행하도록 지시하는 기호입니다. D 언어는 내장 연산자가 풍부하며 다음 유형의 연산자를 제공합니다.

  • 산술 연산자
  • 관계 연산자
  • 논리 연산자
  • 비트 연산자
  • 할당 연산자
  • 기타 연산자

이 장에서는 산술, 관계형, 논리, 비트, 할당 및 기타 연산자를 하나씩 설명합니다.

산술 연산자

다음 표는 D 언어에서 지원하는 모든 산술 연산자를 보여줍니다. 변수 가정A 10 개와 가변 B 20을 보유하고-

예시보기

운영자 기술
+ 두 개의 피연산자를 추가합니다. A + B는 30을 제공합니다.
- 첫 번째 피연산자에서 두 번째 피연산자를 뺍니다. A-B는 -10을 제공합니다.
* 두 피연산자를 곱합니다. A * B는 200을 제공합니다.
/ 분자를 denumerator로 나눕니다. B / A는 2를 준다
% 정수 나누기의 나머지를 반환합니다. B % A는 0을 제공합니다.
++ 증분 연산자는 정수 값을 1 씩 증가시킵니다. A ++는 11을 제공합니다.
- 감소 연산자는 정수 값을 1 씩 감소시킵니다. A--주는 9

관계 연산자

다음 표는 D 언어에서 지원하는 모든 관계 연산자를 보여줍니다. 변수 가정A 10 개와 가변 B 20 개를 보유하면-

예시보기

운영자 기술
== 두 피연산자의 값이 같은지 확인하고, 예이면 조건이 참이됩니다. (A == B)는 사실이 아닙니다.
! = 두 피연산자의 값이 같은지 확인하고, 값이 같지 않으면 조건이 참이됩니다. (A! = B)는 사실입니다.
> 왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 큰지 확인하고, 그렇다면 조건이 참이됩니다. (A> B)는 사실이 아닙니다.
< 왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 작은 지 확인하고, 그렇다면 조건이 참이됩니다. (A <B)는 사실입니다.
> = 왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 크거나 같은지 확인하고, 그렇다면 조건이 참이됩니다. (A> = B)는 사실이 아닙니다.
<= 왼쪽 피연산자의 값이 오른쪽 피연산자의 값보다 작거나 같은지 확인하고, 그렇다면 조건이 참이됩니다. (A <= B)는 참입니다.

논리 연산자

다음 표는 D 언어에서 지원하는 모든 논리 연산자를 보여줍니다. 변수 가정A 1 및 변수 보유 B 0을 보유한 다음-

예시보기

운영자 기술
&& 이를 논리 AND 연산자라고합니다. 두 피연산자가 모두 0이 아니면 조건이 참이됩니다. (A && B)는 거짓입니다.
|| 이를 논리 OR 연산자라고합니다. 두 피연산자 중 하나가 0이 아니면 조건이 참이됩니다. (A || B) 사실입니다.
! 이를 논리 NOT 연산자라고합니다. 피연산자의 논리 상태를 반전하는 데 사용합니다. 조건이 참이면 논리 NOT 연산자는 거짓으로 만듭니다. ! (A && B)는 참입니다.

비트 연산자

비트 연산자는 비트에서 작동하고 비트 단위 연산을 수행합니다. &, |, ^의 진리표는 다음과 같습니다.

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

A | B = 0011 1101

A ^ B = 0011 0001

~ A = 1100 0011

D 언어에서 지원하는 비트 연산자는 다음 표에 나열되어 있습니다. 변수 A가 60을 보유하고 변수 B가 13을 보유한다고 가정하면-

예시보기

운영자 기술
& 이진 AND 연산자는 두 피연산자에 모두있는 경우 결과에 비트를 복사합니다. (A & B)는 12, 평균 0000 1100을 제공합니다.
| 이진 OR 연산자는 피연산자 중 하나에 있으면 비트를 복사합니다. (A | B)는 61을 제공합니다. 0011 1101을 의미합니다.
^ 이진 XOR 연산자는 하나의 피연산자에만 설정되어 있지만 둘 다에 설정되지 않은 경우 비트를 복사합니다. (A ^ B)는 49입니다. 의미는 0011 0001입니다.
~ Binary Ones Complement Operator는 단항이며 비트를 '뒤집는'효과가 있습니다. (~ A)는 -61을 제공합니다. 2의 보수 형태로 1100 0011을 의미합니다.
<< 이진 왼쪽 시프트 연산자. 왼쪽 피연산자 값은 오른쪽 피연산자에 지정된 비트 수만큼 왼쪽으로 이동합니다. A << 2는 240을 제공합니다. 1111 0000을 의미합니다.
>> 이진 오른쪽 시프트 연산자. 왼쪽 피연산자 값은 오른쪽 피연산자에 지정된 비트 수만큼 오른쪽으로 이동합니다. A >> 2 15를 제공합니다. 0000 1111을 의미합니다.

할당 연산자

다음 할당 연산자는 D 언어에서 지원됩니다-

예시보기

운영자 기술
= 간단한 할당 연산자입니다. 오른쪽 피연산자의 값을 왼쪽 피연산자에 할당합니다. C = A + B는 A + B의 값을 C에 할당합니다.
+ = 추가 AND 할당 연산자입니다. 왼쪽 피연산자에 오른쪽 피연산자를 추가하고 결과를 왼쪽 피연산자에 할당합니다. C + = A는 C = C + A와 같습니다.
-= 빼기 AND 할당 연산자입니다. 왼쪽 피연산자에서 오른쪽 피연산자를 빼고 결과를 왼쪽 피연산자에 할당합니다. C-= A는 C = C-A와 같습니다.
* = 곱하기 AND 할당 연산자입니다. 오른쪽 피연산자와 왼쪽 피연산자를 곱하고 결과를 왼쪽 피연산자에 할당합니다. C * = A는 C = C * A와 같습니다.
/ = 나누기 AND 할당 연산자입니다. 왼쪽 피연산자를 오른쪽 피연산자로 나누고 결과를 왼쪽 피연산자에 할당합니다. C / = A는 C = C / A와 같습니다.
% = 계수 AND 할당 연산자입니다. 두 개의 피연산자를 사용하여 모듈러스를 취하고 결과를 왼쪽 피연산자에 할당합니다. C % = A는 C = C % A와 동일합니다.
<< = 왼쪽 시프트 AND 할당 연산자입니다. C << = 2는 C = C << 2와 동일합니다.
>> = 오른쪽 시프트 AND 할당 연산자입니다. C >> = 2는 C = C >> 2와 동일합니다.
& = 비트 AND 할당 연산자입니다. C & = 2는 C = C & 2와 동일합니다.
^ = 비트 배타적 OR 및 할당 연산자입니다. C ^ = 2는 C = C ^ 2와 같습니다.
| = 비트 포함 OR 및 할당 연산자입니다. C | = 2는 C = C | 2

기타 연산자-Sizeof 및 삼항

다음을 포함한 다른 중요한 연산자는 거의 없습니다. sizeof? : D Language에서 지원합니다.

예시보기

운영자 기술
sizeof () 변수의 크기를 반환합니다. a는 정수인 sizeof (a)는 4를 반환합니다.
& 변수의 주소를 반환합니다. &ㅏ; 변수의 실제 주소를 제공합니다.
* 변수에 대한 포인터. *ㅏ; 변수에 대한 포인터를 제공합니다.
? : 조건식 조건이 참이면 값 X : 그렇지 않으면 값 Y.

D의 연산자 우선 순위

연산자 우선 순위는 식의 용어 그룹화를 결정합니다. 이는식이 평가되는 방식에 영향을줍니다. 특정 연산자는 다른 연산자보다 우선합니다.

예를 들어 곱셈 연산자는 더하기 연산자보다 우선 순위가 높습니다.

표현을 생각 해보자

x = 7 + 3 * 2.

여기서 x는 20이 아니라 13으로 지정됩니다. 간단한 이유는 연산자 *가 +보다 우선 순위가 높으므로 3 * 2가 먼저 계산 된 다음 결과가 7에 더해지기 때문입니다.

여기에서 우선 순위가 가장 높은 연산자는 테이블 맨 위에 표시되고 가장 낮은 연산자는 맨 아래에 표시됩니다. 식 내에서 우선 순위가 높은 연산자가 먼저 평가됩니다.

예시보기

범주 운영자 연관성
접미사 () []->. ++-- 좌에서 우로
단항 +-! ~ ++--(유형) * & sizeof 오른쪽에서 왼쪽으로
곱셈 * / % 좌에서 우로
첨가물 +- 좌에서 우로
시프트 << >> 좌에서 우로
관계형 <<=>> = 좌에서 우로
평등 ==! = 좌에서 우로
비트 AND & 좌에서 우로
비트 XOR ^ 좌에서 우로
비트 OR | 좌에서 우로
논리적 AND && 좌에서 우로
논리적 OR || 좌에서 우로
가정 어구 ? : 오른쪽에서 왼쪽으로
할당 = + =-= * = / = % = >> = << = & = ^ = | = 오른쪽에서 왼쪽으로
반점 , 좌에서 우로

코드 블록을 여러 번 실행해야하는 상황이있을 수 있습니다. 일반적으로 명령문은 순차적으로 실행됩니다. 함수의 첫 번째 명령문이 먼저 실행되고 두 번째 명령문이 실행되는 식입니다.

프로그래밍 언어는보다 복잡한 실행 경로를 허용하는 다양한 제어 구조를 제공합니다.

루프 문은 문 또는 문 그룹을 여러 번 실행합니다. 주로 프로그래밍 언어에서 사용되는 다음과 같은 일반적인 형태의 루프 문-

D 프로그래밍 언어는 루핑 요구 사항을 처리하기 위해 다음 유형의 루프를 제공합니다. 세부 사항을 확인하려면 다음 링크를 클릭하십시오.

Sr. 아니. 루프 유형 및 설명
1 while 루프

주어진 조건이 참일 때 문 또는 문 그룹을 반복합니다. 루프 본문을 실행하기 전에 조건을 테스트합니다.

2 for 루프

일련의 명령문을 여러 번 실행하고 루프 변수를 관리하는 코드를 축약합니다.

do ... while 루프

while 문과 비슷하지만 루프 본문의 끝에서 조건을 테스트한다는 점이 다릅니다.

4 중첩 된 루프

while, for 또는 do..while 루프 내에서 하나 이상의 루프를 사용할 수 있습니다.

루프 제어문

루프 제어문은 정상적인 순서에서 실행을 변경합니다. 실행이 범위를 벗어나면 해당 범위에서 생성 된 모든 자동 개체가 삭제됩니다.

D는 다음 제어문을 지원합니다-

Sr. 아니. 제어문 및 설명
1 break 문

루프 또는 스위치 문을 종료하고 실행을 루프 또는 스위치 바로 다음 문으로 전송합니다.

2 계속 진술

루프가 본문의 나머지 부분을 건너 뛰고 반복하기 전에 즉시 상태를 다시 테스트하도록합니다.

무한 루프

조건이 거짓이되지 않으면 루프는 무한 루프가됩니다. 그만큼for루프는 전통적으로 이러한 목적으로 사용됩니다. for 루프를 구성하는 세 가지 표현식이 필요하지 않기 때문에 조건식을 비워 두어 무한 루프를 만들 수 있습니다.

import std.stdio;

int main () {

   for( ; ; ) {
      writefln("This loop will run forever.");
   }
   return 0;
}

조건식이 없으면 참으로 간주됩니다. 초기화 및 증분식이있을 수 있지만 D 프로그래머는 일반적으로 for (;;) 구문을 사용하여 무한 루프를 나타냅니다.

NOTE − Ctrl + C 키를 눌러 무한 루프를 종료 할 수 있습니다.

의사 결정 구조에는 실행될 두 세트의 명령문과 함께 평가할 조건이 포함됩니다. 조건이 참이면 한 세트의 명령문이 실행되고 조건이 거짓이면 다른 명령문 세트가 실행됩니다.

다음은 대부분의 프로그래밍 언어에서 발견되는 일반적인 의사 결정 구조의 일반적인 형태입니다.

D 프로그래밍 언어는 non-zeronon-nulltrue, 그리고 둘 중 하나 인 경우 zero 또는 null이면 다음과 같이 가정합니다. false 값.

D 프로그래밍 언어는 다음 유형의 의사 결정문을 제공합니다.

Sr. 아니. 성명 및 설명
1 if 문

if statement 부울 표현식과 하나 이상의 문으로 구성됩니다.

2 if ... else 문

if statement 뒤에 선택 사항이 올 수 있습니다. else statement, 부울 표현식이 false 일 때 실행됩니다.

중첩 된 if 문

하나를 사용할 수 있습니다 if 또는 else if 다른 내부의 진술 if 또는 else if 진술 (들).

4 switch 문

switch 문을 사용하면 변수가 값 목록과 같은지 테스트 할 수 있습니다.

5 중첩 된 switch 문

하나를 사용할 수 있습니다 switch 다른 내부의 진술 switch 진술 (들).

? : D의 연산자

우리는 conditional operator ? : 대체하는 데 사용할 수있는 이전 장에서 if...else진술. 다음과 같은 일반적인 형태가 있습니다.

Exp1 ? Exp2 : Exp3;

Exp1, Exp2 및 Exp3은 표현식입니다. 콜론의 사용과 배치에 주목하십시오.

값은? 식은 다음과 같이 결정됩니다-

  • Exp1이 평가됩니다. 참이면 Exp2가 평가되고 전체?의 값이됩니다. 표현.

  • Exp1이 false이면 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 함수
  • Ref 함수
  • 자동 기능
  • 가변 함수
  • 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 함수는 Exception 클래스에서 파생 된 예외를 throw하지 않습니다. 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 함수를 사용하면 함수가 참조로 반환 할 수 있습니다. 이것은 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

가변 함수

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은 함수의 매개 변수 및 반환 유형 모두에 사용할 수 있습니다. 그것은 mutable, const 및 immutable에 대한 템플릿과 같습니다. 가변성 속성은 매개 변수에서 추론됩니다. 의미, 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 개의 고유 값만 보유 할 수있는 char 유형으로 표시됩니다. 다른 언어의 문자 유형에 익숙하다면 많은 문자 체계의 기호를 지원할만큼 충분히 크지 않다는 것을 이미 알고있을 것입니다.

유형 저장 크기 목적
1 바이트 UTF-8 코드 단위
wchar 2 바이트 UTF-16 코드 단위
dchar 4 바이트 UTF-32 코드 단위 및 유니 코드 코드 포인트

몇 가지 유용한 문자 기능은 다음과 같습니다.

  • isLower − 소문자 여부를 결정합니다.

  • isUpper − 대문자인지 확인합니다.

  • isAlpha − 유니 코드 영숫자 (일반적으로 문자 또는 숫자)인지 확인합니다.

  • 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 프로그래밍은 유니 코드를 지원하기 때문에 유니 코드 문자를 읽으려면 두 번 읽고 두 번 써야 예상 된 결과를 얻을 수 있습니다. 이것은 온라인 컴파일러에서 작동하지 않습니다. 예는 아래와 같습니다.

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는 다음 두 가지 유형의 문자열 표현을 제공합니다.

  • 문자 배열
  • 핵심 언어 문자열

문자 배열

다음과 같이 두 가지 형식 중 하나로 문자 배열을 나타낼 수 있습니다. 첫 번째 양식은 크기를 직접 제공하고 두 번째 양식은 "Good morning"문자열의 쓰기 가능한 복사본을 만드는 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 프로그래밍에서 문자열 연결은 물결표 (~) 기호를 사용합니다.

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

문자열 길이

문자열 길이 (바이트)는 길이 함수를 사용하여 검색 할 수 있습니다.

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'.

문자열 바꾸기

string []을 사용하여 문자열을 바꿀 수 있습니다.

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를 포함하는 string의 하위 문자열 위치에 대한 인덱스 메서드는 다음 예제에서 설명합니다.

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와 같은 개별 변수를 선언하는 대신 숫자와 같은 하나의 배열 변수를 선언하고 numbers [0], numbers [1] 및 ..., numbers [99]를 사용하여 개별 변수. 배열의 특정 요소는 인덱스로 액세스됩니다.

모든 어레이는 연속적인 메모리 위치로 구성됩니다. 가장 낮은 주소는 첫 번째 요소에 해당하고 가장 높은 주소는 마지막 요소에 해당합니다.

배열 선언

D 프로그래밍 언어로 배열을 선언하기 위해 프로그래머는 다음과 같이 배열에 필요한 요소의 유형과 요소의 수를 지정합니다.

type arrayName [ arraySize ];

이것을 1 차원 배열이라고합니다. arraySize는 0보다 정수 상수에 커야하고 형식은 유효한 D 프로그래밍 언어 데이터 타입 일 수있다. 예를 들어, double 유형의 balance 라는 10 개 요소 배열을 선언 하려면 다음 명령문을 사용하십시오.

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 값을 할당합니다. 모든 배열은 기본 인덱스라고도하는 첫 번째 요소의 인덱스로 0을 갖기 때문에 4 번째 인덱스가있는 배열은 5 번째, 즉 마지막 요소가됩니다. 다음 그림 표현은 위에서 논의한 동일한 배열을 보여줍니다.

배열 요소에 액세스

배열 이름을 인덱싱하여 요소에 액세스합니다. 이는 배열 이름 뒤에 대괄호 안에 요소의 인덱스를 배치하여 수행됩니다. 예를 들면-

double salary = balance[9];

위의 명령문은 배열에서 10 번째 요소를 가져와 변수 salary에 값을 할당합니다 . 다음 예제는 선언, 할당 및 액세스 배열을 구현합니다-

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;

배열 속성

다음은 배열의 속성입니다.

Sr. 아니. 속성 및 설명
1

.init

정적 배열은 리터럴의 각 요소가 배열 요소 유형의 .init 속성 인 배열 리터럴을 반환합니다.

2

.sizeof

정적 배열은 배열 길이에 배열 요소 당 바이트 수를 곱한 값을 반환하는 반면 동적 배열은 동적 배열 참조의 크기를 반환합니다. 이는 32 비트 빌드에서 8 개, 64 비트 빌드에서 16 개입니다.

.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];

다음 선언은 3 차원 5를 만듭니다. 10. 4 개의 정수 배열 −

int threedim[5][10][4];

D의 2 차원 배열

다차원 배열의 가장 간단한 형태는 2 차원 배열입니다. 2 차원 배열은 본질적으로 1 차원 배열의 목록입니다. 크기 [x, y]의 2 차원 정수 배열을 선언하려면 다음과 같이 구문을 작성합니다.

type arrayName [ x ][ y ];

어디 type 유효한 D 프로그래밍 데이터 유형이 될 수 있으며 arrayName 유효한 D 프로그래밍 식별자가됩니다.

여기서 type 은 유효한 D 프로그래밍 데이터 유형이고 arrayName 은 유효한 D 프로그래밍 식별자입니다.

2 차원 배열은 x 개의 행과 y 개의 열이있는 테이블로 생각할 수 있습니다. 2 차원 배열a 3 개의 행과 4 개의 열을 포함하는 것은 아래와 같이 표시 될 수 있습니다

따라서 배열의 모든 요소 a 다음과 같이 요소로 식별됩니다. a[ i ][ j ], 어디 a 배열의 이름이고 ij a의 각 요소를 고유하게 식별하는 아래 첨자입니다.

2 차원 배열 초기화

다차원 배열은 각 행에 대괄호 값을 지정하여 초기화 할 수 있습니다. 다음 배열에는 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 차원 배열 요소에 액세스

2 차원 배열의 요소는 아래 첨자를 사용하여 액세스되며 배열의 행 인덱스와 열 인덱스를 의미합니다. 예를 들면

int val = a[2][3];

위의 문장은 배열의 3 번째 행에서 4 번째 요소를 취합니다. 위의 다이어그램에서 확인할 수 있습니다.

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]

어레이 복사

또한 copying array를 사용합니다. 배열 복사에 대한 간단한 예가 아래에 나와 있습니다.

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]

연관 배열에는 정수일 필요는 없으며 드물게 채워질 수있는 인덱스가 있습니다. 연관 배열의 색인을Key, 그 유형은 KeyType.

연관 배열은 배열 선언의 [] 내에 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"] 
[]

연관 배열 초기화

연관 배열의 간단한 초기화는 다음과 같습니다.

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

연관 배열의 속성

다음은 연관 배열의 속성입니다.

Sr. 아니. 속성 및 설명
1

.sizeof

연관 배열에 대한 참조 크기를 반환합니다. 32 비트 빌드에서는 4이고 64 비트 빌드에서는 8입니다.

2

.length

연관 배열의 값 수를 반환합니다. 동적 배열과 달리 읽기 전용입니다.

.dup

동일한 크기의 새 연관 배열을 만들고 연관 배열의 내용을 여기에 복사합니다.

4

.keys

연관 배열의 키인 동적 배열을 반환합니다.

5

.values

연관 배열의 값인 동적 배열을 반환합니다.

6

.rehash

조회가 더 효율적이되도록 연관 배열을 제자리에 재구성합니다. 예를 들어, 프로그램이 기호 테이블로드를 완료하고 이제 빠른 조회가 필요한 경우 rehash가 효과적입니다. 재구성 된 배열에 대한 참조를 반환합니다.

7

.byKey()

연관 배열의 키를 반복하는 ForeachStatement에 대한 Aggregate로 사용하기에 적합한 대리자를 반환합니다.

8

.byValue()

연관 배열의 값을 반복하는 ForeachStatement에 대한 Aggregate로 사용하기에 적합한 대리자를 반환합니다.

9

.get(Key key, lazy Value defVal)

키를 찾습니다. 존재하는 경우 해당 값을 반환하고 else는 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

정수, 부동 소수점, 문자 등 모든 포인터 값의 실제 데이터 유형은 메모리 주소를 나타내는 긴 16 진수와 동일합니다. 데이터 유형이 다른 포인터 간의 유일한 차이점은 포인터가 가리키는 변수 또는 상수의 데이터 유형입니다.

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 바늘.

널 포인터는 iostream을 포함하여 여러 표준 라이브러리에 정의 된 값이 0 인 상수입니다. 다음 프로그램을 고려하십시오-

import std.stdio;

void main () { 
   int  *ptr = null; 
   writeln("The value of ptr is " , ptr) ;  
}

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

The value of ptr is null

대부분의 운영 체제에서는 해당 메모리가 운영 체제에 의해 예약되어 있기 때문에 프로그램이 주소 0의 메모리에 액세스 할 수 없습니다. 하나; 메모리 주소 0은 특별한 의미가 있습니다. 포인터가 액세스 가능한 메모리 위치를 가리 키도록 의도되지 않았 음을 나타냅니다.

관례 적으로 포인터에 널 (영) 값이 포함되어 있으면 아무 것도 가리키는 것으로 간주되지 않습니다. 널 포인터를 확인하려면 다음과 같이 if 문을 사용할 수 있습니다.

if(ptr)     // succeeds if p is not null 
if(!ptr)    // succeeds if p is null

따라서 사용하지 않는 모든 포인터에 널값이 주어지고 널 포인터의 사용을 피하면 초기화되지 않은 포인터의 우발적 오용을 피할 수 있습니다. 많은 경우 초기화되지 않은 변수는 일부 정크 값을 보유하고 프로그램을 디버그하기가 어려워집니다.

포인터 산술

포인터에 사용할 수있는 산술 연산자는 ++,-, + 및-네 가지입니다.

포인터 산술을 이해하기 위해 다음과 같은 정수 포인터를 고려해 보겠습니다. ptr, 주소 1000을 가리 킵니다. 32 비트 정수를 가정하고 포인터에 대해 다음과 같은 산술 연산을 수행해 보겠습니다.

ptr++

다음 ptrptr이 증가 할 때마다 다음 정수를 가리 키기 때문에 1004 위치를 가리 킵니다. 이 작업은 메모리 위치의 실제 값에 영향을주지 않고 포인터를 다음 메모리 위치로 이동합니다.

만약 ptr 주소가 1000 인 문자를 가리키면 다음 문자를 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]와 0 번째 요소를 설정하는 데 사용되는 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배열 밸런스의 첫 번째 요소 주소 인 & balance [0]에 대한 포인터입니다. 따라서 다음 프로그램 조각은p 첫 번째 요소의 주소 balance

double *p; 
double balance[10]; 
 
p = balance;

배열 이름을 상수 포인터로 사용하는 것은 합법적이며 그 반대의 경우도 마찬가지입니다. 따라서 * (잔액 + 4)는 balance [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

튜플은 여러 값을 단일 객체로 결합하는 데 사용됩니다. 튜플은 일련의 요소를 포함합니다. 요소는 유형, 표현식 또는 별칭 일 수 있습니다. 튜플의 수와 요소는 컴파일 타임에 고정되며 런타임에 변경할 수 없습니다.

튜플에는 구조체와 배열의 특성이 있습니다. 튜플 요소는 구조체와 같은 다른 유형일 수 있습니다. 요소는 배열과 같은 인덱싱을 통해 액세스 할 수 있습니다. std.typecons 모듈의 Tuple 템플릿에 의해 라이브러리 기능으로 구현됩니다. Tuple은 일부 작업을 위해 std.typetuple 모듈의 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 템플릿을 사용하여 튜플을 직접 구성 할 수도 있습니다. 각 멤버의 유형과 이름은 두 개의 연속 템플릿 매개 변수로 지정됩니다. 템플릿을 사용하여 생성 할 때 속성별로 멤버에 액세스 할 수 있습니다.

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의 멤버는 .expand 속성 또는 슬라이스로 확장 할 수 있습니다. 이 확장 / 슬라이스 값은 함수 인수 목록으로 전달할 수 있습니다. 아래에 예가 나와 있습니다.

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

유형 Tuple

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
  • 도서 ID

구조 정의

구조를 정의하려면 다음을 사용해야합니다. struct성명서. struct 문은 프로그램에 대해 둘 이상의 멤버를 사용하여 새 데이터 유형을 정의합니다. struct 문의 형식은 다음과 같습니다.

struct [structure tag] { 
   member definition; 
   member definition; 
   ... 
   member definition; 
} [one or more structure variables];

그만큼 structure tag선택적이며 각 멤버 정의는 int i와 같은 일반 변수 정의입니다. 또는 float f; 또는 다른 유효한 변수 정의. 세미콜론 앞의 구조 정의 끝에서 선택적인 구조 변수를 하나 이상 지정할 수 있습니다. 다음은 Books 구조를 선언하는 방법입니다.

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

정적 멤버

정적 변수는 한 번만 초기화됩니다. 예를 들어 책의 고유 ID를 가지려면 book_id를 정적으로 만들고 책 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

unionD에서 사용 가능한 특수 데이터 유형으로 동일한 메모리 위치에 다른 데이터 유형을 저장할 수 있습니다. 여러 멤버로 유니온을 정의 할 수 있지만 주어진 시간에 하나의 멤버 만 값을 포함 할 수 있습니다. 공용체는 여러 목적을 위해 동일한 메모리 위치를 사용하는 효율적인 방법을 제공합니다.

D에서 Union 정의

공용체를 정의하려면 구조를 정의 할 때와 매우 유사한 방식으로 공용체 문을 사용해야합니다. union 문은 프로그램에 대해 둘 이상의 멤버를 사용하여 새 데이터 유형을 정의합니다. 통합 문의 형식은 다음과 같습니다.

union [union tag] { 
   member definition; 
   member definition; 
   ... 
   member definition; 
} [one or more union variables];

그만큼 union tag선택적이며 각 멤버 정의는 int i와 같은 일반 변수 정의입니다. 또는 float f; 또는 다른 유효한 변수 정의. 공용체 정의의 끝에서 마지막 세미콜론 앞에 하나 이상의 공용체 변수를 지정할 수 있지만 선택 사항입니다. 다음은 세 멤버가있는 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 (.). 멤버 액세스 연산자는 유니온 변수 이름과 액세스하려는 유니온 멤버 사이의 마침표로 코딩됩니다. union 키워드를 사용하여 공용체 유형의 변수를 정의합니다.

다음 예제는 union의 사용법을 설명합니다.

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

여기에서 if 변수에 할당 된 최종 값이 메모리 위치를 차지했기 때문에 union의 구성원이 손상되었으며, 이것이 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

여기에서는 한 번에 하나의 멤버를 사용하기 때문에 모든 멤버가 매우 잘 인쇄되고 있습니다.

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

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

숫자 범위

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

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

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

포보스 산맥

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

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

  • InputRange
  • ForwardRange
  • BidirectionalRange
  • RandomAccessRange
  • OutputRange

InputRange

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

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

  • front − 범위 시작 부분에있는 요소에 대한 액세스를 제공합니다.

  • popFront() − 첫 번째 요소를 제거하여 처음부터 범위를 축소합니다.

import std.stdio; 
import std.string; 
 
struct Student { 
   string name; 
   int number; 
   
   string toString() const { 
      return format("%s(%s)", name, number); 
   } 
}
  
struct School { 
   Student[] students; 
}
struct StudentRange {
   Student[] students; 
   
   this(School school) { 
      this.students = school.students; 
   } 
   @property bool empty() const { 
      return students.length == 0; 
   } 
   @property ref Student front() { 
      return students[0]; 
   } 
   void popFront() { 
      students = students[1 .. $]; 
   } 
}

void main() { 
   auto school = School([ Student("Raj", 1), Student("John", 2), Student("Ram", 3)]);
   auto range = StudentRange(school); 
   writeln(range);  
   
   writeln(school.students.length);
   
   writeln(range.front); 
   
   range.popFront;  
   
   writeln(range.empty); 
   writeln(range); 
}

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

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

ForwardRange

ForwardRange는 InputRange의 다른 세 함수에서 저장 멤버 함수 부분을 추가로 필요로하며 저장 함수가 호출 될 때 범위의 복사본을 반환합니다.

import std.array; 
import std.stdio; 
import std.string; 
import std.range;

struct FibonacciSeries { 
   int first = 0; 
   int second = 1; 
   enum empty = false;   //  infinite range  
   
   @property int front() const { 
      return first; 
   } 
   void popFront() { 
      int third = first + second; 
      first = second; 
      second = third; 
   }
   @property FibonacciSeries save() const { 
      return this; 
   } 
}
  
void report(T)(const dchar[] title, const ref T range) {
   writefln("%s: %s", title, range.take(5)); 
} 

void main() { 
   auto range = FibonacciSeries(); 
   report("Original range", range);
   
   range.popFrontN(2); 
   report("After removing two elements", range); 
   
   auto theCopy = range.save; 
   report("The copy", theCopy);
   
   range.popFrontN(3); 
   report("After removing three more elements", range); 
   report("The copy", theCopy); 
}

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

Original range: [0, 1, 1, 2, 3] 
After removing two elements: [1, 2, 3, 5, 8] 
The copy: [1, 2, 3, 5, 8] 
After removing three more elements: [5, 8, 13, 21, 34] 
The copy: [1, 2, 3, 5, 8]

양방향 범위

BidirectionalRange는 ForwardRange의 멤버 함수에 대해 두 개의 멤버 함수를 추가로 제공합니다. 전면과 유사한 후면 기능은 범위의 마지막 요소에 대한 액세스를 제공합니다. popBack 함수는 popFront 함수와 유사하며 범위에서 마지막 요소를 제거합니다.

import std.array; 
import std.stdio; 
import std.string; 

struct Reversed { 
   int[] range; 
   
   this(int[] range) { 
      this.range = range; 
   } 
   @property bool empty() const { 
      return range.empty; 
   }
   @property int front() const { 
      return range.back;  //  reverse 
   }
   @property int back() const { 
      return range.front; // reverse 
   } 
   void popFront() { 
      range.popBack(); 
   }
   void popBack() { 
      range.popFront(); 
   } 
} 
 
void main() { 
   writeln(Reversed([ 1, 2, 3])); 
}

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

[3, 2, 1]

무한 RandomAccessRange

ForwardRange와 비교할 때 opIndex ()가 추가로 필요합니다. 또한 컴파일 타임에 false로 알려진 빈 함수의 값입니다. 사각형 범위로 간단한 예가 아래에 나와 있습니다.

import std.array; 
import std.stdio; 
import std.string; 
import std.range; 
import std.algorithm; 

class SquaresRange { 
   int first;  
   this(int first = 0) { 
      this.first = first; 
   }
   enum empty = false; 
   @property int front() const { 
      return opIndex(0); 
   }
   void popFront() { 
      ++first; 
   }
   @property SquaresRange save() const { 
      return new SquaresRange(first); 
   }
   int opIndex(size_t index) const { 
      /* This function operates at constant time */ 
      immutable integerValue = first + cast(int)index; 
      return integerValue * integerValue; 
   } 
}
  
bool are_lastTwoDigitsSame(int value) { 
   /* Must have at least two digits */ 
   if (value < 10) { 
      return false; 
   } 
   
   /* Last two digits must be divisible by 11 */ 
   immutable lastTwoDigits = value % 100; 
   return (lastTwoDigits % 11) == 0; 
} 
 
void main() { 
   auto squares = new SquaresRange(); 
   
   writeln(squares[5]);
   
   writeln(squares[10]); 
   
   squares.popFrontN(5); 
   writeln(squares[0]); 
   
   writeln(squares.take(50).filter!are_lastTwoDigitsSame); 
}

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

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

유한 RandomAccessRange

양방향 범위와 비교할 때 opIndex () 및 길이가 추가로 필요합니다. 이것은 이전에 사용 된 피보나치 시리즈와 제곱 범위 예제를 사용하는 자세한 예제의 도움으로 설명됩니다. 이 예제는 일반 D 컴파일러에서는 잘 작동하지만 온라인 컴파일러에서는 작동하지 않습니다.

import std.array; 
import std.stdio; 
import std.string; 
import std.range; 
import std.algorithm; 

struct FibonacciSeries { 
   int first = 0; 
   int second = 1; 
   enum empty = false;   //  infinite range  
   
   @property int front() const { 
      return first;
   }
   void popFront() { 
      int third = first + second; 
      first = second; 
      second = third; 
   }
   @property FibonacciSeries save() const { 
      return this; 
   } 
}
  
void report(T)(const dchar[] title, const ref T range) { 
   writefln("%40s: %s", title, range.take(5)); 
}
  
class SquaresRange { 
   int first;  
   this(int first = 0) { 
      this.first = first; 
   } 
   enum empty = false; 
   @property int front() const { 
      return opIndex(0); 
   }
   void popFront() { 
      ++first; 
   }
   @property SquaresRange save() const { 
      return new SquaresRange(first); 
   } 
   int opIndex(size_t index) const { 
      /* This function operates at constant time */ 
      immutable integerValue = first + cast(int)index; 
      return integerValue * integerValue; 
   } 
}
  
bool are_lastTwoDigitsSame(int value) { 
   /* Must have at least two digits */ 
   if (value < 10) { 
      return false; 
   }
   
   /* Last two digits must be divisible by 11 */ 
   immutable lastTwoDigits = value % 100; 
   return (lastTwoDigits % 11) == 0; 
}
  
struct Together { 
   const(int)[][] slices;  
   this(const(int)[][] slices ...) { 
      this.slices = slices.dup;  
      clearFront(); 
      clearBack(); 
   }
   private void clearFront() { 
      while (!slices.empty && slices.front.empty) { 
         slices.popFront(); 
      } 
   } 
   private void clearBack() { 
      while (!slices.empty && slices.back.empty) { 
         slices.popBack(); 
      } 
   }
   @property bool empty() const { 
      return slices.empty; 
   } 
   @property int front() const { 
      return slices.front.front; 
   }
   void popFront() { 
      slices.front.popFront(); 
      clearFront(); 
   }
   @property Together save() const { 
      return Together(slices.dup); 
   } 
   @property int back() const { 
      return slices.back.back; 
   } 
   void popBack() { 
      slices.back.popBack(); 
      clearBack(); 
   }
   @property size_t length() const { 
      return reduce!((a, b) => a + b.length)(size_t.init, slices); 
   }
   int opIndex(size_t index) const { 
      /* Save the index for the error message */ 
      immutable originalIndex = index;  

      foreach (slice; slices) { 
         if (slice.length > index) { 
            return slice[index];  
         } else { 
            index -= slice.length; 
         } 
      } 
      throw new Exception( 
         format("Invalid index: %s (length: %s)", originalIndex, this.length));
   } 
}
void main() { 
   auto range = Together(FibonacciSeries().take(10).array, [ 777, 888 ],
      (new SquaresRange()).take(5).array); 
   writeln(range.save); 
}

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

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

OutputRange

OutputRange는 문자를 stdout에 보내는 것과 유사한 스트리밍 된 요소 출력을 나타냅니다. OutputRange는 put (range, element) 작업을 지원해야합니다. put ()은 std.range 모듈에 정의 된 함수입니다. 컴파일 타임에 범위 및 요소의 기능을 결정하고 요소를 출력하는 데 가장 적합한 방법을 사용합니다. 간단한 예가 아래에 나와 있습니다.

import std.algorithm; 
import std.stdio; 
 
struct MultiFile { 
   string delimiter;
   File[] files;
   
   this(string delimiter, string[] fileNames ...) { 
      this.delimiter = delimiter; 

      /* stdout is always included */ 
      this.files ~= stdout; 

      /* A File object for each file name */ 
      foreach (fileName; fileNames) { 
         this.files ~= File(fileName, "w"); 
      } 
   }
   void put(T)(T element) { 
      foreach (file; files) { 
         file.write(element, delimiter); 
      } 
   }
}
void main() { 
   auto output = MultiFile("\n", "output_0", "output_1"); 
   copy([ 1, 2, 3], output);  
   copy([ "red", "blue", "green" ], output); 
}

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

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

이름이 참조하는 별칭은 기존 이름의 대체 이름을 제공합니다. 별칭에 대한 구문은 다음과 같습니다.

alias new_name = existing_name;

다음은 이전 형식 예제를 참조하는 경우에 대비 한 이전 구문입니다. 이것의 사용을 강력히 권장하지 않습니다.

alias existing_name new_name;

식과 함께 사용되는 또 다른 구문이 있으며 식 대신 별칭 이름을 직접 사용할 수있는 아래에 나와 있습니다.

alias expression alias_name ;

아시다시피 typedef는 새로운 유형을 생성하는 기능을 추가합니다. Alias는 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)를 사용하는 대신 별칭 이름 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를 사용하고 다른 사람이 double을 사용하는 경우가 될 수 있습니다. 이러한 충돌을 피하기 위해 우리는 종종 데이터 유형에 유형을 사용합니다. 간단한 예가 아래에 나와 있습니다.

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와 이것이 멤버 변수 또는 멤버 함수의 양쪽에 기록 된 것입니다.

alias member_variable_or_member_function this;

Example

An example is shown below to show the power of alias 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)); 
}

In the above example, you can see that the struct rectangle is converted to double value with the help of alias this method.

When the above code is compiled and executed, it produces the following result −

30

Mixins are structs that allow mixing of the generated code into the source code. Mixins can be of the following types −

  • String Mixins
  • Template Mixins
  • Mixin name spaces

String Mixins

D has the capability to insert code as string as long as that string is known at compile time. The syntax of string mixins is shown below −

mixin (compile_time_generated_string)

Example

A simple example for string mixins is shown below.

import std.stdio; 
 
void main() { 
   mixin(`writeln("Hello World!");`); 
}

When the above code is compiled and executed, it produces the following result −

Hello World!

Here is another example where we can pass the string in compile time so that mixins can use the functions to reuse code. It is shown below.

import std.stdio;

string print(string s) {
   return `writeln("` ~ s ~ `");`; 
}
  
void main() { 
   mixin (print("str1")); 
   mixin (print("str2")); 
}

When the above code is compiled and executed, it produces the following result −

str1
str2

Template Mixins

D templates define common code patterns, for the compiler to generate actual instances from that pattern. The templates can generate functions, structs, unions, classes, interfaces, and any other legal D code. The syntax of template mixins is as shown below.

mixin a_template!(template_parameters)

A simple example for string mixins is shown below where we create a template with class Department and a mixin instantiating a template and hence making the the functions setName and printNames available to the structure college.

Example

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

When the above code is compiled and executed, it produces the following result −

The names 
0 : name1 
1 : name2

Mixin Name Spaces

Mixin name spaces are used to avoid ambiguities in template mixins. For example, there can be two variables, one defined explicitly in main and the other is mixed in. When a mixed-in name is the same as a name that is in the surrounding scope, then the name that is in the surrounding scope gets used. This example is shown below.

Example

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

When the above code is compiled and executed, it produces the following result −

name 1 
name 2

Modules are the building blocks of D. They are based on a simple concept. Every source file is a module. Accordingly, the single files in which we write the programs are individual modules. By default, the name of a module is the same as its filename without the .d extension.

When explicitly specified, the name of the module is defined by the module keyword, which must appear as the first non-comment line in the source file. For example, assume that the name of a source file is "employee.d". Then the name of the module is specified by the module keyword followed by employee. It is as shown below.

module employee;

class Employee {
   // Class definition goes here. 
}

The module line is optional. When not specified, it is the same as the file name without the .d extension.

File and Module Names

D supports Unicode in source code and module names. However, the Unicode support of file systems vary. For example, although most Linux file systems support Unicode, the file names in Windows file systems may not distinguish between lower and upper case letters. Additionally, most file systems limit the characters that can be used in file and directory names. For portability reasons, I recommend that you use only lower case ASCII letters in file names. For example, "employee.d" would be a suitable file name for a class named employee.

Accordingly, the name of the module would consist of ASCII letters as well −

module employee;  // Module name consisting of ASCII letters 

class eëmployëë { }

D Packages

A combination of related modules are called a package. D packages are a simple concept as well: The source files that are inside the same directory are considered to belong to the same package. The name of the directory becomes the name of the package, which must also be specified as the first parts of module names.

For example, if "employee.d" and "office.d" are inside the directory "company", then specifying the directory name along with the module name makes them be a part of the same package −

module company.employee; 
 
class Employee { }

Similarly, for the office module −

module company.office; 
 
class Office { }

Since package names correspond to directory names, the package names of modules that are deeper than one directory level must reflect that hierarchy. For example, if the "company" directory included a "branch" directory, the name of a module inside that directory would include branch as well.

module company.branch.employee;

Using Modules in Programs

The import keyword, which we have been using in almost every program so far, is for introducing a module to the current module −

import std.stdio;

The module name may contain the package name as well. For example, the std. part above indicates that stdio is a module that is a part of the std package.

Locations of Modules

The compiler finds the module files by converting the package and module names directly to directory and file names.

For example, the two modules employee and office would be located as "company/employee.d" and "animal/office.d", respectively (or "company\employee.d" and "company\office.d", depending on the file system) for company.employee and company.office.

Long and Short Module Names

The names that are used in the program may be spelled out with the module and package names as shown below.

import company.employee; 
auto employee0 = Employee(); 
auto employee1 = company.employee.Employee();

The long names are normally not needed but sometimes there are name conflicts. For example, when referring to a name that appears in more than one module, the compiler cannot decide which one is meant. The following program is spelling out the long names to distinguish between two separate employee structs that are defined in two separate modules: company and college..

The first employee module in folder company is as follows.

module company.employee; 
 
import std.stdio;
  
class Employee {
   public: 
      string str; 

   void print() {
      writeln("Company Employee: ",str); 
   } 
}

The second employee module in folder college is as follows.

module college.employee;
  
import std.stdio;  

class Employee {
   public: 
      string str;
	
   void print() {
      writeln("College Employee: ",str); 
   } 
}

The main module in hello.d should be saved in the folder which contains the college and company folders. It is as follows.

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

The import keyword is not sufficient to make modules become parts of the program. It simply makes available the features of a module inside the current module. That much is needed only to compile the code.

For the program above to be built, "company/employee.d" and "college/employee.d" must also be specified on the compilation line.

When the above code is compiled and executed, it produces the following result −

$ dmd hello.d company/employee.d college/employee.d -ofhello.amx 
$ ./hello.amx 
Company Employee: emp1 
College Employee: emp2

Templates are the foundation of generic programming, which involve writing code in a way that is independent of any particular type.

A template is a blueprint or formula for creating a generic class or a function.

Templates are the feature that allows describing the code as a pattern, for the compiler to generate program code automatically. Parts of the source code may be left to the compiler to be filled in until that part is actually used in the program. The compiler fills in the missing parts.

Function Template

Defining a function as a template is leaving one or more of the types that it uses as unspecified, to be deduced later by the compiler. The types that are being left unspecified are defined within the template parameter list, which comes between the name of the function and the function parameter list. For that reason, function templates have two parameter lists −

  • template parameter list
  • function parameter list
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의 불변 변수 유형

변경할 수없는 정의 변수에는 세 가지 유형이 있습니다.

  • 열거 형 상수
  • 불변 변수
  • const 변수

D의 열거 형 상수

열거 형 상수를 사용하면 상수 값을 의미있는 이름에 연결할 수 있습니다. 간단한 예가 아래에 나와 있습니다.

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

위의 예에서 데이터 유형을 다른 변수로 전송하고 인쇄하는 동안 stringof를 사용하는 방법을 볼 수 있습니다.

D의 상수 변수

상수 변수는 불변과 유사하게 수정할 수 없습니다. 불변 변수는 불변 매개 변수로 함수에 전달 될 수 있으므로 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 모듈 의 File 구조체 로 표시됩니다 . 파일은 바이트 시퀀스를 나타내며 텍스트 파일이든 바이너리 파일이든 상관 없습니다.

D 프로그래밍 언어는 저장 장치의 파일을 처리하기위한 저수준 (OS 수준) 호출뿐만 아니라 고수준 기능에 대한 액세스를 제공합니다.

D에서 파일 열기

표준 입력 및 출력 스트림 stdin 및 stdout은 프로그램 실행이 시작될 때 이미 열려 있습니다. 사용할 준비가되었습니다. 반면에 파일 이름과 필요한 액세스 권한을 지정하여 먼저 파일을 열어야합니다.

File file = File(filepath, "mode");

여기, filename 파일 이름을 지정하고 액세스하는 데 사용하는 문자열 리터럴입니다. mode 다음 값 중 하나를 가질 수 있습니다.

Sr. 아니. 모드 및 설명
1

r

읽기 목적으로 기존 텍스트 파일을 엽니 다.

2

w

쓰기 위해 텍스트 파일을 엽니 다. 존재하지 않는 경우 새 파일이 생성됩니다. 여기에서 프로그램은 파일의 처음부터 내용을 쓰기 시작합니다.

a

추가 모드로 쓰기 위해 텍스트 파일을 엽니 다. 존재하지 않으면 새 파일이 생성됩니다. 여기서 프로그램은 기존 파일 콘텐츠에 콘텐츠를 추가하기 시작합니다.

4

r+

읽고 쓰기 위해 텍스트 파일을 엽니 다.

5

w+

읽고 쓰기 위해 텍스트 파일을 엽니 다. 먼저 파일이 있으면 길이를 0으로 자릅니다. 그렇지 않으면 파일이 없으면 만듭니다.

6

a+

읽고 쓰기 위해 텍스트 파일을 엽니 다. 파일이없는 경우 생성합니다. 읽기는 처음부터 시작되지만 쓰기는 추가 만 가능합니다.

D에서 파일 닫기

파일을 닫으려면 file에 파일 참조가있는 file.close () 함수를 사용하십시오. 이 함수의 프로토 타입은 다음과 같습니다.

file.close();

프로그램에서 연 파일은 프로그램이 해당 파일 사용을 마치면 닫아야합니다. 대부분의 경우 파일을 명시 적으로 닫을 필요는 없습니다. File 객체가 종료되면 자동으로 닫힙니다.

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 -

위의 예에서 writeln이 실행되면 다음 줄로 이동하므로 빈 세 번째 줄을 볼 수 있습니다.

동시성은 프로그램이 한 번에 여러 스레드에서 실행되도록합니다. 동시 작업 프로그램의 예로는 동시에 많은 클라이언트에 응답하는 웹 서버가 있습니다. 동시성은 메시지 전달과 함께 쉽지만 데이터 공유를 기반으로하는 경우 쓰기가 매우 어렵습니다.

스레드간에 전달되는 데이터를 메시지라고합니다. 메시지는 모든 유형과 여러 변수로 구성 될 수 있습니다. 모든 스레드에는 메시지 수신자를 지정하는 데 사용되는 ID가 있습니다. 다른 스레드를 시작하는 모든 스레드를 새 스레드의 소유자라고합니다.

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의 모듈 수준에서 변수를 사용할 세계적으로는 항상 현재의 thread의 ID입니다. 또한 spawn이 호출 될 때 threadId를받을 수 있습니다. 아래에 예가 나와 있습니다.

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 () 함수는 메시지를 보내고 receiveOnly () 함수는 특정 유형의 메시지를 기다립니다. prioritySend (), receive () 및 receiveTimeout ()이라는 다른 함수가 있습니다. 이에 대해서는 나중에 설명합니다.

다음 프로그램의 소유자는 작업자에게 int 유형의 메시지를 보내고 double 유형의 작업자로부터 메시지를 기다립니다. 스레드는 소유자가 음의 정수를 보낼 때까지 메시지를 계속해서 앞뒤로 보냅니다. 아래에 예가 나와 있습니다.

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에서 Wait로 전달되는 메시지

다음은 wait 메시지가 전달되는 간단한 예입니다.

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 예외는 0으로 나누려는 시도와 같이 프로그램이 실행되는 동안 발생하는 예외적 인 상황에 대한 응답입니다.

예외는 프로그램의 한 부분에서 다른 부분으로 제어를 전송하는 방법을 제공합니다. D 예외 처리는 세 개의 키워드를 기반으로합니다.try, catch, 및 throw.

  • throw− 문제가 발생하면 프로그램에서 예외가 발생합니다. 이것은throw 예어.

  • catch− 프로그램은 문제를 처리하려는 프로그램의 위치에서 예외 처리기로 예외를 포착합니다. 그만큼catch 키워드는 예외 포착을 나타냅니다.

  • try − A tryblock은 특정 예외가 활성화 된 코드 블록을 식별합니다. 그 뒤에 하나 이상의 catch 블록이 있습니다.

블록이 예외를 발생 시킨다고 가정하면 메소드는 다음 조합을 사용하여 예외를 포착합니다. trycatch키워드. 예외를 생성 할 수있는 코드 주위에 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 block은 서로 다른 상황에서 둘 이상의 예외를 발생시킵니다.

D에서 예외 발생

예외는 다음을 사용하여 코드 블록 내 어디에서나 발생할 수 있습니다. throw진술. throw 문의 피연산자는 예외의 유형을 결정하고 모든 표현식이 될 수 있으며 표현식의 결과 유형에 따라 throw되는 예외 유형이 결정됩니다.

다음 예제는 0으로 나누기 조건이 발생하면 예외를 throw합니다.

double division(int a, int b) { 
   if( b == 0 ) { 
      throw new Exception("Division by zero condition!"); 
   }
   
   return (a/b); 
}

D에서 예외 잡기

그만큼 catch 다음 블록 try블록은 모든 예외를 포착합니다. 포착하려는 예외 유형을 지정할 수 있으며 이는 catch 키워드 다음에 괄호 안에 표시되는 예외 선언에 의해 결정됩니다.

try { 
   // protected code 
} 

catch( ExceptionName e ) { 
   // code to handle ExceptionName exception 
}

위의 코드는 다음과 같은 예외를 포착합니다. ExceptionName유형. catch 블록이 try 블록에서 발생하는 모든 유형의 예외를 처리하도록 지정하려면 다음과 같이 예외 선언을 둘러싸는 괄호 사이에 줄임표, ...를 넣어야합니다.

try { 
   // protected code 
} 

catch(...) { 
   // code to handle any exception 
}

다음 예제에서는 0으로 나누기 예외가 발생합니다. 캐치 블록에 걸려 있습니다.

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의 바디 블록

본문 블록에는 실행의 실제 기능 코드가 포함되어 있습니다. in 및 out 블록은 선택 사항이며 본문 블록은 필수입니다. 간단한 구문은 다음과 같습니다.

return_type function_name(function_params) 
in { 
   // in block 
} 

out (result) { 
   // in block 
}
 
body { 
   // actual function block 
}

D의 사전 조건에 대한 블록 내

In block은 입력 매개 변수가 허용 가능한지 그리고 코드에서 처리 할 수있는 범위 내에 있는지 확인하는 간단한 사전 조건을위한 것입니다. in 블록의 이점은 모든 입력 조건을 함께 유지하고 함수의 실제 본문과 분리 할 수 ​​있다는 것입니다. 최소 길이에 대한 암호 유효성 검사를위한 간단한 전제 조건은 다음과 같습니다.

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 블록은 함수의 반환 값을 처리합니다. 반환 값이 예상 범위에 있는지 확인합니다. in과 out을 모두 포함하는 간단한 예는 월, 연도를 결합 된 십진 연령 형식으로 변환하는 다음과 같습니다.

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

조건부 컴파일은 C 및 C ++의 #if / #else / #endif와 유사하게 컴파일 할 코드와 컴파일하지 않을 코드를 선택하는 프로세스입니다. 여전히 컴파일되지 않은 문은 구문 상 정확해야합니다.

조건부 컴파일에는 컴파일 타임에 평가할 수있는 조건 검사가 포함됩니다. if, for, while과 같은 런타임 조건문은 조건부 컴파일 기능이 아닙니다. D의 다음 기능은 조건부 컴파일을위한 것입니다.

  • debug
  • version
  • 정적 경우

D의 디버그 문

디버그 프로그램 개발시 유용합니다. 디버그로 표시된 식과 문은 -debug 컴파일러 스위치가 활성화 된 경우에만 프로그램으로 컴파일됩니다.

debug a_conditionally_compiled_expression;
   
debug { 
   // ... conditionally compiled code ... 
} else { 
   // ... code that is compiled otherwise ... 
}

else 절은 선택 사항입니다. 위의 단일 표현식과 코드 블록은 모두 -debug 컴파일러 스위치가 활성화 된 경우에만 컴파일됩니다.

완전히 제거되는 대신 줄을 디버그로 표시 할 수 있습니다.

debug writefln("%s debug only statement", value);

이러한 행은 -debug 컴파일러 스위치가 활성화 된 경우에만 프로그램에 포함됩니다.

dmd test.d -oftest -w -debug

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

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

D의 버전 (태그) 및 버전 (레벨) 문

버전은 디버그와 유사하며 동일한 방식으로 사용됩니다. else 절은 선택 사항입니다. 버전은 기본적으로 디버그와 동일하게 작동하지만 별도의 키워드를 사용하면 관련없는 용도를 구분하는 데 도움이됩니다. 디버그와 마찬가지로 둘 이상의 버전을 활성화 할 수 있습니다.

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 문과 달리 정적 if는 실행 흐름에 관한 것이 아닙니다. 오히려 프로그램에 코드 조각을 포함해야하는지 여부를 결정합니다.

if 표현식은 구문 적으로나 의미 적으로 앞에서 본 is 연산자와 관련이 없습니다. 컴파일 타임에 평가됩니다. 0 또는 1의 int 값을 생성합니다. 괄호 안에 지정된 식에 따라 다릅니다. 사용하는 표현식은 논리 표현식이 아니지만 is 표현식 자체는 컴파일 시간 논리 표현식으로 사용됩니다. 특히 정적 if 조건 및 템플릿 제약 조건에서 유용합니다.

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 클래스의 두 객체를 선언합니다-

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 클래스 및 객체와 관련된 더 흥미로운 개념이 있습니다.

Sr. 아니. 개념 및 설명
1 클래스 멤버 기능

클래스의 멤버 함수는 다른 변수와 마찬가지로 클래스 정의 내에 정의 또는 프로토 타입이있는 함수입니다.

2 클래스 액세스 수정 자

클래스 멤버는 public, private 또는 protected로 정의 할 수 있습니다. 기본적으로 구성원은 개인으로 간주됩니다.

생성자 및 소멸자

클래스 생성자는 클래스의 새 객체가 생성 될 때 호출되는 클래스의 특수 함수입니다. 소멸자는 생성 된 객체가 삭제 될 때 호출되는 특수 함수이기도합니다.

4 D의 this 포인터

모든 개체에는 특별한 포인터가 있습니다. this 객체 자체를 가리 킵니다.

5 D 클래스에 대한 포인터

클래스에 대한 포인터는 구조에 대한 포인터와 똑같은 방식으로 수행됩니다. 사실 클래스는 함수가있는 구조 일뿐입니다.

6 클래스의 정적 멤버

클래스의 데이터 멤버와 함수 멤버는 모두 정적으로 선언 할 수 있습니다.

객체 지향 프로그래밍에서 가장 중요한 개념 중 하나는 상속입니다. 상속을 통해 다른 클래스의 관점에서 클래스를 정의 할 수 있으므로 응용 프로그램을 쉽게 만들고 유지 관리 할 수 ​​있습니다. 또한 코드 기능을 재사용 할 수있는 기회와 빠른 구현 시간을 제공합니다.

클래스를 만들 때 완전히 새로운 데이터 멤버와 멤버 함수를 작성하는 대신 프로그래머는 새 클래스가 기존 클래스의 멤버를 상속하도록 지정할 수 있습니다. 이 기존 클래스를base 클래스, 새 클래스는 derived 수업.

상속의 개념은 is a 관계를 구현합니다. 예를 들어, 포유류 IS-A 동물, 개 IS-A 포유류이므로 개 IS-A 동물도 마찬가지입니다.

D의 기본 클래스 및 파생 클래스

클래스는 둘 이상의 클래스에서 파생 될 수 있습니다. 즉, 여러 기본 클래스에서 데이터와 함수를 상속 할 수 있습니다. 파생 클래스를 정의하기 위해 클래스 파생 목록을 사용하여 기본 클래스를 지정합니다. 클래스 파생 목록은 하나 이상의 기본 클래스의 이름을 지정하고 다음과 같은 형식을 갖습니다.

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

액세스 제어 및 상속

파생 클래스는 기본 클래스의 모든 비 전용 멤버에 액세스 할 수 있습니다. 따라서 파생 클래스의 멤버 함수에 액세스 할 수없는 기본 클래스 멤버는 기본 클래스에서 private로 선언해야합니다.

파생 클래스는 다음 예외를 제외하고 모든 기본 클래스 메서드를 상속합니다.

  • 기본 클래스의 생성자, 소멸자 및 복사 생성자.
  • 기본 클래스의 오버로드 된 연산자.

다단계 상속

상속은 여러 수준 일 수 있으며 다음 예에 나와 있습니다.

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 overloadingoperator 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에서 사용할 수있는 대부분의 내장 연산자를 재정의하거나 오버로드 할 수 있습니다. 따라서 프로그래머는 사용자 정의 유형에도 연산자를 사용할 수 있습니다.

연산자는 오버로드되는 연산자를 기반으로 문자열 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

연산자 오버로딩 유형

기본적으로 아래와 같이 세 가지 유형의 연산자 오버로딩이 있습니다.

Sr. 아니. 오버로딩 유형
1 단항 연산자 오버로딩
2 이항 연산자 오버로딩
비교 연산자 오버로딩

모든 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

위의 클래스는 숫자를 더하고 합계를 반환합니다. 공개 회원addNumgetTotal외부 세계에 대한 인터페이스이며 사용자는 클래스를 사용하기 위해이를 알아야합니다. private 멤버 합계는 외부 세계에 숨겨져 있지만 클래스가 제대로 작동하려면 필요합니다.

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

추상화는 OOP에서 클래스 추상화를 만드는 기능을 나타냅니다. 추상 클래스는 인스턴스화 할 수없는 클래스입니다. 클래스의 다른 모든 기능은 여전히 ​​존재하며 해당 필드, 메서드 및 생성자는 모두 동일한 방식으로 액세스됩니다. 추상 클래스의 인스턴스를 만들 수 없습니다.

클래스가 추상적이고 인스턴스화 할 수없는 경우 하위 클래스가 아니면 클래스는 많이 사용되지 않습니다. 이것은 일반적으로 디자인 단계에서 추상 클래스가 생성되는 방식입니다. 부모 클래스에는 자식 클래스 모음의 공통 기능이 포함되어 있지만 부모 클래스 자체는 너무 추상적이어서 자체적으로 사용할 수 없습니다.

D에서 추상 클래스 사용

사용 abstract클래스 초록을 선언하는 키워드. 키워드는 class 키워드 앞의 클래스 선언에 나타납니다. 다음은 추상 클래스를 상속하고 사용하는 방법의 예입니다.

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