From 944b432ff581f5f847b9d94f313cc48f6d2b7ed7 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 13 Jul 2017 14:55:55 +0800 Subject: [PATCH 001/225] =?UTF-8?q?=E7=AE=80=E6=B4=81=E7=BF=BB=E8=AF=91And?= =?UTF-8?q?rei=20Alexandrescu=E7=9A=84=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d468407..c1293ab 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The Chinese Translation Project of [Programming in D](http://ddili.org/ders/d.en ## Why "Programming in D"? 为什么选择此书? Instead of falling for getting things done quickly, "Programming in D" focuses on getting things done properly, to the lasting benefit of its reader. -Andrei Alexandrescu -本书注重于如何恰到好处地使用D语言将任务处理得当,而非试图快速地完成一切却有失于代码质量,这使得它的读者能够长久受益。 ——大A +本书没有急于求成,而是将各方面都处理得恰到好处,让读者受益匪浅。 ——Andrei Alexandrescu ## How to join us 怎样加入我们 ### Tencent QQ From c805bd54f2a2d44b079a7745b644809fba43b117 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 14 Jul 2017 09:12:53 +0800 Subject: [PATCH 002/225] Translated by zxp --- omegat/project_save.tmx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 omegat/project_save.tmx diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx new file mode 100644 index 0000000..c4e7fac --- /dev/null +++ b/omegat/project_save.tmx @@ -0,0 +1,9 @@ + + + +
+ + + + + From 4dfc78ac8689903c1b308c32cb14a95555815295 Mon Sep 17 00:00:00 2001 From: ZhangXuePing Date: Fri, 14 Jul 2017 18:13:33 +0800 Subject: [PATCH 003/225] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ddili/src/ders/d.cn/formatted_output.cozum.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddili/src/ders/d.cn/formatted_output.cozum.d b/ddili/src/ders/d.cn/formatted_output.cozum.d index 95a3ca1..95abc54 100644 --- a/ddili/src/ders/d.cn/formatted_output.cozum.d +++ b/ddili/src/ders/d.cn/formatted_output.cozum.d @@ -51,6 +51,6 @@ void main() { Macros: SUBTITLE=格式化输出习题解答 - 练习DESCRIPTION=D 编程语言练习解答:格式化输出 + DESCRIPTION=D 编程语言练习解答:格式化输出 KEYWORDS=D 编程语言教程 格式化输出 解答 From 65cf15c33f493a4a2c6df0e75635742395cb2c05 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 18 Jul 2017 18:57:57 +0800 Subject: [PATCH 004/225] Translated by Heromyth --- omegat/project_save.tmx | 124 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index c4e7fac..2f73687 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -4,6 +4,130 @@
+ + + $(UL +$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) This book in Turkish)) +$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) Contact)) +$(BR) +$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) Rights)) +) + + + 土耳其语版$(UL +$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) +$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) +$(BR) +$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) +) + + + + + COZUM_METIN=the solution +COZUMLER_METIN=the solutions + + + COZUM_METIN=解答 +COZUMLER_METIN=解答 + + + + + DERLEME_HATASI_METIN=compilation ERROR + + + DERLEME_HATASI_METIN=编译出错 + + + + + DUSEY_NAVIGASYON= +<div class="vertinavheader">Other D Resources</div> +$(UL +$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) Books)) +$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) Newsgroups)) +$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) Language spec)) +$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) Standard library)) +) + + + DUSEY_NAVIGASYON= +<div class="vertinavheader">其他D语言资源</div> +$(UL +$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) 书籍)) +$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) 社区)) +$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) +$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) +) + + + + + Ddoc + + + Ddoc + + + + + GERI_METIN=Prev +ILERI_METIN=Next +PROBLEM_METIN=Exercise +PROBLEM_COK_METIN=Exercises +PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... + + + GERI_METIN=上一章 +ILERI_METIN=下一章 +PROBLEM_METIN=练习 +PROBLEM_COK_METIN=练习 +PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… + + + + + MAIN_TITLE=D.ershane Solutions +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= + + + MAIN_TITLE=D 语言解答 +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= + + + + + MAIN_TITLE=Programming in D +SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference +SUB_AUTHOR=Ali Çehreli +LANG=en +LANGUAGE=english + + + MAIN_TITLE=D 编程语言 +SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 +SUB_AUTHOR=Ali Çehreli +LANG=en +LANGUAGE=english其他D语言资源 + + + + + PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... + + + PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… + + From 747a9b7b54162e423901c220dee47750d5942e16 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 18 Jul 2017 18:58:10 +0800 Subject: [PATCH 005/225] Project translation --- target/cozum_ozel.ddoc | 6 + target/derse_ozel.ddoc | 47 +++++ target/uda.d | 421 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+) create mode 100644 target/cozum_ozel.ddoc create mode 100644 target/derse_ozel.ddoc create mode 100644 target/uda.d diff --git a/target/cozum_ozel.ddoc b/target/cozum_ozel.ddoc new file mode 100644 index 0000000..dca697e --- /dev/null +++ b/target/cozum_ozel.ddoc @@ -0,0 +1,6 @@ +MAIN_TITLE=D 语言解答 +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= diff --git a/target/derse_ozel.ddoc b/target/derse_ozel.ddoc new file mode 100644 index 0000000..089f2bd --- /dev/null +++ b/target/derse_ozel.ddoc @@ -0,0 +1,47 @@ +MAIN_TITLE=D 编程语言 +SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 +SUB_AUTHOR=Ali Çehreli +LANG=en +LANGUAGE=english其他D语言资源 + +HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS Programming in D RSS Feed) +$(BR) +$(LINK2 /ders/d.en/index.html, +Download or buy $(IMG book.png)) + +DUSEY_NAVIGASYON= +
其他D语言资源
+$(UL +$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) 书籍)) +$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) 社区)) +$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) +$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) +) + +土耳其语版$(UL +$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) +$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) +$(BR) +$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) +) + +MINIBASLIK = $0 + + +BASLIK =

$0

+ +ALTBASLIK =
$0
+ + +MINI_SOZLUK= + +GERI_METIN=上一章 +ILERI_METIN=下一章 +PROBLEM_METIN=练习 +PROBLEM_COK_METIN=练习 +PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… +PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… +COZUM_METIN=解答 +COZUMLER_METIN=解答 + +DERLEME_HATASI_METIN=编译出错 diff --git a/target/uda.d b/target/uda.d new file mode 100644 index 0000000..9c0bb33 --- /dev/null +++ b/target/uda.d @@ -0,0 +1,421 @@ +Ddoc + +$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) + +$(P +Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. User defined attributes is purely a compile-time feature. +) + +$(P +$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +) + +--- + $(HILITE @Encrypted) string name; +--- + +$(P +Multiple attributes can be specified separately or as a parenthesized list of attributes. For example, both of the following variables have the same attributes: +) + +--- + @Encrypted @Colored string lastName; $(CODE_NOTE separately) + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE together) +--- + +$(P +An attribute can be a type name as well as a value of a user defined or a fundamental type. However, because their meanings may not be clear, attributes consisting of literal values like $(C 42) are discouraged: +) + +--- +$(CODE_NAME Encrypted)struct Encrypted { +} + +enum Color { black, blue, red } + +struct Colored { + Color color; +} + +void main() { + @Encrypted int a; $(CODE_NOTE type name) + @Encrypted() int b; $(CODE_NOTE object) + @Colored(Color.blue) int c; $(CODE_NOTE object) + @(42) int d; $(CODE_NOTE literal (discouraged)) +} +--- + +$(P +The attributes of $(C a) and $(C b) above are of different kinds: The attribute of $(C a) is the type $(C Encrypted) itself, while the attribute of $(C b) is an $(I object) of type $(C Encrypted). This is an important difference that affects the way attributes are used at compile time. We will see an example of this difference below. +) + +$(P +$(IX __traits) $(IX getAttributes) The meaning of attributes is solely determined by the programmer for the needs of the program. The attributes are determined by $(C __traits(getAttributes)) at compile time and the code is compiled according to those attributes. +) + +$(P +The following code shows how the attributes of a specific $(C struct) member (e.g. $(C Person.name)) can be accessed by $(C __traits(getAttributes)): +) + +--- +$(CODE_NAME Person)import std.stdio; + +// ... + +struct Person { + @Encrypted @Colored(Color.blue) string name; + string lastName; + @Colored(Color.red) string address; +} + +void $(CODE_DONT_TEST)main() { + foreach (attr; __traits($(HILITE getAttributes), Person.name)) { + writeln(attr.stringof); + } +} +--- + +$(P +The output of the program lists the attributes of $(C Person.name): +) + +$(SHELL +Encrypted +Colored(cast(Color)1) +) + +$(P +Two other $(C __traits) expressions are useful when dealing with user defined attributes: +) + +$(UL + +$(LI $(IX allMembers) $(C __traits(allMembers)) produces the members of a type (or a module) as strings.) + +$(LI $(IX getMember) $(C __traits(getMember)) produces a $(I symbol) useful when accessing a member. Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. It produces a symbol by combining its first argument, a dot, and its second argument. For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). +) + +) + +--- +$(CODE_XREF Encrypted)$(CODE_XREF Person)import std.string; + +// ... + +void main() { + foreach (memberName; __traits($(HILITE allMembers), Person)) { + writef("The attributes of %-8s:", memberName); + + foreach (attr; __traits(getAttributes, + __traits($(HILITE getMember), + Person, memberName))) { + writef(" %s", attr.stringof); + } + + writeln(); + } +} +--- + +$(P +The output of the program lists all attributes of all members of $(C Person): +) + +$(SHELL +The attributes of name : Encrypted Colored(cast(Color)1) +The attributes of lastName: +The attributes of address : Colored(cast(Color)2) +) + +$(P +$(IX hasUDA, std.traits) Another useful tool is $(C std.traits.hasUDA), which determines whether a symbol has a specific attribute. The following $(C static assert) passes because $(C Person.name) has $(C Encrypted) attribute: +) + +--- +import std.traits; + +// ... + +static assert(hasUDA!(Person.name, Encrypted)); +--- + +$(P +$(C hasUDA) can be used with an attribute type as well as a specific value of that type. The following $(C static assert) checks both pass because $(C Person.name) has $(C Colored(Color.blue)) attribute: +) + +--- +static assert(hasUDA!(Person.name, $(HILITE Colored))); +static assert(hasUDA!(Person.name, $(HILITE Colored(Color.blue)))); +--- + +$(H5 Example) + +$(P +Let's design a function template that prints the values of all members of a $(C struct) object in XML format. The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: +) + +--- +void printAsXML(T)(T object) { +// ... + + foreach (member; __traits($(HILITE allMembers), T)) { // (1) + string value = + __traits($(HILITE getMember), object, member).to!string; // (2) + + static if ($(HILITE hasUDA)!(__traits(getMember, T, member), // (3) + Encrypted)) { + value = value.encrypted.to!string; + } + + writefln(` <%1$s color="%2$s">%3$s`, member, + $(HILITE colorAttributeOf)!(T, member), value); // (4) + } +} +--- + +$(P +The highlighted parts of the code are explained below: +) + +$(OL + +$(LI The members of the type are determined by $(C __traits(allMembers)).) + +$(LI The value of each member is converted to $(C string) to be used later when printing to the output. For example, when the member is $(STRING "name"), the right-hand side expression becomes $(C object.name.to!string).) + +$(LI Each member is tested with $(C hasUDA) to determine whether it has the $(C Encrypted) attribute. The value of the member is encrypted if it has that attribute. (Because $(C hasUDA) requires $(I symbols) to work with, note how $(C __traits(getMember)) is used to get the member as a symbol (e.g. $(C Person.name)).)) + +$(LI The color attribute of each member is determined with $(C colorAttributeOf()), which we will see below.) + +) + +$(P +The $(C colorAttributeOf()) function template can be implemented as in the following code: +) + +--- +Color colorAttributeOf(T, string memberName)() { + foreach (attr; __traits(getAttributes, + __traits(getMember, T, memberName))) { + static if (is ($(HILITE typeof(attr)) == Colored)) { + return attr.color; + } + } + + return Color.black; +} +--- + +$(P +When the compile-time evaluations are completed, the $(C printAsXML()) function template would be instantiated for the $(C Person) type as the equivalent of the following function: +) + +--- +/* The equivalent of the printAsXML!Person instance. */ +void printAsXML_Person(Person object) { +// ... + + { + string value = object.$(HILITE name).to!string; + $(HILITE value = value.encrypted.to!string;) + writefln(` <%1$s color="%2$s">%3$s`, + "name", Color.blue, value); + } + { + string value = object.$(HILITE lastName).to!string; + writefln(` <%1$s color="%2$s">%3$s`, + "lastName", Color.black, value); + } + { + string value = object.$(HILITE address).to!string; + writefln(` <%1$s color="%2$s">%3$s`, + "address", Color.red, value); + } +} +--- + +$(P +The complete program has more explanations: +) + +--- +import std.stdio; +import std.string; +import std.algorithm; +import std.conv; +import std.traits; + +/* Specifies that the symbol that it is assigned to should be + * encrypted. */ +struct Encrypted { +} + +enum Color { black, blue, red } + +/* Specifies the color of the symbol that it is assigned to. + * The default color is Color.black. */ +struct Colored { + Color color; +} + +struct Person { + /* This member is specified to be encrypted and printed in + * blue. */ + @Encrypted @Colored(Color.blue) string name; + + /* This member does not have any user defined + * attributes. */ + string lastName; + + /* This member is specified to be printed in red. */ + @Colored(Color.red) string address; +} + +/* Returns the value of the Colored attribute if the specified + * member has that attribute, Color.black otherwise. */ +Color colorAttributeOf(T, string memberName)() { + auto result = Color.black; + + foreach (attr; + __traits(getAttributes, + __traits(getMember, T, memberName))) { + static if (is (typeof(attr) == Colored)) { + result = attr.color; + } + } + + return result; +} + +/* Returns the Caesar-encrypted version of the specified + * string. (Warning: Caesar cipher is a very weak encryption + * method.) */ +auto encrypted(string value) { + return value.map!(a => dchar(a + 1)); +} + +unittest { + assert("abcdefghij".encrypted.equal("bcdefghijk")); +} + +/* Prints the specified object in XML format according to the + * attributes of its members. */ +void printAsXML(T)(T object) { + writefln("<%s>", T.stringof); + scope(exit) writefln("", T.stringof); + + foreach (member; __traits(allMembers, T)) { + string value = + __traits(getMember, object, member).to!string; + + static if (hasUDA!(__traits(getMember, T, member), + Encrypted)) { + value = value.encrypted.to!string; + } + + writefln(` <%1$s color="%2$s">%3$s`, + member, colorAttributeOf!(T, member), value); + } +} + +void main() { + auto people = [ Person("Alice", "Davignon", "Avignon"), + Person("Ben", "de Bordeaux", "Bordeaux") ]; + + foreach (person; people) { + printAsXML(person); + } +} +--- + +$(P +The output of the program shows that the members have the correct color and that the $(C name) member is encrypted: +) + +$(SHELL +<Person> + <name color="blue">Bmjdf</name> $(SHELL_NOTE blue and encrypted) + <lastName color="black">Davignon</lastName> + <address color="red">Avignon</address> $(SHELL_NOTE red) +</Person> +<Person> + <name color="blue">Cfo</name> $(SHELL_NOTE blue and encrypted) + <lastName color="black">de Bordeaux</lastName> + <address color="red">Bordeaux</address> $(SHELL_NOTE red) +</Person> +) + +$(H5 The benefit of user defined attributes) + +$(P +The benefit of user defined attributes is being able to change the attributes of declarations without needing to change any other part of the program. For example, all of the members of $(C Person) can become encrypted in the XML output by the trivial change below: +) + +--- +struct Person { + $(HILITE @Encrypted) { + string name; + string lastName; + string address; + } +} + +// ... + + printAsXML(Person("Cindy", "de Cannes", "Cannes")); +--- + +$(P +The output: +) + +$(SHELL +<Person> + <name color="black">Djoez</name> $(SHELL_NOTE encrypted) + <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE encrypted) + <address color="black">Dbooft</address> $(SHELL_NOTE encrypted) +</Person> +) + +$(P +Further, $(C printAsXML()) and the attributes that it considers can be used with other types as well: +) + +--- +struct Data { + $(HILITE @Colored(Color.blue)) string message; +} + +// ... + + printAsXML(Data("hello world")); +--- + +$(P +The output: +) + +$(SHELL +<Data> + <message color="blue">hello world</message> $(SHELL_NOTE blue) +</Data> +) + +$(H5 Summary) + +$(UL + +$(LI User defined attributes can be assigned to any declaration.) + +$(LI User defined attributes can be type names as well as values.) + +$(LI User defined attributes can be accessed at compile time by $(C hasUDA) and $(C __traits(getAttributes)) to alter the way the program is compiled.) + +) + +macros: + SUBTITLE=User Defined Attributes (UDA) + + DESCRIPTION=Assigning user defined attributes to declarations, determining the attributes at compile time, and compiling the code according to those attributes. + + KEYWORDS=d programming language tutorial book user defined attributes UDA From 07147e45a934545292c74650ebf06c1704629aea Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 18 Jul 2017 19:00:13 +0800 Subject: [PATCH 006/225] Project translation --- target/derse_ozel.ddoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/derse_ozel.ddoc b/target/derse_ozel.ddoc index 089f2bd..9549354 100644 --- a/target/derse_ozel.ddoc +++ b/target/derse_ozel.ddoc @@ -2,7 +2,7 @@ MAIN_TITLE=D 编程语言 SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 SUB_AUTHOR=Ali Çehreli LANG=en -LANGUAGE=english其他D语言资源 +LANGUAGE=english HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS Programming in D RSS Feed) $(BR) From e74ac6e96dbbb7ae1d847fe60afe57ac87469e1f Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 18 Jul 2017 19:01:42 +0800 Subject: [PATCH 007/225] =?UTF-8?q?=E6=94=B9=E8=BF=9Bddoc=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ddili/src/ders/d.cn/cozum_ozel.ddoc | 6 +++--- ddili/src/ders/d.cn/derse_ozel.ddoc | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ddili/src/ders/d.cn/cozum_ozel.ddoc b/ddili/src/ders/d.cn/cozum_ozel.ddoc index 1f22b3b..dca697e 100644 --- a/ddili/src/ders/d.cn/cozum_ozel.ddoc +++ b/ddili/src/ders/d.cn/cozum_ozel.ddoc @@ -1,6 +1,6 @@ -MAIN_TITLE=D语言教室 +MAIN_TITLE=D 语言解答 CLASS=solution COZUM_BOLUMU = $(H4 $0) -LANG=zh-cn -LANGUAGE=chinese +LANG=en +LANGUAGE=english SUB_AUTHOR= diff --git a/ddili/src/ders/d.cn/derse_ozel.ddoc b/ddili/src/ders/d.cn/derse_ozel.ddoc index 9af4d71..9549354 100644 --- a/ddili/src/ders/d.cn/derse_ozel.ddoc +++ b/ddili/src/ders/d.cn/derse_ozel.ddoc @@ -1,13 +1,13 @@ -MAIN_TITLE=Programming in D -SUB_MAIN_TITLE_DERSE_OZEL=——指南与参考 +MAIN_TITLE=D 编程语言 +SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 SUB_AUTHOR=Ali Çehreli -LANG=zh-cn -LANGUAGE=chinese +LANG=en +LANGUAGE=english -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS Programming in D RSS Feed) +HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS Programming in D RSS Feed) $(BR) -$(LINK2 /ders/d.cn/index.html, -下载或购买 $(IMG book.png)) +$(LINK2 /ders/d.en/index.html, +Download or buy $(IMG book.png)) DUSEY_NAVIGASYON=
其他D语言资源
@@ -18,7 +18,7 @@ $(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) ) -$(UL +土耳其语版$(UL $(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) $(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) $(BR) @@ -39,9 +39,9 @@ GERI_METIN=上一章 ILERI_METIN=下一章 PROBLEM_METIN=练习 PROBLEM_COK_METIN=练习 -PROBLEM_TEK_COZUMSUZ_METIN=答案将稍后发表…… -PROBLEM_COK_COZUMSUZ_METIN=答案将稍后发表…… +PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… +PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… COZUM_METIN=解答 COZUMLER_METIN=解答 -DERLEME_HATASI_METIN=编译出错啦 +DERLEME_HATASI_METIN=编译出错 From 5e11a38521e09276f7917f74870e438af78b3cf7 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 18 Jul 2017 19:02:21 +0800 Subject: [PATCH 008/225] Translated by Heromyth Added: 0, Deleted: 0, Modified: 1 (Conflicts: 0) --- omegat/project_save.tmx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 2f73687..75856a8 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -112,12 +112,12 @@ SUB_AUTHOR=Ali Çehreli LANG=en LANGUAGE=english - + MAIN_TITLE=D 编程语言 SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 SUB_AUTHOR=Ali Çehreli LANG=en -LANGUAGE=english其他D语言资源 +LANGUAGE=english From ce5b31f38e318ad5285f9bf9c05911ec6c2a3ce1 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 19 Jul 2017 13:43:41 +0800 Subject: [PATCH 009/225] Translated by Heromyth --- omegat/project_save.tmx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 75856a8..d081fb8 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -4,6 +4,14 @@
+ + + $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) + + + $(DERS_BOLUMU $(IX 自定义属性) $(IX UDA) 自定义属性(UDA)) + + $(UL From feefc581efbc88de7001089ab2ced93c73448670 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 19 Jul 2017 18:58:07 +0800 Subject: [PATCH 010/225] Translated by Heromyth --- omegat/project_save.tmx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index d081fb8..6e8a06c 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -8,8 +8,18 @@ $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) - - $(DERS_BOLUMU $(IX 自定义属性) $(IX UDA) 自定义属性(UDA)) + + $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) + + + + + $(P +Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. + + + $(P +所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。 From 98b30f7b108dcebcd209239e33b70f685a07c0e6 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 20 Jul 2017 12:26:45 +0800 Subject: [PATCH 011/225] Translated by Heromyth --- omegat/project_save.tmx | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 6e8a06c..3086eb8 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -15,6 +15,16 @@ $(P +$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. + + + $(P +$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。 + + + + + $(P Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. @@ -22,6 +32,16 @@ Any declaration (e.g. struct type, class type, variable, etc.) can be assigned a 所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。 + + + $(P +Multiple attributes can be specified separately or as a parenthesized list of attributes. + + + $(P +多个属性可以分别指定,也可以采用括号列表形式。 + + $(UL @@ -88,6 +108,26 @@ $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准 Ddoc + + + For example, both of the following variables have the same attributes: +) + + + 例如,下面的变量拥有的属性是相同的: +) + + + + + For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +) + + + 例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: +) + + GERI_METIN=Prev @@ -146,6 +186,14 @@ LANGUAGE=english PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… + + + User defined attributes is purely a compile-time feature. + + + 自定义属性完全是一项编译时功能。 + + From f4929fe3185e093dfb8d4125efb5500bf8df6667 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 21 Jul 2017 12:52:20 +0800 Subject: [PATCH 012/225] Project translation --- target/uda.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/uda.d b/target/uda.d index 9c0bb33..9aea24d 100644 --- a/target/uda.d +++ b/target/uda.d @@ -1,13 +1,13 @@ Ddoc -$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) +$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) $(P -Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. User defined attributes is purely a compile-time feature. +所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。自定义属性完全是一项编译时功能。 ) $(P -$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: ) --- @@ -15,7 +15,7 @@ $(IX @) The user defined attribute syntax consists of the $(C @) sign followed b --- $(P -Multiple attributes can be specified separately or as a parenthesized list of attributes. For example, both of the following variables have the same attributes: +多个属性可以分别指定,也可以采用括号列表形式。例如,下面的变量拥有的属性是相同的: ) --- From 1e637afd16d096f460b7682519b4e5db2bdc9501 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 21 Jul 2017 14:03:02 +0800 Subject: [PATCH 013/225] Project translation --- target/derse_ozel.ddoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/derse_ozel.ddoc b/target/derse_ozel.ddoc index 9549354..fba5bd7 100644 --- a/target/derse_ozel.ddoc +++ b/target/derse_ozel.ddoc @@ -1,13 +1,13 @@ MAIN_TITLE=D 编程语言 SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english +LANG=zh-cn +LANGUAGE=chinese -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS Programming in D RSS Feed) +HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS RSS 订阅) $(BR) -$(LINK2 /ders/d.en/index.html, -Download or buy $(IMG book.png)) +$(LINK2 /ders/d.cn/index.html, +下载或购买 $(IMG book.png)) DUSEY_NAVIGASYON=
其他D语言资源
@@ -18,7 +18,7 @@ $(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) ) -土耳其语版$(UL +$(UL $(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) $(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) $(BR) @@ -41,7 +41,7 @@ PROBLEM_METIN=练习 PROBLEM_COK_METIN=练习 PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… -COZUM_METIN=解答 -COZUMLER_METIN=解答 +COZUM_METIN=答案 +COZUMLER_METIN=答案 DERLEME_HATASI_METIN=编译出错 From 7d3afcceae12beeb00b62412b93d16fafa6af231 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 21 Jul 2017 14:10:50 +0800 Subject: [PATCH 014/225] Translated by Heromyth Added: 1, Deleted: 0, Modified: 3 (Conflicts: 0) --- omegat/project_save.tmx | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 3086eb8..491f789 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -51,8 +51,8 @@ $(BR) $(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) Rights)) ) - - 土耳其语版$(UL + + $(UL $(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) $(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) $(BR) @@ -65,9 +65,9 @@ $(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) COZUM_METIN=the solution COZUMLER_METIN=the solutions - - COZUM_METIN=解答 -COZUMLER_METIN=解答 + + COZUM_METIN=答案 +COZUMLER_METIN=答案
@@ -144,6 +144,20 @@ PROBLEM_COK_METIN=练习 PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… + + + HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="Programming in D RSS Feed"/>) +$(BR) +$(LINK2 /ders/d.en/index.html, +Download or buy $(IMG book.png)) + + + HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="RSS 订阅"/>) +$(BR) +$(LINK2 /ders/d.cn/index.html, +下载或购买 $(IMG book.png)) + + MAIN_TITLE=D.ershane Solutions @@ -170,12 +184,12 @@ SUB_AUTHOR=Ali Çehreli LANG=en LANGUAGE=english - + MAIN_TITLE=D 编程语言 SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english +LANG=zh-cn +LANGUAGE=chinese From 0b09a89b8a505a18a8b3bd9404f5e56d7a751444 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 26 Jul 2017 09:01:24 +0800 Subject: [PATCH 015/225] Project translation --- target/aa.cozum.d | 117 ++ target/aa.d | 323 ++++ target/alias.d | 447 ++++++ target/alias_this.d | 176 +++ target/arithmetic.cozum.d | 160 ++ target/arithmetic.d | 871 +++++++++++ target/arrays.cozum.d | 133 ++ target/arrays.d | 602 ++++++++ target/assert.cozum.d | 135 ++ target/assert.d | 419 +++++ target/assignment.cozum.d | 25 + target/assignment.d | 82 + target/auto_and_typeof.cozum.d | 30 + target/auto_and_typeof.d | 97 ++ target/bit_operations.cozum.d | 84 + target/bit_operations.d | 1093 +++++++++++++ target/blurbs.d | 42 + target/cast.d | 715 +++++++++ target/characters.d | 594 ++++++++ target/class.d | 453 ++++++ target/compiler.d | 101 ++ target/concurrency.d | 1343 ++++++++++++++++ target/concurrency_shared.d | 758 +++++++++ target/cond_comp.d | 670 ++++++++ target/const_and_immutable.d | 758 +++++++++ target/const_member_functions.d | 289 ++++ target/contracts.cozum.d | 77 + target/contracts.d | 392 +++++ target/copyright.d | 106 ++ target/destroy.d | 468 ++++++ target/do_while.cozum.d | 22 + target/do_while.d | 91 ++ target/encapsulation.d | 508 +++++++ target/enum.cozum.d | 94 ++ target/enum.d | 334 ++++ target/exceptions.d | 995 ++++++++++++ target/fibers.d | 1107 ++++++++++++++ target/files.cozum.d | 34 + target/files.d | 226 +++ target/floating_point.cozum.d | 110 ++ target/floating_point.d | 490 ++++++ target/for.cozum.d | 86 ++ target/for.d | 257 ++++ target/foreach.cozum.d | 34 + target/foreach.d | 437 ++++++ target/foreach_opapply.cozum.d | 203 +++ target/foreach_opapply.d | 666 ++++++++ target/foreword1.d | 16 + target/foreword2.d | 40 + target/formatted_input.cozum.d | 28 + target/formatted_input.d | 160 ++ target/formatted_output.cozum.d | 56 + target/formatted_output.d | 661 ++++++++ target/frontispiece.d | 9 + target/function_overloading.cozum.d | 147 ++ target/function_overloading.d | 294 ++++ target/function_parameters.cozum.d | 38 + target/function_parameters.d | 992 ++++++++++++ target/functions.cozum.d | 57 + target/functions.d | 744 +++++++++ target/functions_more.d | 959 ++++++++++++ target/goto.d | 263 ++++ target/hello_world.cozum.d | 52 + target/hello_world.d | 216 +++ target/if.cozum.d | 110 ++ target/if.d | 309 ++++ target/index.d | 143 ++ target/inheritance.cozum.d | 203 +++ target/inheritance.d | 1049 +++++++++++++ target/input.cozum.d | 14 + target/input.d | 249 +++ target/interface.d | 657 ++++++++ target/invariant.d | 467 ++++++ target/io.cozum.d | 21 + target/io.d | 58 + target/is_expr.d | 558 +++++++ target/ix.d | 14 + target/lambda.d | 1196 +++++++++++++++ target/lazy_operators.d | 92 ++ target/lifetimes.d | 341 +++++ target/literals.cozum.d | 76 + target/literals.d | 384 +++++ target/logical_expressions.cozum.d | 54 + target/logical_expressions.d | 431 ++++++ target/lvalue_rvalue.d | 218 +++ target/main.cozum.d | 76 + target/main.d | 500 ++++++ target/member_functions.cozum.d | 170 +++ target/member_functions.d | 418 +++++ target/memory.d | 1336 ++++++++++++++++ target/mixin.d | 530 +++++++ target/modules.d | 622 ++++++++ target/name_space.d | 143 ++ target/nested.d | 282 ++++ target/null_is.d | 285 ++++ target/object.cozum.d | 108 ++ target/object.d | 866 +++++++++++ target/operator_overloading.cozum.d | 297 ++++ target/operator_overloading.d | 1902 +++++++++++++++++++++++ target/operator_precedence.d | 323 ++++ target/parallelism.d | 1217 +++++++++++++++ target/parameter_flexibility.cozum.d | 105 ++ target/parameter_flexibility.d | 527 +++++++ target/pointers.cozum.d | 209 +++ target/pointers.d | 1653 ++++++++++++++++++++ target/pragma.d | 235 +++ target/preface.d | 70 + target/property.d | 351 +++++ target/ranges.d | 2013 ++++++++++++++++++++++++ target/ranges_more.d | 428 ++++++ target/scope.d | 142 ++ target/slices.cozum.d | 40 + target/slices.d | 834 ++++++++++ target/special_functions.d | 1234 +++++++++++++++ target/stream_redirect.cozum.d | 26 + target/stream_redirect.d | 136 ++ target/strings.cozum.d | 63 + target/strings.d | 511 +++++++ target/struct.cozum.d | 220 +++ target/struct.d | 932 ++++++++++++ target/switch_case.cozum.d | 103 ++ target/switch_case.d | 375 +++++ target/templates.d | 1104 ++++++++++++++ target/templates_more.d | 2107 ++++++++++++++++++++++++++ target/ternary.cozum.d | 33 + target/ternary.d | 251 +++ target/to_be_continued.d | 17 + target/tuples.d | 571 +++++++ target/types.cozum.d | 36 + target/types.d | 295 ++++ target/ufcs.d | 223 +++ target/union.d | 412 +++++ target/unit_testing.cozum.d | 190 +++ target/unit_testing.d | 530 +++++++ target/value_vs_reference.d | 701 +++++++++ target/variables.cozum.d | 22 + target/variables.d | 107 ++ target/while.cozum.d | 50 + target/while.d | 230 +++ target/writeln.cozum.d | 32 + target/writeln.d | 83 + 141 files changed, 54906 insertions(+) create mode 100644 target/aa.cozum.d create mode 100644 target/aa.d create mode 100644 target/alias.d create mode 100644 target/alias_this.d create mode 100644 target/arithmetic.cozum.d create mode 100644 target/arithmetic.d create mode 100644 target/arrays.cozum.d create mode 100644 target/arrays.d create mode 100644 target/assert.cozum.d create mode 100644 target/assert.d create mode 100644 target/assignment.cozum.d create mode 100644 target/assignment.d create mode 100644 target/auto_and_typeof.cozum.d create mode 100644 target/auto_and_typeof.d create mode 100644 target/bit_operations.cozum.d create mode 100644 target/bit_operations.d create mode 100644 target/blurbs.d create mode 100644 target/cast.d create mode 100644 target/characters.d create mode 100644 target/class.d create mode 100644 target/compiler.d create mode 100644 target/concurrency.d create mode 100644 target/concurrency_shared.d create mode 100644 target/cond_comp.d create mode 100644 target/const_and_immutable.d create mode 100644 target/const_member_functions.d create mode 100644 target/contracts.cozum.d create mode 100644 target/contracts.d create mode 100644 target/copyright.d create mode 100644 target/destroy.d create mode 100644 target/do_while.cozum.d create mode 100644 target/do_while.d create mode 100644 target/encapsulation.d create mode 100644 target/enum.cozum.d create mode 100644 target/enum.d create mode 100644 target/exceptions.d create mode 100644 target/fibers.d create mode 100644 target/files.cozum.d create mode 100644 target/files.d create mode 100644 target/floating_point.cozum.d create mode 100644 target/floating_point.d create mode 100644 target/for.cozum.d create mode 100644 target/for.d create mode 100644 target/foreach.cozum.d create mode 100644 target/foreach.d create mode 100644 target/foreach_opapply.cozum.d create mode 100644 target/foreach_opapply.d create mode 100644 target/foreword1.d create mode 100644 target/foreword2.d create mode 100644 target/formatted_input.cozum.d create mode 100644 target/formatted_input.d create mode 100644 target/formatted_output.cozum.d create mode 100644 target/formatted_output.d create mode 100644 target/frontispiece.d create mode 100644 target/function_overloading.cozum.d create mode 100644 target/function_overloading.d create mode 100644 target/function_parameters.cozum.d create mode 100644 target/function_parameters.d create mode 100644 target/functions.cozum.d create mode 100644 target/functions.d create mode 100644 target/functions_more.d create mode 100644 target/goto.d create mode 100644 target/hello_world.cozum.d create mode 100644 target/hello_world.d create mode 100644 target/if.cozum.d create mode 100644 target/if.d create mode 100644 target/index.d create mode 100644 target/inheritance.cozum.d create mode 100644 target/inheritance.d create mode 100644 target/input.cozum.d create mode 100644 target/input.d create mode 100644 target/interface.d create mode 100644 target/invariant.d create mode 100644 target/io.cozum.d create mode 100644 target/io.d create mode 100644 target/is_expr.d create mode 100644 target/ix.d create mode 100644 target/lambda.d create mode 100644 target/lazy_operators.d create mode 100644 target/lifetimes.d create mode 100644 target/literals.cozum.d create mode 100644 target/literals.d create mode 100644 target/logical_expressions.cozum.d create mode 100644 target/logical_expressions.d create mode 100644 target/lvalue_rvalue.d create mode 100644 target/main.cozum.d create mode 100644 target/main.d create mode 100644 target/member_functions.cozum.d create mode 100644 target/member_functions.d create mode 100644 target/memory.d create mode 100644 target/mixin.d create mode 100644 target/modules.d create mode 100644 target/name_space.d create mode 100644 target/nested.d create mode 100644 target/null_is.d create mode 100644 target/object.cozum.d create mode 100644 target/object.d create mode 100644 target/operator_overloading.cozum.d create mode 100644 target/operator_overloading.d create mode 100644 target/operator_precedence.d create mode 100644 target/parallelism.d create mode 100644 target/parameter_flexibility.cozum.d create mode 100644 target/parameter_flexibility.d create mode 100644 target/pointers.cozum.d create mode 100644 target/pointers.d create mode 100644 target/pragma.d create mode 100644 target/preface.d create mode 100644 target/property.d create mode 100644 target/ranges.d create mode 100644 target/ranges_more.d create mode 100644 target/scope.d create mode 100644 target/slices.cozum.d create mode 100644 target/slices.d create mode 100644 target/special_functions.d create mode 100644 target/stream_redirect.cozum.d create mode 100644 target/stream_redirect.d create mode 100644 target/strings.cozum.d create mode 100644 target/strings.d create mode 100644 target/struct.cozum.d create mode 100644 target/struct.d create mode 100644 target/switch_case.cozum.d create mode 100644 target/switch_case.d create mode 100644 target/templates.d create mode 100644 target/templates_more.d create mode 100644 target/ternary.cozum.d create mode 100644 target/ternary.d create mode 100644 target/to_be_continued.d create mode 100644 target/tuples.d create mode 100644 target/types.cozum.d create mode 100644 target/types.d create mode 100644 target/ufcs.d create mode 100644 target/union.d create mode 100644 target/unit_testing.cozum.d create mode 100644 target/unit_testing.d create mode 100644 target/value_vs_reference.d create mode 100644 target/variables.cozum.d create mode 100644 target/variables.d create mode 100644 target/while.cozum.d create mode 100644 target/while.d create mode 100644 target/writeln.cozum.d create mode 100644 target/writeln.d diff --git a/target/aa.cozum.d b/target/aa.cozum.d new file mode 100644 index 0000000..0997594 --- /dev/null +++ b/target/aa.cozum.d @@ -0,0 +1,117 @@ +Ddoc + +$(COZUM_BOLUMU Associative Arrays) + +$(OL + +$(LI + +$(UL + +$(LI +The $(C .keys) property returns a slice (i.e. dynamic array) that includes all of the keys of the associative array. Iterating over this slice and removing the element for each key by calling $(C .remove) would result in an empty associative array: + +--- +import std.stdio; + +void main() { + string[int] names = + [ + 1 : "one", + 10 : "ten", + 100 : "hundred", + ]; + + writeln("Initial length: ", names.length); + + int[] keys = names.keys; + + /* 'foreach' is similar but superior to 'for'. We will + * see the 'foreach' loop in the next chapter. */ + foreach (key; keys) { + writefln("Removing the element %s", key); + names.remove(key); + } + + writeln("Final length: ", names.length); +} +--- + +$(P +That solution may be slow especially for large arrays. The following methods would empty the array in a single step. +) + +) + +$(LI +Another solution is to assign an empty array: + +--- + string[int] emptyAA; + names = emptyAA; +--- + +) + +$(LI +Since the initial value of an array is an empty array anyway, the following technique would achieve the same result: + +--- + names = names.init; +--- + +) + +) + +) + +$(LI +The goal is to store multiple grades per student. Since multiple grades can be stored in a dynamic array, an associative array that maps from $(C string) to $(C int[]) would work here. The grades can be appended to the dynamic arrays that are stored in the associative array: + +--- +import std.stdio; + +void main() { + /* The key type of this associative array is string and + * the value type is int[], i.e. an array of ints. The + * associative array is being defined with an extra + * space in between to help distinguish the value type: */ + int[] [string] grades; + + /* The array of ints that correspond to "emre" is being + * used for appending the new grade to that array: */ + grades["emre"] ~= 90; + grades["emre"] ~= 85; + + /* Printing the grades of "emre": */ + writeln(grades["emre"]); +} +--- + +$(P +The grades can also be assigned in one go with an array literal: +) + +--- +import std.stdio; + +void main() { + int[][string] grades; + + grades["emre"] = [ 90, 85, 95 ]; + + writeln(grades["emre"]); +} +--- + +) + +) + +Macros: + SUBTITLE=Associative Arrays Solutions + + DESCRIPTION=Programming in D exercise solutions: Associative Arrays + + KEYWORDS=programming in d tutorial associative arrays diff --git a/target/aa.d b/target/aa.d new file mode 100644 index 0000000..29b4033 --- /dev/null +++ b/target/aa.d @@ -0,0 +1,323 @@ +Ddoc + +$(DERS_BOLUMU $(IX associative array) $(IX AA) Associative Arrays) + +$(P +Associative arrays are a feature that is found in most modern high-level languages. They are very fast data structures that work like mini databases and are used in many programs. +) + +$(P +We saw in the $(LINK2 /ders/d.en/arrays.html, Arrays chapter) that plain arrays are containers that store their elements side-by-side and provide access to them by index. An array that stores the names of the days of the week can be defined like this: +) + +--- + string[] dayNames = + [ "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" ]; +--- + +$(P +The name of a specific day can be accessed by its index in that array: +) + +--- + writeln(dayNames[1]); // prints "Tuesday" +--- + +$(P +The fact that plain arrays provide access to their values through index numbers can be described as an $(I association) of indexes with values. In other words, arrays map indexes to values. Plain arrays can use only integers as indexes. +) + +$(P +Associative arrays allow indexing not only using integers but also using any other type. They map the values of one type to the values of another type. The values of the type that associative arrays $(I map from) are called $(I keys), rather than indexes. Associative arrays store their elements as key-value pairs. +) + +$(P +Associative arrays are implemented in D using a $(I hash table). Hash tables are among the fastest collections for storing and accessing elements. Other than in rare pathological cases, the time it takes to store or access an element is independent of the number of elements that are in the associative array. +) + +$(P +The high performance of hash tables comes at the expense of storing the elements in an unordered way. Also, unlike arrays, the elements of hash tables are not stored side-by-side. +) + +$(P +For plain arrays, index values are not stored at all. Because array elements are stored side-by-side in memory, index values are implicitly the relative positions of elements from the beginning of the array. +) + +$(P +On the other hand, associative arrays do store both the keys and the values of elements. Although this difference makes associative arrays use more memory, it also allows them to use $(I sparse) key values. For example, when there are just two elements to store for keys 0 and 999, an associative array stores just two elements, not 1000 as a plain array has to. +) + +$(H5 Definition) + +$(P +The syntax of associative arrays is similar to the array syntax. The difference is that it is the type of the key that is specified within the square brackets, not the length of the array: +) + +--- + $(I value_type)[$(I key_type)] $(I associative_array_name); +--- + +$(P +For example, an associative array that maps day names of type $(C string) to day numbers of type $(C int) can be defined like this: +) + +--- + int[string] dayNumbers; +--- + +$(P +The $(C dayNumbers) variable above is an associative array that can be used as a table that provides a mapping from day names to day numbers. In other words, it can be used as the opposite of the $(C dayNames) array at the beginning of this chapter. We will use the $(C dayNumbers) associative array in the examples below. +) + +$(P +The keys of associative arrays can be of any type, including user-defined $(C struct) and $(C class) types. We will see user-defined types in later chapters. +) + +$(P +The length of associative arrays cannot be specified when defined. They grow automatically as key-value pairs are added. +) + +$(P +$(I $(B Note:) An associative array that is defined without any element is $(LINK2 /ders/d.en/null_is.html, $(C null)), not empty. This distinction has an important consequence when $(LINK2 /ders/d.en/function_parameters.html, passing associative arrays to functions). We will cover these concepts in later chapters.) +) + +$(H5 Adding key-value pairs) + +$(P +Using the assignment operator is sufficient to build the association between a key and a value: +) + +--- + // associates value 0 with key "Monday" + dayNumbers["Monday"] $(HILITE =) 0; + + // associates value 1 with key "Tuesday" + dayNumbers["Tuesday"] $(HILITE =) 1; +--- + +$(P +The table grows automatically with each association. For example, $(C dayNumbers) would have two key-value pairs after the operations above. This can be demonstrated by printing the entire table: +) + +--- + writeln(dayNumbers); +--- + +$(P +The output indicates that the values 0 and 1 correspond to keys "Monday" and "Tuesday", respectively: +) + +$(SHELL +["Monday":0, "Tuesday":1] +) + +$(P +There can be only one value per key. For that reason, when we assign a new key-value pair and the key already exists, the table does not grow; instead, the value of the existing key changes: +) + +--- + dayNumbers["Tuesday"] = 222; + writeln(dayNumbers); +--- + +$(P +The output: +) + +$(SHELL +["Monday":0, "Tuesday":222] +) + + +$(H5 Initialization) + +$(P +$(IX :, associative array) Sometimes some of the mappings between the keys and the values are already known at the time of the definition of the associative array. Associative arrays are initialized similarly to regular arrays, using a colon to separate each key from its respective value: +) + +--- + // key : value + int[string] dayNumbers = + [ "Monday" : 0, "Tuesday" : 1, "Wednesday" : 2, + "Thursday" : 3, "Friday" : 4, "Saturday" : 5, + "Sunday" : 6 ]; + + writeln(dayNumbers["Tuesday"]); // prints 1 +--- + +$(H5 Removing key-value pairs) + +$(P +Key-value pairs can be removed by using $(C .remove()): +) + +--- + dayNumbers.remove("Tuesday"); + writeln(dayNumbers["Tuesday"]); // ← run-time ERROR +--- + +$(P +The first line above removes the key-value pair "Tuesday" / $(C 1). Since that key is not in the container anymore, the second line would cause an exception to be thrown and the program to be terminated if that exception is not caught. We will see exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter). +) + +$(P +$(C .clear) removes all elements: +) + +--- + dayNumbers.clear; // The associative array becomes empty +--- + +$(H5 $(IX in, associative array) Determining the presence of a key) + +$(P +The $(C in) operator determines whether a given key exists in the associative array: +) + +--- + int[string] colorCodes = [ /* ... */ ]; + + if ("purple" $(HILITE in) colorCodes) { + // key "purple" exists in the table + + } else { + // key "purple" does not exist in the table + } +--- + +$(P +Sometimes it makes sense to use a default value if a key does not exist in the associative array. For example, the special value of -1 can be used as the code for colors that are not in $(C colorCodes). $(C .get()) is useful in such cases: it returns the value associated with the specified key if that key exists, otherwise it returns the default value. The default value is specified as the second parameter of $(C .get()): +) + +--- + int[string] colorCodes = [ "blue" : 10, "green" : 20 ]; + writeln(colorCodes.get("purple", $(HILITE -1))); +--- + +$(P +Since the array does not contain a value for the key $(STRING "purple"), $(C .get()) returns -1: +) + +$(SHELL +-1 +) + +$(H5 Properties) + +$(UL + +$(LI $(IX .length) $(C .length) returns the number of key-value pairs.) + +$(LI $(IX .keys) $(C .keys) returns a copy of all keys as a dynamic array.) + +$(LI $(IX .byKey) $(C .byKey) provides access to the keys without copying them; we will see how $(C .byKey) is used in $(C foreach) loops in the next chapter.) + +$(LI $(IX .values) $(C .values) returns a copy of all values as a dynamic array.) + +$(LI $(IX .byValue) $(C .byValue) provides access to the values without copying them.) + +$(LI $(IX .byKeyValue) $(C .byKeyValue) provides access to the key-value pairs without copying them.) + +$(LI $(IX .rehash) $(C .rehash) may make the array more efficient in some cases, such as after inserting a large number of key-value pairs.) + +$(LI $(IX .sizeof, associative array) $(C .sizeof) is the size of the array $(I reference) (it has nothing to do with the number of key-value pairs in the table and is the same value for all associative arrays).) + +$(LI $(IX .get) $(C .get) returns the value if it exists, the default value otherwise.) + +$(LI $(IX .remove, associative array) $(C .remove) removes the specified key and its value from the array.) + +$(LI $(IX .clear) $(C .clear) removes all elements.) + +) + +$(H5 Example) + +$(P +Here is a program that prints the Turkish names of colors that are specified in English: +) + +--- +import std.stdio; +import std.string; + +void main() { + string[string] colors = [ "black" : "siyah", + "white" : "beyaz", + "red" : "kırmızı", + "green" : "yeşil", + "blue" : "mavi" ]; + + writefln("I know the Turkish names of these %s colors: %s", + colors.length, colors.keys); + + write("Please ask me one: "); + string inEnglish = strip(readln()); + + if (inEnglish in colors) { + writefln("\"%s\" is \"%s\" in Turkish.", + inEnglish, colors[inEnglish]); + + } else { + writeln("I don't know that one."); + } +} +--- + +$(PROBLEM_COK + +$(PROBLEM +How can all of the key-value pairs of an associative array be removed other than calling $(C .clear)? ($(C .clear) is the most natural method.) There are at least three methods: + +$(UL + +$(LI Removing them one-by-one from the associative array.) + +$(LI Assigning an empty associative array.) + +$(LI Similar to the previous method, assigning the array's $(C .init) property. + +$(P +$(IX .init, clearing a variable) $(I $(B Note:) The $(C .init) property of any variable or type is the initial value of that type:) +) + +--- + number = int.init; // 0 for int +--- +) + +) + +) + +$(PROBLEM +Just like with arrays, there can be only one value for each key. This may be seen as a limitation for some applications. + +$(P +Assume that an associative array is used for storing student grades. For example, let's assume that the grades 90, 85, 95, etc. are to be stored for the student named "emre". +) + +$(P +Associative arrays make it easy to access the grades by the name of the student as in $(C grades["emre"]). However, the grades cannot be inserted as in the following code because each grade would overwrite the previous one: +) + +--- + int[string] grades; + grades["emre"] = 90; + grades["emre"] = 85; // ← Overwrites the previous grade! +--- + +$(P +How can you solve this problem? Define an associative array that can store multiple grades per student. +) + +) + +) + +Macros: + SUBTITLE=Associative Arrays + + DESCRIPTION=The associative arrays of the d programming language. + + KEYWORDS=d programming language tutorial book associative arrays diff --git a/target/alias.d b/target/alias.d new file mode 100644 index 0000000..46c5459 --- /dev/null +++ b/target/alias.d @@ -0,0 +1,447 @@ +Ddoc + +$(DERS_BOLUMU $(IX alias) $(CH4 alias) and $(CH4 with)) + +$(H5 $(C alias)) + +$(P +The $(C alias) keyword assigns aliases to existing names. $(C alias) is different from and unrelated to $(C alias this). +) + +$(H6 Shortening a long name) + +$(P +As we have encountered in the previous chapter, some names may become too long to be convenient. Let's consider the following function from that chapter: +) + +--- +Stack!(Point!double) randomPoints(size_t count) { + auto points = new Stack!(Point!double); + // ... +} +--- + +$(P +Having to type $(C Stack!(Point!double)) explicitly in multiple places in the program has a number of drawbacks: +) + +$(UL +$(LI +Longer names can make the code harder to read. +) + +$(LI +It is unnecessary to be reminded at every point that the type is the $(C Stack) data structure that contains objects of the $(C double) instantiations of the $(C Point) struct template. +) + +$(LI +If the requirements of the program change and e.g. $(C double) needs to be changed to $(C real), this change must be carried out in multiple places. +) + +) + +$(P +These drawbacks can be eliminated by giving a new name to $(C Stack!(Point!double)): +) + +--- +alias $(HILITE Points) = Stack!(Point!double); + +// ... + +$(HILITE Points) randomPoints(size_t count) { + auto points = new $(HILITE Points); + // ... +} +--- + +$(P +It may make sense to go further and define two aliases, one taking advantage of the other: +) + +--- +alias PrecisePoint = Point!double; +alias Points = Stack!PrecisePoint; +--- + +$(P +The syntax of $(C alias) is the following: +) + +--- + alias $(I new_name) = $(I existing_name); +--- + +$(P +After that definition, the new name and the existing name become synonymous: They mean the same thing in the program. +) + +$(P +You may encounter the older syntax of this feature in some programs: +) + +$(MONO + // Use of old syntax is discouraged: + alias $(I existing_name) $(I new_name); +) + +$(P +$(C alias) is also useful when shortening names which otherwise need to be spelled out along with their module names. Let's assume that the name $(C Queen) appears in two separate modules: $(C chess) and $(C palace). When both modules are imported, typing merely $(C Queen) would cause a compilation error: +) + +--- +import chess; +import palace; + +// ... + + Queen person; $(DERLEME_HATASI) +--- + +$(P +The compiler cannot decide which $(C Queen) has been meant: +) + +$(SHELL_SMALL +Error: $(HILITE chess.Queen) at chess.d(1) conflicts with +$(HILITE palace.Queen) at palace.d(1) +) + +$(P +A convenient way of resolving this conflict is to assign aliases to one or more of the names: +) + +--- +import palace; + +alias $(HILITE PalaceQueen) = palace.Queen; + +void main() { + $(HILITE PalaceQueen) person; + // ... + $(HILITE PalaceQueen) anotherPerson; +} +--- + +$(P +$(C alias) works with other names as well. The following code gives a new name to a variable: +) + +--- + int variableWithALongName = 42; + + alias var = variableWithALongName; + var = 43; + + assert(variableWithALongName == 43); +--- + +$(H6 Design flexibility) + +$(P +For flexibility, even fundamental types like $(C int) can have aliases: +) + +--- +alias CustomerNumber = int; +alias CompanyName = string; +// ... + +struct Customer { + CustomerNumber number; + CompanyName company; + // ... +} +--- + +$(P +If the users of this struct always type $(C CustomerNumber) and $(C CompanyName) instead of $(C int) and $(C string), then the design can be changed in the future to some extent, without affecting user code. +) + +$(P +This helps with the readability of code as well. Having the type of a variable as $(C CustomerNumber) conveys more information about the meaning of that variable than $(C int). +) + +$(P +Sometimes such type aliases are defined inside structs and classes and become parts of the interfaces of those types. The following class has a $(C weight) property: +) + +--- +class Box { +private: + + double weight_; + +public: + + double weight() const @property { + return weight_; + } + // ... +} +--- + +$(P +Because the member variable and the property of that class is defined as $(C double), the users would have to use $(C double) as well: +) + +--- + $(HILITE double) totalWeight = 0; + + foreach (box; boxes) { + totalWeight += box.weight; + } +--- + +$(P +Let's compare it to another design where the type of $(C weight) is defined as an $(C alias): +) + +--- +class Box { +private: + + $(HILITE Weight) weight_; + +public: + + alias $(HILITE Weight) = double; + + $(HILITE Weight) weight() const @property { + return weight_; + } + // ... +} +--- + +$(P +Now the user code would normally use $(C Weight) as well: +) + +--- + $(HILITE Box.Weight) totalWeight = 0; + + foreach (box; boxes) { + totalWeight += box.weight; + } +--- + +$(P +With this design, changing the actual type of $(C Weight) in the future would not affect user code. (That is, if the new type supports the $(C +=) operator as well.) +) + +$(H6 $(IX name hiding) Revealing hidden names of superclasses) + +$(P +When the same name appears both in the superclass and in the subclass, the matching names that are in the superclass are hidden. Even a single name in the subclass is sufficient to hide all of the names of the superclass that match that name: +) + +--- +class Super { + void foo(int x) { + // ... + } +} + +class Sub : Super { + void foo() { + // ... + } +} + +void main() { + auto object = new Sub; + object.foo(42); $(DERLEME_HATASI) +} +--- + +$(P +Since the argument is 42, an $(C int) value, one might expect that the $(C Super.foo) function that takes an $(C int) would be called for that use. However, even though their parameter lists are different, $(C Sub.foo) $(I hides) $(C Super.foo) and causes a compilation error. The compiler disregards $(C Super.foo) altogether and reports that $(C Sub.foo) cannot be called by an $(C int): +) + +$(SHELL_SMALL +Error: function $(HILITE deneme.Sub.foo ()) is not callable +using argument types $(HILITE (int)) +) + +$(P + Note that this is not the same as overriding a function of the superclass. For that, the function signatures would be the same and the function would be overridden by the $(C override) keyword. (The $(C override) keyword has been explained in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter).) +) + +$(P +Here, not overriding, but a language feature called $(I name hiding) is in effect. If there were not name hiding, functions that happen to have the same name $(C foo) that are added to or removed from these classes might silently change the function that would get called. Name hiding prevents such surprises. It is a feature of other OOP languages as well. +) + +$(P +$(C alias) can reveal the hidden names when desired: +) + +--- +class Super { + void foo(int x) { + // ... + } +} + +class Sub : Super { + void foo() { + // ... + } + + alias $(HILITE foo) = Super.foo; +} +--- + +$(P +The $(C alias) above brings the $(C foo) names from the superclass into the subclass interface. As a result, the code now compiles and $(C Super.foo) gets called. +) + +$(P +When it is more appropriate, it is possible to bring the names under a different name as well: +) + +--- +class Super { + void foo(int x) { + // ... + } +} + +class Sub : Super { + void foo() { + // ... + } + + alias $(HILITE generalFoo) = Super.foo; +} + +// ... + +void main() { + auto object = new Sub; + object.$(HILITE generalFoo)(42); +} +--- + +$(P +Name hiding affects member variables as well. $(C alias) can bring those names to the subclass interface as well: +) + +--- +class Super { + int city; +} + +class Sub : Super { + string city() const @property { + return "Kayseri"; + } +} +--- + +$(P +Regardless of one being a member variable and the other a member function, the name $(C city) of the subclass hides the name $(C city) of the superclass: +) + +--- +void main() { + auto object = new Sub; + object.city = 42; $(DERLEME_HATASI) +} +--- + +$(P +Similarly, the names of the member variables of the superclass can be brought to the subclass interface by $(C alias), possibly under a different name: +) + +--- +class Super { + int city; +} + +class Sub : Super { + string city() const @property { + return "Kayseri"; + } + + alias $(HILITE cityCode) = Super.city; +} + +void main() { + auto object = new Sub; + object.$(HILITE cityCode) = 42; +} +--- + +$(H5 $(IX with) $(C with)) + +$(P +$(C with) is for removing repeated references to an object or symbol. It takes an expression or a symbol in parentheses and uses that expression or symbol when looking up other symbols that are used inside the scope of $(C with): +) + +--- +struct S { + int i; + int j; +} + +void main() { + auto s = S(); + + with ($(HILITE s)) { + $(HILITE i) = 1; // means s.i + $(HILITE j) = 2; // means s.j + } +} +--- + +$(P +It is possible to create a temporary object inside the parentheses. In that case, the temporary object becomes $(LINK2 /ders/d.en/lvalue_rvalue.html, an lvalue), lifetime of which ends upon leaving the scope: +) + +--- + with (S()) { + i = 1; // the i member of the temporary object + j = 2; // the j member of the temporary object + } +--- + +$(P +As we will see later in $(LINK2 /ders/d.en/pointers.html, the Pointers chapter), it is possible to construct the temporary object with the $(C new) keyword, in which case its lifetime can be extended beyond the scope. +) + +$(P +$(C with) is especially useful with $(C case) sections for removing repeated references to e.g. an $(C enum) type: +) + +--- +enum Color { red, orange } + +// ... + + final switch (c) $(HILITE with (Color)) { + + case red: // means Color.red + // ... + + case orange: // means Color.orange + // ... + } +--- + +$(H5 Summary) + +$(UL + +$(LI $(C alias) assigns aliases to existing names.) + +$(LI $(C with) removes repeated references to the same object or symbol.) + +) + +Macros: + SUBTITLE=alias + + DESCRIPTION=The alias keyword that enables giving new names to existing names. + + KEYWORDS=d programming lesson book tutorial encapsulation diff --git a/target/alias_this.d b/target/alias_this.d new file mode 100644 index 0000000..24559c4 --- /dev/null +++ b/target/alias_this.d @@ -0,0 +1,176 @@ +Ddoc + +$(DERS_BOLUMU $(IX alias this) $(CH4 alias this)) + +$(P +We have seen the individual meanings of the $(C alias) and the $(C this) keywords in previous chapters. These two keywords have a completely different meaning when used together as $(C alias this). +) + +$(P +$(IX automatic type conversion) $(IX type conversion, automatic) $(IX implicit type conversion) $(IX type conversion, implicit) $(C alias this) enables $(I automatic type conversions) (also known as $(I implicit type conversions)) of user-defined types. As we have seen in $(LINK2 /ders/d.en/operator_overloading.html, the Operator Overloading chapter), another way of providing type conversions for a type is by defining $(C opCast) for that type. The difference is that, while $(C opCast) is for explicit type conversions, $(C alias this) is for automatic type conversions. +) + +$(P +The keywords $(C alias) and $(C this) are written separately where the name of a member variable or a member function is specified between them: +) + +--- + alias $(I member_variable_or_member_function) this; +--- + +$(P +$(C alias this) enables the specific conversion from the user-defined type to the type of that member. The value of the member becomes the resulting value of the conversion . +) + +$(P +The following $(C Fraction) example uses $(C alias this) with a $(I member function). The $(C TeachingAssistant) example that is further below will use it with $(I member variables). +) + +$(P +Since the return type of $(C value()) below is $(C double), the following $(C alias this) enables automatic conversion of $(C Fraction) objects to $(C double) values: +) + +--- +import std.stdio; + +struct Fraction { + long numerator; + long denominator; + + $(HILITE double value()) const @property { + return double(numerator) / denominator; + } + + alias $(HILITE value) this; + + // ... +} + +double calculate(double lhs, double rhs) { + return 2 * lhs + rhs; +} + +void main() { + auto fraction = Fraction(1, 4); // meaning 1/4 + writeln(calculate($(HILITE fraction), 0.75)); +} +--- + +$(P +$(C value()) gets called automatically to produce a $(C double) value when $(C Fraction) objects appear in places where a $(C double) value is expected. That is why the variable $(C fraction) can be passed to $(C calculate()) as an argument. $(C value()) returns 0.25 as the value of 1/4 and the program prints the result of 2 * 0.25 + 0.75: +) + +$(SHELL +1.25 +) + +$(H5 $(IX multiple inheritance) $(IX inheritance, multiple) Multiple inheritance) + +$(P +We have seen in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter) that classes can inherit from only one $(C class). (On the other hand, there is no limit in the number of $(C interface)s to inherit from.) Some other object oriented languages allow inheriting from multiple classes. This is called $(I multiple inheritance). +) + +$(P +$(C alias this) enables using D classes in designs that could benefit from multiple inheritance. Multiple $(C alias this) declarations enable types to be used in places of multiple different types. +) + +$(P +$(HILITE $(I $(B Note:) dmd 2.074.0, the compiler that was used last to compile the examples in this chapter, allowed only one $(C alias this) declaration.)) +) + +$(P +The following $(C TeachingAssistant) class has two member variables of types $(C Student) and $(C Teacher). The $(C alias this) declarations would allow objects of this type to be used in places of both $(C Student) and $(C Teacher): +) + +--- +import std.stdio; + +class Student { + string name; + uint[] grades; + + this(string name) { + this.name = name; + } +} + +class Teacher { + string name; + string subject; + + this(string name, string subject) { + this.name = name; + this.subject = subject; + } +} + +class TeachingAssistant { + Student studentIdentity; + Teacher teacherIdentity; + + this(string name, string subject) { + this.studentIdentity = new Student(name); + this.teacherIdentity = new Teacher(name, subject); + } + + /* The following two 'alias this' declarations will enable + * this type to be used both as a Student and as a Teacher. + * + * Note: dmd 2.074.0 did not support multiple 'alias this' + * declarations. */ + alias $(HILITE teacherIdentity) this; + $(CODE_COMMENT_OUT compiler limitation)alias $(HILITE studentIdentity) this; +} + +void attendClass(Teacher teacher, Student[] students) +in { + assert(teacher !is null); + assert(students.length > 0); + +} body { + writef("%s is teaching %s to the following students:", + teacher.name, teacher.subject); + + foreach (student; students) { + writef(" %s", student.name); + } + + writeln(); +} + +void main() { + auto students = [ new Student("Shelly"), + new Student("Stan") ]; + + /* An object that can be used both as a Teacher and a + * Student: */ + auto tim = new TeachingAssistant("Tim", "math"); + + // 'tim' is the teacher in the following use: + attendClass($(HILITE tim), students); + + // 'tim' is one of the students in the following use: + auto amy = new Teacher("Amy", "physics"); + $(CODE_COMMENT_OUT compiler limitation)attendClass(amy, students ~ $(HILITE tim)); +} +--- + +$(P +The output of the program shows that the same object has been used as two different types: +) + +$(SHELL +$(HILITE Tim) is teaching math to the following students: Shelly Stan +Amy is teaching physics to the following students: Shelly Stan $(HILITE Tim) +) + +Macros: + SUBTITLE=alias this + + DESCRIPTION=Nesnelerin otomatik olarak başka tür olarak kullanılmalarını sağlayan 'alias this'. + + KEYWORDS=d programlama dili ders dersler öğrenmek tutorial alias takma isim alias this + +SOZLER= +$(kalitim) + diff --git a/target/arithmetic.cozum.d b/target/arithmetic.cozum.d new file mode 100644 index 0000000..9ced251 --- /dev/null +++ b/target/arithmetic.cozum.d @@ -0,0 +1,160 @@ +Ddoc + +$(COZUM_BOLUMU Integers and Arithmetic Operations) + +$(OL + +$(LI +We can use the $(C /) operator for the division and the $(C %) operator for the remainder: + +--- +import std.stdio; + +void main() { + int first; + write("Please enter the first number: "); + readf(" %s", &first); + + int second; + write("Please enter the second number: "); + readf(" %s", &second); + + int quotient = first / second; + int remainder = first % second; + + writeln(first, " = ", + second, " * ", quotient, " + ", remainder); +} +--- + +) + +$(LI +We can determine whether the remainder is 0 or not with an $(C if) statement: + +--- +import std.stdio; + +void main() { + int first; + write("Please enter the first number: "); + readf(" %s", &first); + + int second; + write("Please enter the second number: "); + readf(" %s", &second); + + int quotient = first / second; + int remainder = first % second; + + // We cannot call writeln up front before determining + // whether the remainder is 0 or not. We must terminate + // the line later with a writeln. + write(first, " = ", second, " * ", quotient); + + // The remainder must be printed only if nonzero. + if (remainder != 0) { + write(" + ", remainder); + } + + // We are now ready to terminate the line. + writeln(); +} +--- + +) + +$(LI + +--- +import std.stdio; + +void main() { + while (true) { + write("0: Exit, 1: Add, 2: Subtract, 3: Multiply,", + " 4: Divide - Please enter the operation: "); + + int operation; + readf(" %s", &operation); + + // Let's first validate the operation + if ((operation < 0) || (operation > 4)) { + writeln("I don't know this operation"); + continue; + } + + if (operation == 0){ + writeln("Goodbye!"); + break; + } + + // If we are here, we know that we are dealing with + // one of the four operations. Now is the time to read + // two integers from the user: + + int first; + int second; + + write(" First number: "); + readf(" %s", &first); + + write("Second number: "); + readf(" %s", &second); + + int result; + + if (operation == 1) { + result = first + second; + + } else if (operation == 2) { + result = first - second; + + } else if (operation == 3) { + result = first * second; + + } else if (operation == 4) { + result = first / second; + + } else { + writeln( + "There is an error! ", + "This condition should have never occurred."); + break; + } + + writeln(" Result: ", result); + } +} +--- + +) + +$(LI + +--- +import std.stdio; + +void main() { + int value = 1; + + while (value <= 10) { + if (value != 7) { + writeln(value); + } + + ++value; + } +} +--- + +) + +) +$(Ergin) + +Macros: + SUBTITLE=Integers and Arithmetic Operations Solutions + + DESCRIPTION=Programming in D exercise solutions: integers and arithmetic operations + + KEYWORDS=programming in d tutorial arithmetic operations diff --git a/target/arithmetic.d b/target/arithmetic.d new file mode 100644 index 0000000..3fe00de --- /dev/null +++ b/target/arithmetic.d @@ -0,0 +1,871 @@ +Ddoc + +$(DERS_BOLUMU $(IX arithmetic operation) Integers and Arithmetic Operations) + +$(P +We have seen that the $(C if) and $(C while) statements allow programs to make decisions by using the $(C bool) type in the form of logical expressions. In this chapter, we will see arithmetic operations on the $(I integer) types of D. These features will allow us to write much more useful programs. +) + +$(P +Although arithmetic operations are a part of our daily lives and are actually simple, there are very important concepts that a programmer must be aware of in order to produce correct programs: the $(I bit length of a type), $(I overflow) (wrap), and $(I truncation). +) + +$(P +Before going further, I would like to summarize the arithmetic operations in the following table as a reference: +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Operator Effect Sample
++increments by one++variable
--decrements by one--variable
+the result of adding two valuesfirst + second
-the result of subtracting 'second' from 'first'first - second
*the result of multiplying two valuesfirst * second
/the result of dividing 'first' by 'second'first / second
%the remainder of dividing 'first' by 'second'first % second
^^the result of raising 'first' to the power of 'second'$(BR)(multiplying 'first' by itself 'second' times)first ^^ second
+ +$(P +$(IX +=) $(IX -=) $(IX *=) $(IX /=) $(IX %=) $(IX ^^=) Most of those operators have counterparts that have an $(C =) sign attached: $(C +=), $(C -=), $(C *=), $(C /=), $(C %=), and $(C ^^=). The difference with these operators is that they assign the result to the left-hand side: +) + +--- + variable += 10; +--- + +$(P +That expression adds the value of $(C variable) and 10 and assigns the result to $(C variable). In the end, the value of $(C variable) would be increased by 10. It is the equivalent of the following expression: +) + +--- + variable = variable + 10; +--- + +$(P +I would like also to summarize two important concepts here before elaborating on them below. +) + +$(P +$(IX wrap) $(B Overflow:) Not all values can fit in a variable of a given type. If the value is too big for the variable we say that the variable $(I overflows). For example, a variable of type $(C ubyte) can have values only in the range of 0 to 255; so when assigned 260, the variable overflows, wraps around, and its value becomes 4. ($(I $(B Note:) Unlike some other languages like C and C++, overflow for signed types is legal in D. It has the same wrap around behavior of unsigned types.)) +) + +$(P +Similarly, a variable cannot have a value that is less than the minimum value of its type. +) + +$(P +$(B Truncation:) Integer types cannot have values with fractional parts. For example, the value of the $(C int) expression $(C 3/2) is 1, not 1.5. +) + +$(P +We encounter arithmetic operations daily without many surprises: if a bagel is $1, two bagels are $2; if four sandwiches are $15, one sandwich is $3.75, etc. +) + +$(P +Unfortunately, things are not as simple with arithmetic operations in computers. If we don't understand how values are stored in a computer, we may be surprised to see that a company's debt is $(I reduced) to $1.7 billion when it borrows $3 billion more on top of its existing debt of $3 billion! Or when a box of ice cream serves 4 kids, an arithmetic operation may claim that 2 boxes would be sufficient for 11 kids! +) + +$(P +Programmers must understand how integers are stored in computers.) + +$(H6 $(IX integer) Integer types) + +$(P +Integer types are the types that can have only whole values like -2, 0, 10, etc. These types cannot have fractional parts, as in 2.5. All of the integer types that we saw in the $(LINK2 /ders/d.en/types.html, Fundamental Types chapter) are the following: +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
$(BR)Type Number of$(BR)Bits Initial$(BR)Value
byte80
ubyte80
short160
ushort160
int320
uint320
long640L
ulong640L
+ +$(P +The $(C u) at the beginning of the type names stands for "unsigned" and indicates that such types cannot have values less than zero. +) + +$(H6 $(IX bit) Number of bits of a type) + +$(P +In today's computer systems, the smallest unit of information is called a $(I bit). At the physical level, a bit is represented by electrical signals around certain points in the circuitry of a computer. A bit can be in one of two states that correspond to different voltages in the area that defines that particular bit. These two states are arbitrarily defined to have the values 0 and 1. As a result, a bit can have one of these two values. +) + +$(P +As there aren't many concepts that can be represented by just two states, bit is not a very useful type. It can only be useful for concepts with two states like heads or tails or whether a light switch is on or off. +) + +$(P +If we consider two bits together, the total amount of information that can be represented multiplies. Based on each bit having a value of 0 or 1 individually, there are a total of 4 possible states. Assuming that the left and right digits represent the first and second bit respectively, these states are 00, 01, 10, and 11. Let's add one more bit to see this effect better; three bits can be in 8 different states: 000, 001, 010, 011, 100, 101, 110, 111. As can be seen, each added bit doubles the total number of states that can be represented. +) + +$(P +The values to which these eight states correspond to are defined by conventions. The following table shows these values for the signed and unsigned representations of 3 bits: +) + + + + + + + + + + + +
Bit State Unsigned Value Signed Value
000 0 0
001 1 1
010 2 2
011 3 3
100 4 -4
101 5 -3
110 6 -2
111 7 -1
+ +$(P +We can construct the following table by adding more bits: +) + + + + + + + + + + + + + + + + + + +
Bits Number of Distinct ValuesD TypeMinimum ValueMaximum Value
12
24
38
416
532
664
7128
8256 byte$(BR)ubyte-128$(BR)0127$(BR)255
......
1665536 short$(BR)ushort-32768$(BR)032767$(BR)65535
......
324294967296 int$(BR)uint-2147483648$(BR)02147483647$(BR)4294967295
......
6418446744073709551616 long$(BR)ulong-9223372036854775808$(BR)09223372036854775807$(BR)18446744073709551615
......
+ +$(P +I skipped many rows in the table and indicated the signed and unsigned versions of the D types that have the same number of bits on the same row (e.g. $(C int) and $(C uint) are both on the 32-bit row). +) + +$(H6 Choosing a type) + +$(P +Since a 3-bit type can only have 8 distinct values, it can only represent concepts like the value of a die or the number of the day of the week. (This is just an example; there isn't a 3-bit type in D.) +) + +$(P +On the other hand, although $(C uint) is a very large type, it cannot represent the concept of an ID number for each living person, as its maximum value is less than the world population of 7 billion. $(C long) and $(C ulong) would be more than enough to represent many concepts. +) + +$(P +As a general rule, as long as there is no specific reason not to, you can use $(C int) for integer values. +) + +$(H6 $(IX overflow) Overflow) + +$(P +The fact that types can have a limited range of values may cause unexpected results. For example, although adding two $(C uint) variables with values of 3 billion each should produce 6 billion, because that sum is greater than the maximum value that a $(C uint) variable can hold (about 4 billion), this sum $(I overflows). Without any warning, only the difference of 6 and 4 billion gets stored. (A little more accurately, 6 minus 4.3 billion.) +) + +$(H6 $(IX truncation) Truncation) + +$(P +Since integers cannot have values with fractional parts, they lose the part after the decimal point. For example, assuming that a box of ice cream serves 4 kids, although 11 kids would actually need 2.75 boxes, the fractional part of that value cannot be stored in an integer type, so the value becomes 2. +) + +$(P +I will show limited techniques to help reduce the risk of overflow and truncation later in the chapter. +) + +$(H6 $(C .min) and $(C .max)) + +$(P +I will take advantage of the $(C .min) and $(C .max) properties below, which we have seen in the $(LINK2 /ders/d.en/types.html, Fundamental Types chapter). These properties provide the minimum and maximum values that an integer type can have. +) + +$(H6 $(IX ++, pre-increment) $(IX increment) Increment: $(C ++)) + +$(P +This operator is used with a single variable (more generally, with a single expression) and is written before the name of that variable. It increments the value of that variable by 1: +) + +--- +import std.stdio; + +void main() { + int number = 10; + $(HILITE ++)number; + writeln("New value: ", number); +} +--- + +$(SHELL +New value: 11 +) + +$(P +The increment operator is the equivalent of using the $(I add-and-assign) operator with the value of 1: +) + +--- + number += 1; // same as ++number +--- + +$(P +If the result of the increment operation is greater than the maximum value of that type, the result $(I overflows) and becomes the minimum value. We can see this effect by incrementing a variable that initially has the value $(C int.max): +) + +--- +import std.stdio; + +void main() { + writeln("minimum int value : ", int.min); + writeln("maximum int value : ", int.max); + + int number = int.max; + writeln("before the increment: ", number); + ++number; + writeln("after the increment : ", number); +} +--- + +$(P +The value becomes $(C int.min) after the increment: +) + +$(SHELL +minimum int value : -2147483648 +maximum int value : 2147483647 +before the increment: 2147483647 +after the increment : -2147483648 +) + +$(P +This is a very important observation, because the value changes from the maximum to the minimum as a result of $(I incrementing) and without any warning! This effect is called $(I overflow). We will see similar effects with other operations. +) + +$(H6 $(IX --, pre-decrement) $(IX decrement) Decrement: $(C --)) + +$(P +This operator is similar to the increment operator; the difference is that the value is decreased by 1: +) + +--- + --number; // the value decreases by 1 +--- + +$(P +The decrement operation is the equivalent of using the $(I subtract-and-assign) operator with the value of 1: +) + +--- + number -= 1; // same as --number +--- + + +$(P +Similar to the $(C ++) operator, if the value is the minimum value to begin with, it becomes the maximum value. This effect is called $(I overflow) as well. +) + +$(H6 $(IX +, addition) $(IX addition) Addition: +) + +$(P +This operator is used with two expressions and adds their values: +) + +--- +import std.stdio; + +void main() { + int first = 12; + int second = 100; + + writeln("Result: ", first $(HILITE +) second); + writeln("With a constant expression: ", 1000 $(HILITE +) second); +} +--- + +$(SHELL +Result: 112 +With a constant expression: 1100 +) + +$(P +If the result is greater than the sum of the two expressions, it again overflows and becomes a value that is less than both of the expressions: +) + +--- +import std.stdio; + +void main() { + // 3 billion each + uint first = 3000000000; + uint second = 3000000000; + + writeln("maximum value of uint: ", uint.max); + writeln(" first: ", first); + writeln(" second: ", second); + writeln(" sum: ", first + second); + writeln("OVERFLOW! The result is not 6 billion!"); +} +--- + +$(SHELL +maximum value of uint: 4294967295 + first: 3000000000 + second: 3000000000 + sum: 1705032704 +OVERFLOW! The result is not 6 billion! +) + +$(H6 $(IX -, subtraction) $(IX subtraction) Subtraction: $(C -)) + +$(P +This operator is used with two expressions and gives the difference between the first and the second: +) + +--- +import std.stdio; + +void main() { + int number_1 = 10; + int number_2 = 20; + + writeln(number_1 $(HILITE -) number_2); + writeln(number_2 $(HILITE -) number_1); +} +--- + +$(SHELL +-10 +10 +) + +$(P +It is again surprising if the actual result is less than zero and is stored in an unsigned type. Let's rewrite the program using the $(C uint) type: +) + +--- +import std.stdio; + +void main() { + uint number_1 = 10; + uint number_2 = 20; + + writeln("PROBLEM! uint cannot have negative values:"); + writeln(number_1 - number_2); + writeln(number_2 - number_1); +} +--- + +$(SHELL +PROBLEM! uint cannot have negative values: +4294967286 +10 +) + +$(P +It is a good guideline to use signed types to represent concepts that may ever be subtracted. As long as there is no specific reason not to, you can choose $(C int). +) + +$(H6 $(IX *, multiplication) $(IX multiplication) Multiplication: $(C *)) + +$(P +This operator multiplies the values of two expressions; the result is again subject to overflow: +) + +--- +import std.stdio; + +void main() { + uint number_1 = 6; + uint number_2 = 7; + + writeln(number_1 $(HILITE *) number_2); +} +--- + +$(SHELL +42 +) + +$(H6 $(IX /) $(IX division) Division: $(C /)) + +$(P +This operator divides the first expression by the second expression. Since integer types cannot have fractional values, the fractional part of the value is discarded. This effect is called $(I truncation). As a result, the following program prints 3, not 3.5: +) + +--- +import std.stdio; + +void main() { + writeln(7 $(HILITE /) 2); +} +--- + +$(SHELL +3 +) + +$(P +For calculations where fractional parts matter, $(I floating point types) must be used instead of integers. We will see floating point types in the next chapter. +) + +$(H6 $(IX %) $(IX remainder) $(IX modulus) Remainder (modulus): %) + +$(P +This operator divides the first expression by the second expression and produces the remainder of the division: +) + +--- +import std.stdio; + +void main() { + writeln(10 $(HILITE %) 6); +} +--- + +$(SHELL +4 +) + +$(P +A common application of this operator is to determine whether a value is odd or even. Since the remainder of dividing an even number by 2 is always 0, comparing the result against 0 is sufficient to make that distinction: +) + +--- + if ((number % 2) == 0) { + writeln("even number"); + + } else { + writeln("odd number"); + } +--- + +$(H6 $(IX ^^) $(IX power of) Power: ^^) + +$(P +This operator raises the first expression to the power of the second expression. For example, raising 3 to the power of 4 is multiplying 3 by itself 4 times: +) + +--- +import std.stdio; + +void main() { + writeln(3 $(HILITE ^^) 4); +} +--- + +$(SHELL +81 +) + +$(H6 $(IX assignment, operation result) Arithmetic operations with assignment) + +$(P +All of the operators that take two expressions have $(I assignment) counterparts. These operators assign the result back to the expression that is on the left-hand side: +) + +--- +import std.stdio; + +void main() { + int number = 10; + + number += 20; // same as number = number + 20; now 30 + number -= 5; // same as number = number - 5; now 25 + number *= 2; // same as number = number * 2; now 50 + number /= 3; // same as number = number / 3; now 16 + number %= 7; // same as number = number % 7; now 2 + number ^^= 6; // same as number = number ^^ 6; now 64 + + writeln(number); +} +--- + +$(SHELL +64 +) + +$(H6 $(IX -, negation) $(IX negation) Negation: $(C -)) + +$(P +This operator converts the value of the expression from negative to positive or positive to negative: +) + +--- +import std.stdio; + +void main() { + int number_1 = 1; + int number_2 = -2; + + writeln($(HILITE -)number_1); + writeln($(HILITE -)number_2); +} +--- + +$(SHELL +-1 +2 +) + +$(P +The type of the result of this operation is the same as the type of the expression. Since unsigned types cannot have negative values, the result of using this operator with unsigned types can be surprising: +) + +--- + $(HILITE uint) number = 1; + writeln("negation: ", -number); +--- + +$(P +The type of $(C -number) is $(C uint) as well, which cannot have negative values: +) + +$(SHELL +negation: 4294967295 +) + +$(H6 $(IX +, plus sign) $(IX plus sign) Plus sign: $(C +)) + +$(P +This operator has no effect and exists only for symmetry with the negation operator. Positive values stay positive and negative values stay negative: +) + +--- +import std.stdio; + +void main() { + int number_1 = 1; + int number_2 = -2; + + writeln($(HILITE +)number_1); + writeln($(HILITE +)number_2); +} +--- + +$(SHELL +1 +-2 +) + +$(H6 $(IX ++, post-increment) $(IX post-increment) $(IX increment, post) Post-increment: $(C ++)) + +$(P +$(I $(B Note:) Unless there is a strong reason, always use the regular increment operator (which is sometimes called the pre-increment operator).) +) + +$(P +Contrary to the regular increment operator, it is written after the expression and still increments the value of the expression by 1. The difference is that the post-increment operation produces the old value of the expression. To see this difference, let's compare it with the regular increment operator: +) + +--- +import std.stdio; + +void main() { + int incremented_regularly = 1; + writeln(++incremented_regularly); // prints 2 + writeln(incremented_regularly); // prints 2 + + int post_incremented = 1; + + // Gets incremented, but its old value is used: + writeln(post_incremented$(HILITE ++)); // prints 1 + writeln(post_incremented); // prints 2 +} +--- + +$(SHELL +2 +2 +1 +2 +) + +$(P +The $(C writeln(post_incremented++);) statement above is the equivalent of the following code: +) + +--- + int old_value = post_incremented; + ++post_incremented; + writeln(old_value); // prints 1 +--- + +$(H6 $(IX --, post-decrement) $(IX post-decrement) $(IX decrement, post) Post-decrement: $(C --)) + +$(P +$(I $(B Note:) Unless there is a strong reason, always use the regular decrement operator (which is sometimes called the pre-decrement operator).) +) + +$(P +This operator behaves the same way as the post-increment operator except that it decrements. +) + +$(H6 Operator precedence) + +$(P +The operators we've discussed above have all been used in operations on their own with only one or two expressions. However, similar to logical expressions, it is common to combine these operators to form more complex arithmetic expressions: +) + +--- + int value = 77; + int result = (((value + 8) * 3) / (value - 1)) % 5; +--- + +$(P +As with logical operators, arithmetic operators also obey operator precedence rules. For example, the $(C *) operator has precedence over the $(C +) operator. For that reason, when parentheses are not used (e.g. in the $(C value + 8 * 3) expression), the $(C *) operator is evaluated before the $(C +) operator. As a result, that expression becomes the equivalent of $(C value + 24), which is quite different from $(C (value + 8) * 3). +) + +$(P +Using parentheses is useful both for ensuring correct results and for communicating the intent of the code to programmers who may work on the code in the future. +) + +$(P +The operator precedence table will be presented $(LINK2 /ders/d.en/operator_precedence.html, later in the book). +) + +$(H6 Detecting overflow) + +$(P +$(IX core.checkedint) $(IX checkedint) $(IX adds) $(IX addu) $(IX subs) $(IX subu) $(IX muls) $(IX mulu) $(IX negs) Although it uses $(LINK2 /ders/d.en/functions.html, functions) and $(LINK2 /ders/d.en/function_parameters.html, $(C ref) parameters), which we have not covered yet, I would like to mention here that $(LINK2 http://dlang.org/phobos/core_checkedint.html, the $(C core.checkedint) module) contains arithmetic functions that detect overflow. Instead of operators like $(C +) and $(C -), this module uses functions: $(C adds) and $(C addu) for signed and unsigned addition, $(C muls) and $(C mulu) for signed and unsigned multiplication, $(C subs) and $(C subu) for signed and unsigned subtraction, and $(C negs) for negation. +) + +$(P +For example, assuming that $(C a) and $(C b) are two $(C int) variables, the following code would detect whether adding them has caused an overflow: +) + +--- + // This variable will become 'true' if the addition + // operation inside the 'adds' function overflows: + bool hasOverflowed = false; + int result = adds(a, b, $(HILITE hasOverflowed)); + + if (hasOverflowed) { + // We must not use 'result' because it has overflowed + // ... + + } else { + // We can use 'result' + // ... + } +--- + +$(P +$(IX experimental.checkedint) $(IX Checked) There is also $(LINK2 https://dlang.org/phobos/std_experimental_checkedint.html, the std.experimental.checkedint) module that defines the $(C Checked) template but both its usage and its implementation are too advanced at this point in the book. +) + +$(H6 Preventing overflow) + +$(P +If the result of an operation cannot fit in the type of the result, then there is nothing that can be done. Sometimes, although the ultimate result would fit in a certain type, the intermediate calculations may overflow and cause incorrect results. +) + +$(P +As an example, let's assume that we need to plant an apple tree per 1000 square meters of an area that is 40 by 60 kilometers. How many trees are needed? +) + +$(P +When we solve this problem on paper, we see that the result is 40000 times 60000 divided by 1000, being equal to 2.4 million trees. Let's write a program that executes this calculation: +) + +--- +import std.stdio; + +void main() { + int width = 40000; + int length = 60000; + int areaPerTree = 1000; + + int treesNeeded = width * length / areaPerTree; + + writeln("Number of trees needed: ", treesNeeded); +} +--- + +$(SHELL +Number of trees needed: -1894967 +) + +$(P +Not to mention it is not even close, the result is also less than zero! In this case, the intermediate calculation $(C width * length) overflows and the subsequent calculation of $(C / areaPerTree) produces an incorrect result. +) + +$(P +One way of avoiding the overflow in this example is to change the order of operations: +) + +--- + int treesNeeded = width / areaPerTree * length ; +--- + +$(P +The result would now be correct: +) + +$(SHELL +Number of trees needed: 2400000 +) + +$(P +The reason this method works is the fact that all of the steps of the calculation now fit the $(C int) type. +) + +$(P +Please note that this is not a complete solution because this time the intermediate value is prone to truncation, which may affect the result significantly in certain other calculations. Another solution might be to use a floating point type instead of an integer type: $(C float), $(C double), or $(C real). +) + +$(H6 Preventing truncation) + +$(P +Changing the order of operations may be a solution to truncation as well. An interesting example of truncation can be seen by dividing and multiplying a value with the same number. We would expect the result of 10/9*9 to be 10, but it comes out as 9: +) + +--- +import std.stdio; + +void main() { + writeln(10 / 9 * 9); +} +--- + +$(SHELL +9 +) + +$(P +The result is correct when truncation is avoided by changing the order of operations: +) + +--- + writeln(10 * 9 / 9); +--- + +$(SHELL +10 +) + +$(P +This too is not a complete solution: This time the intermediate calculation could be prone to overflow. Using a floating point type may be another solution to truncation in certain calculations. +) + +$(PROBLEM_COK + +$(PROBLEM +Write a program that takes two integers from the user, prints the integer quotient resulting from the division of the first by the second, and also prints the remainder. For example, when 7 and 3 are entered, have the program print the following equation: + +$(SHELL +7 = 3 * 2 + 1 +) + +) + +$(PROBLEM +Modify the program to print a shorter output when the remainder is 0. For example, when 10 and 5 are entered, it should not print "10 = 5 * 2 + 0" but just the following: + +$(SHELL +10 = 5 * 2 +) + +) + +$(PROBLEM +Write a simple calculator that supports the four basic arithmetic operations. Have the program let the operation to be selected from a menu and apply that operation to the two values that are entered. You can ignore overflow and truncation in this program. +) + +$(PROBLEM +Write a program that prints the values from 1 to 10, each on a separate line, with the exception of value 7. Do not use repeated lines like the following: + +--- +import std.stdio; + +void main() { + // Do not do this! + writeln(1); + writeln(2); + writeln(3); + writeln(4); + writeln(5); + writeln(6); + writeln(8); + writeln(9); + writeln(10); +} +--- + +$(P +Instead, imagine a variable whose value is incremented in a loop. You may need to take advantage of the $(I is not equal to) operator $(C !=) here. +) + +) + +) + +Macros: + SUBTITLE=Integers and Arithmetic Operations + + DESCRIPTION=The integer arithmetic operations of the D language + + KEYWORDS=d programming language tutorial book integer arithmetic operations + +MINI_SOZLUK= +$(Ergin) diff --git a/target/arrays.cozum.d b/target/arrays.cozum.d new file mode 100644 index 0000000..3e0d3fa --- /dev/null +++ b/target/arrays.cozum.d @@ -0,0 +1,133 @@ +Ddoc + +$(COZUM_BOLUMU Arrays) + +$(OL + +$(LI + +--- +import std.stdio; +import std.algorithm; + +void main() { + write("How many values will be entered? "); + int count; + readf(" %s", &count); + + double[] values; + values.length = count; + + // The counter is commonly named as 'i' + int i; + while (i < count) { + write("Value ", i, ": "); + readf(" %s", &values[i]); + ++i; + } + + writeln("In sorted order:"); + sort(values); + + i = 0; + while (i < count) { + write(values[i], " "); + ++i; + } + writeln(); + + writeln("In reverse order:"); + reverse(values); + + i = 0; + while (i < count) { + write(values[i], " "); + ++i; + } + writeln(); +} +--- + +) + +$(LI +The explanations are included as code comments: + +--- +import std.stdio; +import std.algorithm; + +void main() { + // Using dynamic arrays because it is not known how many + // values are going to be read from the input + int[] odds; + int[] evens; + + writeln("Please enter integers (-1 to terminate):"); + + while (true) { + + // Reading the value + int value; + readf(" %s", &value); + + // The special value of -1 breaks the loop + if (value == -1) { + break; + } + + // Adding to the corresponding array, depending on + // whether the value is odd or even. It is an even + // number if there is no remainder when divided by 2. + if ((value % 2) == 0) { + evens ~= value; + + } else { + odds ~= value; + } + } + + // The odds and evens arrays are sorted separately + sort(odds); + sort(evens); + + // The two arrays are then appended to form a new array + int[] result; + result = odds ~ evens; + + writeln("First the odds then the evens, sorted:"); + + // Printing the array elements in a loop + int i; + while (i < result.length) { + write(result[i], " "); + ++i; + } + + writeln(); +} +--- + +) + +$(LI +There are three mistakes (bugs) in this program. The first two are with the $(C while) loops: Both of the loop conditions use the $(C <=) operator instead of the $(C <) operator. As a result, the program uses invalid indexes and attempts to access elements that are not parts of the arrays. + +$(P +Since it is more beneficial for you to debug the third mistake yourself, I would like you to first run the program after fixing the previous two bugs. You will notice that the program will not print the results. Can you figure out the remaining problem before reading the following paragraph? +) + +$(P +The value of $(C i) is 5 when the first $(C while) loop terminates, and that value is causing the logical expression of the second loop to be $(C false), which in turn is preventing the second loop to be entered. The solution is to reset $(C i) to 0 before the second $(C while) loop, for example with the statement $(C i = 0;) +) + +) + +) + +Macros: + SUBTITLE=Arrays Solutions + + DESCRIPTION=Programming in D exercise solutions: arrays + + KEYWORDS=programming in d tutorial arrays solution diff --git a/target/arrays.d b/target/arrays.d new file mode 100644 index 0000000..3f668d6 --- /dev/null +++ b/target/arrays.d @@ -0,0 +1,602 @@ +Ddoc + +$(DERS_BOLUMU $(IX array) Arrays) + +$(P +We have defined five variables in one of the exercises of the last chapter, and used them in certain calculations. The definitions of those variables were the following: +) + +--- + double value_1; + double value_2; + double value_3; + double value_4; + double value_5; +--- + +$(P +This method of defining variables individually does not scale to cases where even more variables are needed. Imagine needing a thousand values; it is almost impossible to define a thousand variables from $(C value_1) to $(C value_1000). +) + +$(P +Arrays are useful in such cases: the array feature allows us to define a single variable that stores multiple values together. Although simple, arrays are the most common data structure used to store a collection of values. +) + +$(P +This chapter covers only some of the features of arrays. More features will be introduced later in $(LINK2 /ders/d.en/slices.html, the Slices and Other Array Features chapter). +) + +$(H5 Definition) + +$(P +The definition of array variables is very similar to the definition of normal variables. The only difference is that the number of values associated with the variable is specified in square brackets. We can contrast the two definitions as follows: +) + +--- + int singleValue; + int[10] arrayOfTenValues; +--- + +$(P +The first line above is the definition of a variable which stores a single value, just like the variables that we have defined so far. The second line is the definition of a variable which stores ten consecutive values. In other words, it stores an array of ten integer values. You can also think of it as defining ten variables of the same type, or as defining an array, for short. +) + +$(P +Accordingly, the equivalent of the five separate variables above can be defined as an array of five values using the following syntax: +) + +--- + double[5] values; +--- + +$(P +$(IX scalar) That definition can be read as $(I 5 double values). Note that I have chosen the name of the array variable as plural to avoid confusing it with a single-valued variable. Variables which only store a single value are called scalar variables. +) + +$(P +In summary, the definition of an array variable consists of the type of the values, the number of values, and the name of the variable that refers to the array of values: +) + +--- + $(I type_name)[$(I value_count)] $(I variable_name); +--- + +$(P +The type of the values can also be a user-defined type. (We will see user-defined types later.) For example: +) + +--- + // An array that holds the weather information of all + // cities. Here, the bool values may mean + // false: overcast + // true : sunny + bool[cityCount] weatherConditions; + + // An array that holds the weights of a hundred boxes + double[100] boxWeights; + + // Information about the students of a school + StudentInformation[studentCount] studentInformation; +--- + +$(H5 $(IX container) $(IX element) Containers and elements) + +$(P +Data structures that bring elements of a certain type together are called $(I containers). According to this definition, arrays are containers. For example, an array that holds the air temperatures of the days in July can bring 31 $(C double) values together and form $(I a container of elements of type $(C double)). +) + +$(P +The variables of a container are called $(I elements). The number of elements of an array is called the $(I length) of the array. +) + +$(H5 $(IX []) Accessing the elements) + +$(P +In order to differentiate the variables in the exercise of the previous chapter, we had to append an underscore and a number to their names as in $(C value_1). This is not possible nor necessary when a single array stores all the values under a single name. Instead, the elements are accessed by specifying the $(I element number) within square brackets: +) + +--- + values[0] +--- + +$(P +That expression can be read as $(I the element with the number 0 of the array named values). In other words, instead of typing $(C value_1) one must type $(C values[0]) with arrays. +) + +$(P +There are two important points worth stressing here: +) + +$(UL + +$(LI $(B The numbers start with zero:) Although humans assign numbers to items starting with 1, the numbers in arrays start at 0. The values that we have numbered as 1, 2, 3, 4, and 5 before are numbered as 0, 1, 2, 3, and 4 in the array. This variation can confuse new programmers. +) + +$(LI $(B Two different uses of the $(C[]) characters:) Don't confuse the two separate uses of the $(C []) characters. When defining arrays, the $(C []) characters are written after the type of the elements and specify the number of elements. When accessing elements, the $(C []) characters are written after the name of the array and specify the number of the element that is being accessed: + +--- + // This is a definition. It defines an array that consists + // of 12 elements. This array is used to hold the number + // of days in each month. + int[12] monthDays; + + // This is an access. It accesses the element that + // corresponds to December and sets its value to 31. + monthDays[11] = 31; + + // This is another access. It accesses the element that + // corresponds to January, the value of which is passed to + // writeln. + writeln("January has ", monthDays[0], " days."); +--- + +$(P +$(B Reminder:) The element numbers of January and December are 0 and 11 respectively; not 1 and 12. +) + +) + +) + +$(H5 $(IX index) Index) + +$(P +The number of an element is called its $(I index) and the act of accessing an element is called $(I indexing). +) + +$(P +An index need not be a constant value; the value of a variable can also be used as an index, making arrays even more useful. For example, the month can be determined by the value of the $(C monthIndex) variable below: +) + +--- + writeln("This month has ", monthDays[monthIndex], " days."); +--- + +$(P +When the value of $(C monthIndex) is 2, the expression above would print the value of $(C monthDays[2]), the number of days in March. +) + +$(P +Only the index values between zero and one less than the length of the array are valid. For example, the valid indexes of a three-element array are 0, 1, and 2. Accessing an array with an invalid index causes the program to be terminated with an error. +) + +$(P +Arrays are containers where the elements are placed side by side in the computer's memory. For example, the elements of the array holding the number of days in each month can be shown like the following (assuming a year when February has 28 days): +) + +$(MONO + indexes → 0 1 2 3 4 5 6 7 8 9 10 11 + elements → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | +) + +$(P +$(I $(B Note:) The indexes above are for demonstration purposes only; they are not stored in the computer's memory.) +) + +$(P +The element at index 0 has the value 31 (number of days in January); the element at index 1 has the value of 28 (number of days in February), etc. +) + +$(H5 $(IX fixed-length array) $(IX dynamic array) $(IX static array) Fixed-length arrays vs. dynamic arrays) + +$(P +When the length of an array is specified when the program is written, that array is a $(I fixed-length array). When the length can change during the execution of the program, that array is a $(I dynamic array). +) + +$(P +Both of the arrays that we have defined above are fixed-length arrays because their element counts are specified as 5 and 12 at the time when the program is written. The lengths of those arrays cannot be changed during the execution of the program. To change their lengths, the source code must be modified and the program must be recompiled. +) + +$(P +Defining dynamic arrays is simpler than defining fixed-length arrays because omitting the length makes a dynamic array: +) + +--- + int[] dynamicArray; +--- + +$(P +The length of such an array can increase or decrease during the execution of the program. +) + +$(P +Fixed-length arrays are also known as static arrays. +) + +$(H5 $(IX .length) Using $(C .length) to get or set the number of elements) + +$(P +Arrays have properties as well, of which we will see only $(C .length) here. $(C .length) returns the number of elements of the array: +) + +--- + writeln("The array has ", array.length, " elements."); +--- + +$(P +Additionally, the length of dynamic arrays can be changed by assigning a value to this property: +) + +--- + int[] array; // initially empty + array.length = 5; // now has 5 elements +--- + +$(H5 An array example) + +$(P +Let's now revisit the exercise with the five values and write it again by using an array: +) + +--- +import std.stdio; + +void main() { + // This variable is used as a loop counter + int counter; + + // The definition of a fixed-length array of five + // elements of type double + double[5] values; + + // Reading the values in a loop + while (counter < values.length) { + write("Value ", counter + 1, ": "); + readf(" %s", &values[counter]); + ++counter; + } + + writeln("Twice the values:"); + counter = 0; + while (counter < values.length) { + writeln(values[counter] * 2); + ++counter; + } + + // The loop that calculates the fifths of the values would + // be written similarly +} +--- + +$(P $(B Observations:) The value of $(C counter) determines how many times the loops are repeated (iterated). Iterating the loop while its value is less than $(C values.length) ensures that the loops are executed once per element. As the value of that variable is incremented at the end of each iteration, the $(C values[counter]) expression refers to the elements of the array one by one: $(C values[0]), $(C values[1]), etc. +) + +$(P +To see how this program is better than the previous one, imagine needing to read 20 values. The program above would require a single change: replacing 5 with 20. On the other hand, a program that did not use an array would have to have 20 variable definitions. Furthermore, since you would be unable to use a loop to iterate the 20 values, you would also have to repeat several lines 20 times, one time for each single-valued variable. +) + +$(H5 $(IX initialization, array) Initializing the elements) + +$(P +Like every variable in D, the elements of arrays are automatically initialized. The initial value of the elements depends on the type of the elements: 0 for $(C int), $(C double.nan) for $(C double), etc. +) + +$(P +All of the elements of the $(C values) array above are initialized to $(C double.nan): +) + +--- + double[5] values; // elements are all double.nan +--- + +$(P +Obviously, the values of the elements can be changed later during the execution of the program. We have already seen this above when assigning to an element of an array: +) + +--- + monthDays[11] = 31; +--- + +$(P +That also happened when reading a value from the input: +) + +--- + readf(" %s", &values[counter]); +--- + +$(P +Sometimes the desired values of the elements are known at the time when the array is defined. In such cases, the initial values of the elements can be specified on the right-hand side of the assignment operator, within square brackets. Let's see this in a program that reads the number of the month from the user, and prints the number of days in that month: +) + +--- +import std.stdio; + +void main() { + // Assuming that February has 28 days + int[12] monthDays = + [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; + + write("Please enter the number of the month: "); + int monthNumber; + readf(" %s", &monthNumber); + + int index = monthNumber - 1; + writeln("Month ", monthNumber, " has ", + monthDays[index], " days."); +} +--- + +$(P +As you can see, the $(C monthDays) array is defined and initialized at the same time. Also note that the number of the month, which is in the range 1-12, is converted to a valid array index in the range 0-11. Any value that is entered outside of the 1-12 range would cause the program to be terminated with an error. +) + +$(P +When initializing arrays, it is possible to use a single value on the right-hand side. In that case all of the elements of the array are initialized to that value: +) + +--- + int[10] allOnes = 1; // All of the elements are set to 1 +--- + +$(H5 Basic array operations) + +$(P +Arrays provide convenience operations that apply to all of their elements. +) + +$(H6 $(IX copy, array) Copying fixed-length arrays) + +$(P +The assignment operator copies all of the elements from the right-hand side to the left-hand side: +) +--- + int[5] source = [ 10, 20, 30, 40, 50 ]; + int[5] destination; + + destination $(HILITE =) source; +--- + +$(P +$(I $(B Note:) The meaning of the assignment operation is completely different for dynamic arrays. We will see this in a later chapter.) +) + +$(H6 $(IX ~=) $(IX append, array) $(IX add element, array) Adding elements to dynamic arrays) + +$(P +The $(C ~=) operator adds new elements to the end of a dynamic array: +) + +--- + int[] array; // empty + array ~= 7; // array is now equal to [7] + array ~= 360; // array is now equal to [7, 360] + array ~= [ 30, 40 ]; // array is now equal to [7, 360, 30, 40] +--- + +$(P +It is not possible to add elements to fixed-length arrays: +) + +--- + int[$(HILITE 10)] array; + array ~= 7; $(DERLEME_HATASI) +--- + +$(H6 $(IX remove, array) Removing elements from dynamic arrays) + +$(P +Array elements can be removed with the $(C remove()) function from the $(C std.algorithm) module. Because there may be more than one $(I slice) to the same elements, $(C remove()) cannot actually change the number of element of the array. Rather, it has to move some of the elements of the array one or more positions to the left. For that reason, the result of the remove operation must be assigned back to the same array variable. +) + +$(P +There are two different ways of using $(C remove()): +) + +$(OL +$(LI +Providing the index of the element to remove. For example, the following code removes the element at index 1. +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 10, 20, 30, 40 ]; + $(HILITE array =) array.remove($(HILITE 1)); // Assigned back to array + writeln(array); +} +--- + +$(SHELL +[10, 30, 40] +) + +$(LI +Specifying the elements to remove with a $(I lambda function), which we will cover in $(LINK2 /ders/d.en/lambda.html, a later chapter). For example, the following code removes the elements of the array that are equal to 42. +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 10, 42, 20, 30, 42, 40 ]; + $(HILITE array =) array.remove!(a => $(HILITE a == 42)); // Assigned back to array + writeln(array); +} +--- + +$(SHELL +[10, 20, 30, 40] +) +) + +$(H6 $(IX ~, concatenation) $(IX concatenation, array) Combining arrays) + +$(P +The $(C ~) operator creates a new array by combining two arrays. Its $(C ~=) counterpart combines the two arrays and assigns the result back to the left-hand side array: +) + +--- +import std.stdio; + +void main() { + int[10] first = 1; + int[10] second = 2; + int[] result; + + result = first ~ second; + writeln(result.length); // prints 20 + + result ~= first; + writeln(result.length); // prints 30 +} +--- + +$(P +The $(C ~=) operator cannot be used when the left-hand side array is a fixed-length array: +) + +--- + int[20] result; + // ... + result $(HILITE ~=) first; $(DERLEME_HATASI) +--- + +$(P +If the array sizes are not equal, the program is terminated with an error during assignment: +) + +--- + int[10] first = 1; + int[10] second = 2; + int[$(HILITE 21)] result; + + result = first ~ second; +--- + +$(SHELL +object.Error@(0): Array lengths don't match for copy: $(HILITE 20 != 21) +) + +$(H6 $(IX sort) Sorting the elements) + +$(P +$(C std.algorithm.sort) can sort the elements of many types of collections. In the case of integers, the elements get sorted from the smallest value to the greatest value. In order to use the $(C sort()) function, one must import the $(C std.algorithm) module first. (We will see functions in a later chapter.) +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 4, 3, 1, 5, 2 ]; + $(HILITE sort)(array); + writeln(array); +} +--- + +$(P +The output: +) + +$(SHELL +[1, 2, 3, 4, 5] +) + +$(H6 $(IX reverse) Reversing the elements) + +$(P +$(C std.algorithm.reverse) reverses the elements in place (the first element becomes the last element, etc.): +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 4, 3, 1, 5, 2 ]; + $(HILITE reverse)(array); + writeln(array); +} +--- + +$(P +The output: +) + +$(SHELL +[2, 5, 1, 3, 4] +) + +$(PROBLEM_COK + +$(PROBLEM +Write a program that asks the user how many values will be entered and then reads all of them. Have the program sort the elements using $(C sort()) and then reverse the sorted elements using $(C reverse()). +) + +$(PROBLEM +Write a program that reads numbers from the input, and prints the odd and even ones separately but in order. Treat the value -1 specially to determine the end of the numbers; do not process that value. + +$(P +For example, when the following numbers are entered, +) + +$(SHELL +1 4 7 2 3 8 11 -1 +) + +$(P +have the program print the following: +) + +$(SHELL +1 3 7 11 2 4 8 +) + +$(P +$(B Hint:) You may want to put the elements in separate arrays. You can determine whether a number is odd or even using the $(C %) (remainder) operator. +) + +) + +$(PROBLEM +The following is a program that does not work as expected. The program is written to read five numbers from the input and to place the squares of those numbers into an array. The program then attempts to print the squares to the output. Instead, the program terminates with an error. + +$(P +Fix the bugs of this program and make it work as expected: +) + +--- +import std.stdio; + +void main() { + int[5] squares; + + writeln("Please enter 5 numbers"); + + int i = 0; + while (i <= 5) { + int number; + write("Number ", i + 1, ": "); + readf(" %s", &number); + + squares[i] = number * number; + ++i; + } + + writeln("=== The squares of the numbers ==="); + while (i <= squares.length) { + write(squares[i], " "); + ++i; + } + + writeln(); +} +--- + +) + +) + +Macros: + SUBTITLE=Arrays + + DESCRIPTION=Basic array operations of the D programming language + + KEYWORDS=d programming language tutorial book arrays fixed-length dynamic + + +$(Ergin) diff --git a/target/assert.cozum.d b/target/assert.cozum.d new file mode 100644 index 0000000..37ea2be --- /dev/null +++ b/target/assert.cozum.d @@ -0,0 +1,135 @@ +Ddoc +$(COZUM_BOLUMU $(CH4 assert) and $(CH4 enforce)) + +$(OL + +$(LI +You will notice that the program terminates normally when you enter $(C 06:09) and $(C 1:2). However, you may notice that the start time is not what has been entered by the user: + +$(SHELL +1 hours and 2 minutes after 09:06 is 10:08. +) + +$(P +As you can see, although the time that has been entered as $(C 06:09), the output contains $(C 09:06). This error will be caught by the help of an $(C assert) in the next problem. +) + +) + +$(LI +The $(C assert) failure after entering $(C 06:09) and $(C 15:2) takes us to the following line: + +--- +string timeToString(in int hour, in int minute) { + $(HILITE assert((hour >= 0) && (hour <= 23));) + // ... +} +--- + +$(P +For this $(C assert) check to fail, this function must have been called with invalid $(C hour) value. +) + +$(P +The only two calls to $(C timeToString()) in the program do not appear to have any problems: +) + +--- + writefln("%s hours and %s minutes after %s is %s.", + durationHour, durationMinute, + $(HILITE timeToString)(startHour, startMinute), + $(HILITE timeToString)(endHour, endMinute)); +--- + +$(P +A little more investigation should reveal the actual cause of the bug: The hour and minute variables are swapped when reading the start time: +) + +--- + readTime("Start time", start$(HILITE Minute), start$(HILITE Hour)); +--- + +$(P +That programming error causes the time to be interpreted as $(C 09:06) and incrementing it by duration $(C 15:2) causes an invalid hour value. +) + +$(P +An obvious correction is to pass the hour and minute variables in the right order: +) + +--- + readTime("Start time", startHour, startMinute); +--- + +$(P +The output: +) + +$(SHELL +Start time? (HH:MM) 06:09 +Duration? (HH:MM) 15:2 +15 hours and 2 minutes after 06:09 is 21:11. +) + +) + +$(LI +It is again the same $(C assert) check: + +--- + assert((hour >= 0) && (hour <= 23)); +--- + +$(P +The reason is that $(C addDuration()) can produce hour values that are greater than 23. Adding a $(I remainder) operation at the end would ensure one of the $(I output guarantees) of the function: +) + +--- +void addDuration(in int startHour, in int startMinute, + in int durationHour, in int durationMinute, + out int resultHour, out int resultMinute) { + resultHour = startHour + durationHour; + resultMinute = startMinute + durationMinute; + + if (resultMinute > 59) { + ++resultHour; + } + + $(HILITE resultHour %= 24;) +} +--- + +$(P +Observe that the function has other problems. For example, $(C resultMinute) may end up being greater than 59. The following function calculates the minute value correctly and makes sure that the function's output guarantees are enforced: +) + +--- +void addDuration(in int startHour, in int startMinute, + in int durationHour, in int durationMinute, + out int resultHour, out int resultMinute) { + resultHour = startHour + durationHour; + resultMinute = startMinute + durationMinute; + + resultHour += resultMinute / 60; + resultHour %= 24; + resultMinute %= 60; + + assert((resultHour >= 0) && (resultHour <= 23)); + assert((resultMinute >= 0) && (resultMinute <= 59)); +} +--- + +) + +$(LI +Good luck. +) + +) + +Macros: + SUBTITLE=assert and enforce Solutions + + DESCRIPTION=Programming in D exercise solutions: assert and enforce + + KEYWORDS=programming in d tutorial functions diff --git a/target/assert.d b/target/assert.d new file mode 100644 index 0000000..0d8832e --- /dev/null +++ b/target/assert.d @@ -0,0 +1,419 @@ +Ddoc + +$(DERS_BOLUMU $(IX assert) $(IX enforce) $(CH4 assert) and $(CH4 enforce)) + +$(P +In the previous two chapters we have seen how exceptions and $(C scope) statements are used toward program correctness. $(C assert) is another powerful tool to achieve the same goal by ensuring that certain assumptions that the program is based on are valid. +) + +$(P +It may sometimes be difficult to decide whether to throw an exception or to call $(C assert). I will use $(C assert) in all of the examples below without much justification. I will explain the differences later in the chapter. +) + +$(P +Although not always obvious, programs are full of assumptions. For example, the following function is written under the assumption that both age parameters are greater than or equal to zero: +) + +--- +double averageAge(double first, double second) { + return (first + second) / 2; +} +--- + +$(P +Although it may be invalid for the program to ever have an age value that is negative, the function would still produce an average, which may be used in the program unnoticed, resulting in the program's continuing with incorrect data. +) + +$(P +As another example, the following function assumes that it will always be called with two commands: "sing" or "dance": +) + +--- +void applyCommand(string command) { + if (command == "sing") { + robotSing(); + + } else { + robotDance(); + } +} +--- + +$(P +Because of that assumption, the $(C robotDance()) function would be called for every command other than "sing", valid or invalid. +) + +$(P +When such assumptions are kept only in the programmer's mind, the program may end up working incorrectly. $(C assert) statements check assumptions and terminate programs immediately when they are not valid. +) + +$(H5 Syntax) + +$(P +$(C assert) can be used in two ways: +) + +--- + assert($(I logical_expression)); + assert($(I logical_expression), $(I message)); +--- + +$(P +The logical expression represents an assumption about the program. $(C assert) evaluates that expression to validate that assumption. If the value of the logical expression is $(C true) then the assumption is considered to be valid. Otherwise the assumption is invalid and an $(C AssertError) is thrown. +) + +$(P +As its name suggests, this exception is inherited from $(C Error), and as you may remember from the $(LINK2 /ders/d.en/exceptions.html, Exceptions chapter), exceptions that are inherited from $(C Error) must never be caught. It is important for the program to be terminated right away instead of continuing under invalid assumptions. +) + +$(P +The two implicit assumptions of $(C averageAge()) above may be spelled out by two $(C assert) calls as in the following function: +) + +--- +double averageAge(double first, double second) { + assert(first >= 0); + assert(second >= 0); + + return (first + second) / 2; +} + +void main() { + auto result = averageAge($(HILITE -1), 10); +} +--- + +$(P +Those $(C assert) checks carry the meaning "assuming that both of the ages are greater than or equal to zero". It can also be thought of as meaning "this function can work correctly only if both of the ages are greater than or equal to zero". +) + +$(P +Each $(C assert) checks its assumption and terminates the program with an $(C AssertError) when it is not valid: +) + +$(SHELL +core.exception.$(HILITE AssertError)@deneme(2): Assertion failure +) + +$(P +The part after the $(C @) character in the message indicates the source file and the line number of the $(C assert) check that failed. According to the output above, the $(C assert) that failed is on line 2 of file $(C deneme.d). +) + +$(P +The other syntax of $(C assert) allows printing a custom message when the $(C assert) check fails: +) + +--- + assert(first >= 0, "Age cannot be negative."); +--- + +$(P +The output: +) + +$(SHELL +core.exception.AssertError@deneme.d(2): $(HILITE Age cannot be negative.) +) + +$(P +Sometimes it is thought to be impossible for the program to ever enter a code path. In such cases it is common to use the literal $(C false) as the logical expression to fail an $(C assert) check. For example, to indicate that $(C applyCommand()) function is never expected to be called with a command other than "sing" and "dance", and to guard against such a possibility, an $(C assert(false)) can be inserted into the $(I impossible) branch: +) + +--- +void applyCommand(string command) { + if (command == "sing") { + robotSing(); + + } else if (command == "dance") { + robotDance(); + + } else { + $(HILITE assert(false)); + } +} +--- + +$(P +The function is guaranteed to work with the only two commands that it knows about. ($(I $(B Note:) An alternative choice here would be to use a $(LINK2 /ders/d.en/switch_case.html, $(C final switch) statement).)) +) + +$(H5 $(IX static assert) $(C static assert)) + +$(P +Since $(C assert) checks are for correct execution of programs, they are applied when the program is actually running. There can be other checks that are about the structure of the program, which can be applied even at compile time. +) + +$(P +$(C static assert) is the counterpart of $(C assert) that is applied at compile time. The advantage is that it does not allow even compiling a program that would have otherwise run incorrectly. A natural requirement is that it must be possible to evaluate the logical expression at compile time. +) + +$(P +For example, assuming that the title of a menu will be printed on an output device that has limited width, the following $(C static assert) ensures that it will never be wider than that limit: +) + +--- + enum dstring menuTitle = "Command Menu"; + static assert(menuTitle.length <= 16); +--- + +$(P +Note that the string is defined as $(C enum) so that its length can be evaluated at compile time. +) + +$(P +Let's assume that a programmer changes that title to make it more descriptive: +) + +--- + enum dstring menuTitle = "Directional Commands Menu"; + static assert(menuTitle.length <= 16); +--- + +$(P +The $(C static assert) check prevents compiling the program: +) + +$(SHELL +Error: static assert (25u <= 16u) is false +) + +$(P +This would remind the programmer of the limitation of the output device. +) + +$(P +$(C static assert) is even more useful when used in templates. We will see templates in later chapters. +) + +$(H5 $(C assert) even if $(I absolutely true)) + +$(P +I emphasize "absolutely true" because assumptions about the program are never expected to be false anyway. A large set of program errors are caused by assumptions that are thought to be absolutely true. +) + +$(P +For that reason, take advantage of $(C assert) checks even if they feel unnecessary. Let's look at the following function that returns the days of months in a given year: +) + +--- +int[] monthDays(in int year) { + int[] days = [ + 31, februaryDays(year), + 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + ]; + + assert((sum(days) == 365) || + (sum(days) == 366)); + + return days; +} +--- + +$(P +That $(C assert) check may be seen as unnecessary because the function would naturally return either 365 or 366. However, those checks are guarding against potential mistakes even in the $(C februaryDays()) function. For example, the program would be terminated if $(C februaryDays()) returned 30. +) + +$(P +Another seemingly unnecessary check can ensure that the length of the slice would always be 12: +) + +--- + assert(days.length == 12); +--- + +$(P +That way, deleting or adding elements to the slice unintentionally would also be caught. Such checks are important tools toward program correctness. +) + +$(P +$(C assert) is also the fundamental tool that is used in $(I unit testing) and $(I contract programming), both of which will be covered in later chapters. +) + +$(H5 No value nor side effect) + +$(P +We have seen that expressions produce values or make side effects. $(C assert) checks do not have values nor $(I should) they have any side effects. +) + +$(P +The D language requires that the evaluation of the logical expression must not have any side effect. $(C assert) must remain as a passive observer of program state. +) + +$(H5 $(IX -release, compiler switch) Disabling $(C assert) checks) + +$(P +Since $(C assert) is about program correctness, they can be seen as unnecessary once the program has been tested sufficiently. Further, since $(C assert) checks produce no values nor they have side effects, removing them from the program should not make any difference. +) + +$(P +The compiler switch $(C -release) causes the $(C assert) checks to be ignored as if they have never been included in the program: +) + +$(SHELL +dmd deneme.d -release +) + +$(P +This would allow programs to run faster by not evaluating potentially slow logical expressions of the $(C assert) checks. +) + +$(P +As an exception, the $(C assert) checks that have the literal $(C false) (or 0) as the logical expression are not disabled even when the program is compiled with $(C ‑release). This is because $(C assert(false)) is for ensuring that a block of code is never reached, and that should be prevented even for the $(C ‑release) compilations. +) + +$(H5 $(C enforce) for throwing exceptions) + +$(P +Not every unexpected situation is an indication of a program error. Programs may also experience unexpected inputs and unexpected environmental state. For example, the data that is entered by the user should not be validated by an $(C assert) check because invalid data has nothing to do with the correctness of the program itself. In such cases it is appropriate to throw exceptions like we have been doing in previous programs. +) + +$(P +$(C std.exception.enforce) is a convenient way of throwing exceptions. For example, let's assume that an exception must be thrown when a specific condition is not met: +) + +--- + if (count < 3) { + throw new Exception("Must be at least 3."); + } +--- + +$(P +$(C enforce()) is a wrapper around the condition check and the $(C throw) statement. The following is the equivalent of the previous code: +) + +--- +import std.exception; +// ... + enforce(count >= 3, "Must be at least 3."); +--- + +$(P +Note how the logical expression is negated compared to the $(C if) statement. It now spells out what is being $(I enforced). +) + +$(H5 How to use) + +$(P +$(C assert) is for catching programmer errors. The conditions that $(C assert) guards against in the $(C monthDays()) function and the $(C menuTitle) variable above are all about programmer mistakes. +) + +$(P +Sometimes it is difficult to decide whether to rely on an $(C assert) check or to throw an exception. The decision should be based on whether the unexpected situation is due to a problem with how the program has been coded. +) + +$(P +Otherwise, the program must throw an exception when it is not possible to accomplish a task. $(C enforce()) is expressive and convenient when throwing exceptions. +) + +$(P +Another point to consider is whether the unexpected situation can be remedied in some way. If the program can not do anything special, even by simply printing an error message about the problem with some input data, then it is appropriate to throw an exception. That way, callers of the code that threw the exception can catch it to do something special to recover from the error condition. +) + +$(PROBLEM_COK + +$(PROBLEM +The following program includes a number of $(C assert) checks. Compile and run the program to discover its bugs that are revealed by those $(C assert) checks. + +$(P +The program takes a start time and a duration from the user and calculates the end time by adding the duration to the start time: +) + +$(SHELL +10 hours and 8 minutes after 06:09 is 16:17. +) + +$(P +Note that this problem can be written in a much cleaner way by defining $(C struct) types. We will refer to this program in later chapters. +) + +--- +import std.stdio; +import std.string; +import std.exception; + +/* Reads the time as hour and minute after printing a + * message. */ +void readTime(in string message, + out int hour, + out int minute) { + write(message, "? (HH:MM) "); + + readf(" %s:%s", &hour, &minute); + + enforce((hour >= 0) && (hour <= 23) && + (minute >= 0) && (minute <= 59), + "Invalid time!"); +} + +/* Returns the time in string format. */ +string timeToString(in int hour, in int minute) { + assert((hour >= 0) && (hour <= 23)); + assert((minute >= 0) && (minute <= 59)); + + return format("%02s:%02s", hour, minute); +} + +/* Adds duration to start time and returns the result as the + * third pair of parameters. */ +void addDuration(in int startHour, in int startMinute, + in int durationHour, in int durationMinute, + out int resultHour, out int resultMinute) { + resultHour = startHour + durationHour; + resultMinute = startMinute + durationMinute; + + if (resultMinute > 59) { + ++resultHour; + } +} + +void main() { + int startHour; + int startMinute; + readTime("Start time", startMinute, startHour); + + int durationHour; + int durationMinute; + readTime("Duration", durationHour, durationMinute); + + int endHour; + int endMinute; + addDuration(startHour, startMinute, + durationHour, durationMinute, + endHour, endMinute); + + writefln("%s hours and %s minutes after %s is %s.", + durationHour, durationMinute, + timeToString(startHour, startMinute), + timeToString(endHour, endMinute)); +} +--- + +$(P +Run the program and enter $(C 06:09) as the start time and $(C 1:2) as the duration. Observe that the program terminates normally. +) + +$(P $(I $(B Note:) You may notice a problem with the output. Ignore that problem for now as you will discover it by the help of $(C assert) checks soon.) +) + +) + +$(PROBLEM +This time enter $(C 06:09) and $(C 15:2). Observe that the program is terminated by an $(C AssertError). Go to the line of the program that is indicated in the assert message and see which one of the $(C assert) checks have failed. It may take a while to discover the cause of this particular failure. +) + +$(PROBLEM +Enter $(C 06:09) and $(C 20:0) and observe that the same $(C assert) check still fails and fix that bug as well. +) + +$(PROBLEM +Modify the program to print the times in 12-hour format with an "am" or "pm" indicator. +) + +) + +Macros: + SUBTITLE=assert and enforce + + DESCRIPTION=assert checks for program correctness, enforce() for throwing exception conveniently, and their differences. + + KEYWORDS=d programming language tutorial book assert enforce diff --git a/target/assignment.cozum.d b/target/assignment.cozum.d new file mode 100644 index 0000000..c585f51 --- /dev/null +++ b/target/assignment.cozum.d @@ -0,0 +1,25 @@ +Ddoc + +$(COZUM_BOLUMU Assignment and Order of Evaluation) + +$(P +The values of $(C a), $(C b), and $(C c) are printed on the right-hand side of each operation. The value that changes at every operation is highlighted: +) + +$(MONO +at the beginning → a 1, b 2, c irrelevant +c = a → a 1, b 2, $(HILITE c 1) +a = b → $(HILITE a 2), b 2, c 1 +b = c → a 2, $(HILITE b 1), c 1 +) + +$(P +At the end, the values of $(C a) and $(C b) have been swapped. +) + +Macros: + SUBTITLE=Assignment and Order of Evaluation Solutions + + DESCRIPTION=Assignment and Order of Evaluation chapter exercise solutions + + KEYWORDS=programming in d tutorial assignment and order of evaluation exercise solution diff --git a/target/assignment.d b/target/assignment.d new file mode 100644 index 0000000..32ce973 --- /dev/null +++ b/target/assignment.d @@ -0,0 +1,82 @@ +Ddoc + +$(DERS_BOLUMU Assignment and Order of Evaluation) + +$(P +The first two difficulties that most students face when learning to program involve the assignment operation and the order of evaluation. +) + +$(H5 The assignment operation) + +$(P +You will see lines similar to the following in almost every program in almost every programming language: +) + +--- + a = 10; +--- + +$(P +The meaning of that line is "make $(C a)'s value become 10". Similarly, the following line means "make $(C b)'s value become 20": +) + +--- + b = 20; +--- + +$(P +Based on that information, what can be said about the following line? +) + +--- + a = b; +--- + +$(P +Unfortunately, that line is not about the equality concept of mathematics that we all know. The expression above $(B does not) mean "a is equal to b"! When we apply the same logic from the earlier two lines, the expression above must mean "make $(C a)'s value become the same as $(C b)'s value". +) + +$(P +The well-known $(C =) symbol of mathematics has a completely different meaning in programming: make the left side's value the same as the right side's value. +) + +$(H5 Order of evaluation) + +$(P +In general, the operations of a program are applied step by step in the order that they appear in the program. (There are exceptions to this rule, which we will see in later chapters.) We may see the previous three expressions in a program in the following order: +) + +--- + a = 10; + b = 20; + a = b; +--- + +$(P +The meaning of those three lines altogether is this: "make $(C a)'s value become 10, $(I then) make $(C b)'s value become 20, $(I then) make $(C a)'s value become the same as $(C b)'s value". Accordingly, after those three operations are performed, the value of both $(C a) and $(C b) would be 20. +) + +$(PROBLEM_TEK + +$(P +Observe that the following three operations swap the values of $(C a) and $(C b). If at the beginning their values are 1 and 2 respectively, after the operations the values become 2 and 1: +) + +--- + c = a; + a = b; + b = c; +--- + +) + +$(Ergin) + +Macros: + SUBTITLE=Assignment and Order of Evaluation + + DESCRIPTION=The very first two hurdles that a student faces when learning to program + + KEYWORDS=d programming language tutorial book + +MINI_SOZLUK= diff --git a/target/auto_and_typeof.cozum.d b/target/auto_and_typeof.cozum.d new file mode 100644 index 0000000..e32b529 --- /dev/null +++ b/target/auto_and_typeof.cozum.d @@ -0,0 +1,30 @@ +Ddoc + +$(COZUM_BOLUMU $(C auto) and $(C typeof)) + +$(P +We can use $(C typeof) to determine the type of the literal and $(C .stringof) to get the name of that type as $(C string): +) + +--- +import std.stdio; + +void main() { + writeln(typeof(1.2).stringof); +} +--- + +$(P +The output: +) + +$(SHELL +double +) + +Macros: + SUBTITLE=auto and typeof Solutions + + DESCRIPTION=auto and typeof chapter solutions + + KEYWORDS=programming in d tutorial files diff --git a/target/auto_and_typeof.d b/target/auto_and_typeof.d new file mode 100644 index 0000000..0dd6b1a --- /dev/null +++ b/target/auto_and_typeof.d @@ -0,0 +1,97 @@ +Ddoc + +$(DERS_BOLUMU $(CH4 auto) and $(CH4 typeof)) + +$(H5 $(IX auto, variable) $(C auto)) + +$(P +When defining $(C File) variables in the previous chapter, we have repeated the name of the type on both sides of the $(C =) operator: +) + +--- + $(HILITE File) file = $(HILITE File)("student_records", "w"); +--- + +$(P +It feels redundant. It would also be cumbersome and especially error-prone if the type name were longer: +) + +--- + VeryLongTypeName var = VeryLongTypeName(/* ... */); +--- + +$(P +Fortunately, the type name on the left-hand side is not necessary because the compiler can infer the type of the left-hand side from the expression on the right-hand side. For the compiler to infer the type, the $(C auto) keyword can be used: +) + +--- + $(HILITE auto) var = VeryLongTypeName(/* ... */); +--- + +$(P +$(C auto) can be used with any type even when the type is not spelled out on the right-hand side: +) + +--- + auto duration = 42; + auto distance = 1.2; + auto greeting = "Hello"; + auto vehicle = BeautifulBicycle("blue"); +--- + +$(P +Although "auto" is the abbreviation of $(I automatic), it does not come from $(I automatic type inference). It comes from $(I automatic storage class), which is a concept about the life times of variables. $(C auto) is used when no other specifier is appropriate. For example, the following definition does not use $(C auto): +) + +--- + immutable i = 42; +--- + +$(P +The compiler infers the type of $(C i) as $(C immutable int) above. (We will see $(C immutable) in a later chapter.) +) + +$(H5 $(IX typeof) $(C typeof)) + +$(P +$(C typeof) provides the type of an expression (including single variables, objects, literals, etc.) without actually evaluating that expression. +) + +$(P +The following is an example of how $(C typeof) can be used to specify a type without explicitly spelling it out: +) + +--- + int value = 100; // already defined as 'int' + + typeof(value) value2; // means "type of value" + typeof(100) value3; // means "type of literal 100" +--- + +$(P +The last two variable definitions above are equivalent to the following: +) + +--- + int value2; + int value3; +--- + +$(P +It is obvious that $(C typeof) is not needed in situations like above when the actual types are known. Instead, you would typically use it in more elaborate scenarios, where you want the type of your variables to be consistent with some other piece of code whose type can vary. This keyword is especially useful in $(LINK2 /ders/d.en/templates.html, templates) and $(LINK2 /ders/d.en/mixin.html, mixins), both of which will be covered in later chapters. +) + +$(PROBLEM_TEK + +$(P +As we have seen above, the type of literals like 100 is $(C int) (as opposed to $(C short), $(C long), or any other type). Write a program to determine the type of floating point literals like 1.2. $(C typeof) and $(C .stringof) would be useful in this program. +) + +) + +Macros: + SUBTITLE=auto and typeof keywords + + DESCRIPTION=The 'auto' keyword which is commonly used for D's implicit type inference feature, and 'typeof' which yields type of expressions. + + KEYWORDS=d programming language tutorial book auto typeof diff --git a/target/bit_operations.cozum.d b/target/bit_operations.cozum.d new file mode 100644 index 0000000..058e89c --- /dev/null +++ b/target/bit_operations.cozum.d @@ -0,0 +1,84 @@ +Ddoc + +$(COZUM_BOLUMU Bit Operations) + +$(OL + +$(LI +It may be acceptable to use magic constants in such a short function. Otherwise, the code may get too complicated. + +--- +string dotted(uint address) { + return format("%s.%s.%s.%s", + (address >> 24) & 0xff, + (address >> 16) & 0xff, + (address >> 8) & 0xff, + (address >> 0) & 0xff); +} +--- + +$(P +Because the type is an unsigned type, the bits that are inserted into the value from the left-hand side will all have 0 values. For that reason, there is no need to mask the value that is shifted by 24 bits. Additionally, since shifting by 0 bits has no effect, that operation can be eliminated as well: +) + +--- +string dotted(uint address) { + return format("%s.%s.%s.%s", + address >> 24, + (address >> 16) & 0xff, + (address >> 8) & 0xff, + address & 0xff); +} +--- + +) + +$(LI +Each octet can be shifted to its proper position in the IPv4 address and then these expressions can be "$(I orred)": + +--- +uint ipAddress(ubyte octet3, // most significant octet + ubyte octet2, + ubyte octet1, + ubyte octet0) { // least significant octet + return + (octet3 << 24) | + (octet2 << 16) | + (octet1 << 8) | + (octet0 << 0); +} +--- + +) + +$(LI +The following method starts with a value where all of the bits are 1. First, the value is shifted to the right so that the upper bits become 0, and then it is shifted to the left so that the lower bits become 0: + +--- +uint mask(int lowestBit, int width) { + uint result = uint.max; + result >>= (uint.sizeof * 8) - width; + result <<= lowestBit; + return result; +} +--- + +$(P +$(C uint.max) is the value where all of the bits are 1. Alternatively, the calculation can start with the value that is the complement of 0, which is the same as $(C uint.max): +) + +--- + uint result = ~0; + // ... +--- + +) + +) + +Macros: + SUBTITLE=Bit Operations Solutions + + DESCRIPTION=Bit Operations chapter exercise solutions + + KEYWORDS=programming in d tutorial bit operations exercise solution diff --git a/target/bit_operations.d b/target/bit_operations.d new file mode 100644 index 0000000..c839cb5 --- /dev/null +++ b/target/bit_operations.d @@ -0,0 +1,1093 @@ +Ddoc + +$(DERS_BOLUMU $(IX bit operation) Bit Operations) + +$(P +This chapter covers operations on bits, the smallest data units. Bit operations are among the most fundamental features of microprocessors. +) + +$(P +System programmers must understand bit operations at least to use $(I flag) parameters correctly. +) + +$(H5 Representation of data at the lowest level) + +$(P +Programming languages are abstractions. A user type like $(C Student) defined in a programming language is not directly related to the internals of the computer. Programming languages are tools that help humans use the hardware without needing to know the details of the hardware. +) + +$(P +Although it is usually not necessary to deal with the hardware directly, it is helpful to understand how data is represented at hardware level. +) + +$(H6 $(IX transistor) Transistor) + +$(P +The processing abilities of modern electronic devices are mostly based on the electronic element called $(I the transistor). A significant ability of the transistor is that it can be controlled by other parts of the electronic circuit that the transistor is a part of. In a way, it allows the electronic circuit be aware of itself and be able to change its own state. +) + +$(H6 $(IX bit) Bit) + +$(P +The smallest unit of information is a bit. A bit can be represented by any two-state system (e.g. by a special arrangement of a few transistors of an electronic circuit). A bit can have one of two values: 0 or 1. In the computer's memory, the information that is stored in a bit persists until a new value is stored or until the energy source is disconnected. +) + +$(P +Computers do not provide direct access to bits. One reason is that doing so would complicate the design of the computer and as a consequence make the computer more expensive. Another reason is that there are not many concepts that can be represented by a single bit. +) + +$(H6 $(IX byte) Byte) + +$(P +A byte is a combination of 8 bits. The smallest unit of information that can be addressed uniquely is a byte. Computers read from or write to memory at least one byte at a time. +) + +$(P +For that reason, although it carries one bit of information ($(C false) or $(C true)), even $(C bool) must be implemented as one byte: +) + +--- + writefln("%s is %s byte(s)", bool.stringof, bool.sizeof); +--- + +$(SHELL_SMALL +bool is 1 byte(s) +) + +$(H6 $(IX register, CPU) Register) + +$(P +Data that are being operated on in a microprocessor are stored in registers. Registers provide very limited but very fast operations. +) + +$(P +The size of the registers depend on the architecture of the microprocessor. For example, 32-bit microprocessors commonly have 4-byte registers and 64-bit microprocessors commonly have 8-byte registers. The size of the registers determine how much information the microprocessor can process efficiently at a time and how many memory addresses that it can support. +) + +$(P +Every task that is achieved by a programming language ends up being executed by one or more registers of the microprocessor. +) + +$(H5 $(IX binary system) Binary number system) + +$(P +The decimal number system which is used in daily life consists of 10 numerals: 0123456789. In contrast, the binary number system which is used by computer hardware consists of 2 numerals: 0 and 1. This is a direct consequence of a bit consisting of two values. If bits had three values then the computers would use a number system based on three numerals. +) + +$(P +The digits of the decimal system are named incrementally as $(I ones), $(I tens), $(I hundreds), $(I thousands), etc. For example, the number 1023 can be expanded as in the following way: +) + +$(MONO +1023 == 1 count of thousand, no hundred, 2 counts of ten, and 3 counts of one +) + +$(P +Naturally, moving one digit to the left multiplies the value of that digit by 10: 1, 10, 100, 1000, etc. +) + +$(P +When the same rules are applied to a system that has two numerals, we arrive at the binary number system. The digits are named incrementally as $(I ones), $(I twos), $(I fours), $(I eights), etc. In other words, moving one digit to the left would multiply the value of that digit by 2: 1, 2, 4, 8, etc. For example, the $(I binary) number 1011 can be expanded as in the following way: +) + +$(MONO +1011 == 1 count of eight, no four, 1 count of two, and 1 count of one +) + +$(P +To make it easy to refer to digits, they are numbered from the rightmost digit to the leftmost digit, starting by 0. The following table lists the values of all of the digits of a 32-bit unsigned number in the binary system: +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Digit Value
312,147,483,648
301,073,741,824
29536,870,912
28268,435,456
27134,217,728
2667,108,864
2533,554,432
2416,777,216
238,388,608
224,194,304
212,097,152
201,048,576
19524,288
18262,144
17131,072
1665,536
1532,768
1416,384
138,192
124,096
112,048
101,024
9512
8256
7128
664
532
416
38
24
12
01
+ +$(P +The bits that have higher values are called the $(I upper) bits and the bits that have lower values are called the $(I lower) bits. +) + +$(P +Remembering from $(LINK2 /ders/d.en/literals.html, the Literals chapter) that binary literals are specified by the $(C 0b) prefix, the following program demonstrates how the value of a literal would correspond to the rows of the previous table: +) + +--- +import std.stdio; + +void main() { + // 1073741824 4 1 + // ↓ ↓ ↓ + int number = 0b_01000000_00000000_00000000_00000101; + writeln(number); +} +--- + +$(P +The output: +) + +$(SHELL_SMALL +1073741829 +) + +$(P +Note that the literal consists of only three nonzero bits. The value that is printed is the sum of the values that correspond to those bits from the previous table: 1073741824 + 4 + 1 == 1073741829. +) + +$(H6 $(IX sign bit) The $(I sign) bit of signed integer types) + +$(P +The uppermost bit of a signed type determines whether the value is positive or negative: +) + +--- + int number = 0b_$(HILITE 1)0000000_00000000_00000000_00000000; + writeln(number); +--- + +$(SHELL_SMALL +-2147483648 +) + +$(P +However, the uppermost bit is not entirely separate from the value. For example, as evidenced above, the fact that all of the other bits of the number being 0 does not mean that the value is -0. (In fact, -0 is not a valid value for integers.) I will not get into more detail in this chapter other than noting that this is due to the $(I twos complement) representation, which is used by D as well. +) + +$(P +What is important here is that 2,147,483,648; the highest value in the previous table, is only for unsigned integer types. The same experiment with $(C uint) would print that exact value: +) + +--- + $(HILITE uint) number = 0b_10000000_00000000_00000000_00000000; + writeln(number); +--- + +$(SHELL_SMALL +2147483648 +) + +$(P +Partly for that reason, unless there is a reason not to, bit operations must always be executed on unsigned types: $(C ubyte), $(C uint), and $(C ulong). +) + +$(H5 $(IX hexadecimal system) Hexadecimal number system) + +$(P +As can be seen in the literals above, consisting only of 0s and 1s, the binary system may not be readable especially when the numbers are large. +) + +$(P +For that reason, the more readable hexadecimal system has been widely adopted especially in computer technologies. +) + +$(P +The hexadecimal system has 16 numerals. Since alphabets do not have more than 10 numerals, this system borrows 6 letters from the Latin alphabet and uses them along with regular numerals: 0123456789abcdef. The numerals a, b, c, d, e, and f have the values 10, 11, 12, 13, 14, and 15, respectively. The letters ABCDEF can be used as well. +) + +$(P +Similar to other number systems, the value of every digit is 16 times the value of the digit on its right-hand side: 1, 16, 256, 4096, etc. For example, the values of all of the digits of an 8-digit unsigned hexadecimal number are the following: +) + + + + + + + + + + +
Digit Value
7268,435,456
616,777,216
51,048,576
465,536
34,096
2256
116
01
+ +$(P +Remembering that hexadecimal literals are specified by the $(C 0x) prefix, we can see how the values of the digits contribute to the overall value of a number: +) + +--- + // 1048576 4096 1 + // ↓ ↓ ↓ + uint number = 0x_0030_a00f; + writeln(number); +--- + +$(SHELL_SMALL +3186703 +) + +$(P +The value that is printed is by the contributions of all of the nonzero digits: 3 count of 1048576, $(C a) count of 4096, and $(C f) count of 1. Remembering that $(C a) represents 10 and $(C f) represents 15, the value is 3145728 + 40960 + 15 == 3186703. +) + +$(P +It is straightforward to convert between binary and hexadecimal numbers. In order to convert a hexadecimal number to binary, the digits of the hexadecimal number are converted to their binary representations individually. The corresponding representations in the three number systems are as in the following table: +) + + + + + + + + + + + + + + + + + + +
Hexadecimal Binary Decimal
000000
100011
200102
300113
401004
501015
601106
701117
810008
910019
a101010
b101111
c110012
d110113
e111014
f111115
+ +$(P +For example, the hexadecimal number 0x0030a00f can be written in the binary form by converting its digits individually according to the previous table: +) + +--- + // hexadecimal: 0 0 3 0 a 0 0 f + uint binary = 0b_0000_0000_0011_0000_1010_0000_0000_1111; +--- + +$(P +Converting from binary to hexadecimal is the reverse: The digits of the binary number are converted to their hexadecimal representations four digits at a time. For example, here is how to write in hexadecimal the same binary value that we have used earlier: +) +--- + // binary: 0100 0000 0000 0000 0000 0000 0000 0101 + uint hexadecimal = 0x___4____0____0____0____0____0____0____5; +--- + +$(H5 Bit operations) + +$(P +After going over how values are represented by bits and how numbers are represented in binary and hexadecimal, we can now see operations that change values at bit-level. +) + +$(P +Because there is no direct access to individual bits, even though these operations are at bit-level, they affect at least 8 bits at a time. For example, for a variable of type $(C ubyte), a bit operation would be applied to all of the 8 bits of that variable. +) + +$(P +As the uppermost bit is the sign bit for signed types, I will ignore signed types and use only $(C uint) in the examples below. You can repeat these operations with $(C ulong) and $(C ubyte), as well as $(C byte), $(C int), and $(C long) as long as you remember the special meaning of the uppermost bit. +) + +$(P +Let's first define a function which will be useful later when examining how bit operators work. This function will print a value in binary, hexadecimal, and decimal systems: +) + +--- +import std.stdio; + +void print(uint number) { + writefln(" %032b %08x %10s", number, number, number); +} + +void main() { + print(123456789); +} +--- + +$(P +Here is the same value printed in the binary, hexadecimal, and decimal number systems: +) + +$(SHELL_SMALL + 00000111010110111100110100010101 075bcd15 123456789 +) + +$(H6 $(IX ~, bitwise complement) $(IX complement, bitwise operator) Complement operator: $(C ~)) + +$(P +$(I Not be confused with the $(I binary) $(C ~) operator that is used for array concatenation, this is the unary $(C ~) operator.) +) + +$(P +This operator converts each bit of a value to its opposite: The bits that are 0 become 1, and the bits that are 1 become 0. +) + +--- + uint value = 123456789; + print(value); + writeln("~ --------------------------------"); + print($(HILITE ~)value); +--- + +$(P +The effect is obvious in the binary representation. Every bit has been reversed (under the dashed line): +) + +$(SHELL_SMALL + 00000111010110111100110100010101 075bcd15 123456789 +~ -------------------------------- + 11111000101001000011001011101010 f8a432ea 4171510506 +) + +$(P +Here is the summary of how the unary $(C ~) operator works: +) + +$(MONO +~0 → 1 +~1 → 0 +) + +$(H6 $(IX &, bitwise and) $(IX and, bitwise operator) $(I And) operator: $(C &)) + +$(P +$(C &) is a binary operator, written between two expressions. The microprocessor considers two corresponding bits of the two expressions separately from all of the other bits: Bits 31, 30, 29, etc. of the expressions are evaluated separately. The value of each resultant bit is 1 if both of the corresponding bits of the expressions are 1; 0 otherwise. +) + +--- + uint lhs = 123456789; + uint rhs = 987654321; + + print(lhs); + print(rhs); + writeln("& --------------------------------"); + print(lhs $(HILITE &) rhs); +--- + +$(P +The following output contains first the left-hand side expression (lhs) and then the right-hand side expression (rhs). The result of the $(C &) operation is under the dashed line: +) + +$(SHELL_SMALL + 00000111010110111100110100010101 075bcd15 123456789 + 00111010110111100110100010110001 3ade68b1 987654321 +& -------------------------------- + 00000010010110100100100000010001 025a4811 39471121 +) + +$(P +Note that the bits of the result that have the value 1 are the ones where the corresponding bits of the expressions are both 1. +) + +$(P +This operator is called the $(I and) operator because it produces 1 when both the left-hand side $(I and) the right-hand side bits are 1. Among the four possible combinations of 0 and 1 values, only the one where both of the values are 1 produces 1: +) + +$(MONO +0 & 0 → 0 +0 & 1 → 0 +1 & 0 → 0 +1 & 1 → 1 +) + +$(P +Observations: +) + +$(UL + +$(LI When one of the bits is 0, regardless of the other bit the result is always 0. Accordingly, "$(I anding) a bit by 0" means to clear that bit.) + +$(LI When one of the bits is 1, the result is the value of the other bit; $(I anding) by 1 has no effect.) + +) + +$(H6 $(IX |) $(IX or, bitwise operator) $(I Or) operator: $(C |)) + +$(P +$(C |) is a binary operator, written between two expressions. The microprocessor considers two corresponding bits of the two expressions separately from all of the other bits. The value of each resultant bit is 0 if both of the corresponding bits of the expressions are 0; 1 otherwise. +) + +--- + uint lhs = 123456789; + uint rhs = 987654321; + + print(lhs); + print(rhs); + writeln("| --------------------------------"); + print(lhs $(HILITE |) rhs); +--- + +$(SHELL_SMALL + 00000111010110111100110100010101 075bcd15 123456789 + 00111010110111100110100010110001 3ade68b1 987654321 +| -------------------------------- + 00111111110111111110110110110101 3fdfedb5 1071639989 +) + +$(P +Note that the bits of the result that have the value 0 are the ones where the corresponding bits of the expressions are both 0. When the corresponding bit in the left-hand side or in the right-hand side is 1, then the result is 1: +) + +$(MONO +0 | 0 → 0 +0 | 1 → 1 +1 | 0 → 1 +1 | 1 → 1 +) + +$(P +Observations: +) + +$(UL + +$(LI When one of the bits is 1, regardless of the other bit the result is always 1. Accordingly, "$(I orring) a bit by 1" means to set it.) + +$(LI When one of the bits is 0, the result is the value of the other bit; $(I orring) by 0 has no effect.) + +) + +$(H6 $(IX ^, bitwise exclusive or) $(IX xor, bitwise operator) $(IX exclusive or, bitwise operator) $(I Xor) operator: $(C ^)) + +$(P +$(I Xor) is the short for $(I exclusive or). This is a binary operator as well. It produces 1 if the corresponding bits of the two expressions are different: +) + +--- + uint lhs = 123456789; + uint rhs = 987654321; + + print(lhs); + print(rhs); + writeln("^ --------------------------------"); + print(lhs $(HILITE ^) rhs); +--- + +$(SHELL_SMALL + 00000111010110111100110100010101 075bcd15 123456789 + 00111010110111100110100010110001 3ade68b1 987654321 +^ -------------------------------- + 00111101100001011010010110100100 3d85a5a4 1032168868 +) + +$(P +Note that the bits of the result that have the value 1 are the ones where the corresponding bits of the expressions are different from each other. +) + +$(MONO +0 ^ 0 → 0 +0 ^ 1 → 1 +1 ^ 0 → 1 +1 ^ 1 → 0 +) + +$(P +Observation: +) + +$(UL +$(LI "$(I Xorring) a bit" with itself means to clear that bit.) +) + +$(P +Regardless of its value, $(I xorring) a variable with itself always produces 0: +) + +--- + uint value = 123456789; + + print(value ^ value); +--- + +$(SHELL_SMALL + 00000000000000000000000000000000 00000000 0 +) + +$(H6 $(IX >>) $(IX right shift, bitwise operator) Right-shift operator: $(C >>)) + +$(P +This operator shifts the bits of an expression by the specified number of bits to the right. The rightmost bits, which do not have room to shift into, get $(I dropped) from the value. For unsigned types, the leftmost bits are filled with zeros. +) + +$(P +The following example produces a result by shifting a value by two bits to the right: +) + +--- + uint value = 123456789; + print(value); + print(value $(HILITE >>) 2); +--- + +$(P +In the following output, I highlighted both the bits that are going to be lost due to dropping off from the right-hand side and the leftmost bits that get the value 0: +) + +$(SHELL_SMALL + 000001110101101111001101000101$(HILITE 01) 075bcd15 123456789 + $(HILITE 00)000001110101101111001101000101 01d6f345 30864197 +) + +$(P +Note that the bits that are not highlighted have been shifted two bit positions to the right. +) + +$(P +$(IX sign extension) The new bits that enter from the left-hand side are 0 only for unsigned types. For signed types, the value of the leftmost bits are determined by a process called $(I sign extension). Sign extension preserves the value of the sign bit of the original expression. The value of that bit is used for all of the bits that $(I enter) from the left. +) + +$(P +Let's see this effect on a value of a signed type where the sign bit is 1 (i.e. the value is negative): +) + +--- + $(HILITE int) value = 0x80010300; + print(value); + print(value >> 3); +--- + +$(P +Because the leftmost bit of the original value is 1, all of the new bits of the result are 1 as well: +) + +$(SHELL_SMALL + $(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 111)10000000000000010000001100000 f0002060 4026540128 +) + +$(P +When the leftmost bit is 0, then all new bits are 0: +) + +--- + $(HILITE int) value = 0x40010300; + print(value); + print(value >> 3); +--- + +$(SHELL_SMALL + $(U 0)1000000000000010000001100000$(HILITE 000) 40010300 1073808128 + $(HILITE 000)01000000000000010000001100000 08002060 134226016 +) + +$(H6 $(IX >>>) $(IX unsigned right shift, bitwise operator) Unsigned right-shift operator: $(C >>>)) + +$(P +This operator works similarly to the regular right-shift operator. The difference is that the new leftmost bits are always 0 regardless of the type of the expression and the value of the leftmost bit: +) + +--- + int value = 0x80010300; + print(value); + print(value $(HILITE >>>) 3); +--- + +$(SHELL_SMALL + 10000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 000)10000000000000010000001100000 10002060 268443744 +) + +$(H6 $(IX <<) $(IX left shift, bitwise operator) Left-shift operator: $(C <<)) + +$(P +This operator works as the reverse of the right-shift operator. The bits are shifted to the left: +) + +--- + uint value = 123456789; + print(value); + print(value $(HILITE <<) 4); +--- + +$(P +The bits on the left-hand side are lost and the new bits on the right-hand side are 0: +) + +$(SHELL_SMALL + $(HILITE 0000)0111010110111100110100010101 075bcd15 123456789 + 0111010110111100110100010101$(HILITE 0000) 75bcd150 1975308624 +) + +$(H6 $(IX assignment, operation result) Operators with assignment) + +$(P +All of the binary operators above have assignment counterparts: $(C &=), $(C |=), $(C ^=), $(C >>=), $(C >>>=), and $(C <<=). Similar to the operators that we saw in $(LINK2 /ders/d.en/arithmetic.html, the Integers and Arithmetic Operations chapter), these operators assign the result back to the left-hand operand. +) + +$(P +Let's see this on the $(C &=) operator: +) + +--- + value = value & 123; + value &= 123; // the same as above +--- + +$(H5 Semantics) + +$(P +Merely understanding how these operators work at bit-level may not be sufficient to see how they are useful in programs. The following sections describe common ways that these operators are used in. +) + +$(H6 $(C |) is a union set) + +$(P +The $(C |) operator produces the union of the 1 bits in the two expressions. +) + +$(P +As an extreme example, let's consider two values that both have alternating bits set to 1. The union of these values would produce a result where all of the bits are 1: +) + +--- + uint lhs = 0xaaaaaaaa; + uint rhs = 0x55555555; + + print(lhs); + print(rhs); + writeln("| --------------------------------"); + print(lhs | rhs); +--- + +$(SHELL_SMALL + 10101010101010101010101010101010 aaaaaaaa 2863311530 + 01010101010101010101010101010101 55555555 1431655765 +| -------------------------------- + 11111111111111111111111111111111 ffffffff 4294967295 +) + +$(H6 $(C &) is an intersection set) + +$(P +The $(C &) operator produces the intersection of the 1 bits in the two expressions. +) + +$(P +As an extreme example, let's consider the last two values again. Since none of the 1 bits of the previous two expressions match the ones in the other expression, all of the bits of the result are 0: +) + +--- + uint lhs = 0xaaaaaaaa; + uint rhs = 0x55555555; + + print(lhs); + print(rhs); + writeln("& --------------------------------"); + print(lhs & rhs); +--- + +$(SHELL_SMALL + 10101010101010101010101010101010 aaaaaaaa 2863311530 + 01010101010101010101010101010101 55555555 1431655765 +& -------------------------------- + 00000000000000000000000000000000 00000000 0 +) + +$(H6 $(C |=) sets selected bits to 1) + +$(P +To understand how this works, it helps to see one of the expressions as the $(I actual) expression and the other expression as a $(I selector) for the bits to set to 1: +) + +--- + uint expression = 0x00ff00ff; + uint bitsToSet = 0x10001000; + + write("before :"); print(expression); + write("to set to 1:"); print(bitsToSet); + + expression $(HILITE |=) bitsToSet; + write("after :"); print(expression); +--- + +$(P +The before and after values of the bits that are affected are highlighted: +) + +$(SHELL_SMALL +before : 000$(HILITE 0)000011111111000$(HILITE 0)000011111111 00ff00ff 16711935 +to set to 1: 00010000000000000001000000000000 10001000 268439552 +after : 000$(HILITE 1)000011111111000$(HILITE 1)000011111111 10ff10ff 285151487 +) + +$(P +In a sense, $(C bitsToSet) determines which bits to set to 1. The other bits are not affected. +) + +$(H6 $(C &=) clears selected bits) + +$(P +One of the expressions can be seen as the $(I actual) expression and the other expression can be seen as a $(I selector) for the bits to clear (to set to 0): +) + +--- + uint expression = 0x00ff00ff; + uint bitsToClear = 0xffefffef; + + write("before :"); print(expression); + write("bits to clear:"); print(bitsToClear); + + expression $(HILITE &=) bitsToClear; + write("after :"); print(expression); +--- + +$(P +The before and after values of the bits that are affected are highlighted: +) + +$(SHELL_SMALL +before : 00000000111$(HILITE 1)111100000000111$(HILITE 1)1111 00ff00ff 16711935 +bits to clear: 11111111111011111111111111101111 ffefffef 4293918703 +after : 00000000111$(HILITE 0)111100000000111$(HILITE 0)1111 00ef00ef 15663343 +) + +$(P +In a sense, $(C bitsToClear) determines which bits to set to 0. The other bits are not affected. +) + +$(H6 $(C &) determines whether a bit is 1 or not) + +$(P +If one of the expressions has only one bit set to 1, then it can be used to query whether the corresponding bit of the other expression is 1: +) + +--- + uint expression = 123456789; + uint bitToQuery = 0x00010000; + + print(expression); + print(bitToQuery); + writeln(expression $(HILITE &) bitToQuery ? "yes, 1" : "not 1"); +--- + +$(P +The bit that is being $(I queried) is highlighted: +) + +$(SHELL_SMALL + 000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 + 00000000000000010000000000000000 00010000 65536 +yes, 1 +) + +$(P +Let's query another bit of the same expression by this time having another bit of $(C bitToQuery) set to 1: +) + +--- + uint bitToQuery = 0x00001000; +--- + +$(SHELL_SMALL +0000011101011011110$(HILITE 0)110100010101 075bcd15 123456789 +00000000000000000001000000000000 00001000 4096 +not 1 +) + +$(P +When the query expression has more than one bit set to 1, then the query would determine whether $(I any) of the corresponding bits in the other expression are 1. +) + +$(H6 Right-shifting by one is the equivalent of dividing by two) + +$(P +Shifting all of the bits of a value by one position to the right produces half of the original value. The reason for this can be seen in the digit-value table above: In that table, every bit has half the value of the bit that is on its left. +) + +$(P +Shifting a value to the right multiple bits at a time means dividing by 2 for that many times. For example, right-shifting by 3 bits would divide a value by 8: +) + +--- + uint value = 8000; + + writeln(value >> 3); +--- + +$(SHELL_SMALL +1000 +) + +$(P +According to how the $(I twos complement) system works, right-shifting has the same effect on signed values: +) + +--- + $(HILITE int) value = -8000; + + writeln(value >> 3); +--- + +$(SHELL_SMALL +-1000 +) + +$(H6 Left-shifting by one is the equivalent of multiplying by two) + +$(P +Because each bit is two times the value of the bit on its right, shifting a value one bit to the left means multiplying that value by two: +) + +--- + uint value = 10; + + writeln(value << 5); +--- + +$(P +Multiplying by 2 a total of 5 times is the same as multiplying by 32: +) + +$(SHELL_SMALL +320 +) + +$(H5 Common uses) + +$(H6 $(IX flag, bit) Flags) + +$(P +Flags are single-bit independent data that are kept together in the same variable. As they are only one bit wide each, they are suitable for representing binary concepts like enabled/disabled, valid/invalid, etc. +) + +$(P +Such one-bit concepts are sometimes encountered in D modules that are based on C libraries. +) + +$(P +Flags are usually defined as non-overlapping values of an $(C enum) type. +) + +$(P +As an example, let's consider a car racing game where the realism of the game is configurable: +) + +$(UL +$(LI The fuel consumption is realistic or not.) +$(LI Collisions can damage the cars or not.) +$(LI Tires can deteriorate by use or not.) +$(LI Skid marks are left on the road surface or not.) +) + +$(P +These configuration options can be specified at run time by the following $(C enum) values: +) + +--- +enum Realism { + fuelUse = 1 << 0, + bodyDamage = 1 << 1, + tireUse = 1 << 2, + skidMarks = 1 << 3 +} +--- + +$(P +Note that all of those values consist of single bits that do not conflict with each other. Each value is determined by left-shifting 1 by a different number of bits. The corresponding bit representations are the following: +) + +$(MONO +fuelUse : 0001 +bodyDamage: 0010 +tireUse : 0100 +skidMarks : 1000 +) + +$(P +Since their 1 bits do not match others', these values can be combined by the $(C |) operator to be kept in the same variable. For example, the two configuration options that are related to tires can be combined as in the following code: +) + +--- + Realism flags = Realism.tireUse | Realism.skidMarks; + writefln("%b", flags); +--- + +$(P +The bits of these two flags would be side-by-side in the variable $(C flags): +) + +$(SHELL_SMALL +1100 +) + +$(P +Later, these flags can be queried by the $(C &) operator: +) + +--- + if (flags & Realism.fuelUse) { + // ... code related to fuel consumption ... + } + + if (flags & Realism.tireUse) { + // ... code related to tire consumption ... + } +--- + +$(P +The $(C &) operator produces 1 only if the specified flag is set in $(C flags). +) + +$(P +Also note that the result is usable in the $(C if) condition due to automatic conversion of the nonzero value to $(C true). The conditional expression is $(C false) when the result of $(C &) is 0 and $(C true) otherwise. As a result, the corresponding code block is executed only if the flag is enabled. +) + +$(H6 $(IX mask, bit) Masking) + +$(P +In some libraries and some protocols an integer value may carry more than one piece of information. For example, the upper 3 bits of a 32-bit value may have a certain meaning, while the lower 29 bits may have another meaning. These separate parts of data can be extracted from the variable by masking. +) + +$(P +The four octets of an IPv4 address are an example of this concept. The octets are the individual values that make up the common dotted representation of an IPv4 address. They are all kept in a single 32-bit value. For example, the IPv4 address 192.168.1.2 is the 32-bit value 0xc0a80102: +) + +$(MONO +c0 == 12 * 16 + 0 = 192 +a8 == 10 * 16 + 8 = 168 +01 == 0 * 16 + 1 = 1 +02 == 0 * 16 + 2 = 2 +) + +$(P +A mask consists of a number of 1 bits that would $(I cover) the specific part of a variable. $(I "And"ing) the value by the mask extracts the part of the variable that is covered by that mask. For example, the mask value of 0x000000ff would cover the lower 8 bits of a value: +) + +--- + uint value = 123456789; + uint mask = 0x000000ff; + + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); +--- + +$(P +The bits that are covered by the mask are highlighted. All of the other bits are cleared: +) + +$(SHELL_SMALL +value : 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 +mask : 00000000000000000000000011111111 000000ff 255 +result: 000000000000000000000000$(HILITE 00010101) 00000015 21 +) + +$(P +Let's apply the same method to the 0xc0a80102 IPv4 address with a mask that would cover the uppermost 8 bits: +) + +--- + uint value = 0xc0a80102; + uint mask = 0xff000000; + + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); +--- + +$(P +This mask covers the uppermost 8 bits of the value: +) + +$(SHELL_SMALL +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: $(HILITE 11000000)000000000000000000000000 c0000000 3221225472 +) + +$(P +However, note that the printed result is not the expected 192 but 3221225472. That is because the masked value must also be shifted all the way to the right-hand side. Shifting the value 24 bit positions to the right would produce the value that those 8 bits represent: +) + +--- + uint value = 0xc0a80102; + uint mask = 0xff000000; + + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print((value & mask) $(HILITE >> 24)); +--- + +$(SHELL_SMALL +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: 000000000000000000000000$(HILITE 11000000) 000000c0 $(HILITE 192) +) + +$(PROBLEM_COK + +$(PROBLEM +Write a function that returns an IPv4 address in its dotted form: + +--- +string dotted(uint address) { + // ... +} + +unittest { + assert(dotted(0xc0a80102) == "192.168.1.2"); +} +--- + +) + +$(PROBLEM +Write a function that converts four octet values to the corresponding 32-bit IPv4 address: + +--- +uint ipAddress(ubyte octet3, // most significant octet + ubyte octet2, + ubyte octet1, + ubyte octet0) { // least significant octet + // ... +} + +unittest { + assert(ipAddress(192, 168, 1, 2) == 0xc0a80102); +} +--- + +) + +$(PROBLEM +Write a function that can be used for making a mask. It should start with the specified bit and have the specified width: + +--- +uint mask(int lowestBit, int width) { + // ... +} + +unittest { + assert(mask(2, 5) == + 0b_0000_0000_0000_0000_0000_0000_0111_1100); + // ↑ + // lowest bit is 2, + // and the mask is 5-bit wide +} +--- + +) + +) + +Macros: + SUBTITLE=Bit Operations + + DESCRIPTION=The low-level features of D that allow manipulating values at bit-level. + + KEYWORDS=d programming language tutorial book bit operations diff --git a/target/blurbs.d b/target/blurbs.d new file mode 100644 index 0000000..86c4a68 --- /dev/null +++ b/target/blurbs.d @@ -0,0 +1,42 @@ +DDoc + +$(COMMENTED_OUT + + +"Ali's explanations are succinct and on target. I like that he provides rationale for why D was designed in a particular way and how I can use it most effectively. This is the best computer language book I've read." -- Robbin Carlson, Luthier and Enterprise Architect + + + +“I have been using Ali’s online D book to teach D at the university level. It is up-to-date, complete, and most importantly, extremely readable. Having a print version is even better! This is now the 'go-to’ book for learning D programming.” -- Chuck Allison, Professor and Chair, Computer Science Department, Utah Valley University + + + +"This book is one of the best guides through the language that I've seen." -- Andrew Wray, D Enthusiast + +(w0rp devw0rp@gmail.com) +http://forum.dlang.org/thread/lqqf2l$b1u$1@digitalmars.com#post-rngxsnyvdkererhdtmhh:40forum.dlang.org + + + +"I encourage anyone considering D to read this book. Not exactly "D for Dummies" but it's easy to follow even if you don't have much experience with compiled languages." -- bachmeier, Reddit user + + + +"Having worked through the book, I have to say this is one of the easiest to follow and distraction free read there is and the fact that it made learning a new language a total breeze really impressed me." -- Imran Khan, Student + +imrankhan4587@gmail.com +http://www.reddit.com/r/programming/comments/2bl51j/programming_in_d_a_great_online_book_for_learning/cj6qveg + + + + +"I taught a CS2 Data Structures class in D with more success and student appreciation than when using either C++ or Java as it's an ideal language to express the relevant concepts at all scales, from detailed to big picture, without needless complexity. + +Ali Çehreli's tutorial played a central role supporting students especially during the first half of the course --- without it the course simply would not have worked, so "many thanks Ali" --- and an important part of that is its linearity --- it can be read with only backward dependencies. This meant that with hard work even students of little experience and only moderate current abilities could get up to speed, and we saw just that. It is hard to overstate this factor. I unreservedly recommend this book to all." -- Dr. Carl Sturtivant, University of Minnesota Department of Computer Science & Engineering + + + +"D is pristine, clean, immensely powerful, and arguably the actual state-of-the-art programming language. Ali's book is a gem. Clear, concise, and complete." -- Olivier Henley + + +) diff --git a/target/cast.d b/target/cast.d new file mode 100644 index 0000000..501a86a --- /dev/null +++ b/target/cast.d @@ -0,0 +1,715 @@ +Ddoc + +$(DERS_BOLUMU $(IX type conversion) $(IX conversion, type) Type Conversions) + +$(P +Variables must be compatible with the expressions that they take part in. As it has probably been obvious from the programs that we have seen so far, D is a $(I statically typed language), meaning that the compatibility of types is validated at compile time. +) + +$(P +All of the expressions that we have written so far always had compatible types because otherwise the code would be rejected by the compiler. The following is an example of code that has incompatible types: +) + +--- + char[] slice; + writeln(slice + 5); $(DERLEME_HATASI) +--- + +$(P +The compiler rejects the code due to the incompatible types $(C char[]) and $(C int) for the addition operation: +) + +$(SHELL +Error: $(HILITE incompatible types) for ((slice) + (5)): 'char[]' and 'int' +) + +$(P +Type incompatibility does not mean that the types are different; different types can indeed be used in expressions safely. For example, an $(C int) variable can safely be used in place of a $(C double) value: +) + +--- + double sum = 1.25; + int increment = 3; + sum += increment; +--- + +$(P +Even though $(C sum) and $(C increment) are of different types, the code above is valid because incrementing a $(C double) variable by an $(C int) value is legal. +) + +$(H5 $(IX automatic type conversion) $(IX implicit type conversion) Automatic type conversions) + +$(P +Automatic type conversions are also called $(I implicit type conversions). +) + +$(P +Although $(C double) and $(C int) are compatible types in the expression above, the addition operation must still be evaluated as a specific type at the microprocessor level. As you would remember from the $(LINK2 /ders/d.en/floating_point.html, Floating Point Types chapter), the 64-bit type $(C double) is $(I wider) (or $(I larger)) than the 32-bit type $(C int). Additionally, any value that fits in an $(C int) also fits in a $(C double). +) + +$(P +When the compiler encounters an expression that involves mismatched types, it first converts the parts of the expressions to a common type and then evaluates the overall expression. The automatic conversions that are performed by the compiler are in the direction that avoids data loss. For example, $(C double) can hold any value that $(C int) can hold but the opposite is not true. The $(C +=) operation above can work because any $(C int) value can safely be converted to $(C double). +) + +$(P +The value that has been generated automatically as a result of a conversion is always an anonymous (and often temporary) variable. The original value does not change. For example, the automatic conversion during $(C +=) above does not change the type of $(C increment); it is always an $(C int). Rather, a temporary value of type $(C double) is constructed with the value of $(C increment). The conversion that takes place in the background is equivalent to the following code: +) + +--- + { + double $(I an_anonymous_double_value) = increment; + sum += $(I an_anonymous_double_value); + } +--- + +$(P +The compiler converts the $(C int) value to a temporary $(C double) value and uses that value in the operation. In this example, the temporary variable lives only during the $(C +=) operation. +) + +$(P +Automatic conversions are not limited to arithmetic operations. There are other cases where types are converted to other types automatically. As long as the conversions are valid, the compiler takes advantage of type conversions to be able to use values in expressions. For example, a $(C byte) value can be passed for an $(C int) parameter: +) + +--- +void func(int number) { + // ... +} + +void main() { + byte smallValue = 7; + func(smallValue); // automatic type conversion +} +--- + +$(P +In the code above, first a temporary $(C int) value is constructed and the function is called with that value. +) + +$(H6 $(IX integer promotion) $(IX promotion, integer) Integer promotions) + +$(P +Values of types that are on the left-hand side of the following table never take part in arithmetic expressions as their actual types. Each type is first promoted to the type that is on the right-hand side of the table. +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From To
boolint
byteint
ubyteint
shortint
ushortint
charint
wcharint
dcharuint
+ +$(P +Integer promotions are applied to $(C enum) values as well. +) + +$(P +The reasons for integer promotions are both historical (where the rules come from C) and the fact that the natural arithmetic type for the microprocessor is $(C int). For example, although the following two variables are both $(C ubyte), the addition operation is performed only after both of the values are individually promoted to $(C int): +) + +--- + ubyte a = 1; + ubyte b = 2; + writeln(typeof(a + b).stringof); // the addition is not in ubyte +--- + +$(P +The output: +) + +$(SHELL +int +) + +$(P +Note that the types of the variables $(C a) and $(C b) do not change; only their values are temporarily promoted to $(C int) for the duration of the addition operation. +) + +$(H6 $(IX arithmetic conversion) Arithmetic conversions) + +$(P +There are other conversion rules that are applied for arithmetic operations. In general, automatic arithmetic conversions are applied in the safe direction: from the $(I narrower) type to the $(I wider) type. Although this rule is easy to remember and is correct in most cases, automatic conversion rules are very complicated and in the case of signed-to-unsigned conversions, carry some risk of bugs. +) + +$(P +The arithmetic conversion rules are the following: +) + +$(OL + +$(LI If one of the values is $(C real), then the other value is converted to $(C real)) + +$(LI Else, if one of the values is $(C double), then the other value is converted to $(C double)) + +$(LI Else, if one of the values is $(C float), then the other value is converted to $(C float)) + +$(LI Else, first $(I integer promotions) are applied according to the table above, and then the following rules are followed: + +$(OL +$(LI If both types are the same, then no more steps needed) +$(LI If both types are signed or both types are unsigned, then the narrower value is converted to the wider type) +$(LI If the signed type is wider than the unsigned type, then the unsigned value is converted to the signed type) +$(LI Otherwise the signed type is converted to the unsigned type) +) +) +) + +$(P +Unfortunately, the last rule above can cause subtle bugs: +) + +--- + int a = 0; + int b = 1; + size_t c = 0; + writeln(a - b + c); // Surprising result! +--- + +$(P +Surprisingly, the output is not -1, but $(C size_t.max): +) + +$(SHELL +18446744073709551615 +) + +$(P +Although one would expect $(C (0 - 1 + 0)) to be calculated as -1, according to the rules above, the type of the entire expression is $(C size_t), not $(C int); and since $(C size_t) cannot hold negative values, the result overflows and becomes $(C size_t.max). +) + +$(H6 Slice conversions) + +$(P +$(IX fixed-length array, conversion to slice) $(IX static array, conversion to slice) As a convenience, fixed-length arrays can automatically be converted to slices when calling a function: +) + +--- +import std.stdio; + +void foo() { + $(HILITE int[2]) array = [ 1, 2 ]; + bar(array); // Passes fixed-length array as a slice +} + +void bar($(HILITE int[]) slice) { + writeln(slice); +} + +void main() { + foo(); +} +--- + +$(P +$(C bar()) receives a slice to all elements of the fixed-length array and prints it: +) + +$(SHELL +[1, 2] +) + +$(P +$(B Warning:) A $(I local) fixed-length array must not be passed as a slice if the function stores the slice for later use. For example, the following program has a bug because the slice that $(C bar()) stores would not be valid after $(C foo()) exits: +) + +--- +import std.stdio; + +void foo() { + int[2] array = [ 1, 2 ]; + bar(array); // Passes fixed-length array as a slice + +} // ← NOTE: 'array' is not valid beyond this point + +int[] sliceForLaterUse; + +void bar(int[] slice) { + // Saves a slice that is about to become invalid + sliceForLaterUse = slice; + writefln("Inside bar : %s", sliceForLaterUse); +} + +void main() { + foo(); + + /* BUG: Accesses memory that is not array elements anymore */ + writefln("Inside main: %s", sliceForLaterUse); +} +--- + +$(P +The result of such a bug is undefined behavior. A sample execution can prove that the memory that used to be the elements of $(C array) has already been reused for other purposes: +) + +$(SHELL +Inside bar : [1, 2] $(SHELL_NOTE actual elements) +Inside main: [4396640, 0] $(SHELL_NOTE_WRONG a manifestation of undefined behavior) +) + +$(H6 $(C const) conversions) + +$(P +As we have seen earlier in the $(LINK2 /ders/d.en/function_parameters.html, Function Parameters chapter), reference types can automatically be converted to the $(C const) of the same type. Conversion to $(C const) is safe because the width of the type does not change and $(C const) is a promise to not modify the variable: +) + +--- +char[] parenthesized($(HILITE const char[]) text) { + return "{" ~ text ~ "}"; +} + +void main() { + $(HILITE char[]) greeting; + greeting ~= "hello world"; + parenthesized(greeting); +} +--- + +$(P +The mutable $(C greeting) above is automatically converted to a $(C const char[]) as it is passed to $(C parenthesized()). +) + +$(P +As we have also seen earlier, the opposite conversion is not automatic. A $(C const) reference is not automatically converted to a mutable reference: +) + +--- +char[] parenthesized(const char[] text) { + char[] argument = text; $(DERLEME_HATASI) +// ... +} +--- + +$(P +Note that this topic is only about references; since variables of value types are copied, it is not possible to affect the original through the copy anyway: +) + +--- + const int totalCorners = 4; + int theCopy = totalCorners; // compiles (value type) +--- + +$(P +The conversion from $(C const) to mutable above is legal because the copy is not a reference to the original. +) + +$(H6 $(C immutable) conversions) + +$(P +Because $(C immutable) specifies that a variable can never change, neither conversion from $(C immutable) nor to $(C immutable) are automatic: +) + +--- + string a = "hello"; // immutable characters + char[] b = a; $(DERLEME_HATASI) + string c = b; $(DERLEME_HATASI) +--- + +$(P +As with $(C const) conversions above, this topic is also only about reference types. Since variables of value types are copied anyway, conversions to and from $(C immutable) are valid: +) + +--- + immutable a = 10; + int b = a; // compiles (value type) +--- + +$(H6 $(C enum) conversions) + +$(P +As we have seen in the $(LINK2 /ders/d.en/enum.html, $(C enum) chapter), $(C enum) is for defining $(I named constants): +) + +--- + enum Suit { spades, hearts, diamonds, clubs } +--- + +$(P +Remember that since no values are specified explicitly above, the values of the $(C enum) members start with zero and are automatically incremented by one. Accordingly, the value of $(C Suit.clubs) is 3. +) + +$(P +$(C enum) values are atomatically converted to integral types. For example, the value of $(C Suit.hearts) is taken to be 1 in the following calculation and the result becomes 11: +) + +--- + int result = 10 + Suit.hearts; + assert(result == 11); +--- + +$(P +The opposite conversion is not automatic: Integer values are not automatically converted to corresponding $(C enum) values. For example, the $(C suit) variable below might be expected to become $(C Suit.diamonds), but the code cannot be compiled: +) + +--- + Suit suit = 2; $(DERLEME_HATASI) +--- + +$(P +As we will see below, conversions from integers to $(C enum) values are still possible but they must be explicit. +) + +$(H6 $(IX bool, automatic conversion) $(C bool) conversions) + +$(P +$(C false) and $(C true) are automatically converted to 0 and 1, respectively: +) + +--- + int a = false; + assert(a == 0); + + int b = true; + assert(b == 1); +--- + +$(P +Regarding $(I literal values), the opposite conversion is automatic only for two special literal values: 0 and 1 are converted automatically to $(C false) and $(C true), respectively: +) + +--- + bool a = 0; + assert(!a); // false + + bool b = 1; + assert(b); // true +--- + +$(P +Other literal values cannot be converted to $(C bool) automatically: +) + +--- + bool b = 2; $(DERLEME_HATASI) +--- + +$(P +Some statements make use of logical expressions: $(C if), $(C while), etc. For the logical expressions of such statements, not only $(C bool) but most other types can be used as well. The value zero is automatically converted to $(C false) and the nonzero values are automatically converted to $(C true). +) + +--- + int i; + // ... + + if (i) { // ← int value is being used as a logical expression + // ... 'i' is not zero + + } else { + // ... 'i' is zero + } +--- + +$(P +Similarly, $(C null) references are automatically converted to $(C false) and non-$(C null) references are automatically converted to $(C true). This makes it easy to ensure that a reference is non-$(C null) before actually using it: +) + +--- + int[] a; + // ... + + if (a) { // ← automatic bool conversion + // ... not null; 'a' can be used ... + + } else { + // ... null; 'a' cannot be used ... + } +--- + +$(H5 $(IX explicit type conversion) $(IX type conversion, explicit) Explicit type conversions) + +$(P +As we have seen above, there are cases where automatic conversions are not available: +) + +$(UL +$(LI Conversions from wider types to narrower types) +$(LI Conversions from $(C const) to mutable) +$(LI $(C immutable) conversions) +$(LI Conversions from integers to $(C enum) values) +$(LI etc.) +) + +$(P +If such a conversion is known to be safe, the programmer can explicitly ask for a type conversion by one of the following methods: +) + +$(UL +$(LI Construction syntax) +$(LI $(C std.conv.to) function) +$(LI $(C std.exception.assumeUnique) function) +$(LI $(C cast) operator) +) + +$(H6 $(IX construction, type conversion) Construction syntax) + +$(P +The $(C struct) and $(C class) construction syntax is available for other types as well: +) + +--- + $(I DestinationType)(value) +--- + +$(P +For example, the following $(I conversion) makes a $(C double) value from an $(C int) value, presumably to preserve the fractional part of the division operation: +) + +--- + int i; + // ... + const result = $(HILITE double(i)) / 2; +--- + +$(H6 $(IX to, std.conv) $(C to()) for most conversions) + +$(P +The $(C to()) function, which we have already used mostly to convert values to $(C string), can actually be used for many other types. Its complete syntax is the following: +) + +--- + to!($(I DestinationType))(value) +--- + +$(P +Being a template, $(C to()) can take advantage of the shortcut template parameter notation: When the destination type consists only of a single token (generally, $(I a single word)), it can be called without the first pair of parentheses: +) + +--- + to!$(I DestinationType)(value) +--- + +$(P +The following program is trying to convert a $(C double) value to $(C short) and a $(C string) value to $(C int): +) + +--- +void main() { + double d = -1.75; + + short s = d; $(DERLEME_HATASI) + int i = "42"; $(DERLEME_HATASI) +} +--- + +$(P +Since not every $(C double) value can be represented as a $(C short) and not every $(C string) can be represented as an $(C int), those conversions are not automatic. When it is known by the programmer that the conversions are in fact safe or that the potential consequences are acceptable, then the types can be converted by $(C to()): +) + +--- +import std.conv; + +void main() { + double d = -1.75; + + short s = to!short(d); + assert(s == -1); + + int i = to!int("42"); + assert(i == 42); +} +--- + +$(P +Note that because $(C short) cannot carry fractional values, the converted value is -1. +) + +$(P +$(C to()) is safe: It throws an exception when a conversion is not possible. +) + +$(H6 $(IX assumeUnique, std.exception) $(C assumeUnique()) for fast $(C immutable) conversions) + +$(P +$(C to()) can perform $(C immutable) conversions as well: +) + +--- + int[] slice = [ 10, 20, 30 ]; + auto immutableSlice = to!($(HILITE immutable int[]))(slice); +--- + +$(P +In order to guarantee that the elements of $(C immutableSlice) will never change, it cannot share the same elements with $(C slice). For that reason, $(C to()) creates an additional slice with $(C immutable) elements above. Otherwise, modifications to the elements of $(C slice) would cause the elements of $(C immutableSlice) change as well. This behavior is the same with the $(C .idup) property of arrays. +) + +$(P +We can see that the elements of $(C immutableSlice) are indeed copies of the elements of $(C slice) by looking at the addresses of their first elements: +) + +--- + assert(&(slice[0]) $(HILITE !=) &(immutableSlice[0])); +--- + +$(P +Sometimes this copy is unnecessary and may slow the speed of the program noticeably in certain cases. As an example of this, let's look at the following function that takes an $(C immutable) slice: +) + +--- +void calculate(immutable int[] coordinates) { + // ... +} + +void main() { + int[] numbers; + numbers ~= 10; + // ... various other modifications ... + numbers[0] = 42; + + calculate(numbers); $(DERLEME_HATASI) +} +--- + +$(P +The program above cannot be compiled because the caller is not passing an $(C immutable) argument to $(C calculate()). As we have seen above, an $(C immutable) slice can be created by $(C to()): +) + +--- +import std.conv; +// ... + auto immutableNumbers = to!(immutable int[])(numbers); + calculate(immutableNumbers); // ← now compiles +--- + +$(P +However, if $(C numbers) is needed only to produce this argument and will never be used after the function is called, copying its elements to $(C immutableNumbers) would be unnecessary. $(C assumeUnique()) makes the elements of a slice $(C immutable) without copying: +) + +--- +import std.exception; +// ... + auto immutableNumbers = assumeUnique(numbers); + calculate(immutableNumbers); + assert(numbers is null); // the original slice becomes null +--- + +$(P +$(C assumeUnique()) returns a new slice that provides $(C immutable) access to the existing elements. It also makes the original slice $(C null) to prevent the elements from accidentally being modified through it. +) + +$(H6 $(IX cast) The $(C cast) operator) + +$(P +Both $(C to()) and $(C assumeUnique()) make use of the conversion operator $(C cast), which is available to the programmer as well. +) + +$(P +The $(C cast) operator takes the destination type in parentheses: +) + +--- + cast($(I DestinationType))value +--- + +$(P +$(C cast) is powerful even for conversions that $(C to()) cannot safely perform. For example, $(C to()) fails for the following conversions at runtime: +) + +--- + Suit suit = to!Suit(7); $(CODE_NOTE_WRONG throws exception) + bool b = to!bool(2); $(CODE_NOTE_WRONG throws exception) +--- + +$(SHELL +std.conv.ConvException@phobos/std/conv.d(1778): Value (7) +$(HILITE does not match any member) value of enum 'Suit' +) + +$(P +Sometimes only the programmer can know whether an integer value corresponds to a valid $(C enum) value or that it makes sense to treat an integer value as a $(C bool). The $(C cast) operator can be used when the conversion is known to be correct according the program's logic: +) + +--- + // Probably incorrect but possible: + Suit suit = cast(Suit)7; + + bool b = cast(bool)2; + assert(b); +--- + +$(P +$(C cast) is the only option when converting to and from pointer types: +) + +--- + $(HILITE void *) v; + // ... + int * p = cast($(HILITE int*))v; +--- + +$(P +Although rare, some C library interfaces make it necessary to store a pointer value as a non-pointer type. If it is guaranteed that the conversion will preserve the actual value, $(C cast) can convert between pointer and non-pointer types as well: +) + +--- + size_t savedPointerValue = cast($(HILITE size_t))p; + // ... + int * p2 = cast($(HILITE int*))savedPointerValue; +--- + +$(H5 Summary) + +$(UL + +$(LI Automatic type conversions are mostly in the safe direction: From the narrower type to the wider type and from mutable to $(C const).) + +$(LI However, conversions to unsigned types may have surprising effects because unsigned types cannot have negative values.) + +$(LI $(C enum) types can automatically be converted to integer values but the opposite conversion is not automatic.) + +$(LI $(C false) and $(C true) are automatically converted to 0 and 1 respectively. Similarly, zero values are automatically converted to $(C false) and nonzero values are automatically converted to $(C true).) + +$(LI $(C null) references are automatically converted to $(C false) and non-$(C null) references are automatically converted to $(C true).) + +$(LI The construction syntax can be used for explicit conversions.) + +$(LI $(C to()) covers most of the explicit conversions.) + +$(LI $(C assumeUnique()) converts to $(C immutable) without copying.) + +$(LI The $(C cast) operator is the most powerful conversion tool.) + +) + +Macros: + SUBTITLE=Type Conversions + + DESCRIPTION=The automatic and explicit type conversions in the D programming language. + + + KEYWORDS=d programming lesson book tutorial type conversions diff --git a/target/characters.d b/target/characters.d new file mode 100644 index 0000000..c7ce3a0 --- /dev/null +++ b/target/characters.d @@ -0,0 +1,594 @@ +Ddoc + +$(DERS_BOLUMU $(IX character) Characters) + +$(P +Characters are building blocks of strings. Any symbol of a writing system is called a character: letters of alphabets, numerals, punctuation marks, the space character, etc. Confusingly, building blocks of characters themselves are called characters as well. +) + +$(P +Arrays of characters make up $(I strings). We have seen arrays in the previous chapter; strings will be covered two chapters later. +) + +$(P +Like any other data, characters are also represented as integer values that are made up of bits. For example, the integer value of the lowercase $(C 'a') is 97 and the integer value of the numeral $(C '1') is 49. These values are merely a convention, assigned when the ASCII standard was designed. +) + +$(P +In many programming languages, characters are represented by the $(C char) type, which can hold only 256 distinct values. If you are familiar with the $(C char) type from other languages, you may already know that it is not large enough to support the symbols of many writing systems. Before getting to the three distinct character types of D, let's first take a look at the history of characters in computer systems. +) + +$(H5 History) + +$(H6 $(IX ASCII) ASCII Table) + +$(P +The ASCII table was designed at a time when computer hardware was very limited compared to modern systems. Having been based on 7 bits, the ASCII table can have 128 distinct code values. That many distinct values are sufficient to represent the lowercase and uppercase versions of the 26 letters of the basic Latin alphabet, numerals, commonly used punctuation marks, and some terminal control characters. +) + +$(P +As an example, the ASCII codes of the characters of the string $(STRING "hello") are the following (the commas are inserted just to make it easier to read): +) + +$(MONO +104, 101, 108, 108, 111 +) + +$(P +Every code above represents a single letter of $(STRING "hello"). For example, there are two 108 values corresponding to the two $(C 'l') letters. +) + +$(P +The codes of the ASCII table were later increased to 8 bits to become the Extended ASCII table. The Extended ASCII table has 256 distinct codes. +) + +$(H6 $(IX code page) IBM Code Pages) + +$(P +IBM Corporation has defined a set of tables, each one of which assign the codes of the Extended ASCII table from 128 to 255 to one or more writing systems. These code tables allowed supporting the letters of many more alphabets. For example, the special letters of the Turkish alphabet are a part of IBM's code page 857. +) + +$(P +Despite being much more useful than ASCII, code pages have some problems and limitations: In order to display text correctly, it must be known what code page a given text was originally written in. This is because the same code corresponds to a different character in most other tables. For example, the code that represents $(C 'Ğ') in table 857 corresponds to $(C 'ª') in table 437. +) + +$(P +In addition to the difficulty in supporting multiple alphabets in a single document, alphabets that have more than 128 non-ASCII characters cannot be supported by an IBM table at all. +) + +$(H6 ISO/IEC 8859 Code Pages) + +$(P +The ISO/IEC 8859 code pages are a result of international standardization efforts. They are similar to IBM's code pages in how they assign codes to characters. As an example, the special letters of the Turkish alphabet appear in code page 8859-9. These tables have the same problems and limitations as IBM's tables. For example, the Dutch digraph ij does not appear in any of these tables. +) + +$(H6 $(IX unicode) Unicode) + +$(P +Unicode solves all problems and limitations of previous solutions. Unicode includes more than a hundred thousand characters and symbols of the writing systems of many human languages, current and old. (New ones are constanly under review for addition to the table.) Each of these characters has a unique code. Documents that are encoded in Unicode can include all characters of separate writing systems without any confusion or limitation. +) + +$(H5 $(IX encoding, unicode) Unicode encodings) + +$(P +Unicode assigns a unique code for each character. Since there are more Unicode characters than an 8-bit value can hold, some characters must be represented by at least two 8-bit values. For example, the Unicode character code of $(C 'Ğ') (286) is greater than the maximum value of a $(C ubyte). +) + +$(P +The way characters are represented in electronic mediums is called their $(I encoding). We have seen above how the string $(STRING "hello") is encoded in ASCII. We will now see three Unicode encodings that correspond to D's character types. +) + +$(P +$(IX UTF-32) $(B UTF-32:) This encoding uses 32 bits (4 bytes) for every Unicode character. The UTF-32 encoding of $(STRING "hello") is similar to its ASCII encoding, but every character is represented with 4 bytes: +) + +$(MONO +0,0,0,104, 0,0,0,101, 0,0,0,108, 0,0,0,108, 0,0,0,111 +) + +$(P +As another example, the UTF-32 encoding of $(STRING "aĞ") is the following: +) + +$(MONO +0,0,0,97, 0,0,1,30 +) + +$(P +$(I $(B Note:) The order of the bytes of UTF-32 may be different on different computer systems.) +) + +$(P +$(C 'a') and $(C 'Ğ') are represented by 1 and 2 significant bytes respectively, and the values of the other 5 bytes are all zeros. These zeros can be thought of as filler bytes to make every Unicode character occupy 4 bytes each. +) + +$(P +For documents based on the basic Latin alphabet, this encoding always uses 4 times as many bytes as the ASCII encoding. When most of the characters of a given document have ASCII equivalents, the 3 filler bytes for each of those characters make this encoding more wasteful compared to other encodings. +) + +$(P +On the other hand, there are benefits of representing every character by an equal number of bytes. For example, the next Unicode character is always exactly four bytes away. +) + +$(P +$(IX UTF-16) $(B UTF-16:) This encoding uses 16 bits (2 bytes) to represent most of the Unicode characters. Since 16 bits can have about 65 thousand unique values, the other (less commonly used) 35 thousand Unicode characters must be represented using additional bytes. +) + +$(P +As an example, $(STRING "aĞ") is encoded by 4 bytes in UTF-16: +) + +$(MONO +0,97, 1,30 +) + +$(P +$(I $(B Note:) The order of the bytes of UTF-16 may be different on different computer systems.) +) + +$(P +Compared to UTF-32, this encoding takes less space for most documents, but because some characters must be represented by more than 2 bytes, UTF-16 is more complicated to process. +) + +$(P +$(IX UTF-8) $(B UTF-8:) This encoding uses 1 to 4 bytes for every character. If a character has an equivalent in the ASCII table, it is represented by 1 byte, with the same numeric code as in the ASCII table. The rest of the Unicode characters are represented by 2, 3, or 4 bytes. Most of the special characters of the European writing systems are among the group of characters that are represented by 2 bytes. +) + +$(P +For most documents in western countries, UTF-8 is the encoding that takes the least amount of space. Another benefit of UTF-8 is that the documents that were produced using ASCII can be opened directly (without conversion) as UTF-8 documents. UTF-8 also does not waste any space with filler bytes, as every character is represented by significant bytes. As an example, the UTF-8 encoding of $(STRING "aĞ") is: +) + +$(MONO +97, 196,158 +) + +$(H5 The character types of D) + +$(P +$(IX char) +$(IX wchar) +$(IX dchar) +There are three D types to represent characters. These characters correspond to the three Unicode encodings mentioned above. Copying from $(LINK2 /ders/d.en/types.html, the Fundamental Types chapter): +) + + + + + + + + + + + + + + + + + + +
Type Definition Initial Value
charUTF-8 code unit0xFF
wcharUTF-16 code unit0xFFFF
dcharUTF-32 code unit and Unicode code point0x0000FFFF
+ +$(P +Compared to some other programming languages, characters in D may consist of different number of bytes. For example, because $(C 'Ğ') must be represented by at least 2 bytes in Unicode, it doesn't fit in a variable of type $(C char). On the other hand, because $(C dchar) consists of 4 bytes, it can hold any Unicode character. +) + +$(H5 $(IX literal, character) Character literals) + +$(P +Literals are constant values that are written in the program as a part of the source code. In D, character literals are specified within single quotes: +) + +--- + char letter_a = 'a'; + wchar letter_e_acute = 'é'; +--- + +$(P +Double quotes are not valid for characters because double quotes are used when specifying $(I strings), which we will see in $(LINK2 /ders/d.en/strings.html, a later chapter). $(C 'a') is a character literal and $(STRING "a") is a string literal that consists of a single character. +) + +$(P +Variables of type $(C char) can only hold letters that are in the ASCII table. +) + +$(P +There are many ways of inserting characters in code: +) + +$(UL +$(LI Most naturally, typing them on the keyboard. +) + +$(LI Copying from another program or another text. For example, you can copy and paste from a web site, or from a program that is specifically for displaying Unicode characters. (One such program in most Linux environments is $(I Character Map) ($(C charmap) on the terminal).) +) + +$(LI Using short names of the characters. The syntax for this feature is $(C \&$(I character_name);). For example, the name of the Euro character is $(C euro) and it can be specified in the program like the following: + +--- + wchar currencySymbol = '\€'; +--- + +$(P +See $(LINK2 http://dlang.org/entity.html, the list of named characters) for all characters that can be specified this way. +) + +) + +$(LI Specifying characters by their integer Unicode values: + +--- + char a = 97; + wchar Ğ = 286; +--- + +) + +$(LI Specifying the codes of the characters of the ASCII table either by $(C \$(I value_in_octal)) or $(C \x$(I value_in_hexadecimal)) syntax: + +--- + char questionMarkOctal = '\77'; + char questionMarkHexadecimal = '\x3f'; +--- + +) + +$(LI Specifying the Unicode values of the characters by using the $(C \u$(I four_digit_value)) syntax for $(C wchar), and the $(C \U$(I eight_digit_value)) syntax for $(C dchar) (note $(C u) versus $(C U)). The Unicode values must be specified in hexadecimal: + +--- + wchar Ğ_w = '\u011e'; + dchar Ğ_d = '\U0000011e'; +--- + +) + +) + +$(P +These methods can be used to specify the characters within strings as well. For example, the following two lines have the same string literals: +) +--- + writeln("Résumé preparation: 10.25€"); + writeln("\x52\ésum\u00e9 preparation: 10.25\€"); +--- + +$(H5 $(IX control character) Control characters) + +$(P +Some characters only affect the formatting of the text, they don't have a visual representation themselves. For example, the $(I new-line) character, which specifies that the output should continue on a new line, does not have a visual representation. Such characters are called $(I control characters). Some common control characters can be specified with the $(C \$(I control_character)) syntax. +) + + + + + + + + + + + + + + + + + + + + +
Syntax Name Definition
\nnew lineMoves the printing to a new line
\rcarriage returnMoves the printing to the beginning of the current line
\ttabMoves the printing to the next tab stop
+ +$(P +For example, the $(C write()) function, which does not start a new line automatically, would do so for every $(C \n) character. Every occurrence of the $(C \n) control character within the following literal represents the start of a new line: +) + +--- + write("first line\nsecond line\nthird line\n"); +--- + +$(P +The output: +) + +$(SHELL +first line +second line +third line +) + +$(H5 $(IX ') $(IX \) Single quote and backslash) + +$(P +The single quote character itself cannot be written within single quotes because the compiler would take the second one as the closing character of the first one: $(C '''). The first two would be the opening and closing quotes, and the third one would be left alone, causing a compilation error. +) + +$(P +Similarly, since the backslash character has a special meaning in the control character and literal syntaxes, the compiler would take it as the start of such a syntax: $(C \'). The compiler then would be looking for a closing single quote character, not finding one, and emitting a compilation error. +) + +$(P +For those reasons, the single quote and the backslash characters are $(I escaped) by a preceding backslash character: +) + + + + + + + + + + + + + + + +
Syntax Name Definition
\'single quoteAllows specifying the single quote character:'\''
\\backslashAllows specifying the backslash character: '\\' or "\\"
+ +$(H5 $(IX std.uni) The std.uni module) + +$(P +The $(C std.uni) module includes functions that are useful with Unicode characters. You can see this module at $(LINK2 http://dlang.org/phobos/std_uni.html, its documentation). +) + +$(P +The functions that start with $(C is) answer certain questions about characters. The result is $(C false) or $(C true) depending on whether the answer is no or yes, respectively. These functions are useful in logical expressions: +) + +$(UL +$(LI $(C isLower): is it a lowercase character? +) +$(LI $(C isUpper): is it an uppercase character? +) +$(LI $(C isAlpha): is it a Unicode alphabetic character? +) +$(LI $(C isWhite): is it a whitespace character? +) +) + +$(P +The functions that start with $(C to) produce new characters from existing ones: +) + +$(UL +$(LI $(C toLower): produces the lowercase version of the given character +) +$(LI $(C toUpper): produces the uppercase version of the given character +) +) + +$(P +Here is a program that uses all those functions: +) + +--- +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 \€ alphanumeric? ", isAlpha('\€')); + + 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('ı')); +} +--- + +$(P +The output: +) + +$(SHELL +Is ğ lowercase? true +Is Ş lowercase? false +Is İ uppercase? true +Is ç uppercase? false +Is z alphanumeric? true +Is € alphanumeric? false +Is new-line whitespace? true +Is underline whitespace? false +The lowercase of Ğ: ğ +The lowercase of İ: i +The uppercase of ş: Ş +The uppercase of ı: I +) + +$(H5 Limited support for ı and i) + +$(P +The lowercase and uppercase versions of the letters $(C 'ı') and $(C 'i') are consistently dotted or undotted in some alphabets (e.g. the Turkish alphabet). Most other aphabets are inconsistent in this regard: the uppercase of the dotted $(C 'i') is undotted $(C 'I'). +) + +$(P +Because the computer systems have started with the ASCII table, traditionally the uppercase of $(C 'i') is $(C 'I') and the lowercase of $(C 'I') is $(C 'i'). For that reason, these two letters may need special attention. The following program demonstrates this problem: +) + +--- +import std.stdio; +import std.uni; + +void main() { + writeln("The uppercase of i: ", toUpper('i')); + writeln("The lowercase of I: ", toLower('I')); +} +--- + +$(P +The output is according to the basic Latin alphabet: +) + +$(SHELL +The uppercase of i: I +The lowercase of I: i +) + +$(P +Characters are converted between their uppercase and lowercase versions normally by their Unicode character codes. This method is problematic for many alphabets. For example, the Azeri and Celt alphabets are subject to the same problem of producing the lowercase of $(C 'I') as $(C 'i'). +) + +$(P +There are similar problems with sorting: Many letters like $(C 'ğ') and $(C 'á') may be sorted after $(C 'z') even for the basic Latin alphabet. +) + +$(H5 $(IX read, character) Problems with reading characters) + +$(P +The flexibility and power of D's Unicode characters may cause unexpected results when reading characters from an input stream. This contradiction is due to the multiple meanings of the term $(I character). Before expanding on this further, let's look at a program that exhibits this problem: +) + +--- +import std.stdio; + +void main() { + char letter; + write("Please enter a letter: "); + readf(" %s", &letter); + writeln("The letter that has been read: ", letter); +} +--- + +$(P +If you try that program in an environment that does not use Unicode, you may see that even the non-ASCII characters are read and printed correctly. +) + +$(P +On the other hand, if you start the same program in a Unicode environment (e.g. a Linux terminal), you may see that the character printed on the output is not the same character that has been entered. To see this, let's enter a non-ASCII character in a terminal that uses the UTF-8 encoding (like most Linux terminals): +) + +$(SHELL +Please enter a letter: ğ +The letter that has been read: $(SHELL_NOTE no letter on the output) +) + +$(P +The reason for this problem is that the non-ASCII characters like $(C 'ğ') are represented by two codes, and reading a $(C char) from the input reads only the first one of those codes. Since that single $(C char) is not sufficient to represent the whole Unicode character, the program does not have a complete character to display. +) + +$(P +To show that the UTF-8 codes that make up a character are indeed read one $(C char) at a time, let's read two $(C char) variables and print them one after the other: +) + +--- +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); +} +--- + +$(P +The program reads two $(C char) variables from the input and prints them in the same order that they are read. When those codes are sent to the terminal in that same order, they complete the UTF-8 encoding of the Unicode character on the terminal and this time the Unicode character is printed correctly: +) + +$(SHELL +Please enter a letter: ğ +The letter that has been read: ğ +) + +$(P +These results are also related to the fact that the standard inputs and outputs of programs are $(C char) streams. +) + +$(P +We will see later in $(LINK2 /ders/d.en/strings.html, the Strings chapter) that it is easier to read characters as strings, instead of dealing with UTF codes individually. +) + +$(H5 D's Unicode support) + +$(P +Unicode is a large and complicated standard. D supports a very useful subset of it. +) + +$(P +A Unicode-encoded document consists of the following levels of concepts, from the lowermost to the uppermost: +) + +$(UL + +$(LI $(IX code unit) $(B Code unit): The values that make up the UTF encodings are called code units. Depending on the encoding and the characters themselves, Unicode characters are made up of one or more code units. For example, in the UTF-8 encoding the letter $(C 'a') is made up of a single code unit and the letter $(C 'ğ') is made up of two code units. + +$(P +D's character types $(C char), $(C wchar), and $(C dchar) correspond to UTF-8, UTF-16, and UTF-32 code units, respectively. +) + +) + +$(LI $(IX code point) $(B Code point): Every letter, numeral, symbol, etc. that the Unicode standard defines is called a code point. For example, the Unicode code values of $(C 'a') and $(C 'ğ') are two distinct code points. + +$(P +Depending on the encoding, code points are represented by one or more code units. As mentioned above, in the UTF-8 encoding $(C 'a') is represented by a single code unit, and $(C 'ğ') is represented by two code units. On the other hand, both $(C 'a') and $(C 'ğ') are represented by a single code unit in both UTF-16 and UTF-32 encodings. +) + +$(P +The D type that supports code points is $(C dchar). $(C char) and $(C wchar) can only be used as code units. +) + +) + +$(LI $(B Character): Any symbol that the Unicode standard defines and what we call "character" or "letter" in daily talk is a character. + +$(P +$(IX combined code point) This definition of character is flexible in Unicode, which brings a complication. Some characters can be formed by more than one code point. For example, the letter $(C 'ğ') can be specified in two ways: +) + +$(UL + +$(LI as the single code point for $(C 'ğ')) + +$(LI as the two code points for $(C 'g') and $(C '˘') (combining breve) + +) + +) + +$(P +Although they would mean the same character to a human reader, the single code point $(C 'ğ') is different from the two consecutive code points $(C 'g') and $(C '˘'). +) + +) + +) + +$(H5 Summary) + +$(UL + +$(LI Unicode supports all characters of all writing systems.) + +$(LI $(C char) is for UTF-8 encoding; although it is not suitable to represent characters in general, it supports the ASCII table.) + +$(LI $(C wchar) is for UTF-16 encoding; although it is not suitable to represent characters in general, it can support letters of multiple alphabets.) + +$(LI $(C dchar) is for UTF-32 encoding; as it is 32 bits, it can also represent code points.) + +) + +Macros: + SUBTITLE=Characters + + DESCRIPTION=The character types of D and Unicode encodings + + KEYWORDS=d programming language tutorial book characters encoding char wchar dchar utf-8 utf-16 utf-32 unicode ascii diff --git a/target/class.d b/target/class.d new file mode 100644 index 0000000..67e6446 --- /dev/null +++ b/target/class.d @@ -0,0 +1,453 @@ +Ddoc + +$(DERS_BOLUMU $(IX class) Classes) + +$(P +$(IX OOP) $(IX object oriented programming) $(IX user defined type) Similar to structs, $(C class) is a feature for defining new types. By this definition, classes are $(I user defined types). Different from structs, classes provide the $(I object oriented programming) (OOP) paradigm in D. The major aspects of OOP are the following: +) + +$(UL + +$(LI +$(B Encapsulation:) Controlling access to members ($(I Encapsulation is available for structs as well but it has not been mentioned until this chapter.)) +) + +$(LI +$(B Inheritance:) Acquiring members of another type +) + +$(LI +$(B Polymorphism:) Being able to use a more special type in place of a more general type +) + +) + +$(P +Encapsulation is achieved by $(I protection attributes), which we will see in $(LINK2 /ders/d.en/encapsulation.html, a later chapter). Inheritance is for acquiring $(I implementations) of other types. $(LINK2 /ders/d.en/inheritance.html, Polymorphism) is for abstracting parts of programs from each other and is achieved by class $(I interfaces). +) + +$(P +This chapter will introduce classes at a high level, underlining the fact that they are reference types. Classes will be explained in more detail in later chapters. +) + +$(H5 Comparing with structs) + +$(P +In general, classes are very similar to structs. Most of the features that we have seen for structs in the following chapters apply to classes as well: +) + +$(UL +$(LI $(LINK2 /ders/d.en/struct.html, Structs)) +$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions)) +$(LI $(LINK2 /ders/d.en/const_member_functions.html, $(CH4 const ref) Parameters and $(CH4 const) Member Functions)) +$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions)) +$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading)) +) + +$(P +However, there are important differences between classes and structs. +) + +$(H6 Classes are reference types) + +$(P +The biggest difference from structs is that structs are $(I value types) and classes are $(I reference types). The other differences outlined below are mostly due to this fact. +) + +$(H6 $(IX null, class) $(new, class) Class variables may be $(C null)) + +$(P +As it has been mentioned briefly in $(LINK2 /ders/d.en/null_is.html, The $(CH4 null) Value and the $(CH4 is) Operator chapter), class variables can be $(C null). In other words, class variables may not be providing access to any object. Class variables do not have values themselves; the actual class objects must be constructed by the $(C new) keyword. +) + +$(P +As you would also remember, comparing a reference to $(C null) by the $(C ==) or the $(C !=) operator is an error. Instead, the comparison must be done by the $(C is) or the $(C !is) operator, accordingly: +) + +--- + MyClass referencesAnObject = new MyClass; + assert(referencesAnObject $(HILITE !is) null); + + MyClass variable; // does not reference an object + assert(variable $(HILITE is) null); +--- + +$(P +The reason is that, the $(C ==) operator may need to consult the values of the members of the objects and that attempting to access the members through a potentially $(C null) variable would cause a memory access error. For that reason, class variables must always be compared by the $(C is) and $(C !is) operators. +) + +$(H6 $(IX variable, class) $(IX object, class) Class variables versus class objects) + +$(P +Class variable and class object are separate concepts. +) + +$(P +Class objects are constructed by the $(C new) keyword; they do not have names. The actual concept that a class type represents in a program is provided by a class object. For example, assuming that a $(C Student) class represents students by their names and grades, such information would be stored by the members of $(C Student) $(I objects). Partly because they are anonymous, it is not possible to access class objects directly. +) + +$(P +A class variable on the other hand is a language feature for accessing class objects. Although it may seem syntactically that operations are being performed on a class $(I variable), the operations are actually dispatched to a class $(I object). +) + +$(P +Let's consider the following code that we saw previously in the $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types chapter): +) + +--- + auto variable1 = new MyClass; + auto variable2 = variable1; +--- + +$(P +The $(C new) keyword constructs an anonymous class object. $(C variable1) and $(C variable2) above merely provide access to that anonymous object: +) + +$(MONO + (anonymous MyClass object) variable1 variable2 + ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── + │ ... │ │ o │ │ o │ + ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── + ▲ │ │ + │ │ │ + └────────────────────┴────────────┘ +) + +$(H6 $(IX copy, class) Copying) + +$(P +Copying affects only the variables, not the object. +) + +$(P +Because classes are reference types, defining a new class variable as a copy of another makes two variables that provide access to the same object. The actual object is not copied. +) + +$(P +Since no object gets copied, the postblit function $(C this(this)) is not available for classes. +) + +--- + auto variable2 = variable1; +--- + +$(P +In the code above, $(C variable2) is being initialized by $(C variable1). The two variables start providing access to the same object. +) + +$(P +When the actual object needs to be copied, the class must have a member function for that purpose. To be compatible with arrays, this function may be named $(C dup()). This function must create and return a new class object. Let's see this on a class that has various types of members: +) + +--- +class Foo { + S o; // assume S is a struct type + char[] s; + int i; + +// ... + + this(S o, const char[] s, int i) { + this.o = o; + this.s = s.dup; + this.i = i; + } + + Foo dup() const { + return new Foo(o, s, i); + } +} +--- + +$(P +The $(C dup()) member function makes a new object by taking advantage of the constructor of $(C Foo) and returns the new object. Note that the constructor copies the $(C s) member explicitly by the $(C .dup) property of arrays. Being value types, $(C o) and $(C i) are copied automatically. +) + +$(P +The following code makes use of $(C dup()) to create a new object: +) + +--- + auto var1 = new Foo(S(1.5), "hello", 42); + auto var2 = var1.dup(); +--- + +$(P +As a result, the objects that are associated with $(C var1) and $(C var2) are different. +) + +$(P +Similarly, an $(C immutable) copy of an object can be provided by a member function appropriately named $(C idup()). In this case, the constructor must be defined as $(C pure) as well. We will cover the $(C pure) keyword in $(LINK2 /ders/d.en/functions_more.html, a later chapter). +) + +--- +class Foo { +// ... + this(S o, const char[] s, int i) $(HILITE pure) { + // ... + + } + immutable(Foo) idup() const { + return new immutable(Foo)(o, s, i); + } +} + +// ... + + immutable(Foo) imm = var1.idup(); +--- + +$(H6 $(IX assignment, class) Assignment) + +$(P +Just like copying, assignment affects only the variables. +) + +$(P +Assigning to a class variable disassociates that variable from its current object and associates it with a new object. +) + +$(P +If there is no other class variable that still provides access to the object that has been disassociated from, then that object is going to be destroyed some time in the future by the garbage collector. +) + +--- + auto variable1 = new MyClass(); + auto variable2 = new MyClass(); + variable1 $(HILITE =) variable2; +--- + +$(P +The assignment above makes $(C variable1) leave its object and start providing access to $(C variable2)'s object. Since there is no other variable for $(C variable1)'s original object, that object will be destroyed by the garbage collector. +) + +$(P +The behavior of assignment cannot be changed for classes. In other words, $(C opAssign) cannot be overloaded for them. +) + +$(H6 Definition) + +$(P +Classes are defined by the $(C class) keyword instead of the $(C struct) keyword: +) + +--- +$(HILITE class) ChessPiece { + // ... +} +--- + +$(H6 Construction) + +$(P +As with structs, the name of the constructor is $(C this). Unlike structs, class objects cannot be constructed by the $(C { }) syntax. +) + +--- +class ChessPiece { + dchar shape; + + this(dchar shape) { + this.shape = shape; + } +} +--- + +$(P +Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially: +) + +--- +class ChessPiece { + dchar shape; + size_t value; +} + +void main() { + auto king = new ChessPiece('♔', 100); $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: no constructor for ChessPiece +) + +$(P +For that syntax to work, a constructor must be defined explicitly by the programmer. +) + +$(H6 Destruction) + +$(P +As with structs, the name of the destructor is $(C ~this): +) + +--- + ~this() { + // ... + } +--- + +$(P +$(IX finalizer versus destructor) However, different from structs, class destructors are not executed at the time when the lifetime of a class object ends. As we have seen above, the destructor is executed some time in the future during a garbage collection cycle. (By this distinction, class destructors should have more accurately been called $(I finalizers)). +) + +$(P +As we will see later in $(LINK2 /ders/d.en/memory.html, the Memory Management chapter), class destructors must observe the following rules: +) + +$(UL + +$(LI A class destructor must not access a member that is managed by the garbage collector. This is because garbage collectors are not required to guarantee that the object and its members are finalized in any specific order. All members may have already been finalized when the destructor is executing.) + +$(LI A class destructor must not allocate new memory that is managed by the garbage collector. This is because garbage collectors are not required to guarantee that they can allocate new objects during a garbage collection cycle.) + +) + +$(P +Violating these rules is undefined behavior. It is easy to see an example of such a problem simply by trying to allocate an object in a class destructor: +) + +--- +class C { + ~this() { + auto c = new C(); // ← WRONG: Allocates explicitly + // in a class destructor + } +} + +void main() { + auto c = new C(); +} +--- + +$(P +The program is terminated with an exception: +) + +$(SHELL +core.exception.$(HILITE InvalidMemoryOperationError)@(0) +) + +$(P +It is equally wrong to allocate new memory $(I indirectly) from the garbage collector in a destructor. For example, memory used for the elements of a dynamic array is allocated by the garbage collector as well. Using an array in a way that would require allocating a new memory block for the elements is undefined behavior as well: +) + +--- + ~this() { + auto arr = [ 1 ]; // ← WRONG: Allocates indirectly + // in a class destructor + } +--- + +$(SHELL +core.exception.$(HILITE InvalidMemoryOperationError)@(0) +) + +$(H6 Member access) + +$(P +Same as structs, the members are accessed by the $(I dot) operator: +) + +--- + auto king = new ChessPiece('♔'); + writeln(king$(HILITE .shape)); +--- + +$(P +Although the syntax makes it look as if a member of the $(I variable) is being accessed, it is actually the member of the $(I object). Class variables do not have members, the class objects do. The $(C king) variable does not have a $(C shape) member, the anonymous object does. +) + +$(P +$(I $(B Note:) It is usually not proper to access members directly as in the code above. When that exact syntax is desired, properties should be preferred, which will be explained in $(LINK2 /ders/d.en/property.html, a later chapter).) +) + +$(H6 Operator overloading) + +$(P +Other than the fact that $(C opAssign) cannot be overloaded for classes, operator overloading is the same as structs. For classes, the meaning of $(C opAssign) is always $(I associating a class variable with a class object). +) + +$(H6 Member functions) + +$(P +Although member functions are defined and used the same way as structs, there is an important difference: Class member functions can be and by-default are $(I overridable). We will see this concept later in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter). +) + +$(P +$(IX final) As overridable member functions have a runtime performance cost, without going into more detail, I recommend that you define all $(C class) functions that do not need to be overridden with the $(C final) keyword. You can apply this guideline blindly unless there are compilation errors: +) + +--- +class C { + $(HILITE final) int func() { $(CODE_NOTE Recommended) + // ... + } +} +--- + +$(P +Another difference from structs is that some member functions are automatically inherited from the $(C Object) class. We will see in $(LINK2 /ders/d.en/inheritance.html, the next chapter) how the definition of $(C toString) can be changed by the $(C override) keyword. +) + +$(H6 $(IX is, operator) $(IX !is) The $(C is) and $(C !is) operators) + +$(P +These operators operate on class variables. +) + +$(P +$(C is) specifies whether two class variables provide access to the same class object. It returns $(C true) if the object is the same and $(C false) otherwise. $(C !is) is the opposite of $(C is). +) + +--- + auto myKing = new ChessPiece('♔'); + auto yourKing = new ChessPiece('♔'); + assert(myKing !is yourKing); +--- + +$(P +Since the objects of $(C myKing) and $(C yourKing) variables are different, the $(C !is) operator returns $(C true). Even though the two objects are constructed by the same character $(C'♔'), they are still two separate objects. +) + +$(P +When the variables provide access to the same object, $(C is) returns $(C true): +) + +--- + auto myKing2 = myKing; + assert(myKing2 is myKing); +--- + +$(P +Both of the variables above provide access to the same object. +) + +$(H5 Summary) + +$(UL + +$(LI Classes and structs share common features but have big differences. +) + +$(LI Classes are reference types. The $(C new) keyword constructs an anonymous $(I class object) and returns a $(I class variable). +) + +$(LI Class variables that are not associated with any object are $(C null). Checking against $(C null) must be done by $(C is) or $(C !is), not by $(C ==) or $(C !=). +) + +$(LI The act of copying associates an additional variable with an object. In order to copy class objects, the type must have a special function likely named $(C dup()). +) + +$(LI Assignment associates a variable with an object. This behavior cannot be changed. +) + +) + +Macros: + SUBTITLE=Classes + + DESCRIPTION=The basic object oriented programming (OOP) feature of the D programming language. + + KEYWORDS=d programming lesson book tutorial class diff --git a/target/compiler.d b/target/compiler.d new file mode 100644 index 0000000..3b9765e --- /dev/null +++ b/target/compiler.d @@ -0,0 +1,101 @@ +Ddoc + +$(DERS_BOLUMU $(IX compilation) Compilation) + +$(P +We have seen that the two tools that are used most in D programming are $(I the text editor) and $(I the compiler). D programs are written in text editors. +) + +$(P +The concept of compilation and the function of the compiler must also be understood when using $(I compiled) languages like D. +) + +$(H5 $(IX machine code) Machine code) + +$(P +$(IX CPU) $(IX microprocessor) The brain of the computer is the microprocessor (or the CPU, short for $(I central processing unit)). Telling the CPU what to do is called $(I coding), and the instructions that are used when doing so are called $(I machine code). +) + +$(P +Most CPU architectures use machine code specific to that particular architecture. These machine code instructions are determined under hardware constraints during the design stage of the architecture. At the lowest level these machine code instructions are implemented as electrical signals. Because the ease of coding is not a primary consideration at this level, writing programs directly in the form of the machine code of the CPU is a very difficult task. +) + +$(P +These machine code instructions are special numbers, which represent various operations supported by the CPU. For example, for an imaginary 8-bit CPU, the number 4 might represent the operation of loading, the number 5 might represent the operation of storing, and the number 6 might represent the operation of incrementing. Assuming that the leftmost 3 bits are the operation number and the rightmost 5 bits are the value that is used in that operation, a sample program in machine code for this CPU might look like the following: +) + +$(MONO +$(B +Operation Value Meaning) + 100 11110 LOAD 11110 + 101 10100 STORE 10100 + 110 10100 INCREMENT 10100 + 000 00000 PAUSE +) + +$(P +Being so close to hardware, machine code is not suitable for representing higher level concepts like $(I a playing card) or $(I a student record). +) + +$(H5 $(IX programming language) Programming language) + +$(P +Programming languages are designed as efficient ways of programming a CPU, capable of representing higher-level concepts. Programming languages do not have to deal with hardware constraints; their main purposes are ease of use and expressiveness. Programming languages are easier for humans to understand, closer to natural languages: +) + +$(MONO +if (a_card_has_been_played()) { + display_the_card(); +} +) + +$(P +However, programming languages adhere to much more strict and formal rules than any spoken language. +) + +$(H5 $(IX interpreter) Interpreter) + +$(P +An interpreter is a tool (a program) that reads the instructions from source code and executes them. For example, for the code above, an interpreter would understand to first execute $(C a_card_has_been_played()) and then conditionally execute $(C display_the_card()). From the point of view of the programmer, executing with an interpreter involves just two steps: writing the source code and giving it to the interpreter. +) + +$(P +The interpreter must read and understand the instructions every time the program is executed. For that reason, running a program with an interpreter is usually slower than running the compiled version of the same program. Additionally, interpreters usually perform very little analysis on the code before executing it. As a result, most interpreters discover programming errors only after they start executing the program. +) + +$(P +Some languages like Perl, Python and Ruby have been designed to be very flexible and dynamic, making code analysis harder. These languages have traditionally been used with an interpreter. +) + +$(H5 $(IX compiler) Compiler) + +$(P +A compiler is another tool that reads the instructions of a program from source code. Different from an interpreter, it does not execute the code; rather, it produces a program written in another language (usually machine code). This produced program is responsible for the execution of the instructions that were written by the programmer. From the point of view of the programmer, executing with a compiler involves three steps: writing the source code, compiling it, and running the produced program. +) + +$(P +Unlike an interpreter, the compiler reads and understands the source code only once, during compilation. For that reason and in general, a compiled program runs faster compared to executing that program with an interpreter. Compilers usually perform advanced analysis on the code, which help with producing fast programs and catching programming errors before the program even starts running. On the other hand, having to compile the program every time it is changed is a complication and a potential source of human errors. Moreover, the programs that are produced by a compiler can usually run only on a specific platform; to run on a different kind of processor or on a different operating system, the program would have to be recompiled. Additionally, the languages that are easy to compile are usually less dynamic than those that run in an interpreter. +) + +$(P +For reasons like safety and performance, some languages have been designed to be compiled. Ada, C, C++, and D are some of them. +) + +$(H6 $(IX error, compilation) $(IX compilation error) Compilation error) + +$(P +As the compiler compiles a program according to the rules of the language, it stops the compilation as soon as it comes across $(I illegal) instructions. Illegal instructions are the ones that are outside the specifications of the language. Problems like a mismatched parenthesis, a missing semicolon, a misspelled keyword, etc. all cause compilation errors. +) + +$(P +The compiler may also emit a $(I compilation warning) when it sees a suspicious piece of code that may cause concern but not necessarily an error. However, warnings almost always indicate an actual error or bad style, so it is a common practice to consider most or all warnings as errors. The $(C dmd) compiler switch to enable warnings as errors is $(C -w). +) + +$(Ergin) + +Macros: + SUBTITLE=Compiler + + DESCRIPTION=The introduction of the compiler and compiled languages + + KEYWORDS=d programming language tutorial book diff --git a/target/concurrency.d b/target/concurrency.d new file mode 100644 index 0000000..a01965d --- /dev/null +++ b/target/concurrency.d @@ -0,0 +1,1343 @@ +Ddoc + +$(DERS_BOLUMU $(IX concurrency, message passing) $(IX message passing concurrency) Message Passing Concurrency) + +$(P +Concurrency is similar to but different from the topic of the previous chapter, parallelism. As these two concepts both involve executing programs on threads, and as parallelism is based on concurrency, they are sometimes confused with each other. +) + +$(P +$(IX parallelism vs. concurrency) $(IX concurrency vs. parallelism) The following are the differences between parallelism and concurrency: +) + +$(UL + +$(LI +The main purpose of parallelism is to take advantage of microprocessor cores to improve the performance of programs. Concurrency on the other hand, is a concept that may be needed even on a single-core environment. Concurrency is about making a program run on more than one thread at a time. An example of a concurrent program would be a server program that is responding to requests of more than one client at the same time. +) + +$(LI +In parallelism, tasks are independent from each other. In fact, it would be a bug if they did depend on results of other tasks that are running at the same time. In concurrency, it is normal for threads to depend on results of other threads. +) + +$(LI +Although both programming models use operating system threads, in parallelism threads are encapsulated by the concept of task. Concurrency makes use of threads explicitly. +) + +$(LI +Parallelism is easy to use, and as long as tasks are independent it is easy to produce programs that work correctly. Concurrency is easy only when it is based on $(I message passing). It is very difficult to write correct concurrent programs if they are based on the traditional model of concurrency that involves lock-based data sharing. +) + +) + +$(P +D supports both models of concurrency: message passing and data sharing. We will cover message passing in this chapter and data sharing in the next chapter. +) + +$(H5 Concepts) + +$(P +$(IX thread) $(B Thread): Operating systems execute programs as work units called $(I threads). D programs start executing with $(C main()) on a thread that has been assigned to that program by the operating system. All of the operations of the program are normally executed on that thread. The program is free to start other threads to be able to work on multiple tasks at the same time. In fact, tasks that have been covered in the previous chapter are based on threads that are started automatically by $(C std.parallelism). +) + +$(P +The operating system can pause threads at unpredictable times for unpredictable durations. As a result, even operations as simple as incrementing a variable may be paused mid operation: +) + +--- + ++i; +--- + +$(P +The operation above involves three steps: Reading the value of the variable, incrementing the value, and assigning the new value back to the variable. The thread may be paused at any point between these steps to be continued after an unpredictable time. +) + +$(P +$(IX message) $(B Message): Data that is passed between threads are called messages. Messages may be composed of any type and any number of variables. +) + +$(P +$(IX thread id) $(B Thread identifier): Every thread has an id, which is used for specifying recipients of messages. +) + +$(P +$(IX owner) $(B Owner): Any thread that starts another thread is called the owner of the new thread. +) + +$(P +$(IX worker) $(B Worker): Any thread that is started by an owner is called a worker. +) + +$(H5 $(IX spawn) Starting threads) + +$(P +$(C spawn()) takes a function pointer as a parameter and starts a new thread from that function. Any operations that are carried out by that function, including other functions that it may call, would be executed on the new thread. The main difference between a thread that is started with $(C spawn()) and a thread that is started with $(LINK2 /ders/d.en/parallelism.html, $(C task())) is the fact that $(C spawn()) makes it possible for threads to send messages to each other. +) + +$(P +As soon as a new thread is started, the owner and the worker start executing separately as if they were independent programs: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +void worker() { + foreach (i; 0 .. 5) { + Thread.sleep(500.msecs); + writeln(i, " (worker)"); + } +} + +void main() { + $(HILITE spawn(&worker)); + + foreach (i; 0 .. 5) { + Thread.sleep(300.msecs); + writeln(i, " (main)"); + } + + writeln("main is done."); +} +--- + +$(P +The examples in this chapter call $(C Thread.sleep) to slow down threads to demonstrate that they run at the same time. The output of the program shows that the two threads, one that runs $(C main()) and the other that has been started by $(C spawn()), execute independently at the same time: +) + +$(SHELL +0 (main) +0 (worker) +1 (main) +2 (main) +1 (worker) +3 (main) +2 (worker) +4 (main) +main is done. +3 (worker) +4 (worker) +) + +$(P +The program automatically waits for all of the threads to finish executing. We can see this in the output above by the fact that $(C worker()) continues executing even after $(C main()) exits after printing "main is done." +) + +$(P +The parameters that the thread function takes are passed to $(C spawn()) as its second and later arguments. The two worker threads in the following program print four numbers each. They take the starting number as the thread function parameter: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +void worker($(HILITE int firstNumber)) { + foreach (i; 0 .. 4) { + Thread.sleep(500.msecs); + writeln(firstNumber + i); + } +} + +void main() { + foreach (i; 1 .. 3) { + spawn(&worker, $(HILITE i * 10)); + } +} +--- + +$(P +The output of one of the threads is highlighted: +) + +$(SHELL +10 +$(HILITE 20) +11 +$(HILITE 21) +12 +$(HILITE 22) +13 +$(HILITE 23) +) + +$(P +The lines of the output may be different at different times depending on how the threads are paused and resumed by the operating system. +) + +$(P +$(IX CPU bound) $(IX I/O bound) $(IX thread performance) Every operating system puts limits on the number of threads that can exist at one time. These limits can be set for each user, for the whole system, or for something else. The overall performance of the system can be reduced if there are more threads that are busily working than the number of cores in the system. A thread that is busily working at a given time is said to be $(I CPU bound) at that point in time. On the other hand, some threads spend considerable amount of their time waiting for some event to occur like input from a user, data from a network connection, the completion of a $(C Thread.sleep) call, etc. Such threads are said to be $(I I/O bound) at those times. If the majority of its threads are I/O bound, then a program can afford to start more threads than the number of cores without any degradation of performance. As it should be in every design decision that concerns program performance, one must take actual measurements to be exactly sure whether that really is the case. +) + +$(H5 $(IX Tid) $(IX thisTid) $(IX ownerTid) Thread identifiers) + +$(P +$(C thisTid()) returns the identifier of the $(I current) thread. It is commonly called without the function parentheses: +) + +--- +import std.stdio; +import std.concurrency; + +void printTid(string tag) { + writefln("%s: %s", tag, $(HILITE thisTid)); +} + +void worker() { + printTid("Worker"); +} + +void main() { + spawn(&worker); + printTid("Owner "); +} +--- + +$(P +The return type of $(C thisTid()) is $(C Tid), which has no significance for the program. Even its $(C toString()) function is not overloaded: +) + +$(SHELL +Owner : Tid(std.concurrency.MessageBox) +Worker: Tid(std.concurrency.MessageBox) +) + +$(P +The return value of $(C spawn()), which I have been ignoring until this point, is the id of the worker thread: +) + +--- + $(HILITE Tid myWorker) = spawn(&worker); +--- + +$(P +Conversely, the owner of a worker thread is obtained by the $(C ownerTid()) function. +) + +$(P +In summary, the owner is identified by $(C ownerTid) and the worker is identified by the return value of $(C spawn()). +) + +$(H5 $(IX send) $(IX receiveOnly) Message Passing) + +$(P +$(C send()) sends messages and $(C receiveOnly()) waits for a message of a particular type. (There is also $(C prioritySend()), $(C receive()), and $(C receiveTimeout()), which will be explained later below.) +) + +$(P +The owner in the following program sends its worker a message of type $(C int) and waits for a message from the worker of type $(C double). The threads continue sending messages back and forth until the owner sends a negative $(C int). This is the owner thread: +) + +--- +void $(CODE_DONT_TEST)main() { + Tid worker = spawn(&workerFunc); + + foreach (value; 1 .. 5) { + $(HILITE worker.send)(value); + double result = $(HILITE receiveOnly!double)(); + writefln("sent: %s, received: %s", value, result); + } + + /* Sending a negative value to the worker so that it + * terminates. */ + $(HILITE worker.send)(-1); +} +--- + +$(P +$(C main()) stores the return value of $(C spawn()) under the name $(C worker) and uses that variable when sending messages to the worker. +) + +$(P +On the other side, the worker receives the message that it needs as an $(C int), uses that value in a calculation, and sends the result as type $(C double) to its owner: +) + +--- +void workerFunc() { + int value = 0; + + while (value >= 0) { + value = $(HILITE receiveOnly!int)(); + double result = to!double(value) / 5; + $(HILITE ownerTid.send)(result); + } +} +--- + +$(P +The main thread reports the messages that it sends and the messages that it receives: +) + +$(SHELL +sent: 1, received: 0.2 +sent: 2, received: 0.4 +sent: 3, received: 0.6 +sent: 4, received: 0.8 +) + +$(P +It is possible to send more than one value as a part of the same message. The following message consists of three parts: +) + +--- + ownerTid.send($(HILITE thisTid, 42, 1.5)); +--- + +$(P +Values that are passed as parts of a single message appear as a tuple on the receiver's side. In such cases the template parameters of $(C receiveOnly()) must match the types of the tuple members: +) + +--- + /* Wait for a message composed of Tid, int, and double. */ + auto message = receiveOnly!($(HILITE Tid, int, double))(); + + auto sender = message[0]; // of type Tid + auto integer = message[1]; // of type int + auto floating = message[2]; // of type double +--- + +$(P +$(IX MessageMismatch) If the types do not match, a $(C MessageMismatch) exception is thrown: +) + +--- +import std.concurrency; + +void workerFunc() { + ownerTid.send("hello"); $(CODE_NOTE Sending $(HILITE string)) +} + +void main() { + spawn(&workerFunc); + + auto message = receiveOnly!double(); $(CODE_NOTE Expecting $(HILITE double)) +} +--- + +$(P +The output: +) + +$(SHELL +std.concurrency.$(HILITE MessageMismatch)@std/concurrency.d(235): +Unexpected message type: expected 'double', got 'immutable(char)[]' +) + +$(P +The exceptions that the worker may throw cannot be caught by the owner. One solution is to have the worker catch the exception to be sent as a message. We will see this below. +) + +$(H6 Example) + +$(P +Let's use what we have seen so far in a simulation program. +) + +$(P +The following program simulates independent robots moving around randomly in a two dimensional space. The movement of each robot is handled by a separate thread that takes three pieces of information when started: +) + +$(UL + +$(LI The number (id) of the robot: This information is sent back to the owner to identify the robot that the message is related to. +) + +$(LI The origin: This is where the robot starts moving from. +) + +$(LI The duration between each step: This information is used for determining when the robot's next step will be. +) + +) + +$(P +That information can be stored in the following $(C Job) struct: +) + +--- +struct Job { + size_t robotId; + Position origin; + Duration restDuration; +} +--- + +$(P +The thread function that moves each robot sends the id of the robot and its movement to the owner thread continuously: +) + +--- +void robotMover(Job job) { + Position from = job.origin; + + while (true) { + Thread.sleep(job.restDuration); + + Position to = randomNeighbor(from); + Movement movement = Movement(from, to); + from = to; + + ownerTid.send($(HILITE MovementMessage)(job.robotId, movement)); + } +} +--- + +$(P +The owner simply waits for these messages in an infinite loop. It identifies the robots by the robot ids that are sent as parts of the messages. The owner simply prints every movement: +) + +--- + while (true) { + auto message = receiveOnly!$(HILITE MovementMessage)(); + + writefln("%s %s", + robots[message.robotId], message.movement); + } +--- + +$(P +All of the messages in this simple program go from the worker to the owner. Message passing normally involves more complicated communication in many kinds of programs. +) + +$(P +Here is the complete program: +) + +--- +import std.stdio; +import std.random; +import std.string; +import std.concurrency; +import core.thread; + +struct Position { + int line; + int column; + + string toString() { + return format("%s,%s", line, column); + } +} + +struct Movement { + Position from; + Position to; + + string toString() { + return ((from == to) + ? format("%s (idle)", from) + : format("%s -> %s", from, to)); + } +} + +class Robot { + string image; + Duration restDuration; + + this(string image, Duration restDuration) { + this.image = image; + this.restDuration = restDuration; + } + + override string toString() { + return format("%s(%s)", image, restDuration); + } +} + +/* Returns a random position around 0,0. */ +Position randomPosition() { + return Position(uniform!"[]"(-10, 10), + uniform!"[]"(-10, 10)); +} + +/* Returns at most one step from the specified coordinate. */ +int randomStep(int current) { + return current + uniform!"[]"(-1, 1); +} + +/* Returns a neighbor of the specified Position. It may be one + * of the neighbors at eight directions, or the specified + * position itself. */ +Position randomNeighbor(Position position) { + return Position(randomStep(position.line), + randomStep(position.column)); +} + +struct Job { + size_t robotId; + Position origin; + Duration restDuration; +} + +struct MovementMessage { + size_t robotId; + Movement movement; +} + +void robotMover(Job job) { + Position from = job.origin; + + while (true) { + Thread.sleep(job.restDuration); + + Position to = randomNeighbor(from); + Movement movement = Movement(from, to); + from = to; + + ownerTid.send(MovementMessage(job.robotId, movement)); + } +} + +void main() { + /* Robots with various restDurations. */ + Robot[] robots = [ new Robot("A", 600.msecs), + new Robot("B", 2000.msecs), + new Robot("C", 5000.msecs) ]; + + /* Start a mover thread for each robot. */ + foreach (robotId, robot; robots) { + spawn(&robotMover, Job(robotId, + randomPosition(), + robot.restDuration)); + } + + /* Ready to collect information about the movements of the + * robots. */ + while (true) { + auto message = receiveOnly!MovementMessage(); + + /* Print the movement of this robot. */ + writefln("%s %s", + robots[message.robotId], message.movement); + } +} +--- + +$(P +The program prints every movement until terminated: +) + +$(SHELL +A(600 ms) 6,2 -> 7,3 +A(600 ms) 7,3 -> 8,3 +A(600 ms) 8,3 -> 7,3 +B(2 secs) -7,-4 -> -6,-3 +A(600 ms) 7,3 -> 6,2 +A(600 ms) 6,2 -> 7,1 +A(600 ms) 7,1 (idle) +B(2 secs) -6,-3 (idle) +A(600 ms) 7,1 -> 7,2 +A(600 ms) 7,2 -> 7,3 +C(5 secs) -4,-4 -> -3,-5 +A(600 ms) 7,3 -> 6,4 +... +) + +$(P +This program demonstrates how helpful message passing concurrency can be: Movements of robots are calculated independently by separate threads without knowledge of each other. It is the owner thread that $(I serializes) the printing process simply by receiving messages from its message box one by one. +) + +$(H5 $(IX delegate, message passing) Expecting different types of messages) + +$(P +$(C receiveOnly()) can expect only one type of message. $(C receive()) on the other hand can wait for more than one type of message. It dispatches messages to message handling delegates. When a message arrives, it is compared to the message type of each delegate. The delegate that matches the type of the particular message handles it. +) + +$(P +For example, the following $(C receive()) call specifies two message handlers that handle messages of types $(C int) and $(C string), respectively: +) + +--- +$(CODE_NAME workerFunc)void workerFunc() { + bool isDone = false; + + while (!isDone) { + void intHandler($(HILITE int) message) { + writeln("handling int message: ", message); + + if (message == -1) { + writeln("exiting"); + isDone = true; + } + } + + void stringHandler($(HILITE string) message) { + writeln("handling string message: ", message); + } + + receive($(HILITE &intHandler), $(HILITE &stringHandler)); + } +} +--- + +$(P +Messages of type $(C int) would match $(C intHandler()) and messages of type $(C string) would match $(C stringHandler()). The worker thread above can be tested by the following program: +) + +--- +$(CODE_XREF workerFunc)import std.stdio; +import std.concurrency; + +// ... + +void main() { + auto worker = spawn(&workerFunc); + + worker.send(10); + worker.send(42); + worker.send("hello"); + worker.send(-1); // ← to terminate the worker +} +--- + +$(P +The output of the program indicates that the messages are handled by matching functions on the receiver's side: +) + +$(SHELL +handling int message: 10 +handling int message: 42 +handling string message: hello +handling int message: -1 +exiting +) + +$(P +Lambda functions and objects of types that define the $(C opCall()) member function can also be passed to $(C receive()) as message handlers. The following worker handles messages by lambda functions. The following program also defines a special type named $(C Exit) used for communicating to the thread that it is time for it to exit. Using such a specific type is more expressive than sending the arbitrary value of -1 like it was done in the previous example. +) + +$(P +There are three anonymous functions below that are passed to $(C receive()) as message handlers. Their curly brackets are highlighted: +) + +--- +import std.stdio; +import std.concurrency; + +struct Exit { +} + +void workerFunc() { + bool isDone = false; + + while (!isDone) { + receive( + (int message) $(HILITE {) + writeln("int message: ", message); + $(HILITE }), + + (string message) $(HILITE {) + writeln("string message: ", message); + $(HILITE }), + + (Exit message) $(HILITE {) + writeln("exiting"); + isDone = true; + $(HILITE })); + } +} + +void main() { + auto worker = spawn(&workerFunc); + + worker.send(10); + worker.send(42); + worker.send("hello"); + worker.send($(HILITE Exit())); +} +--- + +$(H6 Receiving any type of message) + +$(P +$(IX Variant, concurrency) $(C std.variant.Variant) is a type that can encapsulate any type of data. Messages that do not match the handlers that are specified earlier in the argument list always match a $(C Variant) handler: +) + +--- +import std.stdio; +import std.concurrency; + +void workerFunc() { + receive( + (int message) { /* ... */ }, + + (double message) { /* ... */ }, + + ($(HILITE Variant) message) { + writeln("Unexpected message: ", message); + }); +} + +struct SpecialMessage { + // ... +} + +void main() { + auto worker = spawn(&workerFunc); + worker.send(SpecialMessage()); +} +--- + +$(P +The output: +) + +$(SHELL +Unexpected message: SpecialMessage() +) + +$(P +The details of $(C Variant) are outside of the scope of this chapter. +) + +$(H5 $(IX receiveTimeout) Waiting for messages up to a certain time) + +$(P +It may not make sense to wait for messages beyond a certain time. The sender may have been busy temporarily or may have terminated with an exception. $(C receiveTimeout()) prevents blocking the receiving thread indefinitely. +) + +$(P +The first parameter of $(C receiveTimeout()) determines how long the message should be waited for. Its return value is $(C true) if a message has been received within that time, $(C false) otherwise. +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +void workerFunc() { + Thread.sleep(3.seconds); + ownerTid.send("hello"); +} + +void main() { + spawn(&workerFunc); + + writeln("Waiting for a message"); + bool received = false; + while (!received) { + received = $(HILITE receiveTimeout)(600.msecs, + (string message) { + writeln("received: ", message); + }); + + if (!received) { + writeln("... no message yet"); + + /* ... other operations may be executed here ... */ + } + } +} +--- + +$(P +The owner above waits for a message for up to 600 milliseconds. It can continue working on other things if a message does not arrive within that time: +) + +$(SHELL +Waiting for a message +... no message yet +... no message yet +... no message yet +... no message yet +received: hello +) + +$(H5 $(IX exception, concurrency) Exceptions during the execution of the worker) + +$(P +As we have seen in the previous chapter, the facilities of the $(C std.parallelism) module automatically catch exceptions that have been thrown during the execution of tasks and rethrow them in the context of the owner. This allows the owner to catch such exceptions: +) + +--- + try { + theTask.yieldForce(); + + } catch (Exception exc) { + writefln("Detected an error in the task: '%s'", + exc.msg); + } +--- + +$(P +$(C std.concurrency) does not provide such a convenience for general exception types. However, the exceptions can be caught and sent explicitly by the worker. As we will see below, it is also possible to receive $(C OwnerTerminated) and $(C LinkTerminated) exceptions as messages. +) + +$(P +The $(C calculate()) function below receives $(C string) messages, converts them to $(C double), adds 0.5, and sends the result back as a message: +) + +--- +$(CODE_NAME calculate)void calculate() { + while (true) { + auto message = receiveOnly!string(); + ownerTid.send(to!double(message) + 0.5); + } +} +--- + +$(P +The $(C to!double()) call above would throw an exception if the string cannot be converted to a $(C double) value. Because such an exception would terminate the worker thread right away, the owner in the following program can receive a response only for the first message: +) + +--- +$(CODE_XREF calculate)import std.stdio; +import std.concurrency; +import std.conv; + +// ... + +void main() { + Tid calculator = spawn(&calculate); + + calculator.send("1.2"); + calculator.send("hello"); // ← incorrect input + calculator.send("3.4"); + + foreach (i; 0 .. 3) { + auto message = receiveOnly!double(); + writefln("result %s: %s", i, message); + } +} +--- + +$(P +The owner receives the response for "1.2" as 1.7 but because the worker has been terminated, the owner would be blocked waiting for a message that would never arrive: +) + +$(SHELL +result 0: 1.7 + $(SHELL_NOTE waiting for a message that will never arrive) +) + +$(P +One thing that the worker can do is to catch the exception explicitly and to send it as a special error message. The following program sends the reason of the failure as a $(C CalculationFailure) message. Additionally, this program takes advantage of a special message type to signal to the worker when it is time to exit: +) + +--- +import std.stdio; +import std.concurrency; +import std.conv; + +struct CalculationFailure { + string reason; +} + +struct Exit { +} + +void calculate() { + bool isDone = false; + + while (!isDone) { + receive( + (string message) { + try { + ownerTid.send(to!double(message) + 0.5); + + } $(HILITE catch) (Exception exc) { + ownerTid.send(CalculationFailure(exc.msg)); + } + }, + + (Exit message) { + isDone = true; + }); + } +} + +void main() { + Tid calculator = spawn(&calculate); + + calculator.send("1.2"); + calculator.send("hello"); // ← incorrect input + calculator.send("3.4"); + calculator.send(Exit()); + + foreach (i; 0 .. 3) { + writef("result %s: ", i); + + receive( + (double message) { + writeln(message); + }, + + (CalculationFailure message) { + writefln("ERROR! '%s'", message.reason); + }); + } +} +--- + +$(P +This time the reason of the failure is printed by the owner: +) + +$(SHELL +result 0: 1.7 +result 1: ERROR! 'no digits seen' +result 2: 3.9 +) + +$(P +Another method would be to send the actual exception object itself to the owner. The owner can use the exception object or simply rethrow it: +) + +--- +// ... at the worker ... + try { + // ... + + } catch ($(HILITE shared(Exception)) exc) { + ownerTid.send(exc); + }}, + +// ... at the owner ... + receive( + // ... + + ($(HILITE shared(Exception)) exc) { + throw exc; + }); +--- + +$(P +The reason why the $(C shared) specifiers are necessary is explained in the next chapter. +) + +$(H5 Detecting thread termination) + +$(P +Threads can detect that the receiver of a message has terminated. +) + +$(H6 $(IX OwnerTerminated) $(C OwnerTerminated) exception) + +$(P +This exception is thrown when receiving a message from the owner if the owner has been terminated. The intermediate owner thread below simply exits after sending two messages to its worker. This causes an $(C OwnerTerminated) exception to be thrown at the worker thread: +) + +--- +import std.stdio; +import std.concurrency; + +void main() { + spawn(&intermediaryFunc); +} + +void intermediaryFunc() { + auto worker = spawn(&workerFunc); + worker.send(1); + worker.send(2); +} // ← Terminates after sending two messages + +void workerFunc() { + while (true) { + auto m = receiveOnly!int(); // ← An exception is + // thrown if the owner + // has terminated. + writeln("Message: ", m); + } +} +--- + +$(P +The output: +) + +$(SHELL +Message: 1 +Message: 2 +std.concurrency.$(HILITE OwnerTerminated)@std/concurrency.d(248): +Owner terminated +) + +$(P +The worker can catch that exception to exit gracefully: +) + +--- +void workerFunc() { + bool isDone = false; + + while (!isDone) { + try { + auto m = receiveOnly!int(); + writeln("Message: ", m); + + } catch ($(HILITE OwnerTerminated) exc) { + writeln("The owner has terminated."); + isDone = true; + } + } +} +--- + +$(P +The output: +) + +$(SHELL +Message: 1 +Message: 2 +The owner has terminated. +) + +$(P +We will see below that this exception can be received as a message as well. +) + +$(H6 $(IX LinkTerminated) $(IX spawnLinked) $(C LinkTerminated) exception) + +$(P +$(C spawnLinked()) is used in the same way as $(C spawn()). When a worker that has been started by $(C spawnLinked()) terminates, a $(C LinkTerminated) exception is thrown at the owner: +) + +--- +import std.stdio; +import std.concurrency; + +void main() { + auto worker = $(HILITE spawnLinked)(&workerFunc); + + while (true) { + auto m = receiveOnly!int(); // ← An exception is + // thrown if the worker + // has terminated. + writeln("Message: ", m); + } +} + +void workerFunc() { + ownerTid.send(10); + ownerTid.send(20); +} // ← Terminates after sending two messages +--- + +$(P +The worker above terminates after sending two messages. Since the worker has been started by $(C spawnLinked()), the owner is notified of the worker's termination by a $(C LinkTerminated) exception: +) + +$(SHELL +Message: 10 +Message: 20 +std.concurrency.$(HILITE LinkTerminated)@std/concurrency.d(263): +Link terminated +) + +$(P +The owner can catch the exception to do something special like terminating gracefully: +) + +--- + bool isDone = false; + + while (!isDone) { + try { + auto m = receiveOnly!int(); + writeln("Message: ", m); + + } catch ($(HILITE LinkTerminated) exc) { + writeln("The worker has terminated."); + isDone = true; + } + } +--- + +$(P +The output: +) + +$(SHELL +Message: 10 +Message: 20 +The worker has terminated. +) + +$(P +This exception can be received as a message as well. +) + +$(H6 Receiving exceptions as messages) + +$(P +The $(C OwnerTerminated) and $(C LinkTerminated) exceptions can be received as messages as well. The following code demonstrates this for the $(C OwnerTerminated) exception: +) + +--- + bool isDone = false; + + while (!isDone) { + receive( + (int message) { + writeln("Message: ", message); + }, + + ($(HILITE OwnerTerminated exc)) { + writeln("The owner has terminated; exiting."); + isDone = true; + } + ); + } +--- + +$(H5 Mailbox management) + +$(P +Every thread has a private mailbox that holds the messages that are sent to that thread. The number of messages in a mailbox may increase or decrease depending on how long it takes for the thread to receive and respond to each message. A continuously growing mailbox puts stress on the entire system and may point to a design flaw in the program. It may also mean that the thread may never get to the most recent messages. +) + +$(P +$(IX setMaxMailboxSize) $(C setMaxMailboxSize()) is used for limiting the number of messages that a mailbox can hold. Its three parameters specify the mailbox, the maximum number of messages that it can hold, and what should happen when the mailbox is full, in that order. There are four choices for the last parameter: +) + +$(UL + +$(LI $(IX OnCrowding) $(C OnCrowding.block): The sender waits until there is room in the mailbox.) + +$(LI $(C OnCrowding.ignore): The message is discarded.) + +$(LI $(IX MailboxFull) $(C OnCrowding.throwException): A $(C MailboxFull) exception is thrown when sending the message.) + +$(LI A function pointer of type $(C bool function(Tid)): The specified function is called.) + +) + +$(P +Before seeing an example of $(C setMaxMailboxSize()), let's first cause a mailbox to grow continuously. The worker in the following program sends messages back to back but the owner spends some time for each message: +) + +--- +/* WARNING: Your system may become unresponsive when this + * program is running. */ +import std.concurrency; +import core.thread; + +void workerFunc() { + while (true) { + ownerTid.send(42); // ← Produces messages continuously + } +} + +void main() { + spawn(&workerFunc); + + while (true) { + receive( + (int message) { + // Spends time for each message + Thread.sleep(1.seconds); + }); + } +} +--- + +$(P +Because the consumer is slower than the producer, the memory that the program above uses would grow continuously. To prevent that, the owner may limit the size of its mailbox before starting the worker: +) + +--- +void $(CODE_DONT_TEST)main() { + setMaxMailboxSize(thisTid, 1000, OnCrowding.block); + + spawn(&workerFunc); +// ... +} +--- + +$(P +The $(C setMaxMailboxSize()) call above sets the main thread's mailbox size to 1000. $(C OnCrowding.block) causes the sender to wait until there is room in the mailbox. +) + +$(P +The following example uses $(C OnCrowding.throwException), which causes a $(C MailboxFull) exception to be thrown when sending a message to a mailbox that is full: +) + +--- +import std.concurrency; +import core.thread; + +void workerFunc() { + while (true) { + try { + ownerTid.send(42); + + } catch ($(HILITE MailboxFull) exc) { + /* Failed to send; will try again later. */ + Thread.sleep(1.msecs); + } + } +} + +void main() { + setMaxMailboxSize(thisTid, 1000, $(HILITE OnCrowding.throwException)); + + spawn(&workerFunc); + + while (true) { + receive( + (int message) { + Thread.sleep(1.seconds); + }); + } +} +--- + +$(H5 $(IX prioritySend) $(IX PriorityMessageException) Priority messages) + +$(P +Messages can be sent with higher priority than regular messages by $(C prioritySend()). These messages are handled before the other messages that are already in the mailbox: +) + +--- + prioritySend(ownerTid, ImportantMessage(100)); +--- + +$(P +If the receiver does not have a message handler that matches the type of the priority message, then a $(C PriorityMessageException) is thrown: +) + +$(SHELL +std.concurrency.$(HILITE PriorityMessageException)@std/concurrency.d(280): +Priority message +) + +$(H5 Thread names) + +$(P +In the simple programs that we have used above, it was easy to pass the thread ids of owners and workers. Passing thread ids from thread to thread may be overly complicated in programs that use more than a couple of threads. To reduce this complexity, it is possible to assign names to threads, which are globally accessible from any thread. +) + +$(P +The following three functions define an interface to an associative array that every thread has access to: +) + +$(UL + +$(LI $(IX register, concurrency) $(C register()): Associates a thread with a name.) + +$(LI $(IX locate) $(C locate()): Returns the thread that is associated with the specified name. If there is no thread associated with that name, then $(C Tid.init) is returned.) + +$(LI $(IX unregister) $(C unregister()): Breaks the association between the specified name and the thread.) + +) + +$(P +The following program starts two threads that find each other by their names. These threads continuously send messages to each other until instructed to terminate by an $(C Exit) message: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +struct Exit { +} + +void main() { + // A thread whose partner is named "second" + auto first = spawn(&player, "second"); + $(HILITE register)("first", first); + scope(exit) $(HILITE unregister)("first"); + + // A thread whose partner is named "first" + auto second = spawn(&player, "first"); + $(HILITE register)("second", second); + scope(exit) $(HILITE unregister)("second"); + + Thread.sleep(2.seconds); + + prioritySend(first, Exit()); + prioritySend(second, Exit()); + + // For the unregister() calls to succeed, main() must wait + // until the workers terminate. + thread_joinAll(); +} + +void player(string nameOfPartner) { + Tid partner; + + while (partner == Tid.init) { + Thread.sleep(1.msecs); + partner = $(HILITE locate)(nameOfPartner); + } + + bool isDone = false; + + while (!isDone) { + partner.send("hello " ~ nameOfPartner); + receive( + (string message) { + writeln("Message: ", message); + Thread.sleep(500.msecs); + }, + + (Exit message) { + writefln("%s, I am exiting.", nameOfPartner); + isDone = true; + }); + } +} +--- + +$(P +$(IX thread_joinAll) The $(C thread_joinAll()) call that is seen at the end of $(C main()) is for making the owner to wait for all of its workers to terminate. +) + +$(P +The output: +) + +$(SHELL +Message: hello second +Message: hello first +Message: hello second +Message: hello first +Message: hello first +Message: hello second +Message: hello first +Message: hello second +second, I am exiting. +first, I am exiting. +) + +$(H5 Summary) + +$(UL + +$(LI When threads do not depend on other threads, prefer $(I parallelism), which has been the topic of the previous chapter. Consider $(I concurrency) only when threads depend on operations of other threads.) + +$(LI Because concurrency by data sharing is hard to implement correctly, prefer concurrency by message passing, which is the subject of this chapter.) + +$(LI $(C spawn()) and $(C spawnLinked()) start threads.) + +$(LI $(C thisTid) is the thread id of the current thread.) + +$(LI $(C ownerTid) is the thread id of the owner of the current thread.) + +$(LI $(C send()) and $(C prioritySend()) send messages.) + +$(LI $(C receiveOnly()), $(C receive()), and $(C receiveTimeout()) wait for messages.) + +$(LI $(C Variant) matches any type of message.) + +$(LI $(C setMaxMailboxSize()) limits the size of mailboxes.) + +$(LI $(C register()), $(C unregister()), and $(C locate()) allow referring to threads by name.) + +$(LI Exceptions may be thrown during message passing: $(C MessageMismatch), $(C OwnerTerminated), $(C LinkTerminated), $(C MailboxFull), and $(C PriorityMessageException).) + +$(LI The owner cannot automatically catch exceptions that are thrown from the worker.) + +) + +macros: + SUBTITLE=Message Passing Concurrency + + DESCRIPTION=Starting multiple threads in the D programming language and the interactions of threads by message passing. + + KEYWORDS=d programming language tutorial book concurrency thread + +MINI_SOZLUK= diff --git a/target/concurrency_shared.d b/target/concurrency_shared.d new file mode 100644 index 0000000..8e32f64 --- /dev/null +++ b/target/concurrency_shared.d @@ -0,0 +1,758 @@ +Ddoc + +$(DERS_BOLUMU $(IX data sharing concurrency) $(IX concurrency, data sharing) Data Sharing Concurrency) + +$(P +The previous chapter was about threads sharing information through message passing. As it has been mentioned in that chapter, message passing is a safe method of concurrency. +) + +$(P +Another method involves more than one thread reading from and writing to the same data. For example, the owner thread can start the worker with the address of a $(C bool) variable and the worker can determine whether to terminate or not by reading the current value of that variable. Another example would be where the owner starts multiple workers with the address of the same variable so that the variable gets modified by more than one worker. +) + +$(P +One of the reasons why data sharing is not safe is $(I race conditions). A race condition occurs when more than one thread accesses the same mutable data in an uncontrolled order. Since the operating system pauses and starts individual threads in unspecified ways, the behavior of a program that has race conditions is unpredictable. +) + +$(P +The examples in this chapter may look simplistic. However, the issues that they convey appear in real programs at greater scales. Also, although these examples use the $(C std.concurrency) module, the concepts of this chapter apply to the $(C core.thread) module as well. +) + +$(H5 Sharing is not automatic) + +$(P +Unlike most other programming languages, data is not automatically shared in D; data is thread-local by default. Although module-level variables may give the impression of being accessible by all threads, each thread actually gets its own copy: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +int $(HILITE variable); + +void printInfo(string message) { + writefln("%s: %s (@%s)", message, variable, &variable); +} + +void worker() { + variable = $(HILITE 42); + printInfo("Before the worker is terminated"); +} + +void main() { + spawn(&worker); + thread_joinAll(); + printInfo("After the worker is terminated"); +} +--- + +$(P +$(C variable) that is modified inside $(C worker()) is not the same $(C variable) that is seen by $(C main()). This fact can be observed by printing both the values and the addresses of the variables: +) + +$(SHELL +Before the worker is terminated: 42 (@7F26C6711670) +After the worker is terminated: 0 (@7F26C68127D0) +) + +$(P +Since each thread gets its own copy of data, $(C spawn()) does not allow passing references to thread-local variables. For example, the following program that tries to pass the address of a $(C bool) variable to another thread cannot be compiled: +) + +--- +import std.concurrency; + +void worker($(HILITE bool * isDone)) { + while (!(*isDone)) { + // ... + } +} + +void main() { + bool isDone = false; + spawn(&worker, $(HILITE &isDone)); $(DERLEME_HATASI) + + // ... + + // Hoping to signal the worker to terminate: + isDone = true; + + // ... +} +--- + +$(P +A $(C static assert) inside the $(C std.concurrency) module prevents accessing $(I mutable) data from another thread: +) + +$(SHELL +src/phobos/std/concurrency.d(329): Error: static assert +"Aliases to $(HILITE mutable thread-local data) not allowed." +) + +$(P +The address of the mutable variable $(C isDone) cannot be passed between threads. +) + +$(P +$(IX __gshared) An exception of this rule is a variable that is defined as $(C __gshared): +) + +--- +__gshared int globallyShared; +--- + +$(P +There is only one copy of such a variable in the entire program and all threads can share that variable. $(C __gshared) is necessary when interacting with libraries of languages like C and C++ where data sharing is automatic by default. +) + +$(H5 $(IX shared) $(C shared) to share mutable data between threads) + +$(P +Mutable variables that need to be shared must be defined with the $(C shared) keyword: +) + +--- +import std.concurrency; + +void worker($(HILITE shared(bool)) * isDone) { + while (*isDone) { + // ... + } +} + +void main() { + $(HILITE shared(bool)) isDone = false; + spawn(&worker, &isDone); + + // ... + + // Signalling the worker to terminate: + isDone = true; + + // ... +} +--- + +$(P +$(I $(B Note:) Prefer message-passing to signal a thread.) +) + +$(P +$(IX immutable, concurrency) On the other hand, since $(C immutable) variables cannot be modified, there is no problem with sharing them directly. For that reason, $(C immutable) implies $(C shared): +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +void worker($(HILITE immutable(int)) * data) { + writeln("data: ", *data); +} + +void main() { + $(HILITE immutable(int)) i = 42; + spawn(&worker, &i); // ← compiles + + thread_joinAll(); +} +--- + +$(P +The output: +) + +$(SHELL +data: 42 +) + +$(P +Note that since the lifetime of $(C i) is defined by the scope of $(C main()), it is important that $(C main()) does not terminate before the worker thread. The call to $(C core.thread.thread_joinAll) above is to make a thread wait for all of its child threads to terminate. +) + +$(H5 A race condition example) + +$(P +The correctness of the program requires extra attention when mutable data is shared between threads. +) + +$(P +To see an example of a race condition let's consider multiple threads sharing the same mutable variable. The threads in the following program receive the addresses as two variables and swap their values a large number of times: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +void swapper($(HILITE shared(int)) * first, $(HILITE shared(int)) * second) { + foreach (i; 0 .. 10_000) { + int temp = *second; + *second = *first; + *first = temp; + } +} + +void main() { + $(HILITE shared(int)) i = 1; + $(HILITE shared(int)) j = 2; + + writefln("before: %s and %s", i, j); + + foreach (id; 0 .. 10) { + spawn(&swapper, &i, &j); + } + + // Wait for all threads to finish their tasks + thread_joinAll(); + + writefln("after : %s and %s", i, j); +} +--- + +$(P +Although the program above gets compiled successfully, in most cases it would work incorrectly. Observe that it starts ten threads that all access the same two variables $(C i) and $(C j). As a result of the $(I race conditions) that they are in, they inadvertently spoil the operations of other threads. +) + +$(P +Also observe that total number of swaps is 10 times 10 thousand. Since that amount is an even number, it is natural to expect that the variables end up having values 1 and 2, their initial values: +) + +$(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE expected result) +) + +$(P +Although it is possible that the program can indeed produce that result, most of the time the actual outcome would be one of the following: +) + +$(SHELL +before: 1 and 2 +after : 1 and 1 $(SHELL_NOTE_WRONG incorrect result) +) + +$(SHELL +before: 1 and 2 +after : 2 and 2 $(SHELL_NOTE_WRONG incorrect result) +) + +$(P +It is possible but highly unlikely that the result may even end up being "2 and 1" as well. +) + +$(P +The reason why the program works incorrectly can be explained by the following scenario between just two threads that are in a race condition. As the operating system pauses and restarts the threads at indeterminate times, the following order of execution of the operations of the two threads is likely as well. +) + +$(P +Let's consider the state where $(C i) is 1 and $(C j) is 2. Although the two threads execute the same $(C swapper()) function, remember that the local variable $(C temp) is separate for each thread and it is independent from the other $(C temp) variables of other threads. To identify those separate variables, they are renamed as $(C tempA) and $(C tempB) below. +) + +$(P +The chart below demonstrates how the 3-line code inside the $(C for) loop may be executed by each thread over time, from top to bottom, operation 1 being the first operation and operation 6 being the last operation. Whether $(C i) or $(C j) is modified at each step is indicated by highlighting that variable: +) + +$(MONO +$(B Operation Thread A Thread B) +──────────────────────────────────────────────────────────────────────────── + + 1: int temp = *second; (tempA==2) + 2: *second = *first; (i==1, $(HILITE j==1)) + + $(I (Assume that A is paused and B is started at this point)) + + 3: int temp = *second; (tempB==1) + 4: *second = *first; (i==1, $(HILITE j==1)) + + $(I (Assume that B is paused and A is restarted at this point)) + + 5: *first = temp; ($(HILITE i==2), j==1) + + $(I (Assume that A is paused and B is restarted at this point)) + + 6: *first = temp; ($(HILITE i==1), j==1) +) + +$(P +As can be seen, at the end of the previous scenario both $(C i) and $(C j) end up having the value 1. It is not possible that they can ever have any other value after that point. +) + +$(P +The scenario above is just one example that is sufficient to explain the incorrect results of the program. Obviously, the race conditions would be much more complicated in the case of the ten threads of this example. +) + +$(H5 $(IX synchronized) $(C synchronized) to avoid race conditions) + +$(P +The incorrect program behavior above is due to more than one thread accessing the same mutable data (and at least one of them modifying it). One way of avoiding these race conditions is to mark the common code with the $(C synchronized) keyword. The program would work correctly with the following change: +) + +--- + foreach (i; 0 .. 10_000) { + $(HILITE synchronized {) + int temp = *b; + *b = *a; + *a = temp; + $(HILITE }) + } +--- + +$(P +The output: +) + +$(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE correct result) +) + +$(P +$(IX lock) The effect of $(C synchronized) is to create a lock behind the scenes and to allow only one thread hold that lock at a given time. Only the thread that holds the lock can be executed and the others wait until the lock becomes available again when the executing thread completes its $(C synchronized) block. Since one thread executes the $(I synchronized) code at a time, each thread would now swap the values safely before another thread does the same. The state of the variables $(C i) and $(C j) would always be either "1 and 2" or "2 and 1" at the end of processing the synchronized block. +) + +$(P +$(I $(B Note:) It is a relatively expensive operation for a thread to wait for a lock, which may slow down the execution of the program noticeably. Fortunately, in some cases program correctness can be ensured without the use of a $(C synchronized) block, by taking advantage of $(I atomic operations) that will be explained below.) +) + +$(P +When it is needed to synchronize more than one block of code, it is possible to specify one or more locks with the $(C synchronized) keyword. +) + +$(P +Let's see an example of this in the following program that has two separate code blocks that access the same shared variable. The program calls two functions with the address of the same variable, one function incrementing and the other function decrementing it equal number of times: +) + +--- +void incrementer(shared(int) * value) { + foreach (i; 0 .. count) { + *value = *value + 1; + } +} + +void decrementer(shared(int) * value) { + foreach (i; 0 .. count) { + *value = *value - 1; + } +} +--- + +$(P +$(I $(B Note:) If the shorter equivalents of the expression above are used (i.e. $(C ++(*value)) and $(C ‑‑(*value))), then the compiler warns that such read-modify-write operations on $(C shared) variables are deprecated.) +) + +$(P +Unfortunately, marking those blocks individually with $(C synchronized) is not sufficient, because the anonymous locks of the two blocks would be independent. So, the two code blocks would still be accessing the same variable concurrently: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +enum count = 1000; + +void incrementer(shared(int) * value) { + foreach (i; 0 .. count) { + $(HILITE synchronized) { // ← This lock is different from the one below. + *value = *value + 1; + } + } +} + +void decrementer(shared(int) * value) { + foreach (i; 0 .. count) { + $(HILITE synchronized) { // ← This lock is different from the one above. + *value = *value - 1; + } + } +} + +void main() { + shared(int) number = 0; + + foreach (i; 0 .. 100) { + spawn(&incrementer, &number); + spawn(&decrementer, &number); + } + + thread_joinAll(); + writeln("Final value: ", number); +} +--- + +$(P +Since there are equal number of threads that increment and decrement the same variable equal number of times, one would expect the final value of $(C number) to be zero. However, that is almost never the case: +) + +$(SHELL +Final value: -672 $(SHELL_NOTE_WRONG not zero) +) + +$(P +For more than one block to use the same lock or locks, the lock objects must be specified within the $(C synchronized) parentheses: +) + +$(P +$(HILITE $(I $(B Note:) This feature is not supported by dmd 2.074.0.)) +) + +--- + // Note: dmd 2.074.0 does not support this feature. + synchronized ($(I lock_object), $(I another_lock_object), ...) +--- + +$(P +There is no need for a special lock type in D because any class object can be used as a $(C synchronized) lock. The following program defines an empty class named $(C Lock) to use its objects as locks: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +enum count = 1000; + +$(HILITE class Lock { +}) + +void incrementer(shared(int) * value, $(HILITE shared(Lock) lock)) { + foreach (i; 0 .. count) { + synchronized $(HILITE (lock)) { + *value = *value + 1; + } + } +} + +void decrementer(shared(int) * value, $(HILITE shared(Lock) lock)) { + foreach (i; 0 .. count) { + synchronized $(HILITE (lock)) { + *value = *value - 1; + } + } +} + +void main() { + $(HILITE shared(Lock) lock = new shared(Lock)()); + shared(int) number = 0; + + foreach (i; 0 .. 100) { + spawn(&incrementer, &number, $(HILITE lock)); + spawn(&decrementer, &number, $(HILITE lock)); + } + + thread_joinAll(); + writeln("Final value: ", number); +} +--- + +$(P +Because this time both $(C synchronized) blocks are connected by the same lock, only one of them is executed at a given time and the result is zero as expected: +) + +$(SHELL +Final value: 0 $(SHELL_NOTE correct result) +) + +$(P +Class types can be defined as $(C synchronized) as well. This means that all of the non-static member functions of that type are synchronized on a given object of that class: +) + +--- +$(HILITE synchronized) class Class { + void foo() { + // ... + } + + void bar() { + // ... + } +} +--- + +$(P +The following is the equivalent of the class definition above: +) + +--- +class Class { + void foo() { + synchronized (this) { + // ... + } + } + + void bar() { + synchronized (this) { + // ... + } + } +} +--- + +$(P +When blocks of code need to be synchronized on more than one object, those objects must be specified together. Otherwise, it is possible that more than one thread may have locked objects that other threads are waiting for, in which case the program may be $(I deadlocked). +) + +$(P +A well known example of this problem is a function that tries to transfer money from one bank account to another. For this function to work correctly in a multi-threaded environment, both of the accounts must first be locked. However, the following attempt would be incorrect: +) + +--- +void transferMoney(shared BankAccount from, + shared BankAccount to) { + synchronized (from) { $(CODE_NOTE_WRONG INCORRECT) + synchronized (to) { + // ... + } + } +} +--- + +$(P +$(IX deadlock) The error can be explained by an example where one thread attempting to transfer money from account A to account to B while another thread attempting to transfer money in the reverse direction. It is possible that each thread may have just locked its respective $(C from) object, hoping next to lock its $(C to) object. Since the $(C from) objects correspond to A and B in the two threads respectively, the objects would be in locked state in separate threads, making it impossible for the other thread to ever lock its $(C to) object. This situation is called a $(I deadlock). +) + +$(P +The solution to this problem is to define an ordering relation between the objects and to lock them in that order, which is handled automatically by the $(C synchronized) statement. In D, it is sufficient to specify the objects in the same $(C synchronized) statement for the code to avoid such deadlocks: +) + +$(P +$(HILITE $(I $(B Note:) This feature is not supported by dmd 2.074.0.)) +) + +--- +void transferMoney(shared BankAccount from, + shared BankAccount to) { + // Note: dmd 2.074.0 does not support this feature. + synchronized (from, to) { $(CODE_NOTE correct) + // ... + } +} +--- + +$(H5 $(IX shared static this) $(IX static this, shared) $(IX shared static ~this) $(IX static ~this, shared) $(IX this, shared static) $(IX ~this, shared static) $(IX module constructor, shared) $(C shared static this()) for single initialization and $(C shared static ~this()) for single finalization) + +$(P +We have already seen that $(C static this()) can be used for initializing modules, including their variables. Because data is thread-local by default, $(C static this()) must be executed by every thread so that module-level variables are initialized for all threads: +) + +--- +import std.stdio; +import std.concurrency; +import core.thread; + +static this() { + writeln("executing static this()"); +} + +void worker() { +} + +void main() { + spawn(&worker); + + thread_joinAll(); +} +--- + +$(P +The $(C static this()) block above would be executed once for the main thread and once for the worker thread: +) + +$(SHELL +executing static this() +executing static this() +) + +$(P +This would cause problems for $(C shared) module variables because initializing a variable more than once would be wrong especially in concurrency due to race conditions. (That applies to $(C immutable) variables as well because they are implicitly $(C shared).) The solution is to use $(C shared static this()) blocks, which are executed only once per program: +) + +--- +int a; // thread-local +immutable int b; // shared by all threads + +static this() { + writeln("Initializing per-thread variable at ", &a); + a = 42; +} + +$(HILITE shared) static this() { + writeln("Initializing per-program variable at ", &b); + b = 43; +} +--- + +$(P +The output: +) + +$(SHELL +Initializing per-program variable at 6B0120 $(SHELL_NOTE only once) +Initializing per-thread variable at 7FBDB36557D0 +Initializing per-thread variable at 7FBDB3554670 +) + +$(P +Similarly, $(C shared static ~this()) is for final operations that must be executed only once per program. +) + +$(H5 $(IX atomic operation) Atomic operations) + +$(P +Another way of ensuring that only one thread mutates a certain variable is by using atomic operations, functionality of which are provided by the microprocessor, the compiler, or the operating system. +) + +$(P +The atomic operations of D are in the $(C core.atomic) module. We will see only two of its functions in this chapter: +) + +$(H6 $(IX atomicOp, core.atomic) $(C atomicOp)) + +$(P +This function applies its template parameter to its two function parameters. The template parameter must be a $(I binary operator) like $(STRING "+"), $(STRING "+="), etc. +) + +--- +import core.atomic; + +// ... + + atomicOp!"+="(*value, 1); // atomic +--- + +$(P +The line above is the equivalent of the following line, with the difference that the $(C +=) operation would be executed without interruptions by other threads (i.e. it would be executed $(I atomically)): +) + +--- + *value += 1; // NOT atomic +--- + +$(P +Consequently, when it is only a binary operation that needs to be synchronized, then there is no need for a $(C synchronized) block, which is known to be slow because of needing to acquire a lock. The following equivalents of the $(C incrementer()) and $(C decrementer()) functions that use $(C atomicOp) are correct as well. Note that there is no need for the $(C Lock) class anymore either: +) + +--- +import core.atomic; + +//... + +void incrementer(shared(int) * value) { + foreach (i; 0 .. count) { + $(HILITE atomicOp!"+="(*value, 1)); + } +} + +void decrementer(shared(int) * value) { + foreach (i; 0 .. count) { + $(HILITE atomicOp!"-="(*value, 1)); + } +} +--- + +$(P +$(C atomicOp) can be used with other binary operators as well. +) + +$(H6 $(IX cas, core.atomic) $(C cas)) + +$(P +The name of this function is the abbreviation of "compare and swap". Its behavior can be described as $(I mutate the variable if it still has its currently known value). It is used by specifying the current and the desired values of the variable at the same time: +) + +--- + bool is_mutated = cas(address_of_variable, currentValue, newValue); +--- + +$(P +The fact that the value of the variable still equals $(C currentValue) when $(C cas()) is operating is an indication that no other thread has mutated the variable since it has last been read by this thread. If so, $(C cas()) assigns $(C newValue) to the variable and returns $(C true). On the other hand, if the variable's value is different from $(C currentValue) then $(C cas()) does not mutate the variable and returns $(C false). +) + +$(P +The following functions re-read the current value and call $(C cas()) until the operation succeeds. Again, these calls can be described as $(I if the value of the variable equals this old value, replace with this new value): +) + +--- +void incrementer(shared(int) * value) { + foreach (i; 0 .. count) { + int currentValue; + + do { + currentValue = *value; + } while (!$(HILITE cas(value, currentValue, currentValue + 1))); + } +} + +void decrementer(shared(int) * value) { + foreach (i; 0 .. count) { + int currentValue; + + do { + currentValue = *value; + } while (!$(HILITE cas(value, currentValue, currentValue - 1))); + } +} +--- + +$(P +The functions above work correctly without the need for $(C synchronized) blocks. +) + +$(P +In most cases, the features of the $(C core.atomic) module can be several times faster than using $(C synchronized) blocks. I recommend that you consider this module as long as the operations that need synchronization are less than a block of code. +) + +$(P +Atomic operations enable $(I lock-free data structures) as well, which are beyond the scope of this book. +) + +$(P +You may also want to investigate the $(C core.sync) package, which contains classic concurrency primitives in the following modules: +) + +$(UL + +$(LI $(C core.sync.barrier)) +$(LI $(C core.sync.condition)) +$(LI $(C core.sync.config)) +$(LI $(C core.sync.exception)) +$(LI $(C core.sync.mutex)) +$(LI $(C core.sync.rwmutex)) +$(LI $(C core.sync.semaphore)) + +) + +$(H5 Summary) + +$(UL + +$(LI When threads do not depend on other threads, prefer $(I parallelism). Consider $(I concurrency) only when threads depend on operations of other threads.) + +$(LI Even then, prefer $(I message passing concurrency), which has been the topic of the previous chapter.) + +$(LI Only $(C shared) data can be shared; $(C immutable) is implicitly $(C shared).) + +$(LI $(C __gshared) provides data sharing as in C and C++ languages.) + +$(LI $(C synchronized) is for preventing other threads from intervening when a thread is executing a certain piece of code.) + +$(LI A class can be defined as $(C synchronized) so that only one member function can be executed on a given object at a given time. In other words, a thread can execute a member function only if no other thread is executing a member function on the same object.) + +$(LI $(C static this()) is executed once for each thread; $(C shared static this()) is executed once for the entire program.) + +$(LI The $(C core.atomic) module enables safe data sharing that can be multiple times faster than $(C synchronized).) + +$(LI The $(C core.sync) package includes many other concurrency primitives.) + +) + +macros: + SUBTITLE=Data Sharing Concurrency + + DESCRIPTION=Executing multiple threads that share data. + + KEYWORDS=d programming language tutorial book concurrency thread data sharing diff --git a/target/cond_comp.d b/target/cond_comp.d new file mode 100644 index 0000000..92c22ab --- /dev/null +++ b/target/cond_comp.d @@ -0,0 +1,670 @@ +Ddoc + +$(DERS_BOLUMU $(IX conditional compilation) Conditional Compilation) + +$(P +Conditional compilation is for compiling parts of programs in special ways depending on certain compile time conditions. Sometimes, entire sections of a program may need to be taken out and not compiled at all. +) + +$(P +Conditional compilation involves condition checks that are evaluable at compile time. Runtime conditional statements like $(C if), $(C for), $(C while) are not conditional compilation features. +) + +$(P +We have already encountered some features in the previous chapters, which can be seen as conditional compilation: +) + +$(UL + +$(LI +$(C unittest) blocks are compiled and run only if the $(C -unittest) compiler switch is enabled. +) + +$(LI +The contract programming blocks $(C in), $(C out), and $(C invariant) are activated only if the $(C -release) compiler switch is $(I not) enabled. +) + +) + +$(P +Unit tests and contracts are about program correctness; whether they are included in the program should not change the behavior of the program. +) + +$(P +The following are the features of D that are specifically for conditional compilation: +) + +$(UL +$(LI $(C debug)) +$(LI $(C version)) +$(LI $(C static if)) +$(LI $(C is) expression) +$(LI $(C __traits)) +) + +$(P +We will see the $(C is) expression in the next chapter and $(C __traits) in a later chapter. +) + +$(H5 $(IX debug) debug) + +$(P +$(IX -debug, compiler switch) $(C debug) is useful during program development. The expressions and statements that are marked as $(C debug) are compiled into the program only when the $(C -debug) compiler switch is enabled: +) + +--- +debug $(I a_conditionally_compiled_expression); + +debug { + // ... conditionally compiled code ... + +} else { + // ... code that is compiled otherwise ... +} +--- + +$(P +The $(C else) clause is optional. +) + +$(P +Both the single expression and the code block above are compiled only when the $(C -debug) compiler switch is enabled. +) + +$(P +We have been adding statements into the programs, which printed messages like "adding", "subtracting", etc. to the output. Such messages (aka $(I logs) and $(I log) messages) are helpful in finding errors by visualizing the steps that are taken by the program. +) + +$(P +Remember the $(C binarySearch()) function from $(LINK2 /ders/d.en/templates.html, the Templates chapter). The following version of the function is intentionally incorrect: +) + +--- +import std.stdio; + +// WARNING! This algorithm is wrong +size_t binarySearch(const int[] values, in int value) { + if (values.length == 0) { + return size_t.max; + } + + immutable midPoint = values.length / 2; + + if (value == values[midPoint]) { + return midPoint; + + } else if (value < values[midPoint]) { + return binarySearch(values[0 .. midPoint], value); + + } else { + return binarySearch(values[midPoint + 1 .. $], value); + } +} + +void main() { + auto numbers = [ -100, 0, 1, 2, 7, 10, 42, 365, 1000 ]; + + auto index = binarySearch(numbers, 42); + writeln("Index: ", index); +} +--- + +$(P +Although the index of 42 is 6, the program incorrectly reports 1: +) + +$(SHELL_SMALL +Index: 1 +) + +$(P +One way of locating the bug in the program is to insert lines that would print messages to the output: +) + +--- +size_t binarySearch(const int[] values, in int value) { + $(HILITE writefln)("searching %s among %s", value, values); + + if (values.length == 0) { + $(HILITE writefln)("%s not found", value); + return size_t.max; + } + + immutable midPoint = values.length / 2; + + $(HILITE writefln)("considering index %s", midPoint); + + if (value == values[midPoint]) { + $(HILITE writefln)("found %s at index %s", value, midPoint); + return midPoint; + + } else if (value < values[midPoint]) { + $(HILITE writefln)("must be in the first half"); + return binarySearch(values[0 .. midPoint], value); + + } else { + $(HILITE writefln)("must be in the second half"); + return binarySearch(values[midPoint + 1 .. $], value); + } +} +--- + +$(P +The output of the program now includes steps that the program takes: +) + +$(SHELL_SMALL +searching 42 among [-100, 0, 1, 2, 7, 10, 42, 365, 1000] +considering index 4 +must be in the second half +searching 42 among [10, 42, 365, 1000] +considering index 2 +must be in the first half +searching 42 among [10, 42] +considering index 1 +found 42 at index 1 +Index: 1 +) + +$(P +Let's assume that the previous output does indeed help the programmer locate the bug. It is obvious that the $(C writefln()) expressions are not needed anymore once the bug has been located and fixed. However, removing those lines can also be seen as wasteful, because they might be useful again in the future. +) + +$(P +Instead of being removed altogether, the lines can be marked as $(C debug) instead: +) + +--- + $(HILITE debug) writefln("%s not found", value); +--- + +$(P +Such lines are included in the program only when the $(C -debug) compiler switch is enabled: +) + +$(SHELL_SMALL +dmd deneme.d -ofdeneme -w $(HILITE -debug) +) + +$(H6 $(C debug($(I tag)))) + +$(P +If there are many $(C debug) keywords in the program, possibly in unrelated parts, the output may become too crowded. To avoid that, the $(C debug) statements can be given names (tags) to be included in the program selectively: +) + +--- + $(HILITE debug(binarySearch)) writefln("%s not found", value); +--- + +$(P +The tagged $(C debug) statements are enabled by the $(C -debug=$(I tag)) compiler switch: +) + +$(SHELL_SMALL +dmd deneme.d -ofdeneme -w $(HILITE -debug=binarySearch) +) + +$(P +$(C debug) blocks can have tags as well: +) + +--- + debug(binarySearch) { + // ... + } +--- + +$(P +It is possible to enable more than one $(C debug) tag at a time: +) + +$(SHELL_SMALL +$ dmd deneme.d -w $(HILITE -debug=binarySearch) $(HILITE -debug=stackContainer) +) + +$(P +In that case both the $(C binarySearch) and the $(C stackContainer) debug statements and blocks would be included. +) + +$(H6 $(C debug($(I level)))) + +$(P +Sometimes it is more useful to associate $(C debug) statements by numerical levels. Increasing levels can provide more detailed information: +) + +--- +$(HILITE debug) import std.stdio; + +void myFunction(string fileName, int[] values) { + $(HILITE debug(1)) writeln("entered myFunction"); + + $(HILITE debug(2)) { + writeln("the arguments:"); + writeln(" file name: ", fileName); + + foreach (i, value; values) { + writefln(" %4s: %s", i, value); + } + } + + // ... the implementation of the function ... +} + +void main() { + myFunction("deneme.txt", [ 10, 4, 100 ]); +} +--- + +$(P +The $(C debug) expressions and blocks that are lower than or equal to the specified level would be compiled: +) + +$(SHELL_SMALL +$ dmd deneme.d -w $(HILITE -debug=1) +$ ./deneme +$(SHELL_OBSERVED entered myFunction) +) + +$(P +The following compilation would provide more information: +) + +$(SHELL_SMALL +$ dmd deneme.d -w $(HILITE -debug=2) +$ ./deneme +$(SHELL_OBSERVED entered myFunction +the arguments: + file name: deneme.txt + 0: 10 + 1: 4 + 2: 100) +) + +$(H5 $(IX version) $(C version($(I tag))) and $(C version($(I level)))) + +$(P +$(C version) is similar to $(C debug) and is used in the same way: +) + +--- + version(testRelease) /* ... an expression ... */; + + version(schoolRelease) { + /* ... expressions that are related to the version of + * this program that is presumably shipped to schools ... */ + + } else { + // ... code compiled otherwise ... + } + + version(1) aVariable = 5; + + version(2) { + // ... a feature of version 2 ... + } +--- + +$(P +The $(C else) clause is optional. +) + +$(P +Although $(C version) works essentially the same as $(C debug), having separate keywords helps distinguish their unrelated uses. +) + +$(P +$(IX -version, compiler switch) As with $(C debug), more than one $(C version) can be enabled: +) + +$(SHELL_SMALL +$ dmd deneme.d -w $(HILITE -version=record) $(HILITE -version=precise_calculation) +) + +$(P +There are many predefined $(C version) tags, the complete list of which is available at the $(LINK2 http://dlang.org/version.html, Conditional Compilation specification). The following short list is just a sampling: +) + + + + + + + + + + + + + +
Predefined $(C version) tags
The compiler $(B DigitalMars GNU LDC SDC)
The operating system $(B Windows Win32 Win64 linux OSX Posix FreeBSD +OpenBSD +NetBSD +DragonFlyBSD +BSD +Solaris +AIX +Haiku +SkyOS +SysV3 +SysV4 +Hurd)
CPU endianness$(B LittleEndian BigEndian)
Enabled compiler switches $(B D_Coverage D_Ddoc D_InlineAsm_X86 D_InlineAsm_X86_64 D_LP64 D_PIC D_X32 +D_HardFloat +D_SoftFloat +D_SIMD +D_Version2 +D_NoBoundsChecks +unittest +assert +)
CPU architecture $(B X86 X86_64)
Platform $(B Android +Cygwin +MinGW +ARM +ARM_Thumb +ARM_Soft +ARM_SoftFP +ARM_HardFP +ARM64 +PPC +PPC_SoftFP +PPC_HardFP +PPC64 +IA64 +MIPS +MIPS32 +MIPS64 +MIPS_O32 +MIPS_N32 +MIPS_O64 +MIPS_N64 +MIPS_EABI +MIPS_NoFloat +MIPS_SoftFloat +MIPS_HardFloat +SPARC +SPARC_V8Plus +SPARC_SoftFP +SPARC_HardFP +SPARC64 +S390 +S390X +HPPA +HPPA64 +SH +SH64 +Alpha +Alpha_SoftFP +Alpha_HardFP +)
... ...
+ +$(P +In addition, there are the following two special $(C version) tags: +) + +$(UL +$(LI $(IX none, version) $(C none): This tag is never defined; it is useful for disabling code blocks.) +$(LI $(IX all, version) $(C all): This tag is always defined; it is useful for enabling code blocks.) +) + +$(P +As an example of how predefined $(C version) tags are used, the following is an excerpt (formatted differently here) from the $(C std.ascii) module, which is for determining the newline character sequence for the system ($(C static assert) will be explained later below): +) + +--- +version(Windows) { + immutable newline = "\r\n"; + +} else version(Posix) { + immutable newline = "\n"; + +} else { + static assert(0, "Unsupported OS"); +} +--- + +$(H5 Assigning identifiers to $(C debug) and $(C version)) + +$(P +Similar to variables, $(C debug) and $(C version) can be assigned identifiers. Unlike variables, this assignment does not change any value, it activates the specified identifier $(I as well). +) + +--- +import std.stdio; + +debug(everything) { + debug $(HILITE =) binarySearch; + debug $(HILITE =) stackContainer; + version $(HILITE =) testRelease; + version $(HILITE =) schoolRelease; +} + +void main() { + debug(binarySearch) writeln("binarySearch is active"); + debug(stackContainer) writeln("stackContainer is active"); + + version(testRelease) writeln("testRelease is active"); + version(schoolRelease) writeln("schoolRelease is active"); +} +--- + +$(P +The assignments inside the $(C debug(everything)) block above activates all of the specified identifiers: +) + +$(SHELL_SMALL +$ dmd deneme.d -w -debug=everything +$ ./deneme +$(SHELL_OBSERVED binarySearch is active +stackContainer is active +testRelease is active +schoolRelease is active) +) + +$(H5 $(IX static if) $(C static if)) + +$(P +$(C static if) is the compile time equivalent of the $(C if) statement. +) + +$(P +Just like the $(C if) statement, $(C static if) takes a logical expression and evaluates it. Unlike the $(C if) statement, $(C static if) is not about execution flow; rather, it determines whether a piece of code should be included in the program or not. +) + +$(P +The logical expression must be evaluable at compile time. If the logical expression evaluates to $(C true), the code inside the $(C static if) gets compiled. If the condition is $(C false), the code is not included in the program as if it has never been written. The logical expressions commonly take advantage of the $(C is) expression and $(C __traits). +) + +$(P +$(C static if) can appear at module scope or inside definitions of $(C struct), $(C class), template, etc. Optionally, there may be $(C else) clauses as well. +) + +$(P +Let's use $(C static if) with a simple template, making use of the $(C is) expression. We will see other examples of $(C static if) in the next chapter: +) + +--- +import std.stdio; + +struct MyType(T) { + static if (is (T == float)) { + alias ResultType = double; + + } $(HILITE else static if) (is (T == double)) { + alias ResultType = real; + + } else { + static assert(false, T.stringof ~ " is not supported"); + } + + ResultType doWork() { + writefln("The return type for %s is %s.", + T.stringof, ResultType.stringof); + ResultType result; + // ... + return result; + } +} + +void main() { + auto f = MyType!float(); + f.doWork(); + + auto d = MyType!double(); + d.doWork(); +} +--- + +$(P +According to the code, $(C MyType) can be used only with two types: $(C float) or $(C double). The return type of $(C doWork()) is chosen depending on whether the template is instantiated for $(C float) or $(C double): +) + +$(SHELL +The return type for float is double. +The return type for double is real. +) + +$(P +Note that one must write $(C else static if) when chaining $(C static if) clauses. Otherwise, writing $(C else if) would result in inserting that $(C if) conditional into the code, which would naturally be executed at run time. +) + +$(H5 $(IX static assert) $(C static assert)) + +$(P +Although it is not a conditional compilation feature, I have decided to introduce $(C static assert) here. +) + +$(P +$(C static assert) is the compile time equivalent of $(C assert). If the conditional expression is $(C false), the compilation gets aborted due to that assertion failure. +) + +$(P +Similar to $(C static if), $(C static assert) can appear in any scope in the program. +) + +$(P +We have seen an example of $(C static assert) in the program above: There, compilation gets aborted if $(C T) is any type other than $(C float) or $(C double): +) + +--- + auto i = MyType!$(HILITE int)(); +--- + +$(P +The compilation is aborted with the message that was given to $(C static assert): +) + +$(SHELL +Error: static assert "int is not supported" +) + +$(P +As another example, let's assume that a specific algorithm can work only with types that are a multiple of a certain size. Such a condition can be ensured at compile time by a $(C static assert): +) + +--- +T myAlgorithm(T)(T value) { + /* This algorithm requires that the size of type T is a + * multiple of 4. */ + static assert((T.sizeof % 4) == 0); + + // ... +} +--- + +$(P +If the function was called with a $(C char), the compilation would be aborted with the following error message: +) + +$(SHELL_SMALL +Error: static assert (1LU == 0LU) is false +) + +$(P +Such a test prevents the function from working with an incompatible type, potentially producing incorrect results. +) + +$(P +$(C static assert) can be used with any logical expression that is evaluable at compile time. +) + +$(H5 $(IX type trait) $(IX traits) Type traits) + +$(P +$(IX __traits) $(IX std.traits) The $(C __traits) keyword and the $(C std.traits) module provide information about types and expressions at compile time. +) + +$(P +Some information that is collected by the compiler is made available to the program by $(C __traits). Its syntax includes a traits $(I keyword) and $(I parameters) that are relevant to that keyword: +) + +--- + __traits($(I keyword), $(I parameters)) +--- + +$(P +$(I keyword) specifies the information that is being queried. The $(I parameters) are either types or expressions, meanings of which are determined by each particular keyword. +) + +$(P +The information that can be gathered by $(C __traits) is especially useful in templates. For example, the $(C isArithmetic) keyword can determine whether a particular template parameter $(C T) is an arithmetic type: +) + +--- + static if (__traits($(HILITE isArithmetic), T)) { + // ... an arithmetic type ... + + } else { + // ... not an arithmetic type ... + } +--- + +$(P +Similarly, the $(C std.traits) module provides information at compile time through its templates. For example, $(C std.traits.isSomeChar) returns $(C true) if its template parameter is a character type: +) + +--- +import std.traits; + +// ... + + static if ($(HILITE isSomeChar)!T) { + // ... char, wchar, or dchar ... + + } else { + // ... not a character type ... + } +--- + +$(P +Please refer to $(LINK2 http://dlang.org/traits.html, the $(C __traits) documentation) and $(LINK2 http://dlang.org/phobos/std_traits.html, the $(C std.traits) documentation) for more information. +) + +$(H5 Summary) + +$(UL + +$(LI +Code that is defined as $(C debug) is included to the program only if the $(C -debug) compiler switch is used. +) + +$(LI +Code that is defined as $(C version) is included to the program only if a corresponding $(C -version) compiler switch is used. +) + +$(LI +$(C static if) is similar to an $(C if) statement that is executed at compile time. It introduces code to the program depending on certain compile-time conditions. +) + +$(LI +$(C static assert) validates assumptions about code at compile time. +) + +$(LI $(C __traits) and $(C std.traits) provide information about types at compile time.) + +) + +Macros: + SUBTITLE=Conditional Compilation + + DESCRIPTION=Specifying parts of a program conditionally depending on logical expressions that are evaluated at compile time. + + KEYWORDS=d programming language tutorial book conditional compilation diff --git a/target/const_and_immutable.d b/target/const_and_immutable.d new file mode 100644 index 0000000..61158ae --- /dev/null +++ b/target/const_and_immutable.d @@ -0,0 +1,758 @@ +Ddoc + +$(DERS_BOLUMU $(IX const) $(IX immutable) Immutability) + +$(P +We have seen that variables represent concepts in programs. The interactions of these concepts are achieved by expressions that change the values of those variables: +) + +--- + // Pay the bill + totalPrice = calculateAmount(itemPrices); + moneyInWallet $(HILITE -=) totalPrice; + moneyAtMerchant $(HILITE +=) totalPrice; +--- + +$(P +Modifying a variable is called $(I mutating) that variable. The concept of mutability is essential for most tasks. However, there are some cases where mutability is not desirable: +) + +$(UL + +$(LI +Some concepts are immutable by definition. For example, there are always seven days in a week, the math constant $(I pi) (π) never changes, the list of natural languages supported by a program may be fixed and small (e.g. only English and Turkish), etc. +) + +$(LI +If every variable were modifiable, as we have seen so far, then every piece of code that used a variable could potentially modify it. Even if there was no reason to modify a variable in an operation there would be no guarantee that this would not happen by accident. Programs are difficult to read and maintain when there are no immutability guarantees. + +$(P +For example, consider a function call $(C retire(office, worker)) that retires a worker of an office. If both of those variables were mutable it would not be clear (just by looking at that function call) which of them would be modified by the function. It may be expected that the number of active employees of $(C office) would be decreased, but would the function call also modify $(C worker) in some way? +) + +) + +) + +$(P +The concept of immutability helps with understanding parts of programs by guaranteeing that certain operations do not change certain variables. It also reduces the risk of some types of programming errors. +) + +$(P +The $(I immutability) concept is expressed in D by the $(C const) and $(C immutable) keywords. Although the two words themselves are close in meaning, their responsibilities in programs are different and they are sometimes incompatible. +) + +$(P +$(IX type qualifier) $(IX qualifier, type) $(C const), $(C immutable), $(C inout), and $(C shared) are $(I type qualifiers). (We will see $(LINK2 /ders/d.en/function_parameters.html, $(C inout)) and $(LINK2 /ders/d.en/concurrency_shared.html, $(C shared)) in later chapters.) +) + +$(H5 Immutable variables) + +$(P +Both of the terms "immutable variable" and "constant variable" are nonsensical when the word "variable" is taken literally to mean $(I something that changes). In a broader sense, the word "variable" is often understood to mean any concept of a program which may be mutable or immutable. +) + +$(P +There are three ways of defining variables that can never be mutated. +) + +$(H6 $(IX enum) $(C enum) constants) + +$(P +We have seen earlier in the $(LINK2 /ders/d.en/enum.html, $(C enum) chapter) that $(C enum) defines named constant values: +) + +--- + enum fileName = "list.txt"; +--- + +$(P +As long as their values can be determined at compile time, $(C enum) variables can be initialized with return values of functions as well: +) + +--- +int totalLines() { + return 42; +} + +int totalColumns() { + return 7; +} + +string name() { + return "list"; +} + +void main() { + enum fileName = name() ~ ".txt"; + enum totalSquares = totalLines() * totalColumns(); +} +--- + +$(P +The D feature that enables such initialization is $(I compile time function execution) (CTFE), which we will see in $(LINK2 /ders/d.en/functions_more.html, a later chapter). +) + + +$(P +As expected, the values of $(C enum) constants cannot be modified: +) + +--- + ++totalSquares; $(DERLEME_HATASI) +--- + +$(P +Although it is a very effective way of representing immutable values, $(C enum) can only be used for compile-time values. +) + +$(P +An $(C enum) constant is $(I a manifest constant), meaning that the program is compiled as if every mention of that constant had been replaced by its value. As an example, let's consider the following $(C enum) definition and the two expressions that make use of it: +) + +--- + enum i = 42; + writeln(i); + foo(i); +--- + +$(P +The code above is completely equivalent to the one below, where we replace every use of $(C i) with its value of $(C 42): +) + +--- + writeln(42); + foo(42); +--- + +$(P +Although that replacement makes sense for simple types like $(C int) and makes no difference to the resulting program, $(C enum) constants can bring a hidden cost when they are used for arrays or associative arrays: +) + +--- + enum a = [ 42, 100 ]; + writeln(a); + foo(a); +--- + +$(P +After replacing $(C a) with its value, the equivalent code that the compiler would be compiling is the following: +) + +--- + writeln([ 42, 100 ]); // an array is created at run time + foo([ 42, 100 ]); // another array is created at run time +--- + +$(P +The hidden cost here is that there would be two separate arrays created for the two expressions above. For that reason, it may make more sense to define arrays and associative arrays as $(C immutable) variables if they are going to be used more than once in the program. +) + +$(H6 $(IX variable, immutable) $(C immutable) variables) + +$(P +Like $(C enum), this keyword specifies that the value of a variable will never change. Unlike $(C enum), an $(C immutable) variable is an actual variable with a memory address, which means that we can set its value during the execution of the program and that we can refer to its memory location. +) + +$(P +The following program compares the uses of $(C enum) and $(C immutable). The program asks for the user to guess a number that has been picked randomly. Since the random number cannot be determined at compile time, it cannot be defined as an $(C enum). Still, since the randomly picked value must never be changed after having been decided, it is suitable to specify that variable as $(C immutable). +) + +$(P +The program takes advantage of the $(C readInt()) function that was defined in the previous chapter: +) + +--- +import std.stdio; +import std.random; + +int readInt(string message) { + int result; + write(message, "? "); + readf(" %s", &result); + return result; +} + +void main() { + enum min = 1; + enum max = 10; + + $(HILITE immutable) number = uniform(min, max + 1); + + writefln("I am thinking of a number between %s and %s.", + min, max); + + auto isCorrect = false; + while (!isCorrect) { + $(HILITE immutable) guess = readInt("What is your guess"); + isCorrect = (guess == number); + } + + writeln("Correct!"); +} +--- + +$(P +Observations: +) + +$(UL + +$(LI $(C min) and $(C max) are integral parts of the behavior of this program and their values are known at compile time. For that reason they are defined as $(C enum) constants. +) + +$(LI $(C number) is specified as $(C immutable) because it would not be appropriate to modify it after its initialization at run time. Likewise for each user guess: once read, the guess should not be modified. +) + +$(LI Observe that the types of those variables are not specified explicitly. As with $(C auto) and $(C enum), the type of an $(C immutable) variable can be inferred from the expression on the right hand side. +) + +) + +$(P +Although it is not necessary to write the type fully, $(C immutable) normally takes the actual type within parentheses, e.g. $(C immutable(int)). The output of the following program demonstrates that the full names of the types of the three variables are in fact the same: +) + +--- +import std.stdio; + +void main() { + immutable inferredType = 0; + immutable int explicitType = 1; + immutable(int) wholeType = 2; + + writeln(typeof(inferredType).stringof); + writeln(typeof(explicitType).stringof); + writeln(typeof(wholeType).stringof); +} +--- + +$(P +The actual name of the type includes $(C immutable): +) + +$(SHELL +immutable(int) +immutable(int) +immutable(int) +) + +$(P +The use of parentheses has significance, and specifies which parts of the type are immutable. We will see this below when discussing the immutability of the whole slice vs. its elements. +) + +$(H6 $(IX variable, const) $(C const) variables) + +$(P +When defining variables the $(C const) keyword has the same effect as $(C immutable). $(C const) variables cannot be modified: +) + +--- + $(HILITE const) half = total / 2; + half = 10; $(DERLEME_HATASI) +--- + +$(P +I recommend that you prefer $(C immutable) over $(C const) for defining variables. The reason is that $(C immutable) variables can be passed to functions that have $(C immutable) parameters. We will see this below. +) + +$(H5 Immutable parameters) + +$(P +It is possible for functions to promise that they do not modify certain parameters that they take, and the compiler will enforce this promise. Before seeing how this is achieved, let's first see that functions can indeed modify the elements of slices that are passed as arguments to those functions. +) + +$(P +As you would remember from the $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features chapter), slices do not own elements but provide access to them. There may be more than one slice at a given time that provides access to the same elements. +) + +$(P +Although the examples in this section focus only on slices, this topic is applicable to associative arrays and classes as well because they too are $(I reference types). +) + +$(P +A slice that is passed as a function argument is not the slice that the function is called with. The argument is a copy of the actual slice: +) + +--- +import std.stdio; + +void main() { + int[] slice = [ 10, 20, 30, 40 ]; // 1 + halve(slice); + writeln(slice); +} + +void halve(int[] numbers) { // 2 + foreach (ref number; numbers) { + number /= 2; + } +} +--- + +$(P +When program execution enters the $(C halve()) function, there are two slices that provide access to the same four elements: +) + +$(OL + +$(LI The slice named $(C slice) that is defined in $(C main()), which is passed to $(C halve()) as its argument +) + +$(LI The slice named $(C numbers) that $(C halve()) receives as its argument, which provides access to the same elements as $(C slice) +) + +) + +$(P +Since both slides refer to the same elements and given that we use the $(C ref) keyword in the $(C foreach) loop, the values of the elements get halved: +) + +$(SHELL +[5, 10, 15, 20] +) + +$(P +It is useful for functions to be able to modify the elements of the slices that are passed as arguments. Some functions exist just for that purpose, as has been seen in this example. +) + +$(P +The compiler does not allow passing $(C immutable) variables as arguments to such functions because we cannot modify an immutable variable: +) + +--- + $(HILITE immutable) int[] slice = [ 10, 20, 30, 40 ]; + halve(slice); $(DERLEME_HATASI) +--- + +$(P +The compilation error indicates that a variable of type $(C immutable(int[])) cannot be used as an argument of type $(C int[]): +) + +$(SHELL +Error: function deneme.halve ($(HILITE int[]) numbers) is not callable +using argument types ($(HILITE immutable(int[]))) +) + +$(H6 $(IX parameter, const) $(C const) parameters) + +$(P +It is important and natural that $(C immutable) variables be prevented from being passed to functions like $(C halve()), which modify their arguments. However, it would be a limitation if they could not be passed to functions that do not modify their arguments in any way: +) + +--- +import std.stdio; + +void main() { + immutable int[] slice = [ 10, 20, 30, 40 ]; + print(slice); $(DERLEME_HATASI) +} + +void print(int[] slice) { + writefln("%s elements: ", slice.length); + + foreach (i, element; slice) { + writefln("%s: %s", i, element); + } +} +--- + +$(P +It does not make sense above that a slice is prevented from being printed just because it is $(C immutable). The proper way of dealing with this situation is by using $(C const) parameters. +) + +$(P +The $(C const) keyword specifies that a variable is not modified through $(I that particular reference) (e.g. a slice) of that variable. Specifying a parameter as $(C const) guarantees that the elements of the slice are not modified inside the function. Once $(C print()) provides this guarantee, the program can now be compiled: +) + +--- + print(slice); // now compiles +// ... +void print($(HILITE const) int[] slice) +--- + +$(P +This guarantee allows passing both mutable and $(C immutable) variables as arguments: +) + +--- + immutable int[] slice = [ 10, 20, 30, 40 ]; + print(slice); // compiles + + int[] mutableSlice = [ 7, 8 ]; + print(mutableSlice); // compiles +--- + +$(P +A parameter that is not modified in a function but is not specified as $(C const) reduces the applicability of that function. Additionally, $(C const) parameters provide useful information to the programmer. Knowing that a variable will not be modified when passed to a function makes the code easier to understand. It also prevents potential errors because the compiler detects modifications to $(C const) parameters: +) + +--- +void print($(HILITE const) int[] slice) { + slice[0] = 42; $(DERLEME_HATASI) +--- + +$(P +The programmer would either realize the mistake in the function or would rethink the design and perhaps remove the $(C const) specifier. +) + +$(P +The fact that $(C const) parameters can accept both mutable and $(C immutable) variables has an interesting consequence. This is explained in the "Should a parameter be $(C const) or $(C immutable)?" section below. +) + +$(H6 $(IX parameter, immutable) $(C immutable) parameters) + +$(P +As we saw above, both mutable and $(C immutable) variables can be passed to functions as their $(C const) parameters. In a way, $(C const) parameters are welcoming. +) + +$(P +In contrast, $(C immutable) parameters bring a strong requirement: only $(C immutable) variables can be passed to functions as their $(C immutable) parameters: +) + +--- +void func($(HILITE immutable) int[] slice) { + // ... +} + +void main() { + immutable int[] immSlice = [ 1, 2 ]; + int[] slice = [ 8, 9 ]; + + func(immSlice); // compiles + func(slice); $(DERLEME_HATASI) +} +--- + +$(P +For that reason, the $(C immutable) specifier should be used only when this requirement is actually necessary. We have indeed been using the $(C immutable) specifier indirectly through certain string types. This will be covered below. +) + +$(P +We have seen that the parameters that are specified as $(C const) or $(C immutable) promise not to modify $(I the actual variable) that is passed as an argument. This is relevant only for reference types because only then there is $(I the actual variable) to talk about the immutability of. +) + +$(P +$(I Reference types) and $(I value types) will be covered in the next chapter. Among the types that we have seen so far, only slices and associative arrays are reference types; the others are value types. +) + +$(H6 $(IX parameter, const vs. immutable) Should a parameter be $(C const) or $(C immutable)?) + +$(P +The two sections above may give the impression that, being more flexible, $(C const) parameters should be preferred over $(C immutable) parameters. This is not always true. +) + +$(P +$(C const) $(I erases) the information about whether the original variable was mutable or $(C immutable). This information is hidden even from the compiler. +) + +$(P +A consequence of this fact is that $(C const) parameters cannot be passed as arguments to functions that take $(C immutable) parameters. For example, $(C foo()) below cannot pass its $(C const) parameter to $(C bar()): +) + +--- +void main() { + /* The original variable is immutable */ + immutable int[] slice = [ 10, 20, 30, 40 ]; + foo(slice); +} + +/* A function that takes its parameter as const, in order to + * be more useful. */ +void foo(const int[] slice) { + bar(slice); $(DERLEME_HATASI) +} + +/* A function that takes its parameter as immutable, for a + * plausible reason. */ +void bar(immutable int[] slice) { + // ... +} +--- + +$(P +$(C bar()) requires the parameter to be $(C immutable). However, it is not known (in general) whether the original variable that $(C foo())'s $(C const) parameter references was $(C immutable) or not. +) + +$(P +$(I $(B Note:) It is clear in the code above that the original variable in $(C main()) is $(C immutable). However, the compiler compiles functions individually, without regard to all of the places that function is called from. To the compiler, the $(C slice) parameter of $(C foo()) may refer to a mutable variable or an $(C immutable) one. +) +) + +$(P +A solution would be to call $(C bar()) with an immutable copy of the parameter: +) + +--- +void foo(const int[] slice) { + bar(slice$(HILITE .idup)); +} +--- + +$(P +Although that is a sensible solution, it does incur into the cost of copying the slice and its contents, which would be wasteful in the case where the original variable was $(C immutable) to begin with. +) + +$(P +After this analysis, it should be clear that always declaring parameters as $(C const) is not the best approach in every situation. After all, if $(C foo())'s parameter had been defined as $(C immutable) there would be no need to copy it before calling $(C bar()): +) + +--- +void foo(immutable int[] slice) { // This time immutable + bar(slice); // Copying is not needed anymore +} +--- + +$(P +Although the code compiles, defining the parameter as $(C immutable) has a similar cost: this time an immutable copy of the original variable is needed when calling $(C foo()), if that variable was not immutable to begin with: +) + +--- + foo(mutableSlice$(HILITE .idup)); +--- + +$(P +Templates can help. (We will see templates in later chapters.) Although I don't expect you to fully understand the following function at this point in the book, I will present it as a solution to this problem. The following function template $(C foo()) can be called both with mutable and $(C immutable) variables. The parameter would be copied only if the original variable was mutable; no copying would take place if it were $(C immutable): +) + +--- +import std.conv; +// ... + +/* Because it is a template, foo() can be called with both mutable + * and immutable variables. */ +void foo(T)(T[] slice) { + /* 'to()' does not make a copy if the original variable is + * already immutable. */ + bar(to!(immutable T[])(slice)); +} +--- + +$(H5 Immutability of the slice versus the elements) + +$(P +We have seen above that the type of an $(C immutable) slice has been printed as $(C immutable(int[])). As the parentheses after $(C immutable) indicate, it is the entire slice that is $(C immutable). Such a slice cannot be modified in any way: elements may not be added or removed, their values may not be modified, and the slice may not start providing access to a different set of elements: +) + +--- + immutable int[] immSlice = [ 1, 2 ]; + immSlice ~= 3; $(DERLEME_HATASI) + immSlice[0] = 3; $(DERLEME_HATASI) + immSlice.length = 1; $(DERLEME_HATASI) + + immutable int[] immOtherSlice = [ 10, 11 ]; + immSlice = immOtherSlice; $(DERLEME_HATASI) +--- + +$(P +Taking immutability to that extreme may not be suitable in every case. In most cases, what is important is the immutability of the elements themselves. Since a slice is just a tool to access the elements, it should not matter if we make changes to the slice itself as long as the elements are not modified. This is especially true in the cases we have seen so far, where the function receives a copy of the slice itself. +) + +$(P +To specify that only the elements are immutable we use the $(C immutable) keyword with parentheses that enclose just the element type. Modifying the code accordingly, now only the elements are immutable, not the slice itself: +) + +--- + immutable$(HILITE (int))[] immSlice = [ 1, 2 ]; + immSlice ~= 3; // can add elements + immSlice[0] = 3; $(DERLEME_HATASI) + immSlice.length = 1; // can drop elements + + immutable int[] immOtherSlice = [ 10, 11 ]; + immSlice = immOtherSlice; /* can provide access to + * other elements */ +--- + +$(P +Although the two syntaxes are very similar, they have different meanings. To summarize: +) + +--- + immutable int[] a = [1]; /* Neither the elements nor the + * slice can be modified */ + + immutable(int[]) b = [1]; /* The same meaning as above */ + + immutable(int)[] c = [1]; /* The elements cannot be + * modified but the slice can be */ +--- + +$(P +This distinction has been in effect in some of the programs that we have written so far. As you may remember, the three string aliases involve immutability: +) + +$(UL +$(LI $(C string) is an alias for $(C immutable(char)[])) +$(LI $(C wstring) is an alias for $(C immutable(wchar)[])) +$(LI $(C dstring) is an alias for $(C immutable(dchar)[])) +) + +$(P +Likewise, string literals are immutable as well: +) + +$(UL +$(LI The type of literal $(STRING "hello"c) is $(C string)) +$(LI The type of literal $(STRING "hello"w) is $(C wstring)) +$(LI The type of literal $(STRING "hello"d) is $(C dstring)) +) + +$(P +According to these definitions, D strings are normally arrays of $(I immutable characters). +) + +$(H6 $(IX transitive, immutability) $(C const) and $(C immutable) are transitive) + +$(P +As mentioned in the code comments of slices $(C a) and $(C b) above, both those slices and their elements are $(C immutable). +) + +$(P +This is true for $(LINK2 /ders/d.en/struct.html, structs) and $(LINK2 /ders/d.en/class.html, classes) as well, both of which will be covered in later chapters. For example, all members of a $(C const) $(C struct) variable are $(C const) and all members of an $(C immutable) $(C struct) variable are $(C immutable). (Likewise for classes.) +) + +$(H6 $(IX .dup) $(IX .idup) $(C .dup) and $(C .idup)) + +$(P +There may be mismatches in immutability when strings are passed to functions as parameters. The $(C .dup) and $(C .idup) properties make copies of arrays with the desired mutability: +) + +$(UL +$(LI $(C .dup) makes a mutable copy of the array; its name comes from "duplicate") +$(LI $(C .idup) makes an immutable copy of the array) +) + +$(P +For example, a function that insists on the immutability of a parameter may have to be called with an immutable copy of a mutable string: +) + +--- +void foo($(HILITE string) s) { + // ... +} + +void main() { + char[] salutation; + foo(salutation); $(DERLEME_HATASI) + foo(salutation$(HILITE .idup)); // ← this compiles +} +--- + +$(H5 How to use) + +$(UL + +$(LI +As a general rule, prefer immutable variables over mutable ones. +) + +$(LI +Define constant values as $(C enum) if their values can be calculated at compile time. For example, the constant value of $(I seconds per minute) can be an $(C enum): + +--- + enum int secondsPerMinute = 60; +--- + +$(P +There is no need to specify the type explicitly if it can be inferred from the right hand side: +) + +--- + enum secondsPerMinute = 60; +--- + +) + +$(LI +Consider the hidden cost of $(C enum) arrays and $(C enum) associative arrays. Define them as $(C immutable) variables if the arrays are large and they are used more than once in the program. +) + +$(LI +Specify variables as $(C immutable) if their values will never change but cannot be known at compile time. Again, the type can be inferred: + +--- + immutable guess = readInt("What is your guess"); +--- + +) + +$(LI +If a function does not modify a parameter, specify that parameter as $(C const). This would allow both mutable and $(C immutable) variables to be passed as arguments: + +--- +void foo(const char[] s) { + // ... +} + +void main() { + char[] mutableString; + string immutableString; + + foo(mutableString); // ← compiles + foo(immutableString); // ← compiles +} +--- + +) + +$(LI +Following from the previous guideline, consider that $(C const) parameters cannot be passed to functions taking $(C immutable). See the section titled "Should a parameter be $(C const) or $(C immutable)?" above. +) + +$(LI +If the function modifies a parameter, leave that parameter as mutable ($(C const) or $(C immutable) would not allow modifications anyway): + +--- +import std.stdio; + +void reverse(dchar[] s) { + foreach (i; 0 .. s.length / 2) { + immutable temp = s[i]; + s[i] = s[$ - 1 - i]; + s[$ - 1 - i] = temp; + } +} + +void main() { + dchar[] salutation = "hello"d.dup; + reverse(salutation); + writeln(salutation); +} +--- + +$(P +The output: +) + +$(SHELL +olleh +) + +) + +) + +$(H5 Summary) + +$(UL + +$(LI $(C enum) variables represent immutable concepts that are known at compile time.) + +$(LI $(C immutable) variables represent immutable concepts that must be calculated at run time, or that must have some memory location that we can refer to.) + +$(LI $(C const) parameters are the ones that functions do not modify. Both mutable and $(C immutable) variables can be passed as arguments of $(C const) parameters.) + +$(LI $(C immutable) parameters are the ones that functions specifically require them to be so. Only $(C immutable) variables can be passed as arguments of $(C immutable) parameters.) + +$(LI $(C immutable(int[])) specifies that neither the slice nor its elements can be modified.) + +$(LI $(C immutable(int)[]) specifies that only the elements cannot be modified.) + +) + +Macros: + SUBTITLE=Immutability + + DESCRIPTION=The const and immutable keywords of D, which support the concept of immutability. + + KEYWORDS=d programming language tutorial book immutable const diff --git a/target/const_member_functions.d b/target/const_member_functions.d new file mode 100644 index 0000000..00adde7 --- /dev/null +++ b/target/const_member_functions.d @@ -0,0 +1,289 @@ +Ddoc + +$(DERS_BOLUMU $(CH4 const ref) Parameters and $(CH4 const) Member Functions) + +$(P +This chapter is about how parameters and member functions are marked as $(C const) so that they can be used with $(C immutable) variables as well. As we have already covered $(C const) parameters in earlier chapters, some information in this chapter will be a review of some of the features that you already know. +) + +$(P +Although the examples in this chapter use only structs, $(C const) member functions apply to classes as well. +) + +$(H5 $(C immutable) objects) + +$(P +We have already seen that it is not possible to modify $(C immutable) variables: +) + +--- + immutable readingTime = TimeOfDay(15, 0); +--- + +$(P +$(C readingTime) cannot be modified: +) + +--- + readingTime = TimeOfDay(16, 0); $(DERLEME_HATASI) + readingTime.minute += 10; $(DERLEME_HATASI) +--- + +$(P +The compiler does not allow modifying $(C immutable) objects in any way. +) + +$(H5 $(C ref) parameters that are not $(C const)) + +$(P +We have seen this concept earlier in the $(LINK2 /ders/d.en/function_parameters.html, Function Parameters chapter). Parameters that are marked as $(C ref) can freely be modified by the function. For that reason, even if the function does not actually modify the parameter, the compiler does not allow passing $(C immutable) objects as that parameter: +) + +--- +/* Although not being modified by the function, 'duration' + * is not marked as 'const' */ +int totalSeconds(ref Duration duration) { + return 60 * duration.minute; +} +// ... + $(HILITE immutable) warmUpTime = Duration(3); + totalSeconds(warmUpTime); $(DERLEME_HATASI) +--- + +$(P +The compiler does not allow passing the $(C immutable) $(C warmUpTime) to $(C totalSeconds) because that function does not guarantee that the parameter will not be modified. +) + +$(H5 $(IX const ref) $(IX ref const) $(IX parameter, const ref) $(C const ref) parameters) + +$(P +$(C const ref) means that the parameter is not modified by the function: +) + +--- +int totalSeconds(const ref Duration duration) { + return 60 * duration.minute; +} +// ... + immutable warmUpTime = Duration(3); + totalSeconds(warmUpTime); // ← now compiles +--- + +$(P +Such functions can receive $(C immutable) objects as parameters because the immutability of the object is enforced by the compiler: +) + +--- +int totalSeconds(const ref Duration duration) { + duration.minute = 7; $(DERLEME_HATASI) +// ... +} +--- + +$(P +$(IX in ref) $(IX ref in) $(IX parameter, in ref) An alternative to $(C const ref) is $(C in ref). As we will see in $(LINK2 /ders/d.en/function_parameters.html, a later chapter), $(C in) means that the parameter is used only as input to the function, disallowing any modification to it. +) + +--- +int totalSeconds($(HILITE in ref) Duration duration) { + // ... +} +--- + +$(H5 Non-$(C const) member functions) + +$(P +As we have seen with the $(C TimeOfDay.increment) member function, objects can be modified through member functions as well. $(C increment()) modifies the members of the object that it is called on: +) + +--- +struct TimeOfDay { +// ... + void increment(in Duration duration) { + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + } +// ... +} +// ... + auto start = TimeOfDay(5, 30); + start.increment(Duration(30)); // 'start' gets modified +--- + +$(H5 $(IX const, member function) $(C const) member functions ) + +$(P +Some member functions do not make any modifications to the object that they are called on. An example of such a function is $(C toString()): +) + +--- +struct TimeOfDay { +// ... + string toString() { + return format("%02s:%02s", hour, minute); + } +// ... +} +--- + +$(P +Since the whole purpose of $(C toString()) is to represent the object in string format anyway, it should not modify the object. +) + +$(P +The fact that a member function does not modify the object is declared by the $(C const) keyword after the parameter list: +) + +--- +struct TimeOfDay { +// ... + string toString() $(HILITE const) { + return format("%02s:%02s", hour, minute); + } +} +--- + +$(P +That $(C const) guarantees that the object itself is not going to be modified by the member function. As a consequence, $(C toString()) member function is allowed to be called even on $(C immutable) objects. Otherwise, the struct's $(C toString()) would not be called: +) + +--- +struct TimeOfDay { +// ... + // Inferior design: Not marked as 'const' + string toString() { + return format("%02s:%02s", hour, minute); + } +} +// ... + $(HILITE immutable) start = TimeOfDay(5, 30); + writeln(start); // TimeOfDay.toString() is not called! +--- + +$(P +The output is not the expected $(C 05:30), indicating that a generic function gets called instead of $(C TimeOfDay.toString): +) + +$(SHELL +immutable(TimeOfDay)(5, 30) +) + +$(P +Further, calling $(C toString()) on an $(C immutable) object explicitly would cause a compilation error: +) + +--- + auto s = start.toString(); $(DERLEME_HATASI) +--- + +$(P +Accordingly, the $(C toString()) functions that we have defined in the previous chapter have all been designed incorrectly; they should have been marked as $(C const). +) + +$(P $(I $(B Note:) The $(C const) keyword can be specified before the definition of the function as well:) +) + +--- + // The same as above + $(HILITE const) string toString() { + return format("%02s:%02s", hour, minute); + } +--- + +$(P $(I Since this version may give the incorrect impression that the $(C const) is a part of the return type, I recommend that you specify it after the parameter list.) +) + +$(H5 $(IX inout, member function) $(C inout) member functions) + +$(P +As we have seen in $(LINK2 /ders/d.en/function_parameters.html, the Function Parameters chapter), $(C inout) transfers the mutability of a parameter to the return type. +) + +$(P +Similarly, an $(C inout) member function transfers the mutability of the $(I object) to the function's return type: +) + +--- +import std.stdio; + +struct Container { + int[] elements; + + $(HILITE inout)(int)[] firstPart(size_t n) $(HILITE inout) { + return elements[0 .. n]; + } +} + +void main() { + { + // An immutable container + auto container = $(HILITE immutable)(Container)([ 1, 2, 3 ]); + auto slice = container.firstPart(2); + writeln(typeof(slice).stringof); + } + { + // A const container + auto container = $(HILITE const)(Container)([ 1, 2, 3 ]); + auto slice = container.firstPart(2); + writeln(typeof(slice).stringof); + } + { + // A mutable container + auto container = Container([ 1, 2, 3 ]); + auto slice = container.firstPart(2); + writeln(typeof(slice).stringof); + } +} +--- + +$(P +The three slices that are returned by the three objects of different mutability are consistent with the objects that returned them: +) + +$(SHELL +$(HILITE immutable)(int)[] +$(HILITE const)(int)[] +int[] +) + +$(P +Because it must be called on $(C const) and $(C immutable) objects as well, an $(C inout) member function is compiled as if it were $(C const). +) + +$(H5 How to use) + +$(UL + +$(LI +To give the guarantee that a parameter is not modified by the function, mark that parameter as $(C in), $(C const), or $(C const ref). +) + +$(LI +Mark member functions that do not modify the object as $(C const): + +--- +struct TimeOfDay { +// ... + string toString() $(HILITE const) { + return format("%02s:%02s", hour, minute); + } +} +--- + +$(P +This would make the struct (or class) more useful by removing an unnecessary limitation. The examples in the rest of the book will observe this guideline. +) + +) + +) + +Macros: + SUBTITLE=const ref Parameters and const Member Functions + + DESCRIPTION=The const ref parameters and the const member functions of the D programming language + + KEYWORDS=d programming lesson book tutorial const member functions diff --git a/target/contracts.cozum.d b/target/contracts.cozum.d new file mode 100644 index 0000000..ff22320 --- /dev/null +++ b/target/contracts.cozum.d @@ -0,0 +1,77 @@ +Ddoc + +$(COZUM_BOLUMU Contract Programming) + +$(P +The $(C unittest) block can be implemented trivially by copying the checks that are already written in $(C main()). The only addition below is the test for the case when the second team wins: +) + +--- +int addPoints(in int goals1, + in int goals2, + ref int points1, + ref int points2) +in { + assert(goals1 >= 0); + assert(goals2 >= 0); + assert(points1 >= 0); + assert(points2 >= 0); + +} out (result) { + assert((result >= 0) && (result <= 2)); + +} body { + int winner; + + if (goals1 > goals2) { + points1 += 3; + winner = 1; + + } else if (goals1 < goals2) { + points2 += 3; + winner = 2; + + } else { + ++points1; + ++points2; + winner = 0; + } + + return winner; +} + +unittest { + int points1 = 10; + int points2 = 7; + int winner; + + // First team wins + winner = addPoints(3, 1, points1, points2); + assert(points1 == 13); + assert(points2 == 7); + assert(winner == 1); + + // Draw + winner = addPoints(2, 2, points1, points2); + assert(points1 == 14); + assert(points2 == 8); + assert(winner == 0); + + // Second team wins + winner = addPoints(0, 1, points1, points2); + assert(points1 == 14); + assert(points2 == 11); + assert(winner == 2); +} + +void main() { + // ... +} +--- + +Macros: + SUBTITLE=Contract Programming + + DESCRIPTION=The exercise solutions for the Contract Programming chapter, explaining the features of D that help with program correctness. + + KEYWORDS=programming in d tutorial unit testing diff --git a/target/contracts.d b/target/contracts.d new file mode 100644 index 0000000..a439c59 --- /dev/null +++ b/target/contracts.d @@ -0,0 +1,392 @@ +Ddoc + +$(DERS_BOLUMU $(IX contract programming) Contract Programming) + +$(P +Contract programming is a software design approach that treats parts of software as individual entities that provide services to each other. This approach realizes that software can work according to its specification as long as the provider and the consumer of the service both obey a $(I contract). +) + +$(P +D's contract programming features involve functions as the units of software services. Like in unit testing, contract programming is also based on $(C assert) checks. +) + +$(P +Contract programming in D is implemented by three types of code blocks: +) + +$(UL +$(LI Function $(C in) blocks) +$(LI Function $(C out) blocks) +$(LI Struct and class $(C invariant) blocks) +) + +$(P +We will see $(C invariant) blocks and $(I contract inheritance) in $(LINK2 /ders/d.en/invariant.html, a later chapter) after covering structs and classes. +) + +$(H5 $(IX in, contract) $(IX precondition) $(C in) blocks for preconditions) + +$(P +Correct execution of functions usually depend on whether the values of their parameters are valid. For example, a square root function may require that its parameter cannot be negative. A function that deals with dates may require that the number of the month must be between 1 and 12. Such requirements of a function are called its $(I preconditions). +) + +$(P +We have already seen such condition checks in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter). Conditions on parameter values can be enforced by $(C assert) checks within function definitions: +) + +--- +string timeToString(in int hour, in int minute) { + assert((hour >= 0) && (hour <= 23)); + assert((minute >= 0) && (minute <= 59)); + + return format("%02s:%02s", hour, minute); +} +--- + +$(P +$(IX body) In contract programming, the same checks are written inside the $(C in) blocks of functions. When an $(C in) or $(C out) block is used, the actual body of the function must be specified as a $(C body) block: +) + +--- +import std.stdio; +import std.string; + +string timeToString(in int hour, in int minute) +$(HILITE in) { + assert((hour >= 0) && (hour <= 23)); + assert((minute >= 0) && (minute <= 59)); + +} $(HILITE body) { + return format("%02s:%02s", hour, minute); +} + +void main() { + writeln(timeToString(12, 34)); +} +--- + +$(P +A benefit of an $(C in) block is that all of the preconditions can be kept together and separate from the actual body of the function. This way, the function body would be free of $(C assert) checks about the preconditions. As needed, it is still possible and advisable to have other $(C assert) checks inside the function body as unrelated checks that guard against potential programming errors in the function body. +) + +$(P +The code that is inside the $(C in) block is executed automatically every time the function is called. The actual execution of the function starts only if all of the $(C assert) checks inside the $(C in) block pass. This prevents executing the function with invalid preconditions and as a consequence, avoids producing incorrect results. +) + +$(P +An $(C assert) check that fails inside the $(C in) block indicates that the contract has been violated by the caller. +) + +$(H5 $(IX out, contract) $(IX postcondition) $(C out) blocks for postconditions) + +$(P +The other side of the contract involves guarantees that the function provides. Such guarantees are called the function's $(I postconditions). An example of a function with a postcondition would be a function that returns the number of days in February: The function can guarantee that the returned value would always be either 28 or 29. +) + +$(P +The postconditions are checked inside the $(C out) blocks of functions. +) + +$(P +Because the value that a function returns by the $(C return) statement need not be defined as a variable inside the function, there is usually no name to refer to the return value. This can be seen as a problem because the $(C assert) checks inside the $(C out) block cannot refer to the returned variable by name. +) + +$(P +D solves this problem by providing a way of naming the return value right after the $(C out) keyword. That name represents the very value that the function is in the process of returning: +) + +--- +int daysInFebruary(in int year) +$(HILITE out (result)) { + assert((result == 28) || (result == 29)); + +} body { + return isLeapYear(year) ? 29 : 28; +} +--- + +$(P +Although $(C result) is a reasonable name for the returned value, other valid names may also be used. +) + +$(P +Some functions do not have return values or the return value need not be checked. In that case the $(C out) block does not specify a name: +) + +--- +out { + // ... +} +--- + +$(P +Similar to $(C in) blocks, the $(C out) blocks are executed automatically after the body of the function is executed. +) + +$(P +An $(C assert) check that fails inside the $(C out) block indicates that the contract has been violated by the function. +) + +$(P +As it has been obvious, $(C in) and $(C out) blocks are optional. Considering the $(C unittest) blocks as well, which are also optional, D functions may consist of up to four blocks of code: +) + +$(UL +$(LI $(C in): Optional) + +$(LI $(C out): Optional) + +$(LI $(C body): Mandatory but the $(C body) keyword may be skipped if no $(C in) or $(C out) block is defined.) + +$(LI $(C unittest): Optional and technically not a part of a function's definition but commonly defined right after the function.) +) + +$(P +Here is an example that uses all of these blocks: +) + +--- +import std.stdio; + +/* Distributes the sum between two variables. + * + * Distributes to the first variable first, but never gives + * more than 7 to it. The rest of the sum is distributed to + * the second variable. */ +void distribute(in int sum, out int first, out int second) +in { + assert(sum >= 0); + +} out { + assert(sum == (first + second)); + +} body { + first = (sum >= 7) ? 7 : sum; + second = sum - first; +} + +unittest { + int first; + int second; + + // Both must be 0 if the sum is 0 + distribute(0, first, second); + assert(first == 0); + assert(second == 0); + + // If the sum is less than 7, then all of it must be given + // to first + distribute(3, first, second); + assert(first == 3); + assert(second == 0); + + // Testing a boundary condition + distribute(7, first, second); + assert(first == 7); + assert(second == 0); + + // If the sum is more than 7, then the first must get 7 + // and the rest must be given to second + distribute(8, first, second); + assert(first == 7); + assert(second == 1); + + // A random large value + distribute(1_000_007, first, second); + assert(first == 7); + assert(second == 1_000_000); +} + +void main() { + int first; + int second; + + distribute(123, first, second); + writeln("first: ", first, " second: ", second); +} +--- + +$(P +The program can be compiled and run on the terminal by the following commands: +) + +$(SHELL +$ dmd deneme.d -w -unittest +$ ./deneme +$(SHELL_OBSERVED first: 7 second: 116) +) + +$(P +Although the actual work of the function consists of only two lines, there are a total of 19 nontrivial lines that support its functionality. It may be argued that so much extra code is too much for such a short function. However, bugs are never intentional. The programmer always writes code that is $(I expected to work correctly), which commonly ends up containing various types of bugs. +) + +$(P +When expectations are laid out explicitly by unit tests and contracts, functions that are initially correct have a greater chance of staying correct. I recommend that you take full advantage of any feature that improves program correctness. Both unit tests and contracts are effective tools toward that goal. They help reduce time spent for debugging, effectively increasing time spent for actually writing code. +) + +$(H5 $(IX -release, compiler switch) Disabling contract programming) + +$(P +Contrary to unit testing, contract programming features are enabled by default. The $(C ‑release) compiler switch disables contract programming: +) + +$(SHELL +dmd deneme.d -w -release +) + +$(P +When the program is compiled with the $(C ‑release) switch, the contents of $(C in), $(C out), and $(C invariant) blocks are ignored. +) + +$(H5 $(IX in vs. enforce) $(IX enforce vs. in) $(IX assert vs. enforce) $(IX enforce vs. assert) $(C in) blocks versus $(C enforce) checks) + +$(P +We have seen in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter) that sometimes it is difficult to decide whether to use $(C assert) or $(C enforce) checks. Similarly, sometimes it is difficult to decide whether to use $(C assert) checks within $(C in) blocks versus $(C enforce) checks within function bodies. +) + +$(P +The fact that it is possible to disable contract programming is an indication that contract programming is for protecting against programmer errors. For that reason, the decision here should be based on the same guidelines that we saw in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter): +) + +$(UL + +$(LI +If the check is guarding against a coding error, then it should be in the $(C in) block. For example, if the function is called only from other parts of the program, likely to help with achieving a functionality of it, then the parameter values are entirely the responsibility of the programmer. For that reason, the preconditions of such a function should be checked in its $(C in) block. +) + +$(LI +If the function cannot achieve some task for any other reason, including invalid parameter values, then it must throw an exception, conveniently by $(C enforce). + +$(P +To see an example of this, let's define a function that returns a slice of the middle of another slice. Let's assume that this function is for the consumption of the users of the module, as opposed to being an internal function used by the module itself. Since the users of this module can call this function by various and potentially invalid parameter values, it would be appropriate to check the parameter values every time the function is called. It would be insufficient to only check them at program development time, after which contracts can be disabled by $(C ‑release). +) + +$(P +For that reason, the following function validates its parameters by calling $(C enforce) in the function body instead of an $(C assert) check in the $(C in) block: +) + +--- +import std.exception; + +inout(int)[] middle(inout(int)[] originalSlice, size_t width) +out (result) { + assert(result.length == width); + +} body { + $(HILITE enforce)(originalSlice.length >= width); + + immutable start = (originalSlice.length - width) / 2; + immutable end = start + width; + + return originalSlice[start .. end]; +} + +unittest { + auto slice = [1, 2, 3, 4, 5]; + + assert(middle(slice, 3) == [2, 3, 4]); + assert(middle(slice, 2) == [2, 3]); + assert(middle(slice, 5) == slice); +} + +void main() { +} +--- + +$(P +There isn't a similar problem with the $(C out) blocks. Since the return value of every function is the responsibility of the programmer, postconditions must always be checked in the $(C out) blocks. The function above follows this guideline. +) + +) + +$(LI +Another criterion to consider when deciding between $(C in) blocks versus $(C enforce) is to consider whether the condition is recoverable. If it is recoverable by the higher layers of code, then it may be more appropriate to throw an exception, conveniently by $(C enforce). +) + +) + +$(PROBLEM_TEK + +$(P +Write a program that increases the total points of two football (soccer) teams according to the result of a game. +) + +$(P +The first two parameters of this function are the goals that each team has scored. The other two parameters are the points of each team before the game. This function should adjust the points of the teams according to the goals that they have scored. As a reminder, the winner takes 3 points and the loser takes no point. In the event of a draw, both teams get 1 point each. +) + +$(P +Additionally, the function should indicate which team has been the winner: 1 if the first team has won, 2 if the second team has won, and 0 if the game has ended in a draw. +) + +$(P +Start with the following program and fill in the four blocks of the function appropriately. Do not remove the $(C assert) checks in $(C main()); they demonstrate how this function is expected to work. +) + +--- +int addPoints(in int goals1, + in int goals2, + ref int points1, + ref int points2) +in { + // ... + +} out (result) { + // ... + +} body { + int winner; + + // ... + + return winner; +} + +unittest { + // ... +} + +void main() { + int points1 = 10; + int points2 = 7; + int winner; + + winner = addPoints(3, 1, points1, points2); + assert(points1 == 13); + assert(points2 == 7); + assert(winner == 1); + + winner = addPoints(2, 2, points1, points2); + assert(points1 == 14); + assert(points2 == 8); + assert(winner == 0); +} +--- + +$(P $(I $(B Note:) It may be better to return an $(C enum) value from this function:) +) + +--- +enum GameResult { + draw, firstWon, secondWon +} + +$(HILITE GameResult) addPoints(in int goals1, + in int goals2, + ref int points1, + ref int points2) +// ... +--- + +$(P +I chose to return an $(C int) for this exercise, so that the return value can be checked against the valid values of 0, 1, and 2. +) + +) + +Macros: + SUBTITLE=Contract Programming + + DESCRIPTION=The contract programming features of D that help with program correctness. + + KEYWORDS=d programming language tutorial book in out dbc diff --git a/target/copyright.d b/target/copyright.d new file mode 100644 index 0000000..9cd52e6 --- /dev/null +++ b/target/copyright.d @@ -0,0 +1,106 @@ +Ddoc + +
+ +$(P +Programming in D, First Edition +) + +$(BR) + +$(P +Revision: $(LINK2 https://bitbucket.org/acehreli/ddili, ) +) + +$(BR) + +$(P +The most recent electronic versions of this book are available $(LINK2 http://ddili.org/ders/d.en, online). +) + +$(BR) +$(BR) + +$(P +Copyleft (ɔ) 2009-2017 Ali Çehreli +) + +$(BR) + +$(P +Creative Commons License +) +$(BR) +

+This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. +

+ +$(BR) +$(BR) + +$(P +Edited by $(LINK2 http://www.luismarques.eu, Luís Marques) +) + +$(BR) + +$(P +Cover design by $(LINK2 http://izgiyapici.com, İzgi Yapıcı) +) + +$(BR) + +$(P +Cover illustration by $(LINK2 mailto:sarah@reeceweb.com, Sarah Reece) +) + +$(BR) + +$(P +Published by $(LINK2 mailto:acehreli@yahoo.com, Ali Çehreli) +) + +$(BR) +$(BR) + +$(P +Fonts: +) +
    +
  • +Andada by Carolina Giovagnoli for Huerta Tipográfica +
  • +
  • +Open Sans by Steve Matteson +
  • +
  • +DejaVu Mono by DejaVu Fonts +
  • +
+ +$(BR) +$(P +PDF version is generated with Prince XML +) +$(P +Other ebook versions are generated with Calibre +) +$(BR) +$(P +ISBNs: +) +
    +
  • 978-0-692-59943-3 hardcover by IngramSpark
  • +
  • 978-0-692-52957-7 paperback by IngramSpark
  • +
  • 978-1-515-07460-1 paperback by CreateSpace
  • +
  • 978-1-519-95441-1 ePUB by Draft2Digital
  • +
+ +
+ +Macros: + SUBTITLE=Copyleft + + DESCRIPTION=The copyleft page of Programming in D + + KEYWORDS=copyright diff --git a/target/destroy.d b/target/destroy.d new file mode 100644 index 0000000..9a39c66 --- /dev/null +++ b/target/destroy.d @@ -0,0 +1,468 @@ +Ddoc + +$(DERS_BOLUMU $(CH4 destroy) and $(CH4 scoped)) + +$(P +We have seen the lifetimes of objects in the $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations chapter). +) + +$(P +In later chapters, we have seen that the objects are prepared for use in the constructor, which is called $(C this()); and the final operations of objects are applied in the destructor, which is called $(C ~this()). +) + +$(P +For structs and other value types, the destructor is executed at the time when the lifetime of an object ends. For classes and other reference types, it is executed by the garbage collector some time in the future. The important distinction is that the destructor of a class object is not executed when its lifetime ends. +) + +$(P +System resources are commonly returned back to the system in destructors. For example, $(C std.stdio.File) returns the file resource back to the operating system in its destructor. As it is not certain when the destructor of a class object will be called, the system resources that it holds may not be returned until too late when other objects cannot get a hold of the same resource. +) + +$(H5 An example of calling destructors late) + +$(P +Let's define a class to see the effects of executing class destructors late. The following constructor increments a $(C static) counter, and the destructor decrements it. As you remember, there is only one of each $(C static) member, which is shared by all of the objects of a type. Such a counter would indicate the number of objects that are yet to be destroyed. +) + +--- +$(CODE_NAME LifetimeObserved)class LifetimeObserved { + int[] array; // ← Belongs to each object + + static size_t counter; // ← Shared by all objects + + this() { + /* We are using a relatively large array to make each + * object consume a large amount of memory. Hopefully + * this will make the garbage collector call object + * destructors more frequently to open up space for + * more objects. */ + array.length = 30_000; + + /* Increment the counter for this object that is being + * constructed. */ + ++counter; + } + + ~this() { + /* Decrement the counter for this object that is being + * destroyed. */ + --counter; + } +} +--- + +$(P +The following program constructs objects of that class inside a loop: +) + +--- +$(CODE_XREF LifetimeObserved)import std.stdio; + +void main() { + foreach (i; 0 .. 20) { + auto variable = new LifetimeObserved; // ← start + write(LifetimeObserved.counter, ' '); + } // ← end + + writeln(); +} +--- + +$(P +The lifetime of each $(C LifetimeObserved) object is in fact very short: Its life starts when it is constructed by the $(C new) keyword and ends at the closing curly bracket of the $(C foreach) loop. Each object then becomes the responsibility of the garbage collector. The $(COMMENT start) and $(COMMENT end) comments indicate the start and end of the lifetimes. +) + +$(P +Even though there is up to one object alive at a given time, the value of the counter indicates that the destructor is not executed when the lifetime ends: +) + +$(SHELL +1 2 3 4 5 6 7 8 2 3 4 5 6 7 8 2 3 4 5 6 +) + +$(P +According to that output, the memory sweep algorithm of the garbage collector has delayed executing the destructor for up to 8 objects. ($(I Note: The output may be different depending on the garbage collection algorithm, available memory, and other factors.)) +) + +$(H5 $(IX destroy) $(IX destructor, execution) $(C destroy()) to execute the destructor) + +$(P +$(C destroy()) executes the destructor for an object: +) + +--- +$(CODE_XREF LifetimeObserved)void main() { + foreach (i; 0 .. 20) { + auto variable = new LifetimeObserved; + write(LifetimeObserved.counter, ' '); + $(HILITE destroy(variable)); + } + + writeln(); +} +--- + +$(P +Like before, the value of $(C LifetimeObserved.counter) is incremented by the constructor as a result of $(C new), and becomes 1. This time, right after it gets printed, $(C destroy()) executes the destructor for the object and the value of the counter is decremented again down to zero. For that reason, this time its value is always 1: +) + +$(SHELL +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +) + +$(P +Once destroyed, the object should be considered to be in an invalid state and must not be used anymore: +) + +--- + destroy(variable); + // ... + // Warning: Using a potentially invalid object + writeln(variable.array); +--- + +$(P +Although $(C destroy()) is primarily for reference types, it can also be called on $(C struct) objects to destroy them before the end of their normal lifetimes. +) + +$(H5 When to use) + +$(P +As has been seen in the previous example, $(C destroy()) is used when resources need to be released at a specific time without relying on the garbage collector. +) + +$(H5 Example) + +$(P +We had designed an $(C XmlElement) struct in the $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions chapter). That struct was being used for printing XML elements in the format $(C <tag>value</tag>). Printing the closing tag has been the responsibility of the destructor: +) + +--- +struct XmlElement { + // ... + + ~this() { + writeln(indentation, "'); + } +} +--- + +$(P +The following output was produced by a program that used that struct. This time, I am replacing the word "class" with "course" to avoid confusing it with the $(C class) keyword: +) + +$(SHELL +<courses> + <course0> + <grade> + 72 + </grade> $(SHELL_NOTE The closing tags appear on correct lines) + <grade> + 97 + </grade> $(SHELL_NOTE) + <grade> + 90 + </grade> $(SHELL_NOTE) + </course0> $(SHELL_NOTE) + <course1> + <grade> + 77 + </grade> $(SHELL_NOTE) + <grade> + 87 + </grade> $(SHELL_NOTE) + <grade> + 56 + </grade> $(SHELL_NOTE) + </course1> $(SHELL_NOTE) +</courses> $(SHELL_NOTE) +) + +$(P +The previous output happens to be correct because $(C XmlElement) is a $(C struct). The desired output is achieved simply by placing the objects in appropriate scopes: +) + +--- +void $(CODE_DONT_TEST)main() { + const $(HILITE courses) = XmlElement("courses", 0); + + foreach (courseId; 0 .. 2) { + const courseTag = "course" ~ to!string(courseId); + const $(HILITE courseElement) = XmlElement(courseTag, 1); + + foreach (i; 0 .. 3) { + const $(HILITE gradeElement) = XmlElement("grade", 2); + const randomGrade = uniform(50, 101); + + writeln(indentationString(3), randomGrade); + + } // ← gradeElement is destroyed + + } // ← courseElement is destroyed + +} // ← courses is destroyed +--- + +$(P +The destructor prints the closing tags as the objects gets destroyed. +) + +$(P +To see how classes behave differently, let's convert $(C XmlElement) to a class: +) + +--- +import std.stdio; +import std.array; +import std.random; +import std.conv; + +string indentationString(in int level) { + return replicate(" ", level * 2); +} + +$(HILITE class) XmlElement { + string name; + string indentation; + + this(in string name, in int level) { + this.name = name; + this.indentation = indentationString(level); + + writeln(indentation, '<', name, '>'); + } + + ~this() { + writeln(indentation, "'); + } +} + +void main() { + const courses = $(HILITE new) XmlElement("courses", 0); + + foreach (courseId; 0 .. 2) { + const courseTag = "course" ~ to!string(courseId); + const courseElement = $(HILITE new) XmlElement(courseTag, 1); + + foreach (i; 0 .. 3) { + const gradeElement = $(HILITE new) XmlElement("grade", 2); + const randomGrade = uniform(50, 101); + + writeln(indentationString(3), randomGrade); + } + } +} +--- + +$(P +As the responsibility of calling the destructors are now left to the garbage collector, the program does not produce the desired output: +) + +$(SHELL_SMALL +<courses> + <course0> + <grade> + 57 + <grade> + 98 + <grade> + 87 + <course1> + <grade> + 84 + <grade> + 60 + <grade> + 99 + </grade> $(SHELL_NOTE The closing tags appear at the end) + </grade> $(SHELL_NOTE) + </grade> $(SHELL_NOTE) + </course1> $(SHELL_NOTE) + </grade> $(SHELL_NOTE) + </grade> $(SHELL_NOTE) + </grade> $(SHELL_NOTE) + </course0> $(SHELL_NOTE) +</courses> $(SHELL_NOTE) +) + +$(P +The destructor is still executed for every object but this time at the end when the program is exiting. ($(I Note: The garbage collector does not guarantee that the destructor will be called for every object. In reality, it is possible that there are no closing tags printed at all.)) +) + +$(P +$(C destroy()) ensures that the destructor is called at desired points in the program: +) + +--- +void $(CODE_DONT_TEST)main() { + const courses = new XmlElement("courses", 0); + + foreach (courseId; 0 .. 2) { + const courseTag = "course" ~ to!string(courseId); + const courseElement = new XmlElement(courseTag, 1); + + foreach (i; 0 .. 3) { + const gradeElement = new XmlElement("grade", 2); + const randomGrade = uniform(50, 101); + + writeln(indentationString(3), randomGrade); + + $(HILITE destroy(gradeElement)); + } + + $(HILITE destroy(courseElement)); + } + + $(HILITE destroy(courses)); +} +--- + +$(P +With those changes, the output of the code now matches the output of the code that use structs: +) + +$(SHELL +<courses> + <course0> + <grade> + 66 + </grade> $(SHELL_NOTE The closing tags appear on correct lines) + <grade> + 75 + </grade> $(SHELL_NOTE) + <grade> + 68 + </grade> $(SHELL_NOTE) + </course0> $(SHELL_NOTE) + <course1> + <grade> + 73 + </grade> $(SHELL_NOTE) + <grade> + 62 + </grade> $(SHELL_NOTE) + <grade> + 100 + </grade> $(SHELL_NOTE) + </course1> $(SHELL_NOTE) +</courses> $(SHELL_NOTE) +) + +$(H5 $(IX scoped) $(C scoped()) to call the destructor automatically) + +$(P +The program above has a weakness: The scopes may be exited before the $(C destroy()) lines are executed, commonly by thrown exceptions. If the $(C destroy()) lines must be executed even when exceptions are thrown, a solution is to take advantage of $(C scope()) and other features that we saw in the $(LINK2 /ders/d.en/exceptions.html, Exceptions chapter). +) + +$(P +Another solution is to construct class objects by $(C std.typecons.scoped) instead of by the $(C new) keyword. $(C scoped()) wraps the class object inside a $(C struct) and the destructor of that $(C struct) object destroys the class object when itself goes out of scope. +) + +$(P +The effect of $(C scoped()) is to make class objects behave similar to struct objects regarding lifetimes. +) + +$(P +With the following changes, the program produces the expected output as before: +) + +--- +$(HILITE import std.typecons;) +// ... +void $(CODE_DONT_TEST)main() { + const courses = $(HILITE scoped!)XmlElement("courses", 0); + + foreach (courseId; 0 .. 2) { + const courseTag = "course" ~ to!string(courseId); + const courseElement = $(HILITE scoped!)XmlElement(courseTag, 1); + + foreach (i; 0 .. 3) { + const gradeElement = $(HILITE scoped!)XmlElement("grade", 2); + const randomGrade = uniform(50, 101); + + writeln(indentationString(3), randomGrade); + } + } +} +--- + +$(P +Note that there are no $(C destroy()) lines anymore. +) + +$(P +$(IX proxy) $(IX RAII) $(C scoped()) is a function that returns a special $(C struct) object encapsulating the actual $(C class) object. The returned object acts as a proxy to the encapsulated one. (In fact, the type of $(C courses) above is $(C Scoped), not $(C XmlElement).) +) + +$(P +When the destructor of the $(C struct) object is called automatically as its lifetime ends, it calls $(C destroy()) on the $(C class) object that it encapsulates. (This is an application of the $(I Resource Acquisition Is Initialization) (RAII) idiom. $(C scoped()) achieves this by the help of $(LINK2 /ders/d.en/templates.html, templates) and $(LINK2 /ders/d.en/alias_this.html, $(C alias this)), both of which we will see in later chapters.) +) + +$(P +It is desirable for a proxy object to be used as conveniently as possible. In fact, the object that $(C scoped()) returns can be used exactly like the actual $(C class) type. For example, the member functions of the actual type can be called on it: +) + +--- +import std.typecons; + +class C { + void foo() { + } +} + +void main() { + auto p = scoped!C(); + p$(HILITE .foo()); // Proxy object p is being used as type C +} +--- + +$(P +However, that convenience comes with a price: The proxy object may hand out a reference to the actual object right before destroying it. This can happen when the actual $(C class) type is specified explicitly on the left hand-side: +) + +--- + $(HILITE C) c = scoped!C(); $(CODE_NOTE_WRONG BUG) + c.foo(); $(CODE_NOTE_WRONG Accesses a destroyed object) +--- + +$(P +In that definition, $(C c) is not the proxy object; rather, as defined by the programmer, a $(C class) variable referencing the encapsulated object. Unfortunately, the proxy object that is constructed on the right-hand side gets terminated at the end of the expression that constructs it. As a result, using $(C c) in the program would be an error, likely causing a runtime error: +) + +$(SHELL +Segmentation fault +) + +$(P +For that reason, do not define $(C scoped()) variables by the actual type: +) + +--- + $(HILITE C) a = scoped!C(); $(CODE_NOTE_WRONG BUG) + auto b = scoped!C(); $(CODE_NOTE correct) + const c = scoped!C(); $(CODE_NOTE correct) + immutable d = scoped!C(); $(CODE_NOTE correct) +--- + +$(H5 Summary) + +$(UL + +$(LI +$(C destroy()) is for executing the destructor of a class object explicitly. +) + +$(LI +Objects that are constructed by $(C scoped()) are destroyed upon leaving their respective scopes. +) + +$(LI It is a bug to define $(C scoped()) variables by the actual type.) + +) + +Macros: + SUBTITLE=destroy and scoped + + DESCRIPTION=destroy() to call destructors explicitly on class objects and std.typecons.scoped to have the destructors executed on class objects automatically. + + KEYWORDS=d programming language tutorial book destroy scoped diff --git a/target/do_while.cozum.d b/target/do_while.cozum.d new file mode 100644 index 0000000..8220e41 --- /dev/null +++ b/target/do_while.cozum.d @@ -0,0 +1,22 @@ +Ddoc + +$(COZUM_BOLUMU The $(C do-while) Loop) + +$(P +This program is not directly related to the $(C do-while) loop, as any problem that is solved by the $(C do-while) loop can also be solved by the other loop statements. +) + +$(P +The program can guess the number that the user is thinking of by shortening the candidate range from top or bottom according to the user's answers. For example, if its first guess is 50 and the user's reply is that the secret number is greater, the program would then know that the number must be in the range [51,100]. If the program then guesses another number right in the middle of that range, this time the number would be known to be either in the range [51,75] or in the range [76,100]. +) + +$(P +When the size of the range is 1, the program would be sure that it must be the number that the user has guessed. +) + +Macros: + SUBTITLE=The do-while Loop Solutions + + DESCRIPTION=Programming in D exercise solutions: the 'do-while' loop + + KEYWORDS=programming in d tutorial do-while solution diff --git a/target/do_while.d b/target/do_while.d new file mode 100644 index 0000000..6f24127 --- /dev/null +++ b/target/do_while.d @@ -0,0 +1,91 @@ +Ddoc + +$(DERS_BOLUMU $(IX do-while) $(IX loop, do-while) $(CH4 do-while) Loop) + +$(P +In the $(LINK2 /ders/d.en/for.html, $(C for) Loop chapter) we saw the steps in which the $(LINK2 /ders/d.en/while.html, $(C while) loop) is executed: +) + +$(MONO +preparation + +condition check +actual work +iteration + +condition check +actual work +iteration + +... +) + +$(P +The $(C do-while) loop is very similar to the $(C while) loop. The difference is that the $(I condition check) is performed at the end of each iteration of the $(C do-while) loop, so that the $(I actual work) is performed at least once: +) + +$(MONO +preparation + +actual work +iteration +condition check $(SHELL_NOTE at the end of the iteration) + +actual work +iteration +condition check $(SHELL_NOTE at the end of the iteration) + +... +) + +$(P +For example, $(C do-while) may be more natural in the following program where the user guesses a number, as the user must guess at least once so that the number can be compared: +) + +--- +import std.stdio; +import std.random; + +void main() { + int number = uniform(1, 101); + + writeln("I am thinking of a number between 1 and 100."); + + int guess; + + do { + write("What is your guess? "); + + readf(" %s", &guess); + + if (number < guess) { + write("My number is less than that. "); + + } else if (number > guess) { + write("My number is greater than that. "); + } + + } while (guess != number); + + writeln("Correct!"); +} +--- + +$(P +The function $(C uniform()) that is used in the program is a part of the $(C std.random) module. It returns a random number in the specified range. The way it is used above, the second number is considered to be outside of the range. In other words, $(C uniform()) would not return 101 for that call. +) + +$(PROBLEM_TEK + +$(P +Write a program that plays the same game but have the program do the guessing. If the program is written correctly, it should be able to guess the user's number in at most 7 tries. +) + +) + +Macros: + SUBTITLE=do-while Loop + + DESCRIPTION=The do-while loop of the D programming languageh and comparing it to the while loop + + KEYWORDS=d programming language tutorial book do while loop diff --git a/target/encapsulation.d b/target/encapsulation.d new file mode 100644 index 0000000..b55e100 --- /dev/null +++ b/target/encapsulation.d @@ -0,0 +1,508 @@ +Ddoc + +$(DERS_BOLUMU Encapsulation and Protection Attributes) + +$(P +All of the structs and classes that we have defined so far have all been accessible from the $(I outside). +) + +$(P +Let's consider the following struct: +) + +--- +enum Gender { female, male } + +struct Student { + string name; + Gender gender; +} +--- + +$(P +The members of that struct is freely accessible to the rest of the program: +) + +--- + auto student = Student("Tim", Gender.male); + writefln("%s is a %s student.", student$(HILITE .name), student$(HILITE .gender)); +--- + +$(P +Such freedom is a convenience in programs. For example, the previous line has been useful to produce the following output: +) + +$(SHELL_SMALL +Tim is a male student. +) + +$(P +However, this freedom is also a liability. As an example, let's assume that perhaps by mistake, the name of a student object gets modified in the program: +) + +--- + student.name = "Anna"; +--- + +$(P +That assignment may put the object in an invalid state: +) + +$(SHELL_SMALL +$(HILITE Anna) is a $(HILITE male) student. +) + +$(P +As another example, let's consider a $(C School) class. Let's assume that this class has two member variables that store the numbers of the male and female students separately: +) + +--- +class School { + Student[] students; + size_t femaleCount; + size_t maleCount; + + void add(in Student student) { + students ~= student; + + final switch (student.gender) { + + case Gender.female: + $(HILITE ++femaleCount); + break; + + case Gender.male: + $(HILITE ++maleCount); + break; + } + } + + override string toString() const { + return format("%s female, %s male; total %s students", + femaleCount, maleCount, students.length); + } +} +--- + +$(P +The $(C add()) member function adds students while ensuring that the counts are always correct: +) + +--- + auto school = new School; + school.add(Student("Lindsey", Gender.female)); + school.add(Student("Mark", Gender.male)); + writeln(school); +--- + +$(P +The program produces the following consistent output: +) + +$(SHELL_SMALL +1 female, 1 male; total 2 students +) + +$(P +However, being able to access the members of $(C School) freely would not guarantee that this consistency would always be maintained. Let's consider adding a new element to the $(C students) member, this time directly: +) + +--- + school$(HILITE .students) ~= Student("Nancy", Gender.female); +--- + +$(P +Because the new student has been added to the array directly, without going through the $(C add()) member function, the $(C School) object is now in an inconsistent state: +) + +$(SHELL_SMALL +$(HILITE 1) female, $(HILITE 1) male; total $(HILITE 3) students +) + +$(H5 $(IX encapsulation) Encapsulation) + +$(P +Encapsulation is a programming concept of restricting access to members to avoid problems similar to the one above. +) + +$(P +Another benefit of encapsulation is to eliminate the need to know the implementation details of types. In a sense, encapsulation allows presenting a type as a black box that is used only through its interface. +) + +$(P +Additionally, preventing users from accessing the members directly allows changing the members of a class freely in the future. As long as the functions that define the interface of a class is kept the same, its implementation can be changed freely. +) + +$(P +Encapsulation is not for restricting access to sensitive data like a credit card number or a password, and it cannot be used for that purpose. Encapsulation is a development tool: It allows using and coding types easily and safely. +) + +$(H5 $(IX protection) $(IX access protection) Protection attributes) + +$(P +Protection attributes limit access to members of structs, classes, and modules. There are two ways of specifying protection attributes: +) + +$(UL + +$(LI +At struct or class level to specify the protection of every struct or class member individually. +) + +$(LI +At module level to specify the protection of every feature of a module individually: class, struct, function, enum, etc. +) + +) + +$(P +Protection attributes can be specified by the following keywords. The default attribute is $(C public). +) + +$(UL + +$(LI +$(IX public) +$(C public): Specifies accessibility by any part of the program without any restriction. + +$(P +An example of this is $(C stdout). Merely importing $(C std.stdio) makes $(C stdout) available to every module that imported it. +) + +) + +$(LI +$(IX private) +$(C private): Specifies restricted accessibility. + +$(P +$(C private) class members and module members can only be accessed by the module that defines that member. +) + +$(P +Additionally, $(C private) member functions cannot be overridden by subclasses. +) + +) + +$(LI +$(IX package, protection) +$(C package): Specifies package-level accessibility. + +$(P +A feature that is marked as $(C package) can be accessed by all of the code that is a part of the same package. The $(C package) attribute involves only the inner-most package. +) + +$(P +For example, a $(C package) definition that is inside the $(C animal.vertebrate.cat) module can be accessed by any other module of the $(C vertebrate) package. +) + +$(P +Similar to the $(C private) attribute, $(C package) member functions cannot be overridden by subclasses. +) + +) + +$(LI +$(IX protected) +$(C protected): Specifies accessibility by derived classes. + +$(P +This attribute extends the $(C private) attribute: A $(C protected) member can be accessed not only by the module that defines it, but also by the classes that inherit from the class that defines that $(C protected) member. +) + +) + +) + +$(P +$(IX export) Additionally, the $(C export) attribute specifies accessibility from the outside of the program. +) + +$(H5 Definition) + +$(P +Protection attributes can be specified in three ways. +) + +$(P +When written in front of a single definition, it specifies the protection attribute of that definition only. This is similar to the Java programming language: +) + +--- +private int foo; + +private void bar() { + // ... +} +--- + +$(P +When specified by a colon, it specifies the protection attributes of all of the following definitions until the next specification of a protection attribute. This is similar to the C++ programming language: +) + +--- +private: + // ... + // ... all of the definitions here are private ... + // ... + +protected: + // ... + // ... all of the definitions here are protected ... + // ... +--- + +$(P +When specified for a block, the protection attribute is for all of the definitions that are inside that block: +) + +--- +private { + // ... + // ... all of the definitions here are private ... + // ... +} +--- + +$(H5 Module imports are private by default) + +$(P +A module that is imported by $(C import) is private to the module that imports it. It would not be visible to other modules that import it indirectly. For example, if a $(C school) module imports $(C std.stdio), modules that import $(C school) cannot automatically use the $(C std.stdio) module. +) + +$(P +Let's assume that the $(C school) module starts by the following lines: +) + +--- +module school.school; + +import std.stdio; // imported for this module's own use... + +// ... +--- + +$(P +The following program cannot be compiled because $(C writeln) is not visible to it: +) + +--- +import school.school; + +void main() { + writeln("hello"); $(DERLEME_HATASI) +} +--- + +$(P +$(C std.stdio) must be imported by that module as well: +) + +--- +import school.school; +$(HILITE import std.stdio;) + +void main() { + writeln("hello"); // now compiles +} +--- + +$(P +$(IX public import) $(IX import, public) Sometimes it is desired that a module presents other modules indirectly. For example, it would make sense for a $(C school) module to automatically import a $(C student) module for its users. This is achieved by marking the $(C import) as $(C public): +) + +--- +module school.school; + +$(HILITE public import) school.student; + +// ... +--- + +$(P +With that definition, modules that import $(C school) can use the definitions that are inside the $(C student) module without needing to import it: +) + +--- +import school.school; + +void main() { + auto student = Student("Tim", Gender.male); + + // ... +} +--- + +$(P +Although the program above imports only the $(C school) module, the $(C student.Student) struct is also available to it. +) + +$(H5 When to use encapsulation) + +$(P +Encapsulation avoids problems similar to the one we have seen in the introduction section of this chapter. It is an invaluable tool to ensure that objects are always in consistent states. Encapsulation helps preserve struct and class $(I invariants) by protecting members from direct modifications by the users of the type. +) + +$(P +Encapsulation provides freedom of implementation by abstracting implementations away from user code. Otherwise, if users had direct access to for example $(C School.students), it would be hard to modify the design of the class by changing that array e.g. to an associative array, because this would affect all user code that has been accessing that member. +) + +$(P +Encapsulation is one of the most powerful benefits of object oriented programming. +) + +$(H5 Example) + +$(P +Let's define the $(C Student) struct and the $(C School) class by taking advantage of encapsulation and let's use them in a short test program. +) + +$(P +This example program will consist of three files. As you remember from the previous chapter, being parts of the $(C school) package, two of these files will be under the "school" directory: +) + +$(UL +$(LI "school/student.d": The $(C student) module that defines the $(C Student) struct) +$(LI "school/school.d": The $(C school) module that defines the $(C School) class) +$(LI "deneme.d": A short test program) +) + +$(P +Here is the "school/student.d" file: +) + +--- +module school.student; + +import std.string; +import std.conv; + +enum Gender { female, male } + +struct Student { + $(HILITE package) string name; + $(HILITE package) Gender gender; + + string toString() const { + return format("%s is a %s student.", + name, to!string(gender)); + } +} +--- + +$(P +The members of this struct are marked as $(C package) to enable access only to modules of the same package. We will soon see that $(C School) will be accessing these members directly. (Note that even this should be considered as violating the principle of encapsulation. Still, let's stick with the $(C package) attribute in this example program.) +) + +$(P +The following is the "school/school.d" module that makes use of the previous one: +) + +--- +module school.school; + +$(HILITE public import) school.student; // 1 + +import std.string; + +class School { +$(HILITE private:) // 2 + + Student[] students; + size_t femaleCount; + size_t maleCount; + +$(HILITE public:) // 3 + + void add(in Student student) { + students ~= student; + + final switch (student$(HILITE .gender)) { // 4a + + case Gender.female: + ++femaleCount; + break; + + case Gender.male: + ++maleCount; + break; + } + } + + override string toString() const { + string result = format( + "%s female, %s male; total %s students", + femaleCount, maleCount, students.length); + + foreach (i, student; students) { + result ~= (i == 0) ? ": " : ", "; + result ~= student$(HILITE .name); // 4b + } + + return result; + } +} +--- + +$(OL + +$(LI +$(C school.student) is being imported publicly so that the users of $(C school.school) will not need to import that module explicitly. In a sense, the $(C student) module is made available by the $(C school) module. +) + +$(LI +All of the member variables of $(C School) are marked as private. This is important to help protect the consistency of the member variables of this class. +) + +$(LI +For this class to be useful, it must present some member functions. $(C add()) and $(C toString()) are made available to the users of this class. +) + +$(LI +As the two member variables of $(C Student) have been marked as $(C package), being a part of the same package, $(C School) can access those variables. +) + +) + +$(P +Finally, the following is a test program that uses those types: +) + +--- +import std.stdio; +import school.school; + +void main() { + auto student = Student("Tim", Gender.male); + writeln(student); + + auto school = new School; + + school.add(Student("Lindsey", Gender.female)); + school.add(Student("Mark", Gender.male)); + school.add(Student("Nancy", Gender.female)); + + writeln(school); +} +--- + +$(P +This program can use $(C Student) and $(C School) only through their public interfaces. It cannot access the member variables of those types. As a result, the objects would always be consistent: +) + +$(SHELL_SMALL +Tim is a male student. +2 female, 1 male; total 3 students: Lindsey, Mark, Nancy +) + +$(P +Note that the program interacts with $(C School) only by its $(C add()) and $(C toString()) functions. As long as the interfaces of these functions are kept the same, changes in their implementations would not affect the program above. +) + +Macros: + SUBTITLE=Encapsulation and Protection Attributes + + DESCRIPTION=Encapsulating data by the protection attributes of D. + + KEYWORDS=d programming lesson book tutorial encapsulation diff --git a/target/enum.cozum.d b/target/enum.cozum.d new file mode 100644 index 0000000..235ff17 --- /dev/null +++ b/target/enum.cozum.d @@ -0,0 +1,94 @@ +Ddoc + +$(COZUM_BOLUMU $(C enum)) + +--- +import std.stdio; +import std.conv; + +enum Operation { exit, add, subtract, multiply, divide } + +void main() { + // Print the supported operations + write("Operations - "); + for (Operation operation; + operation <= Operation.max; + ++operation) { + + writef("%d:%s ", operation, operation); + } + writeln(); + + // Infinite loop until the user wants to exit + while (true) { + write("Operation? "); + + // The input must be read in the actual type (int) of + // the enum + int operationCode; + readf(" %s", &operationCode); + + /* We will start using enum values instead of magic + * constants from this point on. So, the operation code + * that has been read in int must be converted to its + * corresponding enum value. + * + * (Type conversions will be covered in more detail in + * a later chapter.) */ + Operation operation = cast(Operation)operationCode; + + if ((operation < Operation.min) || + (operation > Operation.max)) { + writeln("ERROR: Invalid operation"); + continue; + } + + if (operation == Operation.exit) { + writeln("Goodbye!"); + break; + } + + double first; + double second; + double result; + + write(" First operand? "); + readf(" %s", &first); + + write("Second operand? "); + readf(" %s", &second); + + switch (operation) { + + case Operation.add: + result = first + second; + break; + + case Operation.subtract: + result = first - second; + break; + + case Operation.multiply: + result = first * second; + break; + + case Operation.divide: + result = first / second; + break; + + default: + throw new Exception( + "ERROR: This line should have never been reached."); + } + + writeln(" Result: ", result); + } +} +--- + +Macros: + SUBTITLE=enum Solutions + + DESCRIPTION=Programming in D exercise solutions: enum + + KEYWORDS=programming in d tutorial enum diff --git a/target/enum.d b/target/enum.d new file mode 100644 index 0000000..25c8a82 --- /dev/null +++ b/target/enum.d @@ -0,0 +1,334 @@ +Ddoc + +$(DERS_BOLUMU $(IX enum) $(CH4 enum)) + +$(P +$(C enum) is the feature that enables defining named constant values. +) + +$(H5 $(IX magic constant) Effects of magic constants on code quality) + +$(P +The following code appeared in the $(LINK2 /ders/d.en/arithmetic.cozum.html, exercise solutions) of the Integers and Arithmetic Operations chapter: +) + +--- + if (operation == 1) { + result = first + second; + + } else if (operation == 2) { + result = first - second; + + } else if (operation == 3) { + result = first * second; + + } else if (operation == 4) { + result = first / second; + } +--- + +$(P +The integer literals $(C 1), $(C 2), $(C 3), and $(C 4) in that piece of code are called $(I magic constants). It is not easy to determine what each of those literals means in the program. One must examine the code in each scope to determine that $(C 1) means $(I addition), $(C 2) means $(I subtraction), etc. This task is relatively easy for the code above because all of the scopes contain just a single line. It would be considerably more difficult to decipher the meanings of magic constants in most other programs. +) + +$(P +Magic constants must be avoided because they impair two important qualities of source code: readability and maintainability. +) + +$(P +$(C enum) enables giving names to such constants and, as a consequence, making the code more readable and maintainable. Each condition would be readily understandable if the following $(C enum) constants were used: +) + +--- + if (operation == Operation.add) { + result = first + second; + + } else if (operation == Operation.subtract) { + result = first - second; + + } else if (operation == Operation.multiply) { + result = first * second; + + } else if (operation == Operation.divide) { + result = first / second; + } +--- + +$(P +The $(C enum) type $(C Operation) above that obviates the need for magic constants $(C 1), $(C 2), $(C 3), and $(C 4) can be defined like this: +) + +--- + enum Operation { add = 1, subtract, multiply, divide } +--- + +$(H5 The $(C enum) syntax) + +$(P +The common definition of an $(C enum) is the following: +) + +--- + enum $(I TypeName) { $(I ValueName_1), $(I ValueName_2), /* etc. */ } +--- + +$(P +Sometimes it is necessary to specify the actual type (the $(I base type)) of the values as well: +) + +--- + enum $(I TypeName) $(HILITE : $(I base_type)) { $(I ValueName_1), $(I ValueName_2), /* etc. */ } +--- + +$(P +We will see how this is used in the next section. +) + +$(P +$(I TypeName) defines what the constants collectively mean. All of the member constants of an $(C enum) $(I type) are listed within curly brackets. Here are some examples: +) + +--- + enum HeadsOrTails { heads, tails } + enum Suit { spades, hearts, diamonds, clubs } + enum Fare { regular, child, student, senior } +--- + +$(P +Each set of constants becomes part of a separate type. For example, $(C heads) and $(C tails) become members of the type $(C HeadsOrTails). The new type can be used like other fundamental types when defining variables: +) + +--- + HeadsOrTails result; // default initialized + auto ht = HeadsOrTails.heads; // inferred type +--- + +$(P +As has been seen in the pieces of code above, the members of $(C enum) types are always specified by the name of their $(C enum) type: +) + +--- + if (result == $(HILITE HeadsOrTails.)heads) { + // ... + } +--- + +$(H5 Actual values and base types) + +$(P +The member constants of $(C enum) types are by default implemented as $(C int) values. In other words, although on the surface they appear as just names such as $(C heads) and $(C tails), they also have numerical values. ($(I $(B Note:) It is possible to choose a type other than $(C int) when needed.)). +) + +$(P +Unless explicitly specified by the programmer, the numerical values of $(C enum) members start at $(C 0) and are incremented by one for each member. For example, the two members of the $(C HeadsOrTails) $(C enum) have the numerical values 0 and 1: +) + +--- + writeln("heads is 0: ", (HeadsOrTails.heads == 0)); + writeln("tails is 1: ", (HeadsOrTails.tails == 1)); +--- + +$(P +The output: +) + +$(SHELL +heads is 0: true +tails is 1: true +) + +$(P +It is possible to manually (re)set the values at any point in the $(C enum). That was the case when we specified the value of $(C Operation.add) as 1 above. The following example manually sets a new count twice: +) + +--- + enum Test { a, b, c, ç $(HILITE = 100), d, e, f $(HILITE = 222), g, ğ } + writefln("%d %d %d", Test.b, Test.ç, Test.ğ); +--- + +$(P +The output: +) + +$(SHELL +1 100 224 +) + +$(P +If $(C int) is not suitable as the base type of the $(C enum) values, the base type can be specified explicitly after the name of the $(C enum): +) + +--- + enum NaturalConstant $(HILITE : double) { pi = 3.14, e = 2.72 } + enum TemperatureUnit $(HILITE : string) { C = "Celsius", F = "Fahrenheit" } +--- + +$(H5 $(C enum) values that are not of an $(C enum) type) + +$(P +We have discussed that it is important to avoid magic constants and instead to take advantage of the $(C enum) feature. +) + +$(P +However, sometimes it may not be natural to come up with $(C enum) type names just to use named constants. Let's assume that a named constant is needed to represent the number of seconds per day. It should not be necessary to also define an $(C enum) $(I type) to contain this constant value. All that is needed is a constant value that can be referred to by its name. In such cases, the type name of the $(C enum) and the curly brackets are omitted: +) + +--- + enum secondsPerDay = 60 * 60 * 24; +--- + +$(P +The type of the constant can be specified explicitly, which would be required if the type cannot be inferred from the right hand side: +) + +--- + enum $(HILITE int) secondsPerDay = 60 * 60 * 24; +--- + +$(P +Since there is no $(C enum) type to refer to, such named constants can be used in code simply by their names: +) + +--- + totalSeconds = totalDays * $(HILITE secondsPerDay); +--- + +$(P +$(C enum) can be used for defining named constants of other types as well. For example, the type of the following constant would be $(C string): +) + +--- + enum fileName = "list.txt"; +--- + +$(P +$(IX manifest constant) $(IX constant, manifest) Such constants are $(LINK2 /ders/d.en/lvalue_rvalue.html, $(I rvalues)) and they are called $(I manifest constants). +) + +$(P +It is possible to create manifest constants of arrays an associative arrays as well. However, as we will see later in $(LINK2 /ders/d.en/const_and_immutable.html, the Immutability chapter), $(C enum) arrays and associative arrays may have hidden costs. +) + +$(H5 Properties) + +$(P +The $(C .min) and $(C .max) properties are the minimum and maximum values of an $(C enum) type. When the values of the $(C enum) type are consecutive, they can be iterated over in a $(C for) loop within these limits: +) + +--- + enum Suit { spades, hearts, diamonds, clubs } + + for (auto suit = Suit$(HILITE .min); suit <= Suit$(HILITE .max); ++suit) { + writefln("%s: %d", suit, suit); + } +--- + +$(P +Format specifiers $(STRING "%s") and $(STRING "%d") produce different outputs: +) + +$(SHELL +spades: 0 +hearts: 1 +diamonds: 2 +clubs: 3 +) + +$(P +Note that a $(C foreach) loop over that range would leave the $(C .max) value out of the iteration: +) + +--- + foreach (suit; Suit.min .. Suit.max) { + writefln("%s: %d", suit, suit); + } +--- + +$(P +The output: +) + +$(SHELL +spades: 0 +hearts: 1 +diamonds: 2 + $(SHELL_NOTE_WRONG clubs is missing) +) + +$(P +$(IX EnumMembers, std.traits) For that reason, a correct way of iterating over all values of an $(C enum) is using the $(C EnumMembers) template from the $(C std.traits) module: +) + +--- +import std.traits; +// ... + foreach (suit; $(HILITE EnumMembers!Suit)) { + writefln("%s: %d", suit, suit); + } +--- + +$(P +$(I $(B Note:) The $(C !) character above is for template instantiations, which will be covered in $(LINK2 /ders/d.en/templates.html, a later chapter).) +) + +$(SHELL +spades: 0 +hearts: 1 +diamonds: 2 +clubs: 3 $(SHELL_NOTE clubs is present) +) + +$(H5 Converting from the base type) + +$(P +As we saw in the formatted outputs above, an $(C enum) value can automatically be converted to its base type (e.g. to $(C int)). The reverse conversion is not automatic: +) + +--- + Suit suit = 1; $(DERLEME_HATASI) +--- + +$(P +One reason for this is to avoid ending up with invalid $(C enum) values: +) + +--- + suit = 100; // ← would be an invalid enum value +--- + +$(P +The values that are known to correspond to valid $(C enum) values of a particular $(C enum) type can still be converted to that type by an explicit $(I type cast): +) + +--- + suit = cast(Suit)1; // now hearts +--- + +$(P +It would be the programmer's responsibility to ensure the validity of the values when an explicit cast is used. We will see type casting in $(LINK2 /ders/d.en/cast.html, a later chapter). +) + +$(PROBLEM_TEK + +$(P +Modify the calculator program from the exercises of the $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations chapter) to have the user select the arithmetic operation from a menu. +) + +$(P +This program should be different from the previous one in at least the following areas: +) + +$(UL +$(LI Use $(C enum) values, not magic constants.) +$(LI Use $(C double) instead of $(C int).) +$(LI Use a $(C switch) statement instead of an "if, else if, else" chain.) +) + +) + +Macros: + SUBTITLE=enum + + DESCRIPTION=The enum feature and how it is used to define named constant values. + + KEYWORDS=d programming language tutorial book enum diff --git a/target/exceptions.d b/target/exceptions.d new file mode 100644 index 0000000..4198347 --- /dev/null +++ b/target/exceptions.d @@ -0,0 +1,995 @@ +Ddoc + +$(DERS_BOLUMU $(IX exception) Exceptions) + +$(P +Unexpected situations are parts of programs: user mistakes, programming errors, changes in the program environment, etc. Programs must be written in ways to avoid producing incorrect results when faced with such $(I exceptional) conditions. +) + +$(P +Some of these conditions may be severe enough to stop the execution of the program. For example, a required piece of information may be missing or invalid, or a device may not be functioning correctly. The exception handling mechanism of D helps with stopping program execution when necessary, and to recover from the unexpected situations when possible. +) + +$(P +As an example of a severe condition, we can think of passing an unknown operator to a function that knows only the four arithmetic operators, as we have seen in the exercises of the previous chapter: +) + +--- + switch (operator) { + + case "+": + writeln(first + second); + break; + + case "-": + writeln(first - second); + break; + + case "x": + writeln(first * second); + break; + + case "/": + writeln(first / second); + break; + + default: + throw new Exception(format("Invalid operator: %s", operator)); + } +--- + +$(P +The $(C switch) statement above does not know what to do with operators that are not listed on the $(C case) statements; so throws an exception. +) + +$(P +There are many examples of thrown exceptions in Phobos. For example, $(C to!int), which can be used to convert a string representation of an integer to an $(C int) value throws an exception when that representation is not valid: +) + +--- +import std.conv; + +void main() { + const int value = to!int("hello"); +} +--- + +$(P +The program terminates with an exception that is thrown by $(C to!int): +) + +$(SHELL +std.conv.ConvException@std/conv.d(38): std.conv(1157): $(HILITE Can't +convert value) `hello' of type const(char)[] to type int +) + +$(P +$(C std.conv.ConvException) at the beginning of the message is the type of the thrown exception object. We can tell from the name that the type is $(C ConvException) that is defined in the $(C std.conv) module. +) + +$(H5 $(IX throw) The $(C throw) statement to throw exceptions) + +$(P +We've seen the $(C throw) statement both in the examples above and in the previous chapters. +) + +$(P +$(C throw) throws an $(I exception object) and this terminates the current operation of the program. The expressions and statements that are written after the $(C throw) statement are not executed. This behavior is according to the nature of exceptions: they must be thrown when the program cannot continue with its current task. +) + +$(P +Conversely, if the program could continue then the situation would not warrant throwing an exception. In such cases the function would find a way and continue. +) + +$(H6 $(IX Exception) $(IX Error) $(IX Throwable) The exception types $(C Exception) and $(C Error)) + +$(P +Only the types that are inherited from the $(C Throwable) class can be thrown. $(C Throwable) is almost never used directly in programs. The types that are actually thrown are types that are inherited from $(C Exception) or $(C Error), which themselves are the types that are inherited from $(C Throwable). For example, all of the exceptions that Phobos throws are inherited from either $(C Exception) or $(C Error). +) + +$(P +$(C Error) represents unrecoverable conditions and is not recommended to be $(I caught). For that reason, most of the exceptions that a program throws are the types that are inherited from $(C Exception). ($(I $(B Note:) Inheritance is a topic related to classes. We will see classes in a later chapter.)) +) + +$(P +$(C Exception) objects are constructed with a $(C string) value that represents an error message. You may find it easy to create this message with the $(C format()) function from the $(C std.string) module: +) + +--- +import std.stdio; +import std.random; +import std.string; + +int[] randomDiceValues(int count) { + if (count < 0) { + $(HILITE throw new Exception)( + format("Invalid dice count: %s", count)); + } + + int[] values; + + foreach (i; 0 .. count) { + values ~= uniform(1, 7); + } + + return values; +} + +void main() { + writeln(randomDiceValues(-5)); +} +--- + +$(SHELL +object.Exception...: Invalid dice count: -5 +) + +$(P +In most cases, instead of creating an exception object explicitly by $(C new) and throwing it explicitly by $(C throw), the $(C enforce()) function is called. For example, the equivalent of the error check above is the following $(C enforce()) call: +) + +--- + enforce(count >= 0, format("Invalid dice count: %s", count)); +--- + +$(P +We will see the differences between $(C enforce()) and $(C assert()) in a later chapter. +) + +$(H6 Thrown exception terminates all scopes) + +$(P +We have seen that the program execution starts from the $(C main) function and branches into other functions from there. This layered execution of going deeper into functions and eventually returning from them can be seen as the branches of a tree. +) + +$(P +For example, $(C main()) may call a function named $(C makeOmelet), which in turn may call another function named $(C prepareAll), which in turn may call another function named $(C prepareEggs), etc. Assuming that the arrows indicate function calls, the branching of such a program can be shown as in the following function call tree: +) + +$(MONO +main + │ + ├──▶ makeOmelet + │ │ + │ ├──▶ prepareAll + │ │ │ + │ │ ├─▶ prepareEggs + │ │ ├─▶ prepareButter + │ │ └─▶ preparePan + │ │ + │ ├──▶ cookEggs + │ └──▶ cleanAll + │ + └──▶ eatOmelet +) + +$(P +The following program demonstrates the branching above by using different levels of indentation in its output. The program doesn't do anything useful other than producing an output suitable to our purposes: +) + +--- +$(CODE_NAME all_functions)import std.stdio; + +void indent(in int level) { + foreach (i; 0 .. level * 2) { + write(' '); + } +} + +void entering(in char[] functionName, in int level) { + indent(level); + writeln("▶ ", functionName, "'s first line"); +} + +void exiting(in char[] functionName, in int level) { + indent(level); + writeln("◁ ", functionName, "'s last line"); +} + +void main() { + entering("main", 0); + makeOmelet(); + eatOmelet(); + exiting("main", 0); +} + +void makeOmelet() { + entering("makeOmelet", 1); + prepareAll(); + cookEggs(); + cleanAll(); + exiting("makeOmelet", 1); +} + +void eatOmelet() { + entering("eatOmelet", 1); + exiting("eatOmelet", 1); +} + +void prepareAll() { + entering("prepareAll", 2); + prepareEggs(); + prepareButter(); + preparePan(); + exiting("prepareAll", 2); +} + +void cookEggs() { + entering("cookEggs", 2); + exiting("cookEggs", 2); +} + +void cleanAll() { + entering("cleanAll", 2); + exiting("cleanAll", 2); +} + +void prepareEggs() { + entering("prepareEggs", 3); + exiting("prepareEggs", 3); +} + +void prepareButter() { + entering("prepareButter", 3); + exiting("prepareButter", 3); +} + +void preparePan() { + entering("preparePan", 3); + exiting("preparePan", 3); +} +--- + +$(P +The program produces the following output: +) + +$(SHELL +▶ main, first line + ▶ makeOmelet, first line + ▶ prepareAll, first line + ▶ prepareEggs, first line + ◁ prepareEggs, last line + ▶ prepareButter, first line + ◁ prepareButter, last line + ▶ preparePan, first line + ◁ preparePan, last line + ◁ prepareAll, last line + ▶ cookEggs, first line + ◁ cookEggs, last line + ▶ cleanAll, first line + ◁ cleanAll, last line + ◁ makeOmelet, last line + ▶ eatOmelet, first line + ◁ eatOmelet, last line +◁ main, last line +) + +$(P +The functions $(C entering) and $(C exiting) are used to indicate the first and last lines of functions with the help of the $(C ▶) and $(C ◁) characters. The program starts with the first line of $(C main()), branches down to other functions, and finally ends with the last line of $(C main). +) + +$(P +Let's modify the $(C prepareEggs) function to take the number of eggs as a parameter. Since certain values of this parameter would be an error, let's have this function throw an exception when the number of eggs is less than one: +) + +--- +$(CODE_NAME prepareEggs_int)import std.string; + +// ... + +void prepareEggs($(HILITE int count)) { + entering("prepareEggs", 3); + + if (count < 1) { + throw new Exception( + format("Cannot take %s eggs from the fridge", count)); + } + + exiting("prepareEggs", 3); +} +--- + +$(P +In order to be able to compile the program, we must modify other lines of the program to be compatible with this change. The number of eggs to take out of the fridge can be handed down from function to function, starting with $(C main()). The parts of the program that need to change are the following. The invalid value of -8 is intentional to show how the output of the program will be different from the previous output when an exception is thrown: +) + +--- +$(CODE_NAME makeOmelet_int_etc)$(CODE_XREF all_functions)$(CODE_XREF prepareEggs_int)// ... + +void main() { + entering("main", 0); + makeOmelet($(HILITE -8)); + eatOmelet(); + exiting("main", 0); +} + +void makeOmelet($(HILITE int eggCount)) { + entering("makeOmelet", 1); + prepareAll($(HILITE eggCount)); + cookEggs(); + cleanAll(); + exiting("makeOmelet", 1); +} + +// ... + +void prepareAll($(HILITE int eggCount)) { + entering("prepareAll", 2); + prepareEggs($(HILITE eggCount)); + prepareButter(); + preparePan(); + exiting("prepareAll", 2); +} + +// ... +--- + +$(P +When we start the program now, we see that the lines that used to be printed after the point where the exception is thrown are missing: +) + +$(SHELL +▶ main, first line + ▶ makeOmelet, first line + ▶ prepareAll, first line + ▶ prepareEggs, first line +object.Exception: Cannot take -8 eggs from the fridge +) + +$(P +When the exception is thrown, the program execution exits the $(C prepareEggs), $(C prepareAll), $(C makeOmelet) and $(C main()) functions in that order, from the bottom level to the top level. No additional steps are executed as the program exits these functions. +) + +$(P +The rationale for such a drastic termination is that a failure in a lower level function would mean that the higher level functions that needed its successful completion should also be considered as failed. +) + +$(P +The exception object that is thrown from a lower level function is transferred to the higher level functions one level at a time and causes the program to finally exit the $(C main()) function. The path that the exception takes can be shown as the highlighted path in the following tree: +) + +$(MONO + $(HILITE ▲) + $(HILITE │) + $(HILITE │) +main $(HILITE  ◀───────────┐) + │ $(HILITE │) + │ $(HILITE │) + ├──▶ makeOmelet $(HILITE  ◀─────┐) + │ │ $(HILITE │) + │ │ $(HILITE │) + │ ├──▶ prepareAll $(HILITE  ◀──────────┐) + │ │ │ $(HILITE │) + │ │ │ $(HILITE │) + │ │ ├─▶ prepareEggs $(HILITE X) $(I thrown exception) + │ │ ├─▶ prepareButter + │ │ └─▶ preparePan + │ │ + │ ├──▶ cookEggs + │ └──▶ cleanAll + │ + └──▶ eatOmelet +) + +$(P +The point of the exception mechanism is precisely this behavior of exiting all of the layers of function calls right away. +) + +$(P +Sometimes it makes sense to $(I catch) the thrown exception to find a way to continue the execution of the program. I will introduce the $(C catch) keyword below. +) + +$(H6 When to use $(C throw)) + +$(P +Use $(C throw) in situations when it is not possible to continue. For example, a function that reads the number of students from a file may throw an exception if this information is not available or incorrect. +) + +$(P +On the other hand, if the problem is caused by some user action like entering invalid data, it may make more sense to validate the data instead of throwing an exception. Displaying an error message and asking the user to re-enter the data is more appropriate in many cases. +) + +$(H5 $(IX try) $(IX catch) The $(C try-catch) statement to catch exceptions) + +$(P +As we've seen above, a thrown exception causes the program execution to exit all functions and this finally terminates the whole program. +) + +$(P +The exception object can be caught by a $(C try-catch) statement at any point on its path as it exits the functions. The $(C try-catch) statement models the phrase "$(I try) to do something, and $(I catch) exceptions that may be thrown." Here is the syntax of $(C try-catch): +) + +--- + try { + // the code block that is being executed, where an + // exception may be thrown + + } catch ($(I an_exception_type)) { + // expressions to execute if an exception of this + // type is caught + + } catch ($(I another_exception_type)) { + // expressions to execute if an exception of this + // other type is caught + + // ... more catch blocks as appropriate ... + + } finally { + // expressions to execute regardless of whether an + // exception is thrown + } +--- + +$(P +Let's start with the following program that does not use a $(C try-catch) statement at this state. The program reads the value of a die from a file and prints it to the standard output: +) + +--- +import std.stdio; + +int readDieFromFile() { + auto file = File("the_file_that_contains_the_value", "r"); + + int die; + file.readf(" %s", &die); + + return die; +} + +void main() { + const int die = readDieFromFile(); + + writeln("Die value: ", die); +} +--- + +$(P +Note that the $(C readDieFromFile) function is written in a way that ignores error conditions, expecting that the file and the value that it contains are available. In other words, the function is dealing only with its own task instead of paying attention to error conditions. This is a benefit of exceptions: many functions can be written in ways that focus on their actual tasks, rather than focusing on error conditions. +) + +$(P +Let's start the program when $(C the_file_that_contains_the_value) is missing: +) + +$(SHELL +std.exception.ErrnoException@std/stdio.d(286): Cannot open +file $(BACK_TICK)the_file_that_contains_the_value' in mode $(BACK_TICK)r' (No such +file or directory) +) + +$(P +An exception of type $(C ErrnoException) is thrown and the program terminates without printing "Die value: ". +) + +$(P +Let's add an intermediate function to the program that calls $(C readDieFromFile) from within a $(C try) block and let's have $(C main()) call this new function: +) + +--- +import std.stdio; + +int readDieFromFile() { + auto file = File("the_file_that_contains_the_value", "r"); + + int die; + file.readf(" %s", &die); + + return die; +} + +int $(HILITE tryReadingFromFile)() { + int die; + + $(HILITE try) { + die = readDieFromFile(); + + } $(HILITE catch) (std.exception.ErrnoException exc) { + writeln("(Could not read from file; assuming 1)"); + die = 1; + } + + return die; +} + +void main() { + const int die = $(HILITE tryReadingFromFile)(); + + writeln("Die value: ", die); +} +--- + +$(P +When we start the program again when $(C the_file_that_contains_the_value) is still missing, this time the program does not terminate with an exception: +) + +$(SHELL +(Could not read from file; assuming 1) +Die value: 1 +) + +$(P +The new program $(I tries) executing $(C readDieFromFile) in a $(C try) block. If that block executes successfully, the function ends normally with the $(C return die;) statement. If the execution of the $(C try) block ends with the specified $(C std.exception.ErrnoException), then the program execution enters the $(C catch) block. +) + +$(P +The following is a summary of events when the program is started when the file is missing: +) + +$(UL +$(LI like in the previous program, a $(C std.exception.ErrnoException) object is thrown (by $(C File()), not by our code),) +$(LI this exception is caught by $(C catch),) +$(LI the value of 1 is assumed during the normal execution of the $(C catch) block,) +$(LI and the program continues its normal operations.) +) + +$(P +$(C catch) is to catch thrown exceptions presumably to find a way to continue executing the program. +) + +$(P +As another example, let's go back to the omelet program and add a $(C try-catch) statement to its $(C main()) function: +) + +--- +$(CODE_XREF makeOmelet_int_etc)void main() { + entering("main", 0); + + try { + makeOmelet(-8); + eatOmelet(); + + } catch (Exception exc) { + write("Failed to eat omelet: "); + writeln('"', exc.msg, '"'); + writeln("Will eat at neighbor's..."); + } + + exiting("main", 0); +} +--- + +$(P +($(I $(B Note:) The $(C .msg) property will be explained below.)) +) + +$(P +That $(C try) block contains two lines of code. Any exception thrown from either of those lines would be caught by the $(C catch) block. +) + +$(SHELL +▶ main, first line + ▶ makeOmelet, first line + ▶ prepareAll, first line + ▶ prepareEggs, first line +Failed to eat omelet: "Cannot take -8 eggs from the fridge" +Will eat at neighbor's... +◁ main, last line +) + +$(P +As can be seen from the output, the program doesn't terminate because of the thrown exception anymore. It recovers from the error condition and continues executing normally till the end of the $(C main()) function. +) + +$(H6 $(C catch) blocks are considered sequentially) + +$(P +The type $(C Exception), which we have used so far in the examples is a general exception type. This type merely specifies that an error occurred in the program. It also contains a message that can explain the error further, but it does not contain information about the $(I type) of the error. +) + +$(P +$(C ConvException) and $(C ErrnoException) that we have seen earlier in this chapter are more specific exception types: the former is about a conversion error, and the latter is about a system error. Like many other exception types in Phobos and as their names suggest, $(C ConvException) and $(C ErrnoException) are both inherited from the $(C Exception) class. +) + +$(P +$(C Exception) and its sibling $(C Error) are further inherited from $(C Throwable), the most general exception type. +) + +$(P +Although possible, it is not recommended to catch objects of type $(C Error) and objects of types that are inherited from $(C Error). Since it is more general than $(C Error), it is not recommended to catch $(C Throwable) either. What should normally be caught are the types that are under the $(C Exception) hierarchy, including $(C Exception) itself. +) + +$(MONO + Throwable $(I (not recommended to catch)) + ↗ ↖ + Exception Error $(I (not recommended to catch)) + ↗ ↖ ↗ ↖ + ... ... ... ... +) + +$(P $(I $(B Note:) I will explain the hierarchy representations later in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter). The tree above indicates that $(C Throwable) is the most general and $(C Exception) and $(C Error) are more specific.) +) + +$(P +It is possible to catch exception objects of a particular type. For example, it is possible to catch an $(C ErrnoException) object specifically to detect and handle a system error. +) + +$(P +Exceptions are caught only if they match a type that is specified in a $(C catch) block. For example, a catch block that is trying to catch a $(C SpecialExceptionType) would not catch an $(C ErrnoException). +) + +$(P +The type of the exception object that is thrown during the execution of a $(C try) block is matched to the types that are specified by the $(C catch) blocks, in the order in which the $(C catch) blocks are written. If the type of the object matches the type of the $(C catch) block, then the exception is considered to be caught by that $(C catch) block, and the code that is within that block is executed. Once a match is found, the remaining $(C catch) blocks are ignored. +) + +$(P +Because the $(C catch) blocks are matched in order from the first to the last, the $(C catch) blocks must be ordered from the most specific exception types to the most general exception types. Accordingly, and if it makes sense to catch that type of exceptions, the $(C Exception) type must be specified at the last $(C catch) block. +) + +$(P +For example, a $(C try-catch) statement that is trying to catch several specific types of exceptions about student records must order the $(C catch) blocks from the most specific to the most general as in the following code: +) + +--- + try { + // operations about student records that may throw ... + + } catch (StudentIdDigitException exc) { + + // an exception that is specifically about errors with + // the digits of student ids + + } catch (StudentIdException exc) { + + // a more general exception about student ids but not + // necessarily about their digits + + } catch (StudentRecordException exc) { + + // even more general exception about student records + + } catch (Exception exc) { + + // the most general exception that may not be related + // to student records + + } +--- + +$(H6 $(IX finally) The $(C finally) block) + +$(P +$(C finally) is an optional block of the $(C try-catch) statement. It includes expressions that should be executed regardless of whether an exception is thrown or not. +) + +$(P +To see how $(C finally) works, let's look at a program that throws an exception 50% of the time: +) + +--- +import std.stdio; +import std.random; + +void throwsHalfTheTime() { + if (uniform(0, 2) == 1) { + throw new Exception("the error message"); + } +} + +void foo() { + writeln("the first line of foo()"); + + try { + writeln("the first line of the try block"); + throwsHalfTheTime(); + writeln("the last line of the try block"); + + // ... there may be one or more catch blocks here ... + + } $(HILITE finally) { + writeln("the body of the finally block"); + } + + writeln("the last line of foo()"); +} + +void main() { + foo(); +} +--- + +$(P +The output of the program is the following when the function does not throw: +) + +$(SHELL +the first line of foo() +the first line of the try block +the last line of the try block +$(HILITE the body of the finally block) +the last line of foo() +) + +$(P +The output of the program is the following when the function does throw: +) + +$(SHELL +the first line of foo() +the first line of the try block +$(HILITE the body of the finally block) +object.Exception@deneme.d: the error message +) + +$(P +As can be seen, although "the last line of the try block" and "the last line of foo()" are not printed, the content of the $(C finally) block is still executed when an exception is thrown. +) + +$(H6 When to use the $(C try-catch) statement) + +$(P +The $(C try-catch) statement is useful to catch exceptions to do something special about them. +) + +$(P +For that reason, the $(C try-catch) statement should be used only when there is something special to be done. Do not catch exceptions otherwise and leave them to higher level functions that may want to catch them. +) + +$(H5 Exception properties) + +$(P +The information that is automatically printed on the output when the program terminates due to an exception is available as properties of exception objects as well. These properties are provided by the $(C Throwable) interface: +) + +$(UL + +$(LI $(IX .file) $(C .file): The source file where the exception was thrown from) + +$(LI $(IX .line) $(C .line): The line number where the exception was thrown from) + +$(LI $(IX .msg) $(C .msg): The error message) + +$(LI $(IX .info) $(C .info): The state of the program stack when the exception was thrown) + +$(LI $(IX .next) $(C .next): The next collateral exception) + +) + +$(P +We saw that $(C finally) blocks are executed when leaving scopes due to exceptions as well. (As we will see in later chapters, the same is true for $(C scope) statements and $(I destructors) as well.) +) + +$(P +$(IX collateral exception) Naturally, such code blocks can throw exceptions as well. Exceptions that are thrown when leaving scopes due to an already thrown exception are called $(I collateral exceptions). Both the main exception and the collateral exceptions are elements of a $(I linked list) data structure, where every exception object is accessible through the $(C .next) property of the previous exception object. The value of the $(C .next) property of the last exception is $(C null). (We will see $(C null) in a later chapter.) +) + +$(P +There are three exceptions that are thrown in the example below: The main exception that is thrown in $(C foo()) and the two collateral exceptions that are thrown in the $(C finally) blocks of $(C foo()) and $(C bar()). The program accesses the collateral exceptions through the $(C .next) properties. +) + +$(P +Some of the concepts that are used in this program will be explained in later chapters. For example, the continuation condition of the $(C for) loop that consists solely of $(C exc) means $(I as long as $(C exc) is not $(C null)). +) + +--- +import std.stdio; + +void foo() { + try { + throw new Exception("Exception thrown in foo"); + + } finally { + throw new Exception( + "Exception thrown in foo's finally block"); + } +} + +void bar() { + try { + foo(); + + } finally { + throw new Exception( + "Exception thrown in bar's finally block"); + } +} + +void main() { + try { + bar(); + + } catch (Exception caughtException) { + + for (Throwable exc = caughtException; + exc; // ← Meaning: as long as exc is not 'null' + exc = exc$(HILITE .next)) { + + writefln("error message: %s", exc$(HILITE .msg)); + writefln("source file : %s", exc$(HILITE .file)); + writefln("source line : %s", exc$(HILITE .line)); + writeln(); + } + } +} +--- + +$(P +The output: +) + +$(SHELL +error message: Exception thrown in foo +source file : deneme.d +source line : 6 + +error message: Exception thrown in foo's finally block +source file : deneme.d +source line : 9 + +error message: Exception thrown in bar's finally block +source file : deneme.d +source line : 20 +) + +$(H5 $(IX error, kinds of) Kinds of errors) + +$(P +We have seen how useful the exception mechanism is. It enables both the lower and higher level operations to be aborted right away, instead of the program continuing with incorrect or missing data, or behaving in any other incorrect way. +) + +$(P +This does not mean that every error condition warrants throwing an exception. There may be better things to do depending on the kinds of errors. +) + +$(H6 User errors) + +$(P +Some of the errors are caused by the user. As we have seen above, the user may have entered a string like "hello" even though the program has been expecting a number. It may be more appropriate to display an error message and ask the user to enter appropriate data again. +) + +$(P +Even so, it may be fine to accept and use the data directly without validating the data up front; as long as the code that uses the data would throw anyway. What is important is to be able to notify the user why the data is not suitable. +) + +$(P +For example, let's look at a program that takes a file name from the user. There are at least two ways of dealing with potentially invalid file names: +) + +$(UL +$(LI $(B Validating the data before use): We can determine whether the file with the given name exists by calling $(C exists()) of the $(C std.file) module: + +--- + if (exists(fileName)) { + // yes, the file exists + + } else { + // no, the file doesn't exist + } +--- + +$(P +This gives us the chance to be able to open the data only if it exists. Unfortunately, it is still possible that the file cannot be opened even if $(C exists()) returns $(C true), if for example another process on the system deletes or renames the file before this program actually opens it. +) + +$(P +For that reason, the following method may be more useful. +) + +) + +$(LI $(B Using the data without first validating it): We can assume that the data is valid and start using it right away, because $(C File) would throw an exception if the file cannot be opened anyway. + +--- +import std.stdio; +import std.string; + +void useTheFile(string fileName) { + auto file = File(fileName, "r"); + // ... +} + +string read_string(in char[] prompt) { + write(prompt, ": "); + return strip(readln()); +} + +void main() { + bool is_fileUsed = false; + + while (!is_fileUsed) { + try { + useTheFile( + read_string("Please enter a file name")); + + /* If we are at this line, it means that + * useTheFile() function has been completed + * successfully. This indicates that the file + * name was valid. + * + * We can now set the value of the loop flag to + * terminate the while loop. */ + is_fileUsed = true; + writeln("The file has been used successfully"); + + } catch (std.exception.ErrnoException exc) { + stderr.writeln("This file could not be opened"); + } + } +} +--- + +) + +) + +$(H6 Programmer errors) + +$(P +Some errors are caused by programmer mistakes. For example, the programmer may think that a function that has just been written will always be called with a value greater than or equal to zero, and this may be true according to the design of the program. The function having still been called with a value less than zero would indicate either a mistake in the design of the program or in the implementation of that design. Both of these can be thought of as programming errors. +) + +$(P +It is more appropriate to use $(C assert) instead of the exception mechanism for errors that are caused by programmer mistakes. ($(I $(B Note:) We will cover $(C assert) in $(LINK2 /ders/d.en/assert.html, a later chapter).)) +) + +--- +void processMenuSelection(int selection) { + assert(selection >= 0); + // ... +} + +void main() { + processMenuSelection(-1); +} +--- + +$(P +The program terminates with an $(C assert) failure: +) + +$(SHELL_SMALL +core.exception.AssertError@$(HILITE deneme.d(2)): Assertion failure +) + +$(P +$(C assert) validates program state and prints the file name and line number of the validation if it fails. The message above indicates that the assertion at line 2 of $(C deneme.d) has failed. +) + +$(H6 Unexpected situations) + +$(P +For unexpected situations that are outside of the two general cases above, it is still appropriate to throw exceptions. If the program cannot continue its execution, there is nothing else to do but to throw. +) + +$(P +It is up to the higher layer functions that call this function to decide what to do with thrown exceptions. They may catch the exceptions that we throw to remedy the situation. +) + +$(H5 Summary) + +$(UL + +$(LI +When faced with a user error either warn the user right away or ensure that an exception is thrown; the exception may be thrown anyway by another function when using incorrect data, or you may throw directly. +) + +$(LI +Use $(C assert) to validate program logic and implementation. ($(I $(B Note:) $(C assert) will be explained in a later chapter.)) +) + +$(LI +When in doubt, throw an exception with $(C throw) or $(C enforce()). ($(I $(B Note:) $(C enforce()) will be explained in a later chapter.)) +) + +$(LI +Catch exceptions if and only if you can do something useful about that exception. Otherwise, do not encapsulate code with a $(C try-catch) statement; instead, leave the exceptions to higher layers of the code that may do something about them. +) + +$(LI +Order the $(C catch) blocks from the most specific to the most general. +) + +$(LI +Put the expressions that must always be executed when leaving a scope, in $(C finally) blocks. +) + +) + +Macros: + SUBTITLE=Exceptions + + DESCRIPTION=The exception mechanism of D, which is used in unexpected situations. + + KEYWORDS=d programming language tutorial book exception try catch finally exit failure success + +MINI_SOZLUK= diff --git a/target/fibers.d b/target/fibers.d new file mode 100644 index 0000000..fcb7a43 --- /dev/null +++ b/target/fibers.d @@ -0,0 +1,1107 @@ +Ddoc + +$(DERS_BOLUMU $(IX fiber) Fibers) + +$(P +$(IX coroutine) $(IX green thread) $(IX thread, green) A fiber is a $(I thread of execution) enabling a single thread achieve multiple tasks. Compared to regular threads that are commonly used in parallelism and concurrency, it is more efficient to switch between fibers. Fibers are similar to $(I coroutines) and $(I green threads). +) + +$(P +Fibers enable multiple call stacks per thread. For that reason, to fully understand and appreciate fibers, one must first understand the $(I call stack) of a thread. +) + +$(H5 $(IX call stack) $(IX program stack) Call stack) + +$(P +$(IX local state) The parameters, non-static local variables, the return value, and temporary expressions of a function, as well as any additional information that may be needed during its execution, comprise the $(I local state) of that function. The local state of a function is allocated and initialized automatically at run time every time that function is called. +) + +$(P +$(IX stack frame) The storage space allocated for the local state of a function call is called a $(I frame) (or $(I stack frame)). As functions call other functions during the execution of a thread, their frames are conceptually placed on top of each other to form a stack of frames. The stack of frames of currently active function calls is the $(I call stack) of that thread. +) + +$(P +For example, at the time the main thread of the following program starts executing the $(C bar()) function, there would be three levels of active function calls due to $(C main()) calling $(C foo()) and $(C foo()) calling $(C bar()): +) + +--- +void main() { + int a; + int b; + + int c = $(HILITE foo)(a, b); +} + +int foo(int x, int y) { + $(HILITE bar)(x + y); + return 42; +} + +void bar(int param) { + string[] arr; + // ... +} +--- + +$(P +During the execution of $(C bar()), the call stack would consist of three frames storing the local states of those currently active function calls: +) + +$(MONO +The call stack grows upward +as function calls get deeper. ▲ ▲ + │ │ + Top of the call stack → ┌──────────────┐ + │ int param │ ← bar's frame + │ string[] arr │ + ├──────────────┤ + │ int x │ + │ int y │ ← foo's frame + │ return value │ + ├──────────────┤ + │ int a │ + │ int b │ ← main's frame + │ int c │ +Bottom of the call stack → └──────────────┘ +) + +$(P +As layers of function calls get deeper when functions call other functions and shallower when functions return, the size of the call stack increases and decreases accordingly. For example, once $(C bar()) returns, its frame would no longer be needed and its space would later be used for another function call in the future: +) + +$(MONO +$(LIGHT_GRAY ┌──────────────┐) +$(LIGHT_GRAY │ int param │) +$(LIGHT_GRAY │ string[] arr │) + Top of the call stack → ├──────────────┤ + │ int a │ + │ int b │ ← foo's frame + │ return value │ + ├──────────────┤ + │ int a │ + │ int b │ ← main's frame + │ int c │ +Bottom of the call stack → └──────────────┘ +) + +$(P +We have been taking advantage of the call stack in every program that we have written so far. The advantages of the call stack is especially clear for recursive functions. +) + +$(H6 $(IX recursion) Recursion) + +$(P +Recursion is the situation where a function calls itself either directly or indirectly. Recursion greatly simplifies certain kinds of algorithms like the ones that are classified as $(I divide-and-conquer). +) + +$(P +Let's consider the following function that calculates the sum of the elements of a slice. It achieves this task by calling itself recursively with a slice that is one element shorter than the one that it has received. The recursion continues until the slice becomes empty. The current result is carried over to the next recursion step as the second parameter: +) + +--- +import std.array; + +int sum(int[] arr, int currentSum = 0) { + if (arr.empty) { + /* No element to add. The result is what has been + * calculated so far. */ + return currentSum; + } + + /* Add the front element to the current sum and call self + * with the remaining elements. */ + return $(HILITE sum)(arr[1..$], currentSum + arr.front); +} + +void main() { + assert(sum([1, 2, 3]) == 6); +} +--- + +$(P +$(IX sum, std.algorithm) $(I $(B Note:) The code above is only for demonstration. Otherwise, the sum of the elements of a range should be calculated by $(C std.algorithm.sum), which uses special algorithms to achieve more accurate calculations for floating point types.) +) + +$(P +When $(C sum()) is eventually called with an empty slice for the initial argument of $(C [1, 2, 3]) above, the relevant parts of the call stack would consist of the following frames. The value of each parameter is indicated after an $(C ==) sign. Remember to read the frame contents from bottom to top: +) + +$(MONO + ┌─────────────────────────┐ + │ arr == [] │ ← final call to sum + │ currentSum == 6 │ + ├─────────────────────────┤ + │ arr == [3] │ ← third call to sum + │ currentSum == 3 │ + ├─────────────────────────┤ + │ arr == [2, 3] │ ← second call to sum + │ currentSum == 1 │ + ├─────────────────────────┤ + │ arr == [1, 2, 3] │ ← first call to sum + │ currentSum == 0 │ + ├─────────────────────────┤ + │ ... │ ← main's frame + └─────────────────────────┘ +) + +$(P +$(I $(B Note:) In practice, when the recursive function directly returns the result of calling itself, compilers use a technique called "tail-call optimization", which eliminates separate frames for each recursive call.) +) + +$(P +In a multithreaded program, since each thread would be working on its own task, every thread gets it own call stack to maintain its own execution state. +) + +$(P +The power of fibers is based on the fact that although a fiber is not a thread, it gets its own call stack, effectively enabling multiple call stacks per thread. Since one call stack maintains the execution state of one task, multiple call stacks enable a thread work on multiple tasks. +) + +$(H5 Usage) + +$(P +The following are common operations of fibers. We will see examples of these later below. +) + +$(UL + +$(LI $(IX fiber function) A fiber starts its execution from a callable entity (function pointer, delegate, etc.) that does not take any parameter and does not return anything. For example, the following function can be used as a fiber function: + +--- +void fiberFunction() { + // ... +} +--- + +) + +$(LI $(IX Fiber, core.thread) A fiber can be created as an object of class $(C core.thread.Fiber) with a callable entity: + +--- +import core.thread; + +// ... + + auto fiber = new Fiber($(HILITE &)fiberFunction); +--- + +$(P +Alternatively, a subclass of $(C Fiber) can be defined and the fiber function can be passed to the constructor of the superclass. In the following example, the fiber function is a member function: +) + +--- +class MyFiber : $(HILITE Fiber) { + this() { + super($(HILITE &)run); + } + + void run() { + // ... + } +} + +// ... + + auto fiber = new MyFiber(); +--- + +) + +$(LI $(IX call, Fiber) A fiber is started and resumed by its $(C call()) member function: + +--- + fiber.call(); +--- + +$(P +Unlike threads, the caller is paused while the fiber is executing. +) + +) + +$(LI $(IX yield, Fiber) A fiber pauses itself ($(I yields) execution to its caller) by $(C Fiber.yield()): + +--- +void fiberFunction() { + // ... + + Fiber.yield(); + + // ... +} +--- + +$(P +The caller's execution resumes when the fiber yields. +) + +) + +$(LI $(IX .state, Fiber) The execution state of a fiber is determined by its $(C .state) property: + +--- + if (fiber.state == Fiber.State.TERM) { + // ... + } +--- + +$(P +$(IX State, Fiber) $(C Fiber.State) is an enum with the following values: +) + +$(UL + +$(LI $(IX HOLD, Fiber.State) $(C HOLD): The fiber is paused, meaning that it can be started or resumed.) + +$(LI $(IX EXEC, Fiber.State) $(C EXEC): The fiber is currently executing.) + +$(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): The fiber has terminated. It must be $(C reset()) before it can be used again.) + +) + +) + +) + +$(H5 Fibers in range implementations) + +$(P +Almost every range needs to store some information to remember its state of iteration. This is necessary for it to know what to do when its $(C popFront()) is called next time. Most range examples that we saw in $(LINK2 /ders/d.en/ranges.html, the Ranges) and later chapters have been storing some kind of state to achieve their tasks. +) + +$(P +For example, $(C FibonacciSeries) that we have defined earlier was keeping two member variables to calculate the $(I next next) number in the series: +) + +--- +struct FibonacciSeries { + int $(HILITE current) = 0; + int $(HILITE next) = 1; + + enum empty = false; + + @property int front() const { + return current; + } + + void popFront() { + const nextNext = current + next; + current = next; + next = nextNext; + } +} +--- + +$(P +While maintaining the iteration state is trivial for some ranges like $(C FibonacciSeries), it is surprisingly harder for some others, e.g. recursive data structures like binary search trees. The reason why it is surprising is that for such data structures, the same algorithms are trivial when implemented recursively. +) + +$(P +For example, the following recursive implementations of $(C insert()) and $(C print()) do not define any variables and are independent of the number of elements contained in the tree. The recursive calls are highlighted. (Note that $(C insert()) is recursive indirectly through $(C insertOrSet()).) +) + +--- +import std.stdio; +import std.string; +import std.conv; +import std.random; +import std.range; +import std.algorithm; + +/* Represents the nodes of a binary tree. This type is used in + * the implementation of struct Tree below and should not be + * used directly. */ +struct Node { + int element; + Node * left; // Left sub-tree + Node * right; // Right sub-tree + + void $(HILITE insert)(int element) { + if (element < this.element) { + /* Smaller elements go under the left sub-tree. */ + insertOrSet(left, element); + + } else if (element > this.element) { + /* Larger elements go under the right sub-tree. */ + insertOrSet(right, element); + + } else { + throw new Exception(format("%s already exists", + element)); + } + } + + void $(HILITE print)() const { + /* First print the elements of the left sub-tree */ + if (left) { + left.$(HILITE print)(); + write(' '); + } + + /* Then print this element */ + write(element); + + /* Lastly, print the elements of the right sub-tree */ + if (right) { + write(' '); + right.$(HILITE print)(); + } + } +} + +/* Inserts the element to the specified sub-tree, potentially + * initializing its node. */ +void insertOrSet(ref Node * node, int element) { + if (!node) { + /* This is the first element of this sub-tree. */ + node = new Node(element); + + } else { + node.$(HILITE insert)(element); + } +} + +/* This is the actual Tree representation. It allows an empty + * tree by means of 'root' being equal to 'null'. */ +struct Tree { + Node * root; + + /* Inserts the element to this tree. */ + void insert(int element) { + insertOrSet(root, element); + } + + /* Prints the elements in sorted order. */ + void print() const { + if (root) { + root.print(); + } + } +} + +/* Populates the tree with 'n' random numbers picked out of a + * set of '10 * n' numbers. */ +Tree makeRandomTree(size_t n) { + auto numbers = iota((n * 10).to!int) + .randomSample(n, Random(unpredictableSeed)) + .array; + + randomShuffle(numbers); + + /* Populate the tree with those numbers. */ + auto tree = Tree(); + numbers.each!(e => tree.insert(e)); + + return tree; +} + +void main() { + auto tree = makeRandomTree(10); + tree.print(); +} +--- + +$(P +$(I $(B Note:) The program above uses the following features from Phobos:) +) + +$(UL + +$(LI +$(IX iota, std.range) $(C std.range.iota) generates the elements of a given value range lazily. (By default, the first element is the $(C .init) value). For example, $(C iota(10)) is a range of $(C int) elements from $(C 0) to $(C 9). +) + +$(LI +$(IX each, std.algorithm) $(IX map, vs. each) $(C std.algorithm.each) is similar to $(C std.algorithm.map). While $(C map()) generates a new result for each element, $(C each()) generates side effects for each element. Additionally, $(C map()) is lazy while $(C each()) is eager. +) + +$(LI +$(IX randomSample, std.random) $(C std.random.randomSample) picks a random sampling of elements from a given range without changing their order. +) + +$(LI +$(IX randomShuffle, std.random) $(C std.random.randomShuffle) shuffles the elements of a range randomly. +) + +) + +$(P +Like most containers, one would like this tree to provide a range interface so that its elements can be used with existing range algorithms. This can be done by defining an $(C opSlice()) member function: +) + +--- +struct Tree { +// ... + + /* Provides access to the elements of the tree in sorted + * order. */ + struct InOrderRange { + $(HILITE ... What should the implementation be? ...) + } + + InOrderRange opSlice() const { + return InOrderRange(root); + } +} +--- + +$(P +Although the $(C print()) member function above essentially achieves the same task of visiting every element in sorted order, it is not easy to implement an $(C InputRange) for a tree. I will not attempt to implement $(C InOrderRange) here but I encourage you to implement or at least research tree iterators. (Some implementations require that tree nodes have an additional $(C Node*) to point at each node's parent.) +) + +$(P +The reason why recursive tree algorithms like $(C print()) are trivial is due to the automatic management of the call stack. The call stack implicitly contains information not only about what the current element is, but also how the execution of the program arrived at that element (e.g. at what nodes did the execution follow the left node versus the right node). +) + +$(P +For example, when a recursive call to $(C left.print()) returns after printing the elements of the left sub-tree, the local state of the current $(C print()) call already implies that it is now time to print a space character: +) + +--- + void print() const { + if (left) { + left.print(); + write(' '); // ← Call stack implies this is next + } + + // ... + } +--- + +$(P +Fibers are useful for similar cases where using a call stack is much easier than maintaining state explicitly. +) + +$(P +Although the benefits of fibers would not be apparent on a simple task like generating the Fibonacci series, for simplicity let's cover common fiber operations on a fiber implementation of one. We will implement a tree range later below. +) + +--- +import core.thread; + +/* This is the fiber function that generates each element and + * then sets the 'ref' parameter to that element. */ +void fibonacciSeries($(HILITE ref) int current) { // (1) + current = 0; // Note that 'current' is the parameter + int next = 1; + + while (true) { + $(HILITE Fiber.yield()); // (2) + /* Next call() will continue from this point */ // (3) + + const nextNext = current + next; + current = next; + next = nextNext; + } +} + +void main() { + int current; // (1) + // (4) + Fiber fiber = new Fiber(() => fibonacciSeries(current)); + + foreach (_; 0 .. 10) { + fiber$(HILITE .call()); // (5) + + import std.stdio; + writef("%s ", current); + } +} +--- + +$(OL + +$(LI The fiber function above takes a reference to an $(C int). It uses this parameter to communicate the current element to its caller. (The parameter could be qualified as $(C out) instead of $(C ref) as well).) + +$(LI When the current element is ready for use, the fiber pauses itself by calling $(C Fiber.yield()).) + +$(LI A later $(C call()) will resume the function right after the fiber's last $(C Fiber.yield()) call. (The first $(C call()) starts the function.)) + +$(LI Because fiber functions do not take parameters, $(C fibonacciSeries()) cannot be used directly as a fiber function. Instead, a parameter-less $(LINK2 /ders/d.en/lambda.html, delegate) is used as an adaptor to be passed to the $(C Fiber) constructor.) + +$(LI The caller starts and resumes the fiber by its $(C call()) member function.) + +) + +$(P +As a result, $(C main()) receives the elements of the series through $(C current) and prints them: +) + +$(SHELL +0 1 1 2 3 5 8 13 21 34 +) + +$(H6 $(IX Generator, std.concurrency) $(C std.concurrency.Generator) for presenting fibers as ranges) + +$(P +Although we have achieved generating the Fibonacci series with a fiber, that implementation has the following shortcomings: +) + +$(UL + +$(LI The solution above does not provide a range interface, making it incompatible with existing range algorithms.) + +$(LI Presenting the elements by mutating a $(C ref) parameter is less desirable compared to a design where the elements are copied to the caller's context.) + +$(LI Constructing and using the fiber explicitly through its member functions exposes $(I lower level) implementation details, compared to alternative designs.) + +) + +$(P +The $(C std.concurrency.Generator) class addresses all of these issues. Note how $(C fibonacciSeries()) below is written as a simple function. The only difference is that instead of returning a single element by $(C return), it can make multiple elements available by $(C yield()) ($(I infinite elements) in this example). +) + +$(P +$(IX yield, std.concurrency) Also note that this time it is the $(C std.concurrency.yield) function, not the $(C Fiber.yield) member function that we used above. +) + +--- +import std.stdio; +import std.range; +import std.concurrency; + +/* This alias is used for resolving the name conflict with + * std.range.Generator. */ +alias FiberRange = std.concurrency.Generator; + +void fibonacciSeries() { + int current = 0; + int next = 1; + + while (true) { + $(HILITE yield(current)); + + const nextNext = current + next; + current = next; + next = nextNext; + } +} + +void main() { + auto series = new $(HILITE FiberRange!int)(&fibonacciSeries); + writefln("%(%s %)", series.take(10)); +} +--- + +$(P +As a result, the elements that are produced by a fiber function are used conveniently as an $(C InputRange): +) + +$(SHELL +0 1 1 2 3 5 8 13 21 34 +) + +$(P +Using $(C Generator), we can easily present the elements of a tree as an $(C InputRange) as well. Further, once the tree has an $(C InputRange) interface, the $(C print()) member function would not be needed anymore; hence it is removed. Especially note how $(C byNode()) is implemented as an adaptor over the recursive function $(C nextNode()): +) + +--- +import std.concurrency; + +alias FiberRange = std.concurrency.Generator; + +struct Node { +// ... + + /* Note: print() member function is removed because it is + * not needed anymore. */ + + auto opSlice() const { + return byNode(&this); + } +} + +/* This is the fiber function that yields the next tree node + * in sorted order. */ +void nextNode(const(Node) * node) { + if (!node) { + /* No element at or under this node */ + return; + } + + nextNode(node.left); // First, elements on the left + $(HILITE yield(node)); // Then, this element + nextNode(node.right); // Finally, elements on the right +} + +/* Returns an InputRange to the nodes of the tree. */ +auto byNode(const(Node) * node) { + return new FiberRange!(const(Node)*)( + () => nextNode(node)); +} + +// ... + +struct Tree { +// ... + + /* Note: print() member function is removed because it is + * not needed anymore. */ + + auto opSlice() const { + /* A translation from the nodes to the elements. */ + return byNode(this).map!(n => n.element); + } +} + +/* Returns an InputRange to the nodes of the tree. The + * returned range is empty if the tree has no elements (i.e. + * if 'root' is 'null'). */ +auto byNode(const(Tree) tree) { + if (tree.root) { + return byNode(tree.root); + + } else { + alias RangeType = typeof(return); + return new RangeType(() {}); $(CODE_NOTE Empty range) + } +} +--- + +$(P +$(C Tree) objects can now be sliced with $(C []) and the result can be used as an $(C InputRange): +) + +--- + writefln("%(%s %)", tree$(HILITE [])); +--- + +$(H5 $(IX asynchronous I/O, fiber) Fibers in asynchronous input and output) + +$(P +The call stack of a fiber can simplify asynchronous input and output tasks as well. +) + +$(P +As an example, let's imagine a system where users sign on to a service by connecting to a server and providing their $(I name), $(I email), and $(I age), in that order. This would be similar to the $(I sign-on user flow) of a website. To keep the example simple, instead of implementing an actual web service, let's simulate user interactions using data entered on the command line. Let's use the following simple sign-on protocol, where input data is highlighted: +) + +$(UL +$(LI $(HILITE $(C hi)): A user connects and a flow id is generated) +$(LI $(HILITE $(C $(I id data))): The user of flow that corresponds to $(C id) enters the next expected data. For example, if the expected data for flow $(C 42) is $(I name), then the command for Alice would be $(C 42 Alice).) +$(LI $(HILITE $(C bye)): Program exits) +) + +$(P +For example, the following can be the interactions of Alice and Bob, where the inputs to the simulation program are highlighted. Each user connects and then provides $(I name), $(I email), and $(I age): +) + +$(SHELL +> $(HILITE hi) $(SHELL_NOTE Alice connects) +Flow 0 started. +> $(HILITE 0 Alice) +> $(HILITE 0 alice@example.com) +> $(HILITE 0 20) +Flow 0 has completed. $(SHELL_NOTE Alice finishes) +Added user 'Alice'. +> $(HILITE hi) $(SHELL_NOTE Bob connects) +Flow 1 started. +> $(HILITE 1 Bob) +> $(HILITE 1 bob@example.com) +> $(HILITE 1 30) +Flow 1 has completed. $(SHELL_NOTE Bob finishes) +Added user 'Bob'. +> $(HILITE bye) +Goodbye. +Users: + User("Alice", "alice@example.com", 20) + User("Bob", "bob@example.com", 30) +) + +$(P +This program can be designed to wait for the command $(C hi) in a loop and then call a function to receive the input data of the connected user: +) + +--- + if (input == "hi") { + signOnNewUser(); $(CODE_NOTE_WRONG WARNING: Blocking design) + } +--- + +$(P +Unless the program had some kind of support for multitasking, such a design would be considered $(I blocking), meaning that all other users would be blocked until the current user completes their sign on flow. This would impact the responsiveness of even lightly-used services if users took several minutes to provide data. +) + +$(P +There can be several designs that makes this service $(I non-blocking), meaning that more than one user can sign on at the same time: +) + +$(UL + +$(LI Maintaining tasks explicitly: The main thread can spawn one thread per user sign-on and pass input data to that thread by means of messages. Although this method would work, it might involve thread synchronization and it can be slower than a fiber. (The reasons for this potential performance penalty will be explained in the $(I cooperative multitasking) section below.)) + +$(LI Maintaining state: The program can accept more than one sign-on and remember the state of each sign-on explicitly. For example, if Alice has entered only her name so far, her state would have to indicate that the next input data would be her email information.) + +) + +$(P +Alternatively, a design based on fibers can employ one fiber per sign-on flow. This would enable implementing the flow in a linear fashion, matching the protocol exactly: first the name, then the email, and finally the age. For example, $(C run()) below does not need to do anything special to remember the state of the sign-on flow. When it is $(C call())'ed next time, it continues right after the last $(C Fiber.yield()) call that had paused it. The next line to be executed is implied by the call stack. +) + +$(P +Differently from earlier examples, the following program uses a $(C Fiber) subclass: +) + +--- +import std.stdio; +import std.string; +import std.format; +import std.exception; +import std.conv; +import std.array; +import core.thread; + +struct User { + string name; + string email; + uint age; +} + +/* This Fiber subclass represents the sign-on flow of a + * user. */ +class SignOnFlow : Fiber { + /* The data read most recently for this flow. */ + string inputData_; + + /* The information to construct a User object. */ + string name; + string email; + uint age; + + this() { + /* Set our 'run' member function as the starting point + * of the fiber. */ + super(&run); + } + + void run() { + /* First input is name. */ + name = inputData_; + $(HILITE Fiber.yield()); + + /* Second input is email. */ + email = inputData_; + $(HILITE Fiber.yield()); + + /* Last input is age. */ + age = inputData_.to!uint; + + /* At this point we have collected all information to + * construct the user. We now "return" instead of + * 'Fiber.yield()'. As a result, the state of this + * fiber becomes Fiber.State.TERM. */ + } + + /* This property function is to receive data from the + * caller. */ + @property void inputData(string data) { + inputData_ = data; + } + + /* This property function is to construct a user and + * return it to the caller. */ + @property User user() const { + return User(name, email, age); + } +} + +/* Represents data read from the input for a specific flow. */ +struct FlowData { + size_t id; + string data; +} + +/* Parses data related to a flow. */ +FlowData parseFlowData(string line) { + size_t id; + string data; + + const items = line.formattedRead!" %s %s"(id, data); + enforce(items == 2, format("Bad input '%s'.", line)); + + return FlowData(id, data); +} + +void main() { + User[] users; + SignOnFlow[] flows; + + bool done = false; + + while (!done) { + write("> "); + string line = readln.strip; + + switch (line) { + case "hi": + /* Start a flow for the new connection. */ + flows ~= new SignOnFlow(); + + writefln("Flow %s started.", flows.length - 1); + break; + + case "bye": + /* Exit the program. */ + done = true; + break; + + default: + /* Try to use the input as flow data. */ + try { + auto user = handleFlowData(line, flows); + + if (!user.name.empty) { + users ~= user; + writefln("Added user '%s'.", user.name); + } + + } catch (Exception exc) { + writefln("Error: %s", exc.msg); + } + break; + } + } + + writeln("Goodbye."); + writefln("Users:\n%( %s\n%)", users); +} + +/* Identifies the owner fiber for the input, sets its input + * data, and resumes that fiber. Returns a user with valid + * fields if the flow has been completed. */ +User handleFlowData(string line, SignOnFlow[] flows) { + const input = parseFlowData(line); + const id = input.id; + + enforce(id < flows.length, format("Invalid id: %s.", id)); + + auto flow = flows[id]; + + enforce(flow.state == Fiber.State.HOLD, + format("Flow %s is not runnable.", id)); + + /* Set flow data. */ + flow.inputData = input.data; + + /* Resume the flow. */ + flow$(HILITE .call()); + + User user; + + if (flow.state == Fiber.State.TERM) { + writefln("Flow %s has completed.", id); + + /* Set the return value to the newly created user. */ + user = flow.user; + + /* TODO: This fiber's entry in the 'flows' array can + * be reused for a new flow in the future. However, it + * must first be reset by 'flow.reset()'. */ + } + + return user; +} +--- + +$(P +$(C main()) reads lines from the input, parses them, and dispatches flow data to the appropriate flow to be processed. The call stack of each flow maintains the flow state automatically. New users are added to the system when the complete user information becomes available. +) + +$(P +When you run the program above, you see that no matter how long a user takes to complete their individual sign-on flow, the system always accepts new user connections. As an example, Alice's interaction is highlighted: +) + +$(SHELL +> $(HILITE hi) $(SHELL_NOTE Alice connects) +Flow 0 started. +> $(HILITE 0 Alice) +> hi $(SHELL_NOTE Bob connects) +Flow 1 started. +> hi $(SHELL_NOTE Cindy connects) +Flow 2 started. +> $(HILITE 0 alice@example.com) +> 1 Bob +> 2 Cindy +> 2 cindy@example.com +> 2 40 $(SHELL_NOTE Cindy finishes) +Flow 2 has completed. +Added user 'Cindy'. +> 1 bob@example.com +> 1 30 $(SHELL_NOTE Bob finishes) +Flow 1 has completed. +Added user 'Bob'. +> $(HILITE 0 20) $(SHELL_NOTE Alice finishes) +Flow 0 has completed. +Added user 'Alice'. +> bye +Goodbye. +Users: + User("Cindy", "cindy@example.com", 40) + User("Bob", "bob@example.com", 30) + User("Alice", "alice@example.com", 20) +) + +$(P +Although Alice, Bob, and Cindy connect in that order, they complete their sign-on flows at different paces. As a result, the $(C users) array is populated in the order that the flows are completed. +) + +$(P +One benefit of using fibers in this program is that $(C SignOnFlow.run()) is written trivially without regard to how fast or slow a user's input has been. Additionally, no user is blocked when other sign-on flows are in progress. +) + +$(P +$(IX vibe.d) Many asynchronous input/output frameworks like $(LINK2 http://vibed.org, vibe.d) use similar designs based on fibers. +) + +$(H5 $(IX exception, fiber) Exceptions and fibers) + +$(P +In $(LINK2 /ders/d.en/exceptions.html, the Exceptions chapter) we saw how "an exception object that is thrown from a lower level function is transferred to the higher level functions one level at a time". We also saw that an uncaught exception "causes the program to finally exit the $(C main()) function." Although that chapter did not mention the call stack, the described behavior of the exception mechanism is achieved by the call stack as well. +) + +$(P +$(IX stack unwinding) Continuing with the first example in this chapter, if an exception is thrown inside $(C bar()), first the frame of $(C bar()) would be removed from the call stack, then $(C foo())'s, and finally $(C main())'s. As functions are exited and their frames are removed from the call stack, the destructors of local variables are executed for their final operations. The process of leaving functions and executing destructors of local variables due to a thrown exception is called $(I stack unwinding). +) + +$(P +Since fibers have their own stack, an exception that is thrown during the execution of the fiber unwinds the fiber's call stack, not its caller's. If the exception is not caught, the fiber function terminates and the fiber's state becomes $(C Fiber.State.TERM). +) + +$(P +$(IX yieldAndThrow, Fiber) Although that may be the desired behavior in some cases, sometimes a fiber may need to communicate an error condition to its caller without losing its execution state. $(C Fiber.yieldAndThrow) allows a fiber to yield and immediately throw an exception in the caller's context. +) + +$(P +To see how it can be used let's enter invalid age data to the sign-on program: +) + +$(SHELL +> hi +Flow 0 started. +> 0 Alice +> 0 alice@example.com +> 0 $(HILITE hello) $(SHELL_NOTE_WRONG the user enters invalid age) +Error: Unexpected 'h' when converting from type string to type uint +> 0 $(HILITE 20) $(SHELL_NOTE attempts to correct the error) +Error: Flow 0 is not runnable. $(SHELL_NOTE but the flow is terminated) +) + +$(P +Instead of terminating the fiber and losing the entire sign-on flow, the fiber can catch the conversion error and communicate it to the caller by $(C yieldAndThrow()). This can be done by replacing the following line of the program where the fiber converts age data: +) + +--- + age = inputData_.to!uint; +--- + +$(P +Wrapping that line with a $(C try-catch) statement inside an infinite loop would be sufficient to keep the fiber alive until there is data that can be converted to a $(C uint): +) + +--- + while (true) { + try { + age = inputData_.to!uint; + break; // ← Conversion worked; leave the loop + + } catch (ConvException exc) { + Fiber.yieldAndThrow(exc); + } + } +--- + +$(P +This time the fiber remains in an infinite loop until data is valid: +) + +$(SHELL +> hi +Flow 0 started. +> 0 Alice +> 0 alice@example.com +> 0 $(HILITE hello) $(SHELL_NOTE_WRONG the user enters invalid age) +Error: Unexpected 'h' when converting from type string to type uint +> 0 $(HILITE world) $(SHELL_NOTE_WRONG enters invalid age again) +Error: Unexpected 'w' when converting from type string to type uint +> 0 $(HILITE 20) $(SHELL_NOTE finally, enters valid data) +Flow 0 has completed. +Added user 'Alice'. +> bye +Goodbye. +Users: + User("Alice", "alice@example.com", 20) +) + +$(P +As can be seen from the output, this time the sign-on flow is not lost and the user is added to the system. +) + +$(H5 $(IX preemptive multitasking, vs. cooperative multitasking) $(IX cooperative multitasking) $(IX thread performance) Cooperative multitasking) + +$(P +Unlike operating system threads, which are paused (suspended) and resumed by the operating system at unknown points in time, a fiber pauses itself explicitly and is resumed by its caller explicitly. According to this distinction, the kind of multitasking that the operating system provides is called $(I preemptive multitasking) and the kind that fibers provide is called $(I cooperative multitasking). +) + +$(P +$(IX context switching) In preemptive multitasking, the operating system allots a certain amount of time to a thread when it starts or resumes its execution. When the time is up, that thread is paused and another one is resumed in its place. Moving from one thread to another is called $(I context switching). Context switching takes a relatively large amount of time, which could have better been spent doing actual work by threads. +) + +$(P +Considering that a system is usually busy with high number of threads, context switching is unavoidable and is actually desired. However, sometimes threads need to pause themselves voluntarily before they use up the entire time that was alloted to them. This can happen when a thread needs information from another thread or from a device. When a thread pauses itself, the operating system must spend time again to switch to another thread. As a result, time that could have been used for doing actual work ends up being used for context switching. +) + +$(P +With fibers, the caller and the fiber execute as parts of the same thread. (That is the reason why the caller and the fiber cannot execute at the same time.) As a benefit, there is no overhead of context switching between the caller and the fiber. (However, there is still some light overhead which is comparable to the overhead of a regular function call.) +) + +$(P +Another benefit of cooperative multitasking is that the data that the caller and the fiber exchange is more likely to be in the CPU's data cache. Because data that is in the CPU cache can be accessed hundreds of times faster than data that needs to be read back from system memory, this further improves the performance of fibers. +) + +$(P +Additionally, because the caller and the fiber are never executed at the same time, there is no possibility of race conditions, obviating the need for data synchronization. However, the programmer must still ensure that a fiber yields at the intended time (e.g. when data is actually ready). For example, the $(C func()) call below must not execute a $(C Fiber.yield()) call, even indirectly, as that would be premature, before the value of $(C sharedData) was doubled: +) + +--- +void fiberFunction() { + // ... + + func(); $(CODE_NOTE must not yield prematurely) + sharedData *= 2; + Fiber.yield(); $(CODE_NOTE intended point to yield) + + // ... +} +--- + +$(P +$(IX M:N, threading) One obvious shortcoming of fibers is that only one core of the CPU is used for the caller and its fibers. The other cores of the CPU might be sitting idle, effectively wasting resources. It is possible to use different designs like the $(I M:N threading model (hybrid threading)) that employ other cores as well. I encourage you to research and compare different threading models. +) + +$(H5 Summary) + +$(UL + +$(LI The call stack enables efficient allocation of local state and simplifies certain algorithms, especially the recursive ones.) + +$(LI Fibers enable multiple call stacks per thread instead of the default single call stack per thread.) + +$(LI A fiber and its caller are executed on the same thread (i.e. not at the same time).) + +$(LI A fiber pauses itself by $(I yielding) to its caller and the caller resumes its fiber by $(I calling) it again.) + +$(LI $(C Generator) presents a fiber as an $(C InputRange).) + +$(LI Fibers simplify algorithms that rely heavily on the call stack.) + +$(LI Fibers simplify asynchronous input/output operations.) + +$(LI Fibers provide cooperative multitasking, which has different trade-offs from preemptive multitasking.) + +) + +macros: + SUBTITLE=Fibers + + DESCRIPTION=Generators and cooperative multitasking by fibers. + + KEYWORDS=d programming language tutorial book fiber cooperative multitasking diff --git a/target/files.cozum.d b/target/files.cozum.d new file mode 100644 index 0000000..f931bdb --- /dev/null +++ b/target/files.cozum.d @@ -0,0 +1,34 @@ +Ddoc + +$(COZUM_BOLUMU Files) + +--- +import std.stdio; +import std.string; + +void main() { + write("Please enter the name of the file to read from: "); + string inFileName = strip(readln()); + File inFile = File(inFileName, "r"); + + string outFileName = inFileName ~ ".out"; + File outFile = File(outFileName, "w"); + + while (!inFile.eof()) { + string line = strip(inFile.readln()); + + if (line.length != 0) { + outFile.writeln(line); + } + } + + writeln(outFileName, " has been created."); +} +--- + +Macros: + SUBTITLE=Files Solutions + + DESCRIPTION=Programming in D exercise solutions: basic file operations + + KEYWORDS=programming in d tutorial files diff --git a/target/files.d b/target/files.d new file mode 100644 index 0000000..28df80c --- /dev/null +++ b/target/files.d @@ -0,0 +1,226 @@ +Ddoc + +$(DERS_BOLUMU $(IX file) Files) + +$(P +We have seen in the previous chapter that the standard input and output streams can be redirected to and from files and other programs with the $(C >), $(C <), and $(C |) operators on the terminal. Despite being very powerful, these tools are not suitable in every situation because in many cases programs can not complete their tasks simply by reading from their input and writing to their output. +) + +$(P +For example, a program that deals with student records may use its standard output to display the program menu. Such a program would need to write the student records to an actual file instead of to $(C stdout). +) + +$(P +In this chapter, we will cover reading from and writing to files of file systems. +) + +$(H5 Fundamental concepts) + +$(P +Files are represented by the $(C File) $(I struct) of the $(C std.stdio) module. Since I haven't introduced structs yet, we will have to accept the syntax of struct construction as is for now. +) + +$(P +Before getting to code samples we have to go through fundamental concepts about files. +) + +$(H6 The producer and the consumer) + +$(P +Files that are created on one platform may not be readily usable on other platforms. Merely opening a file and writing data to it may not be sufficient for that data to be available on the consumer's side. The producer and the consumer of the data must have already agreed on the format of the data that is in the file. For example, if the producer has written the id and the name of the student records in a certain order, the consumer must read the data back in the same order. +) + +$(P +Additionally, the code samples below do not write a $(I byte order mark) (BOM) to the beginning of the file. This may make your files incompatible with systems that require a BOM. (The BOM specifies in what order the UTF code units of characters are arranged in a file.) +) + +$(H6 Access rights) + +$(P +File systems present files to programs under certain access rights. Access rights are important for both data integrity and performance. +) + +$(P +When it comes to reading, allowing multiple programs to read from the same file can improve performance, because the programs will not have to wait for each other to perform the read operation. On the other hand, when it comes to writing, it is often beneficial to prevent concurrent accesses to a file, even when only a single program wants to write to it. By locking the file, the operating system can prevent other programs from reading partially written files, from overwriting each other's data and so on. +) + +$(H6 Opening a file) + +$(P +The standard input and output streams $(C stdin) and $(C stdout) are already $(I open) when programs start running. They are ready to be used. +) + +$(P +On the other hand, normal files must first be opened by specifying the name of the file and the access rights that are needed. As we will see in the examples below, creating a $(C File) object is sufficient to open the file specified by its name: +) + +--- + File file = File("student_records", "r"); +--- + +$(H6 Closing a file) + +$(P +Any file that has been opened by a program must be closed when the program finishes using that file. In most cases the files need not be closed explicitly; they are closed automatically when $(C File) objects are terminated automatically: +) + +--- +if (aCondition) { + + // Assume a File object has been created and used here. + // ... + +} // ← The actual file would be closed automatically here + // when leaving this scope. No need to close explicitly. +--- + +$(P +In some cases a file object may need to be re-opened to access a different file or the same file with different access rights. In such cases the file must be closed and re-opened: +) + +--- + file.close(); + file.open("student_records", "r"); +--- + +$(H6 Writing to and reading from files) + +$(P +Since files are character streams, input and output functions $(C writeln), $(C readf), etc. are used exactly the same way with them. The only difference is that the name of the $(C File) variable and a dot must be typed: +) + +--- + writeln("hello"); // writes to the standard output + stdout.writeln("hello"); // same as above + $(HILITE file.)writeln("hello"); // writes to the specified file +--- + +$(H6 $(IX eof) $(C eof()) to determine the end of a file) + +$(P +The $(C eof()) member function determines whether the end of a file has been reached while reading from a file. It returns $(C true) if the end of the file has been reached. +) + +$(P +For example, the following loop will be active until the end of the file: +) + +--- + while (!file.eof()) { + // ... + } +--- + +$(H6 $(IX std.file) The $(C std.file) module) + +$(P +The $(LINK2 http://dlang.org/phobos/std_file.html, $(C std.file) module) contains functions and types that are useful when working with contents of directories. For example, $(C exists) can be used to determine whether a file or a directory exists on the file systems: +) + +--- +import std.file; + +// ... + + if (exists(fileName)) { + // there is a file or directory under that name + + } else { + // no file or directory under that name + } +--- + +$(H5 $(IX File) $(C std.stdio.File) struct) + +$(P +$(IX mode, file) The $(C File) struct is included in the $(LINK2 http://dlang.org/phobos/std_stdio.html, $(C std.stdio) module). To use it you specify the name of the file you want to open and the desired access rights, or mode. It uses the same mode characters that are used by $(C fopen) of the C programming language: +) + + + + + + + + + + + + + + + +
 Mode  Definition
r$(B read) access$(BR)the file is opened to be read from the beginning
r+$(B read and write) access$(BR)the file is opened to be read from and written at the beginning
w$(B write) access$(BR)if the file does not exist, it is created as empty$(BR)if the file already exists, its contents are cleared
w+$(B read and write) access$(BR)if the file does not exist, it is created as empty$(BR)if the file already exists, its contents are cleared
a$(B append) access$(BR)if the file does not exist, it is created as empty$(BR)if the file already exists, its contents are preserved and it is opened to be written at the end
a+$(B read and append) access$(BR)if the file does not exist, it is created as empty$(BR)if the file already exists, its contents are preserved and the file is opened to be read from the beginning and written at the end
+ +$(P +A 'b' character may be added to the mode string, as in "rb". This may have an effect on platforms that support the $(I binary mode), but it is ignored on all POSIX systems. +) + +$(H6 Writing to a file) + +$(P +The file must have been opened in one of the write modes first: +) + +--- +import std.stdio; + +void main() { + File file = File("student_records", $(HILITE "w")); + + file.writeln("Name : ", "Zafer"); + file.writeln("Number: ", 123); + file.writeln("Class : ", "1A"); +} +--- + +$(P +As you remember from the $(LINK2 /ders/d.en/strings.html, Strings chapter), the type of literals like $(STRING "student_records") is $(C string), consisting of immutable characters. For that reason, it is not possible to construct $(C File) objects by using mutable text to specify the file name (e.g. $(C char[])). When needed, call the $(C .idup) property of the mutable string to get an immutable copy. +) + +$(P +The program above creates or overwrites the contents of a file named $(C student_records) in the directory that it has been started under (in the program's $(I working directory)). +) + +$(P +$(I $(B Note:) File names can contain any character that is legal for that file system. To be portable, I will use only the commonly supported ASCII characters.) +) + +$(H6 Reading from a file) + +$(P +To read from a file the file must first have been opened in one of the read modes: +) + +--- +import std.stdio; +import std.string; + +void main() { + File file = File("student_records", $(HILITE "r")); + + while (!file.eof()) { + string line = strip(file.readln()); + writeln("read line -> |", line); + } +} +--- + +$(P +The program above reads all of the lines of the file named $(C student_records) and prints those lines to its standard output. +) + +$(PROBLEM_TEK + +$(P +Write a program that takes a file name from the user, opens that file, and writes all of the non-empty lines of that file to another file. The name of the new file can be based on the name of the original file. For example, if the original file is $(C foo.txt), the new file can be $(C foo.txt.out). +) + +) + +Macros: + SUBTITLE=Files + + DESCRIPTION=Basic file operations. + + KEYWORDS=d programming language tutorial book file diff --git a/target/floating_point.cozum.d b/target/floating_point.cozum.d new file mode 100644 index 0000000..fab6800 --- /dev/null +++ b/target/floating_point.cozum.d @@ -0,0 +1,110 @@ +Ddoc + +$(COZUM_BOLUMU Floating Point Types) + +$(OL + +$(LI +Replacing $(C float) with $(C double) produces an outcome that is surprising in a different way: + +--- +// ... + + $(HILITE double) result = 0; + +// ... + + if ($(HILITE result == 1)) { + writeln("As expected: 1"); + + } else { + writeln("DIFFERENT: ", result); + } +--- + +$(P +Although the value fails the $(C result == 1) comparison, it is still printed as 1: +) + +$(SHELL +DIFFERENT: 1 +) + +$(P +That surprising outcome is related to the way floating point values are formatted for printing. A more accurate approximation of the value can be seen when the value is printed with more digits after the decimal point. (We will see formatted output in $(LINK2 /ders/d.en/formatted_output.html, a later chapter).): +) + +--- + write$(HILITE f)ln("DIFFERENT: %.20f", result); +--- + +$(SHELL +DIFFERENT: 1.00000000000000066613 +) + +) + +$(LI +Replacing the three $(C int)s with three $(C double)s is sufficient: + +--- + double first; + double second; + + // ... + + double result; +--- + +) + +$(LI +The following program demonstrates how much more complicated it would become if more than five variables were needed: + +--- +import std.stdio; + +void main() { + double value_1; + double value_2; + double value_3; + double value_4; + double value_5; + + write("Value 1: "); + readf(" %s", &value_1); + write("Value 2: "); + readf(" %s", &value_2); + write("Value 3: "); + readf(" %s", &value_3); + write("Value 4: "); + readf(" %s", &value_4); + write("Value 5: "); + readf(" %s", &value_5); + + writeln("Twice the values:"); + writeln(value_1 * 2); + writeln(value_2 * 2); + writeln(value_3 * 2); + writeln(value_4 * 2); + writeln(value_5 * 2); + + writeln("One fifth the values:"); + writeln(value_1 / 5); + writeln(value_2 / 5); + writeln(value_3 / 5); + writeln(value_4 / 5); + writeln(value_5 / 5); +} +--- + +) + +) + +Macros: + SUBTITLE=Floating Point Types Solutions + + DESCRIPTION=The exercise solutions for the floating point types chapter + + KEYWORDS=programming in d tutorial floating point types exercise solution diff --git a/target/floating_point.d b/target/floating_point.d new file mode 100644 index 0000000..ab04310 --- /dev/null +++ b/target/floating_point.d @@ -0,0 +1,490 @@ +Ddoc + +$(DERS_BOLUMU $(IX floating point) Floating Point Types) + +$(P +In the previous chapter we have seen that despite their ease of use, arithmetic operations on integers are prone to programming errors due to overflow and truncation. We have also seen that integers cannot have values with fractional parts, as in 1.25. +) + +$(P +Floating point types are designed to support fractional parts. The "point" in their name comes from the $(I radix point), which separates the integer part from the fractional part, and "floating" refers to a detail in how these types are implemented: the decimal point $(I floats) left and right as appropriate. (This detail is not important when using these types.) +) + +$(P +We must cover important details in this chapter as well. Before doing that, I would like to give a list of some of the interesting aspects of floating point types: +) + +$(UL + +$(LI Adding 0.001 a thousand times is not the same as adding 1.) + +$(LI Using the logical operators $(C ==) and $(C !=) with floating point types is erroneous in most cases.) + +$(LI The initial value of floating point types is $(C .nan), not 0. $(C .nan) may not be used in expressions in any meaningful way. When used in comparison operations, $(C .nan) is not less than nor greater than any value.) + +$(LI The two overflow values are $(C .infinity) and negative $(C .infinity).) +) + +$(P +Although floating point types are more useful in some cases, they have peculiarities that every programmer must know. Compared to integers, they are very good at avoiding truncation because their main purpose is to support fractional values. Like any other type, being based on a certain numbers of bits, they too are prone to overflow, but compared to integers, the range of values that they can support is vast. Additionally, instead of being silent in the case of overflow, they get the special values of positive and negative $(I infinity). +) + +$(P +$(IX float) +$(IX double) +$(IX real) +As a reminder, the floating point types are the following: +) + + + + + + + + + + + + + + + + + +
Type Number of Bits Initial Value
float32float.nan
double64double.nan
realat least 64, maybe more$(BR)(e.g. 80, depending on hardware support)real.nan
+ +$(H5 Floating point type properties) + +$(P +Floating point types have more properties than other types: +) + +$(UL + +$(LI $(C .stringof) is the name of the type.) + +$(LI $(C .sizeof) is the length of the type in terms of bytes. (In order to determine the bit count, this value must be multiplied by 8, the number of bits in a $(C byte).)) + +$(LI $(IX .min, floating point) $(C .max) is the short for "maximum" and is the maximum value that the type can have. There is no separate $(C .min) property for floating types; the negative of $(C .max) is the minimum value that the type can have. For example, the minimum value of $(C double) is $(C -double.max).) + +$(LI $(IX .min_normal) $(IX underflow) $(C .min_normal) is the smallest $(I normalized) value that this type can have. The type can have smaller values than $(C .min_normal) but the precision of those values is less than the normal precision of the type and they are generally slower to compute. The condition of a floating point value being between $(C -.min_normal) and $(C .min_normal) (excluding 0) is called $(I underflow).) + +$(LI $(IX .dig) $(C .dig) is short for "digits" and specifies the number of digits that signify the precision of the type.) + +$(LI $(IX .infinity) $(IX overflow, floating point) $(C .infinity) is the special value used to denote overflow.) + +) + +$(P +Other properties of floating point types are used less commonly. You can see all of them at $(LINK2 http://dlang.org/property.html, Properties for Floating Point Types at dlang.org). +) + +$(P +The properties of floating point types and their relations can be shown on a number line like the following: +) + +$(MONO + + +─────────+─────────+ ... + ... +─────────+─────────+ + + │ -max -1 │ 0 │ 1 max │ + │ │ │ │ +-infinity -min_normal min_normal infinity +) + +$(P +Other than the two special infinity values, the line above is to scale: the number of values that can be represented between $(C min_normal) and 1 is equal to the number of values that can be represented between 1 and $(C max). This means that the precision of the fractional parts of the values that are between $(C min_normal) and 1 is very high. (The same is true for the negative side as well.) +) + +$(H5 $(IX .nan) $(C .nan)) + +$(P +We have already seen that this is the default value of floating point variables. $(C .nan) may appear as a result of meaningless floating point expressions as well. For example, the floating point expressions in the following program all produce $(C double.nan): +) + +--- +import std.stdio; + +void main() { + double zero = 0; + double infinity = double.infinity; + + writeln("any expression with nan: ", double.nan + 1); + writeln("zero / zero : ", zero / zero); + writeln("zero * infinity : ", zero * infinity); + writeln("infinity / infinity : ", infinity / infinity); + writeln("infinity - infinity : ", infinity - infinity); +} +--- + +$(P +$(C .nan) is not useful just because it indicates an uninitialized value. It is also useful because it is propagated through computations, making it easier and earlier to detect errors. +) + +$(H5 Specifying floating point values) + +$(P +Floating point values can be built from integer values without a decimal point, like 123, or created directly with a decimal point, like 123.0. +) + +$(P +Floating point values can also be specified with the special floating point syntax, as in $(C 1.23e+4). The $(C e+) part in that syntax can be read as "times 10 to the power of". According to that reading, the previous value is "1.23 times 10 to the power of 4", which is the same as "1.23 times 10$(SUP 4)", which in turn is the same as 1.23x10000, being equal to 12300. +) + +$(P +If the value after $(C e) is negative, as in $(C 5.67e-3), then it is read as "divided by 10 to the power of". Accordingly, this example is "5.67 divided by 10$(SUP 3)", which in turn is the same as 5.67/1000, being equal to 0.00567. +) + +$(P +The floating point format is apparent in the output of the following program that prints the properties of the three floating point types: +) + +--- +import std.stdio; + +void main() { + writeln("Type : ", float.stringof); + writeln("Precision : ", float.dig); + writeln("Minimum normalized value: ", float.min_normal); + writeln("Minimum value : ", -float.max); + writeln("Maximum value : ", float.max); + writeln(); + + writeln("Type : ", double.stringof); + writeln("Precision : ", double.dig); + writeln("Minimum normalized value: ", double.min_normal); + writeln("Minimum value : ", -double.max); + writeln("Maximum value : ", double.max); + writeln(); + + writeln("Type : ", real.stringof); + writeln("Precision : ", real.dig); + writeln("Minimum normalized value: ", real.min_normal); + writeln("Minimum value : ", -real.max); + writeln("Maximum value : ", real.max); +} +--- + +$(P +The output of the program is the following in my environment. Since $(C real) depends on the hardware, you may get a different output: +) + +$(SHELL +Type : float +Precision : 6 +Minimum normalized value: 1.17549e-38 +Minimum value : -3.40282e+38 +Maximum value : 3.40282e+38 + +Type : double +Precision : 15 +Minimum normalized value: 2.22507e-308 +Minimum value : -1.79769e+308 +Maximum value : 1.79769e+308 + +Type : real +Precision : 18 +Minimum normalized value: 3.3621e-4932 +Minimum value : -1.18973e+4932 +Maximum value : 1.18973e+4932 +) + +$(H6 Observations) + +$(P +As you will remember from the previous chapter, the maximum value of $(C ulong) has 20 digits: 18,446,744,073,709,551,616. That value looks small when compared to even the smallest floating point type: $(C float) can have values up to the 10$(SUP 38) range, e.g. 340,282,000,000,000,000,000,000,000,000,000,000,000. The maximum value of $(C real) is in the range 10$(SUP 4932), a value with more than 4900 digits! +) + +$(P +As another observation, let's look at the minimum value that $(C double) can represent with 15-digit precision: +) + +$(MONO + 0.000...$(I (there are 300 more zeroes here))...0000222507385850720 +) + +$(H5 Overflow is not ignored) + +$(P +Despite being able to take very large values, floating point types are prone to overflow as well. The floating point types are safer than integer types in this regard because overflow is not ignored. The values that overflow on the positive side become $(C .infinity), and the values that overflow on the negative side become $(C ‑.infinity). To see this, let's increase the value of $(C .max) by 10%. Since the value is already at the maximum, increasing by 10% would overflow: +) + +--- +import std.stdio; + +void main() { + real value = real.max; + + writeln("Before : ", value); + + // Multiplying by 1.1 is the same as adding 10% + value *= 1.1; + writeln("Added 10% : ", value); + + // Let's try to reduce its value by dividing in half + value /= 2; + writeln("Divided in half: ", value); +} +--- + +$(P +Once the value overflows and becomes $(C real.infinity), it remains that way even after being divided in half: +) + +$(SHELL +Before : 1.18973e+4932 +Added 10% : inf +Divided in half: inf +) + +$(H5 $(IX precision) Precision) + +$(P +Precision is a concept that we come across in daily life but do not talk about much. Precision is the number of digits that is used when specifying a value. For example, when we say that the third of 100 is 33, the precision is 2 because 33 has 2 digits. When the value is specified more precisely as 33.33, then the precision is 4 digits. +) + +$(P +The number of bits that each floating type has not only affects its maximum value, but also its precision. The greater the number of bits, the more precise the values are. +) + +$(H5 There is no truncation in division) + +$(P +As we have seen in the previous chapter, integer division cannot preserve the fractional part of a result: +) + +--- + int first = 3; + int second = 2; + writeln(first / second); +--- + +$(P +Output: +) + +$(SHELL +1 +) + +$(P +Floating point types don't have this $(I truncation) problem; they are specifically designed for preserving the fractional parts: +) + +--- + double first = 3; + double second = 2; + writeln(first / second); +--- + +$(P +Output: +) + +$(SHELL +1.5 +) + +$(P +The accuracy of the fractional part depends on the precision of the type: $(C real) has the highest precision and $(C float) has the lowest precision. +) + +$(H5 Which type to use) + +$(P +Unless there is a specific reason otherwise, you can choose $(C double) for floating point values. $(C float) has low precision but due to being smaller than the other types it may be useful when memory is limited. On the other hand, since the precision of $(C real) is higher than $(C double) on some hardware, it would be preferable for high precision calculations. +) + +$(H5 Cannot represent all values) + +$(P +We cannot represent certain values in our daily lives. In the decimal system that we use daily, the digits before the decimal point represent ones, tens, hundreds, etc. and the digits after the decimal point represent tenths, hundredths, thousandths, etc. +) + +$(P +If a value is created from a combination of these values, it can be represented exactly. For example, because the value 0.23 consists of 2 tenths and 3 hundredths it is represented exactly. On the other hand, the value 1/3 cannot be exactly represented in the decimal system because the number of digits is always insufficient, no matter how many are specified: 0.33333... +) + +$(P +The situation is very similar with the floating point types. Because these types are based on a certain numbers of bits, they cannot represent every value exactly. +) + +$(P +The difference with the binary system that the computers use is that the digits before the decimal point are ones, twos, fours, etc. and the digits after the decimal point are halves, quarters, eighths, etc. Only the values that are exact combinations of those digits can be represented exactly. +) + +$(P +A value that cannot be represented exactly in the binary system used by computers is 0.1, as in 10 cents. Although this value can be represented exactly in the decimal system, its binary representation never ends and continuously repeats four digits: 0.0001100110011... (Note that the value is written in binary system, not decimal.) It is always inaccurate at some level depending on the precision of the floating point type that is used. +) + +$(P +The following program demonstrates this problem. The value of a variable is being incremented by 0.001 a thousand times in a loop. Surprisingly, the result is not 1: +) + +--- +import std.stdio; + +void main() { + float result = 0; + + // Adding 0.001 for a thousand times: + int counter = 1; + while (counter <= 1000) { + result += 0.001; + ++counter; + } + + if (result == 1) { + writeln("As expected: 1"); + + } else { + writeln("DIFFERENT: ", result); + } +} +--- + +$(P +Because 0.001 cannot be represented exactly, that inaccuracy affects the result at every iteration: +) + +$(SHELL +DIFFERENT: 0.999991 +) + +$(P +$(I $(B Note:) The variable $(C counter) above is a loop counter. Defining a variable explicitly for that purpose is not recommended. Instead, a common approach is to use a $(C foreach) loop, which we will see in $(LINK2 /ders/d.en/foreach.html, a later chapter).) +) + +$(H5 $(IX unordered) Unorderedness) + +$(P +The same comparison operators that we have covered with integers are used with floating point types as well. However, since the special value $(C .nan) represents invalid floating point values, comparing $(C .nan) to other values are not meaningful. For example, it does not make sense to ask whether $(C .nan) or $(C 1) is greater. +) + +$(P +For that reason, floating point values introduce another comparison concept: unorderedness. Being unordered means that at least one of the values is $(C .nan). +) + +$(P +The following table lists all the floating point comparison operators. All of them are binary operators (meaning that they take two operands) and used as in $(C left == right). The columns that contain $(C false) and $(C true) are the results of the comparison operations. +) + +$(P +The last column indicates whether the operation is meaningful if one of the operands is $(C .nan). For example, even though the result of the expression $(C 1.2 < real.nan) is $(C false), that result is meaningless because one of the operands is $(C real.nan). The result of the reverse comparison $(C real.nan < 1.2) would produce $(C false) as well. The abreviation lhs stands for $(I left-hand side), indicating the expression on the left-hand side of each operator. +) + + + + + + + + + + + + + + + + + + + + + + + + + + +
$(BR)Operator$(BR)MeaningIf lhs$(BR)is greaterIf lhs$(BR)is lessIf both$(BR)are equalIf at least$(BR)one is .nanMeaningful$(BR)with .nan
==is equal to $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_YES yes)
!=is not equal to $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
>is greater than $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
>=is greater than or equal to $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
<is less than $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
<=is less than or equal to $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
+ +$(P +Although meaningful to use with $(C .nan), the $(C ==) operator always produces $(C false) when used with a $(C .nan) value. This is the case even when both values are $(C .nan): +) + +--- +import std.stdio; + +void main() { + if (double.nan == double.nan) { + writeln("equal"); + + } else { + writeln("not equal"); + } +} +--- + +$(P +Although one would expect $(C double.nan) to be equal to itself, the result of the comparison is $(C false): +) + +$(SHELL +not equal +) + +$(H6 $(IX isNan, std.math) $(C isNaN()) for $(C .nan) equality comparison) + +$(P +As we have seen above, it is not possible to use the $(C ==) operator to determine whether the value of a floating point variable is $(C .nan): +) + +--- + if (variable == double.nan) { $(CODE_NOTE_WRONG WRONG) + // ... + } +--- + +$(P +$(C isNaN()) function from the $(C std.math) module is for determining whether a value is $(C .nan): +) + +--- +import std.math; +// ... + if (isNaN(variable)) { $(CODE_NOTE correct) + // ... + } +--- + +$(P +Similarly, to determine whether a value is $(I not) $(C .nan), one must use $(C !isNaN()) because otherwise the $(C !=) operator would always produce $(C true). +) + +$(PROBLEM_COK + +$(PROBLEM +Instead of $(C float), use $(C double) (or $(C real)) in the program above which added 0.001 a thousand times: + +--- + $(HILITE double) result = 0; +--- + +$(P +This exercise demonstrates how misleading floating point equality comparisons can be. +) + +) + +$(PROBLEM +Modify the calculator from the previous chapter to support floating point types. The new calculator should work more accurately with that change. When trying the calculator, you can enter floating point values in various formats, as in 1000, 1.23, and 1.23e4. +) + +$(PROBLEM +Write a program that reads 5 floating point values from the input. Make the program first print twice of each value and then one fifth of each value. + +$(P +This exercise is a preparation for the array concept of the next chapter. If you write this program with what you have seen so far, you will understand arrays more easily and will better appreciate them. +) + +) + +) + +Macros: + SUBTITLE=Floating Point Types + + DESCRIPTION=The floating point types of the D programming language. + + KEYWORDS=d programming language tutorial book floating point float double real numeric limits diff --git a/target/for.cozum.d b/target/for.cozum.d new file mode 100644 index 0000000..1cda393 --- /dev/null +++ b/target/for.cozum.d @@ -0,0 +1,86 @@ +Ddoc + +$(COZUM_BOLUMU The $(C for) Loop) + +$(OL + +$(LI + +--- +import std.stdio; + +void main() { + for (int line = 0; line != 9; ++line) { + for (int column = 0; column != 9; ++column) { + write(line, ',', column, ' '); + } + + writeln(); + } +} +--- + +) + +$(LI +Triangle: + +--- +import std.stdio; + +void main() { + for (int line = 0; line != 5; ++line) { + int length = line + 1; + + for (int i = 0; i != length; ++i) { + write('*'); + } + + writeln(); + } +} +--- + +$(P +Parallellogram: +) + +--- +import std.stdio; + +void main() { + for (int line = 0; line != 5; ++line) { + for (int i = 0; i != line; ++i) { + write(' '); + } + + writeln("********"); + } +} +--- + +$(P +Can you produce the diamond pattern? +) + +$(SHELL + * + *** + ***** +******* + ***** + *** + * +) + +) + +) + + +Macros: + SUBTITLE=The for Loop Solutions + + DESCRIPTION=Programming in D exercise solutions: the for loop + + KEYWORDS=programming in d tutorial for loop solution diff --git a/target/for.d b/target/for.d new file mode 100644 index 0000000..637ed00 --- /dev/null +++ b/target/for.d @@ -0,0 +1,257 @@ +Ddoc + +$(DERS_BOLUMU $(IX for) $(IX loop, for) $(CH4 for) Loop) + +$(P +The $(C for) loop serves the same purpose as $(LINK2 /ders/d.en/while.html, the $(C while) loop). $(C for) makes it possible to put the definitions and expressions concerning the loop's iteration on the same line. +) + +$(P +Although $(C for) is used much less than $(C foreach) in practice, it is important to understand the $(C for) loop first. We will see $(C foreach) in $(LINK2 /ders/d.en/foreach.html, a later chapter). +) + +$(H5 The sections of the $(C while) loop) + +$(P +The $(C while) loop evaluates the loop condition and continues executing the loop as long as that condition is $(C true). For example, a loop to print the numbers between 1 and 10 may check the condition $(I less than 11): +) + +--- + while (number < 11) +--- + +$(P +$(I Iterating) the loop can be achieved by incrementing $(C number) at the end of the loop: +) + +--- + ++number; +--- + +$(P +To be compilable as D code, $(C number) must have been defined before its first use: +) + +--- + int number = 1; +--- + +$(P +Finally, there is the actual work within the loop body: +) + +--- + writeln(number); +--- + +$(P +These four sections can be combined into the desired loop as follows: +) + +--- + int number = 1; // ← preparation + + while (number < 11) { // ← condition check + writeln(number); // ← actual work + ++number; // ← iteration + } +--- + +$(P +The sections of the $(C while) loop are executed in the following order during the iteration of the $(C while) loop: +) + +$(MONO +preparation + +condition check +actual work +iteration + +condition check +actual work +iteration + +... +) + +$(P +A $(C break) statement or a thrown exception can terminate the loop as well. +) + +$(H5 The sections of the $(C for) loop) + +$(P +The $(C for) loop brings three of these sections onto a single line. They are written within the parentheses of the $(C for) loop, separated by semicolons. The loop body contains only the actual work: +) + +--- +for (/* preparation */; /* condition check */; /* iteration */) { + /* actual work */ +} +--- + +$(P +Here is the same code written as a $(C for) loop: +) + +--- + for (int number = 1; number < 11; ++number) { + writeln(number); + } +--- + +$(P +The benefits of the $(C for) loop are more obvious when the loop body has a large number of statements. The expression that increments the loop variable is visible on the $(C for) line instead of being mixed with the other statements of the loop. It is also more clear that the declared variable is used only as part of the loop, and not by any other surrounding code. +) + +$(P +The sections of the $(C for) loop are executed in the same order as in the $(C while) loop. The $(C break) and $(C continue) statements also work exactly the same way as they do in the $(C for) loop. The only difference between $(C while) and $(C for) loops is the name scope of the loop variable. This is explained below. +) + +$(P +Although very common, the iteration variable need not be an integer, nor it is modified only by incrementing. For example, the following loop is used to print the halves of the previous floating point values: +) + +--- + for (double value = 1; value > 0.001; value /= 2) { + writeln(value); + } +--- + +$(P +$(B Note:) The information above is technically incorrect but better captures the spirit of how the $(C for) loop is used in practice. In reality, D's $(C for) loop does not have $(I three sections that are separated by semicolons). It has two sections, the first of which consisting of the preparation and the loop condition. +) + +$(P +Without getting into the details of this syntax, here is how to define two variables of different types in the preparation section: +) + +--- + for ($(HILITE {) int i = 0; double d = 0.5; $(HILITE }) i < 10; ++i) { + writeln("i: ", i, ", d: ", d); + d /= 2; + } +--- + +$(P +Note that the preparation section is the area within the highlighted curly brackets and that there is no semicolon between the preparation section and the condition section. +) + +$(H5 The sections may be empty) + +$(P +All three of the $(C for) loop sections may be left empty: +) + +$(UL +$(LI Sometimes a special loop variable is not needed, possibly because an already-defined variable would be used. +) +$(LI Sometimes the loop would be exited by a $(C break) statement, instead of by relying on the loop condition. +) +$(LI Sometimes the iteration expressions depend on certain conditions that would be checked within the loop body. +) +) + +$(P +When all of the sections are emtpy, the $(C for) loop means $(I forever): +) + +--- + for ( ; ; ) { + // ... + } +--- + +$(P +Such a loop may be designed to never end or end with a $(C break) statement. +) + +$(H5 The name scope of the loop variable) + +$(P +The only difference between the $(C for) and $(C while) loops is the name scope of the variable defined during loop preparation: The variable is accessible only within the $(C for) loop, not outside of it: +) + +--- + for (int i = 0; i < 5; ++i) { + // ... + } + + writeln(i); $(DERLEME_HATASI) + // i is not accessible here +--- + + +$(P +In contrast, when using a $(C while) loop the variable is defined in the same name scope as that which contains the loop, and therefore the name is accessible even after the loop: +) + +--- + int i = 0; + + while (i < 5) { + // ... + ++i; + } + + writeln(i); // ← 'i' is accessible here +--- + +$(P +We have seen the guideline of $(I defining names closest to their first use) in the previous chapter. Similar to the rationale for that guideline, the smaller the name scope of a variable the better. In this regard, when the loop variable is not needed outside the loop, a $(C for) loop is better than a $(C while) loop. +) + +$(PROBLEM_COK + +$(PROBLEM + +Print the following 9x9 table by using two $(C for) loops, one inside the other: + +$(SHELL +0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 +1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 +2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 +3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 +4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 +5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 +6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 +7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 +8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 +) + +) + +$(PROBLEM + +Use one or more $(C for) loops to print the $(C *) character as needed to produce geometrical patterns: + + +$(SHELL +* +** +*** +**** +***** +) + +$(SHELL +******** + ******** + ******** + ******** + ******** +) + +etc. + +) + +) + +Macros: + SUBTITLE=for Loop + + DESCRIPTION=The for loop of the D programming language and comparing it the while loop. + + KEYWORDS=d programming language tutorial book for loop diff --git a/target/foreach.cozum.d b/target/foreach.cozum.d new file mode 100644 index 0000000..44213a2 --- /dev/null +++ b/target/foreach.cozum.d @@ -0,0 +1,34 @@ +Ddoc + +$(COZUM_BOLUMU The $(C foreach) Loop) + +$(P +To have an associative array that works the opposite of $(C names), the types of the key and the value must be swapped. The new associative array must be defined as of type $(C int[string]). +) + +$(P +Iterating over the keys and the values of the original associative array while using keys as values and values as keys would populate the $(C values) table: +) + +--- +import std.stdio; + +void main() { + string[int] names = [ 1:"one", 7:"seven", 20:"twenty" ]; + + int[string] values; + + foreach (key, value; names) { + values[value] = key; + } + + writeln(values["twenty"]); +} +--- + +Macros: + SUBTITLE=The foreach Loop Solutions + + DESCRIPTION=Programming in D exercise solutions: The foreach Loop + + KEYWORDS=programming in d tutorial foreach diff --git a/target/foreach.d b/target/foreach.d new file mode 100644 index 0000000..bc268f4 --- /dev/null +++ b/target/foreach.d @@ -0,0 +1,437 @@ +Ddoc + +$(DERS_BOLUMU $(IX foreach) $(IX loop, foreach) $(CH4 foreach) Loop) + +$(P +One of the most common statements in D is the $(C foreach) loop. It is used for applying the same operations to every element of a container (or a $(I range)). +) + +$(P +Operations that are applied to elements of containers are very common in programming. We have seen in the $(LINK2 /ders/d.en/for.html, $(C for) Loop chapter) that elements of an array are accessed in a $(C for) loop by an index value that is incremented at each iteration: +) + +--- + for (int i = 0; i != array.length; ++i) { + writeln(array[i]); + } +--- + +$(P +The following steps are involved in iterating over all the elements: +) + +$(UL +$(LI Defining a variable as a counter, which is conventionally named as $(C i)) + +$(LI Iterating the loop up to the value of the $(C .length) property of the array) + +$(LI Incrementing $(C i)) + +$(LI Accessing the element) +) + +$(P +$(C foreach) has essentially the same behavior but it simplifies the code by handling those steps automatically: +) + +--- + foreach (element; array) { + writeln(element); + } +--- + +$(P +Part of the power of $(C foreach) comes from the fact that it can be used the same way regardless of the type of the container. As we have seen in the previous chapter, one way of iterating over the values of an associative array in a $(C for) loop is by first calling the array's $(C .values) property: +) + +--- + auto values = aa$(HILITE .values); + for (int i = 0; i != values.length; ++i) { + writeln(values[i]); + } +--- + +$(P +$(C foreach) does not require anything special for associative arrays; it is used exactly the same as with arrays: +) + +--- + foreach (value; aa) { + writeln(value); + } +--- + +$(H5 The $(C foreach) syntax) + +$(P +$(C foreach) consists of three sections: +) + +--- + foreach ($(I names); $(I container_or_range)) { + $(I operations) + } +--- + +$(UL +$(LI $(B $(I container_or_range)) specifies where the elements are. +) + +$(LI $(B $(I operations)) specifies the operations to apply to each element. +) + +$(LI $(B $(I names)) specifies the name of the element and potentially other variables depending on the type of the container or the range. Although the choice of names is up to the programmer, the number of and the types of these names depend on the type of the container. +) +) + +$(H5 $(C continue) and $(C break)) + +$(P +These keywords have the same meaning as they do for the $(C for) loop: $(C continue) moves to the next iteration before completing the rest of the operations for the current element, and $(C break) terminates the loop altogether. +) + +$(H5 $(C foreach) with arrays) + +$(P +When using $(C foreach) with plain arrays and there is a single name specified in the $(I names) section, that name represents the value of the element at each iteration: +) + +--- + foreach (element; array) { + writeln(element); + } +--- + +$(P +When two names are specified in the $(I names) section, they represent an automatic counter and the value of the element, respectively: +) + +--- + foreach (i, element; array) { + writeln(i, ": ", element); + } +--- + +$(P +The counter is incremented automatically by $(C foreach). Although it can be named anything else, $(C i) is a very common name for the automatic counter. +) + +$(H5 $(IX stride, std.range) $(C foreach) with strings and $(C std.range.stride)) + +$(P +Since strings are arrays of characters, $(C foreach) works with strings the same way as it does with arrays: A single name refers to the character, two names refer to the counter and the character, respectively: +) + +--- + foreach (c; "hello") { + writeln(c); + } + + foreach (i, c; "hello") { + writeln(i, ": ", c); + } +--- + +$(P +However, being UTF code units, $(C char) and $(C wchar) iterate over UTF code units, not Unicode code points: +) + +--- + foreach (i, code; "abcçd") { + writeln(i, ": ", code); + } +--- + +$(P +The two UTF-8 code units that make up ç would be accessed as separate elements: +) + +$(SHELL +0: a +1: b +2: c +3: +4: � +5: d +) + +$(P +One way of iterating over Unicode characters of strings in a $(C foreach) loop is $(C stride) from the $(C std.range) module. $(C stride) presents the string as a container that consists of Unicode characters. Its second parameter is the number of steps that it should take as it $(I strides) over the characters: +) + +--- +import std.range; + +// ... + + foreach (c; stride("abcçd", 1)) { + writeln(c); + } +--- + +$(P +Regardless of the character type of the string, $(C stride) always presents its elements as Unicode characters: +) + +$(SHELL +a +b +c +ç +d +) + +$(P +I will explain below why this loop could not include an automatic counter. +) + +$(H5 $(C foreach) with associative arrays) + +$(P +When using $(C foreach) with associative arrays, a single name refers to the value, while two names refer to the key and the value, respectively: +) + +--- + foreach (value; aa) { + writeln(value); + } + + foreach (key, value; aa) { + writeln(key, ": ", value); + } +--- + +$(P +$(IX .byKey, foreach) $(IX .byValue, foreach) $(IX .byKeyValue, foreach) Associative arrays can provide their keys and values as $(I ranges) as well. We will see ranges in $(LINK2 /ders/d.en/ranges.html, a later chapter). $(C .byKey), $(C .byValue), and $(C .byKeyValue) return efficient range objects that are useful in contexts other than $(C foreach) loops as well. +) + +$(P +$(C .byValue) does not bring much benefit in $(C foreach) loops over the regular value iteration above. On the other hand, $(C .byKey) is the only efficient way of iterating over $(I just) the keys of an associative array: +) + +--- + foreach (key; aa$(HILITE .byKey)) { + writeln(key); + } +--- + +$(P +$(C .byKeyValue) provides each key-value element through a variable that is similar to a $(LINK2 /ders/d.en/tuples.html, tuple). The key and the value are accessed separately through the $(C .key) and $(C .value) properties of that variable: +) + +--- + foreach (element; aa$(HILITE .byKeyValue)) { + writefln("The value for key %s is %s", + element$(HILITE .key), element$(HILITE.value)); + } +--- + +$(H5 $(IX number range) $(IX .., number range) $(C foreach) with number ranges) + +$(P +We have seen number ranges before, in the $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features chapter). It is possible to specify a number range in the $(I container_or_range) section: +) + +--- + foreach (number; 10..15) { + writeln(number); + } +--- + +$(P +Remember that 10 would be included in the range but 15 would not be. +) + +$(H5 $(C foreach) with structs, classes, and ranges) + +$(P +$(C foreach) can also be used with objects of user-defined types that define their own iteration in $(C foreach) loops. Structs and classes provide support for $(C foreach) iteration either by their $(C opApply()) member functions, or by a set of $(I range) member functions. We will see these features in later chapters. +) + +$(H5 $(IX counter, foreach) The counter is automatic only for arrays) + +$(P +The automatic counter is provided only when iterating over arrays. There are two options for other containers +) +$(UL +$(LI Taking advantage of $(C std.range.enumerate) as we will see later in $(LINK2 /ders/d.en/foreach_opapply.html, the $(C foreach) with Structs and Classes chapter).) + +$(LI Defining and incrementing a counter variable explicitly:) + +--- + size_t $(HILITE i) = 0; + foreach (element; container) { + // ... + ++i; + } +--- +) + +$(P +Such a variable is needed when counting a specific condition as well. For example, the following code counts only the values that are divisible by 10: +) + +--- +import std.stdio; + +void main() { + auto numbers = [ 1, 0, 15, 10, 3, 5, 20, 30 ]; + + size_t count = 0; + foreach (number; numbers) { + if ((number % 10) == 0) { + $(HILITE ++count); + write(count); + + } else { + write(' '); + } + + writeln(": ", number); + } +} +--- + +$(P +The output: +) + +$(SHELL + + : 1 +1: 0 + : 15 +2: 10 + : 3 + : 5 +3: 20 +4: 30 +) + +$(H5 The copy of the element, not the element itself) + +$(P +The $(C foreach) loop normally provides a copy of the element, not the actual element that is stored in the container. This may be a cause of bugs. +) + +$(P +To see an example of this, let's have a look at the following program that is trying to double the values of the elements of an array: +) + +--- +import std.stdio; + +void main() { + double[] numbers = [ 1.2, 3.4, 5.6 ]; + + writefln("Before: %s", numbers); + + foreach (number; numbers) { + number *= 2; + } + + writefln("After : %s", numbers); +} +--- + +$(P +The output of the program indicates that the assignment made to each element inside the $(C foreach) body does not have any effect on the elements of the container: +) + +$(SHELL +Before: [1.2, 3.4, 5.6] +After : [1.2, 3.4, 5.6] +) + +$(P +$(IX ref, foreach) That is because $(C number) is not an actual element of the array, but a copy of each element. When the actual elements need to be operated on, the name must be defined as a $(I reference) of the actual element, by using the $(C ref) keyword: +) + +--- + foreach ($(HILITE ref) number; numbers) { + number *= 2; + } +--- + +$(P +The new output shows that the assignments now modify the actual elements of the array: +) + +$(SHELL +Before: [1.2, 3.4, 5.6] +After : [2.4, 6.8, 11.2] +) + +$(P +The $(C ref) keyword makes $(C number) an $(I alias) of the actual element at each iteration. As a result, the modifications through $(C number) modify that actual element of the container. +) + +$(H5 The integrity of the container must be preserved) + +$(P +Although it is fine to modify the elements of a container through $(C ref) variables, the structure of a container must not be changed during its iteration. For example, elements must not be removed nor added to the container during a $(C foreach) loop. +) + +$(P +Such modifications may confuse the inner workings of the loop iteration and result in incorrect program states. +) + +$(H5 $(IX foreach_reverse) $(IX loop, foreach_reverse) $(C foreach_reverse) to iterate in the reverse direction) + +$(P +$(C foreach_reverse) works the same way as $(C foreach) except it iterates in the reverse direction: +) + +--- + auto container = [ 1, 2, 3 ]; + + foreach_reverse (element; container) { + writefln("%s ", element); + } +--- + +$(P +The output: +) + +$(SHELL +3 +2 +1 +) + +$(P +The use of $(C foreach_reverse) is not common because the range function $(C retro()) achieves the same goal. We will see $(C retro()) in a later chapter. +) + +$(PROBLEM_TEK + +$(P +We know that associative arrays provide a mapping from keys to values. This mapping is unidirectional: values are accessed by keys but not the other way around. +) + +$(P +Assume that there is already the following associative array: +) + +--- + string[int] names = [ 1:"one", 7:"seven", 20:"twenty" ]; +--- + +$(P +Use that associative array and a $(C foreach) loop to fill another associative array named $(C values). The new associative array should provide values that correspond to names. For example, the following line should print 20: +) + +--- + writeln(values["twenty"]); +--- + +) + + +Macros: + SUBTITLE=foreach Loop + + DESCRIPTION=The foreach loop that is used to iterate over the elements of containers. + + KEYWORDS=d programming language tutorial book foreach diff --git a/target/foreach_opapply.cozum.d b/target/foreach_opapply.cozum.d new file mode 100644 index 0000000..d80ae74 --- /dev/null +++ b/target/foreach_opapply.cozum.d @@ -0,0 +1,203 @@ +Ddoc + +$(COZUM_BOLUMU $(CH4 foreach) with Structs and Classes) + +$(OL + +$(LI The step size must be stored alongside $(C begin) and $(C end), and the element value must be increased by that step size: + +--- +struct NumberRange { + int begin; + int end; + $(HILITE int stepSize;) + + int opApply(int delegate(ref int) dg) const { + int result; + + for (int number = begin; number != end; $(HILITE number += stepSize)) { + result = dg(number); + + if (result) { + break; + } + } + + return result; + } +} + +import std.stdio; + +void main() { + foreach (element; NumberRange(0, 10, 2)) { + write(element, ' '); + } +} +--- + +) + +$(LI + +--- +import std.stdio; +import std.string; + +class Student { + string name; + int id; + + this(string name, int id) { + this.name = name; + this.id = id; + } + + override string toString() { + return format("%s(%s)", name, id); + } +} + +class Teacher { + string name; + string subject; + + this(string name, string subject) { + this.name = name; + this.subject = subject; + } + + override string toString() { + return format("%s teacher %s", subject, name); + } +} + +class School { +private: + + Student[] students; + Teacher[] teachers; + +public: + + this(Student[] students, Teacher[] teachers) { + this.students = students; + this.teachers = teachers; + } + + /* This opApply override will be called when the foreach + * variable is a Student. */ + int opApply(int delegate(ref $(HILITE Student)) dg) { + int result; + + foreach (student; students) { + result = dg(student); + + if (result) { + break; + } + } + + return result; + } + + /* Similarly, this opApply will be called when the foreach + * variable is a Teacher. */ + int opApply(int delegate(ref $(HILITE Teacher)) dg) { + int result; + + foreach (teacher; teachers) { + result = dg(teacher); + + if (result) { + break; + } + } + + return result; + } +} + +void printIndented(T)(T value) { + writeln(" ", value); +} + +void main() { + auto school = new School( + [ new Student("Can", 1), + new Student("Canan", 10), + new Student("Cem", 42), + new Student("Cemile", 100) ], + + [ new Teacher("Nazmiye", "Math"), + new Teacher("Makbule", "Literature") ]); + + writeln("Student loop"); + foreach ($(HILITE Student) student; school) { + printIndented(student); + } + + writeln("Teacher loop"); + foreach ($(HILITE Teacher) teacher; school) { + printIndented(teacher); + } +} +--- + +$(P +The output: +) + +$(SHELL +Student loop + Can(1) + Canan(10) + Cem(42) + Cemile(100) +Teacher loop + Math teacher Nazmiye + Literature teacher Makbule +) + +$(P +As you can see, the implementations of both of the $(C opApply()) overrides are exactly the same, except the slice that they iterate on. To reduce code duplication, the common functionality can be moved to an implementation function template, which then gets called by the two $(C opApply()) overrides: +) + +--- +class School { +// ... + + int opApplyImpl$(HILITE (T))(T[] slice, int delegate(ref T) dg) { + int result; + + foreach (element; slice) { + result = dg(element); + + if (result) { + break; + } + } + + return result; + } + + int opApply(int delegate(ref Student) dg) { + return opApplyImpl(students, dg); + } + + int opApply(int delegate(ref Teacher) dg) { + return opApplyImpl(teachers, dg); + } +} +--- + +) + +) + +Macros: + SUBTITLE=foreach with Structs and Classes Solutions + + DESCRIPTION=Programming in D exercise solutions: foreach with structs and classes. + + KEYWORDS=programming in d tutorial foreach opApply diff --git a/target/foreach_opapply.d b/target/foreach_opapply.d new file mode 100644 index 0000000..0e843dd --- /dev/null +++ b/target/foreach_opapply.d @@ -0,0 +1,666 @@ +Ddoc + +$(DERS_BOLUMU $(IX foreach, user defined type) $(IX struct, foreach) $(IX class, foreach) $(CH4 foreach) with Structs and Classes) + +$(P +As you remember from $(LINK2 /ders/d.en/foreach.html, the $(C foreach) Loop chapter), both how $(C foreach) works and the types and numbers of loop variables that it supports depend on the kind of collection: For slices, $(C foreach) provides access to elements with or without a counter; for associative arrays, to values with or without keys; for number ranges, to the individual values. For library types, $(C foreach) behaves in a way that is specific to that type; e.g. for $(C File), it provides the lines of a file. +) + +$(P +It is possible to define the behavior of $(C foreach) for user-defined types as well. There are two methods of providing this support: +) + +$(UL +$(LI Defining $(I range member functions), which allows using the user-defined type with other range algorithms as well) + +$(LI Defining one or more $(C opApply) member functions) +) + +$(P +Of the two methods, $(C opApply) has priority: If it is defined, the compiler uses $(C opApply), otherwise it considers the range member functions. However, in most cases range member functions are sufficient, easier, and more useful. +) + +$(P +$(C foreach) need not be supported for every type. Iterating over an object makes sense only if that object defines the concept of $(I a collection). +) + +$(P +For example, it may not be clear what elements should $(C foreach) provide when iterating over a class that represents a student, so the class better not support $(C foreach) at all. On the other hand, a design may require that $(C Student) is a collection of grades and $(C foreach) may provide individual grades of the student. +) + +$(P +It depends on the design of the program what types should provide this support and how. +) + +$(H5 $(IX range, foreach) $(C foreach) support by range member functions) + +$(P +$(IX empty) $(IX front) $(IX popFront) We know that $(C foreach) is very similar to $(C for), except that it is more useful and safer than $(C for). Consider the following loop: +) + +--- + foreach (element; myObject) { + // ... expressions ... + } +--- + +$(P +Behind the scenes, the compiler rewrites that $(C foreach) loop as a $(C for) loop, roughly an equivalent of the following one: +) + +--- + for ( ; /* while not done */; /* skip the front element */) { + + auto element = /* the front element */; + + // ... expressions ... + } +--- + +$(P +User-defined types that need to support $(C foreach) can provide three member functions that correspond to the three sections of the previous code: determining whether the loop is over, skipping the front element, and providing access to the front element. +) + +$(P +Those three member functions must be named as $(C empty), $(C popFront), and $(C front), respectively. The code that is generated by the compiler calls those functions: +) + +--- + for ( ; !myObject.empty(); myObject.popFront()) { + + auto element = myObject.front(); + + // ... expressions ... + } +--- + +$(P +These three functions must work according to the following expectations: +) + +$(UL + +$(LI $(C .empty()) must return $(C true) if the loop is over, $(C false) otherwise) + +$(LI $(C .popFront()) must move to the next element (in other words, skip the front element)) + +$(LI $(C .front()) must return the front element) + +) + +$(P +Any type that defines those member functions can be used with $(C foreach). +) + +$(H6 Example) + +$(P +Let's define a $(C struct) that produces numbers within a certain range. In order to be consistent with D's number ranges and slice indexes, let's have the last number be outside of the valid numbers. Under these requirements, the following $(C struct) would work exactly like D's number ranges: +) + +--- +struct NumberRange { + int begin; + int end; + + invariant() { + // There is a bug if begin is greater than end + assert(begin <= end); + } + + bool empty() const { + // The range is consumed when begin equals end + return begin == end; + } + + void popFront() { + // Skipping the first element is achieved by + // incrementing the beginning of the range + ++begin; + } + + int front() const { + // The front element is the one at the beginning + return begin; + } +} +--- + +$(P +$(I $(B Note:) The safety of that implementation depends solely on a single $(C invariant) block. Additional checks could be added to $(C front) and $(C popFront) to ensure that those functions are never called when the range is empty.) +) + +$(P +Objects of that $(C struct) can be used with $(C foreach): +) + +--- + foreach (element; NumberRange(3, 7)) { + write(element, ' '); + } +--- + +$(P +$(C foreach) uses those three functions behind the scenes and iterates until $(C empty()) returns $(C true): +) + +$(SHELL_SMALL +3 4 5 6 +) + +$(H6 $(IX retro, std.range) $(C std.range.retro) to iterate in reverse) + +$(P +$(IX save) $(IX back) $(IX popBack) The $(C std.range) module contains many range algorithms. $(C retro) is one of those algorithms, which iterates a range in reverse order. It requires two additional range member functions: +) + +$(UL + +$(LI $(C .popBack()) must move to the element that is one before the end (skips the last element)) + +$(LI $(C .back()) must return the last element) + +) + +$(P +However, although not directly related to reverse iteration, for $(C retro) to consider those functions at all, there must be one more function defined: +) + +$(UL +$(LI $(C .save()) must return a copy of this object) +) + +$(P +We will learn more about these member functions later in $(LINK2 /ders/d.en/ranges.html, the Ranges chapter). +) + +$(P +These three additional member functions can trivially be defined for $(C NumberRange): +) + +--- +struct NumberRange { +// ... + + void popBack() { + // Skipping the last element is achieved by + // decrementing the end of the range. + --end; + } + + int back() const { + // As the 'end' value is outside of the range, the + // last element is one less than that + return end - 1; + } + + NumberRange save() const @property { + // Returning a copy of this struct object + return this; + } +} +--- + +$(P +Objects of this type can now be used with $(C retro): +) + +--- +import std.range; + +// ... + + foreach (element; NumberRange(3, 7)$(HILITE .retro)) { + write(element, ' '); + } +--- + +$(P +The output of the program is now in reverse: +) + +$(SHELL_SMALL +6 5 4 3 +) + +$(H5 $(IX opApply) $(IX opApplyReverse) $(C foreach) support by $(C opApply) and $(C opApplyReverse) member functions) + +$(P +$(IX foreach_reverse) Everything that is said about $(C opApply) in this section is valid for $(C opApplyReverse) as well. $(C opApplyReverse) is for defining the behaviors of objects in the $(C foreach_reverse) loops. +) + +$(P +The member functions above allow using objects as ranges. That method is more suitable when there is only one sensible way of iterating over a range. For example, it would be easy to provide access to individual students of a $(C Students) type. +) + +$(P +On the other hand, sometimes it makes more sense to iterate over the same object in different ways. We know this from associative arrays where it is possible to access either only to the values or to both the keys and the values: +) + +--- + string[string] dictionary; // from English to Turkish + + // ... + + foreach (inTurkish; dictionary) { + // ... only values ... + } + + foreach (inEnglish, inTurkish; dictionary) { + // ... keys and values ... + } +--- + +$(P +$(C opApply) allows using user-defined types with $(C foreach) in various and sometimes more complex ways. Before learning how to define $(C opApply), we must first understand how it is called automatically by $(C foreach). +) + +$(P +The program execution alternates between the expressions inside the $(C foreach) block and the expressions inside the $(C opApply()) function. First the $(C opApply()) member function gets called, and then $(C opApply) makes an explicit call to the $(C foreach) block. They alternate in that way until the loop eventually terminates. This process is based on a $(I convention), which I will explain soon. +) + +$(P +Let's first observe the structure of the $(C foreach) loop one more time: +) + +--- +// The loop that is written by the programmer: + + foreach (/* loop variables */; myObject) { + // ... expressions inside the foreach block ... + } +--- + +$(P +$(IX delegate, foreach) If there is an $(C opApply()) member function that matches the loop variables, then the $(C foreach) block becomes a delegate, which is then passed to $(C opApply()). +) + +$(P +Accordingly, the loop above is converted to the following code behind the scenes. The curly brackets that define the body of the delegate are highlighted: +) + +--- +// The code that the compiler generates behind the scenes: + + myObject.opApply(delegate int(/* loop variables */) $(HILITE {) + // ... expressions inside the foreach block ... + return hasBeenTerminated; + $(HILITE })); +--- + +$(P +In other words, the $(C foreach) loop is replaced by a $(C delegate) that is passed to $(C opApply()). Before showing an example, here are the requirements and expectations of this convention that $(C opApply()) must observe: +) + +$(OL + +$(LI The body of the $(C foreach) loop becomes the body of the delegate. $(C opApply) must call this delegate for each iteration.) + +$(LI The loop variables become the parameters of the delegate. $(C opApply()) must define these parameters as $(C ref).) + +$(LI The return type of the delegate is $(C int). Accordingly, the compiler injects a $(C return) statement at the end of the delegate, which determines whether the loop has been terminated (by a $(C break) or a $(C return) statement): If the return value is zero, the iteration must continue, otherwise it must terminate.) + +$(LI The actual iteration happens inside $(C opApply()).) + +$(LI $(C opApply()) must return the same value that is returned by the delegate.) + +) + +$(P +The following is a definition of $(C NumberRange) that is implemented according to that convention: +) + +--- +struct NumberRange { + int begin; + int end; + // (2) (1) + int opApply(int delegate(ref int) operations) const { + int result = 0; + + for (int number = begin; number != end; ++number) { // (4) + result = operations(number); // (1) + + if (result) { + break; // (3) + } + } + + return result; // (5) + } +} +--- + +$(P +This definition of $(C NumberRange) can be used with $(C foreach) in exactly the same way as before: +) + +--- + foreach (element; NumberRange(3, 7)) { + write(element, ' '); + } +--- + +$(P +The output is the same as the one produced by range member functions: +) + +$(SHELL_SMALL +3 4 5 6 +) + +$(H6 Overloading $(C opApply) to iterate in different ways) + +$(P +It is possible to iterate over the same object in different ways by defining overloads of $(C opApply()) that take different types of delegates. The compiler calls the overload that matches the particular set of loop variables. +) + +$(P +As an example, let's make it possible to iterate over $(C NumberRange) by two loop variables as well: +) + +--- + foreach ($(HILITE first, second); NumberRange(0, 15)) { + writef("%s,%s ", first, second); + } +--- + +$(P +Note how it is similar to the way associative arrays are iterated over by both keys and values. +) + +$(P +For this example, let's require that when a $(C NumberRange) object is iterated by two variables, it should provide two consecutive values and that it arbitrarily increases the values by 5. So, the loop above should produce the following output: +) + +$(SHELL_SMALL +0,1 5,6 10,11 +) + +$(P +This is achieved by an additional definition of $(C opApply()) that takes a delegate that takes two parameters. $(C opApply()) must call that delegate with two values: +) + +--- + int opApply(int delegate$(HILITE (ref int, ref int)) dg) const { + int result = 0; + + for (int i = begin; (i + 1) < end; i += 5) { + int first = i; + int second = i + 1; + + result = dg($(HILITE first, second)); + + if (result) { + break; + } + } + + return result; + } +--- + +$(P +When there are two loop variables, this overload of $(C opApply()) gets called. +) + +$(P +There may be as many overloads of $(C opApply()) as needed. +) + +$(P +It is possible and sometimes necessary to give hints to the compiler on what overload to choose. This is done by specifying types of the loop variables explicitly. +) + +$(P +For example, let's assume that there is a $(C School) type that supports iterating over the teachers and the students separately: +) + +--- +class School { + int opApply(int delegate(ref $(HILITE Student)) dg) const { + // ... + } + + int opApply(int delegate(ref $(HILITE Teacher)) dg) const { + // ... + } +} +--- + +$(P +To indicate the desired overload, the loop variable must be specified: +) + +--- + foreach ($(HILITE Student) student; school) { + // ... + } + + foreach ($(HILITE Teacher) teacher; school) { + // ... + } +--- + +$(H5 $(IX loop counter) $(IX counter, loop) Loop counter) + +$(P +The convenient loop counter of slices is not automatic for other types. Loop counter can be achieved for user-defined types in different ways depending on whether the $(C foreach) support is provided by range member functions or by $(C opApply) overloads. +) + +$(H6 Loop counter with range functions) + +$(P +$(IX enumerate, std.range) If $(C foreach) support is provided by range member functions, then a loop counter can be achieved simply by $(C enumerate) from the $(C std.range) module: +) + +--- +import std.range; + +// ... + + foreach ($(HILITE i), element; NumberRange(42, 47)$(HILITE .enumerate)) { + writefln("%s: %s", i, element); + } +--- + +$(P +$(C enumerate) is a range that produces consecutive numbers starting by default from 0. $(C enumerate) pairs each number with the elements of the range that it is applied on. As a result, the numbers that $(C enumerate) generates and the elements of the actual range ($(C NumberRange) in this case) appear in lockstep as loop variables: +) + +$(SHELL_SMALL +0: 42 +1: 43 +2: 44 +3: 45 +4: 46 +) + +$(H6 Loop counter with $(C opApply)) + +$(P +On the other hand, if $(C foreach) support is provided by $(C opApply()), then the loop counter must be defined as a separate parameter of the delegate, suitably as type $(C size_t). Let's see this on a $(C struct) that represents a colored polygon. +) + +$(P +As we have already seen above, an $(C opApply()) that provides access to the points of this polygon can be implemented $(I without) a counter as in the following code: +) + +--- +import std.stdio; + +enum Color { blue, green, red } + +struct Point { + int x; + int y; +} + +struct Polygon { + Color color; + Point[] points; + + int $(HILITE opApply)(int delegate(ref const(Point)) dg) const { + int result = 0; + + foreach (point; points) { + result = dg(point); + + if (result) { + break; + } + } + + return result; + } +} + +void main() { + auto polygon = Polygon(Color.blue, + [ Point(0, 0), Point(1, 1) ] ); + + foreach (point; polygon) { + writeln(point); + } +} +--- + +$(P +Note that $(C opApply()) itself is implemented by a $(C foreach) loop. As a result, the $(C foreach) inside $(C main()) ends up making indirect use of a $(C foreach) over the $(C points) member. +) + +$(P +Also note that the type of the delegate parameter is $(C ref const(Point)). This means that this definition of $(C opApply()) does not allow modifying the $(C Point) elements of the polygon. In order to allow user code to modify the elements, both the $(C opApply()) function itself and the delegate parameter must be defined without the $(C const) specifier. +) + +$(P +The output: +) + +$(SHELL +const(Point)(0, 0) +const(Point)(1, 1) +) + +$(P +Naturally, trying to use this definition of $(C Polygon) with a loop counter would cause a compilation error: +) + +--- + foreach ($(HILITE i), point; polygon) { $(DERLEME_HATASI) + writefln("%s: %s", i, point); + } +--- + +$(P +The compilation error: +) + +$(SHELL +Error: cannot uniquely infer foreach argument types +) + +$(P +For that to work, another $(C opApply()) overload that supports a counter must be defined: +) + +--- + int opApply(int delegate($(HILITE ref size_t), + ref const(Point)) dg) const { + int result = 0; + + foreach ($(HILITE i), point; points) { + result = dg($(HILITE i), point); + + if (result) { + break; + } + } + + return result; + } +--- + +$(P +This time the $(C foreach) variables are matched to the new $(C opApply()) overload and the program prints the desired output: +) + +$(SHELL +0: const(Point)(0, 0) +1: const(Point)(1, 1) +) + +$(P +Note that this implementation of $(C opApply()) takes advantage of the automatic counter over the $(C points) member. ($(I Although the delegate variable is defined as $(C ref size_t), the $(C foreach) loop inside $(C main()) cannot modify the counter variable over $(C points))). +) + +$(P +When needed, the loop counter can be defined and incremented explicitly as well. For example, because the following $(C opApply()) is implemented by a $(C while) statement it must define a separate variable for the counter: +) + +--- + int opApply(int delegate(ref size_t, + ref const(Point)) dg) const { + int result = 0; + bool isDone = false; + + $(HILITE size_t counter = 0;) + while (!isDone) { + // ... + + result = dg(counter, nextElement); + + if (result) { + break; + } + + ++counter; + } + + return result; + } +--- + +$(H5 Warning: The collection must not mutate during the iteration) + +$(P +Regardless of whether the iteration support is provided by the range member functions or by $(C opApply()) functions, the collection itself must not mutate. New elements must not be added to the container and the existing elements must not be removed. (Mutating the existing elements is allowed.) +) + +$(P +Doing otherwise is undefined behavior. +) + +$(PROBLEM_COK + +$(PROBLEM +Design a $(C struct) that works similarly to $(C NumberRange), which also supports specifying the step size. The step size can be the third member: + +--- + foreach (element; NumberRange(0, 10, $(HILITE 2))) { + write(element, ' '); + } +--- + +$(P +The expected output of the code above is every second number from 0 to 10: +) + +$(SHELL_SMALL +0 2 4 6 8 +) + +) + +$(PROBLEM +Implement the $(C School) class that was mentioned in the text in a way that it provides access to students or teachers depending on the $(C foreach) variable. +) + +) + +Macros: + SUBTITLE=Structs and Classes with foreach + + DESCRIPTION=Defining the way user-defined types behave with the foreach loop. + + KEYWORDS=d programming language tutorial book foreach opApply opApplyReverse diff --git a/target/foreword1.d b/target/foreword1.d new file mode 100644 index 0000000..11484ca --- /dev/null +++ b/target/foreword1.d @@ -0,0 +1,16 @@ +Ddoc + +$(DIV_CLASS foreword, + +$(DERS_BOLUMU_CLASS foreword, Foreword by Walter Bright) + +$(P +$(I by Walter Bright) +) + +) + +Macros: + SUBTITLE = Foreword by Walter Bright + DESCRIPTION= + KEYWORDS= diff --git a/target/foreword2.d b/target/foreword2.d new file mode 100644 index 0000000..1444030 --- /dev/null +++ b/target/foreword2.d @@ -0,0 +1,40 @@ +Ddoc + +$(DIV_CLASS foreword, + +$(DERS_BOLUMU_CLASS foreword, Foreword by Andrei Alexandrescu) + +$(P +Those of us who know Ali might notice his book on D is imbued with its author's personality: straightforward, patient, and nice without being pandering. +) + +$(P +There is purpose in every sentence, and with each, a step forward is being made; not too fast, and not too slow. "Note that $(C opApply()) itself is implemented by a $(C foreach) loop. As a result, the $(C foreach) inside $(C main()) ends up making indirect use of a $(C foreach) over the $(C points) member." And so it goes, in just as many words as needed. And in the right order, too; Ali does an admirable job at presenting language concepts – which especially to a beginner overwhelmingly come "in parallel" – in a sequential manner. +) + +$(P +But there's another thing I like most about "Programming in D": it's a good book for learning programming $(I in general). See, a good introductory book on Haskell implicitly teaches functional programming along the way; one on C would come with systems programming notions in tow; one on Python with scripting, and so on. What would, then, a good introductory text to D teach in subtext? At best, Programming with a capital P. +) + +$(P +D fosters a "use the right tool for the job" attitude, and allows its user to tap into a wide range of programming techniques, without throwing too many idiosyncrasies in the way. The most fun way to approach coding in D is with an open mind, because for each design that starts to get stilted there is opportunity to mold it into the right design choosing a different implementation, approach, or paradigm altogether. To best choose what's most fitting, the engineer must know the gamut of what's possible – and "Programming in D" is a great way to equip one's intellect with that knowledge. Internalizing it helps not only writing good code in D, but writing good code, period. +) + +$(P +There's good tactical advice, too, to complement the teaching of programming and language concepts. Timeless teaching on avoiding code duplication, choosing good names, aiming for good decomposition, and more – it's all there, quick-and-dirty hacks iteratively annealed into robust solutions, just as they should in normal practice. Instead of falling for getting things done quickly, "Programming in D" focuses on getting things done properly, to the lasting benefit of its reader. +) + +$(P +I've long suspected D is a good first programming language to learn. It exposes its user to a variety of concepts – systems, functional, object oriented, generic, generative – candidly and without pretense. And so does Ali's book, which seems to me an excellent realization of that opportunity. +$(BR) +$(BR) +Andrei Alexandrescu$(BR) +San Francisco, $(I May 2015) +) + +) + +Macros: + SUBTITLE = Foreword by Andrei Alexandrescu + DESCRIPTION= + KEYWORDS= diff --git a/target/formatted_input.cozum.d b/target/formatted_input.cozum.d new file mode 100644 index 0000000..070fdfe --- /dev/null +++ b/target/formatted_input.cozum.d @@ -0,0 +1,28 @@ +Ddoc + +$(COZUM_BOLUMU Formatted Input) + +$(P +Using a format string where the parts of the date are replaced with $(C %s) would be sufficient: +) + +--- +import std.stdio; + +void main() { + int year; + int month; + int day; + + readf("%s.%s.%s", &year, &month, &day); + + writeln("Month: ", month); +} +--- + +Macros: + SUBTITLE=Formatted Input Solutions + + DESCRIPTION=Programming in D exercise solutions: Formatted input + + KEYWORDS=programming in d tutorial formatted input solution diff --git a/target/formatted_input.d b/target/formatted_input.d new file mode 100644 index 0000000..26bfe1d --- /dev/null +++ b/target/formatted_input.d @@ -0,0 +1,160 @@ +Ddoc + +$(DERS_BOLUMU $(IX input, formatted) $(IX formatted input) Formatted Input) + +$(P +It is possible to specify the format of the data that is expected at the input. The format specifies both the data that is to be read and the characters that should be ignored. +) + +$(P +D's input format specifiers are similar to the ones present in the C language. +) + +$(P +As we have already been using in the previous chapters, the format specifier $(STRING " %s") reads the data according to the type of the variable. For example, as the type of the following variable is $(C double), the characters at the input would be read as floating point number: +) + +--- + double number; + + readf(" %s", &number); +--- + +$(P +The format string can contain three types of information: +) + +$(UL +$(LI $(B The space character): Indicates $(I zero) or more whitespace characters at the input and specifies that all of those characters should be read and ignored.) + +$(LI $(B Format specifier): Similar to the output format specifiers, input format specifiers start with the $(C %) character and determine the format of the data that is to be read.) + +$(LI $(B Any other character): Indicates the characters that are expected at the input as is, which should be read and ignored.) + +) + +$(P +The format string makes it possible to select specific information from the input and ignore the others. +) + +$(P +Let's have a look at an example that uses all of the three types of information in the format string. Let's assume that the student number and the grade are expected to appear at the input in the following format: +) + +$(SHELL +number:123 grade:90 +) + +$(P +Let's further assume that the tags $(C number:) and $(C grade:) must be ignored. The following format string would $(I select) the values of number and grade and would ignore the other characters: +) + +--- + int number; + int grade; + readf("number:%s grade:%s", &number, &grade); +--- + +$(P +The characters that are highlighted in $(STRING "$(HILITE number:)%s $(HILITE grade:)%s") must appear at the input exactly as specified; $(C readf()) reads and ignores them. +) + +$(P +The single space character that appears in the format string above would cause all of the whitespace characters that appear exactly at that position to be read and ignored. +) + +$(P +As the $(C %) character has a special meaning in format strings, when that character itself needs to be read and ignored, it must be written twice in the format string as $(C %%). +) + +$(P +Reading a single line of data from the input has been recommended as $(C strip(readln())) in the $(LINK2 /ders/d.en/strings.html, Strings chapter). Instead of that method, a $(C \n) character at the end of the format string can achieve a similar goal: +) + +--- +import std.stdio; + +void main() { + write("First name: "); + string firstName; + readf(" %s\n", &firstName); // ← \n at the end + + write("Last name : "); + string lastName; + readf(" %s\n", &lastName); // ← \n at the end + + write("Age : "); + int age; + readf(" %s", &age); + + writefln("%s %s (%s)", firstName, lastName, age); +} +--- + +$(P +The $(C \n) characters at the ends of the format strings when reading $(C firstName) and $(C lastName) would cause the new-line characters to be read from the input and to be ignored. However, potential whitespace characters at the ends of the strings may still need to be removed by $(C strip()). +) + +$(H5 Format specifier characters) + +$(P +The way the data should be read is specified with the following format specifier characters: +) + +$(P $(IX %d, input) $(C d): Read an integer in the decimal system.) + +$(P $(IX %o, input) $(C o): Read an integer in the octal system.) + +$(P $(IX %x, input) $(C x): Read an integer in the hexadecimal system.) + +$(P $(IX %f, input) $(C f): Read a floating point number.) + +$(P $(IX %s, input) $(C s): Read according to the type of the variable. This is the most commonly used specifier.) + +$(P $(IX %c) $(C c): Read a single character. This specifier allows reading whitespace characters as well. (It cancels the ignore behavior.) +) + +$(P +For example, if the input contains "23 23 23", the values would be read differently according to different format specifiers: +) + +--- + int number_d; + int number_o; + int number_x; + + readf(" %d %o %x", &number_d, &number_o, &number_x); + + writeln("Read with %d: ", number_d); + writeln("Read with %o: ", number_o); + writeln("Read with %x: ", number_x); +--- + +$(P +Although the input contains three sets of "23" characters, the values of the variables are different: +) + +$(SHELL +Read with %d: 23 +Read with %o: 19 +Read with %x: 35 +) + +$(P +$(I $(B Note:) Very briefly, "23" is equal to 2x8+3=19 in the octal system and to 2x16+3=35 in the hexadecimal system.) +) + +$(PROBLEM_TEK + +$(P +Assume that the input contains the date in the format $(I year.month.day). Write a program that prints the number of the month. For example, if the input is $(C 2009.09.30), the output should be $(C 9). +) + +) + +Macros: + SUBTITLE=Formatted Input + + DESCRIPTION=Reading the input in certain format. + + KEYWORDS=d programming language tutorial book format input diff --git a/target/formatted_output.cozum.d b/target/formatted_output.cozum.d new file mode 100644 index 0000000..62ccc2a --- /dev/null +++ b/target/formatted_output.cozum.d @@ -0,0 +1,56 @@ +Ddoc + +$(COZUM_BOLUMU Formatted Output) + +$(OL + +$(LI We have already seen that this is trivial with format specifiers: + +--- +import std.stdio; + +void main() { + writeln("(Enter 0 to exit the program.)"); + + while (true) { + write("Please enter a number: "); + long number; + readf(" %s", &number); + + if (number == 0) { + break; + } + + writefln("%1$d <=> %1$#x", number); + } +} +--- + +) + +$(LI +Remembering that the $(C %) character must appear twice in the format string to be printed as itself: + +--- +import std.stdio; + +void main() { + write("Please enter the percentage value: "); + double percentage; + readf(" %s", &percentage); + + writefln("%%%.2f", percentage); +} +--- + +) + +) + + +Macros: + SUBTITLE=Formatted Output Solutions + + DESCRIPTION=Programming in D exercise solutions: Formatted output + + KEYWORDS=programming in d tutorial formatted output solution diff --git a/target/formatted_output.d b/target/formatted_output.d new file mode 100644 index 0000000..2023753 --- /dev/null +++ b/target/formatted_output.d @@ -0,0 +1,661 @@ +Ddoc + +$(DERS_BOLUMU $(IX output, formatted) $(IX formatted output) Formatted Output) + +$(P +This chapter is about features of the $(C std.format) module, not about the core features of the D language. +) + +$(P +$(IX std) $(IX Phobos) Like all modules that have the prefix $(C std), $(C std.format) is a module inside Phobos, the standard library of D. There is not enough space to fully explore Phobos in this book. +) + +$(P +D's input and output format specifiers are similar to the ones in the C language. +) + +$(P +Before going further, I would like to summarize the format specifiers and flags, for your reference: +) + +$(MONO +$(B Flags) (can be used together) + - flush left + + print the sign + # print in the alternative way + 0 print zero-filled + $(I space) print space-filled + +$(B Format Specifiers) + s default + b binary + d decimal + o octal + x,X hexadecimal + f,F floating point in the standard decimal notation + e,E floating point in scientific notation + a,A floating point in hexadecimal notation + g,G as e or f + + ( element format start + ) element format end + | element delimiter +) + +$(P +We have been using functions like $(C writeln) with multiple parameters as necessary to print the desired output. The parameters would be converted to their string representations and then + sent to the output. +) + +$(P +Sometimes this is not sufficient. The output may have to be in a very specific format. Let's look at the following code that is used to print items of an invoice: +) + +--- + items ~= 1.23; + items ~= 45.6; + + for (int i = 0; i != items.length; ++i) { + writeln("Item ", i + 1, ": ", items[i]); + } +--- + +$(P +The output: +) + +$(SHELL +Item 1: 1.23 +Item 2: 45.6 +) + +$(P +Despite the information being correct, we may be required to print it in a different format. For example, maybe the decimal marks (the dots, in this case) must line up and we must ensure that there always are two digits after the decimal mark, as in the following output: +) + +$(SHELL +Item 1: 1.23 +Item 2: 45.60 +) + +$(P +Formatted output is useful in such cases. The output functions that we have been using so far have counterparts that contain the letter $(C f) in their names: $(C writef()) and $(C writefln()). The letter $(C f) is short for $(I formatted). The first parameter of these functions is a $(I format string) that describes how the other parameters should be printed. +) + +$(P +For example, $(C writefln()) can produce the desired output above with the following format string: +) + +--- + writefln("Item %d:%9.02f", i + 1, items[i]); +--- + +$(P +The format string contains regular characters that are passed to the output as is, as well as special format specifiers that correspond to each parameter that is to be printed. Format specifiers start with the $(C %) character and end with a $(I format character). The format string above has two format specifiers: $(C %d) and $(C %9.02f). +) + +$(P +Every specifier is associated with the respective parameter, usually in order of appearance. For example, $(C %d) is associated with $(C i + 1) and $(C %9.02f) is associated with $(C items[i]). Every specifier specifies the format of the parameter that it corresponds to. (Format specifiers may have parameter numbers as well. This will be explained later in the chapter.) +) + +$(P +All of the other characters of the format string that are not part of format specifiers are printed as is. Such $(I regular) characters of the format specifier above are highlighted in $(C "$(HILITE Item )%d$(HILITE :)%9.02f"). +) + +$(P +Format specifiers consist of six parts, most of which are optional. The part named $(I position) will be explained later below. The other five are the following: ($(I $(B Note:) The spaces between these parts are inserted here to help with readability; they are not part of the specifiers.)) +) + +$(MONO + % $(I$(C flags width precision format_character)) +) + +$(P +The $(C %) character at the beginning and the format character at the end are required; the others are optional. +) + +$(P +Because $(C %) has a special meaning in format strings, when we need to print a $(C %) as a regular character, we must type it as $(C %%). +) + +$(H5 $(I format_character)) + +$(P $(IX %b) $(C b): An integer parameter is printed in the binary system. +) + +$(P $(IX %o, output) $(C o): An integer parameter is printed in the octal system. +) + +$(P $(IX %x, output) $(IX %X) $(C x) and $(C X): An integer parameter is printed in the hexadecimal system; with lowercase letters when using $(C x) and with uppercase letters when using $(C X). +) + +$(P $(IX %d, output) $(C d): An integer parameter is printed in the decimal system; a negative sign is also printed if it is a signed type and the value is less than zero. +) + +--- + int value = 12; + + writefln("Binary : %b", value); + writefln("Octal : %o", value); + writefln("Hexadecimal: %x", value); + writefln("Decimal : %d", value); +--- + +$(SHELL +Binary : 1100 +Octal : 14 +Hexadecimal: c +Decimal : 12 +) + +$(P $(IX %e) $(C e): A floating point parameter is printed according to the following rules. +) + +$(UL +$(LI a single digit before the decimal mark) +$(LI a decimal mark if $(I precision) is nonzero) +$(LI the required digits after the decimal mark, the number of which is determined by $(I precision) (default precision is 6)) +$(LI the $(C e) character (meaning "10 to the power of")) +$(LI the $(C -) or $(C +) character, depending on whether the exponent is less than or greater than zero) +$(LI the exponent, consisting of at least two digits) +) + +$(P $(IX %E) $(C E): Same as $(C e), with the exception of outputting the character $(C E) instead of $(C e). +) + +$(P $(IX %f, output) $(IX %F) $(C f) and $(C F): A floating point parameter is printed in the decimal system; there is at least one digit before the decimal mark and the default precision is 6 digits after the decimal mark. +) + +$(P $(IX %g) $(C g): Same as $(C f) if the exponent is between -5 and $(I precision); otherwise same as $(C e). $(I precision) does not specify the number of digits after the decimal mark, but the significant digits of the entire value. If there are no significant digits after the decimal mark, then the decimal mark is not printed. The rightmost zeros after the decimal mark are not printed. +) + +$(P $(IX %G) $(C G): Same as $(C g), with the exception of outputting the characters $(C E) or $(C F). +) + +$(P $(IX %a) $(C a): A floating point parameter is printed in the hexadecimal floating point notation: +) + +$(UL +$(LI the characters $(C 0x)) +$(LI a single hexadecimal digit) +$(LI a decimal mark if $(I precision) is nonzero) +$(LI the required digits after the decimal mark, the number of which is determined by $(I precision); if no $(I precision) is specified, then as many digits as necessary) +$(LI the $(C p) character (meaning "2 to the power of")) +$(LI the $(C -) or $(C +) character, depending on whether the exponent is less than or greater than zero) +$(LI the exponent, consisting of at least one digit (the exponent of the value 0 is 0)) +) + +$(P $(IX %A) $(C A): Same as $(C a), with the exception of outputting the characters $(C 0X) and $(C P). +) + +--- + double value = 123.456789; + + writefln("with e: %e", value); + writefln("with f: %f", value); + writefln("with g: %g", value); + writefln("with a: %a", value); +--- + +$(SHELL +with e: 1.234568e+02 +with f: 123.456789 +with g: 123.457 +with a: 0x1.edd3c07ee0b0bp+6 +) + +$(P $(IX %s, output) $(C s): The value is printed in the same way as in regular output, according to the type of the parameter: +) + +$(UL + +$(LI $(C bool) values as $(C true) or $(C false) +) +$(LI integer values same as $(C %d) +) +$(LI floating point values same as $(C %g) +) +$(LI strings in UTF-8 encoding; $(I precision) determines the maximum number of bytes to use (remember that in UTF-8 encoding, the number of bytes is not the same as the number of characters; for example, the string "ağ" has 2 characters, consisting a total of 3 bytes) +) +$(LI struct and class objects as the return value of the $(C toString()) member functions of their types; $(I precision) determines the maximum number of bytes to use +) +$(LI arrays as their element values, side by side +) + +) + +--- + bool b = true; + int i = 365; + double d = 9.87; + string s = "formatted"; + auto o = File("test_file", "r"); + int[] a = [ 2, 4, 6, 8 ]; + + writefln("bool : %s", b); + writefln("int : %s", i); + writefln("double: %s", d); + writefln("string: %s", s); + writefln("object: %s", o); + writefln("array : %s", a); +--- + +$(SHELL +bool : true +int : 365 +double: 9.87 +string: formatted +object: File(55738FA0) +array : [2, 4, 6, 8] +) + +$(H5 $(IX width, output) $(I width)) + +$(P +$(IX *, formatted output) This part determines the width of the field that the parameter is printed in. If the width is specified as the character $(C *), then the actual width value is read from the next parameter (that parameter must be an $(C int)). If width is a negative value, then the $(C -) flag is assumed. +) + +--- + int value = 100; + + writefln("In a field of 10 characters:%10s", value); + writefln("In a field of 5 characters :%5s", value); +--- + +$(SHELL +In a field of 10 characters: 100 +In a field of 5 characters : 100 +) + +$(H5 $(IX precision, output) $(I precision)) + +$(P +Precision is specified after a dot in the format specifier. For floating point types, it determines the precision of the printed representation of the values. If the precision is specified as the character $(C *), then the actual precision is read from the next parameter (that parameter must be an $(C int)). Negative precision values are ignored. +) + +--- + double value = 1234.56789; + + writefln("%.8g", value); + writefln("%.3g", value); + writefln("%.8f", value); + writefln("%.3f", value); +--- + +$(SHELL +1234.5679 +1.23e+03 +1234.56789000 +1234.568 +) + +--- + auto number = 0.123456789; + writefln("Number: %.*g", 4, number); +--- + +$(SHELL +Number: 0.1235 +) + +$(H5 $(IX flags, output) $(I flags)) + +$(P +More than one flag can be specified. +) + +$(P $(C -): the value is printed left-aligned in its field; this flag cancels the $(C 0) flag +) + +--- + int value = 123; + + writefln("Normally right-aligned:|%10d|", value); + writefln("Left-aligned :|%-10d|", value); +--- + +$(SHELL +Normally right-aligned:| 123| +Left-aligned :|123 | +) + +$(P $(C +): if the value is positive, it is prepended with the $(C +) character; this flag cancels the $(I space) flag +) + +--- + writefln("No effect for negative values : %+d", -50); + writefln("Positive value with the + flag : %+d", 50); + writefln("Positive value without the + flag: %d", 50); +--- + +$(SHELL +No effect for negative values : -50 +Positive value with the + flag : +50 +Positive value without the + flag: 50 +) + +$(P $(C #): prints the value in an $(I alternate) form depending on the $(I format_character) +) + +$(UL +$(LI $(C o): the first character of the octal value is always printed as 0) + +$(LI $(C x) and $(C X): if the value is not zero, it is prepended with $(C 0x) or $(C 0X)) + +$(LI floating points: a decimal mark is printed even if there are no significant digits after the decimal mark) + +$(LI $(C g) and $(C G): even the insignificant zero digits after the decimal mark are printed) +) + +--- + writefln("Octal starts with 0 : %#o", 1000); + writefln("Hexadecimal starts with 0x : %#x", 1000); + writefln("Contains decimal mark even when unnecessary: %#g", 1f); + writefln("Rightmost zeros are printed : %#g", 1.2); +--- + +$(SHELL +Octal starts with 0 : 01750 +Hexadecimal starts with 0x : 0x3e8 +Contains decimal mark even when unnecessary: 1.00000 +Rightmost zeros are printed : 1.20000 +) + +$(P $(C 0): the field is padded with zeros (unless the value is $(C nan) or $(C infinity)); if $(I precision) is also specified, this flag is ignored +) + +--- + writefln("In a field of 8 characters: %08d", 42); +--- + +$(SHELL +In a field of 8 characters: 00000042 +) + +$(P $(I space) character: if the value is positive, a space character is prepended to align the negative and positive values) + +--- + writefln("No effect for negative values: % d", -34); + writefln("Positive value with space : % d", 56); + writefln("Positive value without space : %d", 56); +--- + +$(SHELL +No effect for negative values: -34 +Positive value with space : 56 +Positive value without space : 56 +) + + +$(H5 $(IX %1$) $(IX positional parameter, output) $(IX $, formatted output) Positional parameters) + +$(P +We have seen above that the parameters are associated one by one with the specifiers in the format string. It is also possible to use position numbers within format specifiers. This enables associating the specifiers with specific parameters. Parameters are numbered in increasing fashion, starting with 1. The parameter numbers are specified immediately after the $(C %) character, followed by a $(C $): +) + +$(MONO + % $(I$(C $(HILITE position$) flags width precision format_character)) +) + +$(P +An advantage of positional parameters is being able to use the same parameter in more than one place in the same format string: +) + +--- + writefln("%1$d %1$x %1$o %1$b", 42); +--- + +$(P +The format string above uses the parameter numbered 1 within four specifiers to print it in decimal, hexadecimal, octal, and binary formats: +) + +$(SHELL +42 2a 52 101010 +) + +$(P +Another application of positional parameters is supporting multiple natural languages. When referred by position numbers, parameters can be moved anywhere within the specific format string for a given human language. For example, the number of students of a given classroom can be printed as in the following: +) + +--- + writefln("There are %s students in room %s.", count, room); +--- + +$(SHELL +There are 20 students in room 1A. +) + +$(P +Let's assume that the program must also support Turkish. In this case the format string needs to be selected according to the active language. The following method takes advantage of the ternary operator: +) + +--- + auto format = (language == "en" + ? "There are %s students in room %s." + : "%s sınıfında %s öğrenci var."); + + writefln(format, count, room); +--- + +$(P +Unfortunately, when the parameters are associated one by one, the classroom and student count information appear in reverse order in the Turkish message; the room information is where the count should be and the count is where the room should be: +) + +$(SHELL +20 sınıfında 1A öğrenci var. $(SHELL_NOTE_WRONG Wrong: means "room 20", and "1A students"!) +) + +$(P +To avoid this, the parameters can be specified by numbers, such as $(C 1$) and $(C 2$), to associate each specifier with the exact parameter: +) + +--- + auto format = (language == "en" + ? "There are %1$s students in room %2$s." + : "%2$s sınıfında %1$s öğrenci var."); + + writefln(format, count, room); +--- + +$(P +Now the parameters appear in the proper order, regardless of the language selected: +) + +$(SHELL +There are 20 students in room 1A. +) + +$(SHELL +1A sınıfında 20 öğrenci var. +) + +$(H5 $(IX %$(PARANTEZ_AC)) $(IX %$(PARANTEZ_KAPA)) Formatted element output) + +$(P +Format specifiers between $(STRING %$(PARANTEZ_AC)) and $(STRING %$(PARANTEZ_KAPA)) are applied to every element of a container (e.g. an array or a range): +) + +--- + auto numbers = [ 1, 2, 3, 4 ]; + writefln("%(%s%)", numbers); +--- + +$(P +The format string above consists of three parts: +) + +$(UL +$(LI $(STRING %$(PARANTEZ_AC)): Start of element format) +$(LI $(STRING %s): Format for each element) +$(LI $(STRING %$(PARANTEZ_KAPA)): End of element format) +) + +$(P +Each being printed with the $(STRING %s) format, the elements appear one after the other: +) + +$(SHELL +1234 +) + +$(P +The regular characters before and after the element format are repeated for each element. For example, the $(STRING {%s},) specifier would print each element between curly brackets separated by commas: +) + +--- + writefln("%({%s},%)", numbers); +--- + +$(P +However, regular characters to the right of the format specifier are considered to be element delimiters and are printed only between elements, not after the last one: +) + +$(SHELL +{1},{2},{3},{4 $(SHELL_NOTE '}' and ',' are not printed after the last element) +) + +$(P +$(IX %|) $(STRING %|) is used for specifying the characters that should be printed even for the last element. Characters that are to the right of $(STRING %|) are considered to be the delimiters and are not printed for the last element. Conversely, characters to the left of $(STRING %|) are printed even for the last element. +) + +$(P +For example, the following format specifier would print the closing curly bracket after the last element but not the comma: +) +--- + writefln("%({%s}%|,%)", numbers); +--- + +$(SHELL +{1},{2},{3},{4} $(SHELL_NOTE '}' is printed after the last element as well) +) + +$(P +Unlike strings that are printed individually, strings that are printed as elements appear within double quotes: +) + +--- + auto vegetables = [ "spinach", "asparagus", "artichoke" ]; + writefln("%(%s, %)", vegetables); +--- + +$(SHELL +"spinach", "asparagus", "artichoke" +) + +$(P +$(IX %-$(PARANTEZ_AC)) When the double quotes are not desired, the element format must be started with $(STRING %-$(PARANTEZ_AC)) instead of $(STRING %$(PARANTEZ_AC)): +) + +--- + writefln("%-(%s, %)", vegetables); +--- + +$(SHELL +spinach, asparagus, artichoke +) + +$(P +The same applies to characters as well. $(STRING %$(PARANTEZ_AC)) prints them within single quotes: +) + +--- + writefln("%(%s%)", "hello"); +--- + +$(SHELL +'h''e''l''l''o' +) + +$(P +$(STRING %-$(PARANTEZ_AC)) prints them without quotes: +) + +--- + writefln("%-(%s%)", "hello"); +--- + +$(SHELL +hello +) + +$(P +There must be two format specifiers for associative arrays: one for the keys and one for the values. For example, the following $(STRING %s (%s)) specifier would print first the key and then the value in parentheses: +) + +--- + auto spelled = [ 1 : "one", 10 : "ten", 100 : "hundred" ]; + writefln("%-(%s (%s)%|, %)", spelled); +--- + +$(P +Also note that, being specified to the right of $(STRING %|), the comma is not printed for the last element: +) + +$(SHELL +1 (one), 100 (hundred), 10 (ten) +) + +$(H5 $(IX format, std.string) $(C format)) + +$(P +Formatted output is available through the $(C format()) function of the $(C std.string) module as well. $(C format()) works the same as $(C writef()) but it $(I returns) the result as a $(C string) instead of printing it to the output: +) + +--- +import std.stdio; +import std.string; + +void main() { + write("What is your name? "); + auto name = strip(readln()); + + auto result = $(HILITE format)("Hello %s!", name); +} +--- + +$(P +The program can make use of that result in later expressions. +) + +$(H6 $(IX checked format string) Checked format string) + +$(P +There is an alternative syntax for functions like $(C format) in the standard library that take a format string ($(C writef), $(C writefln), $(C formattedWrite), $(C readf), $(C formattedRead), etc.). It is possible to provide the format string as a $(I template argument) to these functions so that the validity of the format string and the arguments are checked at compile time: +) + +--- +import std.stdio; + +void main() { + writefln!"%s %s"(1); $(DERLEME_HATASI) (extra %s) + writefln!"%s"(1, 2); $(DERLEME_HATASI) (extra 2) + writefln!"%s %d"(1, 2.5); $(DERLEME_HATASI) (mismatched %d and 2.5) +} +--- + +$(P +The $(C !) character above is the template instantiation operator, which we will see in $(LINK2 /ders/d.en/templates.html, a later chapter). +) + +$(P +($(I $(B Note:) Although this snytax is safer because it catches potential programmer errors at compile time, it may also make compilation times longer.)) +) + +$(PROBLEM_COK + +$(PROBLEM +Write a program that reads a value and prints it in the hexadecimal system. +) + +$(PROBLEM +Write a program that reads a floating point value and prints it as percentage value with two digits after the decimal mark. For example, if the value is 1.2345, it should print $(C %1.23). +) + +) + +Macros: + SUBTITLE=Formatted Output + + DESCRIPTION=Printing values in certain formats. + + KEYWORDS=d programming language tutorial book format output diff --git a/target/frontispiece.d b/target/frontispiece.d new file mode 100644 index 0000000..27f5200 --- /dev/null +++ b/target/frontispiece.d @@ -0,0 +1,9 @@ +Ddoc + +
+ +
+ +
+ +
diff --git a/target/function_overloading.cozum.d b/target/function_overloading.cozum.d new file mode 100644 index 0000000..73b8c25 --- /dev/null +++ b/target/function_overloading.cozum.d @@ -0,0 +1,147 @@ +Ddoc + +$(COZUM_BOLUMU Function Overloading) + +$(P +The following two overloads take advantage of the existing $(C info()) overloads: +) + +--- +void info(in Meal meal) { + info(meal.time); + write('-'); + info(addDuration(meal.time, TimeOfDay(1, 30))); + + write(" Meal, Address: ", meal.address); +} + +void info(in DailyPlan plan) { + info(plan.amMeeting); + writeln(); + info(plan.lunch); + writeln(); + info(plan.pmMeeting); +} +--- + +$(P +Here is the entire program that uses all of these types: +) + +--- +import std.stdio; + +struct TimeOfDay { + int hour; + int minute; +} + +void info(in TimeOfDay time) { + writef("%02s:%02s", time.hour, time.minute); +} + +TimeOfDay addDuration(in TimeOfDay start, + in TimeOfDay duration) { + TimeOfDay result; + + result.minute = start.minute + duration.minute; + result.hour = start.hour + duration.hour; + result.hour += result.minute / 60; + + result.minute %= 60; + result.hour %= 24; + + return result; +} + +struct Meeting { + string topic; + size_t attendanceCount; + TimeOfDay start; + TimeOfDay end; +} + +void info(in Meeting meeting) { + info(meeting.start); + write('-'); + info(meeting.end); + + writef(" \"%s\" meeting with %s attendees", + meeting.topic, + meeting.attendanceCount); +} + +struct Meal { + TimeOfDay time; + string address; +} + +void info(in Meal meal) { + info(meal.time); + write('-'); + info(addDuration(meal.time, TimeOfDay(1, 30))); + + write(" Meal, Address: ", meal.address); +} + +struct DailyPlan { + Meeting amMeeting; + Meal lunch; + Meeting pmMeeting; +} + +void info(in DailyPlan plan) { + info(plan.amMeeting); + writeln(); + info(plan.lunch); + writeln(); + info(plan.pmMeeting); +} + +void main() { + immutable bikeRideMeeting = Meeting("Bike Ride", 4, + TimeOfDay(10, 30), + TimeOfDay(11, 45)); + + immutable lunch = Meal(TimeOfDay(12, 30), "İstanbul"); + + immutable budgetMeeting = Meeting("Budget", 8, + TimeOfDay(15, 30), + TimeOfDay(17, 30)); + + immutable todaysPlan = DailyPlan(bikeRideMeeting, + lunch, + budgetMeeting); + + info(todaysPlan); + writeln(); +} +--- + +$(P +That $(C main()) function can also be written with only object literals: +) + +--- +void $(CODE_DONT_TEST)main() { + info(DailyPlan(Meeting("Bike Ride", 4, + TimeOfDay(10, 30), + TimeOfDay(11, 45)), + + Meal(TimeOfDay(12, 30), "İstanbul"), + + Meeting("Budget", 8, + TimeOfDay(15, 30), + TimeOfDay(17, 30)))); + + writeln(); +} +--- + + +Macros: + SUBTITLE=Function Overloading + + DESCRIPTION=Programming in D exercise solutions: Function Overloading + + KEYWORDS=d programming book tutorial function overloading exercise solutions diff --git a/target/function_overloading.d b/target/function_overloading.d new file mode 100644 index 0000000..137ef29 --- /dev/null +++ b/target/function_overloading.d @@ -0,0 +1,294 @@ +Ddoc + +$(DERS_BOLUMU $(IX function overloading) $(IX overloading, function) Function Overloading) + +$(P +Defining more than one function having the same name is $(I function overloading). In order to be able to differentiate these functions, their parameters must be different. +) + +$(P +The following code has multiple overloads of the $(C info()) function, each taking a different type of parameter: +) + +--- +import std.stdio; + +void info(in $(HILITE double) number) { + writeln("Floating point: ", number); +} + +void info(in $(HILITE int) number) { + writeln("Integer : ", number); +} + +void info(in $(HILITE char[]) str) { + writeln("String : ", str); +} + +void main() { + info(1.2); + info(3); + info("hello"); +} +--- + +$(P +Although all of the functions are named $(C info()), the compiler picks the one that matches the argument that is used when making the call. For example, because the literal $(C 1.2) is of type $(C double), the $(C info()) function that takes a $(C double) gets called for it. +) + +$(P +The choice of which function to call is made at compile time, which may not always be easy or clear. For example, because $(C int) can implicitly be converted to both $(C double) and $(C real), the compiler cannot decide which of the functions to call in the following program: +) + +--- +real sevenTimes(in real value) { + return 7 * value; +} + +double sevenTimes(in double value) { + return 7 * value; +} + +void main() { + int value = 5; + auto result = sevenTimes(value); $(DERLEME_HATASI) +} +--- + +$(P +$(I $(B Note:) It is usually unnecessary to write separate functions when the function bodies are exactly the same. We will see later in the $(LINK2 /ders/d.en/templates.html, Templates chapter) how a single definition can be used for multiple types.) +) + +$(P +However, if there is another function overload that takes a $(C long) parameter, then the ambiguity would be resolved because $(C long) is a $(I better match) for $(C int) than $(C double) or $(C real): +) + +--- +long sevenTimes(in long value) { + return 7 * value; +} + +// ... + + auto result = sevenTimes(value); // now compiles +--- + +$(H5 Overload resolution) + +$(P +The compiler picks the overload that is the $(I best match) for the arguments. This is called overload resolution. +) + +$(P +Although overload resolution is simple and intuitive in most cases, it is sometimes complicated. The following are the rules of overload resolution. They are being presented in a simplified way in this book. +) + +$(P +There are four states of match, listed from the worst to the best: +) + +$(UL +$(LI mismatch) +$(LI match through automatic type conversion) +$(LI match through $(C const) qualification) +$(LI exact match) +) + +$(P +The compiler considers all of the overloads of a function during overload resolution. It first determines the match state of every parameter for every overload. For each overload, the least match state among the parameters is taken to be the match state of that overload. +) + +$(P +After all of the match states of the overloads are determined, then the overload with the best match is selected. If there are more than one overload that has the best match, then more complicated resolution rules are applied. I will not get into more details of these rules in this book. If your program is in a situation where it depends on complicated overload resolution rules, it may be an indication that it is time to change the design of the program. Another option is to take advantage of other features of D, like templates. An even simpler but not always desirable approach would be to abandon function overloading altogether by naming functions differently for each type e.g. like $(C sevenTimes_real()) and $(C sevenTimes_double()). +) + +$(H5 Function overloading for user-defined types) + +$(P +Function overloading is useful with structs and classes as well. Additionally, overload resolution ambiguities are much less frequent with user-defined types. Let's overload the $(C info()) function above for some of the types that we have defined in the $(LINK2 /ders/d.en/struct.html, Structs chapter): +) + +--- +struct TimeOfDay { + int hour; + int minute; +} + +void info(in TimeOfDay time) { + writef("%02s:%02s", time.hour, time.minute); +} +--- + +$(P +That overload enables $(C TimeOfDay) objects to be used with $(C info()). As a result, variables of user-defined types can be printed in exactly the same way as fundamental types: +) + +--- + auto breakfastTime = TimeOfDay(7, 0); + info(breakfastTime); +--- + +$(P +The $(C TimeOfDay) objects would be matched with that overload of $(C info()): +) + +$(SHELL +07:00 +) + +$(P +The following is an overload of $(C info()) for the $(C Meeting) type: +) + +--- +struct Meeting { + string topic; + size_t attendanceCount; + TimeOfDay start; + TimeOfDay end; +} + +void info(in Meeting meeting) { + info(meeting.start); + write('-'); + info(meeting.end); + + writef(" \"%s\" meeting with %s attendees", + meeting.topic, + meeting.attendanceCount); +} +--- + +$(P +Note that this overload makes use of the already-defined overload for $(C TimeOfDay). $(C Meeting) objects can now be printed in exactly the same way as fundamental types as well: +) + +--- + auto bikeRideMeeting = Meeting("Bike Ride", 3, + TimeOfDay(9, 0), + TimeOfDay(9, 10)); + info(bikeRideMeeting); +--- + +$(P +The output: +) + +$(SHELL +09:00-09:10 "Bike Ride" meeting with 3 attendees +) + +$(H5 Limitations) + +$(P +Although the $(C info()) function overloads above are a great convenience, this method has some limitations: +) + +$(UL + +$(LI +$(C info()) always prints to $(C stdout). It would be more useful if it could print to any $(C File). One way of achieving this is to pass the output stream as a parameter as well e.g. for the $(C TimeOfDay) type: + +--- +void info(File file, in TimeOfDay time) { + file.writef("%02s:%02s", time.hour, time.minute); +} +--- + +$(P +That would enable printing $(C TimeOfDay) objects to any file, including $(C stdout): +) + +--- + info($(HILITE stdout), breakfastTime); + + auto file = File("a_file", "w"); + info($(HILITE file), breakfastTime); +--- + +$(P +$(I $(B Note:) The special objects $(C stdin), $(C stdout), and $(C stderr) are of type $(C File).) +) + +) + +$(LI +More importantly, $(C info()) does not solve the more general problem of producing the string representation of variables. For example, it does not help with passing objects of user-defined types to $(C writeln()): + +--- + writeln(breakfastTime); // Not useful: prints in generic format +--- + +$(P +The code above prints the object in a generic format that includes the name of the type and the values of its members, not in a way that would be useful in the program: +) + +$(SHELL +TimeOfDay(7, 0) +) + +$(P +It would be much more useful if there were a function that converted $(C TimeOfDay) objects to $(C string) in their special format as in $(STRING "12:34"). We will see how to define $(C string) representations of struct objects in the next chapter. +) + +) + +) + +$(PROBLEM_TEK + +$(P +Overload the $(C info()) function for the following structs as well: +) + +--- +struct Meal { + TimeOfDay time; + string address; +} + +struct DailyPlan { + Meeting amMeeting; + Meal lunch; + Meeting pmMeeting; +} +--- + +$(P +Since $(C Meal) has only the start time, add an hour and a half to determine its end time. You can use the $(C addDuration()) function that we have defined earlier in the structs chapter: +) + +--- +TimeOfDay addDuration(in TimeOfDay start, + in TimeOfDay duration) { + TimeOfDay result; + + result.minute = start.minute + duration.minute; + result.hour = start.hour + duration.hour; + result.hour += result.minute / 60; + + result.minute %= 60; + result.hour %= 24; + + return result; +} +--- + +$(P +Once the end times of $(C Meal) objects are calculated by $(C addDuration()), $(C DailyPlan) objects should be printed as in the following output: +) + +$(SHELL +10:30-11:45 "Bike Ride" meeting with 4 attendees +12:30-14:00 Meal, Address: İstanbul +15:30-17:30 "Budget" meeting with 8 attendees +) + +) + +Macros: + SUBTITLE=Function Overloading + + DESCRIPTION=The function overloading feature of the D programming language, which increases the usability of functions by bringing uniformity through many types. + + KEYWORDS=d programming lesson book tutorial function overloading diff --git a/target/function_parameters.cozum.d b/target/function_parameters.cozum.d new file mode 100644 index 0000000..2a32d21 --- /dev/null +++ b/target/function_parameters.cozum.d @@ -0,0 +1,38 @@ +Ddoc + +$(COZUM_BOLUMU Function Parameters) + +$(P +Because the parameters of this function are the kind that gets copied from the arguments, what get swapped in the function are those copies. +) + +$(P +To make the function swap the arguments, both of the parameters must be passed by reference: +) + +--- +void swap($(HILITE ref) int first, $(HILITE ref) int second) { + const int temp = first; + first = second; + second = temp; +} +--- + +$(P +With that change, now the variables in $(C main()) would be swapped: +) + +$(SHELL +2 1 +) + +$(P +Although not related to the original problem, also note that $(C temp) is specified as $(C const) as it is not changed in the function. +) + +Macros: + SUBTITLE=Function Parameters Solutions + + DESCRIPTION=Programming in D exercise solutions: Function Parameters + + KEYWORDS=programming in d tutorial function parameters diff --git a/target/function_parameters.d b/target/function_parameters.d new file mode 100644 index 0000000..5f68297 --- /dev/null +++ b/target/function_parameters.d @@ -0,0 +1,992 @@ +Ddoc + +$(DERS_BOLUMU $(IX parameter) $(IX function parameter) Function Parameters) + +$(P +This chapter covers various kinds of function parameters. +) + +$(P +Some of the concepts of this chapter have already appeared earlier in the book. For example, the $(C ref) keyword that we saw in the $(LINK2 /ders/d.en/foreach.html, $(C foreach) Loop chapter) was making $(I actual elements) available in $(C foreach) loops as opposed to $(I copies) of those elements. +) + +$(P +Additionally, we covered the $(C const) and $(C immutable) keywords and the differences between value types and reference types in previous chapters. +) + +$(P +We have written functions that produced results by making use of their parameters. For example, the following function uses its parameters in a calculation: +) + +--- +double weightedAverage(double quizGrade, double finalGrade) { + return quizGrade * 0.4 + finalGrade * 0.6; +} +--- + +$(P +That function calculates the average grade by taking 40% of the quiz grade and 60% of the final grade. Here is how it may be used: +) + +--- + int quizGrade = 76; + int finalGrade = 80; + + writefln("Weigthed average: %2.0f", + weightedAverage(quizGrade, finalGrade)); +--- + +$(H5 $(IX pass-by copy) $(IX copy, parameter) Parameters are always copied) + +$(P +In the code above, the two variables are passed as arguments to $(C weightedAverage()). The function $(I uses) its parameters. This fact may give the false impression that the function uses the actual variables that have been passed as arguments. In reality, what the function uses are $(I copies) of those variables. +) + +$(P +This distinction is important because modifying a parameter changes only the copy. This can be seen in the following function that is trying to modify its parameter (i.e. making a side effect). Let's assume that the following function is written for reducing the energy of a game character: +) + +--- +void reduceEnergy(double energy) { + energy /= 4; +} +--- + +$(P +Here is a program that tests $(C reduceEnergy()): +) + +--- +import std.stdio; + +void reduceEnergy(double energy) { + energy /= 4; +} + +void main() { + double energy = 100; + + reduceEnergy(energy); + writeln("New energy: ", energy); +} +--- + +$(P +The output: +) + +$(SHELL +New energy: 100 $(SHELL_NOTE_WRONG Not changed) +) + +$(P +Although $(C reduceEnergy()) drops the value of its parameter to a quarter of its original value, the variable $(C energy) in $(C main()) does not change. The reason for this is that the $(C energy) variable in $(C main()) and the $(C energy) parameter of $(C reduceEnergy()) are separate; the parameter is a copy of the variable in $(C main()). +) + +$(P +To observe this more closely, let's insert some $(C writeln()) expressions: +) + +--- +import std.stdio; + +void reduceEnergy(double energy) { + writeln("Entered the function : ", energy); + energy /= 4; + writeln("Leaving the function : ", energy); +} + +void main() { + double energy = 100; + + writeln("Calling the function : ", energy); + reduceEnergy(energy); + writeln("Returned from the function: ", energy); +} +--- + +$(P +The output: +) + +$(SHELL +Calling the function : 100 +Entered the function : 100 +Leaving the function : 25 $(SHELL_NOTE the parameter changes,) +Returned from the function: 100 $(SHELL_NOTE the variable remains the same) +) + +$(H5 $(IX pass-by reference) Referenced variables are not copied) + +$(P +Even parameters of reference types like slices, associative arrays, and class variables are copied to functions. However, the original variables that are referenced (i.e. elements of slices and associative arrays, and class objects) are not copied. Effectively, such variables are passed to functions as $(I references): the parameter becomes another reference to the original object. It means that a modification made through the reference modifies the original object as well. +) + +$(P +Being slices of characters, this applies to strings as well: +) + +--- +import std.stdio; + +void makeFirstLetterDot(dchar[] str) { + str[0] = '.'; +} + +void main() { + dchar[] str = "abc"d.dup; + makeFirstLetterDot(str); + writeln(str); +} +--- + +$(P +The change made to the first element of the parameter affects the actual element in $(C main()): +) + +$(SHELL +.bc +) + +$(P +However, the original slice and associative array variables are still passed by copy. This may have surprising and seemingly unpredictable results unless the parameters are qualified as $(C ref) themselves. +) + +$(H6 Surprising reference semantics of slices) + +$(P +As we saw in the $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features chapter), adding elements to a slice $(I may) terminate element sharing. Obviously, once sharing ends, a slice parameter like $(C str) above would not be a reference to the elements of the passed-in original variable anymore. +) + +$(P +For example, the element that is appended by the following function will not be seen by the caller: +) + +--- +import std.stdio; + +void appendZero(int[] arr) { + arr $(HILITE ~= 0); + writefln("Inside appendZero() : %s", arr); +} + +void main() { + auto arr = [ 1, 2 ]; + appendZero(arr); + writefln("After appendZero() returns: %s", arr); +} +--- + +$(P +The element is appended only to the function parameter, not to the original slice: +) + +$(SHELL +Inside appendZero() : [1, 2, 0] +After appendZero() returns: [1, 2] $(SHELL_NOTE_WRONG No 0) +) + +$(P +If the new elements need to be appended to the original slice, then the slice must be passed as $(C ref): +) + +--- +void appendZero($(HILITE ref) int[] arr) { + // ... +} +--- + +$(P +The $(C ref) qualifier will be explained below. +) + +$(H6 Surprising reference semantics of associative arrays) + +$(P +Associative arrays that are passed as function parameters may cause surprises as well because associative arrays start their lives as $(C null), not empty. +) + +$(P +In this context, $(C null) means an uninitialized associative array. Associative arrays are initialized automatically when their first key-value pair is added. As a consequence, if a function adds an element to a $(C null) associative array, then that element cannot be seen in the original variable because although the parameter is initialized, the original variable remains $(C null): +) + +--- +import std.stdio; + +void appendElement(int[string] aa) { + aa$(HILITE ["red"] = 100); + writefln("Inside appendElement() : %s", aa); +} + +void main() { + int[string] aa; // ← null to begin with + appendElement(aa); + writefln("After appendElement() returns: %s", aa); +} +--- + +$(P +The original variable does not have the added element: +) + +$(SHELL +Inside appendElement() : ["red":100] +After appendElement() returns: [] $(SHELL_NOTE_WRONG Still null) +) + +$(P +On the other hand, if the associative array were not $(C null) to begin with, then the added element would be seen by the caller as well: +) + +--- + int[string] aa; + aa["blue"] = 10; // ← Not null before the call + appendElement(aa); +--- + +$(P +This time the added element is seen by the caller: +) + +$(SHELL +Inside appendElement() : ["red":100, "blue":10] +After appendElement() returns: [$(HILITE "red":100), "blue":10] +) + +$(P +For that reason, it may be better to pass the associative array as a $(C ref) parameter, which will be explained below. +) + +$(H5 Parameter qualifiers) + +$(P +Parameters are passed to functions according to the general rules described above: +) + +$(UL + +$(LI Value types are copied, after which the original variable and the copy are independent.) + +$(LI Reference types are copied as well but both the original reference and the parameter provide access to the same variable.) + +) + +$(P +Those are the default rules that are applied when parameter definitions have no qualifiers. The following qualifiers change the way parameters are passed and what operations are allowed on them. +) + +$(H6 $(IX in, parameter) $(C in)) + +$(P +We have seen that functions can produce values and can have side effects. The $(C in) keyword specifies that the parameter is going be used only as input. Such parameters cannot be modified by the function as its side effects: +) + +--- +import std.stdio; + +double weightedTotal($(HILITE in) double currentTotal, + $(HILITE in) double weight, + $(HILITE in) double addend) { + return currentTotal + (weight * addend); +} + +void main() { + writeln(weightedTotal(1.23, 4.56, 7.89)); +} +--- + +$(P +$(C in) parameters cannot be modified: +) + +--- +void foo(in int value) { + value = 1; $(DERLEME_HATASI) +} +--- + +$(P +$(C in) is the equivalent of $(C const scope). +) + +$(H6 $(IX out, parameter) $(C out)) + +$(P +We know that functions return what they produce as their return values. The fact that there is only one return value is sometimes limiting as some functions may need to produce more than one result. ($(I $(B Note:) It is possible to return more than one result by defining the return type as a $(C Tuple) or a $(C struct). We will see these features in later chapters.)) +) + +$(P +The $(C out) keyword makes it possible for functions to return results through their parameters. When $(C out) parameters are modified within the function, those modifications affect the original variable that has been passed to the function. In a sense, the assigned value goes $(I out) of the function through the $(C out) parameter. +) + +$(P +Let's have a look at a function that divides two numbers and produces both the quotient and the remainder. The return value is used for the quotient and the remainder is $(I returned) through the $(C out) parameter: +) + +--- +import std.stdio; + +int divide(in int dividend, in int divisor, $(HILITE out) int remainder) { + $(HILITE remainder = dividend % divisor); + return dividend / divisor; +} + +void main() { + int remainder; + int result = divide(7, 3, remainder); + + writeln("result: ", result, ", remainder: ", remainder); +} +--- + +$(P +Modifying the $(C remainder) parameter of the function modifies the $(C remainder) variable in $(C main()) (their names need not be the same): +) + +$(SHELL +result: 2, remainder: 1 +) + +$(P +Regardless of their values at the call site, $(C out) parameters are first assigned to the $(C .init) value of their types automatically: +) + +--- +import std.stdio; + +void foo(out int parameter) { + writeln("After entering the function : ", parameter); +} + +void main() { + int variable = 100; + + writeln("Before calling the function : ", variable); + foo(variable); + writeln("After returning from the function: ", variable); +} +--- + +$(P +Even though there is no explicit assignment to the parameter in the function, the value of the parameter automatically becomes the initial value of $(C int), affecting the variable in $(C main()): +) + +$(SHELL +Before calling the function : 100 +After entering the function : 0 $(SHELL_NOTE the value of int.init) +After returning from the function: 0 +) + +$(P +As this demonstrates, $(C out) parameters cannot pass values into functions; they are strictly for passing values out of functions. +) + +$(P +We will see in later chapters that returning $(C Tuple) or $(C struct) types are better alternatives to $(C out) parameters. +) + +$(H6 $(IX const, parameter) $(C const)) + +$(P +As we saw earlier, $(C const) guarantees that the parameter will not be modified inside the function. It is helpful for the programmers to know that certain variables will not be changed by a function. $(C const) also makes functions more useful by allowing $(C const), $(C immutable), and $(I mutable) variables to be passed through that parameter: +) + +--- +import std.stdio; + +dchar lastLetter($(HILITE const) dchar[] str) { + return str[$ - 1]; +} + +void main() { + writeln(lastLetter("constant")); +} +--- + +$(H6 $(IX immutable, parameter) $(C immutable)) + +$(P +As we saw earlier, $(C immutable) makes functions require that certain variables must be immutable. Because of such a requirement, the following function can only be called with strings with $(C immutable) elements (e.g. string literals): +) + +--- +import std.stdio; + +dchar[] mix($(HILITE immutable) dchar[] first, + $(HILITE immutable) dchar[] second) { + dchar[] result; + int i; + + for (i = 0; (i < first.length) && (i < second.length); ++i) { + result ~= first[i]; + result ~= second[i]; + } + + result ~= first[i..$]; + result ~= second[i..$]; + + return result; +} + +void main() { + writeln(mix("HELLO", "world")); +} +--- + +$(P +Since it forces a requirement on the parameter, $(C immutable) parameters should be used only when immutability is required. Otherwise, in general $(C const) is more useful because it accepts $(C immutable), $(C const), and $(I mutable) variables. +) + +$(H6 $(IX ref, parameter) $(C ref)) + +$(P +This keyword allows passing a variable by reference even though it would normally be passed as a copy (i.e. by value). +) + +$(P +For the $(C reduceEnergy()) function that we saw earlier to modify the original variable, it must take its parameter as $(C ref): +) + +--- +import std.stdio; + +void reduceEnergy($(HILITE ref) double energy) { + energy /= 4; +} + +void main() { + double energy = 100; + + reduceEnergy(energy); + writeln("New energy: ", energy); +} +--- + +$(P +This time, the modification that is made to the parameter changes the original variable in $(C main()): +) + +$(SHELL +New energy: 25 +) + +$(P +As can be seen, $(C ref) parameters can be used both as input and output. $(C ref) parameters can also be thought of as aliases of the original variables. The function parameter $(C energy) above is an alias of the variable $(C energy) in $(C main()). +) + +$(P +Similar to $(C out) parameters, $(C ref) parameters allow functions to have side effects as well. In fact, $(C reduceEnergy()) does not return a value; it only causes a side effect through its single parameter. +) + +$(P +The programming style called $(I functional programming) favors return values over side effects, so much so that some functional programming languages do not allow side effects at all. This is because functions that produce results $(I purely) through their return values are easier to understand, implement, and maintain. +) + +$(P +The same function can be written in a functional programming style by returning the result, instead of causing a side effect. The parts of the program that changed are highlighted: +) + +--- +import std.stdio; + +$(HILITE double reducedEnergy)(double energy) { + $(HILITE return energy / 4); +} + +void main() { + double energy = 100; + + $(HILITE energy = reducedEnergy(energy)); + writeln("New energy: ", energy); +} +--- + +$(P +Note the change in the name of the function as well. Now it is a noun as opposed to a verb. +) + +$(H6 $(C auto ref)) + +$(P +This qualifier can only be used with $(LINK2 /ders/d.en/templates.html, templates). As we will see in the next chapter, an $(C auto ref) parameter takes $(I lvalues) by reference and $(I rvalues) by copy. +) + +$(H6 $(IX inout, parameter) $(C inout)) + +$(P +Despite its name consisting of $(C in) and $(C out), this keyword does not mean $(I input and output); we have already seen that input and output is achieved by the $(C ref) keyword. +) + +$(P +$(C inout) carries the $(I mutability) of the parameter to the return type. If the parameter is $(C const), $(C immutable), or $(I mutable); then the return value is also $(C const), $(C immutable), or $(I mutable); respectively. +) + +$(P +To see how $(C inout) helps in programs, let's look at a function that returns a slice to the $(I inner) elements of its parameter: +) + +--- +import std.stdio; + +int[] inner(int[] slice) { + if (slice.length) { + --slice.length; // trim from the end + + if (slice.length) { + slice = slice[1 .. $]; // trim from the beginning + } + } + + return slice; +} + +void main() { + int[] numbers = [ 5, 6, 7, 8, 9 ]; + writeln(inner(numbers)); +} +--- + +$(P +The output: +) + +$(SHELL +[6, 7, 8] +) + +$(P +According to what we have established so far in the book, in order for the function to be more useful, its parameter should be $(C const(int)[]) because the elements are not being modified inside the function. (Note that there is no harm in modifying the parameter slice itself, as it is a copy of the original variable.) +) + +$(P +However, defining the function that way would cause a compilation error: +) + +--- +int[] inner($(HILITE const(int)[]) slice) { + // ... + return slice; $(DERLEME_HATASI) +} +--- + +$(P +The compilation error indicates that a slice of $(C const(int)) cannot be returned as a slice of $(I mutable) $(C int): +) + +$(SHELL +Error: cannot implicitly convert expression (slice) of type +const(int)[] to int[] +) + +$(P +One may think that specifying the return type as $(C const(int)[]) would be the solution: +) + +--- +$(HILITE const(int)[]) inner(const(int)[] slice) { + // ... + return slice; // now compiles +} +--- + +$(P +Although the code now compiles, it brings a limitation: even when the function is called with a slice of $(I mutable) elements, this time the returned slice ends up consisting of $(C const) elements. To see how limiting this would be, let's look at the following code, which tries to modify the inner elements of a slice: +) + +--- + int[] numbers = [ 5, 6, 7, 8, 9 ]; + int[] middle = inner(numbers); $(DERLEME_HATASI) + middle[] *= 10; +--- + +$(P +The returned slice of type $(C const(int)[]) cannot be assigned to a slice of type $(C int[]), resulting in an error: +) + +$(SHELL +Error: cannot implicitly convert expression (inner(numbers)) +of type const(int)[] to int[] +) + +$(P +However, since we started with a slice of mutable elements, this limitation is artificial and unfortunate. $(C inout) solves this mutability problem between parameters and return values. It is specified on both the parameter and the return type and carries the mutability of the former to the latter: +) + +--- +$(HILITE inout)(int)[] inner($(HILITE inout)(int)[] slice) { + // ... + return slice; +} +--- + +$(P +With that change, the same function can now be called with $(C const), $(C immutable), and $(I mutable) slices: +) + +--- + { + $(HILITE int[]) numbers = [ 5, 6, 7, 8, 9 ]; + // The return type is a slice of mutable elements + $(HILITE int[]) middle = inner(numbers); + middle[] *= 10; + writeln(middle); + } + { + $(HILITE immutable int[]) numbers = [ 10, 11, 12 ]; + // The return type is a slice of immutable elements + $(HILITE immutable int[]) middle = inner(numbers); + writeln(middle); + } + { + $(HILITE const int[]) numbers = [ 13, 14, 15, 16 ]; + // The return type is a slice of const elements + $(HILITE const int[]) middle = inner(numbers); + writeln(middle); + } +--- + +$(H6 $(IX lazy) $(C lazy)) + +$(P +It is natural to expect that arguments are evaluated $(I before) entering functions that use those arguments. For example, the function $(C add()) below is called with the return values of two other functions: +) + +--- + result = add(anAmount(), anotherAmount()); +--- + +$(P +In order for $(C add()) to be called, first $(C anAmount()) and $(C anotherAmount()) must be called. Otherwise, the values that $(C add()) needs would not be available. +) + +$(P +Evaluating arguments before calling a function is called $(I eager evaluation). +) + +$(P +However, depending on certain conditions, some parameters may not get a chance to be used in the function at all. In such cases, evaluating the arguments eagerly would be wasteful. +) + +$(P +A classic example of this situation is a $(I logging) function that outputs a message only if the importance of the message is above a certain configuration setting: +) + +--- +enum Level { low, medium, high } + +void log(Level level, string message) { + if (level >= interestedLevel) { + writefln("%s", message); + } +} +--- + +$(P +For example, if the user is interested only in the messages that are $(C Level.high), a message with $(C Level.medium) would not be printed. However, the argument would still be evaluated before calling the function. For example, the entire $(C format()) expression below including the $(C getConnectionState()) call that it makes would be wasted if the message is never printed: +) + +--- + if (failedToConnect) { + log(Level.medium, + format("Failure. The connection state is '%s'.", + getConnectionState())); + } +--- + +$(P +The $(C lazy) keyword specifies that an expression that is passed as a parameter will be evaluated only if and when needed: +) + +--- +void log(Level level, $(HILITE lazy) string message) { + // ... the body of the function is the same as before ... +} +--- + +$(P +This time, the expression would be evaluated only if the $(C message) parameter is used. +) + +$(P +One thing to be careful about is that a $(C lazy) parameter is evaluated $(I every time) that parameter is used in the function. +) + +$(P +For example, because the $(C lazy) parameter of the following function is used three times in the function, the expression that provides its value is evaluated three times: +) + +--- +import std.stdio; + +int valueOfArgument() { + writeln("Calculating..."); + return 1; +} + +void functionWithLazyParameter(lazy int value) { + int result = $(HILITE value + value + value); + writeln(result); +} + +void main() { + functionWithLazyParameter(valueOfArgument()); +} +--- + +$(P +The output: +) + +$(SHELL +Calculating +Calculating +Calculating +3 +) + +$(H6 $(IX scope) $(C scope)) + +$(P +$(IX DIP) $(IX -dip1000) This keyword specifies that a parameter will not be used beyond the scope of the function. As of this writing, $(C scope) is effective only if the function is defined as $(LINK2 /ders/d.en/functions_more.html, $(C @safe)) and if $(C -dip1000) compiler switch is used. DIP is short for $(I D Improvement Proposal). DIP 1000 is experimental as of this writing; so it may not work as expected in all cases. +) + +$(SHELL +$(SHELL_OBSERVED $) dmd -dip1000 deneme.d +) + +--- +int[] globalSlice; + +$(HILITE @safe) int[] foo($(HILITE scope) int[] parameter) { + globalSlice = parameter; $(DERLEME_HATASI) + return parameter; $(DERLEME_HATASI) +} + +void main() { + int[] slice = [ 10, 20 ]; + int[] result = foo(slice); +} +--- + +$(P +The function above violates the promise of $(C scope) in two places: It assigns the parameter to a global variable, and it returns it. Both those actions would make it possible for the parameter to be accessed after the function finishes. +) + +$(H6 $(IX shared, parameter) $(C shared)) + +$(P +This keyword requires that the parameter is shareable between threads of execution: +) + +--- +void foo($(HILITE shared) int[] i) { + // ... +} + +void main() { + int[] numbers = [ 10, 20 ]; + foo(numbers); $(DERLEME_HATASI) +} +--- + +$(P +The program above cannot be compiled because the argument is not $(C shared). The following is the necessary change to make it compile: +) + +--- + $(HILITE shared) int[] numbers = [ 10, 20 ]; + foo(numbers); // now compiles +--- + +$(P +We will see the $(C shared) keyword later in the $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency chapter). +) + +$(H6 $(IX return, parameter) $(C return)) + +$(P +Sometimes it is useful for a function to return one of its $(C ref) parameters directly. For example, the following $(C pick()) function picks and returns one of its parameters randomly so that the caller can mutate the lucky one directly: +) + +--- +import std.stdio; +import std.random; + +$(HILITE ref) int pick($(HILITE ref) int lhs, $(HILITE ref) int rhs) { + return uniform(0, 2) ? lhs : rhs; +} + +void main() { + int a; + int b; + + pick(a, b) $(HILITE = 42); + + writefln("a: %s, b: %s", a, b); +} +--- + +$(P +As a result, either $(C a) or $(C b) inside $(C main()) is assigned the value $(C 42): +) + +$(SHELL +a: 42, b: 0 +) + +$(SHELL +a: 0, b: 42 +) + +$(P +Unfortunately, one of the arguments of $(C pick()) may have a shorter lifetime than the returned reference. For example, the following $(C foo()) function calls $(C pick()) with two local variables, effectively itself returning a reference to one of them: +) + +--- +import std.random; + +ref int pick(ref int lhs, ref int rhs) { + return uniform(0, 2) ? lhs : rhs; +} + +ref int foo() { + int a; + int b; + + return pick(a, b); $(CODE_NOTE_WRONG BUG: returning invalid reference) +} + +void main() { + foo() = 42; $(CODE_NOTE_WRONG BUG: writing to invalid memory) +} +--- + +$(P +Since the lifetimes of both $(C a) and $(C b) end upon leaving $(C foo()), the assignment in $(C main()) cannot be made to a valid variable. This results in $(I undefined behavior). +) + +$(P +$(IX undefined behavior) The term $(I undefined behavior) describes situations where the behavior of the program is not defined by the programming language specification. Nothing can be said about the behavior of a program that contains undefined behavior. (In practice though, for the program above, the value $(C 42) would most likely be written to a memory location that used to be occupied by either $(C a) or $(C b), potentially currently a part of an unrelated variable, effectively corrupting the value of that unrelated variable.) +) + +$(P +The $(C return) keyword can be applied to a parameter to prevent such bugs. It specifies that a parameter must be a reference to a variable with a longer lifetime than the returned reference: +) + +--- +import std.random; + +ref int pick($(HILITE return) ref int lhs, $(HILITE return) ref int rhs) { + return uniform(0, 2) ? lhs : rhs; +} + +ref int foo() { + int a; + int b; + + return pick(a, b); $(DERLEME_HATASI) +} + +void main() { + foo() = 42; +} +--- + +$(P +This time the compiler sees that the arguments to $(C pick()) have a shorter lifetime than the reference that $(C foo()) is attempting to return: +) + +$(SHELL +Error: escaping reference to local variable a +Error: escaping reference to local variable b +) + +$(P +$(IX sealed reference) $(IX reference, sealed) This feature is called $(I sealed references). +) + +$(P +$(I $(B Note:) Although it is conceivable that the compiler could inspect $(C pick()) and detect the bug even without the $(C return) keyword, it cannot do so in general because the bodies of some functions may not be available to the compiler during every compilation.) +) + +$(H5 Summary) + +$(UL + +$(LI A $(I parameter) is what the function takes from its caller to accomplish its task.) + +$(LI An $(I argument) is an expression (e.g. a variable) that is passed to a function as a parameter.) + +$(LI +Every argument is passed by copy. However, for reference types, it is the reference that is copied, not the original variable. +) + +$(LI $(C in) specifies that the parameter is used only for data input.) + +$(LI $(C out) specifies that the parameter is used only for data output.) + +$(LI $(C ref) specifies that the parameter is used for data input and data output.) + +$(LI $(C auto ref) is used in templates only. It specifies that if the argument is an lvalue, then a reference to it is passed; if the argument is an rvalue, then it is passed by copy.) + +$(LI $(C const) guarantees that the parameter is not modified inside the function. (Remember that $(C const) is transitive: any data reached through a $(C const) variable is $(C const) as well.)) + +$(LI $(C immutable) requires the argument to be $(C immutable).) + +$(LI $(C inout) appears both at the parameter and the return type, and transfers the $(I mutability) of the parameter to the return type.) + +$(LI $(C lazy) is used to make a parameter be evaluated when (and every time) it is actually used.) + +$(LI $(C scope) guarantees that no reference to the parameter will be leaked from the function.) + +$(LI $(C shared) requires the parameter to be $(C shared).) + +$(LI $(C return) on a parameter requires the parameter to live longer than the returned reference.) + +) + +$(PROBLEM_TEK + +$(P +The following program is trying to swap the values of two arguments: +) + +--- +import std.stdio; + +void swap(int first, int second) { + int temp = first; + first = second; + second = temp; +} + +void main() { + int a = 1; + int b = 2; + + swap(a, b); + + writeln(a, ' ', b); +} +--- + +$(P +However, the program does not have any effect on $(C a) or $(C b): +) + +$(SHELL +1 2 $(SHELL_NOTE_WRONG not swapped) +) + +$(P +Fix the function so that the values of $(C a) and $(C b) are swapped. +) + +) + +Macros: + SUBTITLE=Function Parameters + + DESCRIPTION=Kinds of function parameters in the D programmning language. + + KEYWORDS=d programming language tutorial book in out inout ref lazy const immutable scope shared diff --git a/target/functions.cozum.d b/target/functions.cozum.d new file mode 100644 index 0000000..e59f9fb --- /dev/null +++ b/target/functions.cozum.d @@ -0,0 +1,57 @@ +Ddoc + +$(COZUM_BOLUMU Functions) + +$(OL + +$(LI + +--- +import std.stdio; + +void printMenu(string[] items, int firstNumber) { + foreach (i, item; items) { + writeln(' ', i + firstNumber, ' ', item); + } +} + +void main() { + string[] items = + [ "Black", "Red", "Green", "Blue", "White" ]; + printMenu(items, 1); +} +--- + +) + +$(LI +Here are some ideas: + +$(UL + +$(LI Write a function named $(C drawHorizontalLine()) to draw horizontal lines.) + +$(LI Write a function named $(C drawSquare()) to draw squares. This function could take advantage of $(C drawVerticalLine()) and $(C drawHorizontalLine()) when drawing the square.) + +$(LI Improve the functions to also take the character that is used when "drawing". This would allow drawing each shape with a different character: + +--- +void putDot(Canvas canvas, int line, int column$(HILITE , dchar dot)) { + canvas[line][column] = $(HILITE dot); +} +--- + +) + +) + +) + +) + +Macros: + SUBTITLE=Functions Solutions + + DESCRIPTION=Programming in D exercise solutions: functions + + KEYWORDS=programming in d tutorial functions diff --git a/target/functions.d b/target/functions.d new file mode 100644 index 0000000..9ec9d24 --- /dev/null +++ b/target/functions.d @@ -0,0 +1,744 @@ +Ddoc + +$(DERS_BOLUMU $(IX function) Functions) + +$(P +Similarly to how fundamental types are building blocks of program data, functions are building blocks of program behavior. +) + +$(P +Functions are also closely related to the craft aspect of programming. The functions that are written by experienced programmers are succinct, simple, and clear. This goes both ways: The mere act of trying to identify and write smaller building blocks of a program makes for a better programmer. +) + +$(P +We have covered basic statements and expressions in previous chapters. Although there will be many more that we will see in later chapters, what we have seen so far are commonly-used features of D. Still, they are not sufficient on their own to write large programs. The programs that we have written so far have all been very short, each demonstrating just a simple feature of the language. Trying to write a program with any level of complexity without functions would be very difficult and prone to bugs. +) + +$(P +This chapter covers only the basic features of functions. We will see more about functions later in the following chapters: +) + +$(UL +$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters)) +$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) +$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas)) +$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions)) +) + +$(P +Functions are features that put statements and expressions together as units of program execution. Such statements and expressions altogether are given a name that describes what they collectively achieve. They can then be $(I called) (or $(I executed)) by using that name. +) + +$(P +The concept of $(I giving names to a group of steps) is common in our daily lives. For example, the act of cooking an omelet can be described in some level of detail by the following steps: +) + +$(UL +$(LI get a pan) +$(LI get butter) +$(LI get an egg) +$(LI turn on the stove) +$(LI put the pan on the fire) +$(LI put butter into the pan when it is hot) +$(LI put the egg into butter when it is melted) +$(LI remove the pan from the fire when the egg is cooked) +$(LI turn off the stove) +) + +$(P +Since that much detail is obviously excessive, steps that are related together would be combined under a single name: +) + +$(UL +$(LI $(HILITE make preparations) (get the pan, butter, and the egg)) +$(LI turn on the stove) +$(LI $(HILITE cook the egg) (put the pan on the fire, etc.)) +$(LI turn off the stove) +) + +$(P +Going further, there can be a single name for all of the steps: +) + +$(UL +$(LI $(HILITE make a one-egg omelet) (all of the steps)) +) + +$(P +Functions are based on the same concept: steps that can collectively be named as a whole are put together to form a function. As an example, let's start with the following lines of code that achieve the task of printing a menu: +) + +--- + writeln(" 0 Exit"); + writeln(" 1 Add"); + writeln(" 2 Subtract"); + writeln(" 3 Multiply"); + writeln(" 4 Divide"); +--- + +$(P +Since it would make sense to name those combined lines as $(C printMenu), they can be put together to form a function by using the following syntax: +) + +--- +$(CODE_NAME printMenu)void printMenu() { + writeln(" 0 Exit"); + writeln(" 1 Add"); + writeln(" 2 Subtract"); + writeln(" 3 Multiply"); + writeln(" 4 Divide"); +} +--- + +$(P +The contents of that function can now be executed from within $(C main()) simply by using its name: +) + +--- +$(CODE_XREF printMenu)void main() { + printMenu(); + + // ... +} +--- + +$(P +It may be obvious from the similarities of the definitions of $(C printMenu()) and $(C main()) that $(C main()) is a function as well. The execution of a D program starts with the function named $(C main()) and branches out to other functions from there. +) + +$(H5 $(IX parameter) Parameters) + +$(P +Some of the powers of functions come from the fact that their behaviors are adjustable through parameters. +) + +$(P +Let's continue with the omelet example by modifying it to make an omelet of five eggs instead of always one. The steps would exactly be the same, the only difference being the number of eggs to use. We can change the more general description above accordingly: +) + +$(UL +$(LI make preparations (get the pan, butter, and $(HILITE five eggs))) +$(LI turn on the stove) +$(LI cook $(HILITE the eggs) (put the pan on the fire, etc.)) +$(LI turn off the stove) +) + +$(P +Likewise, the most general single step would become the following: +) + +$(UL +$(LI make a $(HILITE five-egg) omelet (all of the steps)) +) + +$(P +This time there is an additional information that concerns some of the steps: "get five eggs", "cook the eggs", and "make a five-egg omelet". +) + +$(P +$(IX , (comma), function parameter list) The behaviors of functions can be adjusted similarly to the omelet example. The information that functions use to adjust their behavior are called $(I parameters). Parameters are specified in a comma separated $(I function parameter list). The parameter list rests inside of the parentheses that comes after the name of the function. +) + +$(P +The $(C printMenu()) function above was defined with an empty parameter list because that function always printed the same menu. Let's assume that sometimes the menu will need to be printed differently in different contexts. For example, it may make more sense to print the first entry as "Return" instead of "Exit" depending on the part of the program that is being executed at that time. +) + +$(P +In such a case, the first entry of the menu can be $(I parameterized) by having been defined in the parameter list. The function then uses the value of that parameter instead of the literal $(STRING "Exit"): +) + +--- +void printMenu($(HILITE string firstEntry)) { + writeln(" 0 ", firstEntry); + writeln(" 1 Add"); + writeln(" 2 Subtract"); + writeln(" 3 Multiply"); + writeln(" 4 Divide"); +} +--- + +$(P +Notice that since the information that the $(C firstEntry) parameter conveys is a piece of text, its type has been specified as $(C string) in the parameter list. This function can now be $(I called) with different parameter values to print menus having different first entries. All that needs to be done is to use the appropriate $(C string) values depending on where the function is being called from: +) + +--- + // At some place in the program: + printMenu("Exit"); + // ... + // At some other place in the program: + printMenu("Return"); +--- + +$(P +$(B Note:) When you write and use your own functions with parameters of type $(C string) you may encounter compilation errors. As written, $(C printMenu()) above cannot be called with parameter values of type $(C char[]). For example, the following code would cause a compilation error: +) + +--- + char[] anEntry; + anEntry ~= "Take square root"; + printMenu(anEntry); $(DERLEME_HATASI) +--- + +$(P +On the other hand, if $(C printMenu()) were defined to take its parameter as $(C char[]), then it could not be called with $(C string)s like $(STRING "Exit"). This is related to the concept of immutability and the $(C immutable) keyword, both of which will be covered in the next chapter. +) + +$(P +Let's continue with the menu function and assume that it is not appropriate to always start the menu selection numbers with zero. In that case the starting number can also be passed to the function as its second parameter. The parameters of the function must be separated by commas: +) + +--- +void printMenu(string firstEntry$(HILITE , int firstNumber)) { + writeln(' ', firstNumber + 0, ' ', firstEntry); + writeln(' ', firstNumber + 1, " Add"); + writeln(' ', firstNumber + 2, " Subtract"); + writeln(' ', firstNumber + 3, " Multiply"); + writeln(' ', firstNumber + 4, " Divide"); +} +--- + +$(P +It is now possible to tell the function what number to start from: +) + +--- + printMenu("Return"$(HILITE , 1)); +--- + +$(H5 Calling a function) + +$(P +Starting a function so that it achieves its task is called $(I calling a function). The function call syntax is the following: +) + +--- + $(I function_name)($(I parameter_values)) +--- + + +$(P +$(IX argument) The actual parameter values that are passed to functions are called $(I function arguments). Although the terms $(I parameter) and $(I argument) are sometimes used interchangeably in the literature, they signify different concepts. +) + +$(P +The arguments are matched to the parameters one by one in the order that the parameters are defined. For example, the last call of $(C printMenu()) above uses the $(I arguments) $(STRING "Return") and $(C 1), which correspond to the $(I parameters) $(C firstEntry) and $(C firstNumber), respectively. +) + +$(P +The type of each argument must match the type of the corresponding parameter. +) + +$(H5 Doing work) + +$(P +In previous chapters, we have defined expressions as entities that do work. Function calls are expressions as well: they do some work. Doing work means having a side effect or producing a value: +) + +$(UL + +$(LI +$(IX side effect) +$(B Having side effects): Side effects are any change in the state of the program or its environment. Some operations have only side effects. An example is how the $(C printMenu()) function above changes $(C stdout) by printing to it. As another example, a function that adds a $(C Student) object to a student container would also have a side effect: it would be causing the container to grow. + +$(P +In summary, operations that cause a change in the state of the program have side effects. +) + +) + +$(LI +$(B Producing a value): Some operations only produce values. For example, a function that adds numbers would be producing the result of that addition. As another example, a function that makes a $(C Student) object by using the student's name and address would be producing a $(C Student) object. +) + +$(LI +$(B Having side effects and producing a value:) Some operations do both. For example, a function that reads two values from $(C stdin) and calculates their sum would be having side effects due to changing the state of $(C stdin) and also producing the sum of the two values. +) + +$(LI +$(B No operation:) Although every function is designed as one of the three categories above, depending on certain conditions at compile time or at run time, some functions end up doing no work at all. +) + +) + +$(H5 $(IX return value) The return value) + +$(P +The value that a function produces as a result of its work is called its $(I return value). This term comes from the observation that once the program execution branches into a function, it eventually $(I returns) back to where the function has been called. Functions get $(I called) and they $(I return) values. +) + +$(P +Just like any other value, return values have types. The type of the return value is specified right before the name of the function, at the point where the function is defined. For example, a function that adds two values of type $(C int) and returns their sum also as an $(C int) would be defined as follows: +) + +--- +$(HILITE int) add(int first, int second) { + // ... the actual work of the function ... +} +--- + +$(P +The value that a function returns takes the place of the function call itself. For example, assuming that the function call $(C add(5, 7)) produces the value $(C 12), then the following two lines would be equivalent: +) + +--- + writeln("Result: ", add(5, 7)); + writeln("Result: ", 12); +--- + +$(P +In the first line above, the $(C add()) function is called with the arguments $(C 5) and $(C 7) $(I before) $(C writeln()) gets called. The value $(C 12) that the function returns is in turn passed to $(C writeln()) as its second argument. +) + +$(P +This allows passing the return values of functions to other functions to form complex expressions: +) + +--- + writeln("Result: ", add(5, divide(100, studentCount()))); +--- + +$(P +In the line above, the return value of $(C studentCount()) is passed to $(C divide()) as its second argument, the return value of $(C divide()) is passed to $(C add()) as its second argument, and eventually the return value of $(C add()) is passed to $(C writeln()) as its second argument. +) + +$(H5 $(IX return, statement) The $(C return) statement) + +$(P +The return value of a function is specified by the $(C return) keyword: +) + +--- +int add(int first, int second) { + int result = first + second; + $(HILITE return) result; +} +--- + +$(P +A function produces its return value by taking advantage of statements, expressions, and potentially by calling other functions. The function would then return that value by the $(C return) keyword, at which point the execution of the function ends. +) + +$(P +It is possible to have more than one $(C return) statement in a function. The value of the first $(C return) statement that gets executed determines the return value of the function for a particular call: +) + +--- +int complexCalculation(int aParameter, int anotherParameter) { + if (aParameter == anotherParameter) { + return 0; + } + + return aParameter * anotherParameter; +} +--- + +$(P +The function above returns $(C 0) when the two parameters are equal, and the product of their values when they are different. +) + +$(H5 $(IX void, function) $(C void) functions) + +$(P +The return types of functions that do not produce values are specified as $(C void). We have seen this many times with the $(C main()) function so far, as well as the $(C printMenu()) function above. Since they do not return any value to the caller, their return types have been defined as $(C void). ($(I $(B Note:) $(C main()) can also be defined as returning $(C int). We will see this in $(LINK2 /ders/d.en/main.html, a later chapter).)) +) + +$(H5 The name of the function) + +$(P +The name of a function must be chosen to communicate the purpose of the function clearly. For example, the names $(C add) and $(C printMenu) were appropriate because their purposes were to add two values, and to print a menu, respectively. +) + +$(P +A common guideline for function names is that they contain a verb like $(I add) or $(I print). According to this guideline names like $(C addition()) and $(C menu()) would be less than ideal. +) + +$(P +However, it is acceptable to name functions simply as nouns if those functions do not have any side effects. For example, a function that returns the current temperature can be named as $(C currentTemperature()) instead of $(C getCurrentTemperature()). +) + +$(P +Coming up with names that are clear, short, and consistent is part of the subtle art of programming. +) + +$(H5 Code quality through functions) + +$(P +Functions can improve the quality of code. Smaller functions with fewer responsibilities lead to programs that are easier to maintain. +) + +$(H6 $(IX code duplication) Code duplication is harmful) + +$(P +One of the aspects that is highly detrimental to program quality is code duplication. Code duplication occurs when there is more than one piece of code in the program that performs the same task. +) + +$(P +Although this sometimes happens by copying lines of code around, it may also happen incidentally when writing separate pieces of code. +) + +$(P +One of the problems with pieces of code that duplicate essentially the same functionality is that they present multiple chances for bugs to crop up. When such bugs do occur and we need to fix them, it can be hard to make sure that we have fixed all places where we introduced the problem, as they may be spread around. Conversely, when the code appears in only one place in the program, then we only need to fix it at that one place to get rid of the bug once and for all. +) + +$(P +As I mentioned above, functions are closely related to the craft aspect of programming. Experienced programmers are always on the lookout for code duplication. They continually try to identify commonalities in code and move common pieces of code to separate functions (or to common structs, classes, templates, etc., as we will see in later chapters). +) + +$(P +$(IX refactor) Let's start with a program that contains some code duplication. Let's see how that duplication can be removed by moving code into functions (i.e. by $(I refactoring) the code). The following program reads numbers from the input and prints them first in the order that they have arrived and then in numerical order: +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] numbers; + + int count; + write("How many numbers are you going to enter? "); + readf(" %s", &count); + + // Read the numbers + foreach (i; 0 .. count) { + int number; + write("Number ", i, "? "); + readf(" %s", &number); + + numbers ~= number; + } + + // Print the numbers + writeln("Before sorting:"); + foreach (i, number; numbers) { + writefln("%3d:%5d", i, number); + } + + sort(numbers); + + // Print the numbers + writeln("After sorting:"); + foreach (i, number; numbers) { + writefln("%3d:%5d", i, number); + } +} +--- + +$(P +Some of the duplicated lines of code are obvious in that program. The last two $(C foreach) loops that are used for printing the numbers are exactly the same. Defining a function that might appropriately be named as $(C print()) would remove that duplication. The function could take a slice as a parameter and print it: +) + +--- +void print(int[] slice) { + foreach (i, element; slice) { + writefln("%3s:%5s", i, element); + } +} +--- + +$(P +Notice that the parameter is now referred to using the more general name $(C slice) instead of original and more specific name $(C numbers). The reason for that is the fact that the function would not know what the elements of the slice would specifically represent. That can only be known at the place where the function has been called from. The elements may be student IDs, parts of a password, etc. Since that cannot be known in the $(C print()) function, general names like $(C slice) and $(C element) are used in its implementation. +) + +$(P +The new function can be called from the two places where the slice needs to be printed: +) + +--- +import std.stdio; +import std.algorithm; + +void print(int[] slice) { + foreach (i, element; slice) { + writefln("%3s:%5s", i, element); + } +} + +void main() { + int[] numbers; + + int count; + write("How many numbers are you going to enter? "); + readf(" %s", &count); + + // Read the numbers + foreach (i; 0 .. count) { + int number; + write("Number ", i, "? "); + readf(" %s", &number); + + numbers ~= number; + } + + // Print the numbers + writeln("Before sorting:"); + $(HILITE print(numbers)); + + sort(numbers); + + // Print the numbers + writeln("After sorting:"); + $(HILITE print(numbers)); +} +--- + +$(P +There is more to do. Notice that there is always a title line printed right before printing the elements of the slice. Although the title is different, the task is the same. If printing the title can be seen as a part of printing the slice, the title too can be passed as a parameter. Here are the new changes: +) + +--- +void print($(HILITE string title,) int[] slice) { + $(HILITE writeln(title, ":");) + + foreach (i, element; slice) { + writefln("%3s:%5s", i, element); + } +} + +// ... + + // Print the numbers + print($(HILITE "Before sorting"), numbers); + +// ... + + // Print the numbers + print($(HILITE "After sorting"), numbers); +--- + +$(P +This step has the added benefit of obviating the comments that appear right before the two $(C print()) calls. Since the name of the function already clearly communicates what it does, those comments are unnecessary: +) + +--- + print("Before sorting", numbers); + sort(numbers); + print("After sorting", numbers); +--- + +$(P +Although subtle, there is more code duplication in this program: The values of $(C count) and $(C number) are read in exactly the same way. The only difference is the message that is printed to the user and the name of the variable: +) + +--- + int count; + write("How many numbers are you going to enter? "); + readf(" %s", &count); + +// ... + + int number; + write("Number ", i, "? "); + readf(" %s", &number); +--- + +$(P +The code would become even better if it took advantage of a new function that might be named appropriately as $(C readInt()). The new function can take the message as a parameter, print that message, read an $(C int) from the input, and return that $(C int): +) + +--- +int readInt(string message) { + int result; + write(message, "? "); + readf(" %s", &result); + return result; +} +--- + +$(P +$(C count) can now be initialized directly by the return value of a call to this new function: +) + +--- + int count = + readInt("How many numbers are you going to enter"); +--- + +$(P +$(C number) cannot be initialized in as straightforward a way because the loop counter $(C i) happens to be a part of the message that is displayed when reading $(C number). This can be overcome by taking advantage of $(C format): +) + +--- +import std.string; +// ... + int number = readInt(format("Number %s", i)); +--- + +$(P +Further, since $(C number) is used in only one place in the $(C foreach) loop, its definition can be eliminated altogether and the return value of $(C readInt()) can directly be used in its place: +) + +--- + foreach (i; 0 .. count) { + numbers ~= $(HILITE readInt)(format("Number %s", i)); + } +--- + +$(P +Let's make a final modification to this program by moving the lines that read the numbers to a separate function. This would also eliminate the need for the "Read the numbers" comment because the name of the new function would already carry that information. +) + +$(P +The new $(C readNumbers()) function does not need any parameter to complete its task. It reads some numbers and returns them as a slice. The following is the final version of the program: +) + +--- +import std.stdio; +import std.string; +import std.algorithm; + +void print(string title, int[] slice) { + writeln(title, ":"); + + foreach (i, element; slice) { + writefln("%3s:%5s", i, element); + } +} + +int readInt(string message) { + int result; + write(message, "? "); + readf(" %s", &result); + return result; +} + +int[] $(HILITE readNumbers)() { + int[] result; + + int count = + readInt("How many numbers are you going to enter"); + + foreach (i; 0 .. count) { + result ~= readInt(format("Number %s", i)); + } + + return result; +} + +void main() { + int[] numbers = readNumbers(); + print("Before sorting", numbers); + sort(numbers); + print("After sorting", numbers); +} +--- + +$(P +Compare this version of the program to the first one. The major steps of the program are very clear in the $(C main()) function of the new program. In contrast, the $(C main()) function of the first program had to be carefully examined to understand the purpose of that program. +) + +$(P +Although the total numbers of nontrivial lines of the two versions of the program ended up being equal in this example, functions make programs shorter in general. This effect is not apparent in this simple program. For example, before the $(C readInt()) function has been defined, reading an $(C int) from the input involved three lines of code. After the definition of $(C readInt()), the same goal is achieved by a single line of code. Further, the definition of $(C readInt()) allowed removing the definition of the variable $(C number) altogether. +) + +$(H6 Commented lines of code as functions) + +$(P +Sometimes the need to write a comment to describe the purpose of a group of lines of code is an indication that those lines could better be moved to a newly defined function. If the name of the function is descriptive enough then there will be no need for the comment either. +) + +$(P +The three commented groups of lines of the first version of the program have been used for defining new functions that achieved the same tasks. +) + +$(P +Another important benefit of removing comment lines is that comments tend to become outdated as the code gets modified over time. When updating code, programmers sometimes forget to update associated comments thus these comments become either useless or, even worse, misleading. For that reason, it is beneficial to try to write programs without the need for comments. +) + +$(PROBLEM_COK + +$(PROBLEM Modify the $(C printMenu()) function to take the entire set of menu items as a parameter. For example, the menu items can be passed to the function as in the following code: + +--- + string[] items = + [ "Black", "Red", "Green", "Blue", "White" ]; + printMenu(items, 1); +--- + +$(P +Have the program produce the following output: +) + +$(SHELL + 1 Black + 2 Red + 3 Green + 4 Blue + 5 White +) + +) + +$(PROBLEM +The following program uses a two dimensional array as a canvas. Start with that program and improve it by adding more functionality to it: + +--- +import std.stdio; + +enum totalLines = 20; +enum totalColumns = 60; + +/* The 'alias' in the next line makes 'Line' an alias of + * dchar[totalColumns]. Every 'Line' that is used in the rest + * of the program will mean dchar[totalColumns] from this + * point on. + * + * Also note that 'Line' is a fixed-length array. */ +alias Line = dchar[totalColumns]; + +/* A dynamic array of Lines is being aliased as 'Canvas'. */ +alias Canvas = Line[]; + +/* Prints the canvas line by line. */ +void print(Canvas canvas) { + foreach (line; canvas) { + writeln(line); + } +} + +/* Places a dot at the specified location on the canvas. In a + * sense, "paints" the canvas. */ +void putDot(Canvas canvas, int line, int column) { + canvas[line][column] = '#'; +} + +/* Draws a vertical line of the specified length from the + * specified position. */ +void drawVerticalLine(Canvas canvas, + int line, + int column, + int length) { + foreach (lineToPaint; line .. line + length) { + putDot(canvas, lineToPaint, column); + } +} + +void main() { + Line emptyLine = '.'; + + /* An empty canvas */ + Canvas canvas; + + /* Constructing the canvas by adding empty lines */ + foreach (i; 0 .. totalLines) { + canvas ~= emptyLine; + } + + /* Using the canvas */ + putDot(canvas, 7, 30); + drawVerticalLine(canvas, 5, 10, 4); + + print(canvas); +} +--- + +) + +) + +Macros: + SUBTITLE=Functions + + DESCRIPTION=The function definitions in the D programming language + + KEYWORDS=d programming language tutorial book functions diff --git a/target/functions_more.d b/target/functions_more.d new file mode 100644 index 0000000..a5f3f22 --- /dev/null +++ b/target/functions_more.d @@ -0,0 +1,959 @@ +Ddoc + +$(DERS_BOLUMU More Functions) + +$(P +Functions have been covered in the following chapters so far in the book: +) + +$(UL +$(LI $(LINK2 /ders/d.en/functions.html, Functions)) + +$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters)) + +$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) + +$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas)) + +) + +$(P +This chapter will cover more features of functions. +) + +$(H5 Return type attributes) + +$(P +Functions can be marked as $(C auto), $(C ref), $(C inout), and $(C auto ref). These attributes are about return types of functions. +) + +$(H6 $(IX auto, return type) $(IX auto function) $(C auto) functions) + +$(P +The return types of $(C auto) functions need not be specified: +) + +--- +$(HILITE auto) add(int first, double second) { + double result = first + second; + return result; +} +--- + +$(P +The return type is deduced by the compiler from the $(C return) expression. Since the type of $(C result) is $(C double), the return type of $(C add()) is $(C double). +) + +$(P +If there are more than one $(C return) statement, then the return type of the function is their $(I common type). (We have seen common type in $(LINK2 /ders/d.en/ternary.html, the Ternary Operator ?: chapter).) For example, because the common type of $(C int) and $(C double) is $(C double), the return type of the following $(C auto) function is $(C double) as well: +) + +--- +auto func(int i) { + if (i < 0) { + return i; // returns 'int' here + } + + return i * 1.5; // returns 'double' here +} + +void main() { + // The return type of the function is 'double' + auto result = func(42); + static assert(is (typeof(result) == $(HILITE double))); +} +--- + +$(H6 $(IX ref, return type) $(C ref) functions) + +$(P +Normally, the expression that is returned from a function is copied to the caller's context. $(C ref) specifies that the expression should be returned by-reference instead. +) + +$(P +For example, the following function returns the greater of its two parameters: +) + +--- +$(CODE_NAME greater)int greater(int first, int second) { + return (first > second) ? first : second; +} +--- + +$(P +Normally, both the parameters and the return value of that function are copied: +) + +--- +$(CODE_XREF greater)import std.stdio; + +void main() { + int a = 1; + int b = 2; + int result = greater(a, b); + result += 10; // ← neither a nor b changes + writefln("a: %s, b: %s, result: %s", a, b, result); +} +--- + +$(P +Because the return value of $(C greater()) is copied to $(C result), adding to $(C result) affects only that variable; neither $(C a) nor $(C b) changes: +) + +$(SHELL_SMALL +a: 1, b: 2, result: 12 +) + +$(P +$(C ref) parameters are passed by references instead of being copied. The same keyword has the same effect on return values: +) + +--- +$(HILITE ref) int greater($(HILITE ref) int first, $(HILITE ref) int second) { + return (first > second) ? first : second; +} +--- + +$(P +This time, the returned reference would be an alias to one of the arguments and mutating the returned reference would modify either $(C a) or $(C b): +) + +--- + int a = 1; + int b = 2; + greater(a, b) += 10; // ← either a or b changes + writefln("a: %s, b: %s", a, b); +--- + +$(P +Note that the returned reference is incremented directly. As a result, the greater of the two arguments changes: +) + +$(SHELL_SMALL +a: 1, b: $(HILITE 12) +) + +$(P +$(IX pointer) $(IX local variable) $(IX variable, local) $(B Local reference requires a pointer:) An important point is that although the return type is marked as $(C ref), $(C a) and $(C b) would still not change if the return value were assigned to a local variable: +) + +--- + int result = greater(a, b); + result += 10; // ← only result changes +--- + +$(P +Although $(C greater()) returns a reference to $(C a) or $(C b), that reference gets copied to the local variable $(C result), and again neither $(C a) nor $(C b) changes: +) + +$(SHELL_SMALL +a: 1, b: 2, result: 12 +) + +$(P +For $(C result) be a reference to $(C a) or $(C b), it has to be defined as a pointer: +) + +--- + int $(HILITE *) result = $(HILITE &)greater(a, b); + $(HILITE *)result += 10; + writefln("a: %s, b: %s, result: %s", a, b, $(HILITE *)result); +--- + +$(P +This time $(C result) would be a reference to either $(C a) or $(C b) and the mutation through it would affect the actual variable: +) + +$(SHELL_SMALL +a: 1, b: $(HILITE 12), result: 12 +) + +$(P +$(B It is not possible to return a reference to a local variable:) The $(C ref) return value is an alias to one of the arguments that start their lives even before the function is called. That means, regardless of whether a reference to $(C a) or $(C b) is returned, the returned reference refers to a variable that is still alive. +) + +$(P +Conversely, it is not possible to return a reference to a variable that is not going to be alive upon leaving the function: +) + +--- +$(HILITE ref) string parenthesized(string phrase) { + string result = '(' ~ phrase ~ ')'; + return result; $(DERLEME_HATASI) +} // ← the lifetime of result ends here +--- + +$(P +The lifetime of local $(C result) ends upon leaving the function. For that reason, it is not possible to return a reference to that variable: +) + +$(SHELL_SMALL +Error: escaping $(HILITE reference to local variable) result +) + +$(H6 $(IX auto ref, return type) $(C auto ref) functions) + +$(P +$(C auto ref) helps with functions like $(C parenthesized()) above. Similar to $(C auto), the return type of an $(C auto ref) function is deduced by the compiler. Additionally, if the returned expression can be a reference, that variable is returned by reference as opposed to being copied. +) + +$(P +$(C parenthesized()) can be compiled if the return type is $(C auto ref): +) + +--- +$(HILITE auto ref) string parenthesized(string phrase) { + string result = '(' ~ phrase ~ ')'; + return result; // ← compiles +} +--- + +$(P +The very first $(C return) statement of the function determines whether the function returns a copy or a reference. +) + +$(P +$(C auto ref) is more useful in function templates where template parameters may be references or copies depending on context. +) + +$(H6 $(IX inout, return type) $(C inout) functions) + +$(P +The $(C inout) keyword appears for parameter and return types of functions. It works like a template for $(C const), $(C immutable), and $(I mutable). +) + +$(P +Let's rewrite the previous function as taking $(C string) (i.e. $(C immutable(char)[])) and returning $(C string): +) + +--- +string parenthesized(string phrase) { + return '(' ~ phrase ~ ')'; +} + +// ... + + writeln(parenthesized("hello")); +--- + +$(P +As expected, the code works with that $(C string) argument: +) + +$(SHELL_SMALL +(hello) +) + +$(P +However, as it works only with $(C immutable) strings, the function can be seen as being less useful than it could have been: +) + +--- + char[] m; // has mutable elements + m ~= "hello"; + writeln(parenthesized(m)); $(DERLEME_HATASI) +--- + +$(SHELL_SMALL +Error: function deneme.parenthesized ($(HILITE string) phrase) +is not callable using argument types ($(HILITE char[])) +) + +$(P +The same limitation applies to $(C const(char)[]) strings as well. +) + +$(P +One solution for this usability issue is to overload the function for $(C const) and $(I mutable) strings: +) + +--- +char[] parenthesized(char[] phrase) { + return '(' ~ phrase ~ ')'; +} + +const(char)[] parenthesized(const(char)[] phrase) { + return '(' ~ phrase ~ ')'; +} +--- + +$(P +That design would be less than ideal due to the obvious code duplications. Another solution would be to define the function as a template: +) + +--- +T parenthesized(T)(T phrase) { + return '(' ~ phrase ~ ')'; +} +--- + +$(P +Although that would work, this time it may be seen as being too flexible and potentially requiring template constraints. +) + +$(P +$(C inout) is very similar to the template solution. The difference is that not the entire type but just the mutability attribute is deduced from the parameter: +) + +--- +$(HILITE inout)(char)[] parenthesized($(HILITE inout)(char)[] phrase) { + return '(' ~ phrase ~ ')'; +} +--- + +$(P +$(C inout) transfers the deduced mutability attribute to the return type. +) + +$(P +When the function is called with $(C char[]), it gets compiled as if $(C inout) is not specified at all. On the other hand, when called with $(C immutable(char)[]) or $(C const(char)[]), $(C inout) means $(C immutable) or $(C const), respectively. +) + +$(P +The following code demonstrates this by printing the type of the returned expression: +) + +--- + char[] m; + writeln(typeof(parenthesized(m)).stringof); + + const(char)[] c; + writeln(typeof(parenthesized(c)).stringof); + + immutable(char)[] i; + writeln(typeof(parenthesized(i)).stringof); +--- + +$(P +The output: +) + +$(SHELL_SMALL +char[] +const(char)[] +string +) + +$(H5 Behavioral attributes) + +$(P +$(C pure), $(C nothrow), and $(C @nogc) are about function behaviors. +) + +$(H6 $(IX pure) $(C pure) functions) + +$(P +As we have seen in $(LINK2 /ders/d.en/functions.html, the Functions chapter), functions can produce return values and side effects. When possible, return values should be preferred over side effects because functions that do not have side effects are easier to make sense of, which in turn helps with program correctness and maintainability. +) + +$(P +A similar concept is the purity of a function. Purity is defined differently in D from most other programming languages: In D, a function that does not access $(I mutable) global or $(C static) state is pure. (Since input and output streams are considered as mutable global state, pure functions cannot perform input or output operations either.) +) + +$(P +In other words, a function is pure if it produces its return value and side effects only by accessing its parameters, local variables, and $(I immutable) global state. +) + +$(P +An important aspect of purity in D is that pure functions can mutate their parameters. +) + +$(P +Additionally, the following operations that mutate the global state of the program are explicitly allowed in pure functions: +) + +$(UL +$(LI Allocate memory with the $(C new) expression) +$(LI Terminate the program) +$(LI Access the floating point processing flags) +$(LI Throw exceptions) +) + +$(P +The $(C pure) keyword specifies that a function should behave according to those conditions and the compiler guarantees that it does so. +) + +$(P +Naturally, since impure functions do not provide the same guarantees, a pure function cannot call impure functions. +) + +$(P +The following program demonstrates some of the operations that a pure function can and cannot perform: +) + +--- +import std.stdio; +import std.exception; + +int mutableGlobal; +const int constGlobal; +immutable int immutableGlobal; + +void impureFunction() { +} + +int pureFunction(ref int i, int[] slice) $(HILITE pure) { + // Can throw exceptions: + enforce(slice.length >= 1); + + // Can mutate its parameters: + i = 42; + slice[0] = 43; + + // Can access immutable global state: + i = constGlobal; + i = immutableGlobal; + + // Can use the new expression: + auto p = new int; + + // Cannot access mutable global state: + i = mutableGlobal; $(DERLEME_HATASI) + + // Cannot perform input and output operations: + writeln(i); $(DERLEME_HATASI) + + static int mutableStatic; + + // Cannot access mutable static state: + i = mutableStatic; $(DERLEME_HATASI) + + // Cannot call impure functions: + impureFunction(); $(DERLEME_HATASI) + + return 0; +} + +void main() { + int i; + int[] slice = [ 1 ]; + pureFunction(i, slice); +} +--- + +$(P +Although they are allowed to, some pure functions do not mutate their parameters. Following from the rules of purity, the only observable effect of such a function would be its return value. Further, since the function cannot access any mutable global state, the return value would be the same for a given set of arguments, regardless of when and how many times the function is called during the execution of the program. This fact gives both the compiler and the programmer optimization opportunities. For example, instead of calling the function a second time for a given set of arguments, its return value from the first call can be cached and used instead of actually calling the function again. +) + +$(P +$(IX inference, pure attribute) $(IX attribute inference, pure) Since the exact code that gets generated for a template instantiation depends on the actual template arguments, whether the generated code is pure depends on the arguments as well. For that reason, the purity of a template is inferred by the compiler from the generated code. (The $(C pure) keyword can still be specified by the programmer.) Similarly, the purity of an $(C auto) function is inferred. +) + +$(P +As a simple example, since the following function template would be impure when $(C N) is zero, it would not be possible to call $(C templ!0()) from a pure function: +) + +--- +import std.stdio; + +// This template is impure when N is zero +void templ(size_t N)() { + static if (N == 0) { + // Prints when N is zero: + writeln("zero"); + } +} + +void foo() $(HILITE pure) { + templ!0(); $(DERLEME_HATASI) +} + +void main() { + foo(); +} +--- + +$(P +The compiler infers that the $(C 0) instantiation of the template is impure and rejects calling it from the pure function $(C foo()): +) + +$(SHELL_SMALL +Error: pure function 'deneme.foo' $(HILITE cannot call impure function) +'deneme.templ!0.templ' +) + +$(P +However, since the instantiation of the template for values other than zero is pure, the program can be compiled for such values: +) + +--- +void foo() $(HILITE pure) { + templ!1(); // ← compiles +} +--- + +$(P +We have seen earlier above that input and output functions like $(C writeln()) cannot be used in pure functions because they access global state. Sometimes such limitations are too restrictive e.g. when needing to print a message temporarily during debugging. For that reason, the purity rules are relaxed for code that is marked as $(C debug): +) + +--- +import std.stdio; + +debug size_t fooCounter; + +void foo(int i) $(HILITE pure) { + $(HILITE debug) ++fooCounter; + + if (i == 0) { + $(HILITE debug) writeln("i is zero"); + i = 42; + } + + // ... +} + +void main() { + foreach (i; 0..100) { + if ((i % 10) == 0) { + foo(i); + } + } + + debug writefln("foo is called %s times", fooCounter); +} +--- + +$(P +The pure function above mutates the global state of the program by modifying a global variable and printing a message. Despite those impure operations, it still can be compiled because those operations are marked as $(C debug). +) + +$(P +$(I $(B Note:) Remember that those statements are included in the program only if the program is compiled with the $(C -debug) command line switch.) +) + +$(P +Member functions can be marked as $(C pure) as well. Subclasses can override impure functions as $(C pure) but the reverse is not allowed: +) + +--- +interface Iface { + void foo() pure; // Subclasses must define foo as pure. + + void bar(); // Subclasses may define bar as pure. +} + +class Class : Iface { + void foo() pure { // Required to be pure + // ... + } + + void bar() pure { // pure although not required + // ... + } +} +--- + +$(P +Delegates and anonymous functions can be pure as well. Similar to templates, whether a function or delegate literal, or $(C auto) function is pure is inferred by the compiler: +) + +--- +import std.stdio; + +void foo(int delegate(double) $(HILITE pure) dg) { + int i = dg(1.5); +} + +void main() { + foo(a => 42); // ← compiles + + foo((a) { $(DERLEME_HATASI) + writeln("hello"); + return 42; + }); +} +--- + +$(P +$(C foo()) above requires that its parameter be a pure delegate. The compiler infers that the lambda $(C a => 42) is pure and allows it as an argument for $(C foo()). However, since the other delegate is impure it cannot be passed to $(C foo()): +) + +$(SHELL_SMALL +Error: function deneme.foo (int delegate(double) $(HILITE pure) dg) +is $(HILITE not callable) using argument types (void) +) + +$(P +One benefit of $(C pure) functions is that their return values can be used to initialize $(C immutable) variables. Although the array produced by $(C makeNumbers()) below is mutable, it is not possible for its elements to be changed by any code outside of that function. For that reason, the initialization works. +) + +--- +int[] makeNumbers() pure { + int[] result; + result ~= 42; + return result; +} + +void main() { + $(HILITE immutable) array = makeNumbers(); +} +--- + +$(H6 $(IX nothrow) $(IX throw) $(C nothrow) functions) + +$(P +We saw the exception mechanism in $(LINK2 /ders/d.en/exceptions.html, the Exceptions chapter.) +) + +$(P +It would be good practice for functions to document the types of exceptions that they may throw under specific error conditions. However, as a general rule, callers should assume that any function can throw any exception. +) + +$(P +Sometimes it is more important to know that a function does not emit any exception at all. For example, some algorithms can take advantage of the fact that certain of their steps cannot be interrupted by an exception. +) + +$(P +$(C nothrow) guarantees that a function does not emit any exception: +) + +--- +int add(int lhs, int rhs) $(HILITE nothrow) { + // ... +} +--- + +$(P +$(I $(B Note:) Remember that it is not recommended to catch $(C Error) nor its base class $(C Throwable). What is meant here by "any exception" is "any exception that is defined under the $(C Exception) hierarchy." A $(C nothrow) function can still emit exceptions that are under the $(C Error) hierarchy, which represents irrecoverable error conditions that should preclude the program from continuing its execution.) +) + +$(P +Such a function can neither throw an exception itself nor can call a function that may throw an exception: +) + +--- +int add(int lhs, int rhs) nothrow { + writeln("adding"); $(DERLEME_HATASI) + return lhs + rhs; +} +--- + +$(P +The compiler rejects the code because $(C add()) violates the no-throw guarantee: +) + +$(SHELL_SMALL +Error: function 'deneme.add' is nothrow yet $(HILITE may throw) +) + +$(P +This is because $(C writeln) is not (and cannot be) a $(C nothrow) function. +) + +$(P +$(IX inference, nothrow attribute) $(IX attribute inference, nothrow) The compiler can infer that a function can never emit an exception. The following implementation of $(C add()) is $(C nothrow) because it is obvious to the compiler that the $(C try-catch) block prevents any exception from escaping the function: +) + +--- +int add(int lhs, int rhs) nothrow { + int result; + + try { + writeln("adding"); // ← compiles + result = lhs + rhs; + + } catch (Exception error) { // catches all exceptions + // ... + } + + return result; +} +--- + +$(P +As mentioned above, $(C nothrow) does not include exceptions that are under the $(C Error) hierarchy. For example, although accessing an element of an array with $(C []) can throw $(C RangeError), the following function can still be defined as $(C nothrow): +) + +--- +int foo(int[] arr, size_t i) $(HILITE nothrow) { + return 10 * arr$(HILITE [i]); +} +--- + +$(P +As with purity, the compiler automatically deduces whether a template, delegate, or anonymous function is $(C nothrow). +) + +$(H6 $(IX @nogc) $(C @nogc) functions) + +$(P +D is a garbage collected language. Many data structures and algorithms in most D programs take advantage of dynamic memory blocks that are managed by the garbage collector (GC). Such memory blocks are reclaimed again by the GC by an algorithm called $(I garbage collection). +) + +$(P +Some commonly used D operations take advantage of the GC as well. For example, elements of arrays live on dynamic memory blocks: +) + +--- +// A function that takes advantage of the GC indirectly +int[] append(int[] slice) { + slice $(HILITE ~=) 42; + return slice; +} +--- + +$(P +If the slice does not have sufficient capacity, the $(C ~=) operator above allocates a new memory block from the GC. +) + +$(P +Although the GC is a significant convenience for data structures and algorithms, memory allocation and garbage collection are costly operations that make the execution of some programs noticeably slow. +) + +$(P +$(C @nogc) means that a function cannot use the GC directly or indirectly: +) + +--- +void foo() $(HILITE @nogc) { + // ... +} +--- + +$(P +The compiler guarantees that a $(C @nogc) function does not involve GC operations. For example, the following function cannot call $(C append()) above, which does not provide the $(C @nogc) guarantee: +) + +--- +void foo() $(HILITE @nogc) { + int[] slice; + // ... + append(slice); $(DERLEME_HATASI) +} +--- + +$(SHELL_SMALL +Error: @nogc function 'deneme.foo' $(HILITE cannot call non-@nogc function) +'deneme.append' +) + +$(H5 Code safety attributes) + +$(P +$(IX inference, @safe attribute) $(IX attribute inference, @safe) $(C @safe), $(C @trusted), and $(C @system) are about the code safety that a function provides. As with purity, the compiler infers the safety level of templates, delegates, anonymous functions, and $(C auto) functions. +) + +$(H6 $(IX @safe) $(C @safe) functions) + +$(P +A class of programming errors involve $(I corrupting) data at unrelated locations in memory by writing at those locations unintentionally. Such errors are mostly due to mistakes made in using pointers and applying type casts. +) + +$(P +$(C @safe) functions guarantee that they do not contain any operation that may corrupt memory. The compiler does not allow the following operations in $(C @safe) functions: +) + +$(UL + +$(LI Pointers cannot be converted to other pointer types other than $(C void*).) + +$(LI A non-pointer expression cannot be converted to a pointer value.) + +$(LI Pointers cannot be mutated.) + +$(LI Unions that have pointer or reference members cannot be used.) + +$(LI Functions marked as $(C @system) cannot be called.) + +$(LI Exceptions that are not descended from $(C Exception) cannot be caught.) + +$(LI $(I Inline assembler) cannot be used.) + +$(LI $(I Mutable) variables cannot be cast to $(C immutable).) + +$(LI $(C immutable) variables cannot be cast to $(I mutable).) + +$(LI Thread-local variables cannot be cast to $(C shared).) + +$(LI $(C shared) variables cannot be cast to thread-local.) + +$(LI Addresses of function-local variables cannot be taken.) + +$(LI $(C __gshared) variables cannot be accessed.) + +) + +$(H6 $(IX @trusted) $(C @trusted) functions) + +$(P +Some functions may actually be safe but cannot be marked as $(C @safe) for various reasons. For example, a function may have to call a library written in C, where no language support exists for safety in that language. +) + +$(P +Some other functions may actually perform operations that are not allowed in $(C @safe) code, but may be well tested and $(I trusted) to be correct. +) + +$(P +$(C @trusted) is an attribute that communicates to the compiler that $(I although the function cannot be marked as $(C @safe), consider it safe). The compiler trusts the programmer and treats $(C @trusted) code as if it is safe. For example, it allows $(C @safe) code to call $(C @trusted) code. +) + +$(H6 $(IX @system) $(C @system) functions) + +$(P +Any function that is not marked as $(C @safe) or $(C @trusted) is considered $(C @system), which is the default safety attribute. +) + +$(H5 $(IX CTFE) $(IX compile time function execution) Compile time function execution (CTFE)) + +$(P +In many programming languages, computations that are performed at compile time are very limited. Such computations are usually as simple as calculating the length of a fixed-length array or simple arithmetic operations: +) + +--- + writeln(1 + 2); +--- + +$(P +The $(C 1 + 2) expression above is compiled as if it has been written as $(C 3); there is no computation at runtime. +) + +$(P +D has CTFE, which allows any function to be executed at compile time as long as it is possible to do so. +) + +$(P +Let's consider the following program that prints a menu to the output: +) + +--- +import std.stdio; +import std.string; +import std.range; + +string menuLines(string[] choices) { + string result; + + foreach (i, choice; choices) { + result ~= format(" %s. %s\n", i + 1, choice); + } + + return result; +} + +string menu(string title, + string[] choices, + size_t width) { + return format("%s\n%s\n%s", + title.center(width), + '='.repeat(width), // horizontal line + menuLines(choices)); +} + +void main() { + $(HILITE enum) drinks = + menu("Drinks", + [ "Coffee", "Tea", "Hot chocolate" ], 20); + + writeln(drinks); +} +--- + +$(P +Although the same result can be achieved in different ways, the program above performs non-trivial operations to produce the following $(C string): +) + +$(SHELL_SMALL + Drinks +==================== + 1. Coffee + 2. Tea + 3. Hot chocolate +) + +$(P +Remember that the initial value of $(C enum) constants like $(C drinks) must be known at compile time. That fact is sufficient for $(C menu()) to be executed at compile time. The value that it returns at compile time is used as the initial value of $(C drinks). As a result, the program is compiled as if that value is written explicitly in the program: +) + +--- + // The equivalent of the code above: + enum drinks = " Drinks \n" + "====================\n" + " 1. Coffee\n" + " 2. Tea\n" + " 3. Hot chocolate\n"; +--- + +$(P +For a function to be executed at compile time, it must appear in an expression that in fact is needed at compile time: +) + +$(UL +$(LI Initializing a $(C static) variable) +$(LI Initializing an $(C enum) variable) +$(LI Calculating the length of a fixed-length array) +$(LI Calculating a template $(I value) argument) +) + +$(P +Clearly, it would not be possible to execute every function at compile time. For example, a function that accesses a global variable cannot be executed at compile time because the global variable does not start its life until run time. Similarly, since $(C stdout) is available only at run time, functions that print cannot be executed at compile time. +) + +$(H6 $(IX __ctfe) The $(C __ctfe) variable) + +$(P +It is a powerful aspect of CTFE that the same function is used for both compile time and run time depending on when its result is needed. Although the function need not be written in any special way for CTFE, some operations in the function may make sense only at compile time or run time. The special variable $(C __ctfe) can be used to differentiate the code that are only for compile time or only for run time. The value of this variable is $(C true) when the function is being executed for CTFE, $(C false) otherwise: +) + +--- +import std.stdio; + +size_t counter; + +int foo() { + if (!$(HILITE __ctfe)) { + // This code is for execution at run time + ++counter; + } + + return 42; +} + +void main() { + enum i = foo(); + auto j = foo(); + writefln("foo is called %s times.", counter); +} +--- + +$(P +As $(C counter) lives only at run time, it cannot be incremented at compile time. For that reason, the code above attempts to increment it only for run-time execution. Since the value of $(C i) is determined at compile time and the value of $(C j) is determined at run time, $(C foo()) is reported to have been called just once during the execution of the program: +) + +$(SHELL_SMALL +foo is called 1 times. +) + +$(H5 Summary) + +$(UL + +$(LI The return type of an $(C auto) function is deduced automatically.) + +$(LI The return value of a $(C ref) function is a reference to an existing variable.) + +$(LI The return value of an $(C auto ref) function is a reference if possible, a copy otherwise.) + +$(LI $(C inout) carries the $(C const), $(C immutable), or $(I mutable) attribute of the parameter to the return type.) + +$(LI A $(C pure) function cannot access $(I mutable) global or static state. The compiler infers the purity of templates, delegates, anonymous functions, and $(C auto) functions.) + +$(LI $(C nothrow) functions cannot emit exceptions. The compiler infers whether a template, delegate, anonymous function, or $(C auto) function is no-throw.) + +$(LI $(C @nogc) functions cannot involve GC operations.) + +$(LI $(C @safe) functions cannot corrupt memory. The compiler infers the safety attributes of templates, delegates, anonymous functions, and $(C auto) functions.) + +$(LI $(C @trusted) functions are indeed safe but cannot be specified as such; they are considered $(C @safe) both by the programmer and the compiler.) + +$(LI $(C @system) functions can use every D feature. $(C @system) is the default safety attribute.) + +$(LI Functions can be executed at compile time as well (CTFE). This can be differentiated by the value of the special variable $(C __ctfe).) + +) + +Macros: + SUBTITLE=More Functions + + DESCRIPTION=Additional features of D functions that have not been mentioned up to this point: Automatic return type deduction, purity, not throwing exceptions, memory safety, and CTFE. + + KEYWORDS=d programming language tutorial book auto ref pure ctfe diff --git a/target/goto.d b/target/goto.d new file mode 100644 index 0000000..abc3cbe --- /dev/null +++ b/target/goto.d @@ -0,0 +1,263 @@ +Ddoc + +$(DERS_BOLUMU $(IX label) $(IX goto) Labels and $(CH4 goto)) + +$(P +$(IX :, label) Labels are names given to lines of code in order to direct program flow to those lines later on. +) + +$(P +A label consists of a name and the $(C :) character: +) + +--- +end: // ← a label +--- + +$(P +That label gives the name $(I end) to the line that it is defined on. +) + +$(P +$(I $(B Note:) In reality, a label can appear between statements on the same line to name the exact spot that it appears at, but this is not a common practice:) +) + +--- + anExpression(); $(HILITE end:) anotherExpression(); +--- + +$(H5 $(C goto)) + +$(P +$(C goto) directs program flow to the specified label: +) + +--- +void foo(bool condition) { + writeln("first"); + + if (condition) { + $(HILITE goto) end; + } + + writeln("second"); + +end: + + writeln("third"); +} +--- + +$(P +When $(C condition) is $(C true), the program flow $(I goes to) label $(C end), effectively skipping the line that prints "second": +) + +$(SHELL_SMALL +first +third +) + +$(P +$(C goto) works the same way as in the C and C++ programming languages. Being notorious for making it hard to understand the intent and flow of code, $(C goto) is discouraged even in those languages. Statements like $(C if), $(C while), $(C for) etc. should be used instead. +) + +$(P +For example, the previous code can be written without $(C goto) in a more $(I structured) way: +) + +--- +void foo(bool condition) { + writeln("first"); + + if (!condition) { + writeln("second"); + } + + writeln("third"); +} +--- + +$(P +However, there are two acceptable uses of $(C goto) in C, none of which is necessary in D. +) + +$(H6 Finalization area) + +$(P +One of the valid uses of $(C goto) in C is going to the finalization area where the cleanup operations of a function are performed (e.g. giving resources back, undoing certain operations, etc.): +) + +$(C_CODE +// --- C code --- + +int foo() { + // ... + + if (error) { + goto finally; + } + + // ... + +finally: + $(COMMENT // ... cleanup operations ...) + + return error; +} +) + +$(P +This use of $(C goto) is not necessary in D because there are other ways of managing resources: the garbage collector, destructors, the $(C catch) and $(C finally) blocks, $(C scope()) statements, etc. +) + +$(P $(I $(B Note:) This use of $(C goto) is not necessary in C++ either.) +) + +$(H6 $(C continue) and $(C break) for outer loops) + +$(P +The other valid use of $(C goto) in C is about outer loops. +) + +$(P +Since $(C continue) and $(C break) affect only the inner loop, one way of continuing or breaking out of the outer loop is by $(C goto) statements: +) + +$(C_CODE +// --- C code --- + + while (condition) { + + while (otherCondition) { + + $(COMMENT // affects the inner loop) + continue; + + $(COMMENT // affects the inner loop) + break; + + $(COMMENT // works like 'continue' for the outer loop) + goto continueOuter; + + $(COMMENT // works like 'break' for the outer loop) + goto breakOuter; + } + + continueOuter: + ; + } +breakOuter: +) + +$(P +The same technique can be used for outer $(C switch) statements as well. +) + +$(P +This use of $(C goto) is not needed in D because D has loop labels, which we will see below. +) + +$(P $(I $(B Note:) This use of $(C goto) can be encountered in C++ as well.) +) + +$(H6 The problem of skipping constructors) + +$(P +The constructor is called on an object exactly where that object is defined. This is mainly because the information that is needed to construct an object is usually not available until that point. Also, there is no need to construct an object if that object is not going to be used in the program at all. +) + +$(P +When $(C goto) skips a line that an object is constructed on, the program can be using an object that has not been prepared yet: +) + +--- + if (condition) { + goto aLabel; // skips the constructor + } + + auto s = S(42); // constructs the object properly + +aLabel: + + s.bar(); // BUG: 's' may not be ready for use +--- + +$(P +The compiler prevents this bug: +) + +$(SHELL +Error: goto skips declaration of variable deneme.main.s +) + +$(H5 $(IX loop label) Loop labels) + +$(P +Loops can have labels and $(C goto) statements can refer to those labels: +) + +--- +$(HILITE outerLoop:) + while (condition) { + + while (otherCondition) { + + // affects the inner loop + continue; + + // affects the inner loop + break; + + // continues the outer loop + continue $(HILITE outerLoop); + + // breaks the outer loop + break $(HILITE outerLoop); + } + } +--- + +$(P +$(C switch) statements can have labels as well. An inner $(C break) statement can refer to an outer $(C switch) to break out of the outer $(C switch) statement. +) + +$(H5 $(C goto) in $(C case) sections) + +$(P +We have already seen the use of $(C goto) in $(C case) sections in $(LINK2 /ders/d.en/switch_case.html, the $(C switch) and $(C case) chapter): +) + +$(UL + +$(LI $(IX goto case) $(IX case, goto) $(C goto case) causes the execution to continue to the next $(C case).) + +$(LI $(IX goto default) $(IX default, goto) $(C goto default) causes the execution to continue to the $(C default) section.) + +$(LI $(C goto case $(I expression)) causes the execution to continue to the $(C case) that matches that expression.) + +) + +$(H5 Summary) + +$(UL + +$(LI +Some of the uses of $(C goto) are not necessary in D. +) + +$(LI +$(C break) and $(C continue) can specify labels to affect outer loops and $(C switch) statements. +) + +$(LI +$(C goto) inside $(C case) sections can make the program flow jump to other $(C case) and $(C default) sections. +) + +) + +Macros: + SUBTITLE=Labels and goto + + DESCRIPTION=Labels that are used for giving names to code lines, and goto that causes program flow to jump to those lines. + + KEYWORDS=d programming language tutorial book goto diff --git a/target/hello_world.cozum.d b/target/hello_world.cozum.d new file mode 100644 index 0000000..2adc68b --- /dev/null +++ b/target/hello_world.cozum.d @@ -0,0 +1,52 @@ +Ddoc + +$(COZUM_BOLUMU The Hello World Program) + +$(OL + +$(LI + +--- +import std.stdio; + +void main() { + writeln("Something else... :p"); +} +--- + +) + +$(LI + +--- +import std.stdio; + +void main() { + writeln("A line..."); + writeln("Another line..."); +} +--- + +) + +$(LI + +The following program cannot be compiled because the semicolon at the end of the $(C writeln) line is missing: + +--- +import std.stdio; + +void main() { + writeln("Hello world!") $(DERLEME_HATASI) +} +--- +) + +) + +Macros: + SUBTITLE=The Hello World Program Solutions + + DESCRIPTION=The exercise solutions for the first D program: Hello World! + + KEYWORDS=programming in d tutorial hello world program exercise solution diff --git a/target/hello_world.d b/target/hello_world.d new file mode 100644 index 0000000..e1d396a --- /dev/null +++ b/target/hello_world.d @@ -0,0 +1,216 @@ +Ddoc + +$(DIV_CLASS page_one, + +$(DERS_BOLUMU $(IX hello world) The Hello World Program) + +$(P +The first program to show in most programming language books is the $(I hello world) program. This very short and simple program merely writes "hello world" and finishes. This program is important because it includes some of the essential concepts of that language. +) + +$(P +Here is a $(I hello world) program in D: +) + +--- +import std.stdio; + +void main() { + writeln("Hello world!"); +} +--- + +$(P +The $(I source code) above needs to be compiled by a D compiler to produce an executable program. +) + +$(H5 $(IX compiler installation) $(IX installation, compiler) Compiler installation) + +$(P +$(IX gdc) $(IX ldc) At the time of writing this chapter, there are three D compilers to choose from: $(C dmd), the Digital Mars compiler; $(C gdc), the D compiler of GCC; and $(C ldc), the D compiler that targets the LLVM compiler infrastructure. +) + +$(P +$(IX dmd) $(C dmd) is the D compiler that has been used during the design and development of the language over the years. All of the examples in this book have been tested with $(C dmd). For that reason, it would be the easiest for you to start with $(C dmd) and try other compilers only if you have a specific need to. The code samples in this book were compiled with $(C dmd) version 2.074.0. +) + +$(P +To install the latest version of $(C dmd), go to the $(LINK2 http://www.dlang.org/download.html, download page at Digital Mars) and select the compiler build that matches your computer environment. You must select the $(C dmd) build that is for your operating system and package management system, and whether you have a 32-bit or a 64-bit CPU and operating system. Do not install a D1 compiler. This book covers only $(I D version two). +) + +$(P +The installation steps are different on different environments but it should be as easy as following simple on-screen instructions and clicking a couple of buttons. +) + +$(H5 $(IX source file) Source file) + +$(P +The file that the programmer writes for the D compiler to compile is called the $(I source file). Since D is usually used as a compiled language, the source file itself is not an executable program. The source file must be converted to an executable program by the compiler. +) + +$(P +As with any file, the source file must have a name. Although the name can be anything that is legal on the file system, it is customary to use the $(C .d) $(I file extension) for D source files because development environments, programming tools, and programmers all expect this to be the case. For example, $(C test.d), $(C game.d), $(C invoice.d), etc. are appropriate D source file names. +) + +$(H5 Compiling the hello world program) + +$(P +$(IX text editor) $(IX editor, text) You will write the source file in a $(LINK2 http://wiki.dlang.org/Editors, text editor) (or an $(I IDE) as mentioned below). Copy or type the hello world program above into a text file and save it under the name $(C hello.d). +) + +$(P +The compiler will soon check that the syntax of this source code is correct (i.e. it is valid according to the language rules) and make a program out of it by translating it into machine code. Follow these steps to compile the program: +) + +$(OL + +$(LI Open a terminal window.) + +$(LI Go to the directory where you saved $(C hello.d).) + +$(LI Enter the following command. (Do not type the $(C $) character; it is there to indicate the command line prompt.)) + +) + +$(SHELL +$(SHELL_OBSERVED $) dmd hello.d +) + +$(P +If you did not make any mistake, you may think that nothing has happened. To the contrary, it means that everything went well. There should be an executable file named $(C hello) (or $(C hello.exe) under Windows) that has just been created by the compiler. +) + +$(P +If the compiler has instead printed some messages, you probably have made a mistake when copying the program code. Try to identify the mistake, correct it, and retry compiling. You will routinely make many mistakes when programming, so the process of correcting and compiling will become familiar to you. +) + +$(P +Once the program has been created successfully, type the name of the executable program to run it. You should see that the program prints "Hello world!": +) + +$(SHELL +$(SHELL_OBSERVED $) ./hello $(SHELL_NOTE running the program) +Hello world! $(SHELL_NOTE the message that it prints) +) + +$(P +Congratulations! Your first D program works as expected. +) + +$(H5 $(IX compiler switch) Compiler switches) + +$(P +The compiler has many command line switches that are used for influencing how it compiles the program. To see a list of compiler switches enter just the name of the compiler: +) + +$(SHELL +$(SHELL_OBSERVED $) dmd $(SHELL_NOTE enter just the name) +DMD64 D Compiler v2.074.0 +Copyright (c) 1999-2017 by Digital Mars written by Walter Bright +... + -de show use of deprecated features as errors (halt compilation) +... + -unittest compile in unit tests +... + -w warnings as errors (compilation will halt) +... +) + +$(P +The abbreviated output above shows only the command line switches that I recommend that you always use. Although it makes no difference with the hello world program in this chapter, the following command line would compile the program by enabling unit tests and not allowing any warnings or deprecated features. We will see these and other switches in more detail in later chapters: +) + +$(SHELL +$(SHELL_OBSERVED $) dmd hello.d -de -w -unittest +) + +$(P +The complete list of $(C dmd) command line switches can be found in the $(LINK2 http://dlang.org/dmd-linux.html, DMD Compiler documentation). +) + +$(P +One other command line switch that you may find useful is $(C -run). It compiles the source code, produces the executable program, and runs it with a single command. $(C -run) must be the last of compiler switches, specified right before the name of the source file: +) + +$(SHELL +$(SHELL_OBSERVED $) dmd -de -w -unittest $(HILITE -run) hello.d +Hello world! $(SHELL_NOTE the program is automatically executed) +) + +$(H5 $(IX IDE) IDE) + +$(P +In addition to the compiler, you may also consider installing an IDE (integrated development environment). IDEs are designed to make program development easier by simplifying the steps of writing, compiling, and debugging. +) + +$(P +If you do install an IDE, compiling and running the program will be as simple as pressing a key or clicking a button on the IDE. I still recommend that you familiarize yourself with compiling programs manually in a terminal window. +) + +$(P +If you decide to install an IDE, go to $(LINK2 http://wiki.dlang.org/IDEs, the IDEs page at dlang.org) to see a list of available IDEs. +) + +$(H5 Contents of the hello world program) + +$(P +Here is a quick list of the many D concepts that have appeared in this short program: +) + +$(P $(B Core feature): Every language defines its syntax, fundamental types, keywords, rules, etc. All of these make the $(I core features) of that language. The parentheses, semicolons, and words like $(C main) and $(C void) are all placed according to the rules of D. These are similar to the rules of English: subject, verb, punctuation, sentence structure, etc. +) + +$(P $(B Library and function): The core features define only the structure of the language. They are used for defining functions and user types, and those in turn are used for building libraries. Libraries are collections of reusable program parts that get $(I linked) with your programs to help them achieve their purposes. +) + +$(P +$(C writeln) above is a $(I function) in D's standard $(I library). It is used for printing a line of text, as its name suggests: write line. +) + +$(P $(B Module): Library contents are grouped by types of tasks that they intend to help with. Such a group is called a module. The only module that this program uses is $(C std.stdio), which handles data input and output. +) + +$(P $(B Character and string): Expressions like $(STRING "Hello world!") are called $(I strings), and the elements of strings are called $(I characters). The only string in this program contains characters $(STRING 'H'), $(STRING 'e'), $(STRING '!'), and others. +) + +$(P $(B Order of operations): Programs complete their tasks by executing operations in a certain order. These tasks start with the operations that are written in the function named $(C main). The only operation in this program writes "Hello world!". +) + +$(P $(B Significance of uppercase and lowercase letters): You can choose to type any character inside strings, but you must type the other characters exactly as they appear in the program. This is because lowercase vs. uppercase is significant in D programs. For example, $(C writeln) and $(C Writeln) are two different names. +) + +$(P +$(IX keyword) $(B Keyword): Special words that are a part of the core features of the language are $(I keywords). Such words are reserved for the language itself, and cannot be used for any other purpose in a D program. There are two keywords in this program: $(C import), which is used to introduce a module to the program; and $(C void), which here means "not returning anything". +) + +$(P +The complete list of D keywords is $(C abstract), $(C alias), $(C align), $(C asm), $(C assert), $(C auto), $(C body), $(C bool), $(C break), $(C byte), $(C case), $(C cast), $(C catch), $(C cdouble), $(C cent), $(C cfloat), $(C char), $(C class), $(C const), $(C continue), $(C creal), $(C dchar), $(C debug), $(C default), $(C delegate), $(C delete), $(C deprecated), $(C do), $(C double), $(C else), $(C enum), $(C export), $(C extern), $(C false), $(C final), $(C finally), $(C float), $(C for), $(C foreach), $(C foreach_reverse), $(C function), $(C goto), $(C idouble), $(C if), $(C ifloat), $(C immutable), $(C import), $(C in), $(C inout), $(C int), $(C interface), $(C invariant), $(C ireal), $(C is), $(C lazy), $(C long), $(C macro), $(C mixin), $(C module), $(C new), $(C nothrow), $(C null), $(C out), $(C override), $(C package), $(C pragma), $(C private), $(C protected), $(C public), $(C pure), $(C real), $(C ref), $(C return), $(C scope), $(C shared), $(C short), $(C static), $(C struct), $(C super), $(C switch), $(C synchronized), $(C template), $(C this), $(C throw), $(C true), $(C try), $(C typedef), $(C typeid), $(C typeof), $(C ubyte), $(C ucent), $(C uint), $(C ulong), $(C union), $(C unittest), $(C ushort), $(C version), $(C void), $(C volatile), $(C wchar), $(C while), $(C with), $(C __FILE__), $(C __FILE_FULL_PATH__), $(C __MODULE__), $(C __LINE__), $(C __FUNCTION__), $(C __PRETTY_FUNCTION__), $(C __gshared), $(C __traits), $(C __vector), and $(C __parameters). +) + +$(P +$(IX asm) $(IX __vector) $(IX delete) $(IX typedef) $(IX volatile) $(IX macro) We will cover these keywords in the upcoming chapters with the exception of the following ones: $(LINK2 http://dlang.org/statement.html#AsmStatement, $(C asm)) and $(LINK2 http://dlang.org/phobos/core_simd.html#.Vector, $(C __vector)) are outside of the scope of this book; $(C delete), $(C typedef), and $(C volatile) are deprecated; and $(C macro) is unused by D at this time. +) + +$(PROBLEM_COK + +$(PROBLEM Make the program output something else.) + +$(PROBLEM Change the program to output more than one line. You can do this by adding one more $(C writeln) line to the program.) + +$(PROBLEM Try to compile the program after making other changes; e.g. remove the semicolon at the end of the line with $(C writeln) and observe a compilation error. +) + +) + +) + +$(Ergin) + +Macros: + SUBTITLE=The Hello World Program + + DESCRIPTION=The first D program: Hello World! + + KEYWORDS=d programming language tutorial book + +SOZLER= diff --git a/target/if.cozum.d b/target/if.cozum.d new file mode 100644 index 0000000..f840d38 --- /dev/null +++ b/target/if.cozum.d @@ -0,0 +1,110 @@ +Ddoc + +$(COZUM_BOLUMU The if Statement) + +$(OL + +$(LI +The statement $(C writeln("Washing the plate")) is written indented as if to be within the $(C else) scope. However, because the scope of that $(C else) is not written with curly brackets, only the $(C writeln("Eating pie")) statement is actually inside the scope of that $(C else). + +$(P +Since whitespaces are not important in D programs, the $(I plate statement) is actually an independent statement within $(C main()) and is executed unconditionally. It confuses the reader as well because of having been indented more than usual. If the $(I plate statement) must really be within the $(C else) scope, there must be curly brackets around that scope: +) + +--- +import std.stdio; + +void main() { + bool existsLemonade = true; + + if (existsLemonade) { + writeln("Drinking lemonade"); + writeln("Washing the cup"); + + } else $(HILITE {) + writeln("Eating pie"); + writeln("Washing the plate"); + $(HILITE }) +} +--- + +) + +$(LI +We can come up with more than one design for the conditions of this game. I will show two examples. In the first one, we apply the information directly from the exercise: + +--- +import std.stdio; + +void main() { + write("What is the value of the die? "); + int die; + readf(" %s", &die); + + if (die == 1) { + writeln("You won"); + + } else if (die == 2) { + writeln("You won"); + + } else if (die == 3) { + writeln("You won"); + + } else if (die == 4) { + writeln("I won"); + + } else if (die == 5) { + writeln("I won"); + + } else if (die == 6) { + writeln("I won"); + + } else { + writeln("ERROR: ", die, " is invalid"); + } +} +--- + +$(P +Unfortunately, that program has many repetitions. We can achieve the same result by other designs. Here is one: +) + +--- +import std.stdio; + +void main() { + write("What is the value of the die? "); + int die; + readf(" %s", &die); + + if ((die == 1) || (die == 2) || (die == 3)) { + writeln("You won"); + + } else if ((die == 4) || (die == 5) || (die == 6)) { + writeln("I won"); + + } else { + writeln("ERROR: ", die, " is invalid"); + } +} +--- + +) + +$(LI +The previous designs cannot be used in this case. It is not practical to type 1000 different values in a program and expect them all be correct or readable. For that reason, it is better to determine whether the value of the die is $(I within a range): + +--- + if ((die >= 1) && (die <= 500)) +--- + +) + +) + +Macros: + SUBTITLE=The if Statement Solutions + + DESCRIPTION=Programming in D exercise solutions: the 'if' statement and its optional 'else' clause + + KEYWORDS=programming in d tutorial if else solution diff --git a/target/if.d b/target/if.d new file mode 100644 index 0000000..747c1a1 --- /dev/null +++ b/target/if.d @@ -0,0 +1,309 @@ +Ddoc + +$(DERS_BOLUMU $(IX if) $(CH4 if) Statement) + +$(P +We've learned that the actual work in a program is performed by expressions. All of the expressions of all of the programs that we've seen so far have started with the $(C main()) function and were executed until the end of $(C main). +) + +$(P +$(IX statement) $(I Statements), on the other hand, are features that affect the execution of expressions. Statements don't produce values and don't have side effects themselves. They determine whether and in what order the expressions are executed. Statements sometimes use logical expressions when making such decisions. +) + +$(P $(I $(B Note:) Other programming languages may have different definitions for expression and statement, while some others may not have a distinction at all. +) +) + +$(H5 The $(C if) block and its scope) + +$(P +The $(C if) statement determines whether one or more expressions would be executed. It makes this decision by evaluating a logical expression. It has the same meaning as the English word "if", as in the phrase "if there is coffee then I will drink coffee". +) + +$(P +$(C if) takes a logical expression in parentheses. If the value of that logical expression is $(C true), then it executes the expressions that are within the following curly brackets. Conversely, if the logical expression is $(C false), it does not execute the expressions within the curly brackets. +) + +$(P +The area within the curly brackets is called a $(I scope) and all of the code that is in that scope is called a $(I block of code). +) + +$(P +Here is the syntax of the $(C if) statement: +) + +--- + if (a_logical_expression) { + // ... expression(s) to execute if true + } +--- + +$(P +For example, the program construct that represents "if there is coffee then drink coffee and wash the cup" can be written as in the following program: +) + +--- +import std.stdio; + +void main() { + bool existsCoffee = true; + + if (existsCoffee) { + writeln("Drink coffee"); + writeln("Wash the cup"); + } +} +--- + +$(P +If the value of $(C existsCoffee) is $(C false), then the expressions that are within the block would be skipped and the program would not print anything. +) + +$(H5 $(IX else) The $(C else) block and its scope) + +$(P +Sometimes there are operations to execute for when the logical expression of the $(C if) statement is $(C false). For example, there is always an operation to execute in a decision like "if there is coffee I will drink coffee, else I will drink tea". +) + +$(P +The operations to execute in the $(C false) case are placed in a scope after the $(C else) keyword: +) + +--- + if (a_logical_expression) { + // ... expression(s) to execute if true + + } else { + // ... expression(s) to execute if false + } +--- + +$(P +For example, under the assumption that there is always tea: +) + +--- + if (existsCoffee) { + writeln("Drink coffee"); + + } else { + writeln("Drink tea"); + } +--- + +$(P +In that example, either the first or the second string would be printed depending on the value of $(C existsCoffee). +) + +$(P +$(C else) itself is not a statement but an optional $(I clause) of the $(C if) statement; it cannot be used alone. +) + +$(P +Note the placement of curly brackets of the $(C if) and $(C else) blocks above. Although it is $(LINK2 http://dlang.org/dstyle.html, official D style) to place curly brackets on separate lines, this book uses a common style of inline curly brackets throughout. +) + +$(H5 Always use the scope curly brackets) + +$(P +It is not recommended but is actually possible to omit the curly brackets if there is only one statement within a scope. As both the $(C if) and the $(C else) scopes have just one statement above, that code can also be written as the following: +) + +--- + if (existsCoffee) + writeln("Drink coffee"); + + else + writeln("Drink tea"); +--- + +$(P +Most experienced programmers use curly brackets even for single statements. (One of the exercises of this chapter is about omitting them.) Having said that, I will now show the only case where omitting the curly brackets is actually better. +) + +$(H5 $(IX else if) The "if, else if, else" chain) + +$(P +One of the powers of statements and expressions is the ability to use them in more complex ways. In addition to expressions, scopes can contain other statements. For example, an $(C else) scope can contain an $(C if) statement. Connecting statements and expressions in different ways allows us to make programs behave intelligently according to their purposes. +) + +$(P +The following is a more complex code written under the agreement that riding to a good coffee shop is preferred over walking to a bad one: +) + +--- + if (existsCoffee) { + writeln("Drink coffee at home"); + + } else { + + if (existsBicycle) { + writeln("Ride to the good place"); + + } else { + writeln("Walk to the bad place"); + } + } +--- + +$(P +The code above represents the phrase "if there is coffee, drink at home; else if there is a bicycle, ride to the good place; otherwise walk to the bad place". +) + +$(P +Let's complicate this decision process further: instead of having to walk to the bad place, let's first try the neighbor: +) + +--- + if (existsCoffee) { + writeln("Drink coffee at home"); + + } else { + + if (existsBicycle) { + writeln("Ride to the good place"); + + } else { + + if (neighborIsHome) { + writeln("Have coffee at neighbor's"); + + } else { + writeln("Walk to the bad place"); + } + } + } +--- + +$(P +Such decisions like "if this case, else if that other case, else if that even other case, etc." are common in programs. Unfortunately, when the guideline of always using curly brackets is followed obstinately, the code ends up having too much horizontal and vertical space: ignoring the empty lines, the 3 $(C if) statements and the 4 $(C writeln) expressions above occupy a total of 13 lines. +) + +$(P +In order to write such constructs in a more compact way, when an $(C else) scope contains only one $(C if) statement, then the curly brackets of that $(C else) scope are omitted as an exception of this guideline. +) + +$(P +I am leaving the following code untidy as an intermediate step before showing the better form of it. No code should be written in such an untidy way. +) + +$(P +The following is what the code looks like after removing the curly brackets of the two $(C else) scopes that contain just a single $(C if) statement: +) + +--- + if (existsCoffee) { + writeln("Drink coffee at home"); + + } else + + if (existsBicycle) { + writeln("Ride to the good place"); + + } else + + if (neighborIsHome) { + writeln("Have coffee at neighbor's"); + + } else { + writeln("Walk to the bad place"); + } +--- + +$(P +If we now move those $(C if) statements up to the same lines as their enclosing $(C else) clauses and tidy up the code, we end up with the following more readable format: +) + +--- + if (existsCoffee) { + writeln("Drink coffee at home"); + + } else if (existsBicycle) { + writeln("Ride to the good place"); + + } else if (neighborIsHome) { + writeln("Have coffee at neighbor's"); + + } else { + writeln("Walk to the bad place"); + } +--- + +$(P +Removing the curly brackets allows the code to be more compact and lines up all of the expressions for easier readability. The logical expressions, the order that they are evaluated, and the operations that are executed when they are true are now easier to see at a glance. +) + +$(P +This common programming construct is called the "if, else if, else" chain. +) + + +$(PROBLEM_COK + +$(PROBLEM + +Since the logical expression below is $(C true), we would expect this program to $(I drink lemonade and wash the cup): + +--- +import std.stdio; + +void main() { + bool existsLemonade = true; + + if (existsLemonade) { + writeln("Drinking lemonade"); + writeln("Washing the cup"); + + } else + writeln("Eating pie"); + writeln("Washing the plate"); +} +--- + +But when you run that program you will see that it $(I washes the plate) as well: + +$(SHELL +Drinking lemonade +Washing the cup +Washing the plate +) + +Why? Correct the program to wash the plate only when the logical expression is $(C false). +) + +$(PROBLEM +Write a program that plays a game with the user (obviously with trust). The user throws a die and enters its value. Either the user or the program wins according to the value of the die: + +$(MONO +$(B Value of the die Output of the program) + 1 You won + 2 You won + 3 You won + 4 I won + 5 I won + 6 I won + Any other value ERROR: Invalid value +) + +Bonus: Have the program also mention the value when the value is invalid. For example: + +$(SHELL +ERROR: 7 is invalid +) + +) + +$(PROBLEM +Let's change the game by having the user enter a value from 1 to 1000. Now the user wins when the value is in the range 1-500 and the computer wins when the value is in the range 501-1000. Can the previous program be easily modified to work in this way? +) + +) + +Macros: + SUBTITLE=if Statement + + DESCRIPTION=The if statement, one of the conditional statements of the D programming language + + KEYWORDS=d programming language tutorial book if conditional statement + +MINI_SOZLUK= diff --git a/target/index.d b/target/index.d new file mode 100644 index 0000000..3ec6313 --- /dev/null +++ b/target/index.d @@ -0,0 +1,143 @@ +Ddoc + +$(DERS_BOLUMU Programming in D) + +
+ + + +$(H6 ISBNs) +$(P +978-0-692-59943-3 hardcover by IngramSpark$(BR) +978-0-692-52957-7 paperback by IngramSpark$(BR) +978-1-515-07460-1 paperback by CreateSpace$(BR) +978-1-519-95441-1 ePUB by Draft2Digital$(BR) +) + +$(P +These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. +) + +
+ +$(P +Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). +) + +$(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) +) + +$(H5 Online version) + +$(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) + +$(UL +$(WORK_IN_PROCESS +$(LI $(LINK2 /ders/d.en/foreword1.html, Foreword by Walter Bright)) +) +$(LI $(LINK2 /ders/d.en/foreword2.html, Foreword by Andrei Alexandrescu)) +$(LI $(LINK2 /ders/d.en/preface.html, Preface)) +$(LI $(LINK2 /ders/d.en/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.en/writeln.html, writeln and write)) +$(LI $(LINK2 /ders/d.en/compiler.html, Compilation)) +$(LI $(LINK2 /ders/d.en/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) +$(LI $(LINK2 /ders/d.en/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.en/variables.html, Variables)) +$(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) +$(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.en/if.html, if Statement) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.en/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.en/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.en/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.en/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. $ .dup capacity)) +$(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.en/stream_redirect.html, Redirecting Standard Input and Output Streams)) +$(LI $(LINK2 /ders/d.en/files.html, Files) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.en/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.en/name_space.html, Name Scope)) +$(LI $(LINK2 /ders/d.en/for.html, for Loop) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.en/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.en/literals.html, Literals)) +$(LI $(LINK2 /ders/d.en/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.en/formatted_input.html, Formatted Input)) +$(LI $(LINK2 /ders/d.en/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.en/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.en/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.en/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.en/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.en/functions.html, Functions) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.en/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.en/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.en/lazy_operators.html, Lazy Operators)) +$(LI $(LINK2 /ders/d.en/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.en/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.en/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.en/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.en/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.en/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations)) +$(LI $(LINK2 /ders/d.en/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.en/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.en/struct.html, Structs) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ (and more))) +$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) +$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) +$(LI $(LINK2 /ders/d.en/class.html, Classes) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.en/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.en/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.en/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.en/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.en/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.en/ufcs.html, Universal Function Call Syntax (UFCS))) +$(LI $(LINK2 /ders/d.en/property.html, Properties) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.en/templates.html, Templates)) +$(LI $(LINK2 /ders/d.en/pragma.html, Pragmas)) +$(LI $(LINK2 /ders/d.en/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.en/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.en/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.en/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.en/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.en/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) +$(LI $(LINK2 /ders/d.en/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.en/union.html, Unions) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.en/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.en/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.en/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.en/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.en/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.en/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) +$(LI $(LINK2 /ders/d.en/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.en/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.en/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.en/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.en/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.en/operator_precedence.html, Operator Precedence)) +) + +Macros: + SUBTITLE=Programming in D + + DESCRIPTION=D programming language tutorial from the ground up. + + KEYWORDS=d programming language tutorial book novice beginner + + BREADCRUMBS=$(BREADCRUMBS_INDEX) + +SOZLER= + +MINI_SOZLUK= diff --git a/target/inheritance.cozum.d b/target/inheritance.cozum.d new file mode 100644 index 0000000..1529983 --- /dev/null +++ b/target/inheritance.cozum.d @@ -0,0 +1,203 @@ +Ddoc + +$(COZUM_BOLUMU Inheritance) + +$(OL + +$(LI The member functions that are declared as $(C abstract) by superclasses must be defined by the $(C override) keyword by subclasses. + + +$(P +Ignoring the $(C Train) class for this exercise, $(C Locomotive.makeSound) and $(C RailwayCar.makeSound) can be implemented as in the following program: +) + +--- +import std.stdio; +import std.exception; + +class RailwayVehicle { + void advance(in size_t kilometers) { + writefln("The vehicle is advancing %s kilometers", + kilometers); + + foreach (i; 0 .. kilometers / 100) { + writefln(" %s", makeSound()); + } + } + + abstract string makeSound(); +} + +class Locomotive : RailwayVehicle { + $(HILITE override) string makeSound() { + return "choo choo"; + } +} + +class RailwayCar : RailwayVehicle { + // ... + + $(HILITE override) string makeSound() { + return "clack clack"; + } +} + +class PassengerCar : RailwayCar { + // ... +} + +class FreightCar : RailwayCar { + // ... +} + +void main() { + auto railwayCar1 = new PassengerCar; + railwayCar1.advance(100); + + auto railwayCar2 = new FreightCar; + railwayCar2.advance(200); + + auto locomotive = new Locomotive; + locomotive.advance(300); +} +--- + +) + +$(LI +The following program uses the sounds of the components of $(C Train) to make the sound of $(C Train) itself: + +--- +import std.stdio; +import std.exception; + +class RailwayVehicle { + void advance(in size_t kilometers) { + writefln("The vehicle is advancing %s kilometers", + kilometers); + + foreach (i; 0 .. kilometers / 100) { + writefln(" %s", makeSound()); + } + } + + abstract string makeSound(); +} + +class Locomotive : RailwayVehicle { + override string makeSound() { + return "choo choo"; + } +} + +class RailwayCar : RailwayVehicle { + abstract void load(); + abstract void unload(); + + override string makeSound() { + return "clack clack"; + } +} + +class PassengerCar : RailwayCar { + override void load() { + writeln("The passengers are getting on"); + } + + override void unload() { + writeln("The passengers are getting off"); + } +} + +class FreightCar : RailwayCar { + override void load() { + writeln("The crates are being loaded"); + } + + override void unload() { + writeln("The crates are being unloaded"); + } +} + +class Train : RailwayVehicle { + Locomotive locomotive; + RailwayCar[] cars; + + this(Locomotive locomotive) { + enforce(locomotive !is null, + "Locomotive cannot be null"); + this.locomotive = locomotive; + } + + void addCar(RailwayCar[] cars...) { + this.cars ~= cars; + } + + $(HILITE override) string makeSound() { + string result = locomotive.makeSound(); + + foreach (car; cars) { + result ~= ", " ~ car.makeSound(); + } + + return result; + } + + void departStation(string station) { + foreach (car; cars) { + car.load(); + } + + writefln("Departing from %s station", station); + } + + void arriveStation(string station) { + writefln("Arriving at %s station", station); + + foreach (car; cars) { + car.unload(); + } + } +} + +void main() { + auto locomotive = new Locomotive; + auto train = new Train(locomotive); + + train.addCar(new PassengerCar, new FreightCar); + + train.departStation("Ankara"); + train.advance(500); + train.arriveStation("Haydarpaşa"); +} +--- + +$(P +The output: +) + +$(SHELL +The passengers are getting on +The crates are being loaded +Departing from Ankara station +The vehicle is advancing 500 kilometers + choo choo, clack clack, clack clack + choo choo, clack clack, clack clack + choo choo, clack clack, clack clack + choo choo, clack clack, clack clack + choo choo, clack clack, clack clack +Arriving at Haydarpaşa station +The passengers are getting off +The crates are being unloaded +) + +) + +) + +Macros: + SUBTITLE=Inheritance + + DESCRIPTION=The exercise solutions for the Inheritance chapter, explaining how to specify behaviors of classes in the D programming language. + + KEYWORDS=programming in d tutorial class inheritance diff --git a/target/inheritance.d b/target/inheritance.d new file mode 100644 index 0000000..8c8a1d2 --- /dev/null +++ b/target/inheritance.d @@ -0,0 +1,1049 @@ +Ddoc + +$(DERS_BOLUMU $(IX inheritance) Inheritance) + +$(P +Inheritance is defining a more specialized type based on an existing more general base type. The specialized type acquires the members of the base type and as a result can be substituted in place of the base type. +) + +$(P +$(IX superclass) $(IX subclass) Inheritance is available for classes, not structs. The class that inherits another class is called the $(I subclass), and the class that gets inherited is called the $(I superclass), also called the $(I base class). +) + +$(P +There are two types of inheritance in D. We will cover $(I implementation inheritance) in this chapter and leave $(I interface inheritance) to a later chapter. +) + +$(P +$(IX :, inheritance) When defining a subclass, the superclass is specified after a colon character: +) + +--- +class $(I SubClass) : $(I SuperClass) { + // ... +} +--- + +$(P +To see an example of this, let's assume that there is already the following class that represents a clock: +) + +--- +$(CODE_NAME Clock)$(CODE_COMMENT_OUT)class Clock { + int hour; + int minute; + int second; + + void adjust(int hour, int minute, int second = 0) { + this.hour = hour; + this.minute = minute; + this.second = second; + } +$(CODE_COMMENT_OUT)} +--- + +$(P +Apparently, the members of that class do not need special values during construction; so there is no constructor. Instead, the members are set by the $(C adjust()) member function: +) + +--- + auto deskClock = new Clock; + deskClock.adjust(20, 30); + writefln( + "%02s:%02s:%02s", + deskClock.hour, deskClock.minute, deskClock.second); +--- + +$(P +$(I $(B Note:) It would be more useful to produce the time string by a $(C toString()) function. It will be added later when explaining the $(C override) keyword below.) +) + +$(P +The output: +) + +$(SHELL +20:30:00 +) + +$(P +With only that much functionality, $(C Clock) could be a struct as well, and depending on the needs of the program, that could be sufficient. +) + +$(P +However, being a class makes it possible to inherit from $(C Clock). +) + +$(P +To see an example of inheritance, let's consider an $(C AlarmClock) that not only includes all of the functionality of $(C Clock), but also provides a way of setting the alarm. Let's first define this type without regard to $(C Clock). If we did that, we would have to include the same three members of $(C Clock) and the same $(C adjust()) function that adjusted them. $(C AlarmClock) would also have other members for its additional functionality: +) + +--- +class AlarmClock { + $(HILITE int hour;) + $(HILITE int minute;) + $(HILITE int second;) + int alarmHour; + int alarmMinute; + + $(HILITE void adjust(int hour, int minute, int second = 0) {) + $(HILITE this.hour = hour;) + $(HILITE this.minute = minute;) + $(HILITE this.second = second;) + $(HILITE }) + + void adjustAlarm(int hour, int minute) { + alarmHour = hour; + alarmMinute = minute; + } +} +--- + +$(P +The members that appear exactly in $(C Clock) are highlighted. As can be seen, defining $(C Clock) and $(C AlarmClock) separately results in code duplication. +) + +$(P +Inheritance is helpful in such cases. Inheriting $(C AlarmClock) from $(C Clock) simplifies the new class and reduces code duplication: +) + +--- +$(CODE_NAME AlarmClock)$(CODE_COMMENT_OUT)class AlarmClock $(HILITE : Clock) { + int alarmHour; + int alarmMinute; + + void adjustAlarm(int hour, int minute) { + alarmHour = hour; + alarmMinute = minute; + } +$(CODE_COMMENT_OUT)} +--- + +$(P +The new definition of $(C AlarmClock) is the equivalent of the previous one. The highlighted part of the new definition corresponds to the highlighted parts of the old definition. +) + +$(P +Because $(C AlarmClock) inherits the members of $(C Clock), it can be used just like a $(C Clock): +) + +--- + auto bedSideClock = new AlarmClock; + bedSideClock.$(HILITE adjust(20, 30)); + bedSideClock.adjustAlarm(7, 0); +--- + +$(P +The members that are inherited from the superclass can be accessed as if they were the members of the subclass: +) + +--- + writefln("%02s:%02s:%02s ♫%02s:%02s", + bedSideClock$(HILITE .hour), + bedSideClock$(HILITE .minute), + bedSideClock$(HILITE .second), + bedSideClock.alarmHour, + bedSideClock.alarmMinute); +--- + +$(P +The output: +) + +$(SHELL +20:30:00 ♫07:00 +) + +$(P $(I $(B Note:) An $(C AlarmClock.toString) function would be more useful in this case. It will be defined later below.) +) + +$(P +The inheritance used in this example is $(I implementation inheritance.) +) + +$(P +If we imagine the memory as a ribbon going from top to bottom, the placement of the members of $(C AlarmClock) in memory can be pictured as in the following illustration: +) + +$(MONO + │ . │ + │ . │ +the address of the object → ├─────────────┤ + │$(GRAY $(I (other data))) │ + │$(HILITE  hour )│ + │$(HILITE  minute )│ + │$(HILITE  second )│ + │ alarmHour │ + │ alarmMinute │ + ├─────────────┤ + │ . │ + │ . │ +) + +$(P +$(IX vtbl) The illustration above is just to give an idea on how the members of the superclass and the subclass may be combined together. The actual layout of the members depends on the implementation details of the compiler in use. For example, the part that is marked as $(I other data) typically includes the pointer to the $(I virtual function table) (vtbl) of that particular class type. The details of the object layout are outside the scope of this book. +) + +$(H5 $(IX is-a) Warning: Inherit only if "is a") + +$(P +We have seen that implementation inheritance is about acquiring members. Consider this kind of inheritance only if the subtype can be thought of being a kind of the supertype as in the phrase "alarm clock $(I is a) clock." +) + +$(P +$(IX has-a) "Is a" is not the only relationship between types; a more common relationship is the "has a" relationship. For example, let's assume that we want to add the concept of a $(C Battery) to the $(C Clock) class. It would not be appropriate to add $(C Battery) to $(C Clock) by inheritance because the statement "clock is a battery" is not true: +) + +--- +class Clock : Battery { $(CODE_NOTE_WRONG WRONG DESIGN) + // ... +} +--- + +$(P +A clock is not a battery; it $(I has a) battery. When there is such a relationship of containment, the type that is contained must be defined as a member of the type that contains it: +) + +--- +class Clock { + Battery battery; $(CODE_NOTE Correct design) + // ... +} +--- + +$(H5 $(IX single inheritance) $(IX inheritance, single) $(IX hierarchy) Inheritance from at most one class) + +$(P +Classes can only inherit from a single base class (which itself can potentially inherit from another single class). In other words, multiple inheritance is not supported in D. +) + +$(P +For example, assuming that there is also a $(C SoundEmitter) class, and even though "alarm clock is a sound emitting object" is also true, it is not possible to inherit $(C AlarmClock) both from $(C Clock) and $(C SoundEmitter): +) + +--- +class SoundEmitter { + // ... +} + +class AlarmClock : Clock$(HILITE , SoundEmitter) { $(DERLEME_HATASI) + // ... +} +--- + +$(P +On the other hand, there is no limit to the number of $(I interfaces) that a class can inherit from. We will see the $(C interface) keyword in a later chapter. +) + +$(P +Additionally, there is no limit to how deep the inheritance hierarchy can go: +) + +--- +class MusicalInstrument { + // ... +} + +class StringInstrument : MusicalInstrument { + // ... +} + +class Violin : StringInstrument { + // ... +} +--- + +$(P +The inheritance hierarchy above defines a relationship from the more general to the more specific: musical instrument, string instrument, and violin. +) + +$(H5 Hierarchy charts) + +$(P +Types that are related by the "is a" relationship form a $(I class hierarchy). +) + +$(P +According to OOP conventions, class hierarchies are represented by superclasses being on the top and the subclasses being at the bottom. The inheritance relationships are indicated by arrows pointing from the subclasses to the superclasses. +) + +$(P +For example, the following can be a hierarchy of musical instruments: +) + +$(MONO + MusicalInstrument + ↗ ↖ + StringInstrument WindInstrument + ↗ ↖ ↗ ↖ + Violin Guitar Flute Recorder +) + +$(H5 $(IX super, member access) Accessing superclass members) + +$(P +The $(C super) keyword allows referring to members that are inherited from the superclass. +) + +--- +class AlarmClock : Clock { + // ... + + void foo() { + super.minute = 10; // The inherited 'minute' member + minute = 10; // Same thing if there is no ambiguity + } +} +--- + +$(P +The $(C super) keyword is not always necessary; $(C minute) alone has the same meaning in the code above. The $(C super) keyword is needed when both the superclass and the subclass have members under the same names. We will see this below when we will need to write $(C super.reset()) and $(C super.toString()). +) + +$(P +If multiple classes in an inheritance tree define a symbol with the same name, one can use the specific name of the class in the inheritance tree to disambiguate between the symbols: +) + +--- +class Device { + string $(HILITE manufacturer); +} + +class Clock : Device { + string $(HILITE manufacturer); +} + +class AlarmClock : Clock { + // ... + + void foo() { + $(HILITE Device.)manufacturer = "Sunny Horology, Inc."; + $(HILITE Clock.)manufacturer = "Better Watches, Ltd."; + } +} +--- + +$(H5 $(IX super, construction) Constructing superclass members) + +$(P +The other use of the $(C super) keyword is to call the constructor of the superclass. This is similar to calling the overloaded constructors of the current class: $(C this) when calling constructors of the current class and $(C super) when calling constructors of the superclass. +) + +$(P +It is not required to call the superclass constructor explicitly. If the constructor of the subclass makes an explicit call to any overload of $(C super), then that constructor is executed by that call. Otherwise, and if the superclass has a default constructor, it is executed automatically before entering the body of the subclass. +) + +$(P +We have not defined constructors for the $(C Clock) and $(C AlarmClock) classes yet. For that reason, the members of both of those classes are initialized by the $(C .init) values of their respective types, which is 0 for $(C int). +) + +$(P +Let's assume that $(C Clock) has the following constructor: +) + +--- +$(CODE_NAME Clock_ctor)$(CODE_COMMENT_OUT)class Clock { + this(int hour, int minute, int second) { + this.hour = hour; + this.minute = minute; + this.second = second; + } + + // ... +$(CODE_COMMENT_OUT)} +--- + +$(P +That constructor must be used when constructing $(C Clock) objects: +) + +--- + auto clock = new Clock(17, 15, 0); +--- + +$(P +Naturally, the programmers who use the $(C Clock) type directly would have to use that syntax. However, when constructing an $(C AlarmClock) object, they cannot construct its $(C Clock) part separately. Besides, the users of $(C AlarmClock) need not even know that it inherits from $(C Clock). +) + +$(P +A user of $(C AlarmClock) should simply construct an $(C AlarmClock) object and use it in the program without needing to pay attention to its $(C Clock) heritage: +) + +--- + auto bedSideClock = new AlarmClock(/* ... */); + // ... use as an AlarmClock ... +--- + +$(P +For that reason, constructing the superclass part is the responsibility of the subclass. The subclass calls the constructor of the superclass with the $(C super()) syntax: +) + +--- +$(CODE_NAME AlarmClock_ctor)$(CODE_COMMENT_OUT)class AlarmClock : Clock { + this(int hour, int minute, int second, // for Clock's members + int alarmHour, int alarmMinute) { // for AlarmClock's members + $(HILITE super)(hour, minute, second); + this.alarmHour = alarmHour; + this.alarmMinute = alarmMinute; + } + + // ... +$(CODE_COMMENT_OUT)} +--- + +$(P +The constructor of $(C AlarmClock) takes arguments for both its own members and the members of its superclass. It then uses part of those arguments to construct its superclass part. +) + +$(H5 $(IX override) Overriding the definitions of member functions) + +$(P +One of the benefits of inheritance is being able to redefine the member functions of the superclass in the subclass. This is called $(I overriding): The existing definition of the member function of the superclass is overridden by the subclass with the $(C override) keyword. +) + +$(P +$(IX virtual function) Overridable functions are called $(I virtual functions). Virtual functions are implemented by the compiler through $(I virtual function pointer tables) (vtbl) and $(I vtbl pointers). The details of this mechanism are outside the scope of this book. However, it must be known by every system programmer that virtual function calls are more expensive than regular function calls. Every non-private $(C class) member function in D is virtual by default. For that reason, when a superclass function does not need to be overridden at all, it should be defined as $(C final) so that it is not virtual. We will see the $(C final) keyword later in $(LINK2 /ders/d.en/interface.html, the Interfaces chapter). +) + +$(P +Let's assume that $(C Clock) has a member function that is used for resetting its members all to zero: +) + +--- +class Clock { + void reset() { + hour = 0; + minute = 0; + second = 0; + } + + // ... +} +--- + +$(P +That function is inherited by $(C AlarmClock) and can be called on an $(C AlarmClock) object: +) + +--- + auto bedSideClock = new AlarmClock(20, 30, 0, 7, 0); + // ... + bedSideClock.reset(); +--- + +$(P +However, necessarily ignorant of the members of $(C AlarmClock), $(C Clock.reset) can only reset its own members. For that reason, to reset the members of the subclass as well, $(C reset()) must be overridden: +) + +--- +class AlarmClock : Clock { + $(HILITE override) void reset() { + super.reset(); + alarmHour = 0; + alarmMinute = 0; + } + + // ... +} +--- + +$(P +The subclass resets only its own members and dispatches the rest of the task to $(C Clock) by the $(C super.reset()) call. Note that writing just $(C reset()) would not work as it would call the $(C reset()) function of $(C AlarmClock) itself. Calling $(C reset()) from within itself would cause an infinite recursion. +) + +$(P +The reason that I have delayed the definition of $(C toString()) until this point is that it must be defined by the $(C override) keyword for classes. As we will see in the next chapter, every class is automatically inherited from a superclass called $(C Object) and $(C Object) already defines a $(C toString()) member function. +) + +$(P +For that reason, the $(C toString()) member function for classes must be defined by using the $(C override) keyword: +) + +--- +$(CODE_NAME Clock_AlarmClock)import std.string; + +class Clock { + $(HILITE override) string toString() const { + return format("%02s:%02s:%02s", hour, minute, second); + } + + // ... +$(CODE_XREF Clock)$(CODE_XREF Clock_ctor)} + +class AlarmClock : Clock { + $(HILITE override) string toString() const { + return format("%s ♫%02s:%02s", super.toString(), + alarmHour, alarmMinute); + } + + // ... +$(CODE_XREF AlarmClock)$(CODE_XREF AlarmClock_ctor)} +--- + +$(P +Note that $(C AlarmClock) is again dispatching some of the task to $(C Clock) by the $(C super.toString()) call. +) + +$(P +Those two overrides of $(C toString()) allow converting $(C AlarmClock) objects to strings: +) + +--- +$(CODE_XREF Clock_AlarmClock)void main() { + auto deskClock = new AlarmClock(10, 15, 0, 6, 45); + writeln($(HILITE deskClock)); +} +--- + +$(P +The output: +) + +$(SHELL +10:15:00 ♫06:45 +) + +$(H5 $(IX polymorphism, run-time) $(IX run-time polymorphism) Using the subclass in place of the superclass) + +$(P +Since the superclass is more $(I general) and the subclass is more $(I specialized), objects of a subclass can be used in places where an object of the superclass type is required. This is called $(I polymorphism). +) + +$(P +The concepts of general and specialized types can be seen in statements like "this type is of that type": "alarm clock is a clock", "student is a person", "cat is an animal", etc. Accordingly, an alarm clock can be used where a clock is needed, a student can be used where a person is needed, and a cat can be used where an animal is needed. +) + +$(P +When a subclass object is being used as a superclass object, it does not lose its own specialized type. This is similar to the examples in real life: Using an alarm clock simply as a clock does not change the fact that it is an alarm clock; it would still behave like an alarm clock. +) + +$(P +Let's assume that a function takes a $(C Clock) object as parameter, which it resets at some point during its execution: +) + +--- +void use(Clock clock) { + // ... + clock.reset(); + // ... +} +--- + +$(P +Polymorphism makes it possible to send an $(C AlarmClock) to such a function: +) + +--- + auto deskClock = new AlarmClock(10, 15, 0, 6, 45); + writeln("Before: ", deskClock); + $(HILITE use(deskClock)); + writeln("After : ", deskClock); +--- + +$(P +This is in accordance with the relationship "alarm clock is a clock." As a result, the members of the $(C deskClock) object get reset: +) + +$(SHELL +Before: 10:15:00 ♫06:45 +After : 00:00:00 ♫$(HILITE 00:00) +) + +$(P +The important observation here is that not only the members of $(C Clock) but also the members of $(C AlarmClock) have been reset. +) + +$(P +Although $(C use()) calls $(C reset()) on a $(C Clock) object, since the actual object is an $(C AlarmClock), the function that gets called is $(C AlarmClock.reset). According to its definition above, $(C AlarmClock.reset) resets the members of both $(C Clock) and $(C AlarmClock). +) + +$(P +In other words, although $(C use()) uses the object as a $(C Clock), the actual object may be an inherited type that behaves in its own special way. +) + +$(P +Let's add another class to the $(C Clock) hierarchy. The $(C reset()) function of this type sets its members to random values: +) + +--- +import std.random; + +class BrokenClock : Clock { + this() { + super(0, 0, 0); + } + + override void reset() { + hour = uniform(0, 24); + minute = uniform(0, 60); + second = uniform(0, 60); + } +} +--- + +$(P +When an object of $(C BrokenClock) is sent to $(C use()), then the special $(C reset()) function of $(C BrokenClock) would be called. Again, although it is passed as a $(C Clock), the actual object is still a $(C BrokenClock): +) + +--- + auto shelfClock = new BrokenClock; + use(shelfClock); + writeln(shelfClock); +--- + +$(P +The output shows random time values as a result of resetting a $(C BrokenClock): +) + +$(SHELL +22:46:37 +) + +$(H5 Inheritance is transitive) + +$(P +Polymorphism is not just limited to two classes. Subclasses of subclasses can also be used in place of any superclass in the hierarchy. +) + +$(P +Let's consider the $(C MusicalInstrument) hierarchy: +) + +--- +class MusicalInstrument { + // ... +} + +class StringInstrument : MusicalInstrument { + // ... +} + +class Violin : StringInstrument { + // ... +} +--- + +$(P +The inheritances above builds the following relationships: "string instrument is a musical instrument" and "violin is a string instrument." Therefore, it is also true that "violin is a musical instrument." Consequently, a $(C Violin) object can be used in place of a $(C MusicalInstrument). +) + +$(P +Assuming that all of the supporting code below has also been defined: +) + +--- +void playInTune(MusicalInstrument instrument, + MusicalPiece piece) { + instrument.tune(); + instrument.play(piece); +} + +// ... + +auto myViolin = new Violin; +playInTune(myViolin, improvisation); +--- + +$(P +Although $(C playInTune()) expects a $(C MusicalInstrument), it is being called with a $(C Violin) due to the relationship "violin is a musical instrument." +) + +$(P +Inheritance can be as deep as needed. +) + +$(H5 $(IX abstract) Abstract member functions and abstract classes) + +$(P +Sometimes there are member functions that are natural to appear in a class interface even though that class cannot provide its definition. When there is no $(I concrete) definition of a member function, that function is an $(I abstract) member function. A class that has at least one abstract member function is an abstract class. +) + +$(P +For example, the $(C ChessPiece) superclass in a hierarchy may have an $(C isValid()) member function that determines whether a given move is valid for that chess piece. Since validity of a move depends on the actual type of the chess piece, the $(C ChessPiece) general class cannot make this decision itself. The valid moves can only be known by the subclasses like $(C Pawn), $(C King), etc. +) + +$(P +The $(C abstract) keyword specifies that the inherited class must implement such a method itself: +) + +--- +class ChessPiece { + $(HILITE abstract) bool isValid(in Square from, in Square to); +} +--- + +$(P +It is not possible to construct objects of abstract classes: +) + +--- + auto piece = new ChessPiece; $(DERLEME_HATASI) +--- + +$(P +The subclass would have to override and implement all the abstract functions in order to make the class non-abstract and therefore constructible: +) + +--- +class Pawn : ChessPiece { + override bool isValid(in Square from, in Square to) { + // ... the implementation of isValid for pawn ... + return decision; + } +} +--- + +$(P +It is now possible to construct objects of $(C Pawn): +) + +--- + auto piece = new Pawn; // compiles +--- + +$(P +Note that an abstract function may have an implementation of its own, but it would still require the subclass to provide its own implementation of such a function. For example, the $(C ChessPiece)'es implementation may provide some useful checks of its own: +) + +--- +class ChessPiece { + $(HILITE abstract) bool isValid(in Square from, in Square to) { + // We require the 'to' position to be different than + // the 'from' position + return from != to; + } +} + +class Pawn : ChessPiece { + override bool isValid(in Square from, in Square to) { + // First verify if it is a valid move for any ChessPiece + if (!$(HILITE super.isValid)(from, to)) { + return false; + } + + // ... then check if it is valid for the Pawn ... + + return decision; + } +} +--- + +$(P +The $(C ChessPiece) class is still abstract even though $(C isValid()) was already implemented, but the $(C Pawn) class is non-abstract and can be instantiated. +) + +$(H5 Example) + +$(P +Let's consider a class hierarchy that represents railway vehicles: +) + +$(MONO + RailwayVehicle + / | \ + Locomotive Train RailwayCar { load()?, unload()? } + / \ + PassengerCar FreightCar +) + +$(P +The functions that $(C RailwayCar) will declare as $(C abstract) are indicated by question marks. +) + +$(P +Since my goal is only to present a class hierarchy and point out some of its design decisions, I will not fully implement these classes. Instead of doing actual work, they will simply print messages. +) + +$(P +The most general class of the hierarchy above is $(C RailwayVehicle). In this program, it will only know how to move itself: +) + +--- +$(CODE_NAME RailwayVehicle)class RailwayVehicle { + void advance(in size_t kilometers) { + writefln("The vehicle is advancing %s kilometers", + kilometers); + } +} +--- + +$(P +A class that inherits from $(C RailwayVehicle) is $(C Locomotive), which does not have any special members yet: +) + +--- +$(CODE_NAME Locomotive)$(CODE_XREF RailwayVehicle)class Locomotive : RailwayVehicle { +} +--- + +$(P +We will add a special $(C makeSound()) member function to $(C Locomotive) later during one of the exercises. +) + +$(P +$(C RailwayCar) is a $(C RailwayVehicle) as well. However, if the hierarchy supports different types of railway cars, then certain behaviors like loading and unloading must be done according to their exact types. For that reason, $(C RailwayCar) can only declare these two functions as abstract: +) + +--- +$(CODE_NAME RailwayCar)class RailwayCar : RailwayVehicle { + $(HILITE abstract) void load(); + $(HILITE abstract) void unload(); +} +--- + +$(P +Loading and unloading a passenger car is as simple as opening the doors of the car, while loading and unloading a freight car may involve porters and winches. The following subclasses provide definitions for the abstract functions of $(C RailwayCar): +) + +--- +$(CODE_NAME PassengerCar_FreightCar)class PassengerCar : RailwayCar { + override void load() { + writeln("The passengers are getting on"); + } + + override void unload() { + writeln("The passengers are getting off"); + } +} + +class FreightCar : RailwayCar { + override void load() { + writeln("The crates are being loaded"); + } + + override void unload() { + writeln("The crates are being unloaded"); + } +} +--- + +$(P +Being an abstract class does not preclude the use of $(C RailwayCar) in the program. Objects of $(C RailwayCar) can not be constructed but $(C RailwayCar) can be used as an interface. As the subclasses define the two relationships "passenger car is a railway car" and "freight car is a railway car", the objects of $(C PassengerCar) and $(C FreightCar) can be used in places of $(C RailwayCar). This will be seen in the $(C Train) class below. +) + +$(P +The class that represents a train can consist of a locomotive and an array of railwaycars: +) + +--- +$(CODE_NAME Train_members)$(CODE_COMMENT_OUT)class Train : RailwayVehicle { + Locomotive locomotive; + RailwayCar[] cars; + + // ... +$(CODE_COMMENT_OUT)} +--- + +$(P +I would like to repeat an important point: Although both $(C Locomotive) and $(C RailwayCar) inherit from $(C RailwayVehicle), it would not be correct to inherit $(C Train) from either of them. Inheritance models the "is a" relationship and a train is neither a locomotive nor a passenger car. A train consists of them. +) + +$(P +If we require that every train must have a locomotive, the $(C Train) constructor must ensure that it takes a valid $(C Locomotive) object. Similarly, if the railway cars are optional, they can be added by a member function: +) + +--- +$(CODE_NAME Train_ctor)import std.exception; +// ... + +$(CODE_COMMENT_OUT)class Train : RailwayVehicle { + // ... + + this(Locomotive locomotive) { + enforce(locomotive !is null, + "Locomotive cannot be null"); + this.locomotive = locomotive; + } + + void addCar(RailwayCar[] cars...) { + this.cars ~= cars; + } + + // ... +$(CODE_COMMENT_OUT)} +--- + +$(P +Note that $(C addCar()) can validate the $(C RailwayCar) objects as well. I am ignoring that validation here. +) + +$(P +We can imagine that the departures and arrivals of trains should also be supported: +) + +--- +$(CODE_NAME Train)$(CODE_XREF RailwayCar)class Train : RailwayVehicle { + // ... + + void departStation(string station) { + foreach (car; cars) { + car.load(); + } + + writefln("Departing from %s station", station); + } + + void arriveStation(string station) { + writefln("Arriving at %s station", station); + + foreach (car; cars) { + car.unload(); + } + } +$(CODE_XREF Train_members)$(CODE_XREF Train_ctor)} +--- + +$(P +The following $(C main()) is making use of the $(C RailwayVehicle) hierarchy: +) + +--- +$(CODE_XREF Locomotive)$(CODE_XREF Train)$(CODE_XREF PassengerCar_FreightCar)import std.stdio; + +// ... + +void main() { + auto locomotive = new Locomotive; + auto train = new Train(locomotive); + + train.addCar(new PassengerCar, new FreightCar); + + train.departStation("Ankara"); + train.advance(500); + train.arriveStation("Haydarpaşa"); +} +--- + +$(P +The $(C Train) class is being used by functions that are provided by two separate interfaces: +) + +$(OL + +$(LI +When the $(C advance()) function is called, the $(C Train) object is being used as a $(C RailwayVehicle) because that function is declared by $(C RailwayVehicle). +) + +$(LI When the $(C departStation()) and $(C arriveStation()) functions are called, $(C train) is being used as a $(C Train) because those functions are declared by $(C Train).) + +) + +$(P +The arrows indicate that $(C load()) and $(C unload()) functions work according to the actual type of $(C RailwayCar): +) + +$(SHELL +The passengers are getting on $(SHELL_NOTE) +The crates are being loaded $(SHELL_NOTE) +Departing from Ankara station +The vehicle is advancing 500 kilometers +Arriving at Haydarpaşa station +The passengers are getting off $(SHELL_NOTE) +The crates are being unloaded $(SHELL_NOTE) +) + +$(H5 Summary) + +$(UL +$(LI Inheritance is used for the "is a" relationship.) +$(LI Every class can inherit from up to one $(C class).) +$(LI $(C super) has two uses: Calling the constructor of the superclass and accessing the members of the superclass.) +$(LI $(C override) is for redefining member functions of the superclass specially for the subclass.) +$(LI $(C abstract) requires that a member function must be overridden.) +) + +$(PROBLEM_COK + +$(PROBLEM +Let's modify $(C RailwayVehicle). In addition to reporting the distance that it advances, let's have it also make sounds. To keep the output short, let's print the sounds per 100 kilometers: + +--- +class RailwayVehicle { + void advance(in size_t kilometers) { + writefln("The vehicle is advancing %s kilometers", + kilometers); + + foreach (i; 0 .. kilometers / 100) { + writefln(" %s", makeSound()); + } + } + + // ... +} +--- + +$(P +However, $(C makeSound()) cannot be defined by $(C RailwayVehicle) because vehicles may have different sounds: +) + +$(UL +$(LI "choo choo" for $(C Locomotive)) +$(LI "clack clack" for $(C RailwayCar)) +) + +$(P $(I $(B Note:) Leave $(C Train.makeSound) to the next exercise.) +) + +$(P +Because it must be overridden, $(C makeSound()) must be declared as $(C abstract) by the superclass: +) + +--- +class RailwayVehicle { + // ... + + abstract string makeSound(); +} +--- + +$(P +Implement $(C makeSound()) for the subclasses and try the code with the following $(C main()): +) + +--- +$(CODE_XREF Locomotive)$(CODE_XREF Train)$(CODE_XREF PassengerCar_FreightCar)void main() { + auto railwayCar1 = new PassengerCar; + railwayCar1.advance(100); + + auto railwayCar2 = new FreightCar; + railwayCar2.advance(200); + + auto locomotive = new Locomotive; + locomotive.advance(300); +} +--- + +$(P +Make the program produce the following output: +) + +$(SHELL +The vehicle is advancing 100 kilometers + clack clack +The vehicle is advancing 200 kilometers + clack clack + clack clack +The vehicle is advancing 300 kilometers + choo choo + choo choo + choo choo +) + +$(P +Note that there is no requirement that the sounds of $(C PassengerCar) and $(C FreightCar) be different. They can share the same implemention from $(C RailwayCar). +) + +) + +$(PROBLEM +Think about how $(C makeSound()) can be implemented for $(C Train). One idea is that $(C Train.makeSound) may return a $(C string) that consists of the sounds of the members of $(C Train). +) + +) + +Macros: + SUBTITLE=Inheritance + + DESCRIPTION=Inheriting classes from classes in the D programming language. + + KEYWORDS=d programming lesson book tutorial class inheeritance diff --git a/target/input.cozum.d b/target/input.cozum.d new file mode 100644 index 0000000..609a9f3 --- /dev/null +++ b/target/input.cozum.d @@ -0,0 +1,14 @@ +Ddoc + +$(COZUM_BOLUMU Reading from the Standard Input) + +$(P +When the characters cannot be converted to the desired type, $(C stdin) gets in an unusable state. For example, entering "abc" when an $(C int) is expected would make $(C stdin) unusable. +) + +Macros: + SUBTITLE=Reading from the Standard Input Solutions + + DESCRIPTION=Programming in D exercise solutions: reading from the standard input + + KEYWORDS=programming in d tutorial reading stdin solution diff --git a/target/input.d b/target/input.d new file mode 100644 index 0000000..8126928 --- /dev/null +++ b/target/input.d @@ -0,0 +1,249 @@ +Ddoc + +$(DERS_BOLUMU $(IX input) Reading from the Standard Input) + +$(P +Any data that is read by the program must first be stored in a variable. For example, a program that reads the number of students from the input must store this information in a variable. The type of this specific variable can be $(C int). +) + +$(P +As we've seen in the previous chapter, we don't need to type $(C stdout) when printing to the output, because it is implied. Further, what is to be printed is specified as the argument. So, $(C write(studentCount)) is sufficient to print the value of $(C studentCount). To summarize: +) + +$(MONO +stream: stdout +operation: write +data: the value of the studentCount variable +target: commonly the terminal window +) + +$(P +$(IX readf) The reverse of $(C write) is $(C readf); it reads from the standard input. The "f" in its name comes from "formatted" as what it reads must always be presented in a certain format. +) + +$(P +We've also seen in the previous chapter that the standard input stream is $(C stdin). +) + +$(P +In the case of reading, one piece of the puzzle is still missing: where to store the data. To summarize: +) + +$(MONO +stream: stdin +operation: readf +data: some information +target: ? +) + +$(P +The location of where to store the data is specified by the address of a variable. The address of a variable is the exact location in the computer's memory where its value is stored. +) + +$(P +$(IX &, address of) In D, the $(C &) character that is typed before a name is the address of what that name represents. For example, the address of $(C studentCount) is $(C &studentCount). Here, $(C &studentCount) can be read as "the address of $(C studentCount)" and is the missing piece to replace the question mark above: +) + +$(MONO +stream: stdin +operation: readf +data: some information +target: the location of the studentCount variable +) + +$(P +Typing a $(C &) in front of a name means $(I pointing) at what that name represents. This concept is the foundation of references and pointers that we will see in later chapters. +) + +$(P +I will leave one peculiarity about the use of $(C readf) for later; for now, let's accept as a rule that the first argument to $(C readf) must be $(STRING "%s"): +) + +--- + readf("%s", &studentCount); +--- + +$(P +$(I $(B Note:) As I explain below, in most cases there must also be a space: $(STRING " %s").) +) + +$(P +Actually, $(C readf) can work without the $(C &) character as well: +) + +--- + readf("%s", studentCount); // same as above +--- + +$(P +Although the code is cleaner and safer without the $(C &) character, I will continue to use $(C readf) with pointers partly to prepare you to the concepts of $(LINK2 /ders/d.en/value_vs_reference.html, references) and $(LINK2 /ders/d.en/function_parameters.html, reference function parameters). +) + +$(P +$(STRING "%s") indicates that the data should automatically be converted in a way that is suitable to the type of the variable. For example, when the '4' and '2' characters are read to a variable of type $(C int), they are converted to the integer value 42. +) + +$(P +The program below asks the user to enter the number of students. You must press the Enter key after typing the input: +) + +--- +import std.stdio; + +void main() { + write("How many students are there? "); + + /* The definition of the variable that will be used to + * store the information that is read from the input. */ + int studentCount; + + // Storing the input data to that variable + readf("%s", &studentCount); + + writeln("Got it: There are ", studentCount, " students."); +} +--- + +$(H5 $(IX %s, with whitespace) $(IX whitespace) Skipping the whitespace characters) + +$(P +Even the Enter key that we press after typing the data is stored as a special code and is placed into the $(C stdin) stream. This is useful to the programs to detect whether the information has been input on a single line or multiple lines. +) + +$(P +Although sometimes useful, such special codes are mostly not important for the program and must be filtered out from the input. Otherwise they $(I block) the input and prevent reading other data. +) + +$(P +To see this $(I problem) in a program, let's also read the number of teachers from the input: +) + +--- +import std.stdio; + +void main() { + write("How many students are there? "); + int studentCount; + readf("%s", &studentCount); + + write("How many teachers are there? "); + int teacherCount; + readf("%s", &teacherCount); + + writeln("Got it: There are ", studentCount, " students", + " and ", teacherCount, " teachers."); +} +--- + +$(P +Unfortunately, the program cannot use that special code when expecting an $(C int) value: +) + +$(SHELL +How many students are there? 100 +How many teachers are there? 20 + $(SHELL_NOTE_WRONG An exception is thrown here) +) + +$(P +Although the user enters the number of teachers as 20, the special code(s) that represents the Enter key that has been pressed when entering the previous 100 is still in the input stream and is blocking it. The characters that appeared in the input stream are similar to the following representation: +) + +$(MONO +100$(HILITE [EnterCode])20[EnterCode] +) + +$(P +I have highlighted the Enter code that is blocking the input. +) + +$(P +The solution is to use a space character before $(STRING %s) to indicate that the Enter code that appears before reading the number of teachers is not important: $(STRING " %s"). Spaces that are in the format strings are used to read and ignore zero or more invisible characters that would otherwise appear in the input. Such characters include the actual space character, the code(s) that represent the Enter key, the Tab character, etc. and are called the $(I whitespace characters). +) + +$(P +As a general rule, you can use $(STRING " %s") for every data that is read from the input. The program above works as expected with the following changes: +) + +--- +// ... + readf(" %s", &studentCount); +// ... + readf(" %s", &teacherCount); +// ... +--- + +$(P +The output: +) + +$(SHELL +How many students are there? 100 +How many teachers are there? 20 +Got it: There are 100 students and 20 teachers. +) + +$(H5 Additional information) + +$(UL + +$(LI +$(IX comment) $(IX /*) $(IX */) Lines that start with $(COMMENT //) are useful for single lines of comments. To write multiple lines as a single comment, enclose the lines within $(COMMENT /*) and $(COMMENT */) markers. + + +$(P +$(IX /+) $(IX +/) In order to be able to comment even other comments, use $(COMMENT /+) and $(COMMENT +/): + +) + +--- + /+ + // A single line of comment + + /* + A comment that spans + multiple lines + */ + + /+ + It can even include nested /+ comments +/ + +/ + + A comment block that includes other comments + +/ +--- + +) + +$(LI +Most of the whitespace in the source code is insignificant. It is good practice to write longer expressions as multiple lines or add extra whitespace to make the code more readable. Still, as long as the syntax rules of the language are observed, the programs can be written without any extra whitespace: + +--- +import std.stdio;void main(){writeln("Hard to read!");} +--- + +$(P +It can be hard to read source code with small amounts of whitespace. +) + +) + +) + +$(PROBLEM_TEK + +$(P +Enter non-numerical characters when the program is expecting integer values and observe that the program does not work correctly. +) + +) + + +Macros: + SUBTITLE=Reading from the Standard Input + + DESCRIPTION=Getting information from the input in D + + KEYWORDS=d programming language tutorial book read stdin + +MINI_SOZLUK= diff --git a/target/interface.d b/target/interface.d new file mode 100644 index 0000000..5874380 --- /dev/null +++ b/target/interface.d @@ -0,0 +1,657 @@ +Ddoc + +$(DERS_BOLUMU $(IX interface) Interfaces) + +$(P +The $(C interface) keyword is for defining interfaces in class hierarchies. $(C interface) is very similar to $(C class) with the following restrictions: +) + +$(UL + +$(LI +The member functions that it declares (but not implements) are abstract even without the $(C abstract) keyword. +) + +$(LI +The member functions that it implements must be $(C static) or $(C final). ($(C static) and $(C final) member functions are explained below.) +) + +$(LI +Its member variables must be $(C static). +) + +$(LI +Interfaces can inherit only interfaces. +) + +) + +$(P +Despite these restrictions, there is no limit on the number of $(C interface)s that a class can inherit from. (In contrast, a class can inherit from up to one $(C class).) +) + +$(H5 Definition) + +$(P +Interfaces are defined by the $(C interface) keyword, the same way as classes: +) + +--- +$(HILITE interface) SoundEmitter { + // ... +} +--- + +$(P +An $(C interface) is for declaring member functions that are implicitly abstract: +) + +--- +interface SoundEmitter { + string emitSound(); // Declared (not implemented) +} +--- + +$(P +Classes that inherit from that interface would have to provide the implementations of the abstract functions of the interface. +) + +$(P +Interface function declarations can have $(C in) and $(C out) contract blocks: +) + +--- +interface I { + int func(int i) + $(HILITE in) { + /* Strictest requirements that the callers of this + * function must meet. (Derived interfaces and classes + * can loosen these requirements.) */ + + } $(HILITE out) { // (optionally with (result) parameter) + /* Exit guarantees that the implementations of this + * function must give. (Derived interfaces and classes + * can give additional guarantees.) */ + } +} +--- + +$(P +We will see examples of contract inheritance later in $(LINK2 /ders/d.en/invariant.html, the Contract Programming for Structs and Classes chapter). +) + +$(H5 Inheriting from an $(C interface)) + +$(P +The $(C interface) inheritance syntax is the same as $(C class) inheritance: +) + +--- +class Violin : $(HILITE SoundEmitter) { + string emitSound() { + return "♩♪♪"; + } +} + +class Bell : $(HILITE SoundEmitter) { + string emitSound() { + return "ding"; + } +} +--- + +$(P +Interfaces support polymorphism: Functions that take interface parameters can use those parameters without needing to know the actual types of objects. For example, the following function that takes a parameter of $(C SoundEmitter) calls $(C emitSound()) on that parameter without needing to know the actual type of the object: +) + +--- +void useSoundEmittingObject(SoundEmitter object) { + // ... some operations ... + writeln(object.emitSound()); + // ... more operations ... +} +--- + +$(P +Just like with classes, that function can be called with any type of object that inherits from the $(C SoundEmitter) interface: +) + +--- + useSoundEmittingObject(new Violin); + useSoundEmittingObject(new Bell); +--- + +$(P +The special $(C emitSound()) function for each object would get called and the outputs of $(C Violin.emitSound) and $(C Bell.emitSound) would be printed: +) + +$(SHELL +♩♪♪ +ding +) + +$(H5 Inheriting from more than one $(C interface)) + +$(P +A class can be inherited from up to one $(C class). There is no limit on the number of $(C interface)s to inherit from. +) + +$(P +Let's consider the following interface that represents communication devices: +) + +--- +interface CommunicationDevice { + void talk(string message); + string listen(); +} +--- + +$(P +If a $(C Phone) class needs to be used both as a sound emitter and a communication device, it can inherit both of those interfaces: +) + +--- +class Phone : $(HILITE SoundEmitter, CommunicationDevice) { + // ... +} +--- + +$(P +That definition represents both of these relationships: "phone is a sound emitter" and "phone is a communication device." +) + +$(P +In order to construct objects of this class, $(C Phone) must implement the abstract functions of both of the interfaces: +) + +--- +class Phone : SoundEmitter, CommunicationDevice { + string emitSound() { // for SoundEmitter + return "rrring"; + } + + void talk(string message) { // for CommunicationDevice + // ... put the message on the line ... + } + + string listen() { // for CommunicationDevice + string soundOnTheLine; + // ... get the message from the line ... + return soundOnTheLine; + } +} +--- + +$(P +A class can inherit from any number of interfaces as it makes sense according to the design of the program. +) + +$(H5 Inheriting from $(C interface) and $(C class)) + +$(P +Classes can still inherit from up to one $(C class) as well: +) + +--- +$(HILITE class) Clock { + // ... clock implementation ... +} + +class AlarmClock : $(HILITE Clock), SoundEmitter { + string emitSound() { + return "beep"; + } +} +--- + +$(P +$(C AlarmClock) inherits the members of $(C Clock). Additionally, it also provides the $(C emitSound()) function that the $(C SoundEmitter) interface requires. +) + +$(H5 Inheriting $(C interface) from $(C interface)) + +$(P +An interface that is inherited from another interface effectively increases the number of functions that the subclasses must implement: +) + +--- +interface MusicalInstrument : SoundEmitter { + void adjustTuning(); +} +--- + +$(P +According to the definition above, in order to be a $(C MusicalInstrument), both the $(C emitSound()) function that $(C SoundEmitter) requires and the $(C adjustTuning()) function that $(C MusicalInstrument) requires must be implemented. +) + +$(P +For example, if $(C Violin) inherits from $(C MusicalInstrument) instead of $(C SoundEmitter), it must now also implement $(C adjustTuning()): +) + +--- +class Violin : MusicalInstrument { + string emitSound() { // for SoundEmitter + return "♩♪♪"; + } + + void adjustTuning() { // for MusicalInstrument + // ... special tuning of the violin ... + } +} +--- + +$(H5 $(IX static, member function) $(C static) member functions) + +$(P +I have delayed explaining $(C static) member functions until this chapter to keep the earlier chapters shorter. $(C static) member functions are available for structs, classes, and interfaces. +) + +$(P +Regular member functions are always called on an object. The member variables that are referenced inside the member function are the members of a particular object: +) + +--- +struct Foo { + int i; + + void modify(int value) { + $(HILITE i) = value; + } +} + +void main() { + auto object0 = Foo(); + auto object1 = Foo(); + + object0.modify(10); // object0.i changes + object1.modify(10); // object1.i changes +} +--- + +$(P +The members can also be referenced by $(C this): +) + +--- + void modify(int value) { + this.i = value; // equivalent of the previous one + } +--- + +$(P +A $(C static) member function does not operate on an object; there is no object that the $(C this) keyword would refer to, so $(C this) is not valid inside a $(C static) function. For that reason, none of the regular member variables are available inside $(C static) member functions: +) + +--- +struct Foo { + int i; + + $(HILITE static) void commonFunction(int value) { + i = value; $(DERLEME_HATASI) + this.i = value; $(DERLEME_HATASI) + } +} +--- + +$(P +$(C static) member functions can use only the $(C static) member variables. +) + +$(P +Let's redesign the $(C Point) struct that we have seen earlier in $(LINK2 /ders/d.en/struct.html, the Structs chapter), this time with a $(C static) member function. In the following code, every $(C Point) object gets a unique id, which is determined by a $(C static) member function: +) + +--- +import std.stdio; + +struct Point { + size_t id; // Object id + int line; + int column; + + // The id to be used for the next object + $(HILITE static) size_t nextId; + + this(int line, int column) { + this.line = line; + this.column = column; + this.id = makeNewId(); + } + + $(HILITE static) size_t makeNewId() { + immutable newId = nextId; + ++nextId; + return newId; + } +} + +void main() { + auto top = Point(7, 0); + auto middle = Point(8, 0); + auto bottom = Point(9, 0); + + writeln(top.id); + writeln(middle.id); + writeln(bottom.id); +} +--- + +$(P +The $(C static) $(C makeNewId()) function can use the common variable $(C nextId). As a result, every object gets a unique id: +) + +$(SHELL +0 +1 +2 +) + +$(P +Although the example above contains a $(C struct), $(C static) member functions are available for classes and interfaces as well. +) + +$(H5 $(IX final) $(C final) member functions) + +$(P +I have delayed explaining $(C final) member functions until this chapter to keep the earlier chapters shorter. $(C final) member functions are relevant only for classes and interfaces because structs do not support inheritance. +) + +$(P +$(C final) specifies that a member function cannot be redefined by a subclass. In a sense, the implementation that this $(C class) or $(C interface) provides is the $(I final) implementation of that function. An example of a case where this feature is useful is where the general steps of an algorithm are defined by an interface and the finer details are left to subclasses. +) + +$(P +Let's see an example of this with a $(C Game) interface. The general steps of playing a game is being determined by the $(C play()) function of the following $(C interface): +) + +--- +$(CODE_NAME Game)interface Game { + $(HILITE final) void play() { + string name = gameName(); + writefln("Starting %s", name); + + introducePlayers(); + prepare(); + begin(); + end(); + + writefln("Ending %s", name); + } + + string gameName(); + void introducePlayers(); + void prepare(); + void begin(); + void end(); +} +--- + +$(P +It is not possible for subclasses to modify the definition of the $(C play()) member function. The subclasses can (and must) provide the definitions of the five abstract member functions that are declared by the interface. By doing so, the subclasses complete the missing steps of the algorithm: +) + +--- +$(CODE_XREF Game)import std.stdio; +import std.string; +import std.random; +import std.conv; + +class DiceSummingGame : Game { + string player; + size_t count; + size_t sum; + + string gameName() { + return "Dice Summing Game"; + } + + void introducePlayers() { + write("What is your name? "); + player = strip(readln()); + } + + void prepare() { + write("How many times to throw the dice? "); + readf(" %s", &count); + sum = 0; + } + + void begin() { + foreach (i; 0 .. count) { + immutable dice = uniform(1, 7); + writefln("%s: %s", i, dice); + sum += dice; + } + } + + void end() { + writefln("Player: %s, Dice sum: %s, Average: %s", + player, sum, to!double(sum) / count); + } +} + +void useGame(Game game) { + game.play(); +} + +void main() { + useGame(new DiceSummingGame()); +} +--- + +$(P +Although the example above contains an $(C interface), $(C final) member functions are available for classes as well. +) + +$(H5 How to use) + +$(P +$(C interface) is a commonly used feature. There is one or more $(C interface) at the top of almost every class hierarchy. A kind of hierarchy that is commonly encountered in programs involves a single $(C interface) and a number of classes that implement that interface: +) + +$(MONO + $(I MusicalInstrument + (interface)) + / | \ \ + Violin Guitar Flute ... +) + +$(P +Although there are more complicated hierarchies in practice, the simple hierarchy above solves many problems. +) + +$(P +It is also common to move common implementation details of class hierarchies to intermediate classes. The subclasses inherit from these intermediate classes. The $(C StringInstrument) and $(C WindInstrument) classes below can contain the common members of their respective subclasses: +) + +$(MONO + $(I MusicalInstrument + (interface)) + / \ + StringInstrument WindInstrument + / | \ / | \ +Violin Viola ... Flute Clarinet ... +) + +$(P +The subclasses would implement their respective special definitions of member functions. +) + +$(H5 $(IX abstraction) Abstraction) + +$(P +Interfaces help make parts of programs independent from each other. This is called $(I abstraction). For example, a program that deals with musical instruments can be written primarily by using the $(C MusicalInstrument) interface, without ever specifying the actual types of the musical instruments. +) + +$(P +A $(C Musician) class can contain a $(C MusicalInstrument) without ever knowing the actual type of the instrument: +) + +--- +class Musician { + MusicalInstrument instrument; + // ... +} +--- + +$(P +Different types of musical instruments can be combined in a collection without regard to the actual types of these instruments: +) + +--- + MusicalInstrument[] orchestraInstruments; +--- + +$(P +Most of the functions of the program can be written only by using this interface: +) + +--- +bool needsTuning(MusicalInstrument instrument) { + bool result; + // ... + return result; +} + +void playInTune(MusicalInstrument instrument) { + if (needsTuning(instrument)) { + instrument.adjustTuning(); + } + + writeln(instrument.emitSound()); +} +--- + +$(P +$(I Abstracting away) parts of a program from each other allows making changes in one part of the program without needing to modify the other parts. When implementations of certain parts of the program are $(I behind) a particular interface, the code that uses only that interface does not get affected. +) + +$(H5 Example) + +$(P +The following program defines the $(C SoundEmitter), $(C MusicalInstrument), and $(C CommunicationDevice) interfaces: +) + +--- +import std.stdio; + +/* This interface requires emitSound(). */ +interface SoundEmitter { + string emitSound(); +} + +/* This class needs to implement only emitSound(). */ +class Bell : SoundEmitter { + string emitSound() { + return "ding"; + } +} + +/* This interface additionally requires adjustTuning(). */ +interface MusicalInstrument : SoundEmitter { + void adjustTuning(); +} + +/* This class needs to implement both emitSound() and + * adjustTuning(). */ +class Violin : MusicalInstrument { + string emitSound() { + return "♩♪♪"; + } + + void adjustTuning() { + // ... tuning of the violin ... + } +} + +/* This interface requires talk() and listen(). */ +interface CommunicationDevice { + void talk(string message); + string listen(); +} + +/* This class needs to implement emitSound(), talk(), and + * listen(). */ +class Phone : SoundEmitter, CommunicationDevice { + string emitSound() { + return "rrring"; + } + + void talk(string message) { + // ... put the message on the line ... + } + + string listen() { + string soundOnTheLine; + // ... get the message from the line ... + return soundOnTheLine; + } +} + +class Clock { + // ... the implementation of Clock ... +} + +/* This class needs to implement only emitSound(). */ +class AlarmClock : Clock, SoundEmitter { + string emitSound() { + return "beep"; + } + + // ... the implementation of AlarmClock ... +} + +void main() { + SoundEmitter[] devices; + + devices ~= new Bell; + devices ~= new Violin; + devices ~= new Phone; + devices ~= new AlarmClock; + + foreach (device; devices) { + writeln(device.emitSound()); + } +} +--- + +$(P +Because $(C devices) is a $(C SoundEmitter) slice, it can contain objects of any type that inherits from $(C SoundEmitter) (i.e. types that have an "is a" relationship with $(C SoundEmitter)). As a result, the output of the program consists of different sounds that are emitted by the different types of objects: +) + +$(SHELL +ding +♩♪♪ +rrring +beep +) + +$(H5 Summary) + +$(UL + +$(LI +$(C interface) is similar to a $(C class) that consists only of abstract functions. $(C interface) can have $(C static) member variables and $(C static) or $(C final) member functions. +) + +$(LI +For a class to be constructible, it must have implementations for all member functions of all interfaces that it inherits from. +) + +$(LI +It is possible to inherit from unlimited number of $(C interface)s. +) + +$(LI +A common hierarchy consists of a single $(C interface) and a number of subclasses that implement that interface. +) + +) + +Macros: + SUBTITLE=Interfaces + + DESCRIPTION=The interfaces of the D programming language. + + KEYWORDS=d programming lesson book tutorial interface diff --git a/target/invariant.d b/target/invariant.d new file mode 100644 index 0000000..d06fbce --- /dev/null +++ b/target/invariant.d @@ -0,0 +1,467 @@ +Ddoc + +$(DERS_BOLUMU $(IX contract programming) Contract Programming for Structs and Classes) + +$(P +Contract programming is very effective in reducing coding errors. We have seen two of the contract programming features earlier in $(LINK2 /ders/d.en/contracts.html, the Contract Programming chapter): The $(C in) and $(C out) blocks ensure input and output contracts of functions. +) + +$(P +$(I $(B Note:) It is very important that you consider the guidelines under the "$(C in) blocks versus $(C enforce) checks" section of that chapter. The examples in this chapter are based on the assumption that problems with object and parameter consistencies are due to programmer errors. Otherwise, you should use $(C enforce) checks inside function bodies.) +) + +$(P +As a reminder, let's write a function that calculates the area of a triangle by Heron's formula. We will soon move the $(C in) and $(C out) blocks of this function to the constructor of a struct. +) + +$(P +Obviously, for this calculation to work correctly, the lengths of all of the sides of the triangle must be greater than or equal to zero. Additionally, since it is impossible to have a triangle where one of the sides is greater than the sum of the other two, that condition must also be checked. +) + +$(P +Once these input conditions are satisfied, the area of the triangle would be calculated as greater than or equal to zero. The following function ensures that all of these requirements are satisfied: +) + +--- +private import std.math; + +double triangleArea(in double a, in double b, in double c) +in { + // No side can be less than zero + assert(a >= 0); + assert(b >= 0); + assert(c >= 0); + + // No side can be longer than the sum of the other two + assert(a <= (b + c)); + assert(b <= (a + c)); + assert(c <= (a + b)); + +} out (result) { + assert(result >= 0); + +} body { + immutable halfPerimeter = (a + b + c) / 2; + + return sqrt(halfPerimeter + * (halfPerimeter - a) + * (halfPerimeter - b) + * (halfPerimeter - c)); +} +--- + +$(H5 $(IX in, contract) $(IX out, contract) $(IX precondition) $(IX postcondition) Preconditions and postconditions for member functions) + +$(P +The $(C in) and $(C out) blocks can be used with member functions as well. +) + +$(P +Let's convert the function above to a member function of a $(C Triangle) struct: +) + +--- +import std.stdio; +import std.math; + +struct Triangle { +private: + + double a; + double b; + double c; + +public: + + double area() const @property + $(HILITE out (result)) { + assert(result >= 0); + + } body { + immutable halfPerimeter = (a + b + c) / 2; + + return sqrt(halfPerimeter + * (halfPerimeter - a) + * (halfPerimeter - b) + * (halfPerimeter - c)); + } +} + +void main() { + auto threeFourFive = Triangle(3, 4, 5); + writeln(threeFourFive.area); +} +--- + +$(P +As the sides of the triangle are now member variables, the function does not take parameters anymore. That is why this function does not have an $(C in) block. Instead, it assumes that the members already have consistent values. +) + +$(P +The consistency of objects can be ensured by the following features. +) + +$(H5 Preconditions and postconditions for object consistency) + +$(P +The member function above is written under the assumption that the members of the object already have consistent values. One way of ensuring that assumption is to define an $(C in) block for the constructor so that the objects are guaranteed to start their lives in consistent states: +) + +--- +struct Triangle { +// ... + + this(in double a, in double b, in double c) + $(HILITE in) { + // No side can be less than zero + assert(a >= 0); + assert(b >= 0); + assert(c >= 0); + + // No side can be longer than the sum of the other two + assert(a <= (b + c)); + assert(b <= (a + c)); + assert(c <= (a + b)); + + } body { + this.a = a; + this.b = b; + this.c = c; + } + +// ... +} +--- + +$(P +This prevents creating invalid $(C Triangle) objects at run time: +) + +--- + auto negativeSide = Triangle(-1, 1, 1); + auto sideTooLong = Triangle(1, 1, 10); +--- + +$(P +The $(C in) block of the constructor would prevent such invalid objects: +) + +$(SHELL +core.exception.AssertError@deneme.d: Assertion failure +) + +$(P +Although an $(C out) block has not been defined for the constructor above, it is possible to define one to ensure the consistency of members right after construction. +) + +$(H5 $(IX invariant) $(C invariant()) blocks for object consistency) + +$(P +The $(C in) and $(C out) blocks of constructors guarantee that the objects start their lives in consistent states and the $(C in) and $(C out) blocks of member functions guarantee that those functions themselves work correctly. +) + +$(P +However, these checks are not suitable for guaranteeing that the objects are always in consistent states. Repeating the $(C out) blocks for every member function would be excessive and error-prone. +) + +$(P +The conditions that define the consistency and validity of an object are called the $(I invariants) of that object. For example, if there is a one-to-one correspondence between the orders and the invoices of a customer class, then an invariant of that class would be that the lengths of the order and invoice arrays would be equal. When that condition is not satisfied for any object, then the object would be in an inconsistent state. +) + +$(P +As an example of an invariant, let's consider the $(C School) class from $(LINK2 /ders/d.en/encapsulation.html, the Encapsulation and Protection Attributes chapter): +) + +--- +class School { +private: + + Student[] students; + size_t femaleCount; + size_t maleCount; + +// ... +} +--- + +$(P +The objects of that class are consistent only if an invariant that involves its three members are satisfied. The length of the student array must be equal to the sum of the female and male students: +) + +--- + assert(students.length == (femaleCount + maleCount)); +--- + +$(P +If that condition is ever false, then there must be a bug in the implementation of this class. +) + +$(P +$(C invariant()) blocks are for guaranteeing the invariants of user-defined types. $(C invariant()) blocks are defined inside the body of a $(C struct) or a $(C class). They contain $(C assert) checks similar to $(C in) and $(C out) blocks: +) + +--- +class School { +private: + + Student[] students; + size_t femaleCount; + size_t maleCount; + + $(HILITE invariant()) { + assert(students.length == (femaleCount + maleCount)); + } + +// ... +} +--- + +$(P +As needed, there can be more than one $(C invariant()) block in a user-defined type. +) + +$(P +The $(C invariant()) blocks are executed automatically at the following times: +) + +$(UL + +$(LI After the execution of the constructor: This guarantees that every object starts its life in a consistent state.) + +$(LI Before the execution of the destructor: This guarantees that the destructor will be executed on a consistent object.) + +$(LI Before and after the execution of a $(C public) member function: This guarantees that the member functions do not invalidate the consistency of objects. + +$(P +$(IX export) $(I $(B Note:) $(C export) functions are the same as $(C public) functions in this regard. (Very briefly, $(C export) functions are functions that are exported on dynamic library interfaces.)) +) + +) + +) + +$(P +If an $(C assert) check inside an $(C invariant()) block fails, an $(C AssertError) is thrown. This ensures that the program does not continue executing with invalid objects. +) + +$(P +$(IX -release, compiler switch) As with $(C in) and $(C out) blocks, the checks inside $(C invariant()) blocks can be disabled by the $(C -release) command line option: +) + +$(SHELL +dmd deneme.d -w -release +) + +$(H5 $(IX contract inheritance) $(IX inheritance, contract) $(IX precondition, inherited) $(IX postcondition, inherited) $(IX in, inherited) $(IX out, inherited) Contract inheritance) + +$(P +Interface and class member functions can have $(C in) and $(C out) blocks as well. This allows an $(C interface) or a $(C class) to define preconditions for its derived types to depend on, as well as to define postconditions for its users to depend on. Derived types can define further $(C in) and $(C out) blocks for the overrides of those member functions. Overridden $(C in) blocks can loosen preconditions and overridden $(C out) blocks can offer more guarantees. +) + +$(P +User code is commonly $(I abstracted away) from the derived types, written in a way to satisfy the preconditions of the topmost type in a hierarchy. The user code does not even know about the derived types. Since user code would be written for the contracts of an interface, it would not be acceptable for a derived type to put stricter preconditions on an overridden member function. However, the preconditions of a derived type can be more permissive than the preconditions of its superclass. +) + +$(P +Upon entering a function, the $(C in) blocks are executed automatically from the topmost type to the bottom-most type in the hierarchy . If $(I any) $(C in) block succeeds without any $(C assert) failure, then the preconditions are considered to be fulfilled. (See below for the risk of disabling preconditions unintentionally.) +) + +$(P +Similarly, derived types can define $(C out) blocks as well. Since postconditions are about guarantees that a function provides, the member functions of the derived type must observe the postconditions of its ancestors as well. On the other hand, it can provide additional guarantees. +) + +$(P +Upon exiting a function, the $(C out) blocks are executed automatically from the topmost type to the bottom-most type. The function is considered to have fullfilled its postconditions only if $(I all) of the $(C out) blocks succeed. +) + +$(P +The following artificial program demonstrates these features on an $(C interface) and a $(C class). The $(C class) requires less from its callers while providing more guarantees: +) + +--- +interface Iface { + int[] func(int[] a, int[] b) + $(HILITE in) { + writeln("Iface.func.in"); + + /* This interface member function requires that the + * lengths of the two parameters are equal. */ + assert(a.length == b.length); + + } $(HILITE out) (result) { + writeln("Iface.func.out"); + + /* This interface member function guarantees that the + * result will have even number of elements. + * (Note that an empty slice is considered to have + * even number of elements.) */ + assert((result.length % 2) == 0); + } +} + +class Class : Iface { + int[] func(int[] a, int[] b) + $(HILITE in) { + writeln("Class.func.in"); + + /* This class member function loosens the ancestor's + * preconditions by allowing parameters with unequal + * lengths as long as at least one of them is empty. */ + assert((a.length == b.length) || + (a.length == 0) || + (b.length == 0)); + + } $(HILITE out) (result) { + writeln("Class.func.out"); + + /* This class member function provides additional + * guarantees: The result will not be empty and that + * the first and the last elements will be equal. */ + assert((result.length != 0) && + (result[0] == result[$ - 1])); + + } body { + writeln("Class.func.body"); + + /* This is just an artificial implementation to + * demonstrate how the 'in' and 'out' blocks are + * executed. */ + + int[] result; + + if (a.length == 0) { + a = b; + } + + if (b.length == 0) { + b = a; + } + + foreach (i; 0 .. a.length) { + result ~= a[i]; + result ~= b[i]; + } + + result[0] = result[$ - 1] = 42; + + return result; + } +} + +import std.stdio; + +void main() { + auto c = new Class(); + + /* Although the following call fails Iface's precondition, + * it is accepted because it fulfills Class' precondition. */ + writeln(c.func([1, 2, 3], $(HILITE []))); +} +--- + +$(P +The $(C in) block of $(C Class) is executed only because the parameters fail to satisfy the preconditions of $(C Iface): +) + +$(SHELL +Iface.func.in +Class.func.in $(SHELL_NOTE would not be executed if Iface.func.in succeeded) +Class.func.body +Iface.func.out +Class.func.out +[42, 1, 2, 2, 3, 42] +) + +$(H6 Unintentionally disabling preconditions) + +$(P +A function that does not have an $(C in) block means that that function has $(I no precondition at all). As a consequence, member functions of derived types that do not define preconditions risk disabling preconditions of their superclasses unintentionally. (As described above, if the $(C in) block of any one of the overrides of the member function succeeds, then the preconditions are considered to be fulfilled.) +) + +$(P +As a general guideline, if a member function has $(C in) blocks in its superclass (including interfaces), it is highly likely that a derived type needs to define one as well. For example, you can add a failing precondition to a subclass even though there is no additional precondition that it imposes. +) + +$(P +Let's start with an example where a member function of a subclass effectively disables the preconditions of its superclass: +) + +--- +class Protocol { + void compute(double d) + in { + assert($(HILITE d > 42)); + + } body { + // ... + } +} + +class SpecialProtocol : Protocol { + /* Because it does not have an 'in' block, this function + * effectively disables the preconditions of + * 'Protocol.compute', perhaps unintentionally. */ + override void compute(double d) { + // ... + } +} + +void main() { + auto s = new SpecialProtocol(); + s.compute($(HILITE 10)); /* BUG: Although the value 10 does not + * satisfy the precondition of the + * superclass, this call succeeds. */ +} +--- + +$(P +One solution is to add a failing precondition to $(C SpecialProtocol.compute): +) + +--- +class SpecialProtocol : Protocol { + override void compute(double d) + in { + $(HILITE assert(false)); + + } body { + // ... + } +} +--- + +$(P +This time, the precondition of the superclass would be in effect and the incorrect argument value would be caught: +) + +$(SHELL +core.exception.AssertError@deneme.d: Assertion failure +) + +$(H5 Summary) + +$(UL + +$(LI $(C in) and $(C out) blocks are useful in constructors as well. They ensure that objects are constructed in valid states. +) + +$(LI +$(C invariant()) blocks ensure that objects remain in valid states throughout their lifetimes. +) + +$(LI +Derived types can define $(C in) blocks for overridden member functions. Preconditions of a derived type should not be stricter than the preconditions of its superclasses. ($(I Note that not defining an $(C in) block means "no precondition at all", which may not be the intent of the programmer.)) +) + +$(LI +Derived types can define $(C out) blocks for overridden member functions. In addition to its own, a derived member function must observe the postconditions of its superclasses as well. +) + +) + +Macros: + SUBTITLE=Contract Programming for Structs and Classes + + DESCRIPTION=The invariant keyword that ensures that struct and class objects are always in consistent states. + + KEYWORDS=d programming lesson book tutorial invariant diff --git a/target/io.cozum.d b/target/io.cozum.d new file mode 100644 index 0000000..b84898a --- /dev/null +++ b/target/io.cozum.d @@ -0,0 +1,21 @@ +Ddoc + +$(COZUM_BOLUMU Standard Input and Output Streams) + +--- +import std.stdio; + +void main() { + stdout.write(1, ",", 2); + + // To complete the line if needed: + writeln(); +} +--- + +Macros: + SUBTITLE=Standard Input and Output Streams Solutions + + DESCRIPTION=Programming in D exercise solutions: standard input and output streams + + KEYWORDS=programming in d tutorial standard input and output stream solution diff --git a/target/io.d b/target/io.d new file mode 100644 index 0000000..9254cb2 --- /dev/null +++ b/target/io.d @@ -0,0 +1,58 @@ +Ddoc + +$(DERS_BOLUMU $(IX standard input) $(IX standard output) Standard Input and Output Streams) + +$(P +So far, the printed output of our programs has been appearing on the $(I terminal window) (or $(I screen)). Although the terminal is often the ultimate target of output, this is not always the case. The objects that can accept output are called $(I standard output streams). +) + +$(P +The standard output is character based; everything to be printed is first converted to the corresponding character representation and then sent to the output as characters. For example, the integer value 100 that we've printed in the last chapter is not sent to the output as the value 100, but as the three characters $(C 1), $(C 0), and $(C 0). +) + +$(P +Similarly, what we normally perceive as the $(I keyboard) is actually the $(I standard input stream) of a program and is also character based. The information always comes as characters to be converted to data. For example, the integer value 42 actually comes through the standard input as the characters $(C 4) and $(C 2). +) + +$(P +These conversions happen automatically. +) + +$(P +This concept of consecutive characters is called a $(I character stream). As D's standard input and standard output fit this description, they are character streams. +) + +$(P +$(IX stdin) $(IX stdout) The names of the standard input and output streams in D are $(C stdin) and $(C stdout), respectively. +) + +$(P +Operations on these streams normally require the name of the stream, a dot, and the operation; as in $(C stream.operation()). Because $(C stdin)'s reading methods and $(C stdout)'s writing methods are used very commonly, those operations can be called without the need of the stream name and the dot. +) + +$(P +$(C writeln) that we've been using in the previous chapters is actually short for $(C stdout.writeln). Similarly, $(C write) is short for $(C stdout.write). Accordingly, the $(I hello world) program can also be written as follows: +) + +--- +import std.stdio; + +void main() { + $(HILITE stdout.)writeln("Hello world!"); +} +--- + +$(PROBLEM_TEK + +$(P +Observe that $(C stdout.write) works the same as $(C write). +) + +) + +Macros: + SUBTITLE=Standard Input and Output Streams + + DESCRIPTION=D's standard input and output streams stdin and stdout. + + KEYWORDS=d programming language tutorial book stdin stdout diff --git a/target/is_expr.d b/target/is_expr.d new file mode 100644 index 0000000..1b67cd9 --- /dev/null +++ b/target/is_expr.d @@ -0,0 +1,558 @@ +Ddoc + +$(DERS_BOLUMU $(IX is, expression) $(CH4 is) Expression) + +$(P +The $(C is) $(I expression) is not related to the $(C is) $(I operator) that we saw in $(LINK2 /ders/d.en/null_is.html, The $(CH4 null) Value and the $(CH4 is) Operator chapter), neither syntactically nor semantically: +) + +--- + a is b // is operator, which we have seen before + + is (/* ... */) // is expression +--- + +$(P +The $(C is) expression is evaluated at compile time. It produces an $(C int) value, either 0 or 1 depending on the expression specified in parentheses. Although the expression that it takes is not a logical expression, the $(C is) expression itself is used as a compile time logical expression. It is especially useful in $(C static if) conditionals and template constraints. +) + +$(P +The condition that it takes is always about types, which must be written in one of several syntaxes. +) + +$(H5 $(C is ($(I T)))) + +$(P +Determines whether $(C T) is valid as a type. +) + +$(P +It is difficult to come up with examples for this use at this point. We will take advantage of it in later chapters with template parameters. +) + +--- + static if (is (int)) { + writeln("valid"); + + } else { + writeln("invalid"); + } +--- + +$(P +$(C int) above is a valid type: +) + +$(SHELL_SMALL +valid +) + +$(P +As another example, because $(C void) is not valid as the key type of an associative array, the $(C else) block would be enabled below: +) + +--- + static if (is (string[void])) { + writeln("valid"); + + } else { + writeln("invalid"); + } +--- + +$(P +The output: +) + +$(SHELL_SMALL +invalid +) + + +$(H5 $(C is ($(I T Alias)))) + +$(P +Works in the same way as the previous syntax. Additionally, defines $(C Alias) as an alias of $(C T): +) + +--- + static if (is (int NewAlias)) { + writeln("valid"); + NewAlias var = 42; // int and NewAlias are the same + + } else { + writeln("invalid"); + } +--- + +$(P +Such aliases are useful especially in more complex $(C is) expressions as we will see below. +) + +$(H5 $(C is ($(I T) : $(I OtherT)))) + +$(P +Determines whether $(C T) can automatically be converted to $(C OtherT). +) + +$(P +It is used for detecting automatic type conversions which we have seen in $(LINK2 /ders/d.en/cast.html, the Type Conversions chapter), as well as relationships like "this type is of that type", which we have seen in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter). +) + +--- +import std.stdio; + +interface Clock { + void tellTime(); +} + +class AlarmClock : Clock { + override void tellTime() { + writeln("10:00"); + } +} + +void myFunction(T)(T parameter) { + static if ($(HILITE is (T : Clock))) { + // If we are here then T can be used as a Clock + writeln("This is a Clock; we can tell the time"); + parameter.tellTime(); + + } else { + writeln("This is not a Clock"); + } +} + +void main() { + auto var = new AlarmClock; + myFunction(var); + myFunction(42); +} +--- + +$(P +When the $(C myFunction()) template is instantiated for a type that can be used like a $(C Clock), then the $(C tellTime()) member function is called on its parameter. Otherwise, the $(C else) clause gets compiled: +) + +$(SHELL_SMALL +This is a Clock; we can tell the time $(SHELL_NOTE for AlarmClock) +10:00 $(SHELL_NOTE for AlarmClock) +This is not a Clock $(SHELL_NOTE for int) +) + +$(H5 $(C is ($(I T Alias) : $(I OtherT)))) + +$(P +Works in the same way as the previous syntax. Additionally, defines $(C Alias) as an alias of $(C T). +) + +$(H5 $(C is ($(I T) == $(I Specifier)))) + +$(P +Determines whether $(C T) $(I is the same type) as $(C Specifier) or whether $(C T) $(I matches that specifier). +) + +$(H6 Whether the same type) + +$(P +When we change the previous example to use $(C ==) instead of $(C :), the condition would not be satisfied for $(C AlarmClock): +) + +--- + static if (is (T $(HILITE ==) Clock)) { + writeln("This is a Clock; we can tell the time"); + parameter.tellTime(); + + } else { + writeln("This is not a Clock"); + } +--- + +$(P +Although $(C AlarmClock) $(I is a) $(C Clock), it is not exactly the same type as $(C Clock). For that reason, now the condition is invalid for both $(C AlarmClock) and $(C int): +) + +$(SHELL_SMALL +This is not a Clock +This is not a Clock +) + +$(H6 Whether matches the same specifier) + +$(P +When $(C Specifier) is one of the following keywords, this use of $(C is) determines whether the type matches that specifier (we will see some of these keywords in later chapters): +) + +$(UL +$(LI $(IX struct, is expression) $(C struct)) +$(LI $(IX union, is expression) $(C union)) +$(LI $(IX class, is expression) $(C class)) +$(LI $(IX interface, is expression) $(C interface)) +$(LI $(IX enum, is expression) $(C enum)) +$(LI $(IX function, is expression) $(C function)) +$(LI $(IX delegate, is expression) $(C delegate)) +$(LI $(IX const, is expression) $(C const)) +$(LI $(IX immutable, is expression) $(C immutable)) +$(LI $(IX shared, is expression) $(C shared)) +) + +--- +void myFunction(T)(T parameter) { + static if (is (T == class)) { + writeln("This is a class type"); + + } else static if (is (T == enum)) { + writeln("This is an enum type"); + + } else static if (is (T == const)) { + writeln("This is a const type"); + + } else { + writeln("This is some other type"); + } +} +--- + +$(P +Function templates can take advantage of such information to behave differently depending on the type that the template is instantiated with. The following code demonstrates how different blocks of the template above get compiled for different types: +) + +--- + auto var = new AlarmClock; + myFunction(var); + + // (enum WeekDays will be defined below for another example) + myFunction(WeekDays.Monday); + + const double number = 1.2; + myFunction(number); + + myFunction(42); +--- + +$(P +The output: +) + +$(SHELL_SMALL +This is a class type +This is an enum type +This is a const type +This is some other type +) + +$(H5 $(C is ($(I T identifier) == $(I Specifier)))) + +$(P +$(IX super, is expression) +$(IX return, is expression) +$(IX __parameters, is expression) +Works in the same way as the previous syntax. $(C identifier) is either an alias of the type; or some other information depending on $(C Specifier): +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
$(C Specifier)The meaning of $(C identifier)
$(C struct)alias of the type that satisfied the condition
$(C union)alias of the type that satisfied the condition
$(C class)alias of the type that satisfied the condition
$(C interface)alias of the type that satisfied the condition
$(C super)a $(I tuple) consisting of the base classes and the interfaces
$(C enum)the actual implementation type of the $(C enum)
$(C function)a $(I tuple) consisting of the function parameters
$(C delegate)the type of the $(C delegate)
$(C return)the return type of the regular function, the $(C delegate), or the function pointer
$(C __parameters)a $(I tuple) consisting of the parameters of the regular function, the $(C delegate), or the function pointer
$(C const)alias of the type that satisfied the condition
$(C immutable)alias of the type that satisfied the condition
$(C shared)alias of the type that satisfied the condition
+ +$(P +Let's first define various types before experimenting with this syntax: +) + +--- +struct Point { + // ... +} + +interface Clock { + // ... +} + +class AlarmClock : Clock { + // ... +} + +enum WeekDays { + Monday, Tuesday, Wednesday, Thursday, Friday, + Saturday, Sunday +} + +char foo(double d, int i, Clock c) { + return 'a'; +} +--- + +$(P +The following function template uses different specifiers with this syntax of the $(C is) expression: +) + +--- +void myFunction(T)(T parameter) { + static if (is (T LocalAlias == struct)) { + writefln("\n--- struct ---"); + // LocalAlias is the same as T. 'parameter' is the + // struct object that has been passed to this + // function. + + writefln("Constructing a new %s object by copying it.", + LocalAlias.stringof); + LocalAlias theCopy = parameter; + } + + static if (is (T baseTypes == super)) { + writeln("\n--- super ---"); + // The 'baseTypes' tuple contains all of the base + // types of T. 'parameter' is the class variable that + // has been passed to this function. + + writefln("class %s has %s base types.", + T.stringof, baseTypes.length); + + writeln("All of the bases: ", baseTypes.stringof); + writeln("The topmost base: ", baseTypes[0].stringof); + } + + static if (is (T ImplT == enum)) { + writeln("\n--- enum ---"); + // 'ImplT' is the actual implementation type of this + // enum type. 'parameter' is the enum value that has + // been passed to this function. + + writefln("The implementation type of enum %s is %s", + T.stringof, ImplT.stringof); + } + + static if (is (T ReturnT == return)) { + writeln("\n--- return ---"); + // 'ReturnT' is the return type of the function + // pointer that has been passed to this function. + + writefln("This is a function with a return type of %s:", + ReturnT.stringof); + writeln(" ", T.stringof); + write("calling it... "); + + // Note: Function pointers can be called like + // functions + ReturnT result = parameter(1.5, 42, new AlarmClock); + writefln("and the result is '%s'", result); + } +} +--- + +$(P +Let's now call that function template with various types that we have defined above: +) + +--- + // Calling with a struct object + myFunction(Point()); + + // Calling with a class reference + myFunction(new AlarmClock); + + // Calling with an enum value + myFunction(WeekDays.Monday); + + // Calling with a function pointer + myFunction(&foo); +--- + +$(P +The output: +) + +$(SHELL_SMALL +--- struct --- +Constructing a new Point object by copying it. + +--- super --- +class AlarmClock has 2 base types. +All of the bases: (in Object, in Clock) +The topmost base: Object + +--- enum --- +The implementation type of enum WeekDays is int + +--- return --- +This is a function with a return type of char: + char function(double d, int i, Clock c) +calling it... and the result is 'a' +) + +$(H5 $(C is (/* ... */ $(I Specifier), $(I TemplateParamList)))) + +$(P +There are four different syntaxes of the $(C is) expression that uses a template parameter list: +) + +$(UL + +$(LI $(C is ($(I T) : $(I Specifier), $(I TemplateParamList)))) + +$(LI $(C is ($(I T) == $(I Specifier), $(I TemplateParamList)))) + +$(LI $(C is ($(I T identifier) : $(I Specifier), $(I TemplateParamList)))) + +$(LI $(C is ($(I T identifier) == $(I Specifier), $(I TemplateParamList)))) + +) + +$(P +These syntaxes allow for more complex cases. +) + +$(P +$(C identifier), $(C Specifier), $(C :), and $(C ==) all have the same meanings as described above. +) + +$(P +$(C TemplateParamList) is both a part of the condition that needs to be satisfied and a facility to define additional aliases if the condition is indeed satisfied. It works in the same way as template type deduction. +) + +$(P +As a simple example, let's assume that an $(C is) expression needs to match associative arrays that have keys of type $(C string): +) + +--- + static if (is (T == Value[Key], // (1) + Value, // (2) + Key : string)) { // (3) +--- + +$(P +That condition can be explained in three parts where the last two are parts of the $(C TemplateParamList): +) + +$(OL +$(LI If $(C T) matches the syntax of $(C Value[Key])) +$(LI If $(C Value) is a type) +$(LI If $(C Key) is $(C string) (remember $(LINK2 /ders/d.en/templates.html, template specialization syntax))) +) + +$(P +Having $(C Value[Key]) as the $(C Specifier) requires that $(C T) is an associative array. Leaving $(C Value) $(I as is) means that it can be any type. Additionally, the key type of the associative array must be $(C string). As a result, the previous $(C is) expression means "if $(C T) is an associative array where the key type is $(C string)." +) + +$(P +The following program tests that $(C is) expression with four different types: +) + +--- +import std.stdio; + +void myFunction(T)(T parameter) { + writefln("\n--- Called with %s ---", T.stringof); + + static if (is (T == Value[Key], + Value, + Key : string)) { + + writeln("Yes, the condition has been satisfied."); + + writeln("The value type: ", Value.stringof); + writeln("The key type : ", Key.stringof); + + } else { + writeln("No, the condition has not been satisfied."); + } +} + +void main() { + int number; + myFunction(number); + + int[string] intTable; + myFunction(intTable); + + double[string] doubleTable; + myFunction(doubleTable); + + dchar[long] dcharTable; + myFunction(dcharTable); +} +--- + +$(P +The condition is satisfied only if the key type is $(C string): +) + +$(SHELL_SMALL +--- Called with int --- +No, the condition has not been satisfied. + +--- Called with int[string] --- +Yes, the condition has been satisfied. +The value type: int +The key type : string + +--- Called with double[string] --- +Yes, the condition has been satisfied. +The value type: double +The key type : string + +--- Called with dchar[long] --- +No, the condition has not been satisfied. +) + +Macros: + SUBTITLE=is Expression + + DESCRIPTION=The is expression, one of the introspection features of the D programming language. + + KEYWORDS=d programming language tutorial book is expression diff --git a/target/ix.d b/target/ix.d new file mode 100644 index 0000000..b85c3ae --- /dev/null +++ b/target/ix.d @@ -0,0 +1,14 @@ +Ddoc + +$(H4 Index) + +$(DIV_CLASS web_index_section, +$(INDEX_ENTRIES) +) + +Macros: + SUBTITLE=Index Section + + DESCRIPTION=The index section of the book Programming in D + + KEYWORDS=index diff --git a/target/lambda.d b/target/lambda.d new file mode 100644 index 0000000..330573c --- /dev/null +++ b/target/lambda.d @@ -0,0 +1,1196 @@ +Ddoc + +$(DERS_BOLUMU Function Pointers, Delegates, and Lambdas) + +$(P +Function pointers are for storing addresses of functions in order to execute those functions at a later time. Function pointers are similar to their counterparts in the C programming language. +) + +$(P +Delegates store both a function pointer and the context to execute that function pointer in. The stored context can either be the scope that the function execution will take place or a $(C struct) or $(C class) object. +) + +$(P +Delegates enable $(I closures) as well, a concept that is supported by most functional programming languages. +) + +$(H5 $(IX function pointer) $(IX pointer, function) Function pointers) + +$(P +$(IX &, function address) We have seen in the previous chapter that it is possible to take addresses of functions with the $(C &) operator. In one of those examples, we passed such an address to a function template. +) + +$(P +Taking advantage of the fact that template type parameters can match any type, let's pass a function pointer to a template to observe its type by printing its $(C .stringof) property: +) + +--- +import std.stdio; + +int myFunction(char c, double d) { + return 42; +} + +void main() { + myTemplate($(HILITE &myFunction)); // Taking the function's address and + // passing it as a parameter +} + +void myTemplate(T)(T parameter) { + writeln("type : ", T.stringof); + writeln("value: ", parameter); +} +--- + +$(P +The output of the program reveals the type and the address of $(C myFunction()): +) + +$(SHELL +type : int function(char c, double d) +value: 406948 +) + +$(H6 $(IX member function pointer) $(IX pointer, member function) Member function pointers) + +$(P +The address of a member function can be taken either on a type or on an object of a type, with different results: +) + +--- +struct MyStruct { + void func() { + } +} + +void main() { + auto o = MyStruct(); + + auto f = &$(HILITE MyStruct).func; // on a type + auto d = &$(HILITE o).func; // on an object + + static assert(is (typeof($(HILITE f)) == void $(HILITE function)())); + static assert(is (typeof($(HILITE d)) == void $(HILITE delegate)())); +} +--- + +$(P +As the two $(C static assert) lines above indicate, $(C f) is a $(C function) and $(C d) is a $(C delegate). We will see later below that $(C d) can be called directly but $(C f) needs an object to be called on. +) + +$(H6 Definition) + +$(P +$(IX function) Similar to regular pointers, each function pointer type can point exactly to a particular type of function; the parameter list and the return type of the function pointer and the function must match. Function pointers are defined by the $(C function) keyword between the return type and the parameter list of that particular type: +) + +--- + $(I return_type) function($(I parameters)) ptr; +--- + +$(P +The names of the parameters ($(C c) and $(C d) in the output above) are optional. Because $(C myFunction()) takes a $(C char) and a $(C double) and returns an $(C int), the type of a function pointer that can point at $(C myFunction()) must be defined accordingly: +) + +--- + int function(char, double) ptr = &myFunction; +--- + +$(P +The line above defines $(C ptr) as a function pointer taking two parameters ($(C char) and $(C double)) and returning $(C int). Its value is the address of $(C myFunction()). +) + +$(P +Function pointer syntax is relatively harder to read; it is common to make code more readable by an $(C alias): +) + +--- +alias CalculationFunc = int function(char, double); +--- + +$(P +That alias makes the code easier to read: +) + +--- + CalculationFunc ptr = &myFunction; +--- + +$(P +As with any type, $(C auto) can be used as well: +) + +--- + auto ptr = &myFunction; +--- + +$(H6 Calling a function pointer) + +$(P +Function pointers can be called exactly like functions: +) + +--- + int result = $(HILITE ptr)('a', 5.67); + assert(result == 42); +--- + +$(P +The call $(C ptr('a', 5.67)) above is the equivalent of calling the actual function by $(C myFunction('a', 5.67)). +) + +$(H6 When to use) + +$(P +Because function pointers store what function to call and they are called exactly like the functions that they point at, function pointers effectively store the behavior of the program for later. +) + +$(P +There are many other features of D that are about program behavior. For example, the appropriate function to call to calculate the wages of an $(C Employee) can be determined by the value of an $(C enum) member: +) + +--- + final switch (employee.type) { + + case EmployeeType.fullTime: + fullTimeEmployeeWages(); + break; + + case EmployeeType.hourly: + hourlyEmployeeWages(); + break; + } +--- + +$(P +Unfortunately, that method is relatively harder to maintain because it obviously has to support all known employee types. If a new type of employee is added to the program, then all such $(C switch) statements must be located so that a new $(C case) clause is added for the new employee type. +) + +$(P +A more common alternative of implementing behavior differences is polymorphism. An $(C Employee) interface can be defined and different wage calculations can be handled by different implementations of that interface: +) + +--- +interface Employee { + double wages(); +} + +class FullTimeEmployee : Employee { + double wages() { + double result; + // ... + return result; + } +} + +class HourlyEmployee : Employee { + double wages() { + double result; + // ... + return result; + } +} + +// ... + + double result = employee.wages(); +--- + +$(P +Function pointers are yet another alternative for implementing different behavior. They are more common in programming languages that do not support object oriented programming. +) + +$(H6 Function pointer as a parameter) + +$(P +Let's design a function that takes an array and returns another array. This function will filter out elements with values less than or equal to zero, and multiply the others by ten: +) + +--- +$(CODE_NAME filterAndConvert)int[] filterAndConvert(const int[] numbers) { + int[] result; + + foreach (e; numbers) { + if (e > 0) { // filtering, + immutable newNumber = e * 10; // and conversion + result ~= newNumber; + } + } + + return result; +} +--- + +$(P +The following program demonstrates its behavior with randomly generated values: +) + +--- +$(CODE_XREF filterAndConvert)import std.stdio; +import std.random; + +void main() { + int[] numbers; + + // Random numbers + foreach (i; 0 .. 10) { + numbers ~= uniform(0, 10) - 5; + } + + writeln("input : ", numbers); + writeln("output: ", filterAndConvert(numbers)); +} +--- + +$(P +The output contains numbers that are ten times the original numbers, which were greater than zero to begin with. The original numbers that have been selected are highlighted: +) + +$(SHELL +input : [-2, $(HILITE 2), -2, $(HILITE 3), -2, $(HILITE 2), -1, -4, 0, 0] +output: [20, 30, 20] +) + +$(P +$(C filterAndConvert()) is for a very specific task: It always selects numbers that are greater than zero and always multiplies them by ten. It could be more useful if the behaviors of filtering and conversion were parameterized. +) + +$(P +Noting that filtering is a form of conversion as well (from $(C int) to $(C bool)), $(C filterAndConvert()) performs two conversions: +) + +$(UL +$(LI $(C number > 0), which produces $(C bool) by considering an $(C int) value.) +$(LI $(C number * 10), which produces $(C int) from an $(C int) value.) +) + +$(P +Let's define convenient aliases for function pointers that would match the two conversions above: +) + +--- +alias Predicate = bool function(int); // makes bool from int +alias Convertor = int function(int); // makes int from int +--- + +$(P +$(C Predicate) is the type of functions that take $(C int) and return $(C bool), and $(C Convertor) is the type of functions that take $(C int) and return $(C int). +) + +$(P +If we provide such function pointers as parameters, we can have $(C filterAndConvert()) use those function pointers during its work: +) + +--- +int[] filterAndConvert(const int[] numbers, + $(HILITE Predicate predicate), + $(HILITE Convertor convertor)) { + int[] result; + + foreach (number; numbers) { + if ($(HILITE predicate(number))) { + immutable newNumber = $(HILITE convertor(number)); + result ~= newNumber; + } + } + + return result; +} +--- + +$(P +$(C filterAndConvert()) is now an algorithm that is independent of the actual filtering and conversion operations. When desired, its earlier behavior can be achieved by the following two simple functions: +) + +--- +bool isGreaterThanZero(int number) { + return number > 0; +} + +int tenTimes(int number) { + return number * 10; +} + +// ... + + writeln("output: ", filterAndConvert(numbers, + $(HILITE &isGreaterThanZero), + $(HILITE &tenTimes))); +--- + +$(P +This design allows calling $(C filterAndConvert()) with any filtering and conversion behaviors. For example, the following two functions would make $(C filterAndConvert()) produce $(I the negatives of the even numbers): +) + +--- +bool isEven(int number) { + return (number % 2) == 0; +} + +int negativeOf(int number) { + return -number; +} + +// ... + + writeln("output: ", filterAndConvert(numbers, + &isEven, + &negativeOf)); +--- + +$(P +The output: +) + +$(SHELL +input : [3, -3, 2, 1, -5, 1, 2, 3, 4, -4] +output: [-2, -2, -4, 4] +) + +$(P +As seen in these examples, sometimes such functions are so trivial that defining them as proper functions with name, return type, parameter list, and curly brackets is unnecessarily wordy. +) + +$(P +As we will see below, the $(C =>) syntax of anonymous functions makes the code more concise and more readable. The following line has anonymous functions that are the equivalents of $(C isEven()) and $(C negativeOf()), without proper function definitions: +) + +--- + writeln("output: ", filterAndConvert(numbers, + number => (number % 2) == 0, + number => -number)); +--- + +$(H6 Function pointer as a member) + +$(P +Function pointers can be stored as members of structs and classes as well. To see this, let's design a $(C class) that takes the predicate and convertor as constructor parameters in order to use them later on: +) + +--- +class NumberHandler { + $(HILITE Predicate predicate); + $(HILITE Convertor convertor); + + this(Predicate predicate, Convertor convertor) { + this.predicate = predicate; + this.convertor = convertor; + } + + int[] handle(const int[] numbers) { + int[] result; + + foreach (number; numbers) { + if (predicate(number)) { + immutable newNumber = convertor(number); + result ~= newNumber; + } + } + + return result; + } +} +--- + +$(P +An object of that type can be used similarly to $(C filterAndConvert()): +) + +--- + auto handler = new NumberHandler($(HILITE &isEven), $(HILITE &negativeOf)); + writeln("result: ", handler.handle(numbers)); +--- + +$(H5 $(IX anonymous function) $(IX function, anonymous) $(IX function, lambda) $(IX function literal) $(IX literal, function) $(IX lambda) Anonymous functions) + +$(P +The code can be more readable and concise when short functions are defined without proper function definitions. +) + +$(P +Anonymous functions, which are also knows as $(I function literals) or $(I lambdas), allow defining functions inside of expressions. Anonymous functions can be used at any point where a function pointer can be used. +) + +$(P +We will get to their shorter $(C =>) syntax later below. Let's first see their full syntax, which is usually too wordy especially when it appears inside of other expressions: +) + +--- + function $(I return_type)($(I parameters)) { /* operations */ } +--- + +$(P +For example, an object of $(C NumberHandler) that produces $(I 7 times the numbers that are greater than 2) can be constructed by anonymous functions as in the following code: +) + +--- + new NumberHandler(function bool(int number) { return number > 2; }, + function int(int number) { return number * 7; }); +--- + +$(P +Two advantages of the code above is that the functions are not defined as proper functions and that their implementations are visible right where the $(C NumberHandler) object is constructed. +) + +$(P +Note that the anonymous function syntax is very similar to regular function syntax. Although this consistency has benefits, the full syntax of anonymous functions makes code too wordy. +) + +$(P +For that reason, there are various shorter ways of defining anonymous functions. +) + +$(H6 Shorter syntax) + +$(P +When the return type can be deduced from the $(C return) statement inside the anonymous function, then the return type need not be specified (The place where the return type would normally appear is highlighted by code comments.): +) + +--- + new NumberHandler(function /**/(int number) { return number > 2; }, + function /**/(int number) { return number * 7; }); +--- + +$(P +Further, when the anonymous function does not take parameters, its parameter list need not be provided. Let's consider a function that takes a function pointer that takes $(I nothing) and returns $(C double): +) + +--- +void foo(double function$(HILITE ()) func) { + // ... +} +--- + +$(P +Anonymous functions that are passed to that function need not have the empty parameter list. Therefore, all three of the following anonymous function syntaxes are equivalent: +) + +--- + foo(function double() { return 42.42; }); + foo(function () { return 42.42; }); + foo(function { return 42.42; }); +--- + +$(P +The first one is written in the full syntax. The second one omits the return type, taking advantage of the return type deduction. The third one omits the unnecessary parameter list. +) + +$(P +Even further, the keyword $(C function) need not be provided either. In that case it is left to the compiler to determine whether it is an anonymous function or an anonymous delegate. Unless it uses a variable from one of the enclosing scopes, it is a function: +) + +--- + foo({ return 42.42; }); +--- + +$(P +Most anonymous functions can be defined even shorter by the $(I lambda syntax). +) + +$(H6 $(IX =>) Lambda syntax instead of a single $(C return) statement) + +$(P +In most cases even the shortest syntax above is unnecessarily cluttered. The curly brackets that are just inside the function parameter list make the code harder to read and a $(C return) statement as well as its semicolon inside a function argument looks out of place. +) + +$(P +Let's start with the full syntax of an anonymous function that has a single $(C return) statement: +) + +--- + function $(I return_type)($(I parameters)) { return $(I expression); } +--- + +$(P +We have already seen that the $(C function) keyword is not necessary and the return type can be deduced: +) + +--- + ($(I parameters)) { return $(I expression); } +--- + +$(P +The equivalent of that definition is the following $(C =>) syntax, where the $(C =>) characters replace the curly brackets, the $(C return) keyword, and the semicolon: +) + +--- + ($(I parameters)) => $(I expression) +--- + +$(P +The meaning of that syntax can be spelled out as "given those parameters, produce this expression (value)". +) + +$(P +Further, when there is a single parameter, the parentheses around the parameter list can be omitted as well: +) + +--- + $(I single_parameter) => $(I expression) +--- + +$(P +On the other hand, to avoid grammar ambiguities, the parameter list must still be written as empty parentheses when there is no parameter at all: +) + +--- + () => $(I expression) +--- + +$(P +Programmers who know lambdas from other languages may make a mistake of using curly brackets after the $(C =>) characters, which can be valid D syntax with a different meaning: +) + +--- + // A lambda that returns 'a + 1' + auto l0 = (int a) => a + 1 + + // A lambda that returns a parameter-less lambda that + // returns 'a + 1' + auto l1 = (int a) => $(HILITE {) return a + 1; $(HILITE }) + + assert(l0(42) == 43); + assert(l1(42)$(HILITE ()) == 43); // Executing what l1 returns +--- + +$(P +$(IX filter, std.algorithm) Let's use the lambda syntax in a predicate passed to $(C std.algorithm.filter). $(C filter()) takes a predicate as its template parameter and a range as its function parameter. It applies the predicate to each element of the range and returns the ones that satisfy the predicate. One of several ways of specifying the predicate is the lambda syntax. +) + +$(P +($(I Note: We will see ranges in a later chapter. At this point, it should be sufficient to know that D slices are ranges.)) +) + +$(P +The following lambda is a predicate that matches elements that are greater than 10: +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] numbers = [ 20, 1, 10, 300, -2 ]; + writeln(numbers.filter!($(HILITE number => number > 10))); +} +--- + +$(P +The output contains only the elements that satisfy the predicate: +) + +$(SHELL +[20, 300] +) + +$(P +For comparison, let's write the same lambda in the longest syntax. The curly brackets that define the body of the anonymous function are highlighted: +) + +--- + writeln(numbers.filter!(function bool(int number) $(HILITE {) + return number > 10; + $(HILITE }))); +--- + +$(P +As another example, this time let's define an anonymous function that takes two parameters. The following algorithm takes two slices and passes their corresponding elements one by one to a $(C function) that itself takes two parameters. It then collects and returns the results as another slice: +) + +--- +$(CODE_NAME binaryAlgorithm)import std.exception; + +int[] binaryAlgorithm(int function$(HILITE (int, int)) func, + const int[] slice1, + const int[] slice2) { + enforce(slice1.length == slice2.length); + + int[] results; + + foreach (i; 0 .. slice1.length) { + results ~= func(slice1[i], slice2[i]); + } + + return results; +} +--- + +$(P +Since the $(C function) parameter above takes two parameters, lambdas that can be passed to $(C binaryAlgorithm()) must take two parameters as well: +) + +--- +$(CODE_XREF binaryAlgorithm)import std.stdio; + +void main() { + writeln(binaryAlgorithm($(HILITE (a, b)) => (a * 10) + b, + [ 1, 2, 3 ], + [ 4, 5, 6 ])); +} +--- + +$(P +The output contains ten times the elements of the first array plus the elements of the second array (e.g. 14 is 10 * 1 + 4): +) + +$(SHELL +[14, 25, 36] +) + +$(H5 $(IX delegate) Delegates) + +$(P +$(IX context) $(IX closure) A delegate is a combination of a function pointer and the context that it should be executed in. Delegates also support $(I closures) in D. Closures are a feature supported by many functional programming languages. +) + +$(P +As we have seen in $(LINK2 /ders/d.en/lifetimes.html, the Lifetimes and Fundamental Operations chapter), the lifetime of a variable ends upon leaving the scope that it is defined in: +) + +--- +{ + int increment = 10; + // ... +} // ← the life of 'increment' ends here +--- + +$(P +That is why the address of such a local variable cannot be returned from a function. +) + +$(P +Let's imagine that $(C increment) is a local variable of a function that itself returns a $(C function). Let's make it so that the returned lambda happens to use that local variable: +) + +--- +alias Calculator = int function(int); + +Calculator makeCalculator() { + int increment = 10; + return value => $(HILITE increment) + value; $(DERLEME_HATASI) +} +--- + +$(P +That code is in error because the returned lambda makes use of a local variable that is about to go out of scope. If the code were allowed to compile, the lambda would be trying to access $(C increment), whose life has already ended. +) + +$(P +For that code to be compiled and work correctly, the lifetime of $(C increment) must at least be as long as the lifetime of the lambda that uses it. Delegates extend the lifetime of the context of a lambda so that the local state that the function uses remains valid. +) + +$(P +$(C delegate) syntax is similar to $(C function) syntax, the only difference being the keyword. That change is sufficient to make the previous code compile: +) + +--- +alias Calculator = int $(HILITE delegate)(int); + +Calculator makeCalculator() { + int increment = 10; + return value => increment + value; +} +--- + +$(P +Having been used by a delegate, the local variable $(C increment) will now live as long as that delegate lives. The variable is available to the delegate just as any other variable would be, mutable as needed. We will see examples of this in the next chapter when using delegates with $(C opApply()) member functions. +) + +$(P +The following is a test of the delegate above: +) + +--- + auto calculator = makeCalculator(); + writeln("The result of the calculation: ", calculator(3)); +--- + +$(P +Note that $(C makeCalculator()) returns an anonymous delegate. The code above assigns that delegate to the variable $(C calculator) and then calls it by $(C calculator(3)). Since the delegate is implemented to return the sum of its parameter and the variable $(C increment), the code outputs the sum of 3 and 10: +) + +$(SHELL +The result of the calculation: 13 +) + +$(H6 Shorter syntax) + +$(P +As we have already used in the previous example, delegates can take advantage of the shorter syntaxes as well. When neither $(C function) nor $(C delegate) is specified, the type of the lambda is decided by the compiler, depending on whether the lambda accesses local state. If so, then it is a $(C delegate). +) + +$(P +The following example has a delegate that does not take any parameters: +) + +--- +int[] delimitedNumbers(int count, int delegate$(HILITE ()) numberGenerator) { + int[] result = [ -1 ]; + result.reserve(count + 2); + + foreach (i; 0 .. count) { + result ~= numberGenerator(); + } + + result ~= -1; + + return result; +} +--- + +$(P +The function $(C delimitedNumbers()) generates a slice where the first and last elements are -1. It takes two parameters that specify the other elements that come between those first and last elements. +) + +$(P +Let's call that function with a trivial delegate that always returns the same value. Remember that when there is no parameter, the parameter list of a lambda must be specified as empty: +) + +--- + writeln(delimitedNumbers(3, $(HILITE () => 42))); +--- + +$(P +The output: +) + +$(SHELL +-1 42 42 42 -1 +) + +$(P +Let's call $(C delimitedNumbers()) this time with a delegate that makes use of a local variable: +) + +--- + int lastNumber; + writeln(delimitedNumbers( + 15, $(HILITE () => lastNumber += uniform(0, 3)))); + + writeln("Last number: ", lastNumber); +--- + +$(P +Although that delegate produces a random value, since the value is added to the last one, none of the generated values is less than its predecessor: +) + +$(SHELL +-1 0 2 3 4 6 6 8 9 9 9 10 12 14 15 17 -1 +Last number: 17 +) + +$(H6 $(IX &, object delegate) $(IX delegate, member function) $(IX object delegate) $(IX member function delegate) An object and a member function as a delegate) + +$(P +We have seen that a delegate is nothing but a function pointer and the context that it is to be executed in. Instead of those two, a delegate can also be composed of a member function and an existing object that that member function is to be called on. +) + +$(P +The syntax that defines such a delegate from an object is the following: +) + +--- + &$(I object).$(I member_function) +--- + +$(P +Let's first observe that such a syntax indeed defines a $(C delegate) by printing its $(C string) representation: +) + +--- +import std.stdio; + +struct Location { + long x; + long y; + + void moveHorizontally(long step) { x += step; } + void moveVertically(long step) { y += step; } +} + +void main() { + auto location = Location(); + writeln(typeof($(HILITE &location.moveHorizontally)).stringof); +} +--- + +$(P +According to the output, the type of $(C moveHorizontally()) called on $(C location) is indeed a $(C delegate): +) + +$(SHELL +void delegate(long step) +) + +$(P +Note that the $(C &) syntax is only for constructing the delegate. The delegate will be called later by the function call syntax: +) + +--- + // The definition of the delegate variable: + auto directionFunction = &location.moveHorizontally; + + // Calling the delegate by the function call syntax: + directionFunction$(HILITE (3)); + + writeln(location); +--- + +$(P +Since the $(C delegate) combines the $(C location) object and the $(C moveHorizontally()) member function, calling the delegate is the equivalent of calling $(C moveHorizontally()) on $(C location). The output indicates that the object has indeed moved 3 steps horizontally: +) + +$(SHELL +Location(3, 0) +) + +$(P +Function pointers, lambdas, and delegates are expressions. They can be used in places where a value of their type is expected. For example, a slice of $(C delegate) objects is initialized below from delegates constructed from an object and its various member functions. The $(C delegate) elements of the slice are later called just like functions: +) + +--- + auto location = Location(); + + void delegate(long)[] movements = + [ &location.moveHorizontally, + &location.moveVertically, + &location.moveHorizontally ]; + + foreach (movement; movements) { + movement$(HILITE (1)); + } + + writeln(location); +--- + +$(P +According to the elements of the slice, the location has been changed twice horizontally and once vertically: +) + +$(SHELL +Location(2, 1) +) + +$(H6 $(IX .ptr, delegate context) $(IX pointer, delegate context) $(IX .funcptr) $(IX function pointer, delegate) Delegate properties) + +$(P +The function and context pointers of a delegate can be accessed through its $(C .funcptr) and $(C .ptr) properties, respectively: +) + +--- +struct MyStruct { + void func() { + } +} + +void main() { + auto o = MyStruct(); + + auto d = &o.func; + + assert(d$(HILITE .funcptr) == &MyStruct.func); + assert(d$(HILITE .ptr) == &o); +} +--- + +$(P +It is possible to make a $(C delegate) from scratch by setting those properties explicitly: +) + +--- +struct MyStruct { + int i; + + void func() { + import std.stdio; + writeln(i); + } +} + +void main() { + auto o = MyStruct(42); + + void delegate() d; + assert(d is null); // null to begin with + + d$(HILITE .funcptr) = &MyStruct.func; + d$(HILITE .ptr) = &o; + + $(HILITE d()); +} +--- + +$(P +Calling the delegate above as $(C d()) is the equivalent of the expression $(C o.func()) (i.e. calling $(C MyStruct.func) on $(C o)): +) + +$(SHELL +42 +) + +$(H6 $(IX lazy parameter as delegate) Lazy parameters are delegates) + +$(P +We saw the $(C lazy) keyword in $(LINK2 /ders/d.en/function_parameters.html, the Function Parameters chapter): +) + +--- +void log(Level level, $(HILITE lazy) string message) { + if (level >= interestedLevel) { + writefln("%s", message); + } +} + +// ... + + if (failedToConnect) { + log(Level.medium, + $(HILITE format)("Failure. The connection state is '%s'.", + getConnectionState())); + } +--- + +$(P +Because $(C message) is a $(C lazy) parameter above, the entire $(C format) expression (including the $(C getConnectionState()) call that it makes) would be evaluated if and when that parameter is used inside $(C log()). +) + +$(P +Behind the scenes, lazy parameters are in fact delegates and the arguments that are passed to lazy parameters are delegate objects that are created automatically by the compiler. The code below is the equivalent of the one above: +) + +--- +void log(Level level, string $(HILITE delegate)() lazyMessage) { // (1) + if (level >= interestedLevel) { + writefln("%s", $(HILITE lazyMessage())); // (2) + } +} + +// ... + + if (failedToConnect) { + log(Level.medium, + delegate string() $(HILITE {) // (3) + return format( + "Failure. The connection state is '%s'.", + getConnectionState()); + $(HILITE })); + } +--- + +$(OL + +$(LI The $(C lazy) parameter is not a $(C string) but a delegate that returns a $(C string).) + +$(LI The delegate is called to get its return value.) + +$(LI The entire expression is wrapped inside a delegate and returned from it.) + +) + +$(H6 $(IX lazy variadic functions) $(IX variadic function, lazy) Lazy variadic functions) + +$(P +When a function needs a variable number of lazy parameters, it is necessarily impossible to specify those $(I unknown number of) parameters as $(C lazy). +) + +$(P +The solution is to use variadic $(C delegate) parameters. Such parameters can receive any number of expressions that are the same as the $(I return type) of those delegates. The delegates cannot take parameters: +) + +--- +import std.stdio; + +void foo(double delegate()$(HILITE []) args$(HILITE ...)) { + foreach (arg; args) { + writeln($(HILITE arg())); // Calling each delegate + } +} + +void main() { + foo($(HILITE 1.5), () => 2.5); // 'double' passed as delegate +} +--- + +$(P +Note how both a $(C double) expression and a lambda are matched to the variadic parameter. The $(C double) expression is automatically wrapped inside a delegate and the function prints the values of all its $(I effectively-lazy) parameters: +) + +$(SHELL +1.5 +2.5 +) + +$(P +A limitation of this method is that all parameters must be the same type ($(C double) above). We will see later in the $(LINK2 /ders/d.en/templates_more.html, More Templates chapter) how to take advantage of $(I tuple template parameters) to remove that limitation. +) + +$(H5 $(IX sink) $(IX toString, delegate) $(C toString()) with a $(C delegate) parameter) + +$(P +We have defined many $(C toString()) functions up to this point in the book to represent objects as strings. Those $(C toString()) definitions all returned a $(C string) without taking any parameters. As noted by the comment lines below, structs and classes took advantage of $(C toString()) functions of their respective members by simply passing those members to $(C format()): +) + +--- +import std.stdio; +import std.string; + +struct Point { + int x; + int y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} + +struct Color { + ubyte r; + ubyte g; + ubyte b; + + string toString() const { + return format("RGB:%s,%s,%s", r, g, b); + } +} + +struct ColoredPoint { + Color color; + Point point; + + string toString() const { + /* Taking advantage of Color.toString and + * Point.toString: */ + return format("{%s;%s}", color, point); + } +} + +struct Polygon { + ColoredPoint[] points; + + string toString() const { + /* Taking advantage of ColoredPoint.toString: */ + return format("%s", points); + } +} + +void main() { + auto polygon = Polygon( + [ ColoredPoint(Color(10, 10, 10), Point(1, 1)), + ColoredPoint(Color(20, 20, 20), Point(2, 2)), + ColoredPoint(Color(30, 30, 30), Point(3, 3)) ]); + + writeln(polygon); +} +--- + +$(P +In order for $(C polygon) to be sent to the output as a $(C string) on the last line of the program, all of the $(C toString()) functions of $(C Polygon), $(C ColoredPoint), $(C Color), and $(C Point) are called indirectly, creating a total of 10 strings in the process. Note that the strings that are constructed and returned by the lower-level functions are used only once by the respective higher-level function that called them. +) + +$(P +However, although a total of 10 strings get constructed, only the very last one is printed to the output: +) + +$(SHELL +[{RGB:10,10,10;(1,1)}, {RGB:20,20,20;(2,2)}, {RGB:30,30,30;(3,3)}] +) + +$(P +However practical, this method may degrade the performance of the program because of the many $(C string) objects that are constructed and promptly thrown away. +) + +$(P +An overload of $(C toString()) avoids this performance issue by taking a $(C delegate) parameter: +) + +--- + void toString(void delegate(const(char)[]) sink) const; +--- + +$(P +As seen in its declaration, this overload of $(C toString()) does not return a $(C string). Instead, the characters that are going to be printed are passed to its $(C delegate) parameter. It is the responsibility of the $(C delegate) to append those characters to the single $(C string) that is going to be printed to the output. +) + +$(P +$(IX formattedWrite, std.format) All the programmer needs to do differently is to call $(C std.format.formattedWrite) instead of $(C std.string.format) and pass the $(C delegate) parameter as its first parameter (in UFCS below). Also note that the following calls are providing the format strings as template arguments to take advantage of $(C formattedWrite)'s compile-time format string checks. +) + +--- +import std.stdio; +$(HILITE import std.format;) + +struct Point { + int x; + int y; + + void toString(void delegate(const(char)[]) sink) const { + sink.$(HILITE formattedWrite)!"(%s,%s)"(x, y); + } +} + +struct Color { + ubyte r; + ubyte g; + ubyte b; + + void toString(void delegate(const(char)[]) sink) const { + sink.$(HILITE formattedWrite)!"RGB:%s,%s,%s"(r, g, b); + } +} + +struct ColoredPoint { + Color color; + Point point; + + void toString(void delegate(const(char)[]) sink) const { + sink.$(HILITE formattedWrite)!"{%s;%s}"(color, point); + } +} + +struct Polygon { + ColoredPoint[] points; + + void toString(void delegate(const(char)[]) sink) const { + sink.$(HILITE formattedWrite)!"%s"(points); + } +} + +void main() { + auto polygon = Polygon( + [ ColoredPoint(Color(10, 10, 10), Point(1, 1)), + ColoredPoint(Color(20, 20, 20), Point(2, 2)), + ColoredPoint(Color(30, 30, 30), Point(3, 3)) ]); + + writeln(polygon); +} +--- + +$(P +The advantage of this program is that, even though there are still a total of 10 calls made to various $(C toString()) functions, those calls collectively produce a single $(C string), not 10. +) + +$(H5 Summary) + +$(UL +$(LI The $(C function) keyword is for defining function pointers to be called later just like a function.) + +$(LI The $(C delegate) keyword is for defining delegates. A delegate is the pair of a function pointer and the context that that function pointer to be executed in.) + +$(LI A $(C delegate) can be created from an object and a member function by the syntax $(C &object.member_function).) + +$(LI A delegate can be constructed explicitly by setting its $(C .funcptr) and $(C .ptr) properties.) + +$(LI Anonymous functions and anonymous delegates (lambdas) can be used in places of function pointers and delegates in expressions.) + +$(LI There are several syntaxes for lambdas, the shortest of which is for when the equivalent consists only of a single $(C return) statement: $(C parameter => expression).) + +$(LI A more efficient overload of $(C toString()) takes a $(C delegate).) + +) + +Macros: + SUBTITLE=Function Pointers, Delegates, and Lambdas + + DESCRIPTION=C-like function pointers, delegates that are commonly found in functional programming languages, and anonymous functions (lambdas). + + KEYWORDS=d programming language tutorial book function pointer delegate anonymous function lambda diff --git a/target/lazy_operators.d b/target/lazy_operators.d new file mode 100644 index 0000000..4b60959 --- /dev/null +++ b/target/lazy_operators.d @@ -0,0 +1,92 @@ +Ddoc + +$(DERS_BOLUMU $(IX lazy evaluation) Lazy Operators) + +$(P +Lazy evaluation is the delaying of the execution of expressions until the results of those expressions are needed. Lazy evaluation is among the fundamental features of some programming languages. +) + +$(P +Naturally, this delaying may make programs run faster if the results end up not being needed. +) + +$(P +$(IX short-circuit evaluation) A concept that is similar to lazy evaluation is the short-circuit behavior of the following operators: +) + +$(UL + +$(LI $(IX ||, short-circuit) $(IX logical or operator) $(C ||) ($(I or)) operator: The second expression is evaluated only if the first expression is $(C false). + +--- + if (anExpression() || mayNotBeEvaluated()) { + // ... + } +--- + +$(P +If the result of $(C anExpression()) is $(C true) then the result of the $(C ||) expression is necessarily $(C true). Since we no longer need to evaluate the second expression to determine the result of the $(C ||) expression the second expression is not evaluated. +) + +) + +$(LI $(IX &&, short-circuit) $(IX logical and operator) $(C &&) ($(I and)) operator: The second expression is evaluated only if the first expression is $(C true). + +--- + if (anExpression() && mayNotBeEvaluated()) { + // ... + } +--- + +$(P +If the result of $(C anExpression()) is $(C false) then the result of the $(C &&) expression is necessarily $(C false), so the second expression is not evaluated. +) + +) + +$(LI $(IX ?:, short-circuit) $(IX ternary operator) $(IX conditional operator) $(C ?:) ($(I ternary)) operator: Either the first or the second expression is evaluated, depending on whether the condition is $(C true) or $(C false), respectively. + +--- + int i = condition() ? eitherThis() : orThis(); +--- + +) + +) + +$(P +The laziness of these operators matters not only to performance. Sometimes, evaluating one of the expressions can be an error. +) + +$(P +For example, the $(I is the first letter an A) condition check below would be an error when the string is empty: +) + +--- + dstring s; + // ... + if (s[0] == 'A') { + // ... + } +--- + +$(P +In order to access the first element of $(C s), we must first ensure that the string does have such an element. For that reason, the following condition check moves that potentially erroneous logical expression to the right-hand side of the $(C &&) operator, to ensure that it will be evaluated only when it is safe to do so: +) + +--- + if ((s.length >= 1) && (s[0] == 'A')) { + // ... + } +--- + +$(P +Lazy evaluations can be achieved by using $(LINK2 /ders/d.en/lambda.html, function pointers, delegates), and $(LINK2 /ders/d.en/ranges.html, ranges) as well. We will see these in later chapters. +) + +Macros: + SUBTITLE=Lazy Operators + + DESCRIPTION=The lazy (short-circuit) operators of the D programming language. + + KEYWORDS=d programming language tutorial book lazy diff --git a/target/lifetimes.d b/target/lifetimes.d new file mode 100644 index 0000000..0cd7a3e --- /dev/null +++ b/target/lifetimes.d @@ -0,0 +1,341 @@ +Ddoc + +$(DERS_BOLUMU $(IX lifetime) Lifetimes and Fundamental Operations) + +$(P +We will soon cover structs, the basic feature that allows the programmer to define application-specific types. Structs are for combining fundamental types and other structs together to define higher-level types that behave according to special needs of programs. After structs, we will learn about classes, which are the basis of the object oriented programming features of D. +) + +$(P +Before getting to structs and classes, it will be better to talk about some important concepts first. These concepts will help understand structs and classes and some of their differences. +) + +$(P +We have been calling any piece of data that represented a concept in a program a $(I variable). In a few places we have referred to struct and class variables specifically as $(I objects). I will continue calling both of these concepts variables in this chapter. +) + +$(P +Although this chapter includes only fundamental types, slices, and associative arrays; these concepts apply to user-defined types as well. +) + +$(H5 $(IX variable, lifetime) Lifetime of a variable) + +$(P +The time between when a variable is defined and when it is $(I finalized) is the lifetime of that variable. Although it is the case for many types, $(I becoming unavailable) and $(I being finalized) need not be at the same time. +) + +$(P +You would remember from the $(LINK2 /ders/d.en/name_space.html, Name Scope chapter) how variables become unavailable. In simple cases, exiting the scope where a variable was defined would render that variable unavailable. +) + +$(P +Let's consider the following example as a reminder: +) + +--- +void speedTest() { + int speed; // Single variable ... + + foreach (i; 0 .. 10) { + speed = 100 + i; // ... takes 10 different values. + // ... + } +} // ← 'speed' is unavailable beyond this point. +--- + +$(P +The lifetime of the $(C speed) variable in that code ends upon exiting the $(C speedTest()) function. There is a single variable in the code above, which takes ten different values from 100 to 109. +) + +$(P +When it comes to variable lifetimes, the following code is very different compared to the previous one: +) + +--- +void speedTest() { + foreach (i; 0 .. 10) { + int speed = 100 + i; // Ten separate variables. + // ... + } // ← Lifetime of each variable ends here. +} +--- + +$(P +There are ten separate variables in that code, each taking a single value. Upon every iteration of the loop, a new variable starts its life, which eventually ends at the end of each iteration. +) + +$(H5 $(IX parameter, lifetime) Lifetime of a parameter) + +$(P +The lifetime of a parameter depends on its qualifiers: +) + +$(P +$(IX ref, parameter lifetime) $(C ref): The parameter is just an alias of the actual variable that is specified when calling the function. $(C ref) parameters do not affect the lifetimes of actual variables. +) + +$(P +$(IX in, parameter lifetime) $(C in): For $(I value types), the lifetime of the parameter starts upon entering the function and ends upon exiting it. For $(I reference types), the lifetime of the parameter is the same as with $(C ref). +) + +$(P +$(IX out, parameter lifetime) $(C out): Same with $(C ref), the parameter is just an alias of the actual variable that is specified when calling the function. The only difference is that the variable is set to its $(C .init) value automatically upon entering the function. +) + +$(P +$(IX lazy, parameter lifetime) $(C lazy): The life of the parameter starts when the parameter is actually used and ends right then. +) + +$(P +The following example uses these four types of parameters and explains their lifetimes in program comments: +) + +--- +void main() { + int main_in; /* The value of main_in is copied to the + * parameter. */ + + int main_ref; /* main_ref is passed to the function as + * itself. */ + + int main_out; /* main_out is passed to the function as + * itself. Its value is set to int.init + * upon entering the function. */ + + foo(main_in, main_ref, main_out, aCalculation()); +} + +void foo( + in int p_in, /* The lifetime of p_in starts upon + * entering the function and ends upon + * exiting the function. */ + + ref int p_ref, /* p_ref is an alias of main_ref. */ + + out int p_out, /* p_out is an alias of main_out. Its + * value is set to int.init upon + * entering the function. */ + + lazy int p_lazy) { /* The lifetime of p_lazy starts when + * it is used and ends when its use + * ends. Its value is calculated by + * calling aCalculation() every time + * p_lazy is used in the function. */ + // ... +} + +int aCalculation() { + int result; + // ... + return result; +} +--- + +$(H5 Fundamental operations) + +$(P +Regardless of its type, there are three fundamental operations throughout the lifetime of a variable: +) + +$(UL +$(LI $(B Initialization): The start of its life.) +$(LI $(B Finalization): The end of its life.) +$(LI $(B Assignment): Changing its value as a whole.) +) + +$(P +To be considered an object, it must first be initialized. There may be final operations for some types. The value of a variable may change during its lifetime. +) + +$(H6 $(IX initialization) Initialization) + +$(P +Every variable must be initialized before being used. Initialization involves two steps: +) + +$(OL + +$(LI $(B Reserving space for the variable): This space is where the value of the variable is stored in memory.) + +$(LI $(B Construction): Setting the first value of the variable on that space (or the first values of the members of structs and classes).) + +) + +$(P +Every variable lives in a place in memory that is reserved for it. Some of the code that the compiler generates is about reserving space for each variable. +) + +$(P +Let's consider the following variable: +) + +--- + int speed = 123; +--- + +$(P +As we have seen in $(LINK2 /ders/d.en/value_vs_reference.html, the Value Types and Reference Types chapter), we can imagine this variable living on some part of the memory: +) + +$(MONO + ──┬─────┬─────┬─────┬── + │ │ 123 │ │ + ──┴─────┴─────┴─────┴── +) + +$(P +The memory location that a variable is placed at is called its address. In a sense, the variable lives at that address. When the value of a variable is changed, the new value is stored at the same place: +) + +--- + ++speed; +--- + +$(P +The new value would be at the same place where the old value has been: +) + +$(MONO + ──┬─────┬─────┬─────┬── + │ │ 124 │ │ + ──┴─────┴─────┴─────┴── +) + +$(P +Construction is necessary to prepare variables for use. Since a variable cannot be used reliably before being constructed, it is performed by the compiler automatically. +) + +$(P +Variables can be constructed in three ways: +) + +$(UL +$(LI $(B By their default value): when the programmer does not specify a value explicitly) +$(LI $(B By copying): when the variable is constructed as a copy of another variable of the same type) +$(LI $(B By a specific value): when the programmer specifies a value explicitly) +) + +$(P +When a value is not specified, the value of the variable would be the $(I default) value of its type, i.e. its $(C .init) value. +) + +--- + int speed; +--- + +$(P +The value of $(C speed) above is $(C int.init), which happens to be zero. Naturally, a variable that is constructed by its default value may have other values during its lifetime (unless it is $(C immutable)). +) + +--- + File file; +--- + +$(P +With the definition above, the variable $(C file) is a $(C File) object that is not yet associated with an actual file on the file system. It is not usable until it is modified to be associated with a file. +) + +$(P +Variables are sometimes constructed as a copy of another variable: +) + +--- + int speed = otherSpeed; +--- + +$(P +$(C speed) above is constructed by the value of $(C otherSpeed). +) + +$(P +As we will see in later chapters, this operation has a different meaning for class variables: +) + +--- + auto classVariable = otherClassVariable; +--- + +$(P +Although $(C classVariable) starts its life as a copy of $(C otherClassVariable), there is a fundamental difference with classes: Although $(C speed) and $(C otherSpeed) are distinct values, $(C classVariable) and $(C otherClassVariable) both provide access to the same value. This is the fundamental difference between value types and reference types. +) + +$(P +Finally, variables can be constructed by the value of an expression of a compatible type: +) + +--- + int speed = someCalculation(); +--- + +$(P +$(C speed) above would be constructed by the return value of $(C someCalculation()). +) + +$(H6 $(IX finalization) $(IX destruction) Finalization) + +$(P +Finalizing is the final operations that are executed for a variable and reclaiming its memory: +) + +$(OL +$(LI $(B Destruction): The final operations that must be executed for the variable.) +$(LI $(B Reclaiming the variable's memory): Reclaiming the piece of memory that the variable has been living on.) +) + +$(P +For simple fundamental types, there are no final operations to execute. For example, the value of a variable of type $(C int) is not set back to zero. For such variables there is only reclaiming their memory, so that it will be used for other variables later. +) + +$(P +On the other hand, some types of variables require special operations during finalization. For example, a $(C File) object would need to write the characters that are still in its output buffer to disk and notify the file system that it no longer uses the file. These operations are the destruction of a $(C File) object. +) + +$(P +Final operations of arrays are at a little higher-level: Before finalizing the array, first its elements are destructed. If the elements are of a simple fundamental type like $(C int), then there are no special final operations for them. If the elements are of a struct or a class type that needs finalization, then those operations are executed for each element. +) + +$(P +Associative arrays are similar to arrays. Additionally, the keys may also be finalized if they are of a type that needs destruction. +) + +$(P $(B The garbage collector:) D is a $(I garbage-collected) language. In such languages finalizing an object need not be initiated explicitly by the programmer. When a variable's lifetime ends, its finalization is automatically handled by the garbage collector. We will cover the garbage collector and special memory management in $(LINK2 /ders/d.en/memory.html, a later chapter). +) + +$(P +Variables can be finalized in two ways: +) + +$(UL +$(LI $(B When the lifetime ends): Finalization happens at the end of the variable's life.) +$(LI $(B Some time in the future): Finalization happens at an indeterminate time in the future by the garbage collector.) +) + +$(P +Which of the two ways a variable will be finalized depends primarily on its type. Some types like arrays, associative arrays and classes are normally destructed by the garbage collector some time in the future. +) + +$(H6 $(IX assignment) Assignment) + +$(P +The other fundamental operation that a variable experiences during its lifetime is assignment. +) + +$(P +For simple fundamental types assignment is merely changing the value of the variable. As we have seen above on the memory representation, an $(C int) variable would start having the value 124 instead of 123. However, more generally, assignment consists of two steps, which are not necessarily executed in the following order: +) + +$(UL +$(LI $(B Destructing the old value)) +$(LI $(B Constructing the new value)) +) + +$(P +These two steps are not important for simple fundamental types that don't need destruction. For types that need destruction, it is important to remember that assignment is a combination of the two steps above. +) + +Macros: + SUBTITLE=Lifetimes and Fundamental Operations + + DESCRIPTION=Introducing the concepts of initialization, finalization, construction, destruction, and assignment and defining the lifetimes of variables. + + KEYWORDS=d programming lesson book tutorial constructor destructor diff --git a/target/literals.cozum.d b/target/literals.cozum.d new file mode 100644 index 0000000..cc51868 --- /dev/null +++ b/target/literals.cozum.d @@ -0,0 +1,76 @@ +Ddoc + +$(COZUM_BOLUMU Literals) + +$(OL + +$(LI +The problem here is that the value on the right-hand side is too large to fit in an $(C int). According to the rules about integer literals, its type is $(C long). For that reason it doesn't fit the type of the variable on the left-hand side. There are at least two solutions. + +$(P +One solution is to leave the type of the variable to the compiler for example by the $(C auto) keyword: +) + +--- + auto amount = 10_000_000_000; +--- + +$(P +The type of $(C amount) would be deduced to be $(C long) from its initial value from the right-hand side. +) + +$(P +Another solution is to make the type of the variable $(C long) as well: +) + +--- + long amount = 10_000_000_000; +--- + +) + +$(LI +We can take advantage of the special $(C '\r') character that takes the printing to the beginning of the line. + +--- +import std.stdio; + +void main() { + for (int number = 0; ; ++number) { + write("\rNumber: ", number); + } +} +--- + +$(P +The output of that program may be erratic due to its interactions with the output buffer. The following program flushes the output buffer and waits for 10 millisecond after each write: +) + +--- +import std.stdio; +import core.thread; + +void main() { + for (int number = 0; ; ++number) { + write("\rNumber: ", number); + stdout.flush(); + Thread.sleep(10.msecs); + } +} +--- + +$(P +Flushing the output is normally not necessary as it is flushed automatically before getting to the next line e.g. by $(C writeln), or before reading from $(C stdin). +) + +) + +) + + +Macros: + SUBTITLE=Literals Solutions + + DESCRIPTION=Programming in D exercise solutions: Literals + + KEYWORDS=programming in d tutorial literals solution diff --git a/target/literals.d b/target/literals.d new file mode 100644 index 0000000..d63f3c3 --- /dev/null +++ b/target/literals.d @@ -0,0 +1,384 @@ +Ddoc + +$(DERS_BOLUMU $(IX literal) Literals) + +$(P +Programs achieve their tasks by manipulating the values of variables and objects. They produce new values and new objects by using them with functions and operators. +) + +$(P +Some values need not be produced during the execution of the program; they are instead written directly into the source code. For example, the floating point value $(C 0.75) and the $(C string) value $(STRING "Total price: ") below are not calculated by the program: +) + +--- + discountedPrice = actualPrice * 0.75; + totalPrice += count * discountedPrice; + writeln("Total price: ", totalPrice); +--- + +$(P +Such values that are directly typed into the source code are called literals. We have used many literals in the programs that we have written so far. We will cover all of the types of literals and their syntax rules. +) + +$(H5 Integer literals) + +$(P +Integer literals can be written in one of four ways: the decimal system that we use in our daily lives; the hexadecimal and binary systems, which are more suitable for certain computing tasks; and the octal system, which may be needed in very rare cases. +) + +$(P +In order to make the code more readable, it is possible to insert $(C _) characters anywhere after the first digit of integer literals. For example, we can use it to form groups of three digits, as in $(C 1_234_567). Another example would be if we measured some value in cents of a currency, and used it to separate the currency units from the cents, as in $(C 199_99). These characters are optional; they are ignored by the compiler. +) + +$(P +$(B In the decimal system:) The literals are specified by the decimal numerals in exactly the same way as we are used to in our daily lives, such as $(C 12). When using the decimal system in D the first digit cannot be $(C 0). Such a leading zero digit is often used in other programming languages to indicate the octal system, so this constraint helps to prevent bugs that are caused by this easily overlooked difference. This does not preclude $(C 0) on its own: $(C 0) is zero. +) + +$(P +$(B In the hexadecimal system:) The literals start with $(C 0x) or $(C 0X) and include the numerals of the hexadecimal system: "0123456789abcdef" and "ABCDEF" as in $(C 0x12ab00fe). +) + +$(P +$(B In the octal system:) The literals are specified using the $(C octal) template from the $(C std.conv) module and include the numerals of the octal system: "01234567" as in $(C octal!576). +) + +$(P +$(B In the binary system:) The literals start with $(C 0b) or $(C 0B) and include the numerals of the binary system: 0 and 1 as in $(C 0b01100011). +) + +$(H6 The types of integer literals) + +$(P +Just like any other value, every literal is of a certain type. The types of literals are not specified explicitly as $(C int), $(C double), etc. The compiler infers the type from the value and syntax of the literal itself. +) + +$(P +Although most of the time the types of literals are not important, sometimes the types may not match the expressions that they are used in. In such cases the type must be explicitly specified. +) + +$(P +By default, integer literals are inferred to be of type $(C int). When the value happens to be too large to be represented by an $(C int), the compiler uses the following logic to decide on the type of the literal: +) + +$(UL + +$(LI If the value of the literal does not fit an $(C int) and it is specified in the decimal system, then its type is $(C long). +) + +$(LI If the value of the literal does not fit an $(C int) and it is specified in any other system, then the type becomes the first of the following types that can accomodate the value: $(C uint), $(C long), and $(C ulong). +) + +) + +$(P +To see this logic in action, let's try the following program that takes advantage of $(C typeof) and $(C stringof): +) + +--- +import std.stdio; + +void main() { + writeln("\n--- these are written in decimal ---"); + + // fits an int, so the type is int + writeln( 2_147_483_647, "\t\t", + typeof(2_147_483_647).stringof); + + // does not fit an int and is decimal, so the type is long + writeln( 2_147_483_648, "\t\t", + typeof(2_147_483_648).stringof); + + writeln("\n--- these are NOT written in decimal ---"); + + // fits an int, so the type is int + writeln( 0x7FFF_FFFF, "\t\t", + typeof(0x7FFF_FFFF).stringof); + + // does not fit an int and is not decimal, so the type is uint + writeln( 0x8000_0000, "\t\t", + typeof(0x8000_0000).stringof); + + // does not fit a uint and is not decimal, so the type is long + writeln( 0x1_0000_0000, "\t\t", + typeof(0x1_0000_0000).stringof); + + // does not fit a long and is not decimal, so the type is ulong + writeln( 0x8000_0000_0000_0000, "\t\t", + typeof(0x8000_0000_0000_0000).stringof); +} +--- + +$(P +The output: +) + +$(SHELL +--- these are written in decimal --- +2147483647 int +2147483648 long + +--- these are NOT written in decimal --- +2147483647 int +2147483648 uint +4294967296 long +9223372036854775808 ulong +) + +$(H6 $(IX L, literal suffix) The $(C L) suffix) + +$(P +Regardless of the magnitude of the value, if it ends with $(C L) as in $(C 10L), the type is $(C long). +) + +$(H6 $(IX U, literal suffix) The $(C U) suffix) + +$(P +If the literal ends with $(C U) as in $(C 10U), then its type is an unsigned type. Lowercase $(C u) can also be used. +) + +$(P +$(IX LU, literal suffix) $(IX UL, literal suffix) The $(C L) and $(C U) specifiers can be used together in any order. For example, $(C 7UL) and $(C 8LU) are both of type $(C ulong). +) + +$(H5 Floating point literals) + +$(P +The floating point literals can be specified in either the decimal system, as in $(C 1.234), or in the hexadecimal system, as in $(C 0x9a.bc). +) + +$(P $(B In the decimal system:) An exponent may be appended after the character $(C e) or $(C E), meaning "times 10 to the power of". For example, $(C 3.4e5) means "3.4 times 10 to the power of 5", or 340000. +) + +$(P +The $(C -) character typed before the value of the exponent changes the meaning to be "divided by 10 to the power of". For example, $(C 7.8e-3) means "7.8 divided by 10 to the power of 3". A $(C +) character may also be specified before the value of the exponent, but it has no effect. For example, $(C 5.6e2) and $(C 5.6e+2) are the same. +) + +$(P $(B In the hexadecimal system:) The value starts with either $(C 0x) or $(C 0X) and the parts before and after the point are specified in the numerals of the hexadecimal system. Since $(C e) and $(C E) are valid numerals in this system, the exponent is specified by $(C p) or $(C P). +) + +$(P +Another difference is that the exponent does not mean "10 to the power of", but instead "2 to the power of". For example, the $(C P4) part in $(C 0xabc.defP4) means "2 to the power of 4". +) + +$(P +Floating point literals almost always have a point but it may be omitted if an exponent is specified. For example, $(C 2e3) is a floating point literal with the value 2000. +) + +$(P +The value before the point may be omitted if zero. For example, $(C .25) is a literal having the value "quarter". +) + +$(P +The optional $(C _) characters may be used with floating point literals as well, as in $(C 1_000.5). +) + +$(H6 The types of floating point literals) + +$(P +Unless explicitly specified, the type of a floating point literal is $(C double). The $(C f) and $(C F) specifiers mean $(C float), and the $(C L) specifier means $(C real). For example; $(C 1.2) is $(C double), $(C 3.4f) is $(C float), and $(C 5.6L) is $(C real). +) + +$(H5 Character literals) + +$(P +Character literals are specified within single quotes as in $(C 'a'), $(C '\n'), $(C '\x21'), etc. +) + +$(P $(B As the character itself:) The character may be typed directly by the keyboard or copied from a separate text: 'a', 'ş', etc. +) + +$(P $(IX specifier, character) $(IX control character) $(B As the character specifier:) The character literal may be specified by a backslash character followed by a special character. For example, the backslash character itself can be specified by $(C '\\'). The following character specifiers are accepted: +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Syntax  Definition
\'single quote
\"double quote
\?question mark
\\backslash
\aalert (bell sound on some terminals)
\bdelete character
\fnew page
\nnew-line
\rcarriage return
\ttab
\vvertical tab
+ +$(P $(B As the extended ASCII character code:) Character literals can also be specified by their codes. The codes can be specified either in the hexadecimal system or in the octal system. When using the hexadecimal system, the literal must start with $(C \x) and must use two digits for the code, and when using the octal system the literal must start with $(C \) and have up to three digits. For example, the literals $(C '\x21') and $(C '\41') are both the exclamation point. +) + +$(P $(IX \u) $(IX \U) $(B As the Unicode character code:) When the literal is specified with $(C u) followed by 4 hexadecimal digits, then its type is $(C wchar). When it is specified with $(C U) followed by 8 hexadecimal digits, then its type is $(C dchar). For example, $(C '\u011e') and $(C '\U0000011e') are both the Ğ character, having the type $(C wchar) and $(C dchar), respectively. +) + +$(P $(IX \&) $(B As named character entity:) Characters that have entity names can be specified by that name using the HTML character entity syntax $(C '\&$(I name);'). D supports $(LINK2 http://dlang.org/entity.html, all character entities from HTML 5). For example, $(C '\&euro;') is €, $(C '\&hearts;') is ♥, and $(C '\&copy;') is ©. +) + +$(H5 $(IX string literal) $(IX literal, string) String literals) + +$(P +String literals are a combination of character literals and can be specified in a variety of ways. +) + +$(H6 Double-quoted string literals) + +$(P +The most common way of specifying string literals is by typing their characters within double quotes as in $(C "hello"). Individual characters of string literals follow the rules of character literals. For example, the literal $(C "A4 ka\u011fıt: 3\&frac12;TL") is the same as $(C "A4 kağıt: 3½TL"). +) + +$(H6 $(IX wysiwyg) $(I Wysiwyg) $(IX `) string literals) + +$(P +When string literals are specified using back-quotes, the individual characters of the string do not obey the special syntax rules of character literals. For example, the literal $(STRING $(BACK_TICK)c:\nurten$(BACK_TICK)) can be a directory name on the Windows operating system. If it were written using double quotes, the $(C '\n') part would mean the $(I new-line) character: +) + +--- + writeln(`c:\nurten`); + writeln("c:\nurten"); +--- + +$(SHELL +c:\nurten $(SHELL_NOTE wysiwyg (what you see is what you get)) +c: $(SHELL_NOTE_WRONG the character literal is taken as new-line) +urten +) + +$(P +Wysiwyg string literals can alternatively be specified using double quotes but prepended with the $(C r) character: $(C r"c:\nurten") is also a wysiwyg string literal. +) + +$(H6 $(IX hexadecimal string literal) Hexadecimal string literals) + +$(P +In situations where every character in a string needs to be specified in hexadecimal system, instead of typing $(C \x) before every one of them, a single $(C x) character may be typed before the opening double quote. In that case, every character in the string literal is taken to be hexadecimal. Additionally, the string literal may contain spaces, which are ignored by the compiler. For example, $(C "\x44\x64\x69\x6c\x69") and $(C x"44 64 69 6c 69") are the same string literal. +) + +$(H6 $(IX q"") $(IX delimited string literal) Delimited string literals) + +$(P +The string literal may contain delimiters that are typed right inside the double quotes. These delimiters are not considered to be parts of the value of the literal. Delimited string literals start with a $(C q) before the opening double quote. For example, the value of $(C q".hello.") is "hello"; the dots are not parts of the value. As long as it ends with a new-line, the delimiter can have more than one character: +) + +--- +writeln(q"MY_DELIMITER +first line +second line +MY_DELIMITER"); +--- + +$(P +MY_DELIMITER is not a part of the value: +) + +$(SHELL +first line +second line +) + +$(P +$(IX heredoc) Such a multi-line string literal including all the indentation is called a $(I heredoc). +) + +$(H6 $(IX q{}) $(IX token string literal) $(IX literal, token string) Token string literals) + +$(P +String literals that start with $(C q) and that use $(C {) and $(C }) as delimiters can contain only legal D source code: +) + +--- + auto str = q{int number = 42; ++number;}; + writeln(str); +--- + +$(P +The output: +) + +$(SHELL +int number = 42; ++number; +) + +$(P +This feature is particularly useful to help text editors display the contents of the string as syntax highlighted D code. +) + +$(H6 $(IX string) $(IX wstring) $(IX dstring) Types of string literals) + +$(P +By default the type of a string literal is $(C immutable(char)[]). An appended $(C c), $(C w), or $(C d) character specifies the type of the string explicitly as $(C immutable(char)[]), $(C immutable(wchar)[]), or $(C immutable(dchar)[]), respectively. For example, the characters of $(C "hello"d) are of type $(C immutable(dchar)). +) + +$(P +We have seen in the $(LINK2 /ders/d.en/strings.html, Strings chapter) that these three string types are aliased as $(C string), $(C wstring), and $(C dstring), respectively. +) + +$(H5 Literals are calculated at compile time) + +$(P +It is possible to specify literals as expressions. For example, instead of writing the total number of seconds in January as $(C 2678400) or $(C 2_678_400), it is possible to specify it by the terms that make up that value, namely $(C 60 * 60 * 24 * 31). The multiplication operations in that expression do not affect the run-time speed of the program; the program is compiled as if $(C 2678400) were written instead. +) + +$(P +The same applies to string literals. For example, the concatenation operation in $(C "hello " ~ "world") is executed at compile time, not at run time. The program is compiled as if the code contained the single string literal $(C "hello world"). +) + +$(PROBLEM_COK + +$(PROBLEM +The following line causes a compilation error: + +--- + int amount = 10_000_000_000; $(DERLEME_HATASI) +--- + +$(P +Change the program so that the line can be compiled and that $(C amount) equals ten billions. +) + +) + +$(PROBLEM +Write a program that increases the value of a variable and prints it in an infinite loop. Make the value always be printed on the same line, overwriting the previous value: + +$(SHELL +Number: 25774 $(SHELL_NOTE always on the same line) +) + +$(P +A special character literal other than $(C '\n') may be useful here. +) + +) + +) + +Macros: + SUBTITLE=Literals + + DESCRIPTION=The values that are typed in the source code. + + KEYWORDS=d programming language tutorial book literals diff --git a/target/logical_expressions.cozum.d b/target/logical_expressions.cozum.d new file mode 100644 index 0000000..352e59e --- /dev/null +++ b/target/logical_expressions.cozum.d @@ -0,0 +1,54 @@ +Ddoc + +$(COZUM_BOLUMU Logical Expressions) + +$(OL + +$(LI + +Because the compiler recognizes $(C 10 < value) already as an expression, it expects a comma after it to accept it as a legal argument to $(C writeln). Using parentheses around the whole expression would not work either, because this time a closing parenthesis would be expected after the same expression. +) + +$(LI +Grouping the expression as $(C (10 < value) < 20) removes the compilation error, because in this case first $(C 10 < value) is evaluated and then its result is used with $(C < 20). + +$(P +We know that the value of a logical expression like $(C 10 < value) is either $(C false) or $(C true). $(C false) and $(C true) take part in integer expressions as 0 and 1, respectively. (We will see automatic type conversions in a later chapter.) As a result, the whole expression is the equivalent of either $(C 0 < 20) or $(C 1 < 20), both of which evaluate to $(C true). +) + +) + +$(LI +The expression "greater than the lower value and less than the upper value" can be coded like the following: + +--- + writeln("Is between: ", (value > 10) && (value < 20)); +--- + +) + +$(LI +"There is a bicycle for everyone" can be coded as $(C personCount <= bicycleCount) or $(C bicycleCount >= personCount). The rest of the logical expression can directly be translated to D from the exercise: + +--- + writeln("We are going to the beach: ", + ((distance < 10) && (bicycleCount >= personCount)) + || + ((personCount <= 5) && existsCar && existsLicense) + ); +--- + +$(P +Note the placement of the $(C ||) operator to help with readability by separating the two main conditions. +) + +) + +) + +Macros: + SUBTITLE=Logical Expressions Solutions + + DESCRIPTION=Logical Expressions chapter exercise solutions + + KEYWORDS=programming in d tutorial logical expressions exercise solution diff --git a/target/logical_expressions.d b/target/logical_expressions.d new file mode 100644 index 0000000..ae1bc1d --- /dev/null +++ b/target/logical_expressions.d @@ -0,0 +1,431 @@ +Ddoc + +$(DERS_BOLUMU $(IX logical expression) Logical Expressions) + +$(P +$(IX expression) The actual work that a program performs is accomplished by $(I expressions). Any part of a program that produces a value or a side effect is called an expression. It has a very wide definition because even a constant value like $(C 42) and a string like $(STRING "hello") are expressions, since they produce the respective constant values 42 and "hello". +) + +$(P +$(I $(B Note:) Don't confuse producing a value with defining a variable. Values need not be associated with variables. +) +) + +$(P +Function calls like $(C writeln) are expressions as well because they have side effects. In the case of $(C writeln), the effect is on the output stream by the placement of characters on it. Another example from the programs that we have written so far would be the assignment operation, which affects the variable that is on its left-hand side. +) + +$(P +Because of producing values, expressions can take part in other expressions. This allows us to form more complex expressions from simpler ones. For example, assuming that there is a function named $(C currentTemperature) that produces the value of the current air temperature, the value that it produces may directly be used in a $(C writeln) expression: +) + +--- + writeln("It's ", currentTemperature(), + " degrees at the moment."); +--- + +$(P +That line consists of four expressions: +) + +$(OL +$(LI $(STRING "It's ")) +$(LI $(C currentTemperature())) +$(LI $(STRING " degrees at the moment.")) +$(LI The $(C writeln()) expression that makes use of the other three) +) + +$(P +In this chapter we will cover the particular type of expression that is used in conditional statements. +) + +$(P +Before going further though, I would like to repeat the assignment operator once more, this time emphasizing the two expressions that appear on its left and right sides: the assignment operator ($(C =)) assigns the value of the expression on its right-hand side to the expression on its left-hand side (e.g. to a variable). +) + +--- + temperature $(HILITE =) 23 // temperature's value becomes 23 +--- + +$(H5 Logical Expressions) + +$(P +Logical expressions are the expressions that are used in Boolean arithmetic. Logical expressions are what makes computer programs make decisions like "if the answer is yes, I will save the file". +) + +$(P +Logical expressions can have one of only two values: $(C false) that indicates falsity, and $(C true) that indicates truth. +) + +$(P +I will use $(C writeln) expressions in the following examples. If a line has $(C true) printed at the end, it will mean that what is printed on the line is true. Similarly, $(C false) will mean that what is on the line is false. For example, if the output of a program is the following, +) + +$(SHELL +There is coffee: true +) + +$(P +then it will mean that "there is coffee". Similarly, +) + +$(SHELL +There is coffee: false +) + +$(P +will mean that "there isn't coffee". Note that the fact that "is" appears on the left-hand side does not mean that coffee exists. I use the "... is ...: false" construct to mean "is not" or "is false". +) + +$(P +Logical expressions are used extensively in $(I conditional statements), $(I loops), $(I function parameters), etc. It is essential to understand how they work. Luckily, logical expressions are easy to explain and use. +) + +$(P +The logical operators that are used in logical expressions are the following: +) + +$(UL + +$(LI $(IX ==) $(IX equals, logical operator) The $(C ==) operator answers the question "is equal to?". It compares the two expressions on its left and right sides and produces $(C true) if they are equal and $(C false) if they are not. By definition, the value that $(C ==) produces is a logical expression. + +$(P +As an example, let's assume that we have the following two variables: +) + +--- + int daysInWeek = 7; + int monthsInYear = 12; +--- + +$(P +The following are two logical expressions that use those values: +) + +--- + daysInWeek == 7 // true + monthsInYear == 11 // false +--- + +) + +$(LI $(IX !=) $(IX not equals, logical operator) The $(C !=) operator answers the question "is not equal to?". It compares the two expressions on its sides and produces the opposite of $(C ==). + + +--- + daysInWeek != 7 // false + monthsInYear != 11 // true +--- + +) + +$(LI $(IX ||) $(IX or, logical operator) The $(C ||) operator means "or", and produces $(C true) if any one of the logical expressions is true. + +$(P +If the value of the left-hand expression is $(C true), it produces $(C true) without even looking at the expression that is on the right-hand side. If the left-hand side is $(C false), then it produces the value of the right-hand side. This operator is similar to the "or" in English: if the left one, the right one, or both are $(C true), then it produces $(C true). +) + +$(P +The following table presents all of the possible values for both sides of this operator and its result: +) + + + + + + + +
Left expressionOperatorRight expressionResult
false||falsefalse
false||truetrue
true||false (not evaluated)true
true||true (not evaluated)true
+ +--- +import std.stdio; + +void main() { + // false means "no", true means "yes" + + bool existsCoffee = false; + bool existsTea = true; + + writeln("There is warm drink: ", + existsCoffee $(HILITE ||) existsTea); +} +--- + +$(P +Because at least one of the two expressions is $(C true), the logical expression above produces $(C true). +) + +) + +$(LI $(IX &&) $(IX and, logical operator) The $(C &&) operator means "and", and produces $(C true) if both of the expressions are true. + +$(P +If the value of the left-hand expression is $(C false), it produces $(C false) without even looking at the expression that is on the right-hand side. If the left-hand side is $(C true), then it produces the value of the right-hand side. This operator is similar to the "and" in English: if the left value and the right value are $(C true), then it produces $(C true). +) + + + + + + + +
Left expressionOperatorRight expressionResult
false&&false (not evaluated)false
false&&true (not evaluated)false
true&&falsefalse
true&&truetrue
+ +--- + writeln("I will drink coffee: ", + wantToDrinkCoffee $(HILITE &&) existsCoffee); +--- + +$(P $(I +$(B Note:) The fact that the $(C ||) and $(C &&) operators may not evaluate the right-hand expression is called their) short-circuit behavior $(I. The ternary operator $(C ?:), which we will see in a later chapter, is similar in that it never evaluates one of its three expressions. All of the other operators always evaluate and use all of their expressions. +)) + +) + +$(LI $(IX ^, logical exclusive or) $(IX xor, logical operator) The $(C ^) operator answers the question "is one or the other but not both?". This operator produces $(C true) if only one expression is $(C true), but not both. + +$(P +$(B Warning:) In reality, this operator is not a logical operator but an arithmetic one. It behaves like a logical operator only if both of the expressions are $(C bool). +) + + + + + + + +
Left expressionOperatorRight expressionResult
false^falsefalse
false^truetrue
true^falsetrue
true^truefalse
+ +$(P +For example, the logic that represents my playing chess if $(I only one) of my two friends shows up can be coded like this: +) + +--- + writeln("I will play chess: ", jimShowedUp $(HILITE ^) bobShowedUp); +--- + +) + +$(LI $(IX <, less than) $(IX less than, logical operator) The $(C <) operator answers the question "is less than?" (or "does come before in sort order?"). + +--- + writeln("We beat: ", theirScore $(HILITE <) ourScore); +--- + +) + +$(LI $(IX >, greater than) $(IX greater than, logical operator) The $(C >) operator answers the question "is greater than?" (or "does come after in sort order?"). + +--- + writeln("They beat: ", theirScore $(HILITE >) ourScore); +--- + +) + +$(LI $(IX <=) $(IX less than or equal to, logical operator) The $(C <=) operator answers the question "is less than or equal to?" (or "does come before or the same in sort order?"). This operator is the opposite of the $(C >) operator. + +--- + writeln("We were not beaten: ", theirScore $(HILITE <=) ourScore); +--- + +) + +$(LI $(IX >=) $(IX greater than or equal to, logical operator) The $(C >=) operator answers the question "is greater than or equal to?" (or "does come after or the same in sort order?"). This operator is the opposite of the $(C <) operator. + +--- + writeln("We did not beat: ", theirScore $(HILITE >=) ourScore); +--- + +) + +$(LI $(IX !, logical not) $(IX not, logical operator) The $(C !) operator means "the opposite of". Different from the other logical operators, it takes just one expression and produces $(C true) if that expression is $(C false), and $(C false) if that expression is $(C true). + +--- + writeln("I will walk: ", $(HILITE !)existsBicycle); +--- + +) + +) + +$(H5 Grouping expressions) + +$(P +The order in which the expressions are evaluated can be specified by using parentheses to group them. When parenthesized expressions appear in more complex expressions, the parenthesized expressions are evaluated before they can be used in the expressions that they appear in. For example, the expression "if there is coffee or tea, and also cookie or scone; then I am happy" can be coded like the following: +) + +--- +writeln("I am happy: ", +(existsCoffee || existsTea) && (existsCookie || existsScone)); +--- + +$(P +If the sub expressions were not parenthesized, the expressions would be evaluated according to $(I operator precedence) rules of D (which have been inherited from the C language). Since in these rules $(C &&) has a higher precedence than $(C ||), writing the expression without parentheses would not be evaluated as intended: +) + +--- +writeln("I am happy: ", +existsCoffee || existsTea && existsCookie || existsScone); +--- + +$(P +The $(C &&) operator would be evaluated first and the whole expression would be the semantic equivalent of the following expression: +) + +--- +writeln("I am happy: ", +existsCoffee || (existsTea && existsCookie) || existsScone); +--- + +$(P +That has a totally different meaning: "if there is coffee, or tea and cookie, or scone; then I am happy". +) + +$(P1 +Almost nobody can memorize all of the operator precedence rules. For that reason, for code correctness and clarity, it would be helpful to use parentheses even when not necessary. +) + +$(P +The operator precedence table will be presented $(LINK2 /ders/d.en/operator_precedence.html, later in the book). +) + +$(H5 $(IX input, bool) $(IX read, bool) Reading $(C bool) input) + +$(P +All of the $(C bool) values above are automatically printed as $(STRING "false") or $(STRING "true"). It is the same in the opposite direction: $(C readf()) automatically converts strings $(STRING "false") and $(STRING "true") to $(C bool) values $(C false) and $(C true), respectively. It accepts any combination of lower and uppercase letters as well. For example, $(STRING "False") and $(STRING "FALSE") are converted to $(C false) and $(STRING "True") and $(STRING "TRUE") are converted to $(C true). +) + +$(PROBLEM_COK + +$(PROBLEM +We've seen above that the $(C <) and the $(C >) operators are used to determine whether a value is less than or greater than another value; but there is no operator that answers the question "is between?" to determine whether a value is between two other values. + +$(P +Let's assume that a programmer has written the following code to determine whether $(C value) is between 10 and 20. Observe that the program cannot be compiled as written: +) + +--- +import std.stdio; + +void main() { + int value = 15; + + writeln("Is between: ", + 10 < value < 20); $(DERLEME_HATASI) +} +--- + +$(P +Try using parentheses around the whole expression: +) + +--- + writeln("Is between: ", + (10 < value < 20)); $(DERLEME_HATASI) +--- + +$(P +Observe that it still cannot be compiled. +) + +) + +$(PROBLEM + +While searching for a solution to this problem, the same programmer discovers that the following use of parentheses now enables the code to be compiled: + +--- + writeln("Is between: ", + (10 < value) < 20); // ← compiles but WRONG +--- +$(P +Observe that the program now works as expected and prints "true". Unfortunately, that output is misleading because the program has a bug. To see the effect of that bug, replace 15 with a value greater than 20: +) + +--- + int value = 21; +--- + +$(P +Observe that the program still prints "true" even though 21 is not less than 20. +) + +$(P +$(B Hint:) Remember that the type of a logical expression is $(C bool). It shouldn't make sense whether a $(C bool) value is less than 20. The reason it compiles is due to the compiler converting the boolean expression to a 1 or 0, and then evaluating that against 20 to see if it is less. +) + +) + +$(PROBLEM +The logical expression that answers the question "is between?" must instead be coded like this: "is greater than the lower value and less than the upper value?". + +$(P +Change the expression in the program according to that logic and observe that it now prints "true" as expected. Additionally, test that the logical expression works correctly for other values as well: for example, when $(C value) is 50 or 1, the program should print "false"; and when it is 12, the program should print "true". +) + +) + +$(PROBLEM +Let's assume that we can go to the beach when one of the following conditions is true: + +$(UL + +$(LI If the distance to the beach is less than 10 miles and there is a bicycle for everyone) + +$(LI If there is fewer than 6 of us, and we have a car, and at least one of us has a driver license) + +) + +$(P +As written, the following program always prints "true". Construct a logical expression that will print "true" when one of the conditions above is true. (When trying the program, enter "false" or "true" for questions that start with "Is there a".). +) + +--- +import std.stdio; +import std.conv; +import std.string; + +void main() { + write("How many are we? "); + int personCount; + readf(" %s", &personCount); + + write("How many bicycles are there? "); + int bicycleCount; + readf(" %s", &bicycleCount); + + write("What is the distance to the beach? "); + int distance; + readf(" %s", &distance); + + write("Is there a car? "); + bool existsCar; + readf(" %s", &existsCar); + + write("Is there a driver license? "); + bool existsLicense; + readf(" %s", &existsLicense); + + /* Replace the 'true' below with a logical expression that + * produces the value 'true' when one of the conditions + * listed in the question is satisfied: */ + writeln("We are going to the beach: ", true); +} +--- + +$(P +Enter various values and test that the logical expression that you wrote works correctly. +) + +) + +) + +Macros: + SUBTITLE=Logical Expressions + + DESCRIPTION=The logical expressions and the logical operators of the D programming language + + KEYWORDS=d programming language tutorial book logical expression bool false true + +MINI_SOZLUK= diff --git a/target/lvalue_rvalue.d b/target/lvalue_rvalue.d new file mode 100644 index 0000000..e66bbf9 --- /dev/null +++ b/target/lvalue_rvalue.d @@ -0,0 +1,218 @@ +Ddoc + +$(DERS_BOLUMU $(IX lvalue) $(IX rvalue) Lvalues and Rvalues) + +$(P +$(IX expression, lvalue vs rvalue) The value of every expression is classified as either an lvalue or an rvalue. A simple way of differentiating the two is thinking of lvalues as actual variables (including elements of arrays and associative arrays), and rvalues as temporary results of expressions (including literals). +) + +$(P +As a demonstration, the first $(C writeln()) expression below uses only lvalues and the other one uses only rvalues: +) + +--- +import std.stdio; + +void main() { + int i; + immutable(int) imm; + auto arr = [ 1 ]; + auto aa = [ 10 : "ten" ]; + + /* All of the following arguments are lvalues. */ + + writeln(i, // mutable variable + imm, // immutable variable + arr, // array + arr[0], // array element + aa[10]); // associative array element + // etc. + + enum message = "hello"; + + /* All of the following arguments are rvalues. */ + + writeln(42, // a literal + message, // a manifest constant + i + 1, // a temporary value + calculate(i)); // return value of a function + // etc. +} + +int calculate(int i) { + return i * 2; +} +--- + +$(H5 Limitations of rvalues) + +$(P +Compared to lvalues, rvalues have the following three limitations. +) + +$(H6 Rvalues don't have memory addresses) + +$(P +An lvalue has a memory location to which we can refer, while an rvalue does not. +) + +$(P +For example, it is not possible to take the address of the rvalue expression $(C a + b) in the following program: +) + +--- +import std.stdio; + +void main() { + int a; + int b; + + readf(" %s", &a); $(CODE_NOTE compiles) + readf(" %s", &(a + b)); $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: a + b $(HILITE is not an lvalue) +) + +$(H6 Rvalues cannot be assigned new values) + +$(P +If mutable, an lvalue can be assigned a new value, while an rvalue cannot be: +) + +--- + a = 1; $(CODE_NOTE compiles) + (a + b) = 2; $(DERLEME_HATASI) +--- + +$(SHELL +Error: a + b $(HILITE is not an lvalue) +) + +$(H6 Rvalues cannot be passed to functions by reference) + +$(P +An lvalue can be passed to a function that takes a parameter by reference, while an rvalue cannot be: +) + +--- +void incrementByTen($(HILITE ref int) value) { + value += 10; +} + +// ... + + incrementByTen(a); $(CODE_NOTE compiles) + incrementByTen(a + b); $(DERLEME_HATASI) +--- + +$(SHELL +Error: function deneme.incrementByTen (ref int value) +$(HILITE is not callable) using argument types (int) +) + +$(P +The main reason for this limitation is the fact that a function taking a $(C ref) parameter can hold on to that reference for later use, at a time when the rvalue would not be available. +) + +$(P +Different from languages like C++, in D an rvalue cannot be passed to a function even if that function does $(I not) modify the argument: +) + +--- +void print($(HILITE ref const(int)) value) { + writeln(value); +} + +// ... + + print(a); $(CODE_NOTE compiles) + print(a + b); $(DERLEME_HATASI) +--- + +$(SHELL +Error: function deneme.print (ref const(int) value) +$(HILITE is not callable) using argument types (int) +) + +$(H5 $(IX auto ref, parameter) $(IX parameter, auto ref) Using $(C auto ref) parameters to accept both lvalues and rvalues) + +$(P +As it was mentioned in the previous chapter, $(C auto ref) parameters of $(LINK2 /ders/d.en/templates.html, function templates) can take both lvalues and rvalues. +) + +$(P +When the argument is an lvalue, $(C auto ref) means $(I by reference). On the other hand, since rvalues cannot be passed to functions by reference, when the argument is an rvalue, it means $(I by copy). For the compiler to generate code differently for these two distinct cases, the function must be a template. +) + +$(P +We will see templates in a later chapter. For now, please accept that the highlighted empty parentheses below make the following definition a $(I function template). +) + +--- +void incrementByTen$(HILITE ())($(HILITE auto ref) int value) { + /* WARNING: The parameter may be a copy if the argument is + * an rvalue. This means that the following modification + * may not be observable by the caller. */ + + value += 10; +} + +void main() { + int a; + int b; + + incrementByTen(a); $(CODE_NOTE lvalue; passed by reference) + incrementByTen(a + b); $(CODE_NOTE rvalue; copied) +} +--- + +$(P +$(IX isRef) It is possible to determine whether the parameter is an lvalue or an rvalue by using $(C __traits(isRef)) with $(C static if) : +) + +--- +void incrementByTen()(auto ref int value) { + $(HILITE static if) (__traits($(HILITE isRef), value)) { + // 'value' is passed by reference + } else { + // 'value' is copied + } +} +--- + +$(P +We will see $(C static if) and $(C __traits) later in $(LINK2 /ders/d.en/cond_comp.html, the Conditional Compilation chapter). +) + +$(H5 Terminology) + +$(P +The names "lvalue" and "rvalue" do not represent the characteristics of these two kinds of values accurately. The initial letters $(I l) and $(I r) come from $(I left) and $(I right), referring to the left- and the right-hand side expressions of the assignment operator: +) + +$(UL + +$(LI Assuming that it is mutable, an lvalue can be the left-hand expression of an assignment operation.) + +$(LI An rvalue cannot be the left-hand expression of an assignment operation.) + +) + +$(P +The terms "left value" and "right value" are confusing because in general both lvalues and rvalues can be on either side of an assignment operation: +) + +--- + // rvalue 'a + b' on the left, lvalue 'a' on the right: + array[a + b] = a; +--- + +Macros: + SUBTITLE=Lvalues and Rvalues + + DESCRIPTION=Left-values and right-values and their differences. + + KEYWORDS=d programming language tutorial book lvalue rvalue diff --git a/target/main.cozum.d b/target/main.cozum.d new file mode 100644 index 0000000..912c3b1 --- /dev/null +++ b/target/main.cozum.d @@ -0,0 +1,76 @@ +Ddoc + +$(COZUM_BOLUMU Program Environment) + +$(OL + +$(LI + +--- +import std.stdio; +import std.conv; + +int main(string[] args) { + if (args.length != 4) { + stderr.writeln( + "ERROR! Usage: \n ", args[0], + " a_number operator another_number"); + return 1; + } + + immutable first = to!double(args[1]); + string op = args[2]; + immutable second = to!double(args[3]); + + switch (op) { + + case "+": + writeln(first + second); + break; + + case "-": + writeln(first - second); + break; + + case "x": + writeln(first * second); + break; + + case "/": + writeln(first / second); + break; + + default: + throw new Exception("Invalid operator: " ~ op); + } + + return 0; +} +--- + +) + +$(LI + +--- +import std.stdio; +import std.process; + +void main() { + write("Please enter the command line to execute: "); + string commandLine = readln(); + + writeln("The output: ", executeShell(commandLine)); +} +--- + +) + +) + +Macros: + SUBTITLE=Program Environment Solutions + + DESCRIPTION=The exercise solutions for the Program Environment chapter. + + KEYWORDS=programming in d tutorial main environment diff --git a/target/main.d b/target/main.d new file mode 100644 index 0000000..36cf423 --- /dev/null +++ b/target/main.d @@ -0,0 +1,500 @@ +Ddoc + +$(DERS_BOLUMU $(IX program environment) Program Environment) + +$(P +$(IX main) We have seen that $(C main()) is a function. Program execution starts with $(C main()) and branches off to other functions from there. The definition of $(C main()) that we have used so far has been the following: +) + +--- +void main() { + // ... +} +--- + +$(P +According to that definition $(C main()) does not take any parameters and does not return a value. In reality, in most systems every program necessarily returns a value to its environment when it ends, which is called an exit status or return code. Because of this, although it is possible to specify the return type of $(C main()) as $(C void), it will actually return a value to the operating system or launch environment. +) + +$(H5 The return value of $(C main())) + +$(P +Programs are always started by an entity in a particular environment. The entity that starts the program may be the shell where the user types the name of the program and presses the Enter key, a development environment where the programmer clicks the [Run] button, and so on. +) + +$(P +In D and several other programming languages, the program communicates its exit status to its environment by the return value of $(C main()). +) + +$(P +The exact meaning of return codes depend on the application and the system. In almost all systems a return value of zero means a successful completion, while other values generally mean some type of failure. There are exceptions to this, though. For instance, in OpenVMS even values indicate failure, while odd values indicate success. Still, in most systems the values in the range [0, 125] can be used safely, with values 1 to 125 having a meaning specific to that program. +) + +$(P +For example, the common Unix program $(C ls), which is used for listing contents of directories, returns 0 for success, 1 for minor errors and 2 for serious ones. +) + +$(P +In many environments, the return value of the program that has been executed most recently in the terminal can be seen through the $(C $?) environment variable. For example, when we ask $(C ls) to list a file that does not exist, its nonzero return value can be observed with $(C $?) as seen below. +) + +$(P +$(I $(B Note:) In the command line interactions below, the lines that start with $(C #) indicate the lines that the user types. If you want to try the same commands, you must enter the contents of those lines except for the $(C #) character. Also, the commands below start a program named $(C deneme); replace that name with the name of your test program.) +) + +$(P +$(I Additionally, although the following examples show interactions in a Linux terminal, they would be similar but not exactly the same in terminals of other operating systems.) +) + +$(SHELL +# ls a_file_that_does_not_exist +$(SHELL_OBSERVED ls: cannot access a_file_that_does_not_exist: No such file +or directory) +# $(HILITE echo $?) +$(SHELL_OBSERVED 2) $(SHELL_NOTE the return value of ls) +) + +$(H6 $(C main()) always returns a value) + +$(P +Some of the programs that we have written so far threw exceptions when they could not continue with their tasks. As much as we have seen so far, when an exception is thrown, the program ends with an $(C object.Exception) error message. +) + +$(P +When that happens, even if $(C main()) has been defined as returning $(C void), a nonzero status code is automatically returned to the program's environment. Let's see this in action in the following simple program that terminates with an exception: +) + +--- +void main() { + throw new Exception("There has been an error."); +} +--- + +$(P +Although the return type is specified as $(C void), the return value is nonzero: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED object.Exception: There has been an error. +...) +# echo $? +$(SHELL_OBSERVED $(HILITE 1)) +) + +$(P +Similarly, $(C void main()) functions that terminate successfully also automatically return zero as their return values. Let's see this with the following program that terminates $(I successfully): +) + +--- +import std.stdio; + +void main() { + writeln("Done!"); +} +--- + +$(P +The program returns zero: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED Done!) +# echo $? +$(SHELL_OBSERVED $(HILITE 0)) +) + +$(H6 Specifying the return value) + +$(P +To choose a specific return code we return a value from $(C main()) in the same way as we would from any other function. The return type must be specified as $(C int) and the value must be returned by the $(C return) statement: +) + +--- +import std.stdio; + +$(HILITE int) main() { + int number; + write("Please enter a number between 3 and 6: "); + readf(" %s", &number); + + if ((number < 3) || (number > 6)) { + $(HILITE stderr).writefln("ERROR: %s is not valid!", number); + $(HILITE return 111); + } + + writefln("Thank you for %s.", number); + + $(HILITE return 0); +} +--- + +$(P +When the entered number is within the valid range, the return value of the program is zero: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED Please enter a number between 3 and 6: 5 +Thank you for 5.) +# echo $? +$(SHELL_OBSERVED $(HILITE 0)) +) + +$(P +When the number is outside of the valid range, the return value of the program is 111: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED Please enter a number between 3 and 6: 10 +ERROR: 10 is not valid!) +# echo $? +$(SHELL_OBSERVED $(HILITE 111)) +) + +$(P +The value of 111 in the above program is arbitrary; normally 1 is suitable as the failure code. +) + +$(H5 $(IX stderr) Standard error stream $(C stderr)) + +$(P +The program above uses the stream $(C stderr). That stream is the third of the standard streams. It is used for writing error messages: +) + +$(UL +$(LI $(C stdin): standard input stream) +$(LI $(C stdout): standard output stream) +$(LI $(C stderr): standard error stream) +) + +$(P +When a program is started in a terminal, normally the messages that are written to $(C stdout) and $(C stderr) both appear on the terminal window. When needed, it is possible to redirect these outputs individually. This subject is outside of the focus of this chapter and the details may vary for each shell program. +) + +$(H5 Parameters of $(C main())) + +$(P +It is common for programs to take parameters from the environment that started them. For example, we have already passed a file name as a command line option to $(C ls) above. There are two command line options in the following line: +) + +$(SHELL +# ls $(HILITE -l deneme) +$(SHELL_OBSERVED -rwxr-xr-x 1 acehreli users 460668 Nov 6 20:38 deneme) +) + +$(P +The set of command line parameters and their meanings are defined entirely by the program. Every program documents its usage, including what every parameter means. +) + +$(P +The arguments that are used when starting a D program are passed to that program's $(C main()) as a slice of $(C string)s. Defining $(C main()) as taking a parameter of type $(C string[]) is sufficient to have access to program arguments. The name of this parameter is commonly abbreviated as $(C args). The following program prints all of the arguments with which it is started: +) + +--- +import std.stdio; + +void main($(HILITE string[] args)) { + foreach (i, arg; args) { + writefln("Argument %-3s: %s", i, arg); + } +} +--- + +$(P +Let's start the program with arbitrary arguments: +) + +$(SHELL +# ./deneme some arguments on the command line 42 --an-option +$(SHELL_OBSERVED Argument 0 : ./deneme +Argument 1 : some +Argument 2 : arguments +Argument 3 : on +Argument 4 : the +Argument 5 : command +Argument 6 : line +Argument 7 : 42 +Argument 8 : --an-option) +) + +$(P +In almost all systems, the first argument is the name of the program, in the way it has been entered by the user. The other arguments appear in the order they were entered. +) + +$(P +It is completely up to the program how it makes use of the arguments. The following program prints its two arguments in reverse order: +) + +--- +import std.stdio; + +int main(string[] args) { + if (args.length != 3) { + stderr.writefln("ERROR! Correct usage:\n" ~ + " %s word1 word2", args[0]); + return 1; + } + + writeln(args[2], ' ', args[1]); + + return 0; +} +--- + +$(P +The program also shows its correct usage if you don't enter exactly two words: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED ERROR! Correct usage: + ./deneme word1 word2) +# ./deneme world hello +$(SHELL_OBSERVED hello world) +) + +$(H5 $(IX command line options) $(IX getopt, std.getopt) Command line options and the $(C std.getopt) module) + +$(P +That is all there is to know about the parameters and the return value of $(C main()). However, parsing the arguments is a repetitive task. The $(C std.getopt) module is designed to help with parsing the command line options of programs. +) + +$(P +Some parameters like "world" and "hello" above are purely data for the program to use. Other kinds of parameters are called $(I command line options), and are used to change the behaviors of programs. An example of a command line option is the $(C -l) option that has been passed to $(C ls) above. +) + +$(P +Command line options make programs more useful by removing the need for a human user to interact with the program to have it behave in a certain way. With command line options, programs can be started from script programs and their behaviors can be specified through command line options. +) + +$(P +Although the syntax and meanings of command line arguments of every program is specific to that program, their format is somewhat standard. For example, in POSIX, command line options start with $(C --) followed by the name of the option, and values come after $(C =) characters: +) + +$(SHELL +# ./deneme --an-option=17 +) + +$(P +The $(C std.getopt) module simplifies parsing such options. It has more capabilities than what is covered in this section. +) + +$(P +Let's design a program that prints random numbers. Let's take the minimum, maximum, and total number of these numbers as program arguments. Let's require the following syntax to get these values from the command line: +) + +$(SHELL +# ./deneme --count=7 --minimum=10 --maximum=15 +) + +$(P +The $(C getopt()) function parses and assigns those values to variables. Similarly to $(C readf()), the addresses of variables must be specified by the $(C &) operator: +) + +--- +import std.stdio; +$(HILITE import std.getopt;) +import std.random; + +void main(string[] args) { + int count; + int minimum; + int maximum; + + $(HILITE getopt)(args, + "count", $(HILITE &)count, + "minimum", $(HILITE &)minimum, + "maximum", $(HILITE &)maximum); + + foreach (i; 0 .. count) { + write(uniform(minimum, maximum + 1), ' '); + } + + writeln(); +} +--- + +$(SHELL +# ./deneme --count=7 --minimum=10 --maximum=15 +$(SHELL_OBSERVED 11 11 13 11 14 15 10) +) + +$(P +Many command line options of most programs have a shorter syntax as well. For example, $(C -c) may have the same meaning as $(C --count). Such alternative syntax for each option is specified in $(C getopt()) after a $(C |) character. There may be more than one shortcut for each option: +) + +--- + getopt(args, + "count|c", &count, + "minimum|n", &minimum, + "maximum|x", &maximum); +--- + +$(P +It is common to use a single dash for the short versions and the $(C =) character is usually either omitted or substituted by a space: +) + +$(SHELL +# ./deneme -c7 -n10 -x15 +$(SHELL_OBSERVED 11 13 10 15 14 15 14) +# ./deneme -c 7 -n 10 -x 15 +$(SHELL_OBSERVED 11 13 10 15 14 15 14) +) + +$(P +$(C getopt()) converts the arguments from $(C string) to the type of each variable. For example, since $(C count) above is an $(C int), $(C getopt()) converts the value specified for the $(C --count) argument to an $(C int). When needed, such conversions may also be performed explicitly by $(C to). +) + +$(P +So far we have used $(C std.conv.to) only when converting to $(C string). $(C to) can, in fact, convert from any type to any type, as long as that conversion is possible. For example, the following program takes advantage of $(C to) when converting its argument to $(C size_t): +) + +--- +import std.stdio; +$(HILITE import std.conv); + +void main(string[] args) { + // The default count is 10 + size_t count = 10; + + if (args.length > 1) { + // There is an argument + count = $(HILITE to!size_t)(args[1]); + } + + foreach (i; 0 .. count) { + write(i * 2, ' '); + } + + writeln(); +} +--- + + +$(P +The program produces 10 numbers when no argument is specified: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED 0 2 4 6 8 10 12 14 16 18) +# ./deneme 3 +$(SHELL_OBSERVED 0 2 4) +) + +$(H5 $(IX environment variable) Environment variables) + +$(P +The environment that a program is started in generally provides some variables that the program can make use of. The environment variables can be accessed through the associative array interface of $(C std.process.environment). For example, the following program prints the $(C PATH) environment variable: +) + +--- +import std.stdio; +$(HILITE import std.process;) + +void main() { + writeln($(HILITE environment["PATH"])); +} +--- + +$(P +The output: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED /usr/local/bin:/usr/bin) +) + +$(P +$(C std.process.environment) provides access to the environment variables through the associative array syntax. However, $(C environment) itself is not an associative array. When needed, the environment variables can be converted to an associative array by using $(C toAA()): +) + +--- + string[string] envVars = environment.toAA(); +--- + +$(H5 $(IX executeShell, std.process) Starting other programs) + +$(P +Programs may start other programs and become the $(I environment) for those programs. A function that enables this is $(C executeShell), from the $(C std.process) module. +) + +$(P +$(C executeShell) executes its parameter as if the command was typed at the terminal. It then returns both the return code and the output of that command as a $(I tuple). Tuples are array-like structures, which we will see later in $(LINK2 /ders/d.en/tuples.html, the Tuples chapter): +) + +--- +import std.stdio; +import std.process; + +void main() { + const result = $(HILITE executeShell)("ls -l deneme"); + const returnCode = result[0]; + const output = result[1]; + + writefln("ls returned %s.", returnCode); + writefln("Its output:\n%s", output); +} +--- + +$(P +The output: +) + +$(SHELL +# ./deneme +$(SHELL_OBSERVED +ls returned 0. +Its output: +-rwxrwxr-x. 1 acehreli acehreli 1359178 Apr 21 15:01 deneme +) +) + +$(H5 Summary) + +$(UL + +$(LI Even when it is defined with a return type of $(C void), $(C main()) automatically returns zero for success and nonzero for failure.) + +$(LI $(C stderr) is suitable to print error messages.) + +$(LI $(C main) can take parameters as $(C string[]).) + +$(LI $(C std.getopt) helps with parsing command line options.) + +$(LI $(C std.process) helps with accessing environment variables and starting other programs.) + +) + +$(PROBLEM_COK + +$(PROBLEM +Write a calculator program that takes an operator and two operands as command line arguments. Have the program support the following usage: + +$(SHELL +# ./deneme 3.4 x 7.8 +$(SHELL_OBSERVED 26.52) +) + +$(P +$(I $(B Note:) Because the $(C *) character has a special meaning on most terminals (more accurately, on most $(I shells)), I have used $(C x) instead. You may still use $(C *) as long as it is $(I escaped) as $(C \*).) +) + +) + +$(PROBLEM +Write a program that asks the user which program to start, starts that program, and prints its output. +) + +) + +Macros: + SUBTITLE=Program Environment + + DESCRIPTION=The return value and parameters of main(), environment variables, and starting other programs from D programs. + + KEYWORDS=d programming language tutorial book environment diff --git a/target/member_functions.cozum.d b/target/member_functions.cozum.d new file mode 100644 index 0000000..21fc8bf --- /dev/null +++ b/target/member_functions.cozum.d @@ -0,0 +1,170 @@ +Ddoc + +$(COZUM_BOLUMU Member Functions) + +$(OL + +$(LI +Potentially negative intermediate values make $(C decrement()) slightly more complicated than $(C increment()): + +--- +struct TimeOfDay { + // ... + + void decrement(in Duration duration) { + auto minutesToSubtract = duration.minute % 60; + auto hoursToSubtract = duration.minute / 60; + + minute -= minutesToSubtract; + + if (minute < 0) { + minute += 60; + ++hoursToSubtract; + } + + hour -= hoursToSubtract; + + if (hour < 0) { + hour = 24 - (-hour % 24); + } + } + + // ... +} +--- + +) + +$(LI +To see how much easier it gets with $(C toString()) member functions, let's look at the $(C Meeting) overload of $(C info()) one more time: + +--- +void info(in Meeting meeting) { + info(meeting.start); + write('-'); + info(meeting.end); + + writef(" \"%s\" meeting with %s attendees", + meeting.topic, + meeting.attendanceCount); +} +--- + +$(P +Taking advantage of the already-defined $(C TimeOfDay.toString), the implementation of $(C Meeting.toString) becomes trivial: +) + +--- + string toString() { + return format("%s-%s \"%s\" meeting with %s attendees", + start, end, topic, attendanceCount); + } +--- + +$(P +Here is the entire program: +) + +--- +import std.stdio; +import std.string; + +struct Duration { + int minute; +} + +struct TimeOfDay { + int hour; + int minute; + + string toString() { + return format("%02s:%02s", hour, minute); + } + + void increment(in Duration duration) { + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + } +} + +struct Meeting { + string topic; + int attendanceCount; + TimeOfDay start; + TimeOfDay end; + + string toString() { + return format("%s-%s \"%s\" meeting with %s attendees", + start, end, topic, attendanceCount); + } +} + +struct Meal { + TimeOfDay time; + string address; + + string toString() { + TimeOfDay end = time; + end.increment(Duration(90)); + + return format("%s-%s Meal, Address: %s", + time, end, address); + } +} + +struct DailyPlan { + Meeting amMeeting; + Meal lunch; + Meeting pmMeeting; + + string toString() { + return format("%s\n%s\n%s", + amMeeting, + lunch, + pmMeeting); + } +} + +void main() { + auto bikeRideMeeting = Meeting("Bike Ride", 4, + TimeOfDay(10, 30), + TimeOfDay(11, 45)); + + auto lunch = Meal(TimeOfDay(12, 30), "İstanbul"); + + auto budgetMeeting = Meeting("Budget", 8, + TimeOfDay(15, 30), + TimeOfDay(17, 30)); + + auto todaysPlan = DailyPlan(bikeRideMeeting, + lunch, + budgetMeeting); + + writeln(todaysPlan); + writeln(); +} +--- + +$(P +The output of the program is the same as the earlier one that has been using $(C info()) function overloads: +) + +$(SHELL +10:30-11:45 "Bike Ride" meeting with 4 attendees +12:30-14:00 Meal, Address: İstanbul +15:30-17:30 "Budget" meeting with 8 attendees +) + +) + +) + +Macros: + SUBTITLE=Member Functions + + DESCRIPTION=Programming in D exercise solutions: Member Functions + + KEYWORDS=d programming book tutorial member functions exercise solutions diff --git a/target/member_functions.d b/target/member_functions.d new file mode 100644 index 0000000..12b3665 --- /dev/null +++ b/target/member_functions.d @@ -0,0 +1,418 @@ +Ddoc + +$(DERS_BOLUMU $(IX member function) $(IX function, member) Member Functions) + +$(P +Although this chapter focuses only on structs, most of the information in this chapter is applicable to classes as well. +) + +$(P +In this chapter we will cover member functions of structs and define the special $(C toString()) member function that is used for representing objects in the $(C string) format. +) + +$(P +When a struct or class is defined, usually a number of functions are also defined alongside with it. We have seen examples of such functions in the earlier chapters: $(C addDuration()) and an overload of $(C info()) have been written specifically to be used with the $(C TimeOfDay) type. In a sense, these two functions define the $(I interface) of $(C TimeOfDay). +) + +$(P +The first parameter of both $(C addDuration()) and $(C info()) has been the $(C TimeOfDay) object that each function would be operating on. Additionally, just like all of the other functions that we have seen so far, both of the functions have been defined at the $(I module level), outside of any other scope. +) + +$(P +The concept of a set of functions determining the interface of a struct is very common. For that reason, functions that are closely related to a type can be defined within the body of that type. +) + +$(H5 Defining member functions) + +$(P +Functions that are defined within the curly brackets of a $(C struct) are called $(I member functions): +) + +--- +struct SomeStruct { + void $(I member_function)(/* the parameters of the function */) { + // ... the definition of the function ... + } + + // ... the other members of the struct ... +} +--- + +$(P +Member functions are accessed the same way as member variables, separated from the name of the object by a dot: +) + +--- + $(I object.member_function(arguments)); +--- + +$(P +We have used member functions before when specifying $(C stdin) and $(C stdout) explicitly during input and output operations: +) + +--- + stdin.readf(" %s", &number); + stdout.writeln(number); +--- + +$(P +The $(C readf()) and $(C writeln()) above are member function calls, operating on the objects $(C stdin) and $(C stdout), respectively. +) + +$(P +Let's define $(C info()) as a member function. Its previous definition has been the following: +) + +--- +void info(in TimeOfDay time) { + writef("%02s:%02s", time.hour, time.minute); +} +--- + +$(P +Making $(C info()) a member function is not as simple as moving its definition inside the struct. The function must be modified in two ways: +) + +--- +struct TimeOfDay { + int hour; + int minute; + + void info() { // (1) + writef("%02s:%02s", hour, minute); // (2) + } +} +--- + +$(OL +$(LI The member function does not take the object explicitly as a parameter.) + +$(LI For that reason, it refers to the member variables simply as $(C hour) and $(C minute).) +) + +$(P +This is because member functions are always called on an existing object. The object is implicitly available to the member function: +) + +--- + auto time = TimeOfDay(10, 30); + $(HILITE time.)info(); +--- + +$(P +The $(C info()) member function is being called on the $(C time) object above. The members $(C hour) and $(C minute) that are referred to within the function definition correspond to the members of the $(C time) object, specifically $(C time.hour) and $(C time.minute). +) + +$(P +The member function call above is almost the equivalent of the following regular function call: +) + +--- + time.info(); // member function + info(time); // regular function (the previous definition) +--- + +$(P +Whenever a member function is called on an object, the members of the object are implicitly accessible by the function: +) + +--- + auto morning = TimeOfDay(10, 0); + auto evening = TimeOfDay(22, 0); + + $(HILITE morning.)info(); + write('-'); + $(HILITE evening.)info(); + writeln(); +--- + +$(P +When called on $(C morning), the $(C hour) and $(C minute) that are used inside the member function refer to $(C morning.hour) and $(C morning.minute). Similarly, when called on $(C evening), they refer to $(C evening.hour) and $(C evening.minute): +) + +$(SHELL +10:00-22:00 +) + +$(H6 $(IX toString) $(C toString()) for $(C string) representations) + +$(P +We have discussed the limitations of the $(C info()) function in the previous chapter. There is at least one more inconvenience with it: Although it prints the time in human-readable format, printing the $(C '-') character and terminating the line still needs to be done explicitly by the programmer. +) + +$(P +However, it would be more convenient if $(C TimeOfDay) objects could be used as easy as fundamental types as in the following code: +) + +--- + writefln("%s-%s", morning, evening); +--- + +$(P +In addition to reducing four lines of code to one, it would also allow printing objects to any stream: +) + +--- + auto file = File("time_information", "w"); + file.writefln("%s-%s", morning, evening); +--- + +$(P +The $(C toString()) member function of user-defined types is treated specially: It is called automatically to produce the $(C string) representations of objects. $(C toString()) must return the $(C string) representation of the object. +) + +$(P +Without getting into more detail, let's first see how the $(C toString()) function is defined: +) + +--- +import std.stdio; + +struct TimeOfDay { + int hour; + int minute; + + string toString() { + return "todo"; + } +} + +void main() { + auto morning = TimeOfDay(10, 0); + auto evening = TimeOfDay(22, 0); + + writefln("%s-%s", morning, evening); +} +--- + +$(P +$(C toString()) does not produce anything meaningful yet, but the output shows that it has been called by $(C writefln()) twice for the two object: +) + +$(SHELL +todo-todo +) + +$(P +Also note that $(C info()) is not needed anymore. $(C toString()) is replacing its functionality. +) + +$(P +The simplest implementation of $(C toString()) would be to call $(C format()) of the $(C std.string) module. $(C format()) works in the same way as the formatted output functions like $(C writef()). The only difference is that instead of printing variables, it returns the formatted result in $(C string) format. +) + +$(P +$(C toString()) can simply return the result of $(C format()) directly: +) + +--- +import std.string; +// ... +struct TimeOfDay { +// ... + string toString() { + return $(HILITE format)("%02s:%02s", hour, minute); + } +} +--- + +$(P +Note that $(C toString()) returns the representation of only $(I this) object. The rest of the output is handled by $(C writefln()): It calls the $(C toString()) member function for the two objects separately, prints the $(C '-') character in between, and finally terminates the line: +) + +$(SHELL +10:00-22:00 +) + +$(P +The definition of $(C toString()) that is explained above does not take any parameters; it simply produces a $(C string) and returns it. An alternative definition of $(C toString()) takes a $(C delegate) parameter. We will see that definition later in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). +) + +$(H6 Example: $(C increment()) member function) + +$(P +Let's define a member function that adds a duration to $(C TimeOfDay) objects. +) + +$(P +Before doing that, let's first correct a design flaw that we have been living with. We have seen in the $(LINK2 /ders/d.en/struct.html, Structs chapter) that adding two $(C TimeOfDay) objects in $(C addDuration()) is not a meaningful operation: +) + +--- +TimeOfDay addDuration(in TimeOfDay start, + in TimeOfDay duration) { // meaningless + // ... +} +--- + +$(P +What is natural to add to a point in time is $(I duration). For example, adding the travel duration to the departure time would result in the arrival time. +) + +$(P +On the other hand, subtracting two points in time is a natural operation, in which case the result would be a $(I duration). +) + +$(P +The following program defines a $(C Duration) struct with minute-precision, and an $(C addDuration()) function that uses it: +) + +--- +struct Duration { + int minute; +} + +TimeOfDay addDuration(in TimeOfDay start, + in Duration duration) { + // Begin with a copy of start + TimeOfDay result = start; + + // Add the duration to it + result.minute += duration.minute; + + // Take care of overflows + result.hour += result.minute / 60; + result.minute %= 60; + result.hour %= 24; + + return result; +} + +unittest { + // A trivial test + assert(addDuration(TimeOfDay(10, 30), Duration(10)) + == TimeOfDay(10, 40)); + + // A time at midnight + assert(addDuration(TimeOfDay(23, 9), Duration(51)) + == TimeOfDay(0, 0)); + + // A time in the next day + assert(addDuration(TimeOfDay(17, 45), Duration(8 * 60)) + == TimeOfDay(1, 45)); +} +--- + +$(P +Let's redefine a similar function this time as a member function. $(C addDuration()) has been producing a new object as its result. Let's define an $(C increment()) member function that will directly modify $(I this) object instead: +) + +--- +struct Duration { + int minute; +} + +struct TimeOfDay { + int hour; + int minute; + + string toString() { + return format("%02s:%02s", hour, minute); + } + + void $(HILITE increment)(in Duration duration) { + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + } + + unittest { + auto time = TimeOfDay(10, 30); + + // A trivial test + time$(HILITE .increment)(Duration(10)); + assert(time == TimeOfDay(10, 40)); + + // 15 hours later must be in the next day + time$(HILITE .increment)(Duration(15 * 60)); + assert(time == TimeOfDay(1, 40)); + + // 22 hours 20 minutes later must be midnight + time$(HILITE .increment)(Duration(22 * 60 + 20)); + assert(time == TimeOfDay(0, 0)); + } +} +--- + +$(P +$(C increment()) increments the value of the object by the specified amount of duration. In a later chapter we will see how the $(I operator overloading) feature of D will make it possible to add a duration by the $(C +=) operator syntax: +) + +--- + time += Duration(10); // to be explained in a later chapter +--- + +$(P +Also note that $(C unittest) blocks can be written inside $(C struct) definitions as well, mostly for testing member functions. It is still possible to move such $(C unittest) blocks outside of the body of the struct: +) + +--- +struct TimeOfDay { + // ... struct definition ... +} + +unittest { + // ... struct tests ... +} +--- + +$(PROBLEM_COK + +$(PROBLEM +Add a $(C decrement()) member function to $(C TimeOfDay), which should reduce the time by the specified amount of duration. Similar to $(C increment()), it should $(I overflow) to the previous day when there is not enough time in the current day. For example, subtracting 10 minutes from 00:05 should result in 23:55. + +$(P +In other words, implement $(C decrement()) to pass the following unit tests: +) + +--- +struct TimeOfDay { + // ... + + void decrement(in Duration duration) { + // ... please implement this function ... + } + + unittest { + auto time = TimeOfDay(10, 30); + + // A trivial test + time.decrement(Duration(12)); + assert(time == TimeOfDay(10, 18)); + + // 3 days and 11 hours earlier + time.decrement(Duration(3 * 24 * 60 + 11 * 60)); + assert(time == TimeOfDay(23, 18)); + + // 23 hours and 18 minutes earlier must be midnight + time.decrement(Duration(23 * 60 + 18)); + assert(time == TimeOfDay(0, 0)); + + // 1 minute earlier + time.decrement(Duration(1)); + assert(time == TimeOfDay(23, 59)); + } +} +--- + +) + +$(PROBLEM +Convert $(C Meeting), $(C Meal), and $(C DailyPlan) overloads of $(C info()) to $(C toString()) member functions as well. (See $(LINK2 /ders/d.en/function_overloading.cozum.html, the exercise solutions of the Function Overloading chapter) for their $(C info()) overloads.) + +$(P +You will notice that in addition to making their respective structs more convenient, the implementations of the $(C toString()) member functions will all consist of single lines. +) + +) + +) + +Macros: + SUBTITLE=Member Functions + + DESCRIPTION=Adding special functionality to structs (and classes) as member functions in the D programming language. + + KEYWORDS=d programming lesson book tutorial member functions diff --git a/target/memory.d b/target/memory.d new file mode 100644 index 0000000..e93224d --- /dev/null +++ b/target/memory.d @@ -0,0 +1,1336 @@ +Ddoc + +$(DERS_BOLUMU $(IX memory management) Memory Management) + +$(P +D is a language that does not require explicit memory management. However, it is important for a system programmer to know how to manage memory when needed for special cases. +) + +$(P +Memory management is a very broad topic. This chapter will introduce only the garbage collector (GC), allocating memory from it, and constructing objects at specific memory locations. I encourage you to research various memory management methods as well as the $(C std.allocator) module, which was still at experimental stage at the time of writing this book. +) + +$(P +As in some of the previous chapters, when I write $(I variable) below, I mean any type of variable including $(C struct) and $(C class) objects. +) + +$(H5 $(IX memory) Memory) + +$(P +Memory is a more significant resource than other system resources because both the running program and its data are located in the memory. The memory belongs ultimately to the operating system, which makes it available to programs to satisfy their needs. The amount of memory that a program uses may increase or decrease according to the immediate needs of a program. When a program terminates, the memory areas that it has been using are automatically returned back to the operating system. +) + +$(P +The memory can be imagined like a large sheet of paper where the values of variables are noted down. Each variable is kept at a specific location where its value is written to and read from as needed. Once the lifetime of a variable ends, its place is used for another variable. +) + +$(P +$(IX &, address of) The $(C &) (address-of) operator is useful when experimenting with memory. For example, the following program prints the addresses of two variables that are defined next to each other: +) + +--- +import std.stdio; + +void main() { + int i; + int j; + + writeln("i: ", $(HILITE &)i); + writeln("j: ", $(HILITE &)j); +} +--- + +$(P +$(I $(B Note:) The addresses would likely be different every time the program is executed. Additionally, the mere act of taking the address of a variable disables the optimization that would otherwise make the variable live on a CPU register.) +) + +$(P +As can be seen from the output, the locations of the variables are four bytes apart: +) + +$(SHELL +i: 7FFF2B633E2$(HILITE 8) +j: 7FFF2B633E2$(HILITE C) +) + +$(P +The last digits of the two addresses indicate that $(C i) lives in a memory location that is right before the location of $(C j): 8 plus 4 (size of $(C int)) makes 12 (C in hexadecimal notation). +) + +$(H5 $(IX garbage collector) $(IX GC) The garbage collector) + +$(P +The dynamic variables that are used in D programs live on memory blocks that are owned by the garbage collector (GC). When the lifetime of a variable ends (i.e. it's no longer being used), that variable is subject to being finalized according to an algorithm that is executed by the GC. If nothing else needs the memory location containing the variable, the memory may be reclaimed to be used for other variables. This algorithm is called $(I garbage collection) and an execution of the algorithm is called a $(I garbage collection cycle). +) + +$(P +The algorithm that the GC executes can roughly be described as the following. All of the memory blocks that can be reached directly or indirectly by pointers (including references) that are in the program roots are scanned. Any memory block that can be reached is tagged as being still in use and all the others are tagged as not being used anymore. The finalizers of objects and structs that live on inaccessible blocks are executed and those memory blocks are reclaimed to be used for future variables. The roots are defined as all of the program stack for every thread, all global and thread-local variables, and any additional data added via $(C GC.addRoot) or $(C GC.addRange). +) + +$(P +Some GC algorithms can move objects around to keep them together in one place in memory. To preserve program correctness, all of the pointers (and references) that point to such objects are automatically modified to point to the new locations. D's current GC does not do this. +) + +$(P +A GC is said to be "precise" if it knows exactly which memory contains pointers and which doesn't. A GC is conservative if it scans all memory as if it were pointers. D's GC is partially conservative, scanning only blocks that contain pointers, but it will scan all data in those blocks. For this reason, in some cases blocks are not ever collected, thereby "leaking" that memory. Large blocks are more likely to be targeted by "false pointers". In some cases it may be recommended to manually free large blocks you are no longer using to avoid this problem. +) + +$(P +The order of executing the finalizers is unspecified. For example, a reference member of an object may be finalized before the object that contains that member. For that reason, no class member that refers to a dynamic variable should be accessed inside the destructor. Note that this is very different from the deterministic destruction order of languages like C++. +) + +$(P +A garbage collection cycle can be started for various reasons like needing to find space for more data. Depending on the GC implementation, because allocating new objects during a garbage collection cycle can interfere with the collection process itself, all of the running threads may have to be halted during collection cycles. Sometimes this can be observed as a hesitation in the execution of the program. +) + +$(P +In most cases the programmer does not need to interfere with the garbage collection process. However, it is possible to delay or dispatch garbage collection cycles as needed by the functions defined in the $(C core.memory) module. +) + +$(H6 $(IX GC.enable) $(IX GC.disable) $(IX GC.collect) Starting and delaying garbage collection cycles) + +$(P +It may be desired to delay the execution of garbage collection cycles during a part of the program where it is important for the program to be responsive. $(C GC.disable) disables garbage collection cycles and $(C GC.enable) enables them again: +) + +--- + GC.disable(); + +// ... a part of the program where responsiveness is important ... + + GC.enable(); +--- + +$(P +However, $(C GC.disable) is not guaranteed to prevent a garbage collection cycle from executing: If the GC needs to obtain more memory from the OS, but it cannot, it still goes ahead and runs a garbage collection cycle as a last-ditch effort to gain some available memory. +) + +$(P +Instead of relying on garbage collections happening automatically at unspecified times, a garbage collection cycle can be started explicitly using $(C GC.collect()): +) + +--- +import core.memory; + +// ... + + GC.collect(); // starts a garbage collection cycle +--- + +$(P +Normally, the GC does not return memory blocks back to the operating system; it holds on to those memory pages for future needs of the program. If desired, the GC can be asked to give unused memory back to the operating system using $(C GC.minimize()): +) + +--- + GC.minimize(); +--- + +$(H5 Allocating memory) + +$(P +System languages allow programmers to specify the memory areas where objects should live. Such memory areas are commonly called $(I buffers). +) + +$(P +There are several methods of allocating memory. The simplest method would be using a fixed-length array: +) + +--- + ubyte[100] buffer; // A memory area of 100 bytes +--- + +$(P +$(IX uninitialized array) $(IX array, uninitialized) $(IX = void) $(C buffer) is ready to be used as a 100-byte memory area. Instead of $(C ubyte), it is also possible to define such buffers as arrays of $(C void), without any association to any type. Since $(C void) cannot be assigned any value, it cannot have the $(C .init) value either. Such arrays must be initialized by the special syntax $(C =void): +) + +--- + void[100] buffer = void; // A memory area of 100 bytes +--- + +$(P +$(IX GC.calloc) We will use only $(C GC.calloc) from the $(C core.memory) module to reserve memory in this chapter. That module has many other features that are useful in various situations. Additionally, the memory allocation functions of the C standard library are avaliable in the $(C std.c.stdlib) module. +) + +$(P +$(C GC.calloc) allocates a memory area of the specified size pre-filled with all 0 values, and returns the beginning address of the allocated area: +) + +--- +import core.memory; +// ... + void * buffer = GC.calloc(100); + // A memory area of 100 zero bytes +--- + +$(P +$(IX void*) Normally, the returned $(C void*) value is cast to a pointer of the proper type: +) + +--- + int * intBuffer = cast(int*)buffer; +--- + +$(P +However, that intermediate step is usually skipped and the return value is cast directly: +) + +--- + int * intBuffer = $(HILITE cast(int*))GC.calloc(100); +--- + +$(P +Instead of arbitrary values like 100, the size of the memory area is usually calculated by multiplying the number of elements needed with the size of each element: +) + +--- + // Allocate room for 25 ints + int * intBuffer = cast(int*)GC.calloc($(HILITE int.sizeof * 25)); +--- + +$(P +$(IX classInstanceSize) $(IX .sizeof, class) There is an important difference for classes: The size of a class variable and the size of a class object are not the same. $(C .sizeof) is the size of a class variable and is always the same value: 8 on 64-bit systems and 4 on 32-bit systems. The size of a class object must be obtained by $(C __traits(classInstanceSize)): +) + +--- + // Allocate room for 10 MyClass objects + MyClass * buffer = + cast(MyClass*)GC.calloc( + $(HILITE __traits(classInstanceSize, MyClass)) * 10); +--- + +$(P +$(IX OutOfMemoryError) When there is not enough memory in the system for the requested size, then a $(C core.exception.OutOfMemoryError) exception is thrown: +) + +--- + void * buffer = GC.calloc(10_000_000_000); +--- + +$(P +The output on a system that does not have that much free space: +) + +$(SHELL +core.exception.OutOfMemoryError +) + +$(P +$(IX GC.free) The memory areas that are allocated from the GC can be returned back to it using $(C GC.free): +) + +--- + GC.free(buffer); +--- + +$(P +However, calling $(C free()) does not necessarily execute the destructors of the variables that live on that memory block. The destructors may be executed explicitly by calling $(C destroy()) for each variable. Note that various internal mechanisms are used to call finalizers on $(C class) and $(C struct) variables during GC collection or freeing. The best way to ensure these are called is to use the $(C new) operator when allocating variables. In that case, $(C GC.free) will call the destructors. +) + +$(P +$(IX GC.realloc) Sometimes the program may determine that a previously allocated memory area is all used up and does not have room for more data. It is possible to $(I extend) a previously allocated memory area by $(C GC.realloc). $(C realloc()) takes the previously allocated memory pointer and the newly requested size, and returns a new area: +) + +--- + void * oldBuffer = GC.calloc(100); +// ... + void * newBuffer = GC.realloc(oldBuffer, 200); +--- + +$(P +$(C realloc()) tries to be efficient by not actually allocating new memory unless it is really necessary: +) + +$(UL + +$(LI If the memory area following the old area is not in use for any other purpose and is large enough to satisfy the new request, $(C realloc()) adds that part of the memory to the old area, extending the buffer $(I in-place).) + +$(LI If the memory area following the old area is already in use or is not large enough, then $(C realloc()) allocates a new larger memory area and copies the contents of the old area to the new one.) + +$(LI It is possible to pass $(C null) as $(C oldBuffer), in which case $(C realloc()) simply allocates new memory.) + +$(LI It is possible to pass a size less than the previous one, in which case the remaining part of the old memory is returned back to the GC.) + +$(LI It is possible to pass 0 as the new size, in which case $(C realloc()) simply frees the memory.) + +) + +$(P +$(C GC.realloc) is adapted from the C standard library function $(C realloc()). For having such a complicated behavior, $(C realloc()) is considered to have a badly designed function interface. A potentially surprising aspect of $(C GC.realloc) is that even if the original memory has been allocated with $(C GC.calloc), the extended part is never cleared. For that reason, when it is important that the memory is zero-initialized, a function like $(C reallocCleared()) below would be useful. We will see the meaning of $(C blockAttributes) later below: +) + +--- +$(CODE_NAME reallocCleared)import core.memory; + +/* Works like GC.realloc but clears the extra bytes if memory + * is extended. */ +void * reallocCleared( + void * buffer, + size_t oldLength, + size_t newLength, + GC.BlkAttr blockAttributes = GC.BlkAttr.NONE, + const TypeInfo typeInfo = null) { + /* Dispatch the actual work to GC.realloc. */ + buffer = GC.realloc(buffer, newLength, + blockAttributes, typeInfo); + + /* Clear the extra bytes if extended. */ + if (newLength > oldLength) { + import std.c.string; + + auto extendedPart = buffer + oldLength; + auto extendedLength = newLength - oldLength; + + memset(extendedPart, 0, extendedLength); + } + + return buffer; +} +--- + +$(P +$(IX memset, std.c.string) The function above uses $(C memset()) from the $(C std.c.string) module to clear the newly extended bytes. $(C memset()) assigns the specified value to the bytes of a memory area specified by a pointer and a length. In the example, it assigns $(C 0) to $(C extendedLength) number of bytes at $(C extendedPart). +) + +$(P +We will use $(C reallocCleared()) in an example below. +) + +$(P +$(IX GC.extend) The behavior of the similar function $(C GC.extend) is not complicated like $(C realloc()); it applies only the first item above: If the memory area cannot be extended in-place, $(C extend()) does not do anything and returns 0. +) + +$(H6 $(IX memory block attribute) $(IX BlkAttr) Memory block attributes) + +$(P +The concepts and the steps of a GC algorithm can be configured to some degree for each memory block by $(C enum BlkAttr). $(C BlkAttr) is an optional parameter of $(C GC.calloc) and other allocation functions. It consists of the following values: +) + +$(UL + +$(LI $(C NONE): The value zero; specifies $(I no attribute).) + +$(LI $(C FINALIZE): Specifies that the objects that live in the memory block should be finalized. + +$(P +Normally, the GC assumes that the lifetimes of objects that live on explicitly-allocated memory locations are under the control of the programmer; it does not finalize objects on such memory areas. $(C GC.BlkAttr.FINALIZE) is for requesting the GC to execute the destructors of objects: +) + +--- + Class * buffer = + cast(Class*)GC.calloc( + __traits(classInstanceSize, Class) * 10, + GC.BlkAttr.FINALIZE); +--- + +$(P +Note that $(C FINALIZE) depends on implementation details properly set up on the block. It is highly recommended to let the GC take care of setting up these details using the $(C new) operator. +) + +) + +$(LI $(C NO_SCAN): Specifies that the memory area should not be scanned by the GC. + +$(P +The byte values in a memory area may accidentally look like pointers to unrelated objects in other parts of the memory. When that happens, the GC would assume that those objects are still in use even after their actual lifetimes have ended. +) + +$(P +A memory block that is known to not contain any object pointers should be marked as $(C GC.BlkAttr.NO_SCAN): +) + +--- + int * intBuffer = + cast(int*)GC.calloc(100, GC.BlkAttr.NO_SCAN); +--- + +$(P +The $(C int) variables placed in that memory block can have any value without concern of being mistaken for object pointers. +) + +) + +$(LI $(C NO_MOVE): Specifies that objects in the memory block should not be moved to other places.) + +$(LI $(C APPENDABLE): This is an internal flag used by the D runtime to aid in fast appending. You should not use this flag when allocating memory.) + +$(LI $(C NO_INTERIOR): Specifies that only pointers to the block's first address exist. This allows one to cut down on "false pointers" because a pointer to the middle of the block does not count when tracing where a pointer goes.) + +) + +$(P +$(IX |) The values of $(C enum BlkAttr) are suitable to be used as bit flags that we saw in $(LINK2 /ders/d.en/bit_operations.html, the Bit Operations chapter). The following is how two attributes can be merged by the $(C |) operator: +) + +--- + const attributes = + GC.BlkAttr.NO_SCAN $(HILITE |) GC.BlkAttr.NO_INTERIOR; +--- + +$(P +Naturally, the GC would be aware only of memory blocks that are reserved by its own functions and scans only those memory blocks. For example, it would not know about a memory block allocated by $(C std.c.stdlib.calloc). +) + +$(P +$(IX GC.addRange) $(IX GC.removeRange) $(IX GC.addRoot) $(C GC.addRange) is for introducing unrelated memory blocks to the GC. The complement function $(C GC.removeRange) should be called before freeing a memory block by other means e.g. by $(C std.c.stdlib.free). +) + +$(P +In some cases, there may be no reference in the program to a memory block even if that memory block has been reserved by the GC. For example, if the only reference to a memory block lives inside a C library, the GC would normally not know about that reference and assume that the memory block is not in use anymore. +) + +$(P +$(C GC.addRoot) introduces a memory block to the GC as a $(I root), to be scanned during collection cycles. All of the variables that can be reached directly or indirectly through that memory block would be marked as alive. The complement function $(C GC.removeRoot) should be called when a memory block is not in use anymore. +) + +$(H6 Example of extending a memory area) + +$(P +Let's design a simple $(C struct) template that works like an array. To keep the example short, let's provide only the functionality of adding and accessing elements. Similar to arrays, let's increase the capacity as needed. The following program uses $(C reallocCleared()), which has been defined above: +) + +--- +$(CODE_NAME Array)$(CODE_XREF reallocCleared)struct Array(T) { + T * buffer; // Memory area that holds the elements + size_t capacity; // The element capacity of the buffer + size_t length; // The number of actual elements + + /* Returns the specified element */ + T element(size_t index) { + import std.string; + enforce(index < length, + format("Invalid index %s", index)); + + return *(buffer + index); + } + + /* Appends the element to the end */ + void append(T element) { + writefln("Appending element %s", length); + + if (length == capacity) { + /* There is no room for the new element; must + * increase capacity. */ + size_t newCapacity = capacity + (capacity / 2) + 1; + increaseCapacity(newCapacity); + } + + /* Place the element at the end */ + *(buffer + length) = element; + ++length; + } + + void increaseCapacity(size_t newCapacity) { + writefln("Increasing capacity from %s to %s", + capacity, newCapacity); + + size_t oldBufferSize = capacity * T.sizeof; + size_t newBufferSize = newCapacity * T.sizeof; + + /* Also specify that this memory block should not be + * scanned for pointers. */ + buffer = cast(T*)$(HILITE reallocCleared)( + buffer, oldBufferSize, newBufferSize, + GC.BlkAttr.NO_SCAN); + + capacity = newCapacity; + } +} +--- + +$(P +The capacity of the array grows by about 50%. For example, after the capacity for 100 elements is consumed, the new capacity would become 151. ($(I The extra 1 is for the case of 0 length, where adding 50% would not grow the array.)) +) + +$(P +The following program uses that template with the $(C double) type: +) + +--- +$(CODE_XREF Array)import std.stdio; +import core.memory; +import std.exception; + +// ... + +void main() { + auto array = Array!double(); + + const count = 10; + + foreach (i; 0 .. count) { + double elementValue = i * 1.1; + array.append(elementValue); + } + + writeln("The elements:"); + + foreach (i; 0 .. count) { + write(array.element(i), ' '); + } + + writeln(); +} +--- + +$(P +The output: +) + +$(SHELL +Adding element with index 0 +Increasing capacity from 0 to 1 +Adding element with index 1 +Increasing capacity from 1 to 2 +Adding element with index 2 +Increasing capacity from 2 to 4 +Adding element with index 3 +Adding element with index 4 +Increasing capacity from 4 to 7 +Adding element with index 5 +Adding element with index 6 +Adding element with index 7 +Increasing capacity from 7 to 11 +Adding element with index 8 +Adding element with index 9 +The elements: +0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 +) + +$(H5 $(IX alignment) Alignment) + +$(P +By default, every object is placed at memory locations that are multiples of an amount specific to the type of that object. That amount is called the $(I alignment) of that type. For example, the alignment of $(C int) is 4 because $(C int) variables are placed at memory locations that are multiples of 4 (4, 8, 12, etc.). +) + +$(P +Alignment is needed for CPU performance or requirements, because accessing misaligned memory addresses can be slower or cause a bus error. In addition, certain types of variables only work properly at aligned addresses. +) + +$(H6 $(IX .alignof) The $(C .alignof) property) + +$(P +$(IX classInstanceAlignment) The $(C .alignof) property of a type is its default alignment value. For classes, $(C .alignof) is the alignment of the class variable, not the class object. The alignment of a class object is obtained by $(C std.traits.classInstanceAlignment). +) + +$(P +The following program prints the alignments of various types: +) + +--- +import std.stdio; +import std.meta; +import std.traits; + +struct EmptyStruct { +} + +struct Struct { + char c; + double d; +} + +class EmptyClass { +} + +class Class { + char c; +} + +void main() { + alias Types = AliasSeq!(char, short, int, long, + double, real, + string, int[int], int*, + EmptyStruct, Struct, + EmptyClass, Class); + + writeln(" Size Alignment Type\n", + "========================="); + + foreach (Type; Types) { + static if (is (Type == class)) { + size_t size = __traits(classInstanceSize, Type); + size_t alignment = $(HILITE classInstanceAlignment!Type); + + } else { + size_t size = Type.sizeof; + size_t alignment = $(HILITE Type.alignof); + } + + writefln("%4s%8s %s", + size, alignment, Type.stringof); + } +} +--- + +$(P +The output of the program may be different in different environments. The following is a sample output: +) + +$(SHELL + Size Alignment Type +========================= + 1 1 char + 2 2 short + 4 4 int + 8 8 long + 8 8 double + 16 16 real + 16 8 string + 8 8 int[int] + 8 8 int* + 1 1 EmptyStruct + 16 8 Struct + 16 8 EmptyClass + 17 8 Class +) + +$(P +We will see later below how variables can be constructed (emplaced) at specific memory locations. For correctness and efficiency, objects must be constructed at addresses that match their alignments. +) + +$(P +Let's consider two $(I consecutive) objects of $(C Class) type above, which are 17 bytes each. Although 0 is not a legal address for a variable on most platforms, to simplify the example let's assume that the first object is at address 0. The 17 bytes of this object would be at adresses from 0 to 16: +) + +$(MONO + $(HILITE 0) 1 16 + ┌────┬────┬─ ... ─┬────┬─ ... + │$(HILITE <────first object────>)│ + └────┴────┴─ ... ─┴────┴─ ... +) + +$(P +$(IX padding) Although the next available address is 17, that location cannot be used for a $(C Class) object because 17 is not a multiple of the alignment value 8 of that type. The nearest possible address for the second object is 24 because 24 is the next smallest multiple of 8. When the second object is placed at that address, there would be unused bytes between the two objects. Those bytes are called $(I padding bytes): +) + +$(P +) + +$(MONO + $(HILITE 0) 1 16 17 23 $(HILITE 24) 25 30 + ┌────┬────┬─ ... ─┬────┬────┬─ ... ─┬────┬────┬────┬─ ... ─┬────┬─ ... + │$(HILITE <────first object────>)│<────$(I padding)────>│$(HILITE <───second object────>)│ + └────┴────┴─ ... ─┴────┴────┴─ ... ─┴────┴────┴────┴─ ... ─┴────┴─ ... +) + +$(P +The following formula can determine the nearest address value that an object can be placed at: +) + +--- + (candidateAddress + alignmentValue - 1) + / alignmentValue + * alignmentValue +--- + +$(P +For that formula to work, the fractional part of the result of the division must be truncated. Since truncation is automatic for integral types, all of the variables above are assumed to be integral types. +) + +$(P +We will use the following function in the examples later below: +) + +--- +$(CODE_NAME nextAlignedAddress)T * nextAlignedAddress(T)(T * candidateAddr) { + import std.traits; + + static if (is (T == class)) { + const alignment = classInstanceAlignment!T; + + } else { + const alignment = T.alignof; + } + + const result = (cast(size_t)candidateAddr + alignment - 1) + / alignment * alignment; + return cast(T*)result; +} +--- + +$(P +That function template deduces the type of the object from its template parameter. Since that is not possible when the type is $(C void*), the type must be provided as an explicit template argument for the $(C void*) overload. That overload can trivially forward the call to the function template above: +) + +--- +$(CODE_NAME nextAlignedAddress_void)void * nextAlignedAddress(T)(void * candidateAddr) { + return nextAlignedAddress(cast(T*)candidateAddr); +} +--- + +$(P +The function template above will be useful below when constructing $(I class) objects by $(C emplace()). +) + +$(P +Let's define one more function template to calculate the total size of an object including the padding bytes that must be placed between two objects of that type: +) + +--- +$(CODE_NAME sizeWithPadding)size_t sizeWithPadding(T)() { + static if (is (T == class)) { + const candidateAddr = __traits(classInstanceSize, T); + + } else { + const candidateAddr = T.sizeof; + } + + return cast(size_t)nextAlignedAddress(cast(T*)candidateAddr); +} +--- + +$(H6 $(IX .offsetof) The $(C .offsetof) property) + +$(P +Alignment is observed for members of user-defined types as well. There may be padding bytes $(I between) members so that the members are aligned according to their respective types. For that reason, the size of the following $(C struct) is not 6 bytes as one might expect, but 12: +) + +--- +struct A { + byte b; // 1 byte + int i; // 4 bytes + ubyte u; // 1 byte +} + +static assert($(HILITE A.sizeof == 12)); // More than 1 + 4 + 1 +--- + +$(P +This is due to padding bytes before the $(C int) member so that it is aligned at an address that is a multiple of 4, as well as padding bytes at the end for the alignment of the entire $(C struct) object itself. +) + +$(P +The $(C .offsetof) property gives the number of bytes a member variable is from the beginning of the object that it is a part of. The following function prints the layout of a type by determining the padding bytes by $(C .offsetof): +) + +--- +$(CODE_NAME printObjectLayout)void printObjectLayout(T)() + if (is (T == struct) || is (T == union)) { + import std.stdio; + import std.string; + + writefln("=== Memory layout of '%s'" ~ + " (.sizeof: %s, .alignof: %s) ===", + T.stringof, T.sizeof, T.alignof); + + /* Prints a single line of layout information. */ + void printLine(size_t offset, string info) { + writefln("%4s: %s", offset, info); + } + + /* Prints padding information if padding is actually + * observed. */ + void maybePrintPaddingInfo(size_t expectedOffset, + size_t actualOffset) { + if (expectedOffset < actualOffset) { + /* There is some padding because the actual offset + * is beyond the expected one. */ + + const paddingSize = actualOffset - expectedOffset; + + printLine(expectedOffset, + format("... %s-byte PADDING", + paddingSize)); + } + } + + /* This is the expected offset of the next member if there + * were no padding bytes before that member. */ + size_t noPaddingOffset = 0; + + /* Note: __traits(allMembers) is a 'string' collection of + * names of the members of a type. */ + foreach (memberName; __traits(allMembers, T)) { + mixin (format("alias member = %s.%s;", + T.stringof, memberName)); + + const offset = member$(HILITE .offsetof); + maybePrintPaddingInfo(noPaddingOffset, offset); + + const typeName = typeof(member).stringof; + printLine(offset, + format("%s %s", typeName, memberName)); + + noPaddingOffset = offset + member.sizeof; + } + + maybePrintPaddingInfo(noPaddingOffset, T.sizeof); +} +--- + +$(P +The following program prints the layout of the 12-byte $(C struct A) that was defined above: +) + +--- +$(CODE_XREF printObjectLayout)struct A { + byte b; + int i; + ubyte u; +} + +void main() { + printObjectLayout!A(); +} +--- + +$(P +The output of the program showns where the total of 6 padding bytes are located inside the object. The first column of the output is the offset from the beginning of the object: +) + +$(SHELL +=== Memory layout of 'A' (.sizeof: $(HILITE 12), .alignof: 4) === + 0: byte b + 1: ... 3-byte PADDING + 4: int i + 8: ubyte u + 9: ... 3-byte PADDING +) + +$(P +One technique of minimizing padding is ordering the members by their sizes from the largest to the smallest. For example, when the $(C int) member is moved to the beginning of the previous $(C struct) then the size of the object would be less: +) + +--- +$(CODE_XREF printObjectLayout)struct B { + $(HILITE int i;) // Moved up inside the struct definition + byte b; + ubyte u; +} + +void main() { + printObjectLayout!B(); +} +--- + +$(P +This time, the size of the object is down to 8 due to just 2 bytes of padding at the end: +) + +$(SHELL +=== Memory layout of 'B' (.sizeof: $(HILITE 8), .alignof: 4) === + 0: int i + 4: byte b + 5: ubyte u + 6: ... 2-byte PADDING +) + +$(H6 $(IX align) The $(C align) attribute) + +$(P +The $(C align) attribute is for specifying alignments of variables, user-defined types, and members of user-defined types. The value provided in parentheses specifies the alignment value. Every definition can be specified separately. For example, the following definition would align $(C S) objects at 2-byte boundaries and its $(C i) member at 1-byte boundaries (1-byte alignment always results in no padding at all): +) + +--- +$(CODE_XREF printObjectLayout)$(HILITE align (2)) // The alignment of 'S' objects +struct S { + byte b; + $(HILITE align (1)) int i; // The alignment of member 'i' + ubyte u; +} + +void main() { + printObjectLayout!S(); +} +--- + +$(P +When the $(C int) member is aligned at a 1-byte boundary, there is no padding before it and this time the size of the object ends up being exactly 6: +) + +$(SHELL +=== Memory layout of 'S' (.sizeof: $(HILITE 6), .alignof: 4) === + 0: byte b + 1: int i + 5: ubyte u +) + +$(P +Although $(C align) can reduce sizes of user-defined types, there can be $(I significant performance penalties) when default alignments of types are not observed (and on some CPUs, using misaligned data can actually crash the program). +) + +$(P +$(C align) can specify the alignment of variables as well: +) + +--- + $(HILITE align (32)) double d; // The alignment of a variable +--- + +$(P +However, objects that are allocated by $(C new) must always be aligned at multiples of the size of the $(C size_t) type because that is what the GC assumes. Doing otherwise is undefined behavior. For example, if $(C size_t) is 8 bytes long, than the alignments of variables allocated by $(C new) must be a multiple of 8. +) + +$(H5 $(IX construction, emplace) $(IX emplace) Constructing variables at specific memory locations) + +$(P +$(IX new) The $(C new) expression achieves three tasks: +) + +$(OL + +$(LI Allocates memory large enough for the object. The newly allocated memory area is considered to be $(I raw), not associated with any type or any object. +) + +$(LI Copies the $(C .init) value of that type on that memory area and executes the constructor of the object on that area. Only after this step the object becomes $(I placed) on that memory area. +) + +$(LI Configures the memory block so it has all the necessary flags and infrastructure to properly destroy the object when freed. +) + +) + +$(P +We have already seen that the first of these tasks can explicitly be achieved by memory allocation functions like $(C GC.calloc). Being a system language, D allows the programmer manage the second step as well. +) + +$(P +Variables can be constructed at specific locations with $(C std.conv.emplace). +) + +$(H6 $(IX emplace, struct) Constructing a struct object at a specific location) + +$(P +$(C emplace()) takes the address of a memory location as its first parameter and constructs an object at that location. If provided, it uses the remaining parameters as the object's constructor arguments: +) + +--- +import std.conv; +// ... + emplace($(I address), /* ... constructor arguments ... */); +--- + +$(P +It is not necessary to specify the type of the object explicitly when constructing a $(C struct) object because $(C emplace()) deduces the type of the object from the type of the pointer. For example, since the type of the following pointer is $(C Student*), $(C emplace()) constructs a $(C Student) object at that address: +) + +--- + Student * objectAddr = nextAlignedAddress(candidateAddr); +// ... + emplace(objectAddr, name, id); +--- + +$(P +The following program allocates a memory area large enough for three objects and constructs them one by one at aligned addresses inside that memory area: +) + +--- +$(CODE_XREF sizeWithPadding)$(CODE_XREF nextAlignedAddress)import std.stdio; +import std.string; +import core.memory; +import std.conv; + +// ... + +struct Student { + string name; + int id; + + string toString() { + return format("%s(%s)", name, id); + } +} + +void main() { + /* Some information about this type. */ + writefln("Student.sizeof: %#x (%s) bytes", + Student.sizeof, Student.sizeof); + writefln("Student.alignof: %#x (%s) bytes", + Student.alignof, Student.alignof); + + string[] names = [ "Amy", "Tim", "Joe" ]; + auto totalSize = sizeWithPadding!Student() * names.length; + + /* Reserve room for all Student objects. + * + * Warning!The objects that are accessible through this + * slice are not constructed yet; they should not be + * accessed until after they are properly constructed. */ + Student[] students = + (cast(Student*)GC.calloc(totalSize))[0 .. names.length]; + + foreach (int i, name; names) { + Student * candidateAddr = students.ptr + i; + Student * objectAddr = + nextAlignedAddress(candidateAddr); + writefln("address of object %s: %s", i, objectAddr); + + const id = 100 + i; + $(HILITE emplace)(objectAddr, name, id); + } + + /* All of the objects are constructed and can be used. */ + writeln(students); +} +--- + +$(P +The output of the program: +) + +$(SHELL +Student.sizeof: 0x18 (24) bytes +Student.alignof: 0x8 (8) bytes +address of object 0: 7F1532861F00 +address of object 1: 7F1532861F18 +address of object 2: 7F1532861F30 +[Amy(100), Tim(101), Joe(102)] +) + +$(H6 $(IX emplace, class) Constructing a class object at a specific location) + +$(P +Class variables need not be of the exact type of class objects. For example, a class variable of type $(C Animal) can refer to a $(C Cat) object. For that reason, $(C emplace()) does not determine the type of the object from the type of the memory pointer. Instead, the actual type of the object must be explicitly specified as a template argument of $(C emplace()). ($(I $(B Note:) Additionally, a class pointer is a pointer to a class variable, not to a class object. For that reason, specifying the actual type allows the programmer to specify whether to emplace a class object or a class variable.)) +) + +$(P +$(IX void[]) The memory location for a class object must be specified as a $(C void[]) slice with the following syntax: +) + +--- + Type variable = + emplace!$(I Type)($(I voidSlice), + /* ... constructor arguments ... */); +--- + +$(P +$(C emplace()) constructs a class $(I object) at the location specified by the slice and returns a class $(I variable) for that object. +) + +$(P +Let's use $(C emplace()) on objects of an $(C Animal) hierarchy. The objects of this hierarchy will be placed $(I side-by-side) on a piece of memory that is allocated by $(C GC.calloc). To make the example more interesting, we will ensure that the subclasses have different sizes. This will be useful to demonstrate how the address of a subsequent object can be determined depending on the size of the previous one. +) + +--- +$(CODE_NAME Animal)interface Animal { + string sing(); +} + +class Cat : Animal { + string sing() { + return "meow"; + } +} + +class Parrot : Animal { + string[] lyrics; + + this(string[] lyrics) { + this.lyrics = lyrics; + } + + string sing() { + /* std.algorithm.joiner joins elements of a range with + * the specified separator. */ + return lyrics.joiner(", ").to!string; + } +} +--- + +$(P +The buffer that holds the objects will be allocated with $(C GC.calloc): +) + +--- + auto capacity = 10_000; + void * buffer = GC.calloc(capacity); +--- + +$(P +Normally, it must be ensured that there is always available capacity for objects. We will ignore that check here to keep the example simple and assume that the objects in the example will fit in ten thousand bytes. +) + +$(P +The buffer will be used for constructing a $(C Cat) and a $(C Parrot) object: +) + +--- + Cat cat = emplace!Cat(catPlace); +// ... + Parrot parrot = + emplace!Parrot(parrotPlace, [ "squawk", "arrgh" ]); +--- + +$(P +Note that the constructor argument of $(C Parrot) is specified after the address of the object. +) + +$(P +The variables that $(C emplace()) returns will be stored in an $(C Animal) slice later to be used in a $(C foreach) loop: +) + +--- + Animal[] animals; +// ... + animals ~= cat; +// ... + animals ~= parrot; + + foreach (animal; animals) { + writeln(animal.sing()); + } +--- + +$(P +More explanations are inside the code comments: +) + +--- +$(CODE_XREF Animal)$(CODE_XREF nextAlignedAddress)$(CODE_XREF nextAlignedAddress_void)import std.stdio; +import std.algorithm; +import std.conv; +import core.memory; + +// ... + +void main() { + /* A slice of Animal variables (not Animal objects). */ + Animal[] animals; + + /* Allocating a buffer with an arbitrary capacity and + * assuming that the two objects in this example will fit + * in that area. Normally, this condition must be + * validated. */ + auto capacity = 10_000; + void * buffer = GC.calloc(capacity); + + /* Let's first place a Cat object. */ + void * catCandidateAddr = buffer; + void * catAddr = nextAlignedAddress!Cat(catCandidateAddr); + writeln("Cat address : ", catAddr); + + /* Since emplace() requires a void[] for a class object, + * we must first produce a slice from the pointer. */ + size_t catSize = __traits(classInstanceSize, Cat); + void[] catPlace = catAddr[0..catSize]; + + /* Construct a Cat object inside that memory slice and + * store the returned class variable for later use. */ + Cat cat = $(HILITE emplace!Cat)(catPlace); + animals ~= cat; + + /* Now construct a Parrot object at the next available + * address that satisfies the alignment requirement. */ + void * parrotCandidateAddr = catAddr + catSize; + void * parrotAddr = + nextAlignedAddress!Parrot(parrotCandidateAddr); + writeln("Parrot address: ", parrotAddr); + + size_t parrotSize = __traits(classInstanceSize, Parrot); + void[] parrotPlace = parrotAddr[0..parrotSize]; + + Parrot parrot = + $(HILITE emplace!Parrot)(parrotPlace, [ "squawk", "arrgh" ]); + animals ~= parrot; + + /* Use the objects. */ + foreach (animal; animals) { + writeln(animal.sing()); + } +} +--- + +$(P +The output: +) + +$(SHELL +Cat address : 7F0E343A2000 +Parrot address: 7F0E343A2018 +meow +squawk, arrgh +) + +$(P +Instead of repeating the steps inside $(C main()) for each object, a function template like $(C newObject(T)) would be more useful. +) + +$(H5 Destroying objects explicitly) + +$(P +The reverse operations of the $(C new) operator are destroying an object and returning the object's memory back to the GC. Normally, these operations are executed automatically at unspecified times. +) + +$(P +However, sometimes it is necessary to execute destructors at specific points in the program. For example, an object may be closing a $(C File) member in its destructor and the destructor may have to be executed immediately when the lifetime of the object ends. +) + +$(P +$(IX destroy) $(C destroy()) calls the destructor of an object: +) + +--- + destroy(variable); +--- + +$(P +$(IX .init) After executing the destructor, $(C destroy()) sets the variable to its $(C .init) state. Note that the $(C .init) state of a class variable is $(C null); so, a class variable cannot be used once destroyed. $(C destroy()) merely executes the destructor. It is still up to the GC when to reuse the piece of memory that used to be occupied by the destroyed object. +) + +$(P +$(B Warning:) When used with a $(I struct) pointer, $(C destroy()) must receive the pointee, not the pointer. Otherwise, the pointer would be set to $(C null) but the object would not be destroyed: +) + +--- +import std.stdio; + +struct S { + int i; + + this(int i) { + this.i = i; + writefln("Constructing object with value %s", i); + } + + ~this() { + writefln("Destroying object with value %s", i); + } +} + +void main() { + auto p = new S(42); + + writeln("Before destroy()"); + destroy($(HILITE p)); // ← WRONG USAGE + writeln("After destroy()"); + + writefln("p: %s", p); + + writeln("Leaving main"); +} +--- + +$(P +When $(C destroy()) receives a pointer, it is the pointer that gets destroyed (i.e. the pointer becomes $(C null)): +) + +$(SHELL +Constructing object with value 42 +Before destroy() +After destroy() $(SHELL_NOTE_WRONG The object is not destroyed before this line) +p: null $(SHELL_NOTE_WRONG Instead, the pointer becomes null) +Leaving main +Destroying object with value 42 +) + +$(P +For that reason, when used with a struct pointer, $(C destroy()) must receive the pointee: +) + +--- + destroy($(HILITE *p)); // ← Correct usage +--- + +$(P +This time the destructor is executed at the right spot and the pointer is not set to $(C null): +) + +$(SHELL +Constructing object with value 42 +Before destroy() +Destroying object with value 42 $(SHELL_NOTE Destroyed at the right spot) +After destroy() +p: 7FB64FE3F200 $(SHELL_NOTE The pointer is not null) +Leaving main +Destroying object with value 0 $(SHELL_NOTE Once more for S.init) +) + +$(P +The last line is due to executing the destructor one more time for the same object, which now has the value $(C S.init). +) + +$(H5 $(IX construction, by name) Constructing objects at run time by name) + +$(P +$(IX factory) $(IX Object) The $(C factory()) member function of $(C Object) takes the fully qualified name of a class type as parameter, constructs an object of that type, and returns a class variable for that object: +) + +--- +$(HILITE module test_module); + +import std.stdio; + +interface Animal { + string sing(); +} + +class Cat : Animal { + string sing() { + return "meow"; + } +} + +class Dog : Animal { + string sing() { + return "woof"; + } +} + +void main() { + string[] toConstruct = [ "Cat", "Dog", "Cat" ]; + + Animal[] animals; + + foreach (typeName; toConstruct) { + /* The pseudo variable __MODULE__ is always the name + * of the current module, which can be used as a + * string literal at compile time. */ + const fullName = __MODULE__ ~ '.' ~ typeName; + writefln("Constructing %s", fullName); + animals ~= cast(Animal)$(HILITE Object.factory)(fullName); + } + + foreach (animal; animals) { + writeln(animal.sing()); + } +} +--- + +$(P +Although there is no explicit $(C new) expression in that program, three class objects are created and added to the $(C animals) slice: +) + +$(SHELL +Constructing test_module.Cat +Constructing test_module.Dog +Constructing test_module.Cat +meow +woof +meow +) + +$(P +Note that $(C Object.factory()) takes the fully qualified name of the type of the object. Also, the return type of $(C factory()) is $(C Object); so, it must be cast to the actual type of the object before being used in the program. +) + +$(H5 Summary) + +$(UL +$(LI The garbage collector scans the memory at unspecified times, determines the objects that cannot possibly be reached anymore by the program, destroys them, and reclaims their memory locations.) + +$(LI The operations of the GC may be controlled by the programmer to some extent by $(C GC.collect), $(C GC.disable), $(C GC.enable), $(C GC.minimize), etc.) + +$(LI $(C GC.calloc) and other functions reserve memory, $(C GC.realloc) extends a previously allocated memory area, and $(C GC.free) returns it back to the GC.) + +$(LI It is possible to mark the allocated memory by attributes like $(C GC.BlkAttr.NO_SCAN), $(C GC.BlkAttr.NO_INTERIOR), etc.) + +$(LI The $(C .alignof) property is the default memory alignment of a type. Alignment must be obtained by $(C classInstanceAlignment) for class $(I objects).) + +$(LI The $(C .offsetof) property is the number of bytes a member is from the beginning of the object that it is a part of.) + +$(LI The $(C align) attribute specifies the alignment of a variable, a user-defined type, or a member.) + +$(LI $(C emplace()) takes a pointer when constructing a $(C struct) object, a $(C void[]) slice when constructing a $(C class) object.) + +$(LI $(C destroy()) executes the destructor of objects. (One must destroy the struct pointee, not the struct pointer.)) + +$(LI $(C Object.factory()) constructs objects with their fully qualified type names.) + +) + +macros: + SUBTITLE=Memory Management + + DESCRIPTION=The memory, the garbage collector, and managing memory explicitly. + + KEYWORDS=d programming language tutorial book integer gc memory diff --git a/target/mixin.d b/target/mixin.d new file mode 100644 index 0000000..024a3c0 --- /dev/null +++ b/target/mixin.d @@ -0,0 +1,530 @@ +Ddoc + +$(DERS_BOLUMU $(IX mixin) Mixins) + +$(P +Mixins are for $(I mixing in) generated code into the source code. The mixed in code may be generated as a template instance or a $(C string). +) + +$(H5 $(IX template mixin) Template mixins) + +$(P +We have seen in the $(LINK2 /ders/d.en/templates.html, Templates) and $(LINK2 /ders/d.en/templates_more.html, More Templates) chapters that templates define code as a pattern, for the compiler to generate actual instances from that pattern. Templates can generate functions, structs, unions, classes, interfaces, and any other legal D code. +) + +$(P +Template mixins insert instantiations of templates into the code by the $(C mixin) keyword: +) + +--- + mixin $(I a_template)!($(I template_parameters)) +--- + +$(P +As we will see in the example below, the $(C mixin) keyword is used in the definitions of template mixins as well. +) + +$(P +The instantiation of the template for the specific set of template parameters is inserted into the source code right where the $(C mixin) keyword appears. +) + +$(P +For example, let's have a template that defines both an array of edges and a pair of functions that operate on those edges: +) + +--- +$(CODE_NAME EdgeArrayFeature)$(HILITE mixin) template EdgeArrayFeature(T, size_t count) { + T[count] edges; + + void setEdge(size_t index, T edge) { + edges[index] = edge; + } + + void printEdges() { + writeln("The edges:"); + + foreach (i, edge; edges) { + writef("%s:%s ", i, edge); + } + + writeln(); + } +} +--- + +$(P +That template leaves the type and number of array elements flexible. The instantiation of that template for $(C int) and $(C 2) would be mixed in by the following syntax: +) + +--- + $(HILITE mixin) EdgeArrayFeature!(int, 2); +--- + +$(P +For example, the $(C mixin) above can insert the two-element $(C int) array and the two functions that are generated by the template right inside a $(C struct) definition: +) + +--- +$(CODE_NAME Line)$(CODE_XREF EdgeArrayFeature)struct Line { + mixin EdgeArrayFeature!(int, 2); +} +--- + +$(P +As a result, $(C Line) ends up defining a member array and two member functions: +) + +--- +$(CODE_XREF Line)import std.stdio; + +void main() { + auto line = Line(); + line.setEdge(0, 100); + line.setEdge(1, 200); + line.printEdges(); +} +--- + +$(P +The output: +) + +$(SHELL +The edges: +0:100 1:200 +) + +$(P +Another instantiation of the same template can be used e.g. inside a function: +) + +--- +$(CODE_XREF EdgeArrayFeature)struct Point { + int x; + int y; +} + +void main() { + $(HILITE mixin) EdgeArrayFeature!($(HILITE Point), 5); + + setEdge(3, Point(3, 3)); + printEdges(); +} +--- + +$(P +That $(C mixin) inserts an array and two local functions inside $(C main()). The output: +) + +$(SHELL +The edges: +0:Point(0, 0) 1:Point(0, 0) 2:Point(0, 0) 3:Point(3, 3) 4:Point(0, 0) +) + +$(H6 $(IX local import) $(IX import, local) Template mixins must use local imports) + +$(P +Mixing in template instantiations $(I as is) can cause problems about the modules that the template itself is making use of: Those modules may not be available at the $(C mixin) site. +) + +$(P +Let's consider the following module named $(C a). Naturally, it would have to import the $(C std.string) module that it is making use of: +) + +--- +module a; + +$(HILITE import std.string;) $(CODE_NOTE wrong place) + +mixin template A(T) { + string a() { + T[] array; + // ... + return format("%(%s, %)", array); + } +} +--- + +$(P +However, if $(C std.string) is not imported at the actual $(C mixin) site, then the compiler would not be able to find the definition of $(C format()) at that point. Let's consider the following program that imports $(C a) and tries to mix in $(C A!int) from that module: +) + +--- +import a; + +void main() { + mixin A!int; $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: $(HILITE undefined identifier format) +Error: mixin deneme.main.A!int error instantiating +) + +$(P +For that reason, the modules that template mixins use must be imported in local scopes: +) + +--- +module a; + +mixin template A(T) { + string a() { + $(HILITE import std.string;) $(CODE_NOTE right place) + + T[] array; + // ... + return format("%(%s, %)", array); + } +} +--- + +$(P +As long as it is inside the template definition, the $(C import) directive above can be outside of the $(C a()) function as well. +) + +$(H6 $(IX this, template parameter) Identifying the type that is mixing in) + +$(P +Sometimes a mixin may need to identify the actual type that is mixing it in. That information is available through $(I $(C this) template parameters) as we have seen in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter): +) + +--- +mixin template MyMixin(T) { + void foo$(HILITE (this MixingType))() { + import std.stdio; + writefln("The actual type that is mixing in: %s", + $(HILITE MixingType).stringof); + } +} + +struct MyStruct { + mixin MyMixin!(int); +} + +void main() { + auto a = MyStruct(); + a.foo(); +} +--- + +$(P +The output of the program shows that the actual type is available inside the template as $(C MyStruct): +) + +$(SHELL +The actual type that is mixing in: MyStruct +) + +$(H5 $(IX string mixin) String mixins) + +$(P +Another powerful feature of D is being able to insert code as $(C string) as long as that string is known at compile time. The syntax of string mixins requires the use of parentheses: +) + +--- + mixin $(HILITE $(PARANTEZ_AC))$(I compile_time_generated_string)$(HILITE $(PARANTEZ_KAPA)) +--- + +$(P +For example, the $(I hello world) program can be written with a $(C mixin) as well: +) + +--- +import std.stdio; + +void main() { + mixin (`writeln("hello world");`); +} +--- + +$(P +The string gets inserted as code and the program produces the following output: +) + +$(SHELL +hello world +) + +$(P +We can go further and insert all of the program as a string mixin: +) + +--- +mixin ( +`import std.stdio; void main() { writeln("hello world"); }` +); +--- + +$(P +Obviously, there is no need for mixins in these examples, as the strings could have been written as code as well. +) + +$(P +The power of string mixins comes from the fact that the code can be generated at compile time. The following example takes advantage of CTFE to generate statements at compile time: +) + +--- +import std.stdio; + +string printStatement(string message) { + return `writeln("` ~ message ~ `");`; +} + +void main() { + mixin (printStatement("hello world")); + mixin (printStatement("hi world")); +} +--- + +$(P +The output: +) + +$(SHELL +hello world +hi world +) + +$(P +Note that the $(STRING "writeln") expressions are not executed inside $(C printStatement()). Rather, $(C printStatement()) generates code that includes $(C writeln()) expressions that are executed inside $(C main()). The generated code is the equivalent of the following: +) + +--- +import std.stdio; + +void main() { + writeln("hello world"); + writeln("hi world"); +} +--- + +$(H5 $(IX name space, mixin) Mixin name spaces) + +$(P +It is possible to avoid and resolve name ambiguities in template mixins. +) + +$(P +For example, there are two $(C i) variables defined inside $(C main()) in the following program: one is defined explicitly in $(C 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: +) + +--- +import std.stdio; + +template Templ() { + $(HILITE int i;) + + void print() { + writeln(i); // Always the 'i' that is defined in Templ + } +} + +void main() { + $(HILITE int i;) + mixin Templ; + + i = 42; // Sets the 'i' that is defined explicitly in main + writeln(i); // Prints the 'i' that is defined explicitly in main + print(); // Prints the 'i' that is mixed in +} +--- + +$(P +As implied in the comments above, template mixins define a name space for their contents and the names that appear in the template code are first looked up in that name space. We can see this in the behavior of $(C print()): +) + +$(SHELL +42 +0 $(SHELL_NOTE printed by print()) +) + +$(P +The compiler cannot resolve name conflicts if the same name is defined by more than one template mixin. Let's see this in a short program that mixes in the same template instance twice: +) + +--- +template Templ() { + int i; +} + +void main() { + mixin Templ; + mixin Templ; + + i = 42; $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: deneme.main.Templ!().i at ... $(HILITE conflicts with) +deneme.main.Templ!().i at ... +) + +$(P +To prevent this, it is possible to assign name space identifiers for template mixins and refer to contained names by those identifiers: +) + +--- + mixin Templ $(HILITE A); // Defines A.i + mixin Templ $(HILITE B); // Defines B.i + + $(HILITE A.)i = 42; // ← not ambiguous anymore +--- + +$(P +String mixins do not have these name space features. However, it is trivial to use a string as a template mixin simply by passing it through a simple wrapper template. +) + +$(P +Let's first see a similar name conflict with string mixins: +) + +--- +void main() { + mixin ("int i;"); + mixin ("int i;"); $(DERLEME_HATASI) + + i = 42; +} +--- + +$(SHELL +Error: declaration deneme.main.i is $(HILITE already defined) +) + +$(P +One way of resolving this issue is to pass the $(C string) through the following trivial template that effectively converts a string mixin to a template mixin: +) + +--- +template Templatize(string str) { + mixin (str); +} + +void main() { + mixin Templatize!("int i;") A; // Defines A.i + mixin Templatize!("int i;") B; // Defines B.i + + A.i = 42; // ← not ambiguous anymore +} +--- + +$(H5 $(IX operator overloading, mixin) String mixins in operator overloading) + +$(P +We have seen in $(LINK2 /ders/d.en/operator_overloading.html, the Operator Overloading chapter) how $(C mixin) expressions helped with the definitions of some of the operators. +) + +$(P +In fact, the reason why most operator member functions are defined as templates is to make the operators available as $(C string) values so that they can be used for code generation. We have seen examples of this both in that chapter and its exercise solutions. +) + +$(H5 $(IX destructor, mixin) Mixed in destructors) + +$(P +It is possible to mix in multiple destructors to a user defined type. Those destructors are called in the reverse order of the $(C mixin) statements that added them. This feature allows mixing in different resources to a type, each introducing its own cleanup code. +) + +--- +import std.stdio; + +mixin template Foo() { + ~this() { + writeln("Destructor mixed-in by Foo"); + } +} + +mixin template Bar() { + ~this() { + writeln("Destructor mixed-in by Bar"); + } +} + +struct S { + ~this() { + writeln("Actual destructor"); + } + mixin Foo; + mixin Bar; +} + +void main() { + auto s = S(); +} +--- + +$(SHELL +Destructor mixed-in by Bar +Destructor mixed-in by Foo +Actual destructor +) + +$(P +Due to a bug as of this writing, the same behavior does not apply to other special functions like constructors. Additionally, a destructor mixed in by a string mixin does conflict with the existing destructor of the type. +) + +$(H5 Example) + +$(P +($(I $(B Note:) Specifying predicates as strings was used more commonly before the lambda syntax was added to D. Although string predicates as in this example are still used in Phobos, the $(C =>) lambda syntax may be more suitable in most cases.)) +) + +$(P +Let's consider the following function template that takes an array of numbers and returns another array that consists of the elements that satisfy a specific condition: +) + +--- +int[] filter($(HILITE string predicate))(in int[] numbers) { + int[] result; + + foreach (number; numbers) { + if ($(HILITE mixin (predicate))) { + result ~= number; + } + } + + return result; +} +--- + +$(P +That function template takes the filtering condition as its template parameter and inserts that condition directly into an $(C if) statement as is. +) + +$(P +For that condition to choose numbers that are e.g. less than 7, the $(C if) condition should look like the following code: +) + +--- + if (number < 7) { +--- + +$(P +The users of $(C filter()) template can provide the condition as a $(C string): +) + +--- + int[] numbers = [ 1, 8, 6, -2, 10 ]; + int[] chosen = filter!$(HILITE "number < 7")(numbers); +--- + +$(P +Importantly, the name used in the template parameter must match the name of the variable used in the implementation of $(C filter()). So, the template must document what that name should be and the users must use that name. +) + +$(P +Phobos uses names consisting of single letters like a, b, n, etc. +) + +macros: + SUBTITLE=Mixins + + DESCRIPTION=Template mixins and string mixins. + + KEYWORDS=d programming language tutorial book mixin + +SOZLER= +$(katma) +$(sablon) diff --git a/target/modules.d b/target/modules.d new file mode 100644 index 0000000..ba8d89c --- /dev/null +++ b/target/modules.d @@ -0,0 +1,622 @@ +Ddoc + +$(DERS_BOLUMU $(IX module) Modules and Libraries) + +$(P +The building blocks of D programs (and libraries) are modules. +) + +$(P +D modules are based on a simple concept: Every source file is a module. Accordingly, the single files that we have been writing our programs in have all been individual modules. +) + +$(P +By default, the name of a module is the same as its filename without the $(C .d) extension. When explicitly specified, the name of the module is defined by the $(C module) keyword, which must appear as the first non-comment line in the source file. +) + +$(P +For example, assuming that the name of a source file is "cat.d", the name of the module would be specified by the $(C module) keyword: +) + +--- +module cat; + +class Cat { + // ... +} +--- + +$(P +The $(C module) line is optional if the module is not part of any package (see below). When not specified, it is the same as the file name without the $(C .d) extension. +) + +$(H6 $(IX static this, module) $(IX static ~this, module) $(IX this, static, module) $(IX ~this, static, module) $(IX module constructor, thread-local) $(C static this()) and $(C static ~this())) + +$(P +$(C static this()) and $(C static ~this()) at module scope are similar to their $(C struct) and $(C class) counterparts: +) + +--- +module cat; + +static this() { + // ... the initial operations of the module ... +} + +static ~this() { + // ... the final operations of the module ... +} +--- + +$(P +Code that are in these scopes are executed once for each thread. (Note that most programs consist of a single thread that starts executing the $(C main()) function.) Code that should be executed only once for the entire program (e.g. initializing $(C shared) and $(C immutable) variables) must be defined in $(C shared static this()) and $(C shared static ~this()) blocks, which will be covered in $(LINK2 /ders/d.en/concurrency_shared.html, the Data Sharing Concurrency chapter). +) + +$(H6 File and module names) + +$(P +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. +) + +$(P +For portability reasons, I recommend that you use only lower case ASCII letters in file names. For example, "resume.d" would be a suitable file name for a class named $(C Résumé). +) + +$(P +Accordingly, the name of the module would consist of ASCII letters as well: +) + +--- +module resume; // Module name consisting of ASCII letters + +class Résumé { // Program code consisting of Unicode characters + // ... +} +--- + +$(H5 $(IX package, definition) Packages) + +$(P +A combination of related modules are called a $(I 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. +) + +$(P +For example, if "cat.d" and "dog.d" are inside the directory "animal", then specifying the directory name along with the module name makes them be a part of the same package: +) + +--- +module $(HILITE animal.)cat; + +class Cat { + // ... +} +--- + +$(P +Similarly, for the $(C dog) module: +) + +--- +module $(HILITE animal.)dog; + +class Dog { + // ... +} +--- + +$(P +For modules that are parts of packages, the $(C module) line is not optional and the whole module name including the package name must be specified. +) + +$(P +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 "animal" directory included a "vertebrate" directory, the name of a module inside that directory would include $(C vertebrate) as well: +) + +--- +module animal.vertebrate.cat; +--- + +$(P +The directory hierarchies can be arbitrarily complex depending on the needs of the program. Relatively short programs usually have all of their source files in a single directory. +) + +$(H5 Importing modules) + +$(P +$(IX import) The $(C 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; +--- + +$(P +The module name may contain the package name as well. For example, the $(C std.) part above indicates that $(C stdio) is a module that is a part of the $(C std) package. +) + +$(P +The $(C animal.cat) and $(C animal.dog) modules would be imported similarly. Let's assume that the following code is inside a file named "deneme.d": +) + +--- +module deneme; // the name of this module + +import animal.cat; // a module that it uses +import animal.dog; // another module that it uses + +void main() { + auto cat = new Cat(); + auto dog = new Dog(); +} +--- + +$(P +$(I $(B Note:) As described below, for the program to be built correctly, those module files must also be provided to the linker.) +) + +$(P +More than one module can be imported at the same time: +) + +--- +import animal.cat, animal.dog; +--- + +$(H6 $(IX selective import) $(IX import, selective) Selective imports) + +$(P +$(IX :, import) Instead of importing a module as a whole with all of its names, it is possible to import just specific names from it. +) + +--- +import std.stdio $(HILITE : writeln;) + +// ... + + write$(HILITE f)ln("Hello %s.", name); $(DERLEME_HATASI) +--- + +$(P +The code above cannot be compiled because only $(C writeln) is imported, not $(C writefln). +) + +$(P +Selective imports are considered to be better than importing an entire module because it reduces the chance of $(I name collisions). As we will see in an example below, a name collision can occur when the same name appears in more than one imported module. +) + +$(P +Selective imports may reduce compilation times as well because the compiler needs to compile only the parts of a module that are actually imported. On the other hand, selective imports require more work as every imported name must be specified separately on the $(C import) line. +) + +$(P +This book does not take advantage of selective imports mostly for brevity. +) + +$(H6 $(IX local import) $(IX import, local) Local imports) + +$(P +So far we have always imported all of the required modules at the tops of programs: +) + +--- +import std.stdio; $(CODE_NOTE at the top) +import std.string; $(CODE_NOTE at the top) + +// ... the rest of the module ... +--- + +$(P +Instead, modules can be imported at any other line of the source code. For example, the two functions of the following program import the modules that they need in their own scopes: +) + +--- +string makeGreeting(string name) { + $(HILITE import std.string;) + + string greeting = format("Hello %s", name); + return greeting; +} + +void interactWithUser() { + $(HILITE import std.stdio;) + + write("Please enter your name: "); + string name = readln(); + writeln(makeGreeting(name)); +} + +void main() { + interactWithUser(); +} +--- + +$(P +Local imports are recommended over global imports because instead of importing every module unconditionally at the top, the compiler can import only the ones that are in the scopes that are actually used. If the compiler knows that the program never calls a function, it can ignore the import directives inside that function. +) + +$(P +Additionally, a locally imported module is accessible only inside that local scope, further reducing the risk of name collisions. +) + +$(P +We will later see in $(LINK2 /ders/d.en/mixin.html, the Mixins chapter) that local imports are in fact required for $(I template mixins.) +) + +$(P +The examples throughout this book do not take advantage of local imports mostly because local imports were added to D after the start of writing this book. +) + +$(H6 Locations of modules) + +$(P +The compiler finds the module files by converting the package and module names directly to directory and file names. +) + +$(P +For example, the previous two modules would be located as "animal/cat.d" and "animal/dog.d", respectively (or "animal\cat.d" and "animal\dog.d", depending on the file system). Considering the main source file as well, the program above consists of three files. +) + +$(H6 Long and short module names) + +$(P +The names that are used in the program may be spelled out with the module and package names: +) + +--- + auto cat0 = Cat(); + auto cat1 = animal.cat.Cat(); // same as above +--- + +$(P +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. +) + +$(P +The following program is spelling out the long names to distinguish between two separate $(C Jaguar) structs that are defined in two separate modules: $(C animal) and $(C car): +) + +--- +import animal.jaguar; +import car.jaguar; + +// ... + + auto conflicted = Jaguar(); $(DERLEME_HATASI) + + auto myAnimal = animal.jaguar.Jaguar(); $(CODE_NOTE compiles) + auto myCar = car.jaguar.Jaguar(); $(CODE_NOTE compiles) +--- + +$(H6 Renamed imports) + +$(P +$(IX renamed import) It is possible to rename imported modules either for convenience or to resolve name conflicts: +) + +--- +import $(HILITE carnivore =) animal.jaguar; +import $(HILITE vehicle =) car.jaguar; + +// ... + + auto myAnimal = $(HILITE carnivore.)Jaguar(); $(CODE_NOTE compiles) + auto myCar = $(HILITE vehicle.)Jaguar(); $(CODE_NOTE compiles) +--- + +$(P +Instead of renaming the entire import, it is possible to rename individual imported symbols. +) + +$(P +For example, when the following code is compiled with the $(C -w) compiler switch, the compiler would warn that $(C sort()) $(I function) should be preferred instead of $(C .sort) $(I property): +) + +--- +import std.stdio; +import std.algorithm; + +// ... + + auto arr = [ 2, 10, 1, 5 ]; + arr$(HILITE .sort); $(CODE_NOTE_WRONG compilation WARNING) + writeln(arr); +--- + +$(SHELL +Warning: use std.algorithm.sort instead of .sort property +) + +$(P +$(I $(B Note:) The $(C arr.sort) expression above is the equivalent of $(C sort(arr)) but it is written in the UFCS syntax, which we will see in $(LINK2 /ders/d.en/ufcs.html, a later chapter).) +) + +$(P +One solution in this case is to import $(C std.algorithm.sort) by renaming it. The new name $(C algSort) below means the $(C sort()) $(I function) and the compiler warning is eliminated: +) + +--- +import std.stdio; +import std.algorithm : $(HILITE algSort =) sort; + +void main() { + auto arr = [ 2, 10, 1, 5 ]; + arr$(HILITE .algSort); + writeln(arr); +} +--- + +$(H6 $(IX package import) Importing a package as a module) + +$(P +Sometimes multiple modules of a package may need to be imported together. For example, whenever one module from the $(C animal) package is imported, all of the other modules may need to be imported as well: $(C animal.cat), $(C animal.dog), $(C animal.horse), etc. +) + +$(P +In such cases it is possible to import some or all of the modules of a package by importing the package as if it were a module: +) + +--- +import animal; // ← entire package imported as a module +--- + +$(P +$(IX package.d) It is achieved by a special configuration file in the package directory, which must always be named as $(C package.d). That special file includes the $(C module) directive for the package and imports the modules of the package $(I publicly): +) + +--- +// The contents of the file animal/package.d: +module animal; + +$(HILITE public) import animal.cat; +$(HILITE public) import animal.dog; +$(HILITE public) import animal.horse; +// ... same for the other modules ... +--- + +$(P +Importing a module publicly makes that module available to the users of the importing module as well. As a result, when the users import just the $(C animal) module (which actually is a package), they get access to $(C animal.cat) and all the other modules as well. +) + +$(H6 $(IX deprecated) Deprecating features) + +$(P +Modules evolve over time and get released under new version numbers. Going forward from a particular version, the authors of the module may decide to $(I deprecate) some of its features. Deprecating a feature means that newly written programs should not rely on that feature anymore; using a deprecated feature is disapproved. Deprecated features may even be removed from the module in the future. +) + +$(P +There can be many reasons why a feature is deprecated. For example, the new version of the module may include a better alternative, the feature may have been moved to another module, the name of the feature may have changed to be consistent with the rest of the module, etc. +) + +$(P +The deprecation of a feature is made official by defining it with the $(C deprecated) attribute, optionally with a custom message. For example, the following deprecation message communicates to its user that the name of the function has been changed: +) + +--- +deprecated("Please use doSomething() instead.") +void do_something() { + // ... +} +--- + +$(P +By specifying one of the following compiler switches, the user of the module can determine how the compiler should react when a deprecated feature is used: +) + +$(UL +$(LI $(IX -d, compiler switch) $(C -d): Using deprecated features should be allowed) +$(LI $(IX -dw, compiler switch) $(C -dw): Using deprecated features should produce compilation warnings) +$(LI $(IX -de, compiler switch) $(C -de): Using deprecated features should produce compilation errors) +) + +$(P +For example, calling the deprecated feature in a program and compiling it with $(C -de) would fail compilation: +) + +--- + do_something(); +--- + +$(SHELL_SMALL +$ dmd deneme.d $(HILITE -de) +$(SHELL_OBSERVED deneme.d: $(HILITE Deprecation): function deneme.do_something is +deprecated - Please use doSomething() instead.) +) + +$(P +The name of a deprecated feature is usually defined as an $(C alias) of the new name: +) + +--- +deprecated("Please use doSomething() instead.") +$(HILITE alias do_something) = doSomething; + +void doSomething() { + // ... +} +--- + +$(P +We will see the $(C alias) keyword in $(LINK2 /ders/d.en/alias.html, a later chapter). +) + +$(H6 Adding module definitions to the program) + +$(P +The $(C 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 $(I compile) the code. +) + +$(P +It is not possible to build the previous program only by the main source file, "deneme.d": +) + +$(SHELL_SMALL +$ dmd deneme.d -w -de +$(SHELL_OBSERVED deneme.o: In function `_Dmain': +deneme.d: $(HILITE undefined reference) to `_D6animal3cat3Cat7__ClassZ' +deneme.d: $(HILITE undefined reference) to `_D6animal3dog3Dog7__ClassZ' +collect2: ld returned 1 exit status) +) + +$(P +Those error messages are generated by the $(I linker). Although they are not user-friendly messages, they indicate that some definitions that are needed by the program are missing. +) + +$(P +$(IX linker) $(IX building the program) The actual build of the program is the responsibility of the linker, which gets called automatically by the compiler behind the scenes. The compiler passes the modules that it has just compiled to the linker, and the linker combines those modules (and libraries) to produce the executable program. +) + +$(P +For that reason, all of the modules that make up the program must be provided to the linker. For the program above to be built, "animal/cat.d" and "animal/dog.d" must also be specified on the compilation line: +) + +$(SHELL_SMALL +$ dmd deneme.d animal/cat.d animal/dog.d -w -de +) + +$(P +Instead of having to mention the modules individually every time on the command line, they can be combined as libraries. +) + +$(H5 $(IX library) Libraries) + +$(P +A collection of compiled modules is called a library. Libraries are not programs themselves; they do not have the $(C main()) function. Libraries contain compiled definitions of functions, structs, classes, and other features of modules, which are to be linked later by the linker to produce the program. +) + +$(P +dmd's $(C -lib) command line option is for making libraries. The following command makes a library that contains the "cat.d" and the "dog.d" modules. The name of the library is specified with the $(C -of) switch: +) + +$(SHELL_SMALL +$ dmd animal/cat.d animal/dog.d -lib -ofanimal -w -de +) + +$(P +The actual name of the library file depends on the platform. For example, the extension of library files is $(C .a) under Linux systems: $(C animal.a). +) + +$(P +Once that library is built, It is not necessary to specify the "animal/cat.d" and "animal/dog.d" modules individually anymore. The library file is sufficient: +) + +$(SHELL_SMALL +$ dmd deneme.d animal.a -w -de +) + +$(P +The command above replaces the following one: +) + +$(SHELL_SMALL +$ dmd deneme.d animal/cat.d animal/dog.d -w -de +) + +$(P +$(IX Phobos, library) As an exception, the D standard library Phobos need not be specified on the command line. That library is automatically included behind the scenes. Otherwise, it could be specified similar to the following line: +) + +$(SHELL_SMALL +$ dmd deneme.d animal.a /usr/lib64/libphobos2.a -w -de +) + +$(P +$(I $(B Note:) The name and location of the Phobos library may be different on different systems.) +) + +$(H6 Using libraries of other languages) + +$(P +D can use libraries that are written in some other compiled languages like C and C++. However, because different languages use different $(I linkages), such libraries are available to D code only through their $(I D bindings). +) + +$(P +$(IX linkage) $(IX name mangling) $(IX mangling, name) $(IX symbol) Linkage is the set of rules that determines the accessibility of entities in a library as well as how the names (symbols) of those entities are represented in compiled code. The names in compiled code are different from the names that the programmer writes in source code: The compiled names are $(I name-mangled) according to the rules of a particular language or compiler. +) + +$(P +$(IX mangle, core.demangle) $(IX demangle) For example, according to C linkage, the C function name $(C foo) would be $(I mangled) with a leading underscore as $(C _foo) in compiled code. Name-mangling is more complex in languages like C++ and D because these languages allow using the same name for different entities in different modules, structs, classes, etc. as well as for overloads of functions. A D function named $(C foo) in source code has to be mangled in a way that would differentiate it from all other $(C foo) names that can exist in a program. Although the exact mangled names are usually not important to the programmer, the $(C core.demangle) module can be used to mangle and demangle symbols: +) + +--- +module deneme; + +import std.stdio; +import core.demangle; + +void foo() { +} + +void main() { + writeln($(HILITE mangle)!(typeof(foo))("deneme.foo")); +} +--- + +$(P +$(I $(B Note:) $(C mangle()) is a function template, the syntax of which is unfamiliar at this point in the book. We will see templates later in $(LINK2 /ders/d.en/templates.html, the Templates chapter).) +) + +$(P +A function that has the same type as $(C foo) above and is named as $(C deneme.foo), has the following mangled name in compiled code: +) + +$(SHELL_SMALL +_D6deneme3fooFZv +) + +$(P +Name mangling is the reason why linker error messages cannot include user-friendly names. For example, a symbol in an error message above was $(C _D6animal3cat3Cat7__ClassZ) instead of $(C animal.cat.Cat). +) + +$(P +$(IX extern()) $(IX C) $(IX C++) $(IX D) $(IX Objective-C) $(IX Pascal) $(IX System) $(IX Windows) The $(C extern()) attribute specifies the linkage of entities. The valid linkage types that can be used with $(C extern()) are $(C C), $(C C++), $(C D), $(C Objective-C), $(C Pascal), $(C System), and $(C Windows). For example, when a D code needs to make a call to a function that is defined in a C library, that function must be declared as having C linkage: +) + +--- +// Declaring that 'foo' has C linkage (e.g. it may be defined +// in a C library): +$(HILITE extern(C)) void foo(); + +void main() { + foo(); // this call would be compiled as a call to '_foo' +} +--- + +$(P +$(IX namespace, C++) In the case of C++ linkage, the namespace that a name is defined in is specified as the second argument to the $(C extern()) attribute. For example, according to the following declaration, $(C bar()) is the declaration of the function $(C a::b::c::bar()) defined in a C++ library (note that D code uses dots instead of colons): +) + +--- +// Declaring that 'bar' is defined inside namespace a::b::c +// and that it has C++ linkage: +extern(C++, $(HILITE a.b.c)) void bar(); + +void main() { + bar(); // a call to a::b::c::bar() + a.b.c.bar(); // same as above +} +--- + +$(P +$(IX binding) A file that contains such D declarations of the features of an external library is called a $(I D binding) of that library. Fortunately, in most cases programmers do not need to write them from scratch as D bindings for many popular non-D libraries are available through $(LINK2 https://github.com/D-Programming-Deimos/, the Deimos project). +) + +$(P +$(IX extern) When used without a linkage type, the $(C extern) attribute has a different meaning: It specifies that the storage for a variable is the responsibility of an external library; the D compiler should not reserve space for it in this module. Having different meanings, $(C extern) and $(C extern()) can be used together: +) + +--- +// Declaring that the storage for 'g_variable' is already +// defined in a C library: +extern(C) $(HILITE extern) int g_variable; +--- + +$(P +If the $(C extern) attribute were not specified above, while having C linkage, $(C g_variable) would be a variable of this D module. +) + +Macros: + SUBTITLE=Modules and Libraries + + DESCRIPTION=D modules and libraries + + KEYWORDS=d programming lesson book tutorial module library diff --git a/target/name_space.d b/target/name_space.d new file mode 100644 index 0000000..fd9366d --- /dev/null +++ b/target/name_space.d @@ -0,0 +1,143 @@ +Ddoc + +$(DERS_BOLUMU $(IX name scope) Name Scope) + +$(P +Any name is accessible from the point where it has been defined at to the point where its scope ends, as well as in all of the scopes that its scope includes. In this regard, every scope defines a $(I name scope). +) + +$(P +Names are not available beyond the end of their scope: +) + +--- +void main() { + int outer; + + if (aCondition) $(HILITE {) // ← curly bracket starts a new scope + int inner = 1; + outer = 2; // ← 'outer' is available here + + $(HILITE }) // ← 'inner' is not available beyond this point + + inner = 3; $(DERLEME_HATASI) + // 'inner' is not available in the outer scope +} +--- + +$(P +Because $(C inner) is defined within the scope of the $(C if) condition it is available only in that scope. On the other hand, $(C outer) is available in both the outer scope and the inner scope. +) + +$(P +It is not legal to define the same name in an inner scope: +) + +--- + size_t $(HILITE length) = oddNumbers.length; + + if (aCondition) { + size_t $(HILITE length) = primeNumbers.length; $(DERLEME_HATASI) + } +--- + +$(H5 Defining names closest to their first use) + +$(P +As we have been doing in all of the programs so far, variables must be defined before their first use: +) + +--- + writeln(number); $(DERLEME_HATASI) + // number is not known yet + int number = 42; +--- + +$(P +For that code to be acceptable by the compiler, $(C number) must be defined before it is used with $(C writeln). Although there is no restriction on how many lines earlier it should be defined, it is accepted as good programming practice that variables be defined closest to where they are first used. +) + +$(P +Let's see this in a program that prints the average of the numbers that it takes from the user. Programmers who are experienced in some other programming languages may be used to defining variables at tops of scopes: +) + +--- + int count; // ← HERE + int[] numbers; // ← HERE + double averageValue; // ← HERE + + write("How many numbers are there? "); + + readf(" %s", &count); + + if (count >= 1) { + numbers.length = count; + + // ... assume the calculation is here ... + + } else { + writeln("ERROR: You must enter at least one number!"); + } +--- + +$(P +Contrast the code above to the one below that defines the variables later, as each variable actually starts taking part in the program: +) + +--- + write("How many numbers are there? "); + + int count; // ← HERE + readf(" %s", &count); + + if (count >= 1) { + int[] numbers; // ← HERE + numbers.length = count; + + double averageValue; // ← HERE + + // ... assume that the calculation is here ... + + } else { + writeln("ERROR: You must enter at least one number!"); + } +--- + +$(P +Although defining all of the variables at the top may look better structurally, there are several benefits of defining them as late as possible: +) + +$(UL +$(LI $(B Speed:) Every variable definition tends to add a small speed cost to the program. As every variable is initialized in D, defining variables at the top will result in them always being initialized, even if they are only sometimes used later, wasting resources. +) + +$(LI $(B Risk of mistakes:) Every line between the definition and use of a variable carries a higher risk of programming mistakes. As an example of this, consider a variable using the common name $(C length). It is possible to confuse that variable with some other length and use it inadvertently before reaching the line of its first intended use. When that line is finally reached the variable may no longer have the desired value. +) + +$(LI $(B Readability:) As the number of lines in a scope increase, it becomes more likely that the definition of a variable is too far up in the source code, forcing the programmer to scroll back in order to look at its definition. +) + +$(LI $(B Code maintenance:) Source code is in constant modification and improvement: new features are added, old features are removed, bugs are fixed, etc. These changes sometimes make it necessary to extract a group of lines altogether into a new function. + +$(P +When that happens, having all of the variables defined close to the lines that use them makes it easier to move them as a coherent bunch. +) + +$(P +For example, in the latter code above that followed this guideline, all of the lines within the $(C if) statement can be moved to a new function in the program. +) + +$(P +On the other hand, when the variables are always defined at the top, if some lines ever need to be moved, the variables that are used in those lines must be identified one by one. +) + +) + +) + +Macros: + SUBTITLE=Name Scope + + DESCRIPTION=The scopes in the program where names are valid and accessible, and the benefits of defining variables closest to their first use in the program. + + KEYWORDS=d programming language tutorial book name scopes diff --git a/target/nested.d b/target/nested.d new file mode 100644 index 0000000..2ebbce4 --- /dev/null +++ b/target/nested.d @@ -0,0 +1,282 @@ +Ddoc + +$(DERS_BOLUMU $(IX function, nested) $(IX struct, nested) $(IX class, nested in function) $(IX nested definition) Nested Functions, Structs, and Classes) + +$(P +Up to this point, we have been defining functions, structs, and classes in the outermost scopes (i.e. the module scope). They can be defined in inner scopes as well. Defining them in inner scopes helps with encapsulation by narrowing the visibility of their symbols, as well as creating $(I closures) that we saw in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). +) + +$(P +As an example, the following $(C outerFunc()) function contains definitions of a nested function, a nested $(C struct), and a nested $(C class): +) + +--- +void outerFunc(int parameter) { + int local; + + $(HILITE void nestedFunc()) { + local = parameter * 2; + } + + $(HILITE struct NestedStruct) { + void memberFunc() { + local /= parameter; + } + } + + $(HILITE class NestedClass) { + void memberFunc() { + local += parameter; + } + } + + // Using the nested definitions inside this scope: + + nestedFunc(); + + auto s = NestedStruct(); + s.memberFunc(); + + auto c = new NestedClass(); + c.memberFunc(); +} + +void main() { + outerFunc(42); +} +--- + +$(P +Like any other variable, nested definitions can access symbols that are defined in their outer scopes. For example, all three of the nested definitions above are able to use the variables named $(C parameter) and $(C local). +) + +$(P +As usual, the names of the nested definitions are valid only in the scopes that they are defined in. For example, $(C nestedFunc()), $(C NestedStruct), and $(C NestedClass) are not accessible from $(C main()): +) + +--- +void main() { + auto a = NestedStruct(); $(DERLEME_HATASI) + auto b = outerFunc.NestedStruct(); $(DERLEME_HATASI) +} +--- + +$(P +Although their names cannot be accessed, nested definitions can still be used in other scopes. For example, many Phobos algorithms handle their tasks by nested structs that are defined inside Phobos functions. +) + +$(P +To see an example of this, let's design a function that consumes a slice from both ends in alternating order: +) + +--- +import std.stdio; +import std.array; + +auto alternatingEnds(T)(T[] slice) { + bool isFromFront = true; + + $(HILITE struct EndAlternatingRange) { + bool empty() @property const { + return slice.empty; + } + + T front() @property const { + return isFromFront ? slice.front : slice.back; + } + + void popFront() { + if (isFromFront) { + slice.popFront(); + isFromFront = false; + + } else { + slice.popBack(); + isFromFront = true; + } + } + } + + return EndAlternatingRange(); +} + +void main() { + auto a = alternatingEnds([ 1, 2, 3, 4, 5 ]); + writeln(a); +} +--- + +$(P +Even though the nested $(C struct) cannot be named inside $(C main()), it is still usable: +) + +$(SHELL +[1, 5, 2, 4, 3] +) + +$(P +$(IX Voldemort) $(I $(B Note:) Because their names cannot be mentioned outside of their scopes, such types are called $(I Voldemort types) due to analogy to a Harry Potter character.) +) + +$(P +$(IX closure) $(IX context) Note that the nested $(C struct) that $(C alternatingEnds()) returns does not have any member variables. That $(C struct) handles its task using merely the function parameter $(C slice) and the local function variable $(C isFromFront). The fact that the returned object can safely use those variables even after leaving the context that it was created in is due to a $(I closure) that has been created automatically. We have seen closures in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). +) + +$(H6 $(C static) when a closure is not needed) + +$(P +$(IX static, nested definition) Since they keep their contexts alive, nested definitions are more expensive than their regular counterparts. Additionally, as they must include a $(I context pointer) to determine the context that they are associated with, objects of nested definitions occupy more space as well. For example, although the following two structs have exactly the same member variables, their sizes are different: +) + +--- +import std.stdio; + +$(HILITE struct ModuleStruct) { + int i; + + void memberFunc() { + } +} + +void moduleFunc() { + $(HILITE struct NestedStruct) { + int i; + + void memberFunc() { + } + } + + writefln("OuterStruct: %s bytes, NestedStruct: %s bytes.", + ModuleStruct.sizeof, NestedStruct.sizeof); +} + +void main() { + moduleFunc(); +} +--- + +$(P +The sizes of the two structs may be different on other environments: +) + +$(SHELL +OuterStruct: $(HILITE 4) bytes, NestedStruct: $(HILITE 16) bytes. +) + +$(P +$(IX static class) $(IX static struct) However, some nested definitions are merely for keeping them as local as possible, with no need to access variables from the outer contexts. In such cases, the associated cost would be unnecessary. The $(C static) keyword removes the context pointer from nested definitions, making them equivalents of their module counterparts. As a result, $(C static) nested definitions cannot access their outer contexts: +) + +--- +void outerFunc(int parameter) { + $(HILITE static) class NestedClass { + int i; + + this() { + i = parameter; $(DERLEME_HATASI) + } + } +} +--- + +$(P +$(IX .outer, void*) The context pointer of a nested $(C class) object is available as a $(C void*) through its $(C .outer) property. For example, because they are defined in the same scope, the context pointers of the following two objects are equal: +) + +--- +void foo() { + class C { + } + + auto a = new C(); + auto b = new C(); + + assert(a$(HILITE .outer) is b$(HILITE .outer)); +} +--- + +$(P +As we will see below, for $(I classes nested inside classes), the type of the context pointer is the type of the outer class, not $(C void*). +) + +$(H6 Classes nested inside classes) + +$(P +$(IX class, nested in class) When a $(C class) is nested inside another one, the context that the nested object is associated with is the outer object itself. +) + +$(P +$(IX .new) $(IX .outer, class type) Such nested classes are constructed by the $(C this.new) syntax. When necessary, the outer object of a nested object can be accessed by $(C this.outer): +) + +--- +class OuterClass { + int outerMember; + + $(HILITE class NestedClass) { + int func() { + /* A nested class can access members of the outer + * class. */ + return outerMember * 2; + } + + OuterClass context() { + /* A nested class can access its outer object + * (i.e. its context) by '.outer'. */ + return $(HILITE this.outer); + } + } + + NestedClass algorithm() { + /* An outer class can construct a nested object by + * '.new'. */ + return $(HILITE this.new) NestedClass(); + } +} + +void main() { + auto outerObject = new OuterClass(); + + /* A member function of an outer class is returning a + * nested object: */ + auto nestedObject = outerObject.algorithm(); + + /* The nested object gets used in the program: */ + nestedObject.func(); + + /* Naturally, the context of nestedObject is the same as + * outerObject: */ + assert(nestedObject.context() is outerObject); +} +--- + +$(P +Instead of $(C this.new) and $(C this.outer), $(C .new) and $(C .outer) can be used on existing objects as well: +) + +--- + auto var = new OuterClass(); + auto nestedObject = $(HILITE var.new) OuterClass.NestedClass(); + auto var2 = $(HILITE nestedObject.outer); +--- + +$(H5 Summary) + +$(UL + +$(LI Functions, structs, and classes that are defined in inner scopes can access those scopes as their contexts.) + +$(LI Nested definitions keep their contexts alive to form closures.) + +$(LI Nested definitions are more costly than their module counterparts. When a nested definition does not need to access its context, this cost can be avoided by the $(C static) keyword.) + +$(LI Classes can be nested inside other classes. The context of such a nested object is the outer object itself. Nested class objects are constructed by $(C this.new) or $(C variable.new) and their contexts are available by $(C this.outer) or $(C variable.outer).) + +) + +Macros: + SUBTITLE=Nested functions, structs, and classes + + DESCRIPTION=Defining functions, structs, and classes in nested scopes. + + KEYWORDS=d programming language tutorial book nested outer diff --git a/target/null_is.d b/target/null_is.d new file mode 100644 index 0000000..23b0471 --- /dev/null +++ b/target/null_is.d @@ -0,0 +1,285 @@ +Ddoc + +$(DERS_BOLUMU $(IX null) $(IX is, operator) $(IX !is) The $(CH4 null) Value and the $(CH4 is) Operator) + +$(P +We saw in earlier chapters that a variable of a reference type needs not reference a particular object: +) + +--- + MyClass referencesAnObject = new MyClass; + + MyClass variable; // does not reference an object +--- + +$(P +Being a reference type, $(C variable) above does have an identity but it does not reference any object yet. Such an object can be imagined to have a place in memory as in the following picture: +) + +$(MONO + variable + ───┬──────┬─── + │ null │ + ───┴──────┴─── +) + +$(P +A reference that does not reference any value is $(C null). We will expand on this below. +) + +$(P +Such a variable is in an almost useless state. Since there is no $(C MyClass) object that it references, it cannot be used in a context where an actual $(C MyClass) object is needed: +) + +--- +import std.stdio; + +class MyClass { + int member; +} + +void use(MyClass variable) { + writeln(variable.member); $(CODE_NOTE_WRONG BUG) +} + +void main() { + MyClass variable; + use(variable); +} +--- + +$(P +As there is no object that is referenced by the parameter that $(C use()) receives, attempting to access a member of a non-existing object results in a program crash: +) + +$(SHELL +$ ./deneme +$(SHELL_OBSERVED Segmentation fault) +) + +$(P +$(IX segmentation fault) "Segmentation fault" is an indication that the program has been terminated by the operating system because of attempting to access an illegal memory address. +) + +$(H5 The $(C null) value) + +$(P +The special value $(C null) can be printed just like any other value. +) + +--- + writeln(null); +--- + +$(P +The output: +) + +$(SHELL +null +) + +$(P +A $(C null) variable can be used only in two contexts: +) + +$(OL +$(LI Assigning an object to it + +--- + variable = new MyClass; // now references an object +--- + +$(P +The assignment above makes $(C variable) provide access to the newly constructed object. From that point on, $(C variable) can be used for any valid operation of the $(C MyClass) type. +) + +) + +$(LI Determining whether it is $(C null) + +$(P +However, because the $(C ==) operator needs actual objects to compare, the expression below cannot be compiled: +) + +--- + if (variable == null) $(DERLEME_HATASI) +--- + +$(P +For that reason, whether a variable is $(C null) must be determined by the $(C is) operator. +) + +) + +) + +$(H5 The $(C is) operator) + +$(P +This operator answers the question "does have the null value?": +) + +--- + if (variable $(HILITE is) null) { + // Does not reference any object + } +--- + +$(P +$(C is) can be used with other types of variables as well. In the following use, it compares the $(C values) of two integers: +) + +--- + if (speed is newSpeed) { + // Their values are equal + + } else { + // Their values are different + } +--- + +$(P +When used with slices, it determines whether the two slices reference the same set of elements: +) + +--- + if (slice is slice2) { + // They provide access to the same elements + } +--- + +$(H5 The $(C !is) operator) + +$(P +$(C !is) is the opposite of $(C is). It produces $(C true) when the values are different: +) + +--- + if (speed !is newSpeed) { + // Their values are different + } +--- + +$(H5 Assigning the $(C null) value) + +$(P +Assigning the $(C null) value to a variable of a reference type makes that variable stop referencing its current object. +) + +$(P +If that assignment happens to be terminating the very last reference to the actual object, then the actual object becomes a candidate for finalization by the garbage collector. After all, not being referenced by any variable means that the object is not being used in the program at all. +) + +$(P +Let's look at the example from $(LINK2 /ders/d.en/value_vs_reference.html, an earlier chapter) where two variables were referencing the same object: +) + +--- + auto variable = new MyClass; + auto variable2 = variable; +--- + +$(P +The following is a representation of the state of the memory after executing that code: +) + +$(MONO + (anonymous MyClass object) variable variable2 + ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── + │ ... │ │ o │ │ o │ + ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── + ▲ │ │ + │ │ │ + └────────────────────┴────────────┘ +) + +$(P +Assigning the $(C null) value to one of these variables breaks its relationship with the object: +) + +--- + variable = null; +--- + +$(P +At this point there is only $(C variable2) that references the $(C MyClass) object: +) + +$(MONO + (anonymous MyClass object) variable variable2 + ───┬───────────────────┬─── ───┬────┬─── ───┬───┬─── + │ ... │ │null│ │ o │ + ───┴───────────────────┴─── ───┴────┴─── ───┴─│─┴─── + ▲ │ + │ │ + └──────────────────────────────────┘ +) + +$(P +Assigning $(C null) to the last reference would make the $(C MyClass) object unreachable: +) + +--- + variable2 = null; +--- + +$(P +Such unreachable objects are finalized by the garbage collector at some time in the future. From the point of view of the program, the object does not exist: +) + +$(MONO + variable variable2 + ───┬───────────────────┬─── ───┬────┬─── ───┬────┬─── + │ │ │null│ │null│ + ───┴───────────────────┴─── ───┴────┴─── ───┴────┴── +) + +$(P +We had discussed ways of emptying an associative array in the exercises section of the $(LINK2 /ders/d.en/aa.html, Associative Arrays chapter). We now know a fourth method: Assigning $(C null) to an associative array variable will break the relationship of that variable with the elements: +) + +--- + string[int] names; + // ... + names = null; // Not providing access to any element +--- + +$(P +Similar to the $(C MyClass) examples, if $(C names) has been the last reference to the elements of the associative array, those elements would be finalized by the garbage collector. +) + +$(P +Slices can be assigned $(C null) as well: +) + +--- + int[] slice = otherSlice[ 10 .. 20 ]; + // ... + slice = null; // Not providing access to any element +--- + +$(H5 Summary) + +$(UL + +$(LI $(C null) is the value indicating that a variable does not provide access to any value) + +$(LI References that have the $(C null) value can only be used in two operations: assigning a value to them and determining whether they are $(C null) or not) + +$(LI Since the $(C ==) operator may have to access an actual object, determining whether a variable is $(C null) must be performed by the $(C is) operator) + +$(LI $(C !is) is the opposite of $(C is)) + +$(LI Assigning $(C null) to a variable makes that variable provide access to nothing) + +$(LI Objects that are not referenced by any variable are finalized by the garbage collector) + +) + +Macros: + SUBTITLE=The null Value and the is Operator + + DESCRIPTION=The 'null' value of the D programming language and the operators 'is' and '!is' that determine whether variables are null or not. + + KEYWORDS=d programming language tutorial book object referencee null is !is diff --git a/target/object.cozum.d b/target/object.cozum.d new file mode 100644 index 0000000..d85c92a --- /dev/null +++ b/target/object.cozum.d @@ -0,0 +1,108 @@ +Ddoc + +$(COZUM_BOLUMU $(CH4 Object)) + +$(OL + +$(LI +For the equality comparison, $(C rhs) being non-$(C null) and the members being equal would be sufficient: + +--- +enum Color { blue, green, red } + +class Point { + int x; + int y; + Color color; + +// ... + + override bool opEquals(Object o) const { + const rhs = cast(const Point)o; + + return rhs && (x == rhs.x) && (y == rhs.y); + } +} +--- + +) + +$(LI +When the type of the right-hand side object is also $(C Point), they are compared according to the values of the $(C x) members first and then according to the values of the $(C y) members: + +--- +class Point { + int x; + int y; + Color color; + +// ... + + override int opCmp(Object o) const { + const rhs = cast(const Point)o; + enforce(rhs); + + return (x != rhs.x + ? x - rhs.x + : y - rhs.y); + } +} +--- + +) + +$(LI +Note that it is not possible to cast to type $(C const TriangularArea) inside $(C opCmp) below. When $(C rhs) is $(C const TriangularArea), then its member $(C rhs.points) would be $(C const) as well. Since the parameter of $(C opCmp) is non-$(C const), it would not be possible to pass $(C rhs.points[i]) to $(C point.opCmp). + +--- +class TriangularArea { + Point[3] points; + + this(Point one, Point two, Point three) { + points = [ one, two, three ]; + } + + override bool opEquals(Object o) const { + const rhs = cast(const TriangularArea)o; + return rhs && (points == rhs.points); + } + + override int opCmp(Object o) const { + $(HILITE auto) rhs = $(HILITE cast(TriangularArea))o; + enforce(rhs); + + foreach (i, point; points) { + immutable comparison = point.opCmp(rhs.points[i]); + + if (comparison != 0) { + /* The sort order has already been + * determined. Simply return the result. */ + return comparison; + } + } + + /* The objects are considered equal because all of + * their points have been equal. */ + return 0; + } + + override size_t toHash() const { + /* Since the 'points' member is an array, we can take + * advantage of the existing toHash algorithm for + * array types. */ + return typeid(points).getHash(&points); + } +} +--- + +) + +) + + +Macros: + SUBTITLE=Object Solutions + + DESCRIPTION=Programming in D exercise solutions: Object + + KEYWORDS=programming in d tutorial Object diff --git a/target/object.d b/target/object.d new file mode 100644 index 0000000..607d8a3 --- /dev/null +++ b/target/object.d @@ -0,0 +1,866 @@ +Ddoc + +$(DERS_BOLUMU $(IX Object) $(CH4 Object)) + +$(P +Classes that do not explicitly inherit any class, automatically inherit the $(C Object) class. +) + +$(P +By that definition, the topmost class in any class hierarchy inherits $(C Object): +) + +--- +// ": Object" is not written; it is automatic +class MusicalInstrument $(DEL : Object ) { + // ... +} + +// Inherits Object indirectly +class StringInstrument : MusicalInstrument { + // ... +} +--- + +$(P +Since the topmost class inherits $(C Object), every class indirectly inherits $(C Object) as well. In that sense, every class "is an" $(C Object). +) + +$(P +Every class inherits the following member functions of $(C Object): +) + +$(UL +$(LI $(C toString): The $(C string) representation of the object.) +$(LI $(C opEquals): Equality comparison with another object.) +$(LI $(C opCmp): Sort order comparison with another object.) +$(LI $(C toHash): Associative array hash value.) +) + +$(P +The last three of these functions emphasize the values of objects. They also make a class eligible for being the key type of associative arrays. +) + +$(P +Because these functions are inherited, their redefinitions for the subclasses require the $(C override) keyword. +) + +$(P $(I $(B Note:) $(C Object) defines other members as well. This chapter will include only those four member functions of it.) +) + +$(H5 $(IX typeid) $(IX TypeInfo) $(C typeid) and $(C TypeInfo)) + +$(P +$(C Object) is defined in the $(LINK2 http://dlang.org/phobos/object.html, $(C object) module) (which is not a part of the $(C std) package). The $(C object) module defines $(C TypeInfo) as well, a class that provides information about types. Every type has a distinct $(C TypeInfo) object. The $(C typeid) $(I expression) provides access to the $(C TypeInfo) object that is associated with a particular type. As we will see later below, the $(C TypeInfo) class can be used for determining whether two types are the same, as well as for accessing special functions of a type ($(C toHash), $(C postblit), etc., most of which are not covered in this book). +) + +$(P +$(C TypeInfo) is always about the actual run-time type. For example, although both $(C Violin) and $(C Guitar) below inherit $(C StringInstrument) directly and $(C MusicalInstrument) indirectly, the $(C TypeInfo) instances of $(C Violin) and $(C Guitar) are different. They are exactly for $(C Violin) and $(C Guitar) types, respectively: +) + +--- +class MusicalInstrument { +} + +class StringInstrument : MusicalInstrument { +} + +class Violin : StringInstrument { +} + +class Guitar : StringInstrument { +} + +void main() { + TypeInfo v = $(HILITE typeid)(Violin); + TypeInfo g = $(HILITE typeid)(Guitar); + assert(v != g); $(CODE_NOTE the two types are not the same) +} +--- + +$(P +The $(C typeid) expressions above are being used with $(I types) like $(C Violin) itself. $(C typeid) can take an $(I expression) as well, in which case it returns the $(C TypeInfo) object for the run-time type of that expression. For example, the following function takes two parameters of different but related types: +) + +--- +import std.stdio; + +// ... + +void foo($(HILITE MusicalInstrument) m, $(HILITE StringInstrument) s) { + const isSame = (typeid(m) == typeid(s)); + + writefln("The types of the arguments are %s.", + isSame ? "the same" : "different"); +} + +// ... + + auto a = new $(HILITE Violin)(); + auto b = new $(HILITE Violin)(); + foo(a, b); +--- + +$(P +Since both arguments to $(C foo()) are two $(C Violin) objects for that particular call, $(C foo()) determines that their types are the same: +) + +$(SHELL +The types of the arguments are $(HILITE the same). +) + +$(P +Unlike $(C .sizeof) and $(C typeof), which never execute their expressions, $(C typeid) always executes the expression that it receives: +) + +--- +import std.stdio; + +int foo(string when) { + writefln("Called during '%s'.", when); + return 0; +} + +void main() { + const s = foo("sizeof")$(HILITE .sizeof); // foo() is not called + alias T = $(HILITE typeof)(foo("typeof")); // foo() is not called + auto ti = $(HILITE typeid)(foo("typeid")); // foo() is called +} +--- + +$(P +The output indicates that only the expression of $(C typeid) is executed: +) + +$(SHELL +Called during 'typeid'. +) + +$(P +The reason for this difference is because actual run-time types of expressions may not be known until those expressions are executed. For example, the exact return type of the following function would be either $(C Violin) or $(C Guitar) depending on the value of function argument $(C i): +) + +--- +MusicalInstrument foo(int i) { + return ($(HILITE i) % 2) ? new Violin() : new Guitar(); +} +--- + +$(P +$(IX TypeInfo_Class) $(IX .classinfo) There are various subclasses of $(C TypeInfo) for various kinds of types like arrays, structs, classes, etc. Of these, $(C TypeInfo_Class) can be particularly useful. For example, the name of the run-time type of an object can be obtained through its $(C TypeInfo_Class.name) property as a $(C string). You can access the $(C TypeInfo_Class) instance of an object by its $(C .classinfo) property: +) + +--- + TypeInfo_Class info = a$(HILITE .classinfo); + string runtimeTypeName = info$(HILITE .name); +--- + +$(H5 $(IX toString) $(C toString)) + +$(P +Same with structs, $(C toString) enables using class objects as strings: +) + +--- + auto clock = new Clock(20, 30, 0); + writeln(clock); // Calls clock.toString() +--- + +$(P +The inherited $(C toString()) is usually not useful; it produces just the name of the type: +) + +$(SHELL +deneme.Clock +) + +$(P +The part before the name of the type is the name of the module. The output above indicates that $(C Clock) has been defined in the $(C deneme) module. +) + +$(P +As we have seen in the previous chapter, this function is almost always overridden to produce a more meaningful $(C string) representation: +) + +--- +import std.string; + +class Clock { + override string toString() const { + return format("%02s:%02s:%02s", hour, minute, second); + } + + // ... +} + +class AlarmClock : Clock { + override string toString() const { + return format("%s ♫%02s:%02s", super.toString(), + alarmHour, alarmMinute); + } + + // ... +} + +// ... + + auto bedSideClock = new AlarmClock(20, 30, 0, 7, 0); + writeln(bedSideClock); +--- + +$(P +The output: +) + +$(SHELL +20:30:00 ♫07:00 +) + +$(H5 $(IX opEquals) $(C opEquals)) + +$(P +As we have seen in the $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading chapter), this member function is about the behavior of the $(C ==) operator (and the $(C !=) operator indirectly). The return value of the operator must be $(C true) if the objects are considered to be equal and $(C false) otherwise. +) + +$(P +$(B Warning:) The definition of this function must be consistent with $(C opCmp()); for two objects that $(C opEquals()) returns $(C true), $(C opCmp()) must return zero. +) + +$(P +Contrary to structs, the compiler does not call $(C a.opEquals(b)) right away when it sees the expression $(C a == b). When two class objects are compared by the $(C ==) operator, a four-step algorithm is executed: +) + +--- +bool opEquals(Object a, Object b) { + if (a is b) return true; // (1) + if (a is null || b is null) return false; // (2) + if (typeid(a) == typeid(b)) return a.opEquals(b); // (3) + return a.opEquals(b) && b.opEquals(a); // (4) +} +--- + +$(OL + +$(LI If the two variables provide access to the same object (or they are both $(C null)), then they are equal.) + +$(LI Following from the previous check, if only one is $(C null) then they are not equal.) + +$(LI If both of the objects are of the same type, then $(C a.opEquals(b)) is called to determine the equality.) + +$(LI Otherwise, for the two objects to be considered equal, $(C opEquals) must have been defined for both of their types and $(C a.opEquals(b)) and $(C b.opEquals(a)) must agree that the objects are equal.) + +) + +$(P +Accordingly, if $(C opEquals()) is not provided for a class type, then the values of the objects are not considered; rather, equality is determined by checking whether the two class variables provide access to the same object: +) + +--- + auto variable0 = new Clock(6, 7, 8); + auto variable1 = new Clock(6, 7, 8); + + assert(variable0 != variable1); // They are not equal + // because the objects are + // different +--- + +$(P +Even though the two objects are constructed by the same arguments above, the variables are not equal because they are not associated with the same object. +) + +$(P +On the other hand, because the following two variables provide access to the same object, they are $(I equal): +) + +--- + auto partner0 = new Clock(9, 10, 11); + auto partner1 = partner0; + + assert(partner0 == partner1); // They are equal because + // the object is the same +--- + +$(P +Sometimes it makes more sense to compare objects by their values instead of their identities. For example, it is conceivable that $(C variable0) and $(C variable1) above compare equal because their values are the same. +) + +$(P +Different from structs, the type of the parameter of $(C opEquals) for classes is $(C Object): +) + +--- +class Clock { + override bool opEquals($(HILITE Object o)) const { + // ... + } + + // ... +} +--- + +$(P +As you will see below, the parameter is almost never used directly. For that reason, it should be acceptable to name it simply as $(C o). Most of the time the first thing to do with that parameter is to use it in a type conversion. +) + +$(P +The parameter of $(C opEquals) is the object that appears on the right-hand side of the $(C ==) operator: +) + +--- + variable0 == variable1; // o represents variable1 +--- + +$(P +Since the purpose of $(C opEquals()) is to compare two objects of this class type, the first thing to do is to convert $(C o) to a variable of the same type of this class. Since it would not be appropriate to modify the right-hand side object in an equality comparison, it is also proper to convert the type as $(C const): +) + +--- + override bool opEquals(Object o) const { + auto rhs = cast(const Clock)o; + + // ... + } +--- + +$(P +As you would remember, $(C rhs) is a common abbreviation for $(I right-hand side). Also, $(C std.conv.to) can be used for the conversion as well: +) + +--- +import std.conv; +// ... + auto rhs = to!(const Clock)(o); +--- + +$(P +If the original object on the right-hand side can be converted to $(C Clock), then $(C rhs) becomes a non-$(C null) class variable. Otherwise, $(C rhs) is set to $(C null), indicating that the objects are not of the same type. +) + +$(P +According to the design of a program, it may make sense to compare objects of two incompatible types. I will assume here that for the comparison to be valid, $(C rhs) must not be $(C null); so, the first logical expression in the following $(C return) statement checks that it is not $(C null). Otherwise, it would be an error to try to access the members of $(C rhs): +) + +--- +class Clock { + int hour; + int minute; + int second; + + override bool opEquals(Object o) const { + auto rhs = cast(const Clock)o; + + return ($(HILITE rhs) && + (hour == rhs.hour) && + (minute == rhs.minute) && + (second == rhs.second)); + } + + // ... +} +--- + +$(P +With that definition, $(C Clock) objects can now be compared by their values: +) + +--- + auto variable0 = new Clock(6, 7, 8); + auto variable1 = new Clock(6, 7, 8); + + assert(variable0 == variable1); // Now they are equal + // because their values + // are equal +--- + +$(P +When defining $(C opEquals) it is important to remember the members of the superclass. For example, when comparing objects of $(C AlarmClock) it would make sense to also consider the inherited members: +) + +--- +class AlarmClock : Clock { + int alarmHour; + int alarmMinute; + + override bool opEquals(Object o) const { + auto rhs = cast(const AlarmClock)o; + + return (rhs && + (alarmHour == rhs.alarmHour) && + (alarmMinute == rhs.alarmMinute) && + $(HILITE super.opEquals(o))); + } + + // ... +} +--- + +$(P +That expression could be written as $(C super == o) as well. However, that would initiate the four-step algorithm again and as a result, the code might be a little slower. +) + +$(H5 $(IX opCmp) $(C opCmp)) + +$(P +This operator is used when sorting class objects. $(C opCmp) is the function that gets called behind the scenes for the $(C <), $(C <=), $(C >), and $(C >=). +) + +$(P +This operator must return a negative value when the left-hand object is before, a positive value when the left-hand object is after, and zero when both objects have the same sorting order. +) + +$(P +$(B Warning:) The definition of this function must be consistent with $(C opEquals()); for two objects that $(C opEquals()) returns $(C true), $(C opCmp()) must return zero. +) + +$(P +Unlike $(C toString) and $(C opEquals), there is no default implementation of this function in $(C Object). If the implementation is not available, comparing objects for sort order causes an exception to be thrown: +) + +--- + auto variable0 = new Clock(6, 7, 8); + auto variable1 = new Clock(6, 7, 8); + + assert(variable0 <= variable1); $(CODE_NOTE Causes exception) +--- + +$(SHELL +object.Exception: need opCmp for class deneme.Clock +) + +$(P +It is up to the design of the program what happens when the left-hand and right-hand objects are of different types. One way is to take advantage of the order of types that is maintained by the compiler automatically. This is achieved by calling the $(C opCmp) function on the $(C typeid) values of the two types: +) + +--- +class Clock { + int hour; + int minute; + int second; + + override int opCmp(Object o) const { + /* Taking advantage of the automatically-maintained + * order of the types. */ + if (typeid(this) != typeid(o)) { + return typeid(this).opCmp(typeid(o)); + } + + auto rhs = cast(const Clock)o; + /* No need to check whether rhs is null, because it is + * known at this line that it has the same type as o. */ + + if (hour != rhs.hour) { + return hour - rhs.hour; + + } else if (minute != rhs.minute) { + return minute - rhs.minute; + + } else { + return second - rhs.second; + } + } + + // ... +} +--- + +$(P +The definition above first checks whether the types of the two objects are the same. If not, it uses the ordering of the types themselves. Otherwise, it compares the objects by the values of their $(C hour), $(C minute), and $(C second) members. +) + +$(P +A chain of ternary operators may also be used: +) + +--- + override int opCmp(Object o) const { + if (typeid(this) != typeid(o)) { + return typeid(this).opCmp(typeid(o)); + } + + auto rhs = cast(const Clock)o; + + return (hour != rhs.hour + ? hour - rhs.hour + : (minute != rhs.minute + ? minute - rhs.minute + : second - rhs.second)); + } +--- + +$(P +If important, the comparison of the members of the superclass must also be considered. The following $(C AlarmClock.opCmp) is calling $(C Clock.opCmp) first: +) + +--- +class AlarmClock : Clock { + override int opCmp(Object o) const { + auto rhs = cast(const AlarmClock)o; + + const int superResult = $(HILITE super.opCmp(o)); + + if (superResult != 0) { + return superResult; + + } else if (alarmHour != rhs.alarmHour) { + return alarmHour - rhs.alarmHour; + + } else { + return alarmMinute - rhs.alarmMinute; + } + } + + // ... +} +--- + +$(P +Above, if the superclass comparison returns a nonzero value then that result is used because the sort order of the objects is already determined by that value. +) + +$(P +$(C AlarmClock) objects can now be compared for their sort orders: +) + +--- + auto ac0 = new AlarmClock(8, 0, 0, 6, 30); + auto ac1 = new AlarmClock(8, 0, 0, 6, 31); + + assert(ac0 < ac1); +--- + +$(P +$(C opCmp) is used by other language features and libraries as well. For example, the $(C sort()) function takes advantage of $(C opCmp) when sorting elements. +) + +$(H6 $(C opCmp) for string members) + +$(P +When some of the members are strings, they can be compared explicitly to return a negative, positive, or zero value: +) + +--- +import std.exception; + +class Student { + string name; + + override int opCmp(Object o) const { + auto rhs = cast(Student)o; + enforce(rhs); + + if (name < rhs.name) { + return -1; + + } else if (name > rhs.name) { + return 1; + + } else { + return 0; + } + } + + // ... +} +--- + +$(P +Instead, the existing $(C std.algorithm.cmp) function can be used, which happens to be faster as well: +) + +--- +import std.algorithm; + +class Student { + string name; + + override int opCmp(Object o) const { + auto rhs = cast(Student)o; + enforce(rhs); + + return cmp(name, rhs.name); + } + + // ... +} +--- + +$(P +Also note that $(C Student) does not support comparing incompatible types by enforcing that the conversion from $(C Object) to $(C Student) is possible. +) + +$(H5 $(IX toHash) $(C toHash)) + +$(P +This function allows objects of a class type to be used as associative array $(I keys). It does not affect the cases where the type is used as associative array $(I values). If this function is defined, $(C opEquals) must be defined as well. +) + +$(H6 $(IX hash table) Hash table indexes) + +$(P +Associative arrays are a hash table implementation. Hash table is a very fast data structure when it comes to searching elements in the table. ($(I Note: Like most other things in software, this speed comes at a cost: Hash tables must keep elements in an unordered way, and they may be taking up space that is more than exactly necessary.)) +) + +$(P +The high speed of hash tables comes from the fact that they first produce integer values for keys. These integers are called $(I hash values). The hash values are then used for indexing into an internal array that is maintained by the table. +) + +$(P +A benefit of this method is that any type that can produce unique integer values for its objects can be used as the key type of associative arrays. $(C toHash) is the function that returns the hash value for an object. +) + +$(P +Even $(C Clock) objects can be used as associative array key values: +) + +--- + string[$(HILITE Clock)] timeTags; + timeTags[new Clock(12, 0, 0)] = "Noon"; +--- + +$(P +The default definition of $(C toHash) that is inherited from $(C Clock) produces different hash values for different objects without regard to their values. This is similar to how the default behavior of $(C opEquals) considers different objects as being not equal. +) + +$(P +The code above compiles and runs even when there is no special definition of $(C toHash) for $(C Clock). However, its default behavior is almost never what is desired. To see that default behavior, let's try to access an element by an object that is different from the one that has been used when inserting the element. Although the new $(C Clock) object below has the same value as the $(C Clock) object that has been used when inserting into the associative array above, the value cannot be found: +) + +--- + if (new Clock(12, 0, 0) in timeTags) { + writeln("Exists"); + + } else { + writeln("Missing"); + } +--- + +$(P +According to the $(C in) operator, there is no element in the table that corresponds to the value $(C Clock(12, 0, 0)): +) + +$(SHELL +Missing +) + +$(P +The reason for this surprising behavior is that the key object that has been used when inserting the element is not the same as the key object that has been used when accessing the element. +) + +$(H6 Selecting members for $(C toHash)) + +$(P +Although the hash value is calculated from the members of an object, not every member is suitable for this task. +) + +$(P +The candidates are the members that distinguish objects from each other. For example, the members $(C name) and $(C lastName) of a $(C Student) class would be suitable if those members can be used for identifying objects of that type. +) + +$(P +On the other hand, a $(C grades) array of a $(C Student) class would not be suitable both because many objects may have the same array and also it is likely that the $(C grades) array may change over time. +) + +$(H6 Calculating hash values) + +$(P +The choice of hash values has a direct effect on the performance of associative arrays. Furthermore, a hash calculation that is effective on one type of data may not be as effective on another type of data. As $(I hash algorithms) are beyond the scope of this book, I will give just one guideline here: In general, it is better to produce different hash values for objects that are considered to have different values. However, it is not an error if two objects with different values produce the same index value; it is merely undesirable for performance reasons. +) + +$(P +It is conceivable that all of the members of $(C Clock) are significant to distinguish its objects from each other. For that reason, the hash values can be calculated from the values of its three members. $(I The number of seconds since midnight) would be effective hash values for objects that represent different points in time: +) + +--- +class Clock { + int hour; + int minute; + int second; + + override size_t toHash() const { + /* Because there are 3600 seconds in an hour and 60 + * seconds in a minute: */ + return (3600 * hour) + (60 * minute) + second; + } + + // ... +} +--- + +$(P +Whenever $(C Clock) is used as the key type of associative arrays, that special definition of $(C toHash) would be used. As a result, even though the two key objects of $(C Clock(12, 0, 0)) above are distinct, they would now produce the same hash value. +) + +$(P +The new output: +) + +$(SHELL +Exists +) + +$(P +Similar to the other member functions, the superclass may need to be considered as well. For example, $(C AlarmClock.toHash) can take advantage of $(C Clock.toHash) during its index calculation: +) + +--- +class AlarmClock : Clock { + int alarmHour; + int alarmMinute; + + override size_t toHash() const { + return $(HILITE super.toHash()) + alarmHour + alarmMinute; + } + + // ... +} +--- + +$(P +$(I $(B Note:) Take the calculation above just as an example. In general, adding integer values is not an effective way of generating hash values.) +) + +$(P +There are existing efficient algorithms for calculating hash values for variables of floating point, array, and struct types. These algorithms are available to the programmer as well. +) + +$(P +$(IX getHash) What needs to be done is to call $(C getHash()) on the $(C typeid) of each member. The syntax of this method is the same for floating point, array, and struct types. +) + +$(P +For example, hash values of a $(C Student) type can be calculated from its $(C name) member as in the following code: +) + +--- +class Student { + string name; + + override size_t toHash() const { + return typeid(name).getHash(&name); + } + + // ... +} +--- + +$(H6 Hash values for structs) + +$(P +Since structs are value types, hash values for their objects are calculated automatically by an efficient algorithm. That algorithm takes all of the members of the object into consideration. +) + +$(P +When there is a specific reason like needing to exclude certain members from the hash calculation, $(C toHash()) can be overridden for structs as well. +) + +$(PROBLEM_COK + +$(PROBLEM +Start with the following class, which represents colored points: + +--- +enum Color { blue, green, red } + +class Point { + int x; + int y; + Color color; + + this(int x, int y, Color color) { + this.x = x; + this.y = y; + this.color = color; + } +} +--- + +$(P +Implement $(C opEquals) for this class in a way that ignores colors. When implemented in that way, the following $(C assert) check should pass: +) + +--- + // Different colors + auto bluePoint = new Point(1, 2, Color.blue); + auto greenPoint = new Point(1, 2, Color.green); + + // They are still equal + assert(bluePoint == greenPoint); +--- + +) + +$(PROBLEM +Implement $(C opCmp) by considering first $(C x) then $(C y). The following $(C assert) checks should pass: + +--- + auto redPoint1 = new Point(-1, 10, Color.red); + auto redPoint2 = new Point(-2, 10, Color.red); + auto redPoint3 = new Point(-2, 7, Color.red); + + assert(redPoint1 < bluePoint); + assert(redPoint3 < redPoint2); + + /* Even though blue is before green in enum Color, + * because color is being ignored, bluePoint must not be + * before greenPoint. */ + assert(!(bluePoint < greenPoint)); +--- + +$(P +Like the $(C Student) class above, you can implement $(C opCmp) by excluding incompatible types by the help of $(C enforce). +) + +) + +$(PROBLEM +Consider the following class that combines three $(C Point) objects in an array: + +--- +class TriangularArea { + Point[3] points; + + this(Point one, Point two, Point three) { + points = [ one, two, three ]; + } +} +--- + +$(P +Implement $(C toHash) for that class. Again, the following $(C assert) checks should pass: +) + +--- + /* area1 and area2 are constructed by distinct points that + * happen to have the same values. (Remember that + * bluePoint and greenPoint should be considered equal.) */ + auto area1 = new TriangularArea(bluePoint, greenPoint, redPoint1); + auto area2 = new TriangularArea(greenPoint, bluePoint, redPoint1); + + // The areas should be equal + assert(area1 == area2); + + // An associative array + double[TriangularArea] areas; + + // A value is being entered by area1 + areas[area1] = 1.25; + + // The value is being accessed by area2 + assert(area2 in areas); + assert(areas[area2] == 1.25); +--- + +$(P +Remember that $(C opEquals) must also be defined when $(C toHash) is defined. +) + +) + +) + +Macros: + SUBTITLE=Object + + DESCRIPTION=Object, the topmost class in class hierarchies in the D programming language + + KEYWORDS=d programming lesson book tutorial class Object opEquals opCmp toHash toString diff --git a/target/operator_overloading.cozum.d b/target/operator_overloading.cozum.d new file mode 100644 index 0000000..816f5cd --- /dev/null +++ b/target/operator_overloading.cozum.d @@ -0,0 +1,297 @@ +Ddoc + +$(COZUM_BOLUMU Operator Overloading) + +$(P +The following implementation passes all of the unit tests. The design decisions have been included as code comments. +) + +$(P +Some of the functions of this struct can be implemented to run more efficiently. Additionally, it would be beneficial to also $(I normalize) the numerator and denominator. For example, instead of keeping the values 20 and 60, the values could be divided by their $(I greatest common divisor) and the numerator and the denominator can be stored as 1 and 3 instead. Otherwise, most of the operations on the object would cause the values of the numerator and the denominator to increase. +) + +--- +import std.exception; +import std.conv; + +struct Fraction { + long num; // numerator + long den; // denominator + + /* As a convenience, the constructor uses the default + * value of 1 for the denominator. */ + this(long num, long den = 1) { + enforce(den != 0, "The denominator cannot be zero"); + + this.num = num; + this.den = den; + + /* Ensuring that the denominator is always positive + * will simplify the definitions of some of the + * operator functions. */ + if (this.den < 0) { + this.num = -this.num; + this.den = -this.den; + } + } + + /* Unary -: Returns the negative of this fraction. */ + Fraction opUnary(string op)() const + if (op == "-") { + /* Simply constructs and returns an anonymous + * object. */ + return Fraction(-num, den); + } + + /* ++: Increments the value of the fraction by one. */ + ref Fraction opUnary(string op)() + if (op == "++") { + /* We could have used 'this += Fraction(1)' here. */ + num += den; + return this; + } + + /* --: Decrements the value of the fraction by one. */ + ref Fraction opUnary(string op)() + if (op == "--") { + /* We could have used 'this -= Fraction(1)' here. */ + num -= den; + return this; + } + + /* +=: Adds the right-hand fraction to this one. */ + ref Fraction opOpAssign(string op)(in Fraction rhs) + if (op == "+") { + /* Addition formula: a/b + c/d = (a*d + c*b)/(b*d) */ + num = (num * rhs.den) + (rhs.num * den); + den *= rhs.den; + return this; + } + + /* -=: Subtracts the right-hand fraction from this one. */ + ref Fraction opOpAssign(string op)(in Fraction rhs) + if (op == "-") { + /* We make use of the already-defined operators += and + * unary - here. Alternatively, the subtraction + * formula could explicitly be applied similar to the + * += operator above. + * + * Subtraction formula: a/b - c/d = (a*d - c*b)/(b*d) + */ + this += -rhs; + return this; + } + + /* *=: Multiplies the fraction by the right-hand side. */ + ref Fraction opOpAssign(string op)(in Fraction rhs) + if (op == "*") { + /* Multiplication formula: a/b * c/d = (a*c)/(b*d) */ + num *= rhs.num; + den *= rhs.den; + return this; + } + + /* /=: Divides the fraction by the right-hand side. */ + ref Fraction opOpAssign(string op)(in Fraction rhs) + if (op == "/") { + enforce(rhs.num != 0, "Cannot divide by zero"); + + /* Division formula: (a/b) / (c/d) = (a*d)/(b*c) */ + num *= rhs.den; + den *= rhs.num; + return this; + } + + /* Binary +: Produces the result of adding this and the + * right-hand side fractions. */ + Fraction opBinary(string op)(in Fraction rhs) const + if (op == "+") { + /* Takes a copy of this fraction and adds the + * right-hand side fraction to that copy. */ + Fraction result = this; + result += rhs; + return result; + } + + /* Binary -: Produces the result of subtracting the + * right-hand side fraction from this one. */ + Fraction opBinary(string op)(in Fraction rhs) const + if (op == "-") { + /* Uses the already-defined -= operator. */ + Fraction result = this; + result -= rhs; + return result; + } + + /* Binary *: Produces the result of multiplying this + * fraction with the right-hand side fraction. */ + Fraction opBinary(string op)(in Fraction rhs) const + if (op == "*") { + /* Uses the already-defined *= operator. */ + Fraction result = this; + result *= rhs; + return result; + } + + /* Binary /: Produces the result of dividing this fraction + * by the right-hand side fraction. */ + Fraction opBinary(string op)(in Fraction rhs) const + if (op == "/") { + /* Uses the already-defined /= operator. */ + Fraction result = this; + result /= rhs; + return result; + } + + /* Returns the value of the fraction as double. */ + double opCast(T : double)() const { + /* A simple division. However, as dividing values of + * type long would lose the part of the value after + * the decimal point, we could not have written + * 'num/den' here. */ + return to!double(num) / den; + } + + /* Sort order operator: Returns a negative value if this + * fraction is before, a positive value if this fraction + * is after, and zero if both fractions have the same sort + * order. */ + int opCmp(const Fraction rhs) const { + immutable result = this - rhs; + /* Being a long, num cannot be converted to int + * automatically; it must be converted explicitly by + * 'to' (or cast). */ + return to!int(result.num); + } + + /* Equality comparison: Returns true if the fractions are + * equal. + * + * The equality comparison had to be defined for this type + * because the compiler-generated one would be comparing + * the members one-by-one, without regard to the actual + * values that the objects represent. + * + * For example, although the values of both Fraction(1,2) + * and Fraction(2,4) are 0.5, the compiler-generated + * opEquals would decide that they were not equal on + * account of having members of different values. */ + bool opEquals(const Fraction rhs) const { + /* Checking whether the return value of opCmp is zero + * is sufficient here. */ + return opCmp(rhs) == 0; + } +} + +unittest { + /* Must throw when denominator is zero. */ + assertThrown(Fraction(42, 0)); + + /* Let's start with 1/3. */ + auto a = Fraction(1, 3); + + /* -1/3 */ + assert(-a == Fraction(-1, 3)); + + /* 1/3 + 1 == 4/3 */ + ++a; + assert(a == Fraction(4, 3)); + + /* 4/3 - 1 == 1/3 */ + --a; + assert(a == Fraction(1, 3)); + + /* 1/3 + 2/3 == 3/3 */ + a += Fraction(2, 3); + assert(a == Fraction(1)); + + /* 3/3 - 2/3 == 1/3 */ + a -= Fraction(2, 3); + assert(a == Fraction(1, 3)); + + /* 1/3 * 8 == 8/3 */ + a *= Fraction(8); + assert(a == Fraction(8, 3)); + + /* 8/3 / 16/9 == 3/2 */ + a /= Fraction(16, 9); + assert(a == Fraction(3, 2)); + + /* Must produce the equivalent value in type 'double'. + * + * Note that although double cannot represent every value + * precisely, 1.5 is an exception. That is why this test + * is being applied at this point. */ + assert(to!double(a) == 1.5); + + /* 1.5 + 2.5 == 4 */ + assert(a + Fraction(5, 2) == Fraction(4, 1)); + + /* 1.5 - 0.75 == 0.75 */ + assert(a - Fraction(3, 4) == Fraction(3, 4)); + + /* 1.5 * 10 == 15 */ + assert(a * Fraction(10) == Fraction(15, 1)); + + /* 1.5 / 4 == 3/8 */ + assert(a / Fraction(4) == Fraction(3, 8)); + + /* Must throw when dividing by zero. */ + assertThrown(Fraction(42, 1) / Fraction(0)); + + /* The one with lower numerator is before. */ + assert(Fraction(3, 5) < Fraction(4, 5)); + + /* The one with larger denominator is before. */ + assert(Fraction(3, 9) < Fraction(3, 8)); + assert(Fraction(1, 1_000) > Fraction(1, 10_000)); + + /* The one with lower value is before. */ + assert(Fraction(10, 100) < Fraction(1, 2)); + + /* The one with negative value is before. */ + assert(Fraction(-1, 2) < Fraction(0)); + assert(Fraction(1, -2) < Fraction(0)); + + /* The ones with equal values must be both <= and >=. */ + assert(Fraction(-1, -2) <= Fraction(1, 2)); + assert(Fraction(1, 2) <= Fraction(-1, -2)); + assert(Fraction(3, 7) <= Fraction(9, 21)); + assert(Fraction(3, 7) >= Fraction(9, 21)); + + /* The ones with equal values must be equal. */ + assert(Fraction(1, 3) == Fraction(20, 60)); + + /* The ones with equal values with sign must be equal. */ + assert(Fraction(-1, 2) == Fraction(1, -2)); + assert(Fraction(1, 2) == Fraction(-1, -2)); +} + +void main() { +} +--- + +$(P +As has been mentioned in the chapter, string mixins can be used to combine the definitions of some of the operators. For example, the following definition covers the four arithmetic operators: +) + +--- + /* Binary arithmetic operators. */ + Fraction opBinary(string op)(in Fraction rhs) const + if ((op == "+") || (op == "-") || + (op == "*") || (op == "/")) { + /* Takes a copy of this fraction and applies the + * right-hand side fraction to that copy. */ + Fraction result = this; + mixin ("result " ~ op ~ "= rhs;"); + return result; + } +--- + + +Macros: + SUBTITLE=Operator Overloading Solution + + DESCRIPTION=Programming in D exercise solutions: operators overloading + + KEYWORDS=programming in d tutorial operator overloading solution diff --git a/target/operator_overloading.d b/target/operator_overloading.d new file mode 100644 index 0000000..3bc5545 --- /dev/null +++ b/target/operator_overloading.d @@ -0,0 +1,1902 @@ +Ddoc + +$(DERS_BOLUMU $(IX operator overloading) $(IX overloading, operator) Operator Overloading) + +$(P +The topics covered in this chapter apply mostly for classes as well. The biggest difference is that the behavior of assignment operation $(C opAssign()) cannot be overloaded for classes. +) + +$(P +Operator overloading involves many concepts, some of which will be covered later in the book (templates, $(C auto ref), etc.). For that reason, you may find this chapter to be harder to follow than the previous ones. +) + +$(P +Operator overloading enables defining how user-defined types behave when used with operators. In this context, the term $(I overload) means providing the definition of an operator for a specific type. +) + +$(P +We have seen how to define structs and their member functions in previous chapters. As an example, we have defined the $(C increment()) member function to be able to add $(C Duration) objects to $(C TimeOfDay) objects. Here are the two structs from previous chapters, with only the parts that are relevant to this chapter: +) + +--- +struct Duration { + int minute; +} + +struct TimeOfDay { + int hour; + int minute; + + void $(HILITE increment)(in Duration duration) { + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + } +} + +void main() { + auto lunchTime = TimeOfDay(12, 0); + lunchTime$(HILITE .increment)(Duration(10)); +} +--- + +$(P +A benefit of member functions is being able to define operations of a type alongside the member variables of that type. +) + +$(P +Despite their advantages, member functions can be seen as being limited compared to operations on fundamental types. After all, fundamental types can readily be used with operators: +) + +--- + int weight = 50; + weight $(HILITE +=) 10; // by an operator +--- + +$(P +According to what we have seen so far, similar operations can only be achieved by member functions for user-defined types: +) + +--- + auto lunchTime = TimeOfDay(12, 0); + lunchTime$(HILITE .increment)(Duration(10)); // by a member function +--- + +$(P +Operator overloading enables using structs and classes with operators as well. For example, assuming that the $(C +=) operator is defined for $(C TimeOfDay), the operation above can be written in exactly the same way as with fundamental types: +) + +--- + lunchTime $(HILITE +=) Duration(10); // by an operator + // (even for a struct) +--- + +$(P +Before getting to the details of operator overloading, let's first see how the line above would be enabled for $(C TimeOfDay). What is needed is to redefine the $(C increment()) member function under the special name $(C opOpAssign(string op)) and also to specify that this definition is for the $(C +) character. As it will be explained below, this definition actually corresponds to the $(C +=) operator. +) + +$(P +The definition of this member function does not look like the ones that we have seen so far. That is because $(C opOpAssign) is actually a $(I function template). Since we will see templates in much later chapters, I will have to ask you to accept the operator overloading syntax as is for now: +) + +--- +struct TimeOfDay { +// ... + ref TimeOfDay opOpAssign(string op)(in Duration duration)//(1) + if (op == "+") { //(2) + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + + return this; + } +} +--- + +$(P +The template definition consists of two parts: +) + +$(OL + +$(LI $(C opOpAssign(string op)): This part must be written as is and should be accepted as the $(I name) of the function. We will see below that there are other member functions in addition to $(C opOpAssign). +) + +$(LI $(C if (op == $(STRING "+"))): $(C opOpAssign) is used for more than one operator overload. $(C $(STRING "+")) specifies that this is the operator overload that corresponds to the $(C +) character. This syntax is a $(I template constraint), which will also be covered in later chapters. +) + +) + +$(P +Also note that this time the return type is different from the return type of the $(C increment()) member function: It is not $(C void) anymore. We will discuss the return types of operators later below. +) + +$(P +Behind the scenes, the compiler replaces the uses of the $(C +=) operator with calls to the $(C opOpAssign!$(STRING "+")) member function: +) + +--- + lunchTime += Duration(10); + + // The following line is the equivalent of the previous one + lunchTime.opOpAssign!"+"(Duration(10)); +--- + +$(P +The $(C !$(STRING "+")) part that is after $(C opOpAssign) specifies that this call is for the definition of the operator for the $(C +) character. We will cover this template syntax in later chapters as well. +) + +$(P +Note that the operator definition that corresponds to $(C +=) is defined by $(STRING "+"), not by $(STRING "+="). The $(C Assign) in the name of $(C opOpAssign()) already implies that this name is for an assignment operator. +) + +$(P +Being able to define the behaviors of operators brings a responsibility: The programmer must observe expectations. As an extreme example, the previous operator could have been defined to decrement the time value instead of incrementing it. However, people who read the code would still expect the value to be incremented by the $(C +=) operator. +) + +$(P +To some extent, the return types of operators can also be chosen freely. Still, general expectations must be observed for the return types as well. +) + +$(P +Keep in mind that operators that behave unnaturally would cause confusion and bugs. +) + +$(H5 Overloadable operators) + +$(P +There are different kinds of operators that can be overloaded. +) + +$(H6 $(IX unary operator) $(IX operator, unary) $(IX opUnary) Unary operators) + +$(P +An operator that takes a single operand is called a unary operator: +) + +--- + ++weight; +--- + +$(P +$(C ++) is a unary operator because it works on a single variable. +) + +$(P +Unary operators are defined by member functions named $(C opUnary). $(C opUnary) does not take any parameters because it uses only the object that the operator is being executed on. +) + +$(P +$(IX -, negation) +$(IX +, plus sign) +$(IX ~, bitwise complement) +$(IX *, pointee access) +$(IX ++, pre-increment) +$(IX --, pre-decrement) +The overloadable unary operators and the corresponding operator strings are the following: +) + +$(TABLE full, +$(HEAD3 Operator, Description, Operator String) +$(ROW3 -object, negative of (numeric complement of), "-") +$(ROW3 +object, the same value as (or, a copy of), "+") +$(ROW3 ~object, bitwise negation, "~") +$(ROW3 *object, access to what it points to, "*") +$(ROW3 ++object, increment, "++") +$(ROW3 --object, decrement, "--") +) + +$(P +For example, the $(C ++) operator for $(C Duration) can be defined like this: +) + +--- +struct Duration { + int minute; + + ref Duration opUnary(string op)() + if (op == "++") { + ++minute; + return this; + } +} +--- + +$(P +Note that the return type of the operator is marked as $(C ref) here as well. This will be explained later below. +) + +$(P +$(C Duration) objects can now be incremented by $(C ++): +) + +--- + auto duration = Duration(20); + ++duration; +--- + +$(P +$(IX ++, post-increment) $(IX --, post-decrement) The post-increment and post-decrement operators cannot be overloaded. The $(C object++) and $(C object--) uses are handled by the compiler automatically by saving the previous value of the object. For example, the compiler applies the equivalent of the following code for post-increment: +) + +--- + /* The previous value is copied by the compiler + * automatically: */ + Duration __previousValue__ = duration; + + /* The ++ operator is called: */ + ++duration; + + /* Then __previousValue__ is used as the value of the + * post-increment operation. */ +--- + +$(P +Unlike some other languages, the copy inside post-increment has no cost in D if the value of the post-increment expression is not actually used. This is because the compiler replaces such post-increment expressions with their pre-increment counterparts: +) + +--- + /* The value of the expression is not used below. The + * only effect of the expression is incrementing 'i'. */ + i++; +--- + +$(P +Because the $(I previous value) of $(C i) is not actually used above, the compiler replaces the expression with the following one: +) + +--- + /* The expression that is actually used by the compiler: */ + ++i; +--- + +$(P +Additionally, if an $(C opBinary) overload supports the $(C duration += 1) usage, then $(C opUnary) need not be overloaded for $(C ++duration) and $(C duration++). Instead, the compiler uses the $(C duration += 1) expression behind the scenes. Similarly, the $(C duration -= 1) overload covers the uses of $(C --duration) and $(C duration--) as well. +) + +$(H6 $(IX binary operator) $(IX operator, binary) Binary operators) + +$(P +An operator that takes two operands is called a binary operator: +) + +--- + totalWeight $(HILITE =) boxWeight $(HILITE +) chocolateWeight; +--- + +$(P +The line above has two separate binary operators: the $(C +) operator, which adds the values of the two operands that are on its two sides, and the $(C =) operator that assigns the value of its right-hand operand to its left-hand operand. +) + +$(P +$(IX +, addition) +$(IX -, subtraction) +$(IX *, multiplication) +$(IX /) +$(IX %) +$(IX ^^) +$(IX &, bitwise and) +$(IX |) +$(IX ^, bitwise exclusive or) +$(IX <<) +$(IX >>) +$(IX >>>) +$(IX ~, concatenation) +$(IX in, operator) +$(IX ==) +$(IX !=) +$(IX <, less than) +$(IX <=) +$(IX >, greater than) +$(IX >=) +$(IX =) +$(IX +=) +$(IX -=) +$(IX *=) +$(IX /=) +$(IX %=) +$(IX ^^=) +$(IX &=) +$(IX |=) +$(IX ^=) +$(IX <<=) +$(IX >>=) +$(IX >>>=) +$(IX ~=) +$(IX opBinary) +$(IX opAssign) +$(IX opOpAssign) +$(IX opBinaryRight) +The rightmost column below describes the category of each operator. The ones marked as "=" assign to the left-hand side object. +) + +$(TABLE full, +$(HEAD5 $(BR)Operator, $(BR)Description, $(BR)Function name, Function name$(BR)for right-hand side, $(BR)Category) +$(ROW5 +, add, opBinary, opBinaryRight, arithmetic) +$(ROW5 -, subtract, opBinary, opBinaryRight, arithmetic) +$(ROW5 *, multiply, opBinary, opBinaryRight, arithmetic) +$(ROW5 /, divide, opBinary, opBinaryRight, arithmetic) +$(ROW5 %, remainder of, opBinary, opBinaryRight, arithmetic) +$(ROW5 ^^, to the power of, opBinary, opBinaryRight, arithmetic) +$(ROW5 &, bitwise $(I and), opBinary, opBinaryRight, bitwise) +$(ROW5 |, bitwise $(I or), opBinary, opBinaryRight, bitwise) +$(ROW5 ^, bitwise $(I xor), opBinary, opBinaryRight, bitwise) +$(ROW5 <<, left-shift, opBinary, opBinaryRight, bitwise) +$(ROW5 >>, right-shift, opBinary, opBinaryRight, bitwise) +$(ROW5 >>>, unsigned right-shift, opBinary, opBinaryRight, bitwise) +$(ROW5 ~, concatenate, opBinary, opBinaryRight, ) +$(ROW5 in, whether contained in, opBinary, opBinaryRight, ) +$(ROW5 ==, whether equal to, opEquals, -, logical) +$(ROW5 !=, whether not equal to, opEquals, -, logical) +$(ROW5 <, whether before, opCmp, -, sorting) +$(ROW5 <=, whether not after, opCmp, -, sorting) +$(ROW5 >, whether after, opCmp, -, sorting) +$(ROW5 >=, whether not before, opCmp, -, sorting) +$(ROW5 =, assign, opAssign, -, =) +$(ROW5 +=, increment by, opOpAssign, -, =) +$(ROW5 -=, decrement by, opOpAssign, -, =) +$(ROW5 *=, multiply and assign, opOpAssign, -, =) +$(ROW5 /=, divide and assign, opOpAssign, -, =) +$(ROW5 %=, assign the remainder of, opOpAssign, -, =) +$(ROW5 ^^=, assign the power of, opOpAssign, -, =) +$(ROW5 &=, assign the result of &, opOpAssign, -, =) +$(ROW5 |=, assign the result of |, opOpAssign, -, =) +$(ROW5 ^=, assign the result of ^, opOpAssign, -, =) +$(ROW5 <<=, assign the result of <<, opOpAssign, -, =) +$(ROW5 >>=, assign the result of >>, opOpAssign, -, =) +$(ROW5 >>>=, assign the result of >>>, opOpAssign, -, =) +$(ROW5 ~=, append, opOpAssign, -, =) +) + +$(P +$(C opBinaryRight) is for when the object can appear on the right-hand side of the operator. Let's assume a binary operator that we shall call $(I op) appears in the program: +) + +--- + x $(I op) y +--- + +$(P +In order to determine what member function to call, the compiler considers the following two options: +) + +--- + // the definition for x being on the left: + x.opBinary!"op"(y); + + // the definition for y being on the right: + y.opBinaryRight!"op"(x); +--- + +$(P +The compiler picks the option that is a better match than the other. +) + +$(P +$(C opBinaryRight) is useful when defining arithmetic types that would normally work on both sides of an operator like e.g. $(C int) does: +) + +--- + auto x = MyInt(42); + x + 1; // calls opBinary!"+" + 1 + x; // calls opBinaryRight!"+" +--- + +$(P +Another common use of $(C opBinaryRight) is the $(C in) operator. It usually makes more sense to define $(C opBinaryRight) for the object that appears on the right-hand side of $(C in). We will see an example of this below. +) + +$(P +The parameter name $(C rhs) that appears in the following definitions is short for $(I right-hand side). It denotes the operand that appears on the right-hand side of the operator: +) + +--- + x $(I op) y +--- + +$(P +For the expression above, the $(C rhs) parameter would represent the variable $(C y). +) + +$(H5 Element indexing and slicing operators) + +$(P +The following operators enable using a type as a collection of elements: +) + +$(TABLE full, +$(HEAD3 Description, Function Name, Sample Usage) +$(ROW3 element access, opIndex, collection[i]) +$(ROW3 assignment to element, opIndexAssign, collection[i] = 7) +$(ROW3 unary operation on element, opIndexUnary, ++collection[i]) +$(ROW3 operation with assignment on element, opIndexOpAssign, collection[i] *= 2) +$(ROW3 number of elements, opDollar, collection[$ - 1]) +$(ROW3 slice of all elements, opSlice, collection[]) +$(ROW3 slice of some elements, opSlice(size_t, size_t), collection[i..j]) +) + +$(P +We will cover those operators later below. +) + +$(P +The following operator functions are from the earlier versions of D. They are discouraged: +) + +$(TABLE full, +$(HEAD3 Description, Function Name, Sample Usage) +$(ROW3 unary operation on all elements, opSliceUnary (discouraged), ++collection[]) +$(ROW3 unary operation on some elements, opSliceUnary (discouraged), ++collection[i..j]) +$(ROW3 assignment to all elements, opSliceAssign (discouraged), collection[] = 42) +$(ROW3 assignment to some elements, opSliceAssign (discouraged), collection[i..j] = 7) +$(ROW3 operation with assignment on all elements, opSliceOpAssign (discouraged), collection[] *= 2) +$(ROW3 operation with assignment on some elements, opSliceOpAssign (discouraged), collection[i..j] *= 2) + +) + +$(H6 Other operators) + +$(P +The following operators can be overloaded as well: +) + +$(TABLE full, +$(HEAD3 Description, Function Name, Sample Usage) +$(ROW3 function call, opCall, object(42)) +$(ROW3 type conversion, opCast, to!int(object)) +$(ROW3 dispatch for non-existent function, opDispatch, object.nonExistent()) +) + +$(P +These operators will be explained below under their own sections. +) + +$(H5 Defining more than one operator at the same time) + +$(P +To keep the code samples short, we have used only the $(C ++), $(C +), and $(C +=) operators above. It is conceivable that when one operator is overloaded for a type, many others would also need to be overloaded. For example, the $(C --) and $(C -=) operators are also defined for the following $(C Duration): +) + +--- +struct Duration { + int minute; + + ref Duration opUnary(string op)() + if (op == "++") { + $(HILITE ++)minute; + return this; + } + + ref Duration opUnary(string op)() + if (op == "--") { + $(HILITE --)minute; + return this; + } + + ref Duration opOpAssign(string op)(in int amount) + if (op == "+") { + minute $(HILITE +)= amount; + return this; + } + + ref Duration opOpAssign(string op)(in int amount) + if (op == "-") { + minute $(HILITE -)= amount; + return this; + } +} + +unittest { + auto duration = Duration(10); + + ++duration; + assert(duration.minute == 11); + + --duration; + assert(duration.minute == 10); + + duration += 5; + assert(duration.minute == 15); + + duration -= 3; + assert(duration.minute == 12); +} + +void main() { +} +--- + +$(P +The operator overloads above have code duplications. The only differences between the similar functions are highlighted. Such code duplications can be reduced and sometimes avoided altogether by $(I string mixins). We will see the $(C mixin) keyword in a later chapter as well. I would like to show briefly how this keyword helps with operator overloading. +) + +$(P +$(C mixin) inserts the specified string as source code right where the $(C mixin) statement appears in code. The following struct is the equivalent of the one above: +) + +--- +struct Duration { + int minute; + + ref Duration opUnary(string op)() + if ((op == "++") || (op == "--")) { + $(HILITE mixin) (op ~ "minute;"); + return this; + } + + ref Duration opOpAssign(string op)(in int amount) + if ((op == "+") || (op == "-")) { + $(HILITE mixin) ("minute " ~ op ~ "= amount;"); + return this; + } +} +--- + +$(P +If the $(C Duration) objects also need to be multiplied and divided by an amount, all that is needed is to add two more conditions to the template constraint: +) + +--- +struct Duration { +// ... + + ref Duration opOpAssign(string op)(in int amount) + if ((op == "+") || (op == "-") || + $(HILITE (op == "*") || (op == "/"))) { + mixin ("minute " ~ op ~ "= amount;"); + return this; + } +} + +unittest { + auto duration = Duration(12); + + duration $(HILITE *=) 4; + assert(duration.minute == 48); + + duration $(HILITE /=) 2; + assert(duration.minute == 24); +} +--- + +$(P +In fact, the template constraints are optional: +) + +--- + ref Duration opOpAssign(string op)(in int amount) + /* no constraint */ { + mixin ("minute " ~ op ~ "= amount;"); + return this; + } +--- + +$(H5 $(IX return type, operator) Return types of operators) + +$(P +When overloading an operator, it is advisable to observe the return type of the same operator on fundamental types. This would help with making sense of code and reducing confusions. +) + +$(P +None of the operators on fundamental types return $(C void). This fact should be obvious for some operators. For example, the result of adding two $(C int) values as $(C a + b) is $(C int): +) + +--- + int a = 1; + int b = 2; + int c = a + b; // c gets initialized by the return value + // of the + operator +--- + +$(P +The return values of some other operators may not be so obvious. For example, even operators like $(C ++i) have values: +) + +--- + int i = 1; + writeln(++i); // prints 2 +--- + +$(P +The $(C ++) operator not only increments $(C i), it also produces the new value of $(C i). Further, the value that is produced by $(C ++) is not just the new value of $(C i), rather $(I the variable $(C i) itself). We can see this fact by printing the address of the result of that expression: +) + +--- + int i = 1; + writeln("The address of i : ", &i); + writeln("The address of the result of ++i: ", &(++i)); +--- + +$(P +The output contains identical addresses: +) + +$(SHELL +The address of i : 7FFF39BFEE78 +The address of the result of ++i: 7FFF39BFEE78 +) + +$(P +I recommend that you observe the following guidelines when overloading operators for your own types: +) + +$(UL + +$(LI $(B Operators that modify the object) + +$(P +With the exception of $(C opAssign), it is recommended that the operators that modify the object return the object itself. This guideline has been observed above with the $(C TimeOfDay.opOpAssign!$(STRING "+")) and $(C Duration.opUnary!$(STRING "++")). +) + +$(P +The following two steps achieve returning the object itself: +) + +$(OL + +$(LI The return type is the type of the struct, marked by the $(C ref) keyword to mean $(I reference). +) + +$(LI The function is exited by $$(C return this) to mean $(I return this object). +) + +) + +$(P +The operators that modify the object are $(C opUnary!$(STRING "++")), $(C opUnary!$(STRING "--")), and all of the $(C opOpAssign) overloads. +) + +) + +$(LI $(B Logical operators) + +$(P +$(C opEquals) that represents both $(C ==) and $(C !=) must return $(C bool). Although the $(C in) operator normally returns $(I the contained object), it can simply return $(C bool) as well. +) + +) + +$(LI $(B Sort operators) + +$(P +$(C opCmp) that represents $(C <), $(C <=), $(C >), and $(C >=) must return $(C int). +) + +) + +$(LI $(B Operators that make a new object) + +$(P +Some operators must make and return a new object: +) + +$(UL + +$(LI Unary operators $(C -), $(C +), and $(C ~); and the binary operator $(C ~).) + +$(LI Arithmetic operators $(C +), $(C -), $(C *), $(C /), $(C %), and $(C ^^).) + +$(LI Bitwise operators $(C &), $(C |), $(C ^), $(C <<), $(C >>), and $(C >>>).) + +$(LI As has been seen in the previous chapter, $(C opAssign) returns a copy of this object by $(C return this). + +$(P $(I $(B Note:) As an optimization, sometimes it makes more sense for $(C opAssign) to return $(C const ref) for large structs. I will not apply this optimization in this book.)) + +) + +) + +$(P +As an example of an operator that makes a new object, let's define the $(C opBinary!$(STRING "+")) overload for $(C Duration). This operator should add two $(C Duration) objects to make and return a new one: +) + +--- +struct Duration { + int minute; + + Duration opBinary(string op)(in Duration rhs) const + if (op == "+") { + return Duration(minute + rhs.minute); // new object + } +} +--- + +$(P +That definition enables adding $(C Duration) objects by the $(C +) operator: +) + +--- + auto travelDuration = Duration(10); + auto returnDuration = Duration(11); + Duration totalDuration; + // ... + totalDuration = travelDuration $(HILITE +) returnDuration; +--- + +$(P +The compiler replaces that expression with the following member function call on the $(C travelDuration) object: +) + +--- + // the equivalent of the expression above: + totalDuration = + travelDuration.opBinary!"+"(returnDuration); +--- + +) + +$(LI $(C opDollar) + +$(P +Since it returns the number of elements of the container, the most suitable type for $(C opDollar) is $(C size_t). However, the return type can be other types as well (e.g. $(C int)). +) + +) + +$(LI $(B Unconstrained operators) + +$(P +The return types of some of the operators depend entirely on the design of the user-defined type: The unary $(C *), $(C opCall), $(C opCast), $(C opDispatch), $(C opSlice), and all $(C opIndex) varieties. +) + +) + +) + +$(H5 $(IX opEquals) $(C opEquals()) for equality comparisons) + +$(P +This member function defines the behaviors of the $(C ==) and the $(C !=) operators. +) + +$(P +The return type of $(C opEquals) is $(C bool). +) + +$(P +For structs, the parameter of $(C opEquals) can be defined as $(C in). However, for speed efficiency $(C opEquals) can be defined as a template that takes $(C auto ref const) (also note the empty template parentheses below): +) + +--- + bool opEquals$(HILITE ())(auto ref const TimeOfDay rhs) const { + // ... + } +--- + +$(P +As we have seen in $(LINK2 /ders/d.en/lvalue_rvalue.html, the Lvalues and Rvalues chapter), $(C auto ref) allows lvalues to be passed by reference and rvalues by copy. However, since rvalues are not copied, rather moved, the signature above is efficient for both lvalues and rvalues. +) + +$(P +To reduce confusion, $(C opEquals) and $(C opCmp) must work consistently. For two objects that $(C opEquals) returns $(C true), $(C opCmp) must return zero. +) + +$(P +Once $(C opEquals()) is defined for equality, the compiler uses its opposite for inequality: +) + +--- + x == y; + // the equivalent of the previous expression: + x.opEquals(y); + + x != y; + // the equivalent of the previous expression: + !(x.opEquals(y)); +--- + +$(P +Normally, it is not necessary to define $(C opEquals()) for structs. The compiler generates it for structs automatically. The automatically-generated $(C opEquals) compares all of the members individually. +) + +$(P +Sometimes the equality of two objects must be defined differently from this automatic behavior. For example, some of the members may not be significant in this comparison, or the equality may depend on a more complex logic. +) + +$(P +Just as an example, let's define $(C opEquals()) in a way that disregards the minute information altogether: +) + +--- +struct TimeOfDay { + int hour; + int minute; + + bool opEquals(in TimeOfDay rhs) const { + return hour == rhs.hour; + } +} +// ... + assert(TimeOfDay(20, 10) $(HILITE ==) TimeOfDay(20, 59)); +--- + +$(P +Since the equality comparison considers the values of only the $(C hour) members, 20:10 and 20:59 end up being equal. (This is just an example; it should be clear that such an equality comparison would cause confusions.) +) + +$(H5 $(IX opCmp) $(C opCmp()) for sorting) + +$(P +Sort operators determine the sort orders of objects. All of the ordering operators $(C <), $(C <=), $(C >), and $(C >=) are covered by the $(C opCmp()) member function. +) + +$(P +For structs, the parameter of $(C opCmp) can be defined as $(C in). However, as with $(C opEquals), it is more efficient to define $(C opCmp) as a template that takes $(C auto ref const): +) + +--- + int opCmp$(HILITE ())(auto ref const TimeOfDay rhs) const { + // ... + } +--- + +$(P +To reduce confusion, $(C opEquals) and $(C opCmp) must work consistently. For two objects that $(C opEquals) returns $(C true), $(C opCmp) must return zero. +) + +$(P +Let's assume that one of these four operators is used as in the following code: +) + +--- + if (x $(I op) y) { $(CODE_NOTE $(I op) is one of <, <=, >, or >=) +--- + +$(P +The compiler converts that expression to the following logical expression and uses the result of the new logical expression: +) + +--- + if (x.opCmp(y) $(I op) 0) { +--- + +$(P +Let's consider the $(C <=) operator: +) + +--- + if (x $(HILITE <=) y) { +--- + +$(P +The compiler generates the following code behind the scenes: +) + +--- + if (x.opCmp(y) $(HILITE <=) 0) { +--- + +$(P +For the user-defined $(C opCmp()) to work correctly, this member function must return a result according to the following rules: +) + +$(UL +$(LI $(I A negative value) if the left-hand object is considered to be before the right-hand object) +$(LI $(I A positive value) if the left-hand object is considered to be after the right-hand object) +$(LI $(I Zero) if the objects are considered to have the same sort order) +) + +$(P +To be able to support those values, the return type of $(C opCmp()) must be $(C int), not $(C bool). +) + +$(P +The following is a way of ordering $(C TimeOfDay) objects by first comparing the values of the $(C hour) members, and then comparing the values of the $(C minute) members (only if the $(C hour) members are equal): +) + +--- + int opCmp(in TimeOfDay rhs) const { + /* Note: Subtraction is a bug here if the result can + * overflow. (See the following warning in text.) */ + + return (hour == rhs.hour + ? minute - rhs.minute + : hour - rhs.hour); + } +--- + +$(P +That definition returns the difference between the $(C minute) values when the $(C hour) members are the same, and the difference between the $(C hour) members otherwise. The return value would be a $(I negative value) when the $(I left-hand) object comes before in chronological order, a $(I positive value) if the $(I right-hand) object is before, and $(I zero) when they represent exactly the same time of day. +) + +$(P +$(B Warning:) Using subtraction for the implementation of $(C opCmp) is a bug if valid values of a member can cause overflow. For example, the two objects below would be sorted incorrectly as the object with value $(C -2) is calculated to be $(I greater) than the one with value $(C int.max): +) + +--- +struct S { + int i; + + int opCmp(in S rhs) const { + return i - rhs.i; $(CODE_NOTE_WRONG BUG) + } +} + +void main() { + assert(S(-2) $(HILITE >) S(int.max)); $(CODE_NOTE_WRONG wrong sort order) +} +--- + +$(P +On the other hand, subtraction is acceptable for $(C TimeOfDay) because none of the valid values of the members of that $(C struct) can cause overflow in subtraction. +) + +$(P +$(IX cmp, std.algorithm) $(IX lexicographical) You can use $(C std.algorithm.cmp) for comparing slices (including all string types and ranges). $(C cmp()) compares slices lexicographically and produces a negative value, zero, or positive value depending on their order. That result can directly be used as the return value of $(C opCmp): +) + +--- +import std.algorithm; + +struct S { + string name; + + int opCmp(in S rhs) const { + return $(HILITE cmp)(name, rhs.name); + } +} +--- + +$(P +Once $(C opCmp()) is defined, this type can be used with sorting algorithms like $(C std.algorithm.sort) as well. As $(C sort()) works on the elements, it is the $(C opCmp()) operator that gets called behind the scenes to determine their order. The following program constructs 10 objects with random values and sorts them with $(C sort()): +) + +--- +import std.random; +import std.stdio; +import std.string; +import std.algorithm; + +struct TimeOfDay { + int hour; + int minute; + + int opCmp(in TimeOfDay rhs) const { + return (hour == rhs.hour + ? minute - rhs.minute + : hour - rhs.hour); + } + + string toString() const { + return format("%02s:%02s", hour, minute); + } +} + +void main() { + TimeOfDay[] times; + + foreach (i; 0 .. 10) { + times ~= TimeOfDay(uniform(0, 24), uniform(0, 60)); + } + + sort(times); + + writeln(times); +} +--- + +$(P +As expected, the elements are sorted from the earliest time to the latest time: +) + +$(SHELL +[03:40, 04:10, 09:06, 10:03, 10:09, 11:04, 13:42, 16:40, 18:03, 21:08] +) + +$(H5 $(IX opCall) $(IX ()) $(C opCall()) to call objects as functions) + +$(P +The parentheses around the parameter list when calling functions are operators as well. We have already seen how $(C static opCall()) makes it possible to use the name of the $(I type) as a function. $(C static opCall()) allows creating objects with default values at run time. +) + +$(P +Non-static $(C opCall()) on the other hand allows using the $(I objects) of user-defined types as functions: +) + +--- + Foo foo; + foo$(HILITE ()); +--- + +$(P +The object $(C foo) above is being called like a function. +) + +$(P +As an example, let's consider a $(C struct) that represents a linear equation. This $(C struct) will be used for calculating the $(I y) values of the following linear equation for specific $(I x) values: +) + +$(MONO + $(I y) = $(I a)$(I x) + $(I b) +) + +$(P +The following $(C opCall()) simply calculates and returns the value of $(I y) according to that equation: +) + +--- +struct LinearEquation { + double a; + double b; + + double opCall(double x) const { + return a * x + b; + } +} +--- + +$(P +With that definition, each object of $(C LinearEquation) represents a linear equation for specific $(I a) and $(I b) values. Such an object can be used as a function that calculates the $(I y) values: +) + +--- + LinearEquation equation = { 1.2, 3.4 }; + // the object is being used like a function: + double y = equation(5.6); +--- + +$(P +$(I $(B Note:) Defining $(C opCall()) for a $(C struct) disables the compiler-generated automatic constructor. That is why the $(C { }) syntax is used above instead of the recommended $(C LinearEquation(1.2, 3.4)). When the latter syntax is desired, a $(C static opCall()) that takes two $(C double) parameters must also be defined.) +) + +$(P +$(C equation) above represents the $(I y = 1.2x + 3.4) linear equation. Using that object as a function executes the $(C opCall()) member function. +) + +$(P +This feature can be useful to define and store the $(I a) and $(I b) values in an object once and to use that object multiple times later on. The following code uses such an object in a loop: +) + +--- + LinearEquation equation = { 0.01, 0.4 }; + + for (double x = 0.0; x <= 1.0; x += 0.125) { + writefln("%f: %f", x, equation(x)); + } +--- + +$(P +That object represents the $(I y = 0.01x + 0.4) equation. It is being used for calculating the results for $(I x) values in the range from 0.0 to 1.0. +) + +$(H5 $(IX opIndex) $(IX opIndexAssign) $(IX opIndexUnary) $(IX opIndexOpAssign) $(IX opDollar) Indexing operators) + +$(P +$(C opIndex), $(C opIndexAssign), $(C opIndexUnary), $(C opIndexOpAssign), and $(C opDollar) make it possible to use indexing operators on user-defined types similar to arrays as in $(C object[index]). +) + +$(P +Unlike arrays, these operators support multi-dimensional indexing as well. Multiple index values are specified as a comma-separated list inside the square brackets (e.g. $(C object[index0, index1])). In the following examples we will use these operators only with a single dimension and cover their multi-dimensional uses in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter). +) + +$(P +The $(C deque) variable in the following examples is an object of $(C struct DoubleEndedQueue), which we will define below; and $(C e) is a variable of type $(C int). +) + +$(P +$(C opIndex) is for element access. The index that is specified inside the brackets becomes the parameter of the operator function: +) + +--- + e = deque[3]; // the element at index 3 + e = deque.opIndex(3); // the equivalent of the above +--- + +$(P +$(C opIndexAssign) is for assigning to an element. The first parameter is the value that is being assigned and the second parameter is the index of the element: +) + +--- + deque[5] = 55; // assign 55 to the element at index 5 + deque.opIndexAssign(55, 5); // the equivalent of the above +--- + +$(P +$(C opIndexUnary) is similar to $(C opUnary). The difference is that the operation is applied to the element at the specified index: +) + +--- + ++deque[4]; // increment the element at index 4 + deque.opIndexUnary!"++"(4); // the equivalent of the above +--- + +$(P +$(C opIndexOpAssign) is similar to $(C opOpAssign). The difference is that the operation is applied to an element: +) + +--- + deque[6] += 66; // add 66 to the element at index 6 + deque.opIndexOpAssign!"+"(66, 6);// the equivalent of the above +--- + +$(P +$(C opDollar) defines the $(C $) character that is used during indexing and slicing. It is for returning the number of elements in the container: +) + +--- + e = deque[$ - 1]; // the last element + e = deque[deque.opDollar() - 1]; // the equivalent of the above +--- + +$(H6 Indexing operators example) + +$(P +$(I Double-ended queue) is a data structure that is similar to arrays but it provides efficient insertion at the head of the collection as well. (In contrast, inserting at the head of an array is a relatively slow operation as it requires moving the existing elements to a newly created array.) +) + +$(P +One way of implementing a double-ended queue is to use two arrays in the background but to use the first one in reverse. The element that is conceptually inserted at the head of the queue is actually appended to the $(I head) array. As a result, this operation is as efficient as appending to the end. +) + +$(P +The following $(C struct) implements a double-ended queue that overloads the operators that we have seen in this section: +) + +--- +$(CODE_NAME DoubleEndedQueue)import std.stdio; +import std.string; +import std.conv; + +$(CODE_COMMENT_OUT)struct DoubleEndedQueue // Also known as Deque +$(CODE_COMMENT_OUT){ +private: + + /* The elements are represented as the chaining of the two + * member slices. However, 'head' is indexed in reverse so + * that the first element of the entire collection is + * head[$-1], the second one is head[$-2], etc.: + * + * head[$-1], head[$-2], ... head[0], tail[0], ... tail[$-1] + */ + int[] head; // the first group of elements + int[] tail; // the second group of elements + + /* Determines the actual slice that the specified element + * resides in and returns it as a reference. */ + ref inout(int) elementAt(size_t index) inout { + return (index < head.length + ? head[$ - 1 - index] + : tail[index - head.length]); + } + +public: + + string toString() const { + string result; + + foreach_reverse (element; head) { + result ~= format("%s ", to!string(element)); + } + + foreach (element; tail) { + result ~= format("%s ", to!string(element)); + } + + return result; + } + + /* Note: As we will see in the next chapter, the following + * is a simpler and more efficient implementation of + * toString(): */ + version (none) { + void toString(void delegate(const(char)[]) sink) const { + import std.format; + import std.range; + + formattedWrite( + sink, "%(%s %)", chain(head.retro, tail)); + } + } + + /* Adds a new element to the head of the collection. */ + void insertAtHead(int value) { + head ~= value; + } + + /* Adds a new element to the tail of the collection. + * + * Sample: deque ~= value + */ + ref DoubleEndedQueue opOpAssign(string op)(int value) + if (op == "~") { + tail ~= value; + return this; + } + + /* Returns the specified element. + * + * Sample: deque[index] + */ + inout(int) opIndex(size_t index) inout { + return elementAt(index); + } + + /* Applies a unary operation to the specified element. + * + * Sample: ++deque[index] + */ + int opIndexUnary(string op)(size_t index) { + mixin ("return " ~ op ~ "elementAt(index);"); + } + + /* Assigns a value to the specified element. + * + * Sample: deque[index] = value + */ + int opIndexAssign(int value, size_t index) { + return elementAt(index) = value; + } + + /* Uses the specified element and a value in a binary + * operation and assigns the result back to the same + * element. + * + * Sample: deque[index] += value + */ + int opIndexOpAssign(string op)(int value, size_t index) { + mixin ("return elementAt(index) " ~ op ~ "= value;"); + } + + /* Defines the $ character, which is the length of the + * collection. + * + * Sample: deque[$ - 1] + */ + size_t opDollar() const { + return head.length + tail.length; + } +$(CODE_COMMENT_OUT)} + +void $(CODE_DONT_TEST)main() { + auto deque = DoubleEndedQueue(); + + foreach (i; 0 .. 10) { + if (i % 2) { + deque.insertAtHead(i); + + } else { + deque ~= i; + } + } + + writefln("Element at index 3: %s", + deque[3]); // accessing an element + ++deque[4]; // incrementing an element + deque[5] = 55; // assigning to an element + deque[6] += 66; // adding to an element + + (deque ~= 100) ~= 200; + + writeln(deque); +} +--- + +$(P +According to the guidelines above, the return type of $(C opOpAssign) is $(C ref) so that the $(C ~=) operator can be chained on the same collection: +) + +--- + (deque ~= 100) ~= 200; +--- + +$(P +As a result, both 100 and 200 get appended to the same collection: +) + +$(SHELL +Element at index 3: 3 +9 7 5 3 2 55 68 4 6 8 100 200 +) + +$(H5 $(IX opSlice) Slicing operators) + +$(P +$(C opSlice) allows slicing the objects of user-defined types with the $(C []) operator. +) + +$(P +$(IX opSliceUnary) $(IX opSliceAssign) $(IX opSliceOpAssign) In addition to this operator, there are also $(C opSliceUnary), $(C opSliceAssign), and $(C opSliceOpAssign) but they are discouraged. +) + +$(P +D supports multi-dimensional slicing. We will see a multi-dimensional example later in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter). Although the methods described in that chapter can be used for a single dimension as well, they do not match the indexing operators that are defined above and they involve templates which we have not covered yet. For that reason, we will see the non-templated use of $(C opSlice) in this chapter, which works only with a single dimension. (This use of $(C opSlice) is discouraged as well.) +) + +$(P +$(C opSlice) has two distinct forms: +) + +$(UL + +$(LI The square brackets can be empty as in $(C deque[]) to mean $(I all elements).) + +$(LI The square brackets can contain a number range as in $(C deque[begin..end]) to mean $(I the elements in the specified range).) + +) + +$(P +The slicing operators are relatively more complex than other operators because they involve two distinct concepts: $(I container) and $(I range). We will see these concepts in more detail in later chapters. +) + +$(P +In single-dimensional slicing which does not use templates, $(C opSlice) returns an object that represents a specific range of elements of the container. The object that $(C opSlice) returns is responsible for defining the operations that are applied on that range elements. For example, behind the scenes the following expression is executed by first calling $(C opSlice) to obtain a range object and then applying $(C opOpAssign!$(STRING "*")) on that object: +) + +--- + deque[] *= 10; // multiply all elements by 10 + + // The equivalent of the above: + { + auto range = deque.opSlice(); + range.opOpAssign!"*"(10); + } +--- + +$(P +Accordingly, the $(C opSlice) operators of $(C DoubleEndedQueue) return a special $(C Range) object so that the operations are applied to it: +) + +--- +import std.exception; + +struct DoubleEndedQueue { +// ... + + /* Returns a range that represents all of the elements. + * ('Range' struct is defined below.) + * + * Sample: deque[] + */ + inout(Range) $(HILITE opSlice)() inout { + return inout(Range)(head[], tail[]); + } + + /* Returns a range that represents some of the elements. + * + * Sample: deque[begin .. end] + */ + inout(Range) $(HILITE opSlice)(size_t begin, size_t end) inout { + enforce(end <= opDollar()); + enforce(begin <= end); + + /* Determine what parts of 'head' and 'tail' + * correspond to the specified range: */ + + if (begin < head.length) { + if (end < head.length) { + /* The range is completely inside 'head'. */ + return inout(Range)( + head[$ - end .. $ - begin], + []); + + } else { + /* Some part of the range is inside 'head' and + * the rest is inside 'tail'. */ + return inout(Range)( + head[0 .. $ - begin], + tail[0 .. end - head.length]); + } + + } else { + /* The range is completely inside 'tail'. */ + return inout(Range)( + [], + tail[begin - head.length .. end - head.length]); + } + } + + /* Represents a range of elements of the collection. This + * struct is responsible for defining the opUnary, + * opAssign, and opOpAssign operators. */ + struct $(HILITE Range) { + int[] headRange; // elements that are in 'head' + int[] tailRange; // elements that are in 'tail' + + /* Applies the unary operation to the elements of the + * range. */ + Range opUnary(string op)() { + mixin (op ~ "headRange[];"); + mixin (op ~ "tailRange[];"); + return this; + } + + /* Assigns the specified value to each element of the + * range. */ + Range opAssign(int value) { + headRange[] = value; + tailRange[] = value; + return this; + } + + /* Uses each element and a value in a binary operation + * and assigns the result back to that element. */ + Range opOpAssign(string op)(int value) { + mixin ("headRange[] " ~ op ~ "= value;"); + mixin ("tailRange[] " ~ op ~ "= value;"); + return this; + } + } +$(CODE_XREF DoubleEndedQueue)} + +void $(CODE_DONT_TEST)main() { + auto deque = DoubleEndedQueue(); + + foreach (i; 0 .. 10) { + if (i % 2) { + deque.insertAtHead(i); + + } else { + deque ~= i; + } + } + + writeln(deque); + deque$(HILITE []) *= 10; + deque$(HILITE [3 .. 7]) = -1; + writeln(deque); +} +--- + +$(P +The output: +) + +$(SHELL +9 7 5 3 1 0 2 4 6 8 +90 70 50 -1 -1 -1 -1 40 60 80 +) + +$(H5 $(IX opCast) $(IX type conversion, opCast) $(C opCast) for type conversions) + +$(P +$(C opCast) defines explicit type conversions. It can be overloaded separately for each target type. As you would remember from the earlier chapters, explicit type conversions are performed by the $(C to) function and the $(C cast) operator. +) + +$(P +$(C opCast) is a template as well, but it has a different format: The target type is specified by the $(C (T : target_type)) syntax: +) + +--- + $(I target_type) opCast(T : $(I target_type))() { + // ... + } +--- + +$(P +This syntax will become clear later after the templates chapter as well. +) + +$(P +Let's change the definition of $(C Duration) so that it now has two members: hours and minutes. The operator that converts objects of this type to $(C double) can be defined as in the following code: +) + +--- +import std.stdio; +import std.conv; + +struct Duration { + int hour; + int minute; + + double opCast(T : double)() const { + return hour + (to!double(minute) / 60); + } +} + +void main() { + auto duration = Duration(2, 30); + double d = to!double(duration); + // (could be 'cast(double)duration' as well) + + writeln(d); +} +--- + +$(P +The compiler replaces the type conversion call above with the following one: +) + +--- + double d = duration.opCast!double(); +--- + +$(P +The $(C double) conversion function above produces 2.5 for two hours and thirty minutes: +) + +$(SHELL +2.5 +) + +$(P +$(IX opCast!bool) $(IX bool, opCast) Although $(C opCast) is for explicit type conversions, its $(C bool) specialization is called automatically when the variable is used in a logical expression: +) + +--- +struct Duration { +// ... + + bool opCast($(HILITE T : bool))() const { + return (hour != 0) || (minute != 0); + } +} + +// ... + + if (duration) { // compiles + // ... + } + + while (duration) { // compiles + // ... + } + + auto r = duration ? 1 : 2; // compiles +--- + +$(P +Still, the $(C bool) specialization of $(C opCast) is not for all implicit $(C bool) conversions: +) + +--- +void foo(bool b) { + // ... +} + +// ... + + foo(duration); $(DERLEME_HATASI) + bool b = duration; $(DERLEME_HATASI) +--- + +$(SHELL +Error: cannot implicitly convert expression (duration) of type Duration to +bool +Error: function deneme.foo (bool b) is not callable using argument types +(Duration) +) + +$(H5 $(IX opDispatch) Catch-all operator $(C opDispatch)) + +$(P +$(C opDispatch) gets called whenever a $(I missing) member of an object is accessed. All attempts to access non-existent members are dispatched to this function. +) + +$(P +The name of the missing member becomes the template parameter value of $(C opDispatch). +) + +$(P +The following code demonstrates a simple definition: +) + +--- +import std.stdio; + +struct Foo { + void opDispatch(string name, T)(T parameter) { + writefln("Foo.opDispatch - name: %s, value: %s", + name, parameter); + } +} + +void main() { + Foo foo; + foo.aNonExistentFunction(42); + foo.anotherNonExistentFunction(100); +} +--- + +$(P +There are no compiler errors for the calls to non-existent members. Instead, all of those calls are dispatched to $(C opDispatch). The first template parameter is the name of the member. The parameter values that are used when calling the function appear as the parameters of $(C opDispatch): +) + +$(SHELL +Foo.opDispatch - name: aNonExistentFunction, value: 42 +Foo.opDispatch - name: anotherNonExistentFunction, value: 100 +) + +$(P +The $(C name) template parameter can be used inside the function to make decisions on how the call to that specific non-existent function should be handled: +) + +--- + switch (name) { + // ... + } +--- + +$(H5 $(IX in, operator overloading) $(IX !in) Inclusion query by $(C opBinaryRight!"in")) + +$(P +This operator allows defining the behavior of the $(C in) operator for user-defined types. $(C in) is commonly used with associative arrays to determine whether a value for a specific key exists in the array. +) + +$(P +Different from other operators, this operator is normally overloaded for the case where the object appears on the right-hand side: +) + +--- + if (time in lunchBreak) { +--- + +$(P +The compiler would use $(C opBinaryRight) behind the scenes: +) + +--- + // the equivalent of the above: + if (lunchBreak.opBinaryRight!"in"(time)) { +--- + +$(P +There is also $(C !in) to determine whether a value for a specific key $(I does not) exist in the array: +) + +--- + if (a !in b) { +--- + +$(P +$(C !in) cannot be overloaded because the compiler uses the negative of the result of the $(C in) operator instead: +) + +--- + if (!(a in b)) { // the equivalent of the above +--- + +$(H6 Example of the $(C in) operator) + +$(P +The following program defines a $(C TimeSpan) type in addition to $(C Duration) and $(C TimeOfDay). The $(C in) operator that is defined for $(C TimeSpan) determines whether a moment in time is within that time span. +) + +$(P +To keep the code short, the following program defines only the necessary member functions. +) + +$(P +Note how the $(C TimeOfDay) object is used seamlessly in the $(C for) loop. That loop is a demonstration of how useful operator overloading can be. +) + +--- +import std.stdio; +import std.string; + +struct Duration { + int minute; +} + +struct TimeOfDay { + int hour; + int minute; + + ref TimeOfDay opOpAssign(string op)(in Duration duration) + if (op == "+") { + minute += duration.minute; + + hour += minute / 60; + minute %= 60; + hour %= 24; + + return this; + } + + int opCmp(in TimeOfDay rhs) const { + return (hour == rhs.hour + ? minute - rhs.minute + : hour - rhs.hour); + } + + string toString() const { + return format("%02s:%02s", hour, minute); + } +} + +struct TimeSpan { + TimeOfDay begin; + TimeOfDay end; // end is outside of the span + + bool opBinaryRight(string op)(TimeOfDay time) const + if (op == "in") { + return (time >= begin) && (time < end); + } +} + +void main() { + auto lunchBreak = TimeSpan(TimeOfDay(12, 00), + TimeOfDay(13, 00)); + + for (auto time = TimeOfDay(11, 30); + time < TimeOfDay(13, 30); + time += Duration(15)) { + + if (time in lunchBreak) { + writeln(time, " is during the lunch break"); + + } else { + writeln(time, " is outside of the lunch break"); + } + } +} +--- + +$(P +The output: +) + +$(SHELL +11:30 is outside of the lunch break +11:45 is outside of the lunch break +12:00 is during the lunch break +12:15 is during the lunch break +12:30 is during the lunch break +12:45 is during the lunch break +13:00 is outside of the lunch break +13:15 is outside of the lunch break +) + +$(PROBLEM_TEK + +$(P +Define a fraction type that stores its numerator and denominator as members of type $(C long). Such a type may be useful because it does not lose value like $(C float), $(C double), and $(C real) do due to their precisions. For example, although the result of multiplying a $(C double) value of 1.0/3 by 3 is $(I not) 1.0, multiplying a $(C Fraction) object that represents the fraction 1/3 by 3 would be exactly 1: +) + +--- +struct Fraction { + long num; // numerator + long den; // denominator + + /* As a convenience, the constructor uses the default + * value of 1 for the denominator. */ + this(long num, long den = 1) { + enforce(den != 0, "The denominator cannot be zero"); + + this.num = num; + this.den = den; + + /* Ensuring that the denominator is always positive + * will simplify the definitions of some of the + * operator functions. */ + if (this.den < 0) { + this.num = -this.num; + this.den = -this.den; + } + } + + /* ... you define the operator overloads ... */ +} +--- + +$(P +Define operators as needed for this type to make it a convenient type as close to fundamental types as possible. Ensure that the definition of the type passes all of the following unit tests. The unit tests ensure the following behaviors: +) + +$(UL + +$(LI An exception must be thrown when constructing an object with zero denominator. (This is already taken care of by the $(C enforce) expression above.)) + +$(LI Producing the negative of the value: For example, the negative of 1/3 should be -1/3 and negative of -2/5 should be 2/5.) + +$(LI Incrementing and decrementing the value by $(C ++) and $(C --).) + +$(LI Support for four arithmetic operations: Both modifying the value of an object by $(C +=), $(C -=), $(C *=), and $(C /=); and producing the result of using two objects with the $(C +), $(C -), $(C *), and $(C /) operators. (Similar to the constructor, dividing by zero should be prevented.) + +$(P +As a reminder, here are the formulas of arithmetic operations that involve two fractions a/b and c/d: +) + +$(UL +$(LI Addition: a/b + c/d = (a*d + c*b)/(b*d)) +$(LI Subtraction: a/b - c/d = (a*d - c*b)/(b*d)) +$(LI Multiplication: a/b * c/d = (a*c)/(b*d)) +$(LI Division: (a/b) / (c/d) = (a*d)/(b*c)) +) + +) + +$(LI The actual (and necessarily lossful) value of the object can be converted to $(C double).) + +$(LI Sort order and equality comparisons are performed by the actual values of the fractions, not by the values of the numerators and denominators. For example, the fractions 1/3 and 20/60 must be considered to be equal. +) + +) + +--- +unittest { + /* Must throw when denominator is zero. */ + assertThrown(Fraction(42, 0)); + + /* Let's start with 1/3. */ + auto a = Fraction(1, 3); + + /* -1/3 */ + assert(-a == Fraction(-1, 3)); + + /* 1/3 + 1 == 4/3 */ + ++a; + assert(a == Fraction(4, 3)); + + /* 4/3 - 1 == 1/3 */ + --a; + assert(a == Fraction(1, 3)); + + /* 1/3 + 2/3 == 3/3 */ + a += Fraction(2, 3); + assert(a == Fraction(1)); + + /* 3/3 - 2/3 == 1/3 */ + a -= Fraction(2, 3); + assert(a == Fraction(1, 3)); + + /* 1/3 * 8 == 8/3 */ + a *= Fraction(8); + assert(a == Fraction(8, 3)); + + /* 8/3 / 16/9 == 3/2 */ + a /= Fraction(16, 9); + assert(a == Fraction(3, 2)); + + /* Must produce the equivalent value in type 'double'. + * + * Note that although double cannot represent every value + * precisely, 1.5 is an exception. That is why this test + * is being applied at this point. */ + assert(to!double(a) == 1.5); + + /* 1.5 + 2.5 == 4 */ + assert(a + Fraction(5, 2) == Fraction(4, 1)); + + /* 1.5 - 0.75 == 0.75 */ + assert(a - Fraction(3, 4) == Fraction(3, 4)); + + /* 1.5 * 10 == 15 */ + assert(a * Fraction(10) == Fraction(15, 1)); + + /* 1.5 / 4 == 3/8 */ + assert(a / Fraction(4) == Fraction(3, 8)); + + /* Must throw when dividing by zero. */ + assertThrown(Fraction(42, 1) / Fraction(0)); + + /* The one with lower numerator is before. */ + assert(Fraction(3, 5) < Fraction(4, 5)); + + /* The one with larger denominator is before. */ + assert(Fraction(3, 9) < Fraction(3, 8)); + assert(Fraction(1, 1_000) > Fraction(1, 10_000)); + + /* The one with lower value is before. */ + assert(Fraction(10, 100) < Fraction(1, 2)); + + /* The one with negative value is before. */ + assert(Fraction(-1, 2) < Fraction(0)); + assert(Fraction(1, -2) < Fraction(0)); + + /* The ones with equal values must be both <= and >=. */ + assert(Fraction(-1, -2) <= Fraction(1, 2)); + assert(Fraction(1, 2) <= Fraction(-1, -2)); + assert(Fraction(3, 7) <= Fraction(9, 21)); + assert(Fraction(3, 7) >= Fraction(9, 21)); + + /* The ones with equal values must be equal. */ + assert(Fraction(1, 3) == Fraction(20, 60)); + + /* The ones with equal values with sign must be equal. */ + assert(Fraction(-1, 2) == Fraction(1, -2)); + assert(Fraction(1, 2) == Fraction(-1, -2)); +} +--- + +) + +Macros: + SUBTITLE=Operator Overloading + + DESCRIPTION=The operator overloading feature of the D programming language that makes user-defined types as convenient as fundamental types. + + KEYWORDS=d programming lesson book tutorial operator overloading diff --git a/target/operator_precedence.d b/target/operator_precedence.d new file mode 100644 index 0000000..db4d51e --- /dev/null +++ b/target/operator_precedence.d @@ -0,0 +1,323 @@ +Ddoc + +$(DERS_BOLUMU $(IX operator precedence) $(IX precedence, operator) Operator Precedence) + +$(P +As we have been using throughout the book, expressions can be $(I chained) with more than one operator. For example, the following line contains four expressions chained with three operators: +) + +--- + a = b + c * d // three operators: =, +, and * +--- + +$(P +Operator precedence rules specify the order that the chained operators are executed in and the expressions that they use. Operators are executed according to their precedences: first the higher ones, then the lower ones. +) + +$(P +The following is D's operator precedence table. Operators are listed from the highest precedence to the lowest. The ones that are in the same table row have the same precedence. (Line wrapping inside table cells is insignificant; for example, $(C ==) and $(C !is) have the same precedence.) Unless specified otherwise, operators are $(I left-associative). +) + +$(P +Some of the terms used in the table are explained later below. +) + + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorsDescriptionNotes
$(C !) Template instantiationCannot be chained +
$(C =>) Lambda definition Not a real operator; occurs twice in the table; this row is for binding power to the $(I left) +
$(C . ++ -- $(PARANTEZ_AC) [) Postfix operators $(C $(PARANTEZ_AC)) and $(C [) must be balanced with $(C $(PARANTEZ_KAPA)) and $(C ]), respectively +
$(C ^^) Power operator Right-associative +
$(C ++ -- * + - ! & ~ cast) Unary operators +
$(C * / %) Binary operators +
$(C + - ~) Binary operators
$(C << >> >>>) Bit shift operators +
$(C == != > < >= <= in !in is !is) Comparison operators Unordered with respect to bitwise operators, cannot be chained +
$(C &) Bitwise $(I and) Unordered with respect to comparison operators +
$(C ^) Bitwise $(I xor) Unordered with respect to comparison operators +
$(C |) Bitwise $(I or) Unordered with respect to comparison operators +
$(C &&) Logical $(I and)Short-circuit
$(C ||)Logical $(I or)Short-circuit
$(C ?:)Ternary operatorRight-associative +
$(C = -= += = *= %= ^= ^^= ~= <<= >>= >>>=) + Assignment operators Right-associative +
$(C =>) Lambda definition Not a real operator; occurs twice in the table; this row is for binding power to the $(I right)
$(C ,) Comma operatorNot to be confused with using comma as a separator (e.g. in parameter lists)
$(C ..) Number range Not a real operator; hardwired into syntax at specific points +
+ + +$(H6 $(IX operator chaining) $(IX chaining, operator) Chaining) + +$(P +Let's consider the line from the beginning of the chapter: +) + +--- + a = b + c * d +--- + +$(P +Because binary $(C *) has higher precedence than binary $(C +), and binary $(C +) has higher precedence than $(C =), that expression is executed as the following parenthesized equivalent: +) + +--- + a = (b + (c * d)) // first *, then +, then = +--- + +$(P +As another example, because postfix $(C .) has higher precedence than unary $(C *), the following expression would first access member $(C ptr) of object $(C o) and then dereference it: +) + +--- + *o.ptr $(CODE_NOTE dereferences pointer member o.ptr) + *(o.ptr) $(CODE_NOTE equivalent of the above) + (*o).ptr $(CODE_NOTE_WRONG NOT equivalent of the above) +--- + +$(P +Some operators cannot be chained: +) + +--- + if (a > b == c) { $(DERLEME_HATASI) + // ... + } +--- + +$(SHELL +Error: found '==' when expecting '$(PARANTEZ_KAPA)' +) + +$(P +The programmer must specify the desired execution order with parentheses: +) + +--- + if ((a > b) == c) { $(CODE_NOTE compiles) + // ... + } +--- + +$(H6 $(IX left-associative) $(IX right-associative) $(IX associativity, operator) $(IX operator associativity) Associativity) + +$(P +If two operators have the same precedence, then their associativity determines which operator is executed first: the one on the left or the one on the right. +) + +$(P +Most operators are $(I left-associative); the one on the left-hand side is executed first: +) + +--- + 10 - 7 - 3; + (10 - 7) - 3; $(CODE_NOTE equivalent of the above (== 0)) + 10 - (7 - 3); $(CODE_NOTE_WRONG NOT equivalent of the above (== 6)) +--- + +$(P +Some operators are right-associative; the one on the right-hand side is executed first: +) + +--- + 4 ^^ 3 ^^ 2; + 4 ^^ (3 ^^ 2); $(CODE_NOTE equivalent of the above (== 262144)) + (4 ^^ 3) ^^ 2; $(CODE_NOTE_WRONG NOT equivalent of the above (== 4096)) +--- + +$(H6 Unordered operator groups) + +$(P +Precedence between bitwise operators and logical operators are not specified by the language: +) + +--- + if (a & b == c) { $(DERLEME_HATASI) + // ... + } +--- + +$(SHELL +Error: b == c must be parenthesized when next to operator & +) + +$(P +The programmer must specify the desired execution order with parentheses: +) + +--- + if ((a & b) == c) { $(CODE_NOTE compiles) + // ... + } +--- + +$(H6 $(IX =>, operator precedence) The precedence of $(C =>)) + +$(P +Although $(C =>) is not an operator, it takes part in the table twice to specify how it interacts with operators on its left-hand side and right-hand side. +) + +--- + l = a => a = 1; +--- + +$(P +Although both sides of $(C =>) above have an $(C =) operator, $(C =>) has precedence over $(C =) on the left hand side so it $(I binds stronger) to $(C a) as if the programmer wrote the following parentheses: +) + +--- + l = (a => a = 1); +--- + +$(P +On the right-hand side, $(C =>) has lower precedence than $(C =), so the $(C a) on the right-hand side binds stronger to $(C =) as if the following extra set of parentheses are specified: +) + +--- + l = (a => $(HILITE $(PARANTEZ_AC))a = 1$(HILITE $(PARANTEZ_KAPA))); +--- + +$(P +As a result, the lambda expression does not become just $(C a => a) but includes the rest of the expression as well: $(C a => a = 1), which means $(I given a, produce a = 1). That lambda is then assigned to the variable $(C l). +) + +$(P +$(I $(B Note:) This is just an example; otherwise, $(C a = 1) is not a meaningful body for a lambda because the mutation to its parameter $(C a) is seemingly lost and the result of calling the lambda is always 1. (The reason I say "seemingly" is that the assignment operator may have been overloaded for $(C a)'s type and may have side-effects.)) +) + +$(H6 $(IX , (comma), operator) $(IX comma operator) Comma operator) + +$(P +Comma operator is a binary operator. It executes first the left-hand side expression then the right-hand side expression. The values of both expressions are ignored. +) + +--- + int a = 1; + foo()$(HILITE ,) bar()$(HILITE ,) ++a; + + assert(a == 2); +--- + +$(P +The comma operator is most commonly used with $(C for) loops when the loop iteration involves mutating more than one variable: +) + +--- + for ({ int i; int j; } i < 10; ++i$(HILITE ,) ++j) { + // ... + } +--- + +Macros: + SUBTITLE=Operator Precedence + + DESCRIPTION=The precedences and associativity of D operators. + + KEYWORDS=d programming lesson book tutorial operator precedence associativity diff --git a/target/parallelism.d b/target/parallelism.d new file mode 100644 index 0000000..d82234d --- /dev/null +++ b/target/parallelism.d @@ -0,0 +1,1217 @@ +Ddoc + +$(DERS_BOLUMU $(IX parallelism) Parallelism) + +$(P +$(IX core) Most modern microprocessors consist of more than one $(I core), each of which can operate as an individual processing unit. They can execute different parts of different programs at the same time. The features of the $(C std.parallelism) module make it possible for programs to take advantage of all of the cores in order to run faster. +) + +$(P +This chapter covers the following range algorithms. These algorithms should be used only when the operations that are to be executed $(I in parallel) are truly independent from each other. $(I In parallel) means that operations are executed on multiple cores at the same time: +) + +$(UL + +$(LI $(C parallel): Accesses the elements of a range in parallel.) + +$(LI $(C task): Creates tasks that are executed in parallel.) + +$(LI $(C asyncBuf): Iterates the elements of an $(C InputRange) semi-eagerly in parallel.) + +$(LI $(C map): Calls functions with the elements of an $(C InputRange) semi-eagerly in parallel.) + +$(LI $(C amap): Calls functions with the elements of a $(C RandomAccessRange) fully-eagerly in parallel.) + +$(LI $(C reduce): Makes calculations over the elements of a $(C RandomAccessRange) in parallel.) + +) + +$(P +In the programs that we have written so far we have been assuming that the expressions of a program are executed in a certain order, at least in general line-by-line: +) + +--- + ++i; + ++j; +--- + +$(P +In the code above, we expect that the value of $(C i) is incremented before the value of $(C j) is incremented. Although that is semantically correct, it is rarely the case in reality: microprocessors and compilers use optimization techniques to have some variables reside in microprocessor's registers that are independent from each other. When that is the case, the microprocessor would execute operations like the increments above in parallel. +) + +$(P +Although these optimizations are effective, they cannot be applied automatically to layers higher than the very low-level operations. Only the programmer can determine that certain high-level operations are independent and that they can be executed in parallel. +) + +$(P +In a loop, the elements of a range are normally processed one after the other, operations of each element following the operations of previous elements: +) + +--- + auto students = + [ Student(1), Student(2), Student(3), Student(4) ]; + + foreach (student; students) { + student.aSlowOperation(); + } +--- + +$(P +Normally, a program would be executed on one of the cores of the microprocessor, which has been assigned by the operating system to execute the program. As the $(C foreach) loop normally operates on elements one after the other, $(C aSlowOperation()) would be called for each student sequentially. However, in many cases it is not necessary for the operations of preceding students to be completed before starting the operations of successive students. If the operations on the $(C Student) objects were truly independent, it would be wasteful to ignore the other microprocessor cores, which might potentially be waiting idle on the system. +) + +$(P +$(IX Thread.sleep) To simulate long-lasting operations, the following examples call $(C Thread.sleep()) from the $(C core.thread) module. $(C Thread.sleep()) suspends the operations for the specified amount of time. $(C Thread.sleep) is admittedly an artifical method to use in the following examples because it takes time without ever busying any core. Despite being an unrealistic tool, it is still useful in this chapter to demonstrate the power of parallelism. +) + +--- +import std.stdio; +import core.thread; + +struct Student { + int number; + + void aSlowOperation() { + writefln("The work on student %s has begun", number); + + // Wait for a while to simulate a long-lasting operation + Thread.sleep(1.seconds); + + writefln("The work on student %s has ended", number); + } +} + +void main() { + auto students = + [ Student(1), Student(2), Student(3), Student(4) ]; + + foreach (student; students) { + student.aSlowOperation(); + } +} +--- + +$(P +The execution time of the program can be measured in a terminal by $(C time): +) + +$(SHELL +$ $(HILITE time) ./deneme +$(SHELL_OBSERVED +The work on student 1 has begun +The work on student 1 has ended +The work on student 2 has begun +The work on student 2 has ended +The work on student 3 has begun +The work on student 3 has ended +The work on student 4 has begun +The work on student 4 has ended + +real 0m4.005s $(SHELL_NOTE 4 seconds total) +user 0m0.004s +sys 0m0.000s +) +) + +$(P +Since the students are iterated over in sequence and since the work of each student takes 1 second, the total execution time comes out to be 4 seconds. However, if these operations were executed in an environment that had 4 cores, they could be operated on at the same time and the total time would be reduced to about 1 second. +) + +$(P +$(IX totalCPUs) Before seeing how this is done, let's first determine the number of cores that are available on the system by $(C std.parallelism.totalCPUs): +) + +--- +import std.stdio; +import std.parallelism; + +void main() { + writefln("There are %s cores on this system.", totalCPUs); +} +--- + +$(P +The output of the program in the environment that this chapter has been written is the following: +) + +$(SHELL +There are 4 cores on this system. +) + +$(H5 $(IX parallel) $(C taskPool.parallel())) + +$(P +This function can also be called simply as $(C parallel()). +) + +$(P +$(IX foreach, parallel) $(C parallel()) accesses the elements of a range in parallel. An effective usage is with $(C foreach) loops. Merely importing the $(C std.parallelism) module and replacing $(C students) with $(C parallel(students)) in the program above is sufficient to take advantage of all of the cores of the system: +) + +--- +import std.parallelism; +// ... + foreach (student; $(HILITE parallel(students))) { +--- + +$(P +We have seen earlier in the $(LINK2 /ders/d.en/foreach_opapply.html, $(C foreach) for structs and classes chapter) that the expressions that are in $(C foreach) blocks are passed to $(C opApply()) member functions as delegates. $(C parallel()) returns a range object that knows how to distribute the execution of the $(C delegate) to a separate core for each element. +) + +$(P +As a result, passing the $(C Student) range through $(C parallel()) makes the program above finish in 1 second on a system that has 4 cores: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED The work on student 2 has begun +The work on student 1 has begun +The work on student 4 has begun +The work on student 3 has begun +The work on student 1 has ended +The work on student 2 has ended +The work on student 4 has ended +The work on student 3 has ended + +real 0m1.005s $(SHELL_NOTE now only 1 second) +user 0m0.004s +sys 0m0.004s) +) + +$(P +$(I $(B Note:) The execution time of the program may be different on other systems but it is expected to be roughly "4 seconds divided by the number of cores".) +) + +$(P +$(IX thread) A flow of execution through certain parts of a program is called a a $(I thread of execution) or a $(I thread). Programs can consist of multiple threads that are being actively executed at the same time. The operating system starts and executes each thread on a core and then suspends it to execute other threads. The execution of each thread may involve many cycles of starting and suspending. +) + +$(P +All of the threads of all of the programs that are active at a given time are executed on the very cores of the microprocessor. The operating system decides when and under what condition to start and suspend each thread. That is the reason why the messages that are printed by $(C aSlowOperation()) are in mixed order in the output above. This undeterministic order of thread execution may not matter if the operations of the $(C Student) objects are truly independent from each other. +) + +$(P +It is the responsibility of the programmer to call $(C parallel()) only when the operations applied to each element are independent for each iteration. For example, if it were important that the messages appear in a certain order in the output, calling $(C parallel()) should be considered an error in the program above. The programming model that supports threads that depend on other threads is called $(I concurrency). Concurrency is the topic of the next chapter. +) + +$(P +By the time parallel $(C foreach) ends, all of the operations inside the loop have been completed for all of the elements. The program can safely continue after the $(C foreach) loop. +) + +$(H6 $(IX work unit size) Work unit size) + +$(P +The second parameter of $(C parallel()) has an overloaded meaning and is ignored in some cases: +) + +--- + /* ... */ = parallel($(I range), $(I work_unit_size) = 100); +--- + +$(UL + +$(LI When iterating over $(C RandomAccessRange) ranges: + +$(P +The distribution of threads to cores has some minimal cost. This cost may sometimes be significant especially when the operations of the loop are completed in a very short time. In such cases, it may be faster to have each thread execute more than one iteration of the loop. The work unit size determines the number of elements that each thread should execute at each of its iterations: +) + +--- + foreach (student; parallel(students, $(HILITE 2))) { + // ... + } +--- + +$(P +The default value of work unit size is 100 and is suitable for most cases. +) + +) + +$(LI When iterating over non-$(C RandomAccessRange) ranges: + +$(P +$(C parallel()) does not start parallel executions until $(I work unit size) number of elements of a non-$(C RandomAccessRange) have been executed serially first. Due to the relatively high value of 100, $(C parallel()) may give the wrong impression that it is not effective when tried on short non-$(C RandomAccessRange) ranges. +) + +) + +$(LI When iterating over the result ranges of $(C asyncBuf()) or parallel $(C map()) (both are explained later in this chapter): + +$(P +When $(C parallel()) works on the results of $(C asyncBuf()) or $(C map()), it ignores the work unit size parameter. Instead, $(C parallel()) reuses the internal buffer of the result range. +) + +) + +) + +$(H5 $(IX Task) $(C Task)) + +$(P +Operations that are executed in parallel with other operations of a program are called $(I tasks). Tasks are represented by the type $(C std.parallelism.Task). +) + +$(P +In fact, $(C parallel()) constructs a new $(C Task) object for every worker thread and starts that task automatically. $(C parallel()) then waits for all of the tasks to be completed before finally exiting the loop. $(C parallel()) is very convenient as it $(I constructs), $(I starts), and $(I waits for) the tasks automatically. +) + +$(P +$(IX task) $(IX executeInNewThread) $(IX yieldForce) When tasks do not correspond to or cannot be represented by elements of a range, these three steps can be handled explicitly by the programmer. $(C task()) constructs, $(C executeInNewThread()) starts, and $(C yieldForce()) waits for a task object. These three functions are explained further in the comments of the following program. +) + +$(P +The $(C anOperation()) function is started twice in the following program. It prints the first letter of $(C id) to indicate which task it is working for. +) + +$(P +$(IX flush, std.stdio) $(I $(B Note:) Normally, the characters that are printed to output streams like $(C stdout) do not appear on the output right away. They are instead stored in an output buffer until a line of output is completed. Since $(C write) does not output a new-line character, in order to observe the parallel execution of the following program, $(C stdout.flush()) is called to send the contents of the buffer to $(C stdout) even before reaching the end of a line.) +) + +--- +import std.stdio; +import std.parallelism; +import std.array; +import core.thread; + +/* Prints the first letter of 'id' every half a second. It + * arbitrarily returns the value 1 to simulate functions that + * do calculations. This result will be used later in main. */ +int anOperation(string id, int duration) { + writefln("%s will take %s seconds", id, duration); + + foreach (i; 0 .. (duration * 2)) { + Thread.sleep(500.msecs); /* half a second */ + write(id.front); + stdout.flush(); + } + + return 1; +} + +void main() { + /* Construct a task object that will execute + * anOperation(). The function parameters that are + * specified here are passed to the task function as its + * function parameters. */ + auto theTask = $(HILITE task!anOperation)("theTask", 5); + + /* Start the task object */ + theTask.$(HILITE executeInNewThread()); + + /* As 'theTask' continues executing, 'anOperation()' is + * being called again, this time directly in main. */ + immutable result = anOperation("main's call", 3); + + /* At this point we are sure that the operation that has + * been started directly from within main has been + * completed, because it has been started by a regular + * function call, not as a task. */ + + /* On the other hand, it is not certain at this point + * whether 'theTask' has completed its operations + * yet. yieldForce() waits for the task to complete its + * operations; it returns only when the task has been + * completed. Its return value is the return value of + * the task function, i.e. anOperation(). */ + immutable taskResult = theTask.$(HILITE yieldForce()); + + writeln(); + writefln("All finished; the result is %s.", + result + taskResult); +} +--- + +$(P +The output of the program should be similar to the following. The fact that the $(C m) and $(C t) letters are printed in mixed order indicates that the operations are executed in parallel: +) + +$(SHELL +main's call will take 3 seconds +theTask will take 5 seconds +mtmttmmttmmttttt +All finished; the result is 2. +) + +$(P +The task function above has been specified as a template parameter to $(C task()) as $(C task!anOperation). Although this method works well in most cases, as we have seen in $(LINK2 /ders/d.en/templates.html, the Templates chapter), each different instantiation of a template is a different type. This distinction may be undesirable in certain situations where seemingly $(I equivalent) task objects would actually have different types. +) + +$(P +For example, although the following two functions have the same signature, the two $(C Task) instantiations that are produced through calls to the $(C task()) function template would have different types. As a result, they cannot be members of the same array: +) + +--- +import std.parallelism; + +double foo(int i) { + return i * 1.5; +} + +double bar(int i) { + return i * 2.5; +} + +void main() { + auto tasks = [ task$(HILITE !)foo(1), + task$(HILITE !)bar(2) ]; $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: $(HILITE incompatible types) for ((task(1)) : (task(2))): +'Task!($(HILITE foo), int)*' and 'Task!($(HILITE bar), int)*' +) + +$(P +Another overload of $(C task()) takes the function as its first function parameter instead: +) + +--- + void someFunction(int value) { + // ... + } + + auto theTask = task($(HILITE &someFunction), 42); +--- + +$(P +As this method does not involve different instantiations of the $(C Task) template, it makes it possible to put such objects in the same array: +) + +--- +import std.parallelism; + +double foo(int i) { + return i * 1.5; +} + +double bar(int i) { + return i * 2.5; +} + +void main() { + auto tasks = [ task($(HILITE &)foo, 1), + task($(HILITE &)bar, 2) ]; $(CODE_NOTE compiles) +} +--- + +$(P +A lambda function or an object of a type that defines the $(C opCall) member can also be used as the task function. The following example starts a task that executes a lambda: +) + +--- + auto theTask = task((int value) $(HILITE {) + /* ... */ + $(HILITE }), 42); +--- + +$(H6 $(IX exception, parallelism) Exceptions) + +$(P +As tasks are executed on separate threads, the exceptions that they throw cannot be caught by the thread that started them. For that reason, the exceptions that are thrown are automatically caught by the tasks themselves, to be rethrown later when $(C Task) member functions like $(C yieldForce()) are called. This makes it possible for the main thread to catch exceptions that are thrown by a task. +) + +--- +import std.stdio; +import std.parallelism; +import core.thread; + +void mayThrow() { + writeln("mayThrow() is started"); + Thread.sleep(1.seconds); + writeln("mayThrow() is throwing an exception"); + throw new Exception("Error message"); +} + +void main() { + auto theTask = task!mayThrow(); + theTask.executeInNewThread(); + + writeln("main is continuing"); + Thread.sleep(3.seconds); + + writeln("main is waiting for the task"); + theTask.yieldForce(); +} +--- + +$(P +The output of the program shows that the uncaught exception that has been thrown by the task does not terminate the entire program right away (it terminates only the task): +) + +$(SHELL +main is continuing +mayThrow() is started +mayThrow() is throwing an exception $(SHELL_NOTE thrown) +main is waiting for the task +object.Exception@deneme.d(10): Error message $(SHELL_NOTE terminated) +) + +$(P +$(C yieldForce()) can be called in a $(C try-catch) block to catch the exceptions that are thrown by the task. Note that this is different from single threads: In single-threaded programs like the samples that we have been writing until this chapter, $(C try-catch) wraps the code that may throw. In parallelism, it wraps $(C yieldForce()): +) + +--- + try { + theTask.yieldForce(); + + } catch (Exception exc) { + writefln("Detected an error in the task: '%s'", exc.msg); + } +--- + +$(P +This time the exception is caught by the main thread instead of terminating the program: +) + +$(SHELL +main is continuing +mayThrow() is started +mayThrow() is throwing an exception $(SHELL_NOTE thrown) +main is waiting for the task +Detected an error in the task: 'Error message' $(SHELL_NOTE caught) +) + +$(H6 Member functions of $(C Task)) + +$(UL + +$(LI $(C done): Specifies whether the task has been completed; rethrows the exception if the task has been terminated with an exception. + +--- + if (theTask.done) { + writeln("Yes, the task has been completed"); + + } else { + writeln("No, the task is still going on"); + } +--- + +) + +$(LI $(C executeInNewThread()): Starts the task in a new thread.) + +$(LI $(C executeInNewThread(int priority)): Starts the task in a new thread with the specified priority. (Priority is an operating system concept that determines execution priorities of threads.)) + +) + +$(P +There are three functions to wait for the completion of a task: +) + +$(UL + +$(LI $(C yieldForce()): Starts the task if it has not been started yet; if it has already been completed, returns its return value; if it is still running, waits for its completion without making the microprocessor busy; if an exception has been thrown, rethrows that exception.) + +$(LI $(IX spinForce) $(C spinForce()): Works similarly to $(C yieldForce()), except that it makes the microprocessor busy while waiting, in order to catch the completion as early as possible.) + +$(LI $(IX workForce) $(C workForce()): Works similarly to $(C yieldForce()), except that it starts a new task in the current thread while waiting for the task to be completed.) + +) + +$(P +In most cases $(C yieldForce()) is the most suitable function to call when waiting for a task to complete; it suspends the thread that calls $(C yieldForce()) until the task is completed. Although $(C spinForce()) makes the microprocessor busy while waiting, it is suitable when the task is expected to be completed in a very short time. $(C workForce()) can be called when starting other tasks is preferred over suspending the current thread. +) + +$(P +Please see the online documentation of Phobos for the other member functions of $(C Task). +) + +$(H5 $(IX asyncBuf) $(C taskPool.asyncBuf())) + +$(P +Similarly to $(C parallel()), $(C asyncBuf()) iterates $(C InputRange) ranges in parallel. It stores the elements in a buffer as they are produced by the range, and serves the elements from that buffer to its user. +) + +$(P +In order to avoid making a potentially fully-lazy input range a fully-eager range, it iterates the elements in $(I waves). Once it prepares certain number of elements in parallel, it waits until those elements are consumed by $(C popFront()) before producing the elements of the next wave. +) + +$(P +$(C asyncBuf()) takes a range and an optional $(I buffer size) that determines how many elements to be made available during each wave: +) + +--- + auto elements = taskPool.asyncBuf($(I range), $(I buffer_size)); +--- + +$(P +To see the effects of $(C asyncBuf()), let's use a range that takes half a second to iterate and half a second to process each element. This range simply produces integers up to the specified limit: +) + +--- +import std.stdio; +import core.thread; + +struct Range { + int limit; + int i; + + bool empty() const @property { + return i >= limit; + } + + int front() const @property { + return i; + } + + void popFront() { + writefln("Producing the element after %s", i); + Thread.sleep(500.msecs); + ++i; + } +} + +void main() { + auto range = Range(10); + + foreach (element; range) { + writefln("Using element %s", element); + Thread.sleep(500.msecs); + } +} +--- + +$(P +The elements are produced and used lazily. Since it takes one second for each element, the whole range takes ten seconds to process in this program: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Using element 0 +Producing the element after 0 +Using element 1 +Producing the element after 1 +Using element 2 +... +Producing the element after 8 +Using element 9 +Producing the element after 9 + +real 0m10.007s $(SHELL_NOTE 10 seconds total) +user 0m0.004s +sys 0m0.000s) +) + +$(P +According to that output, the elements are produced and used sequentially. +) + +$(P +On the other hand, it may not be necessary to wait for preceding elements to be processed before starting to produce the successive elements. The program would take less time if other elements could be produced while the front element is in use: +) + +--- +import std.parallelism; +//... + foreach (element; $(HILITE taskPool.asyncBuf)(range, $(HILITE 2))) { +--- + +$(P +In the call above, $(C asyncBuf()) makes two elements ready in its buffer. Elements are produced in parallel while they are being used: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Producing the element after 0 +Producing the element after 1 +Using element 0 +Producing the element after 2 +Using element 1 +Producing the element after 3 +Using element 2 +Producing the element after 4 +Using element 3 +Producing the element after 5 +Using element 4 +Producing the element after 6 +Producing the element after 7 +Using element 5 +Using element 6 +Producing the element after 8 +Producing the element after 9 +Using element 7 +Using element 8 +Using element 9 + +real 0m6.007s $(SHELL_NOTE now 6 seconds) +user 0m0.000s +sys 0m0.004s) +) + +$(P +The default value of buffer size is 100. The buffer size that produces the best performance would be different under different situations. +) + +$(P +$(C asyncBuf()) can be used outside of $(C foreach) loops as well. For example, the following code uses the return value of $(C asyncBuf()) as an $(C InputRange) which operates semi-eagerly: +) + +--- + auto range = Range(10); + auto asyncRange = taskPool.asyncBuf(range, 2); + writeln($(HILITE asyncRange.front)); +--- + +$(H5 $(IX map, parallel) $(C taskPool.map())) + +$(P +$(IX map, std.algorithm) It helps to explain $(C map()) from the $(C std.algorithm) module before explaining $(C taskPool.map()). $(C std.algorithm.map) is an algorithm commonly found in many functional languages. It calls a function with the elements of a range one-by-one and returns a range that consists of the results of calling that function with each element. It is a lazy algorithm: It calls the function as needed. (There is also $(C std.algorithm.each), which is for generating side effects for each element, as opposed to producing a result from it.) +) + +$(P +The fact that $(C std.algorithm.map) operates lazily is very powerful in many programs. However, if the function needs to be called with every element anyway and the operations on each element are independent from each other, laziness may be unnecessarily slower than parallel execution. $(C taskPool.map()) and $(C taskPool.amap()) from the $(C std.parallelism) module take advantage of multiple cores and run faster in many cases. +) + +$(P +Let's compare these three algorithms using the $(C Student) example. Let's assume that $(C Student) has a member function that returns the average grade of the student. To demonstrate how parallel algorithms are faster, let's again slow this function down with $(C Thread.sleep()). +) + +$(P +$(C std.algorithm.map) takes the function as its template parameter, and the range as its function parameter. It returns a range that consists of the results of applying that function to the elements of the range: +) + +--- + auto $(I result_range) = map!$(I func)($(I range)); +--- + +$(P +The function may be specified by the $(C =>) syntax as a $(I lambda expression) as we have seen in earlier chapters. The following program uses $(C map()) to call the $(C averageGrade()) member function on each element: +) + +--- +import std.stdio; +import std.algorithm; +import core.thread; + +struct Student { + int number; + int[] grades; + + double averageGrade() @property { + writefln("Started working on student %s", + number); + Thread.sleep(1.seconds); + + const average = grades.sum / grades.length; + + writefln("Finished working on student %s", number); + return average; + } +} + +void main() { + Student[] students; + + foreach (i; 0 .. 10) { + /* Two grades for each student */ + students ~= Student(i, [80 + i, 90 + i]); + } + + auto results = $(HILITE map)!(a => a.averageGrade)(students); + + foreach (result; results) { + writeln(result); + } +} +--- + +$(P +The output of the program demonstrates that $(C map()) operates lazily; $(C averageGrade()) is called for each result as the $(C foreach) loop iterates: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 0 +Finished working on student 0 +85 $(SHELL_NOTE calculated as foreach iterates) +Started working on student 1 +Finished working on student 1 +86 +... +Started working on student 9 +Finished working on student 9 +94 + +real 0m10.006s $(SHELL_NOTE 10 seconds total) +user 0m0.000s +sys 0m0.004s) +) + +$(P +If $(C std.algorithm.map) were an eager algorithm, the messages about the starts and finishes of the operations would be printed altogether at the top. +) + +$(P +$(C taskPool.map()) from the $(C std.parallelism) module works essentially the same as $(C std.algorithm.map). The only difference is that it executes the function calls semi-eagerly and stores the results in a buffer to be served from as needed. The size of this buffer is determined by the second parameter. For example, the following code would make ready the results of the function calls for three elements at a time: +) + +--- +import std.parallelism; +// ... +double averageGrade(Student student) { + return student.averageGrade; +} +// ... + auto results = $(HILITE taskPool.map)!averageGrade(students, $(HILITE 3)); +--- + +$(P +$(I $(B Note:) The free-standing $(C averageGrade()) function above is needed due to a limitation that involves using local delegates with member function templates like $(C TaskPool.map). There would be a compilation error without that free-standing function: +)) + +--- +auto results = + taskPool.map!(a => a.averageGrade)(students, 3); $(DERLEME_HATASI) +--- + +$(P +This time the operations are executed in waves of three elements: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 1 $(SHELL_NOTE in parallel) +Started working on student 2 $(SHELL_NOTE but in unpredictable order) +Started working on student 0 +Finished working on student 1 +Finished working on student 2 +Finished working on student 0 +85 +86 +87 +Started working on student 4 +Started working on student 5 +Started working on student 3 +Finished working on student 4 +Finished working on student 3 +Finished working on student 5 +88 +89 +90 +Started working on student 7 +Started working on student 8 +Started working on student 6 +Finished working on student 7 +Finished working on student 6 +Finished working on student 8 +91 +92 +93 +Started working on student 9 +Finished working on student 9 +94 + +real 0m4.007s $(SHELL_NOTE 4 seconds total) +user 0m0.000s +sys 0m0.004s) +) + +$(P +The second parameter of $(C map()) has the same meaning as $(C asyncBuf()): It determines the size of the buffer that $(C map()) uses to store the results in. The third parameter is the work unit size as in $(C parallel()); the difference being its default value, which is $(C size_t.max): +) + +--- + /* ... */ = taskPool.map!$(I func)($(I range), + $(I buffer_size) = 100 + $(I work_unit_size) = size_t.max); +--- + +$(H5 $(IX amap) $(C taskPool.amap())) + +$(P +Parallel $(C amap()) works the same as parallel $(C map()) with two differences: +) + +$(UL + +$(LI +It is fully eager. +) + +$(LI +It works with $(C RandomAccessRange) ranges. +) + +) + +--- + auto results = $(HILITE taskPool.amap)!averageGrade(students); +--- + +$(P +Since it is eager, all of the results are ready by the time $(C amap()) returns: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 1 $(SHELL_NOTE all are executed up front) +Started working on student 0 +Started working on student 2 +Started working on student 3 +Finished working on student 1 +Started working on student 4 +Finished working on student 2 +Finished working on student 3 +Started working on student 6 +Finished working on student 0 +Started working on student 7 +Started working on student 5 +Finished working on student 4 +Started working on student 8 +Finished working on student 6 +Started working on student 9 +Finished working on student 7 +Finished working on student 5 +Finished working on student 8 +Finished working on student 9 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 + +real 0m3.005s $(SHELL_NOTE 3 seconds total) +user 0m0.000s +sys 0m0.004s) +) + +$(P +$(C amap()) works faster than $(C map()) at the expense of using an array that is large enough to store all of the results. It consumes more memory to gain speed. +) + +$(P +The optional second parameter of $(C amap()) is the work unit size as well: +) + +--- + auto results = taskPool.amap!averageGrade(students, $(HILITE 2)); +--- + +$(P +The results can also be stored in a $(C RandomAccessRange) that is passed to $(C amap()) as its third parameter: +) + +--- + double[] results; + results.length = students.length; + taskPool.amap!averageGrade(students, 2, $(HILITE results)); +--- + +$(H5 $(IX reduce, parallel) $(C taskPool.reduce())) + +$(P +$(IX reduce, std.algorithm) As with $(C map()), it helps to explain $(C reduce()) from the $(C std.algorithm) module first. +) + +$(P +$(IX fold, std.algorithm) $(C reduce()) is the equivalent of $(C std.algorithm.fold), which we have seen before in the $(LINK2 /ders/d.en/ranges.html, Ranges chapter). The main difference between the two is that their function parameters are reversed. (For that reason, I recommend that you prefer $(C fold()) for non-parallel code as it can take advantage of $(LINK2 /ders/d.en/ufcs.html, UFCS) in chained range expressions.) +) + +$(P +$(C reduce()) is another high-level algorithm commonly found in many functional languages. Just like $(C map()), it takes one or more functions as template parameters. As its function parameters, it takes a value to be used as the initial value of the result, and a range. $(C reduce()) calls the functions with the current value of the result and each element of the range. When no initial value is specified, the first element of the range is used instead. +) + +$(P +Assuming that it defines a variable named $(C result) in its implementation, the way that $(C reduce()) works can be described by the following steps: +) + +$(OL + +$(LI Assigns the initial value to $(C result)) + +$(LI Executes the expression $(C result = func(result, element)) for every element) + +$(LI Returns the final value of $(C result)) + +) + +$(P +For example, the sum of the squares of the elements of an array can be calculated as in the following program: +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + writeln(reduce!((a, b) => a + b * b)(0, [5, 10])); +} +--- + +$(P +When the function is specified by the $(C =>) syntax as in the program above, the first parameter (here $(C a)) represents the current value of the result (initialized by the parameter $(C 0) above) and the second parameter (here $(C b)) represents the current element. +) + +$(P +The program outputs the sum of 25 and 100, the squares of 5 and 10: +) + +$(SHELL +125 +) + +$(P +As obvious from its behavior, $(C reduce()) uses a loop in its implementation. Because that loop is normally executed on a single core, it may be unnecessarily slow when the function calls for each element are independent from each other. In such cases $(C taskPool.reduce()) from the $(C std.parallelism) module can be used for taking advantage of all of the cores. +) + +$(P +To see an example of this let's use $(C reduce()) with a function that is slowed down again artificially: +) + +--- +import std.stdio; +import std.algorithm; +import core.thread; + +int aCalculation(int result, int element) { + writefln("started - element: %s, result: %s", + element, result); + + Thread.sleep(1.seconds); + result += element; + + writefln("finished - element: %s, result: %s", + element, result); + + return result; +} + +void main() { + writeln("Result: ", $(HILITE reduce)!aCalculation(0, [1, 2, 3, 4])); +} +--- + +$(P +$(C reduce()) uses the elements in sequence to reach the final value of the result: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +started - element: 1, result: 0 +finished - element: 1, result: 1 +started - element: 2, result: 1 +finished - element: 2, result: 3 +started - element: 3, result: 3 +finished - element: 3, result: 6 +started - element: 4, result: 6 +finished - element: 4, result: 10 +Result: 10 + +real 0m4.003s $(SHELL_NOTE 4 seconds total) +user 0m0.000s +sys 0m0.000s) +) + +$(P +As in the $(C parallel()) and $(C map()) examples, importing the $(C std.parallelism) module and calling $(C taskPool.reduce()) is sufficient to take advantage of all of the cores: +) + +--- +import std.parallelism; +// ... + writeln("Result: ", $(HILITE taskPool.reduce)!aCalculation(0, [1, 2, 3, 4])); +--- + +$(P +However, there are important differences in the way $(C taskPool.reduce()) works. +) + +$(P +Like the other parallel algorithms, $(C taskPool.reduce()) executes the functions in parallel by using elements in different tasks. Each task works on the elements that it is assigned to and calculates a $(C result) that corresponds to the elements of that task. Since $(C reduce()) is called with only a single initial value, every task must use that same initial value to initialize its own $(C result) (the parameter $(C 0) above). +) + +$(P +The final values of the results that each task produces are themselves used in the same $(C result) calculation one last time. These final calculations are executed sequentially, not in parallel. For that reason, $(C taskPool.reduce()) may execute slower in short examples as in this chapter as will be observed in the following output. +) + +$(P +The fact that the same initial value is used for all of the tasks, effectively being used in the calculations multiple times, $(C taskPool.reduce()) may calculate a result that is different from what $(C std.algorithm.reduce()) calculates. For that reason, the initial value must be the $(I identity value) for the calculation that is being performed, e.g. the $(C 0) in this example which does not have any effect in addition. +) + +$(P +Additionally, as the results are used by the same functions one last time in the sequential calculations, the types of the parameters that the functions take must be compatible with the types of the values that the functions return. +) + +$(P +$(C taskPool.reduce()) should be used only under these considerations. +) + +--- +import std.parallelism; +// ... + writeln("Result: ", $(HILITE taskPool.reduce)!aCalculation(0, [1, 2, 3, 4])); +--- + +$(P +The output of the program indicates that first the calculations are performed in parallel, and then their results are calculated sequentially. The calculations that are performed sequentially are highlighted: +) + +$(SHELL +$ time ./deneme +$(SHELL_OBSERVED +started - element: 3, result: 0 $(SHELL_NOTE first, the tasks in parallel) +started - element: 2, result: 0 +started - element: 1, result: 0 +started - element: 4, result: 0 +finished - element: 3, result: 3 +finished - element: 1, result: 1 +$(HILITE started - element: 1, result: 0) $(SHELL_NOTE then, their results sequentially) +finished - element: 4, result: 4 +finished - element: 2, result: 2 +$(HILITE finished - element: 1, result: 1) +$(HILITE started - element: 2, result: 1) +$(HILITE finished - element: 2, result: 3) +$(HILITE started - element: 3, result: 3) +$(HILITE finished - element: 3, result: 6) +$(HILITE started - element: 4, result: 6) +$(HILITE finished - element: 4, result: 10) +Result: 10 + +real 0m5.006s $(SHELL_NOTE parallel reduce is slower in this example) +user 0m0.004s +sys 0m0.000s) +) + +$(P +Parallel $(C reduce()) is faster in many other calculations like the calculation of the math constant $(I pi) (π) by quadrature. +) + +$(H5 Multiple functions and tuple results) + +$(P +$(C std.algorithm.map()), $(C taskPool.map()), $(C taskPool.amap()), and $(C taskPool.reduce()) can all take more than one function, in which case the results are returned as a $(C Tuple). We have seen the $(C Tuple) type in the $(LINK2 /ders/d.en/tuples.html, Tuples chapter) before. The results of individual functions correspond to the elements of the tuple in the order that the functions are specified. For example, the result of the first function is the first member of the tuple. +) + +$(P +The following program demonstrates multiple functions with $(C std.algorithm.map). Note that the return types of the functions need not be the same, as seen in the $(C quarterOf()) and $(C tenTimes()) functions below. In that case, the types of the members of the tuples would be different as well: +) + +--- +import std.stdio; +import std.algorithm; +import std.conv; + +double quarterOf(double value) { + return value / 4; +} + +string tenTimes(double value) { + return to!string(value * 10); +} + +void main() { + auto values = [10, 42, 100]; + auto results = map!($(HILITE quarterOf, tenTimes))(values); + + writefln(" Quarters Ten Times"); + + foreach (quarterResult, tenTimesResult; results) { + writefln("%8.2f%8s", quarterResult, tenTimesResult); + } +} +--- + +$(P +The output: +) + +$(SHELL + Quarters Ten Times + 2.50 100 + 10.50 420 + 25.00 1000 +) + +$(P +In the case of $(C taskPool.reduce()), the initial values of the results must be specified as a tuple: +) + +--- + taskPool.reduce!(foo, bar)($(HILITE tuple(0, 1)), [1, 2, 3, 4]); +--- + +$(H5 $(IX TaskPool) $(C TaskPool)) + +$(P +Behind the scenes, the parallel algorithms from the $(C std.parallelism) module all use task objects that are elements of a $(C TaskPool) container. Normally, all of the algorithms use the same container object named $(C taskPool). +) + +$(P +$(C taskPool) contains appropriate number of tasks depending on the environment that the program runs under. For that reason, usually there is no need to create any other $(C TaskPool) object. Even so, explicit $(C TaskPool) objects may be created and used as needed. +) + +$(P +The $(C TaskPool) constructor takes the number of threads to use during the parallel operations that are later started through it. The default value of the number of threads is one less than the number of cores on the system. All of the features that we have seen in this chapter can be applied to a separate $(C TaskPool) object. +) + +$(P +The following example calls $(C parallel()) on a local $(C TaskPool) object: +) + +--- +import std.stdio; +import std.parallelism; + +void $(CODE_DONT_TEST compiler_asm_deprecation_warning)main() { + auto workers = new $(HILITE TaskPool(2)); + + foreach (i; $(HILITE workers).parallel([1, 2, 3, 4])) { + writefln("Working on %s", i); + } + + $(HILITE workers).finish(); +} +--- + +$(P +$(C TaskPool.finish()) tells the object to stop processing when all of its current tasks are completed. +) + +$(H5 Summary) + + +$(UL + +$(LI It is an error to execute operations in parallel unless those operations are independent from each other.) + +$(LI $(C parallel()) accesses the elements of a range in parallel.) + +$(LI Tasks can explicitly be created, started, and waited for by $(C task()), $(C executeInNewThread()), and $(C yieldForce()), respectively.) + +$(LI The exceptions that are escaped from tasks can be caught later by most of the parallelism functions like $(C yieldForce()).) + +$(LI $(C asyncBuf()) iterates the elements of an $(C InputRange) semi-eagerly in parallel.) + +$(LI $(C map()) calls functions with the elements of an $(C InputRange) semi-eagerly in parallel.) + +$(LI $(C amap()) calls functions with the elements of a $(C RandomAccessRange) fully-eagerly in parallel.) + +$(LI $(C reduce()) makes calculations over the elements of a $(C RandomAccessRange) in parallel.) + +$(LI $(C map()), $(C amap()), and $(C reduce()) can take multiple functions and return the results as tuples.) + +$(LI When needed, $(C TaskPool) objects other than $(C taskPool) can be used.) + +) + +macros: + SUBTITLE=Parallelism + + DESCRIPTION=Parallel programming that enables taking advantage of microprocessor cores + + KEYWORDS=d programming language tutorial book parallel programming + +MINI_SOZLUK= diff --git a/target/parameter_flexibility.cozum.d b/target/parameter_flexibility.cozum.d new file mode 100644 index 0000000..2830145 --- /dev/null +++ b/target/parameter_flexibility.cozum.d @@ -0,0 +1,105 @@ +Ddoc + +$(COZUM_BOLUMU Variable Number of Parameters) + +$(P +For the $(C calculate()) function to be able to take variable number of parameters, its parameter list must include a slice of $(C Calculation) followed by $(C ...): +) + +--- +double[] calculate(in Calculation[] calculations...) { + double[] results; + + foreach (calculation; calculations) { + final switch (calculation.op) { + + case Operation.add: + results ~= calculation.first + calculation.second; + break; + + case Operation.subtract: + results ~= calculation.first - calculation.second; + break; + + case Operation.multiply: + results ~= calculation.first * calculation.second; + break; + + case Operation.divide: + results ~= calculation.first / calculation.second; + break; + } + } + + return results; +} +--- + +$(P +Each calculation is evaluated inside a loop and their results are appended to a slice of type $(C double[]). +) + +$(P +Here is the entire program: +) + +--- +import std.stdio; + +enum Operation { add, subtract, multiply, divide } + +struct Calculation { + Operation op; + double first; + double second; +} + +double[] calculate(in Calculation[] calculations...) { + double[] results; + + foreach (calculation; calculations) { + final switch (calculation.op) { + + case Operation.add: + results ~= calculation.first + calculation.second; + break; + + case Operation.subtract: + results ~= calculation.first - calculation.second; + break; + + case Operation.multiply: + results ~= calculation.first * calculation.second; + break; + + case Operation.divide: + results ~= calculation.first / calculation.second; + break; + } + } + + return results; +} + +void main() { + writeln(calculate(Calculation(Operation.add, 1.1, 2.2), + Calculation(Operation.subtract, 3.3, 4.4), + Calculation(Operation.multiply, 5.5, 6.6), + Calculation(Operation.divide, 7.7, 8.8))); +} +--- + +$(P +The output: +) + +$(SHELL +[3.3, -1.1, 36.3, 0.875] +) + +Macros: + SUBTITLE=Variable Number of Parameters + + DESCRIPTION=Exercise solutions of the 'Variable Number of Parameters' chapter of the tutorial book 'Programming in D'. + + KEYWORDS=d programming book tutorial variadic functions exercise solutions diff --git a/target/parameter_flexibility.d b/target/parameter_flexibility.d new file mode 100644 index 0000000..dffcd8a --- /dev/null +++ b/target/parameter_flexibility.d @@ -0,0 +1,527 @@ +Ddoc + +$(DERS_BOLUMU $(IX parameter, variable number of) Variable Number of Parameters) + +$(P +This chapter covers two D features that bring flexibility on parameters when calling functions: +) + +$(UL +$(LI Default arguments) +$(LI Variadic functions) +) + +$(H5 $(IX default argument) $(IX argument, default) Default arguments) + +$(P +A convenience with function parameters is the ability to specify default values for them. This is similar to the default initial values of struct members. +) + +$(P +Some of the parameters of some functions are called mostly by the same values. To see an example of this, let's consider a function that prints the elements of an associative array of type $(C string[string]). Let's assume that the function takes the separator characters as parameters as well: +) + +--- +$(CODE_NAME printAA)import std.algorithm; + +// ... + +void printAA(in char[] title, + in string[string] aa, + in char[] keySeparator, + in char[] elementSeparator) { + writeln("-- ", title, " --"); + + auto keys = sort(aa.keys); + + // Don't print element separator before the first element + if (keys.length != 0) { + auto key = keys[0]; + write(key, keySeparator, aa[key]); + keys = keys[1..$]; // Remove the first element + } + + // Print element separator before the remaining elements + foreach (key; keys) { + write(elementSeparator); + write(key, keySeparator, aa[key]); + } + + writeln(); +} +--- + +$(P +That function is being called below with $(STRING ":") as the key separator and $(STRING ", ") as the element separator: +) + +--- +$(CODE_XREF printAA)void main() { + string[string] dictionary = [ + "blue":"mavi", "red":"kırmızı", "gray":"gri" ]; + + printAA("Color Dictionary", dictionary, ":", ", "); +} +--- + +$(P +The output: +) + +$(SHELL +-- Color Dictionary -- +blue:mavi, gray:gri, red:kırmızı +) + +$(P +If the separators are almost always going to be those two, they can be defined with default values: +) + +--- +void printAA(in char[] title, + in string[string] aa, + in char[] keySeparator $(HILITE = ": "), + in char[] elementSeparator $(HILITE = ", ")) { + // ... +} +--- + +$(P +Parameters with default values need not be specified when the function is called: +) + +--- + printAA("Color Dictionary", + dictionary); /* ← No separator specified. Both + * parameters will get their + * default values. */ +--- + +$(P +The parameter values can still be specified when needed, and not necessarily all of them: +) + +--- + printAA("Color Dictionary", dictionary$(HILITE , "=")); +--- + +$(P +The output: +) + +$(SHELL +-- Color Dictionary -- +blue=mavi, gray=gri, red=kırmızı +) + +$(P +The following call specifies both of the parameters: +) + +--- + printAA("Color Dictionary", dictionary$(HILITE , "=", "\n")); +--- + +$(P +The output: +) + +$(SHELL +-- Color Dictionary -- +blue=mavi +gray=gri +red=kırmızı +) + +$(P +Default values can only be defined for the parameters that are at the end of the parameter list. +) + +$(H6 Special keywords as default arguments) + +$(P +$(IX special keyword) $(IX keyword, special) The following special keywords act like compile-time literals having values corresponding to where they appear in code: +) + +$(UL + +$(LI $(IX __MODULE__) $(C __MODULE__): Name of the module as $(C string)) +$(LI $(IX __FILE__) $(C __FILE__): Name of the source file as $(C string)) +$(LI $(IX __FILE_FULL_PATH__) $(C __FILE_FULL_PATH__): Name of the source file including its full path as $(C string)) +$(LI $(IX __LINE__) $(C __LINE__): Line number as $(C int)) +$(LI $(IX __FUNCTION__) $(C __FUNCTION__): Name of the function as $(C string)) +$(LI $(IX __PRETTY_FUNCTION__) $(C __PRETTY_FUNCTION__): Full signature of the function as $(C string)) + +) + +$(P +Although they can be useful anywhere in code, they work differently when used as default arguments. When they are used in regular code, their values refer to where they appear in code: +) + +--- +import std.stdio; + +void func(int parameter) { + writefln("Inside function %s at file %s, line %s.", + __FUNCTION__, __FILE__, __LINE__); $(CODE_NOTE $(HILITE line 5)) +} + +void main() { + func(42); +} +--- + +$(P +The reported line 5 is inside the function: +) + +$(SHELL +Inside function deneme.$(HILITE func) at file deneme.d, $(HILITE line 5). +) + +$(P +However, sometimes it is more interesting to determine the line where a function is called from, not where the definition of the function is. When these special keywords are provided as default arguments, their values refer to where the function is called from: +) + +--- +import std.stdio; + +void func(int parameter, + string functionName = $(HILITE __FUNCTION__), + string file = $(HILITE __FILE__), + int line = $(HILITE __LINE__)) { + writefln("Called from function %s at file %s, line %s.", + functionName, file, line); +} + +void main() { + func(42); $(CODE_NOTE $(HILITE line 12)) +} +--- + +$(P +This time the special keywords refer to $(C main()), the caller of the function: +) + +$(SHELL +Called from function deneme.$(HILITE main) at file deneme.d, $(HILITE line 12). +) + +$(P +$(IX special token) $(IX token, special) In addition to the above, there are also the following $(I special tokens) that take values depending on the compiler and the time of day: +) + +$(UL + +$(LI $(IX __DATE__) $(C __DATE__): Date of compilation as $(C string)) + +$(LI $(IX __TIME__) $(C __TIME__): Time of compilation as $(C string)) + +$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Date and time of compilation as $(C string)) + +$(LI $(IX __VENDOR__) $(C __VENDOR__): Compiler vendor as $(C string) (e.g. $(STRING "Digital Mars D"))) + +$(LI $(IX __VERSION__) $(C __VERSION__): Compiler version as $(C long) (e.g. the value $(C 2072L) for version 2.072)) + +) + +$(H5 $(IX variadic function) Variadic functions) + +$(P +Despite appearances, default parameter values do not change the number of parameters that a function receives. For example, even though some parameters may be assigned their default values, $(C printAA()) always takes four parameters and uses them according to its implementation. +) + +$(P +On the other hand, variadic functions can be called with unspecified number of arguments. We have already been taking advantage of this feature with functions like $(C writeln()). $(C writeln()) can be called with any number of arguments: +) + +--- + writeln( + "hello", 7, "world", 9.8 /*, and any number of other + * arguments as needed */); +--- + +$(P +There are four ways of defining variadic functions in D: +) + +$(UL + +$(LI $(IX _argptr) The feature that works only for functions that are marked as $(C extern(C)). This feature defines the hidden $(C _argptr) variable that is used for accessing the parameters. This book does not cover this feature partly because it is unsafe.) + +$(LI $(IX _arguments) The feature that works with regular D functions, which also uses the hidden $(C _argptr) variable, as well as the $(C _arguments) variable, the latter being of type $(C TypeInfo[]). This book does not cover this feature as well both because it relies on $(I pointers), which have not been covered yet, and because this feature can be used in unsafe ways as well.) + +$(LI A safe feature with the limitation that the unspecified number of parameters must all be of the same type. This is the feature that is covered in this section.) + +$(LI Unspecified number of template parameters. This feature will be explained later in the templates chapters.) + +) + +$(P +$(IX ..., function parameter) The parameters of variadic functions are passed to the function as a slice. Variadic functions are defined with a single parameter of a specific type of slice followed immediately by the $(C ...) characters: +) + +--- +double sum(in double[] numbers$(HILITE ...)) { + double result = 0.0; + + foreach (number; numbers) { + result += number; + } + + return result; +} +--- + +$(P +That definition makes $(C sum()) a variadic function, meaning that it is able to receive any number of arguments as long as they are $(C double) or any other type that can implicitly be convertible to $(C double): +) + +--- + writeln(sum($(HILITE 1.1, 2.2, 3.3))); +--- + +$(P +The single slice parameter and the $(C ...) characters represent all of the arguments. For example, the slice would have five elements if the function were called with five $(C double) values. +) + +$(P +In fact, the variable number of parameters can also be passed as a single slice: +) + +--- + writeln(sum($(HILITE [) 1.1, 2.2, 3.3 $(HILITE ]))); // same as above +--- + +$(P +Variadic functions can also have required parameters, which must be defined first in the parameter list. For example, the following function prints an unspecified number of parameters within parentheses. Although the function leaves the number of the elements flexible, it requires that the parentheses are always specified: +) + +--- +char[] parenthesize( + in char[] opening, // ← The first two parameters must be + in char[] closing, // specified when the function is called + in char[][] words...) { // ← Need not be specified + char[] result; + + foreach (word; words) { + result ~= opening; + result ~= word; + result ~= closing; + } + + return result; +} +--- + +$(P +The first two parameters are mandatory: +) + +--- + parenthesize("{"); $(DERLEME_HATASI) +--- + +$(P +As long as the mandatory parameters are specified, the rest are optional: +) + +--- + writeln(parenthesize("{", "}", "apple", "pear", "banana")); +--- + +$(P +The output: +) + +$(SHELL +{apple}{pear}{banana} +) + +$(H6 Variadic function arguments have a short lifetime) + +$(P +The slice argument that is automatically generated for a variadic parameter points at a temporary array that has a short lifetime. This fact does not matter if the function uses the arguments only during its execution. However, it would be a bug if the function kept a slice to those elements for later use: +) + +--- +int[] numbersForLaterUse; + +void foo(int[] numbers...) { + numbersForLaterUse = numbers; $(CODE_NOTE_WRONG BUG) +} + +struct S { + string[] namesForLaterUse; + + void foo(string[] names...) { + namesForLaterUse = names; $(CODE_NOTE_WRONG BUG) + } +} + +void bar() { + foo(1, 10, 100); /* The temporary array [ 1, 10, 100 ] is + * not valid beyond this point. */ + + auto s = S(); + s.foo("hello", "world"); /* The temporary array + * [ "hello", "world" ] is not + * valid beyond this point. */ + + // ... +} + +void main() { + bar(); +} +--- + +$(P +Both the free-standing function $(C foo()) and the member function $(C S.foo()) are in error because they store slices to automatically-generated temporary arrays that live on the program stack. Those arrays are valid only during the execution of the variadic functions. +) + +$(P +For that reason, if a function needs to store a slice to the elements of a variadic parameter, it must first take a copy of those elements: +) + +--- +void foo(int[] numbers...) { + numbersForLaterUse = numbers$(HILITE .dup); $(CODE_NOTE correct) +} + +// ... + + void foo(string[] names...) { + namesForLaterUse = names$(HILITE .dup); $(CODE_NOTE correct) + } +--- + +$(P +However, since variadic functions can also be called with slices of proper arrays, copying the elements would be unnecessary in those cases. +) + +$(P +A solution that is both correct and efficient is to define two functions having the same name, one taking a variadic parameter and the other taking a proper slice. If the caller passes variable number of arguments, then the variadic version of the function is called; and if the caller passes a proper slice, then the version that takes a proper slice is called: +) + +--- +int[] numbersForLaterUse; + +void foo(int[] numbers$(HILITE ...)) { + /* Since this is the variadic version of foo(), we must + * first take a copy of the elements before storing a + * slice to them. */ + numbersForLaterUse = numbers$(HILITE .dup); +} + +void foo(int[] numbers) { + /* Since this is the non-variadic version of foo(), we can + * store the slice as is. */ + numbersForLaterUse = numbers; +} + +struct S { + string[] namesForLaterUse; + + void foo(string[] names$(HILITE ...)) { + /* Since this is the variadic version of S.foo(), we + * must first take a copy of the elements before + * storing a slice to them. */ + namesForLaterUse = names$(HILITE .dup); + } + + void foo(string[] names) { + /* Since this is the non-variadic version of S.foo(), + * we can store the slice as is. */ + namesForLaterUse = names; + } +} + +void bar() { + // This call is dispatched to the variadic function. + foo(1, 10, 100); + + // This call is dispatched to the proper slice function. + foo($(HILITE [) 2, 20, 200 $(HILITE ])); + + auto s = S(); + + // This call is dispatched to the variadic function. + s.foo("hello", "world"); + + // This call is dispatched to the proper slice function. + s.foo($(HILITE [) "hi", "moon" $(HILITE ])); + + // ... +} + +void main() { + bar(); +} +--- + +$(P +Defining multiple functions with the same name but with different parameters is called $(I function overloading), which is the subject of the next chapter. +) + +$(PROBLEM_TEK + +$(P +Assume that the following $(C enum) is already defined: +) + +--- +enum Operation { add, subtract, multiply, divide } +--- + +$(P +Also assume that there is a $(C struct) that represents the calculation of an operation and its two operands: +) + +--- +struct Calculation { + Operation op; + double first; + double second; +} +--- + +$(P +For example, the object $(C Calculation(Operation.divide, 7.7, 8.8)) would represent the division of 7.7 by 8.8. +) + +$(P +Design a function that receives an unspecified number of these $(C struct) objects, calculates the result of each $(C Calculation), and then returns all of the results as a slice of type $(C double[]). +) + +$(P +For example, it should be possible to call the function as in the following code: +) + +--- +void $(CODE_DONT_TEST)main() { + writeln( + calculate(Calculation(Operation.add, 1.1, 2.2), + Calculation(Operation.subtract, 3.3, 4.4), + Calculation(Operation.multiply, 5.5, 6.6), + Calculation(Operation.divide, 7.7, 8.8))); +} +--- + +$(P +The output of the code should be similar to the following: +) + +$(SHELL +[3.3, -1.1, 36.3, 0.875] +) + +) + + +Macros: + SUBTITLE=Variable Number of Parameters + + DESCRIPTION=Default values for function parameters; and the 'variadic function' feature that allows passing variable number of arguments to functions. + + KEYWORDS=d programming book tutorial struct diff --git a/target/pointers.cozum.d b/target/pointers.cozum.d new file mode 100644 index 0000000..6947c09 --- /dev/null +++ b/target/pointers.cozum.d @@ -0,0 +1,209 @@ +Ddoc + +$(COZUM_BOLUMU Pointers) + +$(OL + +$(LI +When parameters are value types like $(C int), the arguments are copied to functions. The preferred way of defining reference parameters is to specify them as $(C ref). + +$(P +Another way is to define the parameters as pointers that point at the actual variables. The parts of the program that have been changed are highlighted: +) + +--- +void swap(int $(HILITE *) lhs, int $(HILITE *) rhs) { + int temp = $(HILITE *)lhs; + $(HILITE *)lhs = $(HILITE *)rhs; + $(HILITE *)rhs = temp; +} + +void main() { + int i = 1; + int j = 2; + + swap($(HILITE &)i, $(HILITE &)j); + + assert(i == 2); + assert(j == 1); +} +--- + +$(P +The checks at the end of the program now pass. +) + +) + +$(LI +$(C Node) and $(C List) have been written to work only with the $(C int) type. We can convert these types to struct templates by adding $(C (T)) after their names and replacing appropriate $(C int)s in their definitions by $(C T)s: + +--- +$(CODE_NAME List)struct Node$(HILITE (T)) { + $(HILITE T) element; + Node * next; + + string toString() const { + string result = to!string(element); + + if (next) { + result ~= " -> " ~ to!string(*next); + } + + return result; + } +} + +struct List$(HILITE (T)) { + Node!$(HILITE T) * head; + + void insertAtHead($(HILITE T) element) { + head = new Node!$(HILITE T)(element, head); + } + + string toString() const { + return format("(%s)", head ? to!string(*head) : ""); + } +} +--- + +$(P +$(C List) can now be used with any type: +) + +--- +$(CODE_XREF List)import std.stdio; +import std.conv; +import std.string; + +// ... + +struct Point { + double x; + double y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} + +void main() { + $(HILITE List!Point) points; + + points.insertAtHead(Point(1.1, 2.2)); + points.insertAtHead(Point(3.3, 4.4)); + points.insertAtHead(Point(5.5, 6.6)); + + writeln(points); +} +--- + +$(P +The output: +) + +$(SHELL +((5.5,6.6) -> (3.3,4.4) -> (1.1,2.2)) +) + +) + +$(LI In this case we need another pointer to point at the last node of the list. The new code is necessarily more complex in order to manage the new variable as well: + +--- +struct List(T) { + Node!T * head; + $(HILITE Node!T * tail); + + void append(T element) { + /* Since there is no node after the last one, we set + * the new node's next pointer to 'null'. */ + auto newNode = new Node!T(element, null); + + if (!head) { + /* The list has been empty. The new node becomes + * the head. */ + head = newNode; + } + + if (tail) { + /* We place this node after the current tail. */ + tail.next = newNode; + } + + /* The new node becomes the new tail. */ + tail = newNode; + } + + void insertAtHead(T element) { + auto newNode = new Node!T(element, head); + + /* The new node becomes the new head. */ + head = newNode; + + if (!tail) { + /* The list has been empty. The new node becomes + * the tail. */ + tail = newNode; + } + } + + string toString() const { + return format("(%s)", head ? to!string(*head) : ""); + } +} +--- + +$(P +The new implementation of $(C insertAtHead()) can actually be shorter: +) + +--- + void insertAtHead(T element) { + head = new Node!T(element, head); + + if (!tail) { + tail = head; + } + } +--- + +$(P +The following program uses the new $(C List) to insert $(C Point) objects with odd values at the head and $(C Point) objects with even values at the end. +) + +--- +void $(CODE_DONT_TEST)main() { + List!Point points; + + foreach (i; 1 .. 7) { + if (i % 2) { + points.insertAtHead(Point(i, i)); + + } else { + points.append(Point(i, i)); + } + } + + writeln(points); +} +--- + +$(P +The output: +) + +$(SHELL +((5,5) -> (3,3) -> (1,1) -> (2,2) -> (4,4) -> (6,6)) +) + +) + +) + +Macros: + SUBTITLE=Pointers Solutions + + DESCRIPTION=Programming in D Solutions: Pointers + + KEYWORDS=d programming language tutorial book pointers diff --git a/target/pointers.d b/target/pointers.d new file mode 100644 index 0000000..f73a92c --- /dev/null +++ b/target/pointers.d @@ -0,0 +1,1653 @@ +Ddoc + +$(DERS_BOLUMU $(IX pointer) Pointers) + +$(P +Pointers are variables that provide access to other variables. The value of a pointer is the address of the variable that it provides access to. +) + +$(P +Pointers can point at any type of variable, object, and even other pointers. In this chapter, I will refer to all of these simply as $(I variables). +) + +$(P +Pointers are low level features of microprocessors. They are an important part of system programming. +) + +$(P +The syntax and semantics of pointers in D are inherited directly from C. Although pointers are notoriously the most difficult feature of C to comprehend, they should not be as difficult in D. This is because other features of D that are semantically close to pointers are more useful in situations where pointers would have to be used in other languages. When the ideas behind pointers are already understood from those other features of D, pointers should be easier to grasp. +) + +$(P +The short examples throughout the most of this chapter are decidedly simple. The programs at the end of the chapter will be more realistic. +) + +$(P +The names like $(C ptr) (short for "pointer") that I have used in these examples should not be considered as useful names in general. As always, names must be chosen to be more meaningful and explanatory in actual programs. +) + +$(H5 $(IX reference, concept) The concept of a reference) + +$(P +Although we have encountered references many times in the previous chapters, let's summarize this concept one more time. +) + +$(H6 The $(C ref) variables in $(C foreach) loops) + +$(P +As we have seen in $(LINK2 /ders/d.en/foreach.html, the $(C foreach) Loop chapter), normally the loop variables are $(I copies) of elements: +) + +--- +import std.stdio; + +void main() { + int[] numbers = [ 1, 11, 111 ]; + + foreach (number; numbers) { + number = 0; // ← the copy changes, not the element + } + + writeln("After the loop: ", numbers); +} +--- + +$(P +The $(C number) that gets assigned 0 each time is a copy of one of the elements of the array. Modifying that copy does not modify the element: +) + +$(SHELL +After the loop: [1, 11, 111] +) + +$(P +When the actual elements need to be modified, the $(C foreach) variable must be defined as $(C ref): +) + +--- + foreach ($(HILITE ref) number; numbers) { + number = 0; // ← the actual element changes + } +--- + +$(P +This time $(C number) is a reference to an actual element in the array: +) + +$(SHELL_SMALL +After the loop: [0, 0, 0] +) + +$(H6 $(C ref) function parameters) + +$(P +As we have seen in $(LINK2 /ders/d.en/function_parameters.html, the Function Parameters chapter), the parameters of $(I value types) are normally copies of the arguments: +) + +--- +import std.stdio; + +void addHalf(double value) { + value += 0.5; // ← Does not affect 'value' in main +} + +void main() { + double value = 1.5; + + addHalf(value); + + writeln("The value after calling the function: ", value); +} +--- + +$(P +Because the function parameter is not defined as $(C ref), the assignment inside the function affects only the local variable there. The variable in $(C main()) is not affected: +) + +$(SHELL_SMALL +The value after calling the function: 1.5 +) + +$(P +The $(C ref) keyword would make the function parameter a reference to the argument: +) + +--- +void addHalf($(HILITE ref) double value) { + value += 0.5; +} +--- + +$(P +This time the variable in $(C main()) gets modified: +) + +$(SHELL_SMALL +The value after calling the function: 2 +) + +$(H6 Reference types) + +$(P +Some types are reference types. Variables of such types provide access to separate variables: +) + +$(UL +$(LI Class variables) +$(LI Slices) +$(LI Associative arrays) +) + +$(P +We have seen this distinction in $(LINK2 /ders/d.en/value_vs_reference.html, the Value Types and Reference Types chapter). The following example demonstrates reference types by two $(C class) variables: +) + +--- +import std.stdio; + +class Pen { + double ink; + + this() { + ink = 15; + } + + void use(double amount) { + ink -= amount; + } +} + +void main() { + auto pen = new Pen; + auto otherPen = pen; // ← Now both variables provide + // access to the same object + + writefln("Before: %s %s", pen.ink, otherPen.ink); + + pen.use(1); // ← the same object is used + otherPen.use(2); // ← the same object is used + + writefln("After : %s %s", pen.ink, otherPen.ink); +} +--- + +$(P +Because classes are reference types, the class variables $(C pen) and $(C otherPen) provide access to the same $(C Pen) object. As a result, using either of those class variables affects the same object: +) + +$(SHELL_SMALL +Before: 15 15 +After : 12 12 +) + +$(P +That single object and the two class variables would be laid out in memory similar to the following figure: +) + +$(MONO + (The Pen object) pen otherPen + ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── + │ ink │ │ o │ │ o │ + ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── + ▲ │ │ + │ │ │ + └────────────────────┴────────────┘ +) + +$(P +References $(I point at) actual variables as $(C pen) and $(C otherPen) do above. +) + +$(P +Programming languages implement the reference and pointer concepts by special registers of the microprocessor, which are specifically for $(I pointing at) memory locations. +) + +$(P +Behind the scenes, D's higher-level concepts (class variables, slices, associative arrays, etc.) are all implemented by pointers. As these higher-level features are already efficient and convenient, pointers are rarely needed in D programming. Still, it is important for D programmers to understand pointers well. +) + +$(H5 $(IX *, pointer definition) Syntax) + +$(P +The pointer syntax of D is mostly the same as in C. Although this can be seen as an advantage, the peculiarities of C's pointer syntax are necessarily inherited by D as well. For example, the different meanings of the $(C *) character may be confusing. +) + +$(P +With the exception of $(C void) pointers, every pointer is associated with a certain type and can point at only variables of that specific type. For example, an $(C int) pointer can only point at variables of type $(C int). +) + +$(P +The pointer definition syntax consists of the associated type and a $(C *) character: +) + +--- + $(I $(D_KEYWORD type_to_point_at)) * $(I name_of_the_pointer_variable); +--- + +$(P +Accordingly, a pointer variable that would be pointing at $(C int) variables would be defined like this: +) + +--- + int * myPointer; +--- + +$(P +The $(C *) character in that syntax may be pronounced as "pointer". So, the type of $(C myPointer) above is an "int pointer". The spaces before and after the $(C *) character are optional. The following syntaxes are common as well: +) + +--- + int* myPointer; + int *myPointer; +--- + +$(P +When it is specifically a pointer type that is being mentioned as in "int pointer", it is common to write the type without any spaces as in $(C int*). +) + +$(H5 $(IX &, address of) Pointer value and the address-of operator $(C &)) + +$(P +Being variables themselves pointers have values as well. The default value of a pointer is the special value $(C null), which means that the pointer is not $(I pointing at) any variable yet (i.e. does not provide access to any variable). +) + +$(P +To make a pointer provide access to a variable, the value of the pointer must be set to the address of that variable. The pointer starts pointing at the variable that is at that specific address. From now on, I will call that variable $(I the pointee). +) + +$(P +The $(C &) operator which we have used many times before with $(C readf) has also been briefly mentioned in $(LINK2 /ders/d.en/value_vs_reference.html, the Value Types and Reference Types chapter). This operator produces the address of the variable that is written after it. Its value can be used when initializing a pointer: +) + +--- + int myVariable = 180; + int * myPointer = $(HILITE &)myVariable; +--- + +$(P +Initializing $(C myPointer) by the address of $(C myVariable) makes $(C myPointer) point at $(C myVariable). +) + +$(P +The value of the pointer is the same as the address of $(C myVariable): +) + +--- + writeln("The address of myVariable: ", &myVariable); + writeln("The value of myPointer : ", myPointer); +--- + +$(SHELL_SMALL +The address of myVariable: 7FFF2CE73F10 +The value of myPointer : 7FFF2CE73F10 +) + +$(P $(I $(B Note:) The address value is likely to be different every time the program is started.) +) + +$(P +The following figure is a representation of these two variables in memory: +) + +$(MONO + myVariable at myPointer at + address 7FFF2CE73F10 some other address +───┬────────────────┬─── ───┬────────────────┬─── + │ 180 │ │ 7FFF2CE73F10 │ +───┴────────────────┴─── ───┴────────│───────┴─── + ▲ │ + │ │ + └─────────────────────────────┘ +) + +$(P +The value of $(C myPointer) is the address of $(C myVariable), conceptually $(I pointing at) the variable that is at that location. +) + +$(P +Since pointers are variables as well, the $(C &) operator can produce the address of the pointer as well: +) + +--- + writeln("The address of myPointer : ", &myPointer); +--- + +$(SHELL_SMALL +The address of myPointer : 7FFF2CE73F18 +) + +$(P +Since the difference between the two addresses above is 8, remembering that an $(C int) takes up 4 bytes, we can deduce that $(C myVariable) and $(C myPointer) are 4 bytes apart in memory. +) + +$(P +After removing the arrow that represented the concept of $(I pointing at), we can picture the contents of memory around these addresses like this: +) + +$(MONO + 7FFF2CE73F10 7FFF2CE73F14 7FFF2CE73F18 + : : : : + ───┬────────────────┬────────────────┬────────────────┬─── + │ 180 │ (unused) │ 7FFF2CE73F10 │ + ───┴────────────────┴────────────────┴────────────────┴─── +) + +$(P +The names of variables, functions, classes, etc. and keywords are not parts of programs of compiled languages like D. The variables that have been defined by the programmer in the source code are converted to bytes that occupy memory or registers of the microprocessor. +) + +$(P +$(I $(B Note:) The names (a.k.a. symbols) may actually be included in programs to help with debugging but those names do not affect the operation of the program.) +) + +$(H5 $(IX *, pointee access) The access operator $(C *)) + +$(P +We have seen above that the $(C *) character which normally represents multiplication is also used when defining pointers. A difficulty with the syntax of pointers is that the same character has a third meaning: It is also used when accessing the pointee through the pointer. +) + +$(P +When it is written before the name of a pointer, it means $(I the variable that the pointer is pointing at) (i.e. the pointee): +) + +--- + writeln("The value that it is pointing at: ", $(HILITE *)myPointer); +--- + +$(SHELL_SMALL +The value that it is pointing at: 180 +) + +$(H5 $(IX ., pointer) The $(C .) (dot) operator to access a member of the pointee) + +$(P +If you know pointers from C, this operator is the same as the $(C ->) operator in that language. +) + +$(P +We have seen above that the $(C *) operator is used for accessing the pointee. That is sufficiently useful for pointers of fundamental types like $(C int*): The value of a fundamental type is accessed simply by writing $(C *myPointer). +) + +$(P +However, when the pointee is a struct or a class object, the same syntax becomes inconvenient. To see why, let's consider the following struct: +) + +--- +struct Coordinate { + int x; + int y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} +--- + +$(P +The following code defines an object and a pointer of that type: +) + +--- + auto center = Coordinate(0, 0); + Coordinate * ptr = $(HILITE &)center; // pointer definition + writeln($(HILITE *)ptr); // object access +--- + +$(P +That syntax is convenient when accessing the value of the entire $(C Coordinate) object: +) + +$(SHELL_SMALL +(0,0) +) + +$(P +However, the code becomes complicated when accessing a member of an object through a pointer and the $(C *) operator: +) + +--- + // Adjust the x coordinate + (*ptr).x += 10; +--- + +$(P +That expression modifies the value of the $(C x) member of the $(C center) object. The left-hand side of that expression can be explained by the following steps: +) + +$(UL +$(LI $(C ptr): The pointer that points at $(C center)) + +$(LI $(C $(HILITE *)ptr): Accessing the object (i.e. $(C center) itself)) + +$(LI $(C $(HILITE ()*ptr$(HILITE ))): Parentheses so that the $(C .) (dot) operator is applied to the object, not to the pointer) + +$(LI $(C (*ptr)$(HILITE .x)): The $(C x) member of the object that $(C ptr) is pointing at) + +) + +$(P +To reduce the complexity of pointer syntax in D, the $(C .) (dot) operator is transferred to the pointee and provides access to the member of the object. (The exceptions to this rule are at the end of this section.) +) + +$(P +So, the previous expression is normally written as: +) + +--- + $(HILITE ptr.x) += 10; +--- + +$(P +Since the pointer itself does not have a member named $(C x), $(C .x) is applied to the pointee and the $(C x) member of $(C center) gets modified: +) + +$(SHELL_SMALL +(10,0) +) + +$(P +Note that this is the same as the use of the $(C .) (dot) operator with classes. When the $(C .) (dot) operator is applied to a class $(I variable), it provides access to a member of the class $(I object): +) + +--- +class ClassType { + int member; +} + +// ... + + // Variable on the left, object on the right + ClassType variable = new ClassType; + + // Applied to the variable but accesses the member of + // the object + variable.member = 42; +--- + +$(P +As you remember from $(LINK2 /ders/d.en/class.html, the Classes chapter), the class object is constructed by the $(C new) keyword on the right-hand side. $(C variable) is a class variable that provides access to it. +) + +$(P +Realizing that it is the same with pointers is an indication that class variables and pointers are implemented similarly by the compiler. +) + +$(P +There is an exception to this rule both for class variables and for pointers. Type properties like $(C .sizeof) are applied to the type of the pointer, not to the type of the pointee: +) + +--- + char c; + char * p; + + writeln(p.sizeof); // size of the pointer, not the pointee +--- + +$(P +$(C .sizeof) produces the size of $(C p), which is a $(C char*), not the size of $(C c), which is a $(C char). On a 64-bit system pointers are 8-byte long: +) + +$(SHELL_SMALL +8 +) + +$(H5 $(IX arithmetic, pointer) Modifying the value of a pointer) + +$(P +The values of pointers can be incremented or decremented and they can be used in addition and subtraction: +) + +--- + ++ptr; + --ptr; + ptr += 2; + ptr -= 2; + writeln(ptr + 3); + writeln(ptr - 3); +--- + +$(P +Different from their arithmetic counterparts, these operations do not modify the actual value by the specified amount. Rather, the value of the pointer gets modified so that it now points at the variable that is a certain number of variables beyond the current one. The amount of the increment or the decrement specifies $(I how many variables away) should the pointer now point at. +) + +$(P +For example, incrementing the value of a pointer makes it point at the next variable: +) + +--- + ++ptr; // Starts pointing at a variable that is next in + // memory from the old variable +--- + +$(P +For that to work correctly, the actual value of the pointer must be incremented by the size of the variable. For example, because the size of $(C int) is 4, incrementing a pointer of type $(C int*) changes its value by 4. The programmer need not pay attention to this detail; the pointer value is modified by the correct amount automatically. +) + +$(P $(B Warning): It is undefined behavior to point at a location that is not a valid byte that belongs to the program. Even if it is not actually used to access any variable there, it is invalid for a pointer to point at a nonexistent variable. (The only exception of this rule is that it is valid to point at the imaginary element one past the end of an array. This will be explained later below.) +) + +$(P +For example, it is invalid to increment a pointer that points at $(C myVariable), because $(C myVariable) is defined as a single $(C int): +) + +--- + ++myPointer; $(CODE_NOTE_WRONG undefined behavior) +--- + +$(P +Undefined behavior means that it cannot be known what the behavior of the program will be after that operation. There may be systems where the program crashes after incrementing that pointer. However, on most modern systems the pointer is likely to point at the unused memory location that has been shown as being between $(C myVariable) and $(C myPointer) in the previous figure. +) + +$(P +For that reason, the value of a pointer must be incremented or decremented only if there is a valid object at the new location. Arrays (and slices) have that property: The elements of an array are side by side in memory. +) + +$(P +A pointer that is pointing at an element of a slice can be incremented safely as long as it does not go beyond the end of the slice. Incrementing such a pointer by the $(C ++) operator makes it point at the next element: +) + +--- +import std.stdio; +import std.string; +import std.conv; + +enum Color { red, yellow, blue } + +struct Crayon { + Color color; + double length; + + string toString() const { + return format("%scm %s crayon", length, color); + } +} + +void main() { + writefln("Crayon objects are %s bytes each.", Crayon.sizeof); + + Crayon[] crayons = [ Crayon(Color.red, 11), + Crayon(Color.yellow, 12), + Crayon(Color.blue, 13) ]; + + $(HILITE Crayon * ptr) = $(HILITE &)crayons[0]; // (1) + + for (int i = 0; i != crayons.length; ++i) { + writeln("Pointer value: ", $(HILITE ptr)); // (2) + + writeln("Crayon: ", $(HILITE *ptr)); // (3) + $(HILITE ++ptr); // (4) + } +} +--- + +$(OL +$(LI Definition: The pointer is initialized by the address of the first element.) +$(LI Using its value: The value of the pointer is the address of the element that it is pointing at.) +$(LI Accessing the element that is being pointed at.) +$(LI Pointing at the next element.) +) + +$(P +The output: +) + +$(SHELL +Crayon objects are 16 bytes each. +Pointer value: 7F37AC9E6FC0 +Crayon: 11cm red crayon +Pointer value: 7F37AC9E6FD0 +Crayon: 12cm yellow crayon +Pointer value: 7F37AC9E6FE0 +Crayon: 13cm blue crayon +) + +$(P +Note that the loop above is iterated a total of $(C crayons.length) times so that the pointer is always used for accessing a valid element. +) + +$(H5 Pointers are risky) + +$(P +The compiler and the D runtime environment cannot guarantee that the pointers are always used correctly. It is the programmer's responsibility to ensure that a pointer is either $(C null) or points at a valid memory location (at a variable, at an element of an array, etc.). +) + +$(P +For that reason, it is always better to consider higher-level features of D before thinking about using pointers. +) + +$(H5 $(IX element one past the end) The element one past the end of an array) + +$(P +It is valid to point at the imaginary element one past the end of an array. +) + +$(P +This is a useful idiom that is similar to number ranges. When defining a slice with a number range, the second index is one past the elements of the slice: +) + +--- + int[] values = [ 0, 1, 2, 3 ]; + writeln(values[1 .. 3]); // 1 and 2 included, 3 excluded +--- + +$(P +This idiom can be used with pointers as well. It is a common function design in C and C++ where a function parameter points at the first element and another one points at the element after the last element: +) + +--- +import std.stdio; + +void tenTimes(int * begin, int * end) { + while (begin != end) { + *begin *= 10; + ++begin; + } +} + +void main() { + int[] values = [ 0, 1, 2, 3 ]; + + // The address of the second element: + int * begin = &values[1]; + + // The address of two elements beyond that one + tenTimes(begin, begin + 2); + + writeln(values); +} +--- + +$(P +The value $(C begin + 2) means two elements after the one that $(C begin) is pointing at (i.e. the element at index 3). +) + +$(P +The $(C tenTimes()) function takes two pointer parameters. It uses the element that the first one is pointing at but it never accesses the element that the second one is pointing at. As a result, only the elements at indexes 1 and 2 get modified: +) + +$(SHELL_SMALL +[0, 10, 20, 3] +) + +$(P +Such functions can be implemented by a $(C for) loop as well: +) + +--- + for ( ; begin != end; ++begin) { + *begin *= 10; + } +--- + +$(P +Two pointers that define a range can also be used with $(C foreach) loops: +) + +--- + foreach (ptr; begin .. end) { + *ptr *= 10; + } +--- + +$(P +For these methods to be applicable to $(I all of the elements) of a slice, the second pointer must necessarily point after the last element: +) + +--- + // The second pointer is pointing at the imaginary element + // past the end of the array: + tenTimes(begin, begin + values.length); +--- + +$(P +That is the reason why it is legal to point at the imaginary element one beyond the last element of an array. +) + +$(H5 $(IX []) Using pointers with the array indexing operator $(C [])) + +$(P +Although it is not absolutely necessary in D, pointers can directly be used for accessing the elements of an array by an index value: +) + +--- + double[] floats = [ 0.0, 1.1, 2.2, 3.3, 4.4 ]; + + double * ptr = &floats[2]; + + *ptr = -100; // direct access to what it points at + ptr$(HILITE [1]) = -200; // access by indexing + + writeln(floats); +--- + +$(P +The output: +) + +$(SHELL_SMALL +[0, 1.1, -100, -200, 4.4] +) + +$(P +In that syntax, the element that the pointer is pointing at is thought of being the first element of an imaginary slice. The $(C []) operator provides access to the specified element of that slice. The $(C ptr) above initially points at the element at index 2 of the original $(C floats) slice. $(C ptr[1]) is a reference to the element 1 of the imaginary slice that starts at $(C ptr) (i.e. index 3 of the original slice). +) + +$(P +Although this behavior may seem complicated, there is a very simple conversion behind that syntax. Behind the scenes, the compiler converts the $(C pointer[index]) syntax to the $(C *(pointer + index)) expression: +) + +--- + ptr[1] = -200; // slice syntax + *(ptr + 1) = -200; // the equivalent of the previous line +--- + +$(P +As I have mentioned earlier, the compiler may not guarantee that this expression refers to a valid element. D's slices provide a much safer alternative and should be considered instead: +) + +--- + double[] slice = floats[2 .. 4]; + slice[0] = -100; + slice[1] = -200; +--- + +$(P +Normally, index values are checked for slices at run time: +) + +--- + slice[2] = -300; // Runtime error: accessing outside of the slice +--- + +$(P +Because the slice above does not have an element at index 2, an exception would be thrown at run time (unless the program has been compiled with the $(C -release) compiler switch): +) + +$(SHELL_SMALL +core.exception.RangeError@deneme(8391): Range violation +) + +$(H5 $(IX slice from pointer) Producing a slice from a pointer) + +$(P +Pointers are not as safe or as useful as slices because although they can be used with the slice indexing operator, they are not aware of the valid range of elements. +) + +$(P +However, when the number of valid elements is known, a pointer can be used to construct a slice. +) + +$(P +Let's assume that the $(C makeObjects()) function below is inside a C library. Let's assume that $(C makeObjects) makes specified number of $(C Struct) objects and returns a pointer to the first one of those objects: +) + +--- + Struct * ptr = makeObjects(10); +--- + +$(P +The syntax that produces a slice from a pointer is the following: +) + +--- + /* ... */ slice = pointer[0 .. count]; +--- + +$(P +Accordingly, a slice to the 10 objects that are returned by $(C makeObjects()) can be constructed by the following code: +) + +--- + Struct[] slice = ptr[0 .. 10]; +--- + +$(P +After that definition, $(C slice) is ready to be used safely in the program just like any other slice: +) + +--- + writeln(slice[1]); // prints the second element +--- + +$(H5 $(IX void*) $(C void*) can point at any type) + +$(P +Although it is almost never needed in D, C's special pointer type $(C void*) is available in D as well. $(C void*) can point at any type: +) + +--- + int number = 42; + double otherNumber = 1.25; + void * canPointAtAnything; + + canPointAtAnything = &number; + canPointAtAnything = &otherNumber; +--- + +$(P +The $(C void*) above is able to point at variables of two different types: $(C int) and $(C double). +) + +$(P +$(C void*) pointers are limited in functionality. As a consequence of their flexibility, they cannot provide access to the pointee. When the actual type is unknown, its size is not known either: +) + +--- + *canPointAtAnything = 43; $(DERLEME_HATASI) +--- + +$(P +Instead, its value must first be converted to a pointer of the correct type: +) + +--- + int number = 42; // (1) + void * canPointAtAnything = &number; // (2) + + // ... + + int * intPointer = cast(int*)canPointAtAnything; // (3) + *intPointer = 43; // (4) +--- + +$(OL +$(LI The actual variable) +$(LI Storing the address of the variable in a $(C void*)) +$(LI Assigning that address to a pointer of the correct type) +$(LI Modifying the variable through the new pointer) +) + +$(P +It is possible to increment or decrement values of $(C void*) pointers, in which case their values are modified as if they are pointers of 1-byte types like $(C ubyte): +) + +--- + ++canPointAtAnything; // incremented by 1 +--- + +$(P +$(C void*) is sometimes needed when interacting with libraries that are written in C. Since C does not have higher level features like interfaces, classes, templates, etc. C libraries must rely on the $(C void*) type. +) + +$(H5 Using pointers in logical expressions) + +$(P +Pointers can automatically be converted to $(C bool). Pointers that have the value $(C null) produce $(C false) and the others produce $(C true). In other words, pointers that do not point at any variable are $(C false). +) + +$(P +Let's consider a function that prints objects to the standard output. Let's design this function so that it also provides the number of bytes that it has just output. However, let's have it produce this information only when specifically requested. +) + +$(P +It is possible to make this behavior optional by checking whether the value of a pointer is $(C null) or not: +) + +--- +void print(Crayon crayon, size_t * numberOfBytes) { + immutable info = format("Crayon: %s", crayon); + writeln(info); + + $(HILITE if (numberOfBytes)) { + *numberOfBytes = info.length; + } +} +--- + +$(P +When the caller does not need this special information, they can pass $(C null) as the argument: +) + +--- + print(Crayon(Color.yellow, 7), $(HILITE null)); +--- + +$(P +When the number of bytes is indeed important, then a non-$(C null) pointer value must be passed: +) + +--- + size_t numberOfBytes; + print(Crayon(Color.blue, 8), $(HILITE &numberOfBytes)); + writefln("%s bytes written to the output", numberOfBytes); +--- + +$(P +Note that this is just an example. Otherwise, it would be better for a function like $(C print()) to return the number of bytes unconditionally: +) + +--- +size_t print(Crayon crayon) { + immutable info = format("Crayon: %s", crayon); + writeln(info); + + return info.length; +} +--- + +$(H5 $(IX new) $(C new) returns a pointer for some types) + +$(P +$(C new), which we have been using only for constructing class objects can be used with other types as well: structs, arrays, and fundamental types. The variables that are constructed by $(C new) are called dynamic variables. +) + +$(P +$(C new) first allocates space from the memory for the variable and then constructs the variable in that space. The variable itself does not have a symbolic name in the compiled program; it would be accessed through the reference that is returned by $(C new). +) + +$(P +The reference that $(C new) returns is a different kind depending on the type of the variable: +) + +$(UL + +$(LI For class objects, it is a $(I class variable): + +--- + Class classVariable = new Class; +--- + +) + +$(LI For struct objects and variables of fundamental types, it is a $(I pointer): + +--- + Struct $(HILITE *) structPointer = new Struct; + int $(HILITE *) intPointer = new int; +--- + +) + +$(LI For arrays, it is a $(I slice): + +--- + int[] slice = new int[100]; +--- + +) + +) + +$(P +This distinction is usually not obvious when the type is not spelled-out on the left-hand side: +) + +--- + auto classVariable = new Class; + auto structPointer = new Struct; + auto intPointer = new int; + auto slice = new int[100]; +--- + +$(P +The following program prints the return type of $(C new) for different kinds of variables: +) + +--- +import std.stdio; + +struct Struct { +} + +class Class { +} + +void main() { + writeln(typeof(new int ).stringof); + writeln(typeof(new int[5]).stringof); + writeln(typeof(new Struct).stringof); + writeln(typeof(new Class ).stringof); +} +--- + +$(P +$(C new) returns pointers for structs and fundamental types: +) + +$(SHELL_SMALL +int* +int[] +Struct* +Class +) + +$(P +When $(C new) is used for constructing a dynamic variable of a $(LINK2 /ders/d.en/value_vs_reference.html, value type), then the lifetime of that variable is extended as long as there is still a reference (e.g. a pointer) to that object in the program. (This is the default situation for reference types.) +) + +$(H5 $(IX .ptr, array element) $(IX pointer, array element) The $(C .ptr) property of arrays) + +$(P +The $(C .ptr) property of arrays and slices is the address of the first element. The type of this value is a pointer to the type of the elements: +) + +--- + int[] numbers = [ 7, 12 ]; + + int * addressOfFirstElement = numbers$(HILITE .ptr); + writeln("First element: ", *addressOfFirstElement); +--- + +$(P +This property is useful especially when interacting with C libraries. Some C functions take the address of the first of a number of consecutive elements in memory. +) + +$(P +Remembering that strings are also arrays, the $(C .ptr) property can be used with strings as well. However, note that the first element of a string need not be the first $(I letter) of the string; rather, the first Unicode code unit of that letter. As an example, the letter é is stored as two code units in a $(C char) string. +) + +$(P +When accessed through the $(C .ptr) property, the code units of strings can be accessed individually. We will see this in the examples section below. +) + +$(H5 $(IX in, operator) The $(C in) operator of associative arrays) + +$(P +Actually, we have used pointers earlier in $(LINK2 /ders/d.en/aa.html, the Associative Arrays chapter). In that chapter, I had intentionally not mentioned the exact type of the $(C in) operator and had used it only in logical expressions: +) + +--- + if ("purple" in colorCodes) { + // there is an element for key "purple" + + } else { + // no element for key "purple" + } +--- + +$(P +In fact, the $(C in) operator returns the address of the element if there is an element for the specified key; otherwise, it returns $(C null). The $(C if) statement above actually relies on the automatic conversion of the pointer value to $(C bool). +) + +$(P +When the return value of $(C in) is stored in a pointer, the element can be accessed efficiently through that pointer: +) + +--- +import std.stdio; + +void main() { + string[int] numbers = + [ 0 : "zero", 1 : "one", 2 : "two", 3 : "three" ]; + + int number = 2; + auto $(HILITE element) = number in numbers; // (1) + + if ($(HILITE element)) { // (2) + writefln("I know: %s.", $(HILITE *element)); // (3) + + } else { + writefln("I don't know the spelling of %s.", number); + } +} +--- + +$(P +The pointer variable $(C element) is initialized by the value of the $(C in) operator (1) and its value is used in a logical expression (2). The value of the element is accessed through that pointer (3) only if the pointer is not $(C null). +) + +$(P +The actual type of $(C element) above is a pointer to the same type of the elements (i.e. values) of the associative array. Since the elements of $(C numbers) above are of type $(C string), $(C in) returns a $(C string*). Accordingly, the type could have been spelled out explicitly: +) + +--- + $(HILITE string *) element = number in numbers; +--- + +$(H5 When to use pointers) + +$(P +Pointers are rare in D. As we have seen in $(LINK2 /ders/d.en/input.html, the Reading from the Standard Input chapter), $(C readf) can in fact be used without explicit pointers. +) + +$(H6 When required by libraries) + +$(P +Pointers can appear on C and C++ library bindings. For example, the following function from the GtkD library takes a pointer: +) + +--- + GdkGeometry geometry; + // ... set the members of 'geometry' ... + + window.setGeometryHints(/* ... */, $(HILITE &)geometry, /* ... */); +--- + +$(H6 When referencing variables of value types) + +$(P +Pointers can be used for referring to local variables. The following program counts the outcomes of flipping a coin. It takes advantage of a pointer when referring to one of two local variables: +) + +--- +import std.stdio; +import std.random; + +void main() { + size_t headsCount = 0; + size_t tailsCount = 0; + + foreach (i; 0 .. 100) { + size_t * theCounter = (uniform(0, 2) == 1) + ? &headsCount + : &tailsCount; + ++(*theCounter); + } + + writefln("heads: %s tails: %s", headsCount, tailsCount); +} +--- + +$(P +Obviously, there are other ways of achieving the same goal. For example, using the ternary operator in a different way: +) + +--- + uniform(0, 2) ? ++headsCount : ++tailsCount; +--- + +$(P +By using an $(C if) statement: +) + +--- + if (uniform(0, 2)) { + ++headsCount; + + } else { + ++tailsCount; + } +--- + +$(H6 As member variables of data structures) + +$(P +Pointers are essential when implementing many data structures. +) + +$(P +Unlike the elements of an array being next to each other in memory, elements of many other data structures are apart. Such data structures are based on the concept of their elements $(I pointing at) other elements. +) + +$(P +For example, each node of a linked list $(I points at) the next node. Similarly, each node of a binary tree $(I points at) the left and right branches under that node. Pointers are encountered in most other data structures as well. +) + +$(P +Although it is possible to take advantage of D's reference types, pointers may be more natural and efficient in some cases. +) + +$(P +We will see examples of pointer members below. +) + +$(H6 When accessing memory directly) + +$(P +Being low-level microprocessor features, pointers provide byte-level access to memory locations. Note that such locations must still belong to valid variables. It is undefined behavior to attempt to access a random memory location. +) + +$(H5 Examples) + +$(H6 A simple linked list) + +$(P +The elements of linked lists are stored in $(I nodes). The concept of a linked list is based on each node pointing at the node that comes after it. The last node has no other node to point at, so it is set to $(C null): +) + +$(MONO + first node next node last node + ┌─────────┬───┐ ┌─────────┬───┐ ┌─────────┬──────┐ + │ element │ o────▶ │ element │ o────▶ ... │ element │ null │ + └─────────┴───┘ └─────────┴───┘ └─────────┴──────┘ +) + +$(P +The figure above may be misleading: In reality, the nodes are not side-by-side in memory. Each node does point to the next node but the next node may be at a completely different location. +) + +$(P +The following $(C struct) can be used for representing the nodes of such a linked list of $(C int)s: +) + +--- +struct Node { + int element; + Node * next; + + // ... +} +--- + +$(P $(I $(B Note:) Because it contains a reference to the same type as itself, $(C Node) is a $(IX recursive type) recursive type.) +) + +$(P +The entire list can be represented by a single pointer that points at the first node, which is commonly called $(I the head): +) + +--- +struct List { + Node * head; + + // ... +} +--- + +$(P +To keep the example short, let's define just one function that adds an element to the head of the list: +) + +--- +struct List { + Node * head; + + void insertAtHead(int element) { + head = new Node(element, head); + } + + // ... +} +--- + +$(P +The line inside $(C insertAtHead()) keeps the nodes $(I linked) by adding a new node to the head of the list. (A function that adds to the end of the list would be more natural and more useful. We will see that function later in one of the problems.) +) + +$(P +The right-hand side expression of that line constructs a $(C Node) object. When this new object is constructed, its $(C next) member is initialized by the current head of the list. When the $(C head) member of the list is assigned to this newly linked node, the new element ends up being the first element. +) + +$(P +The following program tests these two structs: +) + +--- +import std.stdio; +import std.conv; +import std.string; + +struct Node { + int element; + Node * next; + + string toString() const { + string result = to!string(element); + + if (next) { + result ~= " -> " ~ to!string(*next); + } + + return result; + } +} + +struct List { + Node * head; + + void insertAtHead(int element) { + head = new Node(element, head); + } + + string toString() const { + return format("(%s)", head ? to!string(*head) : ""); + } +} + +void main() { + List numbers; + + writeln("before: ", numbers); + + foreach (number; 0 .. 10) { + numbers.insertAtHead(number); + } + + writeln("after : ", numbers); +} +--- + +$(P +The output: +) + +$(SHELL_SMALL +before: () +after : (9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0) +) + +$(H6 $(IX memory access, pointer) Observing the contents of memory by $(C ubyte*)) + +$(P +The data stored at each memory address is a byte. Every variable is constructed on a piece of memory that consists of as many bytes as the size of the type of that variable. +) + +$(P +A suitable pointer type to observe the content of a memory location is $(C ubyte*). Once the address of a variable is assigned to a $(C ubyte) pointer, then all of the bytes of that variable can be observed by incrementing the pointer. +) + +$(P +Let's consider the following integer that is initialized by the hexadecimal notation so that it will be easy to understand how its bytes are placed in memory: +) + +--- + int variable = 0x01_02_03_04; +--- + +$(P +A pointer that points at that variable can be defined like this: +) + +--- + int * address = &variable; +--- + +$(P +The value of that pointer can be assigned to a $(C ubyte) pointer by the $(C cast) operator: +) + +--- + ubyte * bytePointer = cast(ubyte*)address; +--- + +$(P +Such a pointer allows accessing the four bytes of the $(C int) variable individually: +) + +--- + writeln(bytePointer[0]); + writeln(bytePointer[1]); + writeln(bytePointer[2]); + writeln(bytePointer[3]); +--- + +$(P +If your microprocessor is $(I little-endian) like mine, you should see the bytes of the value $(C 0x01_02_03_04) in reverse: +) + +$(SHELL_SMALL +4 +3 +2 +1 +) + +$(P +Let's use that idea in a function that will be useful when observing the bytes of all types of variables: +) + +--- +$(CODE_NAME printBytes)import std.stdio; + +void printBytes(T)(ref T variable) { + const ubyte * begin = cast(ubyte*)&variable; // (1) + + writefln("type : %s", T.stringof); + writefln("value : %s", variable); + writefln("address: %s", begin); // (2) + writef ("bytes : "); + + writefln("%(%02x %)", begin[0 .. T.sizeof]); // (3) + + writeln(); +} +--- + +$(OL +$(LI Assigning the address of the variable to a $(C ubyte) pointer.) +$(LI Printing the value of the pointer.) +$(LI Obtaining the size of the type by $(C .sizeof) and printing the bytes of the variable. (Note how a slice is produced from the $(C begin) pointer and then that slice is printed directly by $(C writefln()).)) +) + +$(P +Another way of printing the bytes would be to apply the $(C *) operator individually: +) + +--- + foreach (bytePointer; begin .. begin + T.sizeof) { + writef("%02x ", *bytePointer); + } +--- + +$(P +The value of $(C bytePointer) would change from $(C begin) to $(C begin + T.sizeof) to visit all of the bytes of the variable. Note that the value $(C begin + T.sizeof) is outside of the range and is never accessed. +) + +$(P +The following program calls $(C printBytes()) with various types of variables: +) + +--- +$(CODE_XREF printBytes)struct Struct { + int first; + int second; +} + +class Class { + int i; + int j; + int k; + + this(int i, int j, int k) { + this.i = i; + this.j = j; + this.k = k; + } +} + +void main() { + int integerVariable = 0x11223344; + printBytes(integerVariable); + + double doubleVariable = double.nan; + printBytes(doubleVariable); + + string slice = "a bright and charming façade"; + printBytes(slice); + + int[3] array = [ 1, 2, 3 ]; + printBytes(array); + + auto structObject = Struct(0xaa, 0xbb); + printBytes(structObject); + + auto classVariable = new Class(1, 2, 3); + printBytes(classVariable); +} +--- + +$(P +The output of the program is informative: +) + +$(SHELL_SMALL +type : int +value : 287454020 +address: 7FFF19A83FB0 +bytes : 44 33 22 11 $(SHELL_NOTE (1)) + +type : double +value : nan +address: 7FFF19A83FB8 +bytes : 00 00 00 00 00 00 f8 7f $(SHELL_NOTE (2)) + +type : string +value : a bright and charming façade +address: 7FFF19A83FC0 +bytes : 1d 00 00 00 00 00 00 00 e0 68 48 00 00 00 00 00 + $(SHELL_NOTE (3)) +type : int[3LU] +value : [1, 2, 3] +address: 7FFF19A83FD0 +bytes : 01 00 00 00 02 00 00 00 03 00 00 00 $(SHELL_NOTE (1)) + +type : Struct +value : Struct(170, 187) +address: 7FFF19A83FE8 +bytes : aa 00 00 00 bb 00 00 00 $(SHELL_NOTE (1)) + +type : Class +value : deneme.Class +address: 7FFF19A83FF0 +bytes : 80 df 79 d5 97 7f 00 00 $(SHELL_NOTE (4)) +) + +$(P $(B Observations:) +) + +$(OL + +$(LI Although in reverse order on little-endian systems, the bytes of some of the types are as one would expect: The bytes are laid out in memory side by side for $(C int)s, fixed-length arrays ($(C int[3])), and struct objects.) + +$(LI Considering that the bytes of the special value of $(C double.nan) are also in reverse order in memory, we can see that it is represented by the special bit pattern 0x7ff8000000000000.) + +$(LI $(C string) is reported to be consisting of 16 bytes but it is impossible to fit the letters $(STRING "a bright and charming façade") into so few bytes. This is due to the fact that behind the scenes $(C string) is actually implemented as a struct. Prefixing its name by $(C __) to stress the fact that it is an internal type used by the compiler, that struct is similar to the following one: + +--- +struct __string { + size_t length; + char * ptr; // the actual characters +} +--- + +$(P +The evidence of this fact is hidden in the bytes that are printed for $(C string) above. Note that because ç is made up of two UTF-8 code units, the 28 letters of the string $(STRING "a bright and charming façade") consists of a total of 29 bytes. The value 0x000000000000001d, the first 8 of the bytes of the string in the output above, is also 29. This is a strong indicator that strings are indeed laid out in memory as in the struct above. +) + +) + +$(LI Similarly, it is not possible to fit the three $(C int) members of the class object in 8 bytes. The output above hints at the possibility that behind the scenes a class variable is implemented as a single pointer that points at the actual class object: + +--- +struct __Class_VariableType { + __Class_ActualObjecType * object; +} +--- + +) + +) + +$(P +Let's now consider a more flexible function. Instead of printing the bytes of a variable, let's define a function that prints specified number of bytes at a specified location: +) + +--- +$(CODE_NAME printMemory)import std.stdio; +import std.ascii; + +void printMemory(T)(T * location, size_t length) { + const ubyte * begin = cast(ubyte*)location; + + foreach (address; begin .. begin + length) { + char c = (isPrintable(*address) ? *address : '.'); + + writefln("%s: %02x %s", address, *address, c); + } +} +--- + +$(P +Since some of the UTF-8 code units may correspond to control characters of the terminal and disrupt its output, we print only the printable characters by first checking them individually by $(C std.ascii.isPrintable()). The non-printable characters are printed as a dot. +) + +$(P +We can use that function to print the UTF-8 code units of a $(C string) through its $(C .ptr) property: +) + +--- +$(CODE_XREF printMemory)import std.stdio; + +void main() { + string s = "a bright and charming façade"; + printMemory(s.ptr, s.length); +} +--- + +$(P +As seen in the output, the letter ç consists of two bytes: +) + +$(SHELL_SMALL +47B4F0: 61 a +47B4F1: 20 +47B4F2: 62 b +47B4F3: 72 r +47B4F4: 69 i +47B4F5: 67 g +47B4F6: 68 h +47B4F7: 74 t +47B4F8: 20 +47B4F9: 61 a +47B4FA: 6e n +47B4FB: 64 d +47B4FC: 20 +47B4FD: 63 c +47B4FE: 68 h +47B4FF: 61 a +47B500: 72 r +47B501: 6d m +47B502: 69 i +47B503: 6e n +47B504: 67 g +47B505: 20 +47B506: 66 f +47B507: 61 a +47B508: c3 . +47B509: a7 . +47B50A: 61 a +47B50B: 64 d +47B50C: 65 e +) + +$(PROBLEM_COK + +$(PROBLEM +Fix the following function so that the values of the arguments that are passed to it are swapped. For this exercise, do not specify the parameters as $(C ref) but take them as pointers: + +--- +void swap(int lhs, int rhs) { + int temp = lhs; + lhs = rhs; + rhs = temp; +} + +void main() { + int i = 1; + int j = 2; + + swap(i, j); + + // Their values should be swapped + assert(i == 2); + assert(j == 1); +} +--- + +$(P +When you start the program you will notice that the $(C assert) checks currently fail. +) + +) + +$(PROBLEM +Convert the linked list that we have defined above to a template so that it can be used for storing elements of any type. +) + +$(PROBLEM +It is more natural to add elements to the end of a linked list. Modify $(C List) so that it is possible to add elements to the end as well. + +$(P +For this exercise, an additional pointer member variable that points at the last element will be useful. +) + +) + +) + +Macros: + SUBTITLE=Pointers + + DESCRIPTION=Pointers, variables that point at other variables to provide access to them. + + KEYWORDS=d programming language tutorial book pointers diff --git a/target/pragma.d b/target/pragma.d new file mode 100644 index 0000000..1955d4a --- /dev/null +++ b/target/pragma.d @@ -0,0 +1,235 @@ +Ddoc + +$(DERS_BOLUMU $(IX pragma) Pragmas) + +$(P +Pragmas are a way of interacting with the compiler. They can be for providing special information to the compiler as well as getting information from it. Although they are useful in non-templated code as well, $(C pragma(msg)) can be helpful when debugging templates. +) + +$(P +Every compiler vendor is free to introduce their special $(C pragma) directives in addition to the following mandatory ones: +) + +$(H5 $(C pragma(msg))) + +$(P +Prints a message to $(C stderr) during compilation. No message is printed during the execution of the compiled program. +) + +$(P +For example, the following $(C pragma(msg)) is being used for exposing the types of template parameters, presumably during debugging: +) + +--- +import std.string; + +void func(A, B)(A a, B b) { + pragma($(HILITE msg), format("Called with types '%s' and '%s'", + A.stringof, B.stringof)); + // ... +} + +void main() { + func(42, 1.5); + func("hello", 'a'); +} +--- + +$(SHELL +Called with types 'int' and 'double' +Called with types 'string' and 'char' +) + +$(H5 $(C pragma(lib))) + +$(P +Instructs the compiler to link the program with a particular library. This is the easiest way of linking the program with a library that is already installed on the system. +) + +$(P +For example, the following program would be linked with the $(C curl) library without needing to mention the library on the command line: +) + +--- +import std.stdio; +import std.net.curl; + +pragma($(HILITE lib), "curl"); + +void main() { + // Get this chapter + writeln(get("ddili.org/ders/d.en/pragma.html")); +} +--- + +$(H5 $(IX inline) $(IX function inlining) $(IX optimization, compiler) $(C pragma(inline))) + +$(P +Specifies whether a function should be $(I inlined) or not. +) + +$(P +Every function call has some performance cost. Function calls involve passing arguments to the function, returning its return value to the caller, and handling some bookkeeping information to remember where the function was called from so that the execution can continue after the function returns. +) + +$(P +This cost is usually insignificant compared to the cost of actual work that the caller and the callee perform. However, in some cases just the act of calling a certain function can have a measurable effect on the program's performance. This can happen especially when the function body is relatively fast and when it is called from a short loop that repeats many times. +) + +$(P +The following program calls a small function from a loop and increments a counter when the returned value satisfies a condition: +) + +--- +import std.stdio; +import std.datetime; + +// A function with a fast body: +ubyte compute(ubyte i) { + return cast(ubyte)(i * 42); +} + +void main() { + size_t counter = 0; + + StopWatch sw; + sw.start(); + + // A short loop that repeats many times: + foreach (i; 0 .. 100_000_000) { + const number = cast(ubyte)i; + + if ($(HILITE compute(number)) == number) { + ++counter; + } + } + + sw.stop(); + + writefln("%s milliseconds", sw.peek.msecs); +} +--- + +$(P +$(IX StopWatch, std.datetime) The code takes advantage of $(C std.datetime.StopWatch) to measure the time it takes executing the entire loop: +) + +$(SHELL +$(HILITE 674) milliseconds +) + +$(P +$(IX -inline, compiler switch) The $(C -inline) compiler switch instructs the compiler to perform a compiler optimization called $(I function inlining): +) + +$(SHELL +$ dmd deneme.d -w $(HILITE -inline) +) + +$(P +When a function is inlined, its body is injected into code right where it is called from; no actual function call happens. The following is the equivalent code that the compiler would compile after inlining: +) + +--- + // An equivalent of the loop when compute() is inlined: + foreach (i; 0 .. 100_000_000) { + const number = cast(ubyte)i; + + const result = $(HILITE cast(ubyte)(number * 42)); + if (result == number) { + ++counter; + } + } +--- + +$(P +On the platform that I tested that program, eliminating the function call reduced the execution time by about 40%: +) + +$(SHELL +$(HILITE 407) milliseconds +) + +$(P +Although function inlining looks like a big gain, it cannot be applied for every function call because otherwise inlined bodies of functions would make code too large to fit in the CPU's $(I instruction cache). Unfortunately, this can make the code even slower. For that reason, the decision of which function calls to inline is usually left to the compiler. +) + +$(P +However, there may be cases where it is beneficial to help the compiler with this decision. The $(C inline) pragma instructs the compiler in its inlining decisions: +) + +$(UL + +$(LI $(C pragma(inline, false)): Instructs the compiler to never inline certain functions even when the $(C -inline) compiler switch is specified.) + +$(LI $(C pragma(inline, true)): Instructs the compiler to definitely inline certain functions when the $(C -inline) compiler switch is specified. This causes a compilation error if the compiler cannot inline such a function. (The exact behavior of this pragma may be different on your compiler.)) + +$(LI $(C pragma(inline)): Sets the inlining behavior back to the setting on the compiler command line: whether $(C -inline) is specified or not.) + +) + +$(P +These pragmas can affect the function that they appear in, as well as they can be used with a scope or colon to affect more than one function: +) + +--- +pragma(inline, false) $(HILITE {) + // Functions defined in this scope should not be inlined + // ... +$(HILITE }) + +int foo() { + pragma(inline, true); // This function should be inlined + // ... +} + +pragma(inline, true)$(HILITE :) +// Functions defined in this section should be inlined +// ... + +pragma(inline)$(HILITE :) +// Functions defined in this section should be inlined or not +// depending on the -inline compiler switch +// ... +--- + +$(P +$(IX -O, compiler switch) Another compiler switch that can make programs run faster is $(C -O), which instructs the compiler to perform more optimization algorithms. However, faster program speeds come at the expense of slower compilation speeds because these algorithms take significant amounts of time. +) + +$(H5 $(IX startaddress) $(C pragma(startaddress))) + +$(P +Specifies the start address of the program. Since the start address is normally assigned by the D runtime environment it is very unlikely that you will ever use this pragma. +) + +$(H5 $(IX mangle, pragma) $(IX name mangling) $(C pragma(mangle))) + +$(P +Specifies that a symbol should be $(I name mangled) differently from the default name mangling method. Name mangling is about how the linker identifies functions and their callers. This pragma is useful when D code needs to call a library function that happens to be a D keyword. +) + +$(P +For example, if a C library had a function named $(C body), because $(C body) happens to be a keyword in D, the only way of calling it from D would be through a different name. However, that different name must still be mangled as the actual function name in the library for the linker to be able to identify it: +) + +--- +/* If a C library had a function named 'body', it could only + * be called from D through a name like 'c_body', mangled as + * the actual function name: */ +pragma($(HILITE mangle), "body") +extern(C) string c_body(string); + +void main() { + /* D code calls the function as c_body() but the linker + * would find it by its correct C library name 'body': */ + auto s = $(HILITE c_body)("hello"); +} +--- + +Macros: + SUBTITLE=Pragmas + + DESCRIPTION=Introduction of pragmas, which are a way of interacting with the compiler. + + KEYWORDS=d programming language tutorial book pragma inline diff --git a/target/preface.d b/target/preface.d new file mode 100644 index 0000000..6fffa49 --- /dev/null +++ b/target/preface.d @@ -0,0 +1,70 @@ +Ddoc + +$(DIV_CLASS preface, + +$(DERS_BOLUMU_CLASS preface, Preface) + +$(P +D is a multi-paradigm system programming language that combines a wide range of powerful programming concepts from the lowest to the highest levels. It emphasizes memory safety, program correctness, and pragmatism. +) + +$(P +The main aim of this book is to teach D to readers who are new to computer programming. Although having experience in other programming languages is certainly helpful, this book starts from the basics. +) + +$(P +In order for this book to be useful, you will need an environment to write, compile, and run your D programs. This $(I development environment) must include at least a D compiler and a text editor. We will learn how to install a compiler and how to compile programs in the next chapter. +) + +$(P +Each chapter is based on the contents of the previous ones, introducing as few new concepts as possible. I recommend that you read the book in linear fashion, without skipping chapters. Although this book was written with beginners in mind, it covers almost all features of D. More experienced programmers can use the book as a D language reference by starting from the index section. +) + +$(P +Some chapters include exercises and their solutions so that you can write small programs and compare your methods to mine. +) + +$(P +Computer programming is a satisfying craft that involves continuously discovering and learning new tools, techniques, and concepts. I am sure you will enjoy programming in D at least as much as I do. Learning to program is easier and more fun when shared with others. Take advantage of $(LINK2 http://forum.dlang.org/group/digitalmars.D.learn/, the D.learn newsgroup) to follow discussions and to ask and answer questions. +) + +$(P +This book is available in other languages as well, including $(LINK2 http://ddili.org/ders/d/, Turkish) and $(LINK2 http://dlang.unix.cat/programmer-en-d/, French). +) + +$(H5_FRONTMATTER Acknowledgments) + +$(P +I am indebted to the following people who have been instrumental during the evolution of this book: +) + +$(P +Mert Ataol, Zafer Çelenk, Salih Dinçer, Can Alpay Çiftçi, Faruk Erdem Öncel, Muhammet Aydın (aka Mengü Kağan), Ergin Güney, Jordi Sayol, David Herberth, Andre Tampubolon, Gour-Gadadhara Dasa, Raphaël Jakse, Andrej Mitrović, Johannes Pfau, Jerome Sniatecki, Jason Adams, Ali H. Çalışkan, Paul Jurczak, Brian Rogoff, Михаил Страшун (Mihails Strasuns), Joseph Rushton Wakeling, Tove, Hugo Florentino, Satya Pothamsetti, Luís Marques, Christoph Wendler, Daniel Nielsen, Ketmar Dark, Pavel Lukin, Jonas Fiala, Norman Hardy, Rich Morin, Douglas Foster, Paul Robinson, Sean Garratt, Stéphane Goujet, Shammah Chancellor, Steven Schveighoffer, Robbin Carlson, Bubnenkov Dmitry Ivanovich, Bastiaan Veelo, Olivier Pisano, Dave Yost, Tomasz Miazek-Mioduszewski, Gerard Vreeswijk, Justin Whear, Gerald Jansen, Sylvain Gault, Shriramana Sharma, Jay Norwood, Henri Menke, Chen Lejia, Vladimir Panteleev, Martin Tschierschke, ag0aep6g, and Andrew Edwards. +) + +$(P +Thanks especially to Luís Marques who, through his hard work, improved every chapter of the book. If you find any part of this book useful, it is likely due to his diligent editing. +) + +$(P +Thanks to Luís Marques, Steven Schveighoffer, Andrej Mitrović, Robbin Carlson, Ergin Güney, and Andrew Edwards for their suggestions that elevated this book from my Inglish to English. +) + +$(P +I am grateful to the entire D community for keeping my enthusiasm and motivation high. D has an amazing community of tireless individuals like bearophile and Kenji Hara. +) + +$(P +Ebru, Damla, and Derin, thank you for being so patient and supportive while I was lost writing these chapters. +$(BR) +$(BR) +Ali Çehreli$(BR) +Mountain View, $(I May 2017) +) + +) + +Macros: + SUBTITLE = Preface + DESCRIPTION= + KEYWORDS= diff --git a/target/property.d b/target/property.d new file mode 100644 index 0000000..b4bda24 --- /dev/null +++ b/target/property.d @@ -0,0 +1,351 @@ +Ddoc + +$(DERS_BOLUMU $(IX property) Properties) + +$(P +Properties allow using member functions like member variables. +) + +$(P +We are familiar with this feature from slices. The $(C length) property of a slice returns the number of elements of that slice: +) + +--- + int[] slice = [ 7, 8, 9 ]; + assert(slice$(HILITE .length) == 3); +--- + +$(P +Looking merely at that usage, one might think that $(C .length) has been implemented as a member variable: +) + +--- +struct SliceImplementation { + int length; + + // ... +} +--- + +$(P +However, the other functionality of this property proves that it cannot be a member variable: Assigning a new value to the $(C .length) property actually changes the length of the slice, sometimes by adding new elements to the underlying array: +) + +--- + slice$(HILITE .length = 5); // The slice now has 5 elements + assert(slice.length == 5); +--- + +$(P $(I $(B Note:) The $(C .length) property of fixed-length arrays cannot be modified.) +) + +$(P +The assignment to $(C .length) above involves more complicated operations than a simple value change: Determining whether the array has capacity for the new length, allocating more memory if not, and moving the existing elements to the new place; and finally initializing each additional element by $(C .init). +) + +$(P +Evidently, the assignment to $(C .length) operates like a function. +) + +$(P +$(IX @property) Properties are member functions that are used like member variables. They are defined by the $(C @property) attribute. +) + +$(H5 Calling functions without parentheses) + +$(P +$(IX ()) As has been mentioned in the previous chapter, when there is no argument to pass, functions can be called without parentheses: +) + +--- + writeln(); + writeln; // Same as the previous line +--- + +$(P +This feature is closely related to properties because properties are used almost always without parentheses. +) + +$(H5 Property functions that return values) + +$(P +As a simple example, let's consider a rectangle struct that consists of two members: +) + +--- +struct Rectangle { + double width; + double height; +} +--- + +$(P +Let's assume that a third property of this type becomes a requirement, which should provide the area of the rectangle: +) + +--- + auto garden = Rectangle(10, 20); + writeln(garden$(HILITE .area)); +--- + +$(P +One way of achieving that requirement is to define a third member: +) + +--- +struct Rectangle { + double width; + double height; + double area; +} +--- + +$(P +A flaw in that design is that the object may easily become inconsistent: Although rectangles must always have the invariant of "width * height == area", this consistency may be broken if the members are allowed to be modified freely and independently. +) + +$(P +As an extreme example, objects may even begin their lives in inconsistent states: +) + +--- + // Inconsistent object: The area is not 10 * 20 == 200. + auto garden = Rectangle(10, 20, $(HILITE 1111)); +--- + +$(P +A better way would be to represent the concept of area as a property. Instead of defining an additional member, the value of that member is calculated by a function named $(C area), the same as the concept that it represents: +) + +--- +struct Rectangle { + double width; + double height; + + double area() const $(HILITE @property) { + return width * height; + } +} +--- + +$(P $(I $(B Note:) As you would remember from $(LINK2 /ders/d.en/const_member_functions.html, the $(CH4 const ref) Parameters and $(CH4 const) Member Functions chapter), the $(C const) specifier on the function declaration ensures that the object is not modified by this function.) +) + +$(P +That property function enables the struct to be used as if it has a third member variable: +) + +--- + auto garden = Rectangle(10, 20); + writeln("The area of the garden: ", garden$(HILITE .area)); +--- + +$(P +As the value of the $(C area) property is calculated by multiplying the width and the height of the rectangle, this time it would always be consistent: +) + +$(SHELL +The area of the garden: 200 +) + +$(H5 Property functions that are used in assignment) + +$(P +Similar to the $(C length) property of slices, the properties of user-defined types can be used in assignment operations as well: +) + +--- + garden.area = 50; +--- + +$(P +For that assignment to actually change the area of the rectangle, the two members of the struct must be modified accordingly. To enable this functionality, we can assume that the rectangle is $(I flexible) so that to maintain the invariant of "width * height == area", the sides of the rectangle can be changed. +) + +$(P +The function that enables such an assignment syntax is also named as $(C area) and is also marked by $(C @property). The value that is used on the right-hand side of the assignment becomes the only parameter of this function. +) + +$(P +The following additional definition of $(C area()) enables using that property in assignment operations and effectively modifying the area of $(C Rectangle) objects: +) + +--- +import std.stdio; +import std.math; + +struct Rectangle { + double width; + double height; + + double area() const @property { + return width * height; + } + + $(HILITE void area(double newArea) @property) { + auto scale = sqrt(newArea / area); + + width *= scale; + height *= scale; + } +} + +void main() { + auto garden = Rectangle(10, 20); + writeln("The area of the garden: ", garden.area); + + $(HILITE garden.area = 50); + + writefln("New state: %s x %s = %s", + garden.width, garden.height, garden.area); +} +--- + +$(P +The new function takes advantage of the $(C sqrt) function from the $(C std.math) module, which returns the square root of the specified value. When both of the width and the height of the rectangle are scaled by the square root of the ratio, then the area would equal the desired value. +) + +$(P +As a result, assigning the quarter of its current value to $(C area) ends up halving both sides of the rectangle: +) + +$(SHELL +The area of the garden: 200 +New state: 5 x 10 = 50 +) + +$(H5 Properties are not absolutely necessary) + +$(P +We have seen above how $(C Rectangle) can be used as if it has a third member variable. However, regular member functions could also be used instead of properties: +) + +--- +import std.stdio; +import std.math; + +struct Rectangle { + double width; + double height; + + double $(HILITE area()) const { + return width * height; + } + + void $(HILITE setArea(double newArea)) { + auto scale = sqrt(newArea / area); + + width *= scale; + height *= scale; + } +} + +void main() { + auto garden = Rectangle(10, 20); + writeln("The area of the garden: ", garden$(HILITE .area())); + + garden$(HILITE .setArea(50)); + + writefln("New state: %s x %s = %s", + garden.width, garden.height, garden$(HILITE .area())); +} +--- + +$(P +Further, as we have seen in $(LINK2 /ders/d.en/function_overloading.html, the Function Overloading chapter), these two functions could even have the same names: +) + +--- + double area() const { + // ... + } + + void area(double newArea) { + // ... + } +--- + +$(H5 When to use) + +$(P +It may not be easy to chose between regular member functions and properties. Sometimes regular member functions feel more natural and sometimes properties. +) + +$(P +However, as we have seen in $(LINK2 /ders/d.en/encapsulation.html, the Encapsulation and Protection Attributes chapter), it is important to restrict direct access to member variables. Allowing user code to freely modify member variables always ends up causing issues with code maintenance. For that reason, member variables better be encapsulated either by regular member functions or by property functions. +) + +$(P +Leaving members like $(C width) and $(C height) open to $(C public) access is acceptable only for very simple types. Almost always a better design is to use property functions: +) + +--- +struct Rectangle { +$(HILITE private:) + + double width_; + double height_; + +public: + + double area() const @property { + return width * height; + } + + void area(double newArea) @property { + auto scale = sqrt(newArea / area); + + width_ *= scale; + height_ *= scale; + } + + double $(HILITE width()) const @property { + return width_; + } + + double $(HILITE height()) const @property { + return height_; + } +} +--- + +$(P +Note how the members are made $(C private) so that they can only be accessed by corresponding property functions. +) + +$(P +Also note that to avoid confusing their names with the member functions, the names of the member variables are appended by the $(C _) character. $(I Decorating) the names of member variables is a common practice in object oriented programming. +) + +$(P +That definition of $(C Rectangle) still presents $(C width) and $(C height) as if they are member variable: +) + +--- + auto garden = Rectangle(10, 20); + writefln("width: %s, height: %s", + garden$(HILITE .width), garden$(HILITE .height)); +--- + +$(P +When there is no property function that modifies a member variable, then that member is effectively read-only from the outside: +) + +--- + garden.width = 100; $(DERLEME_HATASI) +--- + +$(P +This is important for controlled modifications of members. The member variables can only be modified by the $(C Rectangle) type itself to ensure the consistency of its objects. +) + +$(P +When it later makes sense that a member variable should be allowed to be modified from the outside, then it is simply a matter of defining another property function for that member. +) + +Macros: + SUBTITLE=Properties + + DESCRIPTION=Enabling using member functions like member variables. + + KEYWORDS=d programming lesson book tutorial property diff --git a/target/ranges.d b/target/ranges.d new file mode 100644 index 0000000..f9023ad --- /dev/null +++ b/target/ranges.d @@ -0,0 +1,2013 @@ +Ddoc + +$(DERS_BOLUMU $(IX range) Ranges) + +$(P +Ranges are an abstraction of element access. This abstraction enables the use of great number of algorithms over great number of container types. Ranges emphasize how container elements are accessed, as opposed to how the containers are implemented. +) + +$(P +Ranges are a very simple concept that is based on whether a type defines certain sets of member functions. We have already seen this concept in the $(LINK2 /ders/d.en/foreach_opapply.html, $(C foreach) with Structs and Classes chapter): any type that provides the member functions $(C empty), $(C front), and $(C popFront()) can be used with the $(C foreach) loop. The set of those three member functions is the requirement of the range type $(C InputRange). +) + +$(P +I will start introducing ranges with $(C InputRange), the simplest of all the range types. The other ranges require more member functions over $(C InputRange). +) + +$(P +Before going further, I would like to provide the definitions of containers and algorithms. +) + +$(P +$(IX container) $(IX data structure) $(B Container (data structure):) Container is a very useful concept that appears in almost every program. Variables are put together for a purpose and are used together as elements of a container. D's containers are its core features arrays and associative arrays, and special container types that are defined in the $(C std.container) module. Every container is implemented as a specific data structure. For example, associative arrays are a $(I hash table) implementation. +) + +$(P +Every data structure stores its elements and provides access to them in ways that are special to that data structure. For example, in the array data structure the elements are stored side by side and accessed by an element index; in the linked list data structure the elements are stored in nodes and are accessed by going through those nodes one by one; in a sorted binary tree data structure, the nodes provide access to the preceding and successive elements through separate branches; etc. +) + +$(P +In this chapter, I will use the terms $(I container) and $(I data structure) interchangeably. +) + +$(P +$(IX algorithm) $(B Algorithm (function):) Processing of data structures for specific purposes in specific ways is called an $(I algorithm). For example, $(I linear search) is an algorithm that searches by iterating over a container from the beginning to the end; $(I binary search) is an algorithm that searches for an element by eliminating half of the candidates at every step; etc. +) + +$(P +In this chapter, I will use the terms $(I algorithm) and $(I function) interchangeably. +) + +$(P +For most of the samples below, I will use $(C int) as the element type and $(C int[]) as the container type. In reality, ranges are more powerful when used with templated containers and algorithms. In fact, most of the containers and algorithms that ranges tie together are all templates. I will leave examples of templated ranges to $(LINK2 /ders/d.en/ranges_more.html, the next chapter). +) + +$(H5 History) + +$(P +A very successful library that abstracts algorithms and data structures from each other is the Standard Template Library (STL), which also appears as a part of the C++ standard library. STL provides this abstraction with the $(I iterator) concept, which is implemented by C++'s templates. +) + +$(P +Although they are a very useful abstraction, iterators do have some weaknesses. D's ranges were designed to overcome these weaknesses. +) + +$(P +Andrei Alexandrescu introduces ranges in his paper $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357, On Iteration) and demonstrates how they can be superior to iterators. +) + +$(H5 Ranges are an integral part of D) + +$(P +D's slices happen to be implementations of the most powerful range $(C RandomAccessRange), and there are many range features in Phobos. It is essential to understand how ranges are used in Phobos. +) + +$(P +Many Phobos algorithms return temporary range objects. For example, $(C filter()), which chooses elements that are greater than 10 in the following code, actually returns a range object, not an array: +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] values = [ 1, 20, 7, 11 ]; + writeln(values.filter!(value => value > 10)); +} +--- + +$(P +$(C writeln) uses that range object lazily and accesses the elements as it needs them: +) + +$(SHELL +[20, 11] +) + +$(P +That output may suggest that $(C filter()) returns an $(C int[]) but this is not the case. We can see this from the fact that the following assignment produces a compilation error: +) + +--- + int[] chosen = values.filter!(value => value > 10); $(DERLEME_HATASI) +--- + +$(P +The error message contains the type of the range object: +) + +$(SHELL +Error: cannot implicitly convert expression (filter(values)) +of type $(HILITE FilterResult!(__lambda2, int[])) to int[] +) + +$(P +$(I $(B Note:) The type may be different in the version of Phobos that you are using.) +) + +$(P +It is possible to convert that temporary object to an actual array, as we will see later in the chapter. +) + +$(H5 Traditional implementations of algorithms) + +$(P +In traditional implementations of algorithms, the algorithms know how the data structures that they operate on are implemented. For example, the following function that prints the elements of a linked list must know that the nodes of the linked list have members named $(C element) and $(C next): +) + +--- +struct Node { + int element; + Node * next; +} + +void print(const(Node) * list) { + for ( ; list; list = list.$(HILITE next)) { + write(' ', list.$(HILITE element)); + } +} +--- + +$(P +Similarly, a function that prints the elements of an array must know that arrays have a $(C length) property and their elements are accessed by the $(C []) operator: +) + +--- +void print(const int[] array) { + for (int i = 0; i != array.$(HILITE length); ++i) { + write(' ', array$(HILITE [i])); + } +} +--- + +$(P +$(I $(B Note:) We know that $(C foreach) is more useful when iterating over arrays. As a demonstration of how traditional algorithms are tied to data structures, let's assume that the use of $(C for) is justified.) +) + +$(P +Having algorithms tied to data structures makes it necessary to write them specially for each type. For example, the functions find(), sort(), swap(), etc. must be written separately for array, linked list, associative array, binary tree, heap, etc. As a result, N algorithms that support M data structures must be written NxM times. (Note: In reality, the count is less than NxM because not every algorithm can be applied to every data structure; for example, associative arrays cannot be sorted.) +) + +$(P +Conversely, because ranges abstract algorithms away from data structures, implementing just N algorithms and M data structures would be sufficient. A newly implemented data structure can work with all of the existing algorithms that support the type of range that the new data structure provides, and a newly implemented algorithm can work with all of the existing data structures that support the range type that the new algorithm requires. +) + +$(H5 Phobos ranges) + +$(P +The ranges in this chapter are different from number ranges that are written in the form $(C begin..end). We have seen how number ranges are used with the $(C foreach) loop and with slices: +) + +--- + foreach (value; $(HILITE 3..7)) { // number range, + // NOT a Phobos range + + int[] slice = array[$(HILITE 5..10)]; // number range, + // NOT a Phobos range +--- + +$(P +When I write $(I range) in this chapter, I mean a Phobos range . +) + +$(P +Ranges form a $(I range hierarchy). At the bottom of this hierarchy is the simplest range $(C InputRange). The other ranges bring more requirements on top of the range on which they are based. The following are all of the ranges with their requirements, sorted from the simplest to the more capable: +) + +$(UL + +$(LI $(C InputRange): requires the $(C empty), $(C front) and $(C popFront()) member functions) + +$(LI $(C ForwardRange): additionally requires the $(C save) member function) + +$(LI $(C BidirectionalRange): additionally requires the $(C back) and $(C popBack()) member functions) + +$(LI $(C RandomAccessRange): additionally requires the $(C []) operator (and another property depending on whether the range is finite or infinite)) + +) + +$(P +This hierarchy can be shown as in the following graph. $(C RandomAccessRange) has finite and infinite versions: +) + +$(MONO + InputRange + ↑ + ForwardRange + ↗ ↖ + BidirectionalRange RandomAccessRange (infinite) + ↑ + RandomAccessRange (finite) +) + +$(P +The graph above is in the style of class hierarchies where the lowest level type is at the top. +) + +$(P +Those ranges are about providing element access. There is one more range, which is about element $(I output): +) + +$(UL +$(LI $(C OutputRange): requires support for the $(C put(range, element)) operation) +) + +$(P +These five range types are sufficient to abstract algorithms from data structures. +) + +$(H6 Iterating by shortening the range) + +$(P +Normally, iterating over the elements of a container does not change the container itself. For example, iterating over a slice with $(C foreach) or $(C for) does not affect the slice: +) + +--- + int[] slice = [ 10, 11, 12 ]; + + for (int i = 0; i != slice.length; ++i) { + write(' ', slice[i]); + } + + assert(slice.length == 3); // ← the length doesn't change +--- + +$(P +Another way of iteration requires a different way of thinking: iteration can be achieved by shortening the range from the beginning. In this method, always the first element is used for element access and the first element is $(I popped) from the beginning in order to get to the next element: +) + +--- + for ( ; slice.length; slice = slice[1..$]) { + write(' ', $(HILITE slice[0])); // ← always the first element + } +--- + +$(P +$(I Iteration) is achieved by removing the first element by the $(C slice = slice[1..$]) expression. The slice above is completely consumed by going through the following stages: +) + +$(MONO +[ 10, 11, 12 ] + [ 11, 12 ] + [ 12 ] + [ ] +) + +$(P +The iteration concept of Phobos ranges is based on this new thinking of shortening the range from the beginning. ($(C BidirectionalRange) and finite $(C RandomAccessRange) types can be shortened from the end as well.) +) + +$(P +Please note that the code above is just to demonstrate this type of iteration; it should not be considered normal to iterate as in that example. +) + +$(P +Since losing elements just to iterate over a range would not be desired in most cases, a surrogate range may be consumed instead. The following code uses a separate slice to preserve the elements of the original one: +) + +--- + int[] slice = [ 10, 11, 12 ]; + int[] surrogate = slice; + + for ( ; surrogate.length; $(HILITE surrogate = surrogate[1..$])) { + write(' ', surrogate[0]); + } + + assert(surrogate.length == 0); // ← surrogate is consumed + assert(slice.length == 3); // ← slice remains the same +--- + +$(P +This is the method employed by most of the Phobos range functions: they return special range objects to be consumed in order to preserve the original containers. +) + +$(H5 $(IX InputRange) $(C InputRange)) + +$(P +This type of range models the type of iteration where elements are accessed in sequence as we have seen in the $(C print()) functions above. Most algorithms only require that elements are iterated in the forward direction without needing to look at elements that have already been iterated over. $(C InputRange) models the standard input streams of programs as well, where elements are removed from the stream as they are read. +) + +$(P +For completeness, here are the three functions that $(C InputRange) requires: +) + +$(UL + +$(LI $(IX empty) $(C empty): specifies whether the range is empty; it must return $(C true) when the range is considered to be empty, and $(C false) otherwise) + +$(LI $(IX front) $(C front): provides access to the element at the beginning of the range) + +$(LI $(IX popFront) $(C popFront()): shortens the range from the beginning by removing the first element) + +) + +$(P +$(I $(B Note:) I write $(C empty) and $(C front) without parentheses, as they can be seen as properties of the range; and $(C popFront()) with parentheses as it is a function with side effects.) +) + +$(P +Here is how $(C print()) can be implemented by using these range functions: +) + +--- +void print(T)(T range) { + for ( ; !range$(HILITE .empty); range$(HILITE .popFront())) { + write(' ', range$(HILITE .front)); + } + + writeln(); +} +--- + +$(P +Please also note that $(C print()) is now a function template to avoid limiting the range type arbitrarily. $(C print()) can now work with any type that provides the three $(C InputRange) functions. +) + +$(H6 $(C InputRange) example) + +$(P +Let's redesign the $(C School) type that we have seen before, this time as an $(C InputRange). We can imagine $(C School) as a $(C Student) container so when designed as a range, it can be seen as a range of $(C Student)s. +) + +$(P +In order to keep the example short, let's disregard some important design aspects. Let's +) + +$(UL + +$(LI implement only the members that are related to this section) + +$(LI design all types as structs) + +$(LI ignore specifiers and qualifiers like $(C private), $(C public), and $(C const)) + +$(LI not take advantage of contract programming and unit testing) + +) + +--- +import std.string; + +struct Student { + string name; + int number; + + string toString() const { + return format("%s(%s)", name, number); + } +} + +struct School { + Student[] students; +} + +void main() { + auto school = School( [ Student("Ebru", 1), + Student("Derya", 2) , + Student("Damla", 3) ] ); +} +--- + +$(P +To make $(C School) be accepted as an $(C InputRange), we must define the three $(C InputRange) member functions. +) + +$(P +For $(C empty) to return $(C true) when the range is empty, we can use the length of the $(C students) array. When the length of that array is 0, the range is considered empty: +) + +--- +struct School { + // ... + + @property bool empty() const { + return students.length == 0; + } +} +--- + +$(P +$(C empty) is defined as $(C @property) to be able to code it without parentheses, as in $(C school.empty). +) + +$(P +For $(C front) to return the first element of the range, we can return the first element of the array: +) + +--- +struct School { + // ... + + @property ref Student front() { + return students[0]; + } +} +--- + +$(P +$(I $(B Note:) I have used the $(C ref) keyword to be able to provide access to the actual element instead of a copy of it. Otherwise the elements would be copied because $(C Student) is a struct.) +) + +$(P +For $(C popFront()) to shorten the range from the beginning, we can shorten the $(C students) array from the beginning: +) + +--- +struct School { + // ... + + void popFront() { + students = students[1 .. $]; + } +} +--- + +$(P +$(I $(B Note:) As I have mentioned above, it is not normal to lose the original elements from the container just to iterate over them. We will address this issue below by introducing a special range type.) +) + +$(P +These three functions are sufficient to make $(C School) to be used as an $(C InputRange). As an example, let's add the following line at the end of $(C main()) above to have our new $(C print()) function template to use $(C school) as a range: +) + +--- + print(school); +--- + +$(P +$(C print()) uses that object as an $(C InputRange) and prints its elements to the output: +) + +$(SHELL + Ebru(1) Derya(2) Damla(3) +) + +$(P +We have achieved our goal of defining a user type as an $(C InputRange); we have sent it to an algorithm that operates on $(C InputRange) types. $(C School) is actually ready to be used with algorithms of Phobos or any other library that work with $(C InputRange) types. We will see examples of this below. +) + +$(H6 $(IX slice, as InputRange) The $(C std.array) module to use slices as ranges) + +$(P +Merely importing the $(C std.array) module makes the most common container type conform to the most capable range type: slices can seamlessly be used as $(C RandomAccessRange) objects. +) + +$(P +The $(C std.array) module provides the functions $(C empty), $(C front), $(C popFront()) and other range functions for slices. As a result, slices are ready to be used with any range function, for example with $(C print()): +) + +--- +import $(HILITE std.array); + +// ... + + print([ 1, 2, 3, 4 ]); +--- + +$(P +It is not necessary to import $(C std.array) if the $(C std.range) module has already been imported. +) + +$(P +Since it is not possible to remove elements from fixed-length arrays, $(C popFront()) cannot be defined for them. For that reason, fixed-length arrays cannot be used as ranges themselves: +) + +--- +void print(T)(T range) { + for ( ; !range.empty; range.popFront()) { $(DERLEME_HATASI) + write(' ', range.front); + } + + writeln(); +} + +void main() { + int[$(HILITE 4)] array = [ 1, 2, 3, 4 ]; + print(array); +} +--- + +$(P +It would be better if the compilation error appeared on the line where $(C print()) is called. This is possible by adding a template constraint to $(C print()). The following template constraint takes advantage of $(C isInputRange), which we will see in the next chapter. By the help of the template constraint, now the compilation error is for the line where $(C print()) is called, not for a line where $(C print()) is defined: +) + +--- +void print(T)(T range) + if (isInputRange!T) { // template constraint + // ... +} +// ... + print(array); $(DERLEME_HATASI) +--- + +$(P +The elements of a fixed-length array can still be accessed by range functions. What needs to be done is to use a slice of the whole array, not the array itself: +) + +--- + print(array$(HILITE [])); // now compiles +--- + +$(P +Even though slices can be used as ranges, not every range type can be used as an array. When necessary, all of the elements can be copied one by one into an array. $(C std.array.array) is a helper function to simplify this task; $(C array()) iterates over $(C InputRange) ranges, copies the elements, and returns a new array: +) + +--- +import std.array; + +// ... + + // Note: Also taking advantage of UFCS + auto copiesOfStudents = school.$(HILITE array); + writeln(copiesOfStudents); +--- + +$(P +The output: +) + +$(SHELL +[Ebru(1), Derya(2), Damla(3)] +) + +$(P +Also note the use of $(LINK2 /ders/d.en/ufcs.html, UFCS) in the code above. UFCS goes very well with range algorithms by making code naturally match the execution order of expressions. +) + +$(H6 $(IX string, as range) $(IX dchar, string range) $(IX decoding, automatic) Automatic decoding of strings as ranges of $(C dchar)) + +$(P +Being character arrays by definition, strings can also be used as ranges just by importing $(C std.array). However, $(C char) and $(C wchar) strings cannot be used as $(C RandomAccessRange). +) + +$(P +$(C std.array) provides a special functionality with all types of strings: Iterating over strings becomes iterating over Unicode code points, not over UTF code units. As a result, strings appear as ranges of Unicode characters. +) + +$(P +The following strings contain ç and é, which cannot be represented by a single $(C char), and 𝔸 (mathematical double-struck capital A), which cannot be represented by a single $(C wchar) (note that these characters may not be displayed correctly in the environment that you are reading this chapter): +) + +--- +import std.array; + +// ... + + print("abcçdeé𝔸"c); + print("abcçdeé𝔸"w); + print("abcçdeé𝔸"d); +--- + +$(P +The output of the program is what we would normally expect from a $(I range of letters): +) + +$(XXX We use MONO_NOBOLD instead of SHELL to ensure that 𝔸 is displayed correctly.) +$(MONO_NOBOLD + a b c ç d e é 𝔸 + a b c ç d e é 𝔸 + a b c ç d e é 𝔸 +) + +$(P +As you can see, that output does not match what we saw in the $(LINK2 /ders/d.en/characters.html, Characters) and $(LINK2 /ders/d.en/strings.html, Strings) chapters. We have seen in those chapters that $(C string) is an alias to an array of $(C immutable(char)) and $(C wstring) is an alias to an array of $(C immutable(wchar)). Accordingly, one might expect to see UTF code units in the previous output instead of the properly decoded Unicode characters. +) + +$(P +The reason why the characters are displayed correctly is because when used as ranges, string elements are automatically decoded. As we will see below, the decoded $(C dchar) values are not actual elements of the strings but $(LINK2 /ders/d.en/lvalue_rvalue.html, rvalues). +) + +$(P +As a reminder, let's consider the following function that treats the strings as arrays of code units: +) + +--- +void $(HILITE printElements)(T)(T str) { + for (int i = 0; i != str.length; ++i) { + write(' ', str$(HILITE [i])); + } + + writeln(); +} + +// ... + + printElements("abcçdeé𝔸"c); + printElements("abcçdeé𝔸"w); + printElements("abcçdeé𝔸"d); +--- + +$(P +When the characters are accessed directly by indexing, the elements of the arrays are not decoded: +) + +$(XXX We use MONO_NOBOLD instead of SHELL to ensure that 𝔸 is displayed correctly.) +$(MONO_NOBOLD + a b c � � d e � � � � � � + a b c ç d e é ��� ��� + a b c ç d e é 𝔸 +) + +$(P +$(IX representation, std.string) Automatic decoding is not always the desired behavior. For example, the following program that is trying to assign to the first element of a string cannot be compiled because the return value of $(C .front) is an rvalue: +) + +--- +import std.array; + +void main() { + char[] s = "hello".dup; + s.front = 'H'; $(DERLEME_HATASI) +} +--- + +$(SHELL +Error: front(s) is $(HILITE not an lvalue) +) + +$(P +When a range algorithm needs to modify the actual code units of a string (and when doing so does not invalidate the UTF encoding), then the string can be used as a range of $(C ubyte) elements by $(C std.string.represention): +) + +--- +import std.array; +import std.string; + +void main() { + char[] s = "hello".dup; + s$(HILITE .representation).front = 'H'; // compiles + assert(s == "Hello"); +} +--- + +$(P +$(C representation) presents the actual elements of $(C char), $(C wchar), and $(C dchar) strings as ranges of $(C ubyte), $(C ushort), and $(C uint), respectively. +) + +$(H6 Ranges without actual elements) + +$(P +The elements of the $(C School) objects were actually stored in the $(C students) member slices. So, $(C School.front) returned a reference to an existing $(C Student) object. +) + +$(P +One of the powers of ranges is the flexibility of not actually owning elements. $(C front) need not return an actual element of an actual container. The returned $(I element) can be calculated each time when $(C popFront()) is called, and can be used as the value that is returned by $(C front). +) + +$(P +We have already seen a range without actual elements above: Since $(C char) and $(C wchar) cannot represent all Unicode characters, the Unicode characters that appear as range elements cannot be actual elements of any $(C char) or $(C wchar) array. In the case of strings, $(C front) returns a $(C dchar) that is $(I constructed) from the corresponding UTF code units of arrays: +) + +--- +import std.array; + +void main() { + dchar letter = "é".front; // The dchar that is returned by + // front is constructed from the + // two chars that represent é +} +--- + +$(P +Although the element type of the array is $(C char), the return type of $(C front) above is $(C dchar). That $(C dchar) is not an element of the array but is an $(LINK2 /ders/d.en/lvalue_rvalue.html, rvalue) decoded as a Unicode character from the elements of the array. +) + +$(P +Similarly, some ranges do not own any elements but are used for providing access to elements of other ranges. This is a solution to the problem of losing elements while iterating over $(C School) objects above. In order to preserve the elements of the actual $(C School) objects, a special $(C InputRange) can be used. +) + +$(P +To see how this is done, let's define a new struct named $(C StudentRange) and move all of the range member functions from $(C School) to this new struct. Note that $(C School) itself is not a range anymore: +) + +--- +struct School { + Student[] students; +} + +struct StudentRange { + Student[] students; + + this(School school) { + $(HILITE this.students = school.students); + } + + @property bool empty() const { + return students.length == 0; + } + + @property ref Student front() { + return students[0]; + } + + void popFront() { + students = students[1 .. $]; + } +} +--- + +$(P +The new range starts with a member slice that provides access to the students of $(C School) and consumes that member slice in $(C popFront()). As a result, the actual slice in $(C School) is preserved: +) + +--- + auto school = School( [ Student("Ebru", 1), + Student("Derya", 2) , + Student("Damla", 3) ] ); + + print($(HILITE StudentRange)(school)); + + // The actual array is now preserved: + assert(school.students.length == 3); +--- + +$(P +$(I $(B Note:) Since all its work is dispatched to its member slice, $(C StudentRange) may not be seen as a good example of a range. In fact, assuming that $(C students) is an accessible member of $(C School), the user code could have created a slice of $(C School.students) directly and could have used that slice as a range.) +) + +$(H6 $(IX infinite range) Infinite ranges) + +$(P +Another benefit of not storing elements as actual members is the ability to create infinite ranges. +) + +$(P +Making an infinite range is as simple as having $(C empty) always return $(C false). Since it is constant, $(C empty) need not even be a function and can be defined as an $(C enum) value: +) + +--- + enum empty = false; // ← infinite range +--- + +$(P +Another option is to use an immutable $(C static) member: +) + +--- + static immutable empty = false; // same as above +--- + +$(P +As an example of this, let's design a range that represents the Fibonacci series. Despite having only two $(C int) members, the following range can be used as the infinite Fibonacci series: +) + +--- +$(CODE_NAME FibonacciSeries_InputRange)$(CODE_COMMENT_OUT)struct FibonacciSeries +$(CODE_COMMENT_OUT){ + int current = 0; + int next = 1; + + enum empty = false; // ← infinite range + + @property int front() const { + return current; + } + + void popFront() { + const nextNext = current + next; + current = next; + next = nextNext; + } +$(CODE_COMMENT_OUT)} +--- + +$(P +$(I $(B Note:) Although it is infinite, because the members are of type $(C int), the elements of this Fibonacci series would be wrong beyond $(C int.max).) +) + +$(P +Since $(C empty) is always $(C false) for $(C FibonacciSeries) objects, the $(C for) loop in $(C print()) never terminates for them: +) + +--- + print(FibonacciSeries()); // never terminates +--- + +$(P +An infinite range is useful when the range need not be consumed completely right away. We will see how to use only some of the elements of a $(C FibonacciSeries) below. +) + +$(H6 Functions that return ranges) + +$(P +Earlier, we have created a $(C StudentRange) object by explicitly writing $(C StudentRange(school)). +) + +$(P +$(IX convenience function) In most cases, a convenience function that returns the object of such a range is used instead. For example, a function with the whole purpose of returning a $(C StudentRange) would simplify the code: +) + +--- +StudentRange studentsOf(ref School school) { + return StudentRange(school); +} + +// ... + + // Note: Again, taking advantage of UFCS + print(school.$(HILITE studentsOf)); +--- + +$(P +This is a convenience over having to remember and spell out the names of range types explicitly, which can get quite complicated in practice. +) + +$(P +$(IX take, std.range) We can see an example of this with the simple $(C std.range.take) function. $(C take()) is a function that provides access to a specified number of elements of a range, from the beginning. In reality, this functionality is not achieved by the $(C take()) function itself, but by a special range object that it returns. This fact need not be explicit when using $(C take()): +) + +--- +import std.range; + +// ... + + auto school = School( [ Student("Ebru", 1), + Student("Derya", 2) , + Student("Damla", 3) ] ); + + print(school.studentsOf.$(HILITE take(2))); +--- + +$(P +$(C take()) returns a temporary range object above, which provides access to the first 2 elements of $(C school). In turn, $(C print()) uses that object and produces the following output: +) + +$(SHELL + Ebru(1) Derya(2) +) + +$(P +The operations above still don't make any changes to $(C school); it still has 3 elements: +) + +--- + print(school.studentsOf.take(2)); + assert(school.students.length == 3); +--- + +$(P +The specific types of the range objects that are returned by functions like $(C take()) are not important. These types may sometimes be exposed in error messages, or we can print them ourselves with the help of $(C typeof) and $(C stringof): +) + +--- + writeln(typeof(school.studentsOf.take(2)).stringof); +--- + +$(P +According to the output, $(C take()) returns an instance of a template named $(C Take): +) + +$(SHELL +Take!(StudentRange) +) + +$(H6 $(IX std.range) $(IX std.algorithm) $(C std.range) and $(C std.algorithm) modules) + +$(P +A great benefit of defining our types as ranges is being able to use them not only with our own functions, but with Phobos and other libraries as well. +) + +$(P +$(C std.range) includes a large number of range functions, structs, and classes. $(C std.algorithm) includes many algorithms that are commonly found also in the standard libraries of other languages. +) + +$(P +$(IX swapFront, std.algorithm) To see an example of how our types can be used with standard modules, let's use $(C School) with the $(C std.algorithm.swapFront) algorithm. $(C swapFront()) swaps the front elements of two $(C InputRange) ranges. (It requires that the front elements of the two ranges are swappable. Arrays satisfy that condition.) +) + +$(P + +) + +--- +import std.algorithm; + +// ... + + auto turkishSchool = School( [ Student("Ebru", 1), + Student("Derya", 2) , + Student("Damla", 3) ] ); + + auto americanSchool = School( [ Student("Mary", 10), + Student("Jane", 20) ] ); + + $(HILITE swapFront)(turkishSchool.studentsOf, + americanSchool.studentsOf); + + print(turkishSchool.studentsOf); + print(americanSchool.studentsOf); +--- + +$(P +The first elements of the two schools are swapped: +) + +$(SHELL + $(HILITE Mary(10)) Derya(2) Damla(3) + $(HILITE Ebru(1)) Jane(20) +) + +$(P +$(IX filter, std.algorithm) As another example, let's now look at the $(C std.algorithm.filter) algorithm. $(C filter()) returns a special range that filters out elements that do not satisfy a specific condition (a $(I predicate)). The operation of filtering out the elements only affects accessing the elements; the original range is preserved. +) + +$(P +$(IX predicate) Predicates are expressions that must evaluate to $(C true) for the elements that are considered to satisfy a condition, and $(C false) for the elements that do not. There are a number of ways of specifying the predicate that $(C filter()) should use. As we have seen in earlier examples, one way is to use a lambda expression. The parameter $(C a) below represents each student: +) + +--- + school.studentsOf.filter!(a => a.number % 2) +--- + +$(P +The predicate above selects the elements of the range $(C school.studentsOf) that have odd numbers. +) + +$(P +Like $(C take()), $(C filter()) returns a special range object as well. That range object in turn can be passed to other range functions. For example, it can be passed to $(C print()): +) + +--- + print(school.studentsOf.filter!(a => a.number % 2)); +--- + +$(P +That expression can be explained as $(I start with the range $(C school.studentsOf), construct a range object that will filter out the elements of that initial range, and pass the new range object to $(C print())). +) + +$(P +The output consists of students with odd numbers: +) + +$(SHELL + Ebru(1) Damla(3) +) + +$(P +As long as it returns $(C true) for the elements that satisfy the condition, the predicate can also be specified as a function: +) + +--- +import std.array; + +// ... + + bool startsWithD(Student student) { + return student.name.front == 'D'; + } + + print(school.studentsOf.filter!startsWithD); +--- + +$(P +The predicate function above returns $(C true) for students having names starting with the letter D, and $(C false) for the others. +) + +$(P +$(I $(B Note:) Using $(C student.name[0]) would have meant the first UTF-8 code unit, not the first letter. As I have mentioned above, $(C front) uses $(C name) as a range and always returns the first Unicode character.) +) + +$(P +This time the students whose names start with D are selected and printed: +) + +$(SHELL + Derya(2) Damla(3) +) + +$(P +$(IX generate, std.range) $(C generate()), a convenience function template of the $(C std.range) module, makes it easy to present values returned from a function as the elements of an $(C InputRange). It takes any callable entity (function pointer, delegate, etc.) and returns an $(C InputRange) object conceptually consisting of the values that are returned from that callable entity. +) + +$(P +The returned range object is infinite. Every time the $(C front) property of that range object is accessed, the original callable entity is called to get a new $(I element) from it. The $(C popFront()) function of the range object does not perform any work. +) + +$(P +For example, the following range object $(C diceThrower) can be used as an infinite range: +) + +--- +import std.stdio; +import std.range; +import std.random; + +void main() { + auto diceThrower = $(HILITE generate)!(() => uniform(0, 6)); + writeln(diceThrower.take(10)); +} +--- + +$(SHELL +[1, 0, 3, 5, 5, 1, 5, 1, 0, 4] +) + +$(H6 $(IX lazy range) Laziness) + +$(P +Another benefit of functions' returning range objects is that, those objects can be used lazily. Lazy ranges produce their elements one at a time and only when needed. This may be essential for execution speed and memory consumption. Indeed, the fact that infinite ranges can even exist is made possible by ranges being lazy. +) + +$(P +Lazy ranges produce their elements one at a time and only when needed. We see an example of this with the $(C FibonacciSeries) range: The elements are calculated by $(C popFront()) only as they are needed. If $(C FibonacciSeries) were an eager range and tried to produce all of the elements up front, it could never end or find room for the elements that it produced. +) + +$(P +Another problem of eager ranges is the fact that they would have to spend time and space for elements that would perhaps never going to be used. +) + +$(P +Like most of the algorithms in Phobos, $(C take()) and $(C filter()) benefit from laziness. For example, we can pass $(C FibonacciSeries) to $(C take()) and have it generate a finite number of elements: +) + +--- + print(FibonacciSeries().take(10)); +--- + +$(P +Although $(C FibonacciSeries) is infinite, the output contains only the first 10 numbers: +) + +$(SHELL + 0 1 1 2 3 5 8 13 21 34 +) + +$(H5 $(IX ForwardRange) $(C ForwardRange)) + +$(P +$(C InputRange) models a range where elements are taken out of the range as they are iterated over. +) + +$(P +Some ranges are capable of saving their states, as well as operating as an $(C InputRange). For example, $(C FibonacciSeries) objects can save their states because these objects can freely be copied and the two copies continue their lives independently from each other. +) + +$(P +$(IX save) $(C ForwardRange) provides the $(C save) member function, which is expected to return a copy of the range. The copy that $(C save) returns must operate independently from the range object that it was copied from: iterating over one copy must not affect the other copy. +) + +$(P +Importing $(C std.array) automatically makes slices become $(C ForwardRange) ranges. +) + +$(P +In order to implement $(C save) for $(C FibonacciSeries), we can simply return a copy of the object: +) + +--- +$(CODE_NAME FibonacciSeries)struct FibonacciSeries { +// ... + + @property FibonacciSeries save() const { + return this; + } +$(CODE_XREF FibonacciSeries_InputRange)} +--- + +$(P +The returned copy is a separate range that would continue from the point where it was copied from. +) + +$(P +$(IX popFrontN, std.range) We can demonstrate that the copied object is independent from the actual range with the following program. The algorithm $(C std.range.popFrontN()) in the following code removes a specified number of elements from the specified range: +) + +--- +$(CODE_XREF FibonacciSeries)import std.range; + +// ... + +void report(T)(const dchar[] title, const ref T range) { + writefln("%40s: %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 = $(HILITE range.save); + report("The copy", theCopy); + + range.popFrontN(3); + report("After removing three more elements", range); + report("The copy", theCopy); +} +--- + +$(P +The output of the program shows that removing elements from the range does not affect its saved copy: +) + +$(SHELL + Original range: [0, 1, 1, 2, 3] + After removing two elements: [1, 2, 3, 5, 8] + $(HILITE The copy: [1, 2, 3, 5, 8]) + After removing three more elements: [5, 8, 13, 21, 34] + $(HILITE The copy: [1, 2, 3, 5, 8]) +) + +$(P +Also note that the range is passed directly to $(C writefln) in $(C report()). Like our $(C print()) function, the output functions of the $(C stdio) module can take $(C InputRange) objects. I will use $(C stdio)'s output functions from now on. +) + +$(P +$(IX cycle, std.range) An algorithm that works with $(C ForwardRange) is $(C std.range.cycle). $(C cycle()) iterates over the elements of a range repeatedly from the beginning to the end. In order to be able to start over from the beginning it must be able to save a copy of the initial state of the range, so it requires a $(C ForwardRange). +) + +$(P +Since $(C FibonacciSeries) is now a $(C ForwardRange), we can try $(C cycle()) with a $(C FibonacciSeries) object; but in order to avoid having $(C cycle()) iterate over an infinite range, and as a result never find the end of it, we must first make a finite range by passing $(C FibonacciSeries) through $(C take()): +) + +--- + writeln(FibonacciSeries().take(5).cycle.take(20)); +--- + +$(P +In order to make the resultant range finite as well, the range that is returned by $(C cycle) is also passed through $(C take()). The output consists of $(I the first twenty elements of cycling through the first five elements of $(C FibonacciSeries)): +) + +$(SHELL +[0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3] +) + +$(P +We could have defined intermediate variables as well. The following is an equivalent of the single-line code above: +) + +--- + auto series = FibonacciSeries(); + auto firstPart = series.take(5); + auto cycledThrough = firstPart.cycle; + auto firstPartOfCycledThrough = cycledThrough.take(20); + + writeln(firstPartOfCycledThrough); +--- + +$(P +I would like to point out the importance of laziness one more time: The first four lines above merely construct range objects that will eventually produce the elements. The numbers that are part of the result are calculated by $(C FibonacciSeries.popFront()) as needed. +) + +$(P +$(I $(B Note:) Although we have started with $(C FibonacciSeries) as a $(C ForwardRange), we have actually passed the result of $(C FibonacciSeries().take(5)) to $(C cycle()). $(C take()) is adaptive: the range that it returns is a $(C ForwardRange) if its parameter is a $(C ForwardRange). We will see how this is accomplished with $(C isForwardRange) in the next chapter.) +) + +$(H5 $(IX BidirectionalRange) $(C BidirectionalRange)) + +$(P +$(IX back) $(IX popBack) $(C BidirectionalRange) provides two member functions over the member functions of $(C ForwardRange). $(C back) is similar to $(C front): it provides access to the last element of the range. $(C popBack()) is similar to $(C popFront()): it removes the last element from the range. +) + +$(P +Importing $(C std.array) automatically makes slices become $(C BidirectionalRange) ranges. +) + +$(P +$(IX retro, std.range) A good $(C BidirectionalRange) example is the $(C std.range.retro) function. $(C retro()) takes a $(C BidirectionalRange) and ties its $(C front) to $(C back), and $(C popFront()) to $(C popBack()). As a result, the original range is iterated over in reverse order: +) + +--- + writeln([ 1, 2, 3 ].retro); +--- + +$(P +The output: +) + +$(SHELL +[3, 2, 1] +) + +$(P +Let's define a range that behaves similarly to the special range that $(C retro()) returns. Although the following range has limited functionality, it shows how powerful ranges are: +) + +--- +import std.array; +import std.stdio; + +struct Reversed { + int[] range; + + this(int[] range) { + this.range = range; + } + + @property bool empty() const { + return range.empty; + } + + @property int $(HILITE front)() const { + return range.$(HILITE back); // ← reverse + } + + @property int back() const { + return range.front; // ← reverse + } + + void popFront() { + range.popBack(); // ← reverse + } + + void popBack() { + range.popFront(); // ← reverse + } +} + +void main() { + writeln(Reversed([ 1, 2, 3])); +} +--- + +$(P +The output is the same as $(C retro()): +) + +$(SHELL +[3, 2, 1] +) + +$(H5 $(IX RandomAccessRange) $(C RandomAccessRange)) + +$(P +$(IX opIndex) $(C RandomAccessRange) represents ranges that allow accessing elements by the $(C []) operator. As we have seen in the $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading chapter), $(C []) operator is defined by the $(C opIndex()) member function. +) + +$(P +Importing $(C std.array) module makes slices become $(C RandomAccessRange) ranges only if possible. For example, since UTF-8 and UTF-16 encodings do not allow accessing Unicode characters by an index, $(C char) and $(C wchar) arrays cannot be used as $(C RandomAccessRange) ranges of Unicode characters. On the other hand, since the codes of the UTF-32 encoding correspond one-to-one to Unicode character codes, $(C dchar) arrays can be used as $(C RandomAccessRange) ranges of Unicode characters. +) + +$(P +$(IX constant time access) It is natural that every type would define the $(C opIndex()) member function according to its functionality. However, computer science has an expectation on its algorithmic complexity: random access must take $(I constant time). Constant time access means that the time spent when accessing an element is independent of the number of elements in the container. Therefore, no matter how large the range is, element access should not depend on the length of the range. +) + +$(P +In order to be considered a $(C RandomAccessRange), $(I one) of the following conditions must also be satisfied: +) + +$(UL +$(LI to be an infinite $(C ForwardRange)) +) + +$(P +or +) + +$(UL +$(LI $(IX length, BidirectionalRange) to be a $(C BidirectionalRange) that also provides the $(C length) property) +) + +$(P +Depending on the condition that is satisfied, the range is either infinite or finite. +) + +$(H6 Infinite $(C RandomAccessRange)) + +$(P +The following are all of the requirements of a $(C RandomAccessRange) that is based on an $(I infinite $(C ForwardRange)): +) + +$(UL +$(LI $(C empty), $(C front) and $(C popFront()) that $(C InputRange) requires) +$(LI $(C save) that $(C ForwardRange) requires) +$(LI $(C opIndex()) that $(C RandomAccessRange) requires) +$(LI the value of $(C empty) to be known at compile time as $(C false)) +) + +$(P +We were able to define $(C FibonacciSeries) as a $(C ForwardRange). However, $(C opIndex()) cannot be implemented to operate at constant time for $(C FibonacciSeries) because accessing an element requires accessing all of the previous elements first. +) + +$(P +As an example where $(C opIndex()) can operate at constant time, let's define an infinite range that consists of squares of integers. Although the following range is infinite, accessing any one of its elements can happen at constant time: +) + +--- +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; + } +} +--- + +$(P +$(I $(B Note:) It would make more sense to define $(C SquaresRange) as a $(C struct).) +) + +$(P +Although no space has been allocated for the elements of this range, the elements can be accessed by the $(C []) operator: +) + +--- + auto squares = new SquaresRange(); + + writeln(squares$(HILITE [5])); + writeln(squares$(HILITE [10])); +--- + +$(P +The output contains the elements at indexes 5 and 10: +) + +$(SHELL +25 +100 +) + +$(P +The element with index 0 should always represent the first element of the range. We can take advantage of $(C popFrontN()) when testing whether this really is the case: +) + +--- + squares.popFrontN(5); + writeln(squares$(HILITE [0])); +--- + +$(P +The first 5 elements of the range are 0, 1, 4, 9 and 16; the squares of 0, 1, 2, 3 and 4. After removing those, the square of the next value becomes the first element of the range: +) + +$(SHELL +25 +) + +$(P +Being a $(C RandomAccessRange) (the most functional range), $(C SquaresRange) can also be used as other types of ranges. For example, as an $(C InputRange) when passing to $(C filter()): +) + +--- + 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; + } + + writeln(squares.take(50).filter!are_lastTwoDigitsSame); +--- + +$(P +The output consists of elements among the first 50, where last two digits are the same: +) + +$(SHELL +[100, 144, 400, 900, 1444, 1600] +) + +$(H6 Finite $(C RandomAccessRange)) + +$(P +The following are all of the requirements of a $(C RandomAccessRange) that is based on a $(I finite $(C BidirectionalRange)): +) + +$(UL +$(LI $(C empty), $(C front) and $(C popFront()) that $(C InputRange) requires) +$(LI $(C save) that $(C ForwardRange) requires) +$(LI $(C back) and $(C popBack()) that $(C BidirectionalRange) requires) +$(LI $(C opIndex()) that $(C RandomAccessRange) requires) +$(LI $(C length), which provides the length of the range) +) + +$(P +$(IX chain, std.range) As an example of a finite $(C RandomAccessRange), let's define a range that works similarly to $(C std.range.chain). $(C chain()) presents the elements of a number of separate ranges as if they are elements of a single larger range. Although $(C chain()) works with any type of element and any type of range, to keep the example short, let's implement a range that works only with $(C int) slices. +) + +$(P +Let's name this range $(C Together) and expect the following behavior from it: +) + +--- + auto range = Together([ 1, 2, 3 ], [ 101, 102, 103]); + writeln(range[4]); +--- + +$(P +When constructed with the two separate arrays above, $(C range) should present all of those elements as a single range. For example, although neither array has an element at index 4, the element 102 should be the element that corresponds to index 4 of the collective range: +) + +$(SHELL +102 +) + +$(P +As expected, printing the entire range should contain all of the elements: +) + +--- + writeln(range); +--- + +$(P +The output: +) + +$(SHELL +[1, 2, 3, 101, 102, 103] +) + +$(P +$(C Together) will operate lazily: the elements will not be copied to a new larger array; they will be accessed from the original slices. +) + +$(P +We can take advantage of $(I variadic functions), which were introduced in the $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters chapter), to initialize the range by any number of original slices: +) + +--- +struct Together { + const(int)[][] slices; + + this(const(int)[][] slices$(HILITE ...)) { + this.slices = slices.dup; + + clearFront(); + clearBack(); + } + +// ... +} +--- + +$(P +Note that the element type is $(C const(int)), indicating that this $(C struct) will not modify the elements of the ranges. However, the slices will necessarily be modified by $(C popFront()) to implement iteration. +) + +$(P +The $(C clearFront()) and $(C clearBack()) calls that the constructor makes are to remove empty slices from the beginning and the end of the original slices. Such empty slices do not change the behavior of $(C Together) and removing them up front will simplify the implementation: +) + +--- +struct Together { +// ... + + private void clearFront() { + while (!slices.empty && slices.front.empty) { + slices.popFront(); + } + } + + private void clearBack() { + while (!slices.empty && slices.back.empty) { + slices.popBack(); + } + } +} +--- + +$(P +We will call those functions later from $(C popFront()) and $(C popBack()) as well. +) + +$(P +Since $(C clearFront()) and $(C clearBack()) remove all of the empty slices from the beginning and the end, still having a slice would mean that the collective range is not yet empty. In other words, the range should be considered empty only if there is no slice left: +) + +--- +struct Together { +// ... + + @property bool empty() const { + return slices.empty; + } +} +--- + +$(P +The first element of the first slice is the first element of this $(C Together) range: +) + +--- +struct Together { +// ... + + @property int front() const { + return slices.front.front; + } +} +--- + +$(P +Removing the first element of the first slice removes the first element of this range as well. Since this operation may leave the first slice empty, we must call $(C clearFront()) to remove that empty slice and the ones that are after that one: +) + +--- +struct Together { +// ... + + void popFront() { + slices.front.popFront(); + clearFront(); + } +} +--- + +$(P +A copy of this range can be constructed from a copy of the $(C slices) member: +) + +--- +struct Together { +// ... + + @property Together save() const { + return Together(slices.dup); + } +} +--- + +$(P +$(I Please note that $(C .dup) copies only $(C slices) in this case, not the slice elements that it contains.) +) + +$(P +The operations at the end of the range are similar to the ones at the beginning: +) + +--- +struct Together { +// ... + + @property int back() const { + return slices.back.back; + } + + void popBack() { + slices.back.popBack(); + clearBack(); + } +} +--- + +$(P +The length of the range can be calculated as the sum of the lengths of the slices: +) + +--- +struct Together { +// ... + + @property size_t length() const { + size_t totalLength = 0; + + foreach (slice; slices) { + totalLength += slice.length; + } + + return totalLength; + } +} +--- + +$(P +$(IX fold, std.algorithm) Alternatively, the length may be calculated with less code by taking advantage of $(C std.algorithm.fold). $(C fold()) takes an operation as its template parameter and applies that operation to all elements of a range: +) + +--- +import std.algorithm; + +// ... + + @property size_t length() const { + return slices.fold!((a, b) => a + b.length)(size_t.init); + } +--- + +$(P +The $(C a) in the template parameter represents the current result ($(I the sum) in this case) and $(C b) represents the current element. The first function parameter is the range that contains the elements and the second function parameter is the initial value of the result ($(C size_t.init) is 0). (Note how $(C slices) is written before $(C fold) by taking advantage of $(LINK2 /ders/d.en/ufcs.html, UFCS).) +) + +$(P +$(I $(B Note:) Further, instead of calculating the length every time when $(C length) is called, it may be measurably faster to maintain a member variable perhaps named $(C length_), which always equals the correct length of the collective range. That member may be calculated once in the constructor and adjusted accordingly as elements are removed by $(C popFront()) and $(C popBack()).) +) + +$(P +One way of returning the element that corresponds to a specific index is to look at every slice to determine whether the element would be among the elements of that slice: +) + +--- +struct Together { +// ... + + 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)); + } +} +--- + +$(P +$(I $(B Note:) This $(C opIndex()) does not satisfy the constant time requirement that has been mentioned above. For this implementation to be acceptably fast, the $(C slices) member must not be too long.) +) + +$(P +This new range is now ready to be used with any number of $(C int) slices. With the help of $(C take()) and $(C array()), we can even include the range types that we have defined earlier in this chapter: +) + +--- + auto range = Together(FibonacciSeries().take(10).array, + [ 777, 888 ], + (new SquaresRange()).take(5).array); + + writeln(range.save); +--- + +$(P +The elements of the three slices are accessed as if they were elements of a single large array: +) + +$(SHELL +[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16] +) + +$(P +We can pass this range to other range algorithms. For example, to $(C retro()), which requires a $(C BidirectionalRange): +) + +--- + writeln(range.save.retro); +--- + +$(P +The output: +) + +$(SHELL +[16, 9, 4, 1, 0, 888, 777, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0] +) + +$(P +Of course you should use the more functional $(C std.range.chain) instead of $(C Together) in your programs. +) + +$(H5 $(IX OutputRange) $(C OutputRange)) + +$(P +All of the range types that we have seen so far are about element access. $(C OutputRange) represents streamed element output, similar to sending characters to $(C stdout). +) + +$(P +$(IX put) I have mentioned earlier that $(C OutputRange) requires support for the $(C put(range, element)) operation. $(C put()) is a function defined in the $(C std.range) module. It determines the capabilities of the range and the element at compile time and uses the most appropriate method to $(I output) the element. +) + +$(P +$(C put()) considers the following cases in the order that they are listed below, and applies the method for the first matching case. $(C R) represents the type of the range; $(C range), a range object; $(C E), the type of the element; and $(C e) an element of the range: +) + +$(TABLE full, +$(HEAD2 Case Considered, Method Applied) +$(ROW2 +$(C R) has a member function named $(C put)$(BR) +and $(C put) can take an $(C E) as argument, + +$(C range.put(e);) + +) +$(ROW2 +$(C R) has a member function named $(C put)$(BR) +and $(C put) can take an $(C E[]) as argument, + +$(C range.put([ e ]);) + +) +$(ROW2 +$(C R) is an $(C InputRange)$(BR) +and $(C e) can be assigned to $(C range.front), + +$(C range.front = e;) +$(BR) +$(C range.popFront();) + +) +$(ROW2 +$(C E) is an $(C InputRange)$(BR) +and can be copied to $(C R), + +$(C for (; !e.empty; e.popFront())) +$(BR) +$(C put(range, e.front);) + +) +$(ROW2 +$(C R) can take $(C E) as argument$(BR) +(e.g. $(C R) could be a delegate), + +$(C range(e);) + +) +$(ROW2 +$(C R) can take $(C E[]) as argument$(BR) +(e.g. $(C R) could be a delegate), + +$(C range([ e ]);) + +) +) + +$(P +Let's define a range that matches the first case: The range will have a member function named $(C put()), which takes a parameter that matches the type of the output range. +) + +$(P +This output range will be used for outputting elements to multiple files, including $(C stdout). When elements are outputted with $(C put()), they will all be written to all of those files. As an additional functionality, let's add the ability to specify a delimiter to be written after each element. +) + +--- +$(CODE_NAME MultiFile)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"); + } + } + + // This is the version that takes arrays (but not strings) + void put(T)(T slice) + if (isArray!T && !isSomeString!T) { + foreach (element; slice) { + // Note that this is a call to the other version + // of put() below + put(element); + } + } + + // This is the version that takes non-arrays and strings + void put(T)(T value) + if (!isArray!T || isSomeString!T) { + foreach (file; files) { + file.write(value, delimiter); + } + } +} +--- + +$(P +In order to be used as an output range of any type of elements, $(C put()) is also templatized on the element type. +) + +$(P +$(IX copy, std.algorithm) An algorithm in Phobos that uses $(C OutputRange) is $(C std.algorithm.copy). $(C copy()) is a very simple algorithm, which copies the elements of an $(C InputRange) to an $(C OutputRange). +) + +--- +import std.traits; +import std.stdio; +import std.algorithm; + +$(CODE_XREF MultiFile)// ... + +void main() { + auto output = MultiFile("\n", "output_0", "output_1"); + copy([ 1, 2, 3], output); + copy([ "red", "blue", "green" ], output); +} +--- + +$(P +That code outputs the elements of the input ranges both to $(C stdout) and to files named "output_0" and "output_1": +) + +$(SHELL +1 +2 +3 +red +blue +green +) + +$(H6 $(IX slice, as OutputRange) Using slices as $(C OutputRange)) + +$(P +The $(C std.range) module makes slices $(C OutputRange) objects as well. (By contrast, $(C std.array) makes them only input ranges.) Unfortunately, using slices as $(C OutputRange) objects has a confusing effect: slices lose an element for each $(C put()) operation on them; and that element is the element that has just been outputted! +) + +$(P +The reason for this behavior is a consequence of slices' not having a $(C put()) member function. As a result, the third case of the previous table is matched for slices and the following method is applied: +) + +--- + range.front = e; + range.popFront(); +--- + +$(P +As the code above is executed for each $(C put()), the front element of the slice is assigned to the value of the $(I outputted) element, to be subsequently removed from the slice with $(C popFront()): +) + +--- +import std.stdio; +import std.range; + +void main() { + int[] slice = [ 1, 2, 3 ]; + $(HILITE put(slice, 100)); + writeln(slice); +} +--- + +$(P +As a result, although the slice is used as an $(C OutputRange), it surprisingly $(I loses) elements: +) + +$(SHELL +[2, 3] +) + +$(P +To avoid this, a separate slice must be used as an $(C OutputRange) instead: +) + +--- +import std.stdio; +import std.range; + +void main() { + int[] slice = [ 1, 2, 3 ]; + int[] slice2 = slice; + + put($(HILITE slice2), 100); + + writeln(slice2); + writeln(slice); +} +--- + +$(P +This time the second slice is consumed and the original slice has the expected elements: +) + +$(SHELL +[2, 3] +[100, 2, 3] $(SHELL_NOTE expected result) +) + +$(P +Another important fact is that the length of the slice does not grow when used as an $(C OutputRange). It is the programmer's responsibility to ensure that there is enough room in the slice: +) + +--- + int[] slice = [ 1, 2, 3 ]; + int[] slice2 = slice; + + foreach (i; 0 .. 4) { // ← no room for 4 elements + put(slice2, i * 100); + } +--- + +$(P +When the slice becomes completely empty because of the indirect $(C popFront()) calls, the program terminates with an exception: +) + +$(SHELL +core.exception.AssertError@...: Attempting to fetch the $(HILITE front +of an empty array of int) +) + +$(P +$(IX appender, std.array) $(C std.array.Appender) and its convenience function $(C appender) allows using slices as $(I an $(C OutputRange) where the elements are appended). The $(C put()) function of the special range object that $(C appender()) returns actually appends the elements to the original slice: +) + +--- +import std.array; + +// ... + + auto a = appender([ 1, 2, 3 ]); + + foreach (i; 0 .. 4) { + a.put(i * 100); + } +--- + +$(P +In the code above, $(C appender) is called with an array and returns a special range object. That range object is in turn used as an $(C OutputRange) by calling its $(C put()) member function. The resultant elements are accessed by its $(C .data) property: +) + +--- + writeln(a.data); +--- + +$(P +The output: +) + +$(SHELL +[1, 2, 3, 0, 100, 200, 300] +) + +$(P +$(C Appender) supports the $(C ~=) operator as well: +) + +--- + a $(HILITE ~=) 1000; + writeln(a.data); +--- + +$(P +The output: +) + +$(SHELL +[1, 2, 3, 0, 100, 200, 300, 1000] +) + +$(H5 Range templates) + +$(P +Although we have used mostly $(C int) ranges in this chapter, ranges and range algorithms are much more useful when defined as templates. +) + +$(P +The $(C std.range) module includes many range templates. We will see these templates in the next chapter. +) + +$(H5 Summary) + +$(UL + +$(LI Ranges abstract data structures from algorithms and allow them to be used with algorithms seamlessly.) + +$(LI Ranges are a D concept and are the basis for many features of Phobos.) + +$(LI Many Phobos algorithms return lazy range objects to accomplish their special tasks.) + +$(LI UFCS works well with range algorithms.) + +$(LI When used as $(C InputRange) objects, the elements of strings are Unicode characters.) + +$(LI $(C InputRange) requires $(C empty), $(C front) and $(C popFront()).) + +$(LI $(C ForwardRange) additionally requires $(C save).) + +$(LI $(C BidirectionalRange) additionally requires $(C back) and $(C popBack()).) + +$(LI Infinite $(C RandomAccessRange) requires $(C opIndex()) over $(C ForwardRange).) + +$(LI Finite $(C RandomAccessRange) requires $(C opIndex()) and $(C length) over $(C BidirectionalRange).) + +$(LI $(C std.array.appender) returns an $(C OutputRange) that appends to slices.) + +$(LI Slices are ranges of finite $(C RandomAccessRange)) + +$(LI Fixed-length arrays are not ranges.) + +) + +macros: + SUBTITLE=Ranges + + DESCRIPTION=Phobos ranges that abstract data structures from algorithms and that enables them to be used seamlessly. + + KEYWORDS=d programming language tutorial book range OutputRange InputRange ForwardRange BidirectionalRange RandomAccessRange + +MINI_SOZLUK= diff --git a/target/ranges_more.d b/target/ranges_more.d new file mode 100644 index 0000000..2c28e54 --- /dev/null +++ b/target/ranges_more.d @@ -0,0 +1,428 @@ +Ddoc + +$(DERS_BOLUMU $(IX range) More Ranges) + +$(P +We used mostly $(C int) ranges in the previous chapter. In practice, containers, algorithms, and ranges are almost always implemented as templates. The $(C print()) example in that chapter was a template as well: +) + +--- +void print$(HILITE (T))(T range) { + // ... +} +--- + +$(P +What lacks from the implementation of $(C print()) is that even though it requires $(C T) to be a kind of $(C InputRange), it does not formalize that requirement with a template constraint. (We have seen template constraints in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter).) +) + +$(P +The $(C std.range) module contains templates that are useful both in template constraints and in $(C static if) statements. +) + +$(H5 Range kind templates) + +$(P +The group of templates with names starting with $(C is) determine whether a type satisfies the requirements of a certain kind of range. For example, $(C isInputRange!T) answers the question "is $(C T) an $(C InputRange)?" The following templates are for determining whether a type is of a specific general range kind: +) + +$(UL +$(LI $(IX isInputRange) $(C isInputRange)) +$(LI $(IX isForwardRange) $(C isForwardRange)) +$(LI $(IX isBidirectionalRange) $(C isBidirectionalRange)) +$(LI $(IX isRandomAccessRange) $(C isRandomAccessRange)) +$(LI $(IX isOutputRange) $(C isOutputRange)) +) + +$(P +Accordingly, the template constraint of $(C print()) can use $(C isInputRange): +) + +--- +void print(T)(T range) + if ($(HILITE isInputRange!T)) { + // ... +} +--- + +$(P +Unlike the others, $(C isOutputRange) takes two template parameters: The first one is a range type and the second one is an element type. It returns $(C true) if that range type allows outputting that element type. For example, the following constraint is for requiring that the range must be an $(C OutputRange) that accepts $(C double) elements: +) + +--- +void foo(T)(T range) + if (isOutputRange!($(HILITE T, double))) { + // ... +} +--- + +$(P +When used in conjunction with $(C static if), these constraints can determine the capabilities of user-defined ranges as well. For example, when a dependent range of a user-defined range is a $(C ForwardRange), the user-defined range can take advantage of that fact and can provide the $(C save()) function as well. +) + +$(P +Let's see this on a range that produces the negatives of the elements of an existing range (more accurately, the $(I numeric complements) of the elements). Let's start with just the $(C InputRange) functions: +) + +--- +struct Negative(T) + if (isInputRange!T) { + T range; + + @property bool empty() { + return range.empty; + } + + @property auto front() { + return $(HILITE -range.front); + } + + void popFront() { + range.popFront(); + } +} +--- + +$(P +$(I $(B Note:) As we will see below, the return type of $(C front) can be specified as $(C ElementType!T) as well.) +) + +$(P +The only functionality of this range is in the $(C front) function where it produces the negative of the front element of the original range. +) + +$(P +As usual, the following is the convenience function that goes with that range: +) + +--- +Negative!T negative(T)(T range) { + return Negative!T(range); +} +--- + +$(P +This range is ready to be used with e.g. $(C FibonacciSeries) that was defined in the previous chapter: +) + +--- +struct FibonacciSeries { + int current = 0; + int next = 1; + + enum empty = false; + + @property int front() const { + return current; + } + + void popFront() { + const nextNext = current + next; + current = next; + next = nextNext; + } + + @property FibonacciSeries save() const { + return this; + } +} + +// ... + + writeln(FibonacciSeries().take(5).$(HILITE negative)); +--- + +$(P +The output contains the negatives of the first five elements of the series: +) + +$(SHELL +[0, -1, -1, -2, -3] +) + +$(P +Naturally, being just an $(C InputRange), $(C Negative) cannot be used with algorithms like $(C cycle()) that require a $(C ForwardRange): +) + +--- + writeln(FibonacciSeries() + .take(5) + .negative + .cycle $(DERLEME_HATASI) + .take(10)); +--- + +$(P +However, when the original range is already a $(C ForwardRange), there is no reason for $(C Negative) not to provide the $(C save()) function as well. This condition can be determined by a $(C static if) statement and $(C save()) can be provided if the original range is a $(C ForwardRange). In this case it is as trivial as returning a new $(C Negative) object that is constructed by a copy of the original range: +) + +--- +struct Negative(T) + if (isInputRange!T) { +// ... + + $(HILITE static if) (isForwardRange!T) { + @property Negative save() { + return Negative(range.save); + } + } +} +--- + +$(P +The addition of the new $(C save()) function makes $(C Negative!FibonacciSeries) a $(C ForwardRange) as well and the $(C cycle()) call can now be compiled: +) + +--- + writeln(FibonacciSeries() + .take(5) + .negative + .cycle // ← now compiles + .take(10)); +--- + +$(P +The output of the entire expression can be described as $(I take the first five elements of the Fibonacci series, take their negatives, cycle those indefinitely, and take the first ten of those elements): +) + +$(SHELL +[0, -1, -1, -2, -3, 0, -1, -1, -2, -3] +) + +$(P +With the same approach, $(C Negative) can be made a $(C BidirectionalRange) and a $(C RandomAccessRange) if the original range supports those functionalities: +) + +--- +struct Negative(T) + if (isInputRange!T) { +// ... + + static if (isBidirectionalRange!T) { + @property auto back() { + return -range.back; + } + + void popBack() { + range.popBack(); + } + } + + static if (isRandomAccessRange!T) { + auto opIndex(size_t index) { + return -range[index]; + } + } +} +--- + +$(P +For example, when it is used with a slice, the negative elements can be accessed by the $(C []) operator: +) + +--- + auto d = [ 1.5, 2.75 ]; + auto n = d.negative; + writeln(n$(HILITE [1])); +--- + +$(P +The output: +) + +$(SHELL +-2.75 +) + +$(H5 $(IX ElementType) $(IX ElementEncodingType) $(C ElementType) and $(C ElementEncodingType)) + +$(P +$(C ElementType) provides the types of the elements of the range. +) + +$(P +For example, the following template constraint includes a requirement that is about the element type of the first range: +) + +--- +void foo(I1, I2, O)(I1 input1, I2 input2, O output) + if (isInputRange!I1 && + isForwardRange!I2 && + isOutputRange!(O, $(HILITE ElementType!I1))) { + // ... +} +--- + +$(P +The previous constraint can be described as $(I if $(C I1) is an $(C InputRange) and $(C I2) is a $(C ForwardRange) and $(C O) is an $(C OutputRange) that accepts the element type of $(C I1)). +) + +$(P +$(IX dchar, string range) Since strings are always ranges of Unicode characters, regardless of their actual character types, they are always ranges of $(C dchar), which means that even $(C ElementType!string) and $(C ElementType!wstring) are $(C dchar). For that reason, when needed in a template, the actual UTF encoding type of a string range can be obtained by $(C ElementEncodingType). +) + +$(H5 More range templates) + +$(P +The $(C std.range) module has many more range templates that can be used with D's other compile-time features. The following is a sampling: +) + +$(UL + +$(LI $(IX isInfinite) $(C isInfinite): Whether the range is infinite) + +$(LI $(IX hasLength) $(C hasLength): Whether the range has a $(C length) property) + +$(LI $(IX hasSlicing) $(C hasSlicing): Whether the range supports slicing i.e. with $(C a[x..y])) + +$(LI $(IX hasAssignableElements) $(C hasAssignableElements): Whether the return type of $(C front) is assignable) + +$(LI $(IX hasSwappableElements) $(C hasSwappableElements): Whether the elements of the range are swappable e.g. with $(C std.algorithm.swap)) + +$(LI $(IX hasMobileElements) $(IX move, std.algorithm) $(C hasMobileElements): Whether the elements of the range are movable e.g. with $(C std.algorithm.move) + +$(P +$(IX moveFront) $(IX moveBack) $(IX moveAt) This implies that the range has $(C moveFront()), $(C moveBack()), or $(C moveAt()), depending on the actual kind of the range. Since moving elements is usually faster than copying them, depending on the result of $(C hasMobileElements) a range can provide faster operations by calling $(C move()). +) + +) + +$(LI $(IX hasLvalueElements) $(IX lvalue) $(C hasLvalueElements): Whether the elements of the range are $(I lvalues) (roughly meaning that the elements are not copies of actual elements nor are temporary objects that are created on the fly) + +$(P +For example, $(C hasLvalueElements!FibonacciSeries) is $(C false) because the elements of $(C FibonacciSeries) do not exist as themselves; rather, they are copies of the member $(C current) that is returned by $(C front). Similarly, $(C hasLvalueElements!(Negative!(int[]))) is $(C false) because although the $(C int) slice does have actual elements, the range that is represented by $(C Negative) does not provide access to those elements; rather, it returns copies that have the negative signs of the elements of the actual slice. Conversely, $(C hasLvalueElements!(int[])) is $(C true) because a slice provides access to actual elements of an array. +) + +) + +) + +$(P +The following example takes advantage of $(C isInfinite) to provide $(C empty) as an $(C enum) when the original range is infinite, making it known at compile time that $(C Negative!T) is infinite as well: +) + +--- +struct Negative(T) + if (isInputRange!T) { +// ... + + static if (isInfinite!T) { + // Negative!T is infinite as well + enum empty = false; + + } else { + @property bool empty() { + return range.empty; + } + } + +// ... +} + +static assert( isInfinite!(Negative!FibonacciSeries)); +static assert(!isInfinite!(int[])); +--- + +$(H5 $(IX polymorphism, run-time) $(IX inputRangeObject) $(IX outputRangeObject) Run-time polymorphism with $(C inputRangeObject()) and $(C outputRangeObject())) + +$(P +Being implemented mostly as templates, ranges exhibit $(I compile-time polymorphism), which we have been taking advantage of in the examples of this chapter and previous chapters. ($(I For differences between compile-time polymorphism and run-time polymorphism, see the "Compile-time polymorphism" section in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter).)) +) + +$(P +Compile-time polymorphism has to deal with the fact that every instantiation of a template is a different type. For example, the return type of the $(C take()) template is directly related to the original range: +) + +--- + writeln(typeof([11, 22].negative.take(1)).stringof); + writeln(typeof(FibonacciSeries().take(1)).stringof); +--- + +$(P +The output: +) + +$(SHELL +Take!(Negative!(int[])) +Take!(FibonacciSeries) +) + +$(P +A natural consequence of this fact is that different range types cannot be assigned to each other. The following is an example of this incompatibility between two $(C InputRange) ranges: +) + +--- + auto range = [11, 22].negative; + // ... at a later point ... + range = FibonacciSeries(); $(DERLEME_HATASI) +--- + +$(P +As expected, the compilation error indicates that $(C FibonacciSeries) and $(C Negative!(int[])) are not compatible: +) + +$(SHELL +Error: cannot implicitly convert expression (FibonacciSeries(0, 1)) +of type $(HILITE FibonacciSeries) to $(HILITE Negative!(int[])) +) + +$(P +However, although the actual types of the ranges are different, since they both are $(I ranges of $(C int)), this incompatibility can be seen as an unnecessary limitation. From the usage point of view, since both ranges simply provide $(C int) elements, the actual mechanism that produces those elements should not be important. +) + +$(P +Phobos helps with this issue by $(C inputRangeObject()) and $(C outputRangeObject()). $(C inputRangeObject()) allows presenting ranges as $(I a specific kind of range of specific types of elements). With its help, a range can be used e.g. as $(I an $(C InputRange) of $(C int) elements), regardless of the actual type of the range. +) + +$(P +$(C inputRangeObject()) is flexible enough to support all of the non-output ranges: $(C InputRange), $(C ForwardRange), $(C BidirectionalRange), and $(C RandomAccessRange). Because of that flexibility, the object that it returns cannot be defined by $(C auto). The exact kind of range that is required must be specified explicitly: +) + +--- + // Meaning "InputRange of ints": + $(HILITE InputRange!int) range = [11, 22].negative.$(HILITE inputRangeObject); + + // ... at a later point ... + + // The following assignment now compiles + range = FibonacciSeries().$(HILITE inputRangeObject); +--- + +$(P +As another example, when the range needs to be used as $(I a $(C ForwardRange) of $(C int) elements), its type must be specified explicitly as $(C ForwardRange!int): +) + +--- + $(HILITE ForwardRange!int) range = [11, 22].negative.inputRangeObject; + + auto copy = range.$(HILITE save); + + range = FibonacciSeries().inputRangeObject; + writeln(range.$(HILITE save).take(10)); +--- + +$(P +The example calls $(C save()) just to prove that the ranges can indeed be used as $(C ForwardRange) ranges. +) + +$(P +Similarly, $(C outputRangeObject()) works with $(C OutputRange) ranges and allows their use as $(I an $(C OutputRange) that accepts specific types of elements). +) + +$(H5 Summary) + +$(UL + +$(LI The $(C std.range) module contains many useful range templates.) + +$(LI Some of those templates allow templates be more capable depending on the capabilities of original ranges.) + +$(LI $(C inputRangeObject()) and $(C outputRangeObject()) provide run-time polymorphism, allowing using different types of ranges as $(I specific kinds of ranges of specific types of elements).) +) + +macros: + SUBTITLE=More Ranges + + DESCRIPTION=Many useful range templates of the std.range module. + + KEYWORDS=d programming language tutorial book ranges diff --git a/target/scope.d b/target/scope.d new file mode 100644 index 0000000..c147bae --- /dev/null +++ b/target/scope.d @@ -0,0 +1,142 @@ +Ddoc + +$(DERS_BOLUMU $(IX scope(success)) $(IX scope(exit)) $(IX scope(failure)) $(CH4 scope)) + +$(P +As we have seen in the previous chapter, expressions that must always be executed are written in the $(C finally) block, and expressions that must be executed when there are error conditions are written in $(C catch) blocks. +) + +$(P +We can make the following observations about the use of these blocks: +) + +$(UL + +$(LI $(C catch) and $(C finally) cannot be used without a $(C try) block.) + +$(LI Some of the variables that these blocks need may not be accessible within these blocks: + +--- +void foo(ref int r) { + try { + $(HILITE int addend) = 42; + + r += addend; + mayThrow(); + + } catch (Exception exc) { + r -= addend; $(DERLEME_HATASI) + } +} +--- + +$(P +That function first modifies the reference parameter and then reverts this modification when an exception is thrown. Unfortunately, $(C addend) is accessible only in the $(C try) block, where it is defined. $(I ($(B Note:) This is related to name scopes, as well as object lifetimes, which will be explained in $(LINK2 /ders/d.en/lifetimes.html, a later chapter.))) +) + +) + +$(LI Writing all of potentially unrelated expressions in the single $(C finally) block at the bottom separates those expressions from the actual code that they are related to. +) + +) + +$(P +The $(C scope) statements have similar functionality with the $(C catch) and $(C finally) scopes but they are better in many respects. Like $(C finally), the three different $(C scope) statements are about executing expressions when leaving scopes: +) + +$(UL +$(LI $(C scope(exit)): the expression is always executed when exiting the scope, regardless of whether successfully or due to an exception) + +$(LI $(C scope(success)): the expression is executed only if the scope is being exited successfully) + +$(LI $(C scope(failure)): the expression is executed only if the scope is being exited due to an exception) +) + +$(P +Although these statements are closely related to exceptions, they can be used without a $(C try-catch) block. +) + +$(P +As an example, let's write the function above with a $(C scope(failure)) statement: +) + +--- +void foo(ref int r) { + int addend = 42; + + r += addend; + $(HILITE scope(failure)) r -= addend; + + mayThrow(); +} +--- + +$(P +The $(C scope(failure)) statement above ensures that the $(C r -= addend) expression will be executed if the function's scope is exited due to an exception. A benefit of $(C scope(failure)) is the fact that the expression that reverts another expression is written close to it. +) + +$(P +$(C scope) statements can be specified as blocks as well: +) + +--- + scope(exit) { + // ... expressions ... + } +--- + +$(P +Here is another function that tests all three of these statements: +) + +--- +void test() { + scope(exit) writeln("when exiting 1"); + + scope(success) { + writeln("if successful 1"); + writeln("if successful 2"); + } + + scope(failure) writeln("if thrown 1"); + scope(exit) writeln("when exiting 2"); + scope(failure) writeln("if thrown 2"); + + throwsHalfTheTime(); +} +--- + +$(P +If no exception is thrown, the output of the function includes only the $(C scope(exit)) and $(C scope(success)) expressions: +) + +$(SHELL +when exiting 2 +if successful 1 +if successful 2 +when exiting 1 +) + +$(P +If an exception is thrown, the output includes the $(C scope(exit)) and $(C scope(failure)) expressions: +) + +$(SHELL +if thrown 2 +when exiting 2 +if thrown 1 +when exiting 1 +object.Exception@...: the error message +) + +$(P +As seen in the outputs, the blocks of the $(C scope) statements are executed in reverse order. This is because later code may depend on previous variables. Executing the $(C scope) statements in reverse order enables undoing side effects of earlier expressions in a consistent order. +) + +Macros: + SUBTITLE=scope + + DESCRIPTION=The scope(success), scope(failure), and scope(exit) statements that are used for specifying expressions that must be executed when exiting scopes. + + KEYWORDS=d programming language tutorial book scope diff --git a/target/slices.cozum.d b/target/slices.cozum.d new file mode 100644 index 0000000..9667aea --- /dev/null +++ b/target/slices.cozum.d @@ -0,0 +1,40 @@ +Ddoc + +$(COZUM_BOLUMU Slices and Other Array Features) + +$(P +Iterating over elements by consuming a slice from the beginning is an interesting concept. This method is also the basis of Phobos ranges that we will see in a later chapter. +) + +--- +import std.stdio; + +void main() { + double[] array = [ 1, 20, 2, 30, 7, 11 ]; + + double[] slice = array; // Start with a slice that + // provides access to all of + // the elements of the array + + while (slice.length) { // As long as there is at least + // one element in that slice + + if (slice[0] > 10) { // Always use the first element + slice[0] /= 2; // in the expressions + } + + slice = slice[1 .. $]; // Shorten the slice from the + // beginning + } + + writeln(array); // The actual elements are + // changed +} +--- + +Macros: + SUBTITLE=Slices and Other Array Features Solution + + DESCRIPTION=Programming in D exercise solutions: arrays + + KEYWORDS=programming in d tutorial arrays solution diff --git a/target/slices.d b/target/slices.d new file mode 100644 index 0000000..e4aadcb --- /dev/null +++ b/target/slices.d @@ -0,0 +1,834 @@ +Ddoc + +$(DERS_BOLUMU $(IX slice) $(IX array) Slices and Other Array Features) + +$(P +We have seen in the $(LINK2 /ders/d.en/arrays.html, Arrays chapter) how elements are grouped as a collection in an array. That chapter was intentionally brief, leaving most of the features of arrays to this chapter. +) + +$(P +Before going any further, here are a few brief definitions of some of the terms that happen to be close in meaning: +) + +$(UL + +$(LI $(B Array:) The general concept of a group of elements that are located side by side and are accessed by indexes. +) + +$(LI +$(B Fixed-length array (static array):) An array with a fixed number of elements. This type of array owns its elements. +) + +$(LI +$(B Dynamic array:) An array that can gain or lose elements. This type of array provides access to elements that are owned by the D runtime environment. +) + +$(LI $(B Slice:) Another name for $(I dynamic array). +) + +) + +$(P +When I write $(I slice) I will specifically mean a slice; and when I write $(I array), I will mean either a slice or a fixed-length array, with no distinction. +) + +$(H5 Slices) + +$(P +Slices are the same feature as dynamic arrays. They are called $(I dynamic arrays) for being used like arrays, and are called $(I slices) for providing access to portions of other arrays. They allow using those portions as if they are separate arrays. +) + +$(P +$(IX .., slice element range) Slices are defined by the $(I number range) syntax that correspond to the indexes that specify the beginning and the end of the range: +) + +--- + $(I beginning_index) .. $(I one_beyond_the_end_index) +--- + +$(P +In the number range syntax, the beginning index is a part of the range but the end index is outside of the range: +) + +--- +/* ... */ = monthDays[0 .. 3]; // 0, 1, and 2 are included; but not 3 +--- + +$(P +$(I $(B Note:) Number ranges are different from Phobos ranges. Phobos ranges are about struct and class interfaces. We will see these features in later chapters. +) +) + +$(P +As an example, we can $(I slice) the $(C monthDays) array to be able to use its parts as four smaller arrays: +) + +--- + int[12] monthDays = + [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; + + int[] firstQuarter = monthDays[0 .. 3]; + int[] secondQuarter = monthDays[3 .. 6]; + int[] thirdQuarter = monthDays[6 .. 9]; + int[] fourthQuarter = monthDays[9 .. 12]; +--- + +$(P +The four variables in the code above are slices; they provide access to four parts of an already existing array. An important point worth stressing here is that those slices do not have their own elements. They merely provide access to the elements of the actual array. Modifying an element of a slice modifies the element of the actual array. To see this, let's modify the first elements of each slice and then print the actual array: +) + +--- + firstQuarter[0] = 1; + secondQuarter[0] = 2; + thirdQuarter[0] = 3; + fourthQuarter[0] = 4; + + writeln(monthDays); +--- + +$(P +The output: +) + +$(SHELL +[$(HILITE 1), 28, 31, $(HILITE 2), 31, 30, $(HILITE 3), 31, 30, $(HILITE 4), 30, 31] +) + +$(P +Each slice modifies its first element, and the corresponding element of the actual array is affected. +) + +$(P +We have seen earlier that valid array indexes are from 0 to one less than the length of the array. For example, the valid indexes of a 3-element array are 0, 1, and 2. Similarly, the end index in the slice syntax specifies one beyond the last element that the slice will be providing access to. For that reason, when the last element of an array needs to be included in a slice, the length of the array must be specified as the end index. For example, a slice of all elements of a 3-element array would be $(C array[0..3]). +) + +$(P +An obvious limitation is that the beginning index cannot be greater than the end index: +) + +--- + int[3] array = [ 0, 1, 2 ]; + int[] slice = array[2 .. 1]; // ← run-time ERROR +--- + +$(P +It is legal to have the beginning and the end indexes to be equal. In that case the slice is empty. Assuming that $(C index) is valid: +) + +--- + int[] slice = anArray[index .. index]; + writeln("The length of the slice: ", slice.length); +--- + +$(P +The output: +) + +$(SHELL +The length of the slice: 0 +) + +$(H5 $(IX $, slice length) Using $(C $), instead of $(C array.length)) + +$(P +When indexing, $(C $) is a shorthand for the length of the array: +) + +--- + writeln(array[array.length - 1]); // the last element + writeln(array[$ - 1]); // the same thing +--- + +$(H5 $(IX .dup) $(IX copy, array) Using $(C .dup) to copy) + +$(P +Short for "duplicate", the $(C .dup) property makes a new array from the copies of the elements of an existing array: +) + +--- + double[] array = [ 1.25, 3.75 ]; + double[] theCopy = array.dup; +--- + +$(P +As an example, let's define an array that contains the number of days of the months of a leap year. A method is to take a copy of the non-leap-year array and then to increment the element that corresponds to February: +) + +--- +import std.stdio; + +void main() { + int[12] monthDays = + [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; + + int[] leapYear = monthDays$(HILITE .dup); + + ++leapYear[1]; // increments the days in February + + writeln("Non-leap year: ", monthDays); + writeln("Leap year : ", leapYear); +} +--- + +$(P +The output: +) + +$(SHELL_SMALL +Non-leap year: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] +Leap year : [31, $(HILITE 29), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] +) + +$(H5 $(IX assignment, array) Assignment) + +$(P +We have seen so far that the assignment operator $(I modifies) values of variables. It is the same with fixed-length arrays: +) + +--- + int[3] a = [ 1, 1, 1 ]; + int[3] b = [ 2, 2, 2 ]; + + a = b; // the elements of 'a' become 2 + writeln(a); +--- + +$(P +The output: +) + +$(SHELL +[2, 2, 2] +) + +$(P +The assignment operation has a completely different meaning for slices: It makes the slice start providing access to new elements: +) + +--- + int[] odds = [ 1, 3, 5, 7, 9, 11 ]; + int[] evens = [ 2, 4, 6, 8, 10 ]; + + int[] slice; // not providing access to any elements yet + + $(HILITE slice =) odds[2 .. $ - 2]; + writeln(slice); + + $(HILITE slice =) evens[1 .. $ - 1]; + writeln(slice); +--- + +$(P +Above, $(C slice) does not provide access to any elements when it is defined. It is then used to provide access to some of the elements of $(C odds), and later to some of the elements of $(C evens): +) + +$(SHELL +[5, 7] +[4, 6, 8] +) + +$(H5 Making a slice longer may terminate sharing) + +$(P +Since the length of a fixed-length array cannot be changed, the concept of $(I termination of sharing) is only about slices. +) + +$(P +It is possible to access the same elements by more than one slice. For example, the first two of the eight elements below are being accessed through three slices: +) + +--- +import std.stdio; + +void main() { + int[] slice = [ 1, 3, 5, 7, 9, 11, 13, 15 ]; + int[] half = slice[0 .. $ / 2]; + int[] quarter = slice[0 .. $ / 4]; + + quarter[1] = 0; // modify through one slice + + writeln(quarter); + writeln(half); + writeln(slice); +} +--- + +$(P +The effect of the modification to the second element of $(C quarter) is seen through all slices: +) + +$(SHELL +[1, $(HILITE 0)] +[1, $(HILITE 0), 5, 7] +[1, $(HILITE 0), 5, 7, 9, 11, 13, 15] +) + +$(P +$(IX stomping) When viewed this way, slices provide $(I shared) access to elements. This sharing opens the question of what happens when a new element is added to one of the slices. Since multiple slices can provide access to same elements, there may not be room to add elements to a slice without $(I stomping) on the elements of others. +) + +$(P +D disallows element stomping and answers this question by terminating the sharing relationship if there is no room for the new element: The slice that has no room to grow leaves the sharing. When this happens, all of the existing elements of that slice are copied to a new place automatically and the slice starts providing access to these new elements. +) + +$(P +To see this in action, let's add an element to $(C quarter) before modifying its second element: +) + +--- + quarter ~= 42; // this slice leaves the sharing because + // there is no room for the new element + + quarter[1] = 0; // for that reason this modification + // does not affect the other slices +--- + +$(P +The output of the program shows that the modification to the $(C quarter) slice does not affect the others: +) + +$(SHELL +[1, $(HILITE 0), 42] +[1, 3, 5, 7] +[1, 3, 5, 7, 9, 11, 13, 15] +) + +$(P +Explicitly increasing the length of a slice makes it leave the sharing as well: +) + +--- + ++quarter.length; // leaves the sharing +--- + +$(P +or +) + +--- + quarter.length += 5; // leaves the sharing +--- + +$(P +On the other hand, shortening a slice does not affect sharing. Shortening the slice merely means that the slice now provides access to fewer elements: +) + +--- + int[] a = [ 1, 11, 111 ]; + int[] d = a; + + d = d[1 .. $]; // shortening from the beginning + d[0] = 42; // modifying the element through the slice + + writeln(a); // printing the other slice +--- + +$(P +As can be seen from the output, the modification through $(C d) is seen through $(C a); the sharing is still in effect: +) + +$(SHELL +[1, $(HILITE 42), 111] +) + +$(P +Reducing the length in different ways does not terminate the sharing either: +) + +--- + d = d[0 .. $ - 1]; // shortening from the end + --d.length; // same thing + d.length = d.length - 1; // same thing +--- + +$(P +Sharing of elements is still in effect. +) + +$(H6 $(IX .capacity) Using $(C capacity) to determine whether sharing will be terminated) + +$(P +There are cases when slices continue sharing elements even after an element is added to one of them. This happens when the element is added to the longest slice and there is room at the end of it: +) + +--- +import std.stdio; + +void main() { + int[] slice = [ 1, 3, 5, 7, 9, 11, 13, 15 ]; + int[] half = slice[0 .. $ / 2]; + int[] quarter = slice[0 .. $ / 4]; + + slice ~= 42; // adding to the longest slice ... + $(HILITE slice[1] = 0); // ... and then modifying an element + + writeln(quarter); + writeln(half); + writeln(slice); +} +--- + +$(P +As seen in the output, although the added element increases the length of a slice, the sharing has not been terminated, and the modification is seen through all slices: +) + +$(SHELL +[1, $(HILITE 0)] +[1, $(HILITE 0), 5, 7] +[1, $(HILITE 0), 5, 7, 9, 11, 13, 15, 42] +) + +$(P +The $(C capacity) property of slices determines whether the sharing will be terminated if an element is added to a particular slice. ($(C capacity) is actually a function but this distinction does not have any significance in this discussion.) +) + +$(P +The value of $(C capacity) has the following meanings: +) + +$(UL + +$(LI +When its value is 0, it means that this is not the longest original slice. In this case, adding a new element would definitely relocate the elements of the slice and the sharing would terminate. +) + +$(LI +When its value is nonzero, it means that this is the longest original slice. In this case $(C capacity) denotes the total number of elements that this slice can hold without needing to be copied. The number of $(I new elements) that can be added can be calculated by subtracting the actual length of the slice from the capacity value. If the length of the slice equals its capacity, then the slice will be copied to a new location if one more element is added.) + +) + +$(P +Accordingly, a program that needs to determine whether the sharing will terminate should use a logic similar to the following: +) + +--- + if (slice.capacity == 0) { + /* Its elements would be relocated if one more element + * is added to this slice. */ + + // ... + + } else { + /* This slice may have room for new elements before + * needing to be relocated. Let's calculate how + * many: */ + auto howManyNewElements = slice.capacity - slice.length; + + // ... + } +--- + +$(P +An interesting corner case is when there are more than one slice to $(I all elements). In such a case all slices report to have capacity: +) + +--- +import std.stdio; + +void main() { + // Three slices to all elements + int[] s0 = [ 1, 2, 3, 4 ]; + int[] s1 = s0; + int[] s2 = s0; + + writeln(s0.capacity); + writeln(s1.capacity); + writeln(s2.capacity); +} +--- + +$(P +All three have capacity: +) + +$(SHELL +7 +7 +7 +) + +$(P +However, as soon as an element is added to one of the slices, the capacity of the others drop to 0: +) + +--- + $(HILITE s1 ~= 42); $(CODE_NOTE s1 becomes the longest) + + writeln(s0.capacity); + writeln(s1.capacity); + writeln(s2.capacity); +--- + +$(P +Since the slice with the added element is now the longest, it is the only one with capacity: +) + +$(SHELL +0 +7 $(SHELL_NOTE now only s1 has capacity) +0 +) + +$(H6 $(IX .reserve) Reserving room for elements) + +$(P +Both copying elements and allocating new memory to increase capacity have some cost. For that reason, appending an element can be an expensive operation. When the number of elements to append is known beforehand, it is possible to reserve capacity for the elements: +) + +--- +import std.stdio; + +void main() { + int[] slice; + + slice$(HILITE .reserve(20)); + writeln(slice.capacity); + + foreach (element; 0 .. $(HILITE 17)) { + slice ~= element; $(CODE_NOTE these elements will not be moved) + } +} +--- + +$(SHELL +31 $(SHELL_NOTE Capacity for at least 20 elements) +) + +$(P +The elements of $(C slice) would be moved only after there are more than 31 elements. +) + +$(H5 $(IX array-wise operation) $(IX elements, operation on all) Operations on all elements) + +$(P +This feature is for both fixed-length arrays and slices. +) + +$(P +The $(C []) characters written after the name of an array means $(I all elements). This feature simplifies the program when certain operations need to be applied to all of the elements of an array. +) + +--- +import std.stdio; + +void main() { + double[3] a = [ 10, 20, 30 ]; + double[3] b = [ 2, 3, 4 ]; + + double[3] result = $(HILITE a[] + b[]); + + writeln(result); +} +--- + +$(P +The output: +) + +$(SHELL +[12, 23, 34] +) + +$(P +The addition operation in that program is applied to the corresponding elements of both of the arrays in order: First the first elements are added, then the second elements are added, etc. A natural requirement is that the lengths of the two arrays must be equal. +) + +$(P +The operator can be one of the arithmetic operators $(C +), $(C -), $(C *), $(C /), $(C %), and $(C ^^); one of the binary operators $(C ^), $(C &), and $(C |); as well as the unary operators $(C -) and $(C ~) that are typed in front of an array. We will see some of these operators in later chapters. +) + +$(P +The assignment versions of these operators can also be used: $(C =), $(C +=), $(C -=), $(C *=), $(C /=), $(C %=), $(C ^^=), $(C ^=), $(C &=), and $(C |=). +) + +$(P +This feature works not only using two arrays; it can also be used with an array and a compatible expression. For example, the following operation divides all elements of an array by four: +) + +--- + double[3] a = [ 10, 20, 30 ]; + $(HILITE a[]) /= 4; + + writeln(a); +--- + +$(P +The output: +) + +$(SHELL +[2.5, 5, 7.5] +) + +$(P +To assign a specific value to all elements: +) + +--- + $(HILITE a[]) = 42; + writeln(a); +--- + +$(P +The output: +) + +$(SHELL +[42, 42, 42] +) + +$(P +This feature requires great attention when used with slices. Although there is no apparent difference in element values, the following two expressions have very different meanings: +) + +--- + slice2 = slice1; // ← slice2 starts providing access + // to the same elements that + // slice1 provides access to + + slice3[] = slice1; // ← the values of the elements of + // slice3 change +--- + +$(P +The assignment of $(C slice2) makes it share the same elements as $(C slice1). On the other hand, since $(C slice3[]) means $(I all elements of $(C slice3)), the values of its elements become the same as the values of the elements of $(C slice1). The effect of the presence or absence of the $(C []) characters cannot be ignored. +) + +$(P +We can see an example of this difference in the following program: +) + +--- +import std.stdio; + +void main() { + double[] slice1 = [ 1, 1, 1 ]; + double[] slice2 = [ 2, 2, 2 ]; + double[] slice3 = [ 3, 3, 3 ]; + + slice2 = slice1; // ← slice2 starts providing access + // to the same elements that + // slice1 provides access to + + slice3[] = slice1; // ← the values of the elements of + // slice3 change + + writeln("slice1 before: ", slice1); + writeln("slice2 before: ", slice2); + writeln("slice3 before: ", slice3); + + $(HILITE slice2[0] = 42); // ← the value of an element that + // it shares with slice1 changes + + slice3[0] = 43; // ← the value of an element that + // only it provides access to + // changes + + writeln("slice1 after : ", slice1); + writeln("slice2 after : ", slice2); + writeln("slice3 after : ", slice3); +} +--- + +$(P +The modification through $(C slice2) affects $(C slice1) too: +) + +$(SHELL +slice1 before: [1, 1, 1] +slice2 before: [1, 1, 1] +slice3 before: [1, 1, 1] +slice1 after : [$(HILITE 42), 1, 1] +slice2 after : [$(HILITE 42), 1, 1] +slice3 after : [43, 1, 1] +) + +$(P +The danger here is that the potential bug may not be noticed until after the value of a shared element is changed. +) + +$(H5 $(IX multi-dimensional array) Multi-dimensional arrays) + +$(P +So far we have used arrays with only fundamental types like $(C int) and $(C double). The element type can actually be any other type, including other arrays. This enables the programmer to define complex containers like $(I array of arrays). Arrays of arrays are called $(I multi-dimensional arrays). +) + +$(P +The elements of all of the arrays that we have defined so far have been written in the source code from left to right. To help us understand the concept of a two-dimensional array, let's define an array from top to bottom this time: +) + +--- + int[] array = [ + 10, + 20, + 30, + 40 + ]; +--- + +$(P +As you remember, most spaces in the source code are used to help with readability and do not change the meaning of the code. The array above could have been defined on a single line and would have the same meaning. +) + +$(P +Let's now replace every element of that array with another array: +) + +--- + /* ... */ array = [ + [ 10, 11, 12 ], + [ 20, 21, 22 ], + [ 30, 31, 32 ], + [ 40, 41, 42 ] + ]; +--- + +$(P +We have replaced elements of type $(C int) with elements of type $(C int[]). To make the code conform to the array definition syntax, we must now specify the type of the elements as $(C int[]) instead of $(C int): +) + +--- + $(HILITE int[])[] array = [ + [ 10, 11, 12 ], + [ 20, 21, 22 ], + [ 30, 31, 32 ], + [ 40, 41, 42 ] + ]; +--- + +$(P +Such arrays are called $(I two-dimensional arrays) because they can be seen as having rows and columns. +) + +$(P +Two-dimensional arrays are used the same way as any other array as long as we remember that each element is an array itself and is used in array operations: +) + +--- + array ~= [ 50, 51 ]; // adds a new element (i.e. a slice) + array[0] ~= 13; // adds to the first element +--- + +$(P +The new state of the array: +) + +$(SHELL_SMALL +[[10, 11, 12, $(HILITE 13)], [20, 21, 22], [30, 31, 32], [40, 41, 42], $(HILITE [50, 51])] +) + +$(P +Arrays and elements can be fixed-length as well. The following is a three-dimensional array where all dimensions are fixed-length: +) + +--- + int[2][3][4] array; // 2 columns, 3 rows, 4 pages +--- + +$(P +The definition above can be seen as $(I four pages of three rows of two columns of integers). As an example, such an array can be used to represent a 4-story building in an adventure game, each story consisting of 2x3=6 rooms. +) + +$(P +For example, the number of items in the first room of the second floor can be incremented like the following: +) + +--- + // The index of the second floor is 1, and the first room + // of that floor is accessed by [0][0] + ++itemCounts[1][0][0]; +--- + +$(P +In addition to the syntax above, the $(C new) expression can also be used to create a $(I slice of slices). The following example uses only two dimensions: +) + +--- +import std.stdio; + +void main() { + int[][] s = new int[][](2, 3); + writeln(s); +} +--- + +$(P +The $(C new) expression above creates 2 slices containing 3 elements each and returns a slice that provides access to those slices and elements. The output: +) + +$(SHELL +[[0, 0, 0], [0, 0, 0]] +) + +$(H5 Summary) + +$(UL + +$(LI +Fixed-length arrays own their elements; slices provide access to elements that don't belong exclusively to them. +) + +$(LI +Within the $(C []) operator, $(C $) is the equivalent of $(C $(I array_name).length). +) + +$(LI +$(C .dup) makes a new array that consists of the copies of the elements of an existing array. +) + +$(LI +With fixed-length arrays, the assignment operation changes the values of elements; with slices, it makes the slices start providing access to other elements. +) + +$(LI +Slices that get longer $(I may) stop sharing elements and start providing access to newly copied elements. $(C capacity) determines whether this will be the case. +) + +$(LI +The syntax $(C array[]) means $(I all elements of the array); the operation that is applied to it is applied to each element individually. +) + +$(LI +Arrays of arrays are called multi-dimensional arrays. +) + +) + +$(PROBLEM_TEK + +$(P +Iterate over the elements of an array of $(C double)s and halve the ones that are greater than 10. For example, given the following array: +) + +--- + double[] array = [ 1, 20, 2, 30, 7, 11 ]; +--- + +$(P +Modify it as the following: +) + +$(SHELL +[1, $(HILITE 10), 2, $(HILITE 15), 7, $(HILITE 5.5)] +) + +$(P +Although there are many solutions of this problem, try to use only the features of slices. You can start with a slice that provides access to all elements. Then you can shorten the slice from the beginning and always use the first element. +) + +$(P +The following expression shortens the slice from the beginning: +) + +--- + slice = slice[1 .. $]; +--- + +) + +Macros: + SUBTITLE=Slices and Other Array Features + + DESCRIPTION=More features of D slices and arrays + + KEYWORDS=d programming language tutorial book arrays slices fixed-length dynamic diff --git a/target/special_functions.d b/target/special_functions.d new file mode 100644 index 0000000..00accc3 --- /dev/null +++ b/target/special_functions.d @@ -0,0 +1,1234 @@ +Ddoc + +$(DERS_BOLUMU Constructor and Other Special Functions) + +$(P +Although this chapter focuses only on structs, the topics that are covered here apply mostly to classes as well. The differences will be explained in later chapters. +) + +$(P +Four member functions of structs are special because they define the fundamental operations of that type: +) +$(UL +$(LI Constructor: $(C this())) +$(LI Destructor: $(C ~this())) +$(LI Postblit: $(C this(this))) +$(LI Assignment operator: $(C opAssign())) +) + +$(P +Although these fundamental operations are handled automatically for structs, hence need not be defined by the programmer, they can be overridden to make the $(C struct) behave in special ways. +) + +$(H5 $(IX constructor) $(IX this, constructor) Constructor) + +$(P +The responsibility of the constructor is to prepare an object for use by assigning appropriate values to its members. +) + +$(P +We have already used constructors in previous chapters. When the name of a type is used like a function, it is actually the constructor that gets called. We can see this on the right-hand side of the following line: +) + +--- + auto busArrival = $(HILITE TimeOfDay)(8, 30); +--- + +$(P +Similarly, a $(I class) object is being constructed on the right hand side of the following line: +) + +--- + auto variable = new $(HILITE SomeClass()); +--- + +$(P +The arguments that are specified within parentheses correspond to the constructor parameters. For example, the values 8 and 30 above are passed to the $(C TimeOfDay) constructor as its parameters. +) + +$(P +In addition to different object construction syntaxes that we have seen so far; $(C const), $(C immutable), and $(C shared) objects can be constructed with the $(I type constructor) syntax as well (e.g. as $(C immutable(S)(2))). (We will see the $(C shared) keyword in $(LINK2 /ders/d.en/concurrency_shared.html, a later chapter).) +) + +$(P +For example, although all three variables below are $(C immutable), the construction of variable $(C a) is semantically different from the constructions of variables $(C b) and $(C c): +) + +--- + /* More familiar syntax; immutable variable of a mutable + * type: */ + $(HILITE immutable) a = S(1); + + /* Type constructor syntax; a variable of an immutable + * type: */ + auto b = $(HILITE immutable(S))(2); + + /* Same meaning as 'b' */ + immutable c = $(HILITE immutable(S))(3); +--- + +$(H6 Constructor syntax) + +$(P +Different from other functions, constructors do not have return values. The name of the constructor is always $(C this): +) + +--- +struct SomeStruct { + // ... + + this(/* constructor parameters */) { + // ... operations that prepare the object for use ... + } +} +--- + +$(P +The constructor parameters include information that is needed to make a useful and consistent object. +) + +$(H6 $(IX automatic constructor) $(IX default constructor) Compiler-generated automatic constructor) + +$(P +All of the structs that we have seen so far have been taking advantage of a constructor that has been generated automatically by the compiler. The automatic constructor assigns the parameter values to the members in the order that they are specified. +) + +$(P +As you will remember from $(LINK2 /ders/d.en/struct.html, the Structs chapter), the initial values for the trailing members need not be specified. The members that are not specified get initialized by the $(C .init) value of their respective types. The $(C .init) values of a member could be provided during the definition of that member after the $(C =) operator: +) + +--- +struct Test { + int member $(HILITE = 42); +} +--- + +$(P +Also considering the $(I default parameter values) feature from $(LINK2 /ders/d.en/parameter_flexibility.html, the Variable Number of Parameters chapter), we can imagine that the automatic constructor for the following $(C struct) would be the equivalent of the following $(C this()): +) + +--- +struct Test { + char c; + int i; + double d; + + /* The equivalent of the compiler-generated automatic + * constructor (Note: This is only for demonstration; the + * following constructor would not actually be called + * when default-constructing the object as Test().) */ + this(in char c_parameter = char.init, + in int i_parameter = int.init, + in double d_parameter = double.init) { + c = c_parameter; + i = i_parameter; + d = d_parameter; + } +} +--- + +$(P +For most structs, the compiler-generated constructor is sufficient: Usually, providing appropriate values for each member is all that is needed for objects to be constructed. +) + +$(H6 $(IX this, member access) Accessing the members by $(C this.)) + +$(P +To avoid mixing the parameters with the members, the parameter names above had $(C _parameter) appended to their names. There would be compilation errors without doing that: +) + +--- +struct Test { + char c; + int i; + double d; + + this(in char c = char.init, + in int i = int.init, + in double d = double.init) { + // An attempt to assign an 'in' parameter to itself! + c = c; $(DERLEME_HATASI) + i = i; + d = d; + } +} +--- + +$(P +The reason is; $(C c) alone would mean the parameter, not the member, and as the parameters above are defined as $(C in), they cannot be modified: +) + +$(SHELL +Error: variable deneme.Test.this.c $(HILITE cannot modify const) +) + +$(P +A solution is to prepend the member names with $(C this.). Inside member functions, $(C this) means "this object", making $(C this.c) mean "the c member of this object": +) + +--- + this(in char c = char.init, + in int i = int.init, + in double d = double.init) { + $(HILITE this.)c = c; + $(HILITE this.)i = i; + $(HILITE this.)d = d; + } +--- + +$(P +Now $(C c) alone means the parameter and $(C this.c) means the member, and the code compiles and works as expected: The member $(C c) gets initialized by the value of the parameter $(C c). +) + +$(H6 $(IX user defined constructor) User-defined constructors) + +$(P +I have described the behavior of the compiler-generated constructor. Since that constructor is suitable for most cases, there is no need to define a constructor by hand. +) + +$(P +Still, there are cases where constructing an object involves more complicated operations than assigning values to each member in order. As an example, let's consider $(C Duration) from the earlier chapters: +) + +--- +struct Duration { + int minute; +} +--- + +$(P +The compiler-generated constructor is sufficient for this single-member struct: +) + +--- + time.decrement(Duration(12)); +--- + +$(P +Since that constructor takes the duration in minutes, the programmers would sometimes need to make calculations: +) + +--- + // 23 hours and 18 minutes earlier + time.decrement(Duration(23 * 60 + 18)); + + // 22 hours and 20 minutes later + time.increment(Duration(22 * 60 + 20)); +--- + +$(P +To eliminate the need for these calculations, we can design a $(C Duration) constructor that takes two parameters and makes the calculation automatically: +) + +--- +struct Duration { + int minute; + + this(int hour, int minute) { + this.minute = hour * 60 + minute; + } +} +--- + +$(P +Since hour and minute are now separate parameters, the users simply provide their values without needing to make the calculation themselves: +) + +--- + // 23 hours and 18 minutes earlier + time.decrement(Duration($(HILITE 23, 18))); + + // 22 hours and 20 minutes later + time.increment(Duration($(HILITE 22, 20))); +--- + +$(H6 User-defined constructor disables compiler-generated constructor) + +$(P +A constructor that is defined by the programmer makes some uses of the compiler-generated constructor invalid: Objects cannot be constructed by $(I default parameter values) anymore. For example, trying to construct $(C Duration) by a single parameter is a compilation error: +) + +--- + time.decrement(Duration(12)); $(DERLEME_HATASI) +--- + +$(P +Calling the constructor with a single parameter does not match the programmer's constructor and the compiler-generated constructor is disabled. +) + +$(P +One solution is to $(I overload) the constructor by providing another constructor that takes just one parameter: +) + +--- +struct Duration { + int minute; + + this(int hour, int minute) { + this.minute = hour * 60 + minute; + } + + this(int minute) { + this.minute = minute; + } +} +--- + +$(P +A user-defined constructor disables constructing objects by the $(C { }) syntax as well: +) + +--- + Duration duration = { 5 }; $(DERLEME_HATASI) +--- + +$(P +Initializing without providing any parameter is still valid: +) + +--- + auto d = Duration(); // compiles +--- + +$(P +The reason is, in D, the $(C .init) value of every type must be known at compile time. The value of $(C d) above is equal to the initial value of $(C Duration): +) + +--- + assert(d == Duration.init); +--- + +$(H6 $(IX static opCall) $(IX opCall, static) $(C static opCall) instead of the default constructor) + +$(P +Because the initial value of every type must be known at compile time, it is impossible to define the default constructor explicitly. +) + +$(P +Let's consider the following constructor that tries to print some information every time an object of that type is constructed: +) + +--- +struct Test { + this() { $(DERLEME_HATASI) + writeln("A Test object is being constructed."); + } +} +--- + +$(P +The compiler output: +) + +$(SHELL +Error: constructor deneme.Deneme.this default constructor for +structs only allowed with @disable and no body +) + +$(P $(I $(B Note:) We will see in later chapters that it is possible to define the default constructor for classes. +)) + +$(P +As a workaround, a no-parameter $(C static opCall()) can be used for constructing objects without providing any parameters. Note that this has no effect on the $(C .init) value of the type. +) + +$(P +For this to work, $(C static opCall()) must construct and return an object of that struct type: +) + +--- +import std.stdio; + +struct Test { + $(HILITE static) Test $(HILITE opCall)() { + writeln("A Test object is being constructed."); + Test test; + return test; + } +} + +void main() { + auto test = $(HILITE Test()); +} +--- + +$(P +The $(C Test()) call in $(C main()) executes $(C static opCall()): +) + +$(SHELL +A Test object is being constructed. +) + +$(P +Note that it is not possible to type $(C Test()) inside $(C static opCall()). That syntax would execute $(C static opCall()) as well and cause an infinite recursion: +) + +--- + static Test opCall() { + writeln("A Test object is being constructed."); + return $(HILITE Test()); // ← Calls 'static opCall()' again + } +--- + +$(P +The output: +) + +$(SHELL +A Test object is being constructed. +A Test object is being constructed. +A Test object is being constructed. +... $(SHELL_NOTE repeats the same message) +) + +$(H6 Calling other constructors) + +$(P +Constructors can call other constructors to avoid code duplication. Although $(C Duration) is too simple to demonstrate how useful this feature is, the following single-parameter constructor takes advantage of the two-parameter constructor: +) + +--- + this(int hour, int minute) { + this.minute = hour * 60 + minute; + } + + this(int minute) { + this(0, minute); // calls the other constructor + } +--- + +$(P +The constructor that only takes the minute value calls the other constructor by passing 0 as the value of hour. +) + +$(P +$(I $(B Warning:) There is a design flaw in the $(C Duration) constructors above because the intention is not clear when the objects are constructed by a single parameter): +) + +--- + // 10 hours or 10 minutes? + auto travelDuration = Duration(10); +--- + +$(P +Although it is possible to determine by reading the documentation or the code of the struct that the parameter actually means "10 minutes," it is an inconsistency as the first parameter of the two-parameter constructor is $(I hours). +) + +$(P +Such design mistakes are causes of bugs and must be avoided. +) + +$(H6 $(IX constructor qualifier) $(IX qualifier, constructor) Constructor qualifiers) + +$(P +Normally, the same constructor is used for $(I mutable), $(C const), $(C immutable), and $(C shared) objects: +) + +--- +import std.stdio; + +struct S { + this(int i) { + writeln("Constructing an object"); + } +} + +void main() { + auto m = S(1); + const c = S(2); + immutable i = S(3); + shared s = S(4); +} +--- + +$(P +Semantically, the objects that are constructed on the right-hand sides of those expressions are all mutable but the variables have different type qualifiers. The same constructor is used for all of them: +) + +$(SHELL +Constructing an object +Constructing an object +Constructing an object +Constructing an object +) + +$(P +Depending on the qualifier of the resulting object, sometimes some members may need to be initialized differently or need not be initialized at all. For example, since no member of an $(C immutable) object can be mutated throughout the lifetime of that object, leaving its mutable members uninitialized can improve program performance. +) + +$(P +$(I Qualified constructors) can be defined differently for objects with different qualifiers: +) + +--- +import std.stdio; + +struct S { + this(int i) { + writeln("Constructing an object"); + } + + this(int i) $(HILITE const) { + writeln("Constructing a const object"); + } + + this(int i) $(HILITE immutable) { + writeln("Constructing an immutable object"); + } + + // We will see the 'shared' keyword in a later chapter. + this(int i) $(HILITE shared) { + writeln("Constructing a shared object"); + } +} + +void main() { + auto m = S(1); + const c = S(2); + immutable i = S(3); + shared s = S(4); +} +--- + +$(P +However, as indicated above, as the right-hand side expressions are all semantically mutable, those objects are still constructed with the $(I mutable) object contructor: +) + +$(SHELL +Constructing an object +Constructing an object $(SHELL_NOTE_WRONG NOT the const constructor) +Constructing an object $(SHELL_NOTE_WRONG NOT the immutable constructor) +Constructing an object $(SHELL_NOTE_WRONG NOT the shared constructor) +) + +$(P +$(IX type constructor) To take advantage of qualified constructors, one must use the $(I type constructor) syntax. (The term $(I type constructor) should not be confused with object constructors; type constructor is related to types, not objects.) This syntax $(I makes) a different type by combining a qualifier with an existing type. For example, $(C immutable(S)) is a qualified type made from $(C immutable) and $(C S): +) + +--- + auto m = S(1); + auto c = $(HILITE const(S))(2); + auto i = $(HILITE immutable(S))(3); + auto s = $(HILITE shared(S))(4); +--- + +$(P +This time, the objects that are in the right-hand expressions are different: $(I mutable), $(C const), $(C immutable), and $(C shared), respectively. As a result, each object is constructed with its matching constructor: +) + +$(SHELL +Constructing an object +Constructing a $(HILITE const) object +Constructing an $(HILITE immutable) object +Constructing a $(HILITE shared) object +) + +$(P +Note that, since all of the variables above are defined with the $(C auto) keyword, they are correctly inferred to be $(I mutable), $(C const), $(C immutable), and $(C shared), respectively. +) + +$(H6 Immutability of constructor parameters) + +$(P +In the $(LINK2 /ders/d.en/const_and_immutable.html, Immutability chapter) we have seen that it is not easy to decide whether parameters of reference types should be defined as $(C const) or $(C immutable). Although the same considerations apply for constructor parameters as well, $(C immutable) is usually a better choice for constructor parameters. +) + +$(P +The reason is, it is common to assign the parameters to members to be used at a later time. When a parameter is not $(C immutable), there is no guarantee that the original variable will not change by the time the member gets used. +) + +$(P +Let's consider a constructor that takes a file name as a parameter. The file name will be used later on when writing student grades. According to the guidelines in the $(LINK2 /ders/d.en/const_and_immutable.html, Immutability chapter), to be more useful, let's assume that the constructor parameter is defined as $(C const char[]): +) + +--- +import std.stdio; + +struct Student { + $(HILITE const char[]) fileName; + int[] grades; + + this($(HILITE const char[]) fileName) { + this.fileName = fileName; + } + + void save() { + auto file = File(fileName.idup, "w"); + file.writeln("The grades of the student:"); + file.writeln(grades); + } + + // ... +} + +void main() { + char[] fileName; + fileName ~= "student_grades"; + + auto student = Student(fileName); + + // ... + + /* Assume the fileName variable is modified later on + * perhaps unintentionally (all of its characters are + * being set to 'A' here): */ + $(HILITE fileName[] = 'A'); + + // ... + + /* The grades would be written to the wrong file: */ + student.save(); +} +--- + +$(P +The program above saves the grades of the student under a file name that consists of A characters, not to $(STRING "student_grades"). For that reason, sometimes it is more suitable to define constructor parameters and members of reference types as $(C immutable). We know that this is easy for strings by using aliases like $(C string). The following code shows the parts of the struct that would need to be modified: +) + +--- +struct Student { + $(HILITE string) fileName; + // ... + this($(HILITE string) fileName) { + // ... + } + // ... +} +--- + +$(P +Now the users of the struct must provide $(C immutable) strings and as a result the confusion about the name of the file would be prevented. +) + +$(H6 $(IX type conversion, constructor) Type conversions through single-parameter constructors) + +$(P +Single-parameter constructors can be thought of as providing a sort of type conversion: They produce an object of the particular struct type starting from a constructor parameter. For example, the following constructor produces a $(C Student) object from a $(C string): +) + +--- +struct Student { + string name; + + this(string name) { + this.name = name; + } +} +--- + +$(P +$(C to()) and $(C cast) observe this behavior as a $(I conversion) as well. To see examples of this, let's consider the following $(C salute()) function. Sending a $(C string) parameter when it expects a $(C Student) would naturally cause a compilation error: +) + +--- +void salute(Student student) { + writeln("Hello ", student.name); +} +// ... + salute("Jane"); $(DERLEME_HATASI) +--- + +$(P +On the other hand, all of the following lines ensure that a $(C Student) object is constructed before calling the function: +) + +--- +import std.conv; +// ... + salute(Student("Jane")); + salute(to!Student("Jean")); + salute(cast(Student)"Jim"); +--- + +$(P +$(C to) and $(C cast) take advantage of the single-parameter constructor by constructing a temporary $(C Student) object and calling $(C salute()) with that object. +) + +$(H6 $(IX @disable, constructor) Disabling the default constructor) + +$(P +Functions that are declared as $(C @disable) cannot be called. +) + +$(P +Sometimes there are no sensible default values for the members of a type. For example, it may be illegal for the following type to have an empty file name: +) + +--- +struct Archive { + string fileName; +} +--- + +$(P +Unfortunately, the compiler-generated default constructor would initialize $(C fileName) as empty: +) + +--- + auto archive = Archive(); // ← fileName member is empty +--- + +$(P +The default constructor can explicitly be disabled by declaring it as $(C @disable) so that objects must be constructed by one of the other constructors. There is no need to provide a body for a disabled function: +) + +--- +struct Archive { + string fileName; + + $(HILITE @disable this();) $(CODE_NOTE cannot be called) + + this(string fileName) { $(CODE_NOTE can be called) + // ... + } +} + +// ... + + auto archive = Archive(); $(DERLEME_HATASI) +--- + +$(P +This time the compiler does not allow calling $(C this()): +) + +$(SHELL +Error: constructor deneme.Archive.this is $(HILITE not callable) because +it is annotated with @disable +) + +$(P +Objects of $(C Archive) must be constructed either with one of the other constructors or explicitly with its $(C .init) value: +) + +--- + auto a = Archive("records"); $(CODE_NOTE compiles) + auto b = Archive.init; $(CODE_NOTE compiles) +--- + +$(H5 $(IX destructor) $(IX ~this) Destructor) + +$(P +The destructor includes the operations that must be executed when the lifetime of an object ends. +) + +$(P +The compiler-generated automatic destructor executes the destructors of all of the members in order. For that reason, as it is with the constructor, there is no need to define a destructor for most structs. +) + +$(P +However, sometimes some special operations may need to be executed when an object's lifetime ends. For example, an operating system resource that the object owns may need to be returned to the system; a member function of another object may need to be called; a server running somewhere on the network may need to be notified that a connection to it is about to be terminated; etc. +) + +$(P +The name of the destructor is $(C ~this) and just like constructors, it has no return type. +) + +$(H6 Destructor is executed automatically) + +$(P +The destructor is executed as soon as the lifetime of the struct object ends. (This is not the case for objects that are constructed with the $(C new) keyword.) +) + +$(P +As we have seen in the $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations chapter,) the lifetime of an object ends when leaving the scope that it is defined in. The following are times when the lifetime of a struct ends: +) + +$(UL +$(LI When leaving the scope of the object either normally or due to a thrown exception: + +--- + if (aCondition) { + auto duration = Duration(7); + // ... + + } // ← The destructor is executed for 'duration' + // at this point +--- + +) + +$(LI Anonymous objects are destroyed at the end of the whole expression that they are constructed in: + +--- + time.increment(Duration(5)); // ← The Duration(5) object + // gets destroyed at the end + // of the whole expression. +--- + +) + +$(LI All of the struct members of a struct object get destroyed when the outer object is destroyed. + +) + +) + +$(H6 Destructor example) + +$(P +Let's design a type for generating simple XML documents. XML elements are defined by angle brackets. They contain data and other XML elements. XML elements can have attributes as well; we will ignore them here. +) + +$(P +Our aim will be to ensure that an element that has been $(I opened) by a $(C <name>) tag will always be $(I closed) by a matching $(C </name>) tag: +) + +$(MONO + <class1> ← opening the outer XML element + <grade> ← opening the inner XML element + 57 ← the data + </grade> ← closing the inner element + </class1> ← closing the outer element +) + +$(P +A struct that can produce the output above can be designed by two members that store the tag for the XML element and the indentation to use when printing it: +) + +--- +struct XmlElement { + string name; + string indentation; +} +--- + +$(P +If the responsibilities of opening and closing the XML element are given to the constructor and the destructor, respectively, the desired output can be produced by managing the lifetimes of XmlElement objects. For example, the constructor can print $(C <tag>) and the destructor can print $(C </tag>). +) + +$(P +The following definition of the constructor produces the opening tag: +) + +--- + this(in string name, in int level) { + this.name = name; + this.indentation = indentationString(level); + + writeln(indentation, '<', name, '>'); + } +--- + +$(P +$(C indentationString()) is the following function: +) + +--- +import std.array; +// ... +string indentationString(in int level) { + return replicate(" ", level * 2); +} +--- + +$(P +The function calls $(C replicate()) from the $(C std.array) module, which makes and returns a new string made up of the specified value repeated the specified number of times. +) + +$(P +The destructor can be defined similar to the constructor to produce the closing tag: +) + +--- + ~this() { + writeln(indentation, "'); + } +--- + +$(P +Here is a test code to demonstrate the effects of the automatic constructor and destructor calls: +) + +--- +import std.conv; +import std.random; +import std.array; + +string indentationString(in int level) { + return replicate(" ", level * 2); +} + +struct XmlElement { + string name; + string indentation; + + this(in string name, in int level) { + this.name = name; + this.indentation = indentationString(level); + + writeln(indentation, '<', name, '>'); + } + + ~this() { + writeln(indentation, "'); + } +} + +void main() { + immutable classes = XmlElement("classes", 0); + + foreach (classId; 0 .. 2) { + immutable classTag = "class" ~ to!string(classId); + immutable classElement = XmlElement(classTag, 1); + + foreach (i; 0 .. 3) { + immutable gradeElement = XmlElement("grade", 2); + immutable randomGrade = uniform(50, 101); + + writeln(indentationString(3), randomGrade); + } + } +} +--- + +$(P +Note that the $(C XmlElement) objects are created in three separate scopes in the program above. The opening and closing tags of the XML elements in the output are produced solely by the constructor and the destructor of $(C XmlElement). +) + +$(SHELL +<classes> + <class0> + <grade> + 72 + </grade> + <grade> + 97 + </grade> + <grade> + 90 + </grade> + </class0> + <class1> + <grade> + 77 + </grade> + <grade> + 87 + </grade> + <grade> + 56 + </grade> + </class1> +</classes> +) + +$(P +The $(C <classes>) element is produced by the $(C classesElement) variable. Because that variable is constructed first in $(C main()), the output contains the output of its construction first. Since it is also the variable that is destroyed last, upon leaving $(C main()), the output contains the output of the destructor call for its destruction last. +) + +$(H5 $(IX postblit) $(IX this(this)) Postblit) + +$(P +$(I Copying) is constructing a new object from an existing one. Copying involves two steps: +) + +$(OL + +$(LI Copying the members of the existing object to the new object bit-by-bit. This step is called $(I blit), short for $(I block transfer). +) + +$(LI Making further adjustments to the new object. This step is called $(I postblit). +) + +) + +$(P +The first step is handled automatically by the compiler: It copies the members of the existing object to the members of the new object: +) + +--- + auto returnTripDuration = tripDuration; // copying +--- + +$(P +Do not confuse copying with $(I assignment). The $(C auto) keyword above is an indication that a new object is being defined. (The actual type name could have been spelled out instead of $(C auto).) +) + +$(P +For an operation to be assignment, the object on the left-hand side must be an existing object. For example, assuming that $(C returnTripDuration) has already been defined: +) + +--- + returnTripDuration = tripDuration; // assignment (see below) +--- + +$(P +Sometimes it is necessary to make adjustments to the members of the new object after the automatic blit. These operations are defined in the postblit function of the struct. +) + +$(P +Since it is about object construction, the name of the postblit is $(C this) as well. To separate it from the other constructors, its parameter list contains the keyword $(C this): + +) + +--- + this(this) { + // ... + } +--- + +$(P +We have defined a $(C Student) type in the $(LINK2 /ders/d.en/struct.html, Structs chapter), which had a problem about copying objects of that type: +) + +--- +struct Student { + int number; + int[] grades; +} +--- + +$(P +Being a slice, the $(C grades) member of that $(C struct) is a reference type. The consequence of copying a $(C Student) object is that the $(C grades) members of both the original and the copy provide access to the same actual array elements of type $(C int). As a result, the effect of modifying a grade through one of these objects is seen through the other object as well: +) + +--- + auto student1 = Student(1, [ 70, 90, 85 ]); + + auto student2 = student1; // copying + student2.number = 2; + + student1.grades[0] += 5; // this changes the grade of the + // second student as well: + assert($(HILITE student2).grades[0] == $(HILITE 75)); +--- + +$(P +To avoid such a confusion, the elements of the $(C grades) member of the second object must be separate and belong only to that object. Such $(I adjustments) are done in the postblit: +) + +--- +struct Student { + int number; + int[] grades; + + this(this) { + grades = grades.dup; + } +} +--- + +$(P +Remember that all of the members have already been copied automatically before $(C this(this)) started executing. The single line in the postblit above makes a copy of the $(I elements) of the original array and assigns a slice of it back to $(C grades). As a result, the new object gets its own copy of the grades. +) + +$(P +Making modifications through the first object does not affect the second object anymore: +) + +--- + student1.grades[0] += 5; + assert($(HILITE student2).grades[0] == $(HILITE 70)); +--- + +$(H6 $(IX @disable, postblit) Disabling postblit) + +$(P +The postblit function can be disabled by $(C @disable) as well. Objects of such a type cannot be copied: +) + +--- +struct Archive { +// ... + + $(HILITE @disable this(this);) +} + +// ... + + auto a = Archive("records"); + auto b = a; $(DERLEME_HATASI) +--- + +$(P +The compiler does not allow calling the disabled postblit function: +) + +$(SHELL +Error: struct deneme.Archive is $(HILITE not copyable) because it is +annotated with @disable +) + +$(H5 $(IX assignment operator) $(IX =) $(IX opAssign) Assignment operator) + +$(P +Assigment is giving a new value to an existing object: +) + +--- + returnTripDuration = tripDuration; // assignment +--- + +$(P +Assignment is more complicated from the other special operations because it is actually a combination of two operations: +) + +$(UL +$(LI Destroying the left-hand side object) +$(LI Copying the right-hand side object to the left-hand side object) +) + +$(P +However, applying those two steps in that order is risky because the original object would be destroyed before knowing that copying will succeed. Otherwise, an exception that is thrown during the copy operation can leave the left-hand side object in an inconsistent state: fully destroyed but not completely copied. +) + +$(P +For that reason, the compiler-generated assignment operator acts safely by applying the following steps: +) + +$(OL + +$(LI Copy the right-hand side object to a temporary object + +$(P +This is the actual copying half of the assignment operation. Since there is no change to the left-hand side object yet, it will remain intact if an exception is thrown during this copy operation. +) + +) + +$(LI Destroy the left-hand side object + +$(P +This is the other half of the assignment operation. +) + +) + +$(LI Transfer the temporary object to the left-hand side object + +$(P +No postblit nor a destructor is executed during or after this step. As a result, the left-hand side object becomes the equivalent of the temporary object. +) + +) + +) + +$(P +After the steps above, the temporary object disappears and only the right-hand side object and its copy (i.e. the left-hand side object) remain. +) + +$(P +Although the compiler-generated assignment operator is suitable in most cases, it can be defined by the programmer. When you do that, consider potential exceptions and write the assignment operator in a way that works even at the presence of thrown exceptions. +) + +$(P +The syntax of the assignment operator is the following: +) + +$(UL +$(LI The name of the function is $(C opAssign).) +$(LI The type of the parameter is the same as the $(C struct) type. This parameter is often named as $(C rhs), short for $(I right-hand side).) +$(LI The return type is the same as the $(C struct) type.) +$(LI The function is exited by $(C return this).) +) + +$(P +As an example, let's consider a simple $(C Duration) struct where the assignment operator prints a message: +) + +--- +struct Duration { + int minute; + + $(HILITE Duration) opAssign($(HILITE Duration) rhs) { + writefln("minute is being changed from %s to %s", + this.minute, rhs.minute); + + this.minute = rhs.minute; + + $(HILITE return this;) + } +} +// ... + auto duration = Duration(100); + duration = Duration(200); // assignment +--- + +$(P +The output: +) + +$(SHELL +minute is being changed from 100 to 200 +) + +$(H6 Assigning from other types) + +$(P +Sometimes it is convenient to assign values of types that are different from the type of the $(C struct). For example, instead of requiring a $(C Duration) object on the right-hand side, it may be useful to assign from an integer: +) + +--- + duration = 300; +--- + +$(P +This is possible by defining another assignment operator that takes an $(C int) parameter: +) + +--- +struct Duration { + int minute; + + Duration opAssign(Duration rhs) { + writefln("minute is being changed from %s to %s", + this.minute, rhs.minute); + + this.minute = rhs.minute; + + return this; + } + + Duration opAssign($(HILITE int minute)) { + writefln("minute is being replaced by an int"); + + this.minute = minute; + + return this; + } +} +// ... + duration = Duration(200); + duration = $(HILITE 300); +--- + +$(P +The output: +) + +$(SHELL +minute is being changed from 100 to 200 +minute is being replaced by an int +) + +$(P +$(B Note:) Although convenient, assigning different types to each other may cause confusions and errors. +) + +$(H5 Summary) + +$(UL + +$(LI Constructor ($(C this)) is for preparing objects for use. The compiler-generated default constructor is sufficient in most cases.) + +$(LI The behavior of the default constructor may not be changed in structs; $(C static opCall) can be used instead.) + +$(LI Single-parameter constructors can be used during type conversions by $(C to) and $(C cast).) + +$(LI Destructor ($(C ~this)) is for the operations that must be executed when the lifetimes of objects end.) + +$(LI Postblit ($(C this(this))) is for adjustments to the object after the automatic member copies.) + +$(LI Assigment operator ($(C opAssign)) is for changing values of existing objects.) + +) + +Macros: + SUBTITLE=Constructor and Other Special Functions + + DESCRIPTION=Four special functions of structs and classes: constructor, destructor, postblit, and assignment operator. + + KEYWORDS=d programming lesson book tutorial constructor destructor postblit assignment diff --git a/target/stream_redirect.cozum.d b/target/stream_redirect.cozum.d new file mode 100644 index 0000000..19b3b4e --- /dev/null +++ b/target/stream_redirect.cozum.d @@ -0,0 +1,26 @@ +Ddoc + +$(COZUM_BOLUMU Redirecting the Standard Input and Output Streams) + +$(P +Redirecting standard input and output of programs are commonly used especially on Unix-based operating system shells. (A shell is the program that interacts with the user in the terminal.) Some programs are designed to work well when piped to other programs. +) + +$(P +For example, a file named $(C deneme.d) can be searched under a directory tree by piping $(C find) and $(C grep) as in the following line: +) + +$(SHELL +find | grep deneme.d +) + +$(P +$(C find) prints the names of all of the files to its output. $(C grep) receives that output through its input and prints the lines that contain $(C deneme.d) to its own output. +) + +Macros: + SUBTITLE=Redirecting the Standard Input and Output Streams Solutions + + DESCRIPTION=Problem solutions for the Redirecting Streams Input and Output Streams chapter. + + KEYWORDS=d programming lesson book tutorial redirect standard input output problem solution diff --git a/target/stream_redirect.d b/target/stream_redirect.d new file mode 100644 index 0000000..c4c0309 --- /dev/null +++ b/target/stream_redirect.d @@ -0,0 +1,136 @@ +Ddoc + +$(DERS_BOLUMU $(IX redirect, stream) Redirecting the Standard Input and Output Streams) + +$(P +All of the programs that we have seen so far have interacted through $(C stdin) and $(C stdout), the standard input and output streams. Input and output functions like $(C readf) and $(C writeln) operate on these streams by default. +) + +$(P +While using these streams, we assumed that the standard input comes from the keyboard and that the standard output goes to the screen. +) + +$(P +We will start writing programs that deal with files in later chapters. We will see that, just like the standard input and output streams, files are character streams as well; so they are used in almost the same way as $(C stdin) and $(C stdout). +) + +$(P +But before seeing how files are accessed from within programs, I would like to show how the standard inputs and outputs of programs can be redirected to files or piped to other programs. Existing programs can, without their source code being changed, be made to print to files instead of the screen, and read from files instead of the keyboard. Although these features are not directly related to programming languages, they are useful tools that are available in nearly all modern shells. +) + +$(H5 $(IX >, output redirect) Redirecting the standard output to a file with operator $(C >)) + +$(P +When starting the program from the terminal, typing a $(C >) character and a file name at the end of the command line redirects the standard output of that program to the specified file. Everything that the program prints to its standard output will be written to that file instead. +) + +$(P +Let's test this with a program that reads a floating point number from its input, multiplies that number by two, and prints the result to its standard output: +) + +--- +import std.stdio; + +void main() { + double number; + readf(" %s", &number); + + writeln(number * 2); +} +--- + +$(P +If the name of the program is $(C by_two), its output will be written to a file named $(C by_two_result.txt) when the program is started on the command line as in the following line: +) + +$(SHELL +./by_two > by_two_result.txt +) + +$(P +For example, if we enter $(C 1.2) at the terminal, the result $(C 2.4) will appear in $(C by_two_result.txt). ($(I $(B Note:) Although the program does not display a prompt like "Please enter a number", it still expects a number to be entered.)) +) + +$(H5 $(IX <, input redirect) Redirecting the standard input from a file with operator $(C <)) + +$(P +Similarly to redirecting the standard output by using the $(C >) operator, the standard input can be redirected from a file by using the $(C <) operator. In this case, the program reads from the specified file instead of from the keyboard. +) + +$(P +To test this, let's use a program that calculates one tenth of a number: +) + +--- +import std.stdio; + +void main() { + double number; + readf(" %s", &number); + + writeln(number / 10); +} +--- + +$(P +Assuming that the file $(C by_two_result.txt) still exists and contains $(C 2.4) from the previous output, and that the name of the new program is $(C one_tenth), we can redirect the new program's standard input from that file as in the following line: +) + +$(SHELL +./one_tenth < by_two_result.txt +) + +$(P +This time the program will read from $(C by_two_result.txt) and print the result to the terminal as $(C 0.24). +) + +$(H5 Redirecting both standard streams) + +$(P +The operators $(C >) and $(C <) can be used at the same time: +) + +$(SHELL +./one_tenth < by_two_result.txt > one_tenth_result.txt +) + +$(P +This time the standard input will be read from $(C by_two_result.txt) and the standard output will be written to $(C one_tenth_result.txt). +) + +$(H5 $(IX |, stream pipe) Piping programs with operator $(C |)) + +$(P +Note that $(C by_two_result.txt) is an intermediary between the two programs; $(C by_two) writes to it and $(C one_tenth) reads from it. +) + +$(P +The $(C |) operator pipes the standard output of the program that is on its left-hand side to the standard input of the program that is on its right-hand side without the need for an intermediary file. For example, when the two programs above are piped together as in the following line, they collectively calculate $(I one fifth) of the input: +) + +$(SHELL +./by_two | ./one_tenth +) + +$(P +First $(C by_two) reads a number from its input. (Remember that although it does not prompt for one, it still waits for a number.) Then $(C by_two) writes the result to its standard output. This result of $(C by_two) will appear on the standard input of $(C one_tenth), which in turn will calculate and print one tenth of that result. +) + +$(PROBLEM_TEK + +$(P +Pipe more than one program: +) + +$(SHELL +./one | ./two | ./three +) + +) + +Macros: + SUBTITLE=Redirecting the Standard Input and Output Streams + + DESCRIPTION=Reading from files and other programs instead of from the keyboard, and printing to files and other programs instead of the terminal. + + KEYWORDS=d programming language tutorial book redirect standard input output diff --git a/target/strings.cozum.d b/target/strings.cozum.d new file mode 100644 index 0000000..234c93a --- /dev/null +++ b/target/strings.cozum.d @@ -0,0 +1,63 @@ +Ddoc + +$(COZUM_BOLUMU Strings) + +$(OL + +$(LI +Although some of the functions in Phobos modules will be easy to use with strings, library documentations are usually terse compared to tutorials. You may find especially the Phobos ranges confusing at this point. We will see Phobos ranges in a later chapter. +) + +$(LI +Many other functions may be chained as well: + +--- +import std.stdio; +import std.string; + +void main() { + write("First name: "); + string first = capitalize(strip(readln())); + + write("Last name: "); + string last = capitalize(strip(readln())); + + string fullName = first ~ " " ~ last; + writeln(fullName); +} +--- + +) + +$(LI This program uses two indexes to make a slice: + +--- +import std.stdio; +import std.string; + +void main() { + write("Please enter a line: "); + string line = strip(readln()); + + ptrdiff_t first_e = indexOf(line, 'e'); + + if (first_e == -1) { + writeln("There is no letter e in this line."); + + } else { + ptrdiff_t last_e = lastIndexOf(line, 'e'); + writeln(line[first_e .. last_e + 1]); + } +} +--- + +) + +) + +Macros: + SUBTITLE=Strings Solution + + DESCRIPTION=Programming in D exercise solutions: strings + + KEYWORDS=programming in d tutorial strings solution diff --git a/target/strings.d b/target/strings.d new file mode 100644 index 0000000..d6a8ae9 --- /dev/null +++ b/target/strings.d @@ -0,0 +1,511 @@ +Ddoc + +$(DERS_BOLUMU Strings) + +$(P +We have used strings in many programs that we have seen so far. Strings are a combination of the two features that we have covered in the last three chapters: characters and arrays. In the simplest definition, strings are nothing but arrays of characters. For example, $(C char[]) is a type of string. +) + +$(P +This simple definition may be misleading. As we have seen in the $(LINK2 /ders/d.en/characters.html, Characters chapter), D has three separate character types. Arrays of these character types lead to three separate string types, some of which may have surprising outcomes in some string operations. +) + +$(H5 $(IX readln) $(IX strip) $(C readln) and $(C strip), instead of $(C readf)) + +$(P +There are surprises even when reading strings from the terminal. +) + +$(P +Being character arrays, strings can contain control characters like $(STRING '\n') as well. When reading strings from the input, the control character that corresponds to the Enter key that is pressed at the end of the input becomes a part of the string as well. Further, because there is no way to tell $(C readf()) how many characters to read, it continues to read until the end of the entire input. For these reasons, $(C readf()) does not work as intended when reading strings: +) + +--- +import std.stdio; + +void main() { + char[] name; + + write("What is your name? "); + readf(" %s", &name); + + writeln("Hello ", name, "!"); +} +--- + +$(P +The Enter key that the user presses after the name does not terminate the input. $(C readf()) continues to wait for more characters to add to the string: +) + +$(SHELL +What is your name? Mert + $(SHELL_NOTE The input is not terminated although Enter has been pressed) + $(SHELL_NOTE (Let's assume that Enter is pressed a second time here)) +) + +$(P +One way of terminating the standard input stream in a terminal is pressing Ctrl-D under Unix-based systems and Ctrl-Z under Windows systems. If the user eventually terminates the input that way, we see that the new-line characters have been read as parts of the string as well: +) + +$(SHELL +Hello Mert + $(SHELL_NOTE_WRONG new-line character after the name) +! $(SHELL_NOTE_WRONG (one more before the exclamation mark)) +) + +$(P +The exclamation mark appears after those characters instead of being printed right after the name. +) + +$(P +$(C readln()) is more suitable when reading strings. Short for "read line", $(C readln()) reads until the end of the line. It is used differently because the $(STRING " %s") format string and the $(C &) operator are not needed: +) + +--- +import std.stdio; + +void main() { + char[] name; + + write("What is your name? "); + $(HILITE readln(name)); + + writeln("Hello ", name, "!"); +} +--- + +$(P +$(C readln()) stores the new-line character as well. This is so that the program has a way of determining whether the input consisted of a complete line or whether the end of input has been reached: +) + +$(SHELL +What is your name? Mert +Hello Mert +! $(SHELL_NOTE_WRONG new-line character before the exclamation mark) +) + +$(P +Such control characters as well as all whitespace characters at both ends of strings can be removed by $(C std.string.strip): +) + +--- +import std.stdio; +$(HILITE import std.string;) + +void main() { + char[] name; + + write("What is your name? "); + readln(name); + $(HILITE name = strip(name);) + + writeln("Hello ", name, "!"); +} +--- + +$(P +The $(C strip()) expression above returns a new string that does not contain the trailing control characters. Assigning that return value back to $(C name) produces the intended output: +) + +$(SHELL +What is your name? Mert +Hello Mert! $(SHELL_NOTE no new-line character) +) + +$(P +$(C readln()) can be used without a parameter. In that case it $(I returns) the line that it has just read. Chaining the result of $(C readln()) to $(C strip()) enables a shorter and more readable syntax: +) + +--- + string name = strip(readln()); +--- + +$(P +I will start using that form after introducing the $(C string) type below. +) + +$(H5 $(IX formattedRead) $(C formattedRead) for parsing strings) + +$(P +Once a line is read from the input or from any other source, it is possible to parse and convert separate data that it may contain with $(C formattedRead()) from the $(C std.format) module. Its first parameter is the line that contains the data, and the rest of the parameters are used exacly like $(C readf()): +) + +--- +import std.stdio; +import std.string; +$(HILITE import std.format;) + +void main() { + write("Please enter your name and age," ~ + " separated with a space: "); + + string line = strip(readln()); + + string name; + int age; + $(HILITE formattedRead)(line, " %s %s", name, age); + + writeln("Your name is ", name, + ", and your age is ", age, '.'); +} +--- + +$(SHELL +Please enter your name and age, separated with a space: $(HILITE Mert 30) +Your name is $(HILITE Mert), and your age is $(HILITE 30). +) + +$(P +Both $(C readf()) and $(C formattedRead()) $(I return) the number of items that they could parse and convert successfully. That value can be compared against the expected number of data items so that the input can be validated. For example, as the $(C formattedRead()) call above expects to read $(I two) items (a $(C string) as name and an $(C int) as age), the following check ensures that it really is the case: +) + +--- + $(HILITE uint items) = formattedRead(line, " %s %s", name, age); + + if ($(HILITE items != 2)) { + writeln("Error: Unexpected line."); + + } else { + writeln("Your name is ", name, + ", and your age is ", age, '.'); + } +--- + +$(P +When the input cannot be converted to $(C name) and $(C age), the program prints an error: +) + +$(SHELL +Please enter your name and age, separated with a space: $(HILITE Mert) +Error: Unexpected line. +) + +$(H5 $(IX ") Double quotes, not single quotes) + +$(P +We have seen that single quotes are used to define character literals. String literals are defined with double quotes. $(STRING 'a') is a character; $(STRING "a") is a string that contains a single character. +) + +$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string), $(C wstring), and $(C dstring) are immutable) + +$(P +There are three string types that correspond to the three character types: $(C char[]), $(C wchar[]), and $(C dchar[]). +) + +$(P +There are three $(I aliases) of the $(I immutable) versions of those types: $(C string), $(C wstring), and $(C dstring). The characters of the variables that are defined by these aliases cannot be modified. For example, the characters of a $(C wchar[]) can be modified but the characters of a $(C wstring) cannot be modified. (We will see D's $(I immutability) concept in later chapters.) +) + +$(P +For example, the following code that tries to capitalize the first letter of a $(C string) would cause a compilation error: +) + +--- + string cannotBeMutated = "hello"; + cannotBeMutated[0] = 'H'; $(DERLEME_HATASI) +--- + +$(P +We may think of defining the variable as a $(C char[]) instead of the $(C string) alias but that cannot be compiled either: +) + +--- + char[] a_slice = "hello"; $(DERLEME_HATASI) +--- + +$(P +This time the compilation error is due to the combination of two factors: +) + +$(OL +$(LI The type of string literals like $(STRING "hello") is $(C string), not $(C char[]), so they are immutable. +) +$(LI The $(C char[]) on the left-hand side is a slice, which, if the code compiled, would provide access to all of the characters of the right-hand side. +) +) + +$(P +Since $(C char[]) is mutable and $(C string) is not, there is a mismatch. The compiler does not allow accessing characters of an immutable array through a mutable slice. +) + +$(P +The solution here is to take a copy of the immutable string by using the $(C .dup) property: +) + +--- +import std.stdio; + +void main() { + char[] s = "hello"$(HILITE .dup); + s[0] = 'H'; + writeln(s); +} +--- + +$(P +The program can now be compiled and will print the modified string: +) + +$(SHELL +Hello +) + +$(P +Similarly, $(C char[]) cannot be used where a $(C string) is needed. In such cases, the $(C .idup) property can be used to produce an immutable $(C string) variable from a mutable $(C char[]) variable. For example, if $(C s) is a variable of type $(C char[]), the following line will fail to compile: +) + +--- + string result = s ~ '.'; $(DERLEME_HATASI) +--- + +$(P +When the type of $(C s) is $(C char[]), the type of the expression on the right-hand side of the assignment above is $(C char[]) as well. $(C .idup) is used for producing immutable strings from existing strings: +) + +--- + string result = (s ~ '.')$(HILITE .idup); // ← now compiles +--- + +$(H5 $(IX length, string) Potentially confusing length of strings) + +$(P +We have seen that some Unicode characters are represented by more than one byte. For example, the character 'é' (the latin letter 'e' combined with an acute accent) is represented by Unicode encodings using at least two bytes. This fact is reflected in the $(C .length) property of strings: +) + +--- + writeln("résumé".length); +--- + +$(P +Although "résumé" contains six $(I letters), the length of the $(C string) is the number of UTF-8 code units that it contains: +) + +$(SHELL +8 +) + +$(P +The type of the elements of string literals like $(STRING "hello") is $(C char) and each $(C char) value represents a UTF-8 code unit. A problem that this may cause is when we try to replace a two-code-unit character with a single-code-unit character: +) + +--- + char[] s = "résumé".dup; + writeln("Before: ", s); + s[1] = 'e'; + s[5] = 'e'; + writeln("After : ", s); +--- + +$(P +The two 'e' characters do not replace the two 'é' characters; they replace single code units, resulting in an invalid UTF-8 encoding: +) + +$(SHELL +Before: résumé +After : re�sueé $(SHELL_NOTE_WRONG INCORRECT) +) + +$(P +When dealing with letters, symbols, and other Unicode characters directly, as in the code above, the correct type to use is $(C dchar): +) + +--- + dchar[] s = "résumé"d.dup; + writeln("Before: ", s); + s[1] = 'e'; + s[5] = 'e'; + writeln("After : ", s); +--- + +$(P +The output: +) + +$(SHELL +Before: résumé +After : resume +) + +$(P +Please note the two differences in the new code: +) +$(OL +$(LI The type of the string is $(C dchar[]). +$(LI There is a $(C d) at the end of the literal $(STRING "résumé"d), specifying its type as an array of $(C dchar)s.) +) +) + +$(P +In any case, keep in mind that the use of $(C dchar[]) and $(C dstring) does not solve all of the problems of manipulating Unicode characters. For instance, if the user inputs the text "résumé" you and your program cannot assume that the string length will be 6 even for $(C dchar) strings. It might be greater if e.g. at least one of the 'é' characters is not encoded as a single code point but as the combination of an 'e' and a combining accute accent. To avoid dealing with this and many other Unicode issues, consider using a Unicode-aware text manipulation library in your programs. +) + +$(H5 $(IX literal, string) String literals) + +$(P +The optional character that is specified after string literals determines the type of the elements of the string: +) + +--- +import std.stdio; + +void main() { + string s = "résumé"c; // same as "résumé" + wstring w = "résumé"w; + dstring d = "résumé"d; + + writeln(s.length); + writeln(w.length); + writeln(d.length); +} +--- + +$(P +The output: +) + +$(SHELL +8 +6 +6 +) + +$(P +Because all of the Unicode characters of "résumé" can be represented by a single $(C wchar) or $(C dchar), the last two lengths are equal to the number of characters. +) + +$(H5 $(IX concatenation, string) String concatenation) + +$(P +Since they are actually arrays, all of the array operations can be applied to strings as well. $(C ~) concatenates two strings and $(C ~=) appends to an existing string: +) + +--- +import std.stdio; +import std.string; + +void main() { + write("What is your name? "); + string name = strip(readln()); + + // Concatenate: + string greeting = "Hello " ~ name; + + // Append: + greeting ~= "! Welcome..."; + + writeln(greeting); +} +--- + +$(P +The output: +) + +$(SHELL +What is your name? Can +Hello Can! Welcome... +) + +$(H5 Comparing strings) + +$(P +$(I $(B Note:) Unicode does not define how the characters are ordered other than their Unicode codes. For that reason, you may get results that don't match your expectations below.) +) + +$(P +We have used comparison operators $(C <), $(C >=), etc. with integer and floating point values before. The same operators can be used with strings as well, but with a different meaning: strings are ordered $(I lexicographically). This ordering takes each character's Unicode code to be its place in a hypothetical grand Unicode alphabet. The concepts of $(I less) and $(I greater) are replaced with $(I before) and $(I after) in this hypothetical alphabet: +) + +--- +import std.stdio; +import std.string; + +void main() { + write(" Enter a string: "); + string s1 = strip(readln()); + + write("Enter another string: "); + string s2 = strip(readln()); + + if (s1 $(HILITE ==) s2) { + writeln("They are the same!"); + + } else { + string former; + string latter; + + if (s1 $(HILITE <) s2) { + former = s1; + latter = s2; + + } else { + former = s2; + latter = s1; + } + + writeln("'", former, "' comes before '", latter, "'."); + } +} +--- + +$(P +Because Unicode adopts the letters of the basic Latin alphabet from the ASCII table, the strings that contain only the letters of the ASCII table will always be ordered correctly. +) + +$(H5 Lowercase and uppercase are different) + +$(P +Because each character has a unique code, every letter variant is different from the others. For example, 'A' and 'a' are different letters, when directly comparing Unicode strings. +) + +$(P +Additionally, as a consequence of their ASCII code values, all of the latin uppercase letters are sorted before all of the lowercase letters. For example, 'B' comes before 'a'. The $(C icmp()) function of the $(C std.string) module can be used when strings need to be compared regardless of lowercase and uppercase. You can see the functions of this module at $(LINK2 http://dlang.org/phobos/std_string.html, its online documentation). +) + +$(P +Because strings are arrays (and as a corollary, $(I ranges)), the functions of the $(C std.array), $(C std.algorithm), and $(C std.range) modules are very useful with strings as well. +) + +$(PROBLEM_COK + +$(PROBLEM +Browse the documentations of the $(C std.string), $(C std.array), $(C std.algorithm), and $(C std.range) modules. +) + +$(PROBLEM +Write a program that makes use of the $(C ~) operator: The user enters the first name and the last name, all in lowercase letters. Produce the full name that contains the proper capitalization of the first and last names. For example, when the strings are "ebru" and "domates" the program should print "Ebru Domates". +) + +$(PROBLEM +Read a line from the input and print the part between the first and last 'e' letters of the line. For example, when the line is "this line has five words" the program should print "e has five". + +$(P +You may find the $(C indexOf()) and $(C lastIndexOf()) functions useful to get the two indexes needed to produce a slice. +) + +$(P +As it is indicated in their documentation, the return types of $(C indexOf()) and $(C lastIndexOf()) are not $(C int) nor $(C size_t), but $(C ptrdiff_t). You may have to define variables of that exact type: +) + +--- + ptrdiff_t first_e = indexOf(line, 'e'); +--- + +$(P +It is possible to define variables with the $(C auto) keyword, which we will see in a later chapter: +) + +--- + auto first_e = indexOf(line, 'e'); +--- + +) + +) + +Macros: + SUBTITLE=Strings + + DESCRIPTION=The strings of the D programming language + + KEYWORDS=d programming language tutorial book string diff --git a/target/struct.cozum.d b/target/struct.cozum.d new file mode 100644 index 0000000..d14599d --- /dev/null +++ b/target/struct.cozum.d @@ -0,0 +1,220 @@ +Ddoc + +$(COZUM_BOLUMU Structs) + +$(OL + +$(LI +One of the simplest designs is to use two $(C dchar) members: + +--- +struct Card { + dchar suit; + dchar value; +} +--- + +) + +$(LI +It would be as simple as printing the two members side by side: + +--- +void printCard(in Card card) { + write(card.suit, card.value); +} +--- + +) + +$(LI +Assuming that there is already a function called $(C newSuit()), $(C newDeck()) can be implemented by calling that function for each suit: + +--- +Card[] newDeck() +out (result) { + assert(result.length == 52); + +} body { + Card[] deck; + + deck ~= newSuit('♠'); + deck ~= newSuit('♡'); + deck ~= newSuit('♢'); + deck ~= newSuit('♣'); + + return deck; +} +--- + +$(P +The rest of the work can be accomplished by the following $(C newSuit()), which constructs the suit by combining the suit character with each value of a string: +) + +--- +Card[] newSuit(in dchar suit) +in { + assert((suit == '♠') || + (suit == '♡') || + (suit == '♢') || + (suit == '♣')); + +} out (result) { + assert(result.length == 13); + +} body { + Card[] suitCards; + + foreach (value; "234567890JQKA") { + suitCards ~= Card(suit, value); + } + + return suitCards; +} +--- + +$(P +Note that the functions above take advantage of contract programming to reduce risk of program errors. +) + +) + +$(LI +Swapping two elements at random would make the deck become more and more shuffled at each repetition. Although it is possible to pick the same element by chance, swapping an element with itself does not have any effect other than missing an opportunity toward a more shuffled deck. + +--- +void shuffle(Card[] deck, in int repetition) { + /* Note: A better algorithm is to walk the deck from the + * beginning to the end and to swap each element + * with a random one that is picked among the + * elements from that point to the end. + * + * It would be even better to call randomShuffle() from + * the std.algorithm module, which already applies the + * same algorithm. Please read the comment in main() to + * see how randomShuffle() can be used. */ + foreach (i; 0 .. repetition) { + // Pick two elements at random + immutable first = uniform(0, deck.length); + immutable second = uniform(0, deck.length); + + swap(deck[first], deck[second]); + } +} +--- + +$(P +The function above calls $(C std.algorithm.swap), which simply swaps the values of its two $(C ref) parameters. It is effectively the equivalent of the following function: +) + +--- +void mySwap(ref Card left, + ref Card right) { + immutable temporary = left; + left = right; + right = temporary; +} +--- + +) + +) + +$(P +Here is the entire program: +) + +--- +import std.stdio; +import std.random; +import std.algorithm; + +struct Card { + dchar suit; + dchar value; +} + +void printCard(in Card card) { + write(card.suit, card.value); +} + +Card[] newSuit(in dchar suit) +in { + assert((suit == '♠') || + (suit == '♡') || + (suit == '♢') || + (suit == '♣')); + +} out (result) { + assert(result.length == 13); + +} body { + Card[] suitCards; + + foreach (value; "234567890JQKA") { + suitCards ~= Card(suit, value); + } + + return suitCards; +} + +Card[] newDeck() +out (result) { + assert(result.length == 52); + +} body { + Card[] deck; + + deck ~= newSuit('♠'); + deck ~= newSuit('♡'); + deck ~= newSuit('♢'); + deck ~= newSuit('♣'); + + return deck; +} + +void shuffle(Card[] deck, in int repetition) { + /* Note: A better algorithm is to walk the deck from the + * beginning to the end and to swap each element + * with a random one that is picked among the + * elements from that point to the end. + * + * It would be even better to call randomShuffle() from + * the std.algorithm module, which already applies the + * same algorithm. Please read the comment in main() to + * see how randomShuffle() can be used. */ + foreach (i; 0 .. repetition) { + // Pick two elements at random + immutable first = uniform(0, deck.length); + immutable second = uniform(0, deck.length); + + swap(deck[first], deck[second]); + } +} + +void main() { + Card[] deck = newDeck(); + + shuffle(deck, 100); + /* Note: Instead of the shuffle() call above, it would be + * better to call randomShuffle() as in the + * following line: + * + * randomShuffle(deck); + */ + foreach (card; deck) { + printCard(card); + write(' '); + } + + writeln(); +} +--- + + +Macros: + SUBTITLE=Structs Solutions + + DESCRIPTION=Programming in D exercise solutions: Structs + + KEYWORDS=d programming book tutorial struct exercise solutions diff --git a/target/struct.d b/target/struct.d new file mode 100644 index 0000000..371f852 --- /dev/null +++ b/target/struct.d @@ -0,0 +1,932 @@ +Ddoc + +$(DERS_BOLUMU $(IX struct) Structs) + +$(P +As has been mentioned several times earlier in the book, fundamental types are not suitable to represent higher-level concepts. For example, although a value of type $(C int) is suitable to represent the hour of day, two $(C int) variables would be more suitable together to represent a point in time: one for the hour and the other for the minute. +) + +$(P +$(IX user defined type) Structs are the feature that allow defining new types by combining already existing other types. The new type is defined by the $(C struct) keyword. By this definition, structs are $(I user defined types). Most of the content of this chapter is directly applicable to classes as well. Especially the concept of $(I combining existing types to define a new type) is exactly the same for them. +) + +$(P +This chapter covers only the basic features of structs. We will see more of structs in the following chapters: +) + +$(UL +$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions)) +$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions)) +$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions)) +$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading)) +$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes)) +$(LI $(LINK2 /ders/d.en/property.html, Properties)) +$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes)) +$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes)) +) + +$(P +To understand how useful structs are, let's take a look at the $(C addDuration()) function that we had defined earlier in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter). The following definition is from the exercise solution of that chapter: +) + +--- +void addDuration(in int startHour, in int startMinute, + in int durationHour, in int durationMinute, + out int resultHour, out int resultMinute) { + resultHour = startHour + durationHour; + resultMinute = startMinute + durationMinute; + resultHour += resultMinute / 60; + + resultMinute %= 60; + resultHour %= 24; +} +--- + +$(P $(I $(B Note:) I will ignore the $(C in), $(C out), and $(C unittest) blocks in this chapter to keep the code samples short.) +) + +$(P +Although the function above clearly takes six parameters, when the three pairs of parameters are considered, it is conceptually taking only three bits of information for the starting time, the duration, and the result. +) + +$(H5 Definition) + +$(P +The $(C struct) keyword defines a new type by combining variables that are related in some way: +) + +--- +$(CODE_NAME TimeOfDay)struct TimeOfDay { + int hour; + int minute; +} +--- + +$(P +The code above defines a new type named $(C TimeOfDay), which consists of two variables named $(C hour) and $(C minute). That definition allows the new $(C TimeOfDay) type to be used in the program just like any other type. The following code demonstrates how similar its use is to an $(C int)'s: +) + +--- + int number; // a variable + number = otherNumber; // taking the value of otherNumber + + TimeOfDay time; // an object + time = otherTime; // taking the value of otherTime +--- + +$(P +The syntax of $(C struct) definition is the following: +) + +--- +struct $(I TypeName) { + // ... member variables and functions ... +} +--- + +$(P +We will see member functions in later chapters. +) + +$(P +The variables that a struct combines are called its $(I members). According to this definition, $(C TimeOfDay) has two members: $(C hour) and $(C minute). +) + +$(H6 $(C struct) defines a type, not a variable) + +$(P +There is an important distinction here: Especially after the $(LINK2 /ders/d.en/name_space.html, Name Scope) and $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations) chapters, the curly brackets of $(C struct) definitions may give the wrong impression that the struct members start and end their lives inside that scope. This is not true. +) + +$(P +Member definitions are not variable definitions: +) + +--- +struct TimeOfDay { + int hour; // ← Not a variable; will become a part of + // a struct variable used in the program. + + int minute; // ← Not a variable; will become a part of + // a struct variable used in the program. +} +--- + +$(P +The definition of a $(C struct) determines the types and the names of the members that the objects of that $(C struct) will have. Those member variables will be constructed as parts of $(C TimeOfDay) objects that take part in the program: +) + +--- + TimeOfDay bedTime; // This object contains its own hour + // and minute member variables. + + TimeOfDay wakeUpTime; // This object contains its own hour + // and minute member variables as + // well. The member variables of + // this object are not related to + // the member variables of the + // previous object. +--- + +$(P +$(IX object, struct) Variables of $(C struct) and $(C class) types are called $(I objects). +) + +$(H6 Coding convenience) + +$(P +Being able to combine the concepts of hour and minute together as a new type is a great convenience. For example, the function above can be rewritten in a more meaningful way by taking three $(C TimeOfDay) parameters instead of the existing six $(C int) parameters: +) + +--- +void addDuration(in TimeOfDay start, + in TimeOfDay duration, + out TimeOfDay result) { + // ... +} +--- + +$(P $(I $(B Note:) It is not normal to add two variables that represent two points in time. For example, it is meaningless to add the lunch time 12:00 to the breakfast time 7:30. It would make more sense to define another type, appropriately called $(C Duration), and to add objects of that type to $(C TimeOfDay) objects. Despite this design flaw, I will continue using only $(C TimeOfDay) objects in this chapter and introduce $(C Duration) in a later chapter.) +) + +$(P +As you remember, functions return up-to a single value. That has precisely been the reason why the earlier definition of $(C addDuration()) was taking two $(C out) parameters: It could not return the hour and minute information as a single value. +) + +$(P +Structs remove this limitation as well: Since multiple values can be combined as a single $(C struct) type, functions can return an object of such a $(C struct), effectively returning multiple values at once. $(C addDuration()) can now be defined as returning its result: +) + +--- +TimeOfDay addDuration(in TimeOfDay start, + in TimeOfDay duration) { + // ... +} +--- + +$(P +As a consequence, $(C addDuration()) now becomes a function that produces a value, as opposed to being a function that has side effects. As you would remember from the $(LINK2 /ders/d.en/functions.html, Functions chapter), producing results is preferred over having side effects. +) + +$(P +Structs can be members of other structs. For example, the following $(C struct) has two $(C TimeOfDay) members: +) + +--- +struct Meeting { + string topic; + size_t attendanceCount; + TimeOfDay start; + TimeOfDay end; +} +--- + +$(P +$(C Meeting) can in turn be a member of another $(C struct). Assuming that there is also the $(C Meal) struct: +) + +--- +struct DailyPlan { + Meeting projectMeeting; + Meal lunch; + Meeting budgetMeeting; +} +--- + +$(H5 $(IX ., member) Accessing the members) + +$(P +Struct members are used like any other variable. The only difference is that the actual struct variable and a $(I dot) must be specified before the name of the member: +) + +--- + start.hour = 10; +--- + +$(P +The line above assigns the value 10 to the $(C hour) member of the $(C start) object. +) + +$(P +Let's rewrite the $(C addDuration()) function with what we have seen so far: +) + +--- +$(CODE_NAME addDuration)TimeOfDay addDuration(in TimeOfDay start, + in TimeOfDay duration) { + TimeOfDay result; + + result.minute = start.minute + duration.minute; + result.hour = start.hour + duration.hour; + result.hour += result.minute / 60; + + result.minute %= 60; + result.hour %= 24; + + return result; +} +--- + +$(P +Notice that the names of the variables are now much shorter in this version of the function: $(C start), $(C duration), and $(C result). Additionally, instead of using complex names like $(C startHour), it is possible to access struct members through their respective struct variables as in $(C start.hour). +) + +$(P +Here is a code that uses the new $(C addDuration()) function. Given the start time and the duration, the following code calculates when a class period at a school would end: +) + +--- +$(CODE_XREF TimeOfDay)$(CODE_XREF addDuration)void main() { + TimeOfDay periodStart; + periodStart.hour = 8; + periodStart.minute = 30; + + TimeOfDay periodDuration; + periodDuration.hour = 1; + periodDuration.minute = 15; + + immutable periodEnd = addDuration(periodStart, + periodDuration); + + writefln("Period end: %s:%s", + periodEnd.hour, periodEnd.minute); +} +--- + +$(P +The output: +) + +$(SHELL +Period end: 9:45 +) + +$(P +The $(C main()) above has been written only by what we have seen so far. We will make this code even shorter and cleaner soon. +) + +$(H5 $(IX construction) Construction) + +$(P +The first three lines of $(C main()) are about constructing the $(C periodStart) object and the next three lines are about constructing the $(C periodDuration) object. In each three lines of code first an object is being defined and then its hour and minute values are being set. +) + +$(P +In order for a variable to be used in a safe way, that variable must first be constructed in a consistent state. Because construction is so common, there is a special construction syntax for struct objects: +) + +--- + TimeOfDay periodStart = TimeOfDay(8, 30); + TimeOfDay periodDuration = TimeOfDay(1, 15); +--- + +$(P +The values are automatically assigned to the members in the order that they are specified: Because $(C hour) is defined first in the $(C struct), the value 8 is assigned to $(C periodStart.hour) and 30 is assigned to $(C periodStart.minute). +) + +$(P +As we have seen in $(LINK2 /ders/d.en/cast.html, the Type Conversions chapter), the construction syntax can be used for other types as well: +) + +--- + auto u = ubyte(42); // u is a ubyte + auto i = int(u); // i is an int +--- + +$(H6 Constructing objects as $(C immutable)) + +$(P +Being able to construct the object by specifying the values of its members at once makes it possible to define objects as $(C immutable): +) + +--- + immutable periodStart = TimeOfDay(8, 30); + immutable periodDuration = TimeOfDay(1, 15); +--- + +$(P +Otherwise it would not be possible to mark an object first as $(C immutable) and then modify its members: +) + +--- + $(HILITE immutable) TimeOfDay periodStart; + periodStart.hour = 8; $(DERLEME_HATASI) + periodStart.minute = 30; $(DERLEME_HATASI) +--- + +$(H6 Trailing members need not be specified) + +$(P +There may be fewer values specified than the number of members. In that case, the remaining members are initialized by the $(C .init) values of their respective types. +) + +$(P +The following program constructs $(C Test) objects each time with one less constructor parameter. The $(C assert) checks indicate that the unspecified members are initialized automatically by their $(C .init) values. (The reason for needing to call $(C isNaN()) is explained after the program): +) + +--- +import std.math; + +struct Test { + char c; + int i; + double d; +} + +void main() { + // The initial values of all of the members are specified + auto t1 = Test('a', 1, 2.3); + assert(t1.c == 'a'); + assert(t1.i == 1); + assert(t1.d == 2.3); + + // Last one is missing + auto t2 = Test('a', 1); + assert(t2.c == 'a'); + assert(t2.i == 1); + assert($(HILITE isNaN(t2.d))); + + // Last two are missing + auto t3 = Test('a'); + assert(t3.c == 'a'); + assert($(HILITE t3.i == int.init)); + assert($(HILITE isNaN(t3.d))); + + // No initial value specified + auto t4 = Test(); + assert($(HILITE t4.c == char.init)); + assert($(HILITE t4.i == int.init)); + assert($(HILITE isNaN(t4.d))); + + // The same as above + Test t5; + assert(t5.c == char.init); + assert(t5.i == int.init); + assert(isNaN(t5.d)); +} +--- + +$(P +As you would remember from the $(LINK2 /ders/d.en/floating_point.html, Floating Point Types chapter), the initial value of $(C double) is $(C double.nan). Since the $(C .nan) value is $(I unordered), it is meaningless to use it in equality comparisons. That is why calling $(C std.math.isNaN) is the correct way of determining whether a value equals to $(C .nan). +) + +$(H6 $(IX default value, member) Specifying default values for members) + +$(P +It is important that member variables are automatically initialized with known initial values. This prevents the program from continuing with indeterminate values. However, the $(C .init) value of their respective types may not be suitable for every type. For example, $(C char.init) is not even a valid value. +) + +$(P +The initial values of the members of a struct can be specified when the struct is defined. This is useful for example to initialize floating point members by $(C 0.0), instead of the mostly-unusable $(C .nan). +) + +$(P +The default values are specified by the assignment syntax as the members are defined: +) + +--- +struct Test { + char c $(HILITE = 'A'); + int i $(HILITE = 11); + double d $(HILITE = 0.25); +} +--- + +$(P +Please note that the syntax above is not really assignment. The code above merely determines the default values that will be used when objects of that struct are actually constructed later in the program. +) + +$(P +For example, the following $(C Test) object is being constructed without any specific values: +) + +--- + Test t; // no value is specified for the members + writefln("%s,%s,%s", t.c, t.i, t.d); +--- + +$(P +All of the members are initialized by their default values: +) + +$(SHELL +A,11,0.25 +) + +$(H6 $(IX {}, construction) $(IX C-style struct initialization) Constructing by the $(C {}) syntax) + +$(P +Struct objects can also be constructed by the following syntax: +) + +--- + TimeOfDay periodStart = { 8, 30 }; +--- + +$(P +Similar to the earlier syntax, the specified values are assigned to the members in the order that they are specified. The trailing members get their default values. +) + +$(P +This syntax is inherited from the C programming language: +) + +--- + auto periodStart = TimeOfDay(8, 30); // ← regular + TimeOfDay periodEnd = { 9, 30 }; // ← C-style +--- + +$(P +$(IX designated initializer) This syntax allows $(I designated initializers). Designated initializers are for specifying the member that an initialization value is associated with. It is even possible to initialize members in a different order than they are defined in the $(C struct): +) + +--- + TimeOfDay t = { $(HILITE minute:) 42, $(HILITE hour:) 7 }; +--- + +$(H5 $(IX copy, struct) $(IX assignment, struct) Copying and assignment) + +$(P +Structs are value types. As has been described in the $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types chapter), this means that every $(C struct) object has its own value. Objects get their own values when constructed, and their values change when they are assigned new values. +) + +--- + auto yourLunchTime = TimeOfDay(12, 0); + auto myLunchTime = yourLunchTime; + + // Only my lunch time becomes 12:05: + myLunchTime.minute += 5; + + // ... your lunch time is still the same: + assert(yourLunchTime.minute == 0); +--- + +$(P +During a copy, all of the members of the source object are automatically copied to the corresponding members of the destination object. Similarly, assignment involves assigning each member of the source to the corresponding member of the destination. +) + +$(P +Struct members that are of reference types need extra attention. +) + +$(H6 $(IX reference type, member) Careful with members that are of reference types!) + +$(P +As you remember, copying or assigning variables of reference types does not change any value, it changes what object is being referenced. As a result, copying or assigning generates one more reference to the right-hand side object. The relevance of this for struct members is that, the members of two separate struct objects would start providing access to the same value. +) + +$(P +To see an example of this, let's have a look at a struct where one of the members is a reference type. This struct is used for keeping the student number and the grades of a student: +) + +--- +struct Student { + int number; + int[] grades; +} +--- + +$(P +The following code constructs a second $(C Student) object by copying an existing one: +) + +--- + // Constructing the first object: + auto student1 = Student(1, [ 70, 90, 85 ]); + + // Constructing the second student as a copy of the first + // one and then changing its number: + auto student2 = student1; + student2.number = 2; + + // WARNING: The grades are now being shared by the two objects! + + // Changing the grades of the first student ... + student1.grades[0] += 5; + + // ... affects the second student as well: + writeln(student2.grades[0]); +--- + +$(P +When $(C student2) is constructed, its members get the values of the members of $(C student1). Since $(C int) is a value type, the second object gets its own $(C number) value. +) + +$(P +The two $(C Student) objects also have individual $(C grades) members as well. However, since slices are reference types, the actual elements that the two slices share are the same. Consequently, a change made through one of the slices is seen through the other slice. +) + +$(P +The output of the code indicates that the grade of the second student has been increased as well: +) + +$(SHELL +75 +) + +$(P +For that reason, a better approach might be to construct the second object by the copies of the grades of the first one: +) + +--- + // The second Student is being constructed by the copies + // of the grades of the first one: + auto student2 = Student(2, student1.grades$(HILITE .dup)); + + // Changing the grades of the first student ... + student1.grades[0] += 5; + + // ... does not affect the grades of the second student: + writeln(student2.grades[0]); +--- + +$(P +Since the grades have been copied by $(C .dup), this time the grades of the second student are not affected: +) + +$(SHELL +70 +) + +$(P +$(I Note: It is possible to have even the reference members copied automatically. We will see how this is done later when covering struct member functions.) +) + +$(H5 $(IX literal, struct) Struct literals) + +$(P +Similar to being able to use integer literal values like 10 in expressions without needing to define a variable, struct objects can be used as literals as well. +) + +$(P +Struct literals are constructed by the object construction syntax. +) + +--- + TimeOfDay(8, 30) // ← struct literal value +--- + +$(P +Let's first rewrite the $(C main()) function above with what we have learned since its last version. The variables are constructed by the construction syntax and are $(C immutable) this time: +) + +--- +$(CODE_XREF TimeOfDay)$(CODE_XREF addDuration)void main() { + immutable periodStart = TimeOfDay(8, 30); + immutable periodDuration = TimeOfDay(1, 15); + + immutable periodEnd = addDuration(periodStart, + periodDuration); + + writefln("Period end: %s:%s", + periodEnd.hour, periodEnd.minute); +} +--- + +$(P +Note that $(C periodStart) and $(C periodDuration) need not be defined as named variables in the code above. Those are in fact temporary variables in this simple program, which are used only for calculating the $(C periodEnd) variable. They could be passed to $(C addDuration()) as literal values instead: +) + +--- +$(CODE_XREF TimeOfDay)$(CODE_XREF addDuration)void main() { + immutable periodEnd = addDuration(TimeOfDay(8, 30), + TimeOfDay(1, 15)); + + writefln("Period end: %s:%s", + periodEnd.hour, periodEnd.minute); +} +--- + +$(H5 $(IX static, member) $(C static) members) + +$(P +Although objects mostly need individual copies of the struct's members, sometimes it makes sense for the objects of a particular struct type to share some variables. This may be necessary to maintain e.g. a general information about that struct type. +) + +$(P +As an example, let's imagine a type that assigns a separate identifier for every object that is constructed of that type: +) + +--- +struct Point { + // The identifier of each object + size_t id; + + int line; + int column; +} +--- + +$(P +In order to be able to assign different ids to each object, a separate variable is needed to keep the next available id. It would be incremented every time a new object is created. Assume that $(C nextId) is to be defined elsewhere and to be available in the following function: +) + +--- +Point makePoint(int line, int column) { + size_t id = nextId; + ++nextId; + + return Point(id, line, column); +} +--- + +$(P +A decision must be made regarding where the common $(C nextId) variable is to be defined. $(C static) members are useful in such cases. +) + +$(P +Such common information is defined as a $(C static) member of the struct. Contrary to the regular members, there is a single variable of each $(C static) member for each thread. (Note that most programs consist of a single thread that starts executing the $(C main()) function.) That single variable is shared by all of the objects of that struct in that thread: +) + +--- +import std.stdio; + +struct Point { + // The identifier of each object + size_t id; + + int line; + int column; + + // The id of the next object to construct + $(HILITE static size_t nextId;) +} + +Point makePoint(int line, int column) { + size_t id = $(HILITE Point.)nextId; + ++$(HILITE Point.)nextId; + + return Point(id, line, column); +} + +void main() { + auto top = makePoint(7, 0); + auto middle = makePoint(8, 0); + auto bottom = makePoint(9, 0); + + writeln(top.id); + writeln(middle.id); + writeln(bottom.id); +} +--- + +$(P +As $(C nextId) is incremented at each object construction, each object gets a unique id: +) + +$(SHELL +0 +1 +2 +) + +$(P +Since $(C static) members are owned by the entire type, there need not be an object to access them. As we have seen above, such objects can be accessed by the name of the type, as well as by the name of any object of that struct: +) + +--- + ++Point.nextId; + ++$(HILITE bottom).nextId; // would be the same as above +--- + +$(P +When a variable is needed not $(I one per thread) but $(I one per program), then those variables must be defined as $(C shared static). We will see the $(C shared) keyword in a later chapter. +) + +$(H6 $(IX static this, struct) $(IX static ~this, struct) $(IX this, static, struct) $(IX ~this, static, struct) $(C static this()) for initialization and $(C static ~this()) for finalization) + +$(P +Instead of explicitly assigning an initial value to $(C nextId) above, we relied on its default initial value, zero. We could have used any other value: +) + +--- + static size_t nextId $(HILITE = 1000); +--- + +$(P +However, such initialization is possible only when the initial value is known at compile time. Further, some special code may have to be executed before a struct can be used in a thread. Such code is written in $(C static this()) scopes. +) + +$(P +For example, the following code reads the initial value from a file if that file exists: +) + +--- +import std.file; + +struct Point { +// ... + + enum nextIdFile = "Point_next_id_file"; + + $(HILITE static this()) { + if (exists(nextIdFile)) { + auto file = File(nextIdFile, "r"); + file.readf(" %s", &nextId); + } + } +} +--- + +$(P +The contents of $(C static this()) blocks are automatically executed once per thread before the $(C struct) type is ever used in that thread. Code that should be executed only once for the entire program (e.g. initializing $(C shared) and $(C immutable) variables) must be defined in $(C shared static this()) and $(C shared static ~this()) blocks, which will be covered in $(LINK2 /ders/d.en/concurrency_shared.html, the Data Sharing Concurrency chapter). +) + +$(P +Similarly, $(C static ~this()) is for the final operations of a thread and $(C shared static ~this()) is for the final operations of the entire program. +) + +$(P +The following example complements the previous $(C static this()) by writing the value of $(C nextId) to the same file, effectively persisting the object ids over consecutive executions of the program: +) + +--- +struct Point { +// ... + + $(HILITE static ~this()) { + auto file = File(nextIdFile, "w"); + file.writeln(nextId); + } +} +--- + +$(P +The program would now initialize $(C nextId) from where it was left off. For example, the following would be the output of the program's second execution: +) + +$(SHELL +3 +4 +5 +) + +$(PROBLEM_COK + +$(PROBLEM +Design a struct named $(C Card) to represent a playing card. + +$(P +This struct can have two members for the suit and the value. It may make sense to use an $(C enum) to represent the suit, or you can simply use the characters ♠, ♡, ♢, and ♣. +) + +$(P +An $(C int) or a $(C dchar) value can be used for the card value. If you decide to use an $(C int), the values 1, 11, 12, and 13 may represent the cards that do not have numbers (ace, jack, queen, and king). +) + +$(P +There are other design choices to make. For example, the card values can be represented by an $(C enum) type as well. +) + +$(P +The way objects of this struct will be constructed will depend on the choice of the types of its members. For example, if both members are $(C dchar), then $(C Card) objects can be constructed like this: +) + +--- + auto card = Card('♣', '2'); +--- + +) + +$(PROBLEM +Define a function named $(C printCard()), which takes a $(C Card) object as a parameter and simply prints it: + +--- +struct Card { + // ... please define the struct ... +} + +void printCard(in Card card) { + // ... please define the function body ... +} + +void main() { + auto card = Card(/* ... */); + printCard(card); +} +--- + +$(P +For example, the function can print the 2 of clubs as: +) + +$(SHELL +♣2 +) + +$(P +The implementation of that function may depend on the choice of the types of the members. +) + +) + +$(PROBLEM +Define a function named $(C newDeck()) and have it return 52 cards of a deck as a $(C Card) slice: + +--- +Card[] newDeck() +out (result) { + assert(result.length == 52); + +} body { + // ... please define the function body ... +} +--- + +$(P +It should be possible to call $(C newDeck()) as in the following code: +) + +--- + Card[] deck = newDeck(); + + foreach (card; deck) { + printCard(card); + write(' '); + } + + writeln(); +--- + +$(P +The output should be similar to the following with 52 distinct cards: +) + +$(SHELL +♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 +♡5 ♡6 ♡7 ♡8 ♡9 ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 +♢8 ♢9 ♢0 ♢J ♢Q ♢K ♢A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 +♣J ♣Q ♣K ♣A +) + +) + +$(PROBLEM +Write a function that shuffles the deck. One way is to pick two cards at random by $(C std.random.uniform), to swap those two cards, and to repeat this process a sufficient number of times. The function should take the number of repetition as a parameter: + +--- +void shuffle(Card[] deck, in int repetition) { + // ... please define the function body ... +} +--- + +$(P +Here is how it should be used: +) + +--- + Card[] deck = newDeck(); + shuffle(deck, 1); + + foreach (card; deck) { + printCard(card); + write(' '); + } + + writeln(); +--- + +$(P +The function should swap cards $(C repetition) number of times. For example, when called by 1, the output should be similar to the following: +) + +$(SHELL +♠2 ♠3 ♠4 ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 +♡5 ♡6 ♡7 ♡8 $(HILITE ♣4) ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 +♢8 ♢9 ♢0 ♢J ♢Q ♢K ♢A ♣2 ♣3 $(HILITE ♡9) ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 +♣J ♣Q ♣K ♣A +) + +$(P +A higher value for $(C repetition) should result in a more shuffled deck: +) + +--- + shuffled(deck, $(HILITE 100)); +--- + +$(P +The output: +) + +$(SHELL +♠4 ♣7 ♢9 ♢6 ♡2 ♠6 ♣6 ♢A ♣5 ♢8 ♢3 ♡Q ♢J ♣K ♣8 ♣4 +♡J ♣Q ♠Q ♠9 ♢0 ♡A ♠A ♡9 ♠7 ♡3 ♢K ♢2 ♡0 ♠J ♢7 ♡7 +♠8 ♡4 ♣J ♢4 ♣0 ♡6 ♢5 ♡5 ♡K ♠3 ♢Q ♠2 ♠5 ♣2 ♡8 ♣A +♠K ♣9 ♠0 ♣3 +) + +$(P +$(I $(B Note:) A much better way of shuffling the deck is explained in the solutions.) +) + +) + +) + +Macros: + SUBTITLE=Structs + + DESCRIPTION=The 'struct' feature of the D programming language, which is used for defining user types. + + KEYWORDS=d programming book tutorial struct diff --git a/target/switch_case.cozum.d b/target/switch_case.cozum.d new file mode 100644 index 0000000..38df563 --- /dev/null +++ b/target/switch_case.cozum.d @@ -0,0 +1,103 @@ +Ddoc + +$(COZUM_BOLUMU $(C switch) and $(C case)) + +$(OL + +$(LI + +--- +import std.stdio; +import std.string; + +void main() { + string op; + double first; + double second; + + write("Please enter the operation: "); + op = strip(readln()); + + write("Please enter two values separated by a space: "); + readf(" %s %s", &first, &second); + + double result; + + final switch (op) { + + case "add": + result = first + second; + break; + + case "subtract": + result = first - second; + break; + + case "multiply": + result = first * second; + break; + + case "divide": + result = first / second; + break; + } + + writeln(result); +} +--- + +) + +$(LI +By taking advantage of distinct $(C case) values: + +--- + final switch (op) { + + case "add"$(HILITE , "+"): + result = first + second; + break; + + case "subtract"$(HILITE, "-"): + result = first - second; + break; + + case "multiply"$(HILITE, "*"): + result = first * second; + break; + + case "divide"$(HILITE, "/"): + result = first / second; + break; + } +--- + +) + +$(LI Since the $(C default) section is needed to throw the exception from, it cannot be a $(C final switch) statement anymore. Here are the parts of the program that are modified: + +--- +// ... + + switch (op) { + + // ... + + default: + throw new Exception("Invalid operation: " ~ op); + } + +// ... +--- + +) + +) + +Macros: + SUBTITLE=switch and case Solutions + + DESCRIPTION=Programming in D exercise solutions: the switch statement + + KEYWORDS=programming in d tutorial switch case + diff --git a/target/switch_case.d b/target/switch_case.d new file mode 100644 index 0000000..b2e9e6d --- /dev/null +++ b/target/switch_case.d @@ -0,0 +1,375 @@ +Ddoc + +$(DERS_BOLUMU $(IX switch) $(IX case) $(CH4 switch) and $(CH4 case)) + +$(P +$(C switch) is a statement that allows comparing the value of an expression against multiple values. It is similar to but not the same as an "if, else if, else" chain. $(C case) is used for specifying the values that are to be compared with $(C switch)'s expression. $(C case) is a part of the $(C switch) statement, not a statement itself. +) + +$(P +$(C switch) takes an expression within parentheses, compares the value of that expression to the $(C case) values, and executes the operations of the $(C case) that is equal to the value of the expression. Its syntax consists of a $(C switch) block that contains one or more $(C case) sections and a $(C default) section: +) + +--- + switch ($(I expression)) { + + case $(I value_1): + // operations to execute if the expression is equal to value_1 + // ... + break; + + case $(I value_2): + // operations to execute if the expression is equal to value_2 + // ... + break; + + // ... other cases ... + + default: + // operations to execute if the expression is not equal to any case + // ... + break; + } +--- + +$(P +The expression that $(C switch) takes is not used directly as a logical expression. It is not evaluated as "if this condition is true", as it would be in an $(C if) statement. The $(I value) of the $(C switch) expression is used in equality comparisons with the $(C case) values. It is similar to an "if, else if, else" chain that has only equality comparisons: +) + +--- + auto value = $(I expression); + + if (value == $(I value_1)) { + // operations for value_1 + // ... + + } else if (value == $(I value_2)) { + // operations for value_2 + // ... + } + + // ... other 'else if's ... + + } else { + // operations for other values + // ... + } +--- + +$(P +However, the "if, else if, else" above is not an exact equivalent of the $(C switch) statement. The reasons why will be explained in the following sections. +) + +$(P +If a $(C case) value matches the value of the $(C switch) expression, then the operations that are under the $(C case) are executed. If no value matches, then the operations that are under the $(C default) are executed. +) + +$(H5 $(IX goto case) $(IX goto default) The $(C goto) statement) + +$(P +The use of $(C goto) is generally advised against in most programming languages. However, $(C goto) is useful in $(C switch) statements in some situations, without being as problematic as in other uses. The $(C goto) statement will be covered in more detail in $(LINK2 /ders/d.en/goto.html, a later chapter). +) + +$(P +$(C case) does not introduce a $(I scope) as the $(C if) statement does. Once the operations within an $(C if) or $(C else) scope are finished the evaluation of the entire $(C if) statement is also finished. That does not happen with the $(C case) sections; once a matching $(C case) is found, the execution of the program jumps to that $(C case) and executes the operations under it. When needed in rare situations, $(C goto case) makes the program execution jump to the next $(C case): +) + +--- + switch (value) { + + case 5: + writeln("five"); + $(HILITE goto case); // continues to the next case + + case 4: + writeln("four"); + break; + + default: + writeln("unknown"); + break; + } +--- + +$(P +If $(C value) is 5, the execution continues under the $(C case 5) line and the program prints "five". Then the $(C goto case) statement causes the execution to continue to the next $(C case), and as a result "four" is also printed: +) + +$(SHELL +five +four +) + +$(P +$(IX fallthrough, case) This use of $(C goto case) is optional. Otherwise, if there is no $(C break) statement, the execution $(I falls through) to the next $(C case) or $(C default) section: +) + +--- + case 5: + writeln("five"); + // No 'break' statement; continues to the next case + + case 4: + writeln("four"); + break; +--- + +$(P +$(C goto) can appear in three ways under $(C case) sections: +) + +$(UL + +$(LI $(C goto case) causes the execution to continue to the next $(C case).) + +$(LI $(C goto default) causes the execution to continue to the $(C default) section.) + +$(LI $(C goto case $(I expression)) causes the execution to continue to the $(C case) that matches that expression.) + +) + +$(P +The following program demonstrates these three uses by taking advantage of a $(C foreach) loop: +) + +--- +import std.stdio; + +void main() { + foreach (value; [ 1, 2, 3, 10, 20 ]) { + writefln("--- value: %s ---", value); + + switch (value) { + + case 1: + writeln("case 1"); + $(HILITE goto case); + + case 2: + writeln("case 2"); + $(HILITE goto case 10); + + case 3: + writeln("case 3"); + $(HILITE goto default); + + case 10: + writeln("case 10"); + break; + + default: + writeln("default"); + break; + } + } +} +--- + +$(P +The output: +) + +$(SHELL +--- value: 1 --- +case 1 +case 2 +case 10 +--- value: 2 --- +case 2 +case 10 +--- value: 3 --- +case 3 +default +--- value: 10 --- +case 10 +--- value: 20 --- +default +) + +$(H5 The expression must be an integer, string, or $(C bool) type) + +$(P +Any type can be used in equality comparisons in $(C if) statements. On the other hand, the type of the $(C switch) expression is limited to all integer types, all string types, and $(C bool). +) + +--- + string op = /* ... */; + // ... + switch (op) { + + case "add": + result = first + second; + break; + + case "subtract": + result = first - second; + break; + + case "multiply": + result = first * second; + break; + + case "divide": + result = first / second; + break; + + default: + throw new Exception(format("Unknown operation: %s", op)); + } +--- + +$(P +$(I $(B Note:) The code above throws an exception when the operation is not recognized by the program. We will see exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter).) +) + +$(P +Although it is possible to use $(C bool) expressions as well, because $(C bool) has only two values it may be more suitable to use an $(C if) statement or the ternary operator ($(C ?:)) with that type. +) + +$(H5 $(IX .., case value range) $(IX range, case) Value ranges) + +$(P +Ranges of values can be specified by $(C ..) between $(C case)s: +) + +--- + switch (dieValue) { + + case 1: + writeln("You won"); + break; + + case 2: $(HILITE ..) case 5: + writeln("It's a draw"); + break; + + case 6: + writeln("I won"); + break; + + default: + /* The program should never get here because the cases + * above cover the entire range of valid die values. + * (See 'final switch' below.) */ + break; + } +--- + +$(P +The code above determines that the game ends in a draw when the die value is 2, 3, 4, or 5. +) + +$(H5 Distinct values) + +$(P +$(IX , (comma), case value list) Let's assume that it is a draw for the values 2 and 4, rather than for the values that are in the range [2, 5]. Distinct values of a $(C case) are separated by commas: +) + +--- + case 2$(HILITE ,) 4: + writeln("It's a draw"); + break; +--- + +$(H5 $(IX final switch) The $(C final switch) statement) + +$(P +The $(C final switch) statement works similarly to the regular $(C switch) statement, with the following differences: +) + +$(UL +$(LI It cannot have a $(C default) section. Note that this section is meaningless when the $(C case) sections cover the entire range of values anyway, as has been with the six values of the die above. +) + +$(LI Value ranges cannot be used with $(C case)s (distinct values can be). +) + +$(LI If the expression is of an $(C enum) type, all of the values of the type must be covered by the $(C case)s (we will see $(C enum) types in the next chapter). +) + +) + +--- + int dieValue = 1; + + final switch (dieValue) { + + case 1: + writeln("You won"); + break; + + case 2, 3, 4, 5: + writeln("It's a draw"); + break; + + case 6: + writeln("I won"); + break; + } +--- + +$(H5 When to use) + +$(P +$(C switch) is suitable for comparing the value of an expression against a set of values that are known at compile time. +) + +$(P +When there are only two values to compare, an $(C if) statement may make more sense. For example, to check whether it is heads or tails: +) + +--- + if (headsTailsResult == heads) { + // ... + + } else { + // ... + } +--- + +$(P +As a general rule, $(C switch) is more suitable when there are three or more values to compare. +) + +$(P +When all of the values need to be handled, then prefer $(C final switch). This is especially the case for $(C enum) types. +) + +$(PROBLEM_COK + +$(PROBLEM +Write a calculator program that supports arithmetic operations. Have the program first read the operation as a $(C string), then two values of type $(C double) from the input. The calculator should print the result of the operation. For example, when the operation and values are "add" and "5 7", respectively, the program should print 12. + +$(P +The input can be read as in the following code: +) + +--- + string op; + double first; + double second; + + // ... + + op = strip(readln()); + readf(" %s %s", &first, &second); +--- + +) + +$(PROBLEM +Improve the calculator to support operators like "+" in addition to words like "add". +) + +$(PROBLEM +Have the program throw an exception for unknown operators. We will cover exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter). For now, adapt the $(C throw) statement used above to your program. +) + +) + +Macros: + SUBTITLE=switch and case + + DESCRIPTION=The switch-case statement that is used for comparing the value of an expression against multiple values. + + KEYWORDS=d programming language tutorial book switch case diff --git a/target/templates.d b/target/templates.d new file mode 100644 index 0000000..97d4439 --- /dev/null +++ b/target/templates.d @@ -0,0 +1,1104 @@ +Ddoc + +$(DERS_BOLUMU $(IX template) Templates) + +$(P +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. +) + +$(P +Templates are very useful especially in libraries because they enable writing generic algorithms and data structures, instead of tying them to specific types. +) + +$(P +Compared to the template supports in other languages, D's templates are very powerful and extensive. I will not get into all of the details of templates in this chapter. I will cover only function, struct, and class templates and only $(I type) template parameters. We will see more about templates in $(LINK2 /ders/d.en/templates_more.html, the More Templates chapter). For a complete reference on D templates, see $(LINK2 https://github.com/PhilippeSigaud/D-templates-tutorial, Philippe Sigaud's $(I D Templates: A Tutorial)). +) + +$(P +To see the benefits of templates let's start with a function that prints values in parentheses: +) + +--- +void printInParens(int value) { + writefln("(%s)", value); +} +--- + +$(P +Because the parameter is specified as $(C int), that function can only be used with values of type $(C int), or values that can automatically be converted to $(C int). For example, the compiler would not allow calling it with a floating point type. +) + +$(P +Let's assume that the requirements of a program changes and that other types need to be printed in parentheses as well. One of the solutions for this would be to take advantage of function overloading and provide overloads of the function for all of the types that the function is used with: +) + +--- +// The function that already exists +void printInParens(int value) { + writefln("(%s)", value); +} + +// Overloading the function for 'double' +void printInParens($(HILITE double) value) { + writefln("(%s)", value); +} +--- + +$(P +This solution does not scale well because this time the function cannot be used with e.g. $(C real) or any user-defined type. Although it is possible to overload the function for other types, the cost of doing this may be prohibitive. +) + +$(P +An important observation here is that regardless of the type of the parameter, the contents of the overloads would all be $(I generically) the same: a single $(C writefln()) expression. +) + +$(P +Such genericity is common in algorithms and data structures. For example, the binary search algorithm is independent of the type of the elements: It is about the specific steps and operations of the search. Similarly, the linked list data structure is independent of the type of the elements: Linked list is merely about $(I how) the elements are stored in the container, regardless of their type. +) + +$(P +Templates are useful in such situations: Once a piece of code is described as a template, the compiler generates overloads of the same code automatically according to the actual uses of that code in the program. +) + +$(P +As I have mentioned above, in this chapter I will cover only function, struct, and class templates, and $(I type) template parameters. +) + +$(H5 $(IX function template) Function templates) + +$(P +$(IX parameter, 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. +) + +$(P +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: the template parameter list and the function parameter list: +) + +--- +void printInParens$(HILITE (T))(T value) { + writefln("(%s)", value); +} +--- + +$(P +The $(C T) within the template parameter list above means that $(C T) can be any type. Although $(C T) is an arbitrary name, it is an acronym for "type" and is very common in templates. +) + +$(P +Since $(C T) represents any type, the templated definition of $(C printInParens()) above is sufficient to use it with almost every type, including the user-defined ones: +) + +--- +import std.stdio; + +void printInParens(T)(T value) { + writefln("(%s)", value); +} + +void main() { + printInParens(42); // with int + printInParens(1.2); // with double + + auto myValue = MyStruct(); + printInParens(myValue); // with MyStruct +} + +struct MyStruct { + string toString() const { + return "hello"; + } +} +--- + +$(P +The compiler considers all of the uses of $(C printInParens()) in the program and generates code to support all those uses. The program is then compiled as if the function has been overloaded explicitly for $(C int), $(C double), and $(C MyStruct): +) + +$(MONO +/* Note: These functions are not part of the source + * code. They are the equivalents of the functions that + * the compiler would automatically generate. */ + +void printInParens($(HILITE int) value) { + writefln("(%s)", value); +} + +void printInParens($(HILITE double) value) { + writefln("(%s)", value); +} + +void printInParens($(HILITE MyStruct) value) { + writefln("(%s)", value); +} +) + +$(P +The output of the program is produced by those different $(I instantiations) of the function template: +) + +$(SHELL +(42) +(1.2) +(hello) +) + +$(P +Each template parameter can determine more than one function parameter. For example, both the two function parameters and the return type of the following function template are determined by its single template parameter: +) + +--- +/* Returns a copy of 'slice' except the elements that are + * equal to 'value'. */ +$(HILITE T)[] removed(T)(const($(HILITE T))[] slice, $(HILITE T) value) { + T[] result; + + foreach (element; slice) { + if (element != value) { + result ~= element; + } + } + + return result; +} +--- + +$(H5 More than one template parameter) + +$(P +Let's change the function template to take the parentheses characters as well: +) + +--- +void printInParens(T)(T value, char opening, char closing) { + writeln(opening, value, closing); +} +--- + +$(P +Now we can call the same function with different sets of parentheses: +) + +--- + printInParens(42, '<', '>'); +--- + +$(P +Although being able to specify the parentheses makes the function more usable, specifying the type of the parentheses as $(C char) makes it less flexible because it is not possible to call the function with characters of type $(C wchar) or $(C dchar): +) + +--- + printInParens(42, '→', '←'); $(DERLEME_HATASI) +--- + +$(SHELL_SMALL +Error: template deneme.printInParens(T) cannot deduce +template function from argument types !()(int,$(HILITE wchar),$(HILITE wchar)) +) + +$(P +One solution would be to specify the type of the parentheses as $(C dchar) but this would still be insufficient as this time the function could not be called e.g. with $(C string) or user-defined types. +) + +$(P +$(IX , (comma), template parameter list) Another solution is to leave the type of the parentheses to the compiler as well. Defining an additional template parameter instead of the specific $(C char) is sufficient: +) + +--- +void printInParens(T$(HILITE , ParensType))(T value, + $(HILITE ParensType) opening, + $(HILITE ParensType) closing) { + writeln(opening, value, closing); +} +--- + +$(P +The meaning of the new template parameter is similar to $(C T)'s: $(C ParensType) can be any type. +) + +$(P +It is now possible to use many different types of parentheses. The following are with $(C wchar) and $(C string): +) + +--- + printInParens(42, '→', '←'); + printInParens(1.2, "-=", "=-"); +--- + +$(SHELL +→42← +-=1.2=- +) + +$(P +The flexibility of $(C printInParens()) has been increased, as it now works correctly for any combination of $(C T) and $(C ParensType) as long as those types are printable with $(C writeln()). +) + +$(H5 $(IX type deduction) $(IX deduction, type) Type deduction) + +$(P +The compiler's deciding on what type to use for a template parameter is called $(I type deduction). +) + +$(P +Continuing from the last example above, the compiler decides on the following types according to the two uses of the function template: +) + +$(UL +$(LI $(C int) and $(C wchar) when 42 is printed) +$(LI $(C double) and $(C string) when 1.2 is printed) +) + +$(P +The compiler can deduce types only from the types of the parameter values that are passed to function templates. Although the compiler can usually deduce the types without any ambiguity, there are times when the types must be specified explicitly by the programmer. +) + +$(H5 Explicit type specification) + +$(P +Sometimes it is not possible for the compiler to deduce the template parameters. A situation that this can happen is when the types do not appear in the function parameter list. When template parameters are not related to function parameters, the compiler cannot deduce the template parameter types. +) + +$(P +To see an example of this, let's design a function that asks a question to the user, reads a value as a response, and returns that value. Additionally, let's make this a function template so that it can be used to read any type of response: +) + +--- +$(HILITE T) getResponse$(HILITE (T))(string question) { + writef("%s (%s): ", question, T.stringof); + + $(HILITE T) response; + readf(" %s", &response); + + return response; +} +--- + +$(P +That function template would be very useful in programs to read different types of values from the input. For example, to read some user information, we can imagine calling it as in the following line: +) + +--- + getResponse("What is your age?"); +--- + +$(P +Unfortunately, that call does not give the compiler any clue as to what the template parameter $(C T) should be. What is known is that the question is passed to the function as a $(C string), but the type of the return value cannot be deduced: +) + +$(SHELL_SMALL +Error: template deneme.getResponse(T) $(HILITE cannot deduce) template +function from argument types !()(string) +) + +$(P +$(IX !, template instance) In such cases, the template parameters must be specified explicitly by the programmer. Template parameters are specified in parentheses after an exclamation mark: +) + +--- + getResponse$(HILITE !(int))("What is your age?"); +--- + +$(P +The code above can now be accepted by the compiler and the function template is compiled as $(C T) being an alias of $(C int) within the definition of the template. +) + +$(P +When there is only one template parameter specified, the parentheses around it are optional: +) + +--- + getResponse$(HILITE !int)("What is your age?"); // same as above +--- + +$(P +You may recognize that syntax from $(C to!string), which we have been using in earlier programs. $(C to()) is a function template, which takes the target type of the conversion as a template parameter. Since it has only one template parameter that needs to be specified, it is commonly written as $(C to!string) instead of $(C to!(string)). +) + +$(H5 $(IX instantiation, template) Template instantiation) + +$(P +Automatic generation of code for a specific set of template parameter values is called an $(I instantiation) of that template for that specific set of parameter values. For example, $(C to!string) and $(C to!int) are two different instantiations of the $(C to) function template. +) + +$(P +As I will mention again in a separate section below, distinct instantiations of templates produce distinct and incompatible types. +) + +$(H5 $(IX specialization, template) Template specializations) + +$(P +Although the $(C getResponse()) function template can in theory be used for any template type, the code that the compiler generates may not be suitable for every type. Let's assume that we have the following type that represents points on a two dimensional space: +) + +--- +struct Point { + int x; + int y; +} +--- + +$(P +Although the instantiation of $(C getResponse()) for the $(C Point) type itself would be fine, the generated $(C readf()) call for $(C Point) cannot be compiled. This is because the standard library function $(C readf()) does not know how to read a $(C Point) object. The two lines that actually read the response would look like the following in the $(C Point) instantiation of the $(C getResponse()) function template: +) + +--- + Point response; + readf(" %s", &response); $(DERLEME_HATASI) +--- + +$(P +One way of reading a $(C Point) object would be to read the values of the $(C x) and $(C y) members separately and then to $(I construct) a $(C Point) object from those values. +) + +$(P +Providing a special definition of a template for a specific template parameter value is called a $(I template specialization). The specialization is defined by the type name after a $(C :) character in the template parameter list. A $(C Point) specialization of the $(C getResponse()) function template can be defined as in the following code: +) + +--- +// The general definition of the function template (same as before) +T getResponse(T)(string question) { + writef("%s (%s): ", question, T.stringof); + + T response; + readf(" %s", &response); + + return response; +} + +// The specialization of the function template for Point +T getResponse(T $(HILITE : Point))(string question) { + writefln("%s (Point)", question); + + auto x = getResponse!int(" x"); + auto y = getResponse!int(" y"); + + return Point(x, y); +} +--- + +$(P +Note that the specialization takes advantage of the general definition of $(C getResponse()) to read two $(C int) values to be used as the values of the $(C x) and $(C y) members. +) + +$(P +Instead of instantiating the template itself, now the compiler uses the specialization above whenever $(C getResponse()) is called for the $(C Point) type: +) + +--- + auto center = getResponse!Point("Where is the center?"); +--- + +$(P +Assuming that the user enters 11 and 22: +) + +$(SHELL_SMALL +Where is the center? (Point) + x (int): 11 + y (int): 22 +) + +$(P +The $(C getResponse!int()) calls are directed to the general definition of the template and the $(C getResponse!Point()) calls are directed to the $(C Point) specialization of it. +) + +$(P +As another example, let's consider using the same template with $(C string). As you would remember from the $(LINK2 /ders/d.en/strings.html, Strings chapter), $(C readf()) would read all of the characters from the input as part of a single $(C string) until the end of the input. For that reason, the default definition of $(C getResponse()) would not be useful when reading $(C string) responses: +) + +--- + // Reads the entire input, not only the name! + auto name = getResponse!string("What is your name?"); +--- + +$(P +We can provide a template specialization for $(C string) as well. The following specialization reads just the $(I line) instead: +) + +--- +T getResponse(T $(HILITE : string))(string question) { + writef("%s (string): ", question); + + // Read and ignore whitespace characters which have + // presumably been left over from the previous user input + string response; + do { + response = strip(readln()); + } while (response.length == 0); + + return response; +} +--- + +$(H5 $(IX struct template) $(IX class template) Struct and class templates) + +$(P +The $(C Point) struct may be seen as having a limitation: Because its two members are defined specifically as $(C int), it cannot represent fractional coordinate values. This limitation can be removed if the $(C Point) struct is defined as a template. +) + +$(P +Let's first add a member function that returns the distance to another $(C Point) object: +) + +--- +import std.math; + +// ... + +struct Point { + int x; + int y; + + int distanceTo(in Point that) const { + immutable real xDistance = x - that.x; + immutable real yDistance = y - that.y; + + immutable distance = sqrt((xDistance * xDistance) + + (yDistance * yDistance)); + + return cast(int)distance; + } +} +--- + +$(P +That definition of $(C Point) is suitable when the required precision is relatively low: It can calculate the distance between two points at kilometer precision, e.g. between the center and branch offices of an organization: +) + +--- + auto center = getResponse!Point("Where is the center?"); + auto branch = getResponse!Point("Where is the branch?"); + + writeln("Distance: ", center.distanceTo(branch)); +--- + +$(P +Unfortunately, $(C Point) is inadequate at higher precisions than $(C int) can provide. +) + +$(P +Structs and classes can be defined as templates as well, by specifying a template parameter list after their names. For example, $(C Point) can be defined as a struct template by providing a template parameter and replacing the $(C int)s by that parameter: +) + +--- +struct Point$(HILITE (T)) { + $(HILITE T) x; + $(HILITE T) y; + + $(HILITE T) distanceTo(in Point that) const { + immutable real xDistance = x - that.x; + immutable real yDistance = y - that.y; + + immutable distance = sqrt((xDistance * xDistance) + + (yDistance * yDistance)); + + return cast($(HILITE T))distance; + } +} +--- + +$(P +Since structs and classes are not functions, they cannot be called with parameters. This makes it impossible for the compiler to deduce their template parameters. The template parameter list must always be specified for struct and class templates: +) + +--- + auto center = Point$(HILITE !int)(0, 0); + auto branch = Point$(HILITE !int)(100, 100); + + writeln("Distance: ", center.distanceTo(branch)); +--- + +$(P +The definitions above make the compiler generate code for the $(C int) instantiation of the $(C Point) template, which is the equivalent of its earlier non-template definition. However, now it can be used with any type. For example, when more precision is needed, with $(C double): +) + +--- + auto point1 = Point$(HILITE !double)(1.2, 3.4); + auto point2 = Point$(HILITE !double)(5.6, 7.8); + + writeln(point1.distanceTo(point2)); +--- + +$(P +Although the template itself has been defined independently of any specific type, its single definition makes it possible to represent points of various precisions. +) + +$(P +Simply converting $(C Point) to a template would cause compilation errors in code that has already been written according to its non-template definition. For example, now the $(C Point) specialization of $(C getResponse()) cannot be compiled: +) + +--- +T getResponse(T : Point)(string question) { $(DERLEME_HATASI) + writefln("%s (Point)", question); + + auto x = getResponse!int(" x"); + auto y = getResponse!int(" y"); + + return Point(x, y); +} +--- + +$(P +The reason for the compilation error is that $(C Point) itself is not a type anymore: $(C Point) is now a $(I struct template). Only instantiations of that template would be considered as types. The following changes are required to correctly specialize $(C getResponse()) for any instantiation of $(C Point): +) + +--- +Point!T getResponse(T : Point!T)(string question) { // 2, 1 + writefln("%s (Point!%s)", question, T.stringof); // 5 + + auto x = getResponse!T(" x"); // 3a + auto y = getResponse!T(" y"); // 3b + + return Point!T(x, y); // 4 +} +--- + +$(OL + +$(LI +In order for this template specialization to support all instantiations of $(C Point), the template parameter list must mention $(C Point!T). This simply means that the $(C getResponse()) specialization is for $(C Point!T), regardless of $(C T). This specialization would match $(C Point!int), $(C Point!double), etc. +) + +$(LI +Similarly, to return the correct type as the response, the return type must be specified as $(C Point!T) as well. +) + +$(LI +Since the types of $(C x) and $(C y) members of $(C Point!T) are now $(C T), as opposed to $(C int), the members must be read by calling $(C getResponse!T()), not $(C getResponse!int()), as the latter would be correct only for $(C Point!int). +) + +$(LI +Similar to items 1 and 2, the type of the return value is $(C Point!T). +) + +$(LI +To print the name of the type accurately for every type, as in $(C Point!int), $(C Point!double), etc., $(C T.stringof) is used. +) + +) + +$(H5 $(IX default template parameter) Default template parameters) + +$(P +Sometimes it is cumbersome to provide template parameter types every time a template is used, especially when that type is almost always a particular type. For example, $(C getResponse()) may almost always be called for the $(C int) type in the program, and only in a few places for the $(C double) type. +) + +$(P +It is possible to specify default types for template parameters, which are assumed when the types are not explicitly provided. Default parameter types are specified after the $(C =) character: +) + +--- +T getResponse(T $(HILITE = int))(string question) { + // ... +} + +// ... + + auto age = getResponse("What is your age?"); +--- + +$(P +As no type has been specified when calling $(C getResponse()) above, $(C T) becomes the default type $(C int) and the call ends up being the equivalent of $(C getResponse!int()). +) + +$(P +Default template parameters can be specified for struct and class templates as well, but in their case the template parameter list must always be written even when empty: +) + +--- +struct Point(T = int) { + // ... +} + +// ... + + Point!$(HILITE ()) center; +--- + + +$(P +Similar to default function parameter values as we have seen in the $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters chapter), default template parameters can be specified for all of the template parameters or for the $(I last) ones: +) + +--- +void myTemplate(T0, T1 $(HILITE = int), T2 $(HILITE = char))() { + // ... +} +--- + +$(P +The last two template parameters of that function may be left unspecified but the first one is required: +) + +--- + myTemplate!string(); +--- + +$(P +In that usage, the second and third parameters are $(C int) and $(C char), respectively. +) + +$(H5 Every template instantiation yields a distinct type) + +$(P +Every instantiation of a template for a given set of types is considered to be a distinct type. For example, $(C Point!int) and $(C Point!double) are separate types: +) + +--- +Point!int point3 = Point!double(0.25, 0.75); $(DERLEME_HATASI) +--- + +$(P +Those different types cannot be used in the assignment operation above: +) + +$(SHELL_SMALL +Error: cannot implicitly convert expression (Point(0.25,0.75)) +of type $(HILITE Point!(double)) to $(HILITE Point!(int)) +) + +$(H5 A compile-time feature) + +$(P +Templates are entirely a compile-time feature. The instances of templates are generated by the compiler at compile time. +) + +$(H5 Class template example: stack data structure) + +$(P +Struct and class templates are commonly used in the implementations of data structures. Let's design a stack container that will be able to contain any type. +) + +$(P +Stack is one of the simplest data structures. It represents a container where elements are placed conceptually on top of each other as would be in a stack of papers. New elements go on top, and only the topmost element is accessed. When an element is removed, it is always the topmost one. +) + +$(P +If we also define a property that returns the total number of elements in the stack, all of the operations of this data structure would be the following: +) + +$(UL +$(LI Add element ($(C push()))) +$(LI Remove element ($(C pop()))) +$(LI Access the topmost element ($(C .top))) +$(LI Report the number of elements ($(C .length))) +) + +$(P +An array can be used to store the elements such that the last element of the array would be representing the topmost element of the stack. Finally, it can be defined as a class template to be able to contain elements of any type: +) + +--- +$(CODE_NAME Stack)class Stack$(HILITE (T)) { +private: + + $(HILITE T)[] elements; + +public: + + void push($(HILITE T) element) { + elements ~= element; + } + + void pop() { + --elements.length; + } + + $(HILITE T) top() const @property { + return elements[$ - 1]; + } + + size_t length() const @property { + return elements.length; + } +} +--- + +$(P +As a design decision, $(C push()) and $(C pop()) are defined as regular member functions, and $(C .top) and $(C .length) are defined as properties because they can be seen as providing simple information about the stack collection. +) + +$(P +Here is a $(C unittest) block for this class that uses its $(C int) instantiation: +) + +--- +unittest { + auto stack = new Stack$(HILITE !int); + + // The newly added element must appear on top + stack.push(42); + assert(stack.top == 42); + assert(stack.length == 1); + + // .top and .length should not affect the elements + assert(stack.top == 42); + assert(stack.length == 1); + + // The newly added element must appear on top + stack.push(100); + assert(stack.top == 100); + assert(stack.length == 2); + + // Removing the last element must expose the previous one + stack.pop(); + assert(stack.top == 42); + assert(stack.length == 1); + + // The stack must become empty when the last element is + // removed + stack.pop(); + assert(stack.length == 0); +} +--- + +$(P +To take advantage of this class template, let's try using it this time with a user-defined type. As an example, here is a modified version of $(C Point): +) + +--- +struct Point(T) { + T x; + T y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} +--- + +$(P +A $(C Stack) that contains elements of type $(C Point!double) can be defined like the following: +) + +--- + auto points = new Stack!(Point!double); +--- + +$(P +Here is a test program that first adds ten elements to this stack and then removes them one by one: +) + +--- +$(CODE_XREF Stack)import std.string; +import std.stdio; +import std.random; + +struct Point(T) { + T x; + T y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} + +// Returns a random value between -0.50 and 0.50. +double random_double() +out (result) { + assert((result >= -0.50) && (result < 0.50)); + +} body { + return (double(uniform(0, 100)) - 50) / 100; +} + +// Returns a Stack that contains 'count' number of random +// Point!double elements. +Stack!(Point!double) randomPoints(size_t count) +out (result) { + assert(result.length == count); + +} body { + auto points = new Stack!(Point!double); + + foreach (i; 0 .. count) { + immutable point = Point!double(random_double(), + random_double()); + writeln("adding : ", point); + points.push(point); + } + + return points; +} + +void main() { + auto stackedPoints = randomPoints(10); + + while (stackedPoints.length) { + writeln("removing: ", stackedPoints.top); + stackedPoints.pop(); + } +} +--- + +$(P +As the output of the program shows, the elements are removed in the reverse order as they have been added: +) + +$(SHELL_SMALL +adding : (-0.02,-0.01) +adding : (0.17,-0.5) +adding : (0.12,0.23) +adding : (-0.05,-0.47) +adding : (-0.19,-0.11) +adding : (0.42,-0.32) +adding : (0.48,-0.49) +adding : (0.35,0.38) +adding : (-0.2,-0.32) +adding : (0.34,0.27) +removing: (0.34,0.27) +removing: (-0.2,-0.32) +removing: (0.35,0.38) +removing: (0.48,-0.49) +removing: (0.42,-0.32) +removing: (-0.19,-0.11) +removing: (-0.05,-0.47) +removing: (0.12,0.23) +removing: (0.17,-0.5) +removing: (-0.02,-0.01) +) + +$(H5 Function template example: binary search algorithm) + +$(P +Binary search is the fastest algorithm to search for an element among the elements of an already sorted array. It is a very simple algorithm: The element in the middle is considered; if that element is the one that has been sought, then the search is over. If not, then the algorithm is repeated on the elements that are either on the left-hand side or on the right-hand side of the middle element, depending on whether the sought element is greater or less than the middle element. +) + +$(P +Algorithms that repeat themselves on a smaller range of the initial elements are recursive. Let's write the binary search algorithm recursively by calling itself. +) + +$(P +Before converting it to a template, let's first write this function to support only arrays of $(C int). We can easily convert it to a template later, by adding a template parameter list and replacing appropriate $(C int)s in its definition by $(C T)s. Here is a binary search algorithm that works on arrays of $(C int): +) + +--- +/* This function returns the index of the value if it exists + * in the array, size_t.max otherwise. */ +size_t binarySearch(const int[] values, in int value) { + // The value is not in the array if the array is empty. + if (values.length == 0) { + return size_t.max; + } + + immutable midPoint = values.length / 2; + + if (value == values[midPoint]) { + // Found. + return midPoint; + + } else if (value < values[midPoint]) { + // The value can only be in the left-hand side; let's + // search in a slice that represents that half. + return binarySearch(values[0 .. midPoint], value); + + } else { + // The value can only be in the right-hand side; let's + // search in the right-hand side. + auto index = + binarySearch(values[midPoint + 1 .. $], value); + + if (index != size_t.max) { + // Adjust the index; it is 0-based in the + // right-hand side slice. + index += midPoint + 1; + } + + return index; + } + + assert(false, "We should have never gotten to this line"); +} +--- + +$(P +The function above implements this simple algorithm in four steps: +) + +$(UL +$(LI If the array is empty, return $(C size_t.max) to indicate that the value has not been found.) +$(LI If the element at the mid-point is equal to the sought value, then return the index of that element.) +$(LI If the value is less than the element at the mid-point, then repeat the same algorithm on the left-hand side.) +$(LI Else, repeat the same algorithm on the right-hand side.) +) + +$(P +Here is a unittest block that tests the function: +) + +--- +unittest { + auto array = [ 1, 2, 3, 5 ]; + assert(binarySearch(array, 0) == size_t.max); + assert(binarySearch(array, 1) == 0); + assert(binarySearch(array, 4) == size_t.max); + assert(binarySearch(array, 5) == 3); + assert(binarySearch(array, 6) == size_t.max); +} +--- + +$(P +Now that the function has been implemented and tested for $(C int), we can convert it to a template. $(C int) appears only in two places in the definition of the template: +) + +--- +size_t binarySearch(const int[] values, in int value) { + // ... int does not appear here ... +} +--- + +$(P +The $(C int)s that appear in the parameter list are the types of the elements and the value. Specifying those as template parameters is sufficient to make this algorithm a template and to be usable with other types as well: +) + +--- +size_t binarySearch$(HILITE (T))(const $(HILITE T)[] values, in $(HILITE T) value) { + // ... +} +--- + +$(P +That function template can be used with any type that matches the operations that are applied to that type in the template. In $(C binarySearch()), the elements are used only with comparison operators $(C ==) and $(C <): +) + +--- + if (value $(HILITE ==) values[midPoint]) { + // ... + + } else if (value $(HILITE <) values[midPoint]) { + + // ... +--- + +$(P +For that reason, $(C Point) is not ready to be used with $(C binarySearch()) yet: +) + +--- +import std.string; + +struct Point(T) { + T x; + T y; + + string toString() const { + return format("(%s,%s)", x, y); + } +} + +void $(CODE_DONT_TEST)main() { + Point!int[] points; + + foreach (i; 0 .. 15) { + points ~= Point!int(i, i); + } + + assert(binarySearch(points, Point!int(10, 10)) == 10); +} +--- + +$(P +The program above would cause a compilation error: +) + +$(SHELL_SMALL +Error: need member function $(HILITE opCmp()) for struct +const(Point!(int)) to compare +) + +$(P +According to the error message, $(C opCmp()) needs to be defined for $(C Point). $(C opCmp()) has been covered in $(LINK2 /ders/d.en/operator_overloading.html, the Operator Overloading chapter): +) + +--- +struct Point(T) { +// ... + + int opCmp(const ref Point that) const { + return (x == that.x + ? y - that.y + : x - that.x); + } +} +--- + +$(H5 Summary) + +$(P +We will see other features of templates in $(LINK2 /ders/d.en/templates_more.html, a later chapter). The following are what we have covered in this chapter: +) + +$(UL + +$(LI Templates define the code as a pattern, for the compiler to generate instances of it according to the actual uses in the program.) + +$(LI Templates are a compile-time feature.) + +$(LI Specifying template parameter lists is sufficient to make function, struct, and class definitions templates. + +--- +void functionTemplate$(HILITE (T))(T functionParameter) { + // ... +} + +class ClassTemplate$(HILITE (T)) { + // ... +} +--- + +) + +$(LI Template arguments can be specified explicitly after an exclamation mark. The parentheses are not necessary when there is only one token inside the parentheses. + +--- + auto object1 = new ClassTemplate!(double); + auto object2 = new ClassTemplate!double; // same thing +--- + +) + +$(LI Every template instantiation yields a distinct type. + +--- + assert(typeid(ClassTemplate!$(HILITE int)) != + typeid(ClassTemplate!$(HILITE uint))); +--- + +) + +$(LI Template arguments can only be deduced for function templates. + +--- + functionTemplate(42); // functionTemplate!int is deduced +--- + +) + +$(LI Templates can be specialized for the type that is after the $(C :) character. + +--- +class ClassTemplate(T $(HILITE : dchar)) { + // ... +} +--- + +) + +$(LI Default template arguments are specified after the $(C =) character. + +--- +void functionTemplate(T $(HILITE = long))(T functionParameter) { + // ... +} +--- + +) + +) + +Macros: + SUBTITLE=Templates + + DESCRIPTION=Introduction to D's generic programming features. Templates allow defining code as a pattern and have the compiler generate actual code according to how the template is used in the program. + + KEYWORDS=d programming language tutorial book templates diff --git a/target/templates_more.d b/target/templates_more.d new file mode 100644 index 0000000..72c504c --- /dev/null +++ b/target/templates_more.d @@ -0,0 +1,2107 @@ +Ddoc + +$(DERS_BOLUMU $(IX template) More Templates) + +$(P +We have seen the power and convenience of templates in $(LINK2 /ders/d.en/templates.html, the Templates chapter). A single templated definition of an algorithm or a data structure is sufficient to use that definition for multiple types. +) + +$(P +That chapter covered only the most common uses of templates: function, $(C struct), and $(C class) templates and their uses with $(I type) template parameters. In this chapter we will see templates in more detail. Before going further, I recommend that you review at least the summary section of that chapter. +) + +$(H5 $(IX shortcut syntax, template) The shortcut syntax) + +$(P +In addition to being powerful, D templates are easy to define and use and they are very readable. Defining a function, $(C struct), or $(C class) template is as simple as providing a template parameter list: +) + +--- +T twice$(HILITE (T))(T value) { + return 2 * value; +} + +class Fraction$(HILITE (T)) { + T numerator; + T denominator; + + // ... +} +--- + +$(P +Template definitions like the ones above are taking advantage of D's shortcut template syntax. +) + +$(P +In their full syntax, templates are defined by the $(C template) keyword. The equivalents of the two template definitions above are the following: +) + +--- +template twice$(HILITE (T)) { + T twice(T value) { + return 2 * value; + } +} + +template Fraction$(HILITE (T)) { + class Fraction { + T numerator; + T denominator; + + // ... + } +} +--- + +$(P +Although most templates are defined by the shortcut syntax, the compiler always uses the full syntax. We can imagine the compiler applying the following steps to convert a shortcut syntax to its full form behind the scenes: +) + +$(OL +$(LI Wrap the definition with a template block.) +$(LI Give the same name to that block.) +$(LI Move the template parameter list to the template block.) +) + +$(P +The full syntax that is arrived after those steps is called an $(I eponymous template), which the programmer can define explicitly as well. We will see eponymous templates later below. +) + +$(H6 $(IX name space, template) Template name space) + +$(P +It is possible to have more than one definition inside a template block. The following template contains both a function and a $(C struct) definition: +) + +--- +template MyTemplate(T) { + T foo(T value) { + return value / 3; + } + + struct S { + T member; + } +} +--- + +$(P +Instantiating the template for a specific type instantiates all of the definitions inside the block. The following code instantiates the template for $(C int) and $(C double): +) + +--- + auto result = $(HILITE MyTemplate!int).foo(42); + writeln(result); + + auto s = $(HILITE MyTemplate!double).S(5.6); + writeln(s.member); +--- + +$(P +A specific instantiation of a template introduces a $(I name space). The definitions that are inside an instantiation can be used by that name. However, if these names are too long, it is always possible to use aliases as we have seen in $(LINK2 /ders/d.en/alias.html, the $(C alias) chapter): +) + +--- + alias MyStruct = MyTemplate!dchar.S; + +// ... + + auto o = $(HILITE MyStruct)('a'); + writeln(o.member); +--- + +$(H6 $(IX eponymous template) Eponymous templates) + +$(P +Eponymous templates are $(C template) blocks that contain a definition that has the same name as that block. In fact, each shortcut template syntax is the shortcut of an eponymous template. +) + +$(P +As an example, assume that a program needs to qualify types that are larger than 20 bytes as $(I too large). Such a qualification can be achieved by a constant $(C bool) value inside a template block: +) + +--- +template isTooLarge(T) { + enum isTooLarge = T.sizeof > 20; +} +--- + +$(P +Note how the names of both the template block and its only definition are the same. This eponymous template is used by the shortcut syntax instead of the whole $(C isTooLarge!int.isTooLarge): +) + +--- + writeln($(HILITE isTooLarge!int)); +--- + +$(P +The highlighted part above is the same as the $(C bool) value inside the block. Since the size of $(C int) is less than 20, the output of the code would be $(C false). +) + +$(P +That eponymous template can be defined by the shortcut syntax as well: +) + +--- +enum isTooLarge$(HILITE (T)) = T.sizeof > 20; +--- + +$(P +A common use of eponymous templates is defining type aliases depending on certain conditions. For example, the following eponymous template picks the larger of two types by setting an alias to it: +) + +--- +$(CODE_NAME LargerOf)template LargerOf(A, B) { + static if (A.sizeof < B.sizeof) { + alias LargerOf = B; + + } else { + alias LargerOf = A; + } +} +--- + +$(P +Since $(C long) is larger than $(C int) (8 bytes versus 4 bytes), $(C LargerOf!(int, long)) would be the same as the type $(C long). Such templates are especially useful in other templates where the two types are template parameters themselves (or depend on template parameters): +) + +--- +$(CODE_XREF LargerOf)// ... + +/* The return type of this function is the larger of its two + * template parameters: Either type A or type B. */ +auto calculate(A, B)(A a, B b) { + $(HILITE LargerOf!(A, B)) result; + // ... + return result; +} + +void main() { + auto f = calculate(1, 2$(HILITE L)); + static assert(is (typeof(f) == $(HILITE long))); +} +--- + +$(H5 Kinds of templates) + +$(H6 Function, class, and struct templates) + +$(P +We have already covered function, $(C class), and $(C struct) templates in $(LINK2 /ders/d.en/templates.html, the Templates chapter) and we have seen many examples of them since then. +) + +$(H6 $(IX member function template) Member function templates) + +$(P +$(C struct) and $(C class) member functions can be templates as well. For example, the following $(C put()) member function template would work with any parameter type as long as that type is compatible with the operations inside the template (for this specific template, it should be convertible to $(C string)): +) + +--- +class Sink { + string content; + + void put$(HILITE (T))(auto ref const T value) { + import std.conv; + content ~= value.to!string; + } +} +--- + +$(P +However, as templates can have potentially infinite number of instantiations, they cannot be $(LINK2 /ders/d.en/inheritance.html, virtual functions) because the compiler cannot know which specific instantiations of a template to include in the interface. (Accordingly, the $(C abstract) keyword cannot be used either.) +) + +$(P +For example, although the presence of the $(C put()) template in the following subclass may give the impression that it is overriding a function, it actually hides the $(C put) name of the superclass (see $(I name hiding) in $(LINK2 /ders/d.en/alias.html, the alias chapter)): +) + +--- +class Sink { + string content; + + void put(T)(auto ref const T value) { + import std.conv; + content ~= value.to!string; + } +} + +class SpecialSink : Sink { + /* The following template definition does not override + * the template instances of the superclass; it hides + * those names. */ + void put(T)(auto ref const T value) { + import std.string; + super.put(format("{%s}", value)); + } +} + +void fillSink($(HILITE Sink) sink) { + /* The following function calls are not virtual. Because + * parameter 'sink' is of type 'Sink', the calls will + * always be dispatched to Sink's 'put' template + * instances. */ + + sink.put(42); + sink.put("hello"); +} + +void main() { + auto sink = new $(HILITE SpecialSink)(); + fillSink(sink); + + import std.stdio; + writeln(sink.content); +} +--- + +$(P +As a result, although the object actually is a $(C SpecialSink), both of the calls inside $(C fillSink()) are dispatched to $(C Sink) and the content does not contain the curly brackets that $(C SpecialSink.put()) inserts: +) + +$(SHELL +42hello $(SHELL_NOTE Sink's behavior, not SpecialSink's) +) + +$(H6 $(IX union template) Union templates) + +$(P +Union templates are similar to struct templates. The shortcut syntax is available for them as well. +) + +$(P +As an example, let's design a more general version of the $(C IpAdress) $(C union) that we saw in $(LINK2 /ders/d.en/union.html, the Unions chapter). There, the value of the IPv4 address was kept as a $(C uint) member in that earlier version of $(C IpAdress), and the element type of the segment array was $(C ubyte): +) + +--- +union IpAddress { + uint value; + ubyte[4] bytes; +} +--- + +$(P +The $(C bytes) array provided easy access to the four segments of the IPv4 address. +) + +$(P +The same concept can be implemented in a more general way as the following $(C union) template: +) + +--- +union SegmentedValue($(HILITE ActualT, SegmentT)) { + ActualT value; + SegmentT[/* number of segments */] segments; +} +--- + +$(P +That template would allow specifying the types of the value and its segments freely. +) + +$(P +The number of segments that are needed depends on the types of the actual value and the segments. Since an IPv4 address has four $(C ubyte) segments, that value was hard-coded as $(C 4) in the earlier definition of $(C IpAddress). For the $(C SegmentedValue) template, the number of segments must be computed at compile time when the template is instantiated for the two specific types. +) + +$(P +The following eponymous template takes advantage of the $(C .sizeof) properties of the two types to calculate the number of segments needed: +) + +--- +$(CODE_NAME segmentCount)template segmentCount(ActualT, SegmentT) { + enum segmentCount = ((ActualT.sizeof + (SegmentT.sizeof - 1)) + / SegmentT.sizeof); +} +--- + +$(P +The shortcut syntax may be more readable: +) + +--- +enum segmentCount(ActualT, SegmentT) = + ((ActualT.sizeof + (SegmentT.sizeof - 1)) + / SegmentT.sizeof); +--- + +$(P +$(I $(B Note:) The expression $(C SegmentT.sizeof - 1) is for when the sizes of the types cannot be divided evenly. For example, when the actual type is 5 bytes and the segment type is 2 bytes, even though a total of 3 segments are needed, the result of the integer division 5/2 would incorrectly be 2.) +) + +$(P +The definition of the union template is now complete: +) + +--- +$(CODE_NAME SegmentedValue)$(CODE_XREF segmentCount)union SegmentedValue(ActualT, SegmentT) { + ActualT value; + SegmentT[segmentCount!(ActualT, SegmentT)] segments; +} +--- + +$(P +Instantiation of the template for $(C uint) and $(C ubyte) would be the equivalent of the earlier definition of $(C IpAddress): +) + +--- +$(CODE_XREF SegmentedValue)import std.stdio; + +void main() { + auto address = SegmentedValue!($(HILITE uint, ubyte))(0xc0a80102); + + foreach (octet; address.segments) { + write(octet, ' '); + } +} +--- + +$(P +The output of the program is the same as the one in $(LINK2 /ders/d.en/union.html, the Unions chapter): +) + +$(SHELL_SMALL +2 1 168 192 +) + +$(P +To demonstrate the flexibility of this template, let's imagine that it is required to access the parts of the IPv4 address as two $(C ushort) values. It would be as easy as providing $(C ushort) as the segment type: +) + +--- + auto address = SegmentedValue!(uint, $(HILITE ushort))(0xc0a80102); +--- + +$(P +Although unusual for an IPv4 address, the output of the program would consist of two $(C ushort) segment values: +) + +$(SHELL_SMALL +258 49320 +) + +$(H6 $(IX interface template) Interface templates) + +$(P +Interface templates provide flexibility on the types that are used on an interface (as well as values such as sizes of fixed-length arrays and other features of an interface). +) + +$(P +Let's define an interface for colored objects where the type of the color is determined by a template parameter: +) + +--- +interface ColoredObject(ColorT) { + void paint(ColorT color); +} +--- + +$(P +That interface template requires that its subtypes must define the $(C paint()) function but it leaves the type of the color flexible. +) + +$(P +A class that represents a frame on a web page may choose to use a color type that is represented by its red, green, and blue components: +) + +--- +struct RGB { + ubyte red; + ubyte green; + ubyte blue; +} + +class PageFrame : ColoredObject$(HILITE !RGB) { + void paint(RGB color) { + // ... + } +} +--- + +$(P +On the other hand, a class that uses the frequency of light can choose a completely different type to represent color: +) + +--- +alias Frequency = double; + +class Bulb : ColoredObject$(HILITE !Frequency) { + void paint(Frequency color) { + // ... + } +} +--- + +$(P +However, as explained in $(LINK2 /ders/d.en/templates.html, the Templates chapter), "every template instantiation yields a distinct type". Accordingly, the interfaces $(C ColoredObject!RGB) and $(C ColoredObject!Frequency) are unrelated interfaces, and $(C PageFrame) and $(C Bulb) are unrelated classes. +) + +$(H5 $(IX parameter, template) Kinds of template parameters) + +$(P +The template parameters that we have seen so far have all been $(I type) parameters. So far, parameters like $(C T) and $(C ColorT) all represented types. For example, $(C T) meant $(C int), $(C double), $(C Student), etc. depending on the instantiation of the template. +) + +$(P +There are other kinds of template parameters: value, $(C this), $(C alias), and tuple. +) + +$(H6 $(IX type template parameter) Type template parameters) + +$(P +This section is only for completeness. All of the templates that we have seen so far had type parameters. +) + +$(H6 $(IX value template parameter) Value template parameters) + +$(P +Value template parameters allow flexibility on certain values used in the template implementation. +) + +$(P +Since templates are a compile-time feature, the values for the value template parameters must be known at compile time; values that must be calculated at run time cannot be used. +) + +$(P +To see the advantage of value template parameters, let's start with a set of structs representing geometric shapes: +) + +--- +struct Triangle { + Point[3] corners; +// ... +} + +struct Rectangle { + Point[4] corners; +// ... +} + +struct Pentagon { + Point[5] corners; +// ... +} +--- + +$(P +Let's assume that other member variables and member functions of those types are exactly the same and that the only difference is the $(I value) that determines the number of corners. +) + +$(P +Value template parameters help in such cases. The following struct template is sufficient to represent all of the types above and more: +) + +--- +struct Polygon$(HILITE (size_t N)) { + Point[N] corners; +// ... +} +--- + +$(P +The only template parameter of that struct template is a value named $(C N) of type $(C size_t). The value $(C N) can be used as a compile-time constant anywhere inside the template. +) + +$(P +That template is flexible enough to represent shapes of any sides: +) + +--- + auto centagon = Polygon!100(); +--- + +$(P +The following aliases correspond to the earlier struct definitions: +) + +--- +alias Triangle = Polygon!3; +alias Rectangle = Polygon!4; +alias Pentagon = Polygon!5; + +// ... + + auto triangle = Triangle(); + auto rectangle = Rectangle(); + auto pentagon = Pentagon(); +--- + +$(P +The type of the $(I value) template parameter above was $(C size_t). As long as the value can be known at compile time, a value template parameter can be of any type: a fundamental type, a $(C struct) type, an array, a string, etc. +) + +--- +struct S { + int i; +} + +// Value template parameter of struct S +void foo($(HILITE S s))() { + // ... +} + +void main() { + foo!(S(42))(); // Instantiating with literal S(42) +} +--- + +$(P +The following example uses a $(C string) template parameter to represent an XML tag to produce a simple XML output: +) + +$(UL +$(LI First the tag between the $(C <) $(C >) characters: $(C <tag>)) +$(LI Then the value) +$(LI Finally the tag between the $(C </) $(C >) characters: $(C </tag>)) +) + +$(P +For example, an XML tag representing $(I location 42) would be printed as $(C <location>42</location>). +) + +--- +$(CODE_NAME XmlElement)import std.string; + +class XmlElement$(HILITE (string tag)) { + double value; + + this(double value) { + this.value = value; + } + + override string toString() const { + return format("<%s>%s", tag, value, tag); + } +} +--- + +$(P +Note that the template parameter is not about a type that is used in the implementation of the template, rather it is about a $(C string) $(I value). That value can be used anywhere inside the template as a $(C string). +) + +$(P +The XML elements that a program needs can be defined as aliases as in the following code: +) + +--- +$(CODE_XREF XmlElement)alias Location = XmlElement!"location"; +alias Temperature = XmlElement!"temperature"; +alias Weight = XmlElement!"weight"; + +void main() { + Object[] elements; + + elements ~= new Location(1); + elements ~= new Temperature(23); + elements ~= new Weight(78); + + writeln(elements); +} +--- + +$(P +The output: +) + +$(SHELL_SMALL +[<location>1</location>, <temperature>23</temperature>, <weight>78</weight>] +) + +$(P +Value template parameters can have default values as well. For example, the following struct template represents points in a multi-dimensional space where the default number of dimensions is 3: +) + +--- +struct Point(T, size_t dimension $(HILITE = 3)) { + T[dimension] coordinates; +} +--- + +$(P +That template can be used without specifying the $(C dimension) template parameter: +) + +--- + Point!double center; // a point in 3-dimensional space +--- + +$(P +The number of dimensions can still be specified when needed: +) + +--- + Point!(int, 2) point; // a point on a surface +--- + +$(P +We have seen in $(LINK2 /ders/d.en/parameter_flexibility.html, the Variable Number of Parameters chapter) how $(I special keywords) work differently depending on whether they appear inside code or as default function arguments. +) + +$(P +Similarly, when used as default template arguments, the special keywords refer to where the template is instantiated at, not where the keywords appear: +) + +--- +import std.stdio; + +void func(T, + string functionName = $(HILITE __FUNCTION__), + string file = $(HILITE __FILE__), + size_t line = $(HILITE __LINE__))(T parameter) { + writefln("Instantiated at function %s at file %s, line %s.", + functionName, file, line); +} + +void main() { + func(42); $(CODE_NOTE $(HILITE line 12)) +} +--- + +$(P +Although the special keywords appear in the definition of the template, their values refer to $(C main()), where the template is instantiated at: +) + +$(SHELL +Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 12). +) + +$(P +We will use $(C __FUNCTION__) below in a multi-dimensional operator overloading example. +) + +$(H6 $(IX this, template parameter) $(C this) template parameters for member functions) + +$(P +Member functions can be templates as well. Their template parameters have the same meanings as other templates. +) + +$(P +However, unlike other templates, member function template parameters can also be $(I $(C this) parameters). In that case, the identifier that comes after the $(C this) keyword represents the exact type of the $(C this) reference of the object. ($(I $(C this) reference) means the object itself, as is commonly written in constructors as $(C this.member = value).) +) + +--- +struct MyStruct(T) { + void foo($(HILITE this OwnType))() const { + writeln("Type of this object: ", OwnType.stringof); + } +} +--- + +$(P +The $(C OwnType) template parameter is the actual type of the object that the member function is called on: +) + +--- + auto m = MyStruct!int(); + auto c = const(MyStruct!int)(); + auto i = immutable(MyStruct!int)(); + + m.foo(); + c.foo(); + i.foo(); +--- + +$(P +The output: +) + +$(SHELL_SMALL +Type of this object: MyStruct!int +Type of this object: const(MyStruct!int) +Type of this object: immutable(MyStruct!int) +) + +$(P +As you can see, the type includes the corresponding type of $(C T) as well as the type qualifiers like $(C const) and $(C immutable). +) + +$(P +The $(C struct) (or $(C class)) need not be a template. $(C this) template parameters can appear on member function templates of non-templated types as well. +) + +$(P +$(C this) template parameters can be useful in $(I template mixins) as well, which we will see two chapters later. +) + +$(H6 $(IX alias, template parameter) $(C alias) template parameters) + +$(P +$(C alias) template parameters can correspond to any symbol or expression that is used in the program. The only constraint on such a template argument is that the argument must be compatible with its use inside the template. +) + +$(P +$(C filter()) and $(C map()) use $(C alias) template parameters to determine the operations that they execute. +) + +$(P +Let's see a simple example on a $(C struct) template that is for modifying an existing variable. The $(C struct) template takes the variable as an $(C alias) parameter: +) + +--- +struct MyStruct(alias variable) { + void set(int value) { + variable = value; + } +} +--- + +$(P +The member function simply assigns its parameter to the variable that the $(C struct) template is instantiated with. That variable must be specified during the instantiation of the template: +) + +--- + int x = 1; + int y = 2; + + auto object = MyStruct!$(HILITE x)(); + object.set(10); + writeln("x: ", x, ", y: ", y); +--- + +$(P +In that instantiation, the $(C variable) template parameter corresponds to the variable $(C x): +) + +$(SHELL_SMALL +x: $(HILITE 10), y: 2 +) + +$(P +Conversely, $(C MyStruct!y) instantiation of the template would associate $(C variable) with $(C y). +) + +$(P +Let's now have an $(C alias) parameter that represents a callable entity, similar to $(C filter()) and $(C map()): +) + +--- +void caller(alias func)() { + write("calling: "); + $(HILITE func()); +} +--- + +$(P +As seen by the $(C ()) parentheses, $(C caller()) uses its template parameter as a function. Additionally, since the parentheses are empty, it must be legal to call the function without specifying any arguments. +) + +$(P +Let's have the following two functions that match that description. They can both represent $(C func) because they can be called as $(C func()) in the template: +) + +--- +void foo() { + writeln("foo called."); +} + +void bar() { + writeln("bar called."); +} +--- + +$(P +Those functions can be used as the $(C alias) parameter of $(C caller()): +) + +--- + caller!foo(); + caller!bar(); +--- + +$(P +The output: +) + +$(SHELL_SMALL +calling: foo called. +calling: bar called. +) + +$(P +As long as it matches the way it is used in the template, any symbol can be used as an $(C alias) parameter. As a counter example, using an $(C int) variable with $(C caller()) would cause a compilation error: +) + +--- + int variable; + caller!variable(); $(DERLEME_HATASI) +--- + +$(P +The compilation error indicates that the variable does not match its use in the template: +) + +$(SHELL_SMALL +Error: $(HILITE function expected before ()), not variable of type int +) + +$(P +Although the mistake is with the $(C caller!variable) instantiation, the compilation error necessarily points at $(C func()) inside the $(C caller()) template because from the point of view of the compiler the error is with trying to call $(C variable) as a function. One way of dealing with this issue is to use $(I template constraints), which we will see below. +) + +$(P +If the variable supports the function call syntax perhaps because it has an $(C opCall()) overload or it is a function literal, it would still work with the $(C caller()) template. The following example demonstrates both of those cases: +) + +--- +class C { + void opCall() { + writeln("C.opCall called."); + } +} + +// ... + + auto o = new C(); + caller!o(); + + caller!({ writeln("Function literal called."); })(); +--- + +$(P +The output: +) + +$(SHELL +calling: C.opCall called. +calling: Function literal called. +) + +$(P +$(C alias) parameters can be specialized as well. However, they have a different specialization syntax. The specialized type must be specified between the $(C alias) keyword and the name of the parameter: +) + +--- +import std.stdio; + +void foo(alias variable)() { + writefln("The general definition is using '%s' of type %s.", + variable.stringof, typeof(variable).stringof); +} + +void foo(alias $(HILITE int) i)() { + writefln("The int specialization is using '%s'.", + i.stringof); +} + +void foo(alias $(HILITE double) d)() { + writefln("The double specialization is using '%s'.", + d.stringof); +} + +void main() { + string name; + foo!name(); + + int count; + foo!count(); + + double length; + foo!length(); +} +--- + +$(P +Also note that $(C alias) parameters make the names of the actual variables available inside the template: +) + +$(SHELL +The general definition is using 'name' of type string. +The int specialization is using 'count'. +The double specialization is using 'length'. +) + +$(H6 $(IX tuple template parameter) Tuple template parameters) + +$(P +We have seen in $(LINK2 /ders/d.en/parameter_flexibility.html, the Variable Number of Parameters chapter) that variadic functions can take any number and any type of parameters. For example, $(C writeln()) can be called with any number of parameters of any type. +) + +$(P +$(IX ..., template parameter) $(IX variadic template) Templates can be variadic as well. A template parameter that consists of a name followed by $(C ...) allows any number and kind of parameters at that parameter's position. Such parameters appear as a tuple inside the template, which can be used like an $(C AliasSeq). +) + +$(P +Let's see an example of this with a template that simply prints information about every template argument that it is instantiated with: +) + +--- +$(CODE_NAME info)void info(T...)(T args) { + // ... +} +--- + +$(P +The template parameter $(C T...) makes $(C info) a $(I variadic template). Both $(C T) and $(C args) are tuples: +) + +$(UL +$(LI $(C T) represents the types of the arguments.) +$(LI $(C args) represents the arguments themselves.) +) + +$(P +The following example instantiates that function template with three values of three different types: +) + +--- +$(CODE_XREF info)import std.stdio; + +// ... + +void main() { + info($(HILITE 1, "abc", 2.3)); +} +--- + +$(P +The following implementation simply prints information about the arguments by iterating over them in a $(C foreach) loop: +) + +--- +void info(T...)(T args) { + // 'args' is being used like a tuple: + foreach (i, arg; $(HILITE args)) { + writefln("%s: %s argument %s", + i, typeof(arg).stringof, arg); + } +} +--- + +$(P +The output: +) + +$(SHELL_SMALL +0: int argument 1 +1: string argument abc +2: double argument 2.3 +) + +$(P +Note that instead of obtaining the type of each argument by $(C typeof(arg)), we could have used $(C T[i]) as well. +) + +$(P +We know that template arguments can be deduced for function templates. That's why the compiler deduces the types as $(C int), $(C string), and $(C double) in the previous program. +) + +$(P +However, it is also possible to specify template parameters explicitly. For example, $(C std.conv.to) takes the destination type as an explicit template parameter: +) + +--- + to!$(HILITE string)(42); +--- + +$(P +When template parameters are explicitly specified, they can be a mixture of value, type, and other kinds. That flexibility makes it necessary to be able to determine whether each template parameter is a type or not, so that the body of the template can be coded accordingly. That is achieved by treating the arguments as an $(C AliasSeq). +) + +$(P +Let's see an example of this in a function template that produces $(C struct) definitions as source code in text form. Let's have this function return the produced source code as $(C string). This function can first take the name of the $(C struct) followed by the types and names of the members specified as pairs: +) + +--- +import std.stdio; + +void $(CODE_DONT_TEST)main() { + writeln(structDefinition!("Student", + string, "name", + int, "id", + int[], "grades")()); +} +--- + +$(P +That $(C structDefinition) instantiation is expected to produce the following $(C string): +) + +$(SHELL +struct Student { + string name; + int id; + int[] grades; +} +) + +$(P +$(I $(B Note:) Functions that produce source code are used with the $(C mixin) keyword, which we will see in $(LINK2 /ders/d.en/mixin.html, a later chapter).) +) + +$(P +The following is an implementation that produces the desired output. Note how the function template makes use of the $(C is) expression. Remember that the expression $(C is (arg)) produces $(C true) when $(C arg) is a valid type: +) + +--- +import std.string; + +string structDefinition(string name, $(HILITE Members)...)() { + /* Ensure that members are specified as pairs: first the + * type then the name. */ + static assert(($(HILITE Members).length % 2) == 0, + "Members must be specified as pairs."); + + /* The first part of the struct definition. */ + string result = "struct " ~ name ~ "\n{\n"; + + foreach (i, arg; $(HILITE Members)) { + static if (i % 2) { + /* The odd numbered arguments should be the names + * of members. Instead of dealing with the names + * here, we use them as Members[i+1] in the 'else' + * clause below. + * + * Let's at least ensure that the member name is + * specified as a string. */ + static assert(is (typeof(arg) == string), + "Member name " ~ arg.stringof ~ + " is not a string."); + + } else { + /* In this case 'arg' is the type of the + * member. Ensure that it is indeed a type. */ + static assert(is (arg), + arg.stringof ~ " is not a type."); + + /* Produce the member definition from its type and + * its name. + * + * Note: We could have written 'arg' below instead + * of Members[i]. */ + result ~= format(" %s %s;\n", + $(HILITE Members[i]).stringof, $(HILITE Members[i+1])); + } + } + + /* The closing bracket of the struct definition. */ + result ~= "}"; + + return result; +} + +import std.stdio; + +void main() { + writeln(structDefinition!("Student", + string, "name", + int, "id", + int[], "grades")()); +} +--- + +$(H5 $(IX typeof(this)) $(IX typeof(super)) $(IX typeof(return))$(C typeof(this)), $(C typeof(super)), and $(C typeof(return))) + +$(P +In some cases, the generic nature of templates makes it difficult to know or spell out certain types in the template code. The following three special $(C typeof) varieties are useful in such cases. Although they are introduced in this chapter, they work in non-templated code as well. +) + +$(UL + +$(LI $(C typeof(this)) generates the type of the $(C this) reference. It works in any $(C struct) or $(C class), even outside of member functions: + +--- +struct List(T) { + // The type of 'next' is List!int when T is int + typeof(this) *next; + // ... +} +--- + +) + +$(LI $(C typeof(super)) generates the base type of a $(C class) (i.e. the type of $(C super)). + +--- +class ListImpl(T) { + // ... +} + +class List(T) : ListImpl!T { + // The type of 'next' is ListImpl!int when T is int + typeof(super) *next; + // ... +} +--- + +) + +$(LI $(C typeof(return)) generates the return type of a function, inside that function. + +$(P +For example, instead of defining the $(C calculate()) function above as an $(C auto) function, we can be more explicit by replacing $(C auto) with $(C LargerOf!(A, B)) in its definition. (Being more explicit would have the added benefit of obviating at least some part of its function comment.) +) + +--- +$(HILITE LargerOf!(A, B)) calculate(A, B)(A a, B b) { + // ... +} +--- + +$(P +$(C typeof(return)) prevents having to repeat the return type inside the function body: +) + +--- +LargerOf!(A, B) calculate(A, B)(A a, B b) { + $(HILITE typeof(return)) result; // The type is either A or B + // ... + return result; +} +--- + +) + +) + +$(H5 Template specializations) + +$(P +We have seen template specializations in $(LINK2 /ders/d.en/templates.html, the Templates chapter). Like type parameters, other kinds of template parameters can be specialized as well. The following is both the general definition of a template and its specialization for 0: +) + +--- +void foo(int value)() { + // ... general definition ... +} + +void foo(int value $(HILITE : 0))() { + // ... special definition for zero ... +} +--- + +$(P +We will take advantage of template specializations in the $(I meta programming) section below. +) + +$(H5 $(IX meta programming) Meta programming) + +$(P +As they are about code generation, templates are among the higher level features of D. A template is indeed code that generates code. Writing code that generates code is called $(I meta programming). +) + +$(P +Due to templates being compile-time features, some operations that are normally executed at runtime can be moved to compile time as template instantiations. +) + +$(P +($(I $(B Note:) Compile time function execution) (CTFE) $(I is another feature that achieves the same goal. We will see CTFE in a later chapter.)) +) + +$(P +$(I Executing) templates at compile time is commonly based on recursive template instantiations. +) + +$(P +To see an example of this, let's first consider a regular function that calculates the sum of numbers from 0 to a specific value. For example, when its argument is 4, this fuction should return the result of 0+1+2+3+4: +) + +--- +int sum(int last) { + int result = 0; + + foreach (value; 0 .. last + 1) { + result += value; + } + + return result; +} +--- + +$(P +$(IX recursion) That is an iterative implementation of the function. The same function can be implemented by recursion as well: +) + +--- +int sum(int last) { + return (last == 0 + ? last + : last + $(HILITE sum)(last - 1)); +} +--- + +$(P +The recursive function returns the sum of the last value and the previous sum. As you can see, the function terminates the recursion by treating the value 0 specially. +) + +$(P +Functions are normally run-time features. As usual, $(C sum()) can be executed at run time: +) + +--- + writeln(sum(4)); +--- + +$(P +When the result is needed at compile time, one way of achieving the same calculation is by defining a function template. In this case, the parameter must be a template parameter, not a function parameter: +) + +--- +// WARNING: This code is incorrect. +int sum($(HILITE int last))() { + return (last == 0 + ? last + : last + sum$(HILITE !(last - 1))()); +} +--- + +$(P +That function template instantiates itself by $(C last - 1) and tries to calculate the sum again by recursion. However, that code is incorrect. +) + +$(P +As the ternary operator would be compiled to be executed at run time, there is no condition check that terminates the recursion at compile time: +) + +--- + writeln(sum!4()); $(DERLEME_HATASI) +--- + +$(P +The compiler detects that the template instances would recurse infinitely and stops at an arbitrary number of recursions: +) + +$(SHELL_SMALL +Error: template instance deneme.sum!($(HILITE -296)) recursive expansion +) + +$(P +Considering the difference between the template argument 4 and -296, the compiler restricts template expansion at 300 by default. +) + +$(P +In meta programming, recursion is terminated by a template specialization. The following specialization for 0 produces the expected result: +) + +--- +$(CODE_NAME sum)// The general definition +int sum(int last)() { + return last + sum!(last - 1)(); +} + +// The special definition for zero +int sum(int last $(HILITE : 0))() { + return 0; +} +--- + +$(P +The following is a program that tests $(C sum()): +) + +--- +$(CODE_XREF sum)import std.stdio; + +void main() { + writeln(sum!4()); +} +--- + +$(P +Now the program compiles successfully and produces the result of 4+3+2+1+0: +) + +$(SHELL_SMALL +10 +) + +$(P +An important point to make here is that the function $(C sum!4()) is executed entirely at compile time. The compiled code is the equivalent of calling $(C writeln()) with literal $(C 10): +) + +--- + writeln(10); // the equivalent of writeln(sum!4()) +--- + +$(P +As a result, the compiled code is as fast and simple as can be. Although the value 10 is still calculated as the result of 4+3+2+1+0, the entire calculation happens at compile time. +) + +$(P +The previous example demonstrates one of the benefits of meta programming: moving operations from run time to compile time. CTFE obviates some of the idioms of meta programming in D. +) + +$(H5 $(IX polymorphism, compile-time) $(IX compile-time polymorphism) Compile-time polymorphism) + +$(P +In object oriented programming (OOP), polymorphism is achieved by inheritance. For example, if a function takes an interface, it accepts objects of any class that inherits that interface. +) + +$(P +Let's recall an earlier example from a previous chapter: +) + +--- +import std.stdio; + +interface SoundEmitter { + string emitSound(); +} + +class Violin : SoundEmitter { + string emitSound() { + return "♩♪♪"; + } +} + +class Bell : SoundEmitter { + string emitSound() { + return "ding"; + } +} + +void useSoundEmittingObject($(HILITE SoundEmitter object)) { + // ... some operations ... + writeln(object.emitSound()); + // ... more operations ... +} + +void main() { + useSoundEmittingObject(new Violin); + useSoundEmittingObject(new Bell); +} +--- + +$(P +$(C useSoundEmittingObject()) is benefiting from polymorphism. It takes a $(C SoundEmitter) so that it can be used with any type that is derived from that interface. +) + +$(P +Since $(I working with any type) is inherent to templates, they can be seen as providing a kind of polymorphism as well. Being a compile-time feature, the polymorphism that templates provide is called $(I compile-time polymorphism). Conversely, OOP's polymorphism is called $(I run-time polymorphism). +) + +$(P +In reality, neither kind of polymorphism allows being used with $(I any type) because the types must satisfy certain requirements. +) + +$(P +Run-time polymorphism requires that the type implements a certain interface. +) + +$(P +Compile-time polymorphism requires that the type is compatible with how it is used by the template. As long as the code compiles, the template argument can be used with that template. ($(I $(B Note:) Optionally, the argument must satisfy template constraints as well. We will see template constraints later below.)) +) + +$(P +For example, if $(C useSoundEmittingObject()) were implemented as a function template instead of a function, it could be used with any type that supported the $(C object.emitSound()) call: +) + +--- +void useSoundEmittingObject$(HILITE (T))(T object) { + // ... some operations ... + writeln(object.emitSound()); + // ... more operations ... +} + +class Car { + string emitSound() { + return "honk honk"; + } +} + +// ... + + useSoundEmittingObject(new Violin); + useSoundEmittingObject(new Bell); + useSoundEmittingObject(new Car); +--- + +$(P +Note that although $(C Car) has no inheritance relationship with any other type, the code compiles successfully, and the $(C emitSound()) member function of each type gets called. +) + +$(P +$(IX duck typing) Compile-time polymorphism is also known as $(I duck typing), a humorous term, emphasizing behavior over actual type. +) + +$(H5 $(IX code bloat) Code bloat) + +$(P +The code generated by the compiler is different for every different argument of a type parameter, of a value parameter, etc.) + +$(P +The reason for that can be seen by considering $(C int) and $(C double) as type template arguments. Each type would have to be processed by different kinds of CPU registers. For that reason, the same template needs to be compiled differently for different template arguments. In other words, the compiler needs to generate different code for each instantiation of a template. +) + +$(P +For example, if $(C useSoundEmittingObject()) were implemented as a template, it would be compiled as many times as the number of different instantiations of it. +) + +$(P +Because it results in larger program size, this effect is called $(I code bloat). Although this is not a problem in most programs, it is an effect of templates that must be known. +) + +$(P +Conversely, non-templated version of $(C useSoundEmittingObject()) would not have any code repetition. The compiler would compile that function just once and execute the same code for all types of the $(C SoundEmitter) interface. In run-time polymorphism, having the same code behave differently for different types is achieved by function pointers on the background. Although function pointers have a small cost at run time, that cost is not significant in most programs. +) + +$(P +Since both code bloat and run-time polymorphism have effects on program performance, it cannot be known beforehand whether run-time polymorphism or compile-time polymorphism would be a better approach for a specific program. +) + +$(H5 $(IX constraint, template) $(IX template constraint) Template constraints) + +$(P +The fact that templates can be instantiated with any argument yet not every argument is compatible with every template brings an inconvenience. If a template argument is not compatible with a particular template, the incompatibility is necessarily detected during the compilation of the template code for that argument. As a result, the compilation error points at a line inside the template implementation. +) + +$(P +Let's see this by using $(C useSoundEmittingObject()) with a type that does not support the $(C object.emitSound()) call: +) + +--- +class Cup { + // ... does not have emitSound() ... +} + +// ... + + useSoundEmittingObject(new Cup); // ← incompatible type +--- + +$(P +Although arguably the error is with the code that uses the template with an incompatible type, the compilation error points at a line inside the template: +) + +--- +void useSoundEmittingObject(T)(T object) { + // ... some operations ... + writeln(object.emitSound()); $(DERLEME_HATASI) + // ... more operations ... +} +--- + +$(P +An undesired consequence is that when the template is a part of a third-party library module, the compilation error would appear to be a problem with the library itself. +) + +$(P +Note that this issue does not exist for interfaces: A function that takes an interface can only be called with a type that implements that interface. Attempting to call such a function with any other type is a compilation error at the caller. +) + +$(P +Template contraints are for disallowing incorrect instantiations of templates. They are defined as logical expressions of an $(C if) condition right before the template body: +) + +--- +void foo(T)() + if (/* ... constraints ... */) { + // ... +} +--- + +$(P +A template definition is considered by the compiler only if its constraints evaluate to $(C true) for a specific instantiation of the template. Otherwise, the template definition is ignored for that use. +) + +$(P +Since templates are a compile-time feature, template constraints must be evaluable at compile time. The $(C is) expression that we saw in $(LINK2 /ders/d.en/is_expr.html, the $(C is) Expression chapter) is commonly used in template constraints. We will use the $(C is) expression in the following examples as well. +) + +$(H6 $(IX single-element tuple template parameter) $(IX tuple template parameter, single-element) Tuple parameter of single element) + +$(P +Sometimes the single parameter of a template needs to be one of type, value, or $(C alias) kinds. That can be achieved by a tuple parameter of length one: +) + +--- +template myTemplate(T...) + $(HILITE if (T.length == 1)) { + static if (is ($(HILITE T[0]))) { + // The single parameter is a type + enum bool myTemplate = /* ... */; + + } else { + // The single parameter is some other kind + enum bool myTemplate = /* ... */; + } +} +--- + +$(P +Some of the templates of the $(C std.traits) module take advantage of this idiom. We will see $(C std.traits) in a later chapter. +) + +$(H6 $(IX named template constraint) Named constraints) + +$(P +Sometimes the constraints are complex, making it hard to understand the requirements of template parameters. This complexity can be handled by an idiom that effectively gives names to constraints. This idiom combines four features of D: anonymous functions, $(C typeof), the $(C is) expression, and eponymous templates. +) + +$(P +Let's see this on a function template that has a type parameter. The template uses its function parameter in specific ways: +) + +--- +void use(T)(T object) { + // ... + object.prepare(); + // ... + object.fly(42); + // ... + object.land(); + // ... +} +--- + +$(P +As is obvious from the implementation of the template, the types that this function can work with must support three specific function calls on the object: $(C prepare()), $(C fly(42)), and $(C land()). +) + +$(P +One way of specifying a template constraint for that type is by the $(C is) and $(C typeof) expressions for each function call inside the template: +) + +--- +void use(T)(T object) + if (is (typeof(object.prepare())) && + is (typeof(object.fly(1))) && + is (typeof(object.land()))) { + // ... +} +--- + +$(P +I will explain that syntax below. For now, accept the whole construct of $(C is (typeof(object.prepare()))) to mean $(I whether the type supports the $(C .prepare()) call). +) + +$(P +Although such constraints achieve the desired goal, sometimes they are too complex to be readable. Instead, it is possible to give a more descriptive name to the whole constraint: +) + +--- +void use(T)(T object) + if (canFlyAndLand!T) { + // ... +} +--- + +$(P +That constraint is more readable because it is now more clear that the template is designed to work with types that $(I can fly and land). +) + +$(P +Such constraints are achieved by an idiom that is implemented similar to the following eponymous template: +) + +--- +template canFlyAndLand(T) { + enum canFlyAndLand = is (typeof( + { + T object; + object.prepare(); // should be preparable for flight + object.fly(1); // should be flyable for a certain distance + object.land(); // should be landable + }())); +} +--- + +$(P +The D features that take part in that idiom and how they interact with each other are explained below: +) + +--- +template canFlyAndLand(T) { + // (6) (5) (4) + enum canFlyAndLand = is (typeof( + $(HILITE {) // (1) + T object; // (2) + object.prepare(); + object.fly(1); + object.land(); + // (3) + $(HILITE })())); +} +--- + +$(OL + +$(LI $(B Anonymous function:) We have seen anonymous functions in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). The highlighted curly brackets above define an anonymous function. +) + +$(LI $(B Function block:) The function block uses the type as it is supposed to be used in the actual template. First an object of that type is defined and then that object is used in specific ways. (This code never gets executed; see below.) +) + +$(LI $(B Evaluation of the function:) The empty parentheses at the end of an anonymous function normally execute that function. However, since that call syntax is within a $(C typeof), it is never executed. +) + +$(LI $(IX typeof) $(B The $(C typeof) expression:) $(C typeof) produces the type of an expression. + +$(P +An important fact about $(C typeof) is that it never executes the expression. Rather, it produces the type of the expression $(I if) that expression would be executed: +) + +--- + int i = 42; + typeof(++i) j; // same as 'int j;' + + assert(i == 42); // ++i has not been executed +--- + +$(P +As the previous $(C assert) proves, the expression $(C ++i) has not been executed. $(C typeof) has merely produced the type of that expression as $(C int). +) + +$(P +If the expression that $(C typeof) receives is not valid, $(C typeof) produces no type at all (not even $(C void)). So, if the anonymous function inside $(C canFlyAndLand) can be compiled successfully for $(C T), $(C typeof) produces a valid type. Otherwise, it produces no type at all. +) + +) + +$(LI $(B The $(C is) expression:) We have seen many different uses of the $(C is) expression in $(LINK2 /ders/d.en/is_expr.html, the $(C is) Expression chapter). The $(C is ($(I Type))) syntax produces $(C true) if $(C Type) is valid: + +--- + int i; + writeln(is (typeof(i))); // true + writeln(is (typeof(nonexistentSymbol))); // false +--- + +$(P +Although the second $(C typeof) above receives a nonexistent symbol, the compiler does not emit a compilation error. Rather, the effect is that the $(C typeof) expression does not produce any type, so the $(C is) expression produces $(C false): +) + +$(SHELL_SMALL +true +false +) + +) + +$(LI $(B Eponymous template:) As described above, since the $(C canFlyAndLand) template contains a definition by the same name, the template instantiation is that definition itself. +) + +) + +$(P +In the end, $(C use()) gains a more descriptive constraint: +) + +--- +void use(T)(T object) + if (canFlyAndLand!T) { + // ... +} +--- + +$(P +Let's try to use that template with two types, one that satisfies the constraint and one that does not satisfy the constraint: +) + +--- +// A type that does match the template's operations +class ModelAirplane { + void prepare() { + } + + void fly(int distance) { + } + + void land() { + } +} + +// A type that does not match the template's operations +class Pigeon { + void fly(int distance) { + } +} + +// ... + + use(new ModelAirplane); // ← compiles + use(new Pigeon); $(DERLEME_HATASI) +--- + +$(P +Named or not, since the template has a constraint, the compilation error points at the line where the template is used rather than where it is implemented. +) + +$(H5 $(IX overloading, operator) $(IX multi-dimensional operator overloading) $(IX operator overloading, multi-dimensional) Using templates in multi-dimensional operator overloading) + +$(P +We have seen in $(LINK2 /ders/d.en/operator_overloading.html, the Operator Overloading chapter) that $(C opDollar), $(C opIndex), and $(C opSlice) are for element indexing and slicing. When overloaded for single-dimensional collections, these operators have the following responsibilities: +) + +$(UL + +$(LI $(C opDollar): Returns the number of elements of the collection.) + +$(LI $(C opSlice): Returns an object that represents some or all of the elements of the collection.) + +$(LI $(C opIndex): Provides access to an element.) + +) + +$(P +Those operator functions have templated versions as well, which have different responsibilities from the non-templated ones above. Note especially that in multi-dimensional operator overloading $(C opIndex) assumes the responsibility of $(C opSlice). +) + +$(UL + +$(LI $(IX opDollar template) $(C opDollar) template: Returns the length of a specific dimension of the collection. The dimension is determined by the template parameter: + +--- + size_t opDollar$(HILITE (size_t dimension))() const { + // ... + } +--- + +) + +$(LI $(IX opSlice template) $(C opSlice) template: Returns the range information that specifies the range of elements (e.g. the $(C begin) and $(C end) values in $(C array[begin..end])). The information can be returned as $(C Tuple!(size_t, size_t)) or an equivalent type. The dimension that the range specifies is determined by the template parameter: + +--- + Tuple!(size_t, size_t) opSlice$(HILITE (size_t dimension))(size_t begin, + size_t end) { + return tuple(begin, end); + } +--- + +) + +$(LI $(IX opIndex template) $(C opIndex) template: Returns a range object that represents a part of the collection. The range of elements are determined by the template parameters: + +--- + Range opIndex$(HILITE (A...))(A arguments) { + // ... + } +--- + +) + +) + +$(P +$(IX opIndexAssign template) $(IX opIndexOpAssign template) $(C opIndexAssign) and $(C opIndexOpAssign) have templated versions as well, which operate on a range of elements of the collection. +) + +$(P +The user-defined types that define these operators can be used with the multi-dimensional indexing and slicing syntax: +) + +--- + // Assigns 42 to the elements specified by the + // indexing and slicing arguments: + m[a, b..c, $-1, d..e] = 42; +// ↑ ↑ ↑ ↑ +// dimensions: 0 1 2 3 +--- + +$(P +Such expressions are first converted to the ones that call the operator functions. The conversions are performed by replacing the $(C $) characters with calls to $(C opDollar!dimension()), and the index ranges with calls to $(C opSlice!dimension(begin, end)). The length and range information that is returned by those calls is in turn used as arguments when calling e.g. $(C opIndexAssign). Accordingly, the expression above is executed as the following equivalent (the dimension values are highlighted): +) + +--- + // The equivalent of the above: + m.opIndexAssign( + 42, // ← value to assign + a, // ← argument for dimension 0 + m.opSlice!$(HILITE 1)(b, c), // ← argument for dimension 1 + m.opDollar!$(HILITE 2)() - 1, // ← argument for dimension 2 + m.opSlice!$(HILITE 3)(d, e)); // ← argument for dimension 3 +--- + +$(P +Consequently, $(C opIndexAssign) determines the range of elements from the arguments. +) + +$(H6 Multi-dimensional operator overloading example) + +$(P +The following $(C Matrix) example demonstrates how these operators can be overloaded for a two-dimensional type. +) + +$(P +Note that this code can be implemented in more efficient ways. For example, instead of constructing a $(I single-element sub-matrix) even when operating on a single element e.g. by $(C m[i, j]), it could apply the operation directly on that element. +) + +$(P +Additionally, the $(C writeln(__FUNCTION__)) expressions inside the functions have nothing to do with the behavior of the code. They merely help expose the functions that get called behind the scenes for different operator usages. +) + +$(P +Also note that the correctness of dimension values are enforced by template constraints. +) + +--- +import std.stdio; +import std.format; +import std.string; + +/* Works as a two-dimensional int array. */ +struct Matrix { +private: + + int[][] rows; + + /* Represents a range of rows or columns. */ + struct Range { + size_t begin; + size_t end; + } + + /* Returns the sub-matrix that is specified by the row and + * column ranges. */ + Matrix subMatrix(Range rowRange, Range columnRange) { + writeln(__FUNCTION__); + + int[][] slices; + + foreach (row; rows[rowRange.begin .. rowRange.end]) { + slices ~= row[columnRange.begin .. columnRange.end]; + } + + return Matrix(slices); + } + +public: + + this(size_t height, size_t width) { + writeln(__FUNCTION__); + + rows = new int[][](height, width); + } + + this(int[][] rows) { + writeln(__FUNCTION__); + + this.rows = rows; + } + + void toString(void delegate(const(char)[]) sink) const { + sink.formattedWrite!"%(%(%5s %)\n%)"(rows); + } + + /* Assigns the specified value to each element of the + * matrix. */ + Matrix opAssign(int value) { + writeln(__FUNCTION__); + + foreach (row; rows) { + row[] = value; + } + + return this; + } + + /* Uses each element and a value in a binary operation + * and assigns the result back to that element. */ + Matrix opOpAssign(string op)(int value) { + writeln(__FUNCTION__); + + foreach (row; rows) { + mixin ("row[] " ~ op ~ "= value;"); + } + + return this; + } + + /* Returns the length of the specified dimension. */ + size_t opDollar(size_t dimension)() const + if (dimension <= 1) { + writeln(__FUNCTION__); + + static if (dimension == 0) { + /* The length of dimension 0 is the length of the + * 'rows' array. */ + return rows.length; + + } else { + /* The length of dimension 1 is the lengths of the + * elements of 'rows'. */ + return rows.length ? rows[0].length : 0; + } + } + + /* Returns an object that represents the range from + * 'begin' to 'end'. + * + * Note: Although the 'dimension' template parameter is + * not used here, that information can be useful for other + * types. */ + Range opSlice(size_t dimension)(size_t begin, size_t end) + if (dimension <= 1) { + writeln(__FUNCTION__); + + return Range(begin, end); + } + + /* Returns a sub-matrix that is defined by the + * arguments. */ + Matrix opIndex(A...)(A arguments) + if (A.length <= 2) { + writeln(__FUNCTION__); + + /* We start with ranges that represent the entire + * matrix so that the parameter-less use of opIndex + * means "all of the elements". */ + Range[2] ranges = [ Range(0, opDollar!0), + Range(0, opDollar!1) ]; + + foreach (dimension, a; arguments) { + static if (is (typeof(a) == Range)) { + /* This dimension is already specified as a + * range like 'matrix[begin..end]', which can + * be used as is. */ + ranges[dimension] = a; + + } else static if (is (typeof(a) : size_t)) { + /* This dimension is specified as a single + * index value like 'matrix[i]', which we want + * to represent as a single-element range. */ + ranges[dimension] = Range(a, a + 1); + + } else { + /* We don't expect other types. */ + static assert( + false, format("Invalid index type: %s", + typeof(a).stringof)); + } + } + + /* Return the sub-matrix that is specified by + * 'arguments'. */ + return subMatrix(ranges[0], ranges[1]); + } + + /* Assigns the specified value to each element of the + * sub-matrix. */ + Matrix opIndexAssign(A...)(int value, A arguments) + if (A.length <= 2) { + writeln(__FUNCTION__); + + Matrix subMatrix = opIndex(arguments); + return subMatrix = value; + } + + /* Uses each element of the sub-matrix and a value in a + * binary operation and assigns the result back to that + * element. */ + Matrix opIndexOpAssign(string op, A...)(int value, + A arguments) + if (A.length <= 2) { + writeln(__FUNCTION__); + + Matrix subMatrix = opIndex(arguments); + mixin ("return subMatrix " ~ op ~ "= value;"); + } +} + +/* Executes the expression that is specified as a string, and + * prints the result as well as the new state of the + * matrix. */ +void execute(string expression)(Matrix m) { + writefln("\n--- %s ---", expression); + mixin ("auto result = " ~ expression ~ ";"); + writefln("result:\n%s", result); + writefln("m:\n%s", m); +} + +void main() { + enum height = 10; + enum width = 8; + + auto m = Matrix(height, width); + + int counter = 0; + foreach (row; 0 .. height) { + foreach (column; 0 .. width) { + writefln("Initializing %s of %s", + counter + 1, height * width); + + m[row, column] = counter; + ++counter; + } + } + + writeln(m); + + execute!("m[1, 1] = 42")(m); + execute!("m[0, 1 .. $] = 43")(m); + execute!("m[0 .. $, 3] = 44")(m); + execute!("m[$-4 .. $-1, $-4 .. $-1] = 7")(m); + + execute!("m[1, 1] *= 2")(m); + execute!("m[0, 1 .. $] *= 4")(m); + execute!("m[0 .. $, 0] *= 10")(m); + execute!("m[$-4 .. $-2, $-4 .. $-2] -= 666")(m); + + execute!("m[1, 1]")(m); + execute!("m[2, 0 .. $]")(m); + execute!("m[0 .. $, 2]")(m); + execute!("m[0 .. $ / 2, 0 .. $ / 2]")(m); + + execute!("++m[1..3, 1..3]")(m); + execute!("--m[2..5, 2..5]")(m); + + execute!("m[]")(m); + execute!("m[] = 20")(m); + execute!("m[] /= 4")(m); + execute!("(m[] += 5) /= 10")(m); +} +--- + +$(H5 Summary) + +$(P +The earlier template chapter had the following reminders: +) + +$(UL + +$(LI Templates define the code as a pattern, for the compiler to generate instances of it according to the actual uses in the program.) + +$(LI Templates are a compile-time feature.) + +$(LI Specifying template parameter lists is sufficient to make function, struct, and class definitions templates.) + +$(LI Template arguments can be specified explicitly after an exclamation mark. The parentheses are not necessary when there is only one token inside the parentheses.) + +$(LI Each template instantiation yields a different type.) + +$(LI Template arguments can only be deduced for function templates.) + +$(LI Templates can be specialized for the type that is after the $(C :) character.) + +$(LI Default template arguments are specified after the $(C =) character.) + +) + +$(P +This chapter added the following concepts: +) + +$(UL + +$(LI Templates can be defined by the full syntax or the shortcut syntax.) + +$(LI The scope of the template is a name space.) + +$(LI A template that contains a definition with the same name as the template is called an eponymous template. The template represents that definition.) + +$(LI Templates can be of functions, classes, structs, unions, and interfaces, and every template body can contain any number of definitions.) + +$(LI Template parameters can be of type, value, $(C this), $(C alias), and tuple kinds.) + +$(LI $(C typeof(this)), $(C typeof(super)), and $(C typeof(return)) are useful in templates.) + +$(LI Templates can be specialized for particular arguments.) + +$(LI Meta programming is a way of executing operations at compile time.) + +$(LI Templates enable $(I compile-time polymorphism).) + +$(LI Separate code generation for different instantiations can cause $(I code bloat).) + +$(LI Template constraints limit the uses of templates for specific template arguments. They help move compilation errors from the implementations of templates to where the templates are actually used incorrectly.) + +$(LI It is more readable to give names to template constraints.) + +$(LI The templated versions of $(C opDollar), $(C opSlice), $(C opIndex), $(C opIndexAssign), and $(C opIndexOpAssign) are for multi-dimensional indexing and slicing.) + +) + +Macros: + SUBTITLE=More Templates + + DESCRIPTION=One of generic programming features of D in more detail. + + KEYWORDS=d programming language tutorial book templates diff --git a/target/ternary.cozum.d b/target/ternary.cozum.d new file mode 100644 index 0000000..d660a9c --- /dev/null +++ b/target/ternary.cozum.d @@ -0,0 +1,33 @@ +Ddoc + +$(COZUM_BOLUMU The Ternary Operator $(C ?:)) + +$(P +Although it may make more sense to use an $(C if-else) statement in this exercise, the following program uses two $(C ?:) operators: +) + +--- +import std.stdio; + +void main() { + write("Please enter the net amount: "); + + int amount; + readf(" %s", &amount); + + writeln("$", + amount < 0 ? -amount : amount, + amount < 0 ? " lost" : " gained"); +} +--- + +$(P +The program prints "gained" even when the value is zero. Modify the program to print a message more appropriate for zero. +) + +Macros: + SUBTITLE=The Ternary Operator ?: Solution + + DESCRIPTION=The exercise solution of the ?: operator of the D programming language. + + KEYWORDS=programming in d tutorial ternary solution diff --git a/target/ternary.d b/target/ternary.d new file mode 100644 index 0000000..5dfd505 --- /dev/null +++ b/target/ternary.d @@ -0,0 +1,251 @@ +Ddoc + +$(DERS_BOLUMU $(IX ternary operator) $(IX ?:) $(IX conditional operator) Ternary Operator $(CH4 ?:)) + +$(P +The $(C ?:) operator works very similarly to an $(C if-else) statement: +) + +--- + if (/* condition check */) { + /* ... expression(s) to execute if true */ + + } else { + /* ... expression(s) to execute if false */ + } +--- + +$(P +The $(C if) statement executes either the block for the case of $(C true) or the block for the case of $(C false). As you remember, being a statement, it does not have a value; $(C if) merely affects the execution of code blocks. +) + +$(P +On the other hand, the $(C ?:) operator is an expression. In addition to working similary to the $(C if-else) statement, it produces a value. The equivalent of the above code is the following: +) + +--- +/* condition */ ? /* truth expression */ : /* falsity expression */ +--- + +$(P +Because it uses three expressions, the $(C ?:) operator is called the ternary operator. +) + +$(P +The value that is produced by this operator is either the value of the truth expression or the value of the falsity expression. Because it is an expression, it can be used anywhere that expressions can be used. +) + +$(P +The following examples contrast the $(C ?:) operator to the $(C if-else) statement. The ternary operator is more concise for the cases that are similar to these examples. +) + +$(UL + +$(LI $(B Initialization) + +$(P +To initialize a variable with 366 if it is leap year, 365 otherwise: +) + +--- + int days = isLeapYear ? 366 : 365; +--- + +$(P +With an $(C if) statement, one way to do this is to define the variable without an explicit initial value and then assign the intended value: +) + +--- + int days; + + if (isLeapYear) { + days = 366; + + } else { + days = 365; + } +--- + +$(P +An alternative also using an $(C if) is to initialize the variable with the non-leap year value and then increment it if it is a leap year: +) + +--- + int days = 365; + + if (isLeapYear) { + ++days; + } +--- + +) + +$(LI $(B Printing) + +$(P +Printing part of a message differently depending on a condition: +) +--- + writeln("The glass is half ", + isOptimistic ? "full." : "empty."); +--- + +$(P +With an $(C if), the first and last parts of the message may be printed separately: +) + +--- + write("The glass is half "); + + if (isOptimistic) { + writeln("full."); + + } else { + writeln("empty."); + } +--- + +$(P +Alternatively, the entire message can be printed separately: +) + +--- + if (isOptimistic) { + writeln("The glass is half full."); + + } else { + writeln("The glass is half empty."); + } +--- + +) + +$(LI $(B Calculation) + +$(P +Increasing the score of the winner in a backgammon game 2 points or 1 point depending on whether the game has ended with gammon: +) + +--- + score += isGammon ? 2 : 1; +--- + +$(P +A straightforward equivalent using an $(C if): +) + +--- + if (isGammon) { + score += 2; + + } else { + score += 1; + } +--- + +$(P +An alternative also using an $(C if) is to first increment by one and then increment again if gammon: +) + +--- + ++score; + + if (isGammon) { + ++score; + } +--- + +) + +) + +$(P +As can be seen from the examples above, the code is more concise and clearer with the ternary operator in certain situations. +) + +$(H5 The type of the ternary expression) + +$(P +The value of the $(C ?:) operator is either the value of the truth expression or the value of the falsity expression. The types of these two expressions need not be the same but they must have a $(I common type). +) + +$(P +$(IX common type) The common type of two expressions is decided by a relatively complicated algorithm, involving $(LINK2 /ders/d.en/cast.html, type conversions) and $(LINK2 /ders/d.en/inheritance.html, inheritance). Additionally, depending on the expressions, the $(I kind) of the result is either $(LINK2 /ders/d.en/lvalue_rvalue.html, an lvalue or an rvalue). We will see these concepts in later chapters. +) + +$(P +For now, accept common type as a type that can represent both of the values without requiring an explicit cast. For example, the integer types $(C int) and $(C long) have a common type because they can both be represented as $(C long). On the other hand, $(C int) and $(C string) do not have a common type because neither $(C int) nor $(C string) can automatically be converted to the other type. +) + +$(P +Remember that a simple way of determining the type of an expression is using $(C typeof) and then printing its $(C .stringof) property: +) + +--- + int i; + double d; + + auto result = someCondition ? i : d; + writeln(typeof(result)$(HILITE .stringof)); +--- + +$(P +Because $(C double) can represent $(C int) but not the other way around, the common type of the ternary expression above is $(C double): +) + +$(SHELL +double +) + +$(P +To see an example of two expressions that do not have a common type, let's look at composing a message that reports the number of items to be shipped. Let's print "A dozen" when the value equals 12: "A $(B dozen) items will be shipped." Otherwise, let's have the message include the exact number: "$(B 3) items will be shipped." +) + +$(P +One might think that the varying part of the message can be selected with the $(C ?:) operator: +) + +--- + writeln( + (count == 12) ? "A dozen" : count, $(DERLEME_HATASI) + " items will be shipped."); +--- + +$(P +Unfortunately, the expressions do not have a common type because the type of $(STRING "A dozen") is $(C string) and the type of $(C count) is $(C int). +) + +$(P +A solution is to first convert $(C count) to $(C string). The function $(C to!string) from the $(C std.conv) module produces a $(C string) value from the specified parameter: +) + +--- +import std.conv; +// ... + writeln((count == 12) ? "A dozen" : to!string(count), + " items will be shipped."); +--- + +$(P +Now, as both of the selection expressions of the $(C ?:) operator are of $(C string) type, the code compiles and prints the expected message. +) + +$(PROBLEM_TEK + +$(P +Have the program read a single $(C int) value as $(I the net amount) where a positive value represents a gain and a negative value represents a loss. +) + +$(P +The program should print a message that contains "gained" or "lost" depending on whether the amount is positive or negative. For example, "$100 lost" or "$70 gained". Even though it may be more suitable, do not use the $(C if) statement in this exercise. +) + +) + + +Macros: + SUBTITLE=Ternary Operator ?: + + DESCRIPTION=The ?: operator of the D programming language and comparing it to the if-else statement. + + KEYWORDS=d programming language tutorial book ternary operator diff --git a/target/to_be_continued.d b/target/to_be_continued.d new file mode 100644 index 0000000..1a7f124 --- /dev/null +++ b/target/to_be_continued.d @@ -0,0 +1,17 @@ +Ddoc + +$(B) +$(H4 The End) + +$(P +You can use $(LINK2 /ders/d.en/rss.xml, the RSS feed) to be notified about new chapters. +) + +Macros: + SUBTITLE=To be continued... + + DESCRIPTION= + + KEYWORDS=d programming language tutorial book + +MINI_SOZLUK= diff --git a/target/tuples.d b/target/tuples.d new file mode 100644 index 0000000..63fb3a8 --- /dev/null +++ b/target/tuples.d @@ -0,0 +1,571 @@ +Ddoc + +$(DERS_BOLUMU $(IX tuple) $(IX Tuple, std.typecons) Tuples) + +$(P +Tuples are for combining multiple values to be used as a single object. They are implemented as a library feature by the $(C Tuple) template from the $(C std.typecons) module. +) + +$(P +$(C Tuple) makes use of $(C AliasSeq) from the $(C std.meta) module for some of its operations. +) + +$(P +This chapter covers only the more common operations of tuples. For more information on tuples and templates see $(LINK2 https://github.com/PhilippeSigaud/D-templates-tutorial, Philippe Sigaud's $(I D Templates: A Tutorial)). +) + +$(H5 $(C Tuple) and $(C tuple())) + +$(P +Tuples are usually constructed by the convenience function $(C tuple()): +) + +--- +import std.stdio; +import std.typecons; + +void main() { + auto t = $(HILITE tuple(42, "hello")); + writeln(t); +} +--- + +$(P +The $(C tuple) call above constructs an object that consists of the $(C int) value 42 and the $(C string) value $(STRING "hello"). The output of the program includes the type of the tuple object and its members: +) + +$(SHELL +Tuple!(int, string)(42, "hello") +) + +$(P +The tuple type above is the equivalent of the following pseudo $(C struct) definition and likely have been implemented in exactly the same way: +) + +--- +// The equivalent of Tuple!(int, string) +struct __Tuple_int_string { + int __member_0; + string __member_1; +} +--- + +$(P +The members of a tuple are normally accessed by their index values. That syntax suggests that tuples can be seen as arrays consisting of different types of elements: +) + +--- + writeln(t$(HILITE [0])); + writeln(t$(HILITE [1])); +--- + +$(P +The output: +) + +$(SHELL +42 +hello +) + +$(H6 Member properties ) + +$(P +It is possible to access the members by properties if the tuple is constructed directly by the $(C Tuple) template instead of the $(C tuple()) function. The type and the name of each member are specified as two consecutive template parameters: +) + +--- + auto t = Tuple!(int, "number", + string, "message")(42, "hello"); +--- + +$(P +The definition above allows accessing the members by $(C .number) and $(C .message) properties as well: +) + +--- + writeln("by index 0 : ", t[0]); + writeln("by .number : ", t$(HILITE .number)); + writeln("by index 1 : ", t[1]); + writeln("by .message: ", t$(HILITE .message)); +--- + +$(P +The output: +) + +$(SHELL +by index 0 : 42 +by .number : 42 +by index 1 : hello +by .message: hello +) + +$(H6 $(IX .expand) Expanding the members as a list of values) + +$(P +Tuple members can be expanded as a list of values that can be used e.g. as an argument list when calling a function. The members can be expanded either by the $(C .expand) property or by slicing: +) + +--- +import std.stdio; +import std.typecons; + +void foo(int i, string s, double d, char c) { + // ... +} + +void bar(int i, double d, char c) { + // ... +} + +void main() { + auto t = tuple(1, "2", 3.3, '4'); + + // Both of the following lines are equivalents of + // foo(1, "2", 3.3, '4'): + foo(t$(HILITE .expand)); + foo(t$(HILITE [])); + + // The equivalent of bar(1, 3.3, '4'): + bar(t$(HILITE [0]), t$(HILITE [$-2..$])); +} +--- + +$(P +The tuple above consists of four values of $(C int), $(C string), $(C double), and $(C char). Since those types match the parameter list of $(C foo()), an expansion of its members can be used as arguments to $(C foo()). When calling $(C bar()), a matching argument list is made up of the first member and the last two members of the tuple. +) + +$(P +As long as the members are compatible to be elements of the same array, the expansion of a tuple can be used as the element values of an array literal as well: +) + +--- +import std.stdio; +import std.typecons; + +void main() { + auto t = tuple(1, 2, 3); + auto a = [ t.expand, t[] ]; + writeln(a); +} +--- + +$(P +The array literal above is initialized by expanding the same tuple twice: +) + +$(SHELL +[1, 2, 3, 1, 2, 3] +) + +$(H6 $(IX foreach, compile-time) $(IX compile-time foreach) Compile-time $(C foreach)) + +$(P +Because their values can be expanded, tuples can be used with the $(C foreach) statement as well: +) + +--- + auto t = tuple(42, "hello", 1.5); + + foreach (i, member; $(HILITE t)) { + writefln("%s: %s", i, member); + } +--- + +$(P +The output: +) + +$(SHELL +0: 42 +1: hello +2: 1.5 +) + +$(P +$(IX unroll) +The $(C foreach) statement above may give a false impression: It may be thought of being a loop that gets executed at run time. That is not the case. Rather, a $(C foreach) statement that operates on the members of a tuple is an $(I unrolling) of the loop body for each member. The $(C foreach) statement above is the equivalent of the following code: +) + +--- + { + enum size_t i = 0; + $(HILITE int) member = t[i]; + writefln("%s: %s", i, member); + } + { + enum size_t i = 1; + $(HILITE string) member = t[i]; + writefln("%s: %s", i, member); + } + { + enum size_t i = 2; + $(HILITE double) member = t[i]; + writefln("%s: %s", i, member); + } +--- + +$(P +The reason for the unrolling is the fact that when the tuple members are of different types, the $(C foreach) body has to be compiled differently for each type. +) + +$(H6 Returning multiple values from functions) + +$(P +$(IX findSplit, std.algorithm) Tuples can be a simple solution to the limitation of functions having to return a single value. An example of this is $(C std.algorithm.findSplit). $(C findSplit()) searches for a range inside another range and produces a result consisting of three pieces: the part before the found range, the found range, and the part after the found range: +) + +--- +import std.algorithm; + +// ... + + auto entireRange = "hello"; + auto searched = "ll"; + + auto result = findSplit(entireRange, searched); + + writeln("before: ", result[0]); + writeln("found : ", result[1]); + writeln("after : ", result[2]); +--- + +$(P +The output: +) + +$(SHELL +before: he +found : ll +after : o +) + +$(P +Another option for returning multiple values from a function is to return a $(C struct) object: +) + +--- +struct Result { + // ... +} + +$(HILITE Result) foo() { + // ... +} +--- + +$(H5 $(IX AliasSeq, std.meta) $(C AliasSeq)) + +$(P +$(C AliasSeq) is defined in the $(C std.meta) module. It is used for representing a concept that is normally used by the compiler but otherwise not available to the programmer as an entity: A comma-separated list of values, types, and symbols (i.e. $(C alias) template arguments). The following are three examples of such lists: +) + +$(UL +$(LI Function argument list) +$(LI Template argument list) +$(LI Array literal element list) +) + +$(P +The following three lines of code are examples of those lists in the same order: +) + +--- + foo($(HILITE 1, "hello", 2.5)); // function arguments + auto o = Bar!($(HILITE char, long))(); // template arguments + auto a = [ $(HILITE 1, 2, 3, 4) ]; // array literal elements +--- + +$(P +$(C Tuple) takes advantage of $(C AliasSeq) when expanding its members. +) + +$(P +$(IX TypeTuple, std.typetuple) The name $(C AliasSeq) comes from "alias sequence" and it can contain types, values, and symbols. ($(C AliasSeq) and $(C std.meta) used to be called $(C TypeTuple) and $(C std.typetuple), respectively.) +) + +$(P +This chapter includes $(C AliasSeq) examples that consist only of types or only of values. Examples of its use with both types and values will appear in the next chapter. $(C AliasSeq) is especially useful with variadic templates, which we will see in the next chapter as well. +) + +$(H6 $(C AliasSeq) consisting of values) + +$(P +The values that an $(C AliasSeq) represents are specified as its template arguments. +) + +$(P +Let's imagine a function that takes three parameters: +) + +--- +import std.stdio; + +void foo($(HILITE int i, string s, double d)) { + writefln("foo is called with %s, %s, and %s.", i, s, d); +} +--- + +$(P +That function would normally be called with three arguments: +) + +--- + foo(1, "hello", 2.5); +--- + +$(P +$(C AliasSeq) can combine those arguments as a single entity and can automatically be expanded when calling functions: +) + +--- +import std.meta; + +// ... + + alias arguments = AliasSeq!(1, "hello", 2.5); + foo($(HILITE arguments)); +--- + +$(P +Although it looks like the function is now being called with a single argument, the $(C foo()) call above is the equivalent of the previous one. As a result, both calls produce the same output: +) + +$(SHELL +foo is called with 1, hello, and 2.5. +) + +$(P +Also note that $(C arguments) is not defined as a variable, e.g. with $(C auto). Rather, it is an $(C alias) of a specific $(C AliasSeq) instance. Although it is possible to define variables of $(C AliasSeq)s as well, the examples in this chapter will use them only as aliases. +) + +$(P +As we have seen above with $(C Tuple), when the values are compatible to be elements of the same array, an $(C AliasSeq) can be used to initialize an array literal as well: +) + +--- + alias elements = AliasSeq!(1, 2, 3, 4); + auto arr = [ $(HILITE elements) ]; + assert(arr == [ 1, 2, 3, 4 ]); +--- + +$(H6 Indexing and slicing) + +$(P +Same with $(C Tuple), the members of an $(C AliasSeq) can be accessed by indexes and slices: +) + +--- + alias arguments = AliasSeq!(1, "hello", 2.5); + assert(arguments$(HILITE [0]) == 1); + assert(arguments$(HILITE [1]) == "hello"); + assert(arguments$(HILITE [2]) == 2.5); +--- + +$(P +Let's assume there is a function with parameters matching the last two members of the $(C AliasSeq) above. That function can be called with a slice of just the last two members of the $(C AliasSeq): +) + +--- +void bar(string s, double d) { + // ... +} + +// ... + + bar(arguments$(HILITE [$-2 .. $])); +--- + +$(H6 $(C AliasSeq) consisting of types) + +$(P +Members of an $(C AliasSeq) can consist of types. In other words, not a specific value of a specific type but a type like $(C int) itself. An $(C AliasSeq) consisting of types can represent template arguments. +) + +$(P +Let's use an $(C AliasSeq) with a $(C struct) template that has two parameters. The first parameter of this template determines the element type of a member array and the second parameter determines the return value of a member function: +) + +--- +import std.conv; + +struct S($(HILITE ElementT, ResultT)) { + ElementT[] arr; + + ResultT length() { + return to!ResultT(arr.length); + } +} + +void main() { + auto s = S!$(HILITE (double, int))([ 1, 2, 3 ]); + auto l = s.length(); +} +--- + +$(P +In the code above, we see that the template is instantiated with $(C (double, int)). An $(C AliasSeq) can represent the same argument list as well: +) + +--- +import std.meta; + +// ... + + alias Types = AliasSeq!(double, int); + auto s = S!$(HILITE Types)([ 1, 2, 3 ]); +--- + +$(P +Although it appears to be a single template argument, $(C Types) gets expanded automatically and the template instantiation becomes $(C S!(double, int)) as before. +) + +$(P +$(C AliasSeq) is especially useful in $(I variadic templates). We will see examples of this in the next chapter. +) + +$(H6 $(C foreach) with $(C AliasSeq)) + +$(P +Same with $(C Tuple), the $(C foreach) statement operating on an $(C AliasSeq) is not a run time loop. Rather, it is the unrolling of the loop body for each member. +) + +$(P +Let's see an example of this with a unit test written for the $(C S) struct that was defined above. The following code tests $(C S) for element types $(C int), $(C long), and $(C float) ($(C ResultT) is always $(C size_t) in this example): +) + +--- +unittest { + alias Types = AliasSeq!($(HILITE int, long, float)); + + foreach (Type; $(HILITE Types)) { + auto s = S!(Type, size_t)([ Type.init, Type.init ]); + assert(s.length() == 2); + } +} +--- + +$(P +The $(C foreach) variable $(C Type) corresponds to $(C int), $(C long), and $(C float), in that order. As a result, the $(C foreach) statement gets compiled as the equivalent of the code below: +) + +--- + { + auto s = S!($(HILITE int), size_t)([ $(HILITE int).init, $(HILITE int).init ]); + assert(s.length() == 2); + } + { + auto s = S!($(HILITE long), size_t)([ $(HILITE long).init, $(HILITE long).init ]); + assert(s.length() == 2); + } + { + auto s = S!($(HILITE float), size_t)([ $(HILITE float).init, $(HILITE float).init ]); + assert(s.length() == 2); + } +--- + +$(H5 $(IX .tupleof) $(C .tupleof) property) + +$(P +$(C .tupleof) represents the members of a type or an object. When applied to a user-defined type, $(C .tupleof) provides access to the definitions of the members of that type: +) + +--- +import std.stdio; + +struct S { + int number; + string message; + double value; +} + +void main() { + foreach (i, MemberType; typeof($(HILITE S.tupleof))) { + writefln("Member %s:", i); + writefln(" type: %s", MemberType.stringof); + + string name = $(HILITE S.tupleof)[i].stringof; + writefln(" name: %s", name); + } +} +--- + +$(P +$(C S.tupleof) appears in two places in the program. First, the types of the elements are obtained by applying $(C typeof) to $(C .tupleof) so that each type appears as the $(C MemberType) variable. Second, the name of the member is obtained by $(C S.tupleof[i].stringof). +) + +$(SHELL +Member 0: + type: int + name: number +Member 1: + type: string + name: message +Member 2: + type: double + name: value +) + +$(P +$(C .tupleof) can be applied to an object as well. In that case, it produces a tuple consisting of the values of the members of the object: +) + +--- + auto object = S(42, "hello", 1.5); + + foreach (i, member; $(HILITE object.tupleof)) { + writefln("Member %s:", i); + writefln(" type : %s", typeof(member).stringof); + writefln(" value: %s", member); + } +--- + +$(P +The $(C foreach) variable $(C member) represents each member of the object: +) + +$(SHELL +Member 0: + type : int + value: 42 +Member 1: + type : string + value: hello +Member 2: + type : double + value: 1.5 +) + +$(P +Here, an important point to make is that the tuple that $(C .tupleof) returns consists of the members of the object themselves, not their copies. In other words, the tuple members are references to the actual object members. +) + +$(H5 Summary) + +$(UL + +$(LI $(C tuple()) combines different types of values similar to a $(C struct) object.) + +$(LI Explicit use of $(C Tuple) allows accessing the members by properties.) + +$(LI The members can be expanded as a value list by $(C .expand) or by slicing.) + +$(LI $(C foreach) with a tuple is not a run time loop; rather, it is a loop unrolling.) + +$(LI $(C AliasSeq) represents concepts like function argument list, template argument list, array literal element list, etc.) + +$(LI $(C AliasSeq) can consist of values and types.) + +$(LI Tuples support indexing and slicing.) + +$(LI $(C .tupleof) provides information about the members of types and objects.) + +) + +macros: + SUBTITLE=Tuples + + DESCRIPTION=Combining values and types to be able to access them as members of the same object. + + KEYWORDS=d programming language tutorial book Tuple AliasSeq tuple diff --git a/target/types.cozum.d b/target/types.cozum.d new file mode 100644 index 0000000..79a297a --- /dev/null +++ b/target/types.cozum.d @@ -0,0 +1,36 @@ +Ddoc + +$(COZUM_BOLUMU Fundamental Types) + + +$(P +We can use other types instead of $(C int): +) + +--- +import std.stdio; + +void main() { + writeln("Type : ", short.stringof); + writeln("Length in bytes: ", short.sizeof); + writeln("Minimum value : ", short.min); + writeln("Maximum value : ", short.max); + writeln("Initial value : ", short.init); + + writeln(); + + writeln("Type : ", ulong.stringof); + writeln("Length in bytes: ", ulong.sizeof); + writeln("Minimum value : ", ulong.min); + writeln("Maximum value : ", ulong.max); + writeln("Initial value : ", ulong.init); +} +--- + + +Macros: + SUBTITLE=Fundamental Types Solutions + + DESCRIPTION=Programming in D exercise solutions: fundamental types + + KEYWORDS=programming in d tutorial solution diff --git a/target/types.d b/target/types.d new file mode 100644 index 0000000..45b341b --- /dev/null +++ b/target/types.d @@ -0,0 +1,295 @@ +Ddoc + +$(DERS_BOLUMU $(IX type) $(IX fundamental type) Fundamental Types) + +$(P +We have seen that the brain of a computer is the CPU. Most of the tasks of a program are performed by the CPU and the rest are dispatched to other parts of the computer. +) + +$(P +The smallest unit of data in a computer is called $(I a bit). The value of a bit can be either 0 or 1. +) + +$(P +Since a type of data that can hold only the values 0 and 1 would have very limited use, the CPU supports larger data types that are combinations of more than one bit. As an example, a $(I byte) usually consists of 8 bits. If an N-bit data type is the most efficient data type supported by a CPU, we consider it to be an $(I N-bit CPU): as in 32-bit CPU, 64-bit CPU, etc. +) + +$(P +The data types that the CPU supports are still not sufficient: they can't represent higher level concepts like $(I name of a student) or $(I a playing card). Likewise, D's fundamental data types are not sufficient to represent many higher level concepts. Such concepts must be defined by the programmer as $(I structs) and $(I classes), which we will see in later chapters. +) + +$(P +$(IX bool) +$(IX byte) +$(IX ubyte) +$(IX short) +$(IX ushort) +$(IX int) +$(IX uint) +$(IX long) +$(IX ulong) +$(IX float) +$(IX double) +$(IX real) +$(IX ifloat) +$(IX idouble) +$(IX ireal) +$(IX cfloat) +$(IX cdouble) +$(IX creal) +$(IX char) +$(IX wchar) +$(IX dchar) +D's $(I fundamental types) are very similar to the fundamental types of many other languages, as seen in the following table. The terms that appear in the table are explained below: +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
D's Fundamental Data Types
Type Definition Initial Value
boolBoolean typefalse
bytesigned 8 bits0
ubyteunsigned 8 bits0
shortsigned 16 bits0
ushortunsigned 16 bits0
intsigned 32 bits0
uintunsigned 32 bits0
longsigned 64 bits0L
ulongunsigned 64 bits0L
float32-bit floating pointfloat.nan
double64-bit floating pointdouble.nan
realeither the largest floating point type that the hardware supports, or double; whichever is largerreal.nan
ifloatimaginary value type of floatfloat.nan * 1.0i
idoubleimaginary value type of doubledouble.nan * 1.0i
irealimaginary value type of realreal.nan * 1.0i
cfloatcomplex number type made of two floatsfloat.nan + float.nan * 1.0i
cdoublecomplex number type made of two doublesdouble.nan + double.nan * 1.0i
crealcomplex number type made of two realsreal.nan + real.nan * 1.0i
charUTF-8 code unit0xFF
wcharUTF-16 code unit0xFFFF
dcharUTF-32 code unit and Unicode code point0x0000FFFF
+ +$(P +In addition to the above, the keyword $(C void) represents $(I having no type). The keywords $(C cent) and $(C ucent) are reserved for future use to represent signed and unsigned 128 bit values. +) + +$(P +Unless there is a specific reason not to, you can use $(C int) to represent whole values. To represent concepts that can have fractional values, consider $(C double). +) + +$(P +The following are the terms that appeared in the table: +) + +$(UL + +$(LI +$(B Boolean:) The type of logical expressions, having the value $(C true) for truth and $(C false) for falsity. +) + +$(LI +$(B Signed type:) A type that can have negative and positive values. For example, $(C byte) can have values from -128 to 127. The names of these types come from the negative $(I sign). +) + +$(LI +$(B Unsigned type:) A type that can have only positive values. For example, $(C ubyte) can have values from 0 to 255. The $(C u) at the beginning of the name of these types comes from $(I unsigned). +) + +$(LI +$(B Floating point:) The type that can represent values with fractions as in 1.25. The precision of floating point calculations are directly related to the bit count of the type: higher the bit count, more precise the results are. Only floating point types can represent fractions; integer types like $(C int) can only represent whole values like 1 and 2. +) + +$(LI +$(B Complex number type:) The type that can represent the complex numbers of mathematics. +) + +$(LI +$(B Imaginary number type:) The type that represents only the imaginary part of complex numbers. The $(C i) that appears in the Initial Value column is the square root of -1 in mathematics. +) + +$(LI +$(IX .nan) $(B nan:) Short for "not a number", representing $(I invalid floating point value). +) + +) + +$(H5 Properties of types) + +$(P +D types have $(I properties). Properties are accessed with a dot after the name of the type. For example, the $(C sizeof) property of $(C int) is accessed as $(C int.sizeof). We will see only some of type properties in this chapter: +) + +$(UL + +$(LI $(IX .stringof) $(C .stringof) is the name of the type) + +$(LI $(IX .sizeof) $(C .sizeof) is the length of the type in terms of bytes. (In order to determine the bit count, this value must be multiplied by 8, the number of bits in a $(C byte).) +) + +$(LI $(IX .min) $(C .min) is short for "minimum"; this is the smallest value that the type can have) + +$(LI $(IX .max) $(C .max) is short for "maximum"; this is the largest value that the type can have) + +$(LI $(IX .init) $(IX initial value) $(IX default value, type) $(C .init) is short for "initial value" (default value); this is the value that D assigns to a type when an initial value is not specified) + +) + +$(P +Here is a program that prints these properties for $(C int): +) + +--- +import std.stdio; + +void main() { + writeln("Type : ", int.stringof); + writeln("Length in bytes: ", int.sizeof); + writeln("Minimum value : ", int.min); + writeln("Maximum value : ", int.max); + writeln("Initial value : ", int.init); +} +--- + +$(P +The output of the program is the following: +) + +$(SHELL +Type : int +Length in bytes: 4 +Minimum value : -2147483648 +Maximum value : 2147483647 +Initial value : 0 +) + +$(H5 $(IX size_t) $(C size_t)) + +$(P +You will come across the $(C size_t) type as well. $(C size_t) is not a separate type but an alias of an existing unsigned type. Its name comes from "size type". It is the most suitable type to represent concepts like $(I size) or $(I count). +) + +$(P +$(C size_t) is large enough to represent the number of bytes of the memory that a program can potentially be using. Its actual size depends on the system: $(C uint) on a 32-bit system and $(C ulong) on a 64-bit system. For that reason, $(C ulong) is larger than $(C size_t) on a 32-bit system. +) + +$(P +You can use the $(C .stringof) property to see what $(C size_t) is an alias of on your system: +) + +--- +import std.stdio; + +void main() { + writeln(size_t.stringof); +} +--- + +$(P +The output of the program is the following on my system: +) + +$(SHELL +ulong +) + +$(PROBLEM_TEK + +$(P +Print the properties of other types. +) + +$(P +$(I $(B Note:) You can't use the reserved types $(C cent) and $(C ucent) in any program; and as an exception, $(C void) does not have the properties $(C .min), $(C .max) and $(C .init).) +) + +$(P +$(I Additionally, the $(C .min) property is deprecated for floating point types. (You can see all the various properties for the fundamental types in the $(LINK2 http://dlang.org/property.html, D property specification)). If you use a floating point type in this exercise, you would be warned by the compiler that $(C .min) is not valid for that type. Instead, as we will see later in $(LINK2 /ders/d.en/floating_point.html, the Floating Point Types chapter), you must use the negative of the $(C .max) property e.g. as $(C -double.max).) +) + +) + + +Macros: + SUBTITLE=Fundamental Types + + DESCRIPTION=The fundamental types of the D programming language + + KEYWORDS=d programming language tutorial book fundamental types numeric limits diff --git a/target/ufcs.d b/target/ufcs.d new file mode 100644 index 0000000..bbeceff --- /dev/null +++ b/target/ufcs.d @@ -0,0 +1,223 @@ +Ddoc + +$(DERS_BOLUMU $(IX UFCS) $(IX universal function call syntax) Universal Function Call Syntax (UFCS)) + +$(P +UFCS is a feature that is applied by the compiler automatically. It enables the member function syntax even for regular functions. It can be explained simply by comparing two expressions: +) + +--- + variable.foo($(I arguments)) +--- + +$(P +When the compiler encounters an expression such as the one above, if there is no member function named $(C foo) that can be called on $(C variable) with the provided arguments, then the compiler also tries to compile the following expression: +) + +--- + foo(variable, $(I arguments)) +--- + +$(P +If this new expression can indeed be compiled, then the compiler simply accepts that one. As a result, although $(C foo()) evidently has been a regular function, it gets accepted to be used by the member function syntax. +) + +$(P +We know that functions that are closely related to a type are defined as member functions of that type. This is especially important for encapsulation as only the member functions of a type (and that type's module) can access its $(C private) members. +) + +$(P +Let's consider a $(C Car) class which maintains the amount of fuel: +) + +--- +$(CODE_NAME Car)class Car { + enum economy = 12.5; // kilometers per liter (average) + private double fuelAmount; // liters + + this(double fuelAmount) { + this.fuelAmount = fuelAmount; + } + + double fuel() const { + return fuelAmount; + } + + // ... +} +--- + +$(P +Although member functions are very useful and sometimes necessary, not every function that operates on a type should be a member function. Some operations on a type are too specific to a certain application to be member functions. For example, a function that determines whether a car can travel a specific distance may more appropriately be defined as a regular function: +) + +--- +$(CODE_NAME canTravel)bool canTravel(Car car, double distance) { + return (car.fuel() * car.economy) >= distance; +} +--- + +$(P +This naturally brings a discrepancy in calling functions that are related to a type: objects appear at different places in these two syntaxes: +) + +--- +$(CODE_XREF Car)$(CODE_XREF canTravel)void main() { + auto car = new Car(5); + + auto remainingFuel = $(HILITE car).fuel(); // Member function syntax + + if (canTravel($(HILITE car), 100)) { // Regular function syntax + // ... + } +} +--- + +$(P +UFCS removes this discrepancy by allowing regular functions to be called by the member function syntax: +) + +--- + if ($(HILITE car).canTravel(100)) { // Regular function, called by the + // member function syntax + // ... + } +--- + +$(P +This feature is available for fundamental types as well, including literals: +) + +--- +int half(int value) { + return value / 2; +} + +void main() { + assert(42.half() == 21); +} +--- + +$(P +As we will see in the next chapter, when there are no arguments to pass to a function, that function can be called without parentheses. When that feature is used as well, the expression above gets even shorter. All three of the following statements are equivalent: +) + +--- + result = half(value); + result = value.half(); + result = value.half; +--- + +$(P +$(IX chaining, function call) $(IX function call chaining) UFCS is especially useful when function calls are $(I chained). Let's see this on a group of functions that operate on $(C int) slices: +) + +--- +$(CODE_NAME functions)// Returns the result of dividing all of the elements by +// 'divisor' +int[] divide(int[] slice, int divisor) { + int[] result; + result.reserve(slice.length); + + foreach (value; slice) { + result ~= value / divisor; + } + + return result; +} + +// Returns the result of multiplying all of the elements by +// 'multiplier' +int[] multiply(int[] slice, int multiplier) { + int[] result; + result.reserve(slice.length); + + foreach (value; slice) { + result ~= value * multiplier; + } + + return result; +} + +// Filters out elements that have odd values +int[] evens(int[] slice) { + int[] result; + result.reserve(slice.length); + + foreach (value; slice) { + if (!(value % 2)) { + result ~= value; + } + } + + return result; +} +--- + +$(P +When written by the regular syntax, without taking advantage of UFCS, an expression that chains three calls to these functions can be written as in the following program: +) + +--- +$(CODE_XREF functions)import std.stdio; + +// ... + +void main() { + auto values = [ 1, 2, 3, 4, 5 ]; + writeln(evens(divide(multiply(values, 10), 3))); +} +--- + +$(P +The values are first multiplied by 10, then divided by 3, and finally only the even numbers are used: +) + +$(SHELL +[6, 10, 16] +) + +$(P +A problem with the expression above is that although the pair of $(C multiply) and $(C 10) are related and the pair of $(C divide) and $(C 3) are related, parts of each pair end up written away from each other. UFCS eliminates this issue and enables a more natural syntax that reflects the actual order of operations: +) + +--- + writeln(values.multiply(10).divide(3).evens); +--- + +$(P +Some programmers take advantage of UFCS even for calls like $(C writeln()): +) + +--- + values.multiply(10).divide(3).evens.writeln; +--- + +$(P +As an aside, the entire program above could have been written in a much simpler way by $(C map()) and $(C filter()): +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + auto values = [ 1, 2, 3, 4, 5 ]; + + writeln(values + .map!(a => a * 10) + .map!(a => a / 3) + .filter!(a => !(a % 2))); +} +--- + +$(P +The program above takes advantage of $(LINK2 /ders/d.en/templates.html, templates), $(LINK2 /ders/d.en/ranges.html, ranges), and $(LINK2 /ders/d.en/lambda.html, lambda functions), all of which will be explained in later chapters. +) + +Macros: + SUBTITLE=Universal Function Call Syntax (UFCS) + + DESCRIPTION=Universal function call syntax: The ability to call regular functions by the member function syntax. + + KEYWORDS=d programming lesson book tutorial encapsulation diff --git a/target/union.d b/target/union.d new file mode 100644 index 0000000..eb741de --- /dev/null +++ b/target/union.d @@ -0,0 +1,412 @@ +Ddoc + +$(DERS_BOLUMU $(IX union) Unions) + +$(P +Unions, a low-level feature inherited from the C programming language, allow more than one member to share the same memory area. +) + +$(P +Unions are very similar to structs with the following main differences: +) + +$(UL + +$(LI Unions are defined by the $(C union) keyword.) + +$(LI The members of a $(C union) are not independent; they share the same memory area.) + +) + +$(P +Just like structs, unions can have member functions as well. +) + +$(P +$(IX -m32, compiler switch) The examples below will produce different results depending on whether they are compiled on a 32-bit or a 64-bit environment. To avoid getting confusing results, please use the $(C -m32) compiler switch when compiling the examples in this chapter. Otherwise, your results may be different than mine due to $(I alignment), which we will see in a later chapter. +) + +$(P +Naturally, $(C struct) objects are as large as necessary to accommodate all of their members: +) + +--- +// Note: Please compile with the -m32 compiler switch +struct S { + int i; + double d; +} + +// ... + + writeln(S.sizeof); +--- + +$(P +Since $(C int) is 4 bytes long and $(C double) is 8 bytes long, the size of that $(C struct) is the sum of their sizes: +) + +$(SHELL_SMALL +12 +) + +$(P +In contrast, the size of a $(C union) with the same members is only as large as its largest member: +) + +--- +$(HILITE union) U { + int i; + double d; +} + +// ... + + writeln(U.sizeof); +--- + +$(P +The 4-byte $(C int) and the 8-byte $(C double) share the same area. As a result, the size of the entire $(C union) is the same as its largest member: +) + +$(SHELL_SMALL +8 +) + +$(P +Unions are not a memory-saving feature. It is impossible to fit multiple data into the same memory location. The purpose of a union is to use the same area for different type of data at different times. Only one of the members can be used reliably at one time. However, although doing so may not be portable to different platforms, $(C union) members can be used for accessing fragments of other members. +) + +$(P +One of the examples below takes advantage of $(C typeid) to disallow access to members other than the one that is currently valid. +) + +$(P +The following diagram shows how the 8 bytes of the $(C union) above are shared by its members: +) + +$(MONO + 0 1 2 3 4 5 6 7 +───┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬─── + │$(HILITE <─── 4 bytes for int ───>) │ + │$(HILITE <─────────────── 8 bytes for double ────────────────>)│ +───┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴─── +) + +$(P +Either all of the 8 bytes are used for the $(C double) member, or only the first 4 bytes are used for the $(C int) member and the other 4 bytes are unused. +) + +$(P +Unions can have as many members as needed. All of the members would share the same memory location. +) + +$(P +The fact that the same memory location is used for all of the members can have surprising effects. For example, let's initialize a $(C union) object by its $(C int) member and then access its $(C double) member: +) + +--- + auto u = U(42); // initializing the int member + writeln(u.d); // accessing the double member +--- + +$(P +Initializing the $(C int) member by the value 42 sets just the first 4 bytes, and this affects the $(C double) member in an unpredictable way: +) + +$(SHELL_SMALL +2.07508e-322 +) + +$(P +Depending on the endianness of the microprocessor, the 4 bytes may be arranged in memory as 0|0|0|42, 42|0|0|0, or in some other order. For that reason, the value of the $(C double) member may appear differently on different platforms. +) + +$(H5 $(IX anonymous union) Anonymous unions) + +$(P +Anonymous unions specify what members of a user-defined type share the same area: +) + +--- +struct S { + int first; + + union { + int second; + int third; + } +} + +// ... + + writeln(S.sizeof); +--- + +$(P +The last two members of $(C S) share the same area. So, the size of the $(C struct) is a total of two $(C int)s: 4 bytes needed for $(C first) and another 4 bytes to be shared by $(C second) and $(C third): +) + +$(SHELL_SMALL +8 +) + +$(H5 Dissecting other members) + +$(P +Unions can be used for accessing individual bytes of variables of other types. For example, they make it easy to access the 4 bytes of an IPv4 address individually. +) + +$(P +The 32-bit value of the IPv4 address and a fixed-length array can be defined as the two members of a $(C union): +) + +--- +$(CODE_NAME IpAddress)union IpAddress { + uint value; + ubyte[4] bytes; +} +--- + +$(P +The members of that $(C union) would share the same memory area as in the following figure: +) + +$(MONO + 0 1 2 3 +───┬──────────┬──────────┬──────────┬──────────┬─── + │$(HILITE <──── 32 bits of the IPv4 address ────> )│ + │ bytes[0] │ bytes[1] │ bytes[2] │ bytes[3] │ +───┴──────────┴──────────┴──────────┴──────────┴─── +) + +$(P +For example, when an object of this $(C union) is initialized by 0xc0a80102 (the value that corresponds to the dotted form 192.168.1.2), the elements of the $(C bytes) array would automatically have the values of the four octets: +) + +--- +$(CODE_XREF IpAddress)import std.stdio; + +void main() { + auto address = IpAddress(0xc0a80102); + writeln(address$(HILITE .bytes)); +} +--- + +$(P +When run on a little-endian system, the octets would appear in reverse of their dotted form: +) + +$(SHELL_SMALL +[2, 1, 168, 192] +) + +$(P +The reverse order of the octets is another example of how accessing different members of a $(C union) may produce unpredictable results. This is because the behavior of a $(C union) is guaranteed only if that $(C union) is used through just one of its members. There are no guarantees on the values of the members other than the one that the $(C union) has been initialized with. +) + +$(P +$(IX bswap, std.bitop) $(IX endian, std.system) Although it is not directly related to this chapter, $(C bswap) from the $(C core.bitop) module is useful in dealing with endianness issues. $(C bswap) returns its parameter after swapping its bytes. Also taking advantage of the $(C endian) value from the $(C std.system) module, the octets of the previous IPv4 address can be printed in the expected order after swapping its bytes: +) + +--- +import std.system; +import core.bitop; + +// ... + + if (endian == Endian.littleEndian) { + address.value = bswap(address.value); + } +--- + +$(P +The output: +) + +$(SHELL_SMALL +[192, 168, 1, 2] +) + +$(P +Please take the $(C IpAddress) type as a simple example; in general, it would be better to consider a dedicated networking module for non-trivial programs. +) + +$(H5 Examples) + +$(H6 Communication protocol) + +$(P +In some protocols like TCP/IP, the meanings of certain parts of a protocol packet depend on a specific value inside the same packet. Usually, it is a field in the header of the packet that determines the meanings of successive bytes. Unions can be used for representing such protocol packets. +) + +$(P +The following design represents a protocol packet that has two kinds: +) + +--- +struct Host { + // ... +} + +struct ProtocolA { + // ... +} + +struct ProtocolB { + // ... +} + +enum ProtocolType { A, B } + +struct NetworkPacket { + Host source; + Host destination; + ProtocolType $(HILITE type); + + $(HILITE union) { + ProtocolA aParts; + ProtocolB bParts; + } + + ubyte[] payload; +} +--- + +$(P +The $(C struct) above can make use of the $(C type) member to determine whether $(C aParts) or $(C bParts) of the $(C union) to be used. +) + +$(H6 $(IX discriminated union) Discriminated union) + +$(P +Discriminated union is a data structure that brings type safety over a regular $(C union). Unlike a $(C union), it does not allow accessing the members other than the one that is currently valid. +) + +$(P +The following is a simple discriminated union type that supports only two types: $(C int) and $(C double). In addition to a $(C union) to store the data, it maintains a $(LINK2 /ders/d.en/object.html, $(C TypeInfo)) member to know which one of the two $(C union) members is valid. +) + +--- +$(CODE_NAME Discriminated)import std.stdio; +import std.exception; + +struct Discriminated { +private: + + TypeInfo validType_; + + union { + int i_; + double d_; + } + +public: + + this(int value) { + // This is a call to the property function below: + i = value; + } + + // Setter for 'int' data + @property void i(int value) { + i_ = value; + validType_ $(HILITE = typeid(int)); + } + + // Getter for 'int' data + @property int i() const { + enforce(validType_ $(HILITE == typeid(int)), + "The data is not an 'int'."); + return i_; + } + + this(double value) { + // This is a call to the property function below: + d = value; + } + + // Setter for 'double' data + @property void d(double value) { + d_ = value; + validType_ $(HILITE = typeid(double)); + } + + // Getter for 'double' data + @property double d() const { + enforce(validType_ $(HILITE == typeid(double)), + "The data is not a 'double'." ); + return d_; + } + + // Identifies the type of the valid data + @property const(TypeInfo) type() const { + return validType_; + } +} + +unittest { + // Let's start with 'int' data + auto var = Discriminated(42); + + // The type should be reported as 'int' + assert(var.type == typeid(int)); + + // 'int' getter should work + assert(var.i == 42); + + // 'double' getter should fail + assertThrown(var.d); + + // Let's replace 'int' with 'double' data + var.d = 1.5; + + // The type should be reported as 'double' + assert(var.type == typeid(double)); + + // Now 'double' getter should work ... + assert(var.d == 1.5); + + // ... and 'int' getter should fail + assertThrown(var.i); +} +--- + +$(P +$(IX Variant, std.variant) $(IX Algebraic, std.variant) This is just an example. You should consider using $(C Algebraic) and $(C Variant) from the $(C std.variant) module in your programs. Additionally, this code could take advantage of other features of D like $(LINK2 /ders/d.en/templates.html, templates) and $(LINK2 /ders/d.en/mixin.html, mixins) to reduce code duplication. +) + +$(P +Regardless of the data that is being stored, there is only one $(C Discriminated) type. (An alternative template solution could take the data type as a template parameter, in which case each instantiation of the template would be a distinct type.) For that reason, it is possible to have an array of $(C Discriminated) objects, effectively enabling a collection where elements can be of different types. However, the user must still know the valid member before accessing it. For example, the following function determines the actual type of the valid data with the $(C type) property of $(C Discriminated): +) + +--- +$(CODE_XREF Discriminated)void main() { + Discriminated[] arr = [ Discriminated(1), + Discriminated(2.5) ]; + + foreach (value; arr) { + if (value.type $(HILITE == typeid(int))) { + writeln("Working with an 'int' : ", value$(HILITE .i)); + + } else if (value.type $(HILITE == typeid(double))) { + writeln("Working with a 'double': ", value$(HILITE .d)); + + } else { + assert(0); + } + } +} +--- + +$(SHELL +Working with an 'int' : 1 +Working with a 'double': 2.5 +) + +Macros: + SUBTITLE=Unions + + DESCRIPTION=Unions, the feature that allows sharing the same memory area for more than one member. + + KEYWORDS=d programming language tutorial book union diff --git a/target/unit_testing.cozum.d b/target/unit_testing.cozum.d new file mode 100644 index 0000000..083cff6 --- /dev/null +++ b/target/unit_testing.cozum.d @@ -0,0 +1,190 @@ +Ddoc + +$(COZUM_BOLUMU Unit Testing) + +$(P +The first thing to do is to compile and run the program to ensure that the tests actually work and indeed fail: +) + +$(SHELL +$ dmd deneme.d -w -unittest +$ ./deneme +$(SHELL_OBSERVED core.exception.AssertError@deneme(11): unittest failure) +) + +$(P +The line number 11 indicates that the first one of the tests has failed. +) + +$(P +For demonstration purposes let's write an obviously incorrect implementation that passes the first test by accident. The following function simply returns a copy of the input: +) + +--- +dstring toFront(dstring str, in dchar letter) { + dstring result; + + foreach (c; str) { + result ~= c; + } + + return result; +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} + +void main() { +} +--- + +$(P +The first test passes but the second one fails: +) + +$(SHELL +$ ./deneme +$(SHELL_OBSERVED core.exception.AssertError@deneme.d($(HILITE 17)): unittest failure) +) + +$(P +Here is a correct implementation that passes all of the tests: +) + +--- +dstring toFront(dstring str, in dchar letter) { + dchar[] firstPart; + dchar[] lastPart; + + foreach (c; str) { + if (c == letter) { + firstPart ~= c; + + } else { + lastPart ~= c; + } + } + + return (firstPart ~ lastPart).idup; +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} + +void main() { +} +--- + +$(P +The tests finally pass: +) + +$(SHELL +$ ./deneme +$ +) + +$(P +This function can now be modified in different ways under the confidence that its tests will have to pass. The following two implementations are very different from the first one but they too are correct according to the tests. +) + +$(UL + +$(LI +An implementation that takes advantage of $(C std.algorithm.partition): + +--- +import std.algorithm; + +dstring toFront(dstring str, in dchar letter) { + dchar[] result = str.dup; + partition!(c => c == letter, SwapStrategy.stable)(result); + + return result.idup; +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} + +void main() { +} +--- + +$(P +$(I $(B Note:) The $(C =>) syntax that appears in the program above creates a $(I lambda function). We will see lambda functions in later chapters.) +) + +) + +$(LI +The following implementation first counts how many times the special letter appears in the string. That information is then sent to a separate function named $(C repeated()) to produce the first part of the result. Note that $(C repeated()) has a set of unit tests of its own: + +--- +dstring repeated(size_t count, dchar letter) { + dstring result; + + foreach (i; 0..count) { + result ~= letter; + } + + return result; +} + +unittest { + assert(repeated(3, 'z') == "zzz"); + assert(repeated(10, 'é') == "éééééééééé"); +} + +dstring toFront(dstring str, in dchar letter) { + size_t specialLetterCount; + dstring lastPart; + + foreach (c; str) { + if (c == letter) { + ++specialLetterCount; + + } else { + lastPart ~= c; + } + } + + return repeated(specialLetterCount, letter) ~ lastPart; +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} + +void main() { +} +--- + +) + +) + +Macros: + SUBTITLE=Unit Testing + + DESCRIPTION=The exercise solutions for the Unit Testing chapter. + + KEYWORDS=programming in d tutorial unit testing diff --git a/target/unit_testing.d b/target/unit_testing.d new file mode 100644 index 0000000..e160d10 --- /dev/null +++ b/target/unit_testing.d @@ -0,0 +1,530 @@ +Ddoc + +$(DERS_BOLUMU $(IX unit testing) $(IX test) Unit Testing) + +$(P +As it should be known by most people, any device that runs some piece of computer program contains software bugs. Software bugs plague computer devices from the simplest to the most complex. Debugging and fixing software bugs is among the less favorable daily activities of a programmer. +) + +$(H5 $(IX bug, causes of) Causes of bugs) + +$(P +There are many reasons why software bugs occur. The following is an incomplete list roughly from the design stage of a program through the actual coding of it: +) + +$(UL + +$(LI +The requirements and the specifications of the program may not be clear. What the program should actually do may not be known at the design stage. +) + +$(LI +The programmer may misunderstand some of the requirements of the program. +) + +$(LI +The programming language may not be expressive enough. Considering that there are confusions even between native speakers of human languages, the unnatural syntax and rules of a programming language may be cause of mistakes. +) + +$(LI +Certain assumptions of the programmer may be incorrect. For example, the programmer may be assuming that 3.14 would be precise enough to represent π. +) + +$(LI +The programmer may have incorrect information on a topic or none at all. For example, the programmer may not know that using a floating point variable in a particular logical expression would not be reliable. +) + +$(LI +The program may encounter an unforeseen situation. For example, one of the files of a directory may be deleted or renamed while the program is using the files of that directory in a $(C foreach) loop. +) + +$(LI +The programmer may make silly mistakes. For example, the name of a variable may be mistyped and accidentally matched the name of another variable. +) + +$(LI etc.) + +) + +$(P +Unfortunately, there is still no software development methodology that ensures that a program will always work correctly. This is still a hot software engineering topic where promising solutions emerge every decade or so. +) + +$(H5 Discovering the bugs) + +$(P +Software bugs are discovered at various stages of the lifetime of the program by various types of tools and people. The following is a partial list of when a bug may be discovered, from the earliest to the latest: +) + +$(UL +$(LI When writing the program + +$(UL +$(LI By the programmer +) + +$(LI By another programmer during $(I pair programming) +) + +$(LI By the compiler through compiler messages +) + +$(LI By $(HILITE unit tests) as a part of building the program +) + +) + +) + +$(LI When reviewing the code + +$(UL +$(LI By tools that analyze the code at compile time +) + +$(LI By other programmers during $(I code reviews) +) +) + +) + +$(LI When running the program + +$(UL +$(LI By tools that analyze the execution of the program at run time (e.g. by valgrind) +) + +$(LI During QA testing, either by the failure of $(C assert) checks or by the observed behavior of the program +) + +$(LI By the $(I beta) users before the release of the program +) + +$(LI By the end users after the release of the program) + +) + +) + +) + +$(P +Detecting bugs as early as possible reduces loss of money, time, and in some cases human lives. Additionally, identifying the causes of bugs that have been discovered by the end users are harder than identifying the causes of bugs that are discovered early, during development. +) + +$(H5 Unit testing for catching bugs) + +$(P +Since programs are written by programmers and D is a compiled language, the programmers and the compiler will always be there to discover bugs. Those two aside, the earliest and partly for that reason the most effective way of catching bugs is unit testing. +) + +$(P +Unit testing is an indispensable part of modern programming. It is the most effective method of reducing coding errors. According to some development methodologies, code that is not guarded by unit tests is buggy code. +) + +$(P +Unfortunately, the opposite is not true: Unit tests do not guarantee that the code is free of bugs. Although they are very effective, they can only reduce the risk of bugs. +) + +$(P +Unit testing also enables refactoring the code (i.e. making improvements to it) with ease and confidence. Otherwise, it is common to accidentally break some of the existing functionality of a program when adding new features to it. Bugs of this type are called $(I regressions). Without unit testing, regressions are sometimes discovered as late as during the QA testing of future releases, or worse, by the end users. +) + +$(P +Risk of regressions discourage programmers from refactoring the code, sometimes preventing them from performing the simplest of improvements like correcting the name of a variable. This in turn causes $(I code rot), a condition where the code becomes more and more unmaintainable. For example, although some lines of code would better be moved to a newly defined function in order to be called from more than one place, fear of regressions make programmers copy and paste the existing lines to other places instead, leading to the problem of $(I code duplication). +) + +$(P +Phrases like "if it isn't broken, don't fix it" are related to fear of regressions. Although they seem to be conveying wisdom, such guidelines cause the code to rot slowly and become an untouchable mess. +) + +$(P +Modern programming rejects such "wisdom". To the contrary, to prevent it from becoming a source of bugs, the code is supposed to be "refactored mercilessly". The most powerful tool of this modern approach is unit testing. +) + +$(P +Unit testing involves testing the smallest units of code independently. When units of code are tested independently, it is less likely that there are bugs in higher-level code that use those units. When the parts work correctly, it is more likely that the whole will work correctly as well. +) + +$(P +Unit tests are provided as library solutions in other languages (e.g. JUnit, CppUnit, Unittest++, etc.) In D, unit testing is a core feature of the language. It is debatable whether a library solution or a language feature is better for unit testing. Because D does not provide some of the features that are commonly found in unit testing libraries, it may be worthwhile to consider library solutions as well. +) + +$(P +The unit testing features of D are as simple as inserting $(C assert) checks into $(C unittest) blocks. +) + +$(H5 $(IX -unittest, compiler switch) Activating the unit tests) + +$(P +Unit tests are not a part of the actual execution of the program. They should be activated only during program development when explicitly requested. +) + +$(P +The $(C dmd) compiler switch that activates unit tests is $(C ‑unittest). +) + +$(P +Assuming that the program is written in a single source file named $(C deneme.d), its unit tests can be activated by the following command: +) + +$(SHELL +dmd deneme.d -w $(HILITE -unittest) +) + +$(P +When a program that is built by the $(C ‑unittest) switch is started, its unit test blocks are executed first. Only if all of the unit tests pass that the program execution continues with $(C main()). +) + +$(H5 $(IX unittest) $(C unittest) blocks) + +$(P +The lines of code that involve unit tests are written inside $(C unittest) blocks. These blocks do not have any significance for the program other than containing the unit tests: +) + +--- +unittest { + /* ... the tests and the code that support them ... */ +} +--- + +$(P +Although $(C unittest) blocks can appear anywhere, it is convenient to define them right after the code that they test. +) + +$(P +As an example, let's test a function that returns the ordinal form of the specified number as in "1st", "2nd", etc. A $(C unittest) block of this function can simply contain $(C assert) statements that compare the return values of the function to the expected values. The following function is being tested with the four distinct expected outcomes of the function: +) + +--- +string ordinal(size_t number) { + // ... +} + +$(HILITE unittest) { + assert(ordinal(1) == "1st"); + assert(ordinal(2) == "2nd"); + assert(ordinal(3) == "3rd"); + assert(ordinal(10) == "10th"); +} +--- + +$(P +The four tests above test that the function works correctly at least for the values of 1, 2, 3, and 10 by making four separate calls to the function and comparing the returned values to the expected ones. +) + +$(P +Although unit tests are based on $(C assert) checks, $(C unittest) blocks can contain any D code. This allows for preparations before actually starting the tests or any other supporting code that the tests may need. For example, the following block first defines a variable to reduce code duplication: +) + +--- +dstring toFront(dstring str, in dchar letter) { + // ... +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} +--- + +$(P +The three $(C assert) checks above test that $(C toFront()) works according to its specification. +) + +$(P +As these examples show, unit tests are also useful as examples of how particular functions should be called. Usually it is easy to get an idea on what a function does just by reading its unit tests. +) + +$(H5 $(IX assertThrown, std.exception) $(IX assertNotThrown, std.exception) Testing for exceptions) + +$(P +It is common to test some code for exception types that it should or should not throw under certain conditions. The $(C std.exception) module contains two functions that help with testing for exceptions: +) + +$(UL + +$(LI $(C assertThrown): Ensures that a specific exception type is thrown from an expression) + +$(LI $(C assertNotThrown): Ensures that a specific exception type is $(I not) thrown from an expression) + +) + +$(P +For example, a function that requires that both of its slice parameters have equal lengths and that it works with empty slices can be tested as in the following tests: +) + +--- +import std.exception; + +int[] average(int[] a, int[] b) { + // ... +} + +unittest { + /* Must throw for uneven slices */ + assertThrown(average([1], [1, 2])); + + /* Must not throw for empty slices */ + assertNotThrown(average([], [])); +} +--- + +$(P +Normally, $(C assertThrown) ensures that some type of exception is thrown without regard to the actual type of that exception. When needed, it can test against a specific exception type as well. Likewise, $(C assertNotThrown) ensures that no exception is thrown whatsoever but it can be instructed to test that a specific exception type is not thrown. The specific exception types are specified as template parameters to these functions: +) + +--- + /* Must throw UnequalLengths for uneven slices */ + assertThrown$(HILITE !UnequalLengths)(average([1], [1, 2])); + + /* Must not throw RangeError for empty slices (it may + * throw other types of exceptions) */ + assertNotThrown$(HILITE !RangeError)(average([], [])); +--- + +$(P +We will see templates in a $(LINK2 /ders/d.en/templates.html, later chapter). +) + +$(P +Main purpose of these functions is to make code more succinct and more readable. For example, the following $(C assertThrown) line is the equivalent of the lengthy code below it: +) + +--- + assertThrown(average([1], [1, 2])); + +// ... + + /* The equivalent of the line above */ + { + auto isThrown = false; + + try { + average([1], [1, 2]); + + } catch (Exception exc) { + isThrown = true; + } + + assert(isThrown); + } +--- + +$(H5 $(IX TDD) $(IX test driven development) Test driven development) + +$(P +Test driven development (TDD) is a software development methodology that prescribes writing unit tests before implementing functionality. In TDD, the focus is on unit testing. Coding is a secondary activity that makes the tests pass. +) + +$(P +In accordance to TDD, the $(C ordinal()) function above can first be implemented intentionally incorrectly: +) + +--- +import std.string; + +string ordinal(size_t number) { + return ""; // ← intentionally wrong +} + +unittest { + assert(ordinal(1) == "1st"); + assert(ordinal(2) == "2nd"); + assert(ordinal(3) == "3rd"); + assert(ordinal(10) == "10th"); +} + +void main() { +} +--- + +$(P +Although the function is obviously wrong, the next step would be to run the unit tests to see that the tests do indeed catch problems with the function: +) + +$(SHELL +$ dmd deneme.d -w -unittest +$ ./deneme +$(SHELL_OBSERVED core.exception.AssertError@deneme(10): $(HILITE unittest failure)) +) + +$(P +The function should be implemented only $(I after) seeing the failure, and only to make the tests pass. Here is just one implementation that passes the tests: +) + +--- +import std.string; + +string ordinal(size_t number) { + string suffix; + + switch (number) { + case 1: suffix = "st"; break; + case 2: suffix = "nd"; break; + case 3: suffix = "rd"; break; + default: suffix = "th"; break; + } + + return format("%s%s", number, suffix); +} + +unittest { + assert(ordinal(1) == "1st"); + assert(ordinal(2) == "2nd"); + assert(ordinal(3) == "3rd"); + assert(ordinal(10) == "10th"); +} + +void main() { +} +--- + +$(P +Since the implementation above does pass the unit tests, there is reason to trust that the $(C ordinal()) function is correct. Under the assurance that the tests bring, the implementation of the function can be changed in many ways with confidence. +) + +$(H6 Unit tests before bug fixes) + +$(P +Unit tests are not a panacea; there will always be bugs. If a bug is discovered when actually running the program, it can be seen as an indication that the unit tests have been incomplete. For that reason, it is better to $(I first) write a unit test that reproduces the bug and only $(I then) to fix the bug to pass the new test. +) + +$(P +Let's have a look at the following function that returns the spelling of the ordinal form of a number specified as a $(C dstring): +) + +--- +$(CODE_NAME ordinalSpelled)import std.exception; +import std.string; + +dstring ordinalSpelled(dstring number) { + enforce(number.length, "number cannot be empty"); + + dstring[dstring] exceptions = [ + "one": "first", "two" : "second", "three" : "third", + "five" : "fifth", "eight": "eighth", "nine" : "ninth", + "twelve" : "twelfth" + ]; + + dstring result; + + if (number in exceptions) { + result = exceptions[number]; + + } else { + result = number ~ "th"; + } + + return result; +} + +unittest { + assert(ordinalSpelled("one") == "first"); + assert(ordinalSpelled("two") == "second"); + assert(ordinalSpelled("three") == "third"); + assert(ordinalSpelled("ten") == "tenth"); +} + +void main() { +} +--- + +$(P +The function takes care of exceptional spellings and even includes a unit test for that. Still, the function has a bug yet to be discovered: +) + +--- +$(CODE_XREF ordinalSpelled)import std.stdio; + +void main() { + writefln("He came the %s in the race.", + ordinalSpelled("twenty")); +} +--- + +$(P +The spelling error in the output of the program is due to a bug in $(C ordinalSpelled()), which its unit tests fail to catch: +) + +$(SHELL +He came the $(HILITE twentyth) in the race. +) + +$(P +Although it is easy to see that the function does not produce the correct spelling for numbers that end with the letter y, TDD prescribes that first a unit test must be written to reproduce the bug before actually fixing it: +) + +--- +unittest { +// ... + assert(ordinalSpelled("twenty") == "twentieth"); +} +--- + +$(P +With that improvement to the tests, now the bug in the function is being caught during development: +) + +$(SHELL +core.exception.AssertError@deneme(3274338): unittest failure +) + +$(P +The function should be fixed only then: +) + +--- +dstring ordinalSpelled(dstring number) { +// ... + if (number in exceptions) { + result = exceptions[number]; + + } else { + if ($(HILITE number[$-1] == 'y')) { + result = number[0..$-1] ~ "ieth"; + + } else { + result = number ~ "th"; + } + } + + return result; +} +--- + +$(PROBLEM_TEK + +$(P +Implement $(C toFront()) according to TDD. Start with the intentionally incomplete implementation below. Observe that the unit tests fail and provide an implementation that passes the tests. +) + +--- +dstring toFront(dstring str, in dchar letter) { + dstring result; + return result; +} + +unittest { + immutable str = "hello"d; + + assert(toFront(str, 'h') == "hello"); + assert(toFront(str, 'o') == "ohell"); + assert(toFront(str, 'l') == "llheo"); +} + +void main() { +} +--- + +) + +Macros: + SUBTITLE=Unit Testing + + DESCRIPTION=The unittest feature of the D programming language, which is one of the most effective tools for program correctness. + + KEYWORDS=d programming language tutorial book unittest diff --git a/target/value_vs_reference.d b/target/value_vs_reference.d new file mode 100644 index 0000000..e22a871 --- /dev/null +++ b/target/value_vs_reference.d @@ -0,0 +1,701 @@ +Ddoc + +$(DERS_BOLUMU $(IX value type) $(IX reference type) Value Types and Reference Types) + +$(P +This chapter introduces the concepts of value types and reference types. These concepts are particularly important to understand the differences between structs and classes. +) + +$(P +This chapter also gets into more detail with the $(C &) operator. +) + +$(P +The chapter ends with a table that contains the outcomes of the following two concepts for different types of variables: +) + +$(UL +$(LI Value comparison) +$(LI Address comparison) +) + +$(H5 Value types) + +$(P +Value types are easy to describe: Variables of value types carry values. For example, all of the integer and floating point types are values types. Although not immediately obvious, fixed-length arrays are value types as well. +) + +$(P +For example, a variable of type $(C int) has an integer value: +) + +--- + int speed = 123; +--- + +$(P +The number of bytes that the variable $(C speed) occupies is the size of an $(C int). If we visualize the memory as a ribbon going from left to right, we can imagine the variable living on some part of it: +) + +$(MONO + speed + ───┬─────┬─── + │ 123 │ + ───┴─────┴─── +) + +$(P +When variables of value types are copied, they get their own values: +) + +--- + int newSpeed = speed; +--- + +$(P +The new variable would have a place and a value of its own: +) + +$(MONO + speed newSpeed + ───┬─────┬─── ───┬─────┬─── + │ 123 │ │ 123 │ + ───┴─────┴─── ───┴─────┴─── +) + +$(P +Naturally, modifications that are made to these variables are independent: +) + +--- + speed = 200; +--- + +$(P +The value of the other variable does not change: +) + +$(MONO + speed newSpeed + ───┬─────┬─── ───┬─────┬─── + │ 200 │ │ 123 │ + ───┴─────┴─── ───┴─────┴─── +) + +$(H6 The use of $(C assert) checks below) + +$(P +The following examples contain $(C assert) checks to indicate that their conditions are true. In other words, they are not checks in the normal sense, rather my way of telling to the reader that "this is true". +) + +$(P +For example, the check $(C assert(speed == newSpeed)) below means "speed is equal to newSpeed". +) + +$(H6 Value identity) + +$(P +As the memory representations above indicate, there are two types of equality that concern variables: +) + +$(UL + +$(LI $(B Value equality): The $(C ==) operator that appears in many examples throughout the book compares variables by their values. When two variables are said to be $(I equal) in that sense, their values are equal. +) +$(LI $(B Value identity): In the sense of owning separate values, $(C speed) and $(C newSpeed) have separate identities. Even when their values are equal, they are different variables. +) + +) + +--- + int speed = 123; + int newSpeed = speed; + assert(speed == newSpeed); + speed = 200; + assert(speed != newSpeed); +--- + +$(H6 Address-of operator, $(C &)) + +$(P +We have been using the $(C &) operator so far with $(C readf()). The $(C &) operator tells $(C readf()) where to put the input data. +) + +$(P $(I $(B Note:) As we have seen in $(LINK2 /ders/d.en/input.html, the Reading from the Standard Input chapter), $(C readf()) can be used without explicit pointers as well. +)) + +$(P +The addresses of variables can be used for other purposes as well. The following code simply prints the addresses of two variables: +) + +--- + int speed = 123; + int newSpeed = speed; + + writeln("speed : ", speed, " address: ", $(HILITE &)speed); + writeln("newSpeed: ", newSpeed, " address: ", $(HILITE &)newSpeed); +--- + +$(P +$(C speed) and $(C newSpeed) have the same value but their addresses are different: +) + +$(SHELL +speed : 123 address: 7FFF4B39C738 +newSpeed: 123 address: 7FFF4B39C73C +) + +$(P $(I $(B Note:) It is normal for the addresses to have different values every time the program is run. Variables live at parts of memory that happen to be available during that particular execution of the program.) +) + +$(P +Addresses are normally printed in hexadecimal format. +) + +$(P +Additionally, the fact that the two addresses are 4 apart indicates that those two integers are placed next to each other in memory. (Note that the value of hexadecimal C is 12, so the difference between 8 and 12 is 4.) +) + +$(H5 $(IX variable, reference) Reference variables) + +$(P +Before getting to reference types let's first define reference variables. +) + +$(P +$(B Terminology:) We have been using the phrase $(I to provide access to) so far in several contexts throughout the book. For example, slices and associative arrays do not own any elements but provide access to elements that are owned by the D runtime. Another phrase that is identical in meaning is $(I being a reference of) as in "slices are references of zero or more elements", which is sometimes used even shorter as $(I to reference) as in "this slice references two elements". Finally, the act of accessing a value through a reference is called $(I dereferencing). +) + +$(P +Reference variables are variables that act like aliases of other variables. Although they look and are used like variables, they do not have values themselves. Modifications made on a reference variable change the value of the actual variable. +) + +$(P +We have already used reference variables so far in two contexts: +) + +$(UL + +$(LI $(B $(C ref) in $(C foreach) loops): The $(C ref) keyword makes the loop variable the $(I actual) element that corresponds to that iteration. When the $(C ref) keyword is not used, the loop variable is a $(I copy) of the actual element. + +$(P +This can be demonstrated by the $(C &) operator as well. If their addresses are the same, two variables would be referencing the same value (or the $(I same element) in this case): +) + +--- + int[] slice = [ 0, 1, 2, 3, 4 ]; + + foreach (i, $(HILITE ref) element; slice) { + assert(&element == &slice[i]); + } +--- + +$(P +Although they are separate variables, the fact that the addresses of $(C element) and $(C slice[i]) are the same proves that they have the same value identity. +) + +$(P +In other words, $(C element) and $(C slice[i]) are references of the same value. Modifying either of those affects the actual value. The following memory layout indicates a snapshot of the iteration when $(C i) is 3: +) + +$(MONO + slice[0] slice[1] slice[2] slice[3] slice[4] + ⇢ ⇢ ⇢ (element) +──┬────────┬────────┬────────┬────────┬─────────┬── + │ 0 │ 1 │ 2 │ 3 │ 4 │ +──┴────────┴────────┴────────┴────────┴─────────┴── +) + +) + +$(LI $(B $(C ref) and $(C out) function parameters): Function parameters that are specified as $(C ref) or $(C out) are aliases of the actual variable the function is called with. + +$(P +The following example demonstrates this case by passing the same variable to separate $(C ref) and $(C out) parameters of a function. Again, the $(C &) operator indicates that both parameters have the same value identity: +) + +--- +import std.stdio; + +void main() { + int originalVariable; + writeln("address of originalVariable: ", &originalVariable); + foo(originalVariable, originalVariable); +} + +void foo($(HILITE ref) int refParameter, $(HILITE out) int outParameter) { + writeln("address of refParameter : ", &refParameter); + writeln("address of outParameter : ", &outParameter); + assert($(HILITE &)refParameter == $(HILITE &)outParameter); +} +--- + +$(P +Although they are defined as separate parameters, $(C refParameter) and $(C outParameter) are aliases of $(C originalVariable): +) + +$(SHELL +address of originalVariable: 7FFF24172958 +address of refParameter : 7FFF24172958 +address of outParameter : 7FFF24172958 +) + +) + +) + +$(H5 $(IX type, reference) Reference types) + +$(P +Variables of reference types have individual identities but they do not have individual values. They $(I provide access to) existing variables. +) + +$(P +We have already seen this concept with slices. Slices do not own elements, they provide access to existing elements: +) + +--- +void main() { + // Although it is named as 'array' here, this variable is + // a slice as well. It provides access to all of the + // initial elements: + int[] array = [ 0, 1, 2, 3, 4 ]; + + // A slice that provides access to elements other than the + // first and the last: + int[] slice = array[1 .. $ - 1]; + + // At this point slice[0] and array[1] provide access to + // the same value: + assert($(HILITE &)slice[0] == $(HILITE &)array[1]); + + // Changing slice[0] changes array[1]: + slice[0] = 42; + assert(array[1] == 42); +} +--- + +$(P +Contrary to reference variables, reference types are not simply aliases. To see this distinction, let's define another slice as a copy of one of the existing slices: +) + +--- + int[] slice2 = slice; +--- + +$(P +The two slices have their own adresses. In other words, they have separate identities: +) + +--- + assert(&slice != &slice2); +--- + +$(P +The following list is a summary of the differences between reference variables and reference types: +) + +$(UL + +$(LI Reference variables do not have identities, they are aliases of existing variables.) + +$(LI Variables of reference types have identities but they do not own values; rather, they provide access to existing values.) + +) + +$(P +The way $(C slice) and $(C slice2) live in memory can be illustrated as in the following figure: +) + +$(MONO + slice slice2 + ───┬───┬───┬───┬───┬───┬─── ───┬───┬─── ───┬───┬─── + │ 0 │$(HILITE  1 )│$(HILITE  2 )│$(HILITE  3 )│ 4 │ │ o │ │ o │ + ───┴───┴───┴───┴───┴───┴─── ───┴─│─┴─── ───┴─│─┴─── + ▲ │ │ + │ │ │ + └────────────────────┴────────────┘ +) + +$(P +The three elements that the two slices both reference are highlighted. +) + +$(P +One of the differences between C++ and D is that classes are reference types in D. Although we will cover classes in later chapters in detail, the following is a short example to demonstrate this fact: +) + +--- +class MyClass { + int member; +} +--- + +$(P +Class objects are constructed by the $(C new) keyword: +) + +--- + auto variable = new MyClass; +--- + +$(P +$(C variable) is a reference to an anonymous $(C MyClass) object that has been constructed by $(C new): +) + +$(MONO + (anonymous MyClass object) variable + ───┬───────────────────┬─── ───┬───┬─── + │ ... │ │ o │ + ───┴───────────────────┴─── ───┴─│─┴─── + ▲ │ + │ │ + └────────────────────┘ +) + +$(P +Just like with slices, when $(C variable) is copied, the copy becomes another reference to the same object. The copy has its own address: +) + +--- + auto variable = new MyClass; + auto variable2 = variable; + assert(variable == variable2); + assert(&variable != &variable2); +--- + +$(P +They are equal from the point of view of referencing the same object, but they are separate variables: +) + +$(MONO + (anonymous MyClass object) variable variable2 + ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── + │ ... │ │ o │ │ o │ + ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── + ▲ │ │ + │ │ │ + └────────────────────┴────────────┘ +) + +$(P +This can also be shown by modifying the member of the object: +) + +--- + auto variable = new MyClass; + variable.member = 1; + + auto variable2 = variable; // They share the same object + variable2.member = 2; + + assert(variable.member == 2); // The object that variable + // provides access to has + // changed. +--- + +$(P +Another reference type is associative arrays. Like slices and classes, when an associative array is copied or assigned to another variable, both give access to the same set of elements: +) + +--- + string[int] byName = + [ + 1 : "one", + 10 : "ten", + 100 : "hundred", + ]; + + // The two associative arrays will be sharing the same + // set of elements + string[int] byName2 = byName; + + // The mapping added through the second ... + byName2[4] = "four"; + + // ... is visible through the first. + assert(byName[4] == "four"); +--- + +$(P +As it will be explained in the next chapter, there is no element sharing if the original associative array were $(C null) to begin with. +) + +$(H6 The difference in the assignment operation) + +$(P +With value types and reference variables, the assignment operation changes $(I the actual value): +) + +--- +void main() { + int number = 8; + + halve(number); // The actual value changes + assert(number == 4); +} + +void halve($(HILITE ref) int dividend) { + dividend /= 2; +} +--- + +$(P +On the other hand, with reference types, the assignment operation changes $(I which value is being accessed). For example, the assignment of the $(C slice3) variable below does not change the value of any element; rather, it changes what elements $(C slice3) is now a reference of: +) + +--- + int[] slice1 = [ 10, 11, 12, 13, 14 ]; + int[] slice2 = [ 20, 21, 22 ]; + + int[] slice3 = slice1[1 .. 3]; // Access to element 1 + // element 2 of slice1 + + slice3[0] = 777; + assert(slice1 == [ 10, 777, 12, 13, 14 ]); + + // This assignment does not modify the elements that + // slice3 is providing access to. It makes slice3 provide + // access to other elements. + $(HILITE slice3 =) slice2[$ - 1 .. $]; // Access to the last element + + slice3[0] = 888; + assert(slice2 == [ 20, 21, 888 ]); +--- + +$(P +Let's demonstrate the same effect this time with two objects of the $(C MyClass) type: +) + +--- + auto variable1 = new MyClass; + variable1.member = 1; + + auto variable2 = new MyClass; + variable2.member = 2; + + auto $(HILITE aCopy = variable1); + aCopy.member = 3; + + $(HILITE aCopy = variable2); + aCopy.member = 4; + + assert(variable1.member == 3); + assert(variable2.member == 4); +--- + +$(P +The $(C aCopy) variable above first references the same object as $(C variable1), and then the same object as $(C variable2). As a consequence, the $(C .member) that is modified through $(C aCopy) is first $(C variable1)'s and then $(C variable2)'s. +) + +$(H6 Variables of reference types may not be referencing any object) + +$(P +With a reference variable, there is always an actual variable that it is an alias of; it can not start its life without a variable. On the other hand, variables of reference types can start their lives without referencing any object. +) + +$(P +For example, a $(C MyClass) variable can be defined without an actual object having been created by $(C new): +) + +--- + MyClass variable; +--- + +$(P +Such variables have the special value of $(C null). We will cover $(C null) and the $(C is) keyword in $(LINK2 /ders/d.en/null_is.html, a later chapter). +) + +$(H5 Fixed-length arrays are value types, slices are reference types) + +$(P +D's arrays and slices diverge when it comes to value type versus reference type. +) + +$(P +As we have already seen above, slices are reference types. On the other hand, fixed-length arrays are value types. They own their elements and behave as individual values: +) + +--- + int[3] array1 = [ 10, 20, 30 ]; + + auto array2 = array1; // array2's elements are different + // from array1's + array2[0] = 11; + + // First array is not affected: + assert(array1[0] == 10); +--- + +$(P +$(C array1) is a fixed-length array because its length is specified when it has been defined. Since $(C auto) makes the compiler infer the type of $(C array2), it is a fixed-length array as well. The values of $(C array2)'s elements are copied from the values of the elements of $(C array1). Each array has its own elements. Modifying an element through one does not affect the other. +) + + +$(H5 Experiment) + +$(P +The following program is an experiment of applying the $(C ==) operator to different types. It applies the operator to both variables of a certain type and to the addresses of those variables. The program produces the following output: +) + +$(MONO + Type of variable a == b &a == &b +=========================================================================== + variables with equal values (value type) true false + variables with different values (value type) false false + foreach with 'ref' variable true true + foreach without 'ref' variable true false + function with 'out' parameter true true + function with 'ref' parameter true true + function with 'in' parameter true false + slices providing access to same elements true false + slices providing access to different elements false false + MyClass variables to same object (reference type) true false +MyClass variables to different objects (reference type) false false +) + +$(P +The table above has been generated by the following program: +) + +--- +import std.stdio; +import std.array; + +int moduleVariable = 9; + +class MyClass { + int member; +} + +void printHeader() { + immutable dchar[] header = + " Type of variable" ~ + " a == b &a == &b"; + + writeln(); + writeln(header); + writeln(replicate("=", header.length)); +} + +void printInfo(const dchar[] label, + bool valueEquality, + bool addressEquality) { + writefln("%55s%9s%9s", + label, valueEquality, addressEquality); +} + +void main() { + printHeader(); + + int number1 = 12; + int number2 = 12; + printInfo("variables with equal values (value type)", + number1 == number2, + &number1 == &number2); + + int number3 = 3; + printInfo("variables with different values (value type)", + number1 == number3, + &number1 == &number3); + + int[] slice = [ 4 ]; + foreach (i, ref element; slice) { + printInfo("foreach with 'ref' variable", + element == slice[i], + &element == &slice[i]); + } + + foreach (i, element; slice) { + printInfo("foreach without 'ref' variable", + element == slice[i], + &element == &slice[i]); + } + + outParameter(moduleVariable); + refParameter(moduleVariable); + inParameter(moduleVariable); + + int[] longSlice = [ 5, 6, 7 ]; + int[] slice1 = longSlice; + int[] slice2 = slice1; + printInfo("slices providing access to same elements", + slice1 == slice2, + &slice1 == &slice2); + + int[] slice3 = slice1[0 .. $ - 1]; + printInfo("slices providing access to different elements", + slice1 == slice3, + &slice1 == &slice3); + + auto variable1 = new MyClass; + auto variable2 = variable1; + printInfo( + "MyClass variables to same object (reference type)", + variable1 == variable2, + &variable1 == &variable2); + + auto variable3 = new MyClass; + printInfo( + "MyClass variables to different objects (reference type)", + variable1 == variable3, + &variable1 == &variable3); +} + +void outParameter(out int parameter) { + printInfo("function with 'out' parameter", + parameter == moduleVariable, + ¶meter == &moduleVariable); +} + +void refParameter(ref int parameter) { + printInfo("function with 'ref' parameter", + parameter == moduleVariable, + ¶meter == &moduleVariable); +} + +void inParameter(in int parameter) { + printInfo("function with 'in' parameter", + parameter == moduleVariable, + ¶meter == &moduleVariable); +} +--- + +$(P +Notes: +) + +$(UL + +$(LI +$(IX module variable) $(IX variable, module) The program makes use of a module variable when comparing different types of function parameters. Module variables are defined at module level, outside of all of the functions. They are globally accessible to all of the code in the module. +) + +$(LI +$(IX replicate, std.array) The $(C replicate()) function of the $(C std.array) module takes an array (the $(STRING "=") string above) and repeats it the specified number of times. +) + +) + +$(H5 Summary) + +$(UL + +$(LI Variables of value types have their own values and adresses.) + +$(LI Reference variables do not have their own values nor addresses. They are aliases of existing variables.) + +$(LI Variables of reference types have their own addresses but the values that they reference do not belong to them.) + +$(LI With reference types, assignment does not change value, it changes which value is being accessed.) + +$(LI Variables of reference types may be $(C null).) + +) + +Macros: + SUBTITLE=Value Types and Reference Types + + DESCRIPTION=A comparison of value types and reference types in the D programming language, and the 'address of' operator. + + KEYWORDS=d programming book tutorial value reference diff --git a/target/variables.cozum.d b/target/variables.cozum.d new file mode 100644 index 0000000..86f86ef --- /dev/null +++ b/target/variables.cozum.d @@ -0,0 +1,22 @@ +Ddoc + +$(COZUM_BOLUMU Variables) + +--- +import std.stdio; + +void main() { + int amount = 20; + double rate = 2.11; + + writeln("I have exchanged ", amount, + " Euros at the rate of ", rate); +} +--- + +Macros: + SUBTITLE=Variables Solutions + + DESCRIPTION=Programming in D exercise solutions: variables + + KEYWORDS=programming in d tutorial variables solution diff --git a/target/variables.d b/target/variables.d new file mode 100644 index 0000000..b7c316d --- /dev/null +++ b/target/variables.d @@ -0,0 +1,107 @@ +Ddoc + +$(DERS_BOLUMU $(IX variable) Variables) + +$(P +Concrete concepts that are represented in a program are called $(I variables). A value like $(I air temperature) and a more complicated object like $(I a car engine) can be variables of a program. +) + +$(P +The main purpose of a variable is to represent a value in the program. The value of a variable is the last value that has been assigned to that variable. Since every value is of a certain type, every variable is of a certain type as well. Most variables have names as well, but some variables are anonymous. +) + +$(P +As an example of a variable, we can think of the concept of $(I the number of students) at a school. Since the number of students is a whole number, $(C int) is a suitable type, and $(C studentCount) would be a sufficiently descriptive name. +) + +$(P +According to D's syntax rules, a variable is introduced by its type followed by its name. The introduction of a variable to the program is called its $(I definition). Once a variable is defined, its name represents its value. +) + +--- +import std.stdio; + +void main() { + // The definition of the variable; this definition + // specifies that the type of studentCount is int: + int studentCount; + + // The name of the variable becomes its value: + writeln("There are ", studentCount, " students."); +} +--- + +$(P +The output of this program is the following: +) + +$(SHELL +There are 0 students. +) + +$(P +As seen from that output, the value of $(C studentCount) is 0. This is according to the fundamental types table from the previous chapter: the initial value of $(C int) is 0. +) + +$(P +Note that $(C studentCount) does not appear in the output as its name. In other words, the output of the program is not "There are studentCount students". +) + +$(P +The values of variables are changed by the $(C =) operator. The $(C =) operator assigns new values to variables, and for that reason is called the $(I assignment operator): +) + +--- +import std.stdio; + +void main() { + int studentCount; + writeln("There are ", studentCount, " students."); + + // Assigning the value 200 to the studentCount variable: + studentCount $(HILITE =) 200; + writeln("There are now ", studentCount, " students."); +} +--- + +$(SHELL +There are 0 students. +There are now 200 students. +) + +$(P +When the value of a variable is known at the time of the variable's definition, the variable can be defined and assigned at the same time. This is an important guideline; it makes it impossible to use a variable before assigning its intended value: +) + +--- +import std.stdio; + +void main() { + // Definition and assignment at the same time: + int studentCount = 100; + + writeln("There are ", studentCount, " students."); +} +--- + +$(SHELL +There are 100 students. +) + +$(PROBLEM_TEK + +$(P +Define two variables to print "I have exchanged 20 Euros at the rate of 2.11". You can use the floating point type $(C double) for the decimal value. +) + +) + + +Macros: + SUBTITLE=Variables + + DESCRIPTION=The variables in the D programming language + + KEYWORDS=d programming language tutorial book variables + +MINI_SOZLUK= diff --git a/target/while.cozum.d b/target/while.cozum.d new file mode 100644 index 0000000..8b5660b --- /dev/null +++ b/target/while.cozum.d @@ -0,0 +1,50 @@ +Ddoc + +$(COZUM_BOLUMU The $(CH4 while) Loop) + +$(OL + +$(LI +Because the initial value of $(C number) is 0, the logical expression of the $(C while) loop is $(C false) since the very beginning, and this is preventing from entering the loop body. A solution is to use an initial value that will allow the $(C while) condition to be $(C true) at the beginning: + +--- + int number = 3; +--- + +) + +$(LI +All of the variables in the following program are default initialized to 0. This allows entering both of the loops at least once: + +--- +import std.stdio; + +void main() { + int secretNumber; + + while ((secretNumber < 1) || (secretNumber > 10)) { + write("Please enter a number between 1 and 10: "); + readf(" %s", &secretNumber); + } + + int guess; + + while (guess != secretNumber) { + write("Guess the secret number: "); + readf(" %s", &guess); + } + + writeln("That is correct!"); +} +--- + +) + +) + +Macros: + SUBTITLE=The while Loop Solutions + + DESCRIPTION=Programming in D exercise solutions: the 'while' loop + + KEYWORDS=programming in d tutorial while solution diff --git a/target/while.d b/target/while.d new file mode 100644 index 0000000..4b25e67 --- /dev/null +++ b/target/while.d @@ -0,0 +1,230 @@ +Ddoc + +$(DERS_BOLUMU $(IX while) $(IX loop, while) $(CH4 while) Loop) + +$(P +The $(C while) loop is similar to the $(C if) statement and essentially works as a repeated $(C if) statement. Just like $(C if), $(C while) also takes a logical expression and evaluates the block when the logical expression is $(C true). The difference is that the $(C while) statement evaluates the logical expression and executes the expressions in the block repeatedly, as long as the logical expression is $(C true), not just once. Repeating a block of code this way is called $(I looping). +) + +$(P +Here is the syntax of the $(C while) statement: +) + +--- + while (a_logical_expression) { + // ... expression(s) to execute while true + } +--- + +$(P +For example, the code that represents $(I eat cookies as long as there is cookie) can be coded like this: +) + +--- +import std.stdio; + +void main() { + bool existsCookie = true; + + while (existsCookie) { + writeln("Take cookie"); + writeln("Eat cookie"); + } +} +--- + +$(P +That program would continue repeating the loop because the value of $(C existsCookie) never changes from $(C true). +) + +$(P +$(C while) is useful when the value of the logical expression changes during the execution of the program. To see this, let's write a program that takes a number from the user as long as that number is zero or greater. Remember that the initial value of $(C int) variables is 0: +) + +--- +import std.stdio; + +void main() { + int number; + + while (number >= 0) { + write("Please enter a number: "); + readf(" %s", &number); + + writeln("Thank you for ", number); + } + + writeln("Exited the loop"); +} +--- + +$(P +The program thanks for the provided number and exits the loop only when the number is less than zero. +) + +$(H5 $(IX continue) The $(C continue) statement) + +$(P +The continue statement starts the next iteration of the loop right away, instead of executing the rest of the expressions of the block. +) + +$(P +Let's modify the program above to be a little picky: instead of thanking for any number, let's not accept 13. The following program does not thank for 13 because in that case the $(C continue) statement makes the program go to the beginning of the loop to evaluate the logical expression again: +) + +--- +import std.stdio; + +void main() { + int number; + + while (number >= 0) { + write("Please enter a number: "); + readf(" %s", &number); + + if (number == 13) { + writeln("Sorry, not accepting that one..."); + $(HILITE continue); + } + + writeln("Thank you for ", number); + } + + writeln("Exited the loop"); +} +--- + +$(P +We can define the behavior of that program as $(I take numbers as long as they are greater than or equal to 0 but skip 13). +) + +$(P +$(C continue) works with $(C do-while), $(C for), and $(C foreach) statements as well. We will see these features in later chapters. +) + +$(H5 $(IX break) The $(C break) statement) + +$(P +Sometimes it becomes obvious that there is no need to stay in the $(C while) loop any longer. $(C break) allows the program to exit the loop right away. The following program exits the loop as soon as it finds a special number: +) + +--- +import std.stdio; + +void main() { + int number; + + while (number >= 0) { + write("Please enter a number: "); + readf(" %s", &number); + + if (number == 42) { + writeln("FOUND IT!"); + $(HILITE break); + } + + writeln("Thank you for ", number); + } + + writeln("Exited the loop"); +} +--- + +$(P +We can summarize this behavior as $(I take numbers as long as they are greater than or equal to 0 or until a number is 42). +) + +$(P +$(C break) works with $(C do-while), $(C for), $(C foreach), and $(C switch) statements as well. We will see these features in later chapters. +) + +$(H5 $(IX loop, infinite) Infinite loop) + +$(P +Sometimes the logical expression is intentionally made a constant $(C true). The $(C break) statement is a common way of exiting such $(I infinite loops). +) + +$(P +The following program prints a menu in an infinite loop; the only way of exiting the loop is a $(C break) statement: +) + +--- +import std.stdio; + +void main() { + /* Infinite loop, because the logical expression is always + * true */ + while ($(HILITE true)) { + write("0:Exit, 1:Turkish, 2:English - Your choice? "); + + int choice; + readf(" %s", &choice); + + if (choice == 0) { + writeln("See you later..."); + $(HILITE break); // The only exit of this loop + + } else if (choice == 1) { + writeln("Merhaba!"); + + } else if (choice == 2) { + writeln("Hello!"); + + } else { + writeln("Sorry, I don't know that language. :/"); + } + } +} +--- + +$(P +$(I $(B Note:)) Exceptions $(I can terminate an infinite loop as well. We will see exceptions in a later chapter.) +) + +$(PROBLEM_COK + +$(PROBLEM +The following program is designed to stay in the loop as long as the input is 3, but there is a bug: it doesn't ask for any input: + +--- +import std.stdio; + +void main() { + int number; + + while (number == 3) { + write("Number? "); + readf(" %s", &number); + } +} +--- + +$(P +Fix the bug. The program should stay in the loop as long as the input is 3. +) + +) + +$(PROBLEM +Make the computer help Anna and Bill play a game. First, the computer should take a number from Anna in the range from 1 to 10. The program should not accept any other number; it should ask again. + +$(P +Once the program takes a valid number from Anna, it should start taking numbers from Bill until he guesses Anna's number correctly. +) + +$(P +$(I $(B Note:) The numbers that Anna enters obviously stays on the terminal and can be seen by Bill. Let's ignore this fact and write the program as an exercise of the $(C while) statement.) +) + +) + +) + +Macros: + SUBTITLE=while Loop + + DESCRIPTION=The while loop and the related statements break and continue + + KEYWORDS=d programming language tutorial book while loop statement break continue + +MINI_SOZLUK= diff --git a/target/writeln.cozum.d b/target/writeln.cozum.d new file mode 100644 index 0000000..a45861d --- /dev/null +++ b/target/writeln.cozum.d @@ -0,0 +1,32 @@ +Ddoc + +$(COZUM_BOLUMU writeln and write) + +$(OL + +$(LI +One method is to use another parameter in between: + +--- + writeln("Hello world!", " ", "Hello fish!"); +--- + +) + +$(LI +$(C write) can take multiple parameters as well: + +--- + write("one", " two", " three"); +--- + +) + +) + +Macros: + SUBTITLE=writeln and write Solutions + + DESCRIPTION=Programming in D exercise solutions: writeln and write + + KEYWORDS=programming in d tutorial writeln and write exercise solution diff --git a/target/writeln.d b/target/writeln.d new file mode 100644 index 0000000..77c63f9 --- /dev/null +++ b/target/writeln.d @@ -0,0 +1,83 @@ +Ddoc + +$(DERS_BOLUMU $(IX writeln) $(IX write) $(CH4 writeln) and $(CH4 write)) + +$(P +In the previous chapter we have seen that $(C writeln) takes a string within parentheses and prints the string. +) + +$(P +The parts of programs that actually do work are called $(I functions) and the information that they need to complete their work are called $(I parameters). The act of giving such information to functions is called $(I passing parameter values) to them. Parameters are passed to functions within parentheses, separated by commas. +) + +$(P +$(I $(B Note:) The word ) parameter $(I describes the information that is passed to a function at the conceptual level. The concrete information that is actually passed during the execution of the program is called an) argument. $(I Although not technically the same, these terms are sometimes used interchangably in the software industry.) +) + +$(P +$(C writeln) can take more than one argument. It prints them one after the other on the same line: +) + +--- +import std.stdio; + +void main() { + writeln("Hello world!", "Hello fish!"); +} +--- + +$(P +Sometimes, all of the information that is to be printed on the same line may not be readily available to be passed to $(C writeln). In such cases, the first parts of the line may be printed by $(C write) and the last part of the line may be printed by $(C writeln). +) + +$(P +$(C writeln) advances to the next line, $(C write) stays on the same line: +) + +--- +import std.stdio; + +void main() { + // Let's first print what we have available: + write("Hello"); + + // ... let's assume more operations at this point ... + + write("world!"); + + // ... and finally: + writeln(); +} +--- + +$(P +Calling $(C writeln) without any parameter merely completes the current line, or if nothing has been written, outputs a blank line. +) + +$(P +$(IX //) $(IX comment) Lines that start with $(COMMENT //) are called $(I comment lines) or briefly $(I comments). A comment is not a part of the program code in the sense that it doesn't affect the behavior of the program. Its only purpose is to explain what the code does in that particular section of the program. The audience of a comment is anybody who may be reading the program code later, including the programmer who wrote the comment in the first place. +) + +$(PROBLEM_COK + +$(PROBLEM + +Both of the programs in this chapter print the strings without any spaces between them. Change the programs so that there is space between the arguments as in "Hello world!". + +) + +$(PROBLEM +Try calling $(C write) with more than one parameter as well. +) + +) + + +Macros: + SUBTITLE=writeln and write + + DESCRIPTION=Two functions of the D standard library: writeln and write. + + KEYWORDS=d programming language tutorial book if conditional statement + +MINI_SOZLUK= From 5fe1e73896b9243e769fc0d1bf98f95afaa2b544 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 26 Jul 2017 11:21:10 +0800 Subject: [PATCH 016/225] Translated by Heromyth --- omegat/project_save.tmx | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 491f789..66eac0a 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -12,6 +12,14 @@ $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA))
+ + + $(DERS_BOLUMU Programming in D) + + + $(DERS_BOLUMU D 语言编程) + + $(P @@ -78,6 +86,14 @@ COZUMLER_METIN=答案 DERLEME_HATASI_METIN=编译出错 + + + DESCRIPTION=D programming language tutorial from the ground up. + + + DESCRIPTION=全新编写的 D 编程语言教程。 + + DUSEY_NAVIGASYON= @@ -192,6 +208,14 @@ LANG=zh-cn LANGUAGE=chinese + + + Macros: + + + 宏(Macros): + + PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... @@ -200,6 +224,14 @@ LANGUAGE=chinese PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… + + + SUBTITLE=Programming in D + + + SUBTITLE=D 语言编程 + + User defined attributes is purely a compile-time feature. From f473918e80e9a0ddf95cd7b67532731569027fba Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 26 Jul 2017 11:22:39 +0800 Subject: [PATCH 017/225] Translated by Heromyth --- omegat/project_save.tmx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 66eac0a..f477e42 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -208,14 +208,6 @@ LANG=zh-cn LANGUAGE=chinese - - - Macros: - - - 宏(Macros): - - PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... From fa2fc7c718feb2646954fd33be2a702590ae86cd Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 1 Aug 2017 12:26:18 +0800 Subject: [PATCH 018/225] Translated by Heromyth --- omegat/project_save.tmx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index f477e42..ee1c7f5 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -50,6 +50,18 @@ Multiple attributes can be specified separately or as a parenthesized list of at 多个属性可以分别指定,也可以采用括号列表形式。 + + + $(P +These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. +) + + + $(P +以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 +) + + $(UL @@ -208,6 +220,14 @@ LANG=zh-cn LANGUAGE=chinese + + + Macros: + + + 宏(Macros): + + PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... From 738397123dbd85f7abab039112d77b7b08c08229 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 1 Aug 2017 12:32:28 +0800 Subject: [PATCH 019/225] Translated by Heromyth --- omegat/project_save.tmx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index ee1c7f5..c75d923 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -33,6 +33,16 @@ $(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名 $(P +Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). + + + $(P +其他提供形式还包括 $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). + + + + + $(P Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. From 9589d48f2b577054433e6a8fffb7e94cc1fa51e3 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 1 Aug 2017 13:23:26 +0800 Subject: [PATCH 020/225] Translated by Heromyth --- omegat/project_save.tmx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index c75d923..c4682ba 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -33,11 +33,23 @@ $(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名 $(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) +) + + + $(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载示例代码 $(C .zip) file.) +) + + + + + $(P Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). - + $(P -其他提供形式还包括 $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 From a0681b338927755c29d8ab07ea17c7ddff977e80 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 1 Aug 2017 18:02:52 +0800 Subject: [PATCH 021/225] Translated by Heromyth --- omegat/project_save.tmx | 56 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index c4682ba..7c208c0 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -20,6 +20,14 @@ $(DERS_BOLUMU D 语言编程) + + + $(H5 Online version) + + + $(H5 在线版本) + + $(P @@ -36,9 +44,9 @@ $(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名 $(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) ) - + $(P -$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载示例代码 $(C .zip) file.) +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) ) @@ -84,6 +92,14 @@ These options have different prices, shipping times, shipping costs, customs and ) + + + $(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) + + + $(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) + + $(UL @@ -102,6 +118,42 @@ $(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) ) + + + $(UL +$(WORK_IN_PROCESS +$(LI $(LINK2 /ders/d.en/foreword1.html, Foreword by Walter Bright)) +) +$(LI $(LINK2 /ders/d.en/foreword2.html, Foreword by Andrei Alexandrescu)) +$(LI $(LINK2 /ders/d.en/preface.html, Preface)) +$(LI $(LINK2 /ders/d.en/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.en/writeln.html, writeln and write)) +$(LI $(LINK2 /ders/d.en/compiler.html, Compilation)) +$(LI $(LINK2 /ders/d.en/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) +$(LI $(LINK2 /ders/d.en/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.en/variables.html, Variables)) +$(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) +$(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! + + + $(UL +$(WORK_IN_PROCESS +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) +) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) +$(LI $(LINK2 /ders/d.cn/preface.html, 前言)) +$(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) +$(LI $(LINK2 /ders/d.cn/compiler.html, 编译)) +$(LI $(LINK2 /ders/d.cn/types.html, 基础类型) $(INDEX_KEYWORDS char int double (等))) +$(LI $(LINK2 /ders/d.cn/assignment.html, 赋值与计算顺序) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.cn/variables.html, 变量)) +$(LI $(LINK2 /ders/d.cn/io.html, 标准输入、输出流) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) +$(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! + + COZUM_METIN=the solution From adf75ea9db81398e6dffe791b0c4c7b005516e34 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 1 Aug 2017 18:09:31 +0800 Subject: [PATCH 022/225] Translated by Heromyth --- omegat/project_save.tmx | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 7c208c0..6f53867 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -154,6 +154,28 @@ $(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! + + + == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.en/if.html, if Statement) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.en/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.en/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.en/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.en/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. + + + == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.cn/if.html, if 语句) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.cn/while.html, while 循环) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.cn/arithmetic.html, 整型和算术运算) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.cn/floating_point.html, 浮点类型) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.cn/arrays.html, 数组) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. + + COZUM_METIN=the solution @@ -260,6 +282,14 @@ $(LINK2 /ders/d.cn/index.html, 下载或购买 $(IMG book.png)) + + + KEYWORDS=d programming language tutorial book novice beginner + + + KEYWORDS=d programming language tutorial book novice beginner D编程语言 教程 书籍 新手 初学者 + + MAIN_TITLE=D.ershane Solutions From 9dbb29edcda5026a7978db0edd4c2df4bbe00c30 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 10:53:20 +0800 Subject: [PATCH 023/225] Translated by Heromyth --- omegat/project_save.tmx | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 6f53867..e9e8fd0 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -4,6 +4,76 @@
+ + + $ .dup capacity)) +$(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.en/stream_redirect.html, Redirecting Standard Input and Output Streams)) +$(LI $(LINK2 /ders/d.en/files.html, Files) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.en/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.en/name_space.html, Name Scope)) +$(LI $(LINK2 /ders/d.en/for.html, for Loop) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.en/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.en/literals.html, Literals)) +$(LI $(LINK2 /ders/d.en/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.en/formatted_input.html, Formatted Input)) +$(LI $(LINK2 /ders/d.en/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.en/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.en/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.en/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.en/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.en/functions.html, Functions) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.en/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.en/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.en/lazy_operators.html, Lazy Operators)) +$(LI $(LINK2 /ders/d.en/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.en/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.en/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.en/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.en/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.en/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations)) +$(LI $(LINK2 /ders/d.en/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.en/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.en/struct.html, Structs) $(INDEX_KEYWORDS struct . + + + $ .dup capacity)) +$(LI $(LINK2 /ders/d.cn/strings.html, 字符串) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.cn/stream_redirect.html, 重定向标准输入输出流)) +$(LI $(LINK2 /ders/d.cn/files.html, 文件) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.cn/auto_and_typeof.html, auto 和 typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.cn/name_space.html, 名字作用域)) +$(LI $(LINK2 /ders/d.cn/for.html, for 循环) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.cn/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.cn/literals.html, 文字量)) +$(LI $(LINK2 /ders/d.cn/formatted_output.html, 格式化输出) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.cn/formatted_input.html, 格式化输入)) +$(LI $(LINK2 /ders/d.cn/do_while.html, do-while 循环) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.cn/aa.html, 关联数组) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.cn/foreach.html, foreach 循环) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.cn/switch_case.html, switch 和 case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.cn/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.cn/functions.html, 函数) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, 不变量) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型与引用类型) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值与右值) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.cn/lazy_operators.html, 惰性运算符)) +$(LI $(LINK2 /ders/d.cn/main.html, 程序环境) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.cn/exceptions.html, 异常) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.cn/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.cn/assert.html, assert 与 enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.cn/unit_testing.html, 单元测试) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.cn/contracts.html, 契约编程) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.cn/lifetimes.html, 生命周期与函数式运算)) +$(LI $(LINK2 /ders/d.cn/null_is.html, null 值与 is 运算符) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.cn/cast.html, 类型转换) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . + + $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) From c1f0c7b5846432b1aa83b9b08442ce98983829bd Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 12:56:19 +0800 Subject: [PATCH 024/225] Translated by Heromyth --- omegat/project_save.tmx | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index e9e8fd0..0ef26e9 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -426,6 +426,100 @@ LANGUAGE=chinese 自定义属性完全是一项编译时功能。 + + + {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ (and more))) +$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) +$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) +$(LI $(LINK2 /ders/d.en/class.html, Classes) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.en/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.en/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.en/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.en/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.en/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.en/ufcs.html, Universal Function Call Syntax (UFCS))) +$(LI $(LINK2 /ders/d.en/property.html, Properties) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.en/templates.html, Templates)) +$(LI $(LINK2 /ders/d.en/pragma.html, Pragmas)) +$(LI $(LINK2 /ders/d.en/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.en/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.en/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.en/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.en/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.en/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) +$(LI $(LINK2 /ders/d.en/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.en/union.html, Unions) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.en/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.en/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.en/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.en/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.en/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.en/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) +$(LI $(LINK2 /ders/d.en/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.en/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.en/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.en/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.en/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.en/operator_precedence.html, Operator Precedence)) +) + + + {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, 不定个数参数) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__(等))) +$(LI $(LINK2 /ders/d.cn/function_overloading.html, 函数重载)) +$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数和 const 函数函数) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其他特殊函数) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex(等))) +$(LI $(LINK2 /ders/d.cn/class.html, 类) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.cn/inheritance.html, 继承) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.cn/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.cn/interface.html, 接口) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.cn/destroy.html, destroy 和 scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.cn/modules.html, 模块和库) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/encapsulation.html, 封装和保护属性) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.cn/ufcs.html, 统一调用语法(UFCS))) +$(LI $(LINK2 /ders/d.cn/property.html, 特性) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.cn/invariant.html, 结构和类的契约编程) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.cn/templates.html, 模板)) +$(LI $(LINK2 /ders/d.cn/pragma.html, 编译指令)) +$(LI $(LINK2 /ders/d.cn/alias.html, alias 和 with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.cn/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.cn/pointers.html, 指针) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.cn/bit_operations.html, 位运算) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.cn/cond_comp.html, 条件编译) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.cn/is_expr.html, is 表达式) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.cn/lambda.html, 函数指针、委托和λ) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, 将foreach用于结构和类) $(INDEX_KEYWORDS opApply empty popFront front(等))) +$(LI $(LINK2 /ders/d.cn/nested.html, 嵌套函数、结构和类) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.cn/union.html, 联合) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.cn/goto.html, 标签和 goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.cn/tuples.html, 元组) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.cn/templates_more.html, 模板的更多内容) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.cn/functions_more.html, 函数的更多内容) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.cn/mixin.html, 混入) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.cn/ranges.html, 范围) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.cn/ranges_more.html, 范围的更多内容) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject(等))) +$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行性) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.cn/concurrency.html, 消息传递并发) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, 数据共享并发) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.cn/fibers.html, 纤程) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.cn/memory.html, 内存管理) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.cn/uda.html, 自定义属性(UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.cn/operator_precedence.html, 运算符优先级)) +) + + From 7feb4d040346078f205a83ddeb69d16e2791b882 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 12:57:37 +0800 Subject: [PATCH 025/225] Translated by Heromyth --- omegat/project_save.tmx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 0ef26e9..f9b9d26 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -356,8 +356,8 @@ $(LINK2 /ders/d.cn/index.html, KEYWORDS=d programming language tutorial book novice beginner - - KEYWORDS=d programming language tutorial book novice beginner D编程语言 教程 书籍 新手 初学者 + + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 @@ -394,14 +394,6 @@ LANG=zh-cn LANGUAGE=chinese - - - Macros: - - - 宏(Macros): - - PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... From 19b82d97442b2a52c796bd2bb6e24986e8fe8263 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 12:57:55 +0800 Subject: [PATCH 026/225] Project translation --- target/index.d | 202 ++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/target/index.d b/target/index.d index 3ec6313..02a140e 100644 --- a/target/index.d +++ b/target/index.d @@ -1,6 +1,6 @@ Ddoc -$(DERS_BOLUMU Programming in D) +$(DERS_BOLUMU D 语言编程)
@@ -15,126 +15,126 @@ $(P ) $(P -These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. +以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 )
$(P -Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 ) $(P -$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) ) -$(H5 Online version) +$(H5 在线版本) -$(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) +$(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) $(UL $(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.en/foreword1.html, Foreword by Walter Bright)) +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) ) -$(LI $(LINK2 /ders/d.en/foreword2.html, Foreword by Andrei Alexandrescu)) -$(LI $(LINK2 /ders/d.en/preface.html, Preface)) -$(LI $(LINK2 /ders/d.en/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.en/writeln.html, writeln and write)) -$(LI $(LINK2 /ders/d.en/compiler.html, Compilation)) -$(LI $(LINK2 /ders/d.en/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) -$(LI $(LINK2 /ders/d.en/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.en/variables.html, Variables)) -$(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) -$(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d.en/if.html, if Statement) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.en/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.en/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN)) -$(LI $(LINK2 /ders/d.en/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.en/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. $ .dup capacity)) -$(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.en/stream_redirect.html, Redirecting Standard Input and Output Streams)) -$(LI $(LINK2 /ders/d.en/files.html, Files) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d.en/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.en/name_space.html, Name Scope)) -$(LI $(LINK2 /ders/d.en/for.html, for Loop) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d.en/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.en/literals.html, Literals)) -$(LI $(LINK2 /ders/d.en/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.en/formatted_input.html, Formatted Input)) -$(LI $(LINK2 /ders/d.en/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d.en/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.en/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.en/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d.en/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.en/functions.html, Functions) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.en/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d.en/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d.en/lazy_operators.html, Lazy Operators)) -$(LI $(LINK2 /ders/d.en/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d.en/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d.en/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.en/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.en/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.en/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations)) -$(LI $(LINK2 /ders/d.en/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.en/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.en/struct.html, Structs) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ (and more))) -$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) -$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) -$(LI $(LINK2 /ders/d.en/class.html, Classes) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.en/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d.en/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.en/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.en/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.en/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.en/ufcs.html, Universal Function Call Syntax (UFCS))) -$(LI $(LINK2 /ders/d.en/property.html, Properties) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d.en/templates.html, Templates)) -$(LI $(LINK2 /ders/d.en/pragma.html, Pragmas)) -$(LI $(LINK2 /ders/d.en/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d.en/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.en/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.en/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.en/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d.en/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) -$(LI $(LINK2 /ders/d.en/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.en/union.html, Unions) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.en/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d.en/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.en/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.en/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.en/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.en/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) -$(LI $(LINK2 /ders/d.en/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.en/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.en/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.en/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.en/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.en/operator_precedence.html, Operator Precedence)) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) +$(LI $(LINK2 /ders/d.cn/preface.html, 前言)) +$(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) +$(LI $(LINK2 /ders/d.cn/compiler.html, 编译)) +$(LI $(LINK2 /ders/d.cn/types.html, 基础类型) $(INDEX_KEYWORDS char int double (等))) +$(LI $(LINK2 /ders/d.cn/assignment.html, 赋值与计算顺序) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.cn/variables.html, 变量)) +$(LI $(LINK2 /ders/d.cn/io.html, 标准输入、输出流) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) +$(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false !== != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.cn/if.html, if 语句) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.cn/while.html, while 循环) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.cn/arithmetic.html, 整型和算术运算) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.cn/floating_point.html, 浮点类型) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.cn/arrays.html, 数组) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. $ .dup capacity)) +$(LI $(LINK2 /ders/d.cn/strings.html, 字符串) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.cn/stream_redirect.html, 重定向标准输入输出流)) +$(LI $(LINK2 /ders/d.cn/files.html, 文件) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.cn/auto_and_typeof.html, auto 和 typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.cn/name_space.html, 名字作用域)) +$(LI $(LINK2 /ders/d.cn/for.html, for 循环) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.cn/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.cn/literals.html, 文字量)) +$(LI $(LINK2 /ders/d.cn/formatted_output.html, 格式化输出) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.cn/formatted_input.html, 格式化输入)) +$(LI $(LINK2 /ders/d.cn/do_while.html, do-while 循环) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.cn/aa.html, 关联数组) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.cn/foreach.html, foreach 循环) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.cn/switch_case.html, switch 和 case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.cn/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.cn/functions.html, 函数) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, 不变量) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型与引用类型) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值与右值) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.cn/lazy_operators.html, 惰性运算符)) +$(LI $(LINK2 /ders/d.cn/main.html, 程序环境) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.cn/exceptions.html, 异常) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.cn/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.cn/assert.html, assert 与 enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.cn/unit_testing.html, 单元测试) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.cn/contracts.html, 契约编程) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.cn/lifetimes.html, 生命周期与函数式运算)) +$(LI $(LINK2 /ders/d.cn/null_is.html, null 值与 is 运算符) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.cn/cast.html, 类型转换) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, 不定个数参数) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__(等))) +$(LI $(LINK2 /ders/d.cn/function_overloading.html, 函数重载)) +$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数和 const 函数函数) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其他特殊函数) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex(等))) +$(LI $(LINK2 /ders/d.cn/class.html, 类) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.cn/inheritance.html, 继承) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.cn/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.cn/interface.html, 接口) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.cn/destroy.html, destroy 和 scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.cn/modules.html, 模块和库) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/encapsulation.html, 封装和保护属性) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.cn/ufcs.html, 统一调用语法(UFCS))) +$(LI $(LINK2 /ders/d.cn/property.html, 特性) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.cn/invariant.html, 结构和类的契约编程) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.cn/templates.html, 模板)) +$(LI $(LINK2 /ders/d.cn/pragma.html, 编译指令)) +$(LI $(LINK2 /ders/d.cn/alias.html, alias 和 with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.cn/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.cn/pointers.html, 指针) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.cn/bit_operations.html, 位运算) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.cn/cond_comp.html, 条件编译) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.cn/is_expr.html, is 表达式) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.cn/lambda.html, 函数指针、委托和λ) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, 将foreach用于结构和类) $(INDEX_KEYWORDS opApply empty popFront front(等))) +$(LI $(LINK2 /ders/d.cn/nested.html, 嵌套函数、结构和类) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.cn/union.html, 联合) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.cn/goto.html, 标签和 goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.cn/tuples.html, 元组) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.cn/templates_more.html, 模板的更多内容) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.cn/functions_more.html, 函数的更多内容) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.cn/mixin.html, 混入) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.cn/ranges.html, 范围) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.cn/ranges_more.html, 范围的更多内容) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject(等))) +$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行性) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.cn/concurrency.html, 消息传递并发) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, 数据共享并发) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.cn/fibers.html, 纤程) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.cn/memory.html, 内存管理) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.cn/uda.html, 自定义属性(UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.cn/operator_precedence.html, 运算符优先级)) ) Macros: - SUBTITLE=Programming in D + SUBTITLE=D 语言编程 - DESCRIPTION=D programming language tutorial from the ground up. + DESCRIPTION=全新编写的 D 编程语言教程。 - KEYWORDS=d programming language tutorial book novice beginner + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 BREADCRUMBS=$(BREADCRUMBS_INDEX) From b722ef1c35fedd7a98d6081b1d7ac30a7036046b Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 13:05:27 +0800 Subject: [PATCH 027/225] Translated by Heromyth --- omegat/project_save.tmx | 5371 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 5370 insertions(+), 1 deletion(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index f9b9d26..db53a16 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1,10 +1,5379 @@ -
+
+ + $(C !is) is the opposite of $(C is). + + + $(C !is) 与 $(C is) 相反。 + + + + + $(C .get()) is useful in such cases: it returns the value associated with the specified key if that key exists, otherwise it returns the default value. + + + $(C .get()) 在这样的样例中是有用的:如果指定键存在则返回相应值,否则返回默认值。 + + + + + $(C .idup) is used for producing immutable strings from existing strings: +) + + + $(C .idup) 可用来从存在的字符串中生成不可变的字符串: +) + + + + + $(C .length) returns the number of elements of the array: +) + + + $(C .length) 返回数组元素的个数: +) + + + + + $(C readf()) continues to wait for more characters to add to the string: +) + + + $(C readf()) 继续等待新输入的字符以添加到字符串: +) + + + + + $(C variable1) and $(C variable2) above merely provide access to that anonymous object: +) + + + 上面的 $(C variable1) 和 $(C variable2) 只提供对那个匿名对象的访问: +) + + + + + $(C ~) concatenates two strings and $(C ~=) appends to an existing string: +) + + + $(C ~) 可以连接两个字符串,$(C ~=) 则能够把字符串附加到一个已存在的字符串上: +) + + + + + $(COZUM_BOLUMU Arrays) + + + $(COZUM_BOLUMU 数组) + + + + + $(COZUM_BOLUMU Associative Arrays) + + + $(COZUM_BOLUMU 关联数组) + + + + + $(COZUM_BOLUMU Strings) + + + $(COZUM_BOLUMU 字符串) + + + + + $(DERS_BOLUMU $(IX associative array) $(IX AA) Associative Arrays) + + + $(DERS_BOLUMU $(IX 关联数组) $(IX AA) 关联数组) + + + + + $(DERS_BOLUMU $(IX class) Classes) + + + $(DERS_BOLUMU $(IX class) 类) + + + + + $(DERS_BOLUMU Strings) + + + $(DERS_BOLUMU 字符串) + + + + + $(H5 $(IX &quot;) Double quotes, not single quotes) + + + $(H5 $(IX &quot;) 使用双引号,而非单引号) + + + + + $(H5 $(IX .length) Using $(C .length) to get or set the number of elements) + + + $(H5 $(IX .length) 使用 $(C .length) 来获取或设置元素的个数) + + + + + $(H5 $(IX []) Accessing the elements) + + + $(H5 $(IX []) 元素的访问) + + + + + $(H5 $(IX concatenation, string) String concatenation) + + + $(H5 $(IX concatenation, string) 字符串连接) + + + + + $(H5 $(IX container) $(IX element) Containers and elements) + + + $(H5 $(IX container) $(IX element) 容器和元素) + + + + + $(H5 $(IX fixed-length array) $(IX dynamic array) $(IX static array) Fixed-length arrays vs. dynamic arrays) + + + $(H5 $(IX 定长数组) $(IX 动态数组) $(IX 静态数组) 定长数组与动态数组) + + + + + $(H5 $(IX formattedRead) $(C formattedRead) for parsing strings) + + + $(H5 $(IX formattedRead) 使用 $(C formattedRead) 函数来解析字符串) + + + + + $(H5 $(IX in, associative array) Determining the presence of a key) + + + $(H5 $(IX in, 关联数组) 确定键的存在) + + + + + $(H5 $(IX index) Index) + + + $(H5 $(IX index) Index(索引)) + + + + + $(H5 $(IX initialization, array) Initializing the elements) + + + $(H5 $(IX 初始化,数组) 初始化元素) + + + + + $(H5 $(IX length, string) Potentially confusing length of strings) + + + $(H5 $(IX length, string) 有可能让人困惑的字符串长度) + + + + + $(H5 $(IX literal, string) String literals) + + + $(H5 $(IX literal, string) 字符串字面量) + + + + + $(H5 $(IX readln) $(IX strip) $(C readln) and $(C strip), instead of $(C readf)) + + + $(H5 $(IX readln) $(IX strip) 使用 $(C readln) 和 $(C strip),而非 $(C readf)) + + + + + $(H5 $(IX remove) Removing key-value pairs) + + + $(H5 $(IX remove) 移除键值对) + + + + + $(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string), $(C wstring), and $(C dstring) are immutable) + + + $(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string)、$(C wstring) 和 $(C dstring) 是 immutable (不可变的)) + + + + + $(H5 Adding key-value pairs) + + + $(H5 添加键值对) + + + + + $(H5 An array example) + + + $(H5 一个数组例子) + + + + + $(H5 Basic array operations) + + + $(H5 数组的基本操作) + + + + + $(H5 Comparing strings) + + + $(H5 比较字符串) + + + + + $(H5 Definition) + + + $(H5 定义) + + + + + $(H5 Example) + + + $(H5 样例) + + + + + $(H5 Initialization) + + + $(H5 初始化) + + + + + $(H5 Lowercase and uppercase are different) + + + $(H5 小写与大写不同) + + + + + $(H5 Properties) + + + $(H5 Properties(属性)) + + + + + $(H5 Summary) + + + $(H5 摘要) + + + + + $(H6 $(IX assignment, class) Assignment) + + + $(H6 $(IX assignment, class) 赋值) + + + + + $(H6 $(IX copy, array) Copying fixed-length arrays) + + + $(H6 $(IX 复制, 数组) 复​​制定长数组) + + + + + $(H6 $(IX copy, class) Copying) + + + $(H6 $(IX copy, class) 复制) + + + + + $(H6 $(IX is, operator) $(IX !is) The $(C is) and $(C !is) operators) + + + $(H6 $(IX is, 运算符) $(IX !is) $(C is) 和 $(C !is) 运算符) + + + + + $(H6 $(IX reverse) Reversing the elements) + + + $(H6 $(IX reverse(反转)) 反转元素) + + + + + $(H6 $(IX sort) Sorting the elements) + + + $(H6 $(IX sort(排序)) 排序元素) + + + + + $(H6 $(IX ~, concatenation) $(IX concatenation, array) Combining arrays) + + + $(H6 $(IX ~, 连接) $(IX 连接, 数组) 连接数组) + + + + + $(H6 $(IX ~=) $(IX append, array) $(IX add element, array) Adding elements to dynamic arrays) + + + $(H6 $(IX ~=) $(IX 附加, 数组) $(IX 添加元素, 数组) 给动态数组添加元素) + + + + + $(H6 Construction) + + + $(H6 构造函数) + + + + + $(H6 Definition) + + + $(H6 定义) + + + + + $(H6 Destruction) + + + $(H6 析构函数) + + + + + $(H6 Member functions) + + + $(H6 成员函数) + + + + + $(H6 Operator overloading) + + + $(H6 运算符重载) + + + + + $(HILITE final) int func() { $(CODE_NOTE Recommended) + + + $(HILITE final) int func() { $(CODE_NOTE 推荐) + + + + + $(I ($(B Note:) This is related to name scopes, as well as object lifetimes, which will be explained in $(LINK2 /ders/d.en/lifetimes.html, a later chapter.))) +) + + + $(I ($(B 注:) 这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的章节) 中解释。)) +) + + + + + $(I type_name)[$(I value_count)] $(I variable_name); +--- + + + $(I 类型名称)[$(I 值的个数)] $(I 变量名称); +--- + + + + + $(I value_type)[$(I key_type)] $(I associative_array_name); +--- + + + $(I 值类型)[$(I 键类型)] $(I 关联数组名); +--- + + + + + $(LI +Although some of the functions in Phobos modules will be easy to use with strings, library documentations are usually terse compared to tutorials. + + + $(LI +尽管 Phobos 模块中的一些函数易于处理字符串,但库文档通常比教程简短。 + + + + + $(LI +Another solution is to assign an empty array: + + + $(LI +另一种解法是用空数组赋值: + + + + + $(LI +Many other functions may be chained as well: + + + $(LI +许多别的函数可以嵌套使用: + + + + + $(LI +Since the initial value of an array is an empty array anyway, the following technique would achieve the same result: + + + $(LI +由于数组的初始值无论如何都是一个空数组,那么下面的技术将得到一样的结果: + + + + + $(LI +The $(C .keys) property returns a slice (i.e. dynamic array) that includes all of the keys of the associative array. + + + $(LI +$(C .keys) property(属性)返回一个切片(即 动态数组),它包含了关联数组全部的键。 + + + + + $(LI +The explanations are included as code comments: + + + $(LI +解释包含在代码说明中: + + + + + $(LI +The goal is to store multiple grades per student. + + + $(LI +目标是存储每个学生的多个成绩。 + + + + + $(LI +There are three mistakes (bugs) in this program. + + + $(LI +程序有三个错误(bugs)。 + + + + + $(LI 类是引用类型。The $(C new) 关键字构造一个匿名 $(I class 对象) 并返回一个 $(I class 变量)。 +) + + + $(LI 类是引用类型。 $(C new) 关键字构造一个匿名 $(I class 对象) 并返回一个 $(I class 变量)。 +) + + + + + $(LI $(B The numbers start with zero:) Although humans assign numbers to items starting with 1, the numbers in arrays start at 0. + + + $(LI $(B 从零开始编号:) 虽然人们习惯于从 1 开始给项目分配编号,但数组是从 0 开始的。 + + + + + $(LI $(B Two different uses of the $(C[]) characters:) Don't confuse the two separate uses of the $(C []) characters. + + + $(LI $(B$(C[]) 的两种不同用法:) 不要混淆 $(C []) 的两种独特用法。 + + + + + $(LI $(C catch) and $(C finally) cannot be used without a $(C try) block.) + + + $(LI 没有一个 $(C try) 块, $(C catch) 和 $(C finally) 不能使用。) + + + + + $(LI $(C scope(failure)): the expression is executed only if the scope is being exited due to an exception) +) + + + $(LI $(C scope(failure)):表达式只在因出现异常而退出作用域时被执行。) +) + + + + + $(LI $(C scope(success)): the expression is executed only if the scope is being exited successfully) + + + $(LI $(C scope(success)):表达式只在成功退出作用域时被执行。) + + + + + $(LI $(IX .byKey) $(C .byKey) provides access to the keys without copying them; we will see how $(C .byKey) is used in $(C foreach) loops in the next chapter.) + + + $(LI $(IX .byKey) $(C .byKey) 提供对键的直接访问;在下一章我们将看到在 $(C foreach) 循环中如何使用 $(C .byKey) 。) + + + + + $(LI $(IX .byKeyValue) $(C .byKeyValue) provides access to the key-value pairs without copying them.) + + + $(LI $(IX .byKeyValue) $(C .byKeyValue) 提供对键值对的直接访问。) + + + + + $(LI $(IX .byValue) $(C .byValue) provides access to the values without copying them.) + + + $(LI $(IX .byValue) $(C .byValue) 提供对值的直接访问。) + + + + + $(LI $(IX .clear) $(C .clear) removes all elements.) + + + $(LI $(IX .clear) $(C .clear) 移除全部元素。) + + + + + $(LI $(IX .get) $(C .get) returns the value if it exists, the default value otherwise.) + + + $(LI $(IX .get) $(C .get) 值存在即返回相应值,否则返回默认值。) + + + + + $(LI $(IX .keys) $(C .keys) returns a copy of all keys as a dynamic array.) + + + $(LI $(IX .keys) $(C .keys) 返回全部键的动态数组副本。) + + + + + $(LI $(IX .length) $(C .length) returns the number of key-value pairs.) + + + $(LI $(IX .length) $(C .length) 返回键值对的个数。) + + + + + $(LI $(IX .rehash) $(C .rehash) may make the array more efficient in some cases, such as after inserting a large number of key-value pairs.) + + + $(LI $(IX .rehash) $(C .rehash) 在一些例子中可以让数组更有效率,比如在插入大量的键值对之后。) + + + + + $(LI $(IX .remove) $(C .remove) removes the specified key and its value from the array.) + + + $(LI $(IX .remove) $(C .remove) 从数组中移除指定的键和值。) + + + + + $(LI $(IX .sizeof, associative array) $(C .sizeof) is the size of the array $(I reference) (it has nothing to do with the number of key-value pairs in the table and is the same value for all associative arrays).) + + + $(LI $(IX .sizeof, associative array) $(C .sizeof) 数组$(I 引用)大小(它不受表中键值对个数的影响,对所有的关联数组来说值都是一样的)。) + + + + + $(LI $(IX .values) $(C .values) returns a copy of all values as a dynamic array.) + + + $(LI $(IX .values) $(C .values) 返回全部值的动态数组副本。) + + + + + $(LI Assigning an empty associative array.) + + + $(LI 用一个空的关联数组赋值。) + + + + + $(LI Assignment associates a variable with an object. + + + $(LI 赋值会把一个变量与一个对象相关联。 + + + + + $(LI Class variables that are not associated with any object are $(C null). + + + $(LI 不与任何对象相关联的类变量为 $(C null)。 + + + + + $(LI Classes and structs share common features but have big differences. + + + $(LI 类和结构虽然有共同特点,但还是有很大的差异。 + + + + + $(LI Classes are reference types. + + + $(LI 类是引用类型。 + + + + + $(LI Removing them one-by-one from the associative array.) + + + $(LI 从关联数组中逐个移除它们。) + + + + + $(LI Similar to the previous method, assigning the array's $(C .init) property. + + + $(LI 与前一方法相似,用数组的 $(C .init) 属性赋值。 + + + + + $(LI Some of the variables that these blocks need may not be accessible within these blocks: + + + $(LI 属于块的某些变量,块范围内有可能访问不到: + + + + + $(LI The act of copying associates an additional variable with an object. + + + $(LI 复制操作将增加一个与对象关联的变量。 + + + + + $(LI There is a $(C d) at the end of the literal $(STRING "résumé"d), specifying its type as an array of $(C dchar)s.) +) +) + + + $(LI 有一个 $(C d) 在字面量 $(STRING "résumé"d) 的末尾,指定了它的类型是一个 $(C dchar) 型数组。) +) +) + + + + + $(LI This program uses two indexes to make a slice: + + + $(LI 这段程序使用两个索引值来生成一个切片: + + + + + $(LI Writing all of potentially unrelated expressions in the single $(C finally) block at the bottom separates those expressions from the actual code that they are related to. + + + $(LI 把所有可能无关联的表达式写在底部单独的 $(C finally) 块,就可以分离那些有关联的可执行代码。 + + + + + $(LI 一个类的析构函数不得访问由垃圾回收器管理的成员。这是因为垃圾回收器没有必要保证该对象及其成员按任何特定顺序终结。当析构函数执行时,全部成员应该已经终结。) + + + $(LI 一个类的析构函数不得访问由垃圾回收器管理的成员。这是因为垃圾回收器没有被要求保证该对象及其成员按任何特定顺序终结。当析构函数执行时,全部成员应该已经终结。) + + + + + $(LI 类的析构函数不必分配新的内存给垃圾回收器。这是因为垃圾回收器没有必要保证在垃圾回收周期内能分配新的对象。) + + + $(LI 类的析构函数一定不分配由垃圾回收器管理的新内存。这是因为垃圾回收器没有被要求保证在垃圾回收周期内能分配新的对象。) + + + + + $(OL +$(LI The type of string literals like $(STRING "hello") is $(C string), not $(C char[]), so they are immutable. + + + $(OL +$(LI 像 $(STRING "hello") 这样的字符串字面量的类型是 $(C string),而不是 $(C char[]),因此它们是 immutable。 + + + + + $(P +$(B Hint:) You may want to put the elements in separate arrays. + + + $(P +$(B 提示:) 你可以把元素放入单独的数组。 + + + + + $(P +$(B Reminder:) The element numbers of January and December are 0 and 11 respectively; not 1 and 12. + + + $(P +$(B 提醒:) 一月和十二月的元素位置数分别是 0 和 11;而不是 1 和 12。 + + + + + $(P +$(C .clear) removes all elements: +) + + + $(P +$(C .clear) 移除全部元素: +) + + + + + $(P +$(C is) specifies whether two class variables provide access to the same class object. + + + $(P +$(C is) 确定两个类变量是否提供对同一对象的访问。 + + + + + $(P +$(C readln()) can be used without a parameter. + + + $(P +$(C readln()) 没有参数也可以使用。 + + + + + $(P +$(C readln()) is more suitable when reading strings. + + + $(P +$(C readln()) 更适合读取字符串。 + + + + + $(P +$(C readln()) stores the new-line character as well. + + + $(P +$(C readln()) 也存储换行符。 + + + + + $(P +$(C scope) statements can be specified as blocks as well: +) + + + $(P +$(C scope) 语句也可以像块一样使用: +) + + + + + $(P +$(C std.algorithm.reverse) reverses the elements in place (the first element becomes the last element, etc.): +) + + + $(P +$(C std.algorithm.reverse) 反转元素的位置(第一个元素成为最后一个元素,以此类推): +) + + + + + $(P +$(C std.algorithm.sort) can sort the elements of many types of collections. + + + $(P +$(C std.algorithm.sort) 可以对许多类型集合中的元素进行排序。 + + + + + $(P +$(I $(B Note:) An associative array that is defined without any element is $(LINK2 /ders/d.en/null_is.html, $(C null)), not empty. + + + $(P +$(I $(B 注:) 已定义的没有任何元素的关联数组是 $(LINK2 /ders/d.cn/null_is.html, $(C null)),而不是空。 + + + + + $(P +$(I $(B Note:) It is usually not proper to access members directly as in the code above. + + + $(P +$(I $(B 注:) 在上面的代码中,一般不这样直接访问成员。 + + + + + $(P +$(I $(B Note:) The indexes above are for demonstration purposes only; they are not stored in the computer's memory.) +) + + + $(P +$(I $(B 注意:) 上面的索引仅用于演示;并没有存储在计算机的内存中。) +) + + + + + $(P +$(I $(B Note:) The meaning of the assignment operation is completely different for dynamic arrays. + + + $(P +$(I $(B 注意:) 赋值运算符的含义与动态数组完全不同。 + + + + + $(P +$(I $(B Note:) Unicode does not define how the characters are ordered other than their Unicode codes. + + + $(P +$(I $(B 注:)除了 Unicode 编码顺序之外,Unicode 不定义字符的排列顺序。 + + + + + $(P +$(I $(B 注:) 在上面的代码中,一般不这样直接访问成员。若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的章节) 中解释。) +) + + + $(P +$(I $(B 注:) 在上面的代码中,一般不这样直接访问成员。若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的一章) 中解释。) +) + + + + + $(P +$(IX .init, clearing a variable) $(I $(B Note:) The $(C .init) property of any variable or type is the initial value of that type:) +) + + + $(P +$(IX .init, 清除一个变量) $(I $(B 注:) 任何变量或类型的 $(C .init) 属性是类型的初始值:) +) + + + + + $(P +$(IX :, associative array) Sometimes some of the mappings between the keys and the values are already known at the time of the definition of the associative array. + + + $(P +$(IX :, 关联数组) 有时候一些键与值的映射在定义关联数组的时候就已经明确。 + + + + + $(P +$(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D中,类提供 $(I 面向对象编程) (OOP) 模型。下面是OOP的主要方面: +) + + + $(P +$(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D语言中,类提供 $(I 面向对象编程) (OOP) 模型。下面是OOP的主要方面: +) + + + + + $(P +$(IX final) As overridable member functions have a runtime performance cost, without going into more detail, I recommend that you define all $(C class) functions that do not need to be overridden with the $(C final) keyword. + + + $(P +$(IX final) 由于可重写的成员函数有一个运行时性能消耗,在这儿不讨论更多细节,我推荐您定义全部没必要用 $(C final) 关键字重写的 $(C class) 成员函数。 + + + + + $(P +$(IX scalar) That definition can be read as $(I 5 double values). + + + $(P +$(IX 标量) 这个定义可以理解为 $(I 5 个 double 值)。 + + + + + $(P +$(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期结束时,类的析构函数并不执行。正如上面我们看到的,析构函数在一个垃圾回收周期内的未来某个时候执行。(通过这样的区分,更准确的说,类析构函数应该叫 $(I 终结函数)) 。 +) + + + $(P +$(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期结束时,类的析构函数并不执行。正如上面我们看到的,析构函数在一个垃圾回收周期内的未来某个时候执行。(通过这样的区分,更准确的说,类的析构函数应该叫 $(I 终结函数)) 。 + + + + + $(P +Accordingly, the equivalent of the five separate variables above can be defined as an array of five values using the following syntax: +) + + + $(P +因此,上面的五个独立的变量可以等效的使用下面的语法定义为含五个值的数组: +) + + + + + $(P +Additionally, as a consequence of their ASCII code values, all of the latin uppercase letters are sorted before all of the lowercase letters. + + + $(P +另外,受 ASCII 编码值的影响,所有拉丁大写字母都排在小写字母的前面。 + + + + + $(P +Additionally, the length of dynamic arrays can be changed by assigning a value to this property: +) + + + $(P +另外,通过对这个 property 指定一个值就可以修改动态数组的 length: +) + + + + + $(P +All of the elements of the $(C values) array above are initialized to $(C double.nan): +) + + + $(P +上面 $(C values) 数组的所有元素都初始化为 $(C double.nan): +) + + + + + $(P +Although "résumé" contains six $(I letters), the length of the $(C string) is the number of UTF-8 code units that it contains: +) + + + $(P +虽然 "résumé" 包含6个$(I 字母),但$(C 字符串)的长度是它包含的 UTF-8 编码单元的个数: +) + + + + + $(P +Although member functions are defined and used the same way as structs, there is an important difference: Class member functions can be and by-default are $(I overridable). + + + $(P +虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是 $(I 可重写的) 。 + + + + + $(P +Although the syntax makes it look as if a member of the $(I variable) is being accessed, it is actually the member of the $(I object). + + + $(P +虽然语法上看起来像访问 $(I 变量) 的成员,实际上是 $(I 对象) 的成员。 + + + + + $(P +Although these statements are closely related to exceptions, they can be used without a $(C try-catch) block. + + + $(P +虽然这些语句只在特殊情况下使用,但是没有 $(C try-catch) 块也能用。 + + + + + $(P +An index need not be a constant value; the value of a variable can also be used as an index, making arrays even more useful. + + + $(P +索引不必是恒定值;变量的值也能用作索引,这会让数组更有用。 + + + + + $(P +Another difference from structs is that some member functions are automatically inherited from the $(C Object) class. + + + $(P +与结构不同的是一些成员函数自动继承自 $(C Object) 类。 + + + + + $(P +Arrays are containers where the elements are placed side by side in the computer's memory. + + + $(P +数组是容器,其中的元素在计算机的内存中是逐个放置的。 + + + + + $(P +Arrays are useful in such cases: the array feature allows us to define a single variable that stores multiple values together. + + + $(P +数组在这种情况下是有用的:数组功能允许我们定义一个把多个值存储到一起的单个变量。 + + + + + $(P +Arrays have properties as well, of which we will see only $(C .length) here. + + + $(P +数组也有 properties(属性),在这儿我们只会看到 $(C .length)。 + + + + + $(P +Arrays provide convenience operations that apply to all of their elements. + + + $(P +数组提供了适用于所有元素的方便操作。 + + + + + $(P +As a result, the objects that are associated with $(C var1) and $(C var2) are different. + + + $(P +结果是,$(C var1) 和 $(C var2) 相关联的对象是不同的。 + + + + + $(P +As an example, let's write the function above with a $(C scope(failure)) statement: +) + + + $(P +例如,让我们用 $(C scope(failure)) 语句写一下面的函数: +) + + + + + $(P +As it is indicated in their documentation, the return types of $(C indexOf()) and $(C lastIndexOf()) are not $(C int) nor $(C size_t), but $(C ptrdiff_t). + + + $(P +与它们的文档中所指出的一样,$(C indexOf()) 和 $(C lastIndexOf()) 的返回类型既不是 $(C int) 也不是 $(C size_t),而是 $(C ptrdiff_t)。 + + + + + $(P +As seen in the outputs, the blocks of the $(C scope) statements are executed in reverse order. + + + $(P +在输出中我们看到,$(C scope) 语句块是按相反的顺序执行的。 + + + + + $(P +As we have seen in the previous chapter, expressions that must always be executed are written in the $(C finally) block, and expressions that must be executed when there are error conditions are written in $(C catch) blocks. + + + $(P +在前面的章节我们已经看到,写在 $(C finally) 块里的表达式一定总被执行。当有错误条件的时候,写在 $(C catch) 块里的表达式总被执行。 + + + + + $(P +As with structs, the name of the constructor is $(C this). + + + $(P +与结构一样,构造函数的名称是 $(C this) 。 + + + + + $(P +As with structs, the name of the destructor is $(C ~this): +) + + + $(P +像结构一样,析构函数的名称是 $(C ~this): +) + + + + + $(P +As you can see, the $(C monthDays) array is defined and initialized at the same time. + + + $(P +正如你所看到的,$(C monthDays) 数组在定义的时候就已初始化。 + + + + + $(P +Assigning to a class variable disassociates that variable from its current object and associates it with a new object. + + + $(P +给类变量赋值,会解除变量与当前对象的关联,并关联到一个新对象。 + + + + + $(P +Associative arrays allow indexing not only using integers but also using any other type. + + + $(P +关联数组允许索引不只是整型而是任何类型。 + + + + + $(P +Associative arrays are a feature that is found in most modern high-level languages. + + + $(P +关联数组是大多数现代高级编程语言所具有的功能。 + + + + + $(P +Associative arrays are implemented in D using a $(I hash table). + + + $(P +在D语言中,关联数组是一个 $(I hash 表) 实现。 + + + + + $(P +Associative arrays make it easy to access the grades by the name of the student as in $(C grades["emre"]). + + + $(P +在 $(C grades["emre"]) 中通过学生姓名关联数组让它很容易访问到成绩。 + + + + + $(P +Assume that an associative array is used for storing student grades. + + + $(P +假设使用关联数组来存储学生成绩。 + + + + + $(P +Because Unicode adopts the letters of the basic Latin alphabet from the ASCII table, the strings that contain only the letters of the ASCII table will always be ordered correctly. + + + $(P +由于 Unicode 采用来自于 ASCII 表的基本拉丁字母,仅包含 ASCII 字母的字符串将会正确排序。 + + + + + $(P +Because all of the Unicode characters of "résumé" can be represented by a single $(C wchar) or $(C dchar), the last two lengths are equal to the number of characters. + + + $(P +因为所有的“résumé”的 Unicode 字符都能用单个 $(C wchar) 或者 $(C dchar) 表示,所以最后两个的长度与字符个数是一致的。 + + + + + $(P +Because classes are reference types, defining a new class variable as a copy of another makes two variables that provide access to the same object. + + + $(P +因为类是引用类型,定义一个新的类变量做为另一个副本,将产生两个访问同一对象的变量。 + + + + + $(P +Because each character has a unique code, every letter variant is different from the others. + + + $(P +因为每个字母都有一个唯一的编码,每个字母变体都与其它的不同。 + + + + + $(P +Because strings are arrays (and as a corollary, $(I ranges)), the functions of the $(C std.array), $(C std.algorithm), and $(C std.range) modules are very useful with strings as well. + + + $(P +因为字符串是数组(进一步而言,是 $(I range)),所以 $(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块中的函数对于字符串也都非常有用。 + + + + + $(P +Being character arrays, strings can contain control characters like $(STRING '\n') as well. + + + $(P +作为字符数组,字符串能包含像 $(STRING '\n') 这样的控制字符。 + + + + + $(P +Both $(C readf()) and $(C formattedRead()) $(I return) the number of items that they could parse and convert successfully. + + + $(P +$(C readf()) 和 $(C formattedRead()) 函数 都可以$(I 返回)成功解析及转换的项目个数。 + + + + + $(P +Both of the arrays that we have defined above are fixed-length arrays because their element counts are specified as 5 and 12 at the time when the program is written. + + + $(P +上面我们定义的数组都为定长数组,因为元素的个数在写程序时已指定为 5 和 12。 + + + + + $(P +Both of the variables above provide access to the same object. + + + $(P +上面的两个变量都提供对同一对象的访问。 + + + + + $(P +Classes are defined by the $(C class) keyword instead of the $(C struct) keyword: +) + + + $(P +类由 $(C class) 关键字定义而不是 $(C struct) 关键字: +) + + + + + $(P +Copying affects only the variables, not the object. + + + $(P +复制只影响变量,而不是对象。 + + + + + $(P +Data structures that bring elements of a certain type together are called $(I containers). + + + $(P +聚集特定类型元素的数据结构称为 $(I 容器)。 + + + + + $(P +Defining dynamic arrays is simpler than defining fixed-length arrays because omitting the length makes a dynamic array: +) + + + $(P +因为省略了长度,所以定义动态数组比定长数组简单: +) + + + + + $(P +Fix the bugs of this program and make it work as expected: +) + + + $(P +请修复程序的 bug,让它按预期的方式执行: +) + + + + + $(P +Fixed-length arrays are also known as static arrays. + + + $(P +定长数组也被称为静态数组。 + + + + + $(P +For example, an associative array that maps day names of type $(C string) to day numbers of type $(C int) can be defined like this: +) + + + $(P +例如,下面这个关联数组这样定义,它把日期名的类型 $(C string) 映射到日期的数字类型 $(C int) : +) + + + + + $(P +For example, the following code that tries to capitalize the first letter of a $(C string) would cause a compilation error: +) + + + $(P +例如,下面这段代码尝试着修改 $(C string) 的首字母为大写,这将引发一个编译错误: +) + + + + + $(P +For example, when the following numbers are entered, +) + + + $(P +例如,当输入了下面的数字, +) + + + + + $(P +For plain arrays, index values are not stored at all. + + + $(P +对于简单数组,索引值根本就没有存储。 + + + + + $(P +For that syntax to work, a constructor must be defined explicitly by the programmer. + + + $(P +那样的语法要通过编译,就需要程序员显式的定义构造函数。 + + + + + $(P +Here is a program that prints the Turkish names of colors that are specified in English: +) + + + $(P +这是一段打印英文颜色的土耳其语表达的程序,它的键指定为英文: +) + + + + + $(P +Here is another function that tests all three of these statements: +) + + + $(P +这儿是另一个函数,来测试全部三个语句: +) + + + + + $(P +How can you solve this problem? + + + $(P +怎么解决这个问题? + + + + + $(P +I will start using that form after introducing the $(C string) type below. + + + $(P +在介绍了下面的 $(C string) 类型之后,我们将开始使用这种格式。 + + + + + $(P +If an exception is thrown, the output includes the $(C scope(exit)) and $(C scope(failure)) expressions: +) + + + $(P +如果抛出异常, 输出包括 $(C scope(exit)) 和 $(C scope(failure)) 表达式: +) + + + + + $(P +If no exception is thrown, the output of the function includes only the $(C scope(exit)) and $(C scope(success)) expressions: +) + + + $(P +如果没有抛出异常, 函数的输出只包括 $(C scope(exit)) 和 $(C scope(success)) 表达式: +) + + + + + $(P +If the array sizes are not equal, the program is terminated with an error during assignment: +) + + + $(P +如果数组大小不一致,会导致赋值期间出错而终止程序: +) + + + + + $(P +If there is no other class variable that still provides access to the object that has been disassociated from, then that object is going to be destroyed some time in the future by the garbage collector. + + + $(P +如何没有别的类变量能访问已解除关联对象,那该对象将由垃圾回收器在将来某个时候销毁。 + + + + + $(P +In any case, keep in mind that the use of $(C dchar[]) and $(C dstring) does not solve all of the problems of manipulating Unicode characters. + + + $(P +无论如何,请记住使用 $(C dchar[]) 和 $(C dstring) 并不能解决所有的操作 Unicode 字符的问题。 + + + + + $(P +In order to differentiate the variables in the exercise of the previous chapter, we had to append an underscore and a number to their names as in $(C value_1). + + + $(P +为了与前一章练习中的变量有所区别,我们给变量的名字加上了下划线与数字,像 $(C value_1) 这样。 + + + + + $(P +In summary, the definition of an array variable consists of the type of the values, the number of values, and the name of the variable that refers to the array of values: +) + + + $(P +总之,数组变量的定义包括值的类型、 值的个数和涉及数组值的变量名称: +) + + + + + $(P +In the code above, $(C variable2) is being initialized by $(C variable1). + + + $(P +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。 + + + + + $(P +It is not possible to add elements to fixed-length arrays: +) + + + $(P +不可能给定长数组添加元素: +) + + + + + $(P +It is possible to define variables with the $(C auto) keyword, which we will see in a later chapter: +) + + + $(P +也可以使用 $(C auto) 关键字定义变量,这个我们将在后面的一章中看到: +) + + + + + $(P +Just like copying, assignment affects only the variables. + + + $(P +就像复制,赋值只影响变量。 + + + + + $(P +Key-value pairs can be removed by using $(C .remove()): +) + + + $(P +通过 $(C .remove()) 可以移除键值对: +) + + + + + $(P +Let's consider the following code that we have seen previously in the $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types chapter): +) + + + $(P +让我们考虑一下下面我们以前在 $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型和引用类型章节) 看到过的代码: +) + + + + + $(P +Let's now revisit the exercise with the five values and write it again by using an array: +) + + + $(P +现在我们重温一下使用五个值的例子,用数组重写一下它: +) + + + + + $(P +Like every variable in D, the elements of arrays are automatically initialized. + + + $(P +像在 D 语言中的每个变量,数组的元素自动初始化。 + + + + + $(P +Obviously, the values of the elements can be changed later during the execution of the program. + + + $(P +显然,元素的值可以在以后程序执行期间发生变化。 + + + + + $(P +On the other hand, associative arrays do store both the keys and the values of elements. + + + $(P +另一方面,关联数组既存储键又存储元素值。 + + + + + $(P +Once a line is read from the input or from any other source, it is possible to parse and convert separate data that it may contain with $(C formattedRead()) of the $(C std.format) module. + + + $(P +一但从输入流或其它任何来源中读取了一行字符,就可以用 $(C std.format) 模块的 $(C formattedRead()) 函数来解析并转换它所包含的独立数据。 + + + + + $(P +One way of terminating the standard input stream in a terminal is pressing Ctrl-D under Unix-based systems and Ctrl-Z under Windows systems. + + + $(P +在终端结束标准输入流的方法随系统而不同,在 Unix 系统下按 Ctrl-D,在 Windows 系统下按 Ctrl-Z。 + + + + + $(P +Only the index values between zero and one less than the length of the array are valid. + + + $(P +只有与数组长度的差值在 0 与 1 之间的索引才有效。 + + + + + $(P +Other than the fact that $(C opAssign) cannot be overloaded for classes, operator overloading is the same as structs. + + + $(P +虽然 $(C opAssign) 不能被类重载,但与结构一样,可以实现运算符重载。 + + + + + $(P +Please note the two differences in the new code: +) +$(OL +$(LI The type of the string is $(C dchar[]). + + + $(P +请注意新代码的两个不同: +) +$(OL +$(LI 字符串的类型是 $(C dchar[])。 + + + + + $(P +Same as structs, the members are accessed by the $(I dot) operator: +) + + + $(P +与结构一样,用 $(I 点) 运算符访问成员: +) + + + + + $(P +Similarly, $(C char[]) cannot be used where a $(C string) is needed. + + + $(P +同样的,$(C char[]) 不能被用到需要 $(C string) 的地方。 + + + + + $(P +Similarly, an $(C immutable) copy of an object can be provided by a member function appropriately named $(C idup()): +) + + + $(P +同样的,可以由命名为 $(C idup()) 的适当的成员函数提供对象的 $(C immutable) 副本: +) + + + + + $(P +Since $(C char[]) is mutable and $(C string) is not, there is a mismatch. + + + $(P +由于 $(C char[]) 可变而 $(C string) 不可变,两者不批配。 + + + + + $(P +Since it is more beneficial for you to debug the third mistake yourself, I would like you to first run the program after fixing the previous two bugs. + + + $(P +自行调试第三个错误对你来说更有益,建议你在修复了前两个 bug 之后先运行一下程序。 + + + + + $(P +Since no object gets copied, the postblit function $(C this(this)) is not available for classes. + + + $(P +由于没有复制对象, postblit 函数 $(C this(this)) 不能用于类变量。 + + + + + $(P +Since the array does not contain a value for the key $(STRING "purple"), $(C .get()) returns -1: +) + + + $(P +由于键 $(STRING "purple") 的值不在数组中,$(C .get()) 返回 -1: +) + + + + + $(P +Since the objects of $(C myKing) and $(C yourKing) variables are different, the $(C !is) operator returns $(C true). + + + $(P +由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。 + + + + + $(P +Since they are actually arrays, all of the array operations can be applied to strings as well. + + + $(P +由于它们实际上是数组,所有数组的操作也都能应用到字符串上。 + + + + + $(P +Sometimes it makes sense to use a default value if a key does not exist in the associative array. + + + $(P +有时候在关联数组中,为不存在的键使用一个默认值是有道理的。 + + + + + $(P +Sometimes the desired values of the elements are known at the time when the array is defined. + + + $(P +有时在数组定义时元素的期望值是已知的。 + + + + + $(P +Such control characters as well as all whitespace characters at both ends of strings can be removed by $(C std.string.strip): +) + + + $(P +像字符串两端的空白字符这样的控制字符能被 $(C std.string.strip) 移除: +) + + + + + $(P +That also happened when reading a value from the input: +) + + + $(P +从输入流中读取值时也发生了: +) + + + + + $(P +That expression can be read as $(I the element with the number 0 of the array named values). + + + $(P +这个表达式可以理解为 $(I 数组 values 位置 0 处的元素)。 + + + + + $(P +That function first modifies the reference parameter and then reverts this modification when an exception is thrown. + + + $(P +上面这个函数首先修改引用参数,当出现异常时再恢复修改。 + + + + + $(P +That solution may be slow especially for large arrays. + + + $(P +对于大型数组这恐怕会非常慢。 + + + + + $(P +The $(C dayNumbers) variable above is an associative array that can be used as a table that provides a mapping from day names to day numbers. + + + $(P +上面的 $(C dayNumbers) 变量是一个关联数组,它能用来作为从日期名映射到日期数字的表。 + + + + + $(P +The $(C dup()) member function makes a new object by taking advantage of the constructor of $(C Foo) and returns the new object. + + + $(P +$(C dup()) 成员函数利用 $(C Foo) 的构造函数,创建并返回新的对象。 + + + + + $(P +The $(C in) operator determines whether a given key exists in the associative array: +) + + + $(P +$(C in) 运算符确定一个给定的键是否存在于关联数组中: +) + + + + + $(P +The $(C new) keyword constructs an anonymous class object. + + + $(P + $(C new) 关键字构造了一个匿名的类对象。 + + + + + $(P +The $(C scope(failure)) statement above ensures that the $(C r -= addend) expression will be executed if the function's scope is exited due to an exception. + + + $(P +上面的 $(C scope(failure)) 确保 $(C r -= addend) 表达式在因异常退出时被执行。 + + + + + $(P +The $(C scope) statements have similar functionality with the $(C catch) and $(C finally) scopes but they are better in many respects. + + + $(P +$(C scope) 语句与 $(C catch) 和 $(C finally) 有相似功能,但在许多方面表现的更好。 + + + + + $(P +The $(C strip()) expression above returns a new string that does not contain the trailing control characters. + + + $(P +上面的 $(C strip()) 表达式返回一个不包含尾随控制符的新字符串。 + + + + + $(P +The $(C ~) operator creates a new array by combining two arrays. + + + $(P + $(C ~) 运算符通过连接两个数组从而创建一个新数组。 + + + + + $(P +The $(C ~=) operator adds new elements to the end of a dynamic array: +) + + + $(P +$(C ~=) 运算符是指在动态数组的尾部添加新的元素: +) + + + + + $(P +The $(C ~=) operator cannot be used when the left-hand side array is a fixed-length array: +) + + + $(P +左侧数组是定长数组时 $(C ~=) 运算符不能使用: +) + + + + + $(P +The Enter key that the user presses after the name does not terminate the input. + + + $(P +用户在名字之后按的 Enter 键并没有结束输入。 + + + + + $(P +The assignment above makes $(C variable1) leave its object and start providing access to $(C variable2)'s object. + + + $(P +上面的赋值让 $(C variable1) 离开其对象并且开始提供对 $(C variable2) 的对象的访问。 + + + + + $(P +The assignment operator copies all of the elements from the right-hand side to the left-hand side: +) +--- + + + $(P +赋值运算符将所有元素从右手侧复制到左手侧: +) +--- + + + + + $(P +The behavior of assignment cannot be changed for classes. + + + $(P +赋值操作不能改变类。 + + + + + $(P +The definition of array variables is very similar to the definition of normal variables. + + + $(P +数组变量的定义与正常变量的定义非常相似。 + + + + + $(P +The element at index 0 has the value 31 (number of days in January); the element at index 1 has the value of 28 (number of days in February), etc. +) + + + $(P +索引 0 处元素的值为 31(一月的天数);索引1 处元素的值为 28(二月的天数)依次类推。 +) + + + + + $(P +The exclamation mark appears after those characters instead of being printed right after the name. + + + $(P +感叹号出现在了那些字符之后,而不是在名字之后立即输出。 + + + + + $(P +The fact that plain arrays provide access to their values through index numbers can be described as an $(I association) of indexes with values. + + + $(P +事实上简单数组通过索引访问值可以被描述为索引与值的$(I 关联) 。 + + + + + $(P +The first line above is the definition of a variable which stores a single value, just like the variables that we have defined so far. + + + $(P +上面的第一行定义了一个存储单个值的变量,就像我们以前定义过的变量那样。 + + + + + $(P +The first line above removes the key-value pair "Tuesday" / $(C 1). + + + $(P +上面第一行移除了键值对 "Tuesday" / $(C 1). + + + + + $(P +The following code makes use of $(C dup()) to create a new object: +) + + + $(P +下面的代码演示 $(C dup()) 创建一个新的对象的用法: +) + + + + + $(P +The grades can also be assigned in one go with an array literal: +) + + + $(P +成绩也可以作为数组文本一次性赋值: +) + + + + + $(P +The high performance of hash tables comes at the expense of storing the elements in an unordered way. + + + $(P +hash 表的高性能的代价是元素的存储是无序的。 + + + + + $(P +The keys of associative arrays can be of any type, including user-defined $(C struct) and $(C class) types. + + + $(P +关联数组的键可以是任何类型,包括用户定义的 $(C struct) 和 $(C class) 类型。 + + + + + $(P +The length of associative arrays cannot be specified when defined. + + + $(P +定义时,不能指定关联数组的长度。 + + + + + $(P +The length of such an array can increase or decrease during the execution of the program. + + + $(P +在程序的执行过程中这种数组的长度可以增加或减少。 + + + + + $(P +The name of a specific day can be accessed by its index in that array: +) + + + $(P +在数组里通过索引访问特定的英文日期名: +) + + + + + $(P +The number of an element is called its $(I index) and the act of accessing an element is called $(I indexing). + + + $(P +元素的位置数称为 $(I index),访问元素的行为称为$(I 索引)。 + + + + + $(P +The optional character that is specified after string literals determines the type of the elements of the string: +) + + + $(P +在字符串字面量之后指定的可选字符决定了字符串的元素类型: +) + + + + + $(P +The output indicates that the values 0 and 1 correspond to keys "Monday" and "Tuesday", respectively: +) + + + $(P +下面的输出表明了元素值 0 和 1 分别对应着键 “Monday” 和 “Tuesday”: +) + + + + + $(P +The output: +) + + + $(P +输出: +) + + + + + $(P +The program can now be compiled and will print the modified string: +) + + + $(P +现在程序能通过编译并且打印修改后的字符串: +) + + + + + $(P +The solution here is to take a copy of the immutable string by using the $(C .dup) property: +) + + + $(P +此处的解决办法是通过 $(C .dup) property(属性)得到一个不可变字符串的副本: +) + + + + + $(P +The syntax of associative arrays is similar to the array syntax. + + + $(P +关联数组的语法与数组相似。 + + + + + $(P +The table grows automatically with each association. + + + $(P +随着每次的关联,该表会自动增长。 + + + + + $(P +The two 'e' characters do not replace the two 'é' characters; they replace single code units, resulting in an invalid UTF-8 encoding: +) + + + $(P +两个‘e’字符不能代替两个‘é’字符;用单字节编码单元替换后,结果就是得到一个无效的 UTF-8 编码: +) + + + + + $(P +The type of the elements of string literals like $(STRING "hello") is $(C char) and each $(C char) value represents a UTF-8 code unit. + + + $(P +像 $(STRING "hello") 这样的字符串字面量的元素类型是 $(C char),并且每个 $(C char) 值对应一个 UTF-8 编码单元。 + + + + + $(P +The type of the values can also be a user-defined type. + + + $(P +值的类型也可以是用户定义的类型。 + + + + + $(P +The value of $(C i) is 5 when the first $(C while) loop terminates, and that value is causing the logical expression of the second loop to be $(C false), which in turn is preventing the second loop to be entered. + + + $(P +当第一个 $(C while) 循环结束时 $(C i) 值为 5,该值导致第二个循环的逻辑表达式的值为 $(C false),是它阻止进入第二个循环。 + + + + + $(P +The variables of a container are called $(I elements). + + + $(P +容器的变量称为 $(I 元素)。 + + + + + $(P +There are surprises even when reading strings from the terminal. + + + $(P +从终端读字符串您会有一些惊奇。 + + + + + $(P +There are three $(I aliases) of the $(I immutable) versions of those types: $(C string), $(C wstring), and $(C dstring). + + + $(P +这些类型的 $(I immutable) 版本有三个$(I 别名):$(C string)、$(C wstring) 和 $(C dstring)。 + + + + + $(P +There are three string types that correspond to the three character types: $(C char[]), $(C wchar[]), and $(C dchar[]). + + + $(P +对应着三种字符类型,分别存在着三种字符串类型:$(C char[])、$(C wchar[]) 和 $(C dchar[])。 + + + + + $(P +There are two important points worth stressing here: +) + + + $(P +还有两点值得强调: +) + + + + + $(P +There can be only one value per key. + + + $(P +每个键有且仅有一个对应值。 + + + + + $(P +These operators operate on class variables. + + + $(P +这些运算符应用在类变量上。 + + + + + $(P +This chapter covers only some of the features of arrays. + + + $(P +本章仅涉及数组的部分功能。 + + + + + $(P +This method of defining variables individually does not scale to cases where even more variables are needed. + + + $(P +这种定义个别变量的方法不能扩展到需要更多变量的情况下。 + + + + + $(P +This simple definition may be misleading. + + + $(P +这个简单的定义可能是个误导。 + + + + + $(P +This time the compilation error is due to the combination of two factors: +) + + + $(P +这次的编译错误是因为两个因素的联合作用: +) + + + + + $(P +To see how this program is better than the previous one, imagine needing to read 20 values. + + + $(P +要看一看这个程序怎样才能比以前的更好,让我们设想需要读取 20 个值。 + + + + + $(P +Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially: +) + + + $(P +不像结构,构造函数参数按顺序分配给成员时,类没有自动构造对象: +) + + + + + $(P +Using the assignment operator is sufficient to build the association between a key and a value: +) + + + $(P +使用赋值运算符就足以构建键与值的关联: +) + + + + + $(P +We can make the following observations about the use of these blocks: +) + + + $(P +对于这些块的用法,我们可以作以下观测: +) + + + + + $(P +We have defined five variables in one of the exercises of the last chapter, and used them in certain calculations. + + + $(P +在上一章的一个练习中我们定义过五个变量,并用它们做过特定计算。 + + + + + $(P +We have seen that single quotes are used to define character literals. + + + $(P +我们已经看到单引号用于定义字符字面量。 + + + + + $(P +We have seen that some Unicode characters are represented by more than one byte. + + + $(P +我们已经知道一些 Unicode 字符串由多个字节表示。 + + + + + $(P +We have used comparison operators $(C <), $(C >=), etc. with integer and floating point values before. + + + $(P +我们以前把比较运算符 $(C <),$(C >=) 等等用于整型和浮点数值。 + + + + + $(P +We have used strings in many programs that we have seen so far. + + + $(P +迄今为至,我们已经看到,好多程序都用到了字符串。 + + + + + $(P +We may think of defining the variable as a $(C char[]) instead of the $(C string) alias but that cannot be compiled either: +) + + + $(P +我们可能想到把变量定义为 $(C char[]) 而不是别名 $(C string),但这也不能通过编译: +) + + + + + $(P +We saw in the $(LINK2 /ders/d.en/arrays.html, Arrays chapter) that plain arrays are containers that store their elements side-by-side and provide access to them by index. + + + $(P +在 $(LINK2 /ders/d.cn/arrays.html, 数组) 一章中我们已经看到简单数组作为容器逐个存储元素,按索引访问它们。 + + + + + $(P +When dealing with letters, symbols, and other Unicode characters directly, as in the code above, the correct type to use is $(C dchar): +) + + + $(P +当直接处理字母、符号或其它 Unicode 字符的时候,比如在上面代码中,应该使用正确的类型 $(C dchar): +) + + + + + $(P +When initializing arrays, it is possible to use a single value on the right-hand side. + + + $(P +当初始化数组时,也可以在右手侧使用单个值。 + + + + + $(P +When the actual object needs to be copied, the class must have a member function for that purpose. + + + $(P +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。 + + + + + $(P +When the input cannot be converted to $(C name) and $(C age), the program prints an error: +) + + + $(P +当输入不能转换到 $(C name) 和 $(C age) 时,程序将打印一个错误: +) + + + + + $(P +When the length of an array is specified when the program is written, that array is a $(I fixed-length array). + + + $(P +当数组的长度是在写程序时指定时,该数组就是一个$(I 定长数组)。 + + + + + $(P +When the type of $(C s) is $(C char[]), the type of the expression on the right-hand side of the assignment above is $(C char[]) as well. + + + $(P +当 $(C s) 的类型是 $(C char[]),上面右手侧赋值的表达式类型也是 $(C char[]), + + + + + $(P +When the value of $(C monthIndex) is 2, the expression above would print the value of $(C monthDays[2]), the number of days in March. + + + $(P +当 $(C monthIndex) 的值为 2,上面的表达式将输出 $(C monthDays[2]) 的值,即三月的天数。 + + + + + $(P +When the variables provide access to the same object, $(C is) returns $(C true): +) + + + $(P +当变量提供对同一对象的访问时,$(C is) 返回 $(C true): +) + + + + + $(P +You may find the $(C indexOf()) and $(C lastIndexOf()) functions useful to get the two indexes needed to produce a slice. + + + $(P +你或许会发现 $(C indexOf()) 和 $(C lastIndexOf()) 函数对生成切片所需要的两个索引很有用。 + + + + + $(P +have the program print the following: +) + + + $(P +程序会输出以下内容: +) + + + + + $(P +上面这个函数首先修改引用参数,当出现异常时再恢复修改。不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。$(I ($(B 注:) 这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的章节) 中解释。)) +) + + + $(P +上面这个函数首先修改引用参数,当出现异常时再恢复修改。不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。$(I ($(B 注:) 这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的一章) 中解释。)) +) + + + + + $(P +与结构不同的是一些成员函数自动继承自 $(C Object) 类。在 $(LINK2 /ders/d.cn/inheritance.html, 下一章节) 我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 +) + + + $(P +与结构不同的是一些成员函数自动继承自 $(C Object) 类。在 $(LINK2 /ders/d.cn/inheritance.html, 下一章) 中我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 +) + + + + + $(P +与结构的最大区别是结构是 $(I 值类型) 类是 $(I 引用类型)。下面的其它不同大部分与此有关。 +) + + + $(P +与结构的最大区别是结构是 $(I 值类型) 而类是 $(I 引用类型)。下面的其它不同大部分与此有关。 +) + + + + + $(P +例如,让我们用 $(C scope(failure)) 语句写一下面的函数: +) + + + $(P +例如,让我们用 $(C scope(failure)) 语句写一下下面的函数: +) + + + + + $(P +像结构一样,析构函数的名称是 $(C ~this): +) + + + $(P +像结构一样,析构函数的名称是 $(C ~this): +) + + + + + $(P +原因是, $(C ==) 运算符会查询对象成员的值,并尝试通过一个潜在的 $(C null) 变量访问成员,这将引发一个内存访问错误。因此,类变量必须总是通过 $(C is) 和 $(C !is) 运算符做比较。 +) + + + $(P +原因是 $(C ==) 运算符会查询对象成员的值,并尝试通过一个潜在的 $(C null) 变量访问成员,这将引发一个内存访问错误。因此,类变量必须总是通过 $(C is) 和 $(C !is) 运算符做比较。 +) + + + + + $(P +另一方面,类变量是用于访问类对象的一种语言特性。虽然语法上看起来是在类 $(I 变量) 上执行,但实际上调度了一个类 $(I object)。 +) + + + $(P +另一方面,类变量是用于访问类对象的一种语言特性。虽然语法上看起来是在类 $(I 变量) 上执行,但实际上调度了一个类 $(I 对象)。 +) + + + + + $(P +在 $(LINK2 /ders/d.cn/null_is.html, The $(CH4 null) 值和 $(CH4 is) 运算符章节), 本书已简要的提到,类变量可以是 $(C null)。换句话说,类变量可以不提供对任何对象的访问。类变量没有值本身;实际的类对象必须由 $(C new) 关键字构造。 +) + + + $(P +在 $(LINK2 /ders/d.cn/null_is.html, $(CH4 null) 值和 $(CH4 is) 运算符一章) 中, 本书已简要的提到,类变量可以是 $(C null)。换句话说,类变量可以不提供对任何对象的访问。类变量没有值本身;实际的类对象必须由 $(C new) 关键字构造。 +) + + + + + $(P +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。这俩变量可访问同一对象。The two variables start providing access to the same object. + + + $(P +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。这俩变量可访问同一对象。 + + + + + $(P +封装是通过 $(I 保护属性) 来实现, 在 $(LINK2 /ders/d.cn/encapsulation.html, 稍后的章节) 将会看到。继承是用于获取其它类型的 $(I 实现) 。$(LINK2 /ders/d.cn/inheritance.html, 多态性) 是从类之间抽象出部分代码,通过 $(I 接口) 来实现。 +) + + + $(P +封装是通过 $(I 保护属性) 来实现, 在 $(LINK2 /ders/d.cn/encapsulation.html, 稍后的一章) 中将会看到。继承是用于获取其它类型的 $(I 实现) 。$(LINK2 /ders/d.cn/inheritance.html, 多态性) 是从类之间抽象出部分代码,通过 $(I 接口) 来实现。 +) + + + + + $(P +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。为与数组兼容,该函数可以命名为 $(C dup()). + + + $(P +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。为与数组兼容,该函数可以命名为 $(C dup())。 + + + + + $(P +我们将在后面看到 $(LINK2 /ders/d.cn/memory.html, 内存管理章节),类的析构函数必须遵守以下规则: +) + + + $(P +我们将在后面 $(LINK2 /ders/d.cn/memory.html, 内存管理一章) 中看到,类的析构函数必须遵守以下规则: +) + + + + + $(P +由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。即使这两个对象由同一字符 $(C'♔') 参数构造,, 它们仍是两个单独的对象。 +) + + + $(P +由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。即使这两个对象由同一字符 $(C'♔') 参数构造, 它们仍是两个单独的对象。 +) + + + + + $(P +虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是 $(I 可重写的) 。在 $(LINK2 /ders/d.cn/inheritance.html,继承章节) 我们将看到相关内容。 +) + + + $(P +虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是 $(I 可重写的) 。在 $(LINK2 /ders/d.cn/inheritance.html,继承一章) 中我们将看到相关内容。 +) + + + + + $(P +让我们考虑一下下面我们以前在 $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型和引用类型章节) 看到过的代码: +) + + + $(P +让我们考虑一下下面的我们以前在 $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型和引用类型一章) 中看到过的代码: +) + + + + + $(P $(B Observations:) The value of $(C counter) determines how many times the loops are repeated (iterated). + + + $(P $(B 观测:) $(C counter) 的值决定了循环的重复次数(迭代)。 + + + + + $(PROBLEM +Browse the documentations of the $(C std.string), $(C std.array), $(C std.algorithm), and $(C std.range) modules. + + + $(PROBLEM +浏览 $(C std.string)、$(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块的文档。 + + + + + $(PROBLEM +How can all of the key-value pairs of an associative array be removed other than calling $(C .clear)? + + + $(PROBLEM +除 $(C .clear) 函数外还有什么方法能移除关联数组的全部键值对? + + + + + $(PROBLEM +Just like with arrays, there can be only one value for each key. + + + $(PROBLEM +与数组一样,每个键有且仅有一个值。 + + + + + $(PROBLEM +Read a line from the input and print the part between the first and last 'e' letters of the line. + + + $(PROBLEM +从输入中读取一行并打印该行的第一个和最后一个‘e’字母之间的部分。 + + + + + $(PROBLEM +The following is a program that does not work as expected. + + + $(PROBLEM +以下为预期不起作用的程序。 + + + + + $(PROBLEM +Write a program that asks the user how many values will be entered and then reads all of them. + + + $(PROBLEM +编写一个程序,要求用户输入多少值,然后全部读取。 + + + + + $(PROBLEM +Write a program that makes use of the $(C ~) operator: The user enters the first name and the last name, all in lowercase letters. + + + $(PROBLEM +写一个使用 $(C ~) 运算符的程序:让用户都以小写键入英文名字和姓氏, + + + + + $(PROBLEM +Write a program that reads numbers from the input, and prints the odd and even ones separately but in order. + + + $(PROBLEM +编写一个程序,从输入流中读取数字,并先后按顺序分别输出奇数和偶数。 + + + + + $(SHELL +Before: résumé +After : re�sueé $(SHELL_NOTE_WRONG INCORRECT) +) + + + $(SHELL +Before: résumé +After : re�sueé $(SHELL_NOTE_WRONG 不正确) +) + + + + + $(SHELL_NOTE (Let's assume that Enter is pressed a second time here)) +) + + + $(SHELL_NOTE (让我们假设在这儿第二次按了 Enter 键)) +) + + + + + $(SHELL_NOTE The input is not terminated although Enter has been pressed) + + + $(SHELL_NOTE 虽然按了 Enter 键,但输入没有中断) + + + + + $(SHELL_NOTE no new-line character) +) + + + $(SHELL_NOTE 没有换行符) +) + + + + + $(SHELL_NOTE_WRONG (one more before the exclamation mark)) +) + + + $(SHELL_NOTE_WRONG (在感叹号前还有一个)) +) + + + + + $(SHELL_NOTE_WRONG new-line character after the name) +! + + + $(SHELL_NOTE_WRONG 名字之后的换行符) +! + + + + + $(SHELL_NOTE_WRONG new-line character before the exclamation mark) +) + + + $(SHELL_NOTE_WRONG 感叹号前有换行符) +) + + + + + $(STRING 'a') is a character; $(STRING "a") is a string that contains a single character. + + + $(STRING 'a') 是一个字符;$(STRING "a") 是一个包含单字符的字符串。 + + + + + $(UL +$(LI $(C scope(exit)): the expression is always executed when exiting the scope, regardless of whether successfully or due to an exception) + + + $(UL +$(LI $(C scope(exit)):表达式总是在退出作用域时被执行, 无论是否成功或出现异常。) + + + + + ($(C .clear) is the most natural method.) There are at least three methods: + + + ($(C .clear) 是最自然的方法。) 至少还有三种方法: + + + + + (We will see D's $(I immutability) concept in later chapters.) +) + + + (在稍后的章节我们将看到 D 的$(I 不可变性)概念。) +) + + + + + (We will see functions in a later chapter.) +) + + + (在后面的章节我们将看到这些函数。) +) + + + + + (We will see user-defined types later.) For example: +) + + + (我们将在后面看到用户定义类型。)例如: +) + + + + + (anonymous MyClass object) variable1 variable2 + + + (匿名的 MyClass 对象) variable1 variable2 + + + + + ) +$(LI The $(C char[]) on the left-hand side is a slice, which, if the code compiled, would provide access to all of the characters of the right-hand side. + + + ) +$(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编译成功,它将会访问右手侧的全部字符。 + + + + + * associative array is being defined with an extra + + + *关联数组在定义时附加了额外的空格, + + + + + * see the 'foreach' loop in the next chapter. + + + *看到‘foreach’循环。 + + + + + * space in between to help distinguish the value type: */ + + + * 以易于区分值类型: */ + + + + + * the value type is int[], i.e. an array of ints. + + + * 值类型为 int[] ,即整型数组。 + + + + + * used for appending the new grade to that array: */ + + + * 将用于附加新成绩:*/ + + + + + /* 'foreach' is similar but superior to 'for'. + + + /* ‘foreach’类似于‘for’,但比它更有优势。 + + + + + /* Printing the grades of "emre": */ + + + /* 打印“emre”的成绩: */ + + + + + /* The array of ints that correspond to "emre" is being + + + /* 与“emre” 对应的整型数组 + + + + + /* The key type of this associative array is string and + + + /* 该关联数组的键类型为 string, + + + + + // false: overcast + + + // false:阴天 + + + + + // true : sunny + + + // true :晴天 + + + + + // Adding to the corresponding array, depending on + + + // 根据值的奇偶性,把值添加到 + + + + + // An array that holds the weather information of all + + + // 保存所有城市气象信息的 + + + + + // An array that holds the weights of a hundred boxes + + + // 保存一百个箱子重量的数组 + + + + + // Concatenate: + + + // 连接: + + + + + // Information about the students of a school + + + // 有关学校学生的信息 + + + + + // Printing the array elements in a loop + + + // 在循环中输出数组元素 + + + + + // Reading the value + + + // 从输入流中读取值 + + + + + // Reading the values in a loop + + + // 在循环中从输入流读取元素值 + + + + + // The counter is commonly named as 'i' + + + // 计数器通常命名作‘i’ + + + + + // The definition of a fixed-length array of five + + + // 定义了一个可包含五个 double + + + + + // The loop that calculates the fifths of the values would + + + // 经过计算的五个数值 + + + + + // The odds and evens arrays are sorted separately + + + // 分别排序奇偶数的数组 + + + + + // The special value of -1 breaks the loop + + + // 特殊值 -1 中断循环 + + + + + // The two arrays are then appended to form a new array + + + // 连接两个数组从而形成一个新数组 + + + + + // This is a definition. + + + // 这个是数组的定义。 + + + + + // This is an access. + + + // 这个是数组的访问。 + + + + + // This is another access. + + + // 这是另一个访问。 + + + + + // This variable is used as a loop counter + + + // 该变量用作循环计数器 + + + + + // Using dynamic arrays because it is not known how many + + + // 使用动态数组的原因是不知道有多少 + + + + + // associates value 0 with key "Monday" + + + // 关联值 0 与 键 “Monday” + + + + + // associates value 1 with key "Tuesday" + + + // 关联值 1 与 键 “Tuesday” + + + + + // be written similarly +} +--- + + + //将在循环中输出 +} +--- + + + + + // cities. + + + // 数组。 + + + + + // corresponds to December and sets its value to 31. + + + // 十二月的元素,并设置它的值为 31。 + + + + + // corresponds to January, the value of which is passed to + + + // 一月的元素,并把它的值传给 + + + + + // elements of type double + + + // 类型元素的数组 + + + + + // key "purple" does not exist in the table + + + //表中不存在 键 “purple” + + + + + // key "purple" exists in the table + + + // 键 “purple” 在表中 + + + + + // number if there is no remainder when divided by 2. + + + // 整除而没有余数,那这个数就是偶数。 + + + + + // of 12 elements. + + + // 12个元素的数组。 + + + + + // of days in each month. + + + // 每个月的天数。 + + + + + // values are going to be read from the input + + + // 值要从输入流中读取 + + + + + // whether the value is odd or even. + + + //相应的数组。 + + + + + // writeln. + + + // writeln 函数。 + + + + + A benefit of $(C scope(failure)) is the fact that the expression that reverts another expression is written close to it. + + + $(C scope(failure)) 的好处是靠近它的表达式可以还原已写的另一个表达式。 + + + + + A problem that this may cause is when we try to replace a two-code-unit character with a single-code-unit character: +) + + + 当我们试着用一个单字节字符替换一个双字节字符时问题就来了: +) + + + + + Accessing an array with an invalid index causes the program to be terminated with an error. + + + 访问具有无效索引的数组将导致出错从而终止程序。 + + + + + According to this definition, arrays are containers. + + + 根据定义,数组就是容器。 + + + + + Also note that the number of the month, which is in the range <span style="white-space: nowrap">1-12</span>, is converted to a valid array index in the range <span style="white-space: nowrap">0-11</span>. + + + 另外请注意, <span style="white-space: nowrap">1-12</span> 范围内的月份数字转换为了有效的数组索引范围 <span style="white-space: nowrap">0-11</span>。 + + + + + Also, unlike arrays, the elements of hash tables are not stored side-by-side. + + + 不像数组,hash 表的元素不是逐个排列存储的。 + + + + + Although it may seem syntactically that operations are being performed on a class $(I variable), the operations are actually dispatched to a class $(I object). + + + 虽然语法上看起来是在类 $(I 变量) 上执行,但实际上调度了一个类 $(I object)。 + + + + + Although simple, arrays are the most common data structure used to store a collection of values. + + + 虽然简单,但数组是用于存储值的集合中最常见的数据结构。 + + + + + Although this difference makes associative arrays use more memory, it also allows them to use $(I sparse) key values. + + + 虽然这种不同让关联数组使用更多内存,但它也允许它们使用$(I 稀疏)键值。 + + + + + An array that stores the names of the days of the week can be defined like this: +) + + + 一个存储了星期的英文名称的数组可以这样定义: +) + + + + + Any value that is entered outside of the <span style="white-space: nowrap">1-12</span> range would cause the program to be terminated with an error. + + + <span style="white-space: nowrap">1-12</span> 范围外的任何值都将导致程序出现错误而终止。 + + + + + Arrays of these character types lead to three separate string types, some of which may have surprising outcomes in some string operations. + + + 这些字符类型的数组对应着三种独立的字符串类型,其中一些可能会在一些字符串操作上有出人意料的结果。 + + + + + As a result, the program uses invalid indexes and attempts to access elements that are not parts of the arrays. + + + 结果是,程序使用无效的索引,试图访问不在数组中的元素。 + + + + + As the value of that variable is incremented at the end of each iteration, the $(C values[counter]) expression refers to the elements of the array one by one: $(C values[0]), $(C values[1]), etc. +) + + + 在每个迭代结束时该变量值都会递增,$(C values[counter]) 表达式指的是数组的逐个元素:$(C values[0])、$(C values[1]) 等等。 +) + + + + + As we have seen in the $(LINK2 /ders/d.en/characters.html, Characters chapter), D has three separate character types. + + + 正如我们在 $(LINK2 /ders/d.cn/characters.html, 字符) 一章中看到的,D 具有三种独立的字符类型。 + + + + + Assigning that return value back to $(C name) produces the intended output: +) + + + 返回值再赋值给 $(C name),得到预期的输出: +) + + + + + Associative arrays are initialized similarly to regular arrays, using a colon to separate each key from its respective value: +) + + + 关联数组的初始化与常规数组相似,不同的是用冒号分隔每个键与相应的值: +) + + + + + Associative arrays store their elements as key-value pairs. + + + 关联数组通过键值对存储它们的元素。 + + + + + Because array elements are stored side-by-side in memory, index values are implicitly the relative positions of elements from the beginning of the array. + + + 因为在内存中数组元素是逐个存储的,索引值就是元素位置与数组的起始位置的相对值。 + + + + + Being value types, $(C o) and $(C i) are copied automatically. + + + 做为值类型,$(C o) 和 $(C i) 自动被复制。 + + + + + Can you figure out the remaining problem before reading the following paragraph? + + + 在没有读下面这段话之前你能指出剩下的问题吗? + + + + + Chaining the result of $(C readln()) to $(C strip()) enables a shorter and more readable syntax: +) + + + 把 $(C readln()) 的结果放到 $(C strip()) 中,能得到更短且可读性更好的语法: +) + + + + + Checking against $(C null) must be done by $(C is) or $(C !is), not by $(C ==) or $(C !=). + + + 检查 $(C null) 必须使用 $(C is) 或 $(C !is),而不是 $(C ==) 或 $(C !=)。 + + + + + Class variables do not have members, the class objects do. + + + 类变量没有成员,类对象有。 + + + + + DESCRIPTION=Basic array operations of the D programming language + + + DESCRIPTION=D 语言数组的基本操作 + + + + + DESCRIPTION=Programming in D exercise solutions: Associative Arrays + + + DESCRIPTION=D语言习题解答:关联数组 + + + + + DESCRIPTION=Programming in D exercise solutions: arrays + + + DESCRIPTION=D 语言编程习题解答:数组 + + + + + DESCRIPTION=Programming in D exercise solutions: strings + + + DESCRIPTION=D 语言编程习题解答:字符串 + + + + + DESCRIPTION=The associative arrays of the d programming language. + + + DESCRIPTION=D语言的关联数组。 + + + + + DESCRIPTION=The basic object oriented programming (OOP) feature of the D programming language. + + + DESCRIPTION=D语言基本的面向对象编程 (OOP) 功能。 + + + + + DESCRIPTION=The scope(success), scope(failure), and scope(exit) statements that are used for specifying expressions that must be executed when exiting scopes. + + + DESCRIPTION=scope(success),scope(failure),和 scope(exit) 语句用于当退出作用域时一定要执行的特殊表达式。 + + + + + DESCRIPTION=The strings of the D programming language + + + DESCRIPTION=D 语言的字符串 + + + + + Define an associative array that can store multiple grades per student. + + + 定义一个能存储每个学生的多个成绩的关联数组。 + + + + + Even though the two objects are constructed by the same character $(C'♔'), they are still two separate objects. + + + 即使这两个对象由同一字符 $(C'♔') 参数构造,, 它们仍是两个单独的对象。 + + + + + Executing the $(C scope) statements in reverse order enables undoing side effects of earlier expressions in a consistent order. + + + 这样按相反顺序执行 $(C scope) 语句,让程序能按一致的顺序撤消前边表达式的副作用。 + + + + + For classes, the meaning of $(C opAssign) is always $(I associating a class variable with a class object). + + + 对于类, $(C opAssign) 意味着 $(I 一个类变量总是关联着一个类对象)。 + + + + + For example, $(C char[]) is a type of string. + + + 例如,$(C char[]) 是一个字符串类型。 + + + + + For example, $(C dayNumbers) would have two key-value pairs after the operations above. + + + 例如,上面的操作结束后 $(C dayNumbers) 将有两个键值对。 + + + + + For example, 'A' and 'a' are different letters, when directly comparing Unicode strings. + + + 例如,当直接比较 Unicode 字符串时,‘A’和‘a’是不同的字母。 + + + + + For example, 'B' comes before 'a'. + + + 例如,‘B’排在‘a’之前。 + + + + + For example, an array that holds the air temperatures of the days in July can bring 31 $(C double) values together and form $(I a container of elements of type $(C double)). + + + 例如,一个保存了七月每天的气温的数组能汇集 31 个 double 值,形成一个 $(I $(C double) 类型元素的容器)。 + + + + + For example, as the $(C formattedRead()) call above expects to read $(I two) items (a $(C string) as name and an $(C int) as age), the following check ensures that it really is the case: +) + + + 例如,像上面的 $(C formattedRead()) 函数期望去读$(I 两个)项目(一个 $(C string) 型 name 和一个 $(C int) 型 age),下面的检查确定它真是这样: +) + + + + + For example, if $(C s) is a variable of type $(C char[]), the following line will fail to compile: +) + + + 例如,如果 $(C s) 的变量类型是 $(C char[]),下面这行将编译失败: +) + + + + + For example, let's assume that the grades 90, 85, 95, etc. are to be stored for the student named "emre". + + + 例如,我们假设存储学生“emre”的成绩 90,85,95 等等。 + + + + + For example, the character 'é' (the latin letter 'e' combined with an acute accent) is represented by Unicode encodings using at least two bytes. + + + 例如,字符‘é’ (拉丁字母‘e’包含了一个重音符) 由 Unicode 编码表示,至少用了两个字节。 + + + + + For example, the characters of a $(C wchar[]) can be modified but the characters of a $(C wstring) cannot be modified. + + + 例如,可以修改一个 $(C wchar[]) 中的字符,但不可以修改一个 $(C wstring) 中的字符。 + + + + + For example, the elements of the array holding the number of days in each month can be shown like the following (assuming a year when February has 28 days): +) + + + 例如,下面数组的元素保存了每个月的天数(假定一年中二月有 28 天): +) + + + + + For example, the month can be determined by the value of the $(C monthIndex) variable below: +) + + + 例如,下面通过变量 $(C monthIndex) 的值来确定该月: +) + + + + + For example, the special value of -1 can be used as the code for colors that are not in $(C colorCodes). + + + 例如,特殊值 -1 用作不在 $(C colorCodes) 中的 colors 代码。 + + + + + For example, the valid indexes of a three-element array are 0, 1, and 2. + + + 例如,一个具有三个元素的数组的有效索引是 0、1 和 2。 + + + + + For example, when the line is "this line has five words" the program should print "e has five". + + + 例如,若这行是“this line has five words”程序就应该打印出“e has five”。 + + + + + For example, when the strings are "ebru" and "domates" the program should print "Ebru&nbsp;Domates". + + + 例如,字符串是“ebru”和“domates”,程序应该打印出“Ebru&nbsp;Domates”。 + + + + + For example, when there are just two elements to store for keys 0 and 999, an associative array stores just two elements, not 1000 as a plain array has to. + + + 例如,对于键 0 和 999,当只有两个元素要存储时,关联数组就只存储两个元素,而不像简单数组那样必须 1000 个。 + + + + + For instance, if the user inputs the text "résumé" you and your program cannot assume that the string length will be 6 even for $(C dchar) strings. + + + 例如,如果用户输入文本“résumé”,即使使用 $(C dchar) 字符串,你和你的程序仍然不能确保字符串的长度会是6。 + + + + + For that reason, when we assign a new key-value pair and the key already exists, the table does not grow; instead, the value of the existing key changes: +) + + + 因而,给一个存在的键赋值,表不增长,而现有键所对应的值会发生变化: +) + + + + + For that reason, you may get results that don't match your expectations below.) +) + + + 因此,下面的输出结果不是你所期望的那样。) +) + + + + + For these reasons, $(C readf()) does not work as intended when reading strings: +) + + + 因而,$(C readf()) 不能如愿读取字符串: +) + + + + + Further, because there is no way to tell $(C readf()) how many characters to read, it continues to read until the end of the entire input. + + + 另外,因为没有办法告诉 $(C readf()) 要读取多少字符,它持续读,直到整个输入结束。 + + + + + Furthermore, since you would be unable to use a loop to iterate the 20 values, you would also have to repeat several lines 20 times, one time for each single-valued variable. + + + 此外,因为您无法使用循环遍历 20 个值,您也不得不把那几行代码重复 20 次,每个单值变量来一次。 + + + + + Hash tables are among the fastest collections for storing and accessing elements. + + + Hash 表是存储和访问元素的最快的集合。 + + + + + Have the program sort the elements using $(C sort()) and then reverse the sorted elements using $(C reverse()). + + + 让程序使用 $(C sort()) 函数排序元素,然后使用 $(C reverse()) 函数反转排序的元素。 + + + + + Here, the bool values may mean + + + 在这里,bool 值可能意味着 + + + + + However, the grades cannot be inserted as in the following code because each grade would overwrite the previous one: +) + + + 然而,不能像下面的代码这样插入成绩,因为后来的成绩将覆盖前一个: +) + + + + + If the user eventually terminates the input that way, we see that the new-line characters have been read as parts of the string as well: +) + + + 如果用户最后这样结束输入,我们看到换行符已作为字符串的一部分被读取: +) + + + + + Imagine needing a thousand values; it is almost impossible to define a thousand variables from $(C value_1) to $(C value_1000). + + + 想象一下,需要一千个值;定义从 $(C value_1) 到 $(C value_1000) 一千个变量,这几乎是不可能的。 + + + + + In order to copy class objects, the type must have a special function likely named $(C dup()). + + + 为了复制类对象,类型必须有一个类似于命名为 $(C dup()) 的特殊函数。 + + + + + In order to use the $(C sort()) function, one must import the $(C std.algorithm) module first. + + + 为了使用 $(C sort()) 函数,必须先导入 $(C std.algorithm) 模块。 + + + + + In other words, $(C opAssign) cannot be overloaded for them. + + + 换句话说,$(C opAssign) 不能因为它们而被重载。 + + + + + In other words, arrays map indexes to values. + + + 换句话说,数组映射索引到值。 + + + + + In other words, instead of typing $(C value_1) one must type $(C values[0]) with arrays. + + + 换句话说,对数组输入 $(C values[0]) 而不是键入 $(C value_1) 。 + + + + + In other words, it can be used as the opposite of the $(C dayNames) array at the beginning of this chapter. + + + 换句话说,它能用作本章开始处 $(C dayNames) 数组的逆转。 + + + + + In other words, it stores an array of ten integer values. + + + 换句话说,它是一个可存储十个整数值的数组。 + + + + + In such cases, the $(C .idup) property can be used to produce an immutable $(C string) variable from a mutable $(C char[]) variable. + + + 这种情况下,$(C .idup) property 能被用来从一个可变的 $(C char[]) 变量中生成一个不可变的 $(C string) 变量。 + + + + + In such cases, the initial values of the elements can be specified on the right-hand side of the assignment operator, within square brackets. + + + 在这种情况下,元素的初始值可以在分配操作的右手侧方括号内指定。 + + + + + In that case all of the elements of the array are initialized to that value: +) + + + 在这种情况下,所有的数组元素都初始化为该值: +) + + + + + In that case it $(I returns) the line that it has just read. + + + 在这种情况下它$(I 返回)刚刚读入的行。 + + + + + In the case of integers, the elements get sorted from the smallest value to the greatest value. + + + 对于整数,元素按从小到大排序。 + + + + + In the simplest definition, strings are nothing but arrays of characters. + + + 在最简单的定义中,字符串只不过是字符数组。 + + + + + Instead, the elements are accessed by specifying the $(I element number) within square brackets: +) + + + 相反,通过指定方括号内的元素位置数就可以访问元素: +) + + + + + Instead, the program terminates with an error. + + + 若有错误,程序会终止。 + + + + + It accesses the element that + + + 它访问对应于 + + + + + It defines an array that consists + + + 它定义了一个可以存储 + + + + + It is an even + + + 如果值被 2 + + + + + It is used differently because the $(STRING " %s") format string and the $(C &) operator are not needed: +) + + + 不同的是没有 $(STRING " %s") 格式字符串并且不需要 $(C &) 运算符: +) + + + + + It might be greater if e.g. at least one of the 'é' characters is not encoded as a single code point but as the combination of an 'e' and a combining accute accent. + + + 如果至少其中一个‘é’字符没有做为单个编码点编码,而是一个‘e’与一个组合重音符的组合,那么它就可能会更长。 + + + + + It returns $(C true) if the object is the same and $(C false) otherwise. + + + 如果是同一对象,返回 $(C true) ,否则为 $(C false) 。 + + + + + Iterating over this slice and removing the element for each key by calling $(C .remove) would result in an empty associative array: + + + 迭代这个切片,通过为每个键调用 $(C .remove) 函数来移除元素,就会形成一个空的关联数组: + + + + + Iterating the loop while its value is less than $(C values.length) ensures that the loops are executed once per element. + + + 当它的值小于 $(C values.length) 时,迭代循环可以保证每个元素只执行了一次。 + + + + + Its $(C ~=) counterpart combines the two arrays and assigns the result back to the left-hand side array: +) + + + $(C ~=) 组合两侧的数组并把结果赋值给左侧数组: +) + + + + + Its first parameter is the line that contains the data, and the rest of the parameters are used exacly like $(C readf()): +) + + + 它的第一个参数是包含数据的输入行,而其余的参数就与 $(C readf()) 的一模一样: +) + + + + + KEYWORDS=d programming language tutorial book arrays fixed-length dynamic + + + KEYWORDS=D 编程语言 教程 定长 动态 + + + + + KEYWORDS=d programming language tutorial book associative arrays + + + KEYWORDS=D 编程语言教程 关联数组 + + + + + KEYWORDS=d programming language tutorial book scope + + + KEYWORDS=D 语言编程教程 scope + + + + + KEYWORDS=d programming language tutorial book string + + + KEYWORDS=D 语言教程 string + + + + + KEYWORDS=d programming lesson book tutorial class + + + KEYWORDS=D 语言编程教程 class + + + + + KEYWORDS=programming in d tutorial arrays solution + + + KEYWORDS=D 语言编程教程 数组 习题解答 + + + + + KEYWORDS=programming in d tutorial associative arrays + + + KEYWORDS=D语言教程 关联数组 + + + + + KEYWORDS=programming in d tutorial strings solution + + + KEYWORDS=D 语言教程 字符串 习题解答 + + + + + Let's see this in a program that reads the number of the month from the user, and prints the number of days in that month: +) + + + 让我们来看一看这个程序,读取来自用户的月数,并输出当月的天数: +) + + + + + Let's see this on a class that has various types of members: +) + + + 让我们在有各种类型成员的类上看看它:: +) + + + + + Like $(C finally), the three different $(C scope) statements are about executing expressions when leaving scopes: +) + + + 像 $(C finally),下面三个不同的 $(C scope) 语句就是关于离开作用域时应执行的表达式: +) + + + + + More features will be introduced later in $(LINK2 /ders/d.en/slices.html, the Slices and Other Array Features chapter). + + + 更多功能将在后面 $(LINK2 /ders/d.en/slices.html, 切片和别的数组功能) 一章中介绍。 + + + + + Note that I have chosen the name of the array variable as plural to avoid confusing it with a single-valued variable. + + + 请注意,我选择了数组变量的名字为复数,以避免它与单值变量混淆。 + + + + + Note that the constructor copies the $(C s) member explicitly by the $(C .dup) property of arrays. + + + 注意,构造函数通过数组的 $(C .dup) 属性显式复制 $(C s) 成员。 + + + + + On the other hand, a program that did not use an array would have to have 20 variable definitions. + + + 另一方面,这个程序若不使用数组那将必须定义 20 个变量。 + + + + + Other than in rare pathological cases, the time it takes to store or access an element is independent of the number of elements that are in the associative array. + + + 除极个别情况外,一般存取单个元素所花费的时间不依赖于关联数组中元素的个数。 + + + + + Plain arrays can use only integers as indexes. + + + 简单数组只能使用整型做索引。 + + + + + Produce the full name that contains the proper capitalization of the first and last names. + + + 生成一个姓名首字母大写的全名。 + + + + + S o; // assume S is a struct type + + + S o; // 假设 S 是一个结构类型 + + + + + SUBTITLE=Arrays + + + SUBTITLE=数组 + + + + + SUBTITLE=Arrays Solutions + + + SUBTITLE=数组习题解答 + + + + + SUBTITLE=Associative Arrays + + + SUBTITLE=关联数组 + + + + + SUBTITLE=Associative Arrays Solutions + + + SUBTITLE=关联数组 习题解答 + + + + + SUBTITLE=Classes + + + SUBTITLE=类 + + + + + SUBTITLE=Strings + + + SUBTITLE=字符串 + + + + + SUBTITLE=Strings Solution + + + SUBTITLE=字符串 习题解答 + + + + + Short for "read line", $(C readln()) reads until the end of the line. + + + 它是“read line”的缩写,$(C readln()) 读取到行尾。 + + + + + Since multiple grades can be stored in a dynamic array, an associative array that maps from $(C string) to $(C int[]) would work here. + + + 由于多个成绩可以用一个动态数组存储,从 $(C string) 到 $(C int[]) 映射的关联数组将能用在这儿。 + + + + + Since that key is not in the container anymore, the second line would cause an exception to be thrown and the program to be terminated if that exception is not caught. + + + 由于键已不存在于容器中, 因而第二行将引发一个异常,如果异常没有被捕获,程序将终止。 + + + + + Since there is no other variable for $(C variable1)'s original object, that object will be destroyed by the garbage collector. + + + 由于 $(C variable1) 的原始对象没有别的变量,该对象将由垃圾回收器销毁。 + + + + + String literals are defined with double quotes. + + + 字符串字面量用双引号定义。 + + + + + Strings are a combination of the two features that we have covered in the last three chapters: characters and arrays. + + + 字符串是在过去三章中介绍的两种功能的组合:字符和数组。 + + + + + That value can be compared against the expected number of data items so that the input can be validated. + + + 该值可与数据项的预期数相比较,以便确定输入的有效性。 + + + + + The + + + 这个 + + + + + The $(C icmp()) function of the $(C std.string) module can be used when strings need to be compared regardless of lowercase and uppercase. + + + 无论小写大写,$(C std.string) 模块中的 $(C icmp()) 函数可用于字符串比较。 + + + + + The $(C king) variable does not have a $(C shape) member, the anonymous object does. + + + $(C king) 变量并没有 $(C shape) 成员,匿名对象有。 + + + + + The $(C new) keyword constructs an anonymous $(I class object) and returns a $(I class variable). + + + The $(C new) 关键字构造一个匿名 $(I class 对象) 并返回一个 $(I class 变量)。 + + + + + The actual object is not copied. + + + 实际的对象没有被复制。 + + + + + The characters of the variables that are defined by these aliases cannot be modified. + + + 由这些别名定义的变量中的字符不可修改。 + + + + + The compiler does not allow accessing characters of an immutable array through a mutable slice. + + + 编译器不允许通过可变的切片访问不可变的字符数组。 + + + + + The concepts of $(I less) and $(I greater) are replaced with $(I before) and $(I after) in this hypothetical alphabet: +) + + + 在这个假设的字母表中,$(I 更少)和$(I 更多)的概念就被$(I 之前)和$(I 之后)代替: +) + + + + + The default value is specified as the second parameter of $(C .get()): +) + + + 默认值被指定为 $(C .get()) 的第2个参数: +) + + + + + The definitions of those variables were the following: +) + + + 下面各项是这些变量的定义: +) + + + + + The difference is that it is the type of the key that is specified within the square brackets, not the length of the array: +) + + + 不同的是方框号中指定的是键的类型,而不是数组的长度: +) + + + + + The first two are with the $(C while) loops: Both of the loop conditions use the $(C <=) operator instead of the $(C <) operator. + + + 前两个与 $(C while) 循环有关: 循环条件都使用 $(C <=) 运算符而不使用 $(C <) 运算符。 + + + + + The following methods would empty the array in a single step. + + + 而下面的方法将用一步清空数组。 + + + + + The grades can be appended to the dynamic arrays that are stored in the associative array: + + + 成绩能附加到存储在关联数组中的动态数组上: + + + + + The initial value of the elements depends on the type of the elements: 0 for $(C int), $(C double.nan) for $(C double), etc. +) + + + 元素的初始值取决于元素类型:$(C int) 的为 0,$(C double) 的为 $(C double.nan) 等等。 +) + + + + + The lengths of those arrays cannot be changed during the execution of the program. + + + 在程序的执行过程中数组的长度不可修改。 + + + + + The number of elements of an array is called the $(I length) of the array. + + + 数组元素的个数称为数组的 $(I length)。 + + + + + The only difference is that the number of values associated with the variable is specified in square brackets. + + + 唯一的区别是,与变量相关联的值的个数在方括号中指定。 + + + + + The program above would require a single change: replacing 5 with 20. + + + 修改一下上面的程序:用 20 替换 5。 + + + + + The program is written to read five numbers from the input and to place the squares of those numbers into an array. + + + 程序要求从输入流中读取五个数字,并把这些数字放入一个数组。 + + + + + The program then attempts to print the squares to the output. + + + 然后程序会输出这些平方。 + + + + + The same operators can be used with strings as well, but with a different meaning: strings are ordered $(I lexicographically). + + + 同样的操作也能用于字符串,但含义不同:字符串按$(I 字典顺序)排序。 + + + + + The second line is the definition of a variable which stores ten consecutive values. + + + 第二行定义了一个存储连续十个值的变量。 + + + + + The solution is to reset $(C i) to 0 before the second $(C while) loop, for example with the statement $(C i = 0;) +) + + + 解决办法就是在第二个 $(C while) 循环前重设 $(C i) 为 0,比如使用语句 $(C i = 0;) +) + + + + + The two variables start providing access to the same object. + + + 这俩变量可访问同一对象。The two variables start providing access to the same object. + + + + + The values of the type that associative arrays $(I map from) are called $(I keys), rather than indexes. + + + 关联数组用于$(I 映射)的类型的值叫$(I 键),而不是索引。 + + + + + The values that we have numbered as 1, 2, 3, 4, and 5 before are numbered as 0, 1, 2, 3, and 4 in the array. + + + 以前被我们编号为 1、2、3、4 和 5 的值在数组中编号为 0、1、2、3 和 4。 + + + + + They are very fast data structures that work like mini databases and are used in many programs. + + + 它们是高速的数据结构,如迷你数据库一样地运作,在很多程序里都用到了。 + + + + + They grow automatically as key-value pairs are added. + + + 它们随着键值对的添加而自动增长。 + + + + + They map the values of one type to the values of another type. + + + 它们映射一种类型的值到另一种类型的值上。 + + + + + This array is used to hold the number + + + 这个数组用于存储 + + + + + This behavior cannot be changed. + + + 该行为不能被修改。 + + + + + This can be demonstrated by printing the entire table: +) + + + 这可以通过打印整个表来验证: +) + + + + + This distinction has an important consequence when $(LINK2 /ders/d.en/function_parameters.html, passing associative arrays to functions). + + + 当 $(LINK2 /ders/d.cn/function_parameters.html, 传关联数组给函数) 时,这种区分具有重要意义。 + + + + + This fact is reflected in the $(C .length) property of strings: +) + + + 这个事实反映在字符串的 $(C .length) property 上: +) + + + + + This function must create and return a new class object. + + + 该函数必须创建并返回一个新的类对象。 + + + + + This is because later code may depend on previous variables. + + + 这是因为后边的代码依赖于前边的变量。 + + + + + This is not possible nor necessary when a single array stores all the values under a single name. + + + 让一个数组在一个名字下存储所有的值,那是不可能也没必要的。 + + + + + This is so that the program has a way of determining whether the input consisted of a complete line or whether the end of input has been reached: +) + + + 这就让程序有办法确定输入是否包含一条完整语句或者输入是否已结束: +) + + + + + This may be seen as a limitation for some applications. + + + 对某些程序来说可能是个限制。 + + + + + This ordering takes each character's Unicode code to be its place in a hypothetical grand Unicode alphabet. + + + 这种排序需要在一个假设的大字母表中让每个字符的 Unicode 编码找到它的位置。 + + + + + This variation can confuse new programmers. + + + 这种变化可能会让编程新手感到困惑。 + + + + + To avoid dealing with this and many other Unicode issues, consider using a Unicode-aware text manipulation library in your programs. + + + 为避免处理这种以及许多其它的 Unicode 问题,在你的程序里就要考虑使用一个支持 Unicode 的文本处理库。 + + + + + To be compatible with arrays, this function may be named $(C dup()). + + + 为与数组兼容,该函数可以命名为 $(C dup()). + + + + + To change their lengths, the source code must be modified and the program must be recompiled. + + + 要修改长度,就必须修改源代码,而且程序必须重新编译。 + + + + + Treat the value <span style="white-space: nowrap">-1</span> specially to determine the end of the numbers; do not process that value. + + + 并专门用值 <span style="white-space: nowrap">-1</span> 来确定数字的结束;不处理该值。 + + + + + Unfortunately, $(C addend) is accessible only in the $(C try) block, where it is defined. + + + 不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。 + + + + + Unlike structs, class objects cannot be constructed by the $(C {&nbsp;}) syntax. + + + 不像结构,类对象不能由 $(C {&nbsp;}) 语法构造。 + + + + + Variables which only store a single value are called scalar variables. + + + 只存储一个值的变量称为标量变量。 + + + + + We can contrast the two definitions as follows: +) + + + 我们可以对比两种定义如下: +) + + + + + We have already seen this above when assigning to an element of an array: +) + + + 这在上面给数组的元素赋值时我们已经看到: +) + + + + + We will + + + 在下一章我们将 + + + + + We will cover these concepts in later chapters.) +) + + + 在稍后的章节中我们将涵盖这些概念。) +) + + + + + We will see Phobos ranges in a later chapter. + + + 在稍后的一章中我们将看到 Phobos 的 range。 + + + + + We will see exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter). + + + 在 $(LINK2 /ders/d.cn/exceptions.html, 稍后的一章) 中我们将看到异常。 + + + + + We will see in $(LINK2 /ders/d.en/inheritance.html, the next chapter) how the definition of $(C toString) can be changed by the $(C override) keyword. + + + 在 $(LINK2 /ders/d.cn/inheritance.html, 下一章节) 我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 + + + + + We will see this concept later in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter). + + + 在 $(LINK2 /ders/d.cn/inheritance.html,继承章节) 我们将看到相关内容。 + + + + + We will see this in a later chapter.) +) + + + 我们将在后面的章节中看到这一点。) +) + + + + + We will see user-defined types in later chapters. + + + 在稍后的章节中我们将看到用户定义类型。 + + + + + We will use the $(C dayNumbers) associative array in the examples below. + + + 在下面的例子中我们将使用 $(C dayNumbers) 关联数组。 + + + + + When accessing elements, the $(C []) characters are written after the name of the array and specify the number of the element that is being accessed: + + + 访问元素时,$(C []) 写在数组名称之后,并指定要访问的元素位置数: + + + + + When defining arrays, the $(C []) characters are written after the type of the elements and specify the number of elements. + + + 当我们定义数组时,$(C []) 写在元素类型之后,并指定元素个数。 + + + + + When reading strings from the input, the control character that corresponds to the Enter key that is pressed at the end of the input becomes a part of the string as well. + + + 当从输入中读取字符串,输入结束时按的 Enter 键对应的控制字符将变成字符串的一部分。 + + + + + When that exact syntax is desired, properties should be preferred, which will be explained in $(LINK2 /ders/d.en/property.html, a later chapter).) +) + + + 若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的章节) 中解释。) +) + + + + + When the length can change during the execution of the program, that array is a $(I dynamic array). + + + 当长度可以在程序的执行过程中进行修改时,该数组就是一个$(I 动态数组)。 + + + + + You can also think of it as defining ten variables of the same type, or as defining an array, for short. + + + 你也可以把它定义为同类型的十个变量,或作为数组定义的简称。 + + + + + You can apply this guideline blindly unless there are compilation errors: +) + + + 若没有编译错误,您可以闭着眼睛按教程来: +) + + + + + You can determine whether a number is odd or even using the $(C %) (remainder) operator. + + + 使用 $(C %)(求余数)运算符来确定数字是奇数还是偶数。 + + + + + You can see the functions of this module at $(LINK2 http://dlang.org/phobos/std_string.html, its online documentation). + + + 在 $(LINK2 http://dlang.org/phobos/std_string.html, 它的在线文档) 中你可以看到这个模块的各个函数。 + + + + + You may find especially the Phobos ranges confusing at this point. + + + 此刻你可能会发现 Phobos 的 range 尤其让人迷惑。 + + + + + You may have to define variables of that exact type: +) + + + 您可能就需要定义该类型的变量: +) + + + + + You will notice that the program will not print the results. + + + 你将注意到程序不会输出结果。 + + + + + array ~= 360; // array is now equal to [7, 360] + + + array ~= 360; // 数组现在等于 [7, 360] + + + + + array ~= 7; // array is now equal to [7] + + + array ~= 7; // 数组现在等于 [7] + + + + + array ~= [ 30, 40 ]; // array is now equal to [7, 360, 30, 40] +--- + + + array ~= [ 30, 40 ]; // 数组现在等于 [7, 360, 30, 40] +--- + + + + + array.length = 5; // now has 5 elements +--- + + + array.length = 5; // 现在有 5 个元素 +--- + + + + + dayNumbers.clear; // The associative array becomes empty +--- + + + dayNumbers.clear; // 清空关联数组 +--- + + + + + double[5] values; // elements are all double.nan +--- + + + double[5] values; // 元素都是 double.nan +--- + + + + + elements → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | +) + + + 元素 → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | +) + + + + + grades["emre"] = 85; // ← Overwrites the previous grade! + + + grades["emre"] = 85; // ← 覆盖前一个成绩! + + + + + indexes → 0 1 2 3 4 5 6 7 8 9 10 11 + + + 索引 → 0 1 2 3 4 5 6 7 8 9 10 11 + + + + + int[10] allOnes = 1; // All of the elements are set to 1 +--- + + + int[10] allOnes = 1; // 所有的元素都设置为 1 +--- + + + + + int[] array; // empty + + + int[] array; // 空数组 + + + + + int[] array; // initially empty + + + int[] array; // 初始时为空 + + + + + number = int.init; // 0 for int +--- +) + + + number = int.init; // int 值:0 +--- +) + + + + + string result = (s ~ '.')$(HILITE .idup); // ← now compiles +--- + + + string result = (s ~ '.')$(HILITE .idup); // ← 现在可以编译 +--- + + + + + string s = "résumé"c; // same as "résumé" + + + string s = "résumé"c; // 与“résumé”一样 + + + + + writeln(dayNames[1]); // prints "Tuesday" +--- + + + writeln(dayNames[1]); // 打印 “Tuesday” +--- + + + + + writeln(dayNumbers["Tuesday"]); // prints 1 +--- + + + writeln(dayNumbers["Tuesday"]); // 打印 1 +--- + + + + + writeln(dayNumbers["Tuesday"]); // ← run-time ERROR +--- + + + writeln(dayNumbers["Tuesday"]); // ← 运行时错误 +--- + + + + + writeln(result.length); // prints 20 + + + writeln(result.length); // 输出 20 + + + + + writeln(result.length); // prints 30 +} +--- + + + writeln(result.length); // 输出 30 +} +--- + + + + + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: +) + + + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它: +) + + + $ .dup capacity)) $(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) From a9489e506db5a06af5228ac1f47adcc52dcc28df Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 13:13:35 +0800 Subject: [PATCH 028/225] Project translation --- target/aa.cozum.d | 38 ++--- target/aa.d | 136 ++++++++-------- target/alias.d | 2 +- target/arrays.cozum.d | 38 ++--- target/arrays.d | 222 +++++++++++++-------------- target/assert.cozum.d | 2 +- target/assert.d | 2 +- target/auto_and_typeof.cozum.d | 2 +- target/bit_operations.d | 2 +- target/cast.d | 4 +- target/characters.d | 6 +- target/class.d | 106 ++++++------- target/concurrency.d | 14 +- target/concurrency_shared.d | 8 +- target/cond_comp.d | 2 +- target/const_and_immutable.d | 4 +- target/destroy.d | 4 +- target/encapsulation.d | 4 +- target/enum.d | 8 +- target/exceptions.d | 4 +- target/fibers.d | 4 +- target/foreach.d | 4 +- target/foreach_opapply.cozum.d | 2 +- target/foreach_opapply.d | 2 +- target/formatted_output.d | 2 +- target/function_overloading.d | 2 +- target/function_parameters.d | 10 +- target/functions_more.d | 4 +- target/goto.d | 2 +- target/inheritance.cozum.d | 2 +- target/inheritance.d | 10 +- target/input.d | 2 +- target/interface.d | 6 +- target/invariant.d | 2 +- target/is_expr.d | 6 +- target/lambda.d | 8 +- target/literals.d | 4 +- target/main.d | 6 +- target/memory.d | 6 +- target/mixin.d | 6 +- target/nested.d | 2 +- target/null_is.d | 4 +- target/object.d | 2 +- target/operator_overloading.d | 6 +- target/parallelism.d | 4 +- target/parameter_flexibility.cozum.d | 2 +- target/parameter_flexibility.d | 8 +- target/pointers.cozum.d | 4 +- target/pointers.d | 6 +- target/ranges.d | 14 +- target/ranges_more.d | 6 +- target/scope.d | 40 ++--- target/slices.cozum.d | 4 +- target/slices.d | 16 +- target/special_functions.d | 8 +- target/strings.cozum.d | 14 +- target/strings.d | 164 ++++++++++---------- target/struct.d | 6 +- target/switch_case.d | 2 +- target/templates.d | 2 +- target/templates_more.d | 12 +- target/tuples.d | 10 +- target/uda.d | 8 +- target/union.d | 2 +- target/value_vs_reference.d | 2 +- 65 files changed, 523 insertions(+), 523 deletions(-) diff --git a/target/aa.cozum.d b/target/aa.cozum.d index 0997594..eceb581 100644 --- a/target/aa.cozum.d +++ b/target/aa.cozum.d @@ -1,6 +1,6 @@ Ddoc -$(COZUM_BOLUMU Associative Arrays) +$(COZUM_BOLUMU 关联数组) $(OL @@ -9,7 +9,7 @@ $(LI $(UL $(LI -The $(C .keys) property returns a slice (i.e. dynamic array) that includes all of the keys of the associative array. Iterating over this slice and removing the element for each key by calling $(C .remove) would result in an empty associative array: +$(C .keys) property(属性)返回一个切片(即 动态数组),它包含了关联数组全部的键。迭代这个切片,通过为每个键调用 $(C .remove) 函数来移除元素,就会形成一个空的关联数组: --- import std.stdio; @@ -26,8 +26,8 @@ void main() { int[] keys = names.keys; - /* 'foreach' is similar but superior to 'for'. We will - * see the 'foreach' loop in the next chapter. */ + /* ‘foreach’类似于‘for’,但比它更有优势。在下一章我们将 + *看到‘foreach’循环。*/ foreach (key; keys) { writefln("Removing the element %s", key); names.remove(key); @@ -38,13 +38,13 @@ void main() { --- $(P -That solution may be slow especially for large arrays. The following methods would empty the array in a single step. +对于大型数组这恐怕会非常慢。而下面的方法将用一步清空数组。 ) ) $(LI -Another solution is to assign an empty array: +另一种解法是用空数组赋值: --- string[int] emptyAA; @@ -54,7 +54,7 @@ Another solution is to assign an empty array: ) $(LI -Since the initial value of an array is an empty array anyway, the following technique would achieve the same result: +由于数组的初始值无论如何都是一个空数组,那么下面的技术将得到一样的结果: --- names = names.init; @@ -67,30 +67,30 @@ Since the initial value of an array is an empty array anyway, the following tech ) $(LI -The goal is to store multiple grades per student. Since multiple grades can be stored in a dynamic array, an associative array that maps from $(C string) to $(C int[]) would work here. The grades can be appended to the dynamic arrays that are stored in the associative array: +目标是存储每个学生的多个成绩。由于多个成绩可以用一个动态数组存储,从 $(C string) 到 $(C int[]) 映射的关联数组将能用在这儿。成绩能附加到存储在关联数组中的动态数组上: --- import std.stdio; void main() { - /* The key type of this associative array is string and - * the value type is int[], i.e. an array of ints. The - * associative array is being defined with an extra - * space in between to help distinguish the value type: */ + /* 该关联数组的键类型为 string, + * 值类型为 int[] ,即整型数组。这个 + *关联数组在定义时附加了额外的空格, + * 以易于区分值类型: */ int[] [string] grades; - /* The array of ints that correspond to "emre" is being - * used for appending the new grade to that array: */ + /* 与“emre” 对应的整型数组 + * 将用于附加新成绩:*/ grades["emre"] ~= 90; grades["emre"] ~= 85; - /* Printing the grades of "emre": */ + /* 打印“emre”的成绩: */ writeln(grades["emre"]); } --- $(P -The grades can also be assigned in one go with an array literal: +成绩也可以作为数组文本一次性赋值: ) --- @@ -110,8 +110,8 @@ void main() { ) Macros: - SUBTITLE=Associative Arrays Solutions + SUBTITLE=关联数组 习题解答 - DESCRIPTION=Programming in D exercise solutions: Associative Arrays + DESCRIPTION=D语言习题解答:关联数组 - KEYWORDS=programming in d tutorial associative arrays + KEYWORDS=D语言教程 关联数组 diff --git a/target/aa.d b/target/aa.d index 29b4033..1226782 100644 --- a/target/aa.d +++ b/target/aa.d @@ -1,13 +1,13 @@ Ddoc -$(DERS_BOLUMU $(IX associative array) $(IX AA) Associative Arrays) +$(DERS_BOLUMU $(IX 关联数组) $(IX AA) 关联数组) $(P -Associative arrays are a feature that is found in most modern high-level languages. They are very fast data structures that work like mini databases and are used in many programs. +关联数组是大多数现代高级编程语言所具有的功能。它们是高速的数据结构,如迷你数据库一样地运作,在很多程序里都用到了。 ) $(P -We saw in the $(LINK2 /ders/d.en/arrays.html, Arrays chapter) that plain arrays are containers that store their elements side-by-side and provide access to them by index. An array that stores the names of the days of the week can be defined like this: +在 $(LINK2 /ders/d.cn/arrays.html, 数组) 一章中我们已经看到简单数组作为容器逐个存储元素,按索引访问它们。一个存储了星期的英文名称的数组可以这样定义: ) --- @@ -17,49 +17,49 @@ We saw in the $(LINK2 /ders/d.en/arrays.html, Arrays chapter) that plain arrays --- $(P -The name of a specific day can be accessed by its index in that array: +在数组里通过索引访问特定的英文日期名: ) --- - writeln(dayNames[1]); // prints "Tuesday" + writeln(dayNames[1]); // 打印 “Tuesday” --- $(P -The fact that plain arrays provide access to their values through index numbers can be described as an $(I association) of indexes with values. In other words, arrays map indexes to values. Plain arrays can use only integers as indexes. +事实上简单数组通过索引访问值可以被描述为索引与值的$(I 关联) 。换句话说,数组映射索引到值。简单数组只能使用整型做索引。 ) $(P -Associative arrays allow indexing not only using integers but also using any other type. They map the values of one type to the values of another type. The values of the type that associative arrays $(I map from) are called $(I keys), rather than indexes. Associative arrays store their elements as key-value pairs. +关联数组允许索引不只是整型而是任何类型。它们映射一种类型的值到另一种类型的值上。关联数组用于$(I 映射)的类型的值叫$(I 键),而不是索引。关联数组通过键值对存储它们的元素。 ) $(P -Associative arrays are implemented in D using a $(I hash table). Hash tables are among the fastest collections for storing and accessing elements. Other than in rare pathological cases, the time it takes to store or access an element is independent of the number of elements that are in the associative array. +在D语言中,关联数组是一个 $(I hash 表) 实现。Hash 表是存储和访问元素的最快的集合。除极个别情况外,一般存取单个元素所花费的时间不依赖于关联数组中元素的个数。 ) $(P -The high performance of hash tables comes at the expense of storing the elements in an unordered way. Also, unlike arrays, the elements of hash tables are not stored side-by-side. +hash 表的高性能的代价是元素的存储是无序的。不像数组,hash 表的元素不是逐个排列存储的。 ) $(P -For plain arrays, index values are not stored at all. Because array elements are stored side-by-side in memory, index values are implicitly the relative positions of elements from the beginning of the array. +对于简单数组,索引值根本就没有存储。因为在内存中数组元素是逐个存储的,索引值就是元素位置与数组的起始位置的相对值。 ) $(P -On the other hand, associative arrays do store both the keys and the values of elements. Although this difference makes associative arrays use more memory, it also allows them to use $(I sparse) key values. For example, when there are just two elements to store for keys 0 and 999, an associative array stores just two elements, not 1000 as a plain array has to. +另一方面,关联数组既存储键又存储元素值。虽然这种不同让关联数组使用更多内存,但它也允许它们使用$(I 稀疏)键值。例如,对于键 0 和 999,当只有两个元素要存储时,关联数组就只存储两个元素,而不像简单数组那样必须 1000 个。 ) -$(H5 Definition) +$(H5 定义) $(P -The syntax of associative arrays is similar to the array syntax. The difference is that it is the type of the key that is specified within the square brackets, not the length of the array: +关联数组的语法与数组相似。不同的是方框号中指定的是键的类型,而不是数组的长度: ) --- - $(I value_type)[$(I key_type)] $(I associative_array_name); + $(I 值类型)[$(I 键类型)] $(I 关联数组名); --- $(P -For example, an associative array that maps day names of type $(C string) to day numbers of type $(C int) can be defined like this: +例如,下面这个关联数组这样定义,它把日期名的类型 $(C string) 映射到日期的数字类型 $(C int) : ) --- @@ -67,37 +67,37 @@ For example, an associative array that maps day names of type $(C string) to day --- $(P -The $(C dayNumbers) variable above is an associative array that can be used as a table that provides a mapping from day names to day numbers. In other words, it can be used as the opposite of the $(C dayNames) array at the beginning of this chapter. We will use the $(C dayNumbers) associative array in the examples below. +上面的 $(C dayNumbers) 变量是一个关联数组,它能用来作为从日期名映射到日期数字的表。换句话说,它能用作本章开始处 $(C dayNames) 数组的逆转。在下面的例子中我们将使用 $(C dayNumbers) 关联数组。 ) $(P -The keys of associative arrays can be of any type, including user-defined $(C struct) and $(C class) types. We will see user-defined types in later chapters. +关联数组的键可以是任何类型,包括用户定义的 $(C struct) 和 $(C class) 类型。在稍后的章节中我们将看到用户定义类型。 ) $(P -The length of associative arrays cannot be specified when defined. They grow automatically as key-value pairs are added. +定义时,不能指定关联数组的长度。它们随着键值对的添加而自动增长。 ) $(P -$(I $(B Note:) An associative array that is defined without any element is $(LINK2 /ders/d.en/null_is.html, $(C null)), not empty. This distinction has an important consequence when $(LINK2 /ders/d.en/function_parameters.html, passing associative arrays to functions). We will cover these concepts in later chapters.) +$(I $(B 注:) 已定义的没有任何元素的关联数组是 $(LINK2 /ders/d.cn/null_is.html, $(C null)),而不是空。当 $(LINK2 /ders/d.cn/function_parameters.html, 传关联数组给函数) 时,这种区分具有重要意义。在稍后的章节中我们将涵盖这些概念。) ) -$(H5 Adding key-value pairs) +$(H5 添加键值对) $(P -Using the assignment operator is sufficient to build the association between a key and a value: +使用赋值运算符就足以构建键与值的关联: ) --- - // associates value 0 with key "Monday" + // 关联值 0 与 键 “Monday” dayNumbers["Monday"] $(HILITE =) 0; - // associates value 1 with key "Tuesday" + // 关联值 1 与 键 “Tuesday” dayNumbers["Tuesday"] $(HILITE =) 1; --- $(P -The table grows automatically with each association. For example, $(C dayNumbers) would have two key-value pairs after the operations above. This can be demonstrated by printing the entire table: +随着每次的关联,该表会自动增长。例如,上面的操作结束后 $(C dayNumbers) 将有两个键值对。这可以通过打印整个表来验证: ) --- @@ -105,7 +105,7 @@ The table grows automatically with each association. For example, $(C dayNumbers --- $(P -The output indicates that the values 0 and 1 correspond to keys "Monday" and "Tuesday", respectively: +下面的输出表明了元素值 0 和 1 分别对应着键 “Monday” 和 “Tuesday”: ) $(SHELL @@ -113,7 +113,7 @@ $(SHELL ) $(P -There can be only one value per key. For that reason, when we assign a new key-value pair and the key already exists, the table does not grow; instead, the value of the existing key changes: +每个键有且仅有一个对应值。因而,给一个存在的键赋值,表不增长,而现有键所对应的值会发生变化: ) --- @@ -122,7 +122,7 @@ There can be only one value per key. For that reason, when we assign a new key-v --- $(P -The output: +输出: ) $(SHELL @@ -130,10 +130,10 @@ $(SHELL ) -$(H5 Initialization) +$(H5 初始化) $(P -$(IX :, associative array) Sometimes some of the mappings between the keys and the values are already known at the time of the definition of the associative array. Associative arrays are initialized similarly to regular arrays, using a colon to separate each key from its respective value: +$(IX :, 关联数组) 有时候一些键与值的映射在定义关联数组的时候就已经明确。关联数组的初始化与常规数组相似,不同的是用冒号分隔每个键与相应的值: ) --- @@ -143,51 +143,51 @@ $(IX :, associative array) Sometimes some of the mappings between the keys and t "Thursday" : 3, "Friday" : 4, "Saturday" : 5, "Sunday" : 6 ]; - writeln(dayNumbers["Tuesday"]); // prints 1 + writeln(dayNumbers["Tuesday"]); // 打印 1 --- $(H5 Removing key-value pairs) $(P -Key-value pairs can be removed by using $(C .remove()): +通过 $(C .remove()) 可以移除键值对: ) --- dayNumbers.remove("Tuesday"); - writeln(dayNumbers["Tuesday"]); // ← run-time ERROR + writeln(dayNumbers["Tuesday"]); // ← 运行时错误 --- $(P -The first line above removes the key-value pair "Tuesday" / $(C 1). Since that key is not in the container anymore, the second line would cause an exception to be thrown and the program to be terminated if that exception is not caught. We will see exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter). +上面第一行移除了键值对 "Tuesday" / $(C 1). 由于键已不存在于容器中, 因而第二行将引发一个异常,如果异常没有被捕获,程序将终止。在 $(LINK2 /ders/d.cn/exceptions.html, 稍后的一章) 中我们将看到异常。 ) $(P -$(C .clear) removes all elements: +$(C .clear) 移除全部元素: ) --- - dayNumbers.clear; // The associative array becomes empty + dayNumbers.clear; // 清空关联数组 --- -$(H5 $(IX in, associative array) Determining the presence of a key) +$(H5 $(IX in, 关联数组) 确定键的存在) $(P -The $(C in) operator determines whether a given key exists in the associative array: +$(C in) 运算符确定一个给定的键是否存在于关联数组中: ) --- int[string] colorCodes = [ /* ... */ ]; if ("purple" $(HILITE in) colorCodes) { - // key "purple" exists in the table + // 键 “purple” 在表中 } else { - // key "purple" does not exist in the table + //表中不存在 键 “purple” } --- $(P -Sometimes it makes sense to use a default value if a key does not exist in the associative array. For example, the special value of -1 can be used as the code for colors that are not in $(C colorCodes). $(C .get()) is useful in such cases: it returns the value associated with the specified key if that key exists, otherwise it returns the default value. The default value is specified as the second parameter of $(C .get()): +有时候在关联数组中,为不存在的键使用一个默认值是有道理的。例如,特殊值 -1 用作不在 $(C colorCodes) 中的 colors 代码。$(C .get()) 在这样的样例中是有用的:如果指定键存在则返回相应值,否则返回默认值。默认值被指定为 $(C .get()) 的第2个参数: ) --- @@ -196,45 +196,45 @@ Sometimes it makes sense to use a default value if a key does not exist in the a --- $(P -Since the array does not contain a value for the key $(STRING "purple"), $(C .get()) returns -1: +由于键 $(STRING "purple") 的值不在数组中,$(C .get()) 返回 -1: ) $(SHELL -1 ) -$(H5 Properties) +$(H5 Properties(属性)) $(UL -$(LI $(IX .length) $(C .length) returns the number of key-value pairs.) +$(LI $(IX .length) $(C .length) 返回键值对的个数。) -$(LI $(IX .keys) $(C .keys) returns a copy of all keys as a dynamic array.) +$(LI $(IX .keys) $(C .keys) 返回全部键的动态数组副本。) -$(LI $(IX .byKey) $(C .byKey) provides access to the keys without copying them; we will see how $(C .byKey) is used in $(C foreach) loops in the next chapter.) +$(LI $(IX .byKey) $(C .byKey) 提供对键的直接访问;在下一章我们将看到在 $(C foreach) 循环中如何使用 $(C .byKey) 。) -$(LI $(IX .values) $(C .values) returns a copy of all values as a dynamic array.) +$(LI $(IX .values) $(C .values) 返回全部值的动态数组副本。) -$(LI $(IX .byValue) $(C .byValue) provides access to the values without copying them.) +$(LI $(IX .byValue) $(C .byValue) 提供对值的直接访问。) -$(LI $(IX .byKeyValue) $(C .byKeyValue) provides access to the key-value pairs without copying them.) +$(LI $(IX .byKeyValue) $(C .byKeyValue) 提供对键值对的直接访问。) -$(LI $(IX .rehash) $(C .rehash) may make the array more efficient in some cases, such as after inserting a large number of key-value pairs.) +$(LI $(IX .rehash) $(C .rehash) 在一些例子中可以让数组更有效率,比如在插入大量的键值对之后。) -$(LI $(IX .sizeof, associative array) $(C .sizeof) is the size of the array $(I reference) (it has nothing to do with the number of key-value pairs in the table and is the same value for all associative arrays).) +$(LI $(IX .sizeof, associative array) $(C .sizeof) 数组$(I 引用)大小(它不受表中键值对个数的影响,对所有的关联数组来说值都是一样的)。) -$(LI $(IX .get) $(C .get) returns the value if it exists, the default value otherwise.) +$(LI $(IX .get) $(C .get) 值存在即返回相应值,否则返回默认值。) $(LI $(IX .remove, associative array) $(C .remove) removes the specified key and its value from the array.) -$(LI $(IX .clear) $(C .clear) removes all elements.) +$(LI $(IX .clear) $(C .clear) 移除全部元素。) ) -$(H5 Example) +$(H5 样例) $(P -Here is a program that prints the Turkish names of colors that are specified in English: +这是一段打印英文颜色的土耳其语表达的程序,它的键指定为英文: ) --- @@ -267,22 +267,22 @@ void main() { $(PROBLEM_COK $(PROBLEM -How can all of the key-value pairs of an associative array be removed other than calling $(C .clear)? ($(C .clear) is the most natural method.) There are at least three methods: +除 $(C .clear) 函数外还有什么方法能移除关联数组的全部键值对? ($(C .clear) 是最自然的方法。) 至少还有三种方法: $(UL -$(LI Removing them one-by-one from the associative array.) +$(LI 从关联数组中逐个移除它们。) -$(LI Assigning an empty associative array.) +$(LI 用一个空的关联数组赋值。) -$(LI Similar to the previous method, assigning the array's $(C .init) property. +$(LI 与前一方法相似,用数组的 $(C .init) 属性赋值。 $(P -$(IX .init, clearing a variable) $(I $(B Note:) The $(C .init) property of any variable or type is the initial value of that type:) +$(IX .init, 清除一个变量) $(I $(B 注:) 任何变量或类型的 $(C .init) 属性是类型的初始值:) ) --- - number = int.init; // 0 for int + number = int.init; // int 值:0 --- ) @@ -291,24 +291,24 @@ $(IX .init, clearing a variable) $(I $(B Note:) The $(C .init) property of any v ) $(PROBLEM -Just like with arrays, there can be only one value for each key. This may be seen as a limitation for some applications. +与数组一样,每个键有且仅有一个值。对某些程序来说可能是个限制。 $(P -Assume that an associative array is used for storing student grades. For example, let's assume that the grades 90, 85, 95, etc. are to be stored for the student named "emre". +假设使用关联数组来存储学生成绩。例如,我们假设存储学生“emre”的成绩 90,85,95 等等。 ) $(P -Associative arrays make it easy to access the grades by the name of the student as in $(C grades["emre"]). However, the grades cannot be inserted as in the following code because each grade would overwrite the previous one: +在 $(C grades["emre"]) 中通过学生姓名关联数组让它很容易访问到成绩。然而,不能像下面的代码这样插入成绩,因为后来的成绩将覆盖前一个: ) --- int[string] grades; grades["emre"] = 90; - grades["emre"] = 85; // ← Overwrites the previous grade! + grades["emre"] = 85; // ← 覆盖前一个成绩! --- $(P -How can you solve this problem? Define an associative array that can store multiple grades per student. +怎么解决这个问题?定义一个能存储每个学生的多个成绩的关联数组。 ) ) @@ -316,8 +316,8 @@ How can you solve this problem? Define an associative array that can store multi ) Macros: - SUBTITLE=Associative Arrays + SUBTITLE=关联数组 - DESCRIPTION=The associative arrays of the d programming language. + DESCRIPTION=D语言的关联数组。 - KEYWORDS=d programming language tutorial book associative arrays + KEYWORDS=D 编程语言教程 关联数组 diff --git a/target/alias.d b/target/alias.d index 46c5459..0985e45 100644 --- a/target/alias.d +++ b/target/alias.d @@ -429,7 +429,7 @@ enum Color { red, orange } } --- -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/arrays.cozum.d b/target/arrays.cozum.d index 3e0d3fa..8a4c9b2 100644 --- a/target/arrays.cozum.d +++ b/target/arrays.cozum.d @@ -1,6 +1,6 @@ Ddoc -$(COZUM_BOLUMU Arrays) +$(COZUM_BOLUMU 数组) $(OL @@ -18,7 +18,7 @@ void main() { double[] values; values.length = count; - // The counter is commonly named as 'i' + // 计数器通常命名作‘i’ int i; while (i < count) { write("Value ", i, ": "); @@ -51,15 +51,15 @@ void main() { ) $(LI -The explanations are included as code comments: +解释包含在代码说明中: --- import std.stdio; import std.algorithm; void main() { - // Using dynamic arrays because it is not known how many - // values are going to be read from the input + // 使用动态数组的原因是不知道有多少 + // 值要从输入流中读取 int[] odds; int[] evens; @@ -67,18 +67,18 @@ void main() { while (true) { - // Reading the value + // 从输入流中读取值 int value; readf(" %s", &value); - // The special value of -1 breaks the loop + // 特殊值 -1 中断循环 if (value == -1) { break; } - // Adding to the corresponding array, depending on - // whether the value is odd or even. It is an even - // number if there is no remainder when divided by 2. + // 根据值的奇偶性,把值添加到 + //相应的数组。如果值被 2 + // 整除而没有余数,那这个数就是偶数。 if ((value % 2) == 0) { evens ~= value; @@ -87,17 +87,17 @@ void main() { } } - // The odds and evens arrays are sorted separately + // 分别排序奇偶数的数组 sort(odds); sort(evens); - // The two arrays are then appended to form a new array + // 连接两个数组从而形成一个新数组 int[] result; result = odds ~ evens; writeln("First the odds then the evens, sorted:"); - // Printing the array elements in a loop + // 在循环中输出数组元素 int i; while (i < result.length) { write(result[i], " "); @@ -111,14 +111,14 @@ void main() { ) $(LI -There are three mistakes (bugs) in this program. The first two are with the $(C while) loops: Both of the loop conditions use the $(C <=) operator instead of the $(C <) operator. As a result, the program uses invalid indexes and attempts to access elements that are not parts of the arrays. +程序有三个错误(bugs)。前两个与 $(C while) 循环有关: 循环条件都使用 $(C <=) 运算符而不使用 $(C <) 运算符。结果是,程序使用无效的索引,试图访问不在数组中的元素。 $(P -Since it is more beneficial for you to debug the third mistake yourself, I would like you to first run the program after fixing the previous two bugs. You will notice that the program will not print the results. Can you figure out the remaining problem before reading the following paragraph? +自行调试第三个错误对你来说更有益,建议你在修复了前两个 bug 之后先运行一下程序。你将注意到程序不会输出结果。在没有读下面这段话之前你能指出剩下的问题吗? ) $(P -The value of $(C i) is 5 when the first $(C while) loop terminates, and that value is causing the logical expression of the second loop to be $(C false), which in turn is preventing the second loop to be entered. The solution is to reset $(C i) to 0 before the second $(C while) loop, for example with the statement $(C i = 0;) +当第一个 $(C while) 循环结束时 $(C i) 值为 5,该值导致第二个循环的逻辑表达式的值为 $(C false),是它阻止进入第二个循环。 解决办法就是在第二个 $(C while) 循环前重设 $(C i) 为 0,比如使用语句 $(C i = 0;) ) ) @@ -126,8 +126,8 @@ The value of $(C i) is 5 when the first $(C while) loop terminates, and that val ) Macros: - SUBTITLE=Arrays Solutions + SUBTITLE=数组习题解答 - DESCRIPTION=Programming in D exercise solutions: arrays + DESCRIPTION=D 语言编程习题解答:数组 - KEYWORDS=programming in d tutorial arrays solution + KEYWORDS=D 语言编程教程 数组 习题解答 diff --git a/target/arrays.d b/target/arrays.d index 3f668d6..a4a915c 100644 --- a/target/arrays.d +++ b/target/arrays.d @@ -3,7 +3,7 @@ Ddoc $(DERS_BOLUMU $(IX array) Arrays) $(P -We have defined five variables in one of the exercises of the last chapter, and used them in certain calculations. The definitions of those variables were the following: +在上一章的一个练习中我们定义过五个变量,并用它们做过特定计算。下面各项是这些变量的定义: ) --- @@ -15,21 +15,21 @@ We have defined five variables in one of the exercises of the last chapter, and --- $(P -This method of defining variables individually does not scale to cases where even more variables are needed. Imagine needing a thousand values; it is almost impossible to define a thousand variables from $(C value_1) to $(C value_1000). +这种定义个别变量的方法不能扩展到需要更多变量的情况下。想象一下,需要一千个值;定义从 $(C value_1) 到 $(C value_1000) 一千个变量,这几乎是不可能的。 ) $(P -Arrays are useful in such cases: the array feature allows us to define a single variable that stores multiple values together. Although simple, arrays are the most common data structure used to store a collection of values. +数组在这种情况下是有用的:数组功能允许我们定义一个把多个值存储到一起的单个变量。虽然简单,但数组是用于存储值的集合中最常见的数据结构。 ) $(P -This chapter covers only some of the features of arrays. More features will be introduced later in $(LINK2 /ders/d.en/slices.html, the Slices and Other Array Features chapter). +本章仅涉及数组的部分功能。更多功能将在后面 $(LINK2 /ders/d.en/slices.html, 切片和别的数组功能) 一章中介绍。 ) -$(H5 Definition) +$(H5 定义) $(P -The definition of array variables is very similar to the definition of normal variables. The only difference is that the number of values associated with the variable is specified in square brackets. We can contrast the two definitions as follows: +数组变量的定义与正常变量的定义非常相似。唯一的区别是,与变量相关联的值的个数在方括号中指定。我们可以对比两种定义如下: ) --- @@ -38,11 +38,11 @@ The definition of array variables is very similar to the definition of normal va --- $(P -The first line above is the definition of a variable which stores a single value, just like the variables that we have defined so far. The second line is the definition of a variable which stores ten consecutive values. In other words, it stores an array of ten integer values. You can also think of it as defining ten variables of the same type, or as defining an array, for short. +上面的第一行定义了一个存储单个值的变量,就像我们以前定义过的变量那样。第二行定义了一个存储连续十个值的变量。换句话说,它是一个可存储十个整数值的数组。你也可以把它定义为同类型的十个变量,或作为数组定义的简称。 ) $(P -Accordingly, the equivalent of the five separate variables above can be defined as an array of five values using the following syntax: +因此,上面的五个独立的变量可以等效的使用下面的语法定义为含五个值的数组: ) --- @@ -50,49 +50,49 @@ Accordingly, the equivalent of the five separate variables above can be defined --- $(P -$(IX scalar) That definition can be read as $(I 5 double values). Note that I have chosen the name of the array variable as plural to avoid confusing it with a single-valued variable. Variables which only store a single value are called scalar variables. +$(IX 标量) 这个定义可以理解为 $(I 5 个 double 值)。请注意,我选择了数组变量的名字为复数,以避免它与单值变量混淆。只存储一个值的变量称为标量变量。 ) $(P -In summary, the definition of an array variable consists of the type of the values, the number of values, and the name of the variable that refers to the array of values: +总之,数组变量的定义包括值的类型、 值的个数和涉及数组值的变量名称: ) --- - $(I type_name)[$(I value_count)] $(I variable_name); + $(I 类型名称)[$(I 值的个数)] $(I 变量名称); --- $(P -The type of the values can also be a user-defined type. (We will see user-defined types later.) For example: +值的类型也可以是用户定义的类型。(我们将在后面看到用户定义类型。)例如: ) --- - // An array that holds the weather information of all - // cities. Here, the bool values may mean - // false: overcast - // true : sunny + // 保存所有城市气象信息的 + // 数组。在这里,bool 值可能意味着 + // false:阴天 + // true :晴天 bool[cityCount] weatherConditions; - // An array that holds the weights of a hundred boxes + // 保存一百个箱子重量的数组 double[100] boxWeights; - // Information about the students of a school + // 有关学校学生的信息 StudentInformation[studentCount] studentInformation; --- -$(H5 $(IX container) $(IX element) Containers and elements) +$(H5 $(IX container) $(IX element) 容器和元素) $(P -Data structures that bring elements of a certain type together are called $(I containers). According to this definition, arrays are containers. For example, an array that holds the air temperatures of the days in July can bring 31 $(C double) values together and form $(I a container of elements of type $(C double)). +聚集特定类型元素的数据结构称为 $(I 容器)。根据定义,数组就是容器。例如,一个保存了七月每天的气温的数组能汇集 31 个 double 值,形成一个 $(I $(C double) 类型元素的容器)。 ) $(P -The variables of a container are called $(I elements). The number of elements of an array is called the $(I length) of the array. +容器的变量称为 $(I 元素)。数组元素的个数称为数组的 $(I length)。 ) -$(H5 $(IX []) Accessing the elements) +$(H5 $(IX []) 元素的访问) $(P -In order to differentiate the variables in the exercise of the previous chapter, we had to append an underscore and a number to their names as in $(C value_1). This is not possible nor necessary when a single array stores all the values under a single name. Instead, the elements are accessed by specifying the $(I element number) within square brackets: +为了与前一章练习中的变量有所区别,我们给变量的名字加上了下划线与数字,像 $(C value_1) 这样。让一个数组在一个名字下存储所有的值,那是不可能也没必要的。相反,通过指定方括号内的元素位置数就可以访问元素: ) --- @@ -100,52 +100,52 @@ In order to differentiate the variables in the exercise of the previous chapter, --- $(P -That expression can be read as $(I the element with the number 0 of the array named values). In other words, instead of typing $(C value_1) one must type $(C values[0]) with arrays. +这个表达式可以理解为 $(I 数组 values 位置 0 处的元素)。换句话说,对数组输入 $(C values[0]) 而不是键入 $(C value_1) 。 ) $(P -There are two important points worth stressing here: +还有两点值得强调: ) $(UL -$(LI $(B The numbers start with zero:) Although humans assign numbers to items starting with 1, the numbers in arrays start at 0. The values that we have numbered as 1, 2, 3, 4, and 5 before are numbered as 0, 1, 2, 3, and 4 in the array. This variation can confuse new programmers. +$(LI $(B 从零开始编号:) 虽然人们习惯于从 1 开始给项目分配编号,但数组是从 0 开始的。以前被我们编号为 1、2、3、4 和 5 的值在数组中编号为 0、1、2、3 和 4。这种变化可能会让编程新手感到困惑。 ) -$(LI $(B Two different uses of the $(C[]) characters:) Don't confuse the two separate uses of the $(C []) characters. When defining arrays, the $(C []) characters are written after the type of the elements and specify the number of elements. When accessing elements, the $(C []) characters are written after the name of the array and specify the number of the element that is being accessed: +$(LI $(B$(C[]) 的两种不同用法:) 不要混淆 $(C []) 的两种独特用法。当我们定义数组时,$(C []) 写在元素类型之后,并指定元素个数。访问元素时,$(C []) 写在数组名称之后,并指定要访问的元素位置数: --- - // This is a definition. It defines an array that consists - // of 12 elements. This array is used to hold the number - // of days in each month. + // 这个是数组的定义。它定义了一个可以存储 + // 12个元素的数组。这个数组用于存储 + // 每个月的天数。 int[12] monthDays; - // This is an access. It accesses the element that - // corresponds to December and sets its value to 31. + // 这个是数组的访问。它访问对应于 + // 十二月的元素,并设置它的值为 31。 monthDays[11] = 31; - // This is another access. It accesses the element that - // corresponds to January, the value of which is passed to - // writeln. + // 这是另一个访问。它访问对应于 + // 一月的元素,并把它的值传给 + // writeln 函数。 writeln("January has ", monthDays[0], " days."); --- $(P -$(B Reminder:) The element numbers of January and December are 0 and 11 respectively; not 1 and 12. +$(B 提醒:) 一月和十二月的元素位置数分别是 0 和 11;而不是 1 和 12。 ) ) ) -$(H5 $(IX index) Index) +$(H5 $(IX index) Index(索引)) $(P -The number of an element is called its $(I index) and the act of accessing an element is called $(I indexing). +元素的位置数称为 $(I index),访问元素的行为称为$(I 索引)。 ) $(P -An index need not be a constant value; the value of a variable can also be used as an index, making arrays even more useful. For example, the month can be determined by the value of the $(C monthIndex) variable below: +索引不必是恒定值;变量的值也能用作索引,这会让数组更有用。例如,下面通过变量 $(C monthIndex) 的值来确定该月: ) --- @@ -153,42 +153,42 @@ An index need not be a constant value; the value of a variable can also be used --- $(P -When the value of $(C monthIndex) is 2, the expression above would print the value of $(C monthDays[2]), the number of days in March. +当 $(C monthIndex) 的值为 2,上面的表达式将输出 $(C monthDays[2]) 的值,即三月的天数。 ) $(P -Only the index values between zero and one less than the length of the array are valid. For example, the valid indexes of a three-element array are 0, 1, and 2. Accessing an array with an invalid index causes the program to be terminated with an error. +只有与数组长度的差值在 0 与 1 之间的索引才有效。例如,一个具有三个元素的数组的有效索引是 0、1 和 2。访问具有无效索引的数组将导致出错从而终止程序。 ) $(P -Arrays are containers where the elements are placed side by side in the computer's memory. For example, the elements of the array holding the number of days in each month can be shown like the following (assuming a year when February has 28 days): +数组是容器,其中的元素在计算机的内存中是逐个放置的。例如,下面数组的元素保存了每个月的天数(假定一年中二月有 28 天): ) $(MONO - indexes → 0 1 2 3 4 5 6 7 8 9 10 11 - elements → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | + 索引 → 0 1 2 3 4 5 6 7 8 9 10 11 + 元素 → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | ) $(P -$(I $(B Note:) The indexes above are for demonstration purposes only; they are not stored in the computer's memory.) +$(I $(B 注意:) 上面的索引仅用于演示;并没有存储在计算机的内存中。) ) $(P -The element at index 0 has the value 31 (number of days in January); the element at index 1 has the value of 28 (number of days in February), etc. +索引 0 处元素的值为 31(一月的天数);索引1 处元素的值为 28(二月的天数)依次类推。 ) -$(H5 $(IX fixed-length array) $(IX dynamic array) $(IX static array) Fixed-length arrays vs. dynamic arrays) +$(H5 $(IX 定长数组) $(IX 动态数组) $(IX 静态数组) 定长数组与动态数组) $(P -When the length of an array is specified when the program is written, that array is a $(I fixed-length array). When the length can change during the execution of the program, that array is a $(I dynamic array). +当数组的长度是在写程序时指定时,该数组就是一个$(I 定长数组)。当长度可以在程序的执行过程中进行修改时,该数组就是一个$(I 动态数组)。 ) $(P -Both of the arrays that we have defined above are fixed-length arrays because their element counts are specified as 5 and 12 at the time when the program is written. The lengths of those arrays cannot be changed during the execution of the program. To change their lengths, the source code must be modified and the program must be recompiled. +上面我们定义的数组都为定长数组,因为元素的个数在写程序时已指定为 5 和 12。在程序的执行过程中数组的长度不可修改。要修改长度,就必须修改源代码,而且程序必须重新编译。 ) $(P -Defining dynamic arrays is simpler than defining fixed-length arrays because omitting the length makes a dynamic array: +因为省略了长度,所以定义动态数组比定长数组简单: ) --- @@ -196,17 +196,17 @@ Defining dynamic arrays is simpler than defining fixed-length arrays because omi --- $(P -The length of such an array can increase or decrease during the execution of the program. +在程序的执行过程中这种数组的长度可以增加或减少。 ) $(P -Fixed-length arrays are also known as static arrays. +定长数组也被称为静态数组。 ) -$(H5 $(IX .length) Using $(C .length) to get or set the number of elements) +$(H5 $(IX .length) 使用 $(C .length) 来获取或设置元素的个数) $(P -Arrays have properties as well, of which we will see only $(C .length) here. $(C .length) returns the number of elements of the array: +数组也有 properties(属性),在这儿我们只会看到 $(C .length)。$(C .length) 返回数组元素的个数: ) --- @@ -214,32 +214,32 @@ Arrays have properties as well, of which we will see only $(C .length) here. $(C --- $(P -Additionally, the length of dynamic arrays can be changed by assigning a value to this property: +另外,通过对这个 property 指定一个值就可以修改动态数组的 length: ) --- - int[] array; // initially empty - array.length = 5; // now has 5 elements + int[] array; // 初始时为空 + array.length = 5; // 现在有 5 个元素 --- -$(H5 An array example) +$(H5 一个数组例子) $(P -Let's now revisit the exercise with the five values and write it again by using an array: +现在我们重温一下使用五个值的例子,用数组重写一下它: ) --- import std.stdio; void main() { - // This variable is used as a loop counter + // 该变量用作循环计数器 int counter; - // The definition of a fixed-length array of five - // elements of type double + // 定义了一个可包含五个 double + // 类型元素的数组 double[5] values; - // Reading the values in a loop + // 在循环中从输入流读取元素值 while (counter < values.length) { write("Value ", counter + 1, ": "); readf(" %s", &values[counter]); @@ -253,34 +253,34 @@ void main() { ++counter; } - // The loop that calculates the fifths of the values would - // be written similarly + // 经过计算的五个数值 + //将在循环中输出 } --- -$(P $(B Observations:) The value of $(C counter) determines how many times the loops are repeated (iterated). Iterating the loop while its value is less than $(C values.length) ensures that the loops are executed once per element. As the value of that variable is incremented at the end of each iteration, the $(C values[counter]) expression refers to the elements of the array one by one: $(C values[0]), $(C values[1]), etc. +$(P $(B 观测:) $(C counter) 的值决定了循环的重复次数(迭代)。当它的值小于 $(C values.length) 时,迭代循环可以保证每个元素只执行了一次。在每个迭代结束时该变量值都会递增,$(C values[counter]) 表达式指的是数组的逐个元素:$(C values[0])、$(C values[1]) 等等。 ) $(P -To see how this program is better than the previous one, imagine needing to read 20 values. The program above would require a single change: replacing 5 with 20. On the other hand, a program that did not use an array would have to have 20 variable definitions. Furthermore, since you would be unable to use a loop to iterate the 20 values, you would also have to repeat several lines 20 times, one time for each single-valued variable. +要看一看这个程序怎样才能比以前的更好,让我们设想需要读取 20 个值。修改一下上面的程序:用 20 替换 5。另一方面,这个程序若不使用数组那将必须定义 20 个变量。此外,因为您无法使用循环遍历 20 个值,您也不得不把那几行代码重复 20 次,每个单值变量来一次。 ) -$(H5 $(IX initialization, array) Initializing the elements) +$(H5 $(IX 初始化,数组) 初始化元素) $(P -Like every variable in D, the elements of arrays are automatically initialized. The initial value of the elements depends on the type of the elements: 0 for $(C int), $(C double.nan) for $(C double), etc. +像在 D 语言中的每个变量,数组的元素自动初始化。元素的初始值取决于元素类型:$(C int) 的为 0,$(C double) 的为 $(C double.nan) 等等。 ) $(P -All of the elements of the $(C values) array above are initialized to $(C double.nan): +上面 $(C values) 数组的所有元素都初始化为 $(C double.nan): ) --- - double[5] values; // elements are all double.nan + double[5] values; // 元素都是 double.nan --- $(P -Obviously, the values of the elements can be changed later during the execution of the program. We have already seen this above when assigning to an element of an array: +显然,元素的值可以在以后程序执行期间发生变化。这在上面给数组的元素赋值时我们已经看到: ) --- @@ -288,7 +288,7 @@ Obviously, the values of the elements can be changed later during the execution --- $(P -That also happened when reading a value from the input: +从输入流中读取值时也发生了: ) --- @@ -296,7 +296,7 @@ That also happened when reading a value from the input: --- $(P -Sometimes the desired values of the elements are known at the time when the array is defined. In such cases, the initial values of the elements can be specified on the right-hand side of the assignment operator, within square brackets. Let's see this in a program that reads the number of the month from the user, and prints the number of days in that month: +有时在数组定义时元素的期望值是已知的。在这种情况下,元素的初始值可以在分配操作的右手侧方括号内指定。让我们来看一看这个程序,读取来自用户的月数,并输出当月的天数: ) --- @@ -318,27 +318,27 @@ void main() { --- $(P -As you can see, the $(C monthDays) array is defined and initialized at the same time. Also note that the number of the month, which is in the range 1-12, is converted to a valid array index in the range 0-11. Any value that is entered outside of the 1-12 range would cause the program to be terminated with an error. +正如你所看到的,$(C monthDays) 数组在定义的时候就已初始化。另外请注意, 1-12 范围内的月份数字转换为了有效的数组索引范围 0-111-12 范围外的任何值都将导致程序出现错误而终止。 ) $(P -When initializing arrays, it is possible to use a single value on the right-hand side. In that case all of the elements of the array are initialized to that value: +当初始化数组时,也可以在右手侧使用单个值。在这种情况下,所有的数组元素都初始化为该值: ) --- - int[10] allOnes = 1; // All of the elements are set to 1 + int[10] allOnes = 1; // 所有的元素都设置为 1 --- -$(H5 Basic array operations) +$(H5 数组的基本操作) $(P -Arrays provide convenience operations that apply to all of their elements. +数组提供了适用于所有元素的方便操作。 ) -$(H6 $(IX copy, array) Copying fixed-length arrays) +$(H6 $(IX 复制, 数组) 复​​制定长数组) $(P -The assignment operator copies all of the elements from the right-hand side to the left-hand side: +赋值运算符将所有元素从右手侧复制到左手侧: ) --- int[5] source = [ 10, 20, 30, 40, 50 ]; @@ -348,24 +348,24 @@ The assignment operator copies all of the elements from the right-hand side to t --- $(P -$(I $(B Note:) The meaning of the assignment operation is completely different for dynamic arrays. We will see this in a later chapter.) +$(I $(B 注意:) 赋值运算符的含义与动态数组完全不同。我们将在后面的章节中看到这一点。) ) -$(H6 $(IX ~=) $(IX append, array) $(IX add element, array) Adding elements to dynamic arrays) +$(H6 $(IX ~=) $(IX 附加, 数组) $(IX 添加元素, 数组) 给动态数组添加元素) $(P -The $(C ~=) operator adds new elements to the end of a dynamic array: +$(C ~=) 运算符是指在动态数组的尾部添加新的元素: ) --- - int[] array; // empty - array ~= 7; // array is now equal to [7] - array ~= 360; // array is now equal to [7, 360] - array ~= [ 30, 40 ]; // array is now equal to [7, 360, 30, 40] + int[] array; // 空数组 + array ~= 7; // 数组现在等于 [7] + array ~= 360; // 数组现在等于 [7, 360] + array ~= [ 30, 40 ]; // 数组现在等于 [7, 360, 30, 40] --- $(P -It is not possible to add elements to fixed-length arrays: +不可能给定长数组添加元素: ) --- @@ -423,10 +423,10 @@ $(SHELL ) ) -$(H6 $(IX ~, concatenation) $(IX concatenation, array) Combining arrays) +$(H6 $(IX ~, 连接) $(IX 连接, 数组) 连接数组) $(P -The $(C ~) operator creates a new array by combining two arrays. Its $(C ~=) counterpart combines the two arrays and assigns the result back to the left-hand side array: + $(C ~) 运算符通过连接两个数组从而创建一个新数组。$(C ~=) 组合两侧的数组并把结果赋值给左侧数组: ) --- @@ -438,15 +438,15 @@ void main() { int[] result; result = first ~ second; - writeln(result.length); // prints 20 + writeln(result.length); // 输出 20 result ~= first; - writeln(result.length); // prints 30 + writeln(result.length); // 输出 30 } --- $(P -The $(C ~=) operator cannot be used when the left-hand side array is a fixed-length array: +左侧数组是定长数组时 $(C ~=) 运算符不能使用: ) --- @@ -456,7 +456,7 @@ The $(C ~=) operator cannot be used when the left-hand side array is a fixed-len --- $(P -If the array sizes are not equal, the program is terminated with an error during assignment: +如果数组大小不一致,会导致赋值期间出错而终止程序: ) --- @@ -471,10 +471,10 @@ $(SHELL object.Error@(0): Array lengths don't match for copy: $(HILITE 20 != 21) ) -$(H6 $(IX sort) Sorting the elements) +$(H6 $(IX sort(排序)) 排序元素) $(P -$(C std.algorithm.sort) can sort the elements of many types of collections. In the case of integers, the elements get sorted from the smallest value to the greatest value. In order to use the $(C sort()) function, one must import the $(C std.algorithm) module first. (We will see functions in a later chapter.) +$(C std.algorithm.sort) 可以对许多类型集合中的元素进行排序。对于整数,元素按从小到大排序。为了使用 $(C sort()) 函数,必须先导入 $(C std.algorithm) 模块。(在后面的章节我们将看到这些函数。) ) --- @@ -489,17 +489,17 @@ void main() { --- $(P -The output: +输出: ) $(SHELL [1, 2, 3, 4, 5] ) -$(H6 $(IX reverse) Reversing the elements) +$(H6 $(IX reverse(反转)) 反转元素) $(P -$(C std.algorithm.reverse) reverses the elements in place (the first element becomes the last element, etc.): +$(C std.algorithm.reverse) 反转元素的位置(第一个元素成为最后一个元素,以此类推): ) --- @@ -514,7 +514,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -524,14 +524,14 @@ $(SHELL $(PROBLEM_COK $(PROBLEM -Write a program that asks the user how many values will be entered and then reads all of them. Have the program sort the elements using $(C sort()) and then reverse the sorted elements using $(C reverse()). +编写一个程序,要求用户输入多少值,然后全部读取。让程序使用 $(C sort()) 函数排序元素,然后使用 $(C reverse()) 函数反转排序的元素。 ) $(PROBLEM -Write a program that reads numbers from the input, and prints the odd and even ones separately but in order. Treat the value -1 specially to determine the end of the numbers; do not process that value. +编写一个程序,从输入流中读取数字,并先后按顺序分别输出奇数和偶数。并专门用值 -1 来确定数字的结束;不处理该值。 $(P -For example, when the following numbers are entered, +例如,当输入了下面的数字, ) $(SHELL @@ -539,7 +539,7 @@ $(SHELL ) $(P -have the program print the following: +程序会输出以下内容: ) $(SHELL @@ -547,16 +547,16 @@ $(SHELL ) $(P -$(B Hint:) You may want to put the elements in separate arrays. You can determine whether a number is odd or even using the $(C %) (remainder) operator. +$(B 提示:) 你可以把元素放入单独的数组。使用 $(C %)(求余数)运算符来确定数字是奇数还是偶数。 ) ) $(PROBLEM -The following is a program that does not work as expected. The program is written to read five numbers from the input and to place the squares of those numbers into an array. The program then attempts to print the squares to the output. Instead, the program terminates with an error. +以下为预期不起作用的程序。程序要求从输入流中读取五个数字,并把这些数字放入一个数组。然后程序会输出这些平方。若有错误,程序会终止。 $(P -Fix the bugs of this program and make it work as expected: +请修复程序的 bug,让它按预期的方式执行: ) --- @@ -592,11 +592,11 @@ void main() { ) Macros: - SUBTITLE=Arrays + SUBTITLE=数组 - DESCRIPTION=Basic array operations of the D programming language + DESCRIPTION=D 语言数组的基本操作 - KEYWORDS=d programming language tutorial book arrays fixed-length dynamic + KEYWORDS=D 编程语言 教程 定长 动态 $(Ergin) diff --git a/target/assert.cozum.d b/target/assert.cozum.d index 37ea2be..92280b5 100644 --- a/target/assert.cozum.d +++ b/target/assert.cozum.d @@ -62,7 +62,7 @@ An obvious correction is to pass the hour and minute variables in the right orde --- $(P -The output: +输出: ) $(SHELL diff --git a/target/assert.d b/target/assert.d index 0d8832e..e83bdfe 100644 --- a/target/assert.d +++ b/target/assert.d @@ -108,7 +108,7 @@ The other syntax of $(C assert) allows printing a custom message when the $(C as --- $(P -The output: +输出: ) $(SHELL diff --git a/target/auto_and_typeof.cozum.d b/target/auto_and_typeof.cozum.d index e32b529..6637d0b 100644 --- a/target/auto_and_typeof.cozum.d +++ b/target/auto_and_typeof.cozum.d @@ -15,7 +15,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/bit_operations.d b/target/bit_operations.d index c839cb5..d35fd95 100644 --- a/target/bit_operations.d +++ b/target/bit_operations.d @@ -153,7 +153,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/target/cast.d b/target/cast.d index 501a86a..f4051a8 100644 --- a/target/cast.d +++ b/target/cast.d @@ -151,7 +151,7 @@ The reasons for integer promotions are both historical (where the rules come fro --- $(P -The output: +输出: ) $(SHELL @@ -682,7 +682,7 @@ Although rare, some C library interfaces make it necessary to store a pointer va int * p2 = cast($(HILITE int*))savedPointerValue; --- -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/characters.d b/target/characters.d index c7ce3a0..3e6e3df 100644 --- a/target/characters.d +++ b/target/characters.d @@ -289,7 +289,7 @@ For example, the $(C write()) function, which does not start a new line automati --- $(P -The output: +输出: ) $(SHELL @@ -390,7 +390,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -572,7 +572,7 @@ Although they would mean the same character to a human reader, the single code p ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/class.d b/target/class.d index 67e6446..00f1318 100644 --- a/target/class.d +++ b/target/class.d @@ -1,6 +1,6 @@ Ddoc -$(DERS_BOLUMU $(IX class) Classes) +$(DERS_BOLUMU $(IX class) 类) $(P $(IX OOP) $(IX object oriented programming) $(IX user defined type) Similar to structs, $(C class) is a feature for defining new types. By this definition, classes are $(I user defined types). Different from structs, classes provide the $(I object oriented programming) (OOP) paradigm in D. The major aspects of OOP are the following: @@ -87,7 +87,7 @@ Class objects are constructed by the $(C new) keyword; they do not have names. T ) $(P -A class variable on the other hand is a language feature for accessing class objects. Although it may seem syntactically that operations are being performed on a class $(I variable), the operations are actually dispatched to a class $(I object). +A class variable on the other hand is a language feature for accessing class objects. 虽然语法上看起来是在类 $(I 变量) 上执行,但实际上调度了一个类 $(I object)。 ) $(P @@ -100,11 +100,11 @@ Let's consider the following code that we saw previously in the $(LINK2 /ders/d. --- $(P -The $(C new) keyword constructs an anonymous class object. $(C variable1) and $(C variable2) above merely provide access to that anonymous object: + $(C new) 关键字构造了一个匿名的类对象。上面的 $(C variable1) 和 $(C variable2) 只提供对那个匿名对象的访问: ) $(MONO - (anonymous MyClass object) variable1 variable2 + (匿名的 MyClass 对象) variable1 variable2 ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── │ ... │ │ o │ │ o │ ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── @@ -113,18 +113,18 @@ $(MONO └────────────────────┴────────────┘ ) -$(H6 $(IX copy, class) Copying) +$(H6 $(IX copy, class) 复制) $(P -Copying affects only the variables, not the object. +复制只影响变量,而不是对象。 ) $(P -Because classes are reference types, defining a new class variable as a copy of another makes two variables that provide access to the same object. The actual object is not copied. +因为类是引用类型,定义一个新的类变量做为另一个副本,将产生两个访问同一对象的变量。实际的对象没有被复制。 ) $(P -Since no object gets copied, the postblit function $(C this(this)) is not available for classes. +由于没有复制对象, postblit 函数 $(C this(this)) 不能用于类变量。 ) --- @@ -132,16 +132,16 @@ Since no object gets copied, the postblit function $(C this(this)) is not availa --- $(P -In the code above, $(C variable2) is being initialized by $(C variable1). The two variables start providing access to the same object. +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。这俩变量可访问同一对象。The two variables start providing access to the same object. ) $(P -When the actual object needs to be copied, the class must have a member function for that purpose. To be compatible with arrays, this function may be named $(C dup()). This function must create and return a new class object. Let's see this on a class that has various types of members: +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。为与数组兼容,该函数可以命名为 $(C dup()). 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: ) --- class Foo { - S o; // assume S is a struct type + S o; // 假设 S 是一个结构类型 char[] s; int i; @@ -160,11 +160,11 @@ class Foo { --- $(P -The $(C dup()) member function makes a new object by taking advantage of the constructor of $(C Foo) and returns the new object. Note that the constructor copies the $(C s) member explicitly by the $(C .dup) property of arrays. Being value types, $(C o) and $(C i) are copied automatically. +$(C dup()) 成员函数利用 $(C Foo) 的构造函数,创建并返回新的对象。注意,构造函数通过数组的 $(C .dup) 属性显式复制 $(C s) 成员。做为值类型,$(C o) 和 $(C i) 自动被复制。 ) $(P -The following code makes use of $(C dup()) to create a new object: +下面的代码演示 $(C dup()) 创建一个新的对象的用法: ) --- @@ -173,7 +173,7 @@ The following code makes use of $(C dup()) to create a new object: --- $(P -As a result, the objects that are associated with $(C var1) and $(C var2) are different. +结果是,$(C var1) 和 $(C var2) 相关联的对象是不同的。 ) $(P @@ -197,18 +197,18 @@ class Foo { immutable(Foo) imm = var1.idup(); --- -$(H6 $(IX assignment, class) Assignment) +$(H6 $(IX assignment, class) 赋值) $(P -Just like copying, assignment affects only the variables. +就像复制,赋值只影响变量。 ) $(P -Assigning to a class variable disassociates that variable from its current object and associates it with a new object. +给类变量赋值,会解除变量与当前对象的关联,并关联到一个新对象。 ) $(P -If there is no other class variable that still provides access to the object that has been disassociated from, then that object is going to be destroyed some time in the future by the garbage collector. +如何没有别的类变量能访问已解除关联对象,那该对象将由垃圾回收器在将来某个时候销毁。 ) --- @@ -218,17 +218,17 @@ If there is no other class variable that still provides access to the object tha --- $(P -The assignment above makes $(C variable1) leave its object and start providing access to $(C variable2)'s object. Since there is no other variable for $(C variable1)'s original object, that object will be destroyed by the garbage collector. +上面的赋值让 $(C variable1) 离开其对象并且开始提供对 $(C variable2) 的对象的访问。由于 $(C variable1) 的原始对象没有别的变量,该对象将由垃圾回收器销毁。 ) $(P -The behavior of assignment cannot be changed for classes. In other words, $(C opAssign) cannot be overloaded for them. +赋值操作不能改变类。换句话说,$(C opAssign) 不能因为它们而被重载。 ) -$(H6 Definition) +$(H6 定义) $(P -Classes are defined by the $(C class) keyword instead of the $(C struct) keyword: +类由 $(C class) 关键字定义而不是 $(C struct) 关键字: ) --- @@ -237,10 +237,10 @@ $(HILITE class) ChessPiece { } --- -$(H6 Construction) +$(H6 构造函数) $(P -As with structs, the name of the constructor is $(C this). Unlike structs, class objects cannot be constructed by the $(C { }) syntax. +与结构一样,构造函数的名称是 $(C this) 。不像结构,类对象不能由 $(C { }) 语法构造。 ) --- @@ -254,7 +254,7 @@ class ChessPiece { --- $(P -Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially: +不像结构,构造函数参数按顺序分配给成员时,类没有自动构造对象: ) --- @@ -273,13 +273,13 @@ Error: no constructor for ChessPiece ) $(P -For that syntax to work, a constructor must be defined explicitly by the programmer. +那样的语法要通过编译,就需要程序员显式的定义构造函数。 ) -$(H6 Destruction) +$(H6 析构函数) $(P -As with structs, the name of the destructor is $(C ~this): +像结构一样,析构函数的名称是 $(C ~this): ) --- @@ -347,7 +347,7 @@ core.exception.$(HILITE InvalidMemoryOperationError)@(0) $(H6 Member access) $(P -Same as structs, the members are accessed by the $(I dot) operator: +与结构一样,用 $(I 点) 运算符访问成员: ) --- @@ -356,49 +356,49 @@ Same as structs, the members are accessed by the $(I dot) operator: --- $(P -Although the syntax makes it look as if a member of the $(I variable) is being accessed, it is actually the member of the $(I object). Class variables do not have members, the class objects do. The $(C king) variable does not have a $(C shape) member, the anonymous object does. +虽然语法上看起来像访问 $(I 变量) 的成员,实际上是 $(I 对象) 的成员。类变量没有成员,类对象有。$(C king) 变量并没有 $(C shape) 成员,匿名对象有。 ) $(P -$(I $(B Note:) It is usually not proper to access members directly as in the code above. When that exact syntax is desired, properties should be preferred, which will be explained in $(LINK2 /ders/d.en/property.html, a later chapter).) +$(I $(B 注:) 在上面的代码中,一般不这样直接访问成员。若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的章节) 中解释。) ) -$(H6 Operator overloading) +$(H6 运算符重载) $(P -Other than the fact that $(C opAssign) cannot be overloaded for classes, operator overloading is the same as structs. For classes, the meaning of $(C opAssign) is always $(I associating a class variable with a class object). +虽然 $(C opAssign) 不能被类重载,但与结构一样,可以实现运算符重载。对于类, $(C opAssign) 意味着 $(I 一个类变量总是关联着一个类对象)。 ) -$(H6 Member functions) +$(H6 成员函数) $(P -Although member functions are defined and used the same way as structs, there is an important difference: Class member functions can be and by-default are $(I overridable). We will see this concept later in $(LINK2 /ders/d.en/inheritance.html, the Inheritance chapter). +虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是 $(I 可重写的) 。在 $(LINK2 /ders/d.cn/inheritance.html,继承章节) 我们将看到相关内容。 ) $(P -$(IX final) As overridable member functions have a runtime performance cost, without going into more detail, I recommend that you define all $(C class) functions that do not need to be overridden with the $(C final) keyword. You can apply this guideline blindly unless there are compilation errors: +$(IX final) 由于可重写的成员函数有一个运行时性能消耗,在这儿不讨论更多细节,我推荐您定义全部没必要用 $(C final) 关键字重写的 $(C class) 成员函数。若没有编译错误,您可以闭着眼睛按教程来: ) --- class C { - $(HILITE final) int func() { $(CODE_NOTE Recommended) + $(HILITE final) int func() { $(CODE_NOTE 推荐) // ... } } --- $(P -Another difference from structs is that some member functions are automatically inherited from the $(C Object) class. We will see in $(LINK2 /ders/d.en/inheritance.html, the next chapter) how the definition of $(C toString) can be changed by the $(C override) keyword. +与结构不同的是一些成员函数自动继承自 $(C Object) 类。在 $(LINK2 /ders/d.cn/inheritance.html, 下一章节) 我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 ) -$(H6 $(IX is, operator) $(IX !is) The $(C is) and $(C !is) operators) +$(H6 $(IX is, 运算符) $(IX !is) $(C is) 和 $(C !is) 运算符) $(P -These operators operate on class variables. +这些运算符应用在类变量上。 ) $(P -$(C is) specifies whether two class variables provide access to the same class object. It returns $(C true) if the object is the same and $(C false) otherwise. $(C !is) is the opposite of $(C is). +$(C is) 确定两个类变量是否提供对同一对象的访问。如果是同一对象,返回 $(C true) ,否则为 $(C false) 。$(C !is) 与 $(C is) 相反。 ) --- @@ -408,11 +408,11 @@ $(C is) specifies whether two class variables provide access to the same class o --- $(P -Since the objects of $(C myKing) and $(C yourKing) variables are different, the $(C !is) operator returns $(C true). Even though the two objects are constructed by the same character $(C'♔'), they are still two separate objects. +由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。即使这两个对象由同一字符 $(C'♔') 参数构造,, 它们仍是两个单独的对象。 ) $(P -When the variables provide access to the same object, $(C is) returns $(C true): +当变量提供对同一对象的访问时,$(C is) 返回 $(C true): ) --- @@ -421,33 +421,33 @@ When the variables provide access to the same object, $(C is) returns $(C true): --- $(P -Both of the variables above provide access to the same object. +上面的两个变量都提供对同一对象的访问。 ) -$(H5 Summary) +$(H5 摘要) $(UL -$(LI Classes and structs share common features but have big differences. +$(LI 类和结构虽然有共同特点,但还是有很大的差异。 ) -$(LI Classes are reference types. The $(C new) keyword constructs an anonymous $(I class object) and returns a $(I class variable). +$(LI 类是引用类型。The $(C new) 关键字构造一个匿名 $(I class 对象) 并返回一个 $(I class 变量)。 ) -$(LI Class variables that are not associated with any object are $(C null). Checking against $(C null) must be done by $(C is) or $(C !is), not by $(C ==) or $(C !=). +$(LI 不与任何对象相关联的类变量为 $(C null)。检查 $(C null) 必须使用 $(C is) 或 $(C !is),而不是 $(C ==) 或 $(C !=)。 ) -$(LI The act of copying associates an additional variable with an object. In order to copy class objects, the type must have a special function likely named $(C dup()). +$(LI 复制操作将增加一个与对象关联的变量。为了复制类对象,类型必须有一个类似于命名为 $(C dup()) 的特殊函数。 ) -$(LI Assignment associates a variable with an object. This behavior cannot be changed. +$(LI 赋值会把一个变量与一个对象相关联。该行为不能被修改。 ) ) Macros: - SUBTITLE=Classes + SUBTITLE=类 - DESCRIPTION=The basic object oriented programming (OOP) feature of the D programming language. + DESCRIPTION=D语言基本的面向对象编程 (OOP) 功能。 - KEYWORDS=d programming lesson book tutorial class + KEYWORDS=D 语言编程教程 class diff --git a/target/concurrency.d b/target/concurrency.d index a01965d..4fe4617 100644 --- a/target/concurrency.d +++ b/target/concurrency.d @@ -316,7 +316,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -680,7 +680,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -943,7 +943,7 @@ void workerFunc() { --- $(P -The output: +输出: ) $(SHELL @@ -975,7 +975,7 @@ void workerFunc() { --- $(P -The output: +输出: ) $(SHELL @@ -1046,7 +1046,7 @@ The owner can catch the exception to do something special like terminating grace --- $(P -The output: +输出: ) $(SHELL @@ -1287,7 +1287,7 @@ $(IX thread_joinAll) The $(C thread_joinAll()) call that is seen at the end of $ ) $(P -The output: +输出: ) $(SHELL @@ -1303,7 +1303,7 @@ second, I am exiting. first, I am exiting. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/concurrency_shared.d b/target/concurrency_shared.d index 8e32f64..17bbe8a 100644 --- a/target/concurrency_shared.d +++ b/target/concurrency_shared.d @@ -161,7 +161,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -301,7 +301,7 @@ The incorrect program behavior above is due to more than one thread accessing th --- $(P -The output: +输出: ) $(SHELL @@ -587,7 +587,7 @@ $(HILITE shared) static this() { --- $(P -The output: +输出: ) $(SHELL @@ -726,7 +726,7 @@ $(LI $(C core.sync.semaphore)) ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/cond_comp.d b/target/cond_comp.d index 92c22ab..6ea1ec8 100644 --- a/target/cond_comp.d +++ b/target/cond_comp.d @@ -638,7 +638,7 @@ $(P Please refer to $(LINK2 http://dlang.org/traits.html, the $(C __traits) documentation) and $(LINK2 http://dlang.org/phobos/std_traits.html, the $(C std.traits) documentation) for more information. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/const_and_immutable.d b/target/const_and_immutable.d index 61158ae..e17473a 100644 --- a/target/const_and_immutable.d +++ b/target/const_and_immutable.d @@ -721,7 +721,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -732,7 +732,7 @@ olleh ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/destroy.d b/target/destroy.d index 9a39c66..cb22730 100644 --- a/target/destroy.d +++ b/target/destroy.d @@ -131,7 +131,7 @@ $(P As has been seen in the previous example, $(C destroy()) is used when resources need to be released at a specific time without relying on the garbage collector. ) -$(H5 Example) +$(H5 样例) $(P We had designed an $(C XmlElement) struct in the $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions chapter). That struct was being used for printing XML elements in the format $(C <tag>value</tag>). Printing the closing tag has been the responsibility of the destructor: @@ -444,7 +444,7 @@ For that reason, do not define $(C scoped()) variables by the actual type: immutable d = scoped!C(); $(CODE_NOTE correct) --- -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/encapsulation.d b/target/encapsulation.d index b55e100..9b1d306 100644 --- a/target/encapsulation.d +++ b/target/encapsulation.d @@ -219,7 +219,7 @@ $(P $(IX export) Additionally, the $(C export) attribute specifies accessibility from the outside of the program. ) -$(H5 Definition) +$(H5 定义) $(P Protection attributes can be specified in three ways. @@ -352,7 +352,7 @@ $(P Encapsulation is one of the most powerful benefits of object oriented programming. ) -$(H5 Example) +$(H5 样例) $(P Let's define the $(C Student) struct and the $(C School) class by taking advantage of encapsulation and let's use them in a short test program. diff --git a/target/enum.d b/target/enum.d index 25c8a82..6490b24 100644 --- a/target/enum.d +++ b/target/enum.d @@ -129,7 +129,7 @@ Unless explicitly specified by the programmer, the numerical values of $(C enum) --- $(P -The output: +输出: ) $(SHELL @@ -147,7 +147,7 @@ It is possible to manually (re)set the values at any point in the $(C enum). Tha --- $(P -The output: +输出: ) $(SHELL @@ -209,7 +209,7 @@ $(P It is possible to create manifest constants of arrays an associative arrays as well. However, as we will see later in $(LINK2 /ders/d.en/const_and_immutable.html, the Immutability chapter), $(C enum) arrays and associative arrays may have hidden costs. ) -$(H5 Properties) +$(H5 Properties(属性)) $(P The $(C .min) and $(C .max) properties are the minimum and maximum values of an $(C enum) type. When the values of the $(C enum) type are consecutive, they can be iterated over in a $(C for) loop within these limits: @@ -245,7 +245,7 @@ Note that a $(C foreach) loop over that range would leave the $(C .max) value ou --- $(P -The output: +输出: ) $(SHELL diff --git a/target/exceptions.d b/target/exceptions.d index 4198347..ca4c3c3 100644 --- a/target/exceptions.d +++ b/target/exceptions.d @@ -804,7 +804,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -955,7 +955,7 @@ $(P It is up to the higher layer functions that call this function to decide what to do with thrown exceptions. They may catch the exceptions that we throw to remedy the situation. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/fibers.d b/target/fibers.d index fcb7a43..21688e3 100644 --- a/target/fibers.d +++ b/target/fibers.d @@ -643,7 +643,7 @@ struct Tree { } } -/* Returns an InputRange to the nodes of the tree. The +/* Returns an InputRange to the nodes of the tree. 这个 * returned range is empty if the tree has no elements (i.e. * if 'root' is 'null'). */ auto byNode(const(Tree) tree) { @@ -1077,7 +1077,7 @@ $(P $(IX M:N, threading) One obvious shortcoming of fibers is that only one core of the CPU is used for the caller and its fibers. The other cores of the CPU might be sitting idle, effectively wasting resources. It is possible to use different designs like the $(I M:N threading model (hybrid threading)) that employ other cores as well. I encourage you to research and compare different threading models. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/foreach.d b/target/foreach.d index bc268f4..c63ea1e 100644 --- a/target/foreach.d +++ b/target/foreach.d @@ -293,7 +293,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -391,7 +391,7 @@ $(C foreach_reverse) works the same way as $(C foreach) except it iterates in th --- $(P -The output: +输出: ) $(SHELL diff --git a/target/foreach_opapply.cozum.d b/target/foreach_opapply.cozum.d index d80ae74..4e24bfe 100644 --- a/target/foreach_opapply.cozum.d +++ b/target/foreach_opapply.cozum.d @@ -145,7 +145,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/foreach_opapply.d b/target/foreach_opapply.d index 0e843dd..7dbf4de 100644 --- a/target/foreach_opapply.d +++ b/target/foreach_opapply.d @@ -534,7 +534,7 @@ Also note that the type of the delegate parameter is $(C ref const(Point)). This ) $(P -The output: +输出: ) $(SHELL diff --git a/target/formatted_output.d b/target/formatted_output.d index 2023753..5368f61 100644 --- a/target/formatted_output.d +++ b/target/formatted_output.d @@ -61,7 +61,7 @@ Sometimes this is not sufficient. The output may have to be in a very specific f --- $(P -The output: +输出: ) $(SHELL diff --git a/target/function_overloading.d b/target/function_overloading.d index 137ef29..ec6da36 100644 --- a/target/function_overloading.d +++ b/target/function_overloading.d @@ -171,7 +171,7 @@ Note that this overload makes use of the already-defined overload for $(C TimeOf --- $(P -The output: +输出: ) $(SHELL diff --git a/target/function_parameters.d b/target/function_parameters.d index 5f68297..dacc44e 100644 --- a/target/function_parameters.d +++ b/target/function_parameters.d @@ -72,7 +72,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -106,7 +106,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -547,7 +547,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -734,7 +734,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -909,7 +909,7 @@ $(P $(I $(B Note:) Although it is conceivable that the compiler could inspect $(C pick()) and detect the bug even without the $(C return) keyword, it cannot do so in general because the bodies of some functions may not be available to the compiler during every compilation.) ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/functions_more.d b/target/functions_more.d index a5f3f22..9dba3bd 100644 --- a/target/functions_more.d +++ b/target/functions_more.d @@ -325,7 +325,7 @@ The following code demonstrates this by printing the type of the returned expres --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -923,7 +923,7 @@ $(SHELL_SMALL foo is called 1 times. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/goto.d b/target/goto.d index abc3cbe..73bd19b 100644 --- a/target/goto.d +++ b/target/goto.d @@ -237,7 +237,7 @@ $(LI $(C goto case $(I expression)) causes the execution to continue to the $(C ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/inheritance.cozum.d b/target/inheritance.cozum.d index 1529983..d7adb5d 100644 --- a/target/inheritance.cozum.d +++ b/target/inheritance.cozum.d @@ -173,7 +173,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/inheritance.d b/target/inheritance.d index 8c8a1d2..37a2dc6 100644 --- a/target/inheritance.d +++ b/target/inheritance.d @@ -59,7 +59,7 @@ $(I $(B Note:) It would be more useful to produce the time string by a $(C toStr ) $(P -The output: +输出: ) $(SHELL @@ -147,7 +147,7 @@ The members that are inherited from the superclass can be accessed as if they we --- $(P -The output: +输出: ) $(SHELL @@ -496,7 +496,7 @@ $(CODE_XREF Clock_AlarmClock)void main() { --- $(P -The output: +输出: ) $(SHELL @@ -732,7 +732,7 @@ $(P The $(C ChessPiece) class is still abstract even though $(C isValid()) was already implemented, but the $(C Pawn) class is non-abstract and can be instantiated. ) -$(H5 Example) +$(H5 样例) $(P Let's consider a class hierarchy that represents railway vehicles: @@ -942,7 +942,7 @@ The passengers are getting off $(SHELL_NOTE) The crates are being unloaded $(SHELL_NOTE) ) -$(H5 Summary) +$(H5 摘要) $(UL $(LI Inheritance is used for the "is a" relationship.) diff --git a/target/input.d b/target/input.d index 8126928..7952bc0 100644 --- a/target/input.d +++ b/target/input.d @@ -174,7 +174,7 @@ As a general rule, you can use $(STRING " %s") for every data that is read --- $(P -The output: +输出: ) $(SHELL diff --git a/target/interface.d b/target/interface.d index 5874380..5c90c21 100644 --- a/target/interface.d +++ b/target/interface.d @@ -30,7 +30,7 @@ $(P Despite these restrictions, there is no limit on the number of $(C interface)s that a class can inherit from. (In contrast, a class can inherit from up to one $(C class).) ) -$(H5 Definition) +$(H5 定义) $(P Interfaces are defined by the $(C interface) keyword, the same way as classes: @@ -527,7 +527,7 @@ $(P $(I Abstracting away) parts of a program from each other allows making changes in one part of the program without needing to modify the other parts. When implementations of certain parts of the program are $(I behind) a particular interface, the code that uses only that interface does not get affected. ) -$(H5 Example) +$(H5 样例) $(P The following program defines the $(C SoundEmitter), $(C MusicalInstrument), and $(C CommunicationDevice) interfaces: @@ -627,7 +627,7 @@ rrring beep ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/invariant.d b/target/invariant.d index d06fbce..66f4f54 100644 --- a/target/invariant.d +++ b/target/invariant.d @@ -438,7 +438,7 @@ $(SHELL core.exception.AssertError@deneme.d: Assertion failure ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/is_expr.d b/target/is_expr.d index 1b67cd9..ed870ac 100644 --- a/target/is_expr.d +++ b/target/is_expr.d @@ -61,7 +61,7 @@ As another example, because $(C void) is not valid as the key type of an associa --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -231,7 +231,7 @@ Function templates can take advantage of such information to behave differently --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -414,7 +414,7 @@ Let's now call that function template with various types that we have defined ab --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/target/lambda.d b/target/lambda.d index 330573c..f2513e4 100644 --- a/target/lambda.d +++ b/target/lambda.d @@ -78,7 +78,7 @@ $(P As the two $(C static assert) lines above indicate, $(C f) is a $(C function) and $(C d) is a $(C delegate). We will see later below that $(C d) can be called directly but $(C f) needs an object to be called on. ) -$(H6 Definition) +$(H6 定义) $(P $(IX function) Similar to regular pointers, each function pointer type can point exactly to a particular type of function; the parameter list and the return type of the function pointer and the function must match. Function pointers are defined by the $(C function) keyword between the return type and the parameter list of that particular type: @@ -339,7 +339,7 @@ int negativeOf(int number) { --- $(P -The output: +输出: ) $(SHELL @@ -751,7 +751,7 @@ Let's call that function with a trivial delegate that always returns the same va --- $(P -The output: +输出: ) $(SHELL @@ -1169,7 +1169,7 @@ $(P The advantage of this program is that, even though there are still a total of 10 calls made to various $(C toString()) functions, those calls collectively produce a single $(C string), not 10. ) -$(H5 Summary) +$(H5 摘要) $(UL $(LI The $(C function) keyword is for defining function pointers to be called later just like a function.) diff --git a/target/literals.d b/target/literals.d index d63f3c3..017fb0a 100644 --- a/target/literals.d +++ b/target/literals.d @@ -109,7 +109,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -315,7 +315,7 @@ String literals that start with $(C q) and that use $(C {) and $(C }) as delimit --- $(P -The output: +输出: ) $(SHELL diff --git a/target/main.d b/target/main.d index 36cf423..44ff113 100644 --- a/target/main.d +++ b/target/main.d @@ -401,7 +401,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -442,7 +442,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -454,7 +454,7 @@ Its output: ) ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/memory.d b/target/memory.d index e93224d..9fb8845 100644 --- a/target/memory.d +++ b/target/memory.d @@ -473,7 +473,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -1131,7 +1131,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -1303,7 +1303,7 @@ $(P Note that $(C Object.factory()) takes the fully qualified name of the type of the object. Also, the return type of $(C factory()) is $(C Object); so, it must be cast to the actual type of the object before being used in the program. ) -$(H5 Summary) +$(H5 摘要) $(UL $(LI The garbage collector scans the memory at unspecified times, determines the objects that cannot possibly be reached anymore by the program, destroys them, and reclaims their memory locations.) diff --git a/target/mixin.d b/target/mixin.d index 024a3c0..6755c23 100644 --- a/target/mixin.d +++ b/target/mixin.d @@ -86,7 +86,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -279,7 +279,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -465,7 +465,7 @@ $(P Due to a bug as of this writing, the same behavior does not apply to other special functions like constructors. Additionally, a destructor mixed in by a string mixin does conflict with the existing destructor of the type. ) -$(H5 Example) +$(H5 样例) $(P ($(I $(B Note:) Specifying predicates as strings was used more commonly before the lambda syntax was added to D. Although string predicates as in this example are still used in Phobos, the $(C =>) lambda syntax may be more suitable in most cases.)) diff --git a/target/nested.d b/target/nested.d index 2ebbce4..eff727d 100644 --- a/target/nested.d +++ b/target/nested.d @@ -260,7 +260,7 @@ Instead of $(C this.new) and $(C this.outer), $(C .new) and $(C .outer) can be u auto var2 = $(HILITE nestedObject.outer); --- -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/null_is.d b/target/null_is.d index 23b0471..f8349d3 100644 --- a/target/null_is.d +++ b/target/null_is.d @@ -72,7 +72,7 @@ The special value $(C null) can be printed just like any other value. --- $(P -The output: +输出: ) $(SHELL @@ -259,7 +259,7 @@ Slices can be assigned $(C null) as well: slice = null; // Not providing access to any element --- -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/object.d b/target/object.d index 607d8a3..345ce39 100644 --- a/target/object.d +++ b/target/object.d @@ -209,7 +209,7 @@ class AlarmClock : Clock { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/operator_overloading.d b/target/operator_overloading.d index 3bc5545..67885f5 100644 --- a/target/operator_overloading.d +++ b/target/operator_overloading.d @@ -240,7 +240,7 @@ Unlike some other languages, the copy inside post-increment has no cost in D if ) --- - /* The value of the expression is not used below. The + /* The value of the expression is not used below. 这个 * only effect of the expression is incrementing 'i'. */ i++; --- @@ -1442,7 +1442,7 @@ void $(CODE_DONT_TEST)main() { --- $(P -The output: +输出: ) $(SHELL @@ -1726,7 +1726,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/parallelism.d b/target/parallelism.d index d82234d..fce9c65 100644 --- a/target/parallelism.d +++ b/target/parallelism.d @@ -1125,7 +1125,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -1180,7 +1180,7 @@ $(P $(C TaskPool.finish()) tells the object to stop processing when all of its current tasks are completed. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/parameter_flexibility.cozum.d b/target/parameter_flexibility.cozum.d index 2830145..1dfb531 100644 --- a/target/parameter_flexibility.cozum.d +++ b/target/parameter_flexibility.cozum.d @@ -90,7 +90,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/parameter_flexibility.d b/target/parameter_flexibility.d index dffcd8a..dcf0efa 100644 --- a/target/parameter_flexibility.d +++ b/target/parameter_flexibility.d @@ -65,7 +65,7 @@ $(CODE_XREF printAA)void main() { --- $(P -The output: +输出: ) $(SHELL @@ -106,7 +106,7 @@ The parameter values can still be specified when needed, and not necessarily all --- $(P -The output: +输出: ) $(SHELL @@ -123,7 +123,7 @@ The following call specifies both of the parameters: --- $(P -The output: +输出: ) $(SHELL @@ -331,7 +331,7 @@ As long as the mandatory parameters are specified, the rest are optional: --- $(P -The output: +输出: ) $(SHELL diff --git a/target/pointers.cozum.d b/target/pointers.cozum.d index 6947c09..6d8971d 100644 --- a/target/pointers.cozum.d +++ b/target/pointers.cozum.d @@ -99,7 +99,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -190,7 +190,7 @@ void $(CODE_DONT_TEST)main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/pointers.d b/target/pointers.d index f73a92c..fec78b6 100644 --- a/target/pointers.d +++ b/target/pointers.d @@ -588,7 +588,7 @@ $(LI Pointing at the next element.) ) $(P -The output: +输出: ) $(SHELL @@ -721,7 +721,7 @@ Although it is not absolutely necessary in D, pointers can directly be used for --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -1308,7 +1308,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/target/ranges.d b/target/ranges.d index f9023ad..284b652 100644 --- a/target/ranges.d +++ b/target/ranges.d @@ -523,7 +523,7 @@ import std.array; --- $(P -The output: +输出: ) $(SHELL @@ -1173,7 +1173,7 @@ $(IX retro, std.range) A good $(C BidirectionalRange) example is the $(C std.ran --- $(P -The output: +输出: ) $(SHELL @@ -1426,7 +1426,7 @@ As expected, printing the entire range should contain all of the elements: --- $(P -The output: +输出: ) $(SHELL @@ -1669,7 +1669,7 @@ We can pass this range to other range algorithms. For example, to $(C retro()), --- $(P -The output: +输出: ) $(SHELL @@ -1937,7 +1937,7 @@ In the code above, $(C appender) is called with an array and returns a special r --- $(P -The output: +输出: ) $(SHELL @@ -1954,7 +1954,7 @@ $(C Appender) supports the $(C ~=) operator as well: --- $(P -The output: +输出: ) $(SHELL @@ -1971,7 +1971,7 @@ $(P The $(C std.range) module includes many range templates. We will see these templates in the next chapter. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/ranges_more.d b/target/ranges_more.d index 2c28e54..083b0a6 100644 --- a/target/ranges_more.d +++ b/target/ranges_more.d @@ -227,7 +227,7 @@ For example, when it is used with a slice, the negative elements can be accessed --- $(P -The output: +输出: ) $(SHELL @@ -339,7 +339,7 @@ Compile-time polymorphism has to deal with the fact that every instantiation of --- $(P -The output: +输出: ) $(SHELL @@ -409,7 +409,7 @@ $(P Similarly, $(C outputRangeObject()) works with $(C OutputRange) ranges and allows their use as $(I an $(C OutputRange) that accepts specific types of elements). ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/scope.d b/target/scope.d index c147bae..23fdc6d 100644 --- a/target/scope.d +++ b/target/scope.d @@ -3,18 +3,18 @@ Ddoc $(DERS_BOLUMU $(IX scope(success)) $(IX scope(exit)) $(IX scope(failure)) $(CH4 scope)) $(P -As we have seen in the previous chapter, expressions that must always be executed are written in the $(C finally) block, and expressions that must be executed when there are error conditions are written in $(C catch) blocks. +在前面的章节我们已经看到,写在 $(C finally) 块里的表达式一定总被执行。当有错误条件的时候,写在 $(C catch) 块里的表达式总被执行。 ) $(P -We can make the following observations about the use of these blocks: +对于这些块的用法,我们可以作以下观测: ) $(UL -$(LI $(C catch) and $(C finally) cannot be used without a $(C try) block.) +$(LI 没有一个 $(C try) 块, $(C catch) 和 $(C finally) 不能使用。) -$(LI Some of the variables that these blocks need may not be accessible within these blocks: +$(LI 属于块的某些变量,块范围内有可能访问不到: --- void foo(ref int r) { @@ -31,34 +31,34 @@ void foo(ref int r) { --- $(P -That function first modifies the reference parameter and then reverts this modification when an exception is thrown. Unfortunately, $(C addend) is accessible only in the $(C try) block, where it is defined. $(I ($(B Note:) This is related to name scopes, as well as object lifetimes, which will be explained in $(LINK2 /ders/d.en/lifetimes.html, a later chapter.))) +上面这个函数首先修改引用参数,当出现异常时再恢复修改。不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。$(I ($(B 注:) 这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的章节) 中解释。)) ) ) -$(LI Writing all of potentially unrelated expressions in the single $(C finally) block at the bottom separates those expressions from the actual code that they are related to. +$(LI 把所有可能无关联的表达式写在底部单独的 $(C finally) 块,就可以分离那些有关联的可执行代码。 ) ) $(P -The $(C scope) statements have similar functionality with the $(C catch) and $(C finally) scopes but they are better in many respects. Like $(C finally), the three different $(C scope) statements are about executing expressions when leaving scopes: +$(C scope) 语句与 $(C catch) 和 $(C finally) 有相似功能,但在许多方面表现的更好。像 $(C finally),下面三个不同的 $(C scope) 语句就是关于离开作用域时应执行的表达式: ) $(UL -$(LI $(C scope(exit)): the expression is always executed when exiting the scope, regardless of whether successfully or due to an exception) +$(LI $(C scope(exit)):表达式总是在退出作用域时被执行, 无论是否成功或出现异常。) -$(LI $(C scope(success)): the expression is executed only if the scope is being exited successfully) +$(LI $(C scope(success)):表达式只在成功退出作用域时被执行。) -$(LI $(C scope(failure)): the expression is executed only if the scope is being exited due to an exception) +$(LI $(C scope(failure)):表达式只在因出现异常而退出作用域时被执行。) ) $(P -Although these statements are closely related to exceptions, they can be used without a $(C try-catch) block. +虽然这些语句只在特殊情况下使用,但是没有 $(C try-catch) 块也能用。 ) $(P -As an example, let's write the function above with a $(C scope(failure)) statement: +例如,让我们用 $(C scope(failure)) 语句写一下面的函数: ) --- @@ -73,11 +73,11 @@ void foo(ref int r) { --- $(P -The $(C scope(failure)) statement above ensures that the $(C r -= addend) expression will be executed if the function's scope is exited due to an exception. A benefit of $(C scope(failure)) is the fact that the expression that reverts another expression is written close to it. +上面的 $(C scope(failure)) 确保 $(C r -= addend) 表达式在因异常退出时被执行。$(C scope(failure)) 的好处是靠近它的表达式可以还原已写的另一个表达式。 ) $(P -$(C scope) statements can be specified as blocks as well: +$(C scope) 语句也可以像块一样使用: ) --- @@ -87,7 +87,7 @@ $(C scope) statements can be specified as blocks as well: --- $(P -Here is another function that tests all three of these statements: +这儿是另一个函数,来测试全部三个语句: ) --- @@ -108,7 +108,7 @@ void test() { --- $(P -If no exception is thrown, the output of the function includes only the $(C scope(exit)) and $(C scope(success)) expressions: +如果没有抛出异常, 函数的输出只包括 $(C scope(exit)) 和 $(C scope(success)) 表达式: ) $(SHELL @@ -119,7 +119,7 @@ when exiting 1 ) $(P -If an exception is thrown, the output includes the $(C scope(exit)) and $(C scope(failure)) expressions: +如果抛出异常, 输出包括 $(C scope(exit)) 和 $(C scope(failure)) 表达式: ) $(SHELL @@ -131,12 +131,12 @@ object.Exception@...: the error message ) $(P -As seen in the outputs, the blocks of the $(C scope) statements are executed in reverse order. This is because later code may depend on previous variables. Executing the $(C scope) statements in reverse order enables undoing side effects of earlier expressions in a consistent order. +在输出中我们看到,$(C scope) 语句块是按相反的顺序执行的。这是因为后边的代码依赖于前边的变量。这样按相反顺序执行 $(C scope) 语句,让程序能按一致的顺序撤消前边表达式的副作用。 ) Macros: SUBTITLE=scope - DESCRIPTION=The scope(success), scope(failure), and scope(exit) statements that are used for specifying expressions that must be executed when exiting scopes. + DESCRIPTION=scope(success),scope(failure),和 scope(exit) 语句用于当退出作用域时一定要执行的特殊表达式。 - KEYWORDS=d programming language tutorial book scope + KEYWORDS=D 语言编程教程 scope diff --git a/target/slices.cozum.d b/target/slices.cozum.d index 9667aea..4b73230 100644 --- a/target/slices.cozum.d +++ b/target/slices.cozum.d @@ -35,6 +35,6 @@ void main() { Macros: SUBTITLE=Slices and Other Array Features Solution - DESCRIPTION=Programming in D exercise solutions: arrays + DESCRIPTION=D 语言编程习题解答:数组 - KEYWORDS=programming in d tutorial arrays solution + KEYWORDS=D 语言编程教程 数组 习题解答 diff --git a/target/slices.d b/target/slices.d index e4aadcb..b83f15b 100644 --- a/target/slices.d +++ b/target/slices.d @@ -87,7 +87,7 @@ The four variables in the code above are slices; they provide access to four par --- $(P -The output: +输出: ) $(SHELL @@ -121,7 +121,7 @@ It is legal to have the beginning and the end indexes to be equal. In that case --- $(P -The output: +输出: ) $(SHELL @@ -171,7 +171,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -194,7 +194,7 @@ We have seen so far that the assignment operator $(I modifies) values of variabl --- $(P -The output: +输出: ) $(SHELL @@ -522,7 +522,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -553,7 +553,7 @@ This feature works not only using two arrays; it can also be used with an array --- $(P -The output: +输出: ) $(SHELL @@ -570,7 +570,7 @@ To assign a specific value to all elements: --- $(P -The output: +输出: ) $(SHELL @@ -760,7 +760,7 @@ $(SHELL [[0, 0, 0], [0, 0, 0]] ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/special_functions.d b/target/special_functions.d index 00accc3..b82cb08 100644 --- a/target/special_functions.d +++ b/target/special_functions.d @@ -372,7 +372,7 @@ Note that it is not possible to type $(C Test()) inside $(C static opCall()). Th --- $(P -The output: +输出: ) $(SHELL @@ -1148,7 +1148,7 @@ struct Duration { --- $(P -The output: +输出: ) $(SHELL @@ -1196,7 +1196,7 @@ struct Duration { --- $(P -The output: +输出: ) $(SHELL @@ -1208,7 +1208,7 @@ $(P $(B Note:) Although convenient, assigning different types to each other may cause confusions and errors. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/strings.cozum.d b/target/strings.cozum.d index 234c93a..103c767 100644 --- a/target/strings.cozum.d +++ b/target/strings.cozum.d @@ -1,15 +1,15 @@ Ddoc -$(COZUM_BOLUMU Strings) +$(COZUM_BOLUMU 字符串) $(OL $(LI -Although some of the functions in Phobos modules will be easy to use with strings, library documentations are usually terse compared to tutorials. You may find especially the Phobos ranges confusing at this point. We will see Phobos ranges in a later chapter. +尽管 Phobos 模块中的一些函数易于处理字符串,但库文档通常比教程简短。此刻你可能会发现 Phobos 的 range 尤其让人迷惑。在稍后的一章中我们将看到 Phobos 的 range。 ) $(LI -Many other functions may be chained as well: +许多别的函数可以嵌套使用: --- import std.stdio; @@ -29,7 +29,7 @@ void main() { ) -$(LI This program uses two indexes to make a slice: +$(LI 这段程序使用两个索引值来生成一个切片: --- import std.stdio; @@ -56,8 +56,8 @@ void main() { ) Macros: - SUBTITLE=Strings Solution + SUBTITLE=字符串 习题解答 - DESCRIPTION=Programming in D exercise solutions: strings + DESCRIPTION=D 语言编程习题解答:字符串 - KEYWORDS=programming in d tutorial strings solution + KEYWORDS=D 语言教程 字符串 习题解答 diff --git a/target/strings.d b/target/strings.d index d6a8ae9..7ef8951 100644 --- a/target/strings.d +++ b/target/strings.d @@ -1,23 +1,23 @@ Ddoc -$(DERS_BOLUMU Strings) +$(DERS_BOLUMU 字符串) $(P -We have used strings in many programs that we have seen so far. Strings are a combination of the two features that we have covered in the last three chapters: characters and arrays. In the simplest definition, strings are nothing but arrays of characters. For example, $(C char[]) is a type of string. +迄今为至,我们已经看到,好多程序都用到了字符串。字符串是在过去三章中介绍的两种功能的组合:字符和数组。在最简单的定义中,字符串只不过是字符数组。例如,$(C char[]) 是一个字符串类型。 ) $(P -This simple definition may be misleading. As we have seen in the $(LINK2 /ders/d.en/characters.html, Characters chapter), D has three separate character types. Arrays of these character types lead to three separate string types, some of which may have surprising outcomes in some string operations. +这个简单的定义可能是个误导。正如我们在 $(LINK2 /ders/d.cn/characters.html, 字符) 一章中看到的,D 具有三种独立的字符类型。这些字符类型的数组对应着三种独立的字符串类型,其中一些可能会在一些字符串操作上有出人意料的结果。 ) -$(H5 $(IX readln) $(IX strip) $(C readln) and $(C strip), instead of $(C readf)) +$(H5 $(IX readln) $(IX strip) 使用 $(C readln) 和 $(C strip),而非 $(C readf)) $(P -There are surprises even when reading strings from the terminal. +从终端读字符串您会有一些惊奇。 ) $(P -Being character arrays, strings can contain control characters like $(STRING '\n') as well. When reading strings from the input, the control character that corresponds to the Enter key that is pressed at the end of the input becomes a part of the string as well. Further, because there is no way to tell $(C readf()) how many characters to read, it continues to read until the end of the entire input. For these reasons, $(C readf()) does not work as intended when reading strings: +作为字符数组,字符串能包含像 $(STRING '\n') 这样的控制字符。当从输入中读取字符串,输入结束时按的 Enter 键对应的控制字符将变成字符串的一部分。另外,因为没有办法告诉 $(C readf()) 要读取多少字符,它持续读,直到整个输入结束。因而,$(C readf()) 不能如愿读取字符串: ) --- @@ -34,31 +34,31 @@ void main() { --- $(P -The Enter key that the user presses after the name does not terminate the input. $(C readf()) continues to wait for more characters to add to the string: +用户在名字之后按的 Enter 键并没有结束输入。$(C readf()) 继续等待新输入的字符以添加到字符串: ) $(SHELL -What is your name? Mert - $(SHELL_NOTE The input is not terminated although Enter has been pressed) - $(SHELL_NOTE (Let's assume that Enter is pressed a second time here)) +What is your name?Mert + $(SHELL_NOTE 虽然按了 Enter 键,但输入没有中断) + $(SHELL_NOTE (让我们假设在这儿第二次按了 Enter 键)) ) $(P -One way of terminating the standard input stream in a terminal is pressing Ctrl-D under Unix-based systems and Ctrl-Z under Windows systems. If the user eventually terminates the input that way, we see that the new-line characters have been read as parts of the string as well: +在终端结束标准输入流的方法随系统而不同,在 Unix 系统下按 Ctrl-D,在 Windows 系统下按 Ctrl-Z。如果用户最后这样结束输入,我们看到换行符已作为字符串的一部分被读取: ) $(SHELL Hello Mert - $(SHELL_NOTE_WRONG new-line character after the name) -! $(SHELL_NOTE_WRONG (one more before the exclamation mark)) + $(SHELL_NOTE_WRONG 名字之后的换行符) +!$(SHELL_NOTE_WRONG (在感叹号前还有一个)) ) $(P -The exclamation mark appears after those characters instead of being printed right after the name. +感叹号出现在了那些字符之后,而不是在名字之后立即输出。 ) $(P -$(C readln()) is more suitable when reading strings. Short for "read line", $(C readln()) reads until the end of the line. It is used differently because the $(STRING " %s") format string and the $(C &) operator are not needed: +$(C readln()) 更适合读取字符串。它是“read line”的缩写,$(C readln()) 读取到行尾。不同的是没有 $(STRING " %s") 格式字符串并且不需要 $(C &) 运算符: ) --- @@ -75,17 +75,17 @@ void main() { --- $(P -$(C readln()) stores the new-line character as well. This is so that the program has a way of determining whether the input consisted of a complete line or whether the end of input has been reached: +$(C readln()) 也存储换行符。这就让程序有办法确定输入是否包含一条完整语句或者输入是否已结束: ) $(SHELL -What is your name? Mert +What is your name?Mert Hello Mert -! $(SHELL_NOTE_WRONG new-line character before the exclamation mark) +!$(SHELL_NOTE_WRONG 感叹号前有换行符) ) $(P -Such control characters as well as all whitespace characters at both ends of strings can be removed by $(C std.string.strip): +像字符串两端的空白字符这样的控制字符能被 $(C std.string.strip) 移除: ) --- @@ -104,16 +104,16 @@ void main() { --- $(P -The $(C strip()) expression above returns a new string that does not contain the trailing control characters. Assigning that return value back to $(C name) produces the intended output: +上面的 $(C strip()) 表达式返回一个不包含尾随控制符的新字符串。返回值再赋值给 $(C name),得到预期的输出: ) $(SHELL -What is your name? Mert -Hello Mert! $(SHELL_NOTE no new-line character) +What is your name?Mert +Hello Mert!$(SHELL_NOTE 没有换行符) ) $(P -$(C readln()) can be used without a parameter. In that case it $(I returns) the line that it has just read. Chaining the result of $(C readln()) to $(C strip()) enables a shorter and more readable syntax: +$(C readln()) 没有参数也可以使用。在这种情况下它$(I 返回)刚刚读入的行。把 $(C readln()) 的结果放到 $(C strip()) 中,能得到更短且可读性更好的语法: ) --- @@ -121,13 +121,13 @@ $(C readln()) can be used without a parameter. In that case it $(I returns) the --- $(P -I will start using that form after introducing the $(C string) type below. +在介绍了下面的 $(C string) 类型之后,我们将开始使用这种格式。 ) -$(H5 $(IX formattedRead) $(C formattedRead) for parsing strings) +$(H5 $(IX formattedRead) 使用 $(C formattedRead) 函数来解析字符串) $(P -Once a line is read from the input or from any other source, it is possible to parse and convert separate data that it may contain with $(C formattedRead()) from the $(C std.format) module. Its first parameter is the line that contains the data, and the rest of the parameters are used exacly like $(C readf()): +Once a line is read from the input or from any other source, it is possible to parse and convert separate data that it may contain with $(C formattedRead()) from the $(C std.format) module. 它的第一个参数是包含数据的输入行,而其余的参数就与 $(C readf()) 的一模一样: ) --- @@ -156,7 +156,7 @@ Your name is $(HILITE Mert), and your age is $(HILITE 30). ) $(P -Both $(C readf()) and $(C formattedRead()) $(I return) the number of items that they could parse and convert successfully. That value can be compared against the expected number of data items so that the input can be validated. For example, as the $(C formattedRead()) call above expects to read $(I two) items (a $(C string) as name and an $(C int) as age), the following check ensures that it really is the case: +$(C readf()) 和 $(C formattedRead()) 函数 都可以$(I 返回)成功解析及转换的项目个数。该值可与数据项的预期数相比较,以便确定输入的有效性。例如,像上面的 $(C formattedRead()) 函数期望去读$(I 两个)项目(一个 $(C string) 型 name 和一个 $(C int) 型 age),下面的检查确定它真是这样: ) --- @@ -172,7 +172,7 @@ Both $(C readf()) and $(C formattedRead()) $(I return) the number of items that --- $(P -When the input cannot be converted to $(C name) and $(C age), the program prints an error: +当输入不能转换到 $(C name) 和 $(C age) 时,程序将打印一个错误: ) $(SHELL @@ -180,24 +180,24 @@ Please enter your name and age, separated with a space: $(HILITE Mert) Error: Unexpected line. ) -$(H5 $(IX ") Double quotes, not single quotes) +$(H5 $(IX ") 使用双引号,而非单引号) $(P -We have seen that single quotes are used to define character literals. String literals are defined with double quotes. $(STRING 'a') is a character; $(STRING "a") is a string that contains a single character. +我们已经看到单引号用于定义字符字面量。字符串字面量用双引号定义。$(STRING 'a') 是一个字符;$(STRING "a") 是一个包含单字符的字符串。 ) -$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string), $(C wstring), and $(C dstring) are immutable) +$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string)、$(C wstring) 和 $(C dstring) 是 immutable (不可变的)) $(P -There are three string types that correspond to the three character types: $(C char[]), $(C wchar[]), and $(C dchar[]). +对应着三种字符类型,分别存在着三种字符串类型:$(C char[])、$(C wchar[]) 和 $(C dchar[])。 ) $(P -There are three $(I aliases) of the $(I immutable) versions of those types: $(C string), $(C wstring), and $(C dstring). The characters of the variables that are defined by these aliases cannot be modified. For example, the characters of a $(C wchar[]) can be modified but the characters of a $(C wstring) cannot be modified. (We will see D's $(I immutability) concept in later chapters.) +这些类型的 $(I immutable) 版本有三个$(I 别名):$(C string)、$(C wstring) 和 $(C dstring)。由这些别名定义的变量中的字符不可修改。例如,可以修改一个 $(C wchar[]) 中的字符,但不可以修改一个 $(C wstring) 中的字符。(在稍后的章节我们将看到 D 的$(I 不可变性)概念。) ) $(P -For example, the following code that tries to capitalize the first letter of a $(C string) would cause a compilation error: +例如,下面这段代码尝试着修改 $(C string) 的首字母为大写,这将引发一个编译错误: ) --- @@ -206,7 +206,7 @@ For example, the following code that tries to capitalize the first letter of a $ --- $(P -We may think of defining the variable as a $(C char[]) instead of the $(C string) alias but that cannot be compiled either: +我们可能想到把变量定义为 $(C char[]) 而不是别名 $(C string),但这也不能通过编译: ) --- @@ -214,22 +214,22 @@ We may think of defining the variable as a $(C char[]) instead of the $(C string --- $(P -This time the compilation error is due to the combination of two factors: +这次的编译错误是因为两个因素的联合作用: ) $(OL -$(LI The type of string literals like $(STRING "hello") is $(C string), not $(C char[]), so they are immutable. +$(LI 像 $(STRING "hello") 这样的字符串字面量的类型是 $(C string),而不是 $(C char[]),因此它们是 immutable。 ) -$(LI The $(C char[]) on the left-hand side is a slice, which, if the code compiled, would provide access to all of the characters of the right-hand side. +$(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编译成功,它将会访问右手侧的全部字符。 ) ) $(P -Since $(C char[]) is mutable and $(C string) is not, there is a mismatch. The compiler does not allow accessing characters of an immutable array through a mutable slice. +由于 $(C char[]) 可变而 $(C string) 不可变,两者不批配。编译器不允许通过可变的切片访问不可变的字符数组。 ) $(P -The solution here is to take a copy of the immutable string by using the $(C .dup) property: +此处的解决办法是通过 $(C .dup) property(属性)得到一个不可变字符串的副本: ) --- @@ -243,7 +243,7 @@ void main() { --- $(P -The program can now be compiled and will print the modified string: +现在程序能通过编译并且打印修改后的字符串: ) $(SHELL @@ -251,7 +251,7 @@ Hello ) $(P -Similarly, $(C char[]) cannot be used where a $(C string) is needed. In such cases, the $(C .idup) property can be used to produce an immutable $(C string) variable from a mutable $(C char[]) variable. For example, if $(C s) is a variable of type $(C char[]), the following line will fail to compile: +同样的,$(C char[]) 不能被用到需要 $(C string) 的地方。这种情况下,$(C .idup) property 能被用来从一个可变的 $(C char[]) 变量中生成一个不可变的 $(C string) 变量。例如,如果 $(C s) 的变量类型是 $(C char[]),下面这行将编译失败: ) --- @@ -259,17 +259,17 @@ Similarly, $(C char[]) cannot be used where a $(C string) is needed. In such cas --- $(P -When the type of $(C s) is $(C char[]), the type of the expression on the right-hand side of the assignment above is $(C char[]) as well. $(C .idup) is used for producing immutable strings from existing strings: +当 $(C s) 的类型是 $(C char[]),上面右手侧赋值的表达式类型也是 $(C char[]),$(C .idup) 可用来从存在的字符串中生成不可变的字符串: ) --- - string result = (s ~ '.')$(HILITE .idup); // ← now compiles + string result = (s ~ '.')$(HILITE .idup); // ← 现在可以编译 --- -$(H5 $(IX length, string) Potentially confusing length of strings) +$(H5 $(IX length, string) 有可能让人困惑的字符串长度) $(P -We have seen that some Unicode characters are represented by more than one byte. For example, the character 'é' (the latin letter 'e' combined with an acute accent) is represented by Unicode encodings using at least two bytes. This fact is reflected in the $(C .length) property of strings: +我们已经知道一些 Unicode 字符串由多个字节表示。例如,字符‘é’ (拉丁字母‘e’包含了一个重音符) 由 Unicode 编码表示,至少用了两个字节。这个事实反映在字符串的 $(C .length) property 上: ) --- @@ -277,7 +277,7 @@ We have seen that some Unicode characters are represented by more than one byte. --- $(P -Although "résumé" contains six $(I letters), the length of the $(C string) is the number of UTF-8 code units that it contains: +虽然 "résumé" 包含6个$(I 字母),但$(C 字符串)的长度是它包含的 UTF-8 编码单元的个数: ) $(SHELL @@ -285,7 +285,7 @@ $(SHELL ) $(P -The type of the elements of string literals like $(STRING "hello") is $(C char) and each $(C char) value represents a UTF-8 code unit. A problem that this may cause is when we try to replace a two-code-unit character with a single-code-unit character: +像 $(STRING "hello") 这样的字符串字面量的元素类型是 $(C char),并且每个 $(C char) 值对应一个 UTF-8 编码单元。当我们试着用一个单字节字符替换一个双字节字符时问题就来了: ) --- @@ -297,16 +297,16 @@ The type of the elements of string literals like $(STRING "hello") is $(C char) --- $(P -The two 'e' characters do not replace the two 'é' characters; they replace single code units, resulting in an invalid UTF-8 encoding: +两个‘e’字符不能代替两个‘é’字符;用单字节编码单元替换后,结果就是得到一个无效的 UTF-8 编码: ) $(SHELL Before: résumé -After : re�sueé $(SHELL_NOTE_WRONG INCORRECT) +After : re�sueé $(SHELL_NOTE_WRONG 不正确) ) $(P -When dealing with letters, symbols, and other Unicode characters directly, as in the code above, the correct type to use is $(C dchar): +当直接处理字母、符号或其它 Unicode 字符的时候,比如在上面代码中,应该使用正确的类型 $(C dchar): ) --- @@ -318,7 +318,7 @@ When dealing with letters, symbols, and other Unicode characters directly, as in --- $(P -The output: +输出: ) $(SHELL @@ -327,29 +327,29 @@ After : resume ) $(P -Please note the two differences in the new code: +请注意新代码的两个不同: ) $(OL -$(LI The type of the string is $(C dchar[]). -$(LI There is a $(C d) at the end of the literal $(STRING "résumé"d), specifying its type as an array of $(C dchar)s.) +$(LI 字符串的类型是 $(C dchar[])。 +$(LI 有一个 $(C d) 在字面量 $(STRING "résumé"d) 的末尾,指定了它的类型是一个 $(C dchar) 型数组。) ) ) $(P -In any case, keep in mind that the use of $(C dchar[]) and $(C dstring) does not solve all of the problems of manipulating Unicode characters. For instance, if the user inputs the text "résumé" you and your program cannot assume that the string length will be 6 even for $(C dchar) strings. It might be greater if e.g. at least one of the 'é' characters is not encoded as a single code point but as the combination of an 'e' and a combining accute accent. To avoid dealing with this and many other Unicode issues, consider using a Unicode-aware text manipulation library in your programs. +无论如何,请记住使用 $(C dchar[]) 和 $(C dstring) 并不能解决所有的操作 Unicode 字符的问题。例如,如果用户输入文本“résumé”,即使使用 $(C dchar) 字符串,你和你的程序仍然不能确保字符串的长度会是6。如果至少其中一个‘é’字符没有做为单个编码点编码,而是一个‘e’与一个组合重音符的组合,那么它就可能会更长。为避免处理这种以及许多其它的 Unicode 问题,在你的程序里就要考虑使用一个支持 Unicode 的文本处理库。 ) -$(H5 $(IX literal, string) String literals) +$(H5 $(IX literal, string) 字符串字面量) $(P -The optional character that is specified after string literals determines the type of the elements of the string: +在字符串字面量之后指定的可选字符决定了字符串的元素类型: ) --- import std.stdio; void main() { - string s = "résumé"c; // same as "résumé" + string s = "résumé"c; // 与“résumé”一样 wstring w = "résumé"w; dstring d = "résumé"d; @@ -360,7 +360,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -370,13 +370,13 @@ $(SHELL ) $(P -Because all of the Unicode characters of "résumé" can be represented by a single $(C wchar) or $(C dchar), the last two lengths are equal to the number of characters. +因为所有的“résumé”的 Unicode 字符都能用单个 $(C wchar) 或者 $(C dchar) 表示,所以最后两个的长度与字符个数是一致的。 ) -$(H5 $(IX concatenation, string) String concatenation) +$(H5 $(IX concatenation, string) 字符串连接) $(P -Since they are actually arrays, all of the array operations can be applied to strings as well. $(C ~) concatenates two strings and $(C ~=) appends to an existing string: +由于它们实际上是数组,所有数组的操作也都能应用到字符串上。$(C ~) 可以连接两个字符串,$(C ~=) 则能够把字符串附加到一个已存在的字符串上: ) --- @@ -387,7 +387,7 @@ void main() { write("What is your name? "); string name = strip(readln()); - // Concatenate: + // 连接: string greeting = "Hello " ~ name; // Append: @@ -398,7 +398,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -406,14 +406,14 @@ What is your name? Can Hello Can! Welcome... ) -$(H5 Comparing strings) +$(H5 比较字符串) $(P -$(I $(B Note:) Unicode does not define how the characters are ordered other than their Unicode codes. For that reason, you may get results that don't match your expectations below.) +$(I $(B 注:)除了 Unicode 编码顺序之外,Unicode 不定义字符的排列顺序。因此,下面的输出结果不是你所期望的那样。) ) $(P -We have used comparison operators $(C <), $(C >=), etc. with integer and floating point values before. The same operators can be used with strings as well, but with a different meaning: strings are ordered $(I lexicographically). This ordering takes each character's Unicode code to be its place in a hypothetical grand Unicode alphabet. The concepts of $(I less) and $(I greater) are replaced with $(I before) and $(I after) in this hypothetical alphabet: +我们以前把比较运算符 $(C <),$(C >=) 等等用于整型和浮点数值。同样的操作也能用于字符串,但含义不同:字符串按$(I 字典顺序)排序。这种排序需要在一个假设的大字母表中让每个字符的 Unicode 编码找到它的位置。在这个假设的字母表中,$(I 更少)和$(I 更多)的概念就被$(I 之前)和$(I 之后)代替: ) --- @@ -449,42 +449,42 @@ void main() { --- $(P -Because Unicode adopts the letters of the basic Latin alphabet from the ASCII table, the strings that contain only the letters of the ASCII table will always be ordered correctly. +由于 Unicode 采用来自于 ASCII 表的基本拉丁字母,仅包含 ASCII 字母的字符串将会正确排序。 ) -$(H5 Lowercase and uppercase are different) +$(H5 小写与大写不同) $(P -Because each character has a unique code, every letter variant is different from the others. For example, 'A' and 'a' are different letters, when directly comparing Unicode strings. +因为每个字母都有一个唯一的编码,每个字母变体都与其它的不同。例如,当直接比较 Unicode 字符串时,‘A’和‘a’是不同的字母。 ) $(P -Additionally, as a consequence of their ASCII code values, all of the latin uppercase letters are sorted before all of the lowercase letters. For example, 'B' comes before 'a'. The $(C icmp()) function of the $(C std.string) module can be used when strings need to be compared regardless of lowercase and uppercase. You can see the functions of this module at $(LINK2 http://dlang.org/phobos/std_string.html, its online documentation). +另外,受 ASCII 编码值的影响,所有拉丁大写字母都排在小写字母的前面。例如,‘B’排在‘a’之前。无论小写大写,$(C std.string) 模块中的 $(C icmp()) 函数可用于字符串比较。在 $(LINK2 http://dlang.org/phobos/std_string.html, 它的在线文档) 中你可以看到这个模块的各个函数。 ) $(P -Because strings are arrays (and as a corollary, $(I ranges)), the functions of the $(C std.array), $(C std.algorithm), and $(C std.range) modules are very useful with strings as well. +因为字符串是数组(进一步而言,是 $(I range)),所以 $(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块中的函数对于字符串也都非常有用。 ) $(PROBLEM_COK $(PROBLEM -Browse the documentations of the $(C std.string), $(C std.array), $(C std.algorithm), and $(C std.range) modules. +浏览 $(C std.string)、$(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块的文档。 ) $(PROBLEM -Write a program that makes use of the $(C ~) operator: The user enters the first name and the last name, all in lowercase letters. Produce the full name that contains the proper capitalization of the first and last names. For example, when the strings are "ebru" and "domates" the program should print "Ebru Domates". +写一个使用 $(C ~) 运算符的程序:让用户都以小写键入英文名字和姓氏,生成一个姓名首字母大写的全名。例如,字符串是“ebru”和“domates”,程序应该打印出“Ebru Domates”。 ) $(PROBLEM -Read a line from the input and print the part between the first and last 'e' letters of the line. For example, when the line is "this line has five words" the program should print "e has five". +从输入中读取一行并打印该行的第一个和最后一个‘e’字母之间的部分。例如,若这行是“this line has five words”程序就应该打印出“e has five”。 $(P -You may find the $(C indexOf()) and $(C lastIndexOf()) functions useful to get the two indexes needed to produce a slice. +你或许会发现 $(C indexOf()) 和 $(C lastIndexOf()) 函数对生成切片所需要的两个索引很有用。 ) $(P -As it is indicated in their documentation, the return types of $(C indexOf()) and $(C lastIndexOf()) are not $(C int) nor $(C size_t), but $(C ptrdiff_t). You may have to define variables of that exact type: +与它们的文档中所指出的一样,$(C indexOf()) 和 $(C lastIndexOf()) 的返回类型既不是 $(C int) 也不是 $(C size_t),而是 $(C ptrdiff_t)。您可能就需要定义该类型的变量: ) --- @@ -492,7 +492,7 @@ As it is indicated in their documentation, the return types of $(C indexOf()) an --- $(P -It is possible to define variables with the $(C auto) keyword, which we will see in a later chapter: +也可以使用 $(C auto) 关键字定义变量,这个我们将在后面的一章中看到: ) --- @@ -504,8 +504,8 @@ It is possible to define variables with the $(C auto) keyword, which we will see ) Macros: - SUBTITLE=Strings + SUBTITLE=字符串 - DESCRIPTION=The strings of the D programming language + DESCRIPTION=D 语言的字符串 - KEYWORDS=d programming language tutorial book string + KEYWORDS=D 语言教程 string diff --git a/target/struct.d b/target/struct.d index 371f852..1ddfb15 100644 --- a/target/struct.d +++ b/target/struct.d @@ -49,7 +49,7 @@ $(P Although the function above clearly takes six parameters, when the three pairs of parameters are considered, it is conceptually taking only three bits of information for the starting time, the duration, and the result. ) -$(H5 Definition) +$(H5 定义) $(P The $(C struct) keyword defines a new type by combining variables that are related in some way: @@ -254,7 +254,7 @@ $(CODE_XREF TimeOfDay)$(CODE_XREF addDuration)void main() { --- $(P -The output: +输出: ) $(SHELL @@ -906,7 +906,7 @@ A higher value for $(C repetition) should result in a more shuffled deck: --- $(P -The output: +输出: ) $(SHELL diff --git a/target/switch_case.d b/target/switch_case.d index b2e9e6d..201d5d8 100644 --- a/target/switch_case.d +++ b/target/switch_case.d @@ -166,7 +166,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/target/templates.d b/target/templates.d index 97d4439..3e3408d 100644 --- a/target/templates.d +++ b/target/templates.d @@ -1022,7 +1022,7 @@ struct Point(T) { } --- -$(H5 Summary) +$(H5 摘要) $(P We will see other features of templates in $(LINK2 /ders/d.en/templates_more.html, a later chapter). The following are what we have covered in this chapter: diff --git a/target/templates_more.d b/target/templates_more.d index 72c504c..aab262f 100644 --- a/target/templates_more.d +++ b/target/templates_more.d @@ -599,7 +599,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -701,7 +701,7 @@ The $(C OwnType) template parameter is the actual type of the object that the me --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -808,7 +808,7 @@ Those functions can be used as the $(C alias) parameter of $(C caller()): --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -857,7 +857,7 @@ class C { --- $(P -The output: +输出: ) $(SHELL @@ -967,7 +967,7 @@ void info(T...)(T args) { --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -2039,7 +2039,7 @@ void main() { } --- -$(H5 Summary) +$(H5 摘要) $(P The earlier template chapter had the following reminders: diff --git a/target/tuples.d b/target/tuples.d index 63fb3a8..164c8fe 100644 --- a/target/tuples.d +++ b/target/tuples.d @@ -60,7 +60,7 @@ The members of a tuple are normally accessed by their index values. That syntax --- $(P -The output: +输出: ) $(SHELL @@ -91,7 +91,7 @@ The definition above allows accessing the members by $(C .number) and $(C .messa --- $(P -The output: +输出: ) $(SHELL @@ -174,7 +174,7 @@ Because their values can be expanded, tuples can be used with the $(C foreach) s --- $(P -The output: +输出: ) $(SHELL @@ -232,7 +232,7 @@ import std.algorithm; --- $(P -The output: +输出: ) $(SHELL @@ -541,7 +541,7 @@ $(P Here, an important point to make is that the tuple that $(C .tupleof) returns consists of the members of the object themselves, not their copies. In other words, the tuple members are references to the actual object members. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/uda.d b/target/uda.d index 9aea24d..46392be 100644 --- a/target/uda.d +++ b/target/uda.d @@ -148,7 +148,7 @@ static assert(hasUDA!(Person.name, $(HILITE Colored))); static assert(hasUDA!(Person.name, $(HILITE Colored(Color.blue)))); --- -$(H5 Example) +$(H5 样例) $(P Let's design a function template that prints the values of all members of a $(C struct) object in XML format. The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: @@ -366,7 +366,7 @@ struct Person { --- $(P -The output: +输出: ) $(SHELL @@ -392,7 +392,7 @@ struct Data { --- $(P -The output: +输出: ) $(SHELL @@ -401,7 +401,7 @@ $(SHELL </Data> ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/target/union.d b/target/union.d index eb741de..80d5279 100644 --- a/target/union.d +++ b/target/union.d @@ -221,7 +221,7 @@ import core.bitop; --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/target/value_vs_reference.d b/target/value_vs_reference.d index e22a871..8c457a4 100644 --- a/target/value_vs_reference.d +++ b/target/value_vs_reference.d @@ -677,7 +677,7 @@ $(IX replicate, std.array) The $(C replicate()) function of the $(C std.array) m ) -$(H5 Summary) +$(H5 摘要) $(UL From 33807657f1e652a0c64465f673e1dd5a96048283 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 2 Aug 2017 13:23:51 +0800 Subject: [PATCH 029/225] Translated by Heromyth Added: 0, Deleted: 0, Modified: 0 (Conflicts: 0) --- omegat/project_save.tmx | 1018 +++++++++++++++++++-------------------- 1 file changed, 509 insertions(+), 509 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index db53a16..86e8689 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1,9 +1,79 @@ -
+
+ + + $ .dup capacity)) +$(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.en/stream_redirect.html, Redirecting Standard Input and Output Streams)) +$(LI $(LINK2 /ders/d.en/files.html, Files) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.en/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.en/name_space.html, Name Scope)) +$(LI $(LINK2 /ders/d.en/for.html, for Loop) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.en/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.en/literals.html, Literals)) +$(LI $(LINK2 /ders/d.en/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.en/formatted_input.html, Formatted Input)) +$(LI $(LINK2 /ders/d.en/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.en/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.en/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.en/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.en/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.en/functions.html, Functions) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.en/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.en/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.en/lazy_operators.html, Lazy Operators)) +$(LI $(LINK2 /ders/d.en/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.en/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.en/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.en/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.en/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.en/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations)) +$(LI $(LINK2 /ders/d.en/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.en/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.en/struct.html, Structs) $(INDEX_KEYWORDS struct . + + + $ .dup capacity)) +$(LI $(LINK2 /ders/d.cn/strings.html, 字符串) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) +$(LI $(LINK2 /ders/d.cn/stream_redirect.html, 重定向标准输入输出流)) +$(LI $(LINK2 /ders/d.cn/files.html, 文件) $(INDEX_KEYWORDS File)) +$(LI $(LINK2 /ders/d.cn/auto_and_typeof.html, auto 和 typeof) $(INDEX_KEYWORDS auto typeof)) +$(LI $(LINK2 /ders/d.cn/name_space.html, 名字作用域)) +$(LI $(LINK2 /ders/d.cn/for.html, for 循环) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.cn/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) +$(LI $(LINK2 /ders/d.cn/literals.html, 文字量)) +$(LI $(LINK2 /ders/d.cn/formatted_output.html, 格式化输出) $(INDEX_KEYWORDS writef writefln)) +$(LI $(LINK2 /ders/d.cn/formatted_input.html, 格式化输入)) +$(LI $(LINK2 /ders/d.cn/do_while.html, do-while 循环) $(INDEX_KEYWORDS do while)) +$(LI $(LINK2 /ders/d.cn/aa.html, 关联数组) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) +$(LI $(LINK2 /ders/d.cn/foreach.html, foreach 循环) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.cn/switch_case.html, switch 和 case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.cn/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) +$(LI $(LINK2 /ders/d.cn/functions.html, 函数) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, 不变量) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型与引用类型) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值与右值) $(INDEX_KEYWORDS auto ref)) +$(LI $(LINK2 /ders/d.cn/lazy_operators.html, 惰性运算符)) +$(LI $(LINK2 /ders/d.cn/main.html, 程序环境) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.cn/exceptions.html, 异常) $(INDEX_KEYWORDS throw try catch finally)) +$(LI $(LINK2 /ders/d.cn/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) +$(LI $(LINK2 /ders/d.cn/assert.html, assert 与 enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.cn/unit_testing.html, 单元测试) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.cn/contracts.html, 契约编程) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.cn/lifetimes.html, 生命周期与函数式运算)) +$(LI $(LINK2 /ders/d.cn/null_is.html, null 值与 is 运算符) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.cn/cast.html, 类型转换) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . + + $(C !is) is the opposite of $(C is). @@ -110,6 +180,22 @@ $(DERS_BOLUMU $(IX class) 类) + + + $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) + + + $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) + + + + + $(DERS_BOLUMU Programming in D) + + + $(DERS_BOLUMU D 语言编程) + + $(DERS_BOLUMU Strings) @@ -302,6 +388,14 @@ $(H5 小写与大写不同) + + + $(H5 Online version) + + + $(H5 在线版本) + + $(H5 Properties) @@ -1001,6 +1095,16 @@ $(IX :, 关联数组) 有时候一些键与值的映射在定义关联数组的 $(P +$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. + + + $(P +$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。 + + + + + $(P $(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D中,类提供 $(I 面向对象编程) (OOP) 模型。下面是OOP的主要方面: ) @@ -1044,6 +1148,18 @@ $(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期 $(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) +) + + + $(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) +) + + + + + $(P Accordingly, the equivalent of the five separate variables above can be defined as an array of five values using the following syntax: ) @@ -1090,6 +1206,16 @@ All of the elements of the $(C values) array above are initialized to $(C double $(P +Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). + + + $(P +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 + + + + + $(P Although "résumé" contains six $(I letters), the length of the $(C string) is the number of UTF-8 code units that it contains: ) @@ -1152,6 +1278,16 @@ Another difference from structs is that some member functions are automatically $(P +Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. + + + $(P +所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。 + + + + + $(P Arrays are containers where the elements are placed side by side in the computer's memory. @@ -1760,6 +1896,16 @@ Like every variable in D, the elements of arrays are automatically initialized.< $(P +Multiple attributes can be specified separately or as a parenthesized list of attributes. + + + $(P +多个属性可以分别指定,也可以采用括号列表形式。 + + + + + $(P Obviously, the values of the elements can be changed later during the execution of the program. @@ -2498,6 +2644,18 @@ These operators operate on class variables. $(P +These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. +) + + + $(P +以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 +) + + + + + $(P This chapter covers only some of the features of arrays. @@ -2947,6 +3105,14 @@ have the program print the following: $(P $(B 观测:) $(C counter) 的值决定了循环的重复次数(迭代)。 + + + $(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) + + + $(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) + + $(PROBLEM @@ -3117,6 +3283,60 @@ $(LI $(C scope(exit)): the expression is always executed when exiting the scope, $(LI $(C scope(exit)):表达式总是在退出作用域时被执行, 无论是否成功或出现异常。) + + + $(UL +$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) This book in Turkish)) +$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) Contact)) +$(BR) +$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) Rights)) +) + + + $(UL +$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) +$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) +$(BR) +$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) +) + + + + + $(UL +$(WORK_IN_PROCESS +$(LI $(LINK2 /ders/d.en/foreword1.html, Foreword by Walter Bright)) +) +$(LI $(LINK2 /ders/d.en/foreword2.html, Foreword by Andrei Alexandrescu)) +$(LI $(LINK2 /ders/d.en/preface.html, Preface)) +$(LI $(LINK2 /ders/d.en/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.en/writeln.html, writeln and write)) +$(LI $(LINK2 /ders/d.en/compiler.html, Compilation)) +$(LI $(LINK2 /ders/d.en/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) +$(LI $(LINK2 /ders/d.en/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.en/variables.html, Variables)) +$(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) +$(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! + + + $(UL +$(WORK_IN_PROCESS +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) +) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) +$(LI $(LINK2 /ders/d.cn/preface.html, 前言)) +$(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) +$(LI $(LINK2 /ders/d.cn/compiler.html, 编译)) +$(LI $(LINK2 /ders/d.cn/types.html, 基础类型) $(INDEX_KEYWORDS char int double (等))) +$(LI $(LINK2 /ders/d.cn/assignment.html, 赋值与计算顺序) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.cn/variables.html, 变量)) +$(LI $(LINK2 /ders/d.cn/io.html, 标准输入、输出流) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) +$(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! + + ($(C .clear) is the most natural method.) There are at least three methods: @@ -3537,6 +3757,28 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 // writeln 函数。 + + + == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.en/if.html, if Statement) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.en/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.en/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.en/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.en/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. + + + == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.cn/if.html, if 语句) $(INDEX_KEYWORDS if else)) +$(LI $(LINK2 /ders/d.cn/while.html, while 循环) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.cn/arithmetic.html, 整型和算术运算) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.cn/floating_point.html, 浮点类型) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.cn/arrays.html, 数组) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. + + A benefit of $(C scope(failure)) is the fact that the expression that reverts another expression is written close to it. @@ -3707,6 +3949,16 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 做为值类型,$(C o) 和 $(C i) 自动被复制。 + + + COZUM_METIN=the solution +COZUMLER_METIN=the solutions + + + COZUM_METIN=答案 +COZUMLER_METIN=答案 + + Can you figure out the remaining problem before reading the following paragraph? @@ -3743,10 +3995,26 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 - DESCRIPTION=Basic array operations of the D programming language + DERLEME_HATASI_METIN=compilation ERROR - - DESCRIPTION=D 语言数组的基本操作 + + DERLEME_HATASI_METIN=编译出错 + + + + + DESCRIPTION=Basic array operations of the D programming language + + + DESCRIPTION=D 语言数组的基本操作 + + + + + DESCRIPTION=D programming language tutorial from the ground up. + + + DESCRIPTION=全新编写的 D 编程语言教程。 @@ -3805,6 +4073,36 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 DESCRIPTION=D 语言的字符串 + + + DUSEY_NAVIGASYON= +<div class="vertinavheader">Other D Resources</div> +$(UL +$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) Books)) +$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) Newsgroups)) +$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) Language spec)) +$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) Standard library)) +) + + + DUSEY_NAVIGASYON= +<div class="vertinavheader">其他D语言资源</div> +$(UL +$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) 书籍)) +$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) 社区)) +$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) +$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) +) + + + + + Ddoc + + + Ddoc + + Define an associative array that can store multiple grades per student. @@ -3884,6 +4182,16 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 例如,像上面的 $(C formattedRead()) 函数期望去读$(I 两个)项目(一个 $(C string) 型 name 和一个 $(C int) 型 age),下面的检查确定它真是这样: +) + + + + + For example, both of the following variables have the same attributes: +) + + + 例如,下面的变量拥有的属性是相同的: ) @@ -3928,6 +4236,16 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 例如,下面数组的元素保存了每个月的天数(假定一年中二月有 28 天): +) + + + + + For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +) + + + 例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: ) @@ -4035,6 +4353,36 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 此外,因为您无法使用循环遍历 20 个值,您也不得不把那几行代码重复 20 次,每个单值变量来一次。 + + + GERI_METIN=Prev +ILERI_METIN=Next +PROBLEM_METIN=Exercise +PROBLEM_COK_METIN=Exercises +PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... + + + GERI_METIN=上一章 +ILERI_METIN=下一章 +PROBLEM_METIN=练习 +PROBLEM_COK_METIN=练习 +PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… + + + + + HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="Programming in D RSS Feed"/>) +$(BR) +$(LINK2 /ders/d.en/index.html, +Download or buy $(IMG book.png)) + + + HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="RSS 订阅"/>) +$(BR) +$(LINK2 /ders/d.cn/index.html, +下载或购买 $(IMG book.png)) + + Hash tables are among the fastest collections for storing and accessing elements. @@ -4313,6 +4661,14 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 KEYWORDS=D 编程语言教程 关联数组 + + + KEYWORDS=d programming language tutorial book novice beginner + + + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 + + KEYWORDS=d programming language tutorial book scope @@ -4391,6 +4747,40 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 ) + + + MAIN_TITLE=D.ershane Solutions +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= + + + MAIN_TITLE=D 语言解答 +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= + + + + + MAIN_TITLE=Programming in D +SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference +SUB_AUTHOR=Ali Çehreli +LANG=en +LANGUAGE=english + + + MAIN_TITLE=D 编程语言 +SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 +SUB_AUTHOR=Ali Çehreli +LANG=zh-cn +LANGUAGE=chinese + + More features will be introduced later in $(LINK2 /ders/d.en/slices.html, the Slices and Other Array Features chapter). @@ -4431,6 +4821,14 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 除极个别情况外,一般存取单个元素所花费的时间不依赖于关联数组中元素的个数。 + + + PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... + + + PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… + + Plain arrays can use only integers as indexes. @@ -4495,6 +4893,14 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 SUBTITLE=类 + + + SUBTITLE=Programming in D + + + SUBTITLE=D 语言编程 + + SUBTITLE=Strings @@ -4969,6 +5375,14 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 不像结构,类对象不能由 $(C {&nbsp;}) 语法构造。 + + + User defined attributes is purely a compile-time feature. + + + 自定义属性完全是一项编译时功能。 + + Variables which only store a single value are called scalar variables. @@ -5365,519 +5779,105 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 - 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: + {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ (and more))) +$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) +$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) +$(LI $(LINK2 /ders/d.en/class.html, Classes) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.en/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.en/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.en/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.en/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.en/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.en/ufcs.html, Universal Function Call Syntax (UFCS))) +$(LI $(LINK2 /ders/d.en/property.html, Properties) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.en/templates.html, Templates)) +$(LI $(LINK2 /ders/d.en/pragma.html, Pragmas)) +$(LI $(LINK2 /ders/d.en/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.en/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.en/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.en/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.en/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.en/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) +$(LI $(LINK2 /ders/d.en/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.en/union.html, Unions) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.en/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.en/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.en/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.en/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.en/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.en/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) +$(LI $(LINK2 /ders/d.en/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.en/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.en/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.en/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.en/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.en/operator_precedence.html, Operator Precedence)) ) - - 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它: + + {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, 不定个数参数) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__(等))) +$(LI $(LINK2 /ders/d.cn/function_overloading.html, 函数重载)) +$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数和 const 函数函数) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其他特殊函数) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex(等))) +$(LI $(LINK2 /ders/d.cn/class.html, 类) $(INDEX_KEYWORDS class new)) +$(LI $(LINK2 /ders/d.cn/inheritance.html, 继承) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.cn/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) +$(LI $(LINK2 /ders/d.cn/interface.html, 接口) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.cn/destroy.html, destroy 和 scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.cn/modules.html, 模块和库) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/encapsulation.html, 封装和保护属性) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.cn/ufcs.html, 统一调用语法(UFCS))) +$(LI $(LINK2 /ders/d.cn/property.html, 特性) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.cn/invariant.html, 结构和类的契约编程) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.cn/templates.html, 模板)) +$(LI $(LINK2 /ders/d.cn/pragma.html, 编译指令)) +$(LI $(LINK2 /ders/d.cn/alias.html, alias 和 with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.cn/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) +$(LI $(LINK2 /ders/d.cn/pointers.html, 指针) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.cn/bit_operations.html, 位运算) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.cn/cond_comp.html, 条件编译) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.cn/is_expr.html, is 表达式) $(INDEX_KEYWORDS is())) +$(LI $(LINK2 /ders/d.cn/lambda.html, 函数指针、委托和λ) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, 将foreach用于结构和类) $(INDEX_KEYWORDS opApply empty popFront front(等))) +$(LI $(LINK2 /ders/d.cn/nested.html, 嵌套函数、结构和类) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.cn/union.html, 联合) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.cn/goto.html, 标签和 goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.cn/tuples.html, 元组) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) +$(LI $(LINK2 /ders/d.cn/templates_more.html, 模板的更多内容) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.cn/functions_more.html, 函数的更多内容) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.cn/mixin.html, 混入) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.cn/ranges.html, 范围) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.cn/ranges_more.html, 范围的更多内容) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject(等))) +$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行性) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.cn/concurrency.html, 消息传递并发) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, 数据共享并发) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.cn/fibers.html, 纤程) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.cn/memory.html, 内存管理) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.cn/uda.html, 自定义属性(UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.cn/operator_precedence.html, 运算符优先级)) ) - + - $ .dup capacity)) -$(LI $(LINK2 /ders/d.en/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.en/stream_redirect.html, Redirecting Standard Input and Output Streams)) -$(LI $(LINK2 /ders/d.en/files.html, Files) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d.en/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.en/name_space.html, Name Scope)) -$(LI $(LINK2 /ders/d.en/for.html, for Loop) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d.en/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.en/literals.html, Literals)) -$(LI $(LINK2 /ders/d.en/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.en/formatted_input.html, Formatted Input)) -$(LI $(LINK2 /ders/d.en/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d.en/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.en/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.en/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d.en/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.en/functions.html, Functions) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.en/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.en/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.en/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d.en/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d.en/lazy_operators.html, Lazy Operators)) -$(LI $(LINK2 /ders/d.en/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d.en/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d.en/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.en/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.en/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.en/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.en/lifetimes.html, Lifetimes and Fundamental Operations)) -$(LI $(LINK2 /ders/d.en/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.en/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.en/struct.html, Structs) $(INDEX_KEYWORDS struct . - - - $ .dup capacity)) -$(LI $(LINK2 /ders/d.cn/strings.html, 字符串) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.cn/stream_redirect.html, 重定向标准输入输出流)) -$(LI $(LINK2 /ders/d.cn/files.html, 文件) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d.cn/auto_and_typeof.html, auto 和 typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.cn/name_space.html, 名字作用域)) -$(LI $(LINK2 /ders/d.cn/for.html, for 循环) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d.cn/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.cn/literals.html, 文字量)) -$(LI $(LINK2 /ders/d.cn/formatted_output.html, 格式化输出) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.cn/formatted_input.html, 格式化输入)) -$(LI $(LINK2 /ders/d.cn/do_while.html, do-while 循环) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d.cn/aa.html, 关联数组) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.cn/foreach.html, foreach 循环) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.cn/switch_case.html, switch 和 case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d.cn/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.cn/functions.html, 函数) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, 不变量) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型与引用类型) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值与右值) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d.cn/lazy_operators.html, 惰性运算符)) -$(LI $(LINK2 /ders/d.cn/main.html, 程序环境) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d.cn/exceptions.html, 异常) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d.cn/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.cn/assert.html, assert 与 enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.cn/unit_testing.html, 单元测试) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.cn/contracts.html, 契约编程) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.cn/lifetimes.html, 生命周期与函数式运算)) -$(LI $(LINK2 /ders/d.cn/null_is.html, null 值与 is 运算符) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.cn/cast.html, 类型转换) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . - - - - - $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) - - - $(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) - - - - - $(DERS_BOLUMU Programming in D) - - - $(DERS_BOLUMU D 语言编程) - - - - - $(H5 Online version) - - - $(H5 在线版本) - - - - - $(P -$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. - - - $(P -$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。 - - - - - $(P -$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) -) - - - $(P -$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) -) - - - - - $(P -Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). - - - $(P -其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 - - - - - $(P -Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. - - - $(P -所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。 - - - - - $(P -Multiple attributes can be specified separately or as a parenthesized list of attributes. - - - $(P -多个属性可以分别指定,也可以采用括号列表形式。 - - - - - $(P -These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. -) - - - $(P -以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 -) - - - - - $(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) - - - $(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) - - - - - $(UL -$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) This book in Turkish)) -$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) Contact)) -$(BR) -$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) Rights)) -) - - - $(UL -$(LI $(LINK2 $(ROOT_DIR)/ders/d/, $(IMG bullet_black.png) 土耳其语版)) -$(LI $(LINK2 mailto:acehreli@yahoo.com, $(IMG bullet_black.png) 联系方式)) -$(BR) -$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) 版权)) -) - - - - - $(UL -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.en/foreword1.html, Foreword by Walter Bright)) -) -$(LI $(LINK2 /ders/d.en/foreword2.html, Foreword by Andrei Alexandrescu)) -$(LI $(LINK2 /ders/d.en/preface.html, Preface)) -$(LI $(LINK2 /ders/d.en/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.en/writeln.html, writeln and write)) -$(LI $(LINK2 /ders/d.en/compiler.html, Compilation)) -$(LI $(LINK2 /ders/d.en/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) -$(LI $(LINK2 /ders/d.en/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.en/variables.html, Variables)) -$(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) -$(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! - - - $(UL -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) -) -$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) -$(LI $(LINK2 /ders/d.cn/preface.html, 前言)) -$(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) -$(LI $(LINK2 /ders/d.cn/compiler.html, 编译)) -$(LI $(LINK2 /ders/d.cn/types.html, 基础类型) $(INDEX_KEYWORDS char int double (等))) -$(LI $(LINK2 /ders/d.cn/assignment.html, 赋值与计算顺序) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.cn/variables.html, 变量)) -$(LI $(LINK2 /ders/d.cn/io.html, 标准输入、输出流) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) -$(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! - - - - - == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d.en/if.html, if Statement) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.en/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.en/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.en/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN)) -$(LI $(LINK2 /ders/d.en/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.en/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.en/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. - - - == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d.cn/if.html, if 语句) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.cn/while.html, while 循环) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.cn/arithmetic.html, 整型和算术运算) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.cn/floating_point.html, 浮点类型) $(INDEX_KEYWORDS .nan .infinity isNaN)) -$(LI $(LINK2 /ders/d.cn/arrays.html, 数组) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. - - - - - COZUM_METIN=the solution -COZUMLER_METIN=the solutions - - - COZUM_METIN=答案 -COZUMLER_METIN=答案 - - - - - DERLEME_HATASI_METIN=compilation ERROR - - - DERLEME_HATASI_METIN=编译出错 - - - - - DESCRIPTION=D programming language tutorial from the ground up. - - - DESCRIPTION=全新编写的 D 编程语言教程。 - - - - - DUSEY_NAVIGASYON= -<div class="vertinavheader">Other D Resources</div> -$(UL -$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) Books)) -$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) Newsgroups)) -$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) Language spec)) -$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) Standard library)) -) - - - DUSEY_NAVIGASYON= -<div class="vertinavheader">其他D语言资源</div> -$(UL -$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) 书籍)) -$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) 社区)) -$(LI $(LINK2 http://dlang.org/lex.html, $(IMG bullet_black.png) 语言规范)) -$(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准库)) -) - - - - - Ddoc - - - Ddoc - - - - - For example, both of the following variables have the same attributes: -) - - - 例如,下面的变量拥有的属性是相同的: -) - - - - - For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): -) - - - 例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: -) - - - - - GERI_METIN=Prev -ILERI_METIN=Next -PROBLEM_METIN=Exercise -PROBLEM_COK_METIN=Exercises -PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... - - - GERI_METIN=上一章 -ILERI_METIN=下一章 -PROBLEM_METIN=练习 -PROBLEM_COK_METIN=练习 -PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… - - - - - HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.en/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="Programming in D RSS Feed"/>) -$(BR) -$(LINK2 /ders/d.en/index.html, -Download or buy $(IMG book.png)) - - - HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS&nbsp;<img src="$(ROOT_DIR)/image/rss-icon.png" border="0" width="16" height="16" alt="RSS 订阅"/>) -$(BR) -$(LINK2 /ders/d.cn/index.html, -下载或购买 $(IMG book.png)) - - - - - KEYWORDS=d programming language tutorial book novice beginner - - - KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 - - - - - MAIN_TITLE=D.ershane Solutions -CLASS=solution -COZUM_BOLUMU = $(H4 $0) -LANG=en -LANGUAGE=english -SUB_AUTHOR= - - - MAIN_TITLE=D 语言解答 -CLASS=solution -COZUM_BOLUMU = $(H4 $0) -LANG=en -LANGUAGE=english -SUB_AUTHOR= - - - - - MAIN_TITLE=Programming in D -SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference -SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english - - - MAIN_TITLE=D 编程语言 -SUB_MAIN_TITLE_DERSE_OZEL=——教程与参考 -SUB_AUTHOR=Ali Çehreli -LANG=zh-cn -LANGUAGE=chinese - - - - - PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... - - - PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… - - - - - SUBTITLE=Programming in D - - - SUBTITLE=D 语言编程 - - - - - User defined attributes is purely a compile-time feature. - - - 自定义属性完全是一项编译时功能。 - - - - - {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.en/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ (and more))) -$(LI $(LINK2 /ders/d.en/function_overloading.html, Function Overloading)) -$(LI $(LINK2 /ders/d.en/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.en/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.en/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.en/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) -$(LI $(LINK2 /ders/d.en/class.html, Classes) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.en/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d.en/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.en/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.en/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.en/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.en/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.en/ufcs.html, Universal Function Call Syntax (UFCS))) -$(LI $(LINK2 /ders/d.en/property.html, Properties) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.en/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d.en/templates.html, Templates)) -$(LI $(LINK2 /ders/d.en/pragma.html, Pragmas)) -$(LI $(LINK2 /ders/d.en/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d.en/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.en/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.en/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.en/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d.en/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.en/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.en/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) -$(LI $(LINK2 /ders/d.en/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.en/union.html, Unions) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.en/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d.en/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.en/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.en/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.en/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.en/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.en/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) -$(LI $(LINK2 /ders/d.en/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.en/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.en/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.en/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.en/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.en/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.en/operator_precedence.html, Operator Precedence)) + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: ) - - {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, 不定个数参数) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__(等))) -$(LI $(LINK2 /ders/d.cn/function_overloading.html, 函数重载)) -$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数和 const 函数函数) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其他特殊函数) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex(等))) -$(LI $(LINK2 /ders/d.cn/class.html, 类) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.cn/inheritance.html, 继承) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d.cn/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.cn/interface.html, 接口) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.cn/destroy.html, destroy 和 scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.cn/modules.html, 模块和库) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.cn/encapsulation.html, 封装和保护属性) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.cn/ufcs.html, 统一调用语法(UFCS))) -$(LI $(LINK2 /ders/d.cn/property.html, 特性) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.cn/invariant.html, 结构和类的契约编程) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d.cn/templates.html, 模板)) -$(LI $(LINK2 /ders/d.cn/pragma.html, 编译指令)) -$(LI $(LINK2 /ders/d.cn/alias.html, alias 和 with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d.cn/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.cn/pointers.html, 指针) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.cn/bit_operations.html, 位运算) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.cn/cond_comp.html, 条件编译) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d.cn/is_expr.html, is 表达式) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.cn/lambda.html, 函数指针、委托和λ) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, 将foreach用于结构和类) $(INDEX_KEYWORDS opApply empty popFront front(等))) -$(LI $(LINK2 /ders/d.cn/nested.html, 嵌套函数、结构和类) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.cn/union.html, 联合) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.cn/goto.html, 标签和 goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d.cn/tuples.html, 元组) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.cn/templates_more.html, 模板的更多内容) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.cn/functions_more.html, 函数的更多内容) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.cn/mixin.html, 混入) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.cn/ranges.html, 范围) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.cn/ranges_more.html, 范围的更多内容) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject(等))) -$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行性) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.cn/concurrency.html, 消息传递并发) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, 数据共享并发) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.cn/fibers.html, 纤程) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.cn/memory.html, 内存管理) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.cn/uda.html, 自定义属性(UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.cn/operator_precedence.html, 运算符优先级)) + + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它: ) From 74f50be70ea8cbb41a955203a3dcb70d021a2b78 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 09:26:06 +0800 Subject: [PATCH 030/225] Translated by Heromyth --- omegat/project_save.tmx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 86e8689..ed7233d 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1208,9 +1208,9 @@ All of the elements of the $(C values) array above are initialized to $(C double $(P Also available as $(LINK2 https://gumroad.com/l/PinD, $(I pay-what-you-want) eBooks at Gumroad) and $(I free) here as $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB), $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3), and $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI). - + $(P -其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 按需支付) 电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 From 96d675c83894b4e32a3a3da565f322ae21083e1d Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 09:26:43 +0800 Subject: [PATCH 031/225] Translated by Heromyth --- omegat/project_save.tmx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index ed7233d..e083831 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -3319,12 +3319,12 @@ $(LI $(LINK2 /ders/d.en/io.html, Standard Input and Output Streams) $(INDEX_KEYW $(LI $(LINK2 /ders/d.en/input.html, Reading from the Standard Input)) $(LI $(LINK2 /ders/d.en/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! - + $(UL $(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright 序)) ) -$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu 序)) $(LI $(LINK2 /ders/d.cn/preface.html, 前言)) $(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) $(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) From 6b13ce125719f8a514a31a1ef975bdd63afce223 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 09:27:05 +0800 Subject: [PATCH 032/225] Project translation --- target/index.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/index.d b/target/index.d index 02a140e..2c14e1f 100644 --- a/target/index.d +++ b/target/index.d @@ -21,7 +21,7 @@ $(P $(P -其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 付你所需)电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 按需支付) 电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 ) $(P @@ -34,9 +34,9 @@ $(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) $(UL $(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright序)) +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright 序)) ) -$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu序)) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu 序)) $(LI $(LINK2 /ders/d.cn/preface.html, 前言)) $(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) $(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) From 3aad6ad00328dd372449d724043bcbcd0d98c529 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 12:36:46 +0800 Subject: [PATCH 033/225] Translated by Heromyth --- omegat/project_save.tmx | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index e083831..39c9521 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1258,6 +1258,16 @@ Although these statements are closely related to exceptions, they can be used wi $(P +An attribute can be a type name as well as a value of a user defined or a fundamental type. + + + $(P +属性除了可以是自定义类型值或基础类型值以外,还可是以类型名。 + + + + + $(P An index need not be a constant value; the value of a variable can also be used as an index, making arrays even more useful. @@ -3779,6 +3789,60 @@ $(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dch $(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. + + + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE together) +--- + + + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE 组合一起) +--- + + + + + @(42) int d; $(CODE_NOTE literal (discouraged)) +} +--- + + + @(42) int d; $(CODE_NOTE 文字量(不赞成)) +} +--- + + + + + @Colored(Color.blue) int c; $(CODE_NOTE object) + + + @Colored(Color.blue) int c; $(CODE_NOTE 对象) + + + + + @Encrypted int a; $(CODE_NOTE type name) + + + @Encrypted int a; $(CODE_NOTE 类型名) + + + + + @Encrypted @Colored string lastName; $(CODE_NOTE separately) + + + @Encrypted @Colored string lastName; $(CODE_NOTE 单独分开) + + + + + @Encrypted() int b; $(CODE_NOTE object) + + + @Encrypted() int b; $(CODE_NOTE 对象) + + A benefit of $(C scope(failure)) is the fact that the expression that reverts another expression is written close to it. @@ -4407,6 +4471,16 @@ $(LINK2 /ders/d.cn/index.html, 在这里,bool 值可能意味着 + + + However, because their meanings may not be clear, attributes consisting of literal values like $(C 42) are discouraged: +) + + + 不过,因为它们的含义可能不是很清楚,因此不赞成大家使用文字量值(如 $(C 42) )构成的属性: +) + + However, the grades cannot be inserted as in the following code because each grade would overwrite the previous one: From 36904b23e8452ea72e4e0b62c8ab224a7a8a5cef Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 13:46:10 +0800 Subject: [PATCH 034/225] Translated by Heromyth --- omegat/project_save.tmx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 39c9521..a0a4be1 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -2302,6 +2302,16 @@ The assignment operator copies all of the elements from the right-hand side to t $(P +The attributes of $(C a) and $(C b) above are of different kinds: The attribute of $(C a) is the type $(C Encrypted) itself, while the attribute of $(C b) is an $(I object) of type $(C Encrypted). + + + $(P +上面 $(C a) 和 $(C b) 的属性属于不同类别:$(C a) 的属性是 $(C Encrypted) 类型,而 $(C b) 的属性是 $(C Encrypted) 类型的一个$(I 对象)。 + + + + + $(P The behavior of assignment cannot be changed for classes. From cb130e9fdc71845779239a12e3b707e7e7e48a95 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 13:54:29 +0800 Subject: [PATCH 035/225] Translated by Heromyth --- omegat/project_save.tmx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index a0a4be1..9f1764e 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -5361,6 +5361,14 @@ LANGUAGE=chinese 该函数必须创建并返回一个新的类对象。 + + + This is an important difference that affects the way attributes are used at compile time. + + + 它们有着明显的差异,它会对编译时属性的使用方式产生影响。 + + This is because later code may depend on previous variables. @@ -5521,6 +5529,14 @@ LANGUAGE=chinese 在稍后的一章中我们将看到 Phobos 的 range。 + + + We will see an example of this difference below. + + + 下面来看一个与该差异有关的例子。 + + We will see exceptions in $(LINK2 /ders/d.en/exceptions.html, a later chapter). From 343f793c7b2f5686bedadf335a6116233dff20fc Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 3 Aug 2017 19:02:05 +0800 Subject: [PATCH 036/225] Translated by Heromyth --- omegat/project_save.tmx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 9f1764e..f95db66 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1117,6 +1117,16 @@ $(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新 $(P +$(IX __traits) $(IX getAttributes) The meaning of attributes is solely determined by the programmer for the needs of the program. + + + $(P +$(IX __traits) $(IX getAttributes) 属性的实际含义意义在于The meaning of attributes is solely determined by the programmer for the needs of the program. + + + + + $(P $(IX final) As overridable member functions have a runtime performance cost, without going into more detail, I recommend that you define all $(C class) functions that do not need to be overridden with the $(C final) keyword. From 6fe8394505aac1e05e5e5facbe91a50e0903f3c8 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 4 Aug 2017 13:32:07 +0800 Subject: [PATCH 037/225] Translated by Heromyth --- omegat/project_save.tmx | 48 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index f95db66..a38bdc0 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1119,9 +1119,9 @@ $(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新 $(P $(IX __traits) $(IX getAttributes) The meaning of attributes is solely determined by the programmer for the needs of the program. - + $(P -$(IX __traits) $(IX getAttributes) 属性的实际含义意义在于The meaning of attributes is solely determined by the programmer for the needs of the program. +$(IX __traits) $(IX getAttributes) 属性的实际含义完全由程序员根据程序的需要来决定。 @@ -2400,6 +2400,18 @@ The following code makes use of $(C dup()) to create a new object: $(P 下面的代码演示 $(C dup()) 创建一个新的对象的用法: +) + + + + + $(P +The following code shows how the attributes of a specific $(C struct) member (e.g. $(C Person.name)) can be accessed by $(C __traits(getAttributes)): +) + + + $(P +下面代码展示的是如何使用使用$(C __traits(getAttributes))来访问某个特定 $(C struct) 成员(如 $(C Person.name))的属性: ) @@ -2498,6 +2510,18 @@ The output indicates that the values 0 and 1 correspond to keys "Monday" and "Tu $(P 下面的输出表明了元素值 0 和 1 分别对应着键 “Monday” 和 “Tuesday”: +) + + + + + $(P +The output of the program lists the attributes of $(C Person.name): +) + + + $(P +这个程序的输出内容是 $(C Person.name) 的各个属性: ) @@ -2738,6 +2762,18 @@ To see how this program is better than the previous one, imagine needing to read $(P +Two other $(C __traits) expressions are useful when dealing with user defined attributes: +) + + + $(P +在处理自定义属性时,还有其他两个 $(C __traits) 表达式可以使用: +) + + + + + $(P Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially: ) @@ -5107,6 +5143,14 @@ LANGUAGE=chinese 实际的对象没有被复制。 + + + The attributes are determined by $(C __traits(getAttributes)) at compile time and the code is compiled according to those attributes. + + + 编译时可以通过 $(C __traits(getAttributes)) 来判定属性,而相应代码则会根据那些属性来编译。 + + The characters of the variables that are defined by these aliases cannot be modified. From e091b652af7c98955cdfc8d7d85fdfa5c370125e Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 4 Aug 2017 13:36:56 +0800 Subject: [PATCH 038/225] Translated by Heromyth --- omegat/project_save.tmx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index a38bdc0..6ef49b5 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -774,6 +774,22 @@ There are three mistakes (bugs) in this program. $(LI $(IX .values) $(C .values) 返回全部值的动态数组副本。) + + + $(LI $(IX allMembers) $(C __traits(allMembers)) produces the members of a type (or a module) as strings.) + + + $(LI $(IX allMembers) $(C __traits(allMembers)) 会以字符串列表的形式生成某个类型(或模块)的所有成员。) + + + + + $(LI $(IX getMember) $(C __traits(getMember)) produces a $(I symbol) useful when accessing a member. + + + $(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。 + + $(LI Assigning an empty associative array.) From faebdefc065eb329594b1d91d6e149ad7b1e26a6 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 4 Aug 2017 13:37:20 +0800 Subject: [PATCH 039/225] Project translation --- target/uda.d | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/target/uda.d b/target/uda.d index 46392be..41a0285 100644 --- a/target/uda.d +++ b/target/uda.d @@ -19,12 +19,12 @@ $(P ) --- - @Encrypted @Colored string lastName; $(CODE_NOTE separately) - @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE together) + @Encrypted @Colored string lastName; $(CODE_NOTE 单独分开) + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE 组合一起) --- $(P -An attribute can be a type name as well as a value of a user defined or a fundamental type. However, because their meanings may not be clear, attributes consisting of literal values like $(C 42) are discouraged: +属性除了可以是自定义类型值或基础类型值以外,还可是以类型名。不过,因为它们的含义可能不是很清楚,因此不赞成大家使用文字量值(如 $(C 42) )构成的属性: ) --- @@ -38,23 +38,23 @@ struct Colored { } void main() { - @Encrypted int a; $(CODE_NOTE type name) - @Encrypted() int b; $(CODE_NOTE object) - @Colored(Color.blue) int c; $(CODE_NOTE object) - @(42) int d; $(CODE_NOTE literal (discouraged)) + @Encrypted int a; $(CODE_NOTE 类型名) + @Encrypted() int b; $(CODE_NOTE 对象) + @Colored(Color.blue) int c; $(CODE_NOTE 对象) + @(42) int d; $(CODE_NOTE 文字量(不赞成)) } --- $(P -The attributes of $(C a) and $(C b) above are of different kinds: The attribute of $(C a) is the type $(C Encrypted) itself, while the attribute of $(C b) is an $(I object) of type $(C Encrypted). This is an important difference that affects the way attributes are used at compile time. We will see an example of this difference below. +上面 $(C a) 和 $(C b) 的属性属于不同类别:$(C a) 的属性是 $(C Encrypted) 类型,而 $(C b) 的属性是 $(C Encrypted) 类型的一个$(I 对象)。它们有着明显的差异,它会对编译时属性的使用方式产生影响。下面来看一个与该差异有关的例子。 ) $(P -$(IX __traits) $(IX getAttributes) The meaning of attributes is solely determined by the programmer for the needs of the program. The attributes are determined by $(C __traits(getAttributes)) at compile time and the code is compiled according to those attributes. +$(IX __traits) $(IX getAttributes) 属性的实际含义完全由程序员根据程序的需要来决定。编译时可以通过 $(C __traits(getAttributes)) 来判定属性,而相应代码则会根据那些属性来编译。 ) $(P -The following code shows how the attributes of a specific $(C struct) member (e.g. $(C Person.name)) can be accessed by $(C __traits(getAttributes)): +下面代码展示的是如何使用使用$(C __traits(getAttributes))来访问某个特定 $(C struct) 成员(如 $(C Person.name))的属性: ) --- @@ -76,7 +76,7 @@ void $(CODE_DONT_TEST)main() { --- $(P -The output of the program lists the attributes of $(C Person.name): +这个程序的输出内容是 $(C Person.name) 的各个属性: ) $(SHELL @@ -85,14 +85,14 @@ Colored(cast(Color)1) ) $(P -Two other $(C __traits) expressions are useful when dealing with user defined attributes: +在处理自定义属性时,还有其他两个 $(C __traits) 表达式可以使用: ) $(UL -$(LI $(IX allMembers) $(C __traits(allMembers)) produces the members of a type (or a module) as strings.) +$(LI $(IX allMembers) $(C __traits(allMembers)) 会以字符串列表的形式生成某个类型(或模块)的所有成员。) -$(LI $(IX getMember) $(C __traits(getMember)) produces a $(I symbol) useful when accessing a member. Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. It produces a symbol by combining its first argument, a dot, and its second argument. For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). +$(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. It produces a symbol by combining its first argument, a dot, and its second argument. For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). ) ) From b41f1080b3b41854ffd4e1736df59e93898df8d8 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Fri, 4 Aug 2017 13:40:46 +0800 Subject: [PATCH 040/225] Translated by Heromyth Added: 2, Deleted: 0, Modified: 1 (Conflicts: 0) --- omegat/project_save.tmx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 6ef49b5..b7c6847 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -408,8 +408,8 @@ $(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . $(H5 Summary) - - $(H5 摘要) + + $(H5 小结) @@ -4831,6 +4831,14 @@ $(LINK2 /ders/d.cn/index.html, KEYWORDS=D 语言教程 string + + + KEYWORDS=d programming language tutorial book user defined attributes UDA + + + KEYWORDS=d programming language tutorial book user defined attributes UDA D 编程语言 教程 书籍 自定义属性 + + KEYWORDS=d programming lesson book tutorial class @@ -5063,6 +5071,14 @@ LANGUAGE=chinese SUBTITLE=字符串 习题解答 + + + SUBTITLE=User Defined Attributes (UDA) + + + SUBTITLE=自定义属性(UDA) + + Short for "read line", $(C readln()) reads until the end of the line. From a27821000d49013e2e47a78c754971d85a7cc922 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Sat, 5 Aug 2017 21:50:44 +0800 Subject: [PATCH 041/225] Translated by Heromyth --- omegat/project_save.tmx | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index b7c6847..8ae2b9d 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -2526,6 +2526,18 @@ The output indicates that the values 0 and 1 correspond to keys "Monday" and "Tu $(P 下面的输出表明了元素值 0 和 1 分别对应着键 “Monday” 和 “Tuesday”: +) + + + + + $(P +The output of the program lists all attributes of all members of $(C Person): +) + + + $(P +此程序的输出会列出 $(C Person) 的所有成员的所有属性: ) @@ -4271,6 +4283,14 @@ $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准 对于类, $(C opAssign) 意味着 $(I 一个类变量总是关联着一个类对象)。 + + + For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). + + + 例如,$(C __traits(getMember, Person, $(STRING "name"))) 生成的符号是 $(C Person.name)。 + + For example, $(C char[]) is a type of string. @@ -4747,6 +4767,14 @@ $(LINK2 /ders/d.cn/index.html, 如果至少其中一个‘é’字符没有做为单个编码点编码,而是一个‘e’与一个组合重音符的组合,那么它就可能会更长。 + + + It produces a symbol by combining its first argument, a dot, and its second argument. + + + 它会生成一个符号,其组成部分包含了它的第一个参数、一个小数点和它的第二个参数。 + + It returns $(C true) if the object is the same and $(C false) otherwise. @@ -4781,6 +4809,14 @@ $(LINK2 /ders/d.cn/index.html, ) + + + Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. + + + 它的第一个参数是符号(如类型或者变量名),第二个参数是字符串。 + + Its first parameter is the line that contains the data, and the rest of the parameters are used exacly like $(C readf()): From 1f4677174115f973058b461f779841b171df0c76 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Thu, 10 Aug 2017 18:48:41 +0800 Subject: [PATCH 042/225] Translated by Heromyth --- omegat/project_save.tmx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 8ae2b9d..634a14f 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -951,6 +951,16 @@ $(C .clear) 移除全部元素: $(P +$(C hasUDA) can be used with an attribute type as well as a specific value of that type. + + + $(P +$(C hasUDA) 除了可以与属性类型的特定值一起使用外,还可以与属性类型一起使用。 + + + + + $(P $(C is) specifies whether two class variables provide access to the same class object. @@ -1153,6 +1163,16 @@ $(IX final) 由于可重写的成员函数有一个运行时性能消耗,在 $(P +$(IX hasUDA, std.traits) Another useful tool is $(C std.traits.hasUDA), which determines whether a symbol has a specific attribute. + + + $(P +$(IX hasUDA, std.traits) 另一个有用的工具是 $(C std.traits.hasUDA),它可检测某个符号是否拥有某个特定的属性。 + + + + + $(P $(IX scalar) That definition can be read as $(I 5 double values). @@ -5283,6 +5303,26 @@ LANGUAGE=chinese 前两个与 $(C while) 循环有关: 循环条件都使用 $(C <=) 运算符而不使用 $(C <) 运算符。 + + + The following $(C static assert) checks both pass because $(C Person.name) has $(C Colored(Color.blue)) attribute: +) + + + 下面的两个 $(C static assert) 都会顺利通过,因为 $(C Person.name) 拥有 $(C Colored(Color.blue)) 属性: +) + + + + + The following $(C static assert) passes because $(C Person.name) has $(C Encrypted) attribute: +) + + + 下面的 $(C static assert) 会顺利通过,因为 $(C Person.name) 拥有 $(C Encrypted) 属性: +) + + The following methods would empty the array in a single step. From 3c4367fab9c18bcea197a1d8b96fd26a6d5bbf6f Mon Sep 17 00:00:00 2001 From: BitWorld Date: Mon, 14 Aug 2017 19:01:02 +0800 Subject: [PATCH 043/225] Translated by Heromyth --- omegat/project_save.tmx | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 634a14f..056ff4b 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -862,6 +862,14 @@ There are three mistakes (bugs) in this program. $(LI 复制操作将增加一个与对象关联的变量。 + + + $(LI The members of the type are determined by $(C __traits(allMembers)).) + + + $(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) + + $(LI There is a $(C d) at the end of the literal $(STRING "résumé"d), specifying its type as an array of $(C dchar)s.) @@ -1930,6 +1938,16 @@ Let's consider the following code that we have seen previously in the $(LINK2 /d $(P +Let's design a function template that prints the values of all members of a $(C struct) object in XML format. + + + $(P +一起来设计一个函数模板,让它以XML格式输出一个 $(C struct) 对象的所有成员的值。 + + + + + $(P Let's now revisit the exercise with the five values and write it again by using an array: ) @@ -2476,6 +2494,18 @@ hash 表的高性能的代价是元素的存储是无序的。 $(P +The highlighted parts of the code are explained below: +) + + + $(P +下面来解释一下这段代码的高亮部分: +) + + + + + $(P The keys of associative arrays can be of any type, including user-defined $(C struct) and $(C class) types. @@ -5320,6 +5350,16 @@ LANGUAGE=chinese 下面的 $(C static assert) 会顺利通过,因为 $(C Person.name) 拥有 $(C Encrypted) 属性: +) + + + + + The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: +) + + + 下面这个函数在生成输出内容时会使用到每个成员的 $(C Encrypted) 和 $(C Colored) 属性: ) From 62b1e00db119182d678973575aac42b6f13568a6 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 15 Aug 2017 13:23:23 +0800 Subject: [PATCH 044/225] Translated by Heromyth --- omegat/project_save.tmx | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 056ff4b..09acdd2 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -830,6 +830,14 @@ There are three mistakes (bugs) in this program. $(LI 类是引用类型。 + + + $(LI Each member is tested with $(C hasUDA) to determine whether it has the $(C Encrypted) attribute. + + + $(LI 每个成员都使用 $(C hasUDA) 来测试,以便确定它是否拥有 $(C Encrypted) 特性。 + + $(LI Removing them one-by-one from the associative array.) @@ -862,6 +870,14 @@ There are three mistakes (bugs) in this program. $(LI 复制操作将增加一个与对象关联的变量。 + + + $(LI The color attribute of each member is determined with $(C colorAttributeOf()), which we will see below.) + + + $(LI 每个成员的 color 属性可以使用 $(C colorAttributeOf()) 来检测。下面便来看看这个方法。) + + $(LI The members of the type are determined by $(C __traits(allMembers)).) @@ -870,6 +886,14 @@ There are three mistakes (bugs) in this program. $(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) + + + $(LI The value of each member is converted to $(C string) to be used later when printing to the output. + + + $(LI 每个成员值都被转换为 $(C string),以便后面输出时使用。 + + $(LI There is a $(C d) at the end of the literal $(STRING "résumé"d), specifying its type as an array of $(C dchar)s.) @@ -2226,6 +2250,18 @@ That solution may be slow especially for large arrays. $(P +The $(C colorAttributeOf()) function template can be implemented as in the following code: +) + + + $(P +函数模板 $(C colorAttributeOf()) 可以实现成下面的样子: +) + + + + + $(P The $(C dayNumbers) variable above is an associative array that can be used as a table that provides a mapping from day names to day numbers. @@ -3489,6 +3525,14 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO ($(C .clear) 是最自然的方法。) 至少还有三种方法: + + + (Because $(C hasUDA) requires $(I symbols) to work with, note how $(C __traits(getMember)) is used to get the member as a symbol (e.g. $(C Person.name)).)) + + + (因为 $(C hasUDA) 需要 $(I 符号) 才能工作,请参考一下如何使用 $(C __traits(getMember)) 以符号方式(如 $(C Person.name))获取成员的。)) + + (We will see D's $(I immutability) concept in later chapters.) @@ -4489,6 +4533,14 @@ $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准 例如,若这行是“this line has five words”程序就应该打印出“e has five”。 + + + For example, when the member is $(STRING "name"), the right-hand side expression becomes $(C object.name.to!string).) + + + 例如,当成员为 $(STRING "name") 时,对应的表达式会变成 $(C object.name.to!string)。) + + For example, when the strings are "ebru" and "domates" the program should print "Ebru&nbsp;Domates". @@ -5471,6 +5523,14 @@ LANGUAGE=chinese 这俩变量可访问同一对象。The two variables start providing access to the same object. + + + The value of the member is encrypted if it has that attribute. + + + 如果该成员拥有此属性,则它的值会被加密。 + + The values of the type that associative arrays $(I map from) are called $(I keys), rather than indexes. From ebc1d2f8c73fe6f84056c633f32fab91e73dd007 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 15 Aug 2017 13:32:48 +0800 Subject: [PATCH 045/225] Translated by Heromyth --- omegat/project_save.tmx | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 09acdd2..bfc430d 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -914,6 +914,30 @@ There are three mistakes (bugs) in this program. $(LI 这段程序使用两个索引值来生成一个切片: + + + $(LI User defined attributes can be accessed at compile time by $(C hasUDA) and $(C __traits(getAttributes)) to alter the way the program is compiled.) + + + $(LI 自定义属性在编译时可以通过 $(C hasUDA) 和 $(C __traits(getAttributes)) 来访问,以便达到更改程序编译方式的目的。) + + + + + $(LI User defined attributes can be assigned to any declaration.) + + + $(LI 自定义属性可用于任何声明。) + + + + + $(LI User defined attributes can be type names as well as values.) + + + $(LI 自定义属性可以是类型名,也可以是具体值。) + + $(LI Writing all of potentially unrelated expressions in the single $(C finally) block at the bottom separates those expressions from the actual code that they are related to. @@ -2422,6 +2446,18 @@ The behavior of assignment cannot be changed for classes. $(P +The complete program has more explanations: +) + + + $(P +列出整个程序代码更能说明问题: +) + + + + + $(P The definition of array variables is very similar to the definition of normal variables. @@ -3028,6 +3064,18 @@ When the actual object needs to be copied, the class must have a member function $(P +When the compile-time evaluations are completed, the $(C printAsXML()) function template would be instantiated for the $(C Person) type as the equivalent of the following function: +) + + + $(P +当编译时计算完成时,函数模板 $(C printAsXML()) 会根据 $(C Person) 类型实例化,并与下面这个函数相似: +) + + + + + $(P When the input cannot be converted to $(C name) and $(C age), the program prints an error: ) @@ -3621,6 +3669,16 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 * 将用于附加新成绩:*/ + + + --- +/* The equivalent of the printAsXML!Person instance. + + + --- +/* printAsXML!Person 实例的等同函数。 + + /* 'foreach' is similar but superior to 'for'. From 798c60eaff4434a871ffd8dc464a4a71fda3bffb Mon Sep 17 00:00:00 2001 From: BitWorld Date: Tue, 15 Aug 2017 13:35:19 +0800 Subject: [PATCH 046/225] Translated by Heromyth --- omegat/project_save.tmx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index bfc430d..2236e5e 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -4301,6 +4301,14 @@ COZUMLER_METIN=答案 DERLEME_HATASI_METIN=编译出错 + + + DESCRIPTION=Assigning user defined attributes to declarations, determining the attributes at compile time, and compiling the code according to those attributes. + + + DESCRIPTION=为声明加上自定义属性、在编译时检测属性,并根据那些属性编译代码。 + + DESCRIPTION=Basic array operations of the D programming language From ecb061776bc6ef2cb8bf3ad774bb428a23931823 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 12:17:23 +0800 Subject: [PATCH 047/225] Translated by Heromyth --- omegat/project_save.tmx | 198 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 2236e5e..36c22d5 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -412,6 +412,14 @@ $(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . $(H5 小结) + + + $(H5 The benefit of user defined attributes) + + + $(H5 自定义属性的好处) + + $(H6 $(IX assignment, class) Assignment) @@ -2672,6 +2680,18 @@ The output of the program lists the attributes of $(C Person.name): $(P 这个程序的输出内容是 $(C Person.name) 的各个属性: +) + + + + + $(P +The output of the program shows that the members have the correct color and that the $(C name) member is encrypted: +) + + + $(P +上面程序的输出内容包含那些拥有正确颜色的成员和被加密过的 $(C name) 成员: ) @@ -3565,6 +3585,46 @@ $(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! + + + &lt;address color="red"&gt;Avignon&lt;/address&gt; $(SHELL_NOTE red) +&lt;/Person&gt; +&lt;Person&gt; + + + &lt;address color="red"&gt;Avignon&lt;/address&gt; $(SHELL_NOTE 红色) +&lt;/Person&gt; +&lt;Person&gt; + + + + + &lt;address color="red"&gt;Bordeaux&lt;/address&gt; $(SHELL_NOTE red) +&lt;/Person&gt; +) + + + &lt;address color="red"&gt;Bordeaux&lt;/address&gt; $(SHELL_NOTE 红色) +&lt;/Person&gt; +) + + + + + &lt;name color="blue"&gt;Bmjdf&lt;/name&gt; $(SHELL_NOTE blue and encrypted) + + + &lt;name color="blue"&gt;Bmjdf&lt;/name&gt; $(SHELL_NOTE 蓝色且加密) + + + + + &lt;name color="blue"&gt;Cfo&lt;/name&gt; $(SHELL_NOTE blue and encrypted) + + + &lt;name color="blue"&gt;Cfo&lt;/name&gt; $(SHELL_NOTE 蓝色且加密) + + ($(C .clear) is the most natural method.) There are at least three methods: @@ -3581,6 +3641,14 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO (因为 $(C hasUDA) 需要 $(I 符号) 才能工作,请参考一下如何使用 $(C __traits(getMember)) 以符号方式(如 $(C Person.name))获取成员的。)) + + + (Warning: Caesar cipher is a very weak encryption + + + (警告:Caesar cipher 是一种强度很弱的 + + (We will see D's $(I immutability) concept in later chapters.) @@ -3629,6 +3697,14 @@ $(LI The $(C char[]) on the left-hand side is a slice, which, if the code compil $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编译成功,它将会访问右手侧的全部字符。 + + + * The default color is Color.black. + + + * 默认颜色为 Color.black。 + + * associative array is being defined with an extra @@ -3637,6 +3713,56 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 *关联数组在定义时附加了额外的空格, + + + * attributes of its members. + + + * 以 XML 格式输出它。 + + + + + * attributes. + + + * 属性。 + + + + + * blue. + + + * 蓝色。 + + + + + * encrypted. + + + * 的那个符号。 + + + + + * member has that attribute, Color.black otherwise. + + + * 则输出它的值,否则输出 Color.black。 + + + + + * method.) */ +auto encrypted(string value) { + + + * 加密方法。) */ +auto encrypted(string value) { + + * see the 'foreach' loop in the next chapter. @@ -3653,6 +3779,14 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 * 以易于区分值类型: */ + + + * string. + + + * 版本。 + + * the value type is int[], i.e. an array of ints. @@ -3695,6 +3829,46 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 /* 打印“emre”的成绩: */ + + + /* Prints the specified object in XML format according to the + + + /* 根据指定对象的各个成员的属性 + + + + + /* Returns the Caesar-encrypted version of the specified + + + /* 返回指定字符串的 Caesar 加密 + + + + + /* Returns the value of the Colored attribute if the specified + + + /* 如果被特别要求的成员拥有 Colored 属性, + + + + + /* Specifies that the symbol that it is assigned to should be + + + /* 特别要求加密应用此自定义属性 + + + + + /* Specifies the color of the symbol that it is assigned to. + + + /* 特别指定应用此自定义属性的那个符号的颜色。 + + /* The array of ints that correspond to "emre" is being @@ -3711,6 +3885,30 @@ $(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编 /* 该关联数组的键类型为 string, + + + /* This member does not have any user defined + + + /* 这个成员没有自定义 + + + + + /* This member is specified to be encrypted and printed in + + + /* 被特别要求加密的那个成员,并且输出为 + + + + + /* This member is specified to be printed in red. + + + /* 此成员被特别要求输出为红色。 + + // false: overcast From cfcd5ca740d210141c36191500185c9b2c3dd786 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 12:27:18 +0800 Subject: [PATCH 048/225] Translated by Heromyth --- omegat/project_save.tmx | 84 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 36c22d5..276a744 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -1804,6 +1804,18 @@ For that syntax to work, a constructor must be defined explicitly by the program $(P +Further, $(C printAsXML()) and the attributes that it considers can be used with other types as well: +) + + + $(P +此外,$(C printAsXML()) 和它涉及到的属性还可以与其他类型一起使用: +) + + + + + $(P Here is a program that prints the Turkish names of colors that are specified in English: ) @@ -2454,6 +2466,16 @@ The behavior of assignment cannot be changed for classes. $(P +The benefit of user defined attributes is being able to change the attributes of declarations without needing to change any other part of the program. + + + $(P +自定义属性的好处在于能够更改声明的属性,且不需要更改程序的其他部分。 + + + + + $(P The complete program has more explanations: ) @@ -3585,6 +3607,18 @@ $(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false ! + + + &lt;address color="black"&gt;Dbooft&lt;/address&gt; $(SHELL_NOTE encrypted) +&lt;/Person&gt; +) + + + &lt;address color="black"&gt;Dbooft&lt;/address&gt; $(SHELL_NOTE 加密) +&lt;/Person&gt; +) + + &lt;address color="red"&gt;Avignon&lt;/address&gt; $(SHELL_NOTE red) @@ -3609,6 +3643,34 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO ) + + + &lt;lastName color="black"&gt;ef!Dbooft&lt;/lastName&gt; $(SHELL_NOTE encrypted) + + + &lt;lastName color="black"&gt;ef!Dbooft&lt;/lastName&gt; $(SHELL_NOTE 加密) + + + + + &lt;message color="blue"&gt;hello world&lt;/message&gt; $(SHELL_NOTE blue) +&lt;/Data&gt; +) + + + &lt;message color="blue"&gt;hello world&lt;/message&gt; $(SHELL_NOTE 蓝色) +&lt;/Data&gt; +) + + + + + &lt;name color="black"&gt;Djoez&lt;/name&gt; $(SHELL_NOTE encrypted) + + + &lt;name color="black"&gt;Djoez&lt;/name&gt; $(SHELL_NOTE 加密) + + &lt;name color="blue"&gt;Bmjdf&lt;/name&gt; $(SHELL_NOTE blue and encrypted) @@ -3849,8 +3911,8 @@ auto encrypted(string value) { /* Returns the value of the Colored attribute if the specified - - /* 如果被特别要求的成员拥有 Colored 属性, + + /* 如果被特定要求的成员拥有 Colored 属性, @@ -3897,16 +3959,16 @@ auto encrypted(string value) { /* This member is specified to be encrypted and printed in - - /* 被特别要求加密的那个成员,并且输出为 + + /* 此成员被特定要求加密,并且输出为 /* This member is specified to be printed in red. - - /* 此成员被特别要求输出为红色。 + + /* 此成员被特定要求输出为红色。 @@ -4681,6 +4743,16 @@ $(LI $(LINK2 http://dlang.org/phobos/index.html, $(IMG bullet_black.png) 标准 例如,‘B’排在‘a’之前。 + + + For example, all of the members of $(C Person) can become encrypted in the XML output by the trivial change below: +) + + + 例如,$(C Person) 的所有成员在 XML 格式输出里都会被加密,类似下面内容: +) + + For example, an array that holds the air temperatures of the days in July can bring 31 $(C double) values together and form $(I a container of elements of type $(C double)). From bbcce2c79967800d58baf7598d5d25050d5de3f6 Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 12:28:42 +0800 Subject: [PATCH 049/225] Translated by Heromyth --- omegat/project_save.tmx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx index 276a744..5af2256 100644 --- a/omegat/project_save.tmx +++ b/omegat/project_save.tmx @@ -3613,8 +3613,8 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO &lt;/Person&gt; ) - - &lt;address color="black"&gt;Dbooft&lt;/address&gt; $(SHELL_NOTE 加密) + + &lt;address color="black"&gt;Dbooft&lt;/address&gt; $(SHELL_NOTE 已加密) &lt;/Person&gt; ) @@ -3647,8 +3647,8 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO &lt;lastName color="black"&gt;ef!Dbooft&lt;/lastName&gt; $(SHELL_NOTE encrypted) - - &lt;lastName color="black"&gt;ef!Dbooft&lt;/lastName&gt; $(SHELL_NOTE 加密) + + &lt;lastName color="black"&gt;ef!Dbooft&lt;/lastName&gt; $(SHELL_NOTE 已加密) @@ -3667,8 +3667,8 @@ $(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWO &lt;name color="black"&gt;Djoez&lt;/name&gt; $(SHELL_NOTE encrypted) - - &lt;name color="black"&gt;Djoez&lt;/name&gt; $(SHELL_NOTE 加密) + + &lt;name color="black"&gt;Djoez&lt;/name&gt; $(SHELL_NOTE 已加密) From d5af254acdaf60b3e499b0b3a3816c182a23c51e Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 12:29:17 +0800 Subject: [PATCH 050/225] Project translation --- target/uda.d | 98 ++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/target/uda.d b/target/uda.d index 41a0285..97fcfc3 100644 --- a/target/uda.d +++ b/target/uda.d @@ -92,7 +92,7 @@ $(UL $(LI $(IX allMembers) $(C __traits(allMembers)) 会以字符串列表的形式生成某个类型(或模块)的所有成员。) -$(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. It produces a symbol by combining its first argument, a dot, and its second argument. For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). +$(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。它的第一个参数是符号(如类型或者变量名),第二个参数是字符串。它会生成一个符号,其组成部分包含了它的第一个参数、一个小数点和它的第二个参数。例如,$(C __traits(getMember, Person, $(STRING "name"))) 生成的符号是 $(C Person.name)。 ) ) @@ -118,7 +118,7 @@ void main() { --- $(P -The output of the program lists all attributes of all members of $(C Person): +此程序的输出会列出 $(C Person) 的所有成员的所有属性: ) $(SHELL @@ -128,7 +128,7 @@ The attributes of address : Colored(cast(Color)2) ) $(P -$(IX hasUDA, std.traits) Another useful tool is $(C std.traits.hasUDA), which determines whether a symbol has a specific attribute. The following $(C static assert) passes because $(C Person.name) has $(C Encrypted) attribute: +$(IX hasUDA, std.traits) 另一个有用的工具是 $(C std.traits.hasUDA),它可检测某个符号是否拥有某个特定的属性。下面的 $(C static assert) 会顺利通过,因为 $(C Person.name) 拥有 $(C Encrypted) 属性: ) --- @@ -140,7 +140,7 @@ static assert(hasUDA!(Person.name, Encrypted)); --- $(P -$(C hasUDA) can be used with an attribute type as well as a specific value of that type. The following $(C static assert) checks both pass because $(C Person.name) has $(C Colored(Color.blue)) attribute: +$(C hasUDA) 除了可以与属性类型的特定值一起使用外,还可以与属性类型一起使用。下面的两个 $(C static assert) 都会顺利通过,因为 $(C Person.name) 拥有 $(C Colored(Color.blue)) 属性: ) --- @@ -151,7 +151,7 @@ static assert(hasUDA!(Person.name, $(HILITE Colored(Color.blue)))); $(H5 样例) $(P -Let's design a function template that prints the values of all members of a $(C struct) object in XML format. The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: +一起来设计一个函数模板,让它以XML格式输出一个 $(C struct) 对象的所有成员的值。下面这个函数在生成输出内容时会使用到每个成员的 $(C Encrypted) 和 $(C Colored) 属性: ) --- @@ -174,23 +174,23 @@ void printAsXML(T)(T object) { --- $(P -The highlighted parts of the code are explained below: +下面来解释一下这段代码的高亮部分: ) $(OL -$(LI The members of the type are determined by $(C __traits(allMembers)).) +$(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) -$(LI The value of each member is converted to $(C string) to be used later when printing to the output. For example, when the member is $(STRING "name"), the right-hand side expression becomes $(C object.name.to!string).) +$(LI 每个成员值都被转换为 $(C string),以便后面输出时使用。例如,当成员为 $(STRING "name") 时,对应的表达式会变成 $(C object.name.to!string)。) -$(LI Each member is tested with $(C hasUDA) to determine whether it has the $(C Encrypted) attribute. The value of the member is encrypted if it has that attribute. (Because $(C hasUDA) requires $(I symbols) to work with, note how $(C __traits(getMember)) is used to get the member as a symbol (e.g. $(C Person.name)).)) +$(LI 每个成员都使用 $(C hasUDA) 来测试,以便确定它是否拥有 $(C Encrypted) 特性。如果该成员拥有此属性,则它的值会被加密。(因为 $(C hasUDA) 需要 $(I 符号) 才能工作,请参考一下如何使用 $(C __traits(getMember)) 以符号方式(如 $(C Person.name))获取成员的。)) -$(LI The color attribute of each member is determined with $(C colorAttributeOf()), which we will see below.) +$(LI 每个成员的 color 属性可以使用 $(C colorAttributeOf()) 来检测。下面便来看看这个方法。) ) $(P -The $(C colorAttributeOf()) function template can be implemented as in the following code: +函数模板 $(C colorAttributeOf()) 可以实现成下面的样子: ) --- @@ -207,11 +207,11 @@ Color colorAttributeOf(T, string memberName)() { --- $(P -When the compile-time evaluations are completed, the $(C printAsXML()) function template would be instantiated for the $(C Person) type as the equivalent of the following function: +当编译时计算完成时,函数模板 $(C printAsXML()) 会根据 $(C Person) 类型实例化,并与下面这个函数相似: ) --- -/* The equivalent of the printAsXML!Person instance. */ +/* printAsXML!Person 实例的等同函数。*/ void printAsXML_Person(Person object) { // ... @@ -235,7 +235,7 @@ void printAsXML_Person(Person object) { --- $(P -The complete program has more explanations: +列出整个程序代码更能说明问题: ) --- @@ -245,34 +245,34 @@ import std.algorithm; import std.conv; import std.traits; -/* Specifies that the symbol that it is assigned to should be - * encrypted. */ +/* 特别要求加密应用此自定义属性 + * 的那个符号。*/ struct Encrypted { } enum Color { black, blue, red } -/* Specifies the color of the symbol that it is assigned to. - * The default color is Color.black. */ +/* 特别指定应用此自定义属性的那个符号的颜色。 + * 默认颜色为 Color.black。*/ struct Colored { Color color; } struct Person { - /* This member is specified to be encrypted and printed in - * blue. */ + /* 此成员被特定要求加密,并且输出为 + * 蓝色。*/ @Encrypted @Colored(Color.blue) string name; - /* This member does not have any user defined - * attributes. */ + /* 这个成员没有自定义 + * 属性。*/ string lastName; - /* This member is specified to be printed in red. */ + /* 此成员被特定要求输出为红色。*/ @Colored(Color.red) string address; } -/* Returns the value of the Colored attribute if the specified - * member has that attribute, Color.black otherwise. */ +/* 如果被特定要求的成员拥有 Colored 属性, + * 则输出它的值,否则输出 Color.black。*/ Color colorAttributeOf(T, string memberName)() { auto result = Color.black; @@ -287,9 +287,9 @@ Color colorAttributeOf(T, string memberName)() { return result; } -/* Returns the Caesar-encrypted version of the specified - * string. (Warning: Caesar cipher is a very weak encryption - * method.) */ +/* 返回指定字符串的 Caesar 加密 + * 版本。(警告:Caesar cipher 是一种强度很弱的 + * 加密方法。) */ auto encrypted(string value) { return value.map!(a => dchar(a + 1)); } @@ -298,8 +298,8 @@ unittest { assert("abcdefghij".encrypted.equal("bcdefghijk")); } -/* Prints the specified object in XML format according to the - * attributes of its members. */ +/* 根据指定对象的各个成员的属性 + * 以 XML 格式输出它。*/ void printAsXML(T)(T object) { writefln("<%s>", T.stringof); scope(exit) writefln("", T.stringof); @@ -329,26 +329,26 @@ void main() { --- $(P -The output of the program shows that the members have the correct color and that the $(C name) member is encrypted: +上面程序的输出内容包含那些拥有正确颜色的成员和被加密过的 $(C name) 成员: ) $(SHELL <Person> - <name color="blue">Bmjdf</name> $(SHELL_NOTE blue and encrypted) + <name color="blue">Bmjdf</name> $(SHELL_NOTE 蓝色且加密) <lastName color="black">Davignon</lastName> - <address color="red">Avignon</address> $(SHELL_NOTE red) + <address color="red">Avignon</address> $(SHELL_NOTE 红色) </Person> <Person> - <name color="blue">Cfo</name> $(SHELL_NOTE blue and encrypted) + <name color="blue">Cfo</name> $(SHELL_NOTE 蓝色且加密) <lastName color="black">de Bordeaux</lastName> - <address color="red">Bordeaux</address> $(SHELL_NOTE red) + <address color="red">Bordeaux</address> $(SHELL_NOTE 红色) </Person> ) -$(H5 The benefit of user defined attributes) +$(H5 自定义属性的好处) $(P -The benefit of user defined attributes is being able to change the attributes of declarations without needing to change any other part of the program. For example, all of the members of $(C Person) can become encrypted in the XML output by the trivial change below: +自定义属性的好处在于能够更改声明的属性,且不需要更改程序的其他部分。例如,$(C Person) 的所有成员在 XML 格式输出里都会被加密,类似下面内容: ) --- @@ -371,14 +371,14 @@ $(P $(SHELL <Person> - <name color="black">Djoez</name> $(SHELL_NOTE encrypted) - <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE encrypted) - <address color="black">Dbooft</address> $(SHELL_NOTE encrypted) + <name color="black">Djoez</name> $(SHELL_NOTE 已加密) + <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE 已加密) + <address color="black">Dbooft</address> $(SHELL_NOTE 已加密) </Person> ) $(P -Further, $(C printAsXML()) and the attributes that it considers can be used with other types as well: +此外,$(C printAsXML()) 和它涉及到的属性还可以与其他类型一起使用: ) --- @@ -397,25 +397,25 @@ $(P $(SHELL <Data> - <message color="blue">hello world</message> $(SHELL_NOTE blue) + <message color="blue">hello world</message> $(SHELL_NOTE 蓝色) </Data> ) -$(H5 摘要) +$(H5 小结) $(UL -$(LI User defined attributes can be assigned to any declaration.) +$(LI 自定义属性可用于任何声明。) -$(LI User defined attributes can be type names as well as values.) +$(LI 自定义属性可以是类型名,也可以是具体值。) -$(LI User defined attributes can be accessed at compile time by $(C hasUDA) and $(C __traits(getAttributes)) to alter the way the program is compiled.) +$(LI 自定义属性在编译时可以通过 $(C hasUDA) 和 $(C __traits(getAttributes)) 来访问,以便达到更改程序编译方式的目的。) ) macros: - SUBTITLE=User Defined Attributes (UDA) + SUBTITLE=自定义属性(UDA) - DESCRIPTION=Assigning user defined attributes to declarations, determining the attributes at compile time, and compiling the code according to those attributes. + DESCRIPTION=为声明加上自定义属性、在编译时检测属性,并根据那些属性编译代码。 - KEYWORDS=d programming language tutorial book user defined attributes UDA + KEYWORDS=d programming language tutorial book user defined attributes UDA D 编程语言 教程 书籍 自定义属性 From 881d206e4851693a2d8e333f0f90b0485db941fd Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 13:23:19 +0800 Subject: [PATCH 051/225] =?UTF-8?q?=E5=AE=8C=E6=88=90uda=E5=92=8Cindex?= =?UTF-8?q?=E4=B8=A4=E9=A1=B5=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ddili/src/ders/d.cn/index.d | 181 ++++++++++++++++++------------------ ddili/src/ders/d.cn/uda.d | 138 +++++++++++++-------------- 2 files changed, 157 insertions(+), 162 deletions(-) diff --git a/ddili/src/ders/d.cn/index.d b/ddili/src/ders/d.cn/index.d index e336066..2c14e1f 100644 --- a/ddili/src/ders/d.cn/index.d +++ b/ddili/src/ders/d.cn/index.d @@ -1,145 +1,140 @@ Ddoc -$(DERS_BOLUMU Programming in D) +$(DERS_BOLUMU D 语言编程)
- + +$(H6 ISBNs) $(P -The paper version of this book is available through two publishing platforms: +978-0-692-59943-3 hardcover by IngramSpark$(BR) +978-0-692-52957-7 paperback by IngramSpark$(BR) +978-1-515-07460-1 paperback by CreateSpace$(BR) +978-1-519-95441-1 ePUB by Draft2Digital$(BR) ) -$(P IngramSpark ISBN: 9780692529577) - -$(P CreateSpace ISBN: 9781515074601) - $(P -These options have different prices, shipping times, shipping costs, customs and other fees, availability at local book stores, etc. +以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 )
$(P -$(LINK_DOWNLOAD /ders/d.cn/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) +其他提供形式还包括:$(LINK2 https://gumroad.com/l/PinD, Gumroad提供的$(I 按需支付) 电子书) 以及各种$(I 免费) 版本的电子书,如 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.en/Programming_in_D.mobi, MOBI)。 ) -$(H5 Ebook versions) - -$(UL - -$(LI $(LINK2 https://gum.co/PinD, Download at Gumroad), which allows you to $(I pay what you want)) - -$(LI Download here for free as $(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.epub, EPUB) (for most ebook readers), $(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.azw3, AZW3) (for newer Kindles), or $(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.mobi, MOBI) (for older Kindles).) - +$(P +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) ) -$(H5 Web version) +$(H5 在线版本) + +$(P $(LINK2 /ders/d.en/ix.html, $(B 索引)) (可以使用关键字搜索)) $(UL $(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.cn/foreword1.html, Foreword by Walter Bright)) +$(LI $(LINK2 /ders/d.cn/foreword1.html, Walter Bright 序)) ) -$(LI $(LINK2 /ders/d.cn/ix.html, Book Index)) -$(LI $(LINK2 /ders/d.cn/foreword2.html, Foreword by Andrei Alexandrescu)) +$(LI $(LINK2 /ders/d.cn/foreword2.html, Andrei Alexandrescu 序)) $(LI $(LINK2 /ders/d.cn/preface.html, 前言)) -$(LI $(LINK2 /ders/d.cn/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.cn/writeln.html, writeln and write)) +$(LI $(LINK2 /ders/d.cn/hello_world.html, Hello World程序) $(INDEX_KEYWORDS main)) +$(LI $(LINK2 /ders/d.cn/writeln.html, writeln 和 write)) $(LI $(LINK2 /ders/d.cn/compiler.html, 编译)) -$(LI $(LINK2 /ders/d.cn/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) -$(LI $(LINK2 /ders/d.cn/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.cn/variables.html, Variables)) -$(LI $(LINK2 /ders/d.cn/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入中读取数据)) -$(LI $(LINK2 /ders/d.cn/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) +$(LI $(LINK2 /ders/d.cn/types.html, 基础类型) $(INDEX_KEYWORDS char int double (等))) +$(LI $(LINK2 /ders/d.cn/assignment.html, 赋值与计算顺序) $(INDEX_KEYWORDS =)) +$(LI $(LINK2 /ders/d.cn/variables.html, 变量)) +$(LI $(LINK2 /ders/d.cn/io.html, 标准输入、输出流) $(INDEX_KEYWORDS stdin stdout)) +$(LI $(LINK2 /ders/d.cn/input.html, 从标准输入读取)) +$(LI $(LINK2 /ders/d.cn/logical_expressions.html, 逻辑表达式) $(INDEX_KEYWORDS bool true false !== != < <= > >= || &&)) $(LI $(LINK2 /ders/d.cn/if.html, if 语句) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.cn/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.cn/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.cn/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN <> !<>= (and more))) -$(LI $(LINK2 /ders/d.cn/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.cn/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.cn/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. $ .dup capacity)) +$(LI $(LINK2 /ders/d.cn/while.html, while 循环) $(INDEX_KEYWORDS while continue break)) +$(LI $(LINK2 /ders/d.cn/arithmetic.html, 整型和算术运算) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) +$(LI $(LINK2 /ders/d.cn/floating_point.html, 浮点类型) $(INDEX_KEYWORDS .nan .infinity isNaN)) +$(LI $(LINK2 /ders/d.cn/arrays.html, 数组) $(INDEX_KEYWORDS [] .length ~ ~=)) +$(LI $(LINK2 /ders/d.cn/characters.html, 字符) $(INDEX_KEYWORDS char wchar dchar)) +$(LI $(LINK2 /ders/d.cn/slices.html, 分片与其他数组功能) $(INDEX_KEYWORDS .. $ .dup capacity)) $(LI $(LINK2 /ders/d.cn/strings.html, 字符串) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.cn/stream_redirect.html, Redirecting Standard Input and Output Streams)) +$(LI $(LINK2 /ders/d.cn/stream_redirect.html, 重定向标准输入输出流)) $(LI $(LINK2 /ders/d.cn/files.html, 文件) $(INDEX_KEYWORDS File)) $(LI $(LINK2 /ders/d.cn/auto_and_typeof.html, auto 和 typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.cn/name_space.html, 命名作用域)) -$(LI $(LINK2 /ders/d.cn/for.html, for Loop) $(INDEX_KEYWORDS for)) +$(LI $(LINK2 /ders/d.cn/name_space.html, 名字作用域)) +$(LI $(LINK2 /ders/d.cn/for.html, for 循环) $(INDEX_KEYWORDS for)) $(LI $(LINK2 /ders/d.cn/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.cn/literals.html, Literals)) +$(LI $(LINK2 /ders/d.cn/literals.html, 文字量)) $(LI $(LINK2 /ders/d.cn/formatted_output.html, 格式化输出) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.cn/formatted_input.html, Formatted Input)) +$(LI $(LINK2 /ders/d.cn/formatted_input.html, 格式化输入)) $(LI $(LINK2 /ders/d.cn/do_while.html, do-while 循环) $(INDEX_KEYWORDS do while)) $(LI $(LINK2 /ders/d.cn/aa.html, 关联数组) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.cn/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.cn/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) +$(LI $(LINK2 /ders/d.cn/foreach.html, foreach 循环) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) +$(LI $(LINK2 /ders/d.cn/switch_case.html, switch 和 case) $(INDEX_KEYWORDS switch, case, default, final switch)) $(LI $(LINK2 /ders/d.cn/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.cn/functions.html, Functions) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.cn/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) +$(LI $(LINK2 /ders/d.cn/functions.html, 函数) $(INDEX_KEYWORDS return void)) +$(LI $(LINK2 /ders/d.cn/const_and_immutable.html, 不变量) $(INDEX_KEYWORDS enum const immutable .dup .idup)) +$(LI $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型与引用类型) $(INDEX_KEYWORDS &)) +$(LI $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) $(LI $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值与右值) $(INDEX_KEYWORDS auto ref)) $(LI $(LINK2 /ders/d.cn/lazy_operators.html, 惰性运算符)) -$(LI $(LINK2 /ders/d.cn/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) +$(LI $(LINK2 /ders/d.cn/main.html, 程序环境) $(INDEX_KEYWORDS main stderr)) $(LI $(LINK2 /ders/d.cn/exceptions.html, 异常) $(INDEX_KEYWORDS throw try catch finally)) $(LI $(LINK2 /ders/d.cn/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.cn/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.cn/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.cn/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.cn/lifetimes.html, Lifetimes and Fundamental Operations)) -$(LI $(LINK2 /ders/d.cn/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.cn/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.cn/struct.html, Structs) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ __PRETTY_FUNCTION__)) -$(LI $(LINK2 /ders/d.cn/function_overloading.html, Function Overloading)) -$(LI $(LINK2 /ders/d.cn/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数 and const 成员函数) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.cn/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.cn/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) +$(LI $(LINK2 /ders/d.cn/assert.html, assert 与 enforce) $(INDEX_KEYWORDS assert enforce)) +$(LI $(LINK2 /ders/d.cn/unit_testing.html, 单元测试) $(INDEX_KEYWORDS unittest)) +$(LI $(LINK2 /ders/d.cn/contracts.html, 契约编程) $(INDEX_KEYWORDS in out body)) +$(LI $(LINK2 /ders/d.cn/lifetimes.html, 生命周期与函数式运算)) +$(LI $(LINK2 /ders/d.cn/null_is.html, null 值与 is 运算符) $(INDEX_KEYWORDS null is !is)) +$(LI $(LINK2 /ders/d.cn/cast.html, 类型转换) $(INDEX_KEYWORDS to assumeUnique cast)) +$(LI $(LINK2 /ders/d.cn/struct.html, 结构) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/parameter_flexibility.html, 不定个数参数) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__(等))) +$(LI $(LINK2 /ders/d.cn/function_overloading.html, 函数重载)) +$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数) $(INDEX_KEYWORDS toString)) +$(LI $(LINK2 /ders/d.cn/const_member_functions.html, const ref 参数和 const 函数函数) $(INDEX_KEYWORDS const ref, in ref, inout)) +$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其他特殊函数) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) +$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex(等))) $(LI $(LINK2 /ders/d.cn/class.html, 类) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.cn/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) +$(LI $(LINK2 /ders/d.cn/inheritance.html, 继承) $(INDEX_KEYWORDS : super override abstract)) $(LI $(LINK2 /ders/d.cn/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.cn/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.cn/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.cn/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.cn/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.cn/ufcs.html, 通用函数调用语法(UFCS))) -$(LI $(LINK2 /ders/d.cn/property.html, 属性) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.cn/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) +$(LI $(LINK2 /ders/d.cn/interface.html, 接口) $(INDEX_KEYWORDS interface static final)) +$(LI $(LINK2 /ders/d.cn/destroy.html, destroy 和 scoped) $(INDEX_KEYWORDS destroy scoped)) +$(LI $(LINK2 /ders/d.cn/modules.html, 模块和库) $(INDEX_KEYWORDS import, module, static this, static ~this)) +$(LI $(LINK2 /ders/d.cn/encapsulation.html, 封装和保护属性) $(INDEX_KEYWORDS private protected public package)) +$(LI $(LINK2 /ders/d.cn/ufcs.html, 统一调用语法(UFCS))) +$(LI $(LINK2 /ders/d.cn/property.html, 特性) $(INDEX_KEYWORDS @property)) +$(LI $(LINK2 /ders/d.cn/invariant.html, 结构和类的契约编程) $(INDEX_KEYWORDS invariant)) $(LI $(LINK2 /ders/d.cn/templates.html, 模板)) -$(LI $(LINK2 /ders/d.cn/pragma.html, Pragmas)) -$(LI $(LINK2 /ders/d.cn/alias.html, alias 与 with) $(INDEX_KEYWORDS alias with)) +$(LI $(LINK2 /ders/d.cn/pragma.html, 编译指令)) +$(LI $(LINK2 /ders/d.cn/alias.html, alias 和 with) $(INDEX_KEYWORDS alias with)) $(LI $(LINK2 /ders/d.cn/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.cn/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.cn/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.cn/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) +$(LI $(LINK2 /ders/d.cn/pointers.html, 指针) $(INDEX_KEYWORDS * &)) +$(LI $(LINK2 /ders/d.cn/bit_operations.html, 位运算) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) +$(LI $(LINK2 /ders/d.cn/cond_comp.html, 条件编译) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) $(LI $(LINK2 /ders/d.cn/is_expr.html, is 表达式) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.cn/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) -$(LI $(LINK2 /ders/d.cn/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.cn/union.html, Unions) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.cn/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) +$(LI $(LINK2 /ders/d.cn/lambda.html, 函数指针、委托和λ) $(INDEX_KEYWORDS function delegate => toString)) +$(LI $(LINK2 /ders/d.cn/foreach_opapply.html, 将foreach用于结构和类) $(INDEX_KEYWORDS opApply empty popFront front(等))) +$(LI $(LINK2 /ders/d.cn/nested.html, 嵌套函数、结构和类) $(INDEX_KEYWORDS static)) +$(LI $(LINK2 /ders/d.cn/union.html, 联合) $(INDEX_KEYWORDS union)) +$(LI $(LINK2 /ders/d.cn/goto.html, 标签和 goto) $(INDEX_KEYWORDS goto)) $(LI $(LINK2 /ders/d.cn/tuples.html, 元组) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.cn/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.cn/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.cn/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.cn/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.cn/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) -$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.cn/concurrency.html, 并发消息传递) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.cn/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.cn/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.cn/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.cn/operator_precedence.html, Operator Precedence)) +$(LI $(LINK2 /ders/d.cn/templates_more.html, 模板的更多内容) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) +$(LI $(LINK2 /ders/d.cn/functions_more.html, 函数的更多内容) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) +$(LI $(LINK2 /ders/d.cn/mixin.html, 混入) $(INDEX_KEYWORDS mixin)) +$(LI $(LINK2 /ders/d.cn/ranges.html, 范围) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) +$(LI $(LINK2 /ders/d.cn/ranges_more.html, 范围的更多内容) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject(等))) +$(LI $(LINK2 /ders/d.cn/parallelism.html, 并行性) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) +$(LI $(LINK2 /ders/d.cn/concurrency.html, 消息传递并发) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) +$(LI $(LINK2 /ders/d.cn/concurrency_shared.html, 数据共享并发) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) +$(LI $(LINK2 /ders/d.cn/fibers.html, 纤程) $(INDEX_KEYWORDS call yield)) +$(LI $(LINK2 /ders/d.cn/memory.html, 内存管理) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) +$(LI $(LINK2 /ders/d.cn/uda.html, 自定义属性(UDA)) $(INDEX_KEYWORDS @)) +$(LI $(LINK2 /ders/d.cn/operator_precedence.html, 运算符优先级)) ) Macros: - SUBTITLE=Programming in D + SUBTITLE=D 语言编程 - DESCRIPTION=D programming language tutorial from the ground up. + DESCRIPTION=全新编写的 D 编程语言教程。 - KEYWORDS=d programming language tutorial book novice beginner + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/ders/d.cn/uda.d b/ddili/src/ders/d.cn/uda.d index 9c0bb33..97fcfc3 100644 --- a/ddili/src/ders/d.cn/uda.d +++ b/ddili/src/ders/d.cn/uda.d @@ -1,13 +1,13 @@ Ddoc -$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) User Defined Attributes (UDA)) +$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) $(P -Any declaration (e.g. struct type, class type, variable, etc.) can be assigned attributes, which can then be accessed at compile time to alter the way the code is compiled. User defined attributes is purely a compile-time feature. +所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。自定义属性完全是一项编译时功能。 ) $(P -$(IX @) The user defined attribute syntax consists of the $(C @) sign followed by the attribute and appear before the declaration that it is being assigned to. For example, the following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: ) --- @@ -15,16 +15,16 @@ $(IX @) The user defined attribute syntax consists of the $(C @) sign followed b --- $(P -Multiple attributes can be specified separately or as a parenthesized list of attributes. For example, both of the following variables have the same attributes: +多个属性可以分别指定,也可以采用括号列表形式。例如,下面的变量拥有的属性是相同的: ) --- - @Encrypted @Colored string lastName; $(CODE_NOTE separately) - @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE together) + @Encrypted @Colored string lastName; $(CODE_NOTE 单独分开) + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE 组合一起) --- $(P -An attribute can be a type name as well as a value of a user defined or a fundamental type. However, because their meanings may not be clear, attributes consisting of literal values like $(C 42) are discouraged: +属性除了可以是自定义类型值或基础类型值以外,还可是以类型名。不过,因为它们的含义可能不是很清楚,因此不赞成大家使用文字量值(如 $(C 42) )构成的属性: ) --- @@ -38,23 +38,23 @@ struct Colored { } void main() { - @Encrypted int a; $(CODE_NOTE type name) - @Encrypted() int b; $(CODE_NOTE object) - @Colored(Color.blue) int c; $(CODE_NOTE object) - @(42) int d; $(CODE_NOTE literal (discouraged)) + @Encrypted int a; $(CODE_NOTE 类型名) + @Encrypted() int b; $(CODE_NOTE 对象) + @Colored(Color.blue) int c; $(CODE_NOTE 对象) + @(42) int d; $(CODE_NOTE 文字量(不赞成)) } --- $(P -The attributes of $(C a) and $(C b) above are of different kinds: The attribute of $(C a) is the type $(C Encrypted) itself, while the attribute of $(C b) is an $(I object) of type $(C Encrypted). This is an important difference that affects the way attributes are used at compile time. We will see an example of this difference below. +上面 $(C a) 和 $(C b) 的属性属于不同类别:$(C a) 的属性是 $(C Encrypted) 类型,而 $(C b) 的属性是 $(C Encrypted) 类型的一个$(I 对象)。它们有着明显的差异,它会对编译时属性的使用方式产生影响。下面来看一个与该差异有关的例子。 ) $(P -$(IX __traits) $(IX getAttributes) The meaning of attributes is solely determined by the programmer for the needs of the program. The attributes are determined by $(C __traits(getAttributes)) at compile time and the code is compiled according to those attributes. +$(IX __traits) $(IX getAttributes) 属性的实际含义完全由程序员根据程序的需要来决定。编译时可以通过 $(C __traits(getAttributes)) 来判定属性,而相应代码则会根据那些属性来编译。 ) $(P -The following code shows how the attributes of a specific $(C struct) member (e.g. $(C Person.name)) can be accessed by $(C __traits(getAttributes)): +下面代码展示的是如何使用使用$(C __traits(getAttributes))来访问某个特定 $(C struct) 成员(如 $(C Person.name))的属性: ) --- @@ -76,7 +76,7 @@ void $(CODE_DONT_TEST)main() { --- $(P -The output of the program lists the attributes of $(C Person.name): +这个程序的输出内容是 $(C Person.name) 的各个属性: ) $(SHELL @@ -85,14 +85,14 @@ Colored(cast(Color)1) ) $(P -Two other $(C __traits) expressions are useful when dealing with user defined attributes: +在处理自定义属性时,还有其他两个 $(C __traits) 表达式可以使用: ) $(UL -$(LI $(IX allMembers) $(C __traits(allMembers)) produces the members of a type (or a module) as strings.) +$(LI $(IX allMembers) $(C __traits(allMembers)) 会以字符串列表的形式生成某个类型(或模块)的所有成员。) -$(LI $(IX getMember) $(C __traits(getMember)) produces a $(I symbol) useful when accessing a member. Its first argument is a symbol (e.g. a type or a variable name) and its second argument is a string. It produces a symbol by combining its first argument, a dot, and its second argument. For example, $(C __traits(getMember, Person, $(STRING "name"))) produces the symbol $(C Person.name). +$(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。它的第一个参数是符号(如类型或者变量名),第二个参数是字符串。它会生成一个符号,其组成部分包含了它的第一个参数、一个小数点和它的第二个参数。例如,$(C __traits(getMember, Person, $(STRING "name"))) 生成的符号是 $(C Person.name)。 ) ) @@ -118,7 +118,7 @@ void main() { --- $(P -The output of the program lists all attributes of all members of $(C Person): +此程序的输出会列出 $(C Person) 的所有成员的所有属性: ) $(SHELL @@ -128,7 +128,7 @@ The attributes of address : Colored(cast(Color)2) ) $(P -$(IX hasUDA, std.traits) Another useful tool is $(C std.traits.hasUDA), which determines whether a symbol has a specific attribute. The following $(C static assert) passes because $(C Person.name) has $(C Encrypted) attribute: +$(IX hasUDA, std.traits) 另一个有用的工具是 $(C std.traits.hasUDA),它可检测某个符号是否拥有某个特定的属性。下面的 $(C static assert) 会顺利通过,因为 $(C Person.name) 拥有 $(C Encrypted) 属性: ) --- @@ -140,7 +140,7 @@ static assert(hasUDA!(Person.name, Encrypted)); --- $(P -$(C hasUDA) can be used with an attribute type as well as a specific value of that type. The following $(C static assert) checks both pass because $(C Person.name) has $(C Colored(Color.blue)) attribute: +$(C hasUDA) 除了可以与属性类型的特定值一起使用外,还可以与属性类型一起使用。下面的两个 $(C static assert) 都会顺利通过,因为 $(C Person.name) 拥有 $(C Colored(Color.blue)) 属性: ) --- @@ -148,10 +148,10 @@ static assert(hasUDA!(Person.name, $(HILITE Colored))); static assert(hasUDA!(Person.name, $(HILITE Colored(Color.blue)))); --- -$(H5 Example) +$(H5 样例) $(P -Let's design a function template that prints the values of all members of a $(C struct) object in XML format. The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: +一起来设计一个函数模板,让它以XML格式输出一个 $(C struct) 对象的所有成员的值。下面这个函数在生成输出内容时会使用到每个成员的 $(C Encrypted) 和 $(C Colored) 属性: ) --- @@ -174,23 +174,23 @@ void printAsXML(T)(T object) { --- $(P -The highlighted parts of the code are explained below: +下面来解释一下这段代码的高亮部分: ) $(OL -$(LI The members of the type are determined by $(C __traits(allMembers)).) +$(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) -$(LI The value of each member is converted to $(C string) to be used later when printing to the output. For example, when the member is $(STRING "name"), the right-hand side expression becomes $(C object.name.to!string).) +$(LI 每个成员值都被转换为 $(C string),以便后面输出时使用。例如,当成员为 $(STRING "name") 时,对应的表达式会变成 $(C object.name.to!string)。) -$(LI Each member is tested with $(C hasUDA) to determine whether it has the $(C Encrypted) attribute. The value of the member is encrypted if it has that attribute. (Because $(C hasUDA) requires $(I symbols) to work with, note how $(C __traits(getMember)) is used to get the member as a symbol (e.g. $(C Person.name)).)) +$(LI 每个成员都使用 $(C hasUDA) 来测试,以便确定它是否拥有 $(C Encrypted) 特性。如果该成员拥有此属性,则它的值会被加密。(因为 $(C hasUDA) 需要 $(I 符号) 才能工作,请参考一下如何使用 $(C __traits(getMember)) 以符号方式(如 $(C Person.name))获取成员的。)) -$(LI The color attribute of each member is determined with $(C colorAttributeOf()), which we will see below.) +$(LI 每个成员的 color 属性可以使用 $(C colorAttributeOf()) 来检测。下面便来看看这个方法。) ) $(P -The $(C colorAttributeOf()) function template can be implemented as in the following code: +函数模板 $(C colorAttributeOf()) 可以实现成下面的样子: ) --- @@ -207,11 +207,11 @@ Color colorAttributeOf(T, string memberName)() { --- $(P -When the compile-time evaluations are completed, the $(C printAsXML()) function template would be instantiated for the $(C Person) type as the equivalent of the following function: +当编译时计算完成时,函数模板 $(C printAsXML()) 会根据 $(C Person) 类型实例化,并与下面这个函数相似: ) --- -/* The equivalent of the printAsXML!Person instance. */ +/* printAsXML!Person 实例的等同函数。*/ void printAsXML_Person(Person object) { // ... @@ -235,7 +235,7 @@ void printAsXML_Person(Person object) { --- $(P -The complete program has more explanations: +列出整个程序代码更能说明问题: ) --- @@ -245,34 +245,34 @@ import std.algorithm; import std.conv; import std.traits; -/* Specifies that the symbol that it is assigned to should be - * encrypted. */ +/* 特别要求加密应用此自定义属性 + * 的那个符号。*/ struct Encrypted { } enum Color { black, blue, red } -/* Specifies the color of the symbol that it is assigned to. - * The default color is Color.black. */ +/* 特别指定应用此自定义属性的那个符号的颜色。 + * 默认颜色为 Color.black。*/ struct Colored { Color color; } struct Person { - /* This member is specified to be encrypted and printed in - * blue. */ + /* 此成员被特定要求加密,并且输出为 + * 蓝色。*/ @Encrypted @Colored(Color.blue) string name; - /* This member does not have any user defined - * attributes. */ + /* 这个成员没有自定义 + * 属性。*/ string lastName; - /* This member is specified to be printed in red. */ + /* 此成员被特定要求输出为红色。*/ @Colored(Color.red) string address; } -/* Returns the value of the Colored attribute if the specified - * member has that attribute, Color.black otherwise. */ +/* 如果被特定要求的成员拥有 Colored 属性, + * 则输出它的值,否则输出 Color.black。*/ Color colorAttributeOf(T, string memberName)() { auto result = Color.black; @@ -287,9 +287,9 @@ Color colorAttributeOf(T, string memberName)() { return result; } -/* Returns the Caesar-encrypted version of the specified - * string. (Warning: Caesar cipher is a very weak encryption - * method.) */ +/* 返回指定字符串的 Caesar 加密 + * 版本。(警告:Caesar cipher 是一种强度很弱的 + * 加密方法。) */ auto encrypted(string value) { return value.map!(a => dchar(a + 1)); } @@ -298,8 +298,8 @@ unittest { assert("abcdefghij".encrypted.equal("bcdefghijk")); } -/* Prints the specified object in XML format according to the - * attributes of its members. */ +/* 根据指定对象的各个成员的属性 + * 以 XML 格式输出它。*/ void printAsXML(T)(T object) { writefln("<%s>", T.stringof); scope(exit) writefln("", T.stringof); @@ -329,26 +329,26 @@ void main() { --- $(P -The output of the program shows that the members have the correct color and that the $(C name) member is encrypted: +上面程序的输出内容包含那些拥有正确颜色的成员和被加密过的 $(C name) 成员: ) $(SHELL <Person> - <name color="blue">Bmjdf</name> $(SHELL_NOTE blue and encrypted) + <name color="blue">Bmjdf</name> $(SHELL_NOTE 蓝色且加密) <lastName color="black">Davignon</lastName> - <address color="red">Avignon</address> $(SHELL_NOTE red) + <address color="red">Avignon</address> $(SHELL_NOTE 红色) </Person> <Person> - <name color="blue">Cfo</name> $(SHELL_NOTE blue and encrypted) + <name color="blue">Cfo</name> $(SHELL_NOTE 蓝色且加密) <lastName color="black">de Bordeaux</lastName> - <address color="red">Bordeaux</address> $(SHELL_NOTE red) + <address color="red">Bordeaux</address> $(SHELL_NOTE 红色) </Person> ) -$(H5 The benefit of user defined attributes) +$(H5 自定义属性的好处) $(P -The benefit of user defined attributes is being able to change the attributes of declarations without needing to change any other part of the program. For example, all of the members of $(C Person) can become encrypted in the XML output by the trivial change below: +自定义属性的好处在于能够更改声明的属性,且不需要更改程序的其他部分。例如,$(C Person) 的所有成员在 XML 格式输出里都会被加密,类似下面内容: ) --- @@ -366,19 +366,19 @@ struct Person { --- $(P -The output: +输出: ) $(SHELL <Person> - <name color="black">Djoez</name> $(SHELL_NOTE encrypted) - <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE encrypted) - <address color="black">Dbooft</address> $(SHELL_NOTE encrypted) + <name color="black">Djoez</name> $(SHELL_NOTE 已加密) + <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE 已加密) + <address color="black">Dbooft</address> $(SHELL_NOTE 已加密) </Person> ) $(P -Further, $(C printAsXML()) and the attributes that it considers can be used with other types as well: +此外,$(C printAsXML()) 和它涉及到的属性还可以与其他类型一起使用: ) --- @@ -392,30 +392,30 @@ struct Data { --- $(P -The output: +输出: ) $(SHELL <Data> - <message color="blue">hello world</message> $(SHELL_NOTE blue) + <message color="blue">hello world</message> $(SHELL_NOTE 蓝色) </Data> ) -$(H5 Summary) +$(H5 小结) $(UL -$(LI User defined attributes can be assigned to any declaration.) +$(LI 自定义属性可用于任何声明。) -$(LI User defined attributes can be type names as well as values.) +$(LI 自定义属性可以是类型名,也可以是具体值。) -$(LI User defined attributes can be accessed at compile time by $(C hasUDA) and $(C __traits(getAttributes)) to alter the way the program is compiled.) +$(LI 自定义属性在编译时可以通过 $(C hasUDA) 和 $(C __traits(getAttributes)) 来访问,以便达到更改程序编译方式的目的。) ) macros: - SUBTITLE=User Defined Attributes (UDA) + SUBTITLE=自定义属性(UDA) - DESCRIPTION=Assigning user defined attributes to declarations, determining the attributes at compile time, and compiling the code according to those attributes. + DESCRIPTION=为声明加上自定义属性、在编译时检测属性,并根据那些属性编译代码。 - KEYWORDS=d programming language tutorial book user defined attributes UDA + KEYWORDS=d programming language tutorial book user defined attributes UDA D 编程语言 教程 书籍 自定义属性 From ad710875c1602e2baf0c50074d696f40a66ef94b Mon Sep 17 00:00:00 2001 From: BitWorld Date: Wed, 16 Aug 2017 15:22:21 +0800 Subject: [PATCH 052/225] =?UTF-8?q?=E7=AE=80=E5=8C=96=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ddili/.gitignore | 56 - ddili/BENIOKU | 108 - ddili/README | 91 - .../conversion/azw3_output.py | 8 - .../conversion/comic_input.py | 0 ddili/calibre_config_dir/conversion/debug.py | 0 .../conversion/docx_input.py | 0 .../conversion/docx_output.py | 6 - .../conversion/epub_output.py | 11 - .../conversion/fb2_input.py | 0 .../conversion/fb2_output.py | 4 - .../conversion/heuristics.py | 13 - .../conversion/htmlz_output.py | 5 - .../conversion/look_and_feel.py | 24 - .../conversion/lrf_output.py | 13 - .../calibre_config_dir/conversion/metadata.py | 0 .../conversion/mobi_output.py | 12 - .../conversion/page_setup.py | 8 - .../conversion/pdb_output.py | 5 - .../conversion/pdf_input.py | 0 .../conversion/pdf_output.py | 18 - .../conversion/pmlz_output.py | 5 - .../conversion/rb_output.py | 3 - .../conversion/rtf_input.py | 0 .../conversion/search_and_replace.py | 9 - .../conversion/snb_output.py | 6 - .../conversion/structure_detection.py | 9 - ddili/calibre_config_dir/conversion/toc.py | 11 - .../conversion/txt_input.py | 0 .../conversion/txt_output.py | 11 - .../conversion/txtz_output.py | 11 - ddili/calibre_config_dir/dynamic.pickle | Bin 698 -> 0 bytes .../fonts/scanner_cache.json | 2777 ----------------- ddili/calibre_config_dir/global.py | 98 - ddili/calibre_config_dir/gui.json | 261 -- ddili/calibre_config_dir/gui.py | 178 -- ddili/calibre_config_dir/history.plist | 28 - ddili/calibre_config_dir/iterator.pickle | Bin 8223 -> 0 bytes .../metadata_sources/ISBNDB.json | 3 - ddili/calibre_config_dir/tweak_book_gui.json | 129 - ddili/calibre_config_dir/tweaks.py | 566 ---- ddili/calibre_config_dir/viewer.json | 163 - ddili/calibre_config_dir/viewer.py | 134 - .../viewer_dictionaries.json | 3 - ddili/src/404.d | 24 - ddili/src/AliCehreli_resume.d | 291 -- .../com.apple.ibooks.display-options.xml | 7 - ddili/src/Makefile | 281 -- ddili/src/Makefile.ders.in | 300 -- ddili/src/README.ebook | 38 - ddili/src/alphabet.d | 346 -- ddili/src/anchorgen.d | 605 ---- ddili/src/breadcrumbs.ddoc | 4 - ddili/src/chinese.ddoc | 1 - ddili/src/codetester.d | 514 --- ddili/src/common.ddoc | 143 - ddili/src/copyright.d | 39 - ddili/src/cozum.ddoc | 31 - ddili/src/ddili.ddoc | 50 - ddili/src/ders.ddoc | 57 - ddili/src/ders/breadcrumbs.ddoc | 4 - ddili/src/ders/d.cn/Makefile.in | 150 - ddili/src/ders/d.cn/README | 25 - ddili/src/ders/d.cn/aa.cozum.d | 117 - ddili/src/ders/d.cn/aa.d | 323 -- ddili/src/ders/d.cn/alias.d | 447 --- ddili/src/ders/d.cn/alias_this.d | 174 -- ddili/src/ders/d.cn/arithmetic.d | 867 ----- ddili/src/ders/d.cn/assert.d | 419 --- ddili/src/ders/d.cn/auto_and_typeof.d | 97 - ddili/src/ders/d.cn/bit_operations.d | 1093 ------- ddili/src/ders/d.cn/breadcrumbs.ddoc | 4 - ddili/src/ders/d.cn/characters.d | 594 ---- ddili/src/ders/d.cn/class.d | 449 --- ddili/src/ders/d.cn/compiler.d | 101 - ddili/src/ders/d.cn/concurrency.d | 1343 -------- ddili/src/ders/d.cn/concurrency_shared.d | 748 ----- ddili/src/ders/d.cn/cond_comp.d | 733 ----- ddili/src/ders/d.cn/const_and_immutable.d | 765 ----- ddili/src/ders/d.cn/const_member_functions.d | 289 -- ddili/src/ders/d.cn/copyright.d | 110 - ddili/src/ders/d.cn/cover_CreateSpace.pdf | Bin 793034 -> 0 bytes .../ders/d.cn/cover_IngramSpark_hardcover.pdf | Bin 1285983 -> 0 bytes .../ders/d.cn/cover_IngramSpark_paperback.pdf | Bin 1014369 -> 0 bytes ddili/src/ders/d.cn/cover_ebook.png | Bin 533465 -> 0 bytes ddili/src/ders/d.cn/cover_thumb.png | Bin 20321 -> 0 bytes ddili/src/ders/d.cn/cozum_ozel.ddoc | 6 - ddili/src/ders/d.cn/derse_ozel.ddoc | 47 - ddili/src/ders/d.cn/do_while.cozum.d | 22 - ddili/src/ders/d.cn/do_while.d | 91 - ddili/src/ders/d.cn/exceptions.d | 994 ------ ddili/src/ders/d.cn/fibers.d | 1105 ------- ddili/src/ders/d.cn/files.cozum.d | 34 - ddili/src/ders/d.cn/files.d | 227 -- ddili/src/ders/d.cn/floating_point.d | 517 --- ddili/src/ders/d.cn/foreach_opapply.d | 667 ---- ddili/src/ders/d.cn/formatted_output.cozum.d | 56 - ddili/src/ders/d.cn/formatted_output.d | 637 ---- ddili/src/ders/d.cn/if.cozum.d | 110 - ddili/src/ders/d.cn/if.d | 310 -- ddili/src/ders/d.cn/index.d | 143 - ddili/src/ders/d.cn/input.cozum.d | 14 - ddili/src/ders/d.cn/input.d | 237 -- ddili/src/ders/d.cn/interface.d | 657 ---- ddili/src/ders/d.cn/is_expr.d | 553 ---- ddili/src/ders/d.cn/lazy_operators.d | 92 - ddili/src/ders/d.cn/lifetimes.d | 341 -- ddili/src/ders/d.cn/lvalue_rvalue.d | 203 -- ddili/src/ders/d.cn/mixin.d | 486 --- ddili/src/ders/d.cn/name_space.d | 143 - ddili/src/ders/d.cn/null_is.d | 285 -- ddili/src/ders/d.cn/object.cozum.d | 108 - ddili/src/ders/d.cn/object.d | 861 ----- .../ders/d.cn/operator_overloading.cozum.d | 297 -- ddili/src/ders/d.cn/parallelism.d | 1219 -------- ddili/src/ders/d.cn/parameter_flexibility.d | 520 --- ddili/src/ders/d.cn/pragma.d | 235 -- ddili/src/ders/d.cn/preface.d | 70 - ddili/src/ders/d.cn/property.d | 351 --- ddili/src/ders/d.cn/ranges.d | 2013 ------------ ddili/src/ders/d.cn/scope.d | 142 - ddili/src/ders/d.cn/strings.cozum.d | 63 - ddili/src/ders/d.cn/strings.d | 511 --- ddili/src/ders/d.cn/templates.d | 1099 ------- ddili/src/ders/d.cn/ternary.cozum.d | 33 - ddili/src/ders/d.cn/ternary.d | 251 -- ddili/src/ders/d.cn/to_be_continued.d | 17 - ddili/src/ders/d.cn/tuples.d | 570 ---- ddili/src/ders/d.cn/uda.d | 421 --- ddili/src/ders/d.cn/ufcs.d | 220 -- ddili/src/ders/d.de/Makefile.in | 19 - ddili/src/ders/d.de/breadcrumbs.ddoc | 4 - ddili/src/ders/d.de/code/README | 22 - ddili/src/ders/d.de/copyright.d | 110 - ddili/src/ders/d.de/derse_ozel.ddoc | 47 - ddili/src/ders/d.de/foreword2.d | 40 - ddili/src/ders/d.de/frontispiece.d | 9 - ddili/src/ders/d.de/halftitle.html | 11 - ddili/src/ders/d.de/hello_world.cozum.d | 52 - ddili/src/ders/d.de/hello_world.d | 222 -- ddili/src/ders/d.de/index.d | 148 - ddili/src/ders/d.de/index_section_head.html | 3 - ddili/src/ders/d.de/index_section_tail.html | 2 - ddili/src/ders/d.de/ix.d | 14 - ddili/src/ders/d.de/pdf.derse_ozel.css | 31 - ddili/src/ders/d.de/pdf_cozum_head.html | 7 - ddili/src/ders/d.de/pdf_cozum_tail.html | 2 - ddili/src/ders/d.de/pdf_html_head.html | 15 - ddili/src/ders/d.de/pdf_html_tail.html | 2 - ddili/src/ders/d.de/preface.d | 70 - ddili/src/ders/d.de/rss.xml | 812 ----- ddili/src/ders/d.de/title.html | 27 - ddili/src/ders/d.de/to_be_continued.d | 17 - ddili/src/ders/d.de/toc_head.html | 1 - ddili/src/ders/d.de/toc_tail.html | 1 - ddili/src/ders/d.en/arithmetic.cozum.d | 160 - ddili/src/ders/d.en/arrays.cozum.d | 133 - ddili/src/ders/d.en/arrays.d | 552 ---- ddili/src/ders/d.en/assert.cozum.d | 135 - ddili/src/ders/d.en/assignment.cozum.d | 25 - ddili/src/ders/d.en/assignment.d | 82 - ddili/src/ders/d.en/auto_and_typeof.cozum.d | 30 - ddili/src/ders/d.en/bit_operations.cozum.d | 84 - ddili/src/ders/d.en/blurbs.d | 42 - ddili/src/ders/d.en/cast.d | 715 ----- ddili/src/ders/d.en/code/README | 22 - ddili/src/ders/d.en/contracts.cozum.d | 77 - ddili/src/ders/d.en/contracts.d | 392 --- ddili/src/ders/d.en/cover_CreateSpace.pdf | Bin 793034 -> 0 bytes .../ders/d.en/cover_IngramSpark_hardcover.pdf | Bin 1285983 -> 0 bytes .../ders/d.en/cover_IngramSpark_paperback.pdf | Bin 1014369 -> 0 bytes ddili/src/ders/d.en/cover_ebook.png | Bin 533465 -> 0 bytes ddili/src/ders/d.en/cover_thumb.png | Bin 20321 -> 0 bytes ddili/src/ders/d.en/destroy.d | 468 --- ddili/src/ders/d.en/encapsulation.d | 508 --- ddili/src/ders/d.en/enum.cozum.d | 94 - ddili/src/ders/d.en/enum.d | 330 -- ddili/src/ders/d.en/floating_point.cozum.d | 110 - ddili/src/ders/d.en/for.cozum.d | 86 - ddili/src/ders/d.en/for.d | 257 -- ddili/src/ders/d.en/foreach.cozum.d | 34 - ddili/src/ders/d.en/foreach.d | 432 --- ddili/src/ders/d.en/foreach_opapply.cozum.d | 203 -- ddili/src/ders/d.en/foreword1.d | 16 - ddili/src/ders/d.en/foreword2.d | 40 - ddili/src/ders/d.en/formatted_input.cozum.d | 28 - ddili/src/ders/d.en/formatted_input.d | 160 - ddili/src/ders/d.en/frontispiece.d | 9 - .../ders/d.en/function_overloading.cozum.d | 147 - ddili/src/ders/d.en/function_overloading.d | 294 -- .../src/ders/d.en/function_parameters.cozum.d | 38 - ddili/src/ders/d.en/function_parameters.d | 994 ------ ddili/src/ders/d.en/functions.cozum.d | 57 - ddili/src/ders/d.en/functions.d | 744 ----- ddili/src/ders/d.en/functions_more.d | 943 ------ ddili/src/ders/d.en/goto.d | 263 -- ddili/src/ders/d.en/halftitle.html | 11 - ddili/src/ders/d.en/hello_world.cozum.d | 52 - ddili/src/ders/d.en/hello_world.d | 222 -- ddili/src/ders/d.en/index_section_head.html | 3 - ddili/src/ders/d.en/index_section_tail.html | 2 - ddili/src/ders/d.en/inheritance.cozum.d | 203 -- ddili/src/ders/d.en/inheritance.d | 1049 ------- ddili/src/ders/d.en/invariant.d | 467 --- ddili/src/ders/d.en/io.cozum.d | 21 - ddili/src/ders/d.en/io.d | 58 - ddili/src/ders/d.en/ix.d | 14 - ddili/src/ders/d.en/lambda.d | 1196 ------- ddili/src/ders/d.en/literals.cozum.d | 76 - ddili/src/ders/d.en/literals.d | 384 --- .../src/ders/d.en/logical_expressions.cozum.d | 54 - ddili/src/ders/d.en/logical_expressions.d | 431 --- ddili/src/ders/d.en/main.cozum.d | 76 - ddili/src/ders/d.en/main.d | 500 --- ddili/src/ders/d.en/member_functions.cozum.d | 170 - ddili/src/ders/d.en/member_functions.d | 418 --- ddili/src/ders/d.en/memory.d | 1336 -------- ddili/src/ders/d.en/modules.d | 622 ---- ddili/src/ders/d.en/nested.d | 282 -- ddili/src/ders/d.en/operator_overloading.d | 1844 ----------- ddili/src/ders/d.en/operator_precedence.d | 318 -- .../ders/d.en/parameter_flexibility.cozum.d | 105 - ddili/src/ders/d.en/pdf.derse_ozel.css | 31 - ddili/src/ders/d.en/pdf_cozum_head.html | 7 - ddili/src/ders/d.en/pdf_cozum_tail.html | 2 - ddili/src/ders/d.en/pdf_html_head.html | 15 - ddili/src/ders/d.en/pdf_html_tail.html | 2 - ddili/src/ders/d.en/pdf_sozluk_head.html | 3 - ddili/src/ders/d.en/pointers.cozum.d | 209 -- ddili/src/ders/d.en/pointers.d | 1653 ---------- ddili/src/ders/d.en/ranges_more.d | 428 --- ddili/src/ders/d.en/rss.xml | 812 ----- ddili/src/ders/d.en/slices.cozum.d | 40 - ddili/src/ders/d.en/slices.d | 838 ----- ddili/src/ders/d.en/special_functions.d | 1234 -------- ddili/src/ders/d.en/stream_redirect.cozum.d | 26 - ddili/src/ders/d.en/stream_redirect.d | 136 - ddili/src/ders/d.en/struct.cozum.d | 220 -- ddili/src/ders/d.en/struct.d | 932 ------ ddili/src/ders/d.en/switch_case.cozum.d | 103 - ddili/src/ders/d.en/switch_case.d | 375 --- ddili/src/ders/d.en/templates_more.d | 2107 ------------- ddili/src/ders/d.en/title.html | 27 - ddili/src/ders/d.en/toc_head.html | 1 - ddili/src/ders/d.en/toc_tail.html | 1 - ddili/src/ders/d.en/types.cozum.d | 36 - ddili/src/ders/d.en/types.d | 295 -- ddili/src/ders/d.en/union.d | 412 --- ddili/src/ders/d.en/unit_testing.cozum.d | 190 -- ddili/src/ders/d.en/unit_testing.d | 530 ---- ddili/src/ders/d.en/value_vs_reference.d | 698 ----- ddili/src/ders/d.en/variables.cozum.d | 22 - ddili/src/ders/d.en/variables.d | 107 - ddili/src/ders/d.en/while.cozum.d | 50 - ddili/src/ders/d.en/while.d | 230 -- ddili/src/ders/d.en/writeln.cozum.d | 32 - ddili/src/ders/d.en/writeln.d | 83 - ddili/src/ders/d/Makefile.in | 150 - ddili/src/ders/d/Tennessee_Valley.jpg | Bin 84853 -> 0 bytes ddili/src/ders/d/alias.d | 462 --- ddili/src/ders/d/alias_this.d | 175 -- ddili/src/ders/d/araliklar.d | 2016 ------------ ddili/src/ders/d/araliklar_baska.d | 446 --- ddili/src/ders/d/aritmetik_islemler.cozum.d | 159 - ddili/src/ders/d/aritmetik_islemler.d | 874 ------ ddili/src/ders/d/assert.cozum.d | 211 -- ddili/src/ders/d/assert.d | 496 --- ddili/src/ders/d/atama_ve_sira.cozum.d | 25 - ddili/src/ders/d/atama_ve_sira.d | 80 - ddili/src/ders/d/auto.cozum.d | 31 - ddili/src/ders/d/auto.d | 114 - ddili/src/ders/d/bellek_yonetimi.d | 1360 -------- ddili/src/ders/d/birim_testler.cozum.d | 190 -- ddili/src/ders/d/birim_testler.d | 519 --- ddili/src/ders/d/birlikler.d | 414 --- ddili/src/ders/d/bit_islemleri.cozum.d | 86 - ddili/src/ders/d/bit_islemleri.d | 1096 ------- ddili/src/ders/d/blurbs.d | 29 - ddili/src/ders/d/breadcrumbs.ddoc | 4 - ddili/src/ders/d/cikti_duzeni.cozum.d | 55 - ddili/src/ders/d/cikti_duzeni.d | 639 ---- ddili/src/ders/d/clear.d | 483 --- ddili/src/ders/d/code/BENIOKU | 15 - ddili/src/ders/d/cokuzlular.d | 568 ---- ddili/src/ders/d/const_uye_islevler.d | 302 -- ddili/src/ders/d/const_ve_immutable.d | 783 ----- ddili/src/ders/d/copyright.d | 91 - ddili/src/ders/d/cover.pdf | Bin 797218 -> 0 bytes ddili/src/ders/d/cover_ebook.png | Bin 467798 -> 0 bytes ddili/src/ders/d/cover_thumb.png | Bin 21238 -> 0 bytes ddili/src/ders/d/cozum_ozel.ddoc | 6 - ddili/src/ders/d/deger_referans.d | 708 ----- ddili/src/ders/d/deger_sol_sag.d | 216 -- ddili/src/ders/d/degiskenler.cozum.d | 22 - ddili/src/ders/d/degiskenler.d | 112 - ddili/src/ders/d/derleyici.d | 112 - ddili/src/ders/d/derse_ozel.ddoc | 12 - ddili/src/ders/d/devami_gelecek.d | 24 - ddili/src/ders/d/dilimler.cozum.d | 40 - ddili/src/ders/d/dilimler.d | 853 ----- ddili/src/ders/d/dizgiler.cozum.d | 62 - ddili/src/ders/d/dizgiler.d | 558 ---- ddili/src/ders/d/diziler.cozum.d | 132 - ddili/src/ders/d/diziler.d | 571 ---- ddili/src/ders/d/do_while.cozum.d | 23 - ddili/src/ders/d/do_while.d | 94 - ddili/src/ders/d/dosyalar.cozum.d | 35 - ddili/src/ders/d/dosyalar.d | 240 -- ddili/src/ders/d/enum.cozum.d | 97 - ddili/src/ders/d/enum.d | 335 -- ddili/src/ders/d/es_zamanli.d | 1338 -------- ddili/src/ders/d/es_zamanli_shared.d | 764 ----- ddili/src/ders/d/esleme_tablolari.cozum.d | 120 - ddili/src/ders/d/esleme_tablolari.d | 330 -- ddili/src/ders/d/etiketler.d | 268 -- ddili/src/ders/d/fiberler.d | 1135 ------- ddili/src/ders/d/for_dongusu.cozum.d | 85 - ddili/src/ders/d/for_dongusu.d | 267 -- ddili/src/ders/d/foreach_dongusu.cozum.d | 31 - ddili/src/ders/d/foreach_dongusu.d | 462 --- ddili/src/ders/d/foreach_opapply.cozum.d | 206 -- ddili/src/ders/d/foreach_opapply.d | 686 ---- ddili/src/ders/d/frontispiece.d | 9 - ddili/src/ders/d/giris_cikis.cozum.d | 22 - ddili/src/ders/d/giris_cikis.d | 56 - ddili/src/ders/d/giris_duzeni.cozum.d | 29 - ddili/src/ders/d/giris_duzeni.d | 165 - ddili/src/ders/d/gostergeler.cozum.d | 202 -- ddili/src/ders/d/gostergeler.d | 1647 ---------- ddili/src/ders/d/halftitle.html | 11 - ddili/src/ders/d/hatalar.d | 1014 ------ ddili/src/ders/d/hazir_degerler.cozum.d | 76 - ddili/src/ders/d/hazir_degerler.d | 379 --- ddili/src/ders/d/ic_tanimlar.d | 287 -- ddili/src/ders/d/if_kosulu.cozum.d | 110 - ddili/src/ders/d/if_kosulu.d | 310 -- ddili/src/ders/d/index.d | 139 - ddili/src/ders/d/index_section_head.html | 3 - ddili/src/ders/d/index_section_tail.html | 2 - ddili/src/ders/d/interface.d | 658 ---- ddili/src/ders/d/invariant.d | 465 --- ddili/src/ders/d/is_ifadesi.d | 561 ---- ddili/src/ders/d/isim_alani.d | 149 - ddili/src/ders/d/islec_oncelikleri.d | 325 -- ddili/src/ders/d/islec_yukleme.cozum.d | 290 -- ddili/src/ders/d/islec_yukleme.d | 1850 ----------- ddili/src/ders/d/islev_parametreleri.cozum.d | 39 - ddili/src/ders/d/islev_parametreleri.d | 1021 ------ ddili/src/ders/d/islev_yukleme.cozum.d | 146 - ddili/src/ders/d/islev_yukleme.d | 312 -- ddili/src/ders/d/islevler.cozum.d | 56 - ddili/src/ders/d/islevler.d | 748 ----- ddili/src/ders/d/islevler_diger.d | 984 ------ ddili/src/ders/d/ix.d | 14 - ddili/src/ders/d/kapamalar.d | 1219 -------- ddili/src/ders/d/karakterler.d | 598 ---- ddili/src/ders/d/katmalar.d | 506 --- ddili/src/ders/d/kesirli_sayilar.cozum.d | 109 - ddili/src/ders/d/kesirli_sayilar.d | 514 --- ddili/src/ders/d/kosullu_derleme.d | 741 ----- ddili/src/ders/d/kosut_islemler.d | 1235 -------- ddili/src/ders/d/main.cozum.d | 76 - ddili/src/ders/d/main.d | 525 ---- ddili/src/ders/d/mantiksal_ifadeler.cozum.d | 53 - ddili/src/ders/d/mantiksal_ifadeler.d | 414 --- ddili/src/ders/d/merhaba_dunya.cozum.d | 52 - ddili/src/ders/d/merhaba_dunya.d | 244 -- ddili/src/ders/d/moduller.d | 630 ---- ddili/src/ders/d/nitelikler.d | 354 --- ddili/src/ders/d/null_ve_is.d | 293 -- ddili/src/ders/d/object.cozum.d | 109 - ddili/src/ders/d/object.d | 891 ------ ddili/src/ders/d/onsoz1.d | 15 - ddili/src/ders/d/onsoz2.d | 46 - ddili/src/ders/d/onsoz_yazar.d | 77 - ddili/src/ders/d/ozel_islevler.d | 1239 -------- .../src/ders/d/parametre_serbestligi.cozum.d | 106 - ddili/src/ders/d/parametre_serbestligi.d | 542 ---- ddili/src/ders/d/pdf.derse_ozel.css | 35 - ddili/src/ders/d/pdf_cozum_head.html | 7 - ddili/src/ders/d/pdf_cozum_tail.html | 2 - ddili/src/ders/d/pdf_html_head.html | 15 - ddili/src/ders/d/pdf_html_tail.html | 2 - ddili/src/ders/d/pdf_sozluk_head.html | 6 - ddili/src/ders/d/pdf_sozluk_tail.html | 1 - ddili/src/ders/d/pragma.d | 242 -- ddili/src/ders/d/rss.xml | 1156 ------- ddili/src/ders/d/sablonlar.d | 1105 ------- ddili/src/ders/d/sablonlar_ayrintili.d | 2156 ------------- ddili/src/ders/d/sarma.d | 525 ---- ddili/src/ders/d/scope.d | 145 - ddili/src/ders/d/siniflar.d | 468 --- ddili/src/ders/d/sozlesmeli.cozum.d | 81 - ddili/src/ders/d/sozlesmeli.d | 406 --- .../src/ders/d/standart_akim_baglamak.cozum.d | 27 - ddili/src/ders/d/standart_akim_baglamak.d | 146 - ddili/src/ders/d/standart_giris.cozum.d | 15 - ddili/src/ders/d/standart_giris.d | 231 -- ddili/src/ders/d/switch_case.cozum.d | 101 - ddili/src/ders/d/switch_case.d | 376 --- ddili/src/ders/d/tembel_degerlendirmeler.d | 103 - ddili/src/ders/d/temel_turler.cozum.d | 34 - ddili/src/ders/d/temel_turler.d | 308 -- ddili/src/ders/d/title.html | 27 - ddili/src/ders/d/toc_head.html | 1 - ddili/src/ders/d/toc_tail.html | 1 - ddili/src/ders/d/tur_donusumleri.d | 740 ----- ddili/src/ders/d/tureme.cozum.d | 197 -- ddili/src/ders/d/tureme.d | 1097 ------- ddili/src/ders/d/uclu_islec.cozum.d | 33 - ddili/src/ders/d/uclu_islec.d | 251 -- ddili/src/ders/d/uda.d | 422 --- ddili/src/ders/d/ufcs.d | 227 -- ddili/src/ders/d/uye_islevler.cozum.d | 170 - ddili/src/ders/d/uye_islevler.d | 435 --- ddili/src/ders/d/while_dongusu.cozum.d | 51 - ddili/src/ders/d/while_dongusu.d | 229 -- ddili/src/ders/d/writeln.cozum.d | 31 - ddili/src/ders/d/writeln.d | 80 - ddili/src/ders/d/yapilar.cozum.d | 222 -- ddili/src/ders/d/yapilar.d | 944 ------ ddili/src/ders/d/yasam_surecleri.d | 369 --- ddili/src/ders/gtkd/Makefile.in | 16 - ddili/src/ders/gtkd/breadcrumbs.ddoc | 4 - ddili/src/ders/gtkd/code/BENIOKU | 1 - ddili/src/ders/gtkd/copyright.d | 20 - ddili/src/ders/gtkd/derse_ozel.ddoc | 12 - ddili/src/ders/gtkd/devami_gelecek.d | 24 - ddili/src/ders/gtkd/frontispiece.d | 1 - ddili/src/ders/gtkd/halftitle.html | 11 - ddili/src/ders/gtkd/index.d | 25 - ddili/src/ders/gtkd/index_section_head.html | 3 - ddili/src/ders/gtkd/index_section_tail.html | 2 - ddili/src/ders/gtkd/ix.d | 14 - ddili/src/ders/gtkd/merhaba_gtkd.d | 117 - ddili/src/ders/gtkd/pdf.derse_ozel.css | 12 - ddili/src/ders/gtkd/pdf_cozum_head.html | 7 - ddili/src/ders/gtkd/pdf_cozum_tail.html | 2 - ddili/src/ders/gtkd/pdf_html_head.html | 15 - ddili/src/ders/gtkd/pdf_html_tail.html | 2 - ddili/src/ders/gtkd/pdf_indir.d | 20 - ddili/src/ders/gtkd/pdf_sozluk_head.html | 3 - ddili/src/ders/gtkd/pencere_kurma_islemleri.d | 252 -- ddili/src/ders/gtkd/rss.xml | 49 - ddili/src/ders/gtkd/tanitim.d | 42 - ddili/src/ders/gtkd/title.html | 15 - ddili/src/ders/gtkd/toc_head.html | 1 - ddili/src/ders/gtkd/toc_tail.html | 1 - ddili/src/ders/index.d | 19 - ddili/src/ders/sdl/Makefile.in | 15 - ddili/src/ders/sdl/breadcrumbs.ddoc | 4 - ddili/src/ders/sdl/code/BENIOKU | 1 - ddili/src/ders/sdl/copyright.d | 20 - ddili/src/ders/sdl/derse_ozel.ddoc | 23 - ddili/src/ders/sdl/devami_gelecek.d | 24 - ddili/src/ders/sdl/frontispiece.d | 1 - ddili/src/ders/sdl/halftitle.html | 11 - ddili/src/ders/sdl/hareket.d | 286 -- ddili/src/ders/sdl/hosgeldiniz.d | 587 ---- ddili/src/ders/sdl/index.d | 24 - ddili/src/ders/sdl/index_section_head.html | 3 - ddili/src/ders/sdl/index_section_tail.html | 2 - ddili/src/ders/sdl/ix.d | 14 - ddili/src/ders/sdl/pdf.derse_ozel.css | 12 - ddili/src/ders/sdl/pdf_cozum_head.html | 7 - ddili/src/ders/sdl/pdf_cozum_tail.html | 2 - ddili/src/ders/sdl/pdf_html_head.html | 15 - ddili/src/ders/sdl/pdf_html_tail.html | 2 - ddili/src/ders/sdl/pdf_indir.d | 20 - ddili/src/ders/sdl/pdf_sozluk_head.html | 3 - ddili/src/ders/sdl/rss.xml | 44 - ddili/src/ders/sdl/src/ders1.zip | Bin 1732 -> 0 bytes ddili/src/ders/sdl/title.html | 15 - ddili/src/ders/sdl/toc_head.html | 2 - ddili/src/ders/sdl/toc_tail.html | 1 - ddili/src/ders/template/Makefile.in | 19 - ddili/src/ders/template/README | 25 - ddili/src/ders/template/breadcrumbs.ddoc | 4 - ddili/src/ders/template/code/README | 22 - ddili/src/ders/template/copyright.d | 110 - ddili/src/ders/template/derse_ozel.ddoc | 47 - ddili/src/ders/template/foreword2.d | 40 - ddili/src/ders/template/frontispiece.d | 9 - ddili/src/ders/template/halftitle.html | 11 - ddili/src/ders/template/hello_world.cozum.d | 52 - ddili/src/ders/template/hello_world.d | 222 -- ddili/src/ders/template/index.d | 148 - .../src/ders/template/index_section_head.html | 3 - .../src/ders/template/index_section_tail.html | 2 - ddili/src/ders/template/ix.d | 14 - ddili/src/ders/template/pdf.derse_ozel.css | 31 - ddili/src/ders/template/pdf_cozum_head.html | 7 - ddili/src/ders/template/pdf_cozum_tail.html | 2 - ddili/src/ders/template/pdf_html_head.html | 15 - ddili/src/ders/template/pdf_html_tail.html | 2 - ddili/src/ders/template/preface.d | 70 - ddili/src/ders/template/rss.xml | 812 ----- ddili/src/ders/template/title.html | 27 - ddili/src/ders/template/to_be_continued.d | 17 - ddili/src/ders/template/toc_head.html | 1 - ddili/src/ders/template/toc_tail.html | 1 - ddili/src/dusey_navigasyon.ddoc | 39 - ddili/src/ebook_font_manifest | 14 - ddili/src/ebook_override.css | 142 - ddili/src/ebooksanitizer.d | 35 - ddili/src/fonts.css | 83 - ddili/src/fonts/andada/Andada-Bold.ttf | Bin 132144 -> 0 bytes ddili/src/fonts/andada/Andada-BoldItalic.ttf | Bin 76388 -> 0 bytes ddili/src/fonts/andada/Andada-Italic.ttf | Bin 76476 -> 0 bytes ddili/src/fonts/andada/Andada-Regular.ttf | Bin 76228 -> 0 bytes ddili/src/fonts/andada/OFL.txt | 93 - .../dejavu/DejaVuSansMono-Bold-webfont.ttf | Bin 301304 -> 0 bytes .../DejaVuSansMono-BoldOblique-webfont.ttf | Bin 222768 -> 0 bytes .../dejavu/DejaVuSansMono-Oblique-webfont.ttf | Bin 228648 -> 0 bytes .../fonts/dejavu/DejaVuSansMono-webfont.ttf | Bin 320836 -> 0 bytes ddili/src/fonts/opensans/OpenSans-Bold.ttf | Bin 224592 -> 0 bytes .../fonts/opensans/OpenSans-BoldItalic.ttf | Bin 213292 -> 0 bytes ddili/src/fonts/opensans/OpenSans-Italic.ttf | Bin 212896 -> 0 bytes ddili/src/fonts/opensans/OpenSans-Regular.ttf | Bin 217360 -> 0 bytes ddili/src/iletisim.d | 29 - ddili/src/image/Androgynous_D_coders.png | Bin 46728648 -> 0 bytes .../src/image/Androgynous_D_coders_5.5x7.png | Bin 1290368 -> 0 bytes ddili/src/image/book.png | Bin 680 -> 0 bytes ddili/src/image/bullet_black.png | Bin 326 -> 0 bytes ddili/src/image/by-nc-sa.png | Bin 9887 -> 0 bytes ddili/src/image/cc_80x15.png | Bin 697 -> 0 bytes ddili/src/image/cc_88x31.png | Bin 5460 -> 0 bytes ddili/src/image/d_harfi.jpg | Bin 16516 -> 0 bytes ddili/src/image/d_harfi_renamed.jpg | Bin 22927 -> 0 bytes ddili/src/image/d_source.png | Bin 560 -> 0 bytes ddili/src/image/email.png | Bin 701 -> 0 bytes ddili/src/image/favicon.png | Bin 1166 -> 0 bytes ddili/src/image/forum.png | Bin 863 -> 0 bytes ddili/src/image/gulen.png | Bin 457 -> 0 bytes ddili/src/image/oyundongusu.jpg | Bin 17804 -> 0 bytes ddili/src/image/oyunlardahareket.png | Bin 1007670 -> 0 bytes ddili/src/image/pdficon_small.gif | Bin 361 -> 0 bytes ddili/src/image/pencil.png | Bin 554 -> 0 bytes ddili/src/image/penguen.png | Bin 15681 -> 0 bytes ddili/src/image/rss-icon.png | Bin 764 -> 0 bytes ddili/src/image/sdlkoordinat.png | Bin 33890 -> 0 bytes ddili/src/image/vektorler1.jpg | Bin 42790 -> 0 bytes ddili/src/image/vektorler1.png | Bin 53977 -> 0 bytes ddili/src/image/vektorler2.png | Bin 48134 -> 0 bytes ddili/src/image/vektorler3.png | Bin 18187 -> 0 bytes ddili/src/image/vektorler4.png | Bin 15401 -> 0 bytes ddili/src/index.d | 54 - ddili/src/kurulum/breadcrumbs.ddoc | 4 - ddili/src/kurulum/dmd.d | 102 - ddili/src/kurulum/emacs_d-mode.d | 86 - ddili/src/kurulum/gdc.d | 107 - ddili/src/kurulum/index.d | 69 - ddili/src/kurulum/locale.properties | 346 -- ddili/src/makale/bellek.d | 463 --- ddili/src/makale/breadcrumbs.ddoc | 4 - ddili/src/makale/d_dilimleri.d | 349 --- ddili/src/makale/d_tipleri.d | 244 -- ddili/src/makale/degismez.d | 481 --- ddili/src/makale/dub_tanisma.d | 135 - ddili/src/makale/duzenli_ifadeler.d | 194 -- ddili/src/makale/eleman_erisimi_uzerine.d | 885 ------ ddili/src/makale/index.d | 141 - ddili/src/makale/katma.d | 100 - ddili/src/makale/neden_d.d | 522 ---- ddili/src/makale/saflik.d | 390 --- ddili/src/makale/shared.d | 229 -- ddili/src/makale/tembel_hesap.d | 295 -- ddili/src/make_ders_macros.sh | 48 - ddili/src/ornek_kod/breadcrumbs.ddoc | 4 - ddili/src/ornek_kod/index.d | 210 -- ddili/src/pdf.css | 1220 -------- ddili/src/pdf.ddoc | 8 - ddili/src/pdf_fonts.css | 85 - ddili/src/regular.ddoc | 15 - ddili/src/reveal_ix.css | 34 - ddili/src/robots.txt | 2 - ddili/src/rss.xml | 316 -- ddili/src/sozluk.d | 36 - ddili/src/sozluk.txt | 451 --- ddili/src/sozlukmaker.d | 135 - ddili/src/style.css | 534 ---- ddili/src/sunum/2012_AliCehreli_Cpp11_D.pdf | Bin 138933 -> 0 bytes ddili/src/sunum/2012_AliCehreli_D_Tanitim.pdf | Bin 155439 -> 0 bytes .../sunum/2012_AliCehreli_Kosut_Islemler.pdf | Bin 78296 -> 0 bytes .../2012_SalihDincer_Diziler_Dilimler.pdf | Bin 316519 -> 0 bytes ...2_ZaferCelenk_D_ile_Guvenli_Programlar.pdf | Bin 390579 -> 0 bytes ddili/src/sunum/breadcrumbs.ddoc | 4 - ddili/src/sunum/index.d | 66 - ddili/src/sunum/ingilizce_sunumlar.d | 68 - ddili/src/sunum/merhaba_2012.d | 120 - ddili/src/sunum/sonrasi.d | 30 - ddili/src/tanitim/breadcrumbs.ddoc | 4 - ddili/src/tanitim/dil_kutuphane.d | 155 - ddili/src/tanitim/fark_c.d | 1403 --------- ddili/src/tanitim/fark_cpp.d | 777 ----- ddili/src/tanitim/fark_dizgi.d | 535 ---- ddili/src/tanitim/fark_karmasik.d | 227 -- ddili/src/tanitim/fark_onislemci.d | 686 ---- ddili/src/tanitim/genel.d | 417 --- ddili/src/tanitim/index.d | 44 - ddili/src/todo | 23 - {ddili/src/ders/d.en => source}/Makefile.in | 0 {ddili/src/ders/d.en => source}/aa.cozum.d | 0 {ddili/src/ders/d.en => source}/aa.d | 4 +- {ddili/src/ders/d.en => source}/alias.d | 0 {ddili/src/ders/d.en => source}/alias_this.d | 6 +- .../ders/d.cn => source}/arithmetic.cozum.d | 0 {ddili/src/ders/d.en => source}/arithmetic.d | 6 +- .../src/ders/d.cn => source}/arrays.cozum.d | 0 {ddili/src/ders/d.cn => source}/arrays.d | 50 + .../src/ders/d.cn => source}/assert.cozum.d | 0 {ddili/src/ders/d.en => source}/assert.d | 10 +- .../ders/d.cn => source}/assignment.cozum.d | 0 {ddili/src/ders/d.cn => source}/assignment.d | 0 .../d.cn => source}/auto_and_typeof.cozum.d | 0 .../ders/d.en => source}/auto_and_typeof.d | 0 .../d.cn => source}/bit_operations.cozum.d | 0 .../src/ders/d.en => source}/bit_operations.d | 118 +- {ddili/src/ders/d.cn => source}/blurbs.d | 0 .../src/ders/d.en => source}/breadcrumbs.ddoc | 0 {ddili/src/ders/d.cn => source}/cast.d | 0 {ddili/src/ders/d.en => source}/characters.d | 2 +- {ddili/src/ders/d.en => source}/class.d | 10 +- {ddili/src/ders/d.cn => source}/code/README | 0 {ddili/src/ders/d.en => source}/compiler.d | 0 {ddili/src/ders/d.en => source}/concurrency.d | 0 .../ders/d.en => source}/concurrency_shared.d | 12 +- {ddili/src/ders/d.en => source}/cond_comp.d | 63 - .../d.en => source}/const_and_immutable.d | 19 +- .../d.en => source}/const_member_functions.d | 0 .../ders/d.cn => source}/contracts.cozum.d | 0 {ddili/src/ders/d.cn => source}/contracts.d | 2 +- {ddili/src/ders/d.en => source}/copyright.d | 2 +- .../src/ders/d.en => source}/cozum_ozel.ddoc | 0 .../src/ders/d.en => source}/derse_ozel.ddoc | 0 {ddili/src/ders/d.cn => source}/destroy.d | 2 +- .../src/ders/d.en => source}/do_while.cozum.d | 0 {ddili/src/ders/d.en => source}/do_while.d | 0 .../src/ders/d.cn => source}/encapsulation.d | 0 {ddili/src/ders/d.cn => source}/enum.cozum.d | 0 {ddili/src/ders/d.cn => source}/enum.d | 4 + {ddili/src/ders/d.en => source}/exceptions.d | 4 +- {ddili/src/ders/d.en => source}/fibers.d | 14 +- {ddili/src/ders/d.en => source}/files.cozum.d | 0 {ddili/src/ders/d.en => source}/files.d | 0 .../d.cn => source}/floating_point.cozum.d | 0 .../src/ders/d.en => source}/floating_point.d | 33 +- {ddili/src/ders/d.cn => source}/for.cozum.d | 0 {ddili/src/ders/d.cn => source}/for.d | 0 .../src/ders/d.cn => source}/foreach.cozum.d | 0 {ddili/src/ders/d.cn => source}/foreach.d | 11 +- .../d.cn => source}/foreach_opapply.cozum.d | 0 .../ders/d.en => source}/foreach_opapply.d | 1 - {ddili/src/ders/d.cn => source}/foreword1.d | 0 {ddili/src/ders/d.cn => source}/foreword2.d | 0 .../d.cn => source}/formatted_input.cozum.d | 0 .../ders/d.cn => source}/formatted_input.d | 0 .../d.en => source}/formatted_output.cozum.d | 0 .../ders/d.en => source}/formatted_output.d | 24 + .../src/ders/d.cn => source}/frontispiece.d | 0 .../function_overloading.cozum.d | 0 .../d.cn => source}/function_overloading.d | 0 .../function_parameters.cozum.d | 0 .../d.cn => source}/function_parameters.d | 18 +- .../ders/d.cn => source}/functions.cozum.d | 0 {ddili/src/ders/d.cn => source}/functions.d | 4 +- .../src/ders/d.cn => source}/functions_more.d | 20 +- {ddili/src/ders/d.cn => source}/goto.d | 0 .../src/ders/d.cn => source}/halftitle.html | 0 .../ders/d.cn => source}/hello_world.cozum.d | 0 {ddili/src/ders/d.cn => source}/hello_world.d | 24 +- {ddili/src/ders/d.en => source}/if.cozum.d | 0 {ddili/src/ders/d.en => source}/if.d | 0 {ddili/src/ders/d.en => source}/index.d | 8 +- .../d.cn => source}/index_section_head.html | 0 .../d.cn => source}/index_section_tail.html | 0 .../ders/d.cn => source}/inheritance.cozum.d | 0 {ddili/src/ders/d.cn => source}/inheritance.d | 0 {ddili/src/ders/d.en => source}/input.cozum.d | 0 {ddili/src/ders/d.en => source}/input.d | 12 + {ddili/src/ders/d.en => source}/interface.d | 2 +- {ddili/src/ders/d.cn => source}/invariant.d | 0 {ddili/src/ders/d.cn => source}/io.cozum.d | 0 {ddili/src/ders/d.cn => source}/io.d | 0 {ddili/src/ders/d.en => source}/is_expr.d | 2 +- {ddili/src/ders/d.cn => source}/ix.d | 0 {ddili/src/ders/d.cn => source}/lambda.d | 10 +- .../src/ders/d.en => source}/lazy_operators.d | 0 {ddili/src/ders/d.en => source}/lifetimes.d | 8 +- .../src/ders/d.cn => source}/literals.cozum.d | 0 {ddili/src/ders/d.cn => source}/literals.d | 0 .../logical_expressions.cozum.d | 0 .../d.cn => source}/logical_expressions.d | 0 .../src/ders/d.en => source}/lvalue_rvalue.d | 18 +- {ddili/src/ders/d.cn => source}/main.cozum.d | 0 {ddili/src/ders/d.cn => source}/main.d | 2 +- .../d.cn => source}/member_functions.cozum.d | 0 .../ders/d.cn => source}/member_functions.d | 0 {ddili/src/ders/d.cn => source}/memory.d | 6 +- {ddili/src/ders/d.en => source}/mixin.d | 44 + {ddili/src/ders/d.cn => source}/modules.d | 0 {ddili/src/ders/d.en => source}/name_space.d | 0 {ddili/src/ders/d.cn => source}/nested.d | 2 +- {ddili/src/ders/d.en => source}/null_is.d | 2 +- .../src/ders/d.en => source}/object.cozum.d | 0 {ddili/src/ders/d.en => source}/object.d | 19 +- .../operator_overloading.cozum.d | 4 +- .../d.cn => source}/operator_overloading.d | 60 +- .../d.cn => source}/operator_precedence.d | 15 +- {ddili/src/ders/d.en => source}/parallelism.d | 0 .../parameter_flexibility.cozum.d | 0 .../d.en => source}/parameter_flexibility.d | 43 +- .../ders/d.cn => source}/pdf.derse_ozel.css | 0 .../ders/d.cn => source}/pdf_cozum_head.html | 0 .../ders/d.cn => source}/pdf_cozum_tail.html | 0 .../ders/d.cn => source}/pdf_html_head.html | 0 .../ders/d.cn => source}/pdf_html_tail.html | 0 .../ders/d.cn => source}/pdf_sozluk_head.html | 0 .../src/ders/d.cn => source}/pointers.cozum.d | 0 {ddili/src/ders/d.cn => source}/pointers.d | 8 +- {ddili/src/ders/d.en => source}/pragma.d | 3 +- {ddili/src/ders/d.en => source}/preface.d | 6 +- {ddili/src/ders/d.en => source}/property.d | 0 {ddili/src/ders/d.en => source}/ranges.d | 12 +- {ddili/src/ders/d.cn => source}/ranges_more.d | 0 {ddili/src/ders/d.cn => source}/rss.xml | 0 {ddili/src/ders/d.en => source}/scope.d | 0 .../src/ders/d.cn => source}/slices.cozum.d | 0 {ddili/src/ders/d.cn => source}/slices.d | 6 +- .../ders/d.cn => source}/special_functions.d | 0 .../d.cn => source}/stream_redirect.cozum.d | 0 .../ders/d.cn => source}/stream_redirect.d | 0 .../src/ders/d.en => source}/strings.cozum.d | 0 {ddili/src/ders/d.en => source}/strings.d | 6 +- .../src/ders/d.cn => source}/struct.cozum.d | 0 {ddili/src/ders/d.cn => source}/struct.d | 2 +- .../ders/d.cn => source}/switch_case.cozum.d | 0 {ddili/src/ders/d.cn => source}/switch_case.d | 0 {ddili/src/ders/d.en => source}/templates.d | 6 +- .../src/ders/d.cn => source}/templates_more.d | 16 +- .../src/ders/d.en => source}/ternary.cozum.d | 0 {ddili/src/ders/d.en => source}/ternary.d | 0 {ddili/src/ders/d.cn => source}/title.html | 0 .../ders/d.en => source}/to_be_continued.d | 0 {ddili/src/ders/d.cn => source}/toc_head.html | 0 {ddili/src/ders/d.cn => source}/toc_tail.html | 0 {ddili/src/ders/d.en => source}/tuples.d | 0 {ddili/src/ders/d.cn => source}/types.cozum.d | 0 {ddili/src/ders/d.cn => source}/types.d | 0 {ddili/src/ders/d.en => source}/uda.d | 0 {ddili/src/ders/d.en => source}/ufcs.d | 0 {ddili/src/ders/d.cn => source}/union.d | 2 +- .../ders/d.cn => source}/unit_testing.cozum.d | 0 .../src/ders/d.cn => source}/unit_testing.d | 0 .../ders/d.cn => source}/value_vs_reference.d | 5 +- .../ders/d.cn => source}/variables.cozum.d | 0 {ddili/src/ders/d.cn => source}/variables.d | 0 {ddili/src/ders/d.cn => source}/while.cozum.d | 0 {ddili/src/ders/d.cn => source}/while.d | 0 .../src/ders/d.cn => source}/writeln.cozum.d | 0 {ddili/src/ders/d.cn => source}/writeln.d | 0 761 files changed, 474 insertions(+), 140396 deletions(-) delete mode 100644 ddili/.gitignore delete mode 100644 ddili/BENIOKU delete mode 100644 ddili/README delete mode 100644 ddili/calibre_config_dir/conversion/azw3_output.py delete mode 100644 ddili/calibre_config_dir/conversion/comic_input.py delete mode 100644 ddili/calibre_config_dir/conversion/debug.py delete mode 100644 ddili/calibre_config_dir/conversion/docx_input.py delete mode 100644 ddili/calibre_config_dir/conversion/docx_output.py delete mode 100644 ddili/calibre_config_dir/conversion/epub_output.py delete mode 100644 ddili/calibre_config_dir/conversion/fb2_input.py delete mode 100644 ddili/calibre_config_dir/conversion/fb2_output.py delete mode 100644 ddili/calibre_config_dir/conversion/heuristics.py delete mode 100644 ddili/calibre_config_dir/conversion/htmlz_output.py delete mode 100644 ddili/calibre_config_dir/conversion/look_and_feel.py delete mode 100644 ddili/calibre_config_dir/conversion/lrf_output.py delete mode 100644 ddili/calibre_config_dir/conversion/metadata.py delete mode 100644 ddili/calibre_config_dir/conversion/mobi_output.py delete mode 100644 ddili/calibre_config_dir/conversion/page_setup.py delete mode 100644 ddili/calibre_config_dir/conversion/pdb_output.py delete mode 100644 ddili/calibre_config_dir/conversion/pdf_input.py delete mode 100644 ddili/calibre_config_dir/conversion/pdf_output.py delete mode 100644 ddili/calibre_config_dir/conversion/pmlz_output.py delete mode 100644 ddili/calibre_config_dir/conversion/rb_output.py delete mode 100644 ddili/calibre_config_dir/conversion/rtf_input.py delete mode 100644 ddili/calibre_config_dir/conversion/search_and_replace.py delete mode 100644 ddili/calibre_config_dir/conversion/snb_output.py delete mode 100644 ddili/calibre_config_dir/conversion/structure_detection.py delete mode 100644 ddili/calibre_config_dir/conversion/toc.py delete mode 100644 ddili/calibre_config_dir/conversion/txt_input.py delete mode 100644 ddili/calibre_config_dir/conversion/txt_output.py delete mode 100644 ddili/calibre_config_dir/conversion/txtz_output.py delete mode 100644 ddili/calibre_config_dir/dynamic.pickle delete mode 100644 ddili/calibre_config_dir/fonts/scanner_cache.json delete mode 100644 ddili/calibre_config_dir/global.py delete mode 100644 ddili/calibre_config_dir/gui.json delete mode 100644 ddili/calibre_config_dir/gui.py delete mode 100644 ddili/calibre_config_dir/history.plist delete mode 100644 ddili/calibre_config_dir/iterator.pickle delete mode 100644 ddili/calibre_config_dir/metadata_sources/ISBNDB.json delete mode 100644 ddili/calibre_config_dir/tweak_book_gui.json delete mode 100644 ddili/calibre_config_dir/tweaks.py delete mode 100644 ddili/calibre_config_dir/viewer.json delete mode 100644 ddili/calibre_config_dir/viewer.py delete mode 100644 ddili/calibre_config_dir/viewer_dictionaries.json delete mode 100644 ddili/src/404.d delete mode 100644 ddili/src/AliCehreli_resume.d delete mode 100644 ddili/src/META-INF/com.apple.ibooks.display-options.xml delete mode 100644 ddili/src/Makefile delete mode 100644 ddili/src/Makefile.ders.in delete mode 100644 ddili/src/README.ebook delete mode 100644 ddili/src/alphabet.d delete mode 100644 ddili/src/anchorgen.d delete mode 100644 ddili/src/breadcrumbs.ddoc delete mode 100644 ddili/src/chinese.ddoc delete mode 100644 ddili/src/codetester.d delete mode 100644 ddili/src/common.ddoc delete mode 100644 ddili/src/copyright.d delete mode 100644 ddili/src/cozum.ddoc delete mode 100644 ddili/src/ddili.ddoc delete mode 100644 ddili/src/ders.ddoc delete mode 100644 ddili/src/ders/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/d.cn/Makefile.in delete mode 100644 ddili/src/ders/d.cn/README delete mode 100644 ddili/src/ders/d.cn/aa.cozum.d delete mode 100644 ddili/src/ders/d.cn/aa.d delete mode 100644 ddili/src/ders/d.cn/alias.d delete mode 100644 ddili/src/ders/d.cn/alias_this.d delete mode 100644 ddili/src/ders/d.cn/arithmetic.d delete mode 100644 ddili/src/ders/d.cn/assert.d delete mode 100644 ddili/src/ders/d.cn/auto_and_typeof.d delete mode 100644 ddili/src/ders/d.cn/bit_operations.d delete mode 100644 ddili/src/ders/d.cn/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/d.cn/characters.d delete mode 100644 ddili/src/ders/d.cn/class.d delete mode 100644 ddili/src/ders/d.cn/compiler.d delete mode 100644 ddili/src/ders/d.cn/concurrency.d delete mode 100644 ddili/src/ders/d.cn/concurrency_shared.d delete mode 100644 ddili/src/ders/d.cn/cond_comp.d delete mode 100644 ddili/src/ders/d.cn/const_and_immutable.d delete mode 100644 ddili/src/ders/d.cn/const_member_functions.d delete mode 100644 ddili/src/ders/d.cn/copyright.d delete mode 100644 ddili/src/ders/d.cn/cover_CreateSpace.pdf delete mode 100644 ddili/src/ders/d.cn/cover_IngramSpark_hardcover.pdf delete mode 100644 ddili/src/ders/d.cn/cover_IngramSpark_paperback.pdf delete mode 100644 ddili/src/ders/d.cn/cover_ebook.png delete mode 100644 ddili/src/ders/d.cn/cover_thumb.png delete mode 100644 ddili/src/ders/d.cn/cozum_ozel.ddoc delete mode 100644 ddili/src/ders/d.cn/derse_ozel.ddoc delete mode 100644 ddili/src/ders/d.cn/do_while.cozum.d delete mode 100644 ddili/src/ders/d.cn/do_while.d delete mode 100644 ddili/src/ders/d.cn/exceptions.d delete mode 100644 ddili/src/ders/d.cn/fibers.d delete mode 100644 ddili/src/ders/d.cn/files.cozum.d delete mode 100644 ddili/src/ders/d.cn/files.d delete mode 100644 ddili/src/ders/d.cn/floating_point.d delete mode 100644 ddili/src/ders/d.cn/foreach_opapply.d delete mode 100644 ddili/src/ders/d.cn/formatted_output.cozum.d delete mode 100644 ddili/src/ders/d.cn/formatted_output.d delete mode 100644 ddili/src/ders/d.cn/if.cozum.d delete mode 100644 ddili/src/ders/d.cn/if.d delete mode 100644 ddili/src/ders/d.cn/index.d delete mode 100644 ddili/src/ders/d.cn/input.cozum.d delete mode 100644 ddili/src/ders/d.cn/input.d delete mode 100644 ddili/src/ders/d.cn/interface.d delete mode 100644 ddili/src/ders/d.cn/is_expr.d delete mode 100644 ddili/src/ders/d.cn/lazy_operators.d delete mode 100644 ddili/src/ders/d.cn/lifetimes.d delete mode 100644 ddili/src/ders/d.cn/lvalue_rvalue.d delete mode 100644 ddili/src/ders/d.cn/mixin.d delete mode 100644 ddili/src/ders/d.cn/name_space.d delete mode 100644 ddili/src/ders/d.cn/null_is.d delete mode 100644 ddili/src/ders/d.cn/object.cozum.d delete mode 100644 ddili/src/ders/d.cn/object.d delete mode 100644 ddili/src/ders/d.cn/operator_overloading.cozum.d delete mode 100644 ddili/src/ders/d.cn/parallelism.d delete mode 100644 ddili/src/ders/d.cn/parameter_flexibility.d delete mode 100644 ddili/src/ders/d.cn/pragma.d delete mode 100644 ddili/src/ders/d.cn/preface.d delete mode 100644 ddili/src/ders/d.cn/property.d delete mode 100644 ddili/src/ders/d.cn/ranges.d delete mode 100644 ddili/src/ders/d.cn/scope.d delete mode 100644 ddili/src/ders/d.cn/strings.cozum.d delete mode 100644 ddili/src/ders/d.cn/strings.d delete mode 100644 ddili/src/ders/d.cn/templates.d delete mode 100644 ddili/src/ders/d.cn/ternary.cozum.d delete mode 100644 ddili/src/ders/d.cn/ternary.d delete mode 100644 ddili/src/ders/d.cn/to_be_continued.d delete mode 100644 ddili/src/ders/d.cn/tuples.d delete mode 100644 ddili/src/ders/d.cn/uda.d delete mode 100644 ddili/src/ders/d.cn/ufcs.d delete mode 100644 ddili/src/ders/d.de/Makefile.in delete mode 100644 ddili/src/ders/d.de/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/d.de/code/README delete mode 100644 ddili/src/ders/d.de/copyright.d delete mode 100644 ddili/src/ders/d.de/derse_ozel.ddoc delete mode 100644 ddili/src/ders/d.de/foreword2.d delete mode 100644 ddili/src/ders/d.de/frontispiece.d delete mode 100644 ddili/src/ders/d.de/halftitle.html delete mode 100644 ddili/src/ders/d.de/hello_world.cozum.d delete mode 100644 ddili/src/ders/d.de/hello_world.d delete mode 100644 ddili/src/ders/d.de/index.d delete mode 100644 ddili/src/ders/d.de/index_section_head.html delete mode 100644 ddili/src/ders/d.de/index_section_tail.html delete mode 100644 ddili/src/ders/d.de/ix.d delete mode 100644 ddili/src/ders/d.de/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/d.de/pdf_cozum_head.html delete mode 100644 ddili/src/ders/d.de/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/d.de/pdf_html_head.html delete mode 100644 ddili/src/ders/d.de/pdf_html_tail.html delete mode 100644 ddili/src/ders/d.de/preface.d delete mode 100644 ddili/src/ders/d.de/rss.xml delete mode 100644 ddili/src/ders/d.de/title.html delete mode 100644 ddili/src/ders/d.de/to_be_continued.d delete mode 100644 ddili/src/ders/d.de/toc_head.html delete mode 100644 ddili/src/ders/d.de/toc_tail.html delete mode 100644 ddili/src/ders/d.en/arithmetic.cozum.d delete mode 100644 ddili/src/ders/d.en/arrays.cozum.d delete mode 100644 ddili/src/ders/d.en/arrays.d delete mode 100644 ddili/src/ders/d.en/assert.cozum.d delete mode 100644 ddili/src/ders/d.en/assignment.cozum.d delete mode 100644 ddili/src/ders/d.en/assignment.d delete mode 100644 ddili/src/ders/d.en/auto_and_typeof.cozum.d delete mode 100644 ddili/src/ders/d.en/bit_operations.cozum.d delete mode 100644 ddili/src/ders/d.en/blurbs.d delete mode 100644 ddili/src/ders/d.en/cast.d delete mode 100644 ddili/src/ders/d.en/code/README delete mode 100644 ddili/src/ders/d.en/contracts.cozum.d delete mode 100644 ddili/src/ders/d.en/contracts.d delete mode 100644 ddili/src/ders/d.en/cover_CreateSpace.pdf delete mode 100644 ddili/src/ders/d.en/cover_IngramSpark_hardcover.pdf delete mode 100644 ddili/src/ders/d.en/cover_IngramSpark_paperback.pdf delete mode 100644 ddili/src/ders/d.en/cover_ebook.png delete mode 100644 ddili/src/ders/d.en/cover_thumb.png delete mode 100644 ddili/src/ders/d.en/destroy.d delete mode 100644 ddili/src/ders/d.en/encapsulation.d delete mode 100644 ddili/src/ders/d.en/enum.cozum.d delete mode 100644 ddili/src/ders/d.en/enum.d delete mode 100644 ddili/src/ders/d.en/floating_point.cozum.d delete mode 100644 ddili/src/ders/d.en/for.cozum.d delete mode 100644 ddili/src/ders/d.en/for.d delete mode 100644 ddili/src/ders/d.en/foreach.cozum.d delete mode 100644 ddili/src/ders/d.en/foreach.d delete mode 100644 ddili/src/ders/d.en/foreach_opapply.cozum.d delete mode 100644 ddili/src/ders/d.en/foreword1.d delete mode 100644 ddili/src/ders/d.en/foreword2.d delete mode 100644 ddili/src/ders/d.en/formatted_input.cozum.d delete mode 100644 ddili/src/ders/d.en/formatted_input.d delete mode 100644 ddili/src/ders/d.en/frontispiece.d delete mode 100644 ddili/src/ders/d.en/function_overloading.cozum.d delete mode 100644 ddili/src/ders/d.en/function_overloading.d delete mode 100644 ddili/src/ders/d.en/function_parameters.cozum.d delete mode 100644 ddili/src/ders/d.en/function_parameters.d delete mode 100644 ddili/src/ders/d.en/functions.cozum.d delete mode 100644 ddili/src/ders/d.en/functions.d delete mode 100644 ddili/src/ders/d.en/functions_more.d delete mode 100644 ddili/src/ders/d.en/goto.d delete mode 100644 ddili/src/ders/d.en/halftitle.html delete mode 100644 ddili/src/ders/d.en/hello_world.cozum.d delete mode 100644 ddili/src/ders/d.en/hello_world.d delete mode 100644 ddili/src/ders/d.en/index_section_head.html delete mode 100644 ddili/src/ders/d.en/index_section_tail.html delete mode 100644 ddili/src/ders/d.en/inheritance.cozum.d delete mode 100644 ddili/src/ders/d.en/inheritance.d delete mode 100644 ddili/src/ders/d.en/invariant.d delete mode 100644 ddili/src/ders/d.en/io.cozum.d delete mode 100644 ddili/src/ders/d.en/io.d delete mode 100644 ddili/src/ders/d.en/ix.d delete mode 100644 ddili/src/ders/d.en/lambda.d delete mode 100644 ddili/src/ders/d.en/literals.cozum.d delete mode 100644 ddili/src/ders/d.en/literals.d delete mode 100644 ddili/src/ders/d.en/logical_expressions.cozum.d delete mode 100644 ddili/src/ders/d.en/logical_expressions.d delete mode 100644 ddili/src/ders/d.en/main.cozum.d delete mode 100644 ddili/src/ders/d.en/main.d delete mode 100644 ddili/src/ders/d.en/member_functions.cozum.d delete mode 100644 ddili/src/ders/d.en/member_functions.d delete mode 100644 ddili/src/ders/d.en/memory.d delete mode 100644 ddili/src/ders/d.en/modules.d delete mode 100644 ddili/src/ders/d.en/nested.d delete mode 100644 ddili/src/ders/d.en/operator_overloading.d delete mode 100644 ddili/src/ders/d.en/operator_precedence.d delete mode 100644 ddili/src/ders/d.en/parameter_flexibility.cozum.d delete mode 100644 ddili/src/ders/d.en/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/d.en/pdf_cozum_head.html delete mode 100644 ddili/src/ders/d.en/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/d.en/pdf_html_head.html delete mode 100644 ddili/src/ders/d.en/pdf_html_tail.html delete mode 100644 ddili/src/ders/d.en/pdf_sozluk_head.html delete mode 100644 ddili/src/ders/d.en/pointers.cozum.d delete mode 100644 ddili/src/ders/d.en/pointers.d delete mode 100644 ddili/src/ders/d.en/ranges_more.d delete mode 100644 ddili/src/ders/d.en/rss.xml delete mode 100644 ddili/src/ders/d.en/slices.cozum.d delete mode 100644 ddili/src/ders/d.en/slices.d delete mode 100644 ddili/src/ders/d.en/special_functions.d delete mode 100644 ddili/src/ders/d.en/stream_redirect.cozum.d delete mode 100644 ddili/src/ders/d.en/stream_redirect.d delete mode 100644 ddili/src/ders/d.en/struct.cozum.d delete mode 100644 ddili/src/ders/d.en/struct.d delete mode 100644 ddili/src/ders/d.en/switch_case.cozum.d delete mode 100644 ddili/src/ders/d.en/switch_case.d delete mode 100644 ddili/src/ders/d.en/templates_more.d delete mode 100644 ddili/src/ders/d.en/title.html delete mode 100644 ddili/src/ders/d.en/toc_head.html delete mode 100644 ddili/src/ders/d.en/toc_tail.html delete mode 100644 ddili/src/ders/d.en/types.cozum.d delete mode 100644 ddili/src/ders/d.en/types.d delete mode 100644 ddili/src/ders/d.en/union.d delete mode 100644 ddili/src/ders/d.en/unit_testing.cozum.d delete mode 100644 ddili/src/ders/d.en/unit_testing.d delete mode 100644 ddili/src/ders/d.en/value_vs_reference.d delete mode 100644 ddili/src/ders/d.en/variables.cozum.d delete mode 100644 ddili/src/ders/d.en/variables.d delete mode 100644 ddili/src/ders/d.en/while.cozum.d delete mode 100644 ddili/src/ders/d.en/while.d delete mode 100644 ddili/src/ders/d.en/writeln.cozum.d delete mode 100644 ddili/src/ders/d.en/writeln.d delete mode 100644 ddili/src/ders/d/Makefile.in delete mode 100644 ddili/src/ders/d/Tennessee_Valley.jpg delete mode 100644 ddili/src/ders/d/alias.d delete mode 100644 ddili/src/ders/d/alias_this.d delete mode 100644 ddili/src/ders/d/araliklar.d delete mode 100644 ddili/src/ders/d/araliklar_baska.d delete mode 100644 ddili/src/ders/d/aritmetik_islemler.cozum.d delete mode 100644 ddili/src/ders/d/aritmetik_islemler.d delete mode 100644 ddili/src/ders/d/assert.cozum.d delete mode 100644 ddili/src/ders/d/assert.d delete mode 100644 ddili/src/ders/d/atama_ve_sira.cozum.d delete mode 100644 ddili/src/ders/d/atama_ve_sira.d delete mode 100644 ddili/src/ders/d/auto.cozum.d delete mode 100644 ddili/src/ders/d/auto.d delete mode 100644 ddili/src/ders/d/bellek_yonetimi.d delete mode 100644 ddili/src/ders/d/birim_testler.cozum.d delete mode 100644 ddili/src/ders/d/birim_testler.d delete mode 100644 ddili/src/ders/d/birlikler.d delete mode 100644 ddili/src/ders/d/bit_islemleri.cozum.d delete mode 100644 ddili/src/ders/d/bit_islemleri.d delete mode 100644 ddili/src/ders/d/blurbs.d delete mode 100644 ddili/src/ders/d/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/d/cikti_duzeni.cozum.d delete mode 100644 ddili/src/ders/d/cikti_duzeni.d delete mode 100644 ddili/src/ders/d/clear.d delete mode 100644 ddili/src/ders/d/code/BENIOKU delete mode 100644 ddili/src/ders/d/cokuzlular.d delete mode 100644 ddili/src/ders/d/const_uye_islevler.d delete mode 100644 ddili/src/ders/d/const_ve_immutable.d delete mode 100644 ddili/src/ders/d/copyright.d delete mode 100644 ddili/src/ders/d/cover.pdf delete mode 100644 ddili/src/ders/d/cover_ebook.png delete mode 100644 ddili/src/ders/d/cover_thumb.png delete mode 100644 ddili/src/ders/d/cozum_ozel.ddoc delete mode 100644 ddili/src/ders/d/deger_referans.d delete mode 100644 ddili/src/ders/d/deger_sol_sag.d delete mode 100644 ddili/src/ders/d/degiskenler.cozum.d delete mode 100644 ddili/src/ders/d/degiskenler.d delete mode 100644 ddili/src/ders/d/derleyici.d delete mode 100644 ddili/src/ders/d/derse_ozel.ddoc delete mode 100644 ddili/src/ders/d/devami_gelecek.d delete mode 100644 ddili/src/ders/d/dilimler.cozum.d delete mode 100644 ddili/src/ders/d/dilimler.d delete mode 100644 ddili/src/ders/d/dizgiler.cozum.d delete mode 100644 ddili/src/ders/d/dizgiler.d delete mode 100644 ddili/src/ders/d/diziler.cozum.d delete mode 100644 ddili/src/ders/d/diziler.d delete mode 100644 ddili/src/ders/d/do_while.cozum.d delete mode 100644 ddili/src/ders/d/do_while.d delete mode 100644 ddili/src/ders/d/dosyalar.cozum.d delete mode 100644 ddili/src/ders/d/dosyalar.d delete mode 100644 ddili/src/ders/d/enum.cozum.d delete mode 100644 ddili/src/ders/d/enum.d delete mode 100644 ddili/src/ders/d/es_zamanli.d delete mode 100644 ddili/src/ders/d/es_zamanli_shared.d delete mode 100644 ddili/src/ders/d/esleme_tablolari.cozum.d delete mode 100644 ddili/src/ders/d/esleme_tablolari.d delete mode 100644 ddili/src/ders/d/etiketler.d delete mode 100644 ddili/src/ders/d/fiberler.d delete mode 100644 ddili/src/ders/d/for_dongusu.cozum.d delete mode 100644 ddili/src/ders/d/for_dongusu.d delete mode 100644 ddili/src/ders/d/foreach_dongusu.cozum.d delete mode 100644 ddili/src/ders/d/foreach_dongusu.d delete mode 100644 ddili/src/ders/d/foreach_opapply.cozum.d delete mode 100644 ddili/src/ders/d/foreach_opapply.d delete mode 100644 ddili/src/ders/d/frontispiece.d delete mode 100644 ddili/src/ders/d/giris_cikis.cozum.d delete mode 100644 ddili/src/ders/d/giris_cikis.d delete mode 100644 ddili/src/ders/d/giris_duzeni.cozum.d delete mode 100644 ddili/src/ders/d/giris_duzeni.d delete mode 100644 ddili/src/ders/d/gostergeler.cozum.d delete mode 100644 ddili/src/ders/d/gostergeler.d delete mode 100644 ddili/src/ders/d/halftitle.html delete mode 100644 ddili/src/ders/d/hatalar.d delete mode 100644 ddili/src/ders/d/hazir_degerler.cozum.d delete mode 100644 ddili/src/ders/d/hazir_degerler.d delete mode 100644 ddili/src/ders/d/ic_tanimlar.d delete mode 100644 ddili/src/ders/d/if_kosulu.cozum.d delete mode 100644 ddili/src/ders/d/if_kosulu.d delete mode 100644 ddili/src/ders/d/index.d delete mode 100644 ddili/src/ders/d/index_section_head.html delete mode 100644 ddili/src/ders/d/index_section_tail.html delete mode 100644 ddili/src/ders/d/interface.d delete mode 100644 ddili/src/ders/d/invariant.d delete mode 100644 ddili/src/ders/d/is_ifadesi.d delete mode 100644 ddili/src/ders/d/isim_alani.d delete mode 100644 ddili/src/ders/d/islec_oncelikleri.d delete mode 100644 ddili/src/ders/d/islec_yukleme.cozum.d delete mode 100644 ddili/src/ders/d/islec_yukleme.d delete mode 100644 ddili/src/ders/d/islev_parametreleri.cozum.d delete mode 100644 ddili/src/ders/d/islev_parametreleri.d delete mode 100644 ddili/src/ders/d/islev_yukleme.cozum.d delete mode 100644 ddili/src/ders/d/islev_yukleme.d delete mode 100644 ddili/src/ders/d/islevler.cozum.d delete mode 100644 ddili/src/ders/d/islevler.d delete mode 100644 ddili/src/ders/d/islevler_diger.d delete mode 100644 ddili/src/ders/d/ix.d delete mode 100644 ddili/src/ders/d/kapamalar.d delete mode 100644 ddili/src/ders/d/karakterler.d delete mode 100644 ddili/src/ders/d/katmalar.d delete mode 100644 ddili/src/ders/d/kesirli_sayilar.cozum.d delete mode 100644 ddili/src/ders/d/kesirli_sayilar.d delete mode 100644 ddili/src/ders/d/kosullu_derleme.d delete mode 100644 ddili/src/ders/d/kosut_islemler.d delete mode 100644 ddili/src/ders/d/main.cozum.d delete mode 100644 ddili/src/ders/d/main.d delete mode 100644 ddili/src/ders/d/mantiksal_ifadeler.cozum.d delete mode 100644 ddili/src/ders/d/mantiksal_ifadeler.d delete mode 100644 ddili/src/ders/d/merhaba_dunya.cozum.d delete mode 100644 ddili/src/ders/d/merhaba_dunya.d delete mode 100644 ddili/src/ders/d/moduller.d delete mode 100644 ddili/src/ders/d/nitelikler.d delete mode 100644 ddili/src/ders/d/null_ve_is.d delete mode 100644 ddili/src/ders/d/object.cozum.d delete mode 100644 ddili/src/ders/d/object.d delete mode 100644 ddili/src/ders/d/onsoz1.d delete mode 100644 ddili/src/ders/d/onsoz2.d delete mode 100644 ddili/src/ders/d/onsoz_yazar.d delete mode 100644 ddili/src/ders/d/ozel_islevler.d delete mode 100644 ddili/src/ders/d/parametre_serbestligi.cozum.d delete mode 100644 ddili/src/ders/d/parametre_serbestligi.d delete mode 100644 ddili/src/ders/d/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/d/pdf_cozum_head.html delete mode 100644 ddili/src/ders/d/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/d/pdf_html_head.html delete mode 100644 ddili/src/ders/d/pdf_html_tail.html delete mode 100644 ddili/src/ders/d/pdf_sozluk_head.html delete mode 100644 ddili/src/ders/d/pdf_sozluk_tail.html delete mode 100644 ddili/src/ders/d/pragma.d delete mode 100644 ddili/src/ders/d/rss.xml delete mode 100644 ddili/src/ders/d/sablonlar.d delete mode 100644 ddili/src/ders/d/sablonlar_ayrintili.d delete mode 100644 ddili/src/ders/d/sarma.d delete mode 100644 ddili/src/ders/d/scope.d delete mode 100644 ddili/src/ders/d/siniflar.d delete mode 100644 ddili/src/ders/d/sozlesmeli.cozum.d delete mode 100644 ddili/src/ders/d/sozlesmeli.d delete mode 100644 ddili/src/ders/d/standart_akim_baglamak.cozum.d delete mode 100644 ddili/src/ders/d/standart_akim_baglamak.d delete mode 100644 ddili/src/ders/d/standart_giris.cozum.d delete mode 100644 ddili/src/ders/d/standart_giris.d delete mode 100644 ddili/src/ders/d/switch_case.cozum.d delete mode 100644 ddili/src/ders/d/switch_case.d delete mode 100644 ddili/src/ders/d/tembel_degerlendirmeler.d delete mode 100644 ddili/src/ders/d/temel_turler.cozum.d delete mode 100644 ddili/src/ders/d/temel_turler.d delete mode 100644 ddili/src/ders/d/title.html delete mode 100644 ddili/src/ders/d/toc_head.html delete mode 100644 ddili/src/ders/d/toc_tail.html delete mode 100644 ddili/src/ders/d/tur_donusumleri.d delete mode 100644 ddili/src/ders/d/tureme.cozum.d delete mode 100644 ddili/src/ders/d/tureme.d delete mode 100644 ddili/src/ders/d/uclu_islec.cozum.d delete mode 100644 ddili/src/ders/d/uclu_islec.d delete mode 100644 ddili/src/ders/d/uda.d delete mode 100644 ddili/src/ders/d/ufcs.d delete mode 100644 ddili/src/ders/d/uye_islevler.cozum.d delete mode 100644 ddili/src/ders/d/uye_islevler.d delete mode 100644 ddili/src/ders/d/while_dongusu.cozum.d delete mode 100644 ddili/src/ders/d/while_dongusu.d delete mode 100644 ddili/src/ders/d/writeln.cozum.d delete mode 100644 ddili/src/ders/d/writeln.d delete mode 100644 ddili/src/ders/d/yapilar.cozum.d delete mode 100644 ddili/src/ders/d/yapilar.d delete mode 100644 ddili/src/ders/d/yasam_surecleri.d delete mode 100644 ddili/src/ders/gtkd/Makefile.in delete mode 100644 ddili/src/ders/gtkd/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/gtkd/code/BENIOKU delete mode 100644 ddili/src/ders/gtkd/copyright.d delete mode 100644 ddili/src/ders/gtkd/derse_ozel.ddoc delete mode 100644 ddili/src/ders/gtkd/devami_gelecek.d delete mode 100644 ddili/src/ders/gtkd/frontispiece.d delete mode 100644 ddili/src/ders/gtkd/halftitle.html delete mode 100644 ddili/src/ders/gtkd/index.d delete mode 100644 ddili/src/ders/gtkd/index_section_head.html delete mode 100644 ddili/src/ders/gtkd/index_section_tail.html delete mode 100644 ddili/src/ders/gtkd/ix.d delete mode 100644 ddili/src/ders/gtkd/merhaba_gtkd.d delete mode 100644 ddili/src/ders/gtkd/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/gtkd/pdf_cozum_head.html delete mode 100644 ddili/src/ders/gtkd/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/gtkd/pdf_html_head.html delete mode 100644 ddili/src/ders/gtkd/pdf_html_tail.html delete mode 100644 ddili/src/ders/gtkd/pdf_indir.d delete mode 100644 ddili/src/ders/gtkd/pdf_sozluk_head.html delete mode 100644 ddili/src/ders/gtkd/pencere_kurma_islemleri.d delete mode 100644 ddili/src/ders/gtkd/rss.xml delete mode 100644 ddili/src/ders/gtkd/tanitim.d delete mode 100644 ddili/src/ders/gtkd/title.html delete mode 100644 ddili/src/ders/gtkd/toc_head.html delete mode 100644 ddili/src/ders/gtkd/toc_tail.html delete mode 100644 ddili/src/ders/index.d delete mode 100644 ddili/src/ders/sdl/Makefile.in delete mode 100644 ddili/src/ders/sdl/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/sdl/code/BENIOKU delete mode 100644 ddili/src/ders/sdl/copyright.d delete mode 100644 ddili/src/ders/sdl/derse_ozel.ddoc delete mode 100644 ddili/src/ders/sdl/devami_gelecek.d delete mode 100644 ddili/src/ders/sdl/frontispiece.d delete mode 100644 ddili/src/ders/sdl/halftitle.html delete mode 100644 ddili/src/ders/sdl/hareket.d delete mode 100644 ddili/src/ders/sdl/hosgeldiniz.d delete mode 100644 ddili/src/ders/sdl/index.d delete mode 100644 ddili/src/ders/sdl/index_section_head.html delete mode 100644 ddili/src/ders/sdl/index_section_tail.html delete mode 100644 ddili/src/ders/sdl/ix.d delete mode 100644 ddili/src/ders/sdl/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/sdl/pdf_cozum_head.html delete mode 100644 ddili/src/ders/sdl/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/sdl/pdf_html_head.html delete mode 100644 ddili/src/ders/sdl/pdf_html_tail.html delete mode 100644 ddili/src/ders/sdl/pdf_indir.d delete mode 100644 ddili/src/ders/sdl/pdf_sozluk_head.html delete mode 100644 ddili/src/ders/sdl/rss.xml delete mode 100644 ddili/src/ders/sdl/src/ders1.zip delete mode 100644 ddili/src/ders/sdl/title.html delete mode 100644 ddili/src/ders/sdl/toc_head.html delete mode 100644 ddili/src/ders/sdl/toc_tail.html delete mode 100644 ddili/src/ders/template/Makefile.in delete mode 100644 ddili/src/ders/template/README delete mode 100644 ddili/src/ders/template/breadcrumbs.ddoc delete mode 100644 ddili/src/ders/template/code/README delete mode 100644 ddili/src/ders/template/copyright.d delete mode 100644 ddili/src/ders/template/derse_ozel.ddoc delete mode 100644 ddili/src/ders/template/foreword2.d delete mode 100644 ddili/src/ders/template/frontispiece.d delete mode 100644 ddili/src/ders/template/halftitle.html delete mode 100644 ddili/src/ders/template/hello_world.cozum.d delete mode 100644 ddili/src/ders/template/hello_world.d delete mode 100644 ddili/src/ders/template/index.d delete mode 100644 ddili/src/ders/template/index_section_head.html delete mode 100644 ddili/src/ders/template/index_section_tail.html delete mode 100644 ddili/src/ders/template/ix.d delete mode 100644 ddili/src/ders/template/pdf.derse_ozel.css delete mode 100644 ddili/src/ders/template/pdf_cozum_head.html delete mode 100644 ddili/src/ders/template/pdf_cozum_tail.html delete mode 100644 ddili/src/ders/template/pdf_html_head.html delete mode 100644 ddili/src/ders/template/pdf_html_tail.html delete mode 100644 ddili/src/ders/template/preface.d delete mode 100644 ddili/src/ders/template/rss.xml delete mode 100644 ddili/src/ders/template/title.html delete mode 100644 ddili/src/ders/template/to_be_continued.d delete mode 100644 ddili/src/ders/template/toc_head.html delete mode 100644 ddili/src/ders/template/toc_tail.html delete mode 100644 ddili/src/dusey_navigasyon.ddoc delete mode 100644 ddili/src/ebook_font_manifest delete mode 100644 ddili/src/ebook_override.css delete mode 100644 ddili/src/ebooksanitizer.d delete mode 100644 ddili/src/fonts.css delete mode 100644 ddili/src/fonts/andada/Andada-Bold.ttf delete mode 100644 ddili/src/fonts/andada/Andada-BoldItalic.ttf delete mode 100644 ddili/src/fonts/andada/Andada-Italic.ttf delete mode 100644 ddili/src/fonts/andada/Andada-Regular.ttf delete mode 100644 ddili/src/fonts/andada/OFL.txt delete mode 100644 ddili/src/fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf delete mode 100644 ddili/src/fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf delete mode 100644 ddili/src/fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf delete mode 100644 ddili/src/fonts/dejavu/DejaVuSansMono-webfont.ttf delete mode 100644 ddili/src/fonts/opensans/OpenSans-Bold.ttf delete mode 100644 ddili/src/fonts/opensans/OpenSans-BoldItalic.ttf delete mode 100644 ddili/src/fonts/opensans/OpenSans-Italic.ttf delete mode 100644 ddili/src/fonts/opensans/OpenSans-Regular.ttf delete mode 100644 ddili/src/iletisim.d delete mode 100644 ddili/src/image/Androgynous_D_coders.png delete mode 100644 ddili/src/image/Androgynous_D_coders_5.5x7.png delete mode 100644 ddili/src/image/book.png delete mode 100644 ddili/src/image/bullet_black.png delete mode 100644 ddili/src/image/by-nc-sa.png delete mode 100644 ddili/src/image/cc_80x15.png delete mode 100644 ddili/src/image/cc_88x31.png delete mode 100644 ddili/src/image/d_harfi.jpg delete mode 100644 ddili/src/image/d_harfi_renamed.jpg delete mode 100644 ddili/src/image/d_source.png delete mode 100644 ddili/src/image/email.png delete mode 100644 ddili/src/image/favicon.png delete mode 100644 ddili/src/image/forum.png delete mode 100644 ddili/src/image/gulen.png delete mode 100644 ddili/src/image/oyundongusu.jpg delete mode 100644 ddili/src/image/oyunlardahareket.png delete mode 100644 ddili/src/image/pdficon_small.gif delete mode 100644 ddili/src/image/pencil.png delete mode 100644 ddili/src/image/penguen.png delete mode 100644 ddili/src/image/rss-icon.png delete mode 100644 ddili/src/image/sdlkoordinat.png delete mode 100644 ddili/src/image/vektorler1.jpg delete mode 100644 ddili/src/image/vektorler1.png delete mode 100644 ddili/src/image/vektorler2.png delete mode 100644 ddili/src/image/vektorler3.png delete mode 100644 ddili/src/image/vektorler4.png delete mode 100644 ddili/src/index.d delete mode 100644 ddili/src/kurulum/breadcrumbs.ddoc delete mode 100644 ddili/src/kurulum/dmd.d delete mode 100644 ddili/src/kurulum/emacs_d-mode.d delete mode 100644 ddili/src/kurulum/gdc.d delete mode 100644 ddili/src/kurulum/index.d delete mode 100644 ddili/src/kurulum/locale.properties delete mode 100644 ddili/src/makale/bellek.d delete mode 100644 ddili/src/makale/breadcrumbs.ddoc delete mode 100644 ddili/src/makale/d_dilimleri.d delete mode 100644 ddili/src/makale/d_tipleri.d delete mode 100644 ddili/src/makale/degismez.d delete mode 100644 ddili/src/makale/dub_tanisma.d delete mode 100644 ddili/src/makale/duzenli_ifadeler.d delete mode 100644 ddili/src/makale/eleman_erisimi_uzerine.d delete mode 100644 ddili/src/makale/index.d delete mode 100644 ddili/src/makale/katma.d delete mode 100644 ddili/src/makale/neden_d.d delete mode 100644 ddili/src/makale/saflik.d delete mode 100644 ddili/src/makale/shared.d delete mode 100644 ddili/src/makale/tembel_hesap.d delete mode 100755 ddili/src/make_ders_macros.sh delete mode 100644 ddili/src/ornek_kod/breadcrumbs.ddoc delete mode 100644 ddili/src/ornek_kod/index.d delete mode 100644 ddili/src/pdf.css delete mode 100644 ddili/src/pdf.ddoc delete mode 100644 ddili/src/pdf_fonts.css delete mode 100644 ddili/src/regular.ddoc delete mode 100644 ddili/src/reveal_ix.css delete mode 100644 ddili/src/robots.txt delete mode 100644 ddili/src/rss.xml delete mode 100644 ddili/src/sozluk.d delete mode 100644 ddili/src/sozluk.txt delete mode 100644 ddili/src/sozlukmaker.d delete mode 100644 ddili/src/style.css delete mode 100644 ddili/src/sunum/2012_AliCehreli_Cpp11_D.pdf delete mode 100644 ddili/src/sunum/2012_AliCehreli_D_Tanitim.pdf delete mode 100644 ddili/src/sunum/2012_AliCehreli_Kosut_Islemler.pdf delete mode 100644 ddili/src/sunum/2012_SalihDincer_Diziler_Dilimler.pdf delete mode 100644 ddili/src/sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf delete mode 100644 ddili/src/sunum/breadcrumbs.ddoc delete mode 100644 ddili/src/sunum/index.d delete mode 100644 ddili/src/sunum/ingilizce_sunumlar.d delete mode 100644 ddili/src/sunum/merhaba_2012.d delete mode 100644 ddili/src/sunum/sonrasi.d delete mode 100644 ddili/src/tanitim/breadcrumbs.ddoc delete mode 100644 ddili/src/tanitim/dil_kutuphane.d delete mode 100644 ddili/src/tanitim/fark_c.d delete mode 100644 ddili/src/tanitim/fark_cpp.d delete mode 100644 ddili/src/tanitim/fark_dizgi.d delete mode 100644 ddili/src/tanitim/fark_karmasik.d delete mode 100644 ddili/src/tanitim/fark_onislemci.d delete mode 100644 ddili/src/tanitim/genel.d delete mode 100644 ddili/src/tanitim/index.d delete mode 100644 ddili/src/todo rename {ddili/src/ders/d.en => source}/Makefile.in (100%) rename {ddili/src/ders/d.en => source}/aa.cozum.d (100%) rename {ddili/src/ders/d.en => source}/aa.d (98%) rename {ddili/src/ders/d.en => source}/alias.d (100%) rename {ddili/src/ders/d.en => source}/alias_this.d (87%) rename {ddili/src/ders/d.cn => source}/arithmetic.cozum.d (100%) rename {ddili/src/ders/d.en => source}/arithmetic.d (98%) rename {ddili/src/ders/d.cn => source}/arrays.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/arrays.d (92%) rename {ddili/src/ders/d.cn => source}/assert.cozum.d (100%) rename {ddili/src/ders/d.en => source}/assert.d (97%) rename {ddili/src/ders/d.cn => source}/assignment.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/assignment.d (100%) rename {ddili/src/ders/d.cn => source}/auto_and_typeof.cozum.d (100%) rename {ddili/src/ders/d.en => source}/auto_and_typeof.d (100%) rename {ddili/src/ders/d.cn => source}/bit_operations.cozum.d (100%) rename {ddili/src/ders/d.en => source}/bit_operations.d (91%) rename {ddili/src/ders/d.cn => source}/blurbs.d (100%) rename {ddili/src/ders/d.en => source}/breadcrumbs.ddoc (100%) rename {ddili/src/ders/d.cn => source}/cast.d (100%) rename {ddili/src/ders/d.en => source}/characters.d (98%) rename {ddili/src/ders/d.en => source}/class.d (94%) rename {ddili/src/ders/d.cn => source}/code/README (100%) rename {ddili/src/ders/d.en => source}/compiler.d (100%) rename {ddili/src/ders/d.en => source}/concurrency.d (100%) rename {ddili/src/ders/d.en => source}/concurrency_shared.d (98%) rename {ddili/src/ders/d.en => source}/cond_comp.d (92%) rename {ddili/src/ders/d.en => source}/const_and_immutable.d (98%) rename {ddili/src/ders/d.en => source}/const_member_functions.d (100%) rename {ddili/src/ders/d.cn => source}/contracts.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/contracts.d (99%) rename {ddili/src/ders/d.en => source}/copyright.d (98%) rename {ddili/src/ders/d.en => source}/cozum_ozel.ddoc (100%) rename {ddili/src/ders/d.en => source}/derse_ozel.ddoc (100%) rename {ddili/src/ders/d.cn => source}/destroy.d (99%) rename {ddili/src/ders/d.en => source}/do_while.cozum.d (100%) rename {ddili/src/ders/d.en => source}/do_while.d (100%) rename {ddili/src/ders/d.cn => source}/encapsulation.d (100%) rename {ddili/src/ders/d.cn => source}/enum.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/enum.d (97%) rename {ddili/src/ders/d.en => source}/exceptions.d (99%) rename {ddili/src/ders/d.en => source}/fibers.d (99%) rename {ddili/src/ders/d.en => source}/files.cozum.d (100%) rename {ddili/src/ders/d.en => source}/files.d (100%) rename {ddili/src/ders/d.cn => source}/floating_point.cozum.d (100%) rename {ddili/src/ders/d.en => source}/floating_point.d (87%) rename {ddili/src/ders/d.cn => source}/for.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/for.d (100%) rename {ddili/src/ders/d.cn => source}/foreach.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/foreach.d (95%) rename {ddili/src/ders/d.cn => source}/foreach_opapply.cozum.d (100%) rename {ddili/src/ders/d.en => source}/foreach_opapply.d (99%) rename {ddili/src/ders/d.cn => source}/foreword1.d (100%) rename {ddili/src/ders/d.cn => source}/foreword2.d (100%) rename {ddili/src/ders/d.cn => source}/formatted_input.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/formatted_input.d (100%) rename {ddili/src/ders/d.en => source}/formatted_output.cozum.d (100%) rename {ddili/src/ders/d.en => source}/formatted_output.d (95%) rename {ddili/src/ders/d.cn => source}/frontispiece.d (100%) rename {ddili/src/ders/d.cn => source}/function_overloading.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/function_overloading.d (100%) rename {ddili/src/ders/d.cn => source}/function_parameters.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/function_parameters.d (96%) rename {ddili/src/ders/d.cn => source}/functions.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/functions.d (98%) rename {ddili/src/ders/d.cn => source}/functions_more.d (97%) rename {ddili/src/ders/d.cn => source}/goto.d (100%) rename {ddili/src/ders/d.cn => source}/halftitle.html (100%) rename {ddili/src/ders/d.cn => source}/hello_world.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/hello_world.d (91%) rename {ddili/src/ders/d.en => source}/if.cozum.d (100%) rename {ddili/src/ders/d.en => source}/if.d (100%) rename {ddili/src/ders/d.en => source}/index.d (94%) rename {ddili/src/ders/d.cn => source}/index_section_head.html (100%) rename {ddili/src/ders/d.cn => source}/index_section_tail.html (100%) rename {ddili/src/ders/d.cn => source}/inheritance.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/inheritance.d (100%) rename {ddili/src/ders/d.en => source}/input.cozum.d (100%) rename {ddili/src/ders/d.en => source}/input.d (94%) rename {ddili/src/ders/d.en => source}/interface.d (99%) rename {ddili/src/ders/d.cn => source}/invariant.d (100%) rename {ddili/src/ders/d.cn => source}/io.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/io.d (100%) rename {ddili/src/ders/d.en => source}/is_expr.d (98%) rename {ddili/src/ders/d.cn => source}/ix.d (100%) rename {ddili/src/ders/d.cn => source}/lambda.d (98%) rename {ddili/src/ders/d.en => source}/lazy_operators.d (100%) rename {ddili/src/ders/d.en => source}/lifetimes.d (95%) rename {ddili/src/ders/d.cn => source}/literals.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/literals.d (100%) rename {ddili/src/ders/d.cn => source}/logical_expressions.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/logical_expressions.d (100%) rename {ddili/src/ders/d.en => source}/lvalue_rvalue.d (89%) rename {ddili/src/ders/d.cn => source}/main.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/main.d (99%) rename {ddili/src/ders/d.cn => source}/member_functions.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/member_functions.d (100%) rename {ddili/src/ders/d.cn => source}/memory.d (98%) rename {ddili/src/ders/d.en => source}/mixin.d (92%) rename {ddili/src/ders/d.cn => source}/modules.d (100%) rename {ddili/src/ders/d.en => source}/name_space.d (100%) rename {ddili/src/ders/d.cn => source}/nested.d (98%) rename {ddili/src/ders/d.en => source}/null_is.d (99%) rename {ddili/src/ders/d.en => source}/object.cozum.d (100%) rename {ddili/src/ders/d.en => source}/object.d (96%) rename {ddili/src/ders/d.en => source}/operator_overloading.cozum.d (98%) rename {ddili/src/ders/d.cn => source}/operator_overloading.d (97%) rename {ddili/src/ders/d.cn => source}/operator_precedence.d (93%) rename {ddili/src/ders/d.en => source}/parallelism.d (100%) rename {ddili/src/ders/d.cn => source}/parameter_flexibility.cozum.d (100%) rename {ddili/src/ders/d.en => source}/parameter_flexibility.d (90%) rename {ddili/src/ders/d.cn => source}/pdf.derse_ozel.css (100%) rename {ddili/src/ders/d.cn => source}/pdf_cozum_head.html (100%) rename {ddili/src/ders/d.cn => source}/pdf_cozum_tail.html (100%) rename {ddili/src/ders/d.cn => source}/pdf_html_head.html (100%) rename {ddili/src/ders/d.cn => source}/pdf_html_tail.html (100%) rename {ddili/src/ders/d.cn => source}/pdf_sozluk_head.html (100%) rename {ddili/src/ders/d.cn => source}/pointers.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/pointers.d (99%) rename {ddili/src/ders/d.en => source}/pragma.d (99%) rename {ddili/src/ders/d.en => source}/preface.d (92%) rename {ddili/src/ders/d.en => source}/property.d (100%) rename {ddili/src/ders/d.en => source}/ranges.d (98%) rename {ddili/src/ders/d.cn => source}/ranges_more.d (100%) rename {ddili/src/ders/d.cn => source}/rss.xml (100%) rename {ddili/src/ders/d.en => source}/scope.d (100%) rename {ddili/src/ders/d.cn => source}/slices.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/slices.d (98%) rename {ddili/src/ders/d.cn => source}/special_functions.d (100%) rename {ddili/src/ders/d.cn => source}/stream_redirect.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/stream_redirect.d (100%) rename {ddili/src/ders/d.en => source}/strings.cozum.d (100%) rename {ddili/src/ders/d.en => source}/strings.d (98%) rename {ddili/src/ders/d.cn => source}/struct.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/struct.d (98%) rename {ddili/src/ders/d.cn => source}/switch_case.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/switch_case.d (100%) rename {ddili/src/ders/d.en => source}/templates.d (99%) rename {ddili/src/ders/d.cn => source}/templates_more.d (98%) rename {ddili/src/ders/d.en => source}/ternary.cozum.d (100%) rename {ddili/src/ders/d.en => source}/ternary.d (100%) rename {ddili/src/ders/d.cn => source}/title.html (100%) rename {ddili/src/ders/d.en => source}/to_be_continued.d (100%) rename {ddili/src/ders/d.cn => source}/toc_head.html (100%) rename {ddili/src/ders/d.cn => source}/toc_tail.html (100%) rename {ddili/src/ders/d.en => source}/tuples.d (100%) rename {ddili/src/ders/d.cn => source}/types.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/types.d (100%) rename {ddili/src/ders/d.en => source}/uda.d (100%) rename {ddili/src/ders/d.en => source}/ufcs.d (100%) rename {ddili/src/ders/d.cn => source}/union.d (98%) rename {ddili/src/ders/d.cn => source}/unit_testing.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/unit_testing.d (100%) rename {ddili/src/ders/d.cn => source}/value_vs_reference.d (98%) rename {ddili/src/ders/d.cn => source}/variables.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/variables.d (100%) rename {ddili/src/ders/d.cn => source}/while.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/while.d (100%) rename {ddili/src/ders/d.cn => source}/writeln.cozum.d (100%) rename {ddili/src/ders/d.cn => source}/writeln.d (100%) diff --git a/ddili/.gitignore b/ddili/.gitignore deleted file mode 100644 index 42e41d0..0000000 --- a/ddili/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -pdf_surum.txt -*.pdf_icin.html -*pdf_icin_bir_arada.*_anchors.html -index_section.html -ix.html -ix_body.html -toc.html -ebook_source.*.html* -*.last_modified.ddoc -*.d.macros.ddoc -pdf_local.ddoc -sozluk.ddoc -sozluk_body.ddoc -test.ddoc -*.code_tested -*.o -*.d.pre_ddoc.d -*.d.post_ddoc.d -*.tar.gz -*~ -ebook.css -AliCehreli_resume.pdf - -public_html/ -public_html_test/ -src/anchorgen -src/codetester -src/sozlukmaker -src/ebooksanitizer - -src/ders/d.en/pdf_icin_bir_arada -src/ders/d.en/Programming_in_D.pdf -src/ders/d.en/Programming_in_D.print.pdf -src/ders/d.en/Programming_in_D_code_samples -src/ders/d.en/Programming_in_D_code_samples.zip -src/ders/d.en/code/*[0-9].d - -src/ders/d/pdf_icin_bir_arada -src/ders/d/sozluk.d -src/ders/d/D_Programlama_Dili.pdf -src/ders/d/D_Programlama_Dili.print.pdf -src/ders/d/D_Programlama_Dili_kod_ornekleri -src/ders/d/D_Programlama_Dili_kod_ornekleri.zip -src/ders/d/code/*[0-9].d - -src/ders/gtkd/pdf_icin_bir_arada -src/ders/gtkd/Gtkd_ile_Programlama.pdf -src/ders/gtkd/Gtkd_ile_Programlama.print.pdf -src/ders/gtkd/Gtkd_ile_Programlama_kod_ornekleri -src/ders/gtkd/Gtkd_ile_Programlama_kod_ornekleri.zip - -src/ders/sdl/pdf_icin_bir_arada -src/ders/sdl/SDL_ile_Oyun_Programlama.pdf -src/ders/sdl/SDL_ile_Oyun_Programlama.print.pdf -src/ders/sdl/SDL_ile_Oyun_Programlama_kod_ornekleri -src/ders/sdl/SDL_ile_Oyun_Programlama_kod_ornekleri.zip diff --git a/ddili/BENIOKU b/ddili/BENIOKU deleted file mode 100644 index 6585f7a..0000000 --- a/ddili/BENIOKU +++ /dev/null @@ -1,108 +0,0 @@ -(Bu dosyanın İngilizcesi: README) - -Bu proje ddili.org sitesini oluşturur. - -Gereken araçlar: - - dmd: dlang.org'dan edinebilirsiniz. En son 2.064 sürümü kullanılmıştır ama - herhalde başka sürümler de çalışır - - make: Her Linux dağıtımında bulunan GNU make. En son 3.81 kullanılmıştır ama - herhalde başka sürümler de çalışır - - prince: ('test' hedefi için gerekmez.) html'den pdf'e dönüştüren bir araç: - http://princexml.com. Fontlar için ayrıca kurulum gerekmez. - -Oluşturmak için: - - make -C src - -Bilinen sorunlar: - -- Eğer make "unexpected operator" hataları veriyorsa; sisteminizde /bin/sh, - /bin/dash'e bağlı olduğu için olabilir. Bunun gerçekten böyle olup - olmadığını anlamak için: - - ll /bin/sh - - Eğer satırın sonunda gerçekten dash varsa, - - ... /bin/sh -> dash - - onu bash olarak değiştirmeniz gerekir: - - sudo rm /bin/sh - sudo ln -s /bin/bash /bin/sh - -- 'test' hedefini oluşturduğunuzda ve sayfaları yerel olarak Firefox içinden - açtığınızda doğru fontları görmüyorsanız Firefox'un about:config - sayfasındaki "security.fileuri.strict_origin_policy" ayarına false değerini - vermeniz gerekiyor olabilir. - -Oluşanlar: - - public_html: sitenin bütün dosyalarını içeren dizin - - public_html.tar.gz: yukarıdaki dizinin sıkıştırılmış hali - -Tarayıcınızda yerel olarak açmaya elverişli dosyalar oluşturmak için: - - make -C src test - -Oluşanlar: - - public_html_test: sitenin yerel olarak açmaya biraz daha elverişli olan - sayfaları - -Tek pdf dosyasının oluşturulması: - - Her kitabın iki çeşidi var: Renklisi (*.pdf) ve kağıt baskıya uygun olan - siyah beyazı (*.print.pdf). Bunları ayrı ayrı oluşturabilirsiniz. Alışılmışın - tersine, bu dosyalar kaynak dosyaların bulunduğu klasörde belirirler: - - make -C src ders/d.en/Programming_in_D.print.pdf - make -C src ders/d.en/Programming_in_D.pdf - make -C src ders/d/D_Programlama_Dili.print.pdf - make -C src ders/d/D_Programlama_Dili.pdf - vs. - -Ekitap sürümünün oluşturulması: - -1) 'ebook' hedefini oluşturun - - make -C src ebook - - O komut bütün ekitaplar için gereken bütün dosyaları içeren bir tar dosyası - oluşturur: - - ebook_transfer.tar.gz - -2) O tar dosyasını ekitapları oluşturmak istediğiniz ortamda açın: - - mkdir ebook_gen - cd ebook_gen - tar zxvf ebook_transfer.tar.gz - - 'ders' klasörü bütün ekitap kaynaklarını içerir. Örneğin, "D Programlama - Dili" için gereken HTML ve CSS dosyaları ders/d klasörü altındadır: - - cover_ebook.png - ebook.css - ebook_source.d.html - -3) ebook_source.d.html dosyasında içinde "/image/" geçen bağlantıları bulun ve - bütün bağlantıyı "../../image/" olarak değiştirin. Örneğin, - "/home/acehreli/personal/ddili/ddili/src/image/by-nc-sa.png" dizgisini - "../../image/by-nc-sa.png" ile değiştirin. - -4) ebook.css içindeki bütün URL'leri tam yol adlarıyla değiştirin. Örneğin, - aşağıdakini - - src: url("fonts/opensans/OpenSans-Regular.ttf") - - aşağıdaki ile değiştirin - - src: url("/fonts/klasörünün/tam/yolu/opensans/OpenSans-Regular.ttf") - -5) CSS ve HTML dosyalarını istediğiniz ekitap programı (örneğin, calibre) ile - kullanarak ekitabı oluşturun. diff --git a/ddili/README b/ddili/README deleted file mode 100644 index 509f0ff..0000000 --- a/ddili/README +++ /dev/null @@ -1,91 +0,0 @@ -(Bu dosyanın Türkçesi: BENIOKU) - -This project produces the site ddili.org. - -Required tools: - - dmd: Can be obtained at dlang.org. The earliest required version is 2.067. - - make: GNU make that comes with most Linux distributions. Last used version - was 3.81 but it is likely that other versions will work as well. - - prince: (Not needed for the 'test' target.) A tool that converts from html - to pdf: http://princexml.com. - -To generate the site: - - make -C src - -Issues: - -- If you receive "unexpected operator" errors from make, it may be because - /bin/sh is linked to /bin/dash. To see whether that is the case: - - ll /bin/sh - - If you see 'dash' at the end of the line, - - ... /bin/sh -> dash - - you must change it to 'bash': - - sudo rm /bin/sh - sudo ln -s /bin/bash /bin/sh - -- If you don't see the right fonts in Firefox with the 'test' target during - development, you may have to set "security.fileuri.strict_origin_policy" to - false in the about:config page. - -The outputs of the make process: - - public_html: The directory that contains all of the files of the site. - - public_html.tar.gz: The tarred and zipped version of that directory. - -To produce files that are somewhat suitable to open in a browser locally: - - make -C src test - -The test output: - - public_html_test: Files that are somewhat more suitable to open locally. - -Creating a single pdf: - - There are two copies of each book: The color version (*.pdf) and the - black-and-white print version (*.print.pdf). You can build these targets - individually. Unconventionally, they are built inside their respective - source directories: - - make -C src ders/d.en/Programming_in_D.print.pdf - make -C src ders/d.en/Programming_in_D.pdf - make -C src ders/d/D_Programlama_Dili.print.pdf - make -C src ders/d/D_Programlama_Dili.pdf - etc. - -Creating the ebook version: - -1) Make the 'ebook' target - - make -C src ebook - -That will produce a tarball that includes all the files for ebook creation: - - ebook_transfer.tar.gz - -2) Untar the files where you want to generate the ebooks: - - mkdir ebook_gen - cd ebook_gen - tar zxvf ebook_transfer.tar.gz - - The 'ders' directory contains several ebook sources inside separate - directories. For example, the complete HTML file and the ebook CSS for - "Programming in D" is under ders/d.en: - - cover_ebook.png - ebook.css - ebook_source.d.en.html - -3) One of the untarred files is README.ebook. Please continue with the -instructions that are in that file. diff --git a/ddili/calibre_config_dir/conversion/azw3_output.py b/ddili/calibre_config_dir/conversion/azw3_output.py deleted file mode 100644 index 010f1cd..0000000 --- a/ddili/calibre_config_dir/conversion/azw3_output.py +++ /dev/null @@ -1,8 +0,0 @@ -{ - 'prefer_author_sort' : False, - 'no_inline_toc' : False, - 'dont_compress' : False, - 'share_not_sync' : False, - 'mobi_toc_at_start' : False, - 'toc_title' : None, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/comic_input.py b/ddili/calibre_config_dir/conversion/comic_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/debug.py b/ddili/calibre_config_dir/conversion/debug.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/docx_input.py b/ddili/calibre_config_dir/conversion/docx_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/docx_output.py b/ddili/calibre_config_dir/conversion/docx_output.py deleted file mode 100644 index 263a3c4..0000000 --- a/ddili/calibre_config_dir/conversion/docx_output.py +++ /dev/null @@ -1,6 +0,0 @@ -{ - 'docx_custom_page_size' : None, - 'docx_page_size' : u'letter', - 'docx_no_cover' : False, - 'docx_no_toc' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/epub_output.py b/ddili/calibre_config_dir/conversion/epub_output.py deleted file mode 100644 index a867804..0000000 --- a/ddili/calibre_config_dir/conversion/epub_output.py +++ /dev/null @@ -1,11 +0,0 @@ -{ - 'epub_inline_toc' : False, - 'dont_split_on_page_breaks' : True, - 'flow_size' : 260, - 'no_default_epub_cover' : True, - 'epub_flatten' : False, - 'no_svg_cover' : True, - 'toc_title' : None, - 'epub_toc_at_end' : True, - 'preserve_cover_aspect_ratio' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/fb2_input.py b/ddili/calibre_config_dir/conversion/fb2_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/fb2_output.py b/ddili/calibre_config_dir/conversion/fb2_output.py deleted file mode 100644 index f1d11a0..0000000 --- a/ddili/calibre_config_dir/conversion/fb2_output.py +++ /dev/null @@ -1,4 +0,0 @@ -{ - 'sectionize' : u'files', - 'fb2_genre' : u'antique', -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/heuristics.py b/ddili/calibre_config_dir/conversion/heuristics.py deleted file mode 100644 index e5ff0fe..0000000 --- a/ddili/calibre_config_dir/conversion/heuristics.py +++ /dev/null @@ -1,13 +0,0 @@ -{ - 'fix_indents' : True, - 'replace_scene_breaks' : u'', - 'renumber_headings' : True, - 'dehyphenate' : True, - 'html_unwrap_factor' : 0.4, - 'unwrap_lines' : True, - 'format_scene_breaks' : True, - 'markup_chapter_headings' : True, - 'italicize_common_cases' : True, - 'enable_heuristics' : False, - 'delete_blank_paragraphs' : True, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/htmlz_output.py b/ddili/calibre_config_dir/conversion/htmlz_output.py deleted file mode 100644 index 102d4d1..0000000 --- a/ddili/calibre_config_dir/conversion/htmlz_output.py +++ /dev/null @@ -1,5 +0,0 @@ -{ - 'htmlz_title_filename' : False, - 'htmlz_class_style' : u'external', - 'htmlz_css_type' : u'class', -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/look_and_feel.py b/ddili/calibre_config_dir/conversion/look_and_feel.py deleted file mode 100644 index 0f5e497..0000000 --- a/ddili/calibre_config_dir/conversion/look_and_feel.py +++ /dev/null @@ -1,24 +0,0 @@ -{ - 'unsmarten_punctuation' : False, - 'extra_css' : u'/home/ali/ebook_gen/ders/d.en/ebook.css', - 'input_encoding' : None, - 'insert_blank_line' : False, - 'linearize_tables' : False, - 'smarten_punctuation' : False, - 'change_justification' : u'left', - 'minimum_line_height' : 120.0, - 'embed_font_family' : None, - 'filter_css' : u'', - 'insert_blank_line_size' : 0.5, - 'expand_css' : False, - 'line_height' : 0.0, - 'font_size_mapping' : None, - 'subset_embedded_fonts' : False, - 'asciiize' : False, - 'keep_ligatures' : False, - 'base_font_size' : 0.0, - 'remove_paragraph_spacing' : False, - 'remove_paragraph_spacing_indent_size' : 1.5, - 'disable_font_rescaling' : False, - 'embed_all_fonts' : True, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/lrf_output.py b/ddili/calibre_config_dir/conversion/lrf_output.py deleted file mode 100644 index 040504b..0000000 --- a/ddili/calibre_config_dir/conversion/lrf_output.py +++ /dev/null @@ -1,13 +0,0 @@ -{ - 'header_format' : u'%t by %a', - 'sans_family' : None, - 'wordspace' : 2.5, - 'serif_family' : None, - 'mono_family' : None, - 'render_tables_as_images' : False, - 'minimum_indent' : 0.0, - 'header_separation' : 0.0, - 'text_size_multiplier_for_rendered_tables' : 1.0, - 'header' : False, - 'autorotation' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/metadata.py b/ddili/calibre_config_dir/conversion/metadata.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/mobi_output.py b/ddili/calibre_config_dir/conversion/mobi_output.py deleted file mode 100644 index 43866c3..0000000 --- a/ddili/calibre_config_dir/conversion/mobi_output.py +++ /dev/null @@ -1,12 +0,0 @@ -{ - 'prefer_author_sort' : False, - 'personal_doc' : u'[PDOC]', - 'dont_compress' : False, - 'mobi_keep_original_images' : False, - 'no_inline_toc' : False, - 'share_not_sync' : False, - 'mobi_toc_at_start' : False, - 'mobi_file_type' : u'old', - 'toc_title' : None, - 'mobi_ignore_margins' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/page_setup.py b/ddili/calibre_config_dir/conversion/page_setup.py deleted file mode 100644 index 6078184..0000000 --- a/ddili/calibre_config_dir/conversion/page_setup.py +++ /dev/null @@ -1,8 +0,0 @@ -{ - 'margin_bottom' : 5.0, - 'input_profile' : 'default', - 'margin_top' : 5.0, - 'output_profile' : 'default', - 'margin_right' : 5.0, - 'margin_left' : 5.0, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/pdb_output.py b/ddili/calibre_config_dir/conversion/pdb_output.py deleted file mode 100644 index 04cbd33..0000000 --- a/ddili/calibre_config_dir/conversion/pdb_output.py +++ /dev/null @@ -1,5 +0,0 @@ -{ - 'inline_toc' : False, - 'format' : u'doc', - 'pdb_output_encoding' : u'cp1252', -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/pdf_input.py b/ddili/calibre_config_dir/conversion/pdf_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/pdf_output.py b/ddili/calibre_config_dir/conversion/pdf_output.py deleted file mode 100644 index e352b82..0000000 --- a/ddili/calibre_config_dir/conversion/pdf_output.py +++ /dev/null @@ -1,18 +0,0 @@ -{ - 'pdf_serif_family' : u'Liberation Serif', - 'pdf_header_template' : None, - 'pdf_sans_family' : u'Liberation Sans', - 'pdf_add_toc' : False, - 'pdf_standard_font' : u'serif', - 'pdf_page_numbers' : False, - 'custom_size' : u'7x11', - 'pdf_mono_font_size' : 8, - 'pdf_default_font_size' : 8, - 'pdf_footer_template' : None, - 'override_profile_size' : True, - 'paper_size' : u'letter', - 'preserve_cover_aspect_ratio' : False, - 'toc_title' : None, - 'pdf_mono_family' : u'Liberation Mono', - 'unit' : u'inch', -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/pmlz_output.py b/ddili/calibre_config_dir/conversion/pmlz_output.py deleted file mode 100644 index 55cefb3..0000000 --- a/ddili/calibre_config_dir/conversion/pmlz_output.py +++ /dev/null @@ -1,5 +0,0 @@ -{ - 'inline_toc' : False, - 'pml_output_encoding' : u'cp1252', - 'full_image_depth' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/rb_output.py b/ddili/calibre_config_dir/conversion/rb_output.py deleted file mode 100644 index 12f02eb..0000000 --- a/ddili/calibre_config_dir/conversion/rb_output.py +++ /dev/null @@ -1,3 +0,0 @@ -{ - 'inline_toc' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/rtf_input.py b/ddili/calibre_config_dir/conversion/rtf_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/search_and_replace.py b/ddili/calibre_config_dir/conversion/search_and_replace.py deleted file mode 100644 index 2f491be..0000000 --- a/ddili/calibre_config_dir/conversion/search_and_replace.py +++ /dev/null @@ -1,9 +0,0 @@ -{ - 'sr3_search' : None, - 'sr3_replace' : None, - 'sr2_search' : None, - 'sr1_search' : None, - 'search_replace' : '[]', - 'sr1_replace' : None, - 'sr2_replace' : None, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/snb_output.py b/ddili/calibre_config_dir/conversion/snb_output.py deleted file mode 100644 index 8239125..0000000 --- a/ddili/calibre_config_dir/conversion/snb_output.py +++ /dev/null @@ -1,6 +0,0 @@ -{ - 'snb_full_screen' : False, - 'snb_hide_chapter_name' : False, - 'snb_dont_indent_first_line' : False, - 'snb_insert_empty_line' : False, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/structure_detection.py b/ddili/calibre_config_dir/conversion/structure_detection.py deleted file mode 100644 index 37efc7e..0000000 --- a/ddili/calibre_config_dir/conversion/structure_detection.py +++ /dev/null @@ -1,9 +0,0 @@ -{ - 'chapter' : u"//*[((name()='h1' or name()='h2' or name()='h4') and re:test(., '\\s*((chapter|book|section|part)\\s+)|((prolog|prologue|epilogue)(\\s+|$))', 'i')) or @class = 'chapter']", - 'remove_first_image' : False, - 'insert_metadata' : False, - 'chapter_mark' : u'pagebreak', - 'remove_fake_margins' : True, - 'start_reading_at' : None, - 'page_breaks_before' : u"//*[name()='h1' or name()='h2' or name()='h4']", -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/toc.py b/ddili/calibre_config_dir/conversion/toc.py deleted file mode 100644 index 97d2cc9..0000000 --- a/ddili/calibre_config_dir/conversion/toc.py +++ /dev/null @@ -1,11 +0,0 @@ -{ - 'level1_toc' : u'//h:h4', - 'level3_toc' : None, - 'max_toc_links' : 50, - 'toc_threshold' : 6, - 'level2_toc' : u'//h:h5', - 'duplicate_links_in_toc' : False, - 'use_auto_toc' : False, - 'no_chapters_in_toc' : False, - 'toc_filter' : None, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/txt_input.py b/ddili/calibre_config_dir/conversion/txt_input.py deleted file mode 100644 index e69de29..0000000 diff --git a/ddili/calibre_config_dir/conversion/txt_output.py b/ddili/calibre_config_dir/conversion/txt_output.py deleted file mode 100644 index b0420aa..0000000 --- a/ddili/calibre_config_dir/conversion/txt_output.py +++ /dev/null @@ -1,11 +0,0 @@ -{ - 'txt_output_encoding' : u'utf-8', - 'keep_color' : False, - 'keep_image_references' : False, - 'inline_toc' : False, - 'keep_links' : False, - 'txt_output_formatting' : u'plain', - 'force_max_line_length' : False, - 'newline' : u'system', - 'max_line_length' : 0, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/conversion/txtz_output.py b/ddili/calibre_config_dir/conversion/txtz_output.py deleted file mode 100644 index b0420aa..0000000 --- a/ddili/calibre_config_dir/conversion/txtz_output.py +++ /dev/null @@ -1,11 +0,0 @@ -{ - 'txt_output_encoding' : u'utf-8', - 'keep_color' : False, - 'keep_image_references' : False, - 'inline_toc' : False, - 'keep_links' : False, - 'txt_output_formatting' : u'plain', - 'force_max_line_length' : False, - 'newline' : u'system', - 'max_line_length' : 0, -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/dynamic.pickle b/ddili/calibre_config_dir/dynamic.pickle deleted file mode 100644 index ef646c6a89087e767de2c3f82af731e43b2dd7f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcma))&u-K(5XP5MXm=@px7%(Dg&a66mxLC0g7O4NmiAIv6MK?a>)4st+0`Odf&-{| zYn(Q#s+B5ni4}Q%^UXK@er*W>TBS9{jaS;m!kSFwqwi}2r4r{ifAr|nZ`zv8r-Q+u zsU&!eyp5JvJ7rCD7JvB`f2PMRJ6NF>wnV;CKcSX<1&-H^q3tU+4HTr{Fw$0l?>MPI z+nk=vk3!p|2wW0IN&0zsHx<-tdit>J*C4yovay5Z^+Zy+#is4<{P0fOu10cic~?Y3 zHWme#93x?aj}0`MpWJ~kwRXXCG>Hsm6B(nSXKWv&jJg}wbK$6I??2e*4|+L03tnEd zY|q)+^FldqgZGyg^lHY2UX|!Ploh>h|I(YH`;L))>{rAkYV^omg+gUY@XXdFcn&!z zL;Ee8gpGH*cAft;6t0axNfW_&+9>Un;e6!KkLcjy&ks6elY0*-aj68-lxJ~1$8B>-Z)a>fqzLI.+) - (?P[^_]+)' - -# isbndb com key -# Access key for isbndb.com -isbndb_com_key = '' - -# network timeout -# Default timeout for network operations (seconds) -network_timeout = 5 - -# library path -# Path to directory in which your library of books is stored -library_path = u'/home/ali/calibre_library' - -# language -# The language in which to display the user interface -language = 'en' - -# output format -# The default output format for ebook conversions. -output_format = 'epub' - -# input format order -# Ordered list of formats to prefer for input. -input_format_order = cPickle.loads('\x80\x02]q\x01(U\x04EPUBq\x02U\x04AZW3q\x03U\x04MOBIq\x04U\x03LITq\x05U\x03PRCq\x06U\x03FB2q\x07U\x04HTMLq\x08U\x03HTMq\tU\x04XHTMq\nU\x05SHTMLq\x0bU\x05XHTMLq\x0cU\x03ZIPq\rU\x03ODTq\x0eU\x03RTFq\x0fU\x03PDFq\x10U\x03TXTq\x11e.') - -# read file metadata -# Read metadata from files -read_file_metadata = True - -# worker process priority -# The priority of worker processes. A higher priority means they run faster and consume more resources. Most tasks like conversion/news download/adding books/etc. are affected by this setting. -worker_process_priority = 'normal' - -# swap author names -# Swap author first and last names when reading metadata -swap_author_names = False - -# add formats to existing -# Add new formats to existing book records -add_formats_to_existing = False - -# check for dupes on ctl -# Check for duplicates when copying to another library -check_for_dupes_on_ctl = False - -# installation uuid -# Installation UUID -installation_uuid = '7dcf582b-aabe-4a6c-a08a-bc595d7b6163' - -# new book tags -# Tags to apply to books added to the library -new_book_tags = cPickle.loads('\x80\x02]q\x01.') - -# mark new books -# Mark newly added books. The mark is a temporary mark that is automatically removed when calibre is restarted. -mark_new_books = False - -# saved searches -# List of named saved searches -saved_searches = cPickle.loads('\x80\x02}q\x01.') - -# user categories -# User-created tag browser categories -user_categories = cPickle.loads('\x80\x02}q\x01.') - -# manage device metadata -# How and when calibre updates metadata on the device. -manage_device_metadata = 'manual' - -# limit search columns -# When searching for text without using lookup prefixes, as for example, Red instead of title:Red, limit the columns searched to those named below. -limit_search_columns = False - -# limit search columns to -# Choose columns to be searched when not using prefixes, as for example, when searching for Red instead of title:Red. Enter a list of search/lookup names separated by commas. Only takes effect if you set the option to limit search columns above. -limit_search_columns_to = cPickle.loads('\x80\x02]q\x01(U\x05titleq\x02U\x07authorsq\x03U\x04tagsq\x04U\x06seriesq\x05U\tpublisherq\x06e.') - -# use primary find in search -# Characters typed in the search box will match their accented versions, based on the language you have chosen for the calibre interface. For example, in English, searching for n will match ñ and n, but if your language is Spanish it will only match n. Note that this is much slower than a simple search on very large libraries. -use_primary_find_in_search = True - -# migrated -# For Internal use. Don't modify. -migrated = False - - - diff --git a/ddili/calibre_config_dir/gui.json b/ddili/calibre_config_dir/gui.json deleted file mode 100644 index f2cdfd2..0000000 --- a/ddili/calibre_config_dir/gui.json +++ /dev/null @@ -1,261 +0,0 @@ -{ - "custom_colors_for_color_dialog": [ - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ] - ], - "config_widget_dialog_geometry_Sharing_Metadata download": { - "__class__": "bytearray", - "__value__": "AdnQywABAAAAAADzAAAAPAAAA+IAAAJ6AAAA9AAAAFAAAAPhAAACdQAAAAAAAA==" - }, - "toolbar_icon_size": "small", - "card_a_view books view state": { - "hidden_columns": [], - "column_positions": { - "authors": 2, - "timestamp": 3, - "inlibrary": 0, - "collections": 5, - "size": 4, - "title": 1 - }, - "last_modified_injected": true, - "sort_history": [ - [ - "timestamp", - false - ] - ], - "column_alignment": { - "timestamp": "center", - "size": "center" - }, - "languages_injected": true, - "column_sizes": { - "authors": 97, - "timestamp": 74, - "inlibrary": 99, - "collections": 108, - "size": 58, - "title": 60 - } - }, - "library_usage_stats": { - "/home/ali/calibre_library": 10 - }, - "cover_grid_texture": null, - "show_emblems": false, - "metasingle_window_geometry3": { - "__class__": "bytearray", - "__value__": "AdnQywABAAAAAAB7AAAAAAAABFoAAAK2AAAAfAAAABQAAARZAAACsQAAAAAAAA==" - }, - "card_b_view books view state": { - "hidden_columns": [], - "column_positions": { - "authors": 2, - "timestamp": 3, - "inlibrary": 0, - "collections": 5, - "size": 4, - "title": 1 - }, - "last_modified_injected": true, - "sort_history": [ - [ - "timestamp", - false - ] - ], - "column_alignment": { - "timestamp": "center", - "size": "center" - }, - "languages_injected": true, - "column_sizes": { - "authors": 97, - "timestamp": 74, - "inlibrary": 99, - "collections": 108, - "size": 58, - "title": 60 - } - }, - "jobs view column layout3": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQAAAAFAQEBAAAAAAAAAAAAAAAAAGT/////AAAAhAAAAAAAAAAFAAAAsgAAAAEAAAAAAAAAZgAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAAZAAAAAEAAAAAAAAD6A==" - }, - "replace_scene_breaks_history": [ - "", - "
", - "\u2217 \u2217 \u2217", - "\u2022 \u2022 \u2022", - "\u2666 \u2666 \u2666", - "\u2020 \u2020", - "\u2021 \u2021 \u2021", - "\u221e \u221e \u221e", - "\u00a4 \u00a4 \u00a4" - ], - "book_details_splitter_horizontal_state": [ - true, - 200 - ], - "cover_browser_splitter_vertical_state": [ - false, - 300 - ], - "convert_single_dialog_geom": { - "__class__": "bytearray", - "__value__": "AdnQywACAAAAAABKAAAAAAAABI8AAALSAAAASwAAABQAAASOAAACzQAAAAAAAAAABN4=" - }, - "preferences dialog geometry": { - "__class__": "bytearray", - "__value__": "AdnQywACAAAAAAAcAAAAAAAAA78AAALTAAAAHQAAABQAAAO+AAACzgAAAAAAAAAABN4=" - }, - "cover_grid_color": [ - 80, - 80, - 80 - ], - "memory_view books view state": { - "hidden_columns": [], - "column_positions": { - "authors": 2, - "timestamp": 3, - "inlibrary": 0, - "collections": 5, - "size": 4, - "title": 1 - }, - "last_modified_injected": true, - "sort_history": [ - [ - "timestamp", - false - ] - ], - "column_alignment": { - "timestamp": "center", - "size": "center" - }, - "languages_injected": true, - "column_sizes": { - "authors": 97, - "timestamp": 74, - "inlibrary": 99, - "collections": 108, - "size": 58, - "title": 60 - } - }, - "preferences_window_geometry": { - "__class__": "bytearray", - "__value__": "AdnQywABAAD////f////5gAABAAAAAJ3////4P////oAAAP/AAACcgAAAAAAAA==" - }, - "grid view visible": false, - "jobs_dialog_geometry": { - "__class__": "bytearray", - "__value__": "AdnQywACAAAAAACwAAAAPgAAAyoAAAJ0AAAAsQAAAFIAAAMpAAACbwAAAAAAAAAABN4=" - }, - "duplicates-question-dialog-geometry": { - "__class__": "bytearray", - "__value__": "AdnQywACAAAAAADoAAAAigAAAwcAAAIdAAAA6QAAAJ4AAAMGAAACGAAAAAAAAAAABN4=" - }, - "tag_browser_splitter_horizontal_state": [ - true, - 200 - ], - "quick_start_guide_added": true -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/gui.py b/ddili/calibre_config_dir/gui.py deleted file mode 100644 index ebf16a1..0000000 --- a/ddili/calibre_config_dir/gui.py +++ /dev/null @@ -1,178 +0,0 @@ -# preferences for the calibre GUI - -### Begin group: DEFAULT - -# send to storage card by default -# Send file to storage card instead of main memory by default -send_to_storage_card_by_default = False - -# confirm delete -# Confirm before deleting -confirm_delete = False - -# main window geometry -# Main window geometry -main_window_geometry = cPickle.loads('\x80\x02csip\n_unpickle_type\nq\x01U\x0cPyQt5.QtCoreq\x02U\nQByteArrayU2\x01\xd9\xd0\xcb\x00\x02\x00\x00\x00\x00\x00=\xff\xff\xff\xec\x00\x00\x04\x10\x00\x00\x02\x8b\x00\x00\x00>\x00\x00\x00\x00\x00\x00\x04\x0f\x00\x00\x02\x86\x00\x00\x00\x00\x00\x00\x00\x00\x04\xde\x85\x87Rq\x03.') - -# new version notification -# Notify when a new version is available -new_version_notification = True - -# use roman numerals for series number -# Use Roman numerals for series number -use_roman_numerals_for_series_number = True - -# sort tags by -# Sort tags list by name, popularity, or rating -sort_tags_by = 'name' - -# match tags type -# Match tags by any or all. -match_tags_type = 'any' - -# cover flow queue length -# Number of covers to show in the cover browsing mode -cover_flow_queue_length = 6 - -# LRF conversion defaults -# Defaults for conversion to LRF -LRF_conversion_defaults = cPickle.loads('\x80\x02]q\x01.') - -# LRF ebook viewer options -# Options for the LRF ebook viewer -LRF_ebook_viewer_options = None - -# internally viewed formats -# Formats that are viewed using the internal viewer -internally_viewed_formats = cPickle.loads('\x80\x02]q\x01(U\x03LRFq\x02U\x04EPUBq\x03U\x03LITq\x04U\x04MOBIq\x05U\x03PRCq\x06U\x04POBIq\x07U\x03AZWq\x08U\x04AZW3q\tU\x04HTMLq\nU\x03FB2q\x0bU\x03PDBq\x0cU\x02RBq\rU\x03SNBq\x0eU\x05HTMLZq\x0fU\x05KEPUBq\x10e.') - -# column map -# Columns to be displayed in the book list -column_map = cPickle.loads('\x80\x02]q\x01(U\x05titleq\x02U\x08ondeviceq\x03U\x07authorsq\x04U\x04sizeq\x05U\ttimestampq\x06U\x06ratingq\x07U\tpublisherq\x08U\x04tagsq\tU\x06seriesq\nU\x07pubdateq\x0be.') - -# autolaunch server -# Automatically launch content server on application startup -autolaunch_server = False - -# oldest news -# Oldest news kept in database -oldest_news = 60 - -# systray icon -# Show system tray icon -systray_icon = False - -# upload news to device -# Upload downloaded news to device -upload_news_to_device = True - -# delete news from library on upload -# Delete news books from library after uploading to device -delete_news_from_library_on_upload = False - -# separate cover flow -# Show the cover flow in a separate window instead of in the main calibre window -separate_cover_flow = False - -# disable tray notification -# Disable notifications from the system tray icon -disable_tray_notification = False - -# default send to device action -# Default action to perform when send to device button is clicked -default_send_to_device_action = 'DeviceAction:main::False:False' - -# asked library thing password -# Asked library thing password at least once. -asked_library_thing_password = False - -# search as you type -# Start searching as you type. If this is disabled then search will only take place when the Enter or Return key is pressed. -search_as_you_type = False - -# highlight search matches -# When searching, show all books with search results highlighted instead of showing only the matches. You can use the N or F3 keys to go to the next match. -highlight_search_matches = False - -# save to disk template history -# Previously used Save to Disk templates -save_to_disk_template_history = cPickle.loads('\x80\x02]q\x01.') - -# send to device template history -# Previously used Send to Device templates -send_to_device_template_history = cPickle.loads('\x80\x02]q\x01.') - -# main search history -# Search history for the main GUI -main_search_history = cPickle.loads('\x80\x02]q\x01.') - -# viewer search history -# Search history for the ebook viewer -viewer_search_history = cPickle.loads('\x80\x02]q\x01(X\x06\x00\x00\x00fibersq\x02X\x13\x00\x00\x00operator precedenceq\x03X\x05\x00\x00\x00dutchq\x04X\x02\x00\x00\x00%oq\x05X\x11\x00\x00\x00compilation errorq\x06X\x05\x00\x00\x00errorq\x07X\x02\x00\x00\x00||q\x08X\x06\x00\x00\x00ifloatq\tX\x05\x00\x00\x00iflotq\nX\x0c\x00\x00\x00isInputRangeq\x0bX\x06\x00\x00\x00rangesq\x0cX\x0c\x00\x00\x00Assume a Filq\rX\x13\x00\x00\x00logical expressionsq\x0eX\x0f\x00\x00\x00following tableq\x0fe.') - -# viewer toc search history -# Search history for the ToC in the ebook viewer -viewer_toc_search_history = cPickle.loads('\x80\x02]q\x01(X\x03\x00\x00\x00ackq\x02X\x05\x00\x00\x00erginq\x03X\x08\x00\x00\x00contentsq\x04e.') - -# lrf viewer search history -# Search history for the LRF viewer -lrf_viewer_search_history = cPickle.loads('\x80\x02]q\x01.') - -# scheduler search history -# Search history for the recipe scheduler -scheduler_search_history = cPickle.loads('\x80\x02]q\x01.') - -# plugin search history -# Search history for the plugin preferences -plugin_search_history = cPickle.loads('\x80\x02]q\x01.') - -# shortcuts search history -# Search history for the keyboard preferences -shortcuts_search_history = cPickle.loads('\x80\x02]q\x01.') - -# jobs search history -# Search history for the tweaks preferences -jobs_search_history = cPickle.loads('\x80\x02]q\x01.') - -# tweaks search history -# Search history for tweaks -tweaks_search_history = cPickle.loads('\x80\x02]q\x01.') - -# worker limit -# Maximum number of simultaneous conversion/news download jobs. This number is twice the actual value for historical reasons. -worker_limit = 6 - -# get social metadata -# Download social metadata (tags/rating/etc.) -get_social_metadata = True - -# overwrite author title metadata -# Overwrite author and title with new metadata -overwrite_author_title_metadata = True - -# auto download cover -# Automatically download the cover, if available -auto_download_cover = False - -# enforce cpu limit -# Limit max simultaneous jobs to number of CPUs -enforce_cpu_limit = True - -# gui layout -# The layout of the user interface. Wide has the book details panel on the right and narrow has it at the bottom. -gui_layout = 'wide' - -# show avg rating -# Show the average rating per item indication in the tag browser -show_avg_rating = True - -# disable animations -# Disable UI animations -disable_animations = False - -# tag browser hidden categories -# tag browser categories not to display -tag_browser_hidden_categories = cPickle.loads('\x80\x02c__builtin__\nset\nq\x01]\x85Rq\x02.') - - - diff --git a/ddili/calibre_config_dir/history.plist b/ddili/calibre_config_dir/history.plist deleted file mode 100644 index b1f85f6..0000000 --- a/ddili/calibre_config_dir/history.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - lineedit_history_regex_edit_sr_search - - - lineedit_history_xpath_edit_opt_chapter - - //*[((name()='h1' or name()='h2' or name()='h4') and re:test(., '\s*((chapter|book|section|part)\s+)|((prolog|prologue|epilogue)(\s+|$))', 'i')) or @class = 'chapter'] - - lineedit_history_xpath_edit_opt_level1_toc - - //h:h4 - //h:*h4 - - lineedit_history_xpath_edit_opt_level2_toc - - - lineedit_history_xpath_edit_opt_page_breaks_before - - //*[name()='h1' or name()='h2' or name()='h4'] - - lineedit_history_xpath_edit_opt_start_reading_at - - - - diff --git a/ddili/calibre_config_dir/iterator.pickle b/ddili/calibre_config_dir/iterator.pickle deleted file mode 100644 index c7b26091c5fe0b4d5ffe278822f898e87d492af8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8223 zcmd5>X>Z#`5OveEb=dp98>Xk^)+CpgNF}K+#XZtE?xAK|R~0Ckwh2q5mXZ*paDe1v z|8-}Vl%V&m@E|yp8V^FRA9S~p zSLB)MV{)m2yGu(;ohUl)d-3tGtsX^vUxiPr=`GsyMqd0tZHC8TbU##m`eu0;jpMGb z>??jKR}8y)&gE4({fN?!IsthWUx#m++_gK7V?P|V2i~FIo_zhv$+eTUlNFTyRdv-= zQO$(EsIE!mGX7T(>qak-xBR2nhd6fQ=rH#B{UAJ)gHUeBtMYab4@dH?{a_SC0N*sL zi{G^>&)ih;{<&|E=kN`|h-&t1NDz(-fzgnj$Mwv zfY${9@+o&9U8G1^nPIO}W8HVgxtn_a-MCZ!rIU@NXt_#M(iKI!Ll^Op>Wds7G&lZku+~_!o~x@CwOmxQB_fw_3VIIDy(SzAM<*Tn2g)bf zq2%?*e?(;SWGp+i3mRF$F&sqkma2T9) z8jhAES;H;CFu36smI!RTMKImMfXJrX>9uKB%sL+=Ebm&pur1;4Qhx$A6F_JP zBw}9#<)TFgLs=7M>-nbXlpxPK40c(o&J{LOzUnlfG-Wk)8w44z7bMB%;wFIQ|GMxJ z(lvRS)Ka~;QG)DcbAJ}dP4SJH#=8q|kklb{LTrf%t^Rru0%|STfiUbF7gzhug37{ziEzOv#UO79mj&X~6ZU4QKQ&4qj-HJ; ztjoWZb19-BD1tEx6S98E*;Z*yT-Cg{L2o$wO2X_Bk5mZ>TYiCqw<}}60;sr*a-%GFLPulLCwUItPIBR}>=QB3}7UZMLNcyc~EA}w5n c|4kp-r;qw45AW5 "ln, fn" -# copy : copy author to author_sort without modification -# comma : use 'copy' if there is a ',' in the name, otherwise use 'invert' -# nocomma : "fn ln" -> "ln fn" (without the comma) -# When this tweak is changed, the author_sort values stored with each author -# must be recomputed by right-clicking on an author in the left-hand tags pane, -# selecting 'manage authors', and pressing 'Recalculate all author sort values'. -# The author name suffixes are words that are ignored when they occur at the -# end of an author name. The case of the suffix is ignored and trailing -# periods are automatically handled. The same is true for prefixes. -# The author name copy words are a set of words which if they occur in an -# author name cause the automatically generated author sort string to be -# identical to the author name. This means that the sort for a string like Acme -# Inc. will be Acme Inc. instead of Inc., Acme -author_sort_copy_method = 'comma' -author_name_suffixes = ('Jr', 'Sr', 'Inc', 'Ph.D', 'Phd', - 'MD', 'M.D', 'I', 'II', 'III', 'IV', - 'Junior', 'Senior') -author_name_prefixes = ('Mr', 'Mrs', 'Ms', 'Dr', 'Prof') -author_name_copywords = ('Corporation', 'Company', 'Co.', 'Agency', 'Council', - 'Committee', 'Inc.', 'Institute', 'Society', 'Club', 'Team') - -#: Splitting multiple author names -# By default, calibre splits a string containing multiple author names on -# ampersands and the words "and" and "with". You can customize the splitting -# by changing the regular expression below. Strings are split on whatever the -# specified regular expression matches, in addition to ampersands. -# Default: r'(?i),?\s+(and|with)\s+' -authors_split_regex = r'(?i),?\s+(and|with)\s+' - -#: Use author sort in Tag Browser -# Set which author field to display in the tags pane (the list of authors, -# series, publishers etc on the left hand side). The choices are author and -# author_sort. This tweak affects only what is displayed under the authors -# category in the tags pane and content server. Please note that if you set this -# to author_sort, it is very possible to see duplicate names in the list because -# although it is guaranteed that author names are unique, there is no such -# guarantee for author_sort values. Showing duplicates won't break anything, but -# it could lead to some confusion. When using 'author_sort', the tooltip will -# show the author's name. -# Examples: -# categories_use_field_for_author_name = 'author' -# categories_use_field_for_author_name = 'author_sort' -categories_use_field_for_author_name = 'author' - -#: Control partitioning of Tag Browser -# When partitioning the tags browser, the format of the subcategory label is -# controlled by a template: categories_collapsed_name_template if sorting by -# name, categories_collapsed_rating_template if sorting by average rating, and -# categories_collapsed_popularity_template if sorting by popularity. There are -# two variables available to the template: first and last. The variable 'first' -# is the initial item in the subcategory, and the variable 'last' is the final -# item in the subcategory. Both variables are 'objects'; they each have multiple -# values that are obtained by using a suffix. For example, first.name for an -# author category will be the name of the author. The sub-values available are: -# name: the printable name of the item -# count: the number of books that references this item -# avg_rating: the average rating of all the books referencing this item -# sort: the sort value. For authors, this is the author_sort for that author -# category: the category (e.g., authors, series) that the item is in. -# Note that the "r'" in front of the { is necessary if there are backslashes -# (\ characters) in the template. It doesn't hurt anything to leave it there -# even if there aren't any backslashes. -categories_collapsed_name_template = r'{first.sort:shorten(4,,0)} - {last.sort:shorten(4,,0)}' -categories_collapsed_rating_template = r'{first.avg_rating:4.2f:ifempty(0)} - {last.avg_rating:4.2f:ifempty(0)}' -categories_collapsed_popularity_template = r'{first.count:d} - {last.count:d}' - -#: Control order of categories in the tag browser -# Change the following dict to change the order that categories are displayed in -# the tag browser. Items are named using their lookup name, and will be sorted -# using the number supplied. The lookup name '*' stands for all names that -# otherwise do not appear. Two names with the same value will be sorted -# using the default order; the one used when the dict is empty. -# Example: tag_browser_category_order = {'series':1, 'tags':2, '*':3} -# resulting in the order series, tags, then everything else in default order. -tag_browser_category_order = {'*':1} - - -#: Specify columns to sort the booklist by on startup -# Provide a set of columns to be sorted on when calibre starts -# The argument is None if saved sort history is to be used -# otherwise it is a list of column,order pairs. Column is the -# lookup/search name, found using the tooltip for the column -# Order is 0 for ascending, 1 for descending -# For example, set it to [('authors',0),('title',0)] to sort by -# title within authors. -sort_columns_at_startup = None - -#: Control how dates are displayed -# Format to be used for publication date and the timestamp (date). -# A string controlling how the publication date is displayed in the GUI -# d the day as number without a leading zero (1 to 31) -# dd the day as number with a leading zero (01 to 31) -# ddd the abbreviated localized day name (e.g. 'Mon' to 'Sun'). -# dddd the long localized day name (e.g. 'Monday' to 'Sunday'). -# M the month as number without a leading zero (1-12) -# MM the month as number with a leading zero (01-12) -# MMM the abbreviated localized month name (e.g. 'Jan' to 'Dec'). -# MMMM the long localized month name (e.g. 'January' to 'December'). -# yy the year as two digit number (00-99) -# yyyy the year as four digit number -# h the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) ' -# hh the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) ' -# m the minutes without a leading 0 (0 to 59) ' -# mm the minutes with a leading 0 (00 to 59) ' -# s the seconds without a leading 0 (0 to 59) ' -# ss the seconds with a leading 0 (00 to 59) ' -# ap use a 12-hour clock instead of a 24-hour clock, with "ap" -# replaced by the localized string for am or pm ' -# AP use a 12-hour clock instead of a 24-hour clock, with "AP" -# replaced by the localized string for AM or PM ' -# iso the date with time and timezone. Must be the only format present -# For example, given the date of 9 Jan 2010, the following formats show -# MMM yyyy ==> Jan 2010 yyyy ==> 2010 dd MMM yyyy ==> 09 Jan 2010 -# MM/yyyy ==> 01/2010 d/M/yy ==> 9/1/10 yy ==> 10 -# publication default if not set: MMM yyyy -# timestamp default if not set: dd MMM yyyy -# last_modified_display_format if not set: dd MMM yyyy -gui_pubdate_display_format = 'MMM yyyy' -gui_timestamp_display_format = 'dd MMM yyyy' -gui_last_modified_display_format = 'dd MMM yyyy' - -#: Control sorting of titles and series in the library display -# Control title and series sorting in the library view. If set to -# 'library_order', the title sort field will be used instead of the title. -# Unless you have manually edited the title sort field, leading articles such as -# The and A will be ignored. If set to 'strictly_alphabetic', the titles will be -# sorted as-is (sort by title instead of title sort). For example, with -# library_order, The Client will sort under 'C'. With strictly_alphabetic, the -# book will sort under 'T'. -# This flag affects Calibre's library display. It has no effect on devices. In -# addition, titles for books added before changing the flag will retain their -# order until the title is edited. Double-clicking on a title and hitting return -# without changing anything is sufficient to change the sort. -title_series_sorting = 'library_order' - -#: Control formatting of title and series when used in templates -# Control how title and series names are formatted when saving to disk/sending -# to device. The behavior depends on the field being processed. If processing -# title, then if this tweak is set to 'library_order', the title will be -# replaced with title_sort. If it is set to 'strictly_alphabetic', then the -# title will not be changed. If processing series, then if set to -# 'library_order', articles such as 'The' and 'An' will be moved to the end. If -# set to 'strictly_alphabetic', the series will be sent without change. -# For example, if the tweak is set to library_order, "The Lord of the Rings" -# will become "Lord of the Rings, The". If the tweak is set to -# strictly_alphabetic, it would remain "The Lord of the Rings". Note that the -# formatter function raw_field will return the base value for title and -# series regardless of the setting of this tweak. -save_template_title_series_sorting = 'library_order' - -#: Set the list of words considered to be "articles" for sort strings -# Set the list of words that are to be considered 'articles' when computing the -# title sort strings. The articles differ by language. By default, calibre uses -# a combination of articles from English and whatever language the calibre user -# interface is set to. In addition, in some contexts where the book language is -# available, the language of the book is used. You can change the list of -# articles for a given language or add a new language by editing -# per_language_title_sort_articles. To tell calibre to use a language other -# than the user interface language, set, default_language_for_title_sort. For -# example, to use German, set it to 'deu'. A value of None means the user -# interface language is used. The setting title_sort_articles is ignored -# (present only for legacy reasons). -per_language_title_sort_articles = { - # English - 'eng' : (r'A\s+', r'The\s+', r'An\s+'), - - # Esperanto - 'epo': (r'La\s+', r"L'", 'L\xb4'), - - # Spanish - 'spa' : (r'El\s+', r'La\s+', r'Lo\s+', r'Los\s+', r'Las\s+', r'Un\s+', - r'Una\s+', r'Unos\s+', r'Unas\s+'), - # French - 'fra' : (r'Le\s+', r'La\s+', r"L'", u'L´', r'Les\s+', r'Un\s+', r'Une\s+', - r'Des\s+', r'De\s+La\s+', r'De\s+', r"D'", u'D´'), - # Italian - 'ita': ('Lo\\s+', 'Il\\s+', "L'", 'L\xb4', 'La\\s+', 'Gli\\s+', - 'I\\s+', 'Le\\s+', 'Uno\\s+', 'Un\\s+', 'Una\\s+', "Un'", - 'Un\xb4', 'Dei\\s+', 'Degli\\s+', 'Delle\\s+', 'Del\\s+', - 'Della\\s+', 'Dello\\s+', "Dell'", 'Dell\xb4'), - - # Portuguese - 'por' : (r'A\s+', r'O\s+', r'Os\s+', r'As\s+', r'Um\s+', r'Uns\s+', - r'Uma\s+', r'Umas\s+', ), - # Romanian - 'ron' : (r'Un\s+', r'O\s+', r'Nişte\s+', ), - # German - 'deu' : (r'Der\s+', r'Die\s+', r'Das\s+', r'Den\s+', r'Ein\s+', - r'Eine\s+', r'Einen\s+', r'Dem\s+', r'Des\s+', r'Einem\s+', - r'Eines\s+'), - # Dutch - 'nld' : (r'De\s+', r'Het\s+', r'Een\s+', r"'n\s+", r"'s\s+", r'Ene\s+', - r'Ener\s+', r'Enes\s+', r'Den\s+', r'Der\s+', r'Des\s+', - r"'t\s+"), - # Swedish - 'swe' : (r'En\s+', r'Ett\s+', r'Det\s+', r'Den\s+', r'De\s+', ), - # Turkish - 'tur' : (r'Bir\s+', ), - # Afrikaans - 'afr' : (r"'n\s+", r'Die\s+', ), - # Greek - 'ell' : (r'O\s+', r'I\s+', r'To\s+', r'Ta\s+', r'Tus\s+', r'Tis\s+', - r"'Enas\s+", r"'Mia\s+", r"'Ena\s+", r"'Enan\s+", ), - # Hungarian - 'hun' : (r'A\s+', 'Az\s+', 'Egy\s+',), -} -default_language_for_title_sort = None -title_sort_articles=r'^(A|The|An)\s+' - -#: Specify a folder calibre should connect to at startup -# Specify a folder that calibre should connect to at startup using -# connect_to_folder. This must be a full path to the folder. If the folder does -# not exist when calibre starts, it is ignored. If there are '\' characters in -# the path (such as in Windows paths), you must double them. -# Examples: -# auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib' -# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library' -auto_connect_to_folder = '' - -#: Specify renaming rules for SONY collections -# Specify renaming rules for sony collections. This tweak is only applicable if -# metadata management is set to automatic. Collections on Sonys are named -# depending upon whether the field is standard or custom. A collection derived -# from a standard field is named for the value in that field. For example, if -# the standard 'series' column contains the value 'Darkover', then the -# collection name is 'Darkover'. A collection derived from a custom field will -# have the name of the field added to the value. For example, if a custom series -# column named 'My Series' contains the name 'Darkover', then the collection -# will by default be named 'Darkover (My Series)'. For purposes of this -# documentation, 'Darkover' is called the value and 'My Series' is called the -# category. If two books have fields that generate the same collection name, -# then both books will be in that collection. -# This set of tweaks lets you specify for a standard or custom field how -# the collections are to be named. You can use it to add a description to a -# standard field, for example 'Foo (Tag)' instead of the 'Foo'. You can also use -# it to force multiple fields to end up in the same collection. For example, you -# could force the values in 'series', '#my_series_1', and '#my_series_2' to -# appear in collections named 'some_value (Series)', thereby merging all of the -# fields into one set of collections. -# There are two related tweaks. The first determines the category name to use -# for a metadata field. The second is a template, used to determines how the -# value and category are combined to create the collection name. -# The syntax of the first tweak, sony_collection_renaming_rules, is: -# {'field_lookup_name':'category_name_to_use', 'lookup_name':'name', ...} -# The second tweak, sony_collection_name_template, is a template. It uses the -# same template language as plugboards and save templates. This tweak controls -# how the value and category are combined together to make the collection name. -# The only two fields available are {category} and {value}. The {value} field is -# never empty. The {category} field can be empty. The default is to put the -# value first, then the category enclosed in parentheses, it isn't empty: -# '{value} {category:|(|)}' -# Examples: The first three examples assume that the second tweak -# has not been changed. -# 1: I want three series columns to be merged into one set of collections. The -# column lookup names are 'series', '#series_1' and '#series_2'. I want nothing -# in the parenthesis. The value to use in the tweak value would be: -# sony_collection_renaming_rules={'series':'', '#series_1':'', '#series_2':''} -# 2: I want the word '(Series)' to appear on collections made from series, and -# the word '(Tag)' to appear on collections made from tags. Use: -# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'} -# 3: I want 'series' and '#myseries' to be merged, and for the collection name -# to have '(Series)' appended. The renaming rule is: -# sony_collection_renaming_rules={'series':'Series', '#myseries':'Series'} -# 4: Same as example 2, but instead of having the category name in parentheses -# and appended to the value, I want it prepended and separated by a colon, such -# as in Series: Darkover. I must change the template used to format the category name -# The resulting two tweaks are: -# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'} -# sony_collection_name_template='{category:||: }{value}' -sony_collection_renaming_rules={} -sony_collection_name_template='{value}{category:| (|)}' - -#: Specify how SONY collections are sorted -# Specify how sony collections are sorted. This tweak is only applicable if -# metadata management is set to automatic. You can indicate which metadata is to -# be used to sort on a collection-by-collection basis. The format of the tweak -# is a list of metadata fields from which collections are made, followed by the -# name of the metadata field containing the sort value. -# Example: The following indicates that collections built from pubdate and tags -# are to be sorted by the value in the custom column '#mydate', that collections -# built from 'series' are to be sorted by 'series_index', and that all other -# collections are to be sorted by title. If a collection metadata field is not -# named, then if it is a series- based collection it is sorted by series order, -# otherwise it is sorted by title order. -# [(['pubdate', 'tags'],'#mydate'), (['series'],'series_index'), (['*'], 'title')] -# Note that the bracketing and parentheses are required. The syntax is -# [ ( [list of fields], sort field ) , ( [ list of fields ] , sort field ) ] -# Default: empty (no rules), so no collection attributes are named. -sony_collection_sorting_rules = [] - -#: Control how tags are applied when copying books to another library -# Set this to True to ensure that tags in 'Tags to add when adding -# a book' are added when copying books to another library -add_new_book_tags_when_importing_books = False - -#: Set the maximum number of tags to show per book in the content server -max_content_server_tags_shown=5 - -#: Set custom metadata fields that the content server will or will not display. -# content_server_will_display is a list of custom fields to be displayed. -# content_server_wont_display is a list of custom fields not to be displayed. -# wont_display has priority over will_display. -# The special value '*' means all custom fields. The value [] means no entries. -# Defaults: -# content_server_will_display = ['*'] -# content_server_wont_display = [] -# Examples: -# To display only the custom fields #mytags and #genre: -# content_server_will_display = ['#mytags', '#genre'] -# content_server_wont_display = [] -# To display all fields except #mycomments: -# content_server_will_display = ['*'] -# content_server_wont_display['#mycomments'] -content_server_will_display = ['*'] -content_server_wont_display = [] - -#: Set the maximum number of sort 'levels' -# Set the maximum number of sort 'levels' that calibre will use to resort the -# library after certain operations such as searches or device insertion. Each -# sort level adds a performance penalty. If the database is large (thousands of -# books) the penalty might be noticeable. If you are not concerned about multi- -# level sorts, and if you are seeing a slowdown, reduce the value of this tweak. -maximum_resort_levels = 5 - -#: Choose whether dates are sorted using visible fields -# Date values contain both a date and a time. When sorted, all the fields are -# used, regardless of what is displayed. Set this tweak to True to use only -# the fields that are being displayed. -sort_dates_using_visible_fields = False - -#: Fuzz value for trimming covers -# The value used for the fuzz distance when trimming a cover. -# Colors within this distance are considered equal. -# The distance is in absolute intensity units. -cover_trim_fuzz_value = 10 - -#: Control behavior of the book list -# You can control the behavior of doubleclicks on the books list. -# Choices: open_viewer, do_nothing, -# edit_cell, edit_metadata. Selecting anything other than open_viewer has the -# side effect of disabling editing a field using a single click. -# Default: open_viewer. -# Example: doubleclick_on_library_view = 'do_nothing' -# You can also control whether the book list scrolls horizontal per column or -# per pixel. Default is per column. -doubleclick_on_library_view = 'open_viewer' -horizontal_scrolling_per_column = True - -#: Language to use when sorting. -# Setting this tweak will force sorting to use the -# collating order for the specified language. This might be useful if you run -# calibre in English but want sorting to work in the language where you live. -# Set the tweak to the desired ISO 639-1 language code, in lower case. -# You can find the list of supported locales at -# http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/nls/rbagsicusortsequencetables.htm -# Default: locale_for_sorting = '' -- use the language calibre displays in -# Example: locale_for_sorting = 'fr' -- sort using French rules. -# Example: locale_for_sorting = 'nb' -- sort using Norwegian rules. -locale_for_sorting = '' - -#: Number of columns for custom metadata in the edit metadata dialog -# Set whether to use one or two columns for custom metadata when editing -# metadata one book at a time. If True, then the fields are laid out using two -# columns. If False, one column is used. -metadata_single_use_2_cols_for_custom_fields = True - -#: Order of custom column(s) in edit metadata -# Controls the order that custom columns are listed in edit metadata single -# and bulk. The columns listed in the tweak are displayed first and in the -# order provided. Any columns not listed are dislayed after the listed ones, -# in alphabetical order. Do note that this tweak does not change the size of -# the edit widgets. Putting comments widgets in this list may result in some -# odd widget spacing when using two-column mode. -# Enter a comma-separated list of custom field lookup names, as in -# metadata_edit_custom_column_order = ['#genre', '#mytags', '#etc'] -metadata_edit_custom_column_order = [] - -#: The number of seconds to wait before sending emails -# The number of seconds to wait before sending emails when using a -# public email server like gmail or hotmail. Default is: 5 minutes -# Setting it to lower may cause the server's SPAM controls to kick in, -# making email sending fail. Changes will take effect only after a restart of -# calibre. -public_smtp_relay_delay = 301 - -#: The maximum width and height for covers saved in the calibre library -# All covers in the calibre library will be resized, preserving aspect ratio, -# to fit within this size. This is to prevent slowdowns caused by extremely -# large covers -maximum_cover_size = (1650, 2200) - -#: Where to send downloaded news -# When automatically sending downloaded news to a connected device, calibre -# will by default send it to the main memory. By changing this tweak, you can -# control where it is sent. Valid values are "main", "carda", "cardb". Note -# that if there isn't enough free space available on the location you choose, -# the files will be sent to the location with the most free space. -send_news_to_device_location = "main" - -#: What interfaces should the content server listen on -# By default, the calibre content server listens on '0.0.0.0' which means that it -# accepts IPv4 connections on all interfaces. You can change this to, for -# example, '127.0.0.1' to only listen for connections from the local machine, or -# to '::' to listen to all incoming IPv6 and IPv4 connections (this may not -# work on all operating systems) -server_listen_on = '0.0.0.0' - -#: Unified toolbar on OS X -# If you enable this option and restart calibre, the toolbar will be 'unified' -# with the titlebar as is normal for OS X applications. However, doing this has -# various bugs, for instance the minimum width of the toolbar becomes twice -# what it should be and it causes other random bugs on some systems, so turn it -# on at your own risk! -unified_title_toolbar_on_osx = False - -#: Save original file when converting/polishing from same format to same format -# When calibre does a conversion from the same format to the same format, for -# example, from EPUB to EPUB, the original file is saved, so that in case the -# conversion is poor, you can tweak the settings and run it again. By setting -# this to False you can prevent calibre from saving the original file. -# Similarly, by setting save_original_format_when_polishing to False you can -# prevent calibre from saving the original file when polishing. -save_original_format = True -save_original_format_when_polishing = True - -#: Number of recently viewed books to show -# Right-clicking the View button shows a list of recently viewed books. Control -# how many should be shown, here. -gui_view_history_size = 15 - -#: Change the font size of book details in the interface -# Change the font size at which book details are rendered in the side panel and -# comments are rendered in the metadata edit dialog. Set it to a positive or -# negative number to increase or decrease the font size. -change_book_details_font_size_by = 0 - -#: Compile General Program Mode templates to Python -# Compiled general program mode templates are significantly faster than -# interpreted templates. Setting this tweak to True causes calibre to compile -# (in most cases) general program mode templates. Setting it to False causes -# calibre to use the old behavior -- interpreting the templates. Set the tweak -# to False if some compiled templates produce incorrect values. -# Default: compile_gpm_templates = True -# No compile: compile_gpm_templates = False -compile_gpm_templates = True - -#: What format to default to when using the Tweak feature -# The Tweak feature of calibre allows direct editing of a book format. -# If multiple formats are available, calibre will offer you a choice -# of formats, defaulting to your preferred output format if it is available. -# Set this tweak to a specific value of 'EPUB' or 'AZW3' to always default -# to that format rather than your output format preference. -# Set to a value of 'remember' to use whichever format you chose last time you -# used the Tweak feature. -# Examples: -# default_tweak_format = None (Use output format) -# default_tweak_format = 'EPUB' -# default_tweak_format = 'remember' -default_tweak_format = None - -#: Do not preselect a completion when editing authors/tags/series/etc. -# This means that you can make changes and press Enter and your changes will -# not be overwritten by a matching completion. However, if you wish to use the -# completions you will now have to press Tab to select one before pressing -# Enter. Which technique you prefer will depend on the state of metadata in -# your library and your personal editing style. -preselect_first_completion = False - -#: Completion mode when editing authors/tags/series/etc. -# By default, when completing items, calibre will show you all the candidates -# that start with the text you have already typed. You can instead have it show -# all candidates that contain the text you have already typed. To do this, set -# completion_mode to 'contains'. For example, if you type asi it will match both -# Asimov and Quasimodo, whereas the default behavior would match only Asimov. -completion_mode = 'prefix' - -#: Recognize numbers inside text when sorting -# This means that when sorting on text fields like title the text "Book 2" -# will sort before the text "Book 100". If you want this behavior, set -# numeric_collation = True note that doing so will cause problems with text -# that starts with numbers and is a little slower. -numeric_collation = False - -#: Sort the list of libraries alphabetically -# The list of libraries in the Copy to Library and Quick Switch menus are -# normally sorted by most used. However, if there are more than a certain -# number of such libraries, the sorting becomes alphabetic. You can set that -# number here. The default is ten libraries. -many_libraries = 10 - -#: Highlight the virtual library name when using a Virtual Library -# The virtual library name next to the Virtual Library button is highlighted in -# yellow when using a Virtual Library. You can choose the color used for the -# highlight with this tweak. Set it to 'transparent' to disable highlighting. -highlight_virtual_library = 'yellow' - -#: Choose available output formats for conversion -# Restrict the list of available output formats in the conversion dialogs. -# For example, if you only want to convert to EPUB and AZW3, change this to -# restrict_output_formats = ['EPUB', 'AZW3']. The default value of None causes -# all available output formats to be present. -restrict_output_formats = None - -#: Set the thumbnail image quality used by the content server -# The quality of a thumbnail is largely controlled by the compression quality -# used when creating it. Set this to a larger number to improve the quality. -# Note that the thumbnails get much larger with larger compression quality -# numbers. -# The value can be between 50 and 99 -content_server_thumbnail_compression_quality = 75 diff --git a/ddili/calibre_config_dir/viewer.json b/ddili/calibre_config_dir/viewer.json deleted file mode 100644 index 11a526d..0000000 --- a/ddili/calibre_config_dir/viewer.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "main_window_state": { - "__class__": "bytearray", - "__value__": "AAAA/wAAAAL9AAAAAwAAAAAAAADpAAACj/wCAAAAAfsAAAAQAHQAbwBjAC0AZABvAGMAawAAAAAnAAACjwAAAIgA////AAAAAQAAAAAAAAAA/AIAAAAB+wAAABwAYgBvAG8AawBtAGEAcgBrAHMALQBkAG8AYwBrAAAAAAD/////AAAAzQD///8AAAADAAAAAAAAAAD8AQAAAAH7AAAAHABmAG8AbwB0AG4AbwB0AGUAcwAtAGQAbwBjAGsAAAAAAP////8AAAA6AP///wAAAlEAAAKPAAAAAQAAAAIAAAABAAAAAvwAAAACAAAAAAAAAAEAAAAQAHQAbwBvAGwAXwBiAGEAcgMAAAAA/////wAAAAAAAAAAAAAAAgAAAAEAAAASAHQAbwBvAGwAXwBiAGEAcgAyAQAAAAD/////AAAAAAAAAAA=" - }, - "multiplier": 1.0, - "viewer_window_geometry": { - "__class__": "bytearray", - "__value__": "AdnQywACAAAAAAGuAAAAAwAABDwAAALRAAABrwAAABcAAAQ7AAACzAAAAAAAAAAABN4=" - }, - "in_paged_mode": true, - "custom_colors_for_color_dialog": [ - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ], - [ - 255, - 255, - 255, - 255 - ] - ], - "viewer_toc_isvisible": false, - "viewer_open_history": [ - "/home/ali/calibre_library/Unknown/ebook source.d.en (65)/ebook source.d.en - Unknown.azw3", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (62)/Programming in D - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/No Bold (60)/No Bold - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/No Bold (60)/No Bold - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (59)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (58)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/PinD Fonts 99 (57)/PinD Fonts 99 - Unknown.epub", - "/home/ali/calibre_library/Unknown/PinD Fonts 99 (57)/PinD Fonts 99 - Unknown.azw3", - "/home/ali/calibre_library/Unknown/PinD Fonts 88 (56)/PinD Fonts 88 - Unknown.azw3", - "/home/ali/calibre_library/Unknown/PinD Fonts 88 (56)/PinD Fonts 88 - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (54)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/PinD - Fonts 1 (53)/PinD - Fonts 1 - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (49)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (49)/ebook source.d.en - Unknown.azw3", - "/home/ali/calibre_library/Unknown/ebook source.d.en (49)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (48)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (48)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (46)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (45)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D - First Edition (44)/Programming in D - First Edition - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D - First Edition (44)/Programming in D - First Edition - Ali Cehreli.mobi", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (42)/Programming in D - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/Programming in D - First Edition (44)/Programming in D - First Edition - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/D Programlama Dili (43)/D Programlama Dili - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (42)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (40)/Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (38)/Programming in D - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/Programming in D (38)/Programming in D - Ali Cehreli.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (37)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (36)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (36)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (35)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (34)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (33)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (32)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (31)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (30)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (29)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Unknown/ebook source.d.en (28)/ebook source.d.en - Unknown.mobi", - "/home/ali/calibre_library/Unknown/ebook source.d.en (28)/ebook source.d.en - Unknown.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (24)/D.ershane - Programming in D - Ali Cehreli.mobi", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (21)/D.ershane - Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (20)/D.ershane - Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (18)/D.ershane - Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (17)/D.ershane - Programming in D - Ali Cehreli.mobi", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (16)/D.ershane - Programming in D - Ali Cehreli.mobi", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (12)/D.ershane - Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (11)/D.ershane - Programming in D - Ali Cehreli.epub", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (11)/D.ershane - Programming in D - Ali Cehreli.azw3", - "/home/ali/calibre_library/Ali Cehreli/D.ershane - Programming in D (10)/D.ershane - Programming in D - Ali Cehreli.epub" - ] -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/viewer.py b/ddili/calibre_config_dir/viewer.py deleted file mode 100644 index a7a84a8..0000000 --- a/ddili/calibre_config_dir/viewer.py +++ /dev/null @@ -1,134 +0,0 @@ -# Options to customize the ebook viewer - -### Begin group: DEFAULT - -# remember window size -# Remember last used window size -remember_window_size = False - -# user css -# Set the user CSS stylesheet. This can be used to customize the look of all books. -user_css = u'' - -# max fs width -# Set the maximum width that the book's text and pictures will take when in fullscreen mode. This allows you to read the book text without it becoming too wide. -max_fs_width = 800 - -# max fs height -# Set the maximum height that the book's text and pictures will take when in fullscreen mode. This allows you to read the book text without it becoming too tall. Note that this setting only takes effect in paged mode (which is the default mode). -max_fs_height = -1 - -# fit images -# Resize images larger than the viewer window to fit inside it -fit_images = True - -# hyphenate -# Hyphenate text -hyphenate = False - -# hyphenate default lang -# Default language for hyphenation rules -hyphenate_default_lang = u'en_us' - -# remember current page -# Save the current position in the document, when quitting -remember_current_page = True - -# wheel flips pages -# Have the mouse wheel turn pages -wheel_flips_pages = False - -# tap flips pages -# Tapping on the screen turns pages -tap_flips_pages = True - -# line scrolling stops on pagebreaks -# Prevent the up and down arrow keys from scrolling past page breaks -line_scrolling_stops_on_pagebreaks = False - -# page flip duration -# The time, in seconds, for the page flip animation. Default is half a second. -page_flip_duration = 0.5 - -# font magnification step -# The amount by which to change the font size when clicking the font larger/smaller buttons. Should be a number between 0 and 1. -font_magnification_step = 0.1 - -# fullscreen clock -# Show a clock in fullscreen mode. -fullscreen_clock = False - -# fullscreen pos -# Show reading position in fullscreen mode. -fullscreen_pos = False - -# fullscreen scrollbar -# Show the scrollbar in fullscreen mode. -fullscreen_scrollbar = True - -# start in fullscreen -# Start viewer in full screen mode -start_in_fullscreen = False - -# show fullscreen help -# Show full screen usage help -show_fullscreen_help = True - -# cols per screen -cols_per_screen = 1 - -# use book margins -use_book_margins = False - -# top margin -top_margin = 20 - -# side margin -side_margin = 40 - -# bottom margin -bottom_margin = 20 - -# text color -text_color = None - -# background color -background_color = None - -# show controls -show_controls = True - - -### Begin group: FONTS -# Font options - -# serif family -# The serif font family -serif_family = u'DejaVu Serif' - -# sans family -# The sans-serif font family -sans_family = u'DejaVu Sans' - -# mono family -# The monospaced font family -mono_family = u'DejaVu Sans Mono' - -# default font size -# The standard font size in px -default_font_size = 16 - -# mono font size -# The monospaced font size in px -mono_font_size = 16 - -# standard font -# The standard font type -standard_font = u'serif' - -# minimum font size -# The minimum font size in px -minimum_font_size = 8 - - - diff --git a/ddili/calibre_config_dir/viewer_dictionaries.json b/ddili/calibre_config_dir/viewer_dictionaries.json deleted file mode 100644 index 94c386c..0000000 --- a/ddili/calibre_config_dir/viewer_dictionaries.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "word_lookups": {} -} \ No newline at end of file diff --git a/ddili/src/404.d b/ddili/src/404.d deleted file mode 100644 index b00f7ca..0000000 --- a/ddili/src/404.d +++ /dev/null @@ -1,24 +0,0 @@ -Ddoc - -$(BR) - -$(CENTER $(H5 $(RED Aradığınız sayfa bulunamadı!))) - -$(P -Eğer zaten denemediyseniz sol taraftaki bağlantılar işe yarayabilir... Veya aradığınızı bulmanızda yardımı olabilecek kelimeler varsa Google'da site adını da ekleyerek aratabilirsiniz. -) - -$(P -Bu site içeriğinde aratmak için Google'da şu diziyi girebilirsiniz: -) - -$(SHELL -d programlama dili site:ddili.org -) - -Macros: - SUBTITLE=Sayfa Bulunamadı - - DESCRIPTION= - - KEYWORDS= diff --git a/ddili/src/AliCehreli_resume.d b/ddili/src/AliCehreli_resume.d deleted file mode 100644 index c7f028a..0000000 --- a/ddili/src/AliCehreli_resume.d +++ /dev/null @@ -1,291 +0,0 @@ -Ddoc - - - -$(CENTER $(H4 Ali Çehreli)) -$(CENTER $(LINK2 mailto:acehreli@yahoo.com, acehreli@yahoo.com)) - -$(H5 Skills) - -$(UL - -$(LI $(I Programming languages): Extensive experience with C++, C, and D since 1997, 1989, and 2009, respectively. I am familiar with C++11, C++14, and Python as well. -) - -$(LI $(I Technologies): User-mode C, C++, and D programs on Linux systems; configuration management systems ($(LINK2 http://www.tallmaple.com/, TMS) and proprietary others); $(LINK2 http://www.boost.org/, Boost), libevent, libxml, and other libraries; DDoc, GNU make, BoilerMake, distcc, gdb, valgrind, and other tools) - -$(LI $(I Program correctness): Unit testing; exception-safe C++ API design and implementation) - -) - -$(H5 Employment) - -$(H6 $(LINK2 http://www.riverbed.com/, Riverbed), 2007 - present) - -$(P $(I Member of Technical Staff, Device management framework)) - -$(UL - -$(LI Designed and implemented C++ library APIs to improve program correctness and developer productivity over existing C libraries. Introduced the Boost libraries to the development environment. Improved developer productivity by automating device configuration tree registrations through XML and C++ APIs, and automatic upgrades and fallbacks of device configurations.) - -$(LI Improved program responsiveness by enabling more kinds of non-blocking tasks and by implementing an event-based process startup dependency graph.) - -$(LI Improved software quality by integrating a unit testing framework (Unittest++) into the build process, wrote a mock file system library to enable program and library testing at build time. Introduced Doxygen to the code base. Wrote the coding guidelines document, acted as a resident C++ expert, gave presentations on C++ exception safety and the D programming language.) - -$(XXX Converted the build system to a non-recursive make to improve build speeds.) - -$(LI Interviewed 200+ engineering candidates.) - -) - -$(P $(I Member of Technical Staff, SteelFusion)) - -$(UL - -$(LI Worked on the configuration and management system of $(I SteelFusion). Implemented configuration modules like $(I Hypervisor Image Vault), which manages hypervisor ISO images.) - -) - -$(H6 $(LINK2 http://www.vmware.com/, VMware), 2006 - 2007) - -$(P -$(I Member of the Technical Staff, $(LINK2 http://www.vmware.com/support/converter/doc/releasenotes_conv302.html, VMware Converter)) -) - -$(UL - -$(LI Member of the team developing VMware Converter, a client/server physical-to-virtual computer migration system. Implemented the job scheduler to manage the execution of asynchronous machine conversion jobs, implemented job status message transfers from the server to the user interfaces, implemented the archiving of job status information as XML files. Helped incorporate Converter to VMware's Virtual Center product.) - -) - -$(H6 $(LINK2 https://www.paypal.com/, PayPal), 2004 - 2006) - -$(P $(I Staff Software Engineer, $(LINK2 https://www.paypal.com/cn/, Chinese PayPal site))) - -$(UL - -$(LI Participated in $(I the UTF-8 strings) project that integrated the $(LINK2 http://www-01.ibm.com/software/globalization/icu/, ICU libraries) of IBM to hundreds of source files to make the PayPal site Unicode-aware.) - -$(LI Worked on the Chinese PayPal site by localizing various existing features of PayPal to the Chinese language and culture. Implemented the Chinese site signup and referral bonus programs.) - -$(LI Worked on various other parts of the PayPal system from the back-end financial batch processing to front-end web interfaces.) - -) - -$(H6 NetContinuum, 2002 - 2004) - -$(P $(I Senior Software Engineer, Application security gateway)) - -$(UL - -$(LI One of the primary contributors of the team that developed the configuration agent of the $(I application security gateway) product. Redesigned and implemented the statistics collection framework; optimized the execution speed and reduced development and maintenance efforts. Redesigned the XML messaging framework; improved the communication with the user interfaces. Abstracted the external representation of the configuration tree. Designed and implemented a simple and safe output stream wrapper to increase the speed of string concatenations by a factor of ten. Improved the quality and robustness of the software; refactored the source code. Removed memory leaks; used valgrind and constantly inspected the source code for potential developer errors. -) - -$(LI Acted as the in-house C++ expert. Gave presentions on C++ good practices. Introduced the use of modern C++ idioms, design patterns, and libraries. Increased the overall understanding of the safe and robust software practices. -) - -$(LI Improved the build environment. Automated target dependency generation in the make files. Tripled the build speed by introducing distcc. -) - -) - - -$(H6 Inviscid Networks, 2001 - 2002) - -$(P $(I Lead Software Engineer, Packet forwarding engine ASICs)) - -$(UL - -$(LI Principal designer of network processor verification software and network processor development tools.) - -$(LI Lead contributor to architecture, design, and implementation of full suite of integrated network processor development tools.) - -$(LI Contributor to the architecture of network processor ASICs.) - -$(LI Developed run time libraries for table management and hardware abstraction layer for a family of network processors.) - -$(LI Designed and implemented an unlimited-width and platform independent bit-field class to represent the varying widths of hardware memory blocks, implemented patricia trie, hash table, and other data structures.) - -$(LI Abstracted the hardware layer structures as logical operators and functional blocks of a graphical decision tree.) - -) - -$(H6 $(LINK2 http://en.wikipedia.org/wiki/Berkeley_Networks, Berkeley Networks) ($(LINK2 http://en.wikipedia.org/wiki/FORE_Systems, Fore) and $(LINK2 http://en.wikipedia.org/wiki/Marconi_Communications, Marconi) by acquisitions), 1996 – 2001) - -$(P $(I Senior Software Engineer, Network switches and routers)) - -$(UL - -$(LI Fundamental contributor from the earliest design stages of a family of networks processor ASICs. Contributed to the development of a family of application aware Ethernet switches. Proposed optimizations that were later implemented in successor chip development. -) - -$(LI Implemented low level protocols and tools for ASIC verification, manufacturing diagnostics, and fault isolation. Implemented low level protocols to communicate with line cards over both serial and ethernet ports. Developed tools for ASIC verification, manufacturing diagnostics, and fault isolation. The test methods and programs developed were leveraged from early phases of ASIC design and verification to the eventual deployment into 3rd party manufacturing process flows. -) - -$(LI Developed a functional test program to run on a single inexpensive PC to stress test the routing and bridging protocols of a multi-Gigabit switch. Lead developer of diagnostic stress tests utilizing avalanche traffic generation methods within the switch. Hardware multicast, COS queue scheduling and multiple programmable loop-back interfaces were used to advantage. The test program could automatically isolate faults to specific chips and interfaces within the system by dynamically adjusting the program parameters based upon incorrect returned packet data. Stimulus and verification data could be injected and returned at program controlled rates on a single link to enable simple test bench environments suitable for manufacturing. -) - -) - -$(H6 ITA - Nokia Monitors, 1994 - 1996) - -$(P $(I Software Engineer, Order-entry and invoicing system) -) - -$(UL - -$(LI Programmed a proprietary order-entry and invoicing system. Integrated the system with FedEx's shipping applications. Sole maintainer and improver of a proprietary order-entry and invoicing system written in the MS FoxPro database language. Integrated the system with FedEx's shipping applications to automatically update the status of orders in the database. Designed, configured, and maintained a WAN connecting the branch offices. Connected the system to company's remote offices via modems and routers for automatic data entry. - -) - -) - -$(H6 Opera Computer, 1991 - 1994) - -$(P $(I Founder and Software Engineer, WordPerfect products)) - -$(UL - -$(LI Co-founder of the WordPerfect reseller in Turkey. Localized the products of WordPerfect Corporation. Translated the word processor, database, spreadsheet, and other applications to Turkish. Provided customer support and product training. -) - -) - -$(H6 Karel Computer, 1989 - 1990) - -$(P $(I Software Engineer, Digital Research products)) - -$(UL - -$(LI Localized the products of Digital Research Corporation. Translated the DR DOS operating system and the entire set of GUI products of Digital Research Corporation, as well as some of the user manuals of those products to Turkish. Patched pre-compiled object files to support Turkish keyboard layout and to enable copy protection for applications. -) - -) - -$(H5 Other experience) - -$(H6 $(LINK2 http://millcomputing.com/, Mill Computing), 2014 - present) - -$(P -$(I Software Engineer, Mill CPU) -) - -$(UL - -$(LI Worked on the simulation environment, syscalls, runtime, and various tools of the Mill, a revolutionary CPU architecture.) - -) - -$(H5 Personal projects and accomplishments) - -$(H6 C++ and C Programming Languages) - -$(UL - -$(LI Former president of $(LINK2 http://www.meetup.com/SFBay-Association-of-C-C-Users/, Silicon Valley Chapter of ACCU) (2012-2015); former membership coordinator (2002-2015); four-time speaker (2004-2015)) - -$(LI Author of the Trends column and several C++ articles for the ACC$(SUP ++)ent newsletter (2001-2003)) - -$(LI Former moderator of $(LINK2 http://forum.ceviz.net/k/c-c.28/, the C and C++ forum) on Ceviz.net (2002-2014)) - -$(LI Attendee of $(LINK2 http://groups.yahoo.com/neo/groups/siliconvalleypatterns/info, Silicon Valley Patterns Group)) - -$(LI Author and translator of $(LINK2 http://acehreli.org/turkcecpp/index.html, C++ articles)) - -) - - -$(H6 D Programming Language) - -$(UL - -$(LI Author of the books $(LINK2 http://ddili.org/ders/d.en/index.html, $(I Programming in D)) (textbook at University of Minnesota and $(LINK2 https://www.youtube.com/watch?feature=player_detailpage&v=ymoIx3klQ6M#t=688, Utah Valley University)) and its original $(LINK2 http://ddili.org/ders/d/, $(I D Programlama Dili))) - -$(LI Secretary and board member of The D Language Foundation) - -$(LI Main organizer of $(LINK2 http://www.meetup.com/D-Lang-Silicon-Valley/, Silicon Valley D Lang Meetup) group) - -$(LI Speaker at $(LINK2 http://dconf.org/talks/cehreli.html, DConf 2013), $(LINK2 https://www.youtube.com/watch?v=oF8K4-bieaw, DConf 2014), $(LINK2 http://dconf.org/2016/talks/cehreli.html, DConf 2016), and D Lang meetups) - -$(LI Founder of $(LINK2 http://ddili.org/, Ddili.org)) - -$(LI Moderator of D forums on $(LINK2 http://ddili.org/forum/, Ddili.org)) - -$(LI $(LINK2 https://bitbucket.org/acehreli/, Personal projects) including the generation of ddili.org as well as an experimental Unicode library for collation and capitalization of various writing systems) - -) - -$(H5 Education) - -$(UL - -$(LI M.S., Physics, $(LINK2 http://www.itu.edu.tr/en/, Istanbul Technical University), 1994. Developed tools and emulation programs to study fractals, dynamical systems, neural networks, and cellular automata.) - -$(LI B.S., Electronics Engineering, $(LINK2 http://www.itu.edu.tr/en/, Istanbul Technical University), 1989. Designed microprocessor controlled stopwatch for track and field sports.) - -) - -Macros: - -BREADCRUMBS_ID = resume_breadcrumbs - -BREADCRUMBS_FULL= $(LINK2 /ders/d.en/index.html, Main Page) - -DDOC = $(DOCTYPE) - - - - - - - - - - - $(SUBTITLE) - - - - - - - - -
- -
- $(BREADCRUMBS_DIV) - - - $(BODY) - -
- -
- - - - - SUBTITLE=Ali Çehreli's résumé - - DESCRIPTION=The résumé of Ali Çehreli (a.k.a. Ali Cehreli). - - KEYWORDS=Ali Çehreli Ali Cehreli résumé CV - diff --git a/ddili/src/META-INF/com.apple.ibooks.display-options.xml b/ddili/src/META-INF/com.apple.ibooks.display-options.xml deleted file mode 100644 index 3adfe68..0000000 --- a/ddili/src/META-INF/com.apple.ibooks.display-options.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/ddili/src/Makefile b/ddili/src/Makefile deleted file mode 100644 index 77a3c33..0000000 --- a/ddili/src/Makefile +++ /dev/null @@ -1,281 +0,0 @@ -all: - -CIKIS?=../public_html - -TEST_DEFS?= - -ANA:= \ - ddili.ddoc \ - -ORTAK:= \ - common.ddoc \ - dusey_navigasyon.ddoc \ - sozluk_body.ddoc \ - -REGULAR_ORTAK:= \ - $(ANA) \ - $(ORTAK) \ - regular.ddoc \ - -# En ust duzey ders sayfasi -DERS_ROOT_D:= \ - ders/index.d \ - -DERS_ROOT_LINK= \ - $(DERS_ROOT_D:%.d=/%.html) \ - -KAYNAKLAR= \ - 404.d \ - AliCehreli_resume.d \ - copyright.d \ - iletisim.d \ - index.d \ - sozluk.d \ - \ - $(DERS_ROOT_D) \ - \ - kurulum/dmd.d \ - kurulum/emacs_d-mode.d \ - kurulum/gdc.d \ - kurulum/index.d \ - \ - makale/bellek.d \ - makale/d_dilimleri.d \ - makale/d_tipleri.d \ - makale/degismez.d \ - makale/dub_tanisma.d \ - makale/duzenli_ifadeler.d \ - makale/eleman_erisimi_uzerine.d \ - makale/index.d \ - makale/katma.d \ - makale/neden_d.d \ - makale/saflik.d \ - makale/shared.d \ - makale/tembel_hesap.d \ - \ - ornek_kod/index.d \ - \ - tanitim/dil_kutuphane.d \ - tanitim/fark_c.d \ - tanitim/fark_cpp.d \ - tanitim/fark_dizgi.d \ - tanitim/fark_karmasik.d \ - tanitim/fark_onislemci.d \ - tanitim/genel.d \ - tanitim/index.d \ - \ - sunum/index.d \ - sunum/merhaba_2012.d \ - sunum/sonrasi.d \ - sunum/ingilizce_sunumlar.d \ - -EK_DOSYALAR= \ - robots.txt \ - rss.xml \ - style.css \ - \ - image/book.png \ - image/bullet_black.png \ - image/cc_80x15.png \ - image/cc_88x31.png \ - image/d_harfi.jpg \ - image/d_source.png \ - image/email.png \ - image/favicon.png \ - image/forum.png \ - image/pdficon_small.gif \ - image/pencil.png \ - image/rss-icon.png \ - image/penguen.png \ - image/gulen.png \ - image/oyundongusu.jpg \ - image/sdlkoordinat.png \ - image/vektorler1.png \ - image/vektorler2.png \ - image/vektorler3.png \ - image/vektorler4.png \ - image/oyunlardahareket.png \ - \ - sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf \ - sunum/2012_SalihDincer_Diziler_Dilimler.pdf \ - sunum/2012_AliCehreli_Cpp11_D.pdf \ - sunum/2012_AliCehreli_D_Tanitim.pdf \ - sunum/2012_AliCehreli_Kosut_Islemler.pdf \ - \ - AliCehreli_resume.pdf \ - \ - ders/sdl/src/ders1.zip \ - kurulum/locale.properties \ - -SILINECEK_DOSYALAR= \ - $(SOZLUK_DOSYALARI) \ - $(TAR_FILE) \ - $(TEST_DDOC) \ - $(PDF_LOCAL_DDOC) \ - $(EBOOK_TRANSFER) \ - -SILINECEK_KLASORLER= \ - ../public_html_test \ - ../public_html \ - -# Dersler -include ders/d/Makefile.in -include ders/gtkd/Makefile.in -include ders/sdl/Makefile.in -include ders/d.en/Makefile.in -include ders/d.cn/Makefile.in -#include ders/d.de/Makefile.in - - -BUTUN_D_HTML=$(KAYNAKLAR:%.d=$(CIKIS)/%.html) -BUTUN_HTML=$(BUTUN_D_HTML) - -validate_macro = grep -L -w $(2) $(1) || (echo $(1):1000: $(2) is not defined && false) - -define validate_maybe_index_macro -if [ "$(strip $(2))" = "index.html" ]; then \ - $(call validate_macro, $(1), BREADCRUMBS); \ -fi -endef - -define validate_d - $(call validate_macro, $(1), SUBTITLE) - $(call validate_macro, $(1), DESCRIPTION) - $(call validate_macro, $(1), KEYWORDS) - $(call validate_maybe_index_macro, $(1), $(2)) -endef - -define validate_all_ddoc_expanded - @grep -e '$$(' $(1); \ - case "$$?" in \ - 0) echo $(1):0: Unexpanded ddoc macro; false ;; \ - *) true ;; \ - esac -endef - -define validate_no_back_tick - @grep -e ']*inline' $(1); \ - case "$$?" in \ - 0) echo $(1):0: Back tick in inline code; false ;; \ - *) true ;; \ - esac -endef - -SOZLUK_DOSYALARI=sozluk.ddoc sozluk_body.ddoc ders/d/sozluk.d - -$(SOZLUK_DOSYALARI): sozluk.txt Makefile sozlukmaker - ./sozlukmaker turkish - cp sozluk.d ders/d/sozluk.d - -%.d.last_modified.ddoc: %.d Makefile - echo LAST_MODIFIED=$$(TZ=GMT date -R --date="`git log -n1 --date=rfc $< | sed -n -e 's/Date: //p'`" | sed -n -e 's/+0000/GMT/p') > $@ - -$(CIKIS)/%.html: %.d $(REGULAR_ORTAK) Makefile %.d.last_modified.ddoc - @$(call validate_d, $<, $(strip $(@F))) - dmd $(REGULAR_ORTAK) $(TEST_DEFS) $<.last_modified.ddoc $( $@ - -FILES_FOR_EBOOK+= \ - image/by-nc-sa.png \ - fonts/* \ - META-INF/* \ - ebook_font_manifest \ - README.ebook \ - -.PHONY: all -all: test screen print ebook - -.PHONY: screen -screen: $(TAR_FILE) - -TEST_DDOC:=test.ddoc -PDF_LOCAL_DDOC:=pdf_local.ddoc - -.PHONY: $(TEST_DDOC) -$(TEST_DDOC): - echo ROOT_DIR=`pwd`/../public_html_test > $@ - echo GOOGLE_TRANSLATE=[Google Translate] >> $@ - -$(PDF_LOCAL_DDOC): Makefile - echo ROOT_DIR=`pwd` > $@ - echo GOOGLE_TRANSLATE=[Google Translate] >> $@ - -.PHONY: test -test: $(TEST_DDOC) - $(MAKE) CIKIS=../public_html_test TEST_DEFS=$(TEST_DDOC) butun_dosyalar - -.PHONY: clean_files -clean_files: - @rm -f $(SILINECEK_DOSYALAR) - @find . -type f -name "*.last_modified.ddoc" -exec rm {} \; - -.PHONY: clean_dirs -clean_dirs: - @WHAT_TO_DELETE="$(SILINECEK_KLASORLER)" && [[ ! -z $$WHAT_TO_DELETE ]] - @rm -rf $(SILINECEK_KLASORLER) - -.PHONY: clean -clean: clean_files clean_dirs - -.PHONY: print -print: $(PDF_FILES_FOR_PRINT) - -$(EBOOK_TRANSFER): $(FILES_FOR_EBOOK) - tar zcv $^ > $@ - -.PHONY: ebook -ebook: $(EBOOK_TRANSFER) diff --git a/ddili/src/Makefile.ders.in b/ddili/src/Makefile.ders.in deleted file mode 100644 index 0d77caf..0000000 --- a/ddili/src/Makefile.ders.in +++ /dev/null @@ -1,300 +0,0 @@ -# -# Bu dosya, src/ders/ altindaki derslerin Makefile.in dosyalarinin en -# sonunda "include edilmelidir". -# -# Ders Makefile.in dosyalari, bu dosyayi eklemeden once su degiskenleri -# tanimlamis olmalidirlar: -# -# DERS_SON_D: Dersin sonuncu sayfasi; ornegin devami_gelecek.d -# -# DERS_D_KAYNAK: Dersleri olusturan kaynak .d dosyalari; ornegin -# index.d tanitim.d tesekkur.d ... -# -# COZUM_D_KAYNAK: problem cozumu dosyalari; ornegin -# merhaba_dunya.cozum.d writeln.cozum.d ... -# - -define derse_ozel - -DERS_ORTAK_$(1)= \ - $$(ANA) \ - $$(ORTAK) \ - ders.ddoc \ - sozluk.ddoc \ - -# Overwrite for Chinese -DERS_ORTAK_d.cn= \ - $$(ANA) \ - $$(ORTAK) \ - ders.ddoc \ - sozluk.ddoc \ - chinese.ddoc \ - -COZUM_ORTAK_$(1)= \ - $$(ANA) \ - common.ddoc \ - cozum.ddoc \ - ders/$(1)/derse_ozel.ddoc \ - ders/$(1)/cozum_ozel.ddoc \ - -DERS_SON_D_$(1)=ders/$(1)/$(DERS_SON_D) - -DERS_D_$(1)=$(DERS_D_KAYNAK:%=ders/$(1)/%) - -COZUM_D_$(1)=$(COZUM_D_KAYNAK:%=ders/$(1)/%) - -PDF_FILE_$(1)=ders/$(1)/$(2).pdf -PDF_FILE_$(1)_FOR_PRINT=ders/$(1)/$(2).print.pdf -EBOOK_FILE_$(1)=ders/$(1)/ebook_source.$(1).html - -KAYNAKLAR+= \ - $$(DERS_D_$(1)) \ - $$(DERS_SON_D_$(1)) \ - $$(COZUM_D_$(1)) \ - -CODETEST_BOLUMLER_$(1)= \ - $(DERS_D_KAYNAK) \ - $(COZUM_D_KAYNAK) \ - -CODESAMPLE_ENTRIES_$(1)= \ - $$(CODESAMPLE_DIR_$(1))/*.d \ - -ifeq "$(strip $(3))" "english" - # Example: Programming_in_D_code_samples - CODESAMPLE_DIR_$(1)=$(2)_code_samples - CODESAMPLE_ENTRIES_$(1)+=$$(CODESAMPLE_DIR_$(1))/README -else - CODESAMPLE_DIR_$(1)=$(2)_kod_ornekleri - CODESAMPLE_ENTRIES_$(1)+=$$(CODESAMPLE_DIR_$(1))/BENIOKU -endif - -# Example: ders/d.en/Programming_in_D_code_samples.zip -CODESAMPLE_ZIP_FILE_$(1)=ders/$(1)/$$(CODESAMPLE_DIR_$(1)).zip - -COVER_THUMB_$(1)=ders/$(1)/cover_thumb.png -COVER_EBOOK_$(1)=ders/$(1)/cover_ebook.png - -ifneq ($$(wildcard $$(COVER_THUMB_$(1))),) - EK_DOSYALAR+=$$(COVER_THUMB_$(1)) -endif - -ifneq ($$(wildcard $$(COVER_EBOOK_$(1))),) - FILES_FOR_EBOOK+=$$(COVER_EBOOK_$(1)) -endif - -EK_DOSYALAR+= \ - ders/$(1)/rss.xml \ - -ifeq "$(strip $$(TEST_DEFS))" "" - EK_DOSYALAR+=$$(PDF_FILE_$(1)) - EK_DOSYALAR+=$$(CODESAMPLE_ZIP_FILE_$(1)) -endif - -BUTUN_DERS_LINKS_$(1)= \ - $$(DERS_D_$(1):%.d=/%.html) \ - $$(DERS_SON_D_$(1):%.d=/%.html) \ - -BUTUN_DERS_MACROS_$(1)= \ - $$(DERS_D_$(1):%.d=%.d.macros.ddoc) \ - $$(COZUM_D_$(1):%.d=%.d.macros.ddoc) \ - $$(DERS_SON_D_$(1):%.d=%.d.macros.ddoc) \ - -SILINECEK_DOSYALAR+= \ - $$(BUTUN_DERS_MACROS_$(1)) \ - -$$(BUTUN_DERS_MACROS_$(1)): Makefile Makefile.ders.in ders/$(1)/Makefile.in make_ders_macros.sh - ./make_ders_macros.sh '$$(BUTUN_DERS_MACROS_$(1))' \ - '$$(DERS_ROOT_LINK)$$(BUTUN_DERS_LINKS_$(1))' - -ders/$(1)/%.d.last_modified.ddoc: ders/$(1)/%.d Makefile.ders - echo LAST_MODIFIED=$$$$(TZ=GMT date -R --date="`git log -n1 --date=rfc $$< | sed -n -e 's/Date: //p'`" | sed -n -e 's/+0000/GMT/p') > $$@ - -$(CIKIS)/ders/$(1)/%.cozum.html: ders/$(1)/%.cozum.d $$(BUTUN_DERS_MACROS_$(1)) $$(COZUM_ORTAK_$(1)) Makefile Makefile.ders.in ders/$(1)/breadcrumbs.ddoc ders/$(1)/%.cozum.d.last_modified.ddoc - @$$(call validate_d, $$<, $$(strip $$(@F))) - dmd $$(COZUM_ORTAK_$(1)) $$(TEST_DEFS) $$( $$@ - -SILINECEK_DOSYALAR+=$$(PDF_ICIN_PRE_ANCHORS_$(1)) - -TOC_$(1)=ders/$(1)/toc.html -INDEX_SECTION_$(1)=ders/$(1)/index_section.html -INDEX_FOR_WEB_KAYNAK_$(1)=ders/$(1)/ix.d -INDEX_FOR_WEB_$(1)=$(CIKIS)/ders/$(1)/ix.html -INDEX_FOR_WEB_MACROS_$(1)=$$(INDEX_FOR_WEB_KAYNAK_$(1)).macros.ddoc - -$$(TOC_$(1)) $$(INDEX_SECTION_$(1)) $$(PDF_ICIN_POST_ANCHORS_$(1)) $$(INDEX_FOR_WEB_MACROS_$(1)): $$(PDF_ICIN_PRE_ANCHORS_$(1)) anchorgen - ./anchorgen $$(PDF_ICIN_PRE_ANCHORS_$(1)) \ - $$(PDF_ICIN_POST_ANCHORS_$(1)) $$(TOC_$(1)) $$(INDEX_SECTION_$(1)) \ - $$(INDEX_FOR_WEB_MACROS_$(1)) \ - $(3) -SILINECEK_DOSYALAR+=$$(TOC_$(1)) $$(INDEX_SECTION_$(1)) -SILINECEK_DOSYALAR+=$$(PDF_ICIN_POST_ANCHORS_$(1)) - -SILINECEK_DOSYALAR+=$$(INDEX_FOR_WEB_$(1)) -SILINECEK_DOSYALAR+=$$(INDEX_FOR_WEB_MACROS_$(1)) -SILINECEK_DOSYALAR+=$$(INDEX_FOR_WEB_BODY_$(1)) - -KAYNAKLAR+=$$(INDEX_FOR_WEB_$(1)) - -$$(INDEX_FOR_WEB_$(1)): $$(INDEX_FOR_WEB_KAYNAK_$(1)) $$(INDEX_FOR_WEB_MACROS_$(1)) $$(DERS_ORTAK_$(1)) ders/$(1)/derse_ozel.ddoc Makefile Makefile.ders.in ders/$(1)/breadcrumbs.ddoc ders/$(1)/ix.d.last_modified.ddoc - @$$(call validate_d, $$<, $$(strip $$(@F))) - dmd $$(DERS_ORTAK_$(1)) ders/$(1)/derse_ozel.ddoc \ - $$( $$@ - -SILINECEK_DOSYALAR+=$$(PDF_SURUM_$(1)) - -$$(PDF_ICIN_BIR_ARADA_$(1)): $$(PDF_ICIN_BIR_ARADA_KAYNAK_$(1)) $$(PDF_SURUM_$(1)) Makefile Makefile.ders.in $$(CODE_TESTED_$(1)) - cat $$(PDF_ICIN_BIR_ARADA_KAYNAK_$(1)) > $$@ - $$(call validate_no_back_tick, $$@) || (rm $$@; false) - -PRINCE_COMMON_DEPS_$(1) = pdf_fonts.css pdf.css ders/$(1)/pdf.derse_ozel.css - -PRINCE_COMMON_OPTIONS_$(1) = \ - --no-artificial-fonts \ - --disallow-modify --style=pdf_fonts.css --style=pdf.css \ - --style=ders/$(1)/pdf.derse_ozel.css --output=$$@ \ - -ifneq "$(strip $$(REVEAL_IX))" "" - PRINCE_COMMON_OPTIONS_$(1)+= --style=reveal_ix.css -endif - -$$(PDF_FILE_$(1)): $$(PDF_ICIN_BIR_ARADA_$(1)) $$(PRINCE_COMMON_DEPS_$(1)) - prince $$(PDF_ICIN_BIR_ARADA_$(1)) \ - $$(PRINCE_COMMON_OPTIONS_$(1)) --media=screen \ - -PDF_FILES_FOR_PRINT+=$$(PDF_FILE_$(1)_FOR_PRINT) - -$$(PDF_FILE_$(1)_FOR_PRINT): $$(PDF_ICIN_BIR_ARADA_$(1)) $$(PRINCE_COMMON_DEPS_$(1)) - prince $$(PDF_ICIN_BIR_ARADA_$(1)) \ - $$(PRINCE_COMMON_OPTIONS_$(1)) --media=print \ - -EBOOK_PAGES_$(1) = \ - $$(BEFORE_TOC_$(1)) \ - $$(AFTER_TOC_$(1)) \ - -$$(EBOOK_FILE_$(1)).unsanitized: $$(EBOOK_PAGES_$(1)) Makefile Makefile.ders.in - cat $$(EBOOK_PAGES_$(1)) > $$@ - sed -i -- 's||'$$(PDF_SURUM_TARIH_$(1))'|g' $$@ - -$$(EBOOK_FILE_$(1)): $$(EBOOK_FILE_$(1)).unsanitized ebooksanitizer - ./ebooksanitizer < $$< > $$@ - -EBOOK_CSS_$(1)=ders/$(1)/ebook.css - -$$(EBOOK_CSS_$(1)): pdf_fonts.css pdf.css ders/$(1)/pdf.derse_ozel.css ebook_override.css - cat pdf_fonts.css pdf.css ders/$(1)/pdf.derse_ozel.css ebook_override.css > $$@ - -SILINECEK_DOSYALAR+=$$(EBOOK_FILE_$(1)).unsanitized -SILINECEK_DOSYALAR+=$$(EBOOK_FILE_$(1)) -SILINECEK_DOSYALAR+=$$(EBOOK_CSS_$(1)) - -FILES_FOR_EBOOK+= \ - $$(EBOOK_FILE_$(1)) \ - $$(EBOOK_CSS_$(1)) \ - -endef diff --git a/ddili/src/README.ebook b/ddili/src/README.ebook deleted file mode 100644 index a2c850f..0000000 --- a/ddili/src/README.ebook +++ /dev/null @@ -1,38 +0,0 @@ -=== Creating the EPUB file === - -The following are the steps for embedding fonts inside EPUB ebooks. (As far as -Ali knows, AZW3 already embeds fonts and MOBI cannot embed fonts.) - -1) Use the cover, CSS, and HTML files in your favorite ebook creator -(e.g. calibre) to create the ebook. - -2) Extract the manifest file from the generated ebook file (e.g. let's assume -that the generated file is mybook.epub). The name of the manifest is probably -content.opf: - - unzip mybook.epub content.opf - -(If there is no content.opf in your epub, then you can learn the name of the -manifest file by looking inside META-INF/container.xml) - -3) Insert the content of the 'ebook_font_manifest' file (untarred along with -the file your are currently reading) to content.opf next to the other -entries. - -4) Add content.opf back to the .epub file - - zip mybook.epub content.opf - -5) Go to the directory where the ebook files were untarred and add fonts/, -image/, and META-INF/ directories recursively to the .epub file: - - zip -r mybook.epub fonts/ image/ META-INF/ - -6) Optionally, validate the .epub file at an online validating site like -http://validator.idpf.org/ - -=== Creating the AZW3 file === - -The most convenient method is to start from the EPUB that was created above -and generate AZW3 from that EPUB. At least when generated with Calibre, the -embedded fonts work. diff --git a/ddili/src/alphabet.d b/ddili/src/alphabet.d deleted file mode 100644 index af8af43..0000000 --- a/ddili/src/alphabet.d +++ /dev/null @@ -1,346 +0,0 @@ -/** - * This module handles sorting and capitalization of text according to a - * specific alphabet (English and Turkish). - */ - -import std.uni; -import std.algorithm; -import std.range; -import std.conv; -import std.format; -import std.exception; - -interface Alphabet -{ - bool is_less(dchar lhs, dchar rhs); - bool is_less(string lhs, string rhs); - bool is_greater(dchar lhs, dchar rhs); - string toLower(string s); - dchar toUpper(dchar d); -} - -class EnglishAlphabet : Alphabet -{ - bool is_less(dchar lhs, dchar rhs) - { - return lhs < rhs; - } - - bool is_less(string lhs, string rhs) - { - return lhs < rhs; - } - - bool is_greater(dchar lhs, dchar rhs) - { - return lhs > rhs; - } - - string toLower(string s) - { - return std.uni.toLower(s); - } - - dchar toUpper(dchar d) - { - return std.uni.toUpper(d); - } -} - -class TurkishAlphabet : Alphabet -{ - static const lowerLetters = "abcçdefgğhıijklmnoöpqrsştuüvwxyz"d; - static const upperLetters = "ABCÇDEFGĞHIİJKLMNOÖPQRSŞTUÜVWXYZ"d; - static assert (lowerLetters.length == upperLetters.length); - - int[dchar] order; - dchar[dchar] uppers; - dchar[dchar] lowers; - - this() - { - foreach (int i, dchar c; lowerLetters) { - order[c] = i; - } - - foreach (l, u; zip(lowerLetters, upperLetters)) { - uppers[l] = u; - - /* We are taking a chance here and assuming that all words that - * start with 'I' are English words. We want them appear under İ - * in the index even for Turkish books. Also see the HACK comment - * inside generateIndex(). */ - lowers[u] = (u == 'I' ? 'i' : l); - } - } - - int orderCode(dchar d) - { - const o = d in order; - return (o ? *o : d); - } - - bool is_less(dchar lhs, dchar rhs) - { - return orderCode(lhs) < orderCode(rhs); - } - - bool is_less(string lhs, string rhs) - { - /* Unfortunately, the following does not work due to the following bug: - * - * https://issues.dlang.org/show_bug.cgi?id=13566 - * - * return cmp!((l, r) => orderCode(l) < orderCode(r))(lhs, rhs) < 0; - */ - - foreach (l, r; zip(lhs, rhs)) { - const lo = orderCode(l); - const ro = orderCode(r); - - if (lo < ro) { - return true; - } - } - - /* Shorter one is before */ - return lhs.length < rhs.length; - } - - bool is_greater(dchar lhs, dchar rhs) - { - return orderCode(lhs) > orderCode(rhs); - } - - string toLower(string s) - { - dchar value(dchar src) - { - auto result = src in lowers; - return (result ? *result : std.uni.toLower(src)); - } - - return s.map!value.to!string; - } - - dchar toUpper(dchar d) - { - const u = d in uppers; - return (u ? *u : std.uni.toUpper(d)); - } -} - -class ChineseAlphabet : Alphabet -{ - bool is_less(dchar lhs, dchar rhs) - { - return lhs < rhs; - } - - bool is_less(string lhs, string rhs) - { - return lhs < rhs; - } - - bool is_greater(dchar lhs, dchar rhs) - { - return lhs > rhs; - } - - string toLower(string s) - { - return s; - } - - dchar toUpper(dchar d) - { - return d; - } -} - -Alphabet makeAlphabet(string alphabetName) -{ - Alphabet result; - - switch (alphabetName) { - case "english": - result = new EnglishAlphabet(); - break; - - case "turkish": - result = new TurkishAlphabet(); - break; - - case "german": - result = new EnglishAlphabet(); - break; - - case "chinese": - result = new ChineseAlphabet(); - break; - - default: - throw new Exception(format("Unsupported alphabet: %s", alphabetName)); - } - - return result; -} - -/* Compares two characters according to the index section sorting rules. All - * of the non-alpha characters are sorted before the alpha characters. */ -int indexSectionOrderChar(dchar lhs, dchar rhs, Alphabet alphabet) -{ - const la = isAlpha(lhs); - const ra = isAlpha(rhs); - - if (!la && ra) { - return -1; - } - - if (la && !ra) { - return 1; - } - - if (alphabet.is_less(lhs, rhs)) { - return -1; - - } else if (alphabet.is_greater(lhs, rhs)) { - return 1; - - } else { - return 0; - } -} - -/* Removes leading non-alpha characters if they indeed lead at least one alpha - * character. */ -string removeLeadingNonAlphaMaybe(string s) -{ - string orig = s; - - if (!s.empty) { - switch (s.front) { - case '.', '-', '@', '~', '!': - s.popFront(); - - if (!s.empty && s.front.isAlpha) { - return s; - } - - break; - - case '_': - auto result = s.find!(a => a != '_'); - if (!result.empty && result.front.isAlpha) { - return result; - } - - break; - - default: - break; - } - } - - return orig; -} - -/* Extracts an int from the string and pops the front of the string. */ -int extractInt(ref string s) -{ - int result; - - const items = formattedRead(s, " %d", &result); - assert(items == 1); - - return result; -} - -/* Index section sorting predicate. */ -bool indexSectionOrder(string lhs, string rhs, Alphabet alphabet) -{ - auto a = alphabet.toLower(lhs).removeLeadingNonAlphaMaybe; - auto b = alphabet.toLower(rhs).removeLeadingNonAlphaMaybe; - - while (!a.empty && !b.empty) { - - /* Skip commas, they are just separators. */ - - if (a.front == ',') { - a.popFront(); - continue; - } - - if (b.front == ',') { - b.popFront(); - continue; - } - - if (a.front.isNumber && b.front.isNumber) { - /* We need to switch to numeric comparison for embedded numbers so - * that e.g. UTF-8 is sorted before UTF-16. */ - int aValue = extractInt(a); - int bValue = extractInt(b); - - if (aValue < bValue) { - return true; - - } else if (aValue > bValue) { - return false; - } - - /* Numeric values are equal. Since a and b are already popped by - * extractNumeric(), just continue with the rest of the - * strings. */ - - } else { - /* Compare the corresponding characters. */ - - const result = indexSectionOrderChar(a.front, b.front, alphabet); - - if (result < 0) { - return true; - - } else if (result > 0) { - return false; - } - - a.popFront(); - b.popFront(); - } - } - - if (a.empty && !b.empty) { - /* Shorter string is sorted first. */ - return true; - } - - if (!a.empty && b.empty) { - return false; - } - - /* A final comparison to take the potential leading dot into account. */ - if (lhs.length < rhs.length) { - return true; - - } - - if (lhs.length == rhs.length) { - /* They may differ only in lower vs. upper case. Use the original - * strings to check for that. */ - return alphabet.is_less(lhs, rhs); - } - - return false; -} - -/* Ignores leading non-alpha characters if they indeed lead at least one alpha - * character. */ -dchar initialLetter(string s) -{ - enforce(!s.empty); - - auto maybeRemoved = removeLeadingNonAlphaMaybe(s); - - return maybeRemoved.empty ? s.front : maybeRemoved.front; -} diff --git a/ddili/src/anchorgen.d b/ddili/src/anchorgen.d deleted file mode 100644 index 3143007..0000000 --- a/ddili/src/anchorgen.d +++ /dev/null @@ -1,605 +0,0 @@ -/** - * This program processes an HTML file to generate: - * - * 1) The anchors for all HTML header tags (H1, H2, etc.) - * - * 2) A 'table of contents' file output using those anchors - * - * 3) Anchors for each chapter. (This is a hack because any H4 header is - * considered to be the beginning of a chapter.) - */ - -import std.stdio; -import std.regex; -import std.conv; -import std.string; -import std.array; -import std.exception; -import std.range; -import std.algorithm; -import std.uni; -import std.format; - -import alphabet; - -/* Represents an entry in the table of contents. */ -struct TocEntry -{ - string class_; /* The div class of the header. */ - size_t level; /* The level of the entry (H4, H5, etc.) */ - string anchorName; /* The name of the anchor without leading '#' */ - string value; /* The actual text of the entry */ -} - -struct IndexEntry -{ - string content; /* The actual text of the entry */ - string[] anchorValues; /* The names of the anchors without leading '#' */ - string[] unsanitizedAnchorValues; - /* The names of the anchors without leading '#' */ -} - -version (unittest) { - - void main() - {} - -} else { - -void main(string[] args) -{ - const preFileName = args[1]; /* The input file */ - const postFileName = args[2]; /* The output file */ - const tocFileName = args[3]; /* The table of contents file */ - const indexFileName = args[4]; /* The index file */ - const webIndexMacros = args[5]; /* The index body for the web version */ - const alphabetName = args[6]; /* "english", "turkish", etc. */ - - writefln("Generating %s and %s from %s", - tocFileName, postFileName, preFileName); - - auto preFile = File(preFileName, "r"); - auto postFile = File(postFileName, "w"); - - /* This table will be populated as each file of the input is processed. */ - TocEntry[] tocEntries; - IndexEntry[string] indexEntries; - - /* A mapping from an index anchor value to the number of times it has been - * seen so far. */ - size_t[string] indexAnchors; - - /* This is the name of the chapter anchor, obtained by a hack. */ - string chapterFileName; - - foreach (line; preFile.byLine) { - /* Every chapter generated by ddoc starts with the following - * comment. We will scrape the name of the original D file from that - * line. */ - auto r = regex(``); - - auto c = matchFirst(line, r); - - if (!c.empty) { - /* Modify the value of the chapter anchor to be used for the rest - * of the lines of this section of the input. */ - chapterFileName = c[2].to!string; - - postFile.writeln(line); - - } else { - /* This is an ordinary line. Process it to insert an anchor as - * necessary, as well as to populate the TOC table. */ - auto linePostTocAndXrefs = processChapterLineTocAndXrefs( - line, chapterFileName, tocEntries); - - const processedLine = processChapterLineIndexes( - linePostTocAndXrefs.idup, indexEntries); - - postFile.writeln(processedLine); - } - } - - /* XXX - Hack the index section into the toc section as it does not appear - * in the Makefile.in (partly because we have not generated it yet). - * - * The anchor content is replaced with "Dizin" in the pdf.derse_ozel.css - * file of each Turkish book. */ - tocEntries ~= TocEntry("index", 4, "chapter_index_section", "Index"); - - generateToc(tocFileName, tocEntries); - - writefln("Generating %s and %s", indexFileName, webIndexMacros); - - Alphabet alphabet = makeAlphabet(alphabetName); - generateIndex(indexFileName, webIndexMacros, indexEntries, alphabet); -} - -} /* version (unittest) else */ - -string makeChapterAnchorName(const(char)[] chapterFileName) -{ - return format("chapter_%s", chapterFileName); -} - -/* This is the recursive part of TOC generation. Creates a
    section for - * the specified level. We consume tocEntries as we move along. */ -void generateToc_R(File toc, size_t level, ref const(TocEntry)[] tocEntries) -{ - bool alreadyPrintedForThisLevel = false; - - toc.writeln(`
      `); - scope(exit) { - if (alreadyPrintedForThisLevel) { - toc.writeln(""); - } - toc.writeln("
    "); - } - - while (!tocEntries.empty) { - auto entry = tocEntries.front; - - if (entry.level == level) { - if (alreadyPrintedForThisLevel) { - toc.writeln(""); - } - - /* This is one of our levels; process it. */ - toc.writeln(format(`
  • %s`, - entry.class_.empty ? "toc" : entry.class_, - entry.anchorName, entry.value)); - tocEntries.popFront(); - - alreadyPrintedForThisLevel = true; - - } else if (entry.level < level) { - /* We are moving out to the previous level. We are done. */ - break; - - } else if (entry.level > level) { - if ((entry.level >= 5) - && - [ "index_section", "solution_subsection" ] - .canFind(entry.class_)) { - /* HACK: Do not include any deeper level for "Sözlük", - * "Dizin" ("Index"), and "Exercise Solutions" sections. */ - tocEntries.popFront(); - - } else { - /* We are going in to a deeper level; recurse. */ - generateToc_R(toc, entry.level, tocEntries); - } - } - } -} - -/* This is the non-recurse entry to the TOC generation. */ -void generateToc(string tocFileName, const(TocEntry)[] tocEntries) -{ - auto toc = File(tocFileName, "w"); - generateToc_R(toc, tocEntries[0].level, tocEntries); -} - -const indexLinkChar = "⬁"; - -const indexLinkStrings = - iota(1, 10) - .map!(i => format("%s%s", indexLinkChar, i.to!string)) - .array; - -string anchorFileName(string ix) -{ - enum idxExpr = regex(`ix_(.*?)\.`); - - auto match = matchFirst(ix, idxExpr); - enforce(!match.empty, format("Unexpected index anchor: %s", ix)); - - return match[1]; -} - -void generateIndex(string indexFileName, - string webIndexMacros, - const(IndexEntry[string]) indexEntries, - Alphabet alphabet) -{ - auto idx = File(indexFileName, "w"); - auto webIdx = File(webIndexMacros, "w"); - - idx.writeln(`
    `); - scope(exit) idx.writeln("
    "); - - idx.writeln(`
      `); - scope(exit) idx.writeln("
    "); - - webIdx.write(`INDEX_ENTRIES=`); - - dchar lastInitial = ' '; - - auto keys = indexEntries.keys; - auto sortedKeys = sort!((l, r) => indexSectionOrder(l, r, alphabet))(keys); - - foreach (key; sortedKeys) { - const entry = indexEntries[key]; - - if (entry.content.front == 'ı') { - throw new Exception( - format("Limitation: Current framework will sort this entry " ~ - " among the 'i's: %s (grep for $(IX %s)).", - entry, entry.content)); - } - - dchar initial = alphabet.toUpper(initialLetter(entry.content)); - - if ((initial != lastInitial) && initial.isAlpha) { - if (((initial == 'I') && (lastInitial == 'İ')) || - ((initial == 'İ') && (lastInitial == 'I'))) { - - /* HACK: Ignore this case; we do not distinguish between 'i' - * and 'I'. (We assume that all words that start with 'I' are - * English and should be listed with the 'i's.) */ - - } else { - idx.writeln("
"); - idx.writefln(`
%s
`, initial); - idx.writeln(`