diff --git a/README.md b/README.md index d468407..35a39cc 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 @@ -48,104 +48,105 @@ Instead of falling for getting things done quickly, "Programming in D" focuses o ## Progress 进度 ### Translated 已完成翻译 -- [编译 Compilation](ddili/src/ders/d.cn/compiler.d) By Lucifer & meatatt -- [元组 Tuples](ddili/src/ders/d.cn/tuples.d) By meatatt -- [is 表达式 is Expression](ddili/src/ders/d.cn/is_expr.d) By meatatt -- [命名作用域 Name Scope](ddili/src/ders/d.cn/name_space.d) By LinGo -- [从标准输入中读取数据 Reading from the Standard Input](ddili/src/ders/d.cn/input.d) By Lucifer -- [前言 Preface(合并自:Acknowledgments,Introduction)](ddili/src/ders/d.cn/preface.d) By IceNature -- [文件 Files](ddili/src/ders/d.cn/files.d) By IceNature -- [if 语句 if Statement](ddili/src/ders/d.cn/if.d) By IceNature -- [auto 和 typeof auto and typeof](ddili/src/ders/d.cn/auto_and_typeof.d) By andot -- [Object](ddili/src/ders/d.cn/object.d) By 大处着手小处着眼 -- [异常 Exceptions](ddili/src/ders/d.cn/exceptions.d) By xmdqvb -- [scope](ddili/src/ders/d.cn/scope.d) By 大处着手小处着眼 -- [类 Classes](ddili/src/ders/d.cn/class.d) By 大处着手小处着眼 -- [并行 Parallelism](ddili/src/ders/d.cn/parallelism.d) By IceNature -- [关联数组 Associative Arrays](ddili/src/ders/d.cn/aa.d) By 大处着手小处着眼 -- [do-while 循环 do-while Loop](ddili/src/ders/d.cn/do_while.d) By mogu -- [alias 与 with alias and with](ddili/src/ders/d.cn/alias.d) By mogu -- [alias this](ddili/src/ders/d.cn/alias_this.d) By mogu -- [并发消息传递 Message Passing Concurrency](ddili/src/ders/d.cn/concurrency.d) By IceNature -- [字符串 Strings](ddili/src/ders/d.cn/strings.d) By 大处着手小处着眼 -- [惰性运算符 Lazy Operators](ddili/src/ders/d.cn/lazy_operators.d) By mogu -- [左值与右值 Lvalues and Rvalues](ddili/src/ders/d.cn/lvalue_rvalue.d) By mogu -- [三元运算符 ?: Ternary Operator ?:](ddili/src/ders/d.cn/ternary.d) By IceNature -- [模板 Templates](ddili/src/ders/d.cn/templates.d) By meatatt -- [通用函数调用语法 Universal Function Call Syntax (UFCS)](ddili/src/ders/d.cn/ufcs.d) By mogu -- [属性 Properties](ddili/src/ders/d.cn/property.d) By IceNature -- [const ref 参数 and const 成员函数 const ref Parameters and const Member Functions](ddili/src/ders/d.cn/const_member_functions.d) By IceNature -- [格式化输出 Formatted Output](ddili/src/ders/d.cn/formatted_output.d) By IceNature -- [Pragmas](ddili/src/ders/d.cn/pragma.d) By mogu +- [数组 Arrays](target/arrays.d) By 大处着手小处着眼 +- [编译 Compilation](target/compiler.d) By Lucifer & meatatt +- [元组 Tuples](target/tuples.d) By meatatt +- [is 表达式 is Expression](target/is_expr.d) By meatatt +- [命名作用域 Name Scope](target/name_space.d) By LinGo +- [从标准输入中读取数据 Reading from the Standard Input](target/input.d) By Lucifer +- [前言 Preface(合并自:Acknowledgments,Introduction)](target/preface.d) By IceNature +- [文件 Files](target/files.d) By IceNature +- [if 语句 if Statement](target/if.d) By IceNature +- [auto 和 typeof auto and typeof](target/auto_and_typeof.d) By andot +- [Object](target/object.d) By 大处着手小处着眼 +- [异常 Exceptions](target/exceptions.d) By xmdqvb +- [scope](target/scope.d) By 大处着手小处着眼 +- [类 Classes](target/class.d) By 大处着手小处着眼 +- [并行 Parallelism](target/parallelism.d) By IceNature +- [关联数组 Associative Arrays](target/aa.d) By 大处着手小处着眼 +- [do-while 循环 do-while Loop](target/do_while.d) By mogu +- [alias 与 with alias and with](target/alias.d) By mogu +- [alias this](target/alias_this.d) By mogu +- [并发消息传递 Message Passing Concurrency](target/concurrency.d) By IceNature +- [字符串 Strings](target/strings.d) By 大处着手小处着眼 +- [惰性运算符 Lazy Operators](target/lazy_operators.d) By mogu +- [左值与右值 Lvalues and Rvalues](target/lvalue_rvalue.d) By mogu +- [三元运算符 ?: Ternary Operator ?:](target/ternary.d) By IceNature +- [模板 Templates](target/templates.d) By meatatt +- [通用函数调用语法 Universal Function Call Syntax (UFCS)](target/ufcs.d) By mogu +- [属性 Properties](target/property.d) By IceNature +- [const ref 参数 and const 成员函数 const ref Parameters and const Member Functions](target/const_member_functions.d) By IceNature +- [格式化输出 Formatted Output](target/formatted_output.d) By IceNature +- [Pragmas](target/pragma.d) By mogu +- [自定义属性 (UDA)](target/uda.d) by Heromyth ### Reviewing 审核中 -- [Slices and Other Array Features](ddili/src/ders/d.cn/slices.d) By 大处着手小处着眼 -- [More Templates](ddili/src/ders/d.cn/templates_more.d) By meatatt +- [Slices and Other Array Features](target/slices.d) By 大处着手小处着眼 +- [More Templates](target/templates_more.d) By meatatt ### Review Delayed 审核搁置 -- [Arrays](ddili/src/ders/d.cn/arrays.d) By 大处着手小处着眼 + ### Outdated 旧项目待更新 -- [The Hello World Program](ddili/src/ders/d.cn/hello_world.d) By Lucifer -- [writeln and write](ddili/src/ders/d.cn/writeln.d) By Lucifer -- [Fundamental Types](ddili/src/ders/d.cn/types.d) By Lucifer -- [Assignment and Order of Evaluation](ddili/src/ders/d.cn/assignment.d) By Lucifer -- [Variables](ddili/src/ders/d.cn/variables.d) By Lucifer -- [Standard Input and Output Streams](ddili/src/ders/d.cn/io.d) By Lucifer +- [The Hello World Program](target/hello_world.d) By Lucifer +- [writeln and write](target/writeln.d) By Lucifer +- [Fundamental Types](target/types.d) By Lucifer +- [Assignment and Order of Evaluation](target/assignment.d) By Lucifer +- [Variables](target/variables.d) By Lucifer +- [Standard Input and Output Streams](target/io.d) By Lucifer ### Adopted 已被领取 -- [Lifetimes and Fundamental Operations](ddili/src/ders/d.cn/lifetimes.d) By 渡世白玉 -- [Ranges](ddili/src/ders/d.cn/ranges.d) By Lucifer -- [while Loop](ddili/src/ders/d.cn/while.d) By KimmyLeo -- [Redirecting Standard Input and Output Streams](ddili/src/ders/d.cn/stream_redirect.d) By mogu -- [Function Pointers, Delegates, and Lambdas](ddili/src/ders/d.cn/lambda.d) By zhaopuming -- [Modules and Libraries](ddili/src/ders/d.cn/modules.d) By 大处着手小处着眼 -- [Variable Number of Parameters](ddili/src/ders/d.cn/parameter_flexibility.d) By meatatt -- [Conditional Compilation](ddili/src/ders/d.cn/cond_comp.d) By IceNature -- [Immutability](ddili/src/ders/d.cn/const_and_immutable.d) By IceNature -- [Type Conversions](ddili/src/ders/d.cn/cast.d) By IceNature -- [Formatted Input](ddili/src/ders/d.cn/formatted_input.d) By IceNature +- [Lifetimes and Fundamental Operations](target/lifetimes.d) By 渡世白玉 +- [Ranges](target/ranges.d) By Lucifer +- [while Loop](target/while.d) By KimmyLeo +- [Redirecting Standard Input and Output Streams](target/stream_redirect.d) By mogu +- [Function Pointers, Delegates, and Lambdas](target/lambda.d) By zhaopuming +- [Modules and Libraries](target/modules.d) By 大处着手小处着眼 +- [Variable Number of Parameters](target/parameter_flexibility.d) By meatatt +- [Conditional Compilation](target/cond_comp.d) By IceNature +- [Immutability](target/const_and_immutable.d) By IceNature +- [Type Conversions](target/cast.d) By IceNature +- [Formatted Input](target/formatted_input.d) By IceNature ### Orphans 待领取 -- [Foreword by Andrei Alexandrescu](ddili/src/ders/d.cn/foreword2.d) -- [Logical Expressions](ddili/src/ders/d.cn/logical_expressions.d) -- [Integers and Arithmetic Operations](ddili/src/ders/d.cn/arithmetic.d) -- [Floating Point Types](ddili/src/ders/d.cn/floating_point.d) -- [Characters](ddili/src/ders/d.cn/characters.d) -- [for Loop](ddili/src/ders/d.cn/for.d) -- [Literals](ddili/src/ders/d.cn/literals.d) -- [foreach Loop](ddili/src/ders/d.cn/foreach.d) -- [switch and case](ddili/src/ders/d.cn/switch_case.d) -- [enum](ddili/src/ders/d.cn/enum.d) -- [Functions](ddili/src/ders/d.cn/functions.d) -- [Value Types and Reference Types](ddili/src/ders/d.cn/value_vs_reference.d) -- [Function Parameters](ddili/src/ders/d.cn/function_parameters.d) -- [Program Environment](ddili/src/ders/d.cn/main.d) -- [assert and enforce](ddili/src/ders/d.cn/assert.d) -- [Unit Testing](ddili/src/ders/d.cn/unit_testing.d) -- [Contract Programming](ddili/src/ders/d.cn/contracts.d) -- [The null Value and the is Operator](ddili/src/ders/d.cn/null_is.d) -- [Structs](ddili/src/ders/d.cn/struct.d) -- [Function Overloading](ddili/src/ders/d.cn/function_overloading.d) -- [Member Functions](ddili/src/ders/d.cn/member_functions.d) -- [Constructor and Other Special Functions](ddili/src/ders/d.cn/special_functions.d) -- [Operator Overloading](ddili/src/ders/d.cn/operator_overloading.d) -- [Inheritance](ddili/src/ders/d.cn/inheritance.d) -- [Interfaces](ddili/src/ders/d.cn/interface.d) -- [destroy and scoped](ddili/src/ders/d.cn/destroy.d) -- [Encapsulation and Protection Attributes](ddili/src/ders/d.cn/encapsulation.d) -- [Contract Programming for Structs and Classes](ddili/src/ders/d.cn/invariant.d) -- [Pointers](ddili/src/ders/d.cn/pointers.d) -- [Bit Operations](ddili/src/ders/d.cn/bit_operations.d) -- [foreach with Structs and Classes](ddili/src/ders/d.cn/foreach_opapply.d) -- [Nested Functions, Structs, and Classes](ddili/src/ders/d.cn/nested.d) -- [Unions](ddili/src/ders/d.cn/union.d) -- [Labels and goto](ddili/src/ders/d.cn/goto.d) -- [More Functions](ddili/src/ders/d.cn/functions_more.d) -- [Mixins](ddili/src/ders/d.cn/mixin.d) -- [More Ranges](ddili/src/ders/d.cn/ranges_more.d) -- [Data Sharing Concurrency](ddili/src/ders/d.cn/concurrency_shared.d) -- [Fibers](ddili/src/ders/d.cn/fibers.d) -- [Memory Management](ddili/src/ders/d.cn/memory.d) -- [User Defined Attributes (UDA)](ddili/src/ders/d.cn/uda.d) -- [Operator Precedence](ddili/src/ders/d.cn/operator_precedence.d) +- [Foreword by Andrei Alexandrescu](target/foreword2.d) +- [Logical Expressions](target/logical_expressions.d) +- [Integers and Arithmetic Operations](target/arithmetic.d) +- [Floating Point Types](target/floating_point.d) +- [Characters](target/characters.d) +- [for Loop](target/for.d) +- [Literals](target/literals.d) +- [foreach Loop](target/foreach.d) +- [switch and case](target/switch_case.d) +- [enum](target/enum.d) +- [Functions](target/functions.d) +- [Value Types and Reference Types](target/value_vs_reference.d) +- [Function Parameters](target/function_parameters.d) +- [Program Environment](target/main.d) +- [assert and enforce](target/assert.d) +- [Unit Testing](target/unit_testing.d) +- [Contract Programming](target/contracts.d) +- [The null Value and the is Operator](target/null_is.d) +- [Structs](target/struct.d) +- [Function Overloading](target/function_overloading.d) +- [Member Functions](target/member_functions.d) +- [Constructor and Other Special Functions](target/special_functions.d) +- [Operator Overloading](target/operator_overloading.d) +- [Inheritance](target/inheritance.d) +- [Interfaces](target/interface.d) +- [destroy and scoped](target/destroy.d) +- [Encapsulation and Protection Attributes](target/encapsulation.d) +- [Contract Programming for Structs and Classes](target/invariant.d) +- [Pointers](target/pointers.d) +- [Bit Operations](target/bit_operations.d) +- [foreach with Structs and Classes](target/foreach_opapply.d) +- [Nested Functions, Structs, and Classes](target/nested.d) +- [Unions](target/union.d) +- [Labels and goto](target/goto.d) +- [More Functions](target/functions_more.d) +- [Mixins](target/mixin.d) +- [More Ranges](target/ranges_more.d) +- [Data Sharing Concurrency](target/concurrency_shared.d) +- [Fibers](target/fibers.d) +- [Memory Management](target/memory.d) +- [Operator Precedence](target/operator_precedence.d) diff --git a/ddili/src/ders/d.en/alias.d b/d.en/alias.d similarity index 100% rename from ddili/src/ders/d.en/alias.d rename to d.en/alias.d diff --git a/d.en/alias_this.d b/d.en/alias_this.d new file mode 100644 index 0000000..d93d71e --- /dev/null +++ b/d.en/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.078.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.078.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); + +} do { + 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/ddili/src/ders/d.cn/arithmetic.cozum.d b/d.en/arithmetic.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/arithmetic.cozum.d rename to d.en/arithmetic.cozum.d diff --git a/ddili/src/ders/d.cn/arithmetic.d b/d.en/arithmetic.d similarity index 97% rename from ddili/src/ders/d.cn/arithmetic.d rename to d.en/arithmetic.d index bd22d23..cf6bffa 100644 --- a/ddili/src/ders/d.cn/arithmetic.d +++ b/d.en/arithmetic.d @@ -98,7 +98,7 @@ 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 have seen in the $(LINK2 /ders/d.en/types.html, Fundamental Types chapter) are the following: +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: ) @@ -208,7 +208,7 @@ I skipped many rows in the table and indicated the signed and unsigned versions $(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.) +D has no 3-bit type. But such a hypothetical type could only have 8 distinct values. It could only represent concepts such as the value of a die, or the week's day number. ) $(P @@ -710,6 +710,10 @@ For example, assuming that $(C a) and $(C b) are two $(C int) variables, the fol } --- +$(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 diff --git a/ddili/src/ders/d.cn/assert.cozum.d b/d.en/assert.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/assert.cozum.d rename to d.en/assert.cozum.d diff --git a/ddili/src/ders/d.cn/assert.d b/d.en/assert.d similarity index 97% rename from ddili/src/ders/d.cn/assert.d rename to d.en/assert.d index e905094..62f6c53 100644 --- a/ddili/src/ders/d.cn/assert.d +++ b/d.en/assert.d @@ -92,11 +92,11 @@ Each $(C assert) checks its assumption and terminates the program with an $(C As ) $(SHELL -core.exception.$(HILITE AssertError)@deneme(3): Assertion failure +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 3 of file $(C deneme.d). +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 @@ -112,7 +112,7 @@ The output: ) $(SHELL -core.exception.AssertError@deneme.d(3): $(HILITE Age cannot be negative.) +core.exception.AssertError@deneme.d(2): $(HILITE Age cannot be negative.) ) $(P @@ -232,7 +232,7 @@ $(C assert) is also the fundamental tool that is used in $(I unit testing) and $ $(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 they $(I should) have any side effects. +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 @@ -250,11 +250,11 @@ The compiler switch $(C -release) causes the $(C assert) checks to be ignored as ) $(SHELL -dmd deneme.d -release +$ dmd deneme.d -release ) $(P -This would allow programs run faster by not evaluating potentially slow logical expressions of the $(C assert) checks. +This would allow programs to run faster by not evaluating potentially slow logical expressions of the $(C assert) checks. ) $(P diff --git a/ddili/src/ders/d.cn/assignment.cozum.d b/d.en/assignment.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/assignment.cozum.d rename to d.en/assignment.cozum.d diff --git a/ddili/src/ders/d.cn/assignment.d b/d.en/assignment.d similarity index 100% rename from ddili/src/ders/d.cn/assignment.d rename to d.en/assignment.d diff --git a/ddili/src/ders/d.cn/auto_and_typeof.cozum.d b/d.en/auto_and_typeof.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/auto_and_typeof.cozum.d rename to d.en/auto_and_typeof.cozum.d diff --git a/ddili/src/ders/d.en/auto_and_typeof.d b/d.en/auto_and_typeof.d similarity index 100% rename from ddili/src/ders/d.en/auto_and_typeof.d rename to d.en/auto_and_typeof.d diff --git a/ddili/src/ders/d.cn/bit_operations.cozum.d b/d.en/bit_operations.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/bit_operations.cozum.d rename to d.en/bit_operations.cozum.d diff --git a/ddili/src/ders/d.en/bit_operations.d b/d.en/bit_operations.d similarity index 91% rename from ddili/src/ders/d.en/bit_operations.d rename to d.en/bit_operations.d index 619e314..c839cb5 100644 --- a/ddili/src/ders/d.en/bit_operations.d +++ b/d.en/bit_operations.d @@ -310,7 +310,7 @@ Let's first define a function which will be useful later when examining how bit import std.stdio; void print(uint number) { - writefln("%032b %08x %10s", number, number, number); + writefln(" %032b %08x %10s", number, number, number); } void main() { @@ -323,7 +323,7 @@ Here is the same value printed in the binary, hexadecimal, and decimal number sy ) $(SHELL_SMALL -00000111010110111100110100010101 075bcd15 123456789 + 00000111010110111100110100010101 075bcd15 123456789 ) $(H6 $(IX ~, bitwise complement) $(IX complement, bitwise operator) Complement operator: $(C ~)) @@ -338,9 +338,9 @@ This operator converts each bit of a value to its opposite: The bits that are 0 --- uint value = 123456789; - write(" "); print(value); + print(value); writeln("~ --------------------------------"); - write(" "); print($(HILITE ~)value); + print($(HILITE ~)value); --- $(P @@ -372,10 +372,10 @@ $(C &) is a binary operator, written between two expressions. The microprocessor uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("& --------------------------------"); - write(" "); print(lhs $(HILITE &) rhs); + print(lhs $(HILITE &) rhs); --- $(P @@ -426,10 +426,10 @@ $(C |) is a binary operator, written between two expressions. The microprocessor uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("| --------------------------------"); - write(" "); print(lhs $(HILITE |) rhs); + print(lhs $(HILITE |) rhs); --- $(SHELL_SMALL @@ -472,10 +472,10 @@ $(I Xor) is the short for $(I exclusive or). This is a binary operator as well. uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("^ --------------------------------"); - write(" "); print(lhs $(HILITE ^) rhs); + print(lhs $(HILITE ^) rhs); --- $(SHELL_SMALL @@ -515,7 +515,7 @@ Regardless of its value, $(I xorring) a variable with itself always produces 0: --- $(SHELL_SMALL -00000000000000000000000000000000 00000000 0 + 00000000000000000000000000000000 00000000 0 ) $(H6 $(IX >>) $(IX right shift, bitwise operator) Right-shift operator: $(C >>)) @@ -535,12 +535,12 @@ The following example produces a result by shifting a value by two bits to the r --- $(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 will get the value 0: +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 + 000001110101101111001101000101$(HILITE 01) 075bcd15 123456789 + $(HILITE 00)000001110101101111001101000101 01d6f345 30864197 ) $(P @@ -566,12 +566,12 @@ Because the leftmost bit of the original value is 1, all of the new bits of the ) $(SHELL_SMALL -$(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 111)10000000000000010000001100000 f0002060 4026540128 + $(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 111)10000000000000010000001100000 f0002060 4026540128 ) $(P -When the leftmost bit is 0, then all of the news bits are 0: +When the leftmost bit is 0, then all new bits are 0: ) --- @@ -581,8 +581,8 @@ When the leftmost bit is 0, then all of the news bits are 0: --- $(SHELL_SMALL -$(U 0)1000000000000010000001100000$(HILITE 000) 40010300 1073808128 -$(HILITE 000)01000000000000010000001100000 08002060 134226016 + $(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 >>>)) @@ -598,8 +598,8 @@ This operator works similarly to the regular right-shift operator. The differenc --- $(SHELL_SMALL -10000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 000)10000000000000010000001100000 10002060 268443744 + 10000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 000)10000000000000010000001100000 10002060 268443744 ) $(H6 $(IX <<) $(IX left shift, bitwise operator) Left-shift operator: $(C <<)) @@ -619,14 +619,14 @@ The bits on the left-hand side are lost and the new bits on the right-hand side ) $(SHELL_SMALL -$(HILITE 0000)0111010110111100110100010101 075bcd15 123456789 -0111010110111100110100010101$(HILITE 0000) 75bcd150 1975308624 + $(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 have seen in $(LINK2 /ders/d.en/arithmetic.html, the Integers and Arithmetic Operations chapter), these operators assign the result back to the left-hand operand. +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 @@ -658,10 +658,10 @@ As an extreme example, let's consider two values that both have alternating bits uint lhs = 0xaaaaaaaa; uint rhs = 0x55555555; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("| --------------------------------"); - write(" "); print(lhs | rhs); + print(lhs | rhs); --- $(SHELL_SMALL @@ -685,10 +685,10 @@ As an extreme example, let's consider the last two values again. Since none of t uint lhs = 0xaaaaaaaa; uint rhs = 0x55555555; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("& --------------------------------"); - write(" "); print(lhs & rhs); + print(lhs & rhs); --- $(SHELL_SMALL @@ -708,11 +708,11 @@ To understand how this works, it helps to see one of the expressions as the $(I uint expression = 0x00ff00ff; uint bitsToSet = 0x10001000; - write("before : "); print(expression); - write("to set to 1: "); print(bitsToSet); + write("before :"); print(expression); + write("to set to 1:"); print(bitsToSet); expression $(HILITE |=) bitsToSet; - write("after : "); print(expression); + write("after :"); print(expression); --- $(P @@ -739,11 +739,11 @@ One of the expressions can be seen as the $(I actual) expression and the other e uint expression = 0x00ff00ff; uint bitsToClear = 0xffefffef; - write("before : "); print(expression); - write("bits to clear: "); print(bitsToClear); + write("before :"); print(expression); + write("bits to clear:"); print(bitsToClear); expression $(HILITE &=) bitsToClear; - write("after : "); print(expression); + write("after :"); print(expression); --- $(P @@ -780,8 +780,8 @@ The bit that is being $(I queried) is highlighted: ) $(SHELL_SMALL -000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 -00000000000000010000000000000000 00010000 65536 + 000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 + 00000000000000010000000000000000 00010000 65536 yes, 1 ) @@ -972,9 +972,9 @@ A mask consists of a number of 1 bits that would $(I cover) the specific part of uint value = 123456789; uint mask = 0x000000ff; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print(value & mask); + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); --- $(P @@ -982,9 +982,9 @@ The bits that are covered by the mask are highlighted. All of the other bits are ) $(SHELL_SMALL -value : 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 -mask : 00000000000000000000000011111111 000000ff 255 -result: 000000000000000000000000$(HILITE 00010101) 00000015 21 +value : 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 +mask : 00000000000000000000000011111111 000000ff 255 +result: 000000000000000000000000$(HILITE 00010101) 00000015 21 ) $(P @@ -995,9 +995,9 @@ Let's apply the same method to the 0xc0a80102 IPv4 address with a mask that woul uint value = 0xc0a80102; uint mask = 0xff000000; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print(value & mask); + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); --- $(P @@ -1005,9 +1005,9 @@ 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 +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: $(HILITE 11000000)000000000000000000000000 c0000000 3221225472 ) $(P @@ -1018,15 +1018,15 @@ However, note that the printed result is not the expected 192 but 3221225472. Th uint value = 0xc0a80102; uint mask = 0xff000000; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print((value & mask) $(HILITE >> 24)); + 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) +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: 000000000000000000000000$(HILITE 11000000) 000000c0 $(HILITE 192) ) $(PROBLEM_COK diff --git a/ddili/src/ders/d.cn/blurbs.d b/d.en/blurbs.d similarity index 100% rename from ddili/src/ders/d.cn/blurbs.d rename to d.en/blurbs.d diff --git a/ddili/src/ders/d.cn/cast.d b/d.en/cast.d similarity index 100% rename from ddili/src/ders/d.cn/cast.d rename to d.en/cast.d diff --git a/ddili/src/ders/d.en/characters.d b/d.en/characters.d similarity index 98% rename from ddili/src/ders/d.en/characters.d rename to d.en/characters.d index 40902fb..c7ce3a0 100644 --- a/ddili/src/ders/d.en/characters.d +++ b/d.en/characters.d @@ -49,7 +49,7 @@ IBM Corporation has defined a set of tables, each one of which assign the codes ) $(P -Despite being much 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. +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 diff --git a/ddili/src/ders/d.cn/code/README b/d.en/code/README similarity index 100% rename from ddili/src/ders/d.cn/code/README rename to d.en/code/README diff --git a/ddili/src/ders/d.cn/cond_comp.d b/d.en/cond_comp.d similarity index 91% rename from ddili/src/ders/d.cn/cond_comp.d rename to d.en/cond_comp.d index 24b6041..7a7b8c5 100644 --- a/ddili/src/ders/d.cn/cond_comp.d +++ b/d.en/cond_comp.d @@ -30,69 +30,6 @@ $(P Unit tests and contracts are about program correctness; whether they are included in the program should not change the behavior of the program. ) -$(UL - -$(LI -Template specializations are compiled into the program only for specific types. When a specialization is not actually used in the program, the specialization is not compiled: - ---- -void swap(T)(ref T lhs, ref T rhs) { - T temp = lhs; - lhs = rhs; - rhs = temp; -} - -unittest { - auto a = 'x'; - auto b = 'y'; - swap(a, b); - - assert(a == 'y'); - assert(b == 'x'); -} - -void swap(T $(HILITE : uint))(ref T lhs, ref T rhs) { - lhs ^= rhs; - rhs ^= lhs; - lhs ^= rh; // TYPO! -} - -void main() { -} ---- - -$(P -The $(C uint) specialization above has been implemented by taking advantage of the $(C ^) ($(I xor)) operator, presumably under the belief that it would be executed faster than the general algorithm. ($(I $(B Note:) To the contrary, on most modern microprocessors this method is slower than the one that uses a temporary variable.)) -) - -$(P -Despite the typo at the end of that specialization, since the $(C uint) specialization is never actually used, the program gets compiled without any errors. -) - -$(P -$(I $(B Note:) This is another example of how important unit tests are; the mistake would have been noticed if there were a unit test for that specialization: -) -) - ---- -unittest { - $(HILITE uint) i = 42; - $(HILITE uint) j = 7; - swap(i, j); - - assert(i == 7); - assert(j == 42); -} ---- - -$(P -This example shows that template specializations are also compiled under certain conditions. -) - -) - -) - $(P The following are the features of D that are specifically for conditional compilation: ) @@ -106,7 +43,7 @@ $(LI $(C __traits)) ) $(P -We will see the $(C is) expression in the next chapter and $(C __traits) in a later chapter. +We will see the $(C is) expression in the next chapter. ) $(H5 $(IX debug) debug) @@ -246,7 +183,7 @@ Such lines are included in the program only when the $(C -debug) compiler switch ) $(SHELL_SMALL -dmd deneme.d -ofdeneme -w $(HILITE -debug) +$ dmd deneme.d -ofdeneme -w $(HILITE -debug) ) $(H6 $(C debug($(I tag)))) @@ -264,7 +201,7 @@ The tagged $(C debug) statements are enabled by the $(C -debug=$(I tag)) compile ) $(SHELL_SMALL -dmd deneme.d -ofdeneme -w $(HILITE -debug=binarySearch) +$ dmd deneme.d -ofdeneme -w $(HILITE -debug=binarySearch) ) $(P diff --git a/ddili/src/ders/d.en/const_and_immutable.d b/d.en/const_and_immutable.d similarity index 98% rename from ddili/src/ders/d.en/const_and_immutable.d rename to d.en/const_and_immutable.d index e6113c0..61158ae 100644 --- a/ddili/src/ders/d.en/const_and_immutable.d +++ b/d.en/const_and_immutable.d @@ -67,7 +67,7 @@ We have seen earlier in the $(LINK2 /ders/d.en/enum.html, $(C enum) chapter) tha --- $(P -As long as their values can be determined at compile time, $(C enum) variables can be initialized by the return values of functions as well: +As long as their values can be determined at compile time, $(C enum) variables can be initialized with return values of functions as well: ) --- @@ -89,6 +89,11 @@ void main() { } --- +$(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: ) @@ -143,18 +148,6 @@ $(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. ) -$(P -$(C enum) constants can be initialized with results of function calls: -) - ---- - enum a = makeArray(); // called at compile time ---- - -$(P -This is possible with D's $(I compile time function execution) (CTFE) feature, which we will see in $(LINK2 /ders/d.en/functions_more.html, a later chapter). -) - $(H6 $(IX variable, immutable) $(C immutable) variables) $(P diff --git a/ddili/src/ders/d.en/const_member_functions.d b/d.en/const_member_functions.d similarity index 100% rename from ddili/src/ders/d.en/const_member_functions.d rename to d.en/const_member_functions.d diff --git a/ddili/src/ders/d.en/contracts.cozum.d b/d.en/contracts.cozum.d similarity index 99% rename from ddili/src/ders/d.en/contracts.cozum.d rename to d.en/contracts.cozum.d index ff22320..b2ec985 100644 --- a/ddili/src/ders/d.en/contracts.cozum.d +++ b/d.en/contracts.cozum.d @@ -20,7 +20,7 @@ in { } out (result) { assert((result >= 0) && (result <= 2)); -} body { +} do { int winner; if (goals1 > goals2) { diff --git a/ddili/src/ders/d.en/contracts.d b/d.en/contracts.d similarity index 95% rename from ddili/src/ders/d.en/contracts.d rename to d.en/contracts.d index f4e83f2..ecface9 100644 --- a/ddili/src/ders/d.en/contracts.d +++ b/d.en/contracts.d @@ -44,7 +44,7 @@ string timeToString(in int hour, in int 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: +$(IX do, contract programming) 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 do) block: ) --- @@ -56,7 +56,7 @@ $(HILITE in) { assert((hour >= 0) && (hour <= 23)); assert((minute >= 0) && (minute <= 59)); -} $(HILITE body) { +} $(HILITE do) { return format("%02s:%02s", hour, minute); } @@ -65,6 +65,10 @@ void main() { } --- +$(P +$(IX body) $(I $(B Note:) In earlier versions of D, the $(C body) keyword was used for this purpose instead of $(C do).) +) + $(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. ) @@ -100,7 +104,7 @@ int daysInFebruary(in int year) $(HILITE out (result)) { assert((result == 28) || (result == 29)); -} body { +} do { return isLeapYear(year) ? 29 : 28; } --- @@ -136,7 +140,7 @@ $(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 do): Mandatory but the $(C do) 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.) ) @@ -160,7 +164,7 @@ in { } out { assert(sum == (first + second)); -} body { +} do { first = (sum >= 7) ? 7 : sum; second = sum - first; } @@ -231,7 +235,7 @@ Contrary to unit testing, contract programming features are enabled by default. ) $(SHELL -dmd deneme.d -w -release +$ dmd deneme.d -w -release ) $(P @@ -245,7 +249,7 @@ We have seen in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) ) $(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 have seen in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter): +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 @@ -272,7 +276,7 @@ inout(int)[] middle(inout(int)[] originalSlice, size_t width) out (result) { assert(result.length == width); -} body { +} do { $(HILITE enforce)(originalSlice.length >= width); immutable start = (originalSlice.length - width) / 2; @@ -334,7 +338,7 @@ in { } out (result) { // ... -} body { +} do { int winner; // ... diff --git a/ddili/src/ders/d.en/copyright.d b/d.en/copyright.d similarity index 98% rename from ddili/src/ders/d.en/copyright.d rename to d.en/copyright.d index e812e67..1aac300 100644 --- a/ddili/src/ders/d.en/copyright.d +++ b/d.en/copyright.d @@ -22,7 +22,7 @@ $(BR) $(BR) $(P -Copyleft (ɔ) 2009-2015 Ali Çehreli +Copyleft (ɔ) 2009-2018 Ali Çehreli ) $(BR) diff --git a/ddili/src/ders/d.en/cozum_ozel.ddoc b/d.en/cozum_ozel.ddoc similarity index 100% rename from ddili/src/ders/d.en/cozum_ozel.ddoc rename to d.en/cozum_ozel.ddoc diff --git a/ddili/src/ders/d.en/destroy.d b/d.en/destroy.d similarity index 99% rename from ddili/src/ders/d.en/destroy.d rename to d.en/destroy.d index 8d1273d..9a39c66 100644 --- a/ddili/src/ders/d.en/destroy.d +++ b/d.en/destroy.d @@ -351,7 +351,7 @@ $(SHELL $(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 have seen in the $(LINK2 /ders/d.en/exceptions.html, Exceptions chapter). +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 diff --git a/ddili/src/ders/d.en/do_while.cozum.d b/d.en/do_while.cozum.d similarity index 100% rename from ddili/src/ders/d.en/do_while.cozum.d rename to d.en/do_while.cozum.d diff --git a/ddili/src/ders/d.en/do_while.d b/d.en/do_while.d similarity index 100% rename from ddili/src/ders/d.en/do_while.d rename to d.en/do_while.d diff --git a/ddili/src/ders/d.cn/encapsulation.d b/d.en/encapsulation.d similarity index 100% rename from ddili/src/ders/d.cn/encapsulation.d rename to d.en/encapsulation.d diff --git a/ddili/src/ders/d.en/enum.cozum.d b/d.en/enum.cozum.d similarity index 97% rename from ddili/src/ders/d.en/enum.cozum.d rename to d.en/enum.cozum.d index 235ff17..026b0b4 100644 --- a/ddili/src/ders/d.en/enum.cozum.d +++ b/d.en/enum.cozum.d @@ -19,7 +19,7 @@ void main() { } writeln(); - // Infinite loop until the user wants to exit + // Unconditional loop until the user wants to exit while (true) { write("Operation? "); diff --git a/ddili/src/ders/d.cn/enum.d b/d.en/enum.d similarity index 97% rename from ddili/src/ders/d.cn/enum.d rename to d.en/enum.d index 9d38a6f..73db7c3 100644 --- a/ddili/src/ders/d.cn/enum.d +++ b/d.en/enum.d @@ -205,6 +205,10 @@ $(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 and 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 diff --git a/ddili/src/ders/d.en/exceptions.d b/d.en/exceptions.d similarity index 99% rename from ddili/src/ders/d.en/exceptions.d rename to d.en/exceptions.d index 7c1ef02..4198347 100644 --- a/ddili/src/ders/d.en/exceptions.d +++ b/d.en/exceptions.d @@ -938,11 +938,11 @@ The program terminates with an $(C assert) failure: ) $(SHELL_SMALL -core.exception.AssertError@$(HILITE deneme.d(3)): Assertion failure +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 3 of deneme.d has failed. +$(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) diff --git a/ddili/src/ders/d.en/files.cozum.d b/d.en/files.cozum.d similarity index 100% rename from ddili/src/ders/d.en/files.cozum.d rename to d.en/files.cozum.d diff --git a/ddili/src/ders/d.en/files.d b/d.en/files.d similarity index 100% rename from ddili/src/ders/d.en/files.d rename to d.en/files.d diff --git a/ddili/src/ders/d.cn/floating_point.cozum.d b/d.en/floating_point.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/floating_point.cozum.d rename to d.en/floating_point.cozum.d diff --git a/ddili/src/ders/d.en/floating_point.d b/d.en/floating_point.d similarity index 87% rename from ddili/src/ders/d.en/floating_point.d rename to d.en/floating_point.d index c8a4da6..ab04310 100644 --- a/ddili/src/ders/d.en/floating_point.d +++ b/d.en/floating_point.d @@ -354,18 +354,14 @@ $(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 Comparing floating point values) +$(H5 $(IX unordered) Unorderedness) $(P -We have seen the following comparison operations for integers: equal to ($(C ==)), not equal to ($(C !=)), less than ($(C <)), greater than ($(C >)), less than or equal to ($(C <=)), and greater than or equal to ($(C >=)). Floating point types have many more comparison operators. +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 -Since the special value $(C .nan) represents invalid floating point values, some comparisons with other values are not meaningful. For example, it is meaningless to ask whether $(C .nan) or 1 is greater. -) - -$(P -$(IX unordered) For that reason, floating point values introduce another comparison concept: unordered. Being unordered means that at least one of the values is $(C .nan). +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 @@ -373,8 +369,6 @@ The following table lists all the floating point comparison operators. All of th ) $(P -$(IX <>) -$(IX !<>) 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. ) @@ -402,27 +396,6 @@ The last column indicates whether the operation is meaningful if one of the oper - - - - - - - - - - - - - - - - - - - - -
<=is less than or equal to $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
!<>=is not less than, not greater than, nor equal to $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
<>is less than or greater than $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
<>=is less than, greater than, or equal to $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no)
!<=is not less than nor equal to $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
!<is not less than $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
!>=is not greater than nor equal to $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
!>is not greater than $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
!<>is not less than nor greater than $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes)
diff --git a/ddili/src/ders/d.cn/for.cozum.d b/d.en/for.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/for.cozum.d rename to d.en/for.cozum.d diff --git a/ddili/src/ders/d.cn/for.d b/d.en/for.d similarity index 100% rename from ddili/src/ders/d.cn/for.d rename to d.en/for.d diff --git a/ddili/src/ders/d.cn/foreach.cozum.d b/d.en/foreach.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/foreach.cozum.d rename to d.en/foreach.cozum.d diff --git a/ddili/src/ders/d.cn/foreach.d b/d.en/foreach.d similarity index 95% rename from ddili/src/ders/d.cn/foreach.d rename to d.en/foreach.d index f438c75..bc268f4 100644 --- a/ddili/src/ders/d.cn/foreach.d +++ b/d.en/foreach.d @@ -251,19 +251,24 @@ $(C foreach) can also be used with objects of user-defined types that define the $(H5 $(IX counter, foreach) The counter is automatic only for arrays) $(P -The automatic counter is provided only when iterating over arrays. When a counter is needed while iterating over other types of containers, the counter can be defined and incremented explicitly: +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 i = 0; + size_t $(HILITE i) = 0; foreach (element; container) { // ... ++i; } --- +) $(P -Such a variable is also needed when counting a specific condition. For example, the following code counts only the values that are divisible by 10: +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: ) --- diff --git a/ddili/src/ders/d.cn/foreach_opapply.cozum.d b/d.en/foreach_opapply.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/foreach_opapply.cozum.d rename to d.en/foreach_opapply.cozum.d diff --git a/ddili/src/ders/d.en/foreach_opapply.d b/d.en/foreach_opapply.d similarity index 99% rename from ddili/src/ders/d.en/foreach_opapply.d rename to d.en/foreach_opapply.d index 34b5c9d..0e843dd 100644 --- a/ddili/src/ders/d.en/foreach_opapply.d +++ b/d.en/foreach_opapply.d @@ -314,7 +314,6 @@ The following is a definition of $(C NumberRange) that is implemented according struct NumberRange { int begin; int end; - // (2) (1) int opApply(int delegate(ref int) operations) const { int result = 0; diff --git a/ddili/src/ders/d.cn/foreword1.d b/d.en/foreword1.d similarity index 100% rename from ddili/src/ders/d.cn/foreword1.d rename to d.en/foreword1.d diff --git a/ddili/src/ders/d.cn/foreword2.d b/d.en/foreword2.d similarity index 100% rename from ddili/src/ders/d.cn/foreword2.d rename to d.en/foreword2.d diff --git a/ddili/src/ders/d.cn/formatted_input.cozum.d b/d.en/formatted_input.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/formatted_input.cozum.d rename to d.en/formatted_input.cozum.d diff --git a/ddili/src/ders/d.cn/formatted_input.d b/d.en/formatted_input.d similarity index 100% rename from ddili/src/ders/d.cn/formatted_input.d rename to d.en/formatted_input.d diff --git a/ddili/src/ders/d.en/formatted_output.cozum.d b/d.en/formatted_output.cozum.d similarity index 100% rename from ddili/src/ders/d.en/formatted_output.cozum.d rename to d.en/formatted_output.cozum.d diff --git a/d.en/formatted_output.d b/d.en/formatted_output.d new file mode 100644 index 0000000..899c9f0 --- /dev/null +++ b/d.en/formatted_output.d @@ -0,0 +1,705 @@ +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 + + , digit separators + + ( 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 several parts, most of which are optional. The part named $(I position) will be explained later below. The others 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 separator 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 argument is printed in the binary system. +) + +$(P $(IX %o, output) $(C o): An integer argument is printed in the octal system. +) + +$(P $(IX %x, output) $(IX %X) $(C x) and $(C X): An integer argument 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 argument 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 argument 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 argument 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 argument 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 argument: +) + +$(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 argument is printed in. If the width is specified as the character $(C *), then the actual width value is read from the next argument (that argument 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 $(PERCENT),) $(IX separator) $(I separator)) + +$(P +The comma character specifies to separate digits of a number in groups. The default number of digits in a group is 3 but it can be specified after the comma: +) + +--- + writefln("%,f", 1234.5678); // Groups of 3 + writefln("%,s", 1000000); // Groups of 3 + writefln("%,2s", 1000000); // Groups of 2 +--- + +$(SHELL +1,234.567,800 +1,000,000 +1,00,00,00 +) + +$(P +If the number of digits is specified as the character $(C *), then the actual number of digits is read from the next argument (that argument must be an $(C int)). +) + +--- + writefln("%,*s", $(HILITE 1), 1000000); // Groups of 1 +--- + +$(SHELL +1,0,0,0,0,0,0 +) + +$(P +Similarly, it is possible to specify the separator character by using a question mark after the comma and providing the character as an additional argument before the number: +) + +--- + writefln("%,?s", $(HILITE '.'), 1000000); // The separator is '.' +--- + +$(SHELL +1$(HILITE .)000$(HILITE .)000 +) + +$(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 argument (that argument 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 arguments 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 arguments. Arguments are numbered in increasing fashion, starting with 1. The argument 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 argument 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 argument 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, arguments 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 arguments 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 arguments can be specified by numbers, such as $(C 1$) and $(C 2$), to associate each specifier with the exact argument: +) + +--- + 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 arguments 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/ddili/src/ders/d.cn/frontispiece.d b/d.en/frontispiece.d similarity index 100% rename from ddili/src/ders/d.cn/frontispiece.d rename to d.en/frontispiece.d diff --git a/ddili/src/ders/d.cn/function_overloading.cozum.d b/d.en/function_overloading.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/function_overloading.cozum.d rename to d.en/function_overloading.cozum.d diff --git a/ddili/src/ders/d.cn/function_overloading.d b/d.en/function_overloading.d similarity index 100% rename from ddili/src/ders/d.cn/function_overloading.d rename to d.en/function_overloading.d diff --git a/ddili/src/ders/d.cn/function_parameters.cozum.d b/d.en/function_parameters.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/function_parameters.cozum.d rename to d.en/function_parameters.cozum.d diff --git a/ddili/src/ders/d.en/function_parameters.d b/d.en/function_parameters.d similarity index 96% rename from ddili/src/ders/d.en/function_parameters.d rename to d.en/function_parameters.d index 43021da..c648af5 100644 --- a/ddili/src/ders/d.en/function_parameters.d +++ b/d.en/function_parameters.d @@ -305,10 +305,6 @@ void foo(in int value) { } --- -$(P -$(C in) is the equivalent of $(C const scope). -) - $(H6 $(IX out, parameter) $(C out)) $(P @@ -747,17 +743,19 @@ Calculating $(H6 $(IX scope) $(C scope)) $(P -This keyword specifies that a parameter will not be used beyond the scope of the function: +$(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. ) -$(COMMENT_XXX We don't use the DERLEME_HATASI macro below because the codetester fails because 'scope' does not cause a compilation error.) +$(SHELL +$(SHELL_OBSERVED $) dmd -dip1000 deneme.d +) --- int[] globalSlice; -int[] foo($(HILITE scope) int[] parameter) { - globalSlice = parameter; // ← compilation ERROR - return parameter; // ← compilation ERROR +$(HILITE @safe) int[] foo($(HILITE scope) int[] parameter) { + globalSlice = parameter; $(DERLEME_HATASI) + return parameter; $(DERLEME_HATASI) } void main() { @@ -767,11 +765,7 @@ void main() { --- $(P -That function violates the promise of $(C scope) in two places: It assigns the parameter to a global variable, and it returns it. Both of those would make it possible for the parameter to be accessed after the function finishes. -) - -$(P -($(I $(B Note:) dmd 2.071, the latest compiler used to compile the examples in this chapter, did not yet support the $(C scope) keyword.)) +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)) diff --git a/ddili/src/ders/d.cn/functions.cozum.d b/d.en/functions.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/functions.cozum.d rename to d.en/functions.cozum.d diff --git a/ddili/src/ders/d.cn/functions.d b/d.en/functions.d similarity index 98% rename from ddili/src/ders/d.cn/functions.d rename to d.en/functions.d index 48b2119..9ec9d24 100644 --- a/ddili/src/ders/d.cn/functions.d +++ b/d.en/functions.d @@ -136,7 +136,7 @@ This time there is an additional information that concerns some of the steps: "g ) $(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 the $(I function parameter list), separated by commas from each other. The parameter list rests inside of the parentheses that comes after the name of the function. +$(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 @@ -642,7 +642,7 @@ The three commented groups of lines of the first version of the program have bee ) $(P -Another important benefit of removing comment lines is that comments tend to become outdated as the code gets modified over the time. Comments are sometimes forgotten to be updated along with the code and become either useless or, even worse, misleading. For that reason, it is beneficial to try to write programs without the need for comments. +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 diff --git a/ddili/src/ders/d.en/functions_more.d b/d.en/functions_more.d similarity index 96% rename from ddili/src/ders/d.en/functions_more.d rename to d.en/functions_more.d index 3f165c2..8c3f06b 100644 --- a/ddili/src/ders/d.en/functions_more.d +++ b/d.en/functions_more.d @@ -573,6 +573,22 @@ 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 @@ -655,7 +671,7 @@ int foo(int[] arr, size_t i) $(HILITE nothrow) { --- $(P -As in purity, the compiler automatically deduces whether a template, delegate, or anonymous function is $(C nothrow). +As with purity, the compiler automatically deduces whether a template, delegate, or anonymous function is $(C nothrow). ) $(H6 $(IX @nogc) $(C @nogc) functions) @@ -714,7 +730,7 @@ Error: @nogc function 'deneme.foo' $(HILITE cannot call non-@nogc function) $(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 in purity, the compiler infers the safety level of templates, delegates, anonymous functions, and $(C auto) functions. +$(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) @@ -733,7 +749,7 @@ $(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 Pointer values cannot be changed (no pointer $(I arithmetic); however, assigning a pointer to another pointer of the same type is safe).) $(LI Unions that have pointer or reference members cannot be used.) diff --git a/ddili/src/ders/d.cn/goto.d b/d.en/goto.d similarity index 100% rename from ddili/src/ders/d.cn/goto.d rename to d.en/goto.d diff --git a/ddili/src/ders/d.de/hello_world.cozum.d b/d.en/hello_world.cozum.d similarity index 92% rename from ddili/src/ders/d.de/hello_world.cozum.d rename to d.en/hello_world.cozum.d index 2adc68b..50bd20a 100644 --- a/ddili/src/ders/d.de/hello_world.cozum.d +++ b/d.en/hello_world.cozum.d @@ -37,7 +37,7 @@ The following program cannot be compiled because the semicolon at the end of the import std.stdio; void main() { - writeln("Hello world!") $(DERLEME_HATASI) + writeln("Hello, World!") $(DERLEME_HATASI) } --- ) diff --git a/ddili/src/ders/d.de/hello_world.d b/d.en/hello_world.d similarity index 85% rename from ddili/src/ders/d.de/hello_world.d rename to d.en/hello_world.d index 2b20edd..b544510 100644 --- a/ddili/src/ders/d.de/hello_world.d +++ b/d.en/hello_world.d @@ -5,7 +5,7 @@ $(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. +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 @@ -16,7 +16,7 @@ Here is a $(I hello world) program in D: import std.stdio; void main() { - writeln("Hello world!"); + writeln("Hello, World!"); } --- @@ -31,7 +31,7 @@ $(IX gdc) $(IX ldc) At the time of writing this chapter, there are three D compi ) $(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.069. +$(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 $(DVER). ) $(P @@ -85,12 +85,12 @@ If the compiler has instead printed some messages, you probably have made a mist ) $(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!": +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) +$(SHELL_OBSERVED $) ./hello $(SHELL_NOTE running the program) +Hello, World! $(SHELL_NOTE the message that it prints) ) $(P @@ -100,19 +100,12 @@ 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 only the name of the compiler: +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 only the name) -DMD64 D Compiler v2.069.0 -Copyright (c) 1999-2015 by Digital Mars written by Walter Bright -Documentation: http://dlang.org/ -Config file: /etc/dmd.conf -Usage: - dmd files.d ... { -switch } - - files.d D source files +$(SHELL_OBSERVED $) dmd $(SHELL_NOTE enter just the name) +DMD64 D Compiler v$(DVER) ... -de show use of deprecated features as errors (halt compilation) ... @@ -135,12 +128,12 @@ The complete list of $(C dmd) command line switches can be found in the $(LINK2 ) $(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: +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 $(HILITE -run) hello.d -w -unittest -Hello world! $(SHELL_NOTE the program is automatically executed) +$(SHELL_OBSERVED $) dmd -de -w -unittest $(HILITE -run) hello.d +Hello, World! $(SHELL_NOTE the program is automatically executed) ) $(H5 $(IX IDE) IDE) @@ -176,7 +169,7 @@ $(C writeln) above is a $(I function) in D's standard $(I library). It is used f $(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 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!". @@ -190,11 +183,11 @@ $(IX keyword) $(B Keyword): Special words that are a part of the core features o ) $(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 __MODULE__), $(C __LINE__), $(C __FUNCTION__), $(C __PRETTY_FUNCTION__), $(C __gshared), $(C __traits), $(C __vector), and $(C __parameters). +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 in the upcoming chapters with the exception of these keywords: $(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. +$(IX asm) $(IX body) $(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 body), $(C delete), $(C typedef), and $(C volatile) are deprecated; and $(C macro) is unused by D at this time. ) $(PROBLEM_COK diff --git a/ddili/src/ders/d.en/if.cozum.d b/d.en/if.cozum.d similarity index 100% rename from ddili/src/ders/d.en/if.cozum.d rename to d.en/if.cozum.d diff --git a/ddili/src/ders/d.en/if.d b/d.en/if.d similarity index 100% rename from ddili/src/ders/d.en/if.d rename to d.en/if.d diff --git a/ddili/src/ders/d.cn/inheritance.cozum.d b/d.en/inheritance.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/inheritance.cozum.d rename to d.en/inheritance.cozum.d diff --git a/ddili/src/ders/d.cn/inheritance.d b/d.en/inheritance.d similarity index 100% rename from ddili/src/ders/d.cn/inheritance.d rename to d.en/inheritance.d diff --git a/ddili/src/ders/d.en/input.cozum.d b/d.en/input.cozum.d similarity index 100% rename from ddili/src/ders/d.en/input.cozum.d rename to d.en/input.cozum.d diff --git a/ddili/src/ders/d.en/input.d b/d.en/input.d similarity index 94% rename from ddili/src/ders/d.en/input.d rename to d.en/input.d index 1461557..b4e1255 100644 --- a/ddili/src/ders/d.en/input.d +++ b/d.en/input.d @@ -64,7 +64,15 @@ I will leave one peculiarity about the use of $(C readf) for later; for now, let --- $(P -$(I $(B Note:) As I explain below, in most cases there must also be a space: $(STRING " %s").) +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 diff --git a/ddili/src/ders/d.cn/interface.d b/d.en/interface.d similarity index 99% rename from ddili/src/ders/d.cn/interface.d rename to d.en/interface.d index 0834f41..5874380 100644 --- a/ddili/src/ders/d.cn/interface.d +++ b/d.en/interface.d @@ -354,7 +354,7 @@ Although the example above contains a $(C struct), $(C static) member functions $(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 available only for classes and interfaces; they are not relevant to structs because structs do not support inheritance. +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 diff --git a/ddili/src/ders/d.cn/invariant.d b/d.en/invariant.d similarity index 99% rename from ddili/src/ders/d.cn/invariant.d rename to d.en/invariant.d index d06fbce..b302a56 100644 --- a/ddili/src/ders/d.cn/invariant.d +++ b/d.en/invariant.d @@ -40,7 +40,7 @@ in { } out (result) { assert(result >= 0); -} body { +} do { immutable halfPerimeter = (a + b + c) / 2; return sqrt(halfPerimeter @@ -77,7 +77,7 @@ public: $(HILITE out (result)) { assert(result >= 0); - } body { + } do { immutable halfPerimeter = (a + b + c) / 2; return sqrt(halfPerimeter @@ -123,7 +123,7 @@ struct Triangle { assert(b <= (a + c)); assert(c <= (a + b)); - } body { + } do { this.a = a; this.b = b; this.c = c; @@ -249,7 +249,7 @@ $(IX -release, compiler switch) As with $(C in) and $(C out) blocks, the checks ) $(SHELL -dmd deneme.d -w -release +$ 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) @@ -320,8 +320,8 @@ class Class : Iface { assert((result.length != 0) && (result[0] == result[$ - 1])); - } body { - writeln("Class.func.body"); + } do { + writeln("Class.func.do"); /* This is just an artificial implementation to * demonstrate how the 'in' and 'out' blocks are @@ -366,7 +366,7 @@ The $(C in) block of $(C Class) is executed only because the parameters fail to $(SHELL Iface.func.in Class.func.in $(SHELL_NOTE would not be executed if Iface.func.in succeeded) -Class.func.body +Class.func.do Iface.func.out Class.func.out [42, 1, 2, 2, 3, 42] @@ -392,7 +392,7 @@ class Protocol { in { assert($(HILITE d > 42)); - } body { + } do { // ... } } @@ -424,7 +424,7 @@ class SpecialProtocol : Protocol { in { $(HILITE assert(false)); - } body { + } do { // ... } } diff --git a/ddili/src/ders/d.cn/io.cozum.d b/d.en/io.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/io.cozum.d rename to d.en/io.cozum.d diff --git a/ddili/src/ders/d.en/io.d b/d.en/io.d similarity index 97% rename from ddili/src/ders/d.en/io.d rename to d.en/io.d index 9254cb2..38c5e69 100644 --- a/ddili/src/ders/d.en/io.d +++ b/d.en/io.d @@ -38,7 +38,7 @@ $(C writeln) that we've been using in the previous chapters is actually short fo import std.stdio; void main() { - $(HILITE stdout.)writeln("Hello world!"); + $(HILITE stdout.)writeln("Hello, World!"); } --- diff --git a/ddili/src/ders/d.en/is_expr.d b/d.en/is_expr.d similarity index 98% rename from ddili/src/ders/d.en/is_expr.d rename to d.en/is_expr.d index dbbbfd5..1b67cd9 100644 --- a/ddili/src/ders/d.en/is_expr.d +++ b/d.en/is_expr.d @@ -3,7 +3,7 @@ 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 have seen in $(LINK2 /ders/d.en/null_is.html, The $(CH4 null) Value and the $(CH4 is) Operator chapter), neither syntactically nor semantically: +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: ) --- diff --git a/ddili/src/ders/d.cn/ix.d b/d.en/ix.d similarity index 100% rename from ddili/src/ders/d.cn/ix.d rename to d.en/ix.d diff --git a/ddili/src/ders/d.cn/lambda.d b/d.en/lambda.d similarity index 98% rename from ddili/src/ders/d.cn/lambda.d rename to d.en/lambda.d index dc29fab..330573c 100644 --- a/ddili/src/ders/d.cn/lambda.d +++ b/d.en/lambda.d @@ -1112,7 +1112,7 @@ As seen in its declaration, this overload of $(C toString()) does not return a $ ) $(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: +$(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. ) --- @@ -1124,7 +1124,7 @@ struct Point { int y; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "(%s,%s)", x, y); + sink.$(HILITE formattedWrite)!"(%s,%s)"(x, y); } } @@ -1134,7 +1134,7 @@ struct Color { ubyte b; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "RGB:%s,%s,%s", r, g, b); + sink.$(HILITE formattedWrite)!"RGB:%s,%s,%s"(r, g, b); } } @@ -1143,7 +1143,7 @@ struct ColoredPoint { Point point; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "{%s;%s}", color, point); + sink.$(HILITE formattedWrite)!"{%s;%s}"(color, point); } } @@ -1151,7 +1151,7 @@ struct Polygon { ColoredPoint[] points; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "%s", points); + sink.$(HILITE formattedWrite)!"%s"(points); } } diff --git a/ddili/src/ders/d.en/lazy_operators.d b/d.en/lazy_operators.d similarity index 100% rename from ddili/src/ders/d.en/lazy_operators.d rename to d.en/lazy_operators.d diff --git a/ddili/src/ders/d.cn/lifetimes.d b/d.en/lifetimes.d similarity index 95% rename from ddili/src/ders/d.cn/lifetimes.d rename to d.en/lifetimes.d index 6054431..0cd7a3e 100644 --- a/ddili/src/ders/d.cn/lifetimes.d +++ b/d.en/lifetimes.d @@ -11,7 +11,7 @@ Before getting to structs and classes, it will be better to talk about some impo ) $(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 called struct and class variables specifically as $(I objects). I will continue calling both of these concepts variables in this chapter. +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 @@ -25,7 +25,7 @@ The time between when a variable is defined and when it is $(I finalized) is the ) $(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 has been defined would make that variable unavailable. +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 @@ -306,8 +306,8 @@ Variables can be finalized in two ways: ) $(UL -$(LI $(B When the lifetime ends): The finalization happens upon the end of life of the variable.) -$(LI $(B Some time in the future): The finalization happens at an indeterminate time in the future by the garbage collector.) +$(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 diff --git a/ddili/src/ders/d.cn/literals.cozum.d b/d.en/literals.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/literals.cozum.d rename to d.en/literals.cozum.d diff --git a/ddili/src/ders/d.en/literals.d b/d.en/literals.d similarity index 95% rename from ddili/src/ders/d.en/literals.d rename to d.en/literals.d index d63f3c3..320d115 100644 --- a/ddili/src/ders/d.en/literals.d +++ b/d.en/literals.d @@ -271,12 +271,6 @@ $(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 @@ -362,7 +356,7 @@ Change the program so that the line can be compiled and that $(C amount) equals ) $(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: +Write a program that increases the value of a variable and prints it continuously. Make the value always be printed on the same line, overwriting the previous value: $(SHELL Number: 25774 $(SHELL_NOTE always on the same line) diff --git a/ddili/src/ders/d.cn/logical_expressions.cozum.d b/d.en/logical_expressions.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/logical_expressions.cozum.d rename to d.en/logical_expressions.cozum.d diff --git a/ddili/src/ders/d.en/logical_expressions.d b/d.en/logical_expressions.d similarity index 98% rename from ddili/src/ders/d.en/logical_expressions.d rename to d.en/logical_expressions.d index ae1bc1d..0b7aa47 100644 --- a/ddili/src/ders/d.en/logical_expressions.d +++ b/d.en/logical_expressions.d @@ -74,7 +74,7 @@ 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". +will mean that "there isn't coffee". I use the "... is ...: false" construct to mean "is not" or "is false". ) $(P diff --git a/ddili/src/ders/d.en/lvalue_rvalue.d b/d.en/lvalue_rvalue.d similarity index 89% rename from ddili/src/ders/d.en/lvalue_rvalue.d rename to d.en/lvalue_rvalue.d index 36644df..e66bbf9 100644 --- a/ddili/src/ders/d.en/lvalue_rvalue.d +++ b/d.en/lvalue_rvalue.d @@ -140,7 +140,7 @@ $(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 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. +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 @@ -170,7 +170,21 @@ void main() { --- $(P -As mentioned in the code comment above, the mutation to the parameter may not be observable by the caller. For that reason, $(C auto ref) is mostly used in situations where the parameter is not modified; it is used mostly as $(C auto ref const). +$(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) diff --git a/ddili/src/ders/d.cn/main.cozum.d b/d.en/main.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/main.cozum.d rename to d.en/main.cozum.d diff --git a/ddili/src/ders/d.cn/main.d b/d.en/main.d similarity index 99% rename from ddili/src/ders/d.cn/main.d rename to d.en/main.d index cdf121c..36cf423 100644 --- a/ddili/src/ders/d.cn/main.d +++ b/d.en/main.d @@ -233,7 +233,7 @@ import std.stdio; int main(string[] args) { if (args.length != 3) { - stderr.writefln("ERROR! Correct usage:\n" + stderr.writefln("ERROR! Correct usage:\n" ~ " %s word1 word2", args[0]); return 1; } diff --git a/ddili/src/ders/d.cn/member_functions.cozum.d b/d.en/member_functions.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/member_functions.cozum.d rename to d.en/member_functions.cozum.d diff --git a/ddili/src/ders/d.cn/member_functions.d b/d.en/member_functions.d similarity index 100% rename from ddili/src/ders/d.cn/member_functions.d rename to d.en/member_functions.d diff --git a/ddili/src/ders/d.en/memory.d b/d.en/memory.d similarity index 97% rename from ddili/src/ders/d.en/memory.d rename to d.en/memory.d index 0c7f3a6..ed49992 100644 --- a/ddili/src/ders/d.en/memory.d +++ b/d.en/memory.d @@ -148,7 +148,7 @@ $(IX uninitialized array) $(IX array, uninitialized) $(IX = void) $(C buffer) is --- $(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. +$(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 core.stdc.stdlib) module. ) $(P @@ -275,7 +275,7 @@ void * reallocCleared( /* Clear the extra bytes if extended. */ if (newLength > oldLength) { - import std.c.string; + import core.stdc.string; auto extendedPart = buffer + oldLength; auto extendedLength = newLength - oldLength; @@ -288,7 +288,7 @@ void * reallocCleared( --- $(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). +$(IX memset, core.stdc.string) The function above uses $(C memset()) from the $(C core.stdc.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 @@ -358,7 +358,7 @@ $(LI $(C NO_INTERIOR): Specifies that only pointers to the block's first address ) $(P -$(IX |) The values of $(C enum BlkAttr) are suitable to be used as bit flags that we have seen 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: +$(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: ) --- @@ -367,11 +367,11 @@ $(IX |) The values of $(C enum BlkAttr) are suitable to be used as bit flags tha --- $(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). +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 core.stdc.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). +$(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 core.stdc.stdlib.free). ) $(P @@ -872,10 +872,10 @@ $(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 Calls the constructor of the object on that memory location. Only after this step the object becomes $(I placed) on that memory area. +$(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 Configure the memory block so it has all the necessary flags and infrastructure to properly destroy the object when freed. +$(LI Configures the memory block so it has all the necessary flags and infrastructure to properly destroy the object when freed. ) ) diff --git a/ddili/src/ders/d.cn/mixin.d b/d.en/mixin.d similarity index 78% rename from ddili/src/ders/d.cn/mixin.d rename to d.en/mixin.d index f7b69f6..d62c3c1 100644 --- a/ddili/src/ders/d.cn/mixin.d +++ b/d.en/mixin.d @@ -6,6 +6,10 @@ $(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). ) +$(P +Code can be inserted into the program as a $(I string import) as well. +) + $(H5 $(IX template mixin) Template mixins) $(P @@ -235,7 +239,7 @@ For example, the $(I hello world) program can be written with a $(C mixin) as we import std.stdio; void main() { - mixin (`writeln("hello world");`); + mixin (`writeln("Hello, World!");`); } --- @@ -244,7 +248,7 @@ The string gets inserted as code and the program produces the following output: ) $(SHELL -hello world +Hello, World! ) $(P @@ -253,7 +257,7 @@ We can go further and insert all of the program as a string mixin: --- mixin ( -`import std.stdio; void main() { writeln("hello world"); }` +`import std.stdio; void main() { writeln("Hello, World!"); }` ); --- @@ -273,8 +277,8 @@ string printStatement(string message) { } void main() { - mixin (printStatement("hello world")); - mixin (printStatement("hi world")); + mixin (printStatement("Hello, World!")); + mixin (printStatement("Hi, World!")); } --- @@ -283,8 +287,8 @@ The output: ) $(SHELL -hello world -hi world +Hello, World! +Hi, World! ) $(P @@ -295,8 +299,8 @@ Note that the $(STRING "writeln") expressions are not executed inside $(C printS import std.stdio; void main() { - writeln("hello world"); - writeln("hi world"); + writeln("Hello, World!"); + writeln("Hi, World!"); } --- @@ -421,6 +425,134 @@ $(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 $(IX text file import) $(IX file import) $(IX import, file) $(IX string import) $(IX import, string) Importing text files) + +$(P +It is possible to insert contents of text files into code at compile time. The contents are treated as $(C string) literals and can be used anywhere strings can be used. For example, they can be mixed in as code. +) + +$(P +For example, let's assume there are two text files on the file system named $(C file_one) and $(C file_two) having the following contents. +) + +$(UL +$(LI +$(C file_one): +) + +$(SHELL +Hello +) + +$(LI +$(C file_two): +) + +$(SHELL +s ~= ", World!"; +import std.stdio; +writeln(s); +) + +) + +$(P +The two $(C import) directives in the following program would correspond to the contents of those files converted to $(C string) literals at compile time: +) + +--- +void main() { + string s = import ("file_one"); + mixin (import ("file_two")); +} +--- + +$(P +$(IX -J, compiler switch) Text file imports (a.k.a. string imports) require the $(C -J) compiler switch which tells the compiler where to find the text files. For example, if the two files are in the $(I current directory) (specified with $(C .) in Linux environments), the program can be compiled with the following command: +) + +$(SHELL +$ dmd $(HILITE -J.) deneme.d +) + +$(P +The output: +) + +$(SHELL +Hello, World! +) + +$(P +Considering the file contents as $(C string) literals, the program is the equivalent of the following one: +) + +--- +void main() { + string s = `Hello`; $(CODE_NOTE Content of file_one as string) + mixin (`s ~= ", World!"; +import std.stdio; +writeln(s);`); $(CODE_NOTE Content of file_two as string) +} +--- + +$(P +Further, considering the mixed-in string as well, the program is the equivalent of the following one: +) + +--- +void main() { + string s = `Hello`; + s ~= ", World!"; + import std.stdio; + writeln(s); +} +--- + $(H5 Example) $(P diff --git a/ddili/src/ders/d.cn/modules.d b/d.en/modules.d similarity index 100% rename from ddili/src/ders/d.cn/modules.d rename to d.en/modules.d diff --git a/ddili/src/ders/d.en/name_space.d b/d.en/name_space.d similarity index 100% rename from ddili/src/ders/d.en/name_space.d rename to d.en/name_space.d diff --git a/ddili/src/ders/d.cn/nested.d b/d.en/nested.d similarity index 98% rename from ddili/src/ders/d.cn/nested.d rename to d.en/nested.d index e84dbb9..2ebbce4 100644 --- a/ddili/src/ders/d.cn/nested.d +++ b/d.en/nested.d @@ -3,7 +3,7 @@ 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 have seen in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). +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 diff --git a/ddili/src/ders/d.en/null_is.d b/d.en/null_is.d similarity index 99% rename from ddili/src/ders/d.en/null_is.d rename to d.en/null_is.d index f0c81da..23b0471 100644 --- a/ddili/src/ders/d.en/null_is.d +++ b/d.en/null_is.d @@ -3,7 +3,7 @@ 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 need not be referencing any object: +We saw in earlier chapters that a variable of a reference type needs not reference a particular object: ) --- diff --git a/ddili/src/ders/d.en/object.cozum.d b/d.en/object.cozum.d similarity index 100% rename from ddili/src/ders/d.en/object.cozum.d rename to d.en/object.cozum.d diff --git a/ddili/src/ders/d.en/object.d b/d.en/object.d similarity index 96% rename from ddili/src/ders/d.en/object.d rename to d.en/object.d index 2be7b51..607d8a3 100644 --- a/ddili/src/ders/d.en/object.d +++ b/d.en/object.d @@ -137,7 +137,7 @@ 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 actual value of the argument: +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): ) --- @@ -146,6 +146,15 @@ MusicalInstrument foo(int i) { } --- +$(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 @@ -582,11 +591,7 @@ Also note that $(C Student) does not support comparing incompatible types by enf $(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). -) - -$(P -$(B Warning:) Defining only this function is not sufficient. In order for the class type to be used as associative array keys, consistent definitions of $(C opEquals) and $(C opCmp) must also be defined. +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) @@ -846,7 +851,7 @@ Implement $(C toHash) for that class. Again, the following $(C assert) checks sh --- $(P -Remember that $(C opEquals) and $(C opCmp) must also be defined when $(C toHash) is defined. +Remember that $(C opEquals) must also be defined when $(C toHash) is defined. ) ) diff --git a/ddili/src/ders/d.en/operator_overloading.cozum.d b/d.en/operator_overloading.cozum.d similarity index 98% rename from ddili/src/ders/d.en/operator_overloading.cozum.d rename to d.en/operator_overloading.cozum.d index 0c5b29f..816f5cd 100644 --- a/ddili/src/ders/d.en/operator_overloading.cozum.d +++ b/d.en/operator_overloading.cozum.d @@ -156,7 +156,7 @@ struct Fraction { * fraction is before, a positive value if this fraction * is after, and zero if both fractions have the same sort * order. */ - int opCmp(const ref Fraction rhs) const { + 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 @@ -176,7 +176,7 @@ struct Fraction { * 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 ref Fraction rhs) const { + bool opEquals(const Fraction rhs) const { /* Checking whether the return value of opCmp is zero * is sufficient here. */ return opCmp(rhs) == 0; diff --git a/ddili/src/ders/d.cn/operator_overloading.d b/d.en/operator_overloading.d similarity index 97% rename from ddili/src/ders/d.cn/operator_overloading.d rename to d.en/operator_overloading.d index 181d23f..3bc5545 100644 --- a/ddili/src/ders/d.cn/operator_overloading.d +++ b/d.en/operator_overloading.d @@ -377,7 +377,17 @@ The compiler picks the option that is a better match than the other. ) $(P -In most cases it is not necessary to define $(C opBinaryRight), except for the $(C in) operator: It usually makes more sense to define $(C opBinaryRight) for $(C in). +$(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 @@ -1502,6 +1512,54 @@ $(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 diff --git a/ddili/src/ders/d.cn/operator_precedence.d b/d.en/operator_precedence.d similarity index 93% rename from ddili/src/ders/d.cn/operator_precedence.d rename to d.en/operator_precedence.d index 23ce93c..db4d51e 100644 --- a/ddili/src/ders/d.cn/operator_precedence.d +++ b/d.en/operator_precedence.d @@ -83,7 +83,7 @@ Some of the terms used in the table are explained later below. -$(C == != > < >= <= !> !< !>= !<= <> !<> <>= !<>= in !in is !is) +$(C == != > < >= <= in !in is !is) Comparison operators Unordered with respect to bitwise operators, cannot be chained @@ -295,21 +295,26 @@ $(I $(B Note:) This is just an example; otherwise, $(C a = 1) is not a $(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 value of the left-hand side expression is ignored and the value of the right-hand side expression becomes the value of the comma operator: +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; - int b = (foo()$(HILITE ,) bar()$(HILITE ,) ++a + 10); + foo()$(HILITE ,) bar()$(HILITE ,) ++a; assert(a == 2); - assert(b == 12); --- $(P -The two comma operators above chain three expressions. Although the first two expressions ($(C foo()) and $(C bar())) do get executed, their values are ignored and the overall comma expression produces the value of the last expression ($(C ++a + 10)). +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 diff --git a/ddili/src/ders/d.cn/parameter_flexibility.cozum.d b/d.en/parameter_flexibility.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/parameter_flexibility.cozum.d rename to d.en/parameter_flexibility.cozum.d diff --git a/ddili/src/ders/d.en/parameter_flexibility.d b/d.en/parameter_flexibility.d similarity index 90% rename from ddili/src/ders/d.en/parameter_flexibility.d rename to d.en/parameter_flexibility.d index 434d109..1d2fe45 100644 --- a/ddili/src/ders/d.en/parameter_flexibility.d +++ b/d.en/parameter_flexibility.d @@ -22,7 +22,7 @@ Some of the parameters of some functions are called mostly by the same values. T ) --- -import std.algorithm; +$(CODE_NAME printAA)import std.algorithm; // ... @@ -34,12 +34,16 @@ void printAA(in char[] title, auto keys = sort(aa.keys); - foreach (i, key; keys) { - // No separator before the first element - if (i != 0) { - write(elementSeparator); - } + // 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]); } @@ -48,14 +52,16 @@ void printAA(in char[] title, --- $(P -That function is being called below by $(STRING ":") as the key separator and $(STRING ", ") as the element separator: +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 @@ -139,11 +145,12 @@ $(IX special keyword) $(IX keyword, special) The following special keywords act $(UL -$(LI $(IX __MODULE__) $(C __MODULE__): Name of the module) -$(LI $(IX __FILE__) $(C __FILE__): Name of the source file) -$(LI $(IX __LINE__) $(C __LINE__): Line number) -$(LI $(IX __FUNCTION__) $(C __FUNCTION__): Name of the function) -$(LI $(IX __PRETTY_FUNCTION__) $(C __PRETTY_FUNCTION__): Full signature of the function) +$(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)) ) @@ -182,7 +189,7 @@ import std.stdio; void func(int parameter, string functionName = $(HILITE __FUNCTION__), string file = $(HILITE __FILE__), - size_t line = $(HILITE __LINE__)) { + int line = $(HILITE __LINE__)) { writefln("Called from function %s at file %s, line %s.", functionName, file, line); } @@ -206,15 +213,15 @@ $(IX special token) $(IX token, special) In addition to the above, there are als $(UL -$(LI $(IX __DATE__) $(C __DATE__): Date of compilation) +$(LI $(IX __DATE__) $(C __DATE__): Date of compilation as $(C string)) -$(LI $(IX __TIME__) $(C __TIME__): Time of compilation) +$(LI $(IX __TIME__) $(C __TIME__): Time of compilation as $(C string)) -$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Date and time of compilation) +$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Date and time of compilation as $(C string)) -$(LI $(IX __VENDOR__) $(C __VENDOR__): Compiler vendor (e.g. $(STRING "Digital Mars D"))) +$(LI $(IX __VENDOR__) $(C __VENDOR__): Compiler vendor as $(C string) (e.g. $(STRING "Digital Mars D"))) -$(LI $(IX __VERSION__) $(C __VERSION__): Compiler version as an integer (e.g. the value $(C 2069) for version 2.069)) +$(LI $(IX __VERSION__) $(C __VERSION__): Compiler version as $(C long) (e.g. the value $(C 2081L) for version 2.081)) ) diff --git a/ddili/src/ders/d.cn/pointers.cozum.d b/d.en/pointers.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/pointers.cozum.d rename to d.en/pointers.cozum.d diff --git a/ddili/src/ders/d.cn/pointers.d b/d.en/pointers.d similarity index 99% rename from ddili/src/ders/d.cn/pointers.d rename to d.en/pointers.d index dc0a9f4..c0158ca 100644 --- a/ddili/src/ders/d.cn/pointers.d +++ b/d.en/pointers.d @@ -478,7 +478,7 @@ There is an exception to this rule both for class variables and for pointers. Ty --- char c; - char * p; + char * p = &c; writeln(p.sizeof); // size of the pointer, not the pointee --- @@ -1096,14 +1096,14 @@ The actual type of $(C element) above is a pointer to the same type of the eleme $(H5 When to use pointers) -$(H6 When required by libraries) - $(P -Although pointer parameters are rare in D libraries, we have already seen that $(C readf()) is one function that requires pointers. +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 -As another example, the following function from the GtkD library takes a pointer as well: +Pointers can appear on C and C++ library bindings. For example, the following function from the GtkD library takes a pointer: ) --- diff --git a/d.en/pragma.d b/d.en/pragma.d new file mode 100644 index 0000000..64a2ca6 --- /dev/null +++ b/d.en/pragma.d @@ -0,0 +1,236 @@ +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.stopwatch; + +// 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.total!"msecs"); +} +--- + +$(P +$(IX StopWatch, std.datetime.stopwatch) The code takes advantage of $(C std.datetime.stopwatch.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 override), because $(C override) 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 'override', it could + * only be called from D through a name like 'c_override', + * mangled as the actual function name: */ +pragma($(HILITE mangle), "override") +extern(C) string c_override(string); + +void main() { + /* D code calls the function as c_override() but the + * linker would find it by its correct C library name + * 'override': */ + auto s = $(HILITE c_override)("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/ddili/src/ders/template/preface.d b/d.en/preface.d similarity index 88% rename from ddili/src/ders/template/preface.d rename to d.en/preface.d index a5cef8f..8ed05ed 100644 --- a/ddili/src/ders/template/preface.d +++ b/d.en/preface.d @@ -39,7 +39,7 @@ I am indebted to the following people who have been instrumental during the evol ) $(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, Stéphane Goujet, Olivier Pisano, Dave Yost, Tomasz Miazek-Mioduszewski, Gerard Vreeswijk, Justin Whear, Gerald Jansen, and Sylvain Gault. +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, Andrew Edwards, Steve White, Mark Schwarzmann, and Thibaut Charles. ) $(P @@ -47,7 +47,7 @@ Thanks especially to Luís Marques who, through his hard work, improved every ch ) $(P -Thanks to Luís Marques, Steven Schveighoffer, Andrej Mitrović, Robbin Carlson, and Ergin Güney for their suggestions that elevated this book from my Inglish to English. +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 @@ -59,7 +59,7 @@ Ebru, Damla, and Derin, thank you for being so patient and supportive while I wa $(BR) $(BR) Ali Çehreli$(BR) -Mountain View, $(I November 2015) +Mountain View, $(I May 2017) ) ) diff --git a/ddili/src/ders/d.cn/ranges.d b/d.en/ranges.d similarity index 97% rename from ddili/src/ders/d.cn/ranges.d rename to d.en/ranges.d index e007576..ac0f6aa 100644 --- a/ddili/src/ders/d.cn/ranges.d +++ b/d.en/ranges.d @@ -49,11 +49,11 @@ A very successful library that abstracts algorithms and data structures from eac ) $(P -Although they are a very useful abstraction, iterators do have some weaknesses. D's ranges were designed by Andrei Alexandrescu partly to overcome these weaknesses. D's standard library Phobos takes great advantage of ranges that are the subject of this chapter. +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 the seminal paper $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357, On Iteration) and demonstrates how they are superior to iterators. +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) @@ -155,7 +155,7 @@ Conversely, because ranges abstract algorithms away from data structures, implem $(H5 Phobos ranges) $(P -The ranges in this chapter are different from number ranges that are written in the form $(C begin..end). We had seen how number ranges are used with the $(C foreach) loop and with slices: +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: ) --- @@ -167,11 +167,11 @@ The ranges in this chapter are different from number ranges that are written in --- $(P -When I write $(I range), I mean a Phobos range in this chapter. +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 that they are based on. The following are all of the ranges with their requirements, sorted from the simplest to the more capable: +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 @@ -233,7 +233,7 @@ Normally, iterating over the elements of a container does not change the contain --- $(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: +Another way of iteration requires a different way of thinking: iteration can be achieved by shortening the range from the beginning. In this method, it is always the first element that is used for element access and the first element is $(I popped) from the beginning in order to get to the next element: ) --- @@ -570,7 +570,7 @@ $(MONO_NOBOLD ) $(P -As you can see, that output does not match what we have seen 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. +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 diff --git a/ddili/src/ders/d.cn/ranges_more.d b/d.en/ranges_more.d similarity index 100% rename from ddili/src/ders/d.cn/ranges_more.d rename to d.en/ranges_more.d diff --git a/ddili/src/ders/d.en/scope.d b/d.en/scope.d similarity index 100% rename from ddili/src/ders/d.en/scope.d rename to d.en/scope.d diff --git a/ddili/src/ders/d.cn/slices.cozum.d b/d.en/slices.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/slices.cozum.d rename to d.en/slices.cozum.d diff --git a/ddili/src/ders/d.cn/slices.d b/d.en/slices.d similarity index 98% rename from ddili/src/ders/d.cn/slices.d rename to d.en/slices.d index 598d64f..e4aadcb 100644 --- a/ddili/src/ders/d.cn/slices.d +++ b/d.en/slices.d @@ -472,7 +472,7 @@ $(SHELL $(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 before hand, it is possible to reserve capacity for the elements: +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: ) --- @@ -508,10 +508,6 @@ $(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. ) -$(P -$(I $(B Note:) dmd 2.071, the compiler that was used to compile the examples in this chapter, did not fully support this feature yet. For that reason, some of the examples below use only fixed-length arrays.) -) - --- import std.stdio; diff --git a/ddili/src/ders/d.en/special_functions.d b/d.en/special_functions.d similarity index 96% rename from ddili/src/ders/d.en/special_functions.d rename to d.en/special_functions.d index 00accc3..71af3c9 100644 --- a/ddili/src/ders/d.en/special_functions.d +++ b/d.en/special_functions.d @@ -242,6 +242,39 @@ Since hour and minute are now separate parameters, the users simply provide thei time.increment(Duration($(HILITE 22, 20))); --- +$(H6 First assignment to a member is construction) + +$(P +When setting values of members in a constructor, the first assignment to each member is treated specially: Instead of assigning a new value over the $(C .init) value of that member, the first assignment actually constructs that member. Further assignments to that member are treated regularly as assignment operations. +) + +$(P +This special behavior is necessary so that $(C immutable) and $(C const) members can in fact be constructed with values known only at run time. Otherwise, they could never be set to desired values as assignment is disallowed for $(C immutable) and $(C const) variables. +) + +$(P +The following program demonstrates how assigment operation is allowed only once for an $(C immutable) member: +) + +--- +struct S { + int m; + immutable int i; + + this(int m, int i) { + this.m = m; $(CODE_NOTE construction) + this.m = 42; $(CODE_NOTE assignment (possible for mutable member)) + + this.i = i; $(CODE_NOTE construction) + this.i = i; $(DERLEME_HATASI) + } +} + +void main() { + auto s = S(1, 2); +} +--- + $(H6 User-defined constructor disables compiler-generated constructor) $(P diff --git a/d.en/static_foreach.d b/d.en/static_foreach.d new file mode 100644 index 0000000..ba9139d --- /dev/null +++ b/d.en/static_foreach.d @@ -0,0 +1,220 @@ +Ddoc + +$(DERS_BOLUMU $(IX foreach, static) $(IX static foreach) $(CH4 static foreach)) + +$(P +We saw compile-time $(C foreach) earlier in the $(LINK2 /ders/d.en/tuples.html, Tuples chapter). Compile-time $(C foreach) iterates the loop at compile time and unrolls each iteration as separate pieces of code. For example, given the following $(C foreach) loop over a tuple: +) + +--- + auto t = tuple(42, "hello", 1.5); + + foreach (i, member; $(HILITE t)) { + writefln("%s: %s", i, member); + } +--- + +$(P +The compiler $(I unrolls) the loop similar to the following equivalent code: +) + +--- + { + enum size_t i = 0; + int member = t[i]; + writefln("%s: %s", i, member); + } + { + enum size_t i = 1; + string member = t[i]; + writefln("%s: %s", i, member); + } + { + enum size_t i = 2; + double member = t[i]; + writefln("%s: %s", i, member); + } +--- + +$(P +Although being very powerful, some properties of compile-time $(C foreach) may not be suitable in some cases: +) + +$(UL + +$(LI +With compile-time $(C foreach), each unrolling of the loop introduces a scope. As seen with the $(C i) and $(C member) variables above, this allows the use of a symbol in more than one scope without causing a multiple definition error. Although this can be desirable in some cases, it makes it impossible for code unrolled for one iteration to use code from other iterations. +) + +$(LI +Compile-time $(C foreach) works only with tuples (including template arguments in the form of $(C AliasSeq)). For example, despite the elements of the following array literal being known at compile time, the loop will always be executed at run time (this may very well be the desired behavior): +) + +) + +--- +void main() { + $(HILITE enum) arr = [1, 2]; + // Executed at run time, not unrolled at compile time: + foreach (i; arr) { + // ... + } +} +--- + +$(UL + +$(LI +Like regular $(C foreach), compile-time $(C foreach) can only be used inside functions. For example, it cannot be used at module scope or inside a user-defined type definition. +) + +) + +--- +import std.meta; + +// Attempting to define function overloads at module scope: +foreach (T; AliasSeq!(int, double)) { $(DERLEME_HATASI) + T twoTimes(T arg) { + return arg * 2; + } +} + +void main() { +} +--- + +$(SHELL +Error: declaration expected, not `foreach` +) + +$(UL + +$(LI +With compile-time $(C foreach), it may not be clear whether $(C break) and $(C continue) statements inside the loop body should affect the compile-time loop iteration itself or whether they should be parts of the unrolled code. +) + +) + +$(P +$(C static foreach) is a more powerful compile-time feature that provides more control: +) + +$(UL + +$(LI +$(C static foreach) can work with any range of elements that can be computed at compile time (including number ranges like $(C 1..10)). For example, given the $(C FibonacciSeries) range from $(LINK2 /ders/d.en/ranges.html, the Ranges chapter) and a function that determines whether a number is even: +) + +) + +--- + $(HILITE static foreach) (n; FibonacciSeries().take(10).filter!isEven) { + writeln(n); + } +--- + +$(P +The loop above would be unrolled as the following equivalent: +) + +--- + writeln(0); + writeln(2); + writeln(8); + writeln(34); +--- + +$(UL + +$(LI $(C static foreach) can be used at module scope.) + +$(LI +$(C static foreach) does not introduce a separate scope for each iteration. For example, the following loop defines two overloads of a function at module scope: +) + +) + +--- +import std.meta; + +static foreach (T; AliasSeq!(int, double)) { + T twoTimes(T arg) { + return arg * 2; + } +} + +void main() { +} +--- + +$(P +The loop above would be unrolled as its following equivalent: +) + +--- + int twoTimes(int arg) { + return arg * 2; + } + + double twoTimes(double arg) { + return arg * 2; + } +--- + +$(UL + +$(LI +$(C break) and $(C continue) statements inside a $(C static foreach) loop require labels for clarity. For example, the following code unrolls (generates) $(C case) clauses inside a $(C switch) statement. The $(C break) statements that are under each $(C case) clause must mention the associated $(C switch) statements by labels: +) + +) + +--- +import std.stdio; + +void main(string[] args) { + +$(HILITE theSwitchStatement:) + switch (args.length) { + static foreach (i; 1..3) { + case i: + writeln(i); + break $(HILITE theSwitchStatement); + } + + default: + writeln("default case"); + break; + } +} +--- + +$(P +After the loop above is unrolled, the $(C switch) statement would be the equivalent of the following code: +) + +--- + switch (args.length) { + case 1: + writeln(1); + break; + + case 2: + writeln(2); + break; + + default: + writeln("default case"); + break; + } +--- + +macros: + SUBTITLE=static foreach + + DESCRIPTION=static foreach, one of D's compile-time feature that can generate code by iterating ranges. + + KEYWORDS=d programming language tutorial book static foreach + +MINI_SOZLUK= diff --git a/ddili/src/ders/d.cn/stream_redirect.cozum.d b/d.en/stream_redirect.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/stream_redirect.cozum.d rename to d.en/stream_redirect.cozum.d diff --git a/ddili/src/ders/d.cn/stream_redirect.d b/d.en/stream_redirect.d similarity index 100% rename from ddili/src/ders/d.cn/stream_redirect.d rename to d.en/stream_redirect.d diff --git a/ddili/src/ders/d.en/strings.cozum.d b/d.en/strings.cozum.d similarity index 100% rename from ddili/src/ders/d.en/strings.cozum.d rename to d.en/strings.cozum.d diff --git a/ddili/src/ders/d.en/strings.d b/d.en/strings.d similarity index 98% rename from ddili/src/ders/d.en/strings.d rename to d.en/strings.d index 4751a0c..d6a8ae9 100644 --- a/ddili/src/ders/d.en/strings.d +++ b/d.en/strings.d @@ -127,7 +127,7 @@ 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()) of 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. Its first parameter is the line that contains the data, and the rest of the parameters are used exacly like $(C readf()): ) --- @@ -143,7 +143,7 @@ void main() { string name; int age; - $(HILITE formattedRead)(line, " %s %s", &name, &age); + $(HILITE formattedRead)(line, " %s %s", name, age); writeln("Your name is ", name, ", and your age is ", age, '.'); @@ -160,7 +160,7 @@ Both $(C readf()) and $(C formattedRead()) $(I return) the number of items that ) --- - $(HILITE uint items) = formattedRead(line, " %s %s", &name, &age); + $(HILITE uint items) = formattedRead(line, " %s %s", name, age); if ($(HILITE items != 2)) { writeln("Error: Unexpected line."); diff --git a/ddili/src/ders/d.en/struct.cozum.d b/d.en/struct.cozum.d similarity index 99% rename from ddili/src/ders/d.en/struct.cozum.d rename to d.en/struct.cozum.d index d14599d..e6643ce 100644 --- a/ddili/src/ders/d.en/struct.cozum.d +++ b/d.en/struct.cozum.d @@ -35,7 +35,7 @@ Card[] newDeck() out (result) { assert(result.length == 52); -} body { +} do { Card[] deck; deck ~= newSuit('♠'); @@ -62,7 +62,7 @@ in { } out (result) { assert(result.length == 13); -} body { +} do { Card[] suitCards; foreach (value; "234567890JQKA") { @@ -148,7 +148,7 @@ in { } out (result) { assert(result.length == 13); -} body { +} do { Card[] suitCards; foreach (value; "234567890JQKA") { @@ -162,7 +162,7 @@ Card[] newDeck() out (result) { assert(result.length == 52); -} body { +} do { Card[] deck; deck ~= newSuit('♠'); diff --git a/ddili/src/ders/d.en/struct.d b/d.en/struct.d similarity index 98% rename from ddili/src/ders/d.en/struct.d rename to d.en/struct.d index dc428b3..3080029 100644 --- a/ddili/src/ders/d.en/struct.d +++ b/d.en/struct.d @@ -7,7 +7,7 @@ As has been mentioned several times earlier in the book, fundamental types are n ) $(P -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. 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. +$(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 @@ -828,7 +828,7 @@ Card[] newDeck() out (result) { assert(result.length == 52); -} body { +} do { // ... please define the function body ... } --- diff --git a/ddili/src/ders/d.cn/switch_case.cozum.d b/d.en/switch_case.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/switch_case.cozum.d rename to d.en/switch_case.cozum.d diff --git a/ddili/src/ders/d.cn/switch_case.d b/d.en/switch_case.d similarity index 100% rename from ddili/src/ders/d.cn/switch_case.d rename to d.en/switch_case.d diff --git a/d.en/templates.d b/d.en/templates.d new file mode 100644 index 0000000..a5c41ca --- /dev/null +++ b/d.en/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)); + +} do { + 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); + +} do { + 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/ddili/src/ders/d.en/templates_more.d b/d.en/templates_more.d similarity index 98% rename from ddili/src/ders/d.en/templates_more.d rename to d.en/templates_more.d index 8671a2d..7eb07c5 100644 --- a/ddili/src/ders/d.en/templates_more.d +++ b/d.en/templates_more.d @@ -270,7 +270,7 @@ Union templates are similar to struct templates. The shortcut syntax is availabl ) $(P -As an example, let's design a more general version of the $(C IpAdress) $(C union) that we have seen 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): +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): ) --- @@ -281,7 +281,7 @@ union IpAddress { --- $(P -The $(C bytes) array was an easy access to the four segments of the IPv4 address. +The $(C bytes) array provided easy access to the four segments of the IPv4 address. ) $(P @@ -432,7 +432,7 @@ class Bulb : ColoredObject$(HILITE !Frequency) { --- $(P -However, as explained in $(LINK2 /ders/d.en/templates.html, the Templates chapter), "every different instantiation of a template is a different type". Accordingly, the interfaces $(C ColoredObject!RGB) and $(C ColoredObject!Frequency) are unrelated interfaces, and $(C PageFrame) and $(C Bulb) are unrelated classes. +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) @@ -652,7 +652,7 @@ void func(T, } void main() { - func(42); $(CODE_NOTE $(HILITE line 14)) + func(42); $(CODE_NOTE $(HILITE line 12)) } --- @@ -661,7 +661,7 @@ Although the special keywords appear in the definition of the template, their va ) $(SHELL -Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 14). +Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 12). ) $(P @@ -966,6 +966,10 @@ void info(T...)(T args) { } --- +$(P +$(I $(B Note:) As seen in the previous chapter, since the arguments are a tuple, the $(C foreach) statement above is a) compile-time $(C foreach). +) + $(P The output: ) @@ -1155,7 +1159,7 @@ LargerOf!(A, B) calculate(A, B)(A a, B b) { $(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: +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 the general definition of a template and its specialization for 0: ) --- @@ -1493,7 +1497,7 @@ A template definition is considered by the compiler only if its constraints eval ) $(P -Since templates are a compile-time feature, template constraints must be evaluable at compile time. The $(C is) expression that we have seen 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. +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) @@ -1867,7 +1871,7 @@ public: } void toString(void delegate(const(char)[]) sink) const { - formattedWrite(sink, "%(%(%5s %)\n%)", rows); + sink.formattedWrite!"%(%(%5s %)\n%)"(rows); } /* Assigns the specified value to each element of the @@ -2055,7 +2059,7 @@ $(LI Specifying template parameter lists is sufficient to make function, struct, $(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 different instantiation of a template is a different type.) +$(LI Each template instantiation yields a different type.) $(LI Template arguments can only be deduced for function templates.) diff --git a/ddili/src/ders/d.en/ternary.cozum.d b/d.en/ternary.cozum.d similarity index 100% rename from ddili/src/ders/d.en/ternary.cozum.d rename to d.en/ternary.cozum.d diff --git a/ddili/src/ders/d.en/ternary.d b/d.en/ternary.d similarity index 100% rename from ddili/src/ders/d.en/ternary.d rename to d.en/ternary.d diff --git a/ddili/src/ders/d.en/to_be_continued.d b/d.en/to_be_continued.d similarity index 100% rename from ddili/src/ders/d.en/to_be_continued.d rename to d.en/to_be_continued.d diff --git a/d.en/tuples.d b/d.en/tuples.d new file mode 100644 index 0000000..b989661 --- /dev/null +++ b/d.en/tuples.d @@ -0,0 +1,575 @@ +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. +) + +$(P +We will see $(C static foreach), a more powerful loop unrolling feature, in $(LINK2 /ders/d.en/static_foreach.html, a later chapter). +) + +$(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/ddili/src/ders/d.cn/types.cozum.d b/d.en/types.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/types.cozum.d rename to d.en/types.cozum.d diff --git a/ddili/src/ders/d.cn/types.d b/d.en/types.d similarity index 100% rename from ddili/src/ders/d.cn/types.d rename to d.en/types.d diff --git a/ddili/src/ders/d.en/ufcs.d b/d.en/ufcs.d similarity index 100% rename from ddili/src/ders/d.en/ufcs.d rename to d.en/ufcs.d diff --git a/ddili/src/ders/d.cn/union.d b/d.en/union.d similarity index 98% rename from ddili/src/ders/d.cn/union.d rename to d.en/union.d index baff597..eb741de 100644 --- a/ddili/src/ders/d.cn/union.d +++ b/d.en/union.d @@ -3,7 +3,7 @@ Ddoc $(DERS_BOLUMU $(IX union) Unions) $(P -Unions allow more than one member share the same memory area. They are a low-level feature inherited from the C programming language. +Unions, a low-level feature inherited from the C programming language, allow more than one member to share the same memory area. ) $(P diff --git a/ddili/src/ders/d.cn/unit_testing.cozum.d b/d.en/unit_testing.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/unit_testing.cozum.d rename to d.en/unit_testing.cozum.d diff --git a/ddili/src/ders/d.en/unit_testing.d b/d.en/unit_testing.d similarity index 99% rename from ddili/src/ders/d.en/unit_testing.d rename to d.en/unit_testing.d index e160d10..8326d50 100644 --- a/ddili/src/ders/d.en/unit_testing.d +++ b/d.en/unit_testing.d @@ -169,7 +169,7 @@ Assuming that the program is written in a single source file named $(C deneme.d) ) $(SHELL -dmd deneme.d -w $(HILITE -unittest) +$ dmd deneme.d -w $(HILITE -unittest) ) $(P diff --git a/ddili/src/ders/d.cn/value_vs_reference.d b/d.en/value_vs_reference.d similarity index 98% rename from ddili/src/ders/d.cn/value_vs_reference.d rename to d.en/value_vs_reference.d index c231911..e22a871 100644 --- a/ddili/src/ders/d.cn/value_vs_reference.d +++ b/d.en/value_vs_reference.d @@ -121,6 +121,9 @@ $(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: ) @@ -567,7 +570,7 @@ class MyClass { void printHeader() { immutable dchar[] header = - " Type of variable" + " Type of variable" ~ " a == b &a == &b"; writeln(); diff --git a/ddili/src/ders/d.cn/variables.cozum.d b/d.en/variables.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/variables.cozum.d rename to d.en/variables.cozum.d diff --git a/ddili/src/ders/d.cn/variables.d b/d.en/variables.d similarity index 100% rename from ddili/src/ders/d.cn/variables.d rename to d.en/variables.d diff --git a/ddili/src/ders/d.cn/while.cozum.d b/d.en/while.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/while.cozum.d rename to d.en/while.cozum.d diff --git a/ddili/src/ders/d.en/while.d b/d.en/while.d similarity index 91% rename from ddili/src/ders/d.en/while.d rename to d.en/while.d index 4b25e67..9134507 100644 --- a/ddili/src/ders/d.en/while.d +++ b/d.en/while.d @@ -138,21 +138,21 @@ $(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) +$(H5 $(IX loop, infinite) $(IX loop, unconditional) $(IX infinite loop) $(IX unconditional loop) Unconditional 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). +Sometimes the logical expression is intentionally made a constant $(C true). The $(C break) statement is a common way of exiting such $(I unconditional loops). ($(I Infinite loop) is an alternative but not completely accurate term that means unconditional loop.) ) $(P -The following program prints a menu in an infinite loop; the only way of exiting the loop is a $(C break) statement: +The following program prints a menu in an unconditional 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 + /* Unconditional loop, because the logical expression is always * true */ while ($(HILITE true)) { write("0:Exit, 1:Turkish, 2:English - Your choice? "); @@ -178,7 +178,7 @@ void main() { --- $(P -$(I $(B Note:)) Exceptions $(I can terminate an infinite loop as well. We will see exceptions in a later chapter.) +$(I $(B Note:)) Exceptions $(I can terminate an unconditional loop as well. We will see exceptions in a later chapter.) ) $(PROBLEM_COK diff --git a/ddili/src/ders/d.en/writeln.cozum.d b/d.en/writeln.cozum.d similarity index 89% rename from ddili/src/ders/d.en/writeln.cozum.d rename to d.en/writeln.cozum.d index a45861d..bf8eafd 100644 --- a/ddili/src/ders/d.en/writeln.cozum.d +++ b/d.en/writeln.cozum.d @@ -8,7 +8,7 @@ $(LI One method is to use another parameter in between: --- - writeln("Hello world!", " ", "Hello fish!"); + writeln("Hello, World!", " ", "Hello, fish!"); --- ) diff --git a/ddili/src/ders/d.en/writeln.d b/d.en/writeln.d similarity index 90% rename from ddili/src/ders/d.en/writeln.d rename to d.en/writeln.d index 77c63f9..adb618d 100644 --- a/ddili/src/ders/d.en/writeln.d +++ b/d.en/writeln.d @@ -22,12 +22,12 @@ $(C writeln) can take more than one argument. It prints them one after the other import std.stdio; void main() { - writeln("Hello world!", "Hello fish!"); + 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). +Sometimes, not all of the information that is to be printed on the same line may 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 @@ -39,11 +39,11 @@ import std.stdio; void main() { // Let's first print what we have available: - write("Hello"); + write("Hello,"); // ... let's assume more operations at this point ... - write("world!"); + write("World!"); // ... and finally: writeln(); @@ -62,7 +62,7 @@ $(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!". +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!". ) 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 ef646c6..0000000 Binary files a/ddili/calibre_config_dir/dynamic.pickle and /dev/null differ diff --git a/ddili/calibre_config_dir/fonts/scanner_cache.json b/ddili/calibre_config_dir/fonts/scanner_cache.json deleted file mode 100644 index 71eb188..0000000 --- a/ddili/calibre_config_dir/fonts/scanner_cache.json +++ /dev/null @@ -1,2777 +0,0 @@ -{ - "version": 1, - "fonts": { - "/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf||118264:1342127574.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 7, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationMono-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Mono Bold Italic", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Bold.ttf||313856:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 9, - 3, - 6, - 4, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "DejaVu Sans Mono", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Mono Bold", - "font-family": "DejaVu Sans Mono" - }, - "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf||108140:1342127575.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 2, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Mono", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/freefont/FreeSerif.ttf||1468768:1304195169.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 5, - 4, - 5, - 2, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSerif.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSerif", - "subfamily_name": "Medium", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Serif", - "font-family": "FreeSerif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-Italic.ttf||338140:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 6, - 6, - 6, - 5, - 3, - 5, - 11, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-Italic.ttf", - "preferred_subfamily_name": "Condensed Italic", - "family_name": "DejaVu Serif Condensed", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Serif Condensed Italic", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf||146660:1342127576.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 8, - 3, - 7, - 5, - 5, - 2, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Serif Bold", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf||151996:1342127576.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 5, - 4, - 5, - 2, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Serif", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/freefont/FreeSans.ttf||585252:1304195163.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSans.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSans", - "subfamily_name": "Medium", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Sans", - "font-family": "FreeSans" - }, - "/usr/share/fonts/truetype/nanum/NanumGothic.ttf||4345840:1296594996.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 13, - 6, - 4, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/nanum/NanumGothic.ttf", - "preferred_subfamily_name": "Regular", - "family_name": "NanumGothic", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 2, - "is_italic": false, - "preferred_family_name": "NanumGothic", - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "NanumGothic", - "font-family": "NanumGothic" - }, - "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf||139020:1342127575.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Bold.ttf||109948:1342127577.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "condensed", - "panose": [ - 2, - 11, - 7, - 6, - 2, - 2, - 2, - 3, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 3, - "path": "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans Narrow", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Narrow Bold", - "font-family": "Liberation Sans Narrow" - }, - "/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf||169184:1304195184.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 8, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeMonoBoldOblique.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeMono", - "subfamily_name": "BoldOblique", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Monospaced Bold Oblique", - "font-family": "FreeMono" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-BI.ttf||216208:1317052314.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 9, - 3, - 6, - 2, - 10, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-BI.ttf", - "preferred_subfamily_name": "Bold Italic", - "family_name": "Ubuntu Mono", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": "Ubuntu Mono", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Ubuntu Mono Bold Italic", - "font-family": "Ubuntu Mono" - }, - "/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf||172860:1304195161.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 8, - 9, - 2, - 8, - 5, - 2, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeMono", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Monospaced Bold", - "font-family": "FreeMono" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSans-Regular.ttf||352308:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSans-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf||284968:1304195159.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 7, - 3, - 6, - 5, - 5, - 9, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSerif", - "subfamily_name": "BoldItalic", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Serif Bold Italic", - "font-family": "FreeSerif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-BoldItalic.ttf||332036:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 6, - 8, - 3, - 5, - 3, - 5, - 11, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-BoldItalic.ttf", - "preferred_subfamily_name": "Bold Italic", - "family_name": "DejaVu Serif", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Serif Bold Italic", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf||134540:1342127575.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Bold Italic", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-Bold.ttf||631992:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 11, - 8, - 6, - 3, - 6, - 4, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-Bold.ttf", - "preferred_subfamily_name": "Condensed Bold", - "family_name": "DejaVu Sans Condensed", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Condensed Bold", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-Oblique.ttf||576004:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 11, - 6, - 6, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-Oblique.ttf", - "preferred_subfamily_name": "Condensed Oblique", - "family_name": "DejaVu Sans Condensed", - "subfamily_name": "Oblique", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans Condensed Oblique", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/freefont/FreeSerifItalic.ttf||443216:1304195182.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 5, - 4, - 5, - 9, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSerifItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSerif", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Serif Italic", - "font-family": "FreeSerif" - }, - "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf||329376:1304195157.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSans", - "subfamily_name": "Oblique", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Sans Oblique", - "font-family": "FreeSans" - }, - "/usr/share/fonts/truetype/freefont/FreeMono.ttf||343284:1304195160.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 2, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeMono.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeMono", - "subfamily_name": "Medium", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Monospaced", - "font-family": "FreeMono" - }, - "/usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf||3808496:1296594996.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 2, - 1, - 1, - 2, - 1, - 1 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/nanum/NanumMyeongjo.ttf", - "preferred_subfamily_name": "Regular", - "family_name": "NanumMyeongjo", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "NanumMyeongjo", - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "NanumMyeongjo", - "font-family": "NanumMyeongjo" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-ExtraLight.ttf||345208:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 2, - 3, - 3, - 8, - 4, - 2, - 2, - 4 - ], - "font-weight": "200", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-ExtraLight.ttf", - "preferred_subfamily_name": "ExtraLight", - "family_name": "DejaVu Sans Light", - "subfamily_name": "ExtraLight", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 200, - "wws_family_name": null, - "full_name": "DejaVu Sans ExtraLight", - "font-family": "DejaVu Sans" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSerif-Regular.ttf||390836:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 5, - 4, - 5, - 2, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSerif-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Serif", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf||673144:1334043650.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 3, - 3, - 6, - 4, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Bold.ttf", - "preferred_subfamily_name": "Bold", - "family_name": "DejaVu Sans", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Bold", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf||136672:1342127575.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Bold", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf||112720:1342127577.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "condensed", - "panose": [ - 2, - 11, - 6, - 6, - 2, - 2, - 2, - 3, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 3, - "path": "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans Narrow", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans Narrow", - "font-family": "Liberation Sans Narrow" - }, - "/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf||105428:1342127574.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 7, - 9, - 2, - 2, - 5, - 2, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Mono Bold", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Oblique.ttf||241972:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 9, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Oblique.ttf", - "preferred_subfamily_name": null, - "family_name": "DejaVu Sans Mono", - "subfamily_name": "Oblique", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans Mono Oblique", - "font-family": "DejaVu Sans Mono" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSerif-Bold.ttf||367460:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 8, - 3, - 7, - 5, - 5, - 2, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSerif-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Serif Bold", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf||4291480:1296594996.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 13, - 8, - 4, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "font-weight": "600", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf", - "preferred_subfamily_name": "Bold", - "family_name": "NanumGothic", - "subfamily_name": "Bold", - "is_bold": false, - "os2_version": 2, - "is_italic": false, - "preferred_family_name": "NanumGothic", - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 600, - "wws_family_name": null, - "full_name": "NanumGothic Bold", - "font-family": "NanumGothic" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf||350420:1316705700.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 6, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf", - "preferred_subfamily_name": "Regular", - "family_name": "Ubuntu Condensed", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "Ubuntu Condensed", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Ubuntu Condensed", - "font-family": "Ubuntu Condensed" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf||356980:1316703662.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 4, - 3, - 6, - 2, - 10, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf", - "preferred_subfamily_name": null, - "family_name": "Ubuntu", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Ubuntu Bold Italic", - "font-family": "Ubuntu" - }, - "/opt/calibre/resources/fonts/liberation/LiberationMono-Regular.ttf||314408:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 2, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationMono-Regular.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Mono", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf||333616:1316703356.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 4, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf", - "preferred_subfamily_name": null, - "family_name": "Ubuntu", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Ubuntu Bold", - "font-family": "Ubuntu" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed.ttf||330012:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 6, - 6, - 6, - 5, - 6, - 5, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed.ttf", - "preferred_subfamily_name": "Condensed", - "family_name": "DejaVu Serif Condensed", - "subfamily_name": "Book", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Serif Condensed", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf||124184:1342127574.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationMono-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Mono Italic", - "font-family": "Liberation Mono" - }, - "/opt/calibre/resources/fonts/liberation/LiberationMono-Italic.ttf||275740:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationMono-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Mono Italic", - "font-family": "Liberation Mono" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSerif-BoldItalic.ttf||372716:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 7, - 3, - 6, - 5, - 5, - 9, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSerif-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Serif Bold Italic", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-BoldItalic.ttf||331128:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 6, - 8, - 6, - 5, - 3, - 5, - 11, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-BoldItalic.ttf", - "preferred_subfamily_name": "Condensed Bold Italic", - "family_name": "DejaVu Serif Condensed", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Serif Condensed Bold Italic", - "font-family": "DejaVu Serif" - }, - "/opt/calibre/resources/fonts/liberation/LiberationMono-BoldItalic.ttf||278692:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 7, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationMono-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Mono Bold Italic", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf||309212:1304195186.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 8, - 3, - 7, - 5, - 5, - 2, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSerif", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Serif Bold", - "font-family": "FreeSerif" - }, - "/usr/share/fonts/truetype/freefont/Untitled1.ttf||284964:1304195173.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 0, - 6, - 3, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "font-weight": "500", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/Untitled1.ttf", - "preferred_subfamily_name": null, - "family_name": "Untitled1", - "subfamily_name": "Medium", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 500, - "wws_family_name": null, - "full_name": "Untitled1", - "font-family": "Untitled1" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-BoldOblique.ttf||580168:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 11, - 8, - 6, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed-BoldOblique.ttf", - "preferred_subfamily_name": "Condensed Bold Oblique", - "family_name": "DejaVu Sans Condensed", - "subfamily_name": "Bold Oblique", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Condensed Bold Oblique", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-B.ttf||191400:1317052287.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 9, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-B.ttf", - "preferred_subfamily_name": "Bold", - "family_name": "Ubuntu Mono", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "Ubuntu Mono", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Ubuntu Mono Bold", - "font-family": "Ubuntu Mono" - }, - "/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf||207412:1304195183.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 4, - 9, - 2, - 2, - 5, - 9, - 4, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeMono", - "subfamily_name": "Oblique", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Free Monospaced Oblique", - "font-family": "FreeMono" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed.ttf||643852:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 11, - 6, - 6, - 3, - 8, - 4, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansCondensed.ttf", - "preferred_subfamily_name": "Condensed", - "family_name": "DejaVu Sans Condensed", - "subfamily_name": "Book", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans Condensed", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-Bold.ttf||316440:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "semi-condensed", - "panose": [ - 2, - 6, - 8, - 6, - 5, - 6, - 5, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 4, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerifCondensed-Bold.ttf", - "preferred_subfamily_name": "Condensed Bold", - "family_name": "DejaVu Serif Condensed", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Serif Condensed Bold", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf||205676:1304195188.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSans", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Sans Bold", - "font-family": "FreeSans" - }, - "/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf||144020:1342127576.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 5, - 3, - 5, - 4, - 5, - 9, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSerif-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Serif Italic", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Oblique.ttf||611556:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 3, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-Oblique.ttf", - "preferred_subfamily_name": "Oblique", - "family_name": "DejaVu Sans", - "subfamily_name": "Oblique", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans Oblique", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-BoldItalic.ttf||127780:1342127577.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "condensed", - "panose": [ - 2, - 11, - 7, - 6, - 2, - 2, - 2, - 10, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 3, - "path": "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans Narrow", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Narrow Bold Italic", - "font-family": "Liberation Sans Narrow" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-RI.ttf||210216:1317052262.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 9, - 3, - 6, - 2, - 10, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-RI.ttf", - "preferred_subfamily_name": "Italic", - "family_name": "Ubuntu Mono", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": "Ubuntu Mono", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Ubuntu Mono Italic", - "font-family": "Ubuntu Mono" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSans-Bold.ttf||356140:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSans-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Bold", - "font-family": "Liberation Sans" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSerif-Italic.ttf||371712:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 5, - 3, - 5, - 4, - 5, - 9, - 3, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSerif-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Serif Italic", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/freefont/FreeSansBoldOblique.ttf||189124:1304195187.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/freefont/FreeSansBoldOblique.ttf", - "preferred_subfamily_name": null, - "family_name": "FreeSans", - "subfamily_name": "BoldOblique", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Free Sans Bold Oblique", - "font-family": "FreeSans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf||363200:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 6, - 6, - 3, - 5, - 6, - 5, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif.ttf", - "preferred_subfamily_name": "Book", - "family_name": "DejaVu Serif", - "subfamily_name": "Book", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Serif", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-R.ttf||205748:1317052235.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 9, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-R.ttf", - "preferred_subfamily_name": "Regular", - "family_name": "Ubuntu Mono", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "Ubuntu Mono", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Ubuntu Mono", - "font-family": "Ubuntu Mono" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-Bold.ttf||341072:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 6, - 8, - 3, - 5, - 6, - 5, - 2, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-Bold.ttf", - "preferred_subfamily_name": "Bold", - "family_name": "DejaVu Serif", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Serif Bold", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Italic.ttf||131788:1342127577.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "condensed", - "panose": [ - 2, - 11, - 6, - 6, - 2, - 2, - 2, - 10, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 3, - "path": "/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans Narrow", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans Narrow Italic", - "font-family": "Liberation Sans Narrow" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-BoldOblique.ttf||235848:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 9, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-BoldOblique.ttf", - "preferred_subfamily_name": null, - "family_name": "DejaVu Sans Mono", - "subfamily_name": "Bold Oblique", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Mono Bold Oblique", - "font-family": "DejaVu Sans Mono" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-LI.ttf||409608:1316703586.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 3, - 4, - 3, - 6, - 2, - 10, - 2, - 4 - ], - "font-weight": "300", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-LI.ttf", - "preferred_subfamily_name": "Light Italic", - "family_name": "Ubuntu Light", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": "Ubuntu", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 300, - "wws_family_name": null, - "full_name": "Ubuntu Light Italic", - "font-family": "Ubuntu" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-RI.ttf||386440:1316704340.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 4, - 3, - 6, - 2, - 10, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-RI.ttf", - "preferred_subfamily_name": null, - "family_name": "Ubuntu", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Ubuntu Italic", - "font-family": "Ubuntu" - }, - "/opt/calibre/resources/fonts/liberation/LiberationMono-Bold.ttf||302688:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 7, - 7, - 9, - 2, - 2, - 5, - 2, - 4, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationMono-Bold.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Mono", - "subfamily_name": "Bold", - "is_bold": true, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Mono Bold", - "font-family": "Liberation Mono" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf||353824:1316703436.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 5, - 4, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf", - "preferred_subfamily_name": null, - "family_name": "Ubuntu", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "Ubuntu", - "font-family": "Ubuntu" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSans-Italic.ttf||357416:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSans-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans Italic", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf||720856:1334043650.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 3, - 3, - 8, - 4, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", - "preferred_subfamily_name": "Book", - "family_name": "DejaVu Sans", - "subfamily_name": "Book", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans", - "font-family": "DejaVu Sans" - }, - "/opt/calibre/resources/fonts/liberation/LiberationSans-BoldItalic.ttf||351672:1369721134.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 7, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/opt/calibre/resources/fonts/liberation/LiberationSans-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Sans Bold Italic", - "font-family": "Liberation Sans" - }, - "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-L.ttf||415552:1316703712.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 3, - 4, - 3, - 6, - 2, - 3, - 2, - 4 - ], - "font-weight": "300", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-L.ttf", - "preferred_subfamily_name": "Light", - "family_name": "Ubuntu Light", - "subfamily_name": "Regular", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "Ubuntu", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 300, - "wws_family_name": null, - "full_name": "Ubuntu Light", - "font-family": "Ubuntu" - }, - "/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf||150396:1342127576.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 7, - 3, - 6, - 5, - 5, - 9, - 3, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSerif-BoldItalic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Serif", - "subfamily_name": "Bold Italic", - "is_bold": true, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "Liberation Serif Bold Italic", - "font-family": "Liberation Serif" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-BoldOblique.ttf||611212:1298817440.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 8, - 3, - 3, - 3, - 4, - 11, - 2, - 4 - ], - "font-weight": "bold", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans-BoldOblique.ttf", - "preferred_subfamily_name": "Bold Oblique", - "family_name": "DejaVu Sans", - "subfamily_name": "Bold Oblique", - "is_bold": true, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Sans", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 700, - "wws_family_name": null, - "full_name": "DejaVu Sans Bold Oblique", - "font-family": "DejaVu Sans" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-Italic.ttf||338776:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 6, - 6, - 3, - 5, - 3, - 5, - 11, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSerif-Italic.ttf", - "preferred_subfamily_name": "Italic", - "family_name": "DejaVu Serif", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 1, - "is_italic": true, - "preferred_family_name": "DejaVu Serif", - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Serif Italic", - "font-family": "DejaVu Serif" - }, - "/usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf||4638736:1296594996.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 2, - 6, - 3, - 2, - 1, - 1, - 2, - 1, - 1 - ], - "font-weight": "600", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/nanum/NanumMyeongjoBold.ttf", - "preferred_subfamily_name": "Bold", - "family_name": "NanumMyeongjo", - "subfamily_name": "Bold", - "is_bold": false, - "os2_version": 3, - "is_italic": false, - "preferred_family_name": "NanumMyeongjo", - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 600, - "wws_family_name": null, - "full_name": "NanumMyeongjoBold", - "font-family": "NanumMyeongjo" - }, - "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf||333636:1298817441.0": { - "is_otf": false, - "is_wws": false, - "is_regular": true, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 9, - 3, - 8, - 4, - 2, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", - "preferred_subfamily_name": null, - "family_name": "DejaVu Sans Mono", - "subfamily_name": "Book", - "is_bold": false, - "os2_version": 1, - "is_italic": false, - "preferred_family_name": null, - "fs_type": 0, - "wws_subfamily_name": null, - "font-style": "normal", - "weight": 400, - "wws_family_name": null, - "full_name": "DejaVu Sans Mono", - "font-family": "DejaVu Sans Mono" - }, - "/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf||161520:1342127575.0": { - "is_otf": false, - "is_wws": false, - "is_regular": false, - "font-stretch": "normal", - "panose": [ - 2, - 11, - 6, - 4, - 2, - 2, - 2, - 9, - 2, - 4 - ], - "font-weight": "normal", - "is_oblique": false, - "width": 5, - "path": "/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf", - "preferred_subfamily_name": null, - "family_name": "Liberation Sans", - "subfamily_name": "Italic", - "is_bold": false, - "os2_version": 3, - "is_italic": true, - "preferred_family_name": null, - "fs_type": 8, - "wws_subfamily_name": null, - "font-style": "italic", - "weight": 400, - "wws_family_name": null, - "full_name": "Liberation Sans Italic", - "font-family": "Liberation Sans" - } - } -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/global.py b/ddili/calibre_config_dir/global.py deleted file mode 100644 index 394f7b1..0000000 --- a/ddili/calibre_config_dir/global.py +++ /dev/null @@ -1,98 +0,0 @@ -# calibre wide preferences - -### Begin group: DEFAULT - -# database path -# Path to the database in which books are stored -database_path = '/home/ali/library1.db' - -# filename pattern -# Pattern to guess metadata from filenames -filename_pattern = u'(?P.+) - (?P<author>[^_]+)' - -# 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": [ - "", - "<hr />", - "\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 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>lineedit_history_regex_edit_sr_search</key> - <array> - </array> - <key>lineedit_history_xpath_edit_opt_chapter</key> - <array> - <string>//*[((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']</string> - </array> - <key>lineedit_history_xpath_edit_opt_level1_toc</key> - <array> - <string>//h:h4</string> - <string>//h:*h4</string> - </array> - <key>lineedit_history_xpath_edit_opt_level2_toc</key> - <array> - </array> - <key>lineedit_history_xpath_edit_opt_page_breaks_before</key> - <array> - <string>//*[name()='h1' or name()='h2' or name()='h4']</string> - </array> - <key>lineedit_history_xpath_edit_opt_start_reading_at</key> - <array> - </array> -</dict> -</plist> diff --git a/ddili/calibre_config_dir/iterator.pickle b/ddili/calibre_config_dir/iterator.pickle deleted file mode 100644 index c7b2609..0000000 Binary files a/ddili/calibre_config_dir/iterator.pickle and /dev/null differ diff --git a/ddili/calibre_config_dir/metadata_sources/ISBNDB.json b/ddili/calibre_config_dir/metadata_sources/ISBNDB.json deleted file mode 100644 index 2787805..0000000 --- a/ddili/calibre_config_dir/metadata_sources/ISBNDB.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "key_migrated": true -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/tweak_book_gui.json b/ddili/calibre_config_dir/tweak_book_gui.json deleted file mode 100644 index 2fdeda2..0000000 --- a/ddili/calibre_config_dir/tweak_book_gui.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "recent-books": [ - "/home/ali/calibre_library/Ali Cehreli/Programming in D - First Edition (44)/Programming in D - First Edition - Ali Cehreli.epub" - ], - "main_window_geometry": { - "__value__": "AdnQywABAAAAAAArAAAABQAABLwAAAK/AAAALAAAABkAAAS7AAACugAAAAAAAA==", - "__class__": "bytearray" - }, - "find-widget-state": { - "case_sensitive": false, - "direction": "down", - "wrap": true, - "dot_all": false, - "where": "current", - "mode": "normal" - }, - "saved_seaches_state": { - "direction": "down", - "wrap": true, - "where": "current" - }, - "search-panel-visible": false, - "check-book-splitter-state": { - "__value__": "AAAA/wAAAAEAAAAC//////////8AAAAABAEAAAABAA==", - "__class__": "bytearray" - }, - "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 - ] - ], - "main_window_state": { - "__value__": "AAAA/wAAAAD9AAAABAAAAAAAAAEAAAACPfwCAAAABPsAAAAkAGYAaQBsAGUAcwAtAGIAcgBvAHcAcwBlAHIALQBkAG8AYwBrAQAAAE8AAAI9AAAAWQD////7AAAAHgB0AG8AYwAtAHYAaQBlAHcAZQByAC0AZABvAGMAawAAAAAA/////wAAAFkA////+wAAACAAYwBoAGUAYwBrAHAAbwBpAG4AdABzAC0AZABvAGMAawAAAAAA/////wAAAIoA////+wAAACYAcwBhAHYAZQBkAC0AcwBlAGEAcgBjAGgAZQBzAC0AZABvAGMAawAAAAAA/////wAAAgcA////AAAAAQAAAZYAAAI9/AIAAAAC+wAAABgAcAByAGUAdgBpAGUAdwAtAGQAbwBjAGsBAAAATwAAAj0AAABAAP////sAAAAaAGwAaQB2AGUALQBjAHMAcwAtAGQAbwBjAGsAAAAAAP////8AAABZAP///wAAAAIAAAAAAAAAAPwBAAAAAfsAAAAeAGMAaABlAGMAawAtAGIAbwBvAGsALQBkAG8AYwBrAAAAAAD/////AAAAkAD///8AAAADAAAAAAAAAAD8AQAAAAH7AAAAHABpAG4AcwBwAGUAYwB0AG8AcgAtAGQAbwBjAGsAAAAAAP////8AAAApAP///wAAAe4AAAI9AAAABAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAMAAAAUAGcAbABvAGIAYQBsAC0AYgBhAHIBAAAAAP////8AAAAAAAAAAAAAABIAdABvAG8AbABzAC0AYgBhAHIBAAABOP////8AAAAAAAAAAAAAABYAcABsAHUAZwBpAG4AcwAtAGIAYQByAAAAAAD/////AAAAAAAAAAA=", - "__class__": "bytearray" - } -} \ No newline at end of file diff --git a/ddili/calibre_config_dir/tweaks.py b/ddili/calibre_config_dir/tweaks.py deleted file mode 100644 index 4b468c3..0000000 --- a/ddili/calibre_config_dir/tweaks.py +++ /dev/null @@ -1,566 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -__license__ = 'GPL v3' -__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' -__docformat__ = 'restructuredtext en' - -''' -Contains various tweaks that affect calibre behavior. Only edit this file if -you know what you are doing. If you delete this file, it will be recreated from -defaults. -''' - -#: Auto increment series index -# The algorithm used to assign a book added to an existing series a series number. -# New series numbers assigned using this tweak are always integer values, except -# if a constant non-integer is specified. -# Possible values are: -# next - First available integer larger than the largest existing number -# first_free - First available integer larger than 0 -# next_free - First available integer larger than the smallest existing number -# last_free - First available integer smaller than the largest existing number -# Return largest existing + 1 if no free number is found -# const - Assign the number 1 always -# no_change - Do not change the series index -# a number - Assign that number always. The number is not in quotes. Note that -# 0.0 can be used here. -# Examples: -# series_index_auto_increment = 'next' -# series_index_auto_increment = 'next_free' -# series_index_auto_increment = 16.5 -# -# Set the use_series_auto_increment_tweak_when_importing tweak to True to -# use the above values when importing/adding books. If this tweak is set to -# False (the default) then the series number will be set to 1 if it is not -# explicitly set during the import. If set to True, then the -# series index will be set according to the series_index_auto_increment setting. -# Note that the use_series_auto_increment_tweak_when_importing tweak is used -# only when a value is not provided during import. If the importing regular -# expression produces a value for series_index, or if you are reading metadata -# from books and the import plugin produces a value, than that value will -# be used irrespective of the setting of the tweak. -series_index_auto_increment = 'next' -use_series_auto_increment_tweak_when_importing = False - -#: Add separator after completing an author name -# Should the completion separator be append -# to the end of the completed text to -# automatically begin a new completion operation -# for authors. -# Can be either True or False -authors_completer_append_separator = False - -#: Author sort name algorithm -# The algorithm used to copy author to author_sort -# Possible values are: -# invert: use "fn ln" -> "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 - -<div class="pdf_link"> -$(BR) -$(CENTER $(LINK2 /AliCehreli_resume.pdf, [Click for PDF version])) -</div> - -$(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) - -<html xmlns="http://www.w3.org/1999/xhtml" lang="$(LANG)"> - - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <meta http-equiv="content-language" content="$(LANG)" /> - <meta http-equiv="last-modified" content="$(LAST_MODIFIED)" /> - <meta name="language" content="$(LANGUAGE)" /> - <meta name="description" content="$(DESCRIPTION)" /> - <meta name="keywords" content="$(KEYWORDS)" /> - <title>$(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(`
    `); - - webIdx.writefln(`
    %s
    `, - initial); - - lastInitial = initial; - } - } - - // Book index - { - idx.writefln(`
  • %s `, entry.content); - scope(exit) idx.writeln(`
  • `); - - const indexLinks = - zip(sequence!"n", entry.anchorValues) - .map!(t => format(`%s`, - t[1], - (entry.anchorValues.length == 1 - ? indexLinkChar - : indexLinkStrings[t[0]]))) - .array; - - idx.writefln("%-(  %s%)", indexLinks); - } - - // Web index - { - webIdx.writefln(`%s `, entry.content); - - const indexLinks = - zip(sequence!"n", entry.unsanitizedAnchorValues) - .map!(t => - format(`%s`, - anchorFileName(t[1]), - t[1], - (entry.unsanitizedAnchorValues.length == 1 - ? indexLinkChar - : indexLinkStrings[t[0]]))) - .array; - - webIdx.writefln("%-(  %s%)", indexLinks); - webIdx.writeln("
    "); - } - } -} - -/* This function removes HTML tags altogether and replaces some special - * characters with underscores in order to make clean anchor names. */ -string sanitize(const(char)[] line) -{ - string result; - bool inside = false; - - foreach (c; line.stride(1)) { - if (inside) { - if (c == '>') { - inside = false; - } - - /* Ignore everything that's inside an HTML tag. */ - - } else { - switch (c) { - case '<': - inside = true; - break; - - default: - if (c.isValidXmlNameChar) { - result ~= c; - - } else { - result ~= encoded(c); - } - - break; - } - } - } - - return result.strip; -} - -struct InclusiveCharRange { - dchar beg; - dchar end; - - bool contains(dchar c) const { - return (c >= beg) && (c <= end); - } -} - -bool isContainedBy(dchar c, const InclusiveCharRange[] ranges) { - foreach (range; ranges) { - if (range.contains(c)) { - return true; - } - } - - return false; -} - -bool isValidXmlNameStartChar(dchar c) { - /* Although Ali does not agree with EPUB validators, we are removing ':' - * from valid XML name characters. - * - * (c == ':') || - * See http://www.w3.org/TR/xml11/#NT-NameStartChar - */ - static immutable InclusiveCharRange[] ranges = [ - { '_', '_' }, - { 'a', 'z' }, - { 'A', 'Z' }, - { 'a', 'z' }, - { '\xC0', '\xD6' }, - { '\xD8', '\xF6' }, - { '\xF8', '\u02FF' }, - { '\u0370', '\u037D' }, - { '\u037F', '\u1FFF' }, - { '\u200C', '\u200D' }, - { '\u2070', '\u218F' }, - { '\u2C00', '\u2FEF' }, - { '\u3001', '\uD7FF' }, - { '\uF900', '\uFDCF' }, - { '\uFDF0', '\uFFFD' }, - { '\U00010000', '\U000EFFFF' }, - ]; - - return c.isContainedBy(ranges); -} - -bool isValidXmlNameChar(dchar c) { - static immutable InclusiveCharRange[] ranges = [ - { '-', '-' }, - { '.', '.' }, - { '0', '9' }, - { '\xB7', '\xB7' }, - { '\u0300', '\u036F' }, - { '\u203F', '\u2040' }, - ]; - - return (c.isValidXmlNameStartChar || - c.isContainedBy(ranges)); -} - -static immutable dchar[dchar] encodings; - -static this() { - encodings = [ - '!' : 'X', - '"' : 'X', - '“' : 'X', - '”' : 'X', - '‘' : 'X', - '’' : 'X', - '#' : 'X', - '$' : 'X', - '%' : 'X', - '&' : 'X', - '\'' : 'X', - '(' : 'X', - ')' : 'X', - '(' : 'X', - ')' : 'X', - '*' : 'X', - '+' : 'X', - ',' : 'X', - '/' : 'X', - ':' : 'X', - ';' : 'X', - '=' : 'X', - '?' : 'X', - '@' : 'X', - '[' : 'X', - '\\' : 'X', - ']' : 'X', - '^' : 'X', - '`' : 'X', - '{' : 'X', - '|' : 'X', - '}' : 'X', - '~' : 'X', - ]; -} - -string encoded(dchar c) { - if (c == ' ') { - return "_"; - } - - const encoding = (c in encodings); - - /* If this throws, try enabling other character categories inside - * isValidXmlNameStartChar() and isValidXmlNameChar(). */ - enforce(encoding, format("No encoding for %c (%x)", c, c)); - - return format(".%x.", c); -} - -/* Makes an anchor tag from the anchor name and value. */ -string makeAnchor(const(char)[] anchor) -{ - return format(``, anchor); -} - -/* Makes a chapter cross reference. */ -char[] makeChapterRef(Captures!(const(char)[]) refMatch) -{ - const chapterFileName = refMatch[2]; - const anchorValue = refMatch[3]; - - auto result = format(`%s`, - makeChapterAnchorName(chapterFileName), anchorValue); - - return result.dup; -} - -string removeAnchors(const(char)[] line) -{ - auto begResult = line.findSplit(""); - - return removeAnchors(format("%s%s", begResult[0], endResult[2])); - } -} - -const(char)[] processChapterLineTocAndXrefs(const(char)[] line, - const(char)[] chapterFileName, - ref TocEntry[] tocEntries) -{ - const(char)[] result; - - enum headerExpr = regex( - `(<[hH]([0-9]) class="(.*?)"[^>]*>)(.*)()`); - enum refExpr = regex( - `(.*?)`, "g"); - enum solutionExpr = regex( - `(.*)`); - - auto headerMatch = matchFirst(line, headerExpr); - if (!headerMatch.empty) { - const open = headerMatch[1]; - const level = headerMatch[2]; - const class_ = headerMatch[3]; - const value = headerMatch[4]; - const close = headerMatch[5]; - - const headingAnchorName = - format("%s_%s", chapterFileName, sanitize(line.strip)); - const headingAnchor = makeAnchor(headingAnchorName); - - const chapterAnchorName = makeChapterAnchorName(chapterFileName); - const chapterAnchor = makeAnchor(chapterAnchorName); - - if ((level == "4") || chapterFileName.endsWith(".cozum")) { - auto anchoredValue = value.idup; - - const cozumFound = chapterAnchorName.findSplit(".cozum"); - if (!cozumFound[1].empty) { - /* Add a back reference to the actual chapter of this - * solution. */ - anchoredValue = format(`%s`, - cozumFound[0], anchoredValue); - } - - /* As a workaround for a prince bug, we don't leave values of - * chapter anchors empty. Instead, we wrap it around the H4 anchor - * and text. See: - * - * http://www.princexml.com/forum/topic/1883 - */ - result = format("%s%s%s%s%s", open, chapterAnchor, - headingAnchor, anchoredValue, close); - - - } else { - result = format("%s%s%s%s", open, headingAnchor, value, close); - } - - tocEntries ~= TocEntry( - class_.to!string, level.to!size_t, headingAnchorName, - removeAnchors(value.to!string)); - - } else if (matchFirst(line, refExpr)) { - auto sink = appender!(char[])(); - replaceAllInto!makeChapterRef(sink, line, refExpr); - result = sink.data; - - } else if (matchFirst(line, solutionExpr)) { - auto sink = appender!(char[])(); - - char[] makeSolutionRef(Captures!(const(char)[]) refMatch) - { - const anchorValue = refMatch[1]; - auto result = - format(`%s`, - chapterFileName, anchorValue); - return result.dup; - } - - replaceAllInto!makeSolutionRef(sink, line, solutionExpr); - result = sink.data; - - } else { - result = line; - } - - return result; -} - -const(char)[] processChapterLineIndexes(const(char)[] line, - ref IndexEntry[string] indexEntries) -{ - const(char)[] result; - - enum idxExpr = regex( - ``); - - if (matchFirst(line, idxExpr)) { - auto sink = appender!(char[])(); - - char[] makeIndexAnchor(Captures!(const(char)[]) match) - { - const anchorValue = sanitize(match[1]); - auto anchorKey = match[2]; - const content = match[3]; - - IndexEntry* entry = anchorKey in indexEntries; - - if (entry) { - enforce(entry.content == content, - format("Mismatched index section anchor contents: " ~ - "'%s' versus '%s' of %s", - content, entry.content, *entry)); - - entry.anchorValues ~= anchorValue.to!string; - entry.unsanitizedAnchorValues ~= match[1].to!string; - - } else { - indexEntries[anchorKey] = IndexEntry(content.to!string, - [ anchorValue.to!string ], - [ match[1].to!string ]); - } - - return format(``, anchorValue).dup; - } - - replaceAllInto!makeIndexAnchor(sink, line, idxExpr); - result = sink.data; - - } else { - result = line; - } - - return result; -} diff --git a/ddili/src/breadcrumbs.ddoc b/ddili/src/breadcrumbs.ddoc deleted file mode 100644 index 9732631..0000000 --- a/ddili/src/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=Ana Sayfa - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) diff --git a/ddili/src/chinese.ddoc b/ddili/src/chinese.ddoc deleted file mode 100644 index 54a4bb8..0000000 --- a/ddili/src/chinese.ddoc +++ /dev/null @@ -1 +0,0 @@ -P=

      $0

    diff --git a/ddili/src/codetester.d b/ddili/src/codetester.d deleted file mode 100644 index bf84820..0000000 --- a/ddili/src/codetester.d +++ /dev/null @@ -1,514 +0,0 @@ -/** - * This program processes a Ddoc file to scrape the code examples and compile - * them. Here are the rules of the game: - * - * o Code samples that do not have a main() are ignored. - * - * o Code samples that import non-standard modules are ignored. - * - * o Code samples that have a main() but should not be compiled must be marked - * with $(CODE_DONT_TEST). - * - * o Certain lines of code can be commented-out in code compilation testing by - * $(CODE_COMMENT_OUT) - * - * o Any code sample can be named by $(CODE_NAME). - * - * o Code samples that are named by $(CODE_NAME) can be imported by other code - * samples by $(CODE_XREF). - * - * o Code samples that have $(DERLEME_HATASI) ("compilation error" in English) - * are expected to fail compilation. Others are expected to succeed - * compilation. - */ - -import std.stdio; -import std.string; -import std.regex; -import std.process; -import std.algorithm; -import std.array; -import std.exception; -import std.file; -import std.path; - -enum TestResult { passed, failed, noTestRun } - -struct TestOutcome -{ - TestResult result; - string output; - string input; -} - -struct FileStatus -{ - TestResult lastTestResult; - size_t totalPrograms; // The number of sample programs actually - // compiled -} - -/* This is for correct grammar in the test reports. */ -string pluralFor(size_t n) -{ - return n == 1 ? "" : "s"; -} - -int main(string[] args) -{ - auto file = args[1]; - auto sourceDir = args[2]; - auto targetDir = args[3]; - const alphabetName = args[4]; /* "english", "turkish", etc. */ - - const FileStatus status = - testCodes(file, sourceDir, targetDir, alphabetName); - - final switch (status.lastTestResult) { - case TestResult.passed: - writefln("Compiled %s program%s", - status.totalPrograms, pluralFor(status.totalPrograms)); - break; - - case TestResult.failed: - writeln("Failed"); - return 1; - - case TestResult.noTestRun: - writeln("Nothing to test; skipped"); - break; - } - - return 0; -} - -/* Represents a scraped code section. */ -struct CodeSection -{ - string name; - size_t lineNumber; - string[] lines; - bool hasMain = false; - bool hasNonStandardImport = false; - bool expectedToFail = false; - bool importsStdStdio; - bool usesStdStdio; - - @property bool isStripped() const - { - return (lines.empty || (!lines.front.strip.empty && - !lines.back.strip.empty)); - } -} - -/* Renames main() presumably to move it out of the way of the actual - * main(). */ -auto main_renamed(const(string[]) lines) -{ - return lines.map!(line => line.replace(" main(", " RENAMED_main(")); -} - -CodeSection extractCodeSection(R)(ref R range, - string fileName, - ref size_t lineNumber, - CodeSection[string] codeSections) -{ - /* Skip the opening code delimiter. */ - range.popFront(); - ++lineNumber; - - CodeSection result; - - while (true) { - auto line = range.front; - - if (line.isCodeDelimiter) { - /* This section ends here. Skip the closing code delimiter. */ - range.popFront(); - ++lineNumber; - break; - } - - // Use a do-while loop so that we catch leading and trailing empty lines - do { - line = processCodeLine(line, fileName, lineNumber, - codeSections, result); - } while (!line.empty); - - ++lineNumber; - range.popFront(); - - if (range.empty) { - break; - } - } - - enforceWithContext( - result.isStripped, - format("Code section has leading or trailing empty lines: %-(%s\n%)", - result.lines), - fileName, lineNumber); - - result.lineNumber = lineNumber; - - return result; -} - -void enforceWithContext(bool condition, - string message, - string fileName, - size_t lineNumber) -{ - if (!condition) { - writefln("Error: %s:%s: %s", fileName, lineNumber, message); - throw new Exception("Enforcement failed"); - } -} - -/* Processes code lines by parsing CODE_NAME, expanding CODE_XREF, etc. */ -char[] processCodeLine(char[] line, - string fileName, - size_t lineNumber, - CodeSection[string] codeSections, - ref CodeSection result) -{ - auto codeNameResult = line.findSplit("$(CODE_NAME "); - - if (!codeNameResult[1].empty) { - /* This line introduces the name of this section. */ - - enforceWithContext(result.name.empty, - "Multiple CODE_NAME tags", fileName, lineNumber); - - const closingParenResult = codeNameResult[2].findSplit(")"); - - result.name = closingParenResult[0].strip.idup; - - enforceWithContext( - !closingParenResult[2].empty, - format("Rest of CODE_NAME line is empty: %s", line), - fileName, lineNumber); - - line = closingParenResult[2].dup; - - } else { - - const codeInsertResult = line.findSplit("$(CODE_XREF "); - - if (!codeInsertResult[1].empty) { - /* This line cross-references another code section. */ - - const closingParenResult = - codeInsertResult[2].findSplit(")"); - - const codeRef = closingParenResult[0].strip; - - enforceWithContext((codeRef in codeSections) !is null, - format("Undefined code section: %s", codeRef), - fileName, lineNumber); - - enforceWithContext(!closingParenResult[2].empty, - "CODE_XREF cannot be the last tag on a line", - fileName, lineNumber); - - // There may be other CODE_XREF on this line - line = closingParenResult[2].dup; - - /* Insert the referenced code section after renaming its - * main() function so that it doesn't conflict with the actual - * main of this sample program. */ - result.lines ~= main_renamed(codeSections[codeRef].lines).array; - - if (codeSections[codeRef].usesStdStdio) { - result.usesStdStdio = true; - } - - } else { - /* This is not a code cross-reference. */ - - const commentOutResult = line.findSplit("$(CODE_COMMENT_OUT"); - - if (!commentOutResult[1].empty) { - /* This line should be ignored. */ - - } else { - /* This is an ordinary code line. */ - - result.lines ~= line.dup; - - enum stdStdioExp = ctRegex!("^import std.stdio;"); - - if (matchFirst(line, stdStdioExp)) { - result.importsStdStdio = true; - } - - enum stdStdioUseExp = ctRegex!("(writeln|writefln|File)"); - - if (matchFirst(line, stdStdioUseExp)) { - result.usesStdStdio = true; - } - - enum mainLineExpr = ctRegex!("(void|int) main[ ]*(.*)"); - - if (matchFirst(line, mainLineExpr)) { - /* Ok, this section has a main() function. */ - result.hasMain = true; - } - - enum importLineExpr = ctRegex!(`.*import *(.*?)(\..*)*;`); - line = line.replace("$(HILITE", ""); - auto importMatch = matchFirst(line, importLineExpr); - - if (!importMatch.empty) { - const packageName = importMatch[1]; - - if (!["std", "core"].canFind(packageName)) { - /* This section has a non-standard import. */ - result.hasNonStandardImport = true; - } - } - - enum compFailureExpr = ctRegex!(`.*DERLEME_HATASI`); - - if (matchFirst(line, compFailureExpr)) { - /* This section is expected to fail. */ - result.expectedToFail = true; - } - } - - line = []; - } - } - - return line; -} - -/* Whether this is a Ddoc code section delimiter. */ -bool isCodeDelimiter(const(char)[] line) -{ - return line == "---"; -} - -/* Tests the sample codes of the provided Ddoc file. */ -FileStatus testCodes(string fileName, - string sourceDir, - string targetDir, - string alphabetName) -{ - const sourceFileName = buildPath(sourceDir, fileName); - auto file = File(sourceFileName, "r"); - - CodeSection[string] codeSections; - size_t testCount = 0; - size_t lineNumber = 1; - - char[][] range; - - /* We could not use byLine due to a dmd bug where byLine would write - * out-of-bounds. Instead, we read the file eagerly. */ - while (true) { - import std.conv; - string line = file.readln.stripRight; - if (file.eof) { - break; - } - - range ~= line.to!(char[]); - } - - size_t programCount = 1; - - while (!range.empty) { - auto line = range.front; - - if (line.isCodeDelimiter) { - auto codeSection = - extractCodeSection( - range, sourceFileName, lineNumber, codeSections); - - if (codeSection.hasNonStandardImport) { - writefln("Code has non-standard import; skipping"); - continue; - } - - if (!codeSection.name.empty && codeSection.name !in codeSections) { - codeSections[codeSection.name] = codeSection; - } - - if (codeSection.hasMain) { - const programName = - format("%s.%s.d", baseName(fileName, ".d"), programCount); - - const testOutcome = testProgram( - programName, targetDir, codeSection, alphabetName); - ++programCount; - - const testResult = testOutcome.result; - - const goodOutcome = ((codeSection.expectedToFail && - (testResult == TestResult.failed)) - || - (!codeSection.expectedToFail && - (testResult == TestResult.passed))); - - if (!goodOutcome) { - writefln("Unexpected compilation outcome"); - writefln("Result: %s", testOutcome.result); - writefln("Output:\n%s\n", testOutcome.output); - if (!testOutcome.input.empty) { - writefln("Input:\n%s", testOutcome.input); - } - - writefln("Program:\n%-(%s\n%)", codeSection.lines); - - writefln("Error: %s:%s:", - sourceFileName, - codeSection.lineNumber); - - return FileStatus(TestResult.failed, testCount); - } - - ++testCount; - } - } - - if (!range.empty) { - ++lineNumber; - range.popFront(); - } - } - - return FileStatus(testCount ? TestResult.passed : TestResult.noTestRun, - testCount); -} - -/* Tests the provided program. */ -TestOutcome testProgram(string programName, - string targetDir, - CodeSection codeSection, - string alphabetName) -{ - const preFileName = - buildPath(targetDir, format("%s.pre_ddoc.d", programName)); - auto postFileName = - buildPath(targetDir, programName); - - if (alphabetName == "english") { - postFileName = postFileName.replace(".cozum.", ".solution."); - } - - const compilationFailureComment = - (alphabetName == "english" - ? "// ← compilation ERROR" - : "// ← derleme HATASI"); - - /* We want to remove Ddoc markup from the code. */ - const ddocMacros = [ - "Macros:", - "DDOC=$(BODY)", - "DDOC_COMMENT=", - "HILITE=$0", - format("DERLEME_HATASI=%s", compilationFailureComment), - "D_CODE=$0", - "RED=$0", - "BLUE=$0", - "GREEN=$0", - "YELLOW=$0", - "BLACK=$0", - "WHITE=$0", - "CODE_NOTE=// $0", - "ESCAPES=/&/&/ //>/" - ]; - - /* Pre-process the file to remove all of the Ddoc markup from the sample - * program. */ - - auto preDdocFile = File(preFileName, "w"); - - preDdocFile.writeln("Ddoc"); - preDdocFile.writeln("---"); - - const compilationFailureNotice = - (alphabetName == "english" - ? "NOTE: This program is expected to fail compilation." - : "NOT: Bu program derleme hatasına neden olur."); - - if (codeSection.expectedToFail) { - preDdocFile.writefln("/* %s */\n", compilationFailureNotice); - } - - const program = codeSection.lines; - - if (!program[0].canFind("module")) { - const moduleName = baseName(postFileName, ".d").replace(".", "_"); - preDdocFile.writefln("module %s;\n", moduleName); - } - - const missingStdStdio = (codeSection.usesStdStdio && - !codeSection.importsStdStdio); - - if (missingStdStdio) { - preDdocFile.writeln("import std.stdio;"); - } - - preDdocFile.writefln("%-(%s\n%)", program); - preDdocFile.writeln("---"); - preDdocFile.writefln("%-(%s\n%)", ddocMacros); - preDdocFile.close(); - - auto ddocResult = - executeShell(format("dmd -Dd%s -Df%s %s", - postFileName.dirName, postFileName, preFileName)); - - if (ddocResult.status) { - return TestOutcome(TestResult.failed, - ddocResult.output, - preFileName.readText); - } - - auto compResult = - executeShell(format("dmd -c -de -w -o- %s", postFileName)); - - removeLeadingTrailingEmptyLines(postFileName); - - remove(preFileName); - - if (compResult.status) { - return TestOutcome(TestResult.failed, compResult.output); - } - - return TestOutcome(TestResult.passed); -} - -void removeLeadingTrailingEmptyLines(string fileName) -{ - const outFileName = fileName ~ ".tmp"; - string[] buffer; - - auto isEmptyLine(E)(E line) { - return strip(line).empty; - } - - auto input = File(fileName, "r") - .byLineCopy - .array; - - while (!input.empty && isEmptyLine(input.front)) { - input.popFront(); - } - - while (!input.empty && isEmptyLine(input.back)) { - input.popBack(); - } - - auto output = File(outFileName, "w"); - - foreach (line; input) { - // Remove DOS line-endings - output.writeln(line.filter!(c => c != '\r')); - } - - output.close(); - remove(fileName); - rename(outFileName, fileName); -} diff --git a/ddili/src/common.ddoc b/ddili/src/common.ddoc deleted file mode 100644 index 545deb8..0000000 --- a/ddili/src/common.ddoc +++ /dev/null @@ -1,143 +0,0 @@ - DOCTYPE = - - DEL = $0 - - H1 =

    $0

    - H2 =

    $0

    - H3 =

    $0

    - H4 =

    $0

    - H5 =
    $0
    - H5_FRONTMATTER =
    $0
    - H6 =
    $0
    - SMALL=$0 - HR =
    - BR =
    - SUP= $0 - SUB= $0 - CENTER =
    $0
    - MONO =
    $0
    - MONO_NOBOLD =
    $0
    - CODE = $0 - CODE_SMALL = $0 - C=$(CODE $0) - CH4=$(C $0) - c=$(CODE_SMALL $0) - INDEX_KEYWORDS=    $(c $0) - - LINK_TARGET=$3 - LINK_DOWNLOAD = $+ - - SHELL =
    $0
    - SHELL_SMALL = $(SHELL $0) - SHELL_NOTE = ← $0 - SHELL_NOTE_WRONG = ← $0 - - SHELL_OBSERVED= $0 - - C_CODE =
    $0
    - UL_FARK=
      $0
    - UL_CLASS=
      $+
    - FARK_INDEX=
  • $(LINK2 #$1, $2)
  • - FARK=$(BR)$(HR)
    $2
    - FARK_C=$(H6 C)$(C_CODE $0) - FARK_CPP=$(H6 C++)$(C_CODE $0) - FARK_D=$(H6 D)$0 - - HEADER_INDEX=$(FARK_INDEX $1, $2) - HEADER=
    $2
    - - STEPS =
      $0
    - - QUOTE =
    $0
    - - RED = $0 - BLUE = $0 - GREEN = $0 - YELLOW = $0 - BLACK = $0 - WHITE = $0 - DARKRED= $0 - ORANGE= $0 - GRAY= $0 - LIGHT_GRAY= $0 - DARK_GRAY= $0 - HILITE= $0 - COLOR= $+ - - STRING = $(CODE $0) - COMMENT = $0 - KEYWORD = $0 - - D_COMMENT = $0 - D_STRING = $0 - D_KEYWORD = $0 - D_PSYMBOL = $0 - D_PARAM = $0 - - UNORDERED_FALSE = $0 - UNORDERED_TRUE = $0 - UNORDERED_NO = $0 - UNORDERED_YES = $0 - - GULEN = $(DARKRED $(B :)o)) - PARANTEZ_AC = ( - PARANTEZ_KAPA = ) - VIRGUL = , - DOLAR = $ - BACK_TICK = ` - HYPHEN = ‑ - ASIL = ($0) - - DERLEME_HATASI_METIN=derleme HATASI - DERLEME_HATASI = // ← $(DERLEME_HATASI_METIN) - CODE_NOTE = $(COMMENT // ← $0) - CODE_NOTE_WRONG = $(COMMENT // ← $0) - - IMG_D = D harfi - - BREADCRUMBS=$(BREADCRUMBS_FULL) - - BREADCRUMBS_DIV=
     $(BREADCRUMBS)
    - - FOOTER_DIV= - - HEADER_SECONDARY_DIV=
    $(HORIZNAV_CONTENT)
    - -VERTINAV_DIV=
    - $(BR) - $(VERTINAV_CONTENT) - $(BR) -
    - -CLASS=chapter -DIV_CLASS=
    $+
    - -HEADER1_ID=header1 - -DERSHANE_LINK2=$(LINK2 $1, $2) - -IMG= - -DERS_BOLUMU =

    $0

    -DERS_BOLUMU_CLASS =

    $+

    -COZUM_BOLUMU = $(H4 $0 Dersi Problem Çözümleri) - -UYARI=
    - UYARI:  $0
    - -TABLE=$+
    -HEAD=$0 -HEAD2=$(ROW $(HEAD $1) $(HEAD $2)) -HEAD3=$(ROW $(HEAD $1) $(HEAD $2) $(HEAD $3)) -HEAD4=$(ROW $(HEAD $1) $(HEAD $2) $(HEAD $3) $(HEAD $4)) -HEAD5=$(ROW $(HEAD $1) $(HEAD $2) $(HEAD $3) $(HEAD $4) $(HEAD $5)) -HEAD6=$(ROW $(HEAD $1) $(HEAD $2) $(HEAD $3) $(HEAD $4) $(HEAD $5) $(HEAD $6)) -ROW2=$(ROW $(DATA $1) $(DATA $2)) -ROW3=$(ROW $(DATA $1) $(DATA $2) $(DATA $3)) -ROW4=$(ROW $(DATA $1) $(DATA $2) $(DATA $3) $(DATA $4)) -ROW5=$(ROW $(DATA $1) $(DATA $2) $(DATA $3) $(DATA $4) $(DATA $5)) -ROW6=$(ROW $(DATA $1) $(DATA $2) $(DATA $3) $(DATA $4) $(DATA $5) $(DATA $6)) -ROW=$0 -DATA=$0 - -ESKI_KARSILASTIRMA=$(P $(CENTER $(HILITE $(I $(B Not:) Bu bir D1 karşılaştırmasıdır. Modern D'nin başka farklılıkları da var.)))) diff --git a/ddili/src/copyright.d b/ddili/src/copyright.d deleted file mode 100644 index 28fa20e..0000000 --- a/ddili/src/copyright.d +++ /dev/null @@ -1,39 +0,0 @@ -Ddoc - -
    -$(H5 Copyleft (ɔ) 2009-2015 Ali Çehreli) - -$(P -Creative Commons License
    Ddili.org icerigi by Ali Cehreli is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
    Based on a work at digitalmars.com.
    Permissions beyond the scope of this license may be available at http://ddili.org/copyright.html. -) - -$(HR) - -$(P -Forum ve Wiki bölümlerinin içerikleri genele aittir. -) - -$(P -Her dersin hakları dersin yazarına aittir. Kişisel kullanım için olmak şartıyla istediğiniz şekilde yararlanabilirsiniz. -) - -$(P -Bu sitenin geri kalanının hakları Ali Çehreli'ye aittir. -) - -$(P -Kaynak göstermek şartıyla istediğiniz bölümlerini başka sitelere kopyalayabilirsiniz. -) - -$(P -Kişisel kullanım için olmak şartıyla, istediğiniz bölümünü istediğiniz gibi kopyalayabilirsiniz. -) - -
    - -Macros: - SUBTITLE=Kopyalama Hakları - - DESCRIPTION=Site içeriği kopyalama hakları - - KEYWORDS=copyright hak lisans diff --git a/ddili/src/cozum.ddoc b/ddili/src/cozum.ddoc deleted file mode 100644 index ba19a54..0000000 --- a/ddili/src/cozum.ddoc +++ /dev/null @@ -1,31 +0,0 @@ -BODY_DIV=$(DIV_CLASS chapter, $(BODY)) - -MAIN_TITLE=D.ershane Çözümleri - -SUB_MAIN_TITLE=Ddili.org - -PROBLEM= - -COZUM= - -CONTAINER_ID=container_cozum - -HEADER_ID=header_cozum - -HEADER1_ID=header1_cozum - -HORIZNAV_CONTENT= - -VERTINAV_DIV= - -BREADCRUMBS_DIV= - -DERS_NAV_BAS= - -DERS_NAV_SON= - -FOOTER_DIV= - -HEADER_SECONDARY_DIV= - -GIZLI=$(HR)$0$(HR) diff --git a/ddili/src/ddili.ddoc b/ddili/src/ddili.ddoc deleted file mode 100644 index 172b720..0000000 --- a/ddili/src/ddili.ddoc +++ /dev/null @@ -1,50 +0,0 @@ -BODY_DIV=$(BODY) - -DDOC = $(DOCTYPE) - - - - - - - - - - - $(MAIN_TITLE) - $(SUBTITLE) - - - - - - - -
    - -
    -
    - $(MAIN_TITLE) - $(I $(SUB_MAIN_TITLE)) -
    - $(SUB_AUTHOR) -
    - - $(HEADER_SECONDARY_DIV) -
    - - $(VERTINAV_DIV) - -
    - $(BREADCRUMBS_DIV) - - $(DERS_NAV_BAS) - $(BODY_DIV) - $(DERS_NAV_SON) - -
    - -
    - - - - diff --git a/ddili/src/ders.ddoc b/ddili/src/ders.ddoc deleted file mode 100644 index f3b5c4e..0000000 --- a/ddili/src/ders.ddoc +++ /dev/null @@ -1,57 +0,0 @@ - -MAIN_TITLE=D.ershane - -SUB_MAIN_TITLE=$(SUB_MAIN_TITLE_DERSE_OZEL) - -HEADER_ID=header_ders - -BREADCRUMBS_ID=breadcrumbs_ders - -CONTAINER_ID=container - -PROBLEM= $(LI $0) - -IX= - -GERI_METIN=Geri -ILERI_METIN=İleri -PROBLEM_METIN=Problem -PROBLEM_COK_METIN=Problemler -PROBLEM_TEK_COZUMSUZ_METIN=çözümü sonra gelecek -PROBLEM_COK_COZUMSUZ_METIN=çözümler sonra gelecek -COZUM_METIN=çözüm -COZUMLER_METIN=çözümler - -PROBLEM_TEK_IMPL=$(H5 $(PROBLEM_METIN)) - $+ $1 - -PROBLEM_TEK=$(PROBLEM_TEK_IMPL $(DIV_CLASS cozum_link_tek, $(P $(LINK_TARGET ddili_cozum, $(COZUM_HTML), $(I ... $(COZUM_METIN))))), $0) -PROBLEM_TEK_COZUMSUZ=$(PROBLEM_TEK_IMPL $(P $(I ($(PROBLEM_TEK_COZUMSUZ_METIN ...)))), $0) - - -PROBLEM_COK_IMPL=$(H5 $(PROBLEM_COK_METIN)) - $(OL $+) $1 - -PROBLEM_COK=$(PROBLEM_COK_IMPL $(DIV_CLASS cozum_link_cok, $(P $(LINK_TARGET ddili_cozum, $(COZUM_HTML), $(I ... $(COZUMLER_METIN))))), $0) - -PROBLEM_COK_COZUMSUZ=$(PROBLEM_COK_IMPL $(P $(I ($(PROBLEM_COK_COZUMSUZ_METIN ...)))), $0) - - -VERTINAV_CONTENT=$(MINI_SOZLUK $(SOZLER)) - $(DUSEY_NAVIGASYON) - -HORIZNAV_CONTENT=$(HORIZNAV_CONTENT_DERSE_OZEL) - -DERS_NAV_BAS=
    - $(DERS_NAV_GERI)   $(DERS_NAV_ILERI) -
    - -DERS_NAV_SON=
    - $(DERS_NAV_GERI)   $(DERS_NAV_ILERI) -
    - -MINI_SOZLUK=
    $0
    - $(I $(LINK_TARGET ddili_sozluk, $(ROOT_DIR)/sozluk.html, ... bütün sözlük)) -$(BR)$(BR) - -DERSHANE_LINK2=$2 \ No newline at end of file diff --git a/ddili/src/ders/breadcrumbs.ddoc b/ddili/src/ders/breadcrumbs.ddoc deleted file mode 100644 index 9cf78ab..0000000 --- a/ddili/src/ders/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Ana Sayfa) > Dersler - -BREADCRUMBS_FULL=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) diff --git a/ddili/src/ders/d.cn/Makefile.in b/ddili/src/ders/d.cn/Makefile.in deleted file mode 100644 index f8387a6..0000000 --- a/ddili/src/ders/d.cn/Makefile.in +++ /dev/null @@ -1,150 +0,0 @@ -DERS_SON_D=to_be_continued.d - -# Taken out for the draft release -# foreword1.d \ - -DERS_D_BOLUMLER= \ - foreword2.d \ - preface.d \ - hello_world.d \ - writeln.d \ - compiler.d \ - types.d \ - assignment.d \ - variables.d \ - io.d \ - input.d \ - logical_expressions.d \ - if.d \ - while.d \ - arithmetic.d \ - floating_point.d \ - arrays.d \ - characters.d \ - slices.d \ - strings.d \ - stream_redirect.d \ - files.d \ - auto_and_typeof.d \ - name_space.d \ - for.d \ - ternary.d \ - literals.d \ - formatted_output.d \ - formatted_input.d \ - do_while.d \ - aa.d \ - foreach.d \ - switch_case.d \ - enum.d \ - functions.d \ - const_and_immutable.d \ - value_vs_reference.d \ - function_parameters.d \ - lvalue_rvalue.d \ - lazy_operators.d \ - main.d \ - exceptions.d \ - scope.d \ - assert.d \ - unit_testing.d \ - contracts.d \ - lifetimes.d \ - null_is.d \ - cast.d \ - struct.d \ - parameter_flexibility.d \ - function_overloading.d \ - member_functions.d \ - const_member_functions.d \ - special_functions.d \ - operator_overloading.d \ - class.d \ - inheritance.d \ - object.d \ - interface.d \ - destroy.d \ - modules.d \ - encapsulation.d \ - ufcs.d \ - property.d \ - invariant.d \ - templates.d \ - pragma.d \ - alias.d \ - alias_this.d \ - pointers.d \ - bit_operations.d \ - cond_comp.d \ - is_expr.d \ - lambda.d \ - foreach_opapply.d \ - nested.d \ - union.d \ - goto.d \ - tuples.d \ - templates_more.d \ - functions_more.d \ - mixin.d \ - ranges.d \ - ranges_more.d \ - parallelism.d \ - concurrency.d \ - concurrency_shared.d \ - fibers.d \ - memory.d \ - uda.d \ - operator_precedence.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - hello_world.cozum.d \ - writeln.cozum.d \ - types.cozum.d \ - assignment.cozum.d \ - variables.cozum.d \ - io.cozum.d \ - input.cozum.d \ - logical_expressions.cozum.d \ - if.cozum.d \ - while.cozum.d \ - arithmetic.cozum.d \ - floating_point.cozum.d \ - arrays.cozum.d \ - slices.cozum.d \ - strings.cozum.d \ - stream_redirect.cozum.d \ - files.cozum.d \ - auto_and_typeof.cozum.d \ - for.cozum.d \ - ternary.cozum.d \ - literals.cozum.d \ - formatted_output.cozum.d \ - formatted_input.cozum.d \ - do_while.cozum.d \ - aa.cozum.d \ - foreach.cozum.d \ - switch_case.cozum.d \ - enum.cozum.d \ - functions.cozum.d \ - function_parameters.cozum.d \ - main.cozum.d \ - assert.cozum.d \ - unit_testing.cozum.d \ - contracts.cozum.d \ - struct.cozum.d \ - parameter_flexibility.cozum.d \ - function_overloading.cozum.d \ - member_functions.cozum.d \ - operator_overloading.cozum.d \ - inheritance.cozum.d \ - object.cozum.d \ - pointers.cozum.d \ - bit_operations.cozum.d \ - foreach_opapply.cozum.d \ - -include Makefile.ders.in -$(eval $(call derse_ozel,d.cn,Programming_in_D,chinese)) diff --git a/ddili/src/ders/d.cn/README b/ddili/src/ders/d.cn/README deleted file mode 100644 index f253c1f..0000000 --- a/ddili/src/ders/d.cn/README +++ /dev/null @@ -1,25 +0,0 @@ -This is the template to use for a new book (or a new translation for an existing one). - -1) Copy this directory as a new one that you will work on: - - cp -pr template d.de - -2) Add the equivalent of the following line to src/Makefile - - include ders/d.de/Makefile.in - -3) If missing for your language, implement the proper alphabet class and add it to alphabet.d: - - case "german": - result = new GermanAlphabet(); - break; - -4) This template comes with three chapters and one solution solution chapter (*.cozum.d (cozum is the transliteration of çözüm)) Make the entire project to see that everything builds properly: - - make - -5) Edit relevant files in your directory - -6) Add new chapters. - -If this is a translation, you can copy and paste from e.g. the English version (d.en directory) one by one. Also, keep an eye on changes made to the original so that you can keep the book up to date. diff --git a/ddili/src/ders/d.cn/alias.d b/ddili/src/ders/d.cn/alias.d deleted file mode 100644 index 6b308ae..0000000 --- a/ddili/src/ders/d.cn/alias.d +++ /dev/null @@ -1,447 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX alias) $(CH4 alias) 与 $(CH4 with)) - -$(H5 $(C alias)) - -$(P -The $(C alias) 关键字为已有名字生成一个别名。 $(C alias) 与 $(C alias this) 不同,之间没有明确联系。 -) - -$(H6 缩短名字) - -$(P -正如我们在前面章节中遇到的,许多名字变得很长而难于使用。如下面这个函数: -) - ---- -Stack!(Point!double) randomPoints(size_t count) { - auto points = new Stack!(Point!double); - // ... -} ---- - -$(P -多次显式地指定类型 $(C Stack!(Point!double)) 有许多缺点: -) - -$(UL -$(LI -名字太长难以阅读。 -) - -$(LI -没必要在所有的地方都说明,该类型是一个由 $(C double) 实例化的 $(C Point) struct 模板对象组成的 $(C Stack) 数据结构。 -) - -$(LI -如果程序需求变更,比如 $(C double) 改成 $(C real),那你就需要更改多个位置。 -) - -) - -$(P -可以给 $(C Stack!(Point!double)) 一个新的名字来消除这些缺点: -) - ---- -alias $(HILITE Points) = Stack!(Point!double); - -// ... - -$(HILITE Points) randomPoints(size_t count) { - auto points = new $(HILITE Points); - // ... -} ---- - -$(P -更进一步,定义两个 alias 可能会更有用,其中一个在另一个基础上定义。 -) - ---- -alias PrecisePoint = Point!double; -alias Points = Stack!PrecisePoint; ---- - -$(P -$(C alias) 的语法如下: -) - ---- - alias $(I 新名字) = $(I 已有名字); ---- - -$(P -定义之后,新名字和已有名字等同:在程序里是相同的东西。 -) - -$(P -你可能会在一些程序里遇到这个特性的旧语法: -) - -$(MONO - // 不推荐使用旧语法: - alias $(I 已有名字) $(I 新名字); -) - -$(P -在一些需要讲明模块名字的情况,用 $(C alias) 来缩短名字也会很有用。假定名字 $(C Queen) 同时存在于两个不同的模块: $(C chess) 和 $(C palace)。如果两个模块同时被导入,仅仅输入 $(C Queen) 会导致一个编译错误: -) - ---- -import chess; -import palace; - -// ... - - Queen person; $(DERLEME_HATASI) ---- - -$(P -编译器无法判断 $(C Queen) 指的是哪一个: -) - -$(SHELL_SMALL -Error: $(HILITE chess.Queen) at chess.d(1) conflicts with -$(HILITE palace.Queen) at palace.d(1) -) - -$(P -要解决这个冲突,一个便利的方式就是给这些名字定义 alias: -) - ---- -import palace; - -alias $(HILITE PalaceQueen) = palace.Queen; - -void main() { - $(HILITE PalaceQueen) person; - // ... - $(HILITE PalaceQueen) anotherPerson; -} ---- - -$(P -$(C alias) 也可以用于其他地方的名字。下面的代码为变量定义了一个新名字: -) - ---- - int variableWithALongName = 42; - - alias var = variableWithALongName; - var = 43; - - assert(variableWithALongName == 43); ---- - -$(H6 设计灵活性) - -$(P -为了灵活性,就算是基本类型,比如 $(C int) 都可以有 alias: -) - ---- -alias CustomerNumber = int; -alias CompanyName = string; -// ... - -struct Customer { - CustomerNumber number; - CompanyName company; - // ... -} ---- - -$(P -如果这个 struct 的用户一直使用 $(C CustomerNumber) 和 $(C CompanyName) 而不是 $(C int) 和 $(C string),那将来扩展导致设计变更的时候就不会影响用户的代码了。 -) - -$(P -这也会提高代码的可读性,把变量的类型写做 $(C CustomerNumber) 会比 $(C int) 传递更多变量的信息。 -) - -$(P -有时候这样的类型会定义在 struct 或 class 中,成为它们的一部分。下面这个 class 有一个叫 $(C weight) 的 property: -) - ---- -class Box { -private: - - double weight_; - -public: - - double weight() const @property { - return weight_; - } - // ... -} ---- - -$(P -因为 class 中的成员变量和 property 都定义成了 $(C double),用户也就必须使用 $(C double): -) - ---- - $(HILITE double) totalWeight = 0; - - foreach (box; boxes) { - totalWeight += box.weight; - } ---- - -$(P -我们来比较一下另外一种设计,把 $(C weight) 定义为一个 alias: -) - ---- -class Box { -private: - - $(HILITE Weight) weight_; - -public: - - alias $(HILITE Weight) = double; - - $(HILITE Weight) weight() const @property { - return weight_; - } - // ... -} ---- - -$(P -现在,用户代码可以正常地使用 $(C Weight) 了: -) - ---- - $(HILITE Box.Weight) totalWeight = 0; - - foreach (box; boxes) { - totalWeight += box.weight; - } ---- - -$(P -这样的设计,将来改变 $(C Weight) 的真实类型就不再会影响用户代码了。(新的类型也要支持 $(C +=) 运算符。) -) - -$(H6 $(IX 名字隐藏) 暴露父类中隐藏的名字) - -$(P -当相同的名字同时出现在父类与子类中时,父类中的名字就会被隐藏,即使只是子类中的一个名字也足以隐藏父类中所有匹配的名字: -) - ---- -class Super { - void foo(int x) { - // ... - } -} - -class Sub : Super { - void foo() { - // ... - } -} - -void main() { - auto object = new Sub; - object.foo(42); $(DERLEME_HATASI) -} ---- - -$(P -因为调用参数是整型 42,你可能会认为接受 $(C int) 参数的 $(C Super.foo) 会被调用。然而,$(C Sub.foo) 会 $(I 隐藏) $(C Super.foo) 并导致一个编译错误,虽然他们的参数列表不同。编译器完全忽略了 $(C Super.foo),并且报告说不能用 $(C int) 去调用 $(C Sub.foo): -) - -$(SHELL_SMALL -Error: function $(HILITE deneme.Sub.foo ()) is not callable -using argument types $(HILITE (int)) -) - -$(P -注意,这与重写父类中的函数不一样。重写要求函数的签名必须完全一样,并且需要使用 $(C override) 关键字。($(C override) 关键字已经在 $(LINK2 /ders/d.cn/inheritance.html, 继承) 章节中介绍过了。) -) - -$(P -这里并不是重写,而是一个语言特性,叫做$(I 名字隐藏)。如果没有名字隐藏,在这些 class 中添加或者删除名字 $(C foo) 就可能会改变本该调用的函数,名字隐藏阻止了这样的情况发生。这也是其他 OOP 语言中常见的特性。 -) - -$(P -$(C alias) 可以在需要的时候暴露被隐藏的名字: -) - ---- -class Super { - void foo(int x) { - // ... - } -} - -class Sub : Super { - void foo() { - // ... - } - - alias $(HILITE foo) = Super.foo; -} ---- - -$(P -如上所示,$(C alias) 将名字 $(C foo) 从父类引入成为子类接口。因此,代码现在能够编译了,$(C Super.foo) 也会被正确调用。 -) - -$(P -若需要,也在可以引入的同时改变名字: -) - ---- -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 -名字隐藏同样也会影响成员变量。当然,$(C alias) 也可以引入这些名字: -) - ---- -class Super { - int city; -} - -class Sub : Super { - string city() const @property { - return "Kayseri"; - } -} ---- - -$(P -虽然一个是成员变量,一个是成员函数,子类中的名字 $(C city) 也会隐藏父类中的名字 $(C city): -) - ---- -void main() { - auto object = new Sub; - object.city = 42; $(DERLEME_HATASI) -} ---- - -$(P -同样的,父类中成员变量的名字可以通过 $(C alias) 引入为子类接口,并改名: -) - ---- -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) 用于避免重复的引用一个对象或者符号。只用在括号里输入一个表达式或者符号,就可以在 $(C with) 的作用域内通过这个表达式或者符号来查找其他的符号: -) - ---- -struct S { - int i; - int j; -} - -void main() { - auto s = S(); - - with ($(HILITE s)) { - $(HILITE i) = 1; // 表示 s.i - $(HILITE j) = 2; // 表示 s.j - } -} ---- - -$(P -允许在括号内创建一个临时对象。这时,这个临时对象是一个 $(LINK2 /ders/d.cn/lvalue_rvalue.html, 左值),一旦离开作用域,生命期就结束: -) - ---- - with (S()) { - i = 1; // 临时对象的 i 成员 - j = 2; // 临时对象的 j 成员 - } ---- - -$(P -以后我们将在 $(LINK2 /ders/d.en/pointers.html, 指针) 章节重看到,可以用 $(C new) 创建临时对象,这时对象的生命期就可以超过作用域。 -) - -$(P -在 $(C case) 块中,删除重复的引用,比如 $(C enum) 类型时,$(C with) 会特别有用: -) - ---- -enum Color { red, orange } - -// ... - - final switch (c) $(HILITE with (Color)) { - - case red: // 表示 Color.red - // ... - - case orange: // 表示 Color.orange - // ... - } ---- - -$(H5 总结) - -$(UL - -$(LI $(C alias) 为已有名字取别名。) - -$(LI $(C with) 避免重复引用相同的对象或符号。) - -) - -Macros: - SUBTITLE=alias - - DESCRIPTION=alias 关键字可以为已有名字创建新的名字。 - - KEYWORDS=d 编程 教程 封装 diff --git a/ddili/src/ders/d.cn/alias_this.d b/ddili/src/ders/d.cn/alias_this.d deleted file mode 100644 index e68d261..0000000 --- a/ddili/src/ders/d.cn/alias_this.d +++ /dev/null @@ -1,174 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX alias this) $(CH4 alias this)) - -$(P -在前面的章节中,我们已经看到独立的 $(C alias) 和 $(C this) 关键字的作用,然而,这两个关键字同时使用时,即 $(C alias this),意思就完全不同了。 -) - -$(P -$(IX 自动类型转换) $(IX 类型转换, 自动) $(C alias this) 允许用户自定类型的$(I 自动类型转换)(通常也作$(I 隐式类型转换))。 我们已经在 $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) 一章中看到另外一种类型转换的方式,即为要转到的类型定义 $(C opCast)。不同之处在于,$(C opCast) 用于显式类型转换,而 $(C alias this) 用于自动类型转换。 -) - -$(P -关键字 $(C alias) 和 $(C this) 要分开写,中间指定成员变量或者成员函数: -) - ---- - alias $(I 成员变量或成员函数) this; ---- - -$(P -$(C alias this) 允许指定从用户自定义类型到该成员的转换,成员的值即转换的结果。 -) - -$(P -如下的 $(C Fraction) 例子中使用 $(C alias this) 的同时用到了一个$(I 成员函数)。更下面的 $(C TeachingAssistant) 例子中将使用 $(I 成员变量)。 -) - -$(P -因为 $(C value()) 的返回值是 $(C double),所以下面的 $(C alias this) 允许 $(C Fraction) 对象到 $(C double) 值的自动转换: -) - ---- -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); // 表示 1/4 - writeln(calculate($(HILITE fraction), 0.75)); -} ---- - -$(P -在 $(C Fraction) 对象出现的地方,如果需要的是一个 $(C double),$(C value()) 就会被自动调用以产生一个 $(C double) 类型的值。这就是为什么 $(C fraction) 可以作为 $(C calculate()) 的参数的原因。$(C value()) 返回 1/4 的值,即 0.25,程序将会输出 2 * 0.25 + 0.75 的结果: -) - -$(SHELL -1.25 -) - -$(H5 $(IX 多重继承) $(IX 继承, 多重) 多重继承) - -$(P -我们已经在 $(LINK2 /ders/d.cn/inheritance.html, 继承) 一章中看到类(class)只能继承自一个 $(C class)。(另一方面,$(C interface) 继承的数量并没有限制。)一些其他的面向对象语言允许同时继承自多个类,这叫做 $(I 多重继承)。 -) - -$(P -通过 $(C alias this),用 D 的 class 也可以做多重继承的设计,多个 $(C alias this) 的声明用于表示多个不同的类型。 -) - -$(P -$(HILITE $(I $(B 注意:) dmd 2.071,编译此章节例子的最新编译器,只允许一个 $(C alias this) 声明。)) -) - -$(P -下面的 $(C TeachingAssistant) class 有两个成员成员变量,类型分别为 $(C Student) 和 $(C Teacher),$(C alias this) 声明将允许这个类型用在任何需要 $(C Student) 或 $(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); - } - - /* 下面的两个 ‘alias this’ 声明将允许 this 类型可以同时用作 - * Student 或 Teacher。 - * - * 注意: dmd 2.071 不支持多个 ‘alias this’ 声明 */ - 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") ]; - - /* 对象可以同时用作 Teacher 或 Student: */ - auto tim = new TeachingAssistant("Tim", "math"); - - // 'tim' 作为一个 teacher: - attendClass($(HILITE tim), students); - - // 'tim' 作为一个 student: - auto amy = new Teacher("Amy", "physics"); - $(CODE_COMMENT_OUT compiler limitation)attendClass(amy, students ~ $(HILITE tim)); -} ---- - -$(P -程序输出表明相同的对象被用作两个不同的类型: -) - -$(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=通过 ‘alias this’ 提供到其他类型的自动转换。 - - KEYWORDS=d 编程 语言 教程 学习 alias 别名 alias this - -SOZLER= -$(kalitim) - diff --git a/ddili/src/ders/d.cn/arrays.cozum.d b/ddili/src/ders/d.cn/arrays.cozum.d deleted file mode 100644 index 3e0d3fa..0000000 --- a/ddili/src/ders/d.cn/arrays.cozum.d +++ /dev/null @@ -1,133 +0,0 @@ -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/ddili/src/ders/d.cn/arrays.d b/ddili/src/ders/d.cn/arrays.d deleted file mode 100644 index e57befb..0000000 --- a/ddili/src/ders/d.cn/arrays.d +++ /dev/null @@ -1,552 +0,0 @@ -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 ~, 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/ddili/src/ders/d.cn/auto_and_typeof.d b/ddili/src/ders/d.cn/auto_and_typeof.d deleted file mode 100644 index 22d71af..0000000 --- a/ddili/src/ders/d.cn/auto_and_typeof.d +++ /dev/null @@ -1,97 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(CH4 auto) 和 $(CH4 typeof)) - -$(H5 $(IX auto, 变量) $(C auto)) - -$(P -在前一章中定义 $(C File) 变量时, 我们在 $(C =) 运算符的两边重复了类型名: -) - ---- - $(HILITE File) file = $(HILITE File)("student_records", "w"); ---- - -$(P -这显得很多余。在类型名较长的时候,这不仅麻烦而且易错: -) - ---- - VeryLongTypeName var = VeryLongTypeName(/* ... */); ---- - -$(P -幸运的是,左边的类型名不是必需的,因为编译器可以从右边的表达式推断出左边的类型。想要编译器来推断类型,可以使用关键字 $(C auto): -) - ---- - $(HILITE auto) var = VeryLongTypeName(/* ... */); ---- - -$(P -$(C auto) 可被用作任意类型,即使右边的类型没有拼写出来: -) - ---- - auto duration = 42; - auto distance = 1.2; - auto greeting = "Hello"; - auto vehicle = BeautifulBicycle("blue"); ---- - -$(P -尽管 "auto" 是自动 $(I automatic) 的缩写,但它不是来自于自动类型推理 $(I automatic type inference)。它来自于自动存储类别 $(I automatic storage class),这是一个跟变量生命周期相关的概念。$(C auto) 适用于没有使用其它说明符时使用。例如,下面的定义就无需使用 $(C auto): -) - ---- - immutable i = 42; ---- - -$(P -上面的代码中,编译器将 $(C i) 的类型自动推断为 $(C immutable int)。(我们将会在稍后的章节中看到 $(C immutable) 。) -) - -$(H5 $(IX typeof) $(C typeof)) - -$(P -$(C typeof) 提供表达式的类型(包括单个变量,对象,字母常量等)而不必对表达式进行实际计算。 -) - -$(P -下面这个例子演示了 $(C typeof) 是如何在不需要显示拼写的情况下被用来指定类型的: -) - ---- - int value = 100; // 已经定义为 'int' - - typeof(value) value2; // 意思是 “value 的类型” - typeof(100) value3; // 意思是 “字面常量 100 的类型” ---- - -$(P -上面后两个变量的定义等同于下列代码: -) - ---- - int value2; - int value3; ---- - -$(P -显然,像上面那种实际类型已知的情况下,$(C typeof) 并不是必需的。相反,你通常会在更复杂的情况下才使用它,比如当你希望你的变量类型同它处代码中的可以改变的类型保持一致的时候。该关键字在 $(LINK2 /ders/d.cn/templates.html, 模板) 和 $(LINK2 /ders/d.cn/mixin.html, 混入) 中特别有用,此二者将在后面的章节中介绍。 -) - -$(PROBLEM_TEK - -$(P -如上所见,如 100 这样的字面常量的类型为 $(C int)(而不是 $(C short),$(C long),或任何其它类型)。像 1.2 那样来写一个程序用以确定浮点数字面常量的类型。 $(C typeof) 和 $(C .stringof) 对该程序会很有用。 -) - -) - -Macros: - SUBTITLE=关键字 auto 和 typeof - - DESCRIPTION=关键字 'auto' 是 D 语言中常用的隐式类型推断特征,而 'typeof' 用来获取表达式的类型。 - - KEYWORDS=D 编程语言教程 auto typeof diff --git a/ddili/src/ders/d.cn/breadcrumbs.ddoc b/ddili/src/ders/d.cn/breadcrumbs.ddoc deleted file mode 100644 index 63140bd..0000000 --- a/ddili/src/ders/d.cn/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, 主页) > $(LINK2 /ders/index.html, 书籍) > Prg in D - -BREADCRUMBS_FULL=$(LINK2 /index.html, 主页) > $(LINK2 /ders/index.html, 书籍) > $(LINK2 /ders/d.cn/index.html, Prg in D) diff --git a/ddili/src/ders/d.cn/const_member_functions.d b/ddili/src/ders/d.cn/const_member_functions.d deleted file mode 100644 index 2175c2f..0000000 --- a/ddili/src/ders/d.cn/const_member_functions.d +++ /dev/null @@ -1,289 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(CH4 const ref) 参数和 $(CH4 const) 成员函数) - -$(P -本章主要是关于如何将参数和成员函数标记为 $(C const) 来配合 $(C immutabl) 变量使用的。我们已经在之前的章节中了解过 $(C const) 参数了,所以本章的某些部分其实只是对前面知识的复习。 -) - -$(P -虽然我们这里的例子使用的都是结构体,但实际上 $(C const) 成员函数也适用于类。 -) - -$(H5 $(C immutable) 对象) - -$(P -我们都知道 $(C immutable) 变量是无法被修改的: -) - ---- - immutable readingTime = TimeOfDay(15, 0); ---- - -$(P -$(C readingTime) 无法被修改: -) - ---- - readingTime = TimeOfDay(16, 0); $(DERLEME_HATASI) - readingTime.minute += 10; $(DERLEME_HATASI) ---- - -$(P -编译器不允许以任何方式修改 $(C immutable) 对象。 -) - -$(H5 非 $(C const) 的 $(C ref) 参数) - -$(P -我们之前在 $(LINK2 /ders/d.cn/function_parameters.html, 函数参数) 一节中见到过这个概念。被标记为 $(C ref) 的参数可以被函数不受限制地随意修改。因此,即便函数实际上并没有修改参数,编译器也不允许对其传入 $(C immutable) 对象。 -) - ---- -/* 虽然 'duration' 并没有被函数修改, - * 它也不能被标记为 'const' */ -int totalSeconds(ref Duration duration) { - return 60 * duration.minute; -} -// ... - $(HILITE immutable) warmUpTime = Duration(3); - totalSeconds(warmUpTime); $(DERLEME_HATASI) ---- - -$(P -编译器不允许向 $(C totalSeconds) 传递 $(C immutable) $(C warmUpTime),因为函数没有保证参数一定不会被修改。 -) - -$(H5 $(IX const ref) $(IX ref const) $(IX parameter, const ref) $(C const ref) 参数) - -$(P -$(C const ref) 表示参数不会被函数修改: -) - ---- -int totalSeconds(const ref Duration duration) { - return 60 * duration.minute; -} -// ... - immutable warmUpTime = Duration(3); - totalSeconds(warmUpTime); // ← 现在编译通过了 ---- - -$(P -编译器保证这种函数只会接收到 $(C immutable) 对象: -) - ---- -int totalSeconds(const ref Duration duration) { - duration.minute = 7; $(DERLEME_HATASI) -// ... -} ---- - -$(P -$(IX in ref) $(IX ref in) $(IX parameter, in ref) 可用 $(C in ref) 替代 $(C const ref)。$(C in) 表示参数只是用来向函数传入信息,禁止对其进行任何修改。我们将会在 $(LINK2 /ders/d.cn/function_parameters.html, 后面的章节) 中见到它。 -) - ---- -int totalSeconds($(HILITE in ref) Duration duration) { - // ... -} ---- - -$(H5 非 $(C const) 成员函数) - -$(P -对象可以被其成员函数修改,就像之前的 $(C TimeOfDay.increment) 成员函数。$(C increment()) 将修改调用它的对象中的成员变量: -) - ---- -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' 已被修改 ---- - -$(H5 $(IX const, 成员函数) $(C const) 成员函数) - -$(P -我们也会遇到一些不会修改其调用对象的成员函数。比如 $(C toString()): -) - ---- -struct TimeOfDay { -// ... - string toString() { - return format("%02s:%02s", hour, minute); - } -// ... -} ---- - -$(P -$(C toString()) 只是根据指定格式将对象转换为字符串,因此它不应该有修改对象的权限。 -) - -$(P -我们可以通过在成员函数的参数列表后添加 $(C const) 关键字来声明这一点: -) - ---- -struct TimeOfDay { -// ... - string toString() $(HILITE const) { - return format("%02s:%02s", hour, minute); - } -} ---- - -$(P -$(C const) 保证了对象不会被其自己的成员函数修改。通过这种方式 $(C immutable) 对象也可以调用 $(C toString()) 成员函数了。否则,编译器将不允许调用 $(C immutable) 对象的 $(C toString()): -) - ---- -struct TimeOfDay { -// ... - // 较差的设计:没有被标记为 'const' - string toString() { - return format("%02s:%02s", hour, minute); - } -} -// ... - $(HILITE immutable) start = TimeOfDay(5, 30); - writeln(start); // TimeOfDay.toString() 不会被调用! ---- - -$(P -输出并不是我们想要的 $(C 05:30),也就是说实际上 $(C writeln) 只是调用了那个默认的转换函数而不是我们定义的 $(C TimeOfDay.toString): -) - -$(SHELL -immutable(TimeOfDay)(5, 30) -) - -$(P -如果在 $(C immutable) 对象上显式调用 $(C toString()) 则会引发编译错误: -) - ---- - auto s = start.toString(); $(DERLEME_HATASI) ---- - -$(P -因此我们在之前章节定义的 $(C toString()) 函数的设计都不够好,它们都应该增加一个 $(C const) 声明。 -) - -$(P $(I $(B 注:)$(C const) 关键字也可放在函数声明前:) -) - ---- - // 与之前的定义相同 - $(HILITE const) string toString() { - return format("%02s:%02s", hour, minute); - } ---- - -$(P $(I 由于这种形式会让人误以为 $(C const) 是返回值的一部分,所以我推荐将其放在参数列表后。) -) - -$(H5 $(IX inout, 成员函数) $(C inout) 成员函数) - -$(P -$(C inout) 将参数的可变性转移给了返回值,这一点我们已经在 $(LINK2 /ders/d.en/function_parameters.html, 函数参数) 一节中介绍过了。 -) - -$(P -而成员函数的 $(C inout) 也同样会将$(I 对象)的可变性转移给函数返回值: -) - ---- -import std.stdio; - -struct Container { - int[] elements; - - $(HILITE inout)(int)[] firstPart(size_t n) $(HILITE inout) { - return elements[0 .. n]; - } -} - -void main() { - { - // 一个 immutable container - auto container = $(HILITE immutable)(Container)([ 1, 2, 3 ]); - auto slice = container.firstPart(2); - writeln(typeof(slice).stringof); - } - { - // 一个 const container - auto container = $(HILITE const)(Container)([ 1, 2, 3 ]); - auto slice = container.firstPart(2); - writeln(typeof(slice).stringof); - } - { - // 一个可变的 container - auto container = Container([ 1, 2, 3 ]); - auto slice = container.firstPart(2); - writeln(typeof(slice).stringof); - } -} ---- - -$(P -这三个由不同可变性对象返回的 slice 包含了返回它们的对象: -) - -$(SHELL -$(HILITE immutable)(int)[] -$(HILITE const)(int)[] -int[] -) - -$(P -由于我们需要调用 $(C const) 和 $(C immutable) 对象的 $(C inout) 成员函数,编译器会自动为其添加 $(C const) 声明。 -) - -$(H5 使用方法小结) - -$(UL - -$(LI -将参数标记为 $(C in)、$(C const) 或 $(C const ref) 以确保其不会被函数修改。 -) - -$(LI -将不会修改其对象的成员函数标记为 $(C const): - ---- -struct TimeOfDay { -// ... - string toString() $(HILITE const) { - return format("%02s:%02s", hour, minute); - } -} ---- - -$(P -这能帮助你避免一些不必要的限制以扩大结构体或类的适用范围。本书之后的章节都会遵守这个约定。 -) - -) - -) - -Macros: - SUBTITLE=const ref 参数和 const 成员函数 - - DESCRIPTION=D 语言中的 const ref 参数和 const 成员函数 - - KEYWORDS=D 编程语言教程 const 成员函数 diff --git a/ddili/src/ders/d.cn/copyright.d b/ddili/src/ders/d.cn/copyright.d deleted file mode 100644 index 3d18fe4..0000000 --- a/ddili/src/ders/d.cn/copyright.d +++ /dev/null @@ -1,110 +0,0 @@ -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-2015 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 -Printed by IngramSpark -) -$(P -ISBN 978-0-692-52957-7 -) -$(BR) -$(P -or by CreateSpace -) -$(P -ISBN 978-1515074601 -) - -
    - -Macros: - SUBTITLE=Copyleft - - DESCRIPTION=The copyleft page of Programming in D - - KEYWORDS=copyright diff --git a/ddili/src/ders/d.cn/cover_CreateSpace.pdf b/ddili/src/ders/d.cn/cover_CreateSpace.pdf deleted file mode 100644 index 91e5544..0000000 Binary files a/ddili/src/ders/d.cn/cover_CreateSpace.pdf and /dev/null differ diff --git a/ddili/src/ders/d.cn/cover_IngramSpark_hardcover.pdf b/ddili/src/ders/d.cn/cover_IngramSpark_hardcover.pdf deleted file mode 100644 index dcd6ff0..0000000 Binary files a/ddili/src/ders/d.cn/cover_IngramSpark_hardcover.pdf and /dev/null differ diff --git a/ddili/src/ders/d.cn/cover_IngramSpark_paperback.pdf b/ddili/src/ders/d.cn/cover_IngramSpark_paperback.pdf deleted file mode 100644 index 7e69500..0000000 Binary files a/ddili/src/ders/d.cn/cover_IngramSpark_paperback.pdf and /dev/null differ diff --git a/ddili/src/ders/d.cn/cover_ebook.png b/ddili/src/ders/d.cn/cover_ebook.png deleted file mode 100644 index f9ae6c6..0000000 Binary files a/ddili/src/ders/d.cn/cover_ebook.png and /dev/null differ diff --git a/ddili/src/ders/d.cn/cover_thumb.png b/ddili/src/ders/d.cn/cover_thumb.png deleted file mode 100644 index e7f4b7e..0000000 Binary files a/ddili/src/ders/d.cn/cover_thumb.png and /dev/null differ diff --git a/ddili/src/ders/d.cn/do_while.cozum.d b/ddili/src/ders/d.cn/do_while.cozum.d deleted file mode 100644 index 4214d02..0000000 --- a/ddili/src/ders/d.cn/do_while.cozum.d +++ /dev/null @@ -1,22 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C do-while) 循环) - -$(P -程序并不与 $(C do-while) 循环直接相关,因为任何问题如果能用 $(C do-while) 循环解决也一定可以用其他的循环语句解决。 -) - -$(P -程序可以通过用户回答是小了还是大了来缩小猜数字的范围,例如,如果第一次猜 50 而用户回答是小了,那程序就知道数字的范围是在 [51,100],然后如果程序再猜该范围正中的数字,这时数字就一定是在 [51,75] 或 [76,100] 范围中。 -) - -$(P -如果最后范围的大小为 1,程序就可以确定这个数就是要猜的数。 -) - -Macros: - SUBTITLE=do-while 循环解答 - - DESCRIPTION=D 编程练习解答:'do-while' 循环 - - KEYWORDS=D 编程教程 do-while 解答 diff --git a/ddili/src/ders/d.cn/do_while.d b/ddili/src/ders/d.cn/do_while.d deleted file mode 100644 index 0249fcb..0000000 --- a/ddili/src/ders/d.cn/do_while.d +++ /dev/null @@ -1,91 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX do-while) $(IX loop, do-while) $(CH4 do-while) 循环) - -$(P -在 $(LINK2 /ders/d.cn/for.html, $(C for) 循环) 一章中,我们已经见过 $(LINK2 /ders/d.cn/while.html, $(C while) 循环) 执行的步骤: -) - -$(MONO -准备 - -条件检查 -循环体 -迭代 - -条件检查 -循环体 -迭代 - -... -) - -$(P -$(C do-while) 循环与 $(C while) 循环十分相似,不同之处在于 $(C do-while) 循环的$(I 条件检查)是在每一次迭代的最后,因此$(I 循环体)至少会被执行一次: -) - -$(MONO -准备 - -循环体 -迭代 -条件检查 $(SHELL_NOTE 迭代最后) - -循环体 -迭代 -条件检查 $(SHELL_NOTE 迭代最后) - -... -) - -$(P -例如, $(C do-while) 用在下面这个猜数字程序里会更加自然,因为用户至少需要猜一次数字以用作比较: -) - ---- -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 -这个程序用到了函数 $(C uniform()) ,它是 $(C std.random) 模块的一部分,作用是返回一个指定范围内的随机数。使用方法如上所示,但要注意,函数的第二个参数并不在指定范围内,也就是说此例中, $(C uniform()) 永远不会返回 101。 -) - -$(PROBLEM_TEK - -$(P -编程实现例子中相同的游戏,但是由程序来猜数字,如果实现正确,最多只需要7次程序就能正确的猜到用户指定的数字。 -) - -) - -Macros: - SUBTITLE=do-while 循环 - - DESCRIPTION=D 语言中的 do-while 循环,以及与 while 循环的比较 - - KEYWORDS=D 编程语言教程 do while 循环 diff --git a/ddili/src/ders/d.cn/exceptions.d b/ddili/src/ders/d.cn/exceptions.d deleted file mode 100644 index 533ac0e..0000000 --- a/ddili/src/ders/d.cn/exceptions.d +++ /dev/null @@ -1,994 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX 异常) 异常) - -$(P -意外情况是程序的一部分:用户错误、编程错误、程序运行环境的改变等等。程序面临异常情况时必须以一定的方式来避免生成错误的结果。 -) - -$(P -这些情况中有些可能严重到足以停止程序的执行。例如,所需的信息可能丢失或无效、或设备可能无法正常工作。D 语言的异常处理机制用以在必要时停止程序的执行,以及在可能时从意外状况中恢复。 -) - -$(P -作为一个严重情况的示例,我们可以构想一个将未知操作符传递给只知四个算术操作符的函数,正如我们在之前章节的练习中所看到的那样: -) - ---- - 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 -上方的 $(C switch) 语句不知如何处理那些不在 $(C case) 语句中列出的操作符,所以抛出了异常。 -) - -$(P -Phobos 里有很多抛出异常的例子。比如 $(C to!int),它能将一个整型的字符串表示形式转变为 $(C int) 值,而在该表示形式无效时抛出异常: -) - ---- -import std.conv; - -void main() { - const int value = to!int("hello"); -} ---- - -$(P -该程序以 $(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) 是抛出的异常对象的类型。我们能由名字来辨认出该类型是定义在 $(C std.conv) 模块的 $(C ConvException)。 -) - -$(H5 $(IX throw) 用以抛出异常的 $(C throw) 语句 ) - -$(P -我们在上方示例和先前章节中都看到了 $(C throw) 语句。 -) - -$(P -$(C throw) 抛出一个(I 异常对象)并终止当前的程序运行。在 $(C throw) 语句之后的表达式或语句将不会执行。这种行为是出于异常的天性:当程序无法继续执行目前的任务时,它们必须被抛出。 -) - -$(P -相反,如果程序能继续执行,那么该情况下不一定会抛出异常。这样的情况下函数会找到一种方式继续下去。 -) - -$(H6 $(IX Exception) $(IX Error) $(IX Throwable) 异常类型 $(C Exception) 与 $(C Error)) - -$(P -只有从 $(C Throwable) 类继承的类型能被抛出。$(C Throwable) 几乎从不在程序里被直接使用。真正被抛出的类型是那些继承自 $(C Exception) 或 $(C Error) 的类型,因而它们本身也继承自 $(C Throwable)。例如,所有由 Phobos 抛出的异常是从 $(C Exception) 或 $(C Error) 继承的。 -) - -$(P -$(C Error) 代表无法恢复的情况,并且它不建议被$(I 捕捉)。由是,程序所抛出大部分的异常是继承自 $(C Exception) 的类型。($(I $(B 注:) 继承是一个与类相关的主题。我们会在此后的一章中看到类。)) -) - -$(P -$(C Exception) 对象构建自一个代表错误信息的 $(C string) 值。或许你会觉得以 $(C std.string) 模块的 $(C format()) 函数能很容易地创建该信息: -) - ---- -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 -大多数情况下,将会调用 $(C enforce()) 函数,而非由 $(C new) 明确地创建一个异常对象,并由 $(C throw) 直接抛出。例如,上方的错误检查与下方对 $(C enforce()) 的调用等效: -) - ---- - enforce(count >= 0, format("Invalid dice count: %s", count)); ---- - -$(P -我们将在后面章节了解到 $(C enforce()) 和 $(C assert()) 的差异。 -) - -$(H6 抛出异常将终结所有作用域) - -$(P -我们已经看到,程序从 $(C main) 函数开始执行并从那里分支进入其他函数。这种逐层执行、深入函数并从它们返回的方式可以被看作是树状图的分支。 -) - -$(P -例如,$(C main()) 会调用出一个叫 $(C makeOmelet) 的函数,从而调用出另一个名为 $(C prepareAll) 的函数,接着调用出名为 $(C prepareEggs) 的函数,如此继续下去。假设箭头指向所调用的函数,那么这样一个程序的分支可以以下面的函数调用树状图表示出来: -) - -$(MONO -main - │ - ├──▶ makeOmelet - │ │ - │ ├──▶ prepareAll - │ │ │ - │ │ ├─▶ prepareEggs - │ │ ├─▶ prepareButter - │ │ └─▶ preparePan - │ │ - │ ├──▶ cookEggs - │ └──▶ cleanAll - │ - └──▶ eatOmelet -) - -$(P -下面的程序通过使用不同级别的缩进来实现上方输出中的分支化。该程序除了生成合乎我们目的的输出外并没有起到其他任何作用: -) - ---- -$(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 -该程序生成了如下输出: -) - -$(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 -$(C entering) 和 $(C exiting) 两个函数通过字符 $(C ▶) 和 $(C ◁) 表示函数的第一行和最后一行。程序由第一行的 $(C main()) 启动,随即分支进入其它函数,并最终由末行的 $(C main) 结束。 -) - -$(P -我们来修改 $(C prepareEggs) 函数,把鸡蛋的数量作为一个参数。由于这个参数在某些值时会出错,我们让这个函数在鸡蛋数少于一时抛出异常: -) - ---- -$(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 -为了编译该程序,我们必须修改程序的其他行来适应上述变化。冰箱里拿出来的鸡蛋数目可以由 $(C main()) 开始,从一个函数到另一个函数来进行处理。下方是程序中需要更改的部分。-8 这个无效值是有意生成的,用以显示出抛出异常时的程序输出与先前的区别之处: -) - ---- -$(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 -我们现在运行程序,便能看到之前在异常抛出点之后的那些输出行丢失了: -) - -$(SHELL -▶ main, first line - ▶ makeOmelet, first line - ▶ prepareAll, first line - ▶ prepareEggs, first line -object.Exception: Cannot take -8 eggs from the fridge -) - -$(P -抛出异常时,程序会以从底层到顶层的执行顺序,使 $(C prepareEggs),$(C prepareAll),$(C makeOmelet) 和 $(C main()) 三个函数依次逐步退出。当程序从这些函数退出时不会执行其他步骤。 -) - -$(P -这样即刻终止的原因为:低等级函数的失败表明依赖于它的成功执行的高等级函数也是失败的。 -) - -$(P -由较低等级函数抛出的异常对象逐级地转移到较高等级的函数,使得程序最终退出 $(C main()) 函数。该异常采取的路径在下方的树状图中被高亮标出: -) - -$(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 -异常处理机制的要点正是立即退出所有的函数调用层。 -) - -$(P -有时$(I 捕捉(catch))抛出的异常以继续程序的执行是有意义的。我会在下面介绍 $(C catch) 这个关键字。 -) - -$(H6 何时使用 $(C throw)) - -$(P -在无法继续执行的情况下使用 $(C throw)。例如,从文件里读取学生数的函数在该信息不可获取或错误时会抛出异常。 -) - -$(P -另一方面,如果问题是由像输入无效数据这样的用户行为引发,比起抛出异常,再次确认数据会显得更加有意义。在许多情况下显示错误信息并让用户重新输入数据会更恰当一些。 -) - -$(H5 $(IX try) $(IX catch) 捕获异常的 $(C try-catch) 语句) - -$(P -正如我们上方所见,异常抛出导致程序退出了所有函数,并最终终止了整个程序。 -) - -$(P -退出函数时,异常对象会在路径上的任意点被 $(C try-catch) 语句捕获。$(C try-catch) 语句类似于“$(I 尝试(try)) 做这些事情,并 $(I 捕捉(catch)) 其中可能被抛出的异常”这个短句。这是 $(C try-catch) 的语法: -) - ---- - try { - // 将被执行的代码块 - // 其中可能会有异常抛出 - - } catch ($(I an_exception_type)) { - // 若这种类型的异常被捕获 - // 便要执行的表达式 - - } catch ($(I another_exception_type)) { - // 若这另一种类型的异常被捕获 - // 便要执行的表达式 - - // ……更多恰当的 catch 语句块…… - - } finally { - // 无论异常抛出与否 - // 都要执行的表达式 - } ---- - -$(P -我们以下面这个暂不使用 $(C try-catch) 语句的程序开始。该程序从文件里读取整型变量 die 的值并将它打印到标准输出里: -) - ---- -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 -注意 $(C readDieFromFile) 函数是以忽略错误情况的方式下编写的,默认了它所求的文件和值是有效的。换句话说,该函数只处理它自己的任务,而非关注错误情况。这是异常的一个好处:比之关注于错误情况,许多函数能以关注实际任务的方式被编写。 -) - -$(P -我们在文件 $(C the_file_that_contains_the_value) 不存在时启动程序: -) - -$(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 -抛出了一个 $(C ErrnoException) 类型的异常对象,并且程序立即终止,而非打印“Die value: ”。 -) - -$(P -我们把一个在 $(C try) 语句块中调用 $(C readDieFromFile) 的中间函数添加到该程序里,并让 $(C main()) 调出这个新函数: -) - ---- -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 -我们在 $(C the_file_that_contains_the_value) 仍然丢失的状态下再次启动程序,这次该程序就不会因异常而终止。 -) - -$(SHELL -(Could not read from file; assuming 1) -Die value: 1 -) - -$(P -新程序$(I 尝试(try))在 $(C try) 语句块里执行 $(C readDieFromFile)。如果那个块成功执行,函数会正常地以 $(C return die;) 语句结束。如果 $(C try) 块的执行以抛出明确指定的 $(C std.exception.ErrnoException) 异常退出,那么程序执行便进入 $(C catch) 语句块。 -) - -$(P -下面是程序在文件不存在时启动经过的概括: -) - -$(UL -$(LI 像先前程序里一样,$(C std.exception.ErrnoException) 对象被($(C File(),而非由我们的代码)抛出,) -$(LI 该异常对象被 $(C catch) 捕获,) -$(LI 在 $(C catch) 语句块的正常执行中假定值为 1,) -$(LI 而程序会接着以它的正常路径运行。) -) - -$(P -$(C catch) 可能会捕获抛出的异常以求继续执行该程序的方法。 -) - -$(P -作为另一个示例,我们回到煎蛋卷(Omelet)程序并将一个 $(C try-catch) 语句添加到 $(C main()) 函数中: -) - ---- -$(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 注:)$(C .msg) property(属性)会在下面得到解释。)) -) - -$(P -$(C try) 语句块包括两行代码。任何从这些行中抛出的异常均会被 $(C catch) 语句块捕获。 -) - -$(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 -正如我们在输出中所见,程序不再因为抛出的异常而终止。它从错误状态中恢复,并继续执行到 $(C main()) 函数的末尾。 -) - -$(H6 $(C catch) 语句块的顺次匹配) - -$(P -我们至今为止在示例里用的 $(C Exception) 类型是种一般的异常类型。此类型仅表明程序里发生了错误。尽管它也包含了能更深入解读错误的错误消息,但它不包含关于错误$(I 类型)的信息。 -) - -$(P -我们在此章节前面所见的 $(C ConvException) 和 $(C ErrnoException) 是更加特殊的异常类型:前一个关于转换错误,后一个关于系统错误。正如 Phobos 里的许多其他异常类型及它们名称所暗示,$(C ConvException) 和 $(C ErrnoException) 都继承自类 $(C Exception)。 -) - -$(P -类 $(C Exception) 和它的兄弟类 $(C Error) 更进一步是继承自类 $(C Throwable) 的,这是最一般的异常类型。 -) - -$(P -虽有可能,但仍不建议捕获 $(C Error) 类型和那些自 $(C Error) 继承而来的类型。由于 $(C Throwable) 比 $(C Error) 更一般,所以也不建议去捕获 此类型。需要被正常捕获的是在 $(C Exception) 继承结构下的类型, 包括 $(C Exception) 自身。 -) - -$(MONO - throwable$(I (不建议捕获)) - ↗ ↖ - exception error$(I (不建议捕获)) - ↗ ↖ ↗ ↖ - ... ... ... ... -) - -$(P $(I $(B 注:) 我会在后面的 $(LINK2 /ders/d.cn/inheritance.html, 继承) 一章中解释层次结构表示形式。上方的树状图表示 $(C Throwable) 最为一般而 $(C Exception) 与 $(C Error) 较为特殊。) -) - -$(P -捕获特定类型的异常对象是有可能的。例如,专门捕获 $(C ErrnoException) 对象来检查并处理系统错误是可行的。 -) - -$(P -异常当且仅当它们匹配由 $(C catch) 语句块规定的类型时才会被捕获。例如,尝试捕获 $(C SpecialExceptionType) 的 catch 语句块捕获块不会捕获 $(C ErrnoException)。 -) - -$(P -$(C try) 语句块执行时被抛出的异常对象类型以 $(C catch) 语句块的顺序被依次匹配到由 $(C catch) 语句块所指定的类型。如果该对象匹配 $(C catch) 语句块的指定的类型,那么异常便被看作被 $(C catch) 语句块捕获,同时执行编写在这个语句块里的代码。一旦找到一处匹配,剩下的 $(C catch) 语句块就会被忽略。 -) - -$(P -因为 $(C catch) 语句块的匹配次序为顺序,$(C catch) 语句块必须以从特殊到一般的异常类型顺序排列。因此,如果需要捕获 exception 异常类型,那么类 $(C Exception) 必须被指定在末尾的 $(C catch) 语句块内。 -) - -$(P -例如,正尝试捕获关于学生记录的特定类型异常的 $(C try-catch) 语句必须以从特殊到一般的顺序来排列 $(C catch) 语句块,正如下方代码所示: -) - ---- - try { - // 可能抛出的有关学生纪录异常的操作…… - - } catch (StudentIdDigitException exc) { - - // 特定关于学生 ID - // 数字错误的异常 - - } catch (StudentIdException exc) { - - // 有关学生 ID 的更为一般的异常, - // 但是不一定关于他们的数字 - - } catch (StudentRecordException exc) { - - // 关于学生纪录的更一般的异常 - - } catch (Exception exc) { - - // 可能和学生纪录无关 - // 但最为一般的异常 - - } ---- - -$(H6 $(IX finally) $(C finally) 语句块) - -$(P -$(C finally) 是 $(C try-catch) 语句的可选块。它包括那些无论异常抛出与否,都一定会执行的表达式。 -) - -$(P -为了了解 $(C finally) 是怎样运作的,我们看看这个有一半几率抛出异常的程序: -) - ---- -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"); - - // ……这里可能有一个或多个 catch 语句块…… - - } $(HILITE finally) { - writeln("the body of the finally block"); - } - - writeln("the last line of foo()"); -} - -void main() { - foo(); -} ---- - -$(P -函数没有抛出异常时,程序输出如下内容: -) - -$(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 -函数抛出异常时,程序输出如下内容: -) - -$(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 -正如所见到的,尽管 "the last line of the try block" 和 "the last line of foo()" 未被输出,$(C finally) 语句块的内容在有异常抛出时仍然被执行。 -) - -$(H6 何时使用 $(C try-catch) 语句) - -$(P -$(C try-catch) 语句对于捕获异常并做出些特殊处理十分有效。 -) - -$(P -因此,$(C try-catch) 语句必须仅在需要执行某些特殊任务时被使用。另外不要捕获异常,而应该让它们去可能会捕获它们的更高等级函数那里。 -) - -$(H5 异常的 property) - -$(P -程序由于异常而终止、从而自动打印在输出中的信息亦可以在异常对象的 property 中取得获取。这些 property 由类 $(C Throwable) 定义: -) - -$(UL - -$(LI $(IX .file) $(C .file):抛出异常的源文件名) - -$(LI $(IX .line) $(C .line):抛出异常的行数) - -$(LI $(IX .msg) $(C .msg):错误消息) - -$(LI $(IX .info) $(C .info):抛出异常时程序堆栈的状态) - -$(LI $(IX .next) $(C .next):下一个附属异常) - -) - -$(P -我们看到 $(C finally) 语句块在因异常而退出作用域时被执行。(我们会在以后的章节中看到,对于 $(C scope) 语句和$(I 析构函数)也是如此。) -) - -$(P -$(IX 附属异常) 当然,这样的代码块也能抛出异常。由于已抛出的异常而在离开作用域时被抛出的异常叫做$(I 附属异常)。主异常和附属异常都是$(I 链表)数据结构中的元素,每个异常对象都可以通过前一个异常对象的 $(C .next) property进行访问。最后一个异常的 $(C .next) property 值为 $(C null)。(我们在之后的一章中将会看到 $(C null)。) -) - -$(P -下面例子里有三处异常:主异常在 $(C foo()) 里被抛出,以及在 $(C foo()) 和 $(C bar()) 里的两处 $(C finally) 语句块中被抛出的附属异常。程序通过 $(C .next) property 访问附属异常。 -) - -$(P -在此程序中使用的一些概念将在之后的章节里作出解释。例如,仅包含 $(C exc) 的 $(C for) 循环持续条件表明$(I 当 $(C exc) 不是 $(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; // ← 表示:当 exc 不是‘ull’ - 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 -输出为: -) - -$(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 错误, 种类) 错误种类) - -$(P -我们已经见识到异常处理机制是多么有效。比起让程序以错误或丢失的数据或以任何其他错误的状态运行,它使得更低与更高等级的操作立即终止。 -) - -$(P -但这不意味着每种错误情况都适宜抛出异常。根据错误类型不同也许可以做些更有用的事。 -) - -$(H6 用户错误) - -$(P -有些错误由用户导致。正如我们上方所见,即便程序期望输入数字,用户仍可能输入像“hello”这样的字符串。向用户显示错误信息并让他重新输入合适的数据可能更为恰当。 -) - -$(P -即便如此,由于无论如何使用错误数据的代码都会抛出异常,不复验数据而直接接受并使用该数据是可行的。重要的是需要告知用户数据错误的原因。 -) - -$(P -例如,我们看看这个从用户输入获取文件名的程序。这里至少有两种处理潜在错误的文件名的方法: -) - -$(UL -$(LI $(B 在使用前验证数据):我们能通过调用 $(C std.file) 模块的 $(C exists()) 来确认文件名对应的文件存在与否: ---- - if (exists(fileName)) { - // 是的,文件存在 - - } else { - // 不,文件不存在 - } ---- - -$(P -这使得我们能够在当且仅当数据存在时打开它。然而,即便 $(C exists()) 返回 $(C true),如果系统上另一进程在本程序打开它之前将文档删除或重命名,文档仍有可能无法打开。 -) - -$(P -因此,下列解决方案会更加有用。 -) - -) - -$(LI $(B 在没有复验的情况下使用数据):我们假定该数据有效,并立即使用它,因为在 $(C File) 无法打开文件时无论如何都会抛出异常。 - ---- -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")); - - /* 如果运行至这一行,这表示 - * useTheFile() 函数已经成功完成 - * 即表明文件名有效 - * - * 我们现在可以将循环标志的值 - * 设置为终止循环 - */ - is_fileUsed = true; - writeln("The file has been used successfully"); - - } catch (std.exception.ErrnoException exc) { - stderr.writeln("This file could not be opened"); - } - } -} ---- - -) - -) - -$(H6 编程错误) - -$(P -一些错误由程序员失误而导致。例如,程序员可能认为:一个刚被写入的函数会永远以一个大于或等于零的值调用,根据该程序的设计这是可能成立的。而若它依然被小于零的值调用即指示着在程序设计上或该设计实现上的错误。两者都可以被认为是某种编程错误。 -) - -$(P -比起异常处理机制,应对由编程失误导致的错误时使用 $(C assert) 会更加合适。($(I $(B 注:)我们会在 $(LINK2 /ders/d.cn/assert.html, 之后的一章) 中谈及 $(C assert) 。)) -) - ---- -void processMenuSelection(int selection) { - assert(selection >= 0); - // ... -} - -void main() { - processMenuSelection(-1); -} ---- - -$(P -程序以 $(C assert) 失败终止: -) - -$(SHELL_SMALL -core.exception.AssertError@$(HILITE deneme.d(3)): Assertion failure -) - -$(P -$(C assert) 验证程序的状态并在失败时输出相关文件名与行数。上方的信息表示文件 deneme.d 第三行的断言失败了。 -) - -$(H6 意外情况) - -$(P -对于除上方两种一般状况之外的意外情况,抛出异常仍然是适宜的。如果程序无法继续执行,那么便只能抛出异常。 -) - -$(P -对抛出的异常如何处理取决于调用出该函数的更高层函数。它们会捕获我们为了修复该状况而抛出的异常。 -) - -$(H5 总结) - -$(UL - -$(LI -当面临用户错误时立即警告用户或确保异常被抛出。该异常可能是由使用错误数据的另一个函数抛出,或者你可以直接抛出它。 -) - -$(LI -使用 $(C assert) 来验证程序逻辑和执行。($(I $(B 注:)$(C assert) 将会在之后的一章作出解释。)) -) - -$(LI -存在疑问时,用 $(C throw) 或 $(C enforce()) 抛出异常。($(I $(B 注:)$(C enforce()) 将会在之后的一章作出解释。)) -) - -$(LI -当且仅当你能对异常做出些有效处理时将它捕获。否则,不要用 $(C try-catch) 语句封装代码,而要把异常留给或能处理它们的更高层代码。 -) - -$(LI -以从特殊到一般的顺序排布 $(C catch) 语句块。 -) - -$(LI -把所有离开作用域时必须执行的表达式放在 $(C finally) 语句块里。 -) - -) - -Macros: - SUBTITLE=异常 - - DESCRIPTION=D 语言在意外情况下的异常处理机制 - - KEYWORDS=D 编程语言教程 异常 try catch finally exit failure success - -MINI_SOZLUK= diff --git a/ddili/src/ders/d.cn/files.d b/ddili/src/ders/d.cn/files.d deleted file mode 100644 index c24b2c1..0000000 --- a/ddili/src/ders/d.cn/files.d +++ /dev/null @@ -1,227 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX file) 文件) - -$(P -在前面的章节我们了解到标准输入输出流可以通过在终端中使用 $(C >)、$(C <) 或 $(C |) 运算符重定向至文件或另一个程序。虽然管道非常强大,但是它并不适用于所有情况。因为在大多数情况下仅通过简单的读取和输出程序并不能完成任务。 -) - -$(P -举个例子:一个处理学生记录的程序可能需要使用标准输出来显示菜单。程序需要将学生记录写入到一个真实存在的文件而不是 $(C stdout)。 -) - -$(P -本章中,我们将学习读取和写入文件系统中的文件的方法。 -) - -$(H5 基本概念) - -$(P -文件是指 $(C std.stdio) 模块中的 $(C File) $(I 结构体)。因为我还没有介绍结构体,所以我们现在不得不暂且接受结构体的构造语法。 -) - -$(P -在接触代码示例之前我们需要先了解一些有关文件的基本概念。 -) - -$(H6 创建者和用户) - -$(P -在某个平台上创建的文件可能无法在其他平台上使用。仅仅是打开文件并向其中写入数据是不够的,我们还需要确保数据能被用户正确获取。数据的创建者和用户必须在文件的数据格式上达成一致。例如,如果创建者将学生记录中的学生 ID 和学生姓名以某种顺序写入文件,那用户必须以相同的顺序读取。 -) - -$(P -除此之外,下面的代码示例并没有在文件开头写入$(I 字节顺序标记(byte order mark)) (BOM)。这可能使你的文件无法兼容需要 BOM 的系统。(BOM 指明文件是以什么样的顺序组织 UTF 字符的代码单元。) -) - -$(H6 访问权限) - -$(P -文件系统在给程序提供文件时会附以特定的权限。访问权限对数据的完整性和操作文件的性能都非常重要。 -) - -$(P -在读取文件时,文件系统可能允许多个程序同时读取一个文件以提高其性能,因为程序不再需要等待其他程序读取完成后再开始读取。而对于写入,即使只有一个程序想要写入文件,禁止并发访问文件也往往是有益的。通过锁定文件,操作系统可以防止其他程序读取到不完整的数据或将彼此的数据覆盖等情况的发生。 -) - -$(H6 打开文件) - -$(P -在程序启动时标准输入流 $(C stdin) 和标准输出流 $(C stdout) 就已经被$(I 打开)。你可以随时使用它们。 -) - -$(P -通常情况下,必须先通过指定文件名和访问权限打开文件才能对其进行操作。就像我们在下面这个例子中看到的这样,创建一个 $(C File) 对象来打开指定的文件: -) - ---- - File file = File("student_records", "r"); ---- - -$(H6 关闭文件) - -$(P -在使用完成后程序必须将其打开的文件关闭。在大多数情况下你不需要手动关闭文件;它们会在 $(C File) 对象被销毁时自动关闭。 - -) - ---- -if (aCondition) { - - // 假设我们在这里创建并使用了一个文件对象。 - // ... - -} // ← 文件将会在离开此作用域时自动关闭 - // 你不需要显式关闭它。 ---- - -$(P -某些时候你需要重新打开文件对象以访问另一个文件或以不同的权限访问之前的文件。在这种情况下需先将文件关闭然后再重新打开它。 -) - ---- - file.close(); - file.open("student_records", "r"); ---- - -$(H6 写入和读取文件) - -$(P -文件的输入输出函数与向控制台输入输出的函数 $(C writeln)、$(C readf) 等的用法相同,因为它们都是字符流。唯一的区别是你需要一个类型为 $(C File) 的变量名和一个点运算符: -) - ---- - writeln("hello"); // 写至标准输出 - stdout.writeln("hello"); // 同上 - $(HILITE file.)writeln("hello"); // 写入某个文件 ---- - -$(H6 $(IX eof) 使用 $(C eof()) 判断是否到达文件末尾) - -$(P -在读取文件时,$(C File) 的成员函数 $(C eof()) 可以用来判断是否已经读取到文件末尾。在到达文件末尾时该函数返回 $(C true)。 -) - -$(P -例如下面这个循环会一直执行直到文件流到达末尾: -) - ---- - while (!file.eof()) { - // ... - } ---- - -$(H6 $(IX std.file) $(C std.file) 模块) - -$(P -$(LINK2 http://dlang.org/phobos/std_file.html, $(C std.file) 模块) 提供了一些用于操作目录内容的实用函数和类型。例如 $(C exists) 可以检测一个文件或目录是否存在于文件系统上: -) - ---- -import std.file; - -// ... - - if (exists(fileName)) { - // 对应名称的文件或目录存在 - - } else { - // 对应名称的文件或目录不存在 - } ---- - -$(H5 $(IX File) $(C std.stdio.File) 结构体) - -$(P -$(IX mode, 文件) $(C File) 结构体在 $(LINK2 http://dlang.org/phobos/std_stdio.html, $(C std.stdio) 模块) 中。要使用它只需指定要打开的文件名称和访问权限或访问模式。它和 C 语言中的 $(C fopen) 函数的模式字符相同: -) - - - - - - - - - - - - - - - -
     模式  说明
    r$(B 读取)权限$(BR)打开文件并从文件开头读取
    r+$(B 读取和写入)权限$(BR)打开文件并从文件开头读取或写入
    w$(B 写入)权限$(BR)如果文件不存在,则创建一个空文件$(BR)如果文件存在,则将其内容清空
    w+$(B 读取和写入)权限$(BR)如果文件不存在,则创建一个空文件$(BR)如果文件存在,则将其内容清空
    a$(B 追加)权限$(BR)如果文件不存在,则创建一个空文件$(BR)如果文件存在,则打开文件并在文件末尾写入,同时不会影响原有内容
    a+$(B 读取和追加)权限$(BR)如果文件不存在,则创建一个空文件$(BR)如果文件存在,则打开文件并在文件末尾写入或从其开头读取,同时不会影响原有内容
    - -$(P -除此之外,你还可以在模式字符中添加 ’b‘,例如 “rb”。如果平台支持程序将会以$(I 二进制模式)操作文件,但在 POSIX 系统中它将被忽略。 -) - -$(H6 写入文件) - -$(P -首先,文件必须以一种写入模式打开: -) - ---- -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 -就像我们在 $(LINK2 /ders/d.cn/strings.html, Strings 一节) 中提到的,像 $(STRING "student_records") 这样的指定文件名的字符串的类型是 $(C string),它包含的是不可变的字符。因此我们不能使用可变的文本(比如 $(C char[]))来指定文件名创建 $(C File) 对象。如果出于某种原因你必须使用可变的文本类型,那你应当使用其 $(C .idup) 属性来获得其不可变字符拷贝。 -) - -$(P -上面的程序创建或覆盖了一个与其处在相同文件夹(程序的$(I 工作目录))的名为 $(C student_records) 的文件。 -) - -$(P -$(I $(B 注意:)文件名可以包含任意文件系统认为是合法的字符。为简便我们将只是用常见的 ASCII 字符作文件名。) -) - -$(H6 读取文件) - -$(P -在读取文件前文件必须以某种读取模式打开: -) - ---- -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 -上面的程序读取了名为 $(C student_records) 的文件中的每一行并将其打印至标准输出。 -) - -$(PROBLEM_TEK - -$(P -编写一个程序,从用户处获取文件名,打开文件并将其中所有非空行写入到另一个文件。新文件的名称应基于原文件。例如:如果源文件名为 $(C foo.txt),那新文件名可为 $(C foo.txt.out)。 -) - -) - -Macros: - SUBTITLE=文件 - - DESCRIPTION=基础文件操作。 - - KEYWORDS=D 编程语言教程 file diff --git a/ddili/src/ders/d.cn/formatted_output.d b/ddili/src/ders/d.cn/formatted_output.d deleted file mode 100644 index 6796a7a..0000000 --- a/ddili/src/ders/d.cn/formatted_output.d +++ /dev/null @@ -1,637 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX output, 格式化) $(IX 格式化输出) 格式化输出) - -$(P -本章的内容是标准库中的 $(C std.format) 模块,与 D 语言的语言核心无关。 -) - -$(P -$(IX std) $(IX Phobos) 与其它带有 $(C std) 前缀的模块一样,$(C std.format) 也来自 D 语言标准库 Phobos。由于 Phobos 过于庞大,我们无法在这本书中涉及其所有的部分。 -) - -$(P -D 语言的输入输出格式化说明符与 C 语言相同。 -) - -$(P -在深入学习之前我们先来看下最基本的标识和说明符,你可以保留此表以供日后参考: -) - -$(MONO -$(B 标识) (可同时使用多个) - - 左对齐 - + 输出整数时显示正号 - # 在八进制数前添加 `0`,在十六进制数前添加 `0x`,科学计数浮点型总是使用小数点 - 0 使用 `0` 填充空位 - $(I 空格) 使用空格填充空位 - -$(B 格式化说明符) - s 默认的(字符串) - b 二进制 - d 十进制 - o 八进制 - x,X 无符号十六进制 - f,F 标准十进制计数法浮点数 - e,E 科学计数法浮点数 - a,A 十六进制浮点数 - g,G 与 e 和 f 相同 - - ( 格式化元素列表开始 - ) 格式化元素列表结束 - | 元素分隔符 -) - -$(P -我们之前肯定用过像 $(C writeln) 这样的函数,那时我们需要传递给它多个参数来输出期望格式的信息。参数先会被转换为它们的字符串形式,然后 - 才会被传递给标准输出流。 -) - -$(P -但有时这并不能满足需求。我们可能要求输出某种特殊的格式。比如下面这段打印发票项目的代码: -) - ---- - items ~= 1.23; - items ~= 45.6; - - students ~= Student(i, [80 + i, 90 + i]); - writeln("Item ", i + 1, ": ", items[i]); - } ---- - -$(P -输出为: -) - -$(SHELL -Item 1: 1.23 -Item 2: 45.6 -) - -$(P -尽管数据是正确的,但客户还要要将其以其指定的格式输出。比如,我们需要将小数点对齐(本例中的点)并确保小数点后有两位小数。下面是示例输出格式: -) - -$(SHELL -Item 1: 1.23 -Item 2: 45.60 -) - -$(P -这个时候我们就需要格式化输出了。在之前我们使用的输出函数名中添加一个字母 $(C f) 就得到了我们需要的函数:$(C writef()) 和 $(C writefln())。$(C f) 即为$(I 格式化(formatted))的缩写。这些函数的第一个参数是$(I 格式化字符串),它决定了之后的参数以何种格式被输出至屏幕。 -) - -$(P -对我们上面的需求来说,$(C writefln()) 可以通过使用这样的格式化字符串来将其实现: -) - ---- - writefln("Item %d:%9.02f", i + 1, items[i]); ---- - -$(P -格式化字符串中既可包含被直接输出的普通字符,也可包含用于描述输出的特殊说明符。格式化说明符以 $(C %) 开头,以$(I 格式化字符)结束。上面的格式化字符串包含两个说明符:$(C %d) 和 $(C %9.02f)。 -) - -$(P -每一个说明符都分别关联了一个参数,其指代参数的顺序与说明符顺序相同。比如 $(C %d) 关联 $(C i + 1),$(C %9.02f) 关联 $(C items[i])。每一个说明符都将描述其关联参数的输出形式。(说明符也可能会带有参数。稍后我们会介绍。) -) - -$(P -除此之外,格式化字符串中的其它字符均不是格式化说明符,它们将会按照原样显示。这是将格式化字符串中这种$(I 普通)字符高亮后的样子:$(C "$(HILITE Item )%d$(HILITE :)%9.02f")。 -) - -$(P -格式化说明符由六个部分组成,但其中的大部分都是可选的。我们之后会在本章介绍名为$(I 位置)的部分。而下面我们将介绍其它五个部分:($(I $(B 注:)两部分之间的空格是为了帮助你阅读;它们不是说明符的一部分。)) -) - -$(MONO - % $(I$(C 标识 宽度 精度 格式化字符)) -) - -$(P -开头的 $(C %) 和结尾的格式化字符是必须的;其它部分则是可选的。 -) - -$(P -由于 $(C %) 在格式化字符串中的特殊含义,当我们需要像一个普通字符一样输出 $(C %) 时需要将其写作 $(C %%)。 -) - -$(H5 $(I 格式化字符)) - -$(P $(IX %b) $(C b):以二进制的形式输出一个整数。 -) - -$(P $(IX %o, 输出) $(C o):以八进制的形式输出一个整数。 -) - -$(P $(IX %x, 输出) $(IX %X) $(C x) 和 $(C X):以十六进制的形式输出一个整数;若使用 $(C x) 则十六进制中的 x 也为小写,若使用 $(C X) 则十六进制中的 X 也为大写。 -) - -$(P $(IX %d, 输出) $(C d):以十进制的形式输出一个整数;如果是有符号整数且其值小于零则显示负号。 -) - ---- - 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):按照下面的规则输出一个浮点数。 -) - -$(UL -$(LI 小数点前必有一位数) -$(LI 如果$(I 精度)不为零则显示小数点) -$(LI 小数点后必须存在小数部分,小数位数取决于$(I 精度) (精度通常是 6)) -$(LI 字符 $(C e) (表示“以 10 为底的次方”)) -$(LI $(C -) 或 $(C +) 号,取决于指数是否大于零) -$(LI 指数,至少有两位) -) - -$(P $(IX %E) $(C E):与 $(C e) 相同,但将输出中的小写 $(C e) 换为大写 $(C E)。 -) - -$(P $(IX %f, 输出) $(IX %F) $(C f) 和 $(C F):以十进制的形式输出一个浮点数;小数点前至少有两位数字,默认保留 6 位小数。 -) - -$(P $(IX %g) $(C g):如果指数大于 -5 小于$(I 精度)则与 $(C f) 相同,反之则与 $(C e) 相同。$(I 精度)指定的是整个浮点数的有效数字,而不仅仅是小数点后的有效位数。若小数点后没有数字那则不会显示小数点。小数点后最右端的零不会显示。 -) - -$(P $(IX %G) $(C G):与 $(C g) 相同,但 $(C E) 和 $(C F) 都会变为大写。 -) - -$(P $(IX %a) $(C a):以十六进制浮点数表示法显示浮点数: -) - -$(UL -$(LI 符号 $(C 0x)) -$(LI 一个十六进制数字) -$(LI 若$(I 精度)不为零则显示小数点) -$(LI 小数点后的小数,位数由$(I 精度决定);若没有指定$(I 精度)则尽可能显示更多的数字) -$(LI 符号 $(C p) (表示“以 2 为底的次方”)) -$(LI $(C -) 或 $(C +) 号,取决于指数是否大于零) -$(LI 指数,至少包含一个数字(0 指数即显示 0)) -) - -$(P $(IX %A) $(C A):与 $(C a) 相同,但 $(C 0X) 和 $(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, 输出) $(C s):以常规形式输出数据,具体情况与类型有关: -) - -$(UL - -$(LI $(C bool) 值将显示 $(C true) 或 $(C false) -) -$(LI 整数输出形式与 $(C %d) 相同 -) -$(LI 浮点数输出形式与 $(C %g) 相同 -) -$(LI 字符串以 UTF-8 编码;$(I 精度)决定最多可使用多少字节 (在 UTF-8 中字节数并不是字符数;比如 "ağ" 中有两个字符,但实际上包含 3 个字节) -) -$(LI 结构体和类将输出其 $(C toString()) 成员函数的返回值;$(I 精度)决定最多可使用多少字节 -) -$(LI 数组则会将其所有值并排输出出来 -) - -) - ---- - 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, 输出) $(I 宽度)) - -$(P -$(IX *, 格式化输出) 这部分参数决定了输出内容的宽度。如果使用 $(C *) 指定宽度,那它将从下一个参数读取宽度(那个参数必须是 $(C int))。小于零的宽度会使其带有 $(C -) 标识的效果。 -) - ---- - 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, 输出) $(I 精度)) - -$(P -精度通常写在标识符中的点号后面。对于浮点类型,它将决定输出的小数的位数。如果使用 $(C *) 指定精度,那那它将从下一个参数读取精度(那个参数必须是 $(C int))。负精度将被忽略。 -) - ---- - 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, 输出) $(I 标识)) - -$(P -一个说明符中可以指定多个标识。 -) - -$(P $(C -):输出的数据将被左对齐;它将使 $(C 0) 标识失效 -) - ---- - int value = 123; - - writefln("Normally right-aligned:|%10d|", value); - writefln("Left-aligned :|%-10d|", value); ---- - -$(SHELL -Normally right-aligned:| 123| -Left-aligned :|123 | -) - -$(P $(C +):如果输出的数据是正数则在其开头添加 $(C +) 号;它将使$(I 空格)标识失效 -) - ---- - 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 #):根据不同的$(I 格式化字符)显示$(I 不同的)输出格式 -) - -$(UL -$(LI $(C o):八进制数的第一个数字一般都是 0) - -$(LI $(C x) 和 $(C X):如果是非零值则在其开头处添加 $(C 0x) 或 $(C 0X)) - -$(LI 浮点数:即使小数点后没有有效数字也会显示小数点) - -$(LI $(C g) 和 $(C G):小数点后的非有效数字(即 0)也会显示) -) - ---- - 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):用零填充空白(除非传入的值是 $(C nan) 或 $(C infinity));如果同时指定了$(I 精度)那这个标志会被忽略 -) - ---- - writefln("In a field of 8 characters: %08d", 42); ---- - -$(SHELL -In a field of 8 characters: 00000042 -) - -$(P $(I 空格)符:如果传入的值是正数,那它将会在正数前插入空格使其能与负数对齐) - ---- - 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, 输出) $(IX $, 格式化输出) 位置参数) - -$(P -之前我们编写的程序中格式化字符串中的说明符与参数是按照顺序一个一个相关联的。除此之外我们也可以在格式化说明符中使用位置编号。这样说明符就能与指定的参数相关联。参数的序号从 1 开始递增。参数序号应写在 $(C %) 后,并以 $(C $) 结尾: -) - -$(MONO - % $(I$(C $(HILITE 位置$) 标识 宽度 精度 格式化字符)) -) - -$(P -参数序号的作用之一是允许一个格式化字符串中的多个说明符关联同一个参数: -) - ---- - writefln("%1$d %1$x %1$o %1$b", 42); ---- - -$(P -上面这个格式化字符串在 4 个说明符中使用一个参数序号 1 来将同一个数分别以十进制、十六进制、八进制和二进制的形式显示: -) - -$(SHELL -42 2a 52 101010 -) - -$(P -还有就是位置编号能让程序支持多种自然语言。有了位置编号,程序只需在格式化字符串中移动参数位置就可以适应多种人类语言。举个例子,按照下面的格式显示指定教室中的学生个数: -) - ---- - writefln("There are %s students in room %s.", count, room); ---- - -$(SHELL -There are 20 students in room 1A. -) - -$(P -我们假设现在程序要支持土耳其语。为了应对这种需求,程序需要根据活动语言来选择格式化字符串。下面这种方法运用了三元运算符: -) - ---- - auto format = (language == "en" - ?"There are %s students in room %s." - : "%s sınıfında %s öğrenci var."); - - writefln(format, count, room); ---- - -$(P -然而这样的话土耳其语信息中的教室和学生信息的顺序是错误的;教教室信息的位置显示的是学生数,而学生数的位置显示的则是教室: -) - -$(SHELL -20 sınıfında 1A öğrenci var. $(SHELL_NOTE_WRONG 错误的:表示的是 “room 20” 和 “1A students”!) -) - -$(P -为了避免出现这种情况,我们通常为说明符指定像 $(C 1$) 或 $(C 2$) 这样的参数编号使其能与正确的参数相关联: -) - ---- - 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 -现在无论选择那个语言参数都能出现在正确的位置了: -) - -$(SHELL -There are 20 students in room 1A. -) - -$(SHELL -1A sınıfında 20 öğrenci var. -) - -$(H5 $(IX %$(PARANTEZ_AC)) $(IX %$(PARANTEZ_KAPA)) 元素格式化输出) - -$(P -在 $(STRING %$(PARANTEZ_AC)) 和 $(STRING %$(PARANTEZ_KAPA)) 之间的格式化说明符将会被应用到其关联的容器中的每个参数(比如数组或 range): -) - ---- - auto numbers = [ 1, 2, 3, 4 ]; - writefln("%(%s%)", numbers); ---- - -$(P -上面这个格式化字符串由三个部分组成: -) - -$(UL -$(LI $(STRING %$(PARANTEZ_AC)):开始元素格式) -$(LI $(STRING %s):每一个元素的格式) -$(LI $(STRING %$(PARANTEZ_KAPA)):结束元素格式) -) - -$(P -容器中的元素会以 $(STRING %s) 格式挨个输出: -) - -$(SHELL -1234 -) - -$(P -元素格式两侧的普通字符都会在每一个元素中重复。比如使用 $(STRING {%s},) 说明符,将元素包裹在花括号中并以逗号分隔的格式输出: -) - ---- - writefln("%({%s},%)", numbers); ---- - -$(P -但元素右侧的普通字符将会被当作元素分隔符,它们只会在两个元素之间显示而不会显示在最后: -) - -$(SHELL -{1},{2},{3},{4 $(SHELL_NOTE ‘}’ 和 ‘,’ 都不会在最后一个元素后显示) -) - -$(P -$(IX %|) $(STRING %|)是用来说明允许在最后一个元素后显示的字符的。$(STRING %|) 右侧的字符将会被当作分隔符,它们将不会在最后一个元素后显示。而 $(STRING %|) 左侧的字符将为最后一个元素显示。 -) - -$(P -比如下面这个格式化说明符,花括号会显示在输出信息的最后而逗号不会: -) ---- - writefln("%({%s}%|,%)", numbers); ---- - -$(SHELL -{1},{2},{3},{4} $(SHELL_NOTE 现在最后一个元素后也有 ‘}’ 了) -) - -$(P -与单独显示字符串不同的是,以元素的方式输出的字符串都会被包裹在双引号中: -) - ---- - auto vegetables = [ "spinach", "asparagus", "artichoke" ]; - writefln("%(%s, %)", vegetables); ---- - -$(SHELL -"spinach", "asparagus", "artichoke" -) - -$(P -$(IX %-$(PARANTEZ_AC)) 如果我们不需要双引号,那我们需要将开头处的 $(STRING %$(PARANTEZ_AC)) 替换为 $(STRING %-$(PARANTEZ_AC)): -) - ---- - writefln("%-(%s, %)", vegetables); ---- - -$(SHELL -spinach, asparagus, artichoke -) - -$(P -单个字符也有类似的情况。$(STRING %$(PARANTEZ_AC)) 会对其每一个元素两侧添加双引号: -) - ---- - writefln("%(%s%)", "hello"); ---- - -$(SHELL -'h''e''l''l''o' -) - -$(P -$(STRING %-$(PARANTEZ_AC)) 则不会添加双引号: -) - ---- - writefln("%-(%s%)", "hello"); ---- - -$(SHELL -hello -) - -$(P -对于关联数组来说格式化字符串中必须有两个格式化说明符,一个对应键,一个对应值。下面这个例子中的说明符 $(STRING %s (%s)) 会将值放在键后的圆括号中: -) - ---- - auto spelled = [ 1 : "one", 10 : "ten", 100 : "hundred" ]; - writefln("%-(%s (%s)%|, %)", spelled); ---- - -$(P -注意由于指定了 $(STRING %|),逗号将不会出现在最后一个元素后。 -) - -$(SHELL -1 (one), 100 (hundred), 10 (ten) -) - -$(H5 $(IX format, std.string) $(C format)) - -$(P -我们也可以使用 $(C std.string) 模块中的 $(C format()) 函数来实现格式化输出。$(C format()) 与 $(C writef()) 的作用相似,不同的是它将$(I 返回)格式化后的得到的 $(C string) 而不是将其输出: -) - ---- -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 -我们可以在程序之后的表达式中使用格式化后的结果字符串。 -) - -$(PROBLEM_COK - -$(PROBLEM -编写一个程序,让用户输入一个数并将其以十六进制形式输出。 -) - -$(PROBLEM -编写一个程序,让用户输入一个浮点数,将得到的浮点数转换为百分比形式并保留两位小数输出。比如输入 1.2345,程序将显示 $(C %1.23)。 -) - -) - -Macros: - SUBTITLE=格式化输出 - - DESCRIPTION=以确定的格式显示数据 - - KEYWORDS=D 编程语言教程 格式化 输出 diff --git a/ddili/src/ders/d.cn/halftitle.html b/ddili/src/ders/d.cn/halftitle.html deleted file mode 100644 index bbfcfd9..0000000 --- a/ddili/src/ders/d.cn/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -Programming in D -

    - -

    -Ali Çehreli -

    - -
    diff --git a/ddili/src/ders/d.cn/if.cozum.d b/ddili/src/ders/d.cn/if.cozum.d deleted file mode 100644 index ea43ec7..0000000 --- a/ddili/src/ders/d.cn/if.cozum.d +++ /dev/null @@ -1,110 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU if 语句) - -$(OL - -$(LI -语句 $(C writeln("Washing the plate")) 的缩进使这条语句像是处在 $(C else) 的作用域中。但 $(C else) 并没有使用花括号指定作用域,所以只有 $(C writeln("Eating pie")) 语句处在 $(C else) 的作用域中。 - -$(P -由于空白在 D 语言的语法中并不起作用$(I 刷盘子的语句) 实际上是 $(C main()) 函数中的一条独立语句。无论如何它都会执行。让读者混淆的地方在于它使用了一个与一般情况不同的缩进。如果要将$(I 刷盘子的语句) 写在 $(C else) 的作用域中则需用花括号把作用域括起来: -) - ---- -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 -有不止一种方法可以实现这个游戏。我将会展示两个例子。在第一个例子中我们直接按照题干提供的信息编写代码: - ---- -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 -但这个程序有太多的重复代码。我们可以设计另一种方法实现相同的功能。代码如下: -) - ---- -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 -上一题的第一种方法并不能在这道题中使用。在程序中分别处理 1000 个输入的数还要保证代码正确且易读是不现实的。所以,最好还是判断输入的数处在$(I 哪一个范围): - ---- - if ((die >= 1) && (die <= 500)) ---- - -) - -) - -Macros: - SUBTITLE=if 语句习题解答 - - DESCRIPTION=D 编程语言练习解答:‘if’语句和它可选的‘else’从句 - - KEYWORDS=D 编程语言教程 if else 解答 diff --git a/ddili/src/ders/d.cn/if.d b/ddili/src/ders/d.cn/if.d deleted file mode 100644 index 04b4f8c..0000000 --- a/ddili/src/ders/d.cn/if.d +++ /dev/null @@ -1,310 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX if) $(CH4 if) 语句) - -$(P -我们已经学习到程序是通过使用表达式来实现具体功能的。至今为止我们见到的所有程序的所有表达式都是从 $(C main()) 函数开始,执行到 $(C main) 结尾。 -) - -$(P -$(IX statement) 而$(I 语句)是一种能影响表达式执行顺序的特性。语句不产生值,自己也没有副作用。它们通常使用一个逻辑判断表达式来决定接下来要执行哪一条表达式。 -) - -$(P $(I $(B 注意:) 其它编程语言也许对表达式或语句有不同的定义,也有一些语言与 D 语言的定义相同。 -) -) - -$(H5 $(C if) 块和它的作用域) - -$(P -$(C if) 语句通过使用一个逻辑表达式来决定了哪条或哪些语句将要被执行。它和英语单词 “if” 有着相同的意思,就想下面这个句子:“if there is coffee then I will drink coffee”。 -) - -$(P -$(C if) 用于判断的逻辑表达式应放在 if 后的括号中。如果逻辑表达式的值为 $(C true) 则执行它下方花括号中的表达式;反之如果逻辑表达式的值为 $(C false),下方花括号中的语句将不会被执行。 -) - -$(P -花括号中的趋于被称作$(I 作用域),全部位于作用于中的代码被称作一个$(I 代码块)。 -) - -$(P -下面是 $(C if) 语句的语法: -) - ---- - if (a_logical_expression) { - // ... 逻辑表达式为 true 时此处表达式将会被执行 - } ---- - -$(P -例如,表示“如果有咖啡,那就喝咖啡并把杯子洗干净”的程序可以像下面这样写: -) - ---- -import std.stdio; - -void main() { - bool existsCoffee = true; - - if (existsCoffee) { - writeln("Drink coffee"); - writeln("Wash the cup"); - } -} ---- - -$(P -如果 $(C existsCoffee) 的值为 $(C false),程序将跳过 if 块中的语句,什么都不会输出。 -) - -$(H5 $(IX else) $(C else) 块和它的作用域) - -$(P -有时,我们想在 $(C if) 语句的逻辑表达式为 $(C false) 执行某些操作,即无论怎样总有一些表达式将被执行。例如:如果有咖啡,我就喝咖啡,否则我会喝茶”。 -) - -$(P -当逻辑表达式为 $(C false) 时要执行的操作应写在 $(C else) 关键字后的作用域中。 -) - ---- - if (a_logical_expression) { - // ... 逻辑表达式为 true 时此处表达式将会被执行 - - } else { - // ... 逻辑表达式为 false 时此处表达式将会被执行 - } ---- - -$(P -例如,我们假设总是有茶: -) - ---- - if (existsCoffee) { - writeln("Drink coffee"); - - } else { - writeln("Drink tea"); - } ---- - -$(P -在上面的例子中,哪一个字符串将被打印取决于 $(C existsCoffee) 的值。 -) - -$(P -如果只看 $(C else) 的话它并不是一个语句,它只是一个 $(C if) 语句的可选$(I 分句);它不能被单独使用。 -) - -$(P -注意本书将花括号写在与 $(C if) 和 $(C else) 同一行上。虽然将花括号单独写在一行是 $(LINK2 http://dlang.org/dstyle.html, D 语言官方编程风格),但本书依旧使用更加普遍的内嵌花括号风格。 - -) - -$(H5 始终使用作用域花括号) - -$(P -虽然当作用域中只有一条语句时花括号可以省略,但我们并不推荐这样做。如果 $(C if) 或 $(C else) 的作用域中只有一条语句我们可以将代码写成下面这样: -) - ---- - if (existsCoffee) - writeln("Drink coffee"); - - else - writeln("Drink tea"); ---- - -$(P -即使只有一条语句,大多数有经验的程序员也会使用花括号。(只有本章中的一个练习忽略了它们)不得不说的一点是,我将展示唯一一种省略花括号反而更好的情况。 -) - -$(H5 $(IX else if) “if, else if, else” 链) - -$(P -你可以以一种复杂嵌套的方式使用语句和表达式。除了表达式,作用域还可以包含其它语句。例如:在 $(C else) 作用域中包含一个 $(C if) 语句。以不同的方式连接表达式和语句可以使我们程序的行为更加智能。 -) - -$(P -下面这段更复杂的代码表示的是:优先选择骑车到一个好的咖啡店,如果没有自行车的话则步行至稍差的咖啡店。 -) - ---- - if (existsCoffee) { - writeln("Drink coffee at home"); - - } else { - - if (existsBicycle) { - writeln("Ride to the good place"); - - } else { - writeln("Walk to the bad place"); - } - } ---- - -$(P -上面这段代码代表这样一个句子:“如果有咖啡,就在家喝咖啡;如果没有咖啡但有自行车,则骑车去不错的咖啡店;否则步行到一般的咖啡店”。 -) - -$(P -让我们继续这一复杂的决策过程:在没有自行车的时候先去邻居家问一下邻居是否有咖啡,如果邻居也没有咖啡则再走路到一般的咖啡店: -) - ---- - 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 this case, else if that other case, else if that even other case, etc." 这样的决策在程序中非常常见。而如果一定要使用花括号,代码将会有太多水平和竖直的空白:忽略空行,上面代码中 3 个 $(C if) 语句和 4 个 $(C writeln) 表达式总共占用 13 行。 -) - -$(P -为了能以更紧凑的方式书写上面的结构,当 $(C else) 作用域中只有一个 $(C if) 语句时,省略 $(C else) 的花括号。这是本指南的例外情况。 -) - -$(P -在重构至更好的形式之前,我们先看一段凌乱代码。任何代码都不应该写得这么乱。 -) - -$(P -下面这段代码是移除两个只包含一个 $(C else) 语句的 $(C if) 语句大括号后的样子: -) - ---- - 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 -如果现在我们将 $(C if) 语句提到与包裹住它的 $(C else) 语句同一行并整理下代码,我们将会得到像下面这样可读性更强的代码: -) - ---- - 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 -移除花括号使得代码更加紧凑并使表达式可读性更强。逻辑表达式、其求值顺序、以及值为 true 时执行的操作都一目了然。 -) - -$(P -这种常见的编程结构被称为 “if, else if, else” 链。 -) - - -$(PROBLEM_COK - -$(PROBLEM - -当逻辑表达式的值为 $(C true) 时,我们希望程序$(I 喝柠檬水并将杯子洗干净): - ---- -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"); -} ---- - -但是当你运行程序时除了我们预期的结果,你还会看到 $(I washes the plate): - -$(SHELL -Drinking lemonade -Washing the cup -Washing the plate -) - -为什么会这样呢?修正程序使其只在表达式为 $(C false) 时才清洗盘子。 -) - -$(PROBLEM -写一个可以和用户玩游戏的程序(一个需要用户足够诚实的游戏)。用户将掷骰子得到的数输入程序。之后根据这个值来确定是程序获胜还是用户获胜: - -$(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 -) - -额外任务:让程序考虑输入值的有效性。例如: - -$(SHELL -ERROR: 7 is invalid -) - -) - -$(PROBLEM -修改下程序让我们可以输入 1 到 1000 的值。当输入的值在 1-500 内时用户获胜,当值在 501-1000 时程序获胜。上一题你写的程序可以经过简单的修改就达到我们的要求吗? -) - -) - -Macros: - SUBTITLE=if 语句 - - DESCRIPTION=if 语句是 D 语言条件语句之一 - - KEYWORDS=D 编程语言教程 if 条件语句 - -MINI_SOZLUK= diff --git a/ddili/src/ders/d.cn/index.d b/ddili/src/ders/d.cn/index.d deleted file mode 100644 index e336066..0000000 --- a/ddili/src/ders/d.cn/index.d +++ /dev/null @@ -1,148 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Programming in D) - -
    - - - -$(P -The paper version of this book is available through two publishing platforms: -) - -$(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.) -) - -$(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).) - -) - -$(H5 Web version) - -$(UL -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.cn/foreword1.html, Foreword by 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/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/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/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/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/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/ternary.html, 三元运算符 ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.cn/literals.html, Literals)) -$(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/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/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/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/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/class.html, 类) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.cn/inheritance.html, Inheritance) $(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/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/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/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/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)) -) - -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/ddili/src/ders/d.cn/index_section_head.html b/ddili/src/ders/d.cn/index_section_head.html deleted file mode 100644 index 862063a..0000000 --- a/ddili/src/ders/d.cn/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Index

    diff --git a/ddili/src/ders/d.cn/index_section_tail.html b/ddili/src/ders/d.cn/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.cn/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.cn/input.cozum.d b/ddili/src/ders/d.cn/input.cozum.d deleted file mode 100644 index d9e7384..0000000 --- a/ddili/src/ders/d.cn/input.cozum.d +++ /dev/null @@ -1,14 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU 从标准输入中读取数据) - -$(P -当字符无法被转换为所求的类型,$(C stdin) 即进入不可用的状态。例如在需求 $(C int) 值时输入“abc”将使得 $(C stdin) 不可用。 -) - -Macros: - SUBTITLE=从标准输入中读取数据 - - DESCRIPTION=Programming in D 练习解答:从标准输入中读取数据 - - KEYWORDS=programming in d 教程 读取 stdin 解答 diff --git a/ddili/src/ders/d.cn/input.d b/ddili/src/ders/d.cn/input.d deleted file mode 100644 index d60660f..0000000 --- a/ddili/src/ders/d.cn/input.d +++ /dev/null @@ -1,237 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX input) 从标准输入中读取数据) - -$(P -程序读取的任何数据首先必须要存储在一个变量中。比如,一个从输入中读取学生人数的程序必须存储此信息在一个变量中。这个特定变量的类型可以是 $(C int)。 -) - -$(P -正如在前面的章节所看到的,当打印信息到输出时,我们不需要键入 $(C stdout)。因为它是隐含的。此外,要打印的信息会被指定为参数。所以,$(C write(studentCount)) 语句足够打印出 $(C studentCount) 的值了。概述如下: -) - -$(MONO -stream: stdout -operation: write -data: the value of the studentCount variable -target: commonly the terminal window -) - -$(P -$(IX readf) $(C write) 的相反操作是 $(C readf);它会从标准输入中读取数据。该函数名字中的 "f" 来源于 "formatted",它表示总是按照一个特定格式读取数据。 -) - -$(P -从前面的章节中,我们也知道了标准输入流是 $(C stdin)。 -) - -$(P -在读取数据时,还有一个令人困惑的事情:把数据存在哪儿呢。概述如下: -) - -$(MONO -stream: stdin -operation: readf -data: some information -target: ? -) - -$(P -可以指定一个变量的地址作为存储数据的位置。变量的地址指的是数据存储在计算机内存中的精确位置。 -) - -$(P -$(IX &, address of) 在 D 中,某个名字前面的 $(C &) 字符表示该名字代表的地址。举个例子,$(C studentCount) 变量的地址就是 $(C &studentCount)。这里,$(C &studentCount) 可以解读为“$(C studentCount) 的地址",同时也用于代替上面问号处缺失的部分: -) - -$(MONO -stream: stdin -operation: readf -data: some information -target: the location of the studentCount variable -) - -$(P -在某个名字前面键入 $(C &) 表示 $(I 指向) 该名字代表处。这个概念是稍后章节中引用和指针的基础。 -) - -$(P -稍后,我将解释 $(C readf) 的使用。现在,让我们先接受 $(C readf) 的第一个参数必须是 $(STRING "%s") 的规则: -) - ---- - readf("%s", &studentCount); ---- - -$(P -$(I $(B 注意:) 正如我在下面解释的那样,大多数情况还必须有一个空格: $(STRING " %s")。) -) - -$(P -$(STRING "%s") 表明数据应该被自动转换成适合该变量类型的方式。举个例子,当读取字符 '4' 和 '2' 到一个 $(C int) 类型的变量时,它们会被转换成整型值 42。 -) - -$(P -下面的程序要求用户输入学生的数量。你必须在打字输入后按下回车键: -) - ---- -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) 跳过空白字符) - -$(P -按下回车键之后,数据就会存储为特殊的编码,并放入 $(C stdin) 流中。这对于检测输入的信息是单行还是多行非常有帮助。 -) - -$(P -尽管有时候有用,但这样的特殊编码通常对于程序来说并不重要,而且必须从输入中过滤掉。否则的话,它们会 $(I 阻塞) 输入,防止读取其它数据。 -) - -$(P -要在程序中看到这个 $(I 问题),让我们还从输入中读取教师的数量: -) - ---- -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 -很不幸,程序在期望一个 $(C int) 值时,不能用特殊编码: -) - -$(SHELL -How many students are there? 100 -How many teachers are there? 20 - $(SHELL_NOTE_WRONG An exception is thrown here) -) - -$(P -虽然用户输入了教师数量 20,但用户前面输入 100 时按下了回车键。这个特殊编码仍然在输入流中,并阻塞了该输入流。出现在输入流中的字符跟下面的表示很类似: -) - -$(MONO -100$(HILITE [EnterCode])20[EnterCode] -) - -$(P -我对阻塞输入的 Enter code 进行了高亮显示。 -) - -$(P -解决方案是在 $(STRING %s) 前面使用一个空格字符,以表明出现在读取教师数量之前的 Enter code 并不重要: $(STRING " %s")。格式化字符串中的空格用于读取并忽略 0 个或多个隐藏在输入中的不可见字符。这样的字符包括空格字符,表示回车键的编码,制表符等。它们统一叫做 $(I 空白字符)。 -) - -$(P -作为一个通用规则,从输入中读取的每份数据你都可以使用 $(STRING " %s")。上面的程序应用下面的更改就可以像期望的那样工作了: -) - ---- -// ... - readf(" %s", &studentCount); -// ... - readf(" %s", &teacherCount); -// ... ---- - -$(P -输出: -) - -$(SHELL -How many students are there? 100 -How many teachers are there? 20 -Got it: There are 100 students and 20 teachers. -) - -$(H5 附加信息) - -$(UL - -$(LI -$(IX comment) $(IX /*) $(IX */) 起始于 $(COMMENT //) 的行适用于单行注释。要进行多行注释,可以把多行封装入一对 $(COMMENT /*) 和 $(COMMENT */) 标记中。 - - -$(P -$(IX /+) $(IX +/) 为了能够注释其它注释,可以使用 $(COMMENT /+) 和 $(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 -源代码中的大多数空白都是无关紧要的。把很长的表达式写成多行或者添加额外的空白使得代码更易读是一个良好实践。当然,只要符合编程语言的语法规则,编写程序也可以不用额外的空白: - ---- -import std.stdio;void main(){writeln("Hard to read!");} ---- - -$(P -只有少量空白的源代码非常难读。 -) - -) - -) - -$(PROBLEM_TEK - -$(P -当程序期望整型值时,输入非数字字符。此时就会观察到程序不能正常工作。 -) - -) - - -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/ddili/src/ders/d.cn/is_expr.d b/ddili/src/ders/d.cn/is_expr.d deleted file mode 100644 index 1a34dbc..0000000 --- a/ddili/src/ders/d.cn/is_expr.d +++ /dev/null @@ -1,553 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX is, 表达式) $(CH4 is) 表达式) - -$(P -$(C is) $(I 表达式)与我们之前在 $(LINK2 /ders/d.cn/null_is.html, $(CH4 null) 值与 $(CH4 is) 运算符) 一章中所见的 $(C is) $(I 运算符)在语法或语义上均无任何关联: -) - ---- - a is b // 这是一个运算符,如我们之前所见过的一样 - - is (/* ... */) // 这是一个表达式 ---- - -$(P -$(C is) 表达式是在编译时进行计算的。它生成一个取决于括号中表达式的为0或1的 $(C int) 值。尽管这其中的表达式并非一个逻辑表达式,$(C is) 表达式自身却被用作一个编译期的逻辑表达式。这在被用于 $(C static if) 条件语句或模板约束时特别有用。 -) - -$(P -它接受的条件总是关乎类型,这些条件必须以几种语法中的一种来书写。 -) - -$(H5 $(C is ($(I T)))) - -$(P -确定 $(C T) 是否是一个有效的类型。 -) - -$(P -当前举出这个用法的例子是比较困难的。而在之后有关模板参数的章节里它会被充分利用。 -) - ---- - static if (is (int)) { - writeln("valid"); - - } else { - writeln("invalid"); - } ---- - -$(P -上面的 $(C int) 是一个有效的类型: -) - -$(SHELL_SMALL -valid -) - -$(P -另一个例子中,由于 $(C void) 不能作为关联数组键值的类型,下面的 $(C else) 语句块即被启用: -) - ---- - static if (is (string[void])) { - writeln("valid"); - - } else { - writeln("invalid"); - } ---- - -$(P -输出为: -) - -$(SHELL_SMALL -invalid -) - - -$(H5 $(C is ($(I T Alias)))) - -$(P -与上一种语法效果相同,并附带着将 $(C NewAlias) 定义为了 $(C T) 的别名: -) - ---- - static if (is (int NewAlias)) { - writeln("valid"); - NewAlias var = 42; // int 与 NewAlias 相一致 - - } else { - writeln("invalid"); - } ---- - -$(P -正如下面我们将看到的,像这样的别名在更复杂的 $(C is) 表达式中特别有用。 -) - -$(H5 $(C is ($(I T) : $(I OtherT)))) - -$(P -判别 $(C T) 是否能自动转换为 $(C OtherT)。 -) - -$(P -这在之前 $(LINK2 /ders/d.cn/cast.html, 类型转换) 一章中被用于检测自动类型转换,亦可用于判别之前 $(LINK2 /ders/d.cn/inheritance.html, 继承) 一章中“这个类型根本上是那个类型”一类的关系。 -) - ---- -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))) { - // 如果可以执行到此,那么 T 便能像 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 -若 $(C myFunction()) 以一个能像 $(C Clock) 一样使用的类型实例化,参数的成员函数 $(C tellTime()) 即被调用,否则就编译 $(C else) 语句块: -) - -$(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 -与上一种语法效果相同,并附带着将 $(C Alias) 定义为了 $(C T) 的别名。 -) - -$(H5 $(C is ($(I T) == $(I Specifier)))) - -$(P -判断 $(C T) 是否与 $(C Specifier) $(I 是同一个类型)或与这个限定符相匹配。 -) - -$(H6 是否为同一个类型) - -$(P -如果我们在前一个例子中使用 $(C ==) 而非 $(C :),那么 $(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 -尽管 $(C AlarmClock) $(I 是一个) $(C Clock),但是它并不完全与 $(C Clock) 相同。因此,现在 $(C AlarmClock) 和 $(C int) 都不满足条件: -) - -$(SHELL_SMALL -This is not a Clock -This is not a Clock -) - -$(H6 是否与相同的限定符相匹配) - -$(P -若 $(C Specifier) 是如下关键字之一,那么这种对 $(C is) 的运用便是在判断类型是否与该限定符相匹配(其中一些关键字我们在以后的章节中才会遇到): -) - -$(UL -$(LI $(IX struct, is 表达式) $(C struct)) -$(LI $(IX union, is 表达式) $(C union)) -$(LI $(IX class, is 表达式) $(C class)) -$(LI $(IX interface, is 表达式) $(C interface)) -$(LI $(IX enum, is 表达式) $(C enum)) -$(LI $(IX function, is 表达式) $(C function)) -$(LI $(IX delegate, is 表达式) $(C delegate)) -$(LI $(IX const, is 表达式) $(C const)) -$(LI $(IX immutable, is 表达式) $(C immutable)) -$(LI $(IX shared, is 表达式) $(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 -函数模板能利用此类信息以做出取决于用于实例化该模板的不同类型的不同表现。如下代码演示了上述的模板中的不同代码块是如何针对不同类型进行编译的: -) - ---- - auto var = new AlarmClock; - myFunction(var); - - // (枚举 WeekDays 定义于下面的例子中) - myFunction(WeekDays.Monday); - - const double number = 1.2; - myFunction(number); - - myFunction(42); ---- - -$(P -输出为: -) - -$(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 表达式) -$(IX return, is 表达式) -$(IX __parameters, is 表达式) -与上一种语法效果相同。而 $(C identifier) 要么是此类型的一个别名,要么是取决于 $(C Specifier) 的某些信息: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $(C Specifier)$(C identifier) 的含义
    $(C struct)满足条件类型的别名
    $(C union)满足条件类型的别名
    $(C class)满足条件类型的别名
    $(C interface)满足条件类型的别名
    $(C super)一个包含了所有基类与界面的$(I 元组)
    $(C enum)枚举的实际实现类型
    $(C function)一个包含了函数所有参数的$(I 元组)
    $(C delegate)$(C delegate) 的类型
    $(C return)普通函数、函数指针或 $(C delegate) 返回值的类型
    $(C __parameters)一个包含普通函数、函数指针或 $(C delegate) 所有参数的$(I 元组)
    $(C const)满足条件类型的别名
    $(C immutable)满足条件类型的别名
    $(C shared)满足条件类型的别名
    - -$(P -让我们在试验这个语法之前先定义一系列的类型: -) - ---- -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 -如下函数模板以不同的限定符来运用 $(C is) 表达式的这种语法: -) - ---- -void myFunction(T)(T parameter) { - static if (is (T LocalAlias == struct)) { - writefln("\n--- struct ---"); - // LocalAlias 与 T 一样。 - // “parameter”是传入本函数的 struct 对象。 - - writefln("Constructing a new %s object by copying it.", - LocalAlias.stringof); - LocalAlias theCopy = parameter; - } - - static if (is (T baseTypes == super)) { - writeln("\n--- super ---"); - // “baseTypes”元组包含了所有 T 的基类。 - // “parameter”是传入本函数的 class 变量。 - - 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”是该枚举类型的实际实现类型。 - // “parameter”是传入本函数的枚举值。 - - writefln("The implementation type of enum %s is %s", - T.stringof, ImplT.stringof); - } - - static if (is (T ReturnT == return)) { - writeln("\n--- return ---"); - // “ReturnT”是传入本函数的函数指针的返回值类型。 - - writefln("This is a function with a return type of %s:", - ReturnT.stringof); - writeln(" ", T.stringof); - write("calling it... "); - - // 注:调用函数指针的方式与调用函数一样 - ReturnT result = parameter(1.5, 42, new AlarmClock); - writefln("and the result is '%s'", result); - } -} ---- - -$(P -现在以我们上面定义的类型来调用这个函数模板: -) - ---- - // 用 struct 对象调用 - myFunction(Point()); - - // 用 class 引用调用 - myFunction(new AlarmClock); - - // 用枚举值调用 - myFunction(WeekDays.Monday); - - // 用函数指针调用 - myFunction(&foo); ---- - -$(P -输出为: -) - -$(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 -共有四种使用模板参数列表的 $(C is) 表达式语法: -) - -$(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 -这些语法允许了更复杂的情况。 -) - -$(P -这里的 $(C identifier)、$(C Specifier)、$(C :) 以及 $(C ==) 都和上面所描述的含义一样。 -) - -$(P -$(C TemplateParamList) 均是需要被满足的条件的一部分,亦是在条件完全被满足时定义附加别名的措施。它与推导模板类型的方式是一样的。 -) - -$(P -作为一个简单的例子,让我们假定一个 $(C is) 表达式需要与键值类型为 $(C string) 的关联数组相匹配: -) - ---- - static if (is (T == Value[Key], // (1) - Value, // (2) - Key : string)) { // (3) ---- - -$(P -这个条件可以分为三个部分来阐释,而后两个部分就是 $(C TemplateParamList) 的一部分: -) - -$(OL -$(LI 若 $(C T) 匹配于语法 $(C Value[Key])) -$(LI 若 $(C Value) 是一个类型) -$(LI 若 $(C Key) 是 $(C string)(想想 $(LINK2 /ders/d.cn/templates.html, 模板特化语法))) -) - -$(P -以 $(C Value[Key]) 作为 $(C Specifier) 要求 $(C T) 是一个关联数组。让 $(C Value) 单独出现则意味着它可以是任何类型。并且该关联数组键值的类型必须为 $(C string)。由是,之前的那个 $(C is) 表达式便表示“$(C T) 是否为一个键值类型为 $(C string) 的关联数组”。 -) - -$(P -如下的程序用四个不同的类型来测试该 $(C is) 表达式: -) - ---- -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 -当且仅当键值类型为 $(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 表达式 - - DESCRIPTION=is 表达式是D语言的自检功能之一。 - - KEYWORDS=D 编程语言教程 is 表达式 diff --git a/ddili/src/ders/d.cn/lazy_operators.d b/ddili/src/ders/d.cn/lazy_operators.d deleted file mode 100644 index b42ebe2..0000000 --- a/ddili/src/ders/d.cn/lazy_operators.d +++ /dev/null @@ -1,92 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX 惰性求值) 惰性运算符) - -$(P -惰性求值即推迟表达式的执行,直到需要表达式的值。惰性求值是许多语言的基础特性。 -) - -$(P -如果结果最终并不需要,延迟求值自然可以让程序运行得更快。 -) - -$(P -$(IX 短路求值) 下面的运算符短路行为与惰性求值是相似的概念: -) - -$(UL - -$(LI $(IX ||, 短路) $(IX 逻辑或运算符) $(C ||) ($(I 或))运算符:第二个表达式只有在第一个表达式为 $(C false) 时才会执行。 - ---- - if (anExpression() || mayNotBeEvaluated()) { - // ... - } ---- - -$(P -如果 $(C anExpression()) 的结果是 $(C true), 那么 $(C ||) 表达式的结果就一定是 $(C true)。因为我们不需要对第二个表达式求值来确定 $(C ||) 表达式的结果,所以第二个表达式不会被求值。 -) - -) - -$(LI $(IX &&, 短路) $(IX 逻辑与运算符) $(C &&) ($(I 与))运算符:第二个表达式只有在第一个表达式为 $(C true) 时才会执行。 - ---- - if (anExpression() && mayNotBeEvaluated()) { - // ... - } ---- - -$(P -如果 $(C anExpression()) 的结果是 $(C false),那么 $(C &&) 表达式的结果必然是 $(C false),因此第二个表达式不会被求值。 -) - -) - -$(LI $(IX ?:, 短路) $(IX 三元运算符) $(IX 条件运算符) $(C ?:) ($(I 三元))运算符:第一个或第二个表达式是否被执行分别依赖于条件是 $(C true) 还是 $(C false)。 - ---- - int i = condition() ? eitherThis() : orThis(); ---- - -) - -) - -$(P -这些运算符的惰性不仅仅是与性能相关。有时,执行表达式会发生错误。 -) - -$(P -例如,如果字符串是空的,下面的条件$(I 第一个字符是 A) 会出错: -) - ---- - dstring s; - // ... - if (s[0] == 'A') { - // ... - } ---- - -$(P -为了正确的访问 $(C s) 的第一个元素,我们首先要确认字符串里的确有那个元素。因此,下面的条件把有潜在错误的逻辑表达式放在了 $(C &&) 运算符的右边,确保其只有在安全的情况下才执行: -) - ---- - if ((s.length >= 1) && (s[0] == 'A')) { - // ... - } ---- - -$(P -惰性求值可以通过 $(LINK2 /ders/d.cn/lambda.html, 函数指针, delegate) 完成,也可以用 $(LINK2 /ders/d.cn/ranges.html, range)。我们会在后面的章节见到这些。 -) - -Macros: - SUBTITLE=惰性运算符 - - DESCRIPTION=D 编程语言中的惰性(短路)运算符。 - - KEYWORDS=d 编程 语言 教程 惰性 lazy diff --git a/ddili/src/ders/d.cn/lvalue_rvalue.d b/ddili/src/ders/d.cn/lvalue_rvalue.d deleted file mode 100644 index b2d6fab..0000000 --- a/ddili/src/ders/d.cn/lvalue_rvalue.d +++ /dev/null @@ -1,203 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX 左值) $(IX 右值) 左值与右值) - -$(P -$(IX 表达式, 左值与右值) 任何表达式的值都可以用左值或右值归类。要区分他们可以简单地认为左值是真实变量(包括数组和关联数组的元素),而右值是表达式(包括文字量)的临时结果。 -) - -$(P -例如,下面的第一个 $(C writeln()) 表达式只使用了左值,其他的只使用了右值: -) - ---- -import std.stdio; - -void main() { - int i; - immutable(int) imm; - auto arr = [ 1 ]; - auto aa = [ 10 : "ten" ]; - - /* 下面所有参数都是左值。 */ - - writeln(i, // 可变变量 - imm, // 不可变变量 - arr, // 数组 - arr[0], // 数组元素 - aa[10]); // 关联数组元素 - // 等 - - enum message = "hello"; - - /* 下面所有参数都是右值。 */ - - writeln(42, // 一个文字量 - message, // 显然是一个常量 - i + 1, // 一个临时值 - calculate(i)); // 函数的返回值 - // 等 -} - -int calculate(int i) { - return i * 2; -} ---- - -$(H5 右值的限制) - -$(P -相对于左值,右值有下面三条限制。 -) - -$(H6 右值没有内存地址) - -$(P -左值有可以引用的内存地址,但右值没有。 -) - -$(P -例如,下面的程序里不可能取到右值表达式 $(C a + b) 的值: -) - ---- -import std.stdio; - -void main() { - int a; - int b; - - readf(" %s", &a); $(CODE_NOTE 编译通过) - readf(" %s", &(a + b)); $(DERLEME_HATASI) -} ---- - -$(SHELL -Error: a + b $(HILITE is not an lvalue) -) - -$(H6 右值不能被赋新值) - -$(P -左值如果为可变,则可以被赋予新值,但右值却不行: -) - ---- - a = 1; $(CODE_NOTE 编译通过) - (a + b) = 2; $(DERLEME_HATASI) ---- - -$(SHELL -Error: a + b $(HILITE is not an lvalue) -) - -$(H6 右值不能通过引用方式传递给函数) - -$(P -左值能传递给一个接受引用参数的函数,但右值不能: -) - ---- -void incrementByTen($(HILITE ref int) value) { - value += 10; -} - -// ... - - incrementByTen(a); $(CODE_NOTE 编译通过) - incrementByTen(a + b); $(DERLEME_HATASI) ---- - -$(SHELL -Error: function deneme.incrementByTen (ref int value) -$(HILITE is not callable) using argument types (int) -) - -$(P -有这个限制的主要原因是接受一个引用参数的函数可以保持这个引用在稍后使用,而在那个时间点右值却已经失效了。 -) - -$(P -与如 C++ 等语言不同,在 D 中就算函数$(I 不)改变实参,右值也不能传递给函数: -) - ---- -void print($(HILITE ref const(int)) value) { - writeln(value); -} - -// ... - - print(a); $(CODE_NOTE 编译通过) - 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, 参数) $(IX 参数, auto ref) 使用 $(C auto ref) 参数可以同时接受左值和右值) - -$(P -如前面章节中提到的,$(LINK2 /ders/d.cn/templates.html, 函数模板) 的 $(C auto ref) 参数可以同时接受左值和右值。 -) - -$(P -当参数是一个左值时,$(C auto ref) 表示$(I 传引用)。另一方面,因为右值不能通过引用传给函数,所以当参数是一个右值时,它表示是$(I 传值)。由于编译器要为两种不同的情况生成不同的代码,因此这个函数必须是一个模板。 -) - -$(P -我们会在后面的章节中看到模板。现在,请先了解下面高亮的括号让这个定义变成了一个$(I 函数)模板。 -) - ---- -void incrementByTen$(HILITE ())($(HILITE auto ref) int value) { - /* 警告:如果实参是一个右值则形参会是一个拷贝。也就是说下面的更改对 - * 对调用者来说不可见。 */ - - value += 10; -} - -void main() { - int a; - int b; - - incrementByTen(a); $(CODE_NOTE 左值;引用传递) - incrementByTen(a + b); $(CODE_NOTE 右值;拷贝) -} ---- - -$(P -正如上面代码注释提到的,对形参的改动可能会对调用者不可见。因此,$(C auto ref) 最常用于形参不会被改变的场景;通常写作 $(C auto ref const)。 -) - -$(H5 术语) - -$(P -“左值(lvalue)”和“右值(rvalue)”这两个名字并不能准确地表示两种值的特性。第一个字母 $(I l) 和 $(I r) 分别源自$(I 左(left))和$(I 右(right)),表示赋值运算符左边或右边表达式: -) - -$(UL - -$(LI 左值如果可变,则可以是赋值运算左边的表达式。) - -$(LI 右值不能是赋值运算左边的表达式。) - -) - -$(P -术语“左值”和“右值”会有些混淆,因为通常左值和右值都可以出现在赋值运算的任何一边: -) - ---- - // 右值“a + b”在左边,左值“a”在右边: - array[a + b] = a; ---- - -Macros: - SUBTITLE=左值与右值 - - DESCRIPTION=左值与右值及它们的区别。 - - KEYWORDS=d 编程 语言 教程 左值 右值 diff --git a/ddili/src/ders/d.cn/name_space.d b/ddili/src/ders/d.cn/name_space.d deleted file mode 100644 index 002c84f..0000000 --- a/ddili/src/ders/d.cn/name_space.d +++ /dev/null @@ -1,143 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX name scope) 命名作用域) - -$(P -任何命名,从定义它开始,到它作用域结束之前,包括其所包含的作用域在内,都是可以访问它的。从这一方面来说,每个作用域都定义了一个$(I 命名作用域)。 -) - -$(P -然而命名在其作用域结束之后,就不能被访问了: -) - ---- -void main() { - int outer; - - if (aCondition) $(HILITE {) // ← 大括号新建了一个作用域 - int inner = 1; - outer = 2; // ← 'outer' 是可以被访问的 - - $(HILITE }) // ← 从这里开始 'inner' 就不能被访问了 - - inner = 3; $(DERLEME_HATASI) - // 在 'inner' 的作用域之外,就不能访问它了 -} ---- - -$(P -因为 $(C inner) 是在 $(C if) 判断的作用域内定义的,所以只能在这里访问到它。还有一点就是,在 outer 和 inner 的作用域内 $(C outer) 都是可以访问到的。 -) - -$(P -在一个内部作用域,不允许定义相同的命名: -) - ---- - size_t $(HILITE length) = oddNumbers.length; - - if (aCondition) { - size_t $(HILITE length) = primeNumbers.length; $(DERLEME_HATASI) - } ---- - -$(H5 定义命名,要在距第一次使用最近的地方) - -$(P -就像我们以往写过的程序那样,变量必须被定义在第一次使用之前: -) - ---- - writeln(number); $(DERLEME_HATASI) - // number 还没有被定义 - int number = 42; ---- - -$(P -为了让上面的代码通过编译,$(C number) 必须在 $(C writeln) 调用之前被定义。虽然没有限制,让你把它定义到之前的哪个地方,但是有一个普遍觉得良好的习惯,就是把变量定义在,距它们第一次被使用最近的地方。 -) - -$(P -我们来看一个这样的程序,它接收用户输入的一些数字,然后输出它们的平均值。一些有经验的开发人员,会把变量定义在作用域最上面的地方: -) - ---- - int count; // ← 比如这里 - int[] numbers; // ← 还有这里 - double averageValue; // ← 最后还有这个地方 - - write("How many numbers are there? "); - - readf(" %s", &count); - - if (count >= 1) { - numbers.length = count; - - // ... 假设计算的部分在这里 ... - - } else { - writeln("ERROR: You must enter at least one number!"); - } ---- - -$(P -我们把下面的程序跟上面的比较一下,上面的每个变量都应用到了这个程序中: -) - ---- - write("How many numbers are there? "); - - int count; // ← 比如这里 - readf(" %s", &count); - - if (count >= 1) { - int[] numbers; // ← 还有这里 - numbers.length = count; - - double averageValue; // ← 最后还有这个地方 - - // ... 假设计算的部分在这里 ... - - } else { - writeln("ERROR: You must enter at least one number!"); - } ---- - -$(P -尽管把变量定义在最上面,在结构上看起来更好些,不过尽可能晚些定义变量有如下几点好处: -) - -$(UL -$(LI $(B 性能:)每个变量在被定义的时候,都会对程序造成少量的开销。由于 D 语言里面,每个变量都会被初始化,在顶部定义变量,会导致它们始终都被初始化,甚至它们只是在之后的某些时间才会被使用,这样的话就浪费了资源。 -) - -$(LI $(B 出错的风险:)对于定义和使用变量来说,每行代码都会带来一些出错的风险。比如,一个变量的名字使用了一个常见的命名 $(C length)。我们很容易把它跟其它的 length 变量混淆,有可能在我们打算第一次使用它之前,就已经不经意地使用了它。然后当程序使用这个变量的时候,它的值早已不是我们所期望的了。 -) - -$(LI $(B 可读性:)随着作用域内的代码行数增加,我们所定义的变量可能就会离得越来越远,这样的话就会避免强制开发人员,为了查看变量的定义就要回滚到开始的地方。 -) - -$(LI $(B 代码维护:)源代码都会被持续地进行调整和改进:添加新的、移除旧的特性,修复 bug 等等。对于这些改动来说,有的时候把一些代码封装到一个函数中是很有必要的。 - -$(P -如果是这种情况的话,在行内定义变量就会让这些工作实施得更加容易和清晰。 -) - -$(P -比如,在上文提到的后续代码中,在这个程序里面,$(C if) 判断内部的所有代码,都可以被封装到一个新的函数中。 -) - -$(P -另外,在最上面声明变量的时候,如果某些代码需要被移走,那么这里涉及到的变量每一个都需要确认和检查。 -) - -) - -) - -Macros: - SUBTITLE=命名作用域 - - DESCRIPTION=程序当中,有效命名且可访问的作用域,以及把命名定义在,距第一次使用最近地方的好处。 - - KEYWORDS=d 编程语言教程 name scopes diff --git a/ddili/src/ders/d.cn/object.d b/ddili/src/ders/d.cn/object.d deleted file mode 100644 index f0ce769..0000000 --- a/ddili/src/ders/d.cn/object.d +++ /dev/null @@ -1,861 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX Object) $(CH4 Object)) - -$(P -没有明确地继承自任何基类的类自动继承自 $(C Object) 类。 -) - -$(P -根据这一定义, 任何类继承结构中的最顶端基类继承自 $(C Object): -) - ---- -// “: Object”没有写;它是自动的 -class MusicalInstrument $(DEL : Object ) { - // ... -} - -// 间接继承自 Object -class StringInstrument : MusicalInstrument { - // ... -} ---- - -$(P -由于顶端类继承自 $(C Object),每个类也间接继承自 $(C Object)。 从某种意义上来讲, 每个类“是一个”$(C Object)。 -) - -$(P -每个类继承 $(C Object) 的成员函数: -) - -$(UL -$(LI $(C toString):对象的 $(C string) 表示。) -$(LI $(C opEquals):与另一对象的相等性比较。) -$(LI $(C opCmp): 与另一个对象的排序顺序比较。) -$(LI $(C toHash):关联数组的Hash值。) -) - -$(P -后三个函数强调对象的值。 他们还让一个类有资格作为关联数组的键类型。 -) - -$(P -因为这些函数是继承的, 所以子类重新定义的时候需要关键字 $(C override)。 -) - -$(P $(I $(B 注:)$(C Object) 也定义了别的成员。 这章将只涉及上面所提到的四个成员函数。) -) - -$(H5 $(IX typeid) $(IX TypeInfo) $(C typeid) 和 $(C TypeInfo)) - -$(P -$(C Object) 的定义在文件 $(LINK2 http://dlang.org/phobos/object.html, $(C object) 模块) (它不是 $(C std) 包的一部分)。该 $(C object) 模块定义 $(C TypeInfo) ,它是一个提供有关类型的信息的类。 每个类型具有不同的 $(C TypeInfo) 对象。 $(C typeid) $(I expression) 提供对与特定类型有关联的 $(C TypeInfo) 对象的访问。 随后我们将看到,$(C TypeInfo) 类能用于确定两个类型是否相同, 以及用于访问类型的特殊函数 ($(C toHash)、 $(C postblit) 等等, 其中的大部分本书并不包括)。 -) - -$(P -$(C TypeInfo) 通常是指实际运行时类型。例如, 虽然 $(C Violin) 和 $(C Guitar) 都直接继承自 $(C StringInstrument) 并间接继承自 $(C MusicalInstrument), 但是 $(C Violin) 和 $(C Guitar) 的$(C TypeInfo) 不一样。它们恰恰是 $(C Violin) 和 $(C Guitar) 类型, 分别为: -) - ---- -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 这两个类型不同) -} ---- - -$(P -上面的$(C typeid) 表达式用于像 $(C Violin) 这样的 $(I types) 自身。 $(C typeid) 也能带出一个 $(I expression),这种情况下,它为那个表达式的运行时类型返回 $(C TypeInfo) 对象。 举例来说,下列的函数有两个不同参数但类型相关: -) - ---- -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 -虽然 $(C foo())调用的两个参数都是 $(C Violin) 对象, $(C foo()) 决定它们的类型相同: -) - -$(SHELL -The types of the arguments are $(HILITE the same). -) - -$(P -不像 $(C .sizeof) 和 $(C typeof), 从来不执行它们的表达式, $(C typeid) 总是执行它接收的表达式: -) - ---- -import std.stdio; - -int foo(string when) { - writefln("Called during '%s'.", when); - return 0; -} - -void main() { - const s = foo("sizeof")$(HILITE .sizeof); // foo() 不被调用 - alias T = $(HILITE typeof)(foo("typeof")); // foo() 不被调用 - auto ti = $(HILITE typeid)(foo("typeid")); // foo() 被调用 -} ---- - -$(P -输出表明只有含$(C typeid)的表达式被执行: -) - -$(SHELL -Called during 'typeid'. -) - -$(P -不同的原因是表达式的实际运行时类型只在表达式被执行时才能知道。举例来说,下面函数返回的准确类型将是 $(C Violin) 还是 $(C Guitar) 取决于参数的实际值: -) - ---- -MusicalInstrument foo(int i) { - return ($(HILITE i) % 2) ? new Violin() : new Guitar(); -} ---- - -$(H5 $(IX toString) $(C toString)) - -$(P -跟结构一样, $(C toString) 也能把类对象用作字符串: -) - ---- - auto clock = new Clock(20, 30, 0); - writeln(clock); // 调用 clock.toString() ---- - -$(P -继承的 $(C toString()) 通常没用;它只产生类型的名称: -) - -$(SHELL -deneme.Clock -) - -$(P -类型名称的前部是模块名称。 上面的输出表明 $(C Clock) 已被定义在 $(C deneme) 模块。 -) - -$(P -在前面的章节我们已经看到,为产生更有意义的$(C string)表达,函数总是被重写: -) - ---- -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 -输出: -) - -$(SHELL -20:30:00 ♫07:00 -) - -$(H5 $(IX opEquals) $(C opEquals)) - -$(P -如我们在 $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) 一章中看到的一样,这个成员函数是关于 $(C ==) 运算符(和 $(C !=) 不等运算符)的行为。如果对象被认为是相等的,运算符的返回值是 $(C true) 否则为 $(C false)。 -) - -$(P -$(B 注意:)该函数的定义必须符合 $(C opCmp());对两个对象 $(C opEquals()) 返回 $(C true), $(C opCmp()) 一定返回0。 -) - -$(P -跟结构相反, 编译器见到 $(C a == b)时不立即调用 $(C a.opEquals(b))。 当两个对象用 $(C ==) 运算符比较时, 将执行一个四步算法: -) - ---- -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 如果两个变量提供对同一对象的访问 (或者他们都是 $(C null)), 那么它们相等。) - -$(LI 紧接着前面的检查,如果仅有一个是 $(C null) 那么它们不等。) - -$(LI 如何两个对象类型相同, 那么 $(C a.opEquals(b)) 被调用来决定相等。) - -$(LI 另外,对两个被认为相等的对象,一定曾经为它们的类型以及 $(C a.opEquals(b)) 和 $(C b.opEquals(a)) 定义了 $(C opEquals),一定同意对象是相等的。) - -) - -$(P -因此, 如果一个类没有提供 $(C opEquals()),那么对象的值就不被考虑;然而,相等性决定于检查两个类变量是否访问同一对象: -) - ---- - auto variable0 = new Clock(6, 7, 8); - auto variable1 = new Clock(6, 7, 8); - - assert(variable0 != variable1); // 它们不相等 - // 因为对象 - // 不同 ---- - -$(P -即使这两个对象构造于上面的同一参数,因为不与同一对象关联,所以变量不相等。 -) - -$(P -另一方面,因为下面两个变量访问同一对象,所以它们是 $(I equal): -) - ---- - auto partner0 = new Clock(9, 10, 11); - auto partner1 = partner0; - - assert(partner0 == partner1); // 它们是相等的,因为 - // 是同一个对象 ---- - -$(P -有时它给人更多的感觉是比较对象的值而不是它们的身份。举例来说, 可以想到,上面的 $(C variable0) 和 $(C variable1) 比较的结果是相等的,因为它们的值一样。 -) - -$(P -与结构不同, $(C opEquals) 对类的参数类型为 $(C Object): -) - ---- -class Clock { - override bool opEquals($(HILITE Object o)) const { - // ... - } - - // ... -} ---- - -$(P -正如您将在下面看到的,该参数几乎从未直接使用。出于这个原因,将它简单的命名为 $(C o),应该是可以接受的。大部分时间,该参数所做的第一件事是在类型转换中使用它。 -) - -$(P -$(C opEquals) 的参数是在$(C ==) 运算符的右手侧出现的对象: -) - ---- - variable0 == variable1; // o 表示 variable1 ---- - -$(P -由于 $(C opEquals()) 的目的是比较类类型的两个对象,要做的第一件事是转换 $(C o) 为该类的同类型的一个变量。因为在一个相等性的比较中,修改右手侧的对象是不恰当的,所以转换它的类型为 $(C const) 也是合适的: -) - ---- - override bool opEquals(Object o) const { - auto rhs = cast(const Clock)o; - - // ... - } ---- - -$(P -正如您所记得的,$(C rhs) 是 $(I right-hand side) 的通用缩写。同时,$(C std.conv.to) 用于转换: -) - ---- -import std.conv; -// ... - auto rhs = to!(const Clock)(o); ---- - -$(P -如果在右手侧上的原始对象可转换为 $(C Clock),则 $(C rhs) 变为非 $(C null) 类变量。否则,$(C rhs) 被设置为 $(C null),这表明这俩对象不是同一类型。 -) - -$(P -根据程序的设计,它给人的感觉就像比较两个不相干的类型。在这儿我假设比较有效,$(C rhs) 不能是 $(C null);因此,在下面的第一个逻辑表达式 $(C return) 语句检查,它非 $(C null)。否则,尝试访问 $(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 -根据定义, $(C Clock) 对象现在能根据它们的值做比较: -) - ---- - auto variable0 = new Clock(6, 7, 8); - auto variable1 = new Clock(6, 7, 8); - - assert(variable0 == variable1); // 现在它们相等 - // 因为它们的值 - // 相等 ---- - -$(P -当定义 $(C opEquals) 时一定要记住,超类的成员是非常重要的。 例如,当比较 $(C AlarmClock) 时,给人感觉就是它也考虑到了继承的成员: -) - ---- -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 -表达式将会写成 $(C super == o) 这样。然而,这将再次开始一个四步算法,结果就是代码可能有点慢。 -) - -$(H5 $(IX opCmp) $(C opCmp)) - -$(P -这个运算符会在排序类对象时用到。$(C opCmp) 函数将在后面 $(C <), $(C <=), $(C >), and $(C >=) 这些场景中被调用。 -) - -$(P -当左手侧对象在前时,该运算符返回负值,在后时,返回正值。当两个对象具有一致的排序顺序时为零。 -) - -$(P -$(B 注意:)函数的定义一定与 $(C opEquals()) 一致的;对象间的 $(C opEquals()) 返回 $(C true), $(C opCmp()) 一定返回0。 -) - -$(P -不像 $(C toString) 和 $(C opEquals),$(C Object) 里没有这个函数的默认实现。如果实现不可用,为排序而比较对象会抛出异常: -) - ---- - 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 -当左、右手侧对象的类型不同时,它取决于程序的设计怎么做。一种方式是利用编译器自动保持的类型排序。这通过对两个类型的 $(C typeid) 值调用 $(C opCmp) 函数来实现: -) - ---- -class Clock { - int hour; - int minute; - int second; - - override int opCmp(Object o) const { - /* 利用自动保持的 - * 类型排序。 */ - if (typeid(this) != typeid(o)) { - return typeid(this).opCmp(typeid(o)); - } - - auto rhs = cast(const Clock)o; - /* 没有必要检查 rhs 是 null, 因为已经知道 - * 它与 o 具有同一类型。*/ - - if (hour != rhs.hour) { - return hour - rhs.hour; - - } else if (minute != rhs.minute) { - return minute - rhs.minute; - - } else { - return second - rhs.second; - } - } - - // ... -} ---- - -$(P -上面的定义首先检查两个对象的类型是否一致。若不一致,它使用他们自己的类型顺序。否则,它通过它们的 $(C hour), $(C minute), and $(C second) 成员值来比较对象。 -) - -$(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 -如果重要,也一定要考虑比较成员的超类。下面 $(C AlarmClock.opCmp) 先被 $(C Clock.opCmp) 调用: -) - ---- -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 -如前,如果超类的比较返回一个非零值,因为对象的排序已经由值决定,那结果就可用。 -) - -$(P -$(C AlarmClock) objects 因为它们的排序顺序现在可以比较: -) - ---- - auto ac0 = new AlarmClock(8, 0, 0, 6, 30); - auto ac1 = new AlarmClock(8, 0, 0, 6, 31); - - assert(ac0 < ac1); ---- - -$(P -$(C opCmp) 也被其它语言的特征和库使用。例如,$(C sort()) 函数利用 $(C opCmp) 排序元素。 -) - -$(H6 字符串成员的 $(C opCmp)) - -$(P -当一些成员是字符串时,它们能被明确比较来返回负值,正徝,或零值: -) - ---- -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 -另外,也可以使用现有的 $(C std.algorithm.cmp) 函数,这恐怕更快: -) - ---- -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 -注意,$(C Student) 不支持比较不相容的类型,通过执行由 $(C Object) 到 $(C Student) 的转换,那就可以了。 -) - -$(H5 $(IX toHash) $(C toHash)) - -$(P -该函数允许类的类型的对象做为关联数组的 $(I keys)。 它不影响类型用作关联数组的 $(I values)。 -) - -$(P -$(B 注意:)只定义这个函数是不够的。为了让类类型能被用做关联数组的键,也一定要做 $(C opEquals) 和 $(C opCmp) 的一致性定义。 -) - -$(H6 $(IX hash table) 哈希表索引) - -$(P -关联数组是一个哈希表实现。哈希表是一个在表中搜索元素时速度非常快的数据结构。($(I 注:像软件界大部分别的事情一样,速度决定价值:哈希表一定保持元素无序,而且它们占据超过完全必需的空间。)) -) - -$(P -哈希表的高速来自于它们最初为键产生整数值。这些整数值叫做 $(I hash values). 哈希值被用作索引,放在由表维护的整数数组里。 -) - -$(P -这种方法的好处是能为它们的对象产生唯一整数值的任何类型都能用作关联数组的键类型。$(C toHash) 是为对象返回哈希值的函数。 -) - -$(P -甚至 $(C Clock) 对象能被用作关联数组的键值: -) - ---- - string[$(HILITE Clock)] timeTags; - timeTags[new Clock(12, 0, 0)] = "Noon"; ---- - -$(P -$(C toHash) 的默认定义是继承自 $(C Clock),对不同的对象产生不同的哈希值,不涉及它们的值。这个跟 $(C opEquals) 把不同对象视为不相等的默认行为很相似。 -) - -$(P -对 $(C Clock) 即使没有特殊的 $(C toHash) 定义,上面的代码也编译运行。然而,它的默认行为几乎没有必要。为看到默认行为,当插入元素时,我们来尝试访问一个对象的元素,该对象不同于已有对象。然而,当把下面新的 $(C Clock) 对象插入上面的关联数组时,虽与已有的 $(C Clock) 对象有相同的值,但值还是没找到: -) - ---- - if (new Clock(12, 0, 0) in timeTags) { - writeln("Exists"); - - } else { - writeln("Missing"); - } ---- - -$(P -由 $(C in) 运算符得知,表中没有元素符合 $(C Clock(12, 0, 0)) 的值: -) - -$(SHELL -Missing -) - -$(P -这个让人惊讶的行为的原因是那个插入元素时已有的键对象与访问元素时那个已有的键对象并不相同。 -) - -$(H6 为 $(C toHash) 选取成员) - -$(P -虽然哈希值计算自对象的成员,但并不是每个成员都适合作这个任务。 -) - -$(P -候选成员应能分清各个对象。例如,$(C Student) 中的 $(C name) 和 $(C lastName) 适合作能识别对象类型的成员。 -) - -$(P -另一方面, $(C Student) 类中的 $(C grades) 数组是不适合的,因为好多对象有一样的数组而且 $(C grades) 数组也有可能随着时间的变化而改变。 -) - -$(H6 哈希值的计算) - -$(P -哈希值的选择对关联数组的性能有直接的影响。此外,一个数据类型的哈希计算有效,并不代表另一个同样有效。As $(I hash 算法) 超出了本书的范围,在这儿我将只给一个引导:通常,最好是有不同值的对象产生不同的哈希值。然而,有不同值的对象产生同一索引值并不是一个错误;只是因为性能原因而不受欢迎。 -) - -$(P -可以想到,对于区分不同对象, $(C Clock) 的所有成员都一样。因此,可从三个成员的值来计算哈希值。对于表示不同时间点的对象,$(I 午夜后的秒钟数) 将是有效的哈希值: -) - ---- -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 -只要 $(C Clock) 被用作关联数组的键类型, $(C toHash) 的特殊定义就可用。 因此,尽管上面两个 $(C Clock(12, 0, 0)) 键对象截然不同,它们也将产生同一个哈希值。 -) - -$(P -新的输出: -) - -$(SHELL -Exists -) - -$(P -类似于其它成员函数,可能需要考虑一下超类。例如,在索引计算期间, $(C AlarmClock.toHash) 可以利用 $(C Clock.toHash) : -) - ---- -class AlarmClock : Clock { - int alarmHour; - int alarmMinute; - - override size_t toHash() const { - return $(HILITE super.toHash()) + alarmHour + alarmMinute; - } - - // ... -} ---- - -$(P -$(I $(B 注:)以上所做的计算只是一个例子。一般情况下,填加整数值并不是一种有效的产生哈希值的方式。) -) - -$(P -现有的计算哈希值的高效算法主要是针对浮点变量、数组、结构类型。这些算法对程序员来说是可用的。 -) - -$(P -$(IX getHash) 所作的就是对每个成员的 $(C typeid) 调用 $(C getHash()) 。这种方法的语法对浮点、数组、类结构都是一样的。 -) - -$(P -例如,在下面的代码中, $(C Student) 类型的哈希值可以使用$(C name) 成员来计算: -) - ---- -class Student { - string name; - - override size_t toHash() const { - return typeid(name).getHash(&name); - } - - // ... -} ---- - -$(H6 结构的哈希值) - -$(P -由于结构是值类型,对它们的对象的哈希值通过一个高效算法来自动计算。那个算法把对象的所有成员都考虑进去了。 -) - -$(P -特殊情况下,像哈希计算时需要排除某些确定成员,也可重写结构的 $(C toHash()) 。 -) - -$(PROBLEM_COK - -$(PROBLEM -首先用下面的类表示带颜色的点: - ---- -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 -对该类用忽略颜色的方法实现 $(C opEquals) 。在实现之后,下面的 $(C assert) 检查应该通过: -) - ---- - // 不同的颜色 - auto bluePoint = new Point(1, 2, Color.blue); - auto greenPoint = new Point(1, 2, Color.green); - - // 它们仍然相等 - assert(bluePoint == greenPoint); ---- - -) - -$(PROBLEM -通过先考虑$(C x) 然后 $(C y) 来实现 $(C opCmp) 。随后的 $(C assert) 检查应该通过: - ---- - 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); - - /* 在 enum Color 中,即使 蓝色在绿色之前, - *因为颜色已被忽略,bluePoint 不一定 - * 在greenPoint之前。*/ - assert(!(bluePoint < greenPoint)); ---- - -$(P -像上面的 $(C Student) 类,在 $(C enforce) 的帮助下,通过排除不符合的类型,你能实现 $(C opCmp)。 -) - -) - -$(PROBLEM -考虑一下下面的类,在一个数组中有三个 $(C Point) 对象: - ---- -class TriangularArea { - Point[3] points; - - this(Point one, Point two, Point three) { - points = [ one, two, three ]; - } -} ---- - -$(P -对该类实现 $(C toHash) 。紧接着 $(C assert) 检查应该再次通过: -) - ---- - /* area1 和 area2 由明显不同的点构造 - * 即使有相同的值。(记得 - * bluePoint 和 greenPoint 应该被认为是相等的。) */ - auto area1 = new TriangularArea(bluePoint, greenPoint, redPoint1); - auto area2 = new TriangularArea(greenPoint, bluePoint, redPoint1); - - // 它们应该是相等的 - assert(area1 == area2); - - // 一个关联数组 - double[TriangularArea] areas; - - // 由area1存进一个值 - areas[area1] = 1.25; - - // area2访问这个值 - assert(area2 in areas); - assert(areas[area2] == 1.25); ---- - -$(P -记住在定义 $(C toHash) 的时候也一定要定义 $(C opEquals) 和 $(C opCmp) 。 -) - -) - -) - -Macros: - SUBTITLE=Object - - DESCRIPTION=Object,D语言里类继承结构中最顶层的类 - - KEYWORDS=D 语言编程教程 class Object opEquals opCmp toHash toString diff --git a/ddili/src/ders/d.cn/pdf.derse_ozel.css b/ddili/src/ders/d.cn/pdf.derse_ozel.css deleted file mode 100644 index fe51491..0000000 --- a/ddili/src/ders/d.cn/pdf.derse_ozel.css +++ /dev/null @@ -1,31 +0,0 @@ -a.xref:after { - content: " (page " target-counter(attr(href, url), page) ")"; -} - -div.cozum_link_cok a.xref { - content: "The solutions are on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_cok a.xref:after { - content: normal; -} - -div.cozum_link_tek a.xref { - content: "The solution is on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_tek a.xref:after { - content: normal; -} - -div.cozum_link_cok, div.cozum_link_tek { - padding-top: .1em; - page-break-before: avoid; -} - -body -{ - counter-reset: h4 -2; -} diff --git a/ddili/src/ders/d.cn/pdf_cozum_head.html b/ddili/src/ders/d.cn/pdf_cozum_head.html deleted file mode 100644 index d7ebaae..0000000 --- a/ddili/src/ders/d.cn/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Exercise Solutions

    diff --git a/ddili/src/ders/d.cn/pdf_cozum_tail.html b/ddili/src/ders/d.cn/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.cn/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.cn/pdf_html_head.html b/ddili/src/ders/d.cn/pdf_html_head.html deleted file mode 100644 index d75ecee..0000000 --- a/ddili/src/ders/d.cn/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - Programming in D - - - diff --git a/ddili/src/ders/d.cn/pdf_html_tail.html b/ddili/src/ders/d.cn/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/d.cn/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/d.cn/pdf_sozluk_head.html b/ddili/src/ders/d.cn/pdf_sozluk_head.html deleted file mode 100644 index f5edc52..0000000 --- a/ddili/src/ders/d.cn/pdf_sozluk_head.html +++ /dev/null @@ -1,3 +0,0 @@ - -

    Sözlük

    - diff --git a/ddili/src/ders/d.cn/pragma.d b/ddili/src/ders/d.cn/pragma.d deleted file mode 100644 index a57b1a1..0000000 --- a/ddili/src/ders/d.cn/pragma.d +++ /dev/null @@ -1,235 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX pragma) Pragmas) - -$(P -Pragma 是一种与编译器交互的方法。可以通过他们向编译器提供或获取特殊的信息。$(C pragma(msg)) 在非模板代码中非常有用,调试模板亦然。 -) - -$(P -每一个编译器生产商都可以任意添加他们的特殊 $(C pragma) 指令,但以下指令是强制实现的: -) - -$(H5 $(C pragma(msg))) - -$(P -在编译期向 $(C stderr) 打印消息。编译后的程序在执行过程中不会输出任何消息。 -) - -$(P -例如,如下的 $(C pragma(msg)) 通常用于在调试期暴露模板参数的类型: -) - ---- -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 -命令编译器将程序与一个特别的库链接。这是将程序链接到一个已经安装到系统里的库的最方便的做法。 -) - -$(P -例如,下面的程序会被链接到 $(C curl) 而不用在命令行指定这个库: -) - ---- -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 内联) $(IX 函数内联) $(IX 优化, 编译器) $(C pragma(inline))) - -$(P -指定函数是否被$(I 内联)。 -Specifies whether a function should be $(I inlined) or not. -) - -$(P -每一次函数调用都有性能损耗。函数调用需要处理函数的参数传递,返回调用者函数的返回值,还需要处理记录一些函数调用位置的书签信息,因而可以在函数返回后继续执行。 -) - -$(P -通常函数调用开销相比较调用者和函数本身的工作而言并不算什么。但是,在一些场景调用函数的行为会对程序的性能有重要影响。例如,函数体的执行相对很快,或者是函数是在一个很短的循环里重复被调用。 -) - -$(P -如下的程序会在循环里调用一个很小的函数,在函数返回值满足条件时增加计数器: -) - ---- -import std.stdio; -import std.datetime; - -// 函数体执行速度很快: -ubyte compute(ubyte i) { - return cast(ubyte)(i * 42); -} - -void main() { - size_t counter = 0; - - StopWatch sw; - sw.start(); - - // 重复很多此的小循环: - 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) 代码使用 $(C std.datetime.StopWatch) 来测量整个循环执行的时间: -) - -$(SHELL -$(HILITE 674) milliseconds -) - -$(P -$(IX -inline, 编译器开关) 编译器开关 $(C -inline) 命令编译器执行$(I 函数内联)优化: -) - -$(SHELL -$ dmd deneme.d -w $(HILITE -inline) -) - -$(P -当一个函数被内联,则函数体会被注入到函数被调用的地方,而不是函数调用发生的时候。下面的代码与编译器内联后的代码等价: -) - ---- - // 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 -在我测试程序的平台上,消除这个函数调用减少了 40% 的执行时间: -) - -$(SHELL -$(HILITE 407) milliseconds -) - -$(P -尽管函数内联看起来是巨大的收获,但是它却不能应用与所有函数调用,因为内联的函数体会使代码过大而不能塞进 CPU 的$(I 指令缓存)。这样会让代码更慢。因此,决定函数调用是否内联通常都交给编译器了。 -) - -$(P -但是,也有一些场景帮助编译器做决定会从中受益。$(C inline) pragma 命令编译器做出内联的决定: -) - -$(UL - -$(LI $(C pragma(inline, false)): 命令编译器不要内联函数,即使指定了编译器开关 $(C -inline)。) - -$(LI $(C pragma(inline, true)): 当指定了编译器开关 $(C -inline),命令编译器强制内联函数。如果编译器无法内联这个函数会导致编译错误(这个 pragma 确切的行为在你的编译器上可能不同。)) - -$(LI $(C pragma(inline)): 让内联行为由编译器命令行决定:$(C -inline) 是否被指定。) - -) - -$(P -下面这些 pragma 会影响引入点的函数,同样可以用 scope 和冒号来影响更多的函数: -) - ---- -pragma(inline, false) -$(HILITE {) - // 定义在这个 scope 内的函数不应该内联 - // ... -$(HILITE }) - -int foo() { - pragma(inline, true); // 这个函数应该内联 - // ... -} - -pragma(inline, true)$(HILITE :) -// 在这个段的函数定义应该被内联 -// ... - -pragma(inline)$(HILITE :) -// 在这个段的函数定义是否被内联取决于 -inline 编译器开关 -// ... ---- - -$(P -$(IX -O, 编译器开关) 另外一个可以使程序运行得更快的编译器开关是 $(C -O),它命令编译器执行更优化的算法。但是,更快的程序运行速度导致更慢的编译速度,这是因为这些算法需要大量的时间。 -) - -$(H5 $(IX 起始地址) $(C pragma(startaddress))) - -$(P -指定程序的起始地址。因为起始地址正常情况下会被 D 运行时环境重新设置,所以你不太会用到这个 pragma。 -) - -$(H5 $(IX mangle, pragma) $(IX 名字改编) $(C pragma(mangle))) - -$(P -指定一个需要$(I 名字改编)的符号,以不同于默认名字改编的方法。名字改变主要用于让连接器辨别函数以及他们的调用者。当 D 代码需要调用一个用 D 关键字命名的库函数时,这个 pragma 会非常有用。 -) - -$(P -例如,如果 C 库里面有一个函数叫做 $(C body),因为 $(C body) 是 D 的关键字,在 D 中调用的唯一的办法就是通过一个不同的名字。但是这个不同的名字依然需要改编成库中实际的函数名字,连接器才能找到它: -) - ---- -/* 如果 C 库有一个函数名字叫做“body”,在 D 里只能通过像“c_body” - * 这样的名字来调用,然后改编成实际的函数名字:*/ -pragma($(HILITE mangle), "body") -extern(C) string c_body(string); - -void main() { - /* D 代码用 c_body() 来调用这个函数,但连接器依然会找到其 - * 正确的 C 库名字“body”: */ - auto s = $(HILITE c_body)("hello"); -} ---- - -Macros: - SUBTITLE=Pragma - - DESCRIPTION=介绍 pragma —— 一种与编译器交互的方法。 - - KEYWORDS=d 编程 语言 教程 pragma inline 内联 diff --git a/ddili/src/ders/d.cn/preface.d b/ddili/src/ders/d.cn/preface.d deleted file mode 100644 index 60d3c6a..0000000 --- a/ddili/src/ders/d.cn/preface.d +++ /dev/null @@ -1,70 +0,0 @@ -Ddoc - -$(DIV_CLASS preface, - -$(DERS_BOLUMU_CLASS preface, Preface) - -$(P -D 是一个多范式的系统级编程语言,无论是高级还是低级编程概念,在 D 语言中都有它的体现。D 强调内存安全性,程序的正确性和实用性。 -) - -$(P -本书的目标是教会编程新手使用 D 语言。虽然在其他编程语言方面的经验可能对学习 D 有所帮助,但本书还是选择从基础开始。 -) - -$(P -如果你想充分发挥本书在你学习 D 语言过程中的作用,你需要先准备好一个编码,编译和运行 D 语言程序的环境。这个$(I 开发环境)至少要包括一个文本编辑器和一个 D 语言编译器。我们将会在下一章学习如何安装编译器以及如何编译 D 语言代码。 -) - -$(P -每一章的知识都是在上一章的基础上增加一点新的概念。所以我建议你从头开始一章一章的读,不要跳过任何一个章节。虽然本书的目标人群是初学者,但是本书包含了 D 语言中几乎所有的特性。富有经验的程序员可以从索引开始,把本书当作 D 语言特性的“字典”使用。 -) - -$(P -一些章节包含了练习题和它们的参考答案,这样你可以在阅读的同时写一些小程序并与我提供的方法比较来更快的掌握这门语言。 -) - -$(P -计算机编程是一个令人愉悦的工作,因为你总是在发现和学习新工具、新技术、新概念。我相信在读完本书后你会和我一样喜欢使用 D 编写程序。与他人一同学习将会使学习过程更简单并富有乐趣。可以利用 $(LINK2 http://forum.dlang.org/group/digitalmars.D.learn/, the D.learn 新闻组) 跟进讨论,提问和回答问题。 -) - -$(P -本书还有其他语言的翻译,包括 $(LINK2 http://ddili.org/ders/d/, 土耳其语) 和 $(LINK2 http://dlang.unix.cat/programmer-en-d/, 法语)。 -) - -$(H5_FRONTMATTER 致谢) - -$(P -感谢所有在本书编写和完善过程中付出努力的人: -) - -$(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, and Chen Lejia. -) - -$(P -尤其感谢 Luís Marques,他完善了书中每一个章节。如果你在读完本书某一部分后感到获益匪浅,那有可能就是他勤劳编辑的成果。 -) - -$(P -感谢 Luís Marques, Steven Schveighoffer, Andrej Mitrović, Robbin Carlson, and Ergin Güney 在从土耳其语向英语翻译的过程中提供的建议。 -) - -$(P -感谢整个 D 语言社区让我能够始终保持热情和积极性。D 语言有一个令人惊叹的社区和像 bearophile and Kenji Hara 这样不懈努力的参与者。 -) - -$(P -感谢 Ebru, Damla, and Derin 能在我沉迷于写书时还能保持耐心并支持我前进。 -$(BR) -$(BR) -Ali Çehreli$(BR) -Mountain View,$(I 2016 四月) -) - -) - -Macros: - SUBTITLE = 前言 - DESCRIPTION= - KEYWORDS= diff --git a/ddili/src/ders/d.cn/rss.xml b/ddili/src/ders/d.cn/rss.xml deleted file mode 100644 index 1e35711..0000000 --- a/ddili/src/ders/d.cn/rss.xml +++ /dev/null @@ -1,812 +0,0 @@ - - - - - Programming in D - http://ddili.org/ders/d.en/ - Programming with the D programming language - en - D Programming Language Book - - - 'deprecated', 'extern', and 'extern()' - http://ddili.org/ders/d.en/modules.html - Added 'deprecated' that helps with deprecation process of library features, 'extern' that is for declaring external symbols, and 'extern()' that allows interacting with libraries of other languages (extern(C), extern(C++, std), etc.). - Chapter - 24 Oct 2015 22:00 - - - - Code Samples .zip File - http://ddili.org/ders/d.en/index.html - You can download most of the code samples that appear in the book as a .zip file. - Code - 16 Oct 2015 20:00 - - - - Book Index - http://ddili.org/ders/d.en/ix.html - The index section of the book. - Chapter - 21 Aug 2015 02:00 - - - - Fibers - http://ddili.org/ders/d.en/fibers.html - Cooperative multitasking in D with fibers. - Chapter - 17 Aug 2015 15:00 - - - - Pragmas - http://ddili.org/ders/d.en/pragma.html - Pragmas that allow interactions with the compiler, including the most recent pragma(inline). - Chapter - 17 Aug 2015 15:00 - - - - Operator Precedence - http://ddili.org/ders/d.en/operator_precedence.html - The rules that specify the execution order of chained operators and the expressions that they use. - Chapter - 17 Aug 2015 15:00 - - - - Foreword by Andrei Alexandrescu - http://ddili.org/ders/d.en/foreword2.html - "Instead of falling for getting things done quickly, 'Programming in D' focuses on getting things done properly, to the lasting benefit of its reader." - Chapter - 17 Aug 2015 15:00 - - - - Ebook versions - http://ddili.org/ders/d.en/index.html - In addition to the PDF version, now there are EPUB and AZW3 versions as well. - Ebook - 15 Dec 2014 02:00 - - - - '.offsetof' property and 'align' attribute - http://ddili.org/ders/d.en/memory.html - The .offsetof property to get the offsets of and the align attribute to specify the alignments of struct members. - Chapter - 25 Nov 2014 17:00 - - - - Contract inheritance - http://ddili.org/ders/d.en/invariant.html - Inheriting 'in' and 'out' blocks of interface and class functions. - Chapter - 25 Nov 2014 17:00 - - - - The special keywords - http://ddili.org/ders/d.en/templates_more.html - __MODULE__, __FILE__, __LINE__, __FUNCTION__, and __PRETTY_FUNCTION__ - Chapter - 25 Nov 2014 17:00 - - - - pragma - http://ddili.org/ders/d.en/templates.html - The pragma directive - Chapter - 25 Nov 2014 17:00 - - - - Nested Functions, Structs, and Classes - http://ddili.org/ders/d.en/nested.html - Defining functions, structs, and classes inside existing scopes. - Chapter - 25 Nov 2014 17:00 - - - - Lvalues and Rvalues - http://ddili.org/ders/d.en/lvalue_rvalue.html - Lvalues, rvalues, their differences, and 'auto ref' parameter type that can accept either kind. - Chapter - 25 Nov 2014 17:00 - - - - The 'Index' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated index section for the PDF version of the book. - Chapter - 25 Nov 2014 17:00 - - - - The 'Table of Contents' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated TOC section for the PDF version of the book. - Chapter - 28 Sep 2014 23:00 - - - - The attributes of Throwable - http://ddili.org/ders/d.en/exceptions.html - The attributes of the Throwable interface, collateral exceptions, and an example of accessing collateral exceptions through the .next property. - Chapter - 18 Sep 2014 16:00 - - - - Selective, local, renamed, and package imports - http://ddili.org/ders/d.en/modules.html - Importing modules selectively, locally, under a different name, and as a package. - Chapter - 17 Sep 2014 00:30 - - - - @disable - http://ddili.org/ders/d.en/special_functions.html - The @disable attribute to disable special functions of structs. - Chapter - 12 Sep 2014 00:15 - - - - Operator overloading for multi-dimensional indexing and slicing - http://ddili.org/ders/d.en/templates_more.html - The templated versions of opDollar, opIndex, and opSlice, and examples of using them to support multiple indexes inside square brackets. - Chapter - 11 Sep 2014 00:15 - - - - static this, static ~this, shared static this, and shared static ~this - http://ddili.org/ders/d.en/index.html - Specifying the initial and final operations of threads and programs. - Chapter - 26 Aug 2014 23:30 - - - - User Defined Attributes (UDA) - http://ddili.org/ders/d.en/uda.html - Assigning user defined attributes to type and variable declarations, testing the attributes at compile time, and compiling the program according to those attributes. - Chapter - 26 Aug 2014 22:30 - - - - Memory Management - http://ddili.org/ders/d.en/memory.html - The garbage collector, allocating memory, and placing objects at specific locations in memory. - Chapter - 24 Jul 2014 01:00 - - - - Type traits - http://ddili.org/ders/d.en/cond_comp.html - Using type traits in conditional compilation. - Chapter - 29 May 2014 20:30 - - - - Data Sharing Concurrency - http://ddili.org/ders/d.en/concurrency_shared.html - Multi-threaded programming in D by data sharing. - Chapter - 20 May 2014 12:00 - - - - More Ranges - http://ddili.org/ders/d.en/ranges_more.html - Useful range templates of the std.range module. Providing range capabilities depending on the capabilities on dependent ranges. - Chapter - 01 November 2013 17:00 - - - - Mixins - http://ddili.org/ders/d.en/mixin.html - Template and string mixins that allow inserting compile-time generated code into the source code. - Chapter - 01 November 2013 17:00 - - - - More Functions - http://ddili.org/ders/d.en/functions_more.html - More features of D functions: auto, ref, auto ref, and inout return attributes; pure and nothrow behavioral attributes; and @safe, @trusted, and @system memory safety attributes. - Chapter - 01 November 2013 17:00 - - - - More Templates - http://ddili.org/ders/d.en/templates_more.html - More information about templates: Templates can define any kind of code; template parameters can be of type, value, alias, this, and tuple; template constraints enable template definitions only for template arguments that satisfy that template's requirements. - Chapter - 19 September 2013 00:30 - - - - Tuples - http://ddili.org/ders/d.en/tuples.html - Tuples, which combine values of different types and make them available similar to struct objects, and TypeTuple, which represents the concept of 'list of values' as seen in parameter lists, template argument lists, and array literal initialization lists. - Chapter - 19 September 2013 00:30 - - - - Ranges - http://ddili.org/ders/d.en/ranges.html - The code examples in the Ranges chapter are made const-correct and they take advantage of UFCS. - Chapter - 09 July 2013 19:30 - - - - Labels and goto - http://ddili.org/ders/d.en/goto.html - Labels that give names to code lines and the goto statement that make program execution go to a label. - Chapter - 28 June 2013 19:00 - - - - Unions - http://ddili.org/ders/d.en/union.html - Sharing the same memory area for multiple members. - Chapter - 26 June 2013 10:00 - - - - foreach with Structs and Classes - http://ddili.org/ders/d.en/foreach_opapply.html - Providing foreach support for structs and classes. - Chapter - 14 June 2013 18:30 - - - - Function Pointers, Delegates, and Lambdas - http://ddili.org/ders/d.en/lambda.html - Function pointers and delegates allow storing how the program should behave at a later time. Lambdas (anonymous function or function literals) make code more readable and reduce boilerplate code. - Chapter - 9 June 2013 18:00 - - - - is Expression - http://ddili.org/ders/d.en/is_expr.html - is expression, one of the most powerful compile-time features of the D programming language. - Chapter - 3 June 2013 22:00 - - - - Conditional Compilation - http://ddili.org/ders/d.en/cond_comp.html - Compiling parts of programs in special ways depending on conditions that are checked at compile time. - Chapter - 3 June 2013 21:30 - - - - Bit Operations - http://ddili.org/ders/d.en/bit_operations.html - The D features that enable manipulating data bit-by-bit. - Chapter - 21 May 2013 23:30 - - - - Pointers - http://ddili.org/ders/d.en/pointers.html - Pointers are variables that provide access to other variables. They are low-level capabilities of the microprocessor. - Chapter - 31 Jan 2013 20:30 - - - - alias this - http://ddili.org/ders/d.en/alias_this.html - 'alias this' enables automatic type conversions of user-defined types. - Chapter - 31 Jan 2013 20:30 - - - - alias - http://ddili.org/ders/d.en/alias.html - 'alias' assigns aliases to existing names - Chapter - 31 Jan 2013 20:30 - - - - Contract Programming for Structs and Classes - http://ddili.org/ders/d.en/invariant.html - The 'invariant' keyword and the use of the 'in' and 'out' blocks with structs and classes. - Chapter - 31 Jan 2013 20:30 - - - - Properties - http://ddili.org/ders/d.en/property.html - Properties allow using member functions like member variables. - Chapter - 31 Jan 2013 20:30 - - - - Universal Function Call Syntax (UFCS) - http://ddili.org/ders/d.en/ufcs.html - UFCS enables the member function syntax even for regular functions. - Chapter - 31 Jan 2013 20:30 - - - - Encapsulation and Protection Attributes - http://ddili.org/ders/d.en/encapsulation.html - Preserving class invariants by limiting access to class members. - Chapter - 2 Nov 2012 21:00 - - - - Modules and Libraries - http://ddili.org/ders/d.en/modules.html - Organizing D programs and libraries as modules and packages. - Chapter - 2 Nov 2012 21:00 - - - - destroy and scoped - http://ddili.org/ders/d.en/destroy.html - 'destroy()' to call destructors explicitly and 'scoped()' to destroy objects automatically. - Chapter - 2 Nov 2012 21:00 - - - - Interfaces - http://ddili.org/ders/d.en/interface.html - The 'interface' keyword to define class interfaces. - Chapter - 2 Nov 2012 21:00 - - - - Object - http://ddili.org/ders/d.en/object.html - The Object class that is at the top of class hierarchies and its member functions toString(), opEquals(), opCmp(), and toHash(). - Chapter - 2 Nov 2012 21:00 - - - - Inheritance - http://ddili.org/ders/d.en/inheritance.html - Inheriting the members of existing classes. - Chapter - 2 Nov 2012 21:00 - - - - Classes - http://ddili.org/ders/d.en/class.html - The 'class' feature of the D programming language, which supports the object oriented programming (OOP) paradigm. - Chapter - 2 Nov 2012 21:00 - - - - Operator Overloading - http://ddili.org/ders/d.en/operator_overloading.html - Defining the behaviors of operators for structs to allow their uses as convenient as the fundamental types. - Chapter - 15 Sep 2012 23:30 - - - - Constructor and Other Special Functions - http://ddili.org/ders/d.en/special_functions.html - The four special functions of structs: constructor, destructor, postblit, and assignment. - Chapter - 15 Sep 2012 23:30 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Receiving LinkTerminated and OwnerTerminated exceptions as messages. - Chapter - 09 Aug 2012 23:45 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - Explain the parameters of the functions of the std.parallelism module: Work unit size and buffer size. - Chapter - 09 Aug 2012 23:30 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The consequences of marking function parameters const or immutable. - Chapter - 09 Aug 2012 22:00 - - - - const ref Parameters and const Member Functions - http://ddili.org/ders/d.en/const_member_functions.html - Marking parameters as 'const ref' and member functions as 'const' in order to be able to use them with immutable variables as well. - Chapter - Sun, 10 Jun 2012 17:40 - - - - Member Functions - http://ddili.org/ders/d.en/member_functions.html - Defining functions that are closely related to a struct inside the curly brackets of that struct definition. - Chapter - Sun, 10 Jun 2012 16:15 - - - - Function Overloading - http://ddili.org/ders/d.en/function_overloading.html - The function overloading feature that enables defining multiple functions having the same name. - Chapter - Sun, 10 Jun 2012 14:50 - - - - Variable Number of Parameters - http://ddili.org/ders/d.en/parameter_flexibility.html - Default parameter values and variadic functions. - Chapter - Sun, 10 Jun 2012 13:20 - - - - Structs - http://ddili.org/ders/d.en/struct.html - The 'struct' feature for defining higher-level concepts as user-defined types. - Chapter - Sun, 10 Jun 2012 12:20 - - - - Type Conversions - http://ddili.org/ders/d.en/cast.html - Automatic and explicit type conversions of D: integer promotions, arithmetic conversions, the to() and assumeUnique() functions, and the cast operator. - Chapter - Sun, 10 Jun 2012 00:05 - - - - The null Value and the is Operator - http://ddili.org/ders/d.en/null_is.html - The null value and the is and !is operators. - Chapter - Sat, 09 Jun 2012 18:30 - - - - Value Types and Reference Types - http://ddili.org/ders/d.en/value_vs_reference.html - Introducing value types, reference variables, and reference types; and their differences. - Chapter - Sat, 09 Jun 2012 17:45 - - - - Lifetimes and Fundamental Operations - http://ddili.org/ders/d.en/lifetimes.html - Object lifetimes: Initialization and finalization of variables. - Chapter - Sat, 09 Jun 2012 15:15 - - - - Contract Programming - http://ddili.org/ders/d.en/contracts.html - Contract programming in D: the 'in' and 'out' blocks of functions. - Chapter - Tue, 27 Apr 2012 23:15 - - - - Unit Testing - http://ddili.org/ders/d.en/unit_testing.html - Unit tests for reducing the risk of bugs and test driven development (TDD). - Chapter - Tue, 24 Apr 2012 22:15 - - - - Installing dmd and compiling programs - http://ddili.org/ders/d.en/hello_world.html - Added how to install dmd and how to compile programs on the command line. - Chapter - Sat, 21 Apr 2012 22:15 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Multi-threaded programming in D by message passing, provided by the std.concurrency module. - Chapter - 15 Apr 2012 23:15 - - - - assert and enforce - http://ddili.org/ders/d.en/assert.html - The assert checks and the enforce() function that help with program correctness. - Chapter - 12 April 2012 23:30 - - - - scope - http://ddili.org/ders/d.en/scope.html - The scope(success), scope(failure), and scope(exit) statements of D, which in many cases obviate the need for try-catch-finally blocks and RAII classes. - Chapter - 28 Mar 2012 23:20 - - - - Program Environment - http://ddili.org/ders/d.en/main.html - The environment that starts a D program and the ways the program can interact with its environment: return value, parameters, environment variables. - Chapter - 28 Mar 2012 23:10 - - - - Lazy Operators - http://ddili.org/ders/d.en/lazy_operators.html - The shortcut evaluations of three operators: logical or, logical and, and the ternary operator. - Chapter - 28 Mar 2012 01:00 - - - - Function Parameters - http://ddili.org/ders/d.en/function_parameters.html - Different kinds of function parameters and how they affect the functions and the arguments. - Chapter - 28 Mar 2012 01:00 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The concept of immutability in the D programming language, the const and immutable keywords, and recommendations on how to take advantage of immutability when defining variables and function parameters. - Chapter - 22 Mar 2012 22:50 - - - - Functions - http://ddili.org/ders/d.en/functions.html - The functions that define the building blocks of program behavior. - Chapter - 14 Mar 2012 00:30 - - - - enum - http://ddili.org/ders/d.en/enum.html - The enum feature that enables defining named constant values. - Chapter - 13 Mar 2012 18:30 - - - - switch and case - http://ddili.org/ders/d.en/switch_case.html - The switch and final switch statements, their case sections, and the use of the goto statement under the case sections. - Chapter - 27 Feb 2012 18:10 - - - - The foreach Loop - http://ddili.org/ders/d.en/foreach.html - The foreach loop, one of the most common statements of D. Its use with arrays, strings, and associative arrays. - Chapter - 27 Feb 2012 18:00 - - - - Associative Arrays - http://ddili.org/ders/d.en/aa.html - Associative arrays, the hash table implementation of the D programming language. - Chapter - 26 Feb 2012 22:40 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - The std.parallelism module to make programs run faster by taking advantage of multiple cores of the system. - Chapter - 19 Feb 2012 23:10 - - - - The do-while Loop - http://ddili.org/ders/d.en/do_while.html - The do-while loop and its comparison to the while loop. - Chapter - 11 Feb 2012 20:10 - - - - Formatted Input - http://ddili.org/ders/d.en/formatted_input.html - Reading data that match specific formats. - Chapter - 11 Feb 2012 20:00 - - - - Formatted Output - http://ddili.org/ders/d.en/formatted_output.html - Determining the format of printed values. - Chapter - 31 Jan 2012 00:00 - - - - Literals - http://ddili.org/ders/d.en/literals.html - The syntax of literals of different D types. - Chapter - 31 Jan 2012 00:00 - - - - The Ternary Operator ?: - http://ddili.org/ders/d.en/ternary.html - The ternary operator and comparing it to the if-else statement. - Chapter - 31 Jan 2012 00:00 - - - - The for Loop - http://ddili.org/ders/d.en/for.html - The for loop and its comparison to the while loop. - Chapter - 31 Jan 2012 00:00 - - - - PDF version of the book - http://ddili.org/ders/d.en/index.html - The PDF version of the book is now available through a link in chapter headers - News - 21 Jan 2012 18:00 - - - - Name Space - http://ddili.org/ders/d.en/name_space.html - The lifetime and accessibility of names of variables and other program constructs - Chapter - 21 Jan 2012 18:00 - - - - auto and typeof - http://ddili.org/ders/d.en/auto_and_typeof.html - The 'auto' keyword and its use during type inference, and the typeof keyword to get the type of expressions - Chapter - 21 Jan 2012 18:00 - - - - Files - http://ddili.org/ders/d.en/files.html - Reading from and writing to files using the std.stdio.File struct - Chapter - 21 Jan 2012 18:00 - - - - Redirecting Standard Input and Output Streams - http://ddili.org/ders/d.en/stream_redirect.html - How to redirect standard input and output streams of program to files and other programs - Chapter - 21 Jan 2012 18:00 - - - - Templates - http://ddili.org/ders/d.en/templates.html - The 'Templates' chapter - Chapter - 12 Jan 2012 00:40 - - - - Strings - http://ddili.org/ders/d.en/strings.html - The 'Strings' chapter - Chapter - 03 Jan 2012 18:00 - - - - Slices and Other Array Features - http://ddili.org/ders/d.en/slices.html - The 'Slices and Other Array Features' chapter - Chapter - 31 Dec 2011 19:15 - - - - Characters - http://ddili.org/ders/d.en/characters.html - The 'Characters' chapter - Chapter - 18 Dec 2011 19:15 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter has been proofread. - Proofreading - 18 Dec 2011 19:00 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter - Chapter - 11 Dec 2011 14:00 - - - - Floating Point Types - http://ddili.org/ders/d.en/floating_point.html - The 'Floating Point Types' chapter - Chapter - 09 Dec 2011 22:10 - - - - Index - http://ddili.org/ders/d.en/index.html - The index of the book - Book - 13 Nov 2011 23:00 - - - - diff --git a/ddili/src/ders/d.cn/templates.d b/ddili/src/ders/d.cn/templates.d deleted file mode 100644 index 0698417..0000000 --- a/ddili/src/ders/d.cn/templates.d +++ /dev/null @@ -1,1099 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX template) 模板) - -$(P -模板是以模型来描述代码、从而让编译器来自动地生成程式代码的功能。源码的一些部分可以直到在程序中真正使用时才由编译器来完成。 -) - -$(P -模板能够用来写通用的算法与数据结构,而非针对一个特定的类型,因此模板特别有用,尤其是在代码库中。 -) - -$(P -与其他语言中的模板支持相比,D 的模板非常强大与广泛。然而我并不会在本章中展开所有有关模板的细节。我将只涵盖函数、结构与类,并只针对以类型为参数的模板。我们将在 $(LINK2 /ders/d.cn/templates_more.html, 更多模板) 一章中看到有关模板的更多信息。完整的 D 模板参考说明详见 $(LINK2 https://github.com/PhilippeSigaud/D-templates-tutorial, Philippe Sigaud's $(I D Templates: A Tutorial))。 -) - -$(P -以见识模板的诸多好处,让我们从一个打印括号中值的函数开始: -) - ---- -void printInParens(int value) { - writefln("(%s)", value); -} ---- - -$(P -因为参数被指定为了 $(C int),这个函数只能被用于类型为 $(C int) 或是能被自动转换为 $(C int) 的值。比如说,编译器不会允许以浮点值调用这个函数。 -) - -$(P -假使程序的需求变化了,括号中其他类型一样需要被打印出来,解决方式之一是利用函数重载、提供所有所需类型的重载函数: -) - ---- -// 原先存在的函数 -void printInParens(int value) { - writefln("(%s)", value); -} - -// 针对‘double’类型进行重载 -void printInParens($(HILITE double) value) { - writefln("(%s)", value); -} ---- - -$(P -这种解决方式并没有很好的适应性,因为此时该函数不能被用于诸如 $(C real) 以及任何自定义类型。尽管针对其他类型重载仍旧是可能的,这么做的代价却是令人望而却步的。 -) - -$(P -很重要的一个现象是无关乎参数的类型,重载的内容始终是一样的:一个单一的 $(C writefln()) 表达式。 -) - -$(P -像这样的单一性在算法和数据结构中十分常见。例如二分查找算法与元素的类型无关——搜索的特定步骤和操作才是关键所在。与此相似地,链表数据结构亦与元素类型无关:链表仅仅关乎元素在容器中的储存方式,不论它们的类型如何。 -) - -$(P -这种情况下模板十分有用:一旦代码被写为模板,编译器将根据其在程序中的具体调用自动地生成同一代码的不同重载。 -) - -$(P -与上面提及的一样,在本章中,我将只涵盖函数、结构与类,并只针对以类型为参数的模板。 -) - -$(H5 $(IX 函数模板) 函数模板) - -$(P -$(IX 参数,模板) 定义函数模板是引入一个或多个不明的类型来在函数中使用,这些类型将由编译器推断得出。 -) - -$(P -这些不明的类型定义于函数名与函数参数列表之间的模板参数列表中。因此,函数模板有两个参数列表——模板参数列表与函数参数列表: -) - ---- -void printInParens$(HILITE (T))(T value) { - writefln("(%s)", value); -} ---- - -$(P -在上述模板参数列表中的 $(C T) 意味着 $(C T) 可以是任何类型。尽管 $(C T) 是一个随意的名称,但它是“类型(type)”的缩写形式,因此在模板中很寻常。 -) - -$(P -因为 $(C T) 表示任何类型,所以上述 $(C printInParens()) 模板定义足以应用于包括自定义的几乎每个类型: -) - ---- -import std.stdio; - -void printInParens(T)(T value) { - writefln("(%s)", value); -} - -void main() { - printInParens(42); // 以 int - printInParens(1.2); // 以 double - - auto myValue = MyStruct(); - printInParens(myValue); // 以 MyStruct -} - -struct MyStruct { - string toString() const { - return "hello"; - } -} ---- - -$(P -编译器概览 $(C printInParens()) 所有在程序中的使用并生成代码来支持所有相应的调用。程序被如同该函数明确地为 $(C int)、$(C double) 与 $(C MyStruct) 重载一样地编译: -) - -$(MONO -/* 注:这些函数并不是源代码的一部分。 - *   它们与编译器自动生成的函数相等价。 */ - -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 -程序的输出是由该函数模板的不同$(I 实例)生成的: -) - -$(SHELL -(42) -(1.2) -(hello) -) - -$(P -每一个模板参数能确定不止一个的函数参数。例如,如下函数的两个函数参数及其返回类型都取决于它单一的模板参数: -) - ---- -/* 返回“slice”除去与“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 多于一个的模板参数) - -$(P -变化函数模板来加上首尾字符: -) - ---- -void printInParens(T)(T value, char opening, char closing) { - writeln(opening, value, closing); -} ---- - -$(P -现在我们能以不同的首尾字符调用该函数: -) - ---- - printInParens(42, '<', '>'); ---- - -$(P -尽管能指定首尾字符使得该函数更加实用,限定首尾字符的类型为 $(C char) 却下降了它的灵活性,因为它无法以 $(C wchar) 或 $(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 -一个解决方式是指定首尾的类型为 $(C dchar),但这还不够,此时函数无法运用比如 $(C string) 或自定义类型。 -) - -$(P -$(IX , (逗号),模板参数列表) 另一种解决方式是将首尾字符类型同样交给编译器来确定。定义一个附加的模板参数以替代指定的 $(C char) 就足矣: -) - ---- -void printInParens(T$(HILITE , ParensType))(T value, - $(HILITE ParensType) opening, - $(HILITE ParensType) closing) { - writeln(opening, value, closing); -} ---- - -$(P -新模板参数的意义与 $(C T) 的一致:$(C ParensType) 可以是任何类型。 -) - -$(P -现在就可以用许多不同类型的首尾字符。如下分别以 $(C wchar) 与 $(C string) 进行调用: -) - ---- - printInParens(42, '→', '←'); - printInParens(1.2, "-=", "=-"); ---- - -$(SHELL -→42← --=1.2=- -) - -$(P -$(C printInParens()) 的灵活性被大大增加了,只要是 $(C writeln()) 可以打印的类型,对于任何 $(C T) 和 $(C ParensType) 的组合它都可以正确工作。 -) - -$(H5 $(IX 类型推断) $(IX 推断,类型) 类型推断) - -$(P -编译器决定模板参数的具体类型的过程就叫做$(I 类型推断)。 -) - -$(P -继续看上面最后一个示例,编译器分别根据对该函数模板的两次不同调用来确定下述类型: -) - -$(UL -$(LI 输出 42 时的 $(C int) 与 $(C wchar)) -$(LI 输出 1.2 时的 $(C double) 与 $(C string)) -) - -$(P -编译器只能在对函数模板的调用中由参数的值类型进行类型推断。尽管大多数情况下编译器能毫无歧义地推断类型,但有时相应类型还是必须明确地由编程者指定。 -) - -$(H5 明确指定类型) - -$(P -有时编译器不可能自动推断相应的模板参数。比如当所需的类型不出现在函数参数列表中时。若模板参数与函数参数没有关联,那么编译器便不能推断出模板类型参数。 -) - -$(P -作为这种情况的一个实例,让我们设计一个向用户提出问题、接纳一个值作为回应、并返回该值的函数。另外,把它写为函数模板来用以读取任何类型的回应: -) - ---- -$(HILITE T) getResponse$(HILITE (T))(string question) { - writef("%s (%s): ", question, T.stringof); - - $(HILITE T) response; - readf(" %s", &response); - - return response; -} ---- - -$(P -这个函数模板或在需要读入不同类型输入的程序中非常有用。例如,读入一些用户信息,就可以像下面一行一样调用它: -) - ---- - getResponse("What is your age?"); ---- - -$(P -不幸的是这个调用没有给编译器任何关于模板参数 $(C T) 是什么的线索。它唯一知道的是传递给该函数的是一个 $(C string),但返回类型却不能借此被推断出来: -) - -$(SHELL_SMALL -Error: template deneme.getResponse(T) $(HILITE cannot deduce) template -function from argument types !()(string) -) - -$(P -$(IX !, 模板实例化) 在这类情况下,模板参数必须由编程者明确指定。模板参数指定于感叹号之后的括号中: -) - ---- - getResponse$(HILITE !(int))("What is your age?"); ---- - -$(P -上述代码现在便能被编译器采纳,$(C T) 作为了 $(C int) 在模板内的一个别名。 -) - -$(P -当仅需要指定一个模板参数时,它周围的括号可以被省略: -) - ---- - getResponse$(HILITE !int)("What is your age?"); // 同上 ---- - -$(P -你或能由已经在早先的程序中使用过的 $(C to!string) 认出这种语法。$(C to()) 是一个函数模板,它以模板参数的形式接受转换的目标类型。由于只有一个待指定的模板参数,它通常被写作 $(C to!string) 而非 $(C to!(string))。 -) - -$(H5 $(IX 实例, 模板) 模板实例) - -$(P -为一组特定的模板参数自动生成代码称之为该模板对于这一组参数的一个$(实例)。例如,$(C to!string) 和 $(C to!int) 就是函数模板 $(C to) 的两个不同的实例。 -) - -$(P -如在下面单独的片段中提及的一样,模板的不同实例生成不同且不相兼容的类型。 -) - -$(H5 $(IX 特化, 模板) 模板特化) - -$(P -尽管函数模板 $(C getResponse()) 理论上可以用于任何类型,编译器生成的代码却未必适合每一种类型。假定有如下表示二维空间上点的类型: -) - ---- -struct Point { - int x; - int y; -} ---- - -$(P -尽管 $(C getResponse()) 对于 $(C Point) 类型的实例化本身是没问题的,其生成的对 $(C readf()) 的调用却不能通过编译。这是因为标准库中的函数 $(C readf()) 不知道如何从输入中读取一个 $(C Point) 对象。而实际上用于读入输入的两行代码就如同如下在 $(C getResponse()) 函数模板的 $(C Point) 类型实例中的一样: -) - ---- - Point response; - readf(" %s", &response); $(DERLEME_HATASI) ---- - -$(P -一种读入 $(C Point) 对象的方式是单独地读入 $(C x) 与 $(C y) 成员的值,再以此$(构建)一个 $(C Point) 对象。 -) - -$(P -为特定模板参数值提供特殊的模板定义称之为一个$(模板特化)。特化的定义方式为在模板参数列表中的 $(C :) 字符后指定类型。一个为 $(C Point) 特化的 $(C getResponse()) 函数模板定义如下: -) - ---- -// 函数模板的通用定义(与之前一样) -T getResponse(T)(string question) { - writef("%s (%s): ", question, T.stringof); - - T response; - readf(" %s", &response); - - return response; -} - -// 针对 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 -注意这个特化利用了 $(C getResponse()) 的通用定义来读入成员 $(C x) 和 $(C y)。 -) - -$(P -现在编译器不再自己实例化该模板,而是在 $(C getResponse()) 以 $(C Point) 调用时使用上面的特化: -) - ---- - auto center = getResponse!Point("Where is the center?"); ---- - -$(P -假设用户输入了 11 与 22: -) - -$(SHELL_SMALL -Where is the center? (Point) - x (int): 11 - y (int): 22 -) - -$(P -对 $(C getResponse!int()) 的调用指向了该模板的通用定义,对 $(C getResponse!Point()) 的调用则指向了它针对 $(C Point) 的特化。 -) - -$(P -作为另一个示例,考虑一下以 $(C string) 使用同样模板的情况。如同你在 $(LINK2 /ders/d.cn/strings.html, 字符串) 一章中学到的那样,$(C readf()) 会将所有的输入字符用作一个单一的 $(C string),直至输入结束。因此,$(C getResponse()) 的默认定义在读入 $(C string) 回应时是无济于事的: -) - ---- - // 这会读入整个输入,而不仅仅是名字而已 - auto name = getResponse!string("What is your name?"); ---- - -$(P -我们同样可以为 $(C string) 提供一个模板特化。以下的特化仅读入一行: -) - ---- -T getResponse(T $(HILITE : string))(string question) { - writef("%s (string): ", question); - - // 读取并忽略想必是从之前的用户输入中遗留的空白字符 - string response; - do { - response = strip(readln()); - } while (response.length == 0); - - return response; -} ---- - -$(H5 $(IX struct 模板) $(IX class 模板) struct 与 class 模板) - -$(P -$(C Point) struct 或有一个限制:由于两个成员被指定为了 $(C int) 类型,它不能表示小数坐标值。若将 $(C Point) struct 定义为模板即可豁免这个限制。 -) - -$(P -先添加一个返回到另一个 $(C Point) 对象距离的成员函数: -) - ---- -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 -这个 $(C Point) 的定义适用于所需精确度较低的情况:它能计算精确到千米的两点距离,例如一个组织的总部与其分支机构间的距离: -) - ---- - auto center = getResponse!Point("Where is the center?"); - auto branch = getResponse!Point("Where is the branch?"); - - writeln("Distance: ", center.distanceTo(branch)); ---- - -$(P -不幸的是,$(C Point) 无法满足精度需求比 $(C int) 类型所提供的精度更高的情形。 -) - -$(P -struct 和 class 同样可以被定义为模板,只需要在名称之后附加一个模板参数列表即可。例如,$(C Point) 能被定义为 struct 模板,仅需提供一个取代 $(C int) 的模板参数: -) - ---- -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 -由于 struct 和 class 不是函数,它们不能以参数调用。这使得编译器不能自动推断它们的模板参数。 struct 和 class 模板的模板参数列表必须每次都手动指定: -) - ---- - auto center = Point$(HILITE !int)(0, 0); - auto branch = Point$(HILITE !int)(100, 100); - - writeln("Distance: ", center.distanceTo(branch)); ---- - -$(P -上述定义使得编译器针对 $(C Point) 模板的 $(C int) 实例生成代码,这与先前的非模板定义是等价的。然而,现在它可以用于任何类型。例如,当需要更高精度时使用 $(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 -尽管模板本身不依赖于任何特定的类型,但这单一的定义却使得它可以表示各个精度的点。 -) - -$(P -仅将 $(C Point) 转为模板将在为它的非模板定义写就的代码中造成编译错误。例如,现在 $(C getResponse()) 模板针对 $(C Point) 的特化将不再能通过编译: -) - ---- -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 -编译出错的原因是 $(C Point) 本身不再是一个类型了:$(C Point) 现在是一个 $(I struct template)。这个模板的实例才是类型。需要如下的变更来正确地为 $(C Point) 提供 $(C getResponse()) 的特化: -) - ---- -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 -以便让这个模板特化支持所有的 $(C Point) 实例,模板参数列表中必须提及 $(C Point!T)。这简明地表示该 $(C getResponse()) 特化针对的是 $(C Point!T),不论 $(C T) 是什么。这个特化会匹配 $(C Point!int)、$(C Point!double)、等等。 -) - -$(LI -相似地,为了返回正确的类型作为回应,返回类型也必须被指定为 $(C Point!T)。 -) - -$(LI -由于 $(C Point!T) 的成员 $(C x) 与 $(C y) 的类型现在为 $(C T),与 $(C int) 相当,成员必须以调用 $(C getResponse!T()) 的形式进行读入,而非 $(C getResponse!int()),否则这将仅能适用于 $(C Point!int)。 -) - -$(LI -如 1 与 2 说的一样,返回值的类型为 $(C Point!T)。 -) - -$(LI -以精确地输出每个类型的名称,诸如 $(C Point!int)、$(C Point!double)、等等,使用 $(C T.stringof)。 -) - -) - -$(H5 $(IX 默认模板参数) 默认模板参数) - -$(P -有时每次都提供模板类型参数是十分累赘的,尤其是当该类型几乎总是同一个特定类型的时候。譬如,$(C getResponse()) 或几乎总是为 $(C int) 类型在程序中调用,而仅有几处使用 $(C double) 类型。 -) - -$(P -可以为模板参数指定默认类型,在没有显式指定类型时即采用默认类型。默认类型指定于 $(C =) 字符之后: -) - ---- -T getResponse(T $(HILITE = int))(string question) { - // ... -} - -// ... - - auto age = getResponse("What is your age?"); ---- - -$(P -由于上面对 $(C getResponse()) 的调用没有指定类型,$(C T) 成为默认类型 $(C int),这个调用即等价于 $(C getResponse!int())。 -) - -$(P -亦能为 struct 和 class 模板指定默认参数,但在这种情况下,尽管模板参数列表是空的,我们却仍然必须总是写出它: -) - ---- -struct Point(T = int) { - // ... -} - -// ... - - Point!$(HILITE ()) center; ---- - - -$(P -与在 $(LINK2 /ders/d.cn/parameter_flexibility.html, 可变参数) 一章中看到的函数默认参数相似,可以为全部参数或仅为末尾参数指定模板默认参数: -) - ---- -void myTemplate(T0, T1 $(HILITE = int), T2 $(HILITE = char))() { - // ... -} ---- - -$(P -该函数的最后两个模板参数可以不显式指定,但第一个参数是必须指定的: -) - ---- - myTemplate!string(); ---- - -$(P -那么,第二第三个参数便分别是 $(C int) 与 $(C char)。 -) - -$(H5 每个不同的模板实例都是一个独特的类型) - -$(P -模板对于每一组类型的实例都是一个不同的类型。例如,$(C Point!int) 和 $(C Point!double) 就是区别的两个类型: -) - ---- -Point!int point3 = Point!double(0.25, 0.75); $(DERLEME_HATASI) ---- - -$(P -这样不同的类型不能被用于上面的赋值操作: -) - -$(SHELL_SMALL -Error: cannot implicitly convert expression (Point(0.25,0.75)) -of type $(HILITE Point!(double)) to $(HILITE Point!(int)) -) - -$(H5 是一种编译期功能) - -$(P -模板根本上是一种编译期功能。模板实例是在编译期由编译器生成的。 -) - -$(H5 class 模板示例:堆栈数据结构) - -$(P -struct 和 class 模板被广泛运用于数据结构的实现中。让我们设计一个可以容纳任意类型的堆栈容器。 -) - -$(P -堆栈是最简单的数据结构之一。它表示一个将元素概念上堆叠于其他元素之上的容器,就像一叠纸张一样。新的元素放置于顶端,并且只有在最顶端的元素可以被访问。若一个元素被移除,它总是最顶端的那个。 -) - -$(P -若我们再定义一个返回堆栈中元素总数的 property,那么这个数据结构的全部操作如下: -) - -$(UL -$(LI 添加元素 ($(C push()))) -$(LI 移除元素 ($(C pop()))) -$(LI 访问顶端元素 ($(C .top))) -$(LI 查询元素数量 ($(C .length))) -) - -$(P -可以用一个数组来存储这些元素,这样数组的最后一个元素将表示堆栈的顶端元素。最后,它可以被定义为一个 class 模板以用于容纳任何类型的元素: -) - ---- -$(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 -作为一个设计考虑,$(C push()) 和 $(C pop()) 被定义为普通的成员函数,而 $(C .top) 和 $(C .length) 则被定义为了 property,因为它们能被看作是提供了该堆栈的简单信息。 -) - -$(P -这里是一个测试改类的 $(C unittest) 语句块,使用了它的 $(C int) 实例: -) - ---- -unittest { - auto stack = new Stack$(HILITE !int); - - // 新添加的元素必须位于顶端 - stack.push(42); - assert(stack.top == 42); - assert(stack.length == 1); - - // .top 和 .length 不应该影响元素 - assert(stack.top == 42); - assert(stack.length == 1); - - // 新添加的元素必须位于顶端 - stack.push(100); - assert(stack.top == 100); - assert(stack.length == 2); - - // 移除末尾元素后必须暴露出前一个元素 - stack.pop(); - assert(stack.top == 42); - assert(stack.length == 1); - - // 最后一个元素移除后堆栈必须变空 - stack.pop(); - assert(stack.length == 0); -} ---- - -$(P -为了利用这个 class 模板,让我们这次试着以一个自定义类型使用它。作为示例,此处是一个修改过的 $(C Point) 版本: -) - ---- -struct Point(T) { - T x; - T y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} ---- - -$(P -一个包含 $(C Point!double) 类型元素的 $(C Stack) 可以以如下方式定义: -) - ---- - auto points = new Stack!(Point!double); ---- - -$(P -这里是一个向这个堆栈中加入十个元素并一一移除的程序: -) - ---- -$(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); - } -} - -// 返回在 -0.50 与 0.50 之间的一个随机值。 -double random_double() -out (result) { - assert((result >= -0.50) && (result < 0.50)); - -} body { - return (double(uniform(0, 100)) - 50) / 100; -} - -// 返回一个包含‘count’个随机 Point!double 元素的 Stack。 -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 -如这个程序的输出,元素以与添加相反的顺序被移除: -) - -$(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 函数模板示例:二分查找算法) - -$(P -二分查找是在有序序列中最快的搜索算法。它是一个非常简单的算法:仅考虑中位元素;若该元素就是搜索目标,那么结束搜索。若不是,那么依据该元素与搜索目标的大小关系在中位元素的左侧或右侧重复该算法。 -) - -$(P -在初始元素的子集中重复本身的算法是迭代式的。让我们以调用自身的方式实现该二分查找算法的迭代。 -) - -$(P -在将其转换为模板之前,让我们先实现仅支持 $(C int) 数组的该函数。以此为基础,加入模板参数列表并在定义中用 $(C T) 替代 $(C int),便能轻易地将其转化为一个模板。如下便是工作于 $(C int) 数组上的二分查找算法: -) - ---- -/* 若目标值存在于数组中,那么该函数就返回其索引值, - * 否则便返回 size_t.max。 */ -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 { - // 目标值仅可能存在于右手侧; - // 在右手侧中继续查找。 - auto index = - binarySearch(values[midPoint + 1 .. $], value); - - if (index != size_t.max) { - // 调整索引值; - // 在右手侧切片上它是基于 0 的。 - index += midPoint + 1; - } - - return index; - } - - assert(false, "We should have never gotten to this line"); -} ---- - -$(P -上述函数分四个步骤实现了这个简单的算法: -) - -$(UL -$(LI 若数组是空的,返回 $(C size_t.max) 以表示没有找到目标值。) -$(LI 若中位元素与目标值相等,那么返回该元素的索引。) -$(LI 若目标值较中位元素小,那么便在左手侧重复同样的算法。) -$(LI 反之,在右手侧重复同样的算法。) -) - -$(P -这是一个用于测试该函数的 unittest 语句块: -) - ---- -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 -现在这个函数已为 $(C int) 类型实现并测试,我们可以将其转换为模板了。$(C int) 只出现在了函数定义中的两处: -) - ---- -size_t binarySearch(const int[] values, in int value) { - // ……int 在这里没有出现过…… -} ---- - -$(P -在参数列表中出现的 $(C int) 是集合元素与目标值的类型。将这些指定为模板参数就足以将这个算法变为一个模板并使其也能被用于其他类型: -) - ---- -size_t binarySearch$(HILITE (T))(const $(HILITE T)[] values, in $(HILITE T) value) { - // ... -} ---- - -$(P -这个函数模板能应用于支持模板中所涉及操作的任何类型。在 $(C binarySearch()) 中,只有两个涉及到元素的操作,$(C ==) 与 $(C <): -) - ---- - if (value $(HILITE ==) values[midPoint]) { - // ... - - } else if (value $(HILITE <) values[midPoint]) { - - // ... ---- - -$(P -由是,$(C Point) 尚不能使用 $(C binarySearch()): -) - ---- -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 -上述程序将导致一个编译错误: -) - -$(SHELL_SMALL -Error: need member function $(HILITE opCmp()) for struct -const(Point!(int)) to compare -) - -$(P -根据错误消息,需要为 $(C Point) 定义 $(C opCmp())。$(C opCmp()) 详见 $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载) 一章: -) - ---- -struct Point(T) { -// ... - - int opCmp(const ref Point that) const { - return (x == that.x - ? y - that.y - : x - that.x); - } -} ---- - -$(H5 小结) - -$(P -我们将在 $(LINK2 /ders/d.cn/templates_more.html, 之后的一章) 中看到模板的更多特性。下面是本章中涵盖的内容: -) - -$(UL - -$(LI 模板像模型一样定义代码,以让编译器来根据它在程序中的实际使用生成实例。) - -$(LI 模板是一个编译期功能。) - -$(LI 指定模板参数列表就足以将函数、struct 和 class 定义变为模板。 - ---- -void functionTemplate$(HILITE (T))(T functionParameter) { - // ... -} - -class ClassTemplate$(HILITE (T)) { - // ... -} ---- - -) - -$(LI 模板参数能在感叹号后显式指定。当括号中仅有一个符号时可以省略括号。 - ---- - auto object1 = new ClassTemplate!(double); - auto object2 = new ClassTemplate!double; // 等价 ---- - -) - -$(LI 每一个不同的模板实例都是一个不同的类型。 - ---- - assert(typeid(ClassTemplate!$(HILITE int)) != - typeid(ClassTemplate!$(HILITE uint))); ---- - -) - -$(LI 只有函数模板参数能进行自动推断。 - ---- - functionTemplate(42); // 推断为 functionTemplate!int ---- - -) - -$(LI 模板能针对 $(C :) 字符之后的类型进行特化。 - ---- -class ClassTemplate(T $(HILITE : dchar)) { - // ... -} ---- - -) - -$(LI 默认模板参数指定于 $(C =) 字符之后。 - ---- -void functionTemplate(T $(HILITE = long))(T functionParameter) { - // ... -} ---- - -) - -) - -Macros: - SUBTITLE=模板 - - DESCRIPTION=介绍 D 的泛型编程功能。模板能够像模型一样定义代码,并让编译器根据该模板在程序中的使用自动生成真正的代码。 - - KEYWORDS=d 编程 语言 教程 模板 diff --git a/ddili/src/ders/d.cn/ternary.cozum.d b/ddili/src/ders/d.cn/ternary.cozum.d deleted file mode 100644 index 2622024..0000000 --- a/ddili/src/ders/d.cn/ternary.cozum.d +++ /dev/null @@ -1,33 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU 三元运算符 $(C ?:)) - -$(P -虽然在本练习中使用 $(C if-else) 语句可能更合理,但下面的程序还是使用了两个 $(C ?:) 运算符来实现需求: -) - ---- -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 -如果输入 0,程序也会显示“gained”。修改这个程序使其对 0 的输出更加合理的信息。 -) - -Macros: - SUBTITLE=三元运算符 ?: 习题解答 - - DESCRIPTION=D 语言编程习题解答:运算符 ?: - - KEYWORDS=D 编程语言教程 习题解答 diff --git a/ddili/src/ders/d.cn/ternary.d b/ddili/src/ders/d.cn/ternary.d deleted file mode 100644 index 1a1c529..0000000 --- a/ddili/src/ders/d.cn/ternary.d +++ /dev/null @@ -1,251 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX 三元运算符) $(IX ?:) $(IX 条件运算符) 三元运算符 $(CH4 ?:)) - -$(P -$(C ?:) 运算符与 $(C if-else) 语句的工作模式很像: -) - ---- - if (/* 条件检查 */) { - /* ... 如果条件为 ture 将执行的语句 */ - - } else { - /* ... 如果条件为 false 将执行的语句 */ - } ---- - -$(P -$(C if) 一定会执行 $(C true) 和 $(C false) 块中的一个。快速回忆一下:作为一个语句,$(C if) 并没有值,它只是影响代码块的执行流程。 -) - -$(P -而我们这章要学习的 $(C ?:) 运算符是一个表达式。除了有 $(C if-else) 的功能,它还会产生一个值。它等价于下面这样的代码: -) - ---- -/* 条件 */ ?/* 条件为 true 时执行的表达式 */ : /* 条件为 false 时执行的表达式 */ ---- - -$(P -由于 $(C ?:) 运算符一共使用了三个表达式,所以我们称它为三元运算符。 -) - -$(P -运算符产生的值为后两个表达式中的一个。别忘了它是表达式,所以它可以用在任何一个表达式能使用的地方。 -) - -$(P -下面这个例子对比了 $(C ?:) 运算符和 $(C if-else) 语句。对于和例子相似的情况来说,三元运算符通常更加简洁明了。 -) - -$(UL - -$(LI $(B 初始化) - -$(P -初始化一个变量,如果是闰年则将其初始化为 366,如果不是则初始化为 365: -) - ---- - int days = isLeapYear ?366 : 365; ---- - -$(P -如果使用 $(C if) 语句,我们只能先以无显式初始值的方式定义变量,然后在将需要的值赋给它: -) - ---- - int days; - - if (isLeapYear) { - days = 366; - - } else { - days = 365; - } ---- - -$(P -另一种使用 $(C if) 初始化的方法是先以非闰年的值初始化变量,如果是闰年则再使其自增 1: -) - ---- - int days = 365; - - if (isLeapYear) { - ++days; - } ---- - -) - -$(LI $(B 显示) - -$(P -在屏幕上显示信息,信息的一部分会随着条件的不同而改变: -) ---- - writeln("The glass is half ", - isOptimistic ?"full." : "empty."); ---- - -$(P -如果使用 $(C if),信息的最后一部分则需要单独输出: -) - ---- - write("The glass is half "); - - if (isOptimistic) { - writeln("full."); - - } else { - writeln("empty."); - } ---- - -$(P -当然也可以将整条信息分开输出: -) - ---- - if (isOptimistic) { - writeln("The glass is half full."); - - } else { - writeln("The glass is half empty."); - } ---- - -) - -$(LI $(B 运算) - -$(P -在一个西洋双陆棋的游戏中为胜者加分,如果全胜加 2 分,反之加 1 分: -) - ---- - score += isGammon ?2 : 1; ---- - -$(P -直接使用 $(C if) 的等价方法是: -) - ---- - if (isGammon) { - score += 2; - - } else { - score += 1; - } ---- - -$(P -当然也可以直接令其自增 1,之后再用 $(C if) 判断,如果全胜则令其再自增 1: -) - ---- - ++score; - - if (isGammon) { - ++score; - } ---- - -) - -) - -$(P -就像我们从上面的例子看到的那样,在特定情况下使用三元运算符能使代码更加简洁明了。 -) - -$(H5 三元表达式的返回的类型) - -$(P -三元运算符 $(C ?:) 的值为后两个表达式中的一个。这两个表达式的类型可以不同,但它们必须有一个$(I 公共类型)。 -) - -$(P -$(IX 公共类型) 表达式的公共类型是由一个相对复杂的算法定义的,涉及 $(LINK2 /ders/d.cn/cast.html, 类型转换) 和 $(LINK2 /ders/d.cn/inheritance.html, 继承)。除此之外,结果的$(I 值类型)取决于表达式,它 $(LINK2 /ders/d.cn/lvalue_rvalue.html, 既可能是左值也可能是右值)。我们会在之后的章节中了解到这些概念。 -) - -$(P -就眼下来说,你只需要接受一个公共类型就可以避免显式类型转换。例如,数字类型 $(C int) 和 $(C long) 就有一个公共类型,因为它们都可以被 $(C long) 储存。而 $(C int) 和 $(C string) 就没有公共类型,即 $(C int) 和 $(C string) 无法自动相互转换。 -) - -$(P -你可以对其使用 $(C typeof) 运算符并输出其结果的 $(C .stringof) 属性来判断表达式的类型。 -) - ---- - int i; - double d; - - auto result = someCondition ?i : d; - writeln(typeof(result)$(HILITE .stringof)); ---- - -$(P -由于 $(C double) 可以储存 $(C int) 而 $(C int) 无法储存 $(C double),这个三元表达式的公共类型即为 $(C double): -) - -$(SHELL -double -) - -$(P -下面这个例子是用于排版装载货物数量信息的,其中三元运算符的两个表达式没有公共类型。当数量为 12 时显示“A dozen”:“A $(B dozen) items will be shipped.”。如果数量不等于 12 则令消息包含精确的数字:“$(B 3) items will be shipped.”。 -) - -$(P -你可能会想对消息中变化的部分使用 $(C ?:) 运算符: -) - ---- - writeln( - (count == 12) ?"A dozen" : count, $(DERLEME_HATASI) - " items will be shipped."); ---- - -$(P -然而由于 $(STRING "A dozen") 的类型为 $(C string) 而 $(C count) 的类型为 $(C int),这个表达式没有公共类型。 -) - -$(P -解决方案是提前将 $(C count) 转换为 $(C string)。$(C std.conv) 模块中的 $(C to!string) 可将其参数转换为 $(C string)。 -) - ---- -import std.conv; -// ... - writeln((count == 12) ?"A dozen" : to!string(count), - " items will be shipped."); ---- - -$(P -现在 $(C ?:) 运算符的两个选择表达式都是 $(C string) 类型了,这样程序就可通过编译并正常运行。 -) - -$(PROBLEM_TEK - -$(P -编写一个程序来对业绩信息进行排版。程序接收一个 $(C int) 作为$(I 净增长额),整数表示收益,负数表示亏损。 -) - -$(P -若净增长额为正数,则程序的输出中应包含“gained”,反之则包含“lost”。例如:"$100 lost" 或 "$70 gained"。不要在这次练习中使用 $(C if),即使你觉得它写起来很舒服。 -) - -) - - -Macros: - SUBTITLE=三元运算符 ?: - - DESCRIPTION=D 语言中的 ?: 运算符及它与 if-else 语句的对比 - - KEYWORDS=D 编程语言教程 三元运算符 diff --git a/ddili/src/ders/d.cn/title.html b/ddili/src/ders/d.cn/title.html deleted file mode 100644 index f189df7..0000000 --- a/ddili/src/ders/d.cn/title.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - -

    -Programming in D -

    - -
    - -

    -First Edition -

    - -
    - -

    -Ali Çehreli -

    - -
    - -
    -

    -Edited by Luís Marques -

    -
    - -
    diff --git a/ddili/src/ders/d.cn/to_be_continued.d b/ddili/src/ders/d.cn/to_be_continued.d deleted file mode 100644 index d327691..0000000 --- a/ddili/src/ders/d.cn/to_be_continued.d +++ /dev/null @@ -1,17 +0,0 @@ -Ddoc - -$(B) -$(H4 The End) - -$(P -You can use $(LINK2 /ders/d.de/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/ddili/src/ders/d.cn/toc_head.html b/ddili/src/ders/d.cn/toc_head.html deleted file mode 100644 index 345f048..0000000 --- a/ddili/src/ders/d.cn/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    Contents

    diff --git a/ddili/src/ders/d.cn/toc_tail.html b/ddili/src/ders/d.cn/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/d.cn/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/d.cn/tuples.d b/ddili/src/ders/d.cn/tuples.d deleted file mode 100644 index 2374585..0000000 --- a/ddili/src/ders/d.cn/tuples.d +++ /dev/null @@ -1,570 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX tuple) $(IX Tuple, std.typecons) 元组) - -$(P -元组是用来合并多值并将其作为一个单一对象使用的。这是一个标准库所提供的功能,即模块 $(C std.typecons) 中的 $(C Tuple) 模板。 -) - -$(P -$(C Tuple) 凭借模块 $(C std.meta) 中的 $(C AliasSeq) 来实现它的一部分功能。 -) - -$(P -这一章只涵盖了元组较为常见的操作。更多有关元组及模板的信息详见 $(LINK2 https://github.com/PhilippeSigaud/D-templates-tutorial, Philippe Sigaud's $(I D Templates: A Tutorial))。 -) - -$(H5 $(C Tuple) 与 $(C tuple())) - -$(P -元组常常用便捷的 $(C tuple()) 函数来构建: -) - ---- -import std.stdio; -import std.typecons; - -void main() { - auto t = $(HILITE tuple(42, "hello")); - writeln(t); -} ---- - -$(P -上面对 $(C tuple) 的调用构造了一个包含值为 42 的 $(C int) 与值为 $(STRING "hello") 的 $(C string) 的对象。上述程序的输出包括该元组对象的类型与它的成员: -) - -$(SHELL -Tuple!(int, string)(42, "hello") -) - -$(P -上面的元组类型与下面 $(C struct) 的定义是等价的,而事实上也几乎是这样实现的: -) - ---- -// 与 Tuple!(int, string) 等价 -struct __Tuple_int_string { - int __member_0; - string __member_1; -} ---- - -$(P -元组成员通常使用索引来访问。这种语法暗示了元组可以被视为包含不同类型元素的数组。 -) - ---- - writeln(t$(HILITE [0])); - writeln(t$(HILITE [1])); ---- - -$(P -输出为: -) - -$(SHELL -42 -hello -) - -$(H6 成员属性) - -$(P -如果元组直接由 $(C Tuple) 模板构建而非调用 $(C tuple()) 函数,那么便可能以相应的成员属性来访问成员。成员的类型与名称可以以模板参数的方式来指定: -) - ---- - auto t = Tuple!(int, "number", - string, "message")(42, "hello"); ---- - -$(P -上述的定义使 $(C .number) 与 $(C .message) 也可以访问相应的成员: -) - ---- - 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 -输出为: -) - -$(SHELL -by index 0 : 42 -by .number : 42 -by index 1 : hello -by .message: hello -) - -$(H6 $(IX .expand) 展开成员为列表) - -$(P -元组成员能被展开为一列值,举例来说,这能被用作调用函数时的参数列表。元组的 $(C .expand) 属性或是切片操作都可以用来展开成员: -) - ---- -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'); - - // 下面的两行都等价于 foo(1, "2", 3.3, '4'): - foo(t$(HILITE .expand)); - foo(t$(HILITE [])); - - // 与 bar(1, 3.3, '4') 等价: - bar(t$(HILITE [0]), t$(HILITE [$-2..$])); -} ---- - -$(P -上面的元组包含四个类型分别为 $(C int)、$(C string)、$(C double) 与 $(C char) 的值。由于这些类型与 $(C foo()) 的参数列表相匹配,它成员的展开能够被用作 $(C foo()) 的参数。当调用 $(C bar()) 时,一个匹配的参数列表由该元组的第一个与最后两个成员构成。 -) - -$(P -只要元组成员能够兼容成为同一个数组的元素,那么其成员的展开就可以作为一个数组字面量的元素: -) - ---- -import std.stdio; -import std.typecons; - -void main() { - auto t = tuple(1, 2, 3); - auto a = [ t.expand, t[] ]; - writeln(a); -} ---- - -$(P -上述数组字面量根据同一个元组的两次展开初始化而成: -) - -$(SHELL -[1, 2, 3, 1, 2, 3] -) - -$(H6 $(IX foreach, 编译期) $(IX 编译期 foreach) 编译期 $(C foreach)) - -$(P -因为元组包含的值可以被展开,所以它们也可以被用于 $(C foreach) 语句中: -) - ---- - auto t = tuple(42, "hello", 1.5); - - foreach (i, member; $(HILITE t)) { - writefln("%s: %s", i, member); - } ---- - -$(P -输出为: -) - -$(SHELL -0: 42 -1: hello -2: 1.5 -) - -$(P -$(IX 展开) -上述 $(C foreach) 语句或许会给人以一个错误的印象——被当成一个运行时的循环体。事实并非如此。一个基于元组的 $(C foreach) 语句会针对每一个元组成员进行$(I 展开)。由是,上述 $(C foreach) 语句等价于如下代码: -) - ---- - { - 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 -进行展开的根本原因在于元组的成员可能具有不同的类型, $(C foreach) 语句块必须根据每一个类型以不同的方式被编译。 -) - -$(H6 从函数中返回多个值) - -$(P -$(IX findSplit, std.algorithm) 元组可以作为一个对函数必须返回单一值这一限制的简单解决方式。这样的一个例子是 $(C std.algorithm.findSplit)。$(C findSplit()) 在一个区间中搜索另一个区间并生成一个包含三个片段的结果,即在子区间之前的部分、找到的子区间、以及子区间之后的部分: -) - ---- -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 -输出为: -) - -$(SHELL -before: he -found : ll -after : o -) - -$(P -从函数中返回多个值的另一种选择是返回一个 $(C struct) 对象: -) - ---- -struct Result { - // ... -} - -$(HILITE Result) foo() { - // ... -} ---- - -$(H5 $(IX AliasSeq, std.meta) $(C AliasSeq)) - -$(P -$(C AliasSeq) 定义于 $(C std.meta) 模块中。它用于表示一个通常情况下属于编译器的概念,换句话说,不是一个能被程序员掌控的实体:一个以逗号分隔的由值、类型、符号(即 $(C alias) 模板参数)组成的列表。下面是三个这种列表的例子: -) - -$(UL -$(LI 函数参数列表) -$(LI 模板参数列表) -$(LI 数组字面量元素列表) -) - -$(P -如下三行代码依次是上述列表的实例: -) - ---- - foo($(HILITE 1, "hello", 2.5)); // 函数参数 - auto o = Bar!($(HILITE char, long))(); // 模板参数 - auto a = [ $(HILITE 1, 2, 3, 4) ]; // 数组字面量元素 ---- - -$(P -$(C Tuple) 的展开就是对 $(C AliasSeq) 的充分利用。 -) - -$(P -$(IX TypeTuple, std.typetuple) $(C AliasSeq) 的名称来源于“alias sequence”即“别名序列”,它可以包含类型、值与符号。($(C AliasSeq) 与 $(C std.meta) 曾经分别被称为 $(C TypeTuple) 与 $(C std.typetuple)) -) - -$(P -本章只举出了完全由值或是完全由类型组成的 $(C AliasSeq) 之实例。同时包含了类型与值的例子将会在下一章中出现。$(C AliasSeq) 对于可变参数模板特别有用,这我们亦会在下一章中看到。 -) - -$(H6 由值组成的 $(C AliasSeq)) - -$(P -$(C AliasSeq) 所表示的值是通过模板参数的方式来指定的。 -) - -$(P -想象一个接受三个参数的函数: -) - ---- -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 -这个函数通常需用三个参数进行调用: -) - ---- - foo(1, "hello", 2.5); ---- - -$(P -而 $(C AliasSeq) 能够合并这些参数为一个单一的实体,而且这个实体可以自动在调用函数的时候展开: -) - ---- -import std.meta; - -// ... - - alias arguments = AliasSeq!(1, "hello", 2.5); - foo($(HILITE arguments)); ---- - -$(P -尽管看起来调用该函数只用了一个参数,上面对 $(C foo()) 的调用与之前那个相等价。因此,两者都产生相同的输出: -) - -$(SHELL -foo is called with 1, hello, and 2.5. -) - -$(P -还需注意的是 $(C arguments) 并非作为变量被定义,比如说以 $(C auto) 的形式。恰恰相反,它是一个特定 $(C AliasSeq) 模板实现的 $(C alias)。尽管定义 $(C AliasSeq) 的变量亦是可能的,本章中的例子只会将其作为别名使用。 -) - -$(P -如我们之前对 $(C Tuple) 的了解一样,当一个 $(C AliasSeq) 所有包含的值能够兼容成为同一个数组的元素,那么它就亦可以用于初始化数组字面量: -) - ---- - alias elements = AliasSeq!(1, 2, 3, 4); - auto arr = [ $(HILITE elements) ]; - assert(arr == [ 1, 2, 3, 4 ]); ---- - -$(H6 索引与切片) - -$(P -与 $(C Tuple) 一样,$(C AliasSeq) 的成员可以通过索引与切片访问: -) - ---- - alias arguments = AliasSeq!(1, "hello", 2.5); - assert(arguments$(HILITE [0]) == 1); - assert(arguments$(HILITE [1]) == "hello"); - assert(arguments$(HILITE [2]) == 2.5); ---- - -$(P -若有一个函数的参数与上述 $(C AliasSeq) 的最后两个成员相匹配,那么这个函数便可以用该 $(C AliasSeq) 的一个切片调用: -) - ---- -void bar(string s, double d) { - // ... -} - -// ... - - bar(arguments$(HILITE [$-2 .. $])); ---- - -$(H6 由类型组成的 $(C AliasSeq)) - -$(P -$(C AliasSeq) 可以包含类型。换句话说,不是一个特定类型的特定值,而是一个如 $(C int) 本身的类型。一个由类型组成的 $(C AliasSeq) 能用以表示一系列模板参数。 -) - -$(P -这里我们将一个 $(C AliasSeq) 用于一个有两个参数的 $(C struct) 模板。第一个参数决定其数组成员的元素类型,第二个参数决定其函数成员的返回值: -) - ---- -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 -在上述代码中,我们看到模板使用 $(C (double, int)) 进行实例化。而一个 $(C AliasSeq) 也可以用来表示同样的参数列表: -) - ---- -import std.meta; - -// ... - - alias Types = AliasSeq!(double, int); - auto s = S!$(HILITE Types)([ 1, 2, 3 ]); ---- - -$(P -尽管这看起来像是一个单一的模板参数,但由于 $(C Types) 会自动展开,模板实例如之前一样变为 $(C S!(double, int))。 -) - -$(P -$(C AliasSeq) 在$(I 可变参数模板)中特别有用。我们将会在下一章中看到这样的例子。 -) - -$(H6 将 $(C AliasSeq) 用于 $(C foreach)) - -$(P -与 $(C Tuple) 一样,基于 $(C AliasSeq) 的 $(C foreach) 语句并非一个运行时的循环,它会针对每一个成员展开循环体。 -) - -$(P -这是一个相应的例子,一个为上面定义的 $(C S) 结构所写的单元测试。如下代码以 $(C int)、$(C long) 与 $(C float) 作为元素类型来测试 $(C S)(这个例子中 $(C ResultT) 始终是 $(C size_t)): -) - ---- -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 -$(C foreach) 变量 $(C Type) 依次对应 $(C int)、$(C long) 及 $(C float)。由是,该 $(C foreach) 语句将与如下语句等价地被编译: -) - ---- - { - 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) 属性) - -$(P -$(C .tupleof) 表示一个类型或对象的全部成员。当被用于一个用户定义的类型时,$(C .tupleof) 提供对该类型成员定义的访问: -) - ---- -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) 在此程序中出现了两次。首先,所有元素的类型由将 $(C typeof) 加于 $(C .tupleof) 取得,因而每一个类型都依次表现为变量 $(C MemberType)。其次,每个元素的名称由 $(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) 也可以用于一个具体的对象。在这种情况下,它将生成一个包含该对象所有成员的值的元组: -) - ---- - 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 -$(C foreach) 变量 $(C member) 表示该对象的每一个成员: -) - -$(SHELL -Member 0: - type : int - value: 42 -Member 1: - type : string - value: hello -Member 2: - type : double - value: 1.5 -) - -$(P -此处很重要的一点是由 $(C .tupleof) 返回的元组包含的是对象成员自身,而非其拷贝。换言之,该元组的成员是对实际对象成员的引用。 -) - -$(H5 总结) - -$(UL - -$(LI $(C tuple()) 合并不同类型的值,类似于一个 $(C struct) 对象。) - -$(LI 直接使用 $(C Tuple) 能够以成员属性访问成员。) - -$(LI 元组能用 $(C .expand) 或切片操作被展开为值列表。) - -$(LI 基于元组的 $(C foreach) 不是运行时循环,而是将循环体进行展开。) - -$(LI $(C AliasSeq) 表示诸如函数参数列表、模板参数列表、数组字面量元素列表之类的概念。) - -$(LI $(C AliasSeq) 能够包含值和类型。) - -$(LI 元组支持索引与切片。) - -$(LI $(C .tupleof) 提供有关类型、对象之成员的信息。) - -) - -macros: - SUBTITLE=元组 - - DESCRIPTION=合并值与类型,并以访问同一个对象成员的方式访问它们。 - - KEYWORDS=D 编程语言教程 Tuple AliasSeq tuple diff --git a/ddili/src/ders/d.cn/uda.d b/ddili/src/ders/d.cn/uda.d deleted file mode 100644 index 9c0bb33..0000000 --- a/ddili/src/ders/d.cn/uda.d +++ /dev/null @@ -1,421 +0,0 @@ -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 diff --git a/ddili/src/ders/d.cn/ufcs.d b/ddili/src/ders/d.cn/ufcs.d deleted file mode 100644 index 1be87c3..0000000 --- a/ddili/src/ders/d.cn/ufcs.d +++ /dev/null @@ -1,220 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX UFCS) $(IX 通用 函数 调用 语法) 通用函数调用语法(UFCS)) - -$(P -UFCS 是编译器自动应用的特性。它能让普通函数使用成员函数的语法。语法可以用两个表达的对比来简单描述: -) - ---- - variable.foo($(I 参数)) ---- - -$(P -当编译器遇到如上的表达式,如果使用给定的参数,变量 $(C variable) 没有能调用的成员函数 $(C foo),那么编译器会尝试如下的表达式: -) - ---- - foo(variable, $(I 参数)) ---- - -$(P -如果能够被编译,那编译器就会简单的接受这个新的表达式。因此,尽管 $(C foo()) 显然是一个普通函数,但是用成员函数的调用语法同样也能被接受。 -) - -$(P -我们知道与类型紧密相关的函数都通常定义为这个类型的成员函数。这一点对于封装而言尤其重要,只有类型的成员函数(以及类型所在的模块)才能访问其$(C 私有)成员。 -) - -$(P -来看一个 $(C Car) 类的实例,这个类维护着燃油的数量: -) - ---- -$(CODE_NAME Car)class Car { - enum economy = 12.5; // 千米/升(平均) - private double fuelAmount; // 升 - - this(double fuelAmount) { - this.fuelAmount = fuelAmount; - } - - double fuel() const { - return fuelAmount; - } - - // ... -} ---- - -$(P -尽管成员函数非常有用,有时候也是必须的,但并不是所有操作某类型的函数都应当是成员函数。对于真正的程序,有些类型的操作非常特殊,因而不能作为成员函数。例如,确定一辆车是否能行驶指定的距离这样的函数定义为普通函数会更为合适: -) - ---- -$(CODE_NAME canTravel)bool canTravel(Car car, double distance) { - return (car.fuel() * car.economy) >= distance; -} ---- - -$(P -这种自然的定义会让与类相关的函数的调用方式产生分歧:在这两种语法里,对象出现在了不同的位置: -) - ---- -$(CODE_XREF Car)$(CODE_XREF canTravel)void main() { - auto car = new Car(5); - - auto remainingFuel = $(HILITE car).fuel(); // 成员函数调用语法 - - if (canTravel($(HILITE car), 100)) { // 普通函数调用语法 - // ... - } -} ---- - -$(P -UFCS 通过允许普通函数的调用使用成员函数的语法消除了这种分歧: -) - ---- - if ($(HILITE car).canTravel(100)) { // 普通函数,调用采用成员函数调用语法 - // ... - } ---- - -$(P -该特性对于基础类型同样适用,包括字面量: -) - ---- -int half(int value) { - return value / 2; -} - -void main() { - assert(42.half() == 21); -} ---- - -$(P -在下一章我们会看到,当函数没有传入的参数时,函数调用可以省略括号。与这个特性一起使用是,上面的表达式会变得更加小巧。下面的三种语句是等同的: -) - ---- - result = half(value); - result = value.half(); - result = value.half; ---- - -$(P -$(IX 链式, 函数调用) $(IX 函数调用链) 当函数调用是$(I 链式)形式时,UFCS 尤其好用。我们来看个例子,例子中包含了一组操作 $(C int) 切片的函数: -) - ---- -$(CODE_NAME functions)// 返回所有元素除以‘divisor’的结果 -int[] divide(int[] slice, int divisor) { - int[] result; - result.reserve(slice.length); - - foreach (value; slice) { - result ~= value / divisor; - } - - return result; -} - -// 返回所有元素乘以‘multiplier’的结果 -int[] multiply(int[] slice, int multiplier) { - int[] result; - result.reserve(slice.length); - - foreach (value; slice) { - result ~= value * multiplier; - } - - return result; -} - -// 筛选出所有偶数 -int[] evens(int[] slice) { - int[] result; - result.reserve(slice.length); - - foreach (value; slice) { - if (!(value % 2)) { - result ~= value; - } - } - - return result; -} ---- - -$(P -如果用普通函数语法,而不是 UFCS,链式调用这三个函数的表达式可以如下写出: -) - ---- -$(CODE_XREF functions)import std.stdio; - -// ... - -void main() { - auto values = [ 1, 2, 3, 4, 5 ]; - writeln(evens(divide(multiply(values, 10), 3))); -} ---- - -$(P -values 首先乘以 10,然后除以 3,最后只有偶数会被输出: -) - -$(SHELL -[6, 10, 16] -) - -$(P -上面表达式的问题在于,尽管 $(C multiply) 和 $(C 10) 对是相关的,$(C divide) 和 $(C 3) 对是相关的,结果最终两者都写得很开。UFCS 消除了这个问题,允许用更自然的方式反映实际的操作顺序: -) - ---- - writeln(values.multiply(10).divide(3).evens); ---- - -$(P -一些程序员甚至对像 $(C writeln()) 这样的调用都使用 UFCS: -) - ---- - values.multiply(10).divide(3).evens.writeln; ---- - -$(P -顺便提一下,上面的程序完全可以用 $(C map()) 和 $(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 -上面的程序使用了 $(LINK2 /ders/d.cn/templates.html, 模板),$(LINK2 /ders/d.cn/ranges.html, ranges),以及 $(LINK2 /ders/d.cn/lambda.html, lambda 函数),所有这些都会在下面的章节中阐述。 -) - -Macros: - SUBTITLE=通用函数调用语法 (UFCS) - - DESCRIPTION=通用函数调用语法:普通函数使用成员函数调用语法的能力。 - - KEYWORDS=d 编程 课程 教程 封装 diff --git a/ddili/src/ders/d.de/Makefile.in b/ddili/src/ders/d.de/Makefile.in deleted file mode 100644 index 90f541c..0000000 --- a/ddili/src/ders/d.de/Makefile.in +++ /dev/null @@ -1,19 +0,0 @@ -DERS_SON_D=to_be_continued.d - -# Taken out for the draft release -# foreword1.d \ - -DERS_D_BOLUMLER= \ - foreword2.d \ - preface.d \ - hello_world.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - hello_world.cozum.d \ - -include Makefile.ders.in -$(eval $(call derse_ozel,d.de,Programmierung_in_D,german)) diff --git a/ddili/src/ders/d.de/breadcrumbs.ddoc b/ddili/src/ders/d.de/breadcrumbs.ddoc deleted file mode 100644 index 37a58e3..0000000 --- a/ddili/src/ders/d.de/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > Prg in D - -BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > $(LINK2 /ders/d.de/index.html, Prg in D) diff --git a/ddili/src/ders/d.de/code/README b/ddili/src/ders/d.de/code/README deleted file mode 100644 index 028b9e5..0000000 --- a/ddili/src/ders/d.de/code/README +++ /dev/null @@ -1,22 +0,0 @@ -This directory contains most of the code samples that appear in the book -"Programming in D": - - http://ddili.org/ders/d.en/ - -The names of the sample programs are in chapterName.N.d format. The programs -are extracted from the chapter files and named automatically. - - chapterName - The name of the chapter file without the .html extension, from - which the program was extracted (e.g. hello_world for - hello_world.html) - - N - The order in which the program was extracted from that chapter - file (e.g. 1) - -For example, hello_world.1.d is the first program that was extracted from -hello_world.html. - -The word 'cozum' (transliteration of Turkish "çözüm") that appears in some of -the chapter file names has been replaced with its English translation -(solution). Accordingly, unit_testing.solution.2.d is the second program that -was extracted from unit_testing.cozum.html. diff --git a/ddili/src/ders/d.de/copyright.d b/ddili/src/ders/d.de/copyright.d deleted file mode 100644 index 3d18fe4..0000000 --- a/ddili/src/ders/d.de/copyright.d +++ /dev/null @@ -1,110 +0,0 @@ -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-2015 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 -Printed by IngramSpark -) -$(P -ISBN 978-0-692-52957-7 -) -$(BR) -$(P -or by CreateSpace -) -$(P -ISBN 978-1515074601 -) - -
    - -Macros: - SUBTITLE=Copyleft - - DESCRIPTION=The copyleft page of Programming in D - - KEYWORDS=copyright diff --git a/ddili/src/ders/d.de/derse_ozel.ddoc b/ddili/src/ders/d.de/derse_ozel.ddoc deleted file mode 100644 index 9b60d0a..0000000 --- a/ddili/src/ders/d.de/derse_ozel.ddoc +++ /dev/null @@ -1,47 +0,0 @@ -MAIN_TITLE=Programming in D -SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference -SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english - -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.de/rss.xml, RSS Programming in D RSS Feed) -$(BR) -$(LINK2 /ders/d.de/index.html, -Download or buy $(IMG book.png)) - -DUSEY_NAVIGASYON= -
    Other D Resources
    -$(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)) -) - -$(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)) -) - -MINIBASLIK = $0 - - -BASLIK =

    $0

    - -ALTBASLIK =
    $0
    - - -MINI_SOZLUK= - -GERI_METIN=Prev -ILERI_METIN=Next -PROBLEM_METIN=Exercise -PROBLEM_COK_METIN=Exercises -PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... -PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... -COZUM_METIN=the solution -COZUMLER_METIN=the solutions - -DERLEME_HATASI_METIN=compilation ERROR diff --git a/ddili/src/ders/d.de/halftitle.html b/ddili/src/ders/d.de/halftitle.html deleted file mode 100644 index bbfcfd9..0000000 --- a/ddili/src/ders/d.de/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -Programming in D -

    - -

    -Ali Çehreli -

    - -
    diff --git a/ddili/src/ders/d.de/index.d b/ddili/src/ders/d.de/index.d deleted file mode 100644 index 4a7dba5..0000000 --- a/ddili/src/ders/d.de/index.d +++ /dev/null @@ -1,148 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Programming in D) - -
    - - - -$(P -The paper version of this book is available through two publishing platforms: -) - -$(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.de/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) -) - -$(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.de/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.epub, EPUB) (for most ebook readers), $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.azw3, AZW3) (for newer Kindles), or $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.mobi, MOBI) (for older Kindles).) - -) - -$(H5 Web version) - -$(UL -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.de/foreword1.html, Foreword by Walter Bright)) -) -$(LI $(LINK2 /ders/d.de/ix.html, Book Index)) -$(LI $(LINK2 /ders/d.de/foreword2.html, Foreword by Andrei Alexandrescu)) -$(LI $(LINK2 /ders/d.de/preface.html, Preface)) -$(LI $(LINK2 /ders/d.de/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.de/writeln.html, writeln and write)) -$(LI $(LINK2 /ders/d.de/compiler.html, Compilation)) -$(LI $(LINK2 /ders/d.de/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) -$(LI $(LINK2 /ders/d.de/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.de/variables.html, Variables)) -$(LI $(LINK2 /ders/d.de/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.de/input.html, Reading from the Standard Input)) -$(LI $(LINK2 /ders/d.de/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d.de/if.html, if Statement) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.de/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.de/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.de/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN <> !<>= (and more))) -$(LI $(LINK2 /ders/d.de/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.de/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.de/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. $ .dup capacity)) -$(LI $(LINK2 /ders/d.de/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.de/stream_redirect.html, Redirecting Standard Input and Output Streams)) -$(LI $(LINK2 /ders/d.de/files.html, Files) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d.de/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.de/name_space.html, Name Scope)) -$(LI $(LINK2 /ders/d.de/for.html, for Loop) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d.de/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.de/literals.html, Literals)) -$(LI $(LINK2 /ders/d.de/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.de/formatted_input.html, Formatted Input)) -$(LI $(LINK2 /ders/d.de/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d.de/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.de/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.de/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d.de/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.de/functions.html, Functions) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.de/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.de/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.de/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d.de/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d.de/lazy_operators.html, Lazy Operators)) -$(LI $(LINK2 /ders/d.de/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d.de/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d.de/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.de/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.de/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.de/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.de/lifetimes.html, Lifetimes and Fundamental Operations)) -$(LI $(LINK2 /ders/d.de/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.de/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.de/struct.html, Structs) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.de/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ __PRETTY_FUNCTION__)) -$(LI $(LINK2 /ders/d.de/function_overloading.html, Function Overloading)) -$(LI $(LINK2 /ders/d.de/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.de/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.de/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.de/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) -$(LI $(LINK2 /ders/d.de/class.html, Classes) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.de/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d.de/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.de/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.de/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.de/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.de/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.de/ufcs.html, Universal Function Call Syntax (UFCS))) -$(LI $(LINK2 /ders/d.de/property.html, Properties) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.de/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d.de/templates.html, Templates)) -$(LI $(LINK2 /ders/d.de/pragma.html, Pragmas)) -$(LI $(LINK2 /ders/d.de/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d.de/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.de/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.de/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.de/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d.de/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.de/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.de/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) -$(LI $(LINK2 /ders/d.de/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.de/union.html, Unions) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.de/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d.de/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.de/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.de/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.de/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.de/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.de/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) -$(LI $(LINK2 /ders/d.de/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.de/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.de/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.de/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.de/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.de/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.de/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/ddili/src/ders/d.de/index_section_head.html b/ddili/src/ders/d.de/index_section_head.html deleted file mode 100644 index 862063a..0000000 --- a/ddili/src/ders/d.de/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Index

    diff --git a/ddili/src/ders/d.de/index_section_tail.html b/ddili/src/ders/d.de/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.de/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.de/pdf.derse_ozel.css b/ddili/src/ders/d.de/pdf.derse_ozel.css deleted file mode 100644 index fe51491..0000000 --- a/ddili/src/ders/d.de/pdf.derse_ozel.css +++ /dev/null @@ -1,31 +0,0 @@ -a.xref:after { - content: " (page " target-counter(attr(href, url), page) ")"; -} - -div.cozum_link_cok a.xref { - content: "The solutions are on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_cok a.xref:after { - content: normal; -} - -div.cozum_link_tek a.xref { - content: "The solution is on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_tek a.xref:after { - content: normal; -} - -div.cozum_link_cok, div.cozum_link_tek { - padding-top: .1em; - page-break-before: avoid; -} - -body -{ - counter-reset: h4 -2; -} diff --git a/ddili/src/ders/d.de/pdf_cozum_head.html b/ddili/src/ders/d.de/pdf_cozum_head.html deleted file mode 100644 index d7ebaae..0000000 --- a/ddili/src/ders/d.de/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Exercise Solutions

    diff --git a/ddili/src/ders/d.de/pdf_cozum_tail.html b/ddili/src/ders/d.de/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.de/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.de/pdf_html_head.html b/ddili/src/ders/d.de/pdf_html_head.html deleted file mode 100644 index d75ecee..0000000 --- a/ddili/src/ders/d.de/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - Programming in D - - - diff --git a/ddili/src/ders/d.de/pdf_html_tail.html b/ddili/src/ders/d.de/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/d.de/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/d.de/preface.d b/ddili/src/ders/d.de/preface.d deleted file mode 100644 index a5cef8f..0000000 --- a/ddili/src/ders/d.de/preface.d +++ /dev/null @@ -1,70 +0,0 @@ -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, Stéphane Goujet, Olivier Pisano, Dave Yost, Tomasz Miazek-Mioduszewski, Gerard Vreeswijk, Justin Whear, Gerald Jansen, and Sylvain Gault. -) - -$(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, and Ergin Güney 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 November 2015) -) - -) - -Macros: - SUBTITLE = Preface - DESCRIPTION= - KEYWORDS= diff --git a/ddili/src/ders/d.de/rss.xml b/ddili/src/ders/d.de/rss.xml deleted file mode 100644 index 1e35711..0000000 --- a/ddili/src/ders/d.de/rss.xml +++ /dev/null @@ -1,812 +0,0 @@ - - - - - Programming in D - http://ddili.org/ders/d.en/ - Programming with the D programming language - en - D Programming Language Book - - - 'deprecated', 'extern', and 'extern()' - http://ddili.org/ders/d.en/modules.html - Added 'deprecated' that helps with deprecation process of library features, 'extern' that is for declaring external symbols, and 'extern()' that allows interacting with libraries of other languages (extern(C), extern(C++, std), etc.). - Chapter - 24 Oct 2015 22:00 - - - - Code Samples .zip File - http://ddili.org/ders/d.en/index.html - You can download most of the code samples that appear in the book as a .zip file. - Code - 16 Oct 2015 20:00 - - - - Book Index - http://ddili.org/ders/d.en/ix.html - The index section of the book. - Chapter - 21 Aug 2015 02:00 - - - - Fibers - http://ddili.org/ders/d.en/fibers.html - Cooperative multitasking in D with fibers. - Chapter - 17 Aug 2015 15:00 - - - - Pragmas - http://ddili.org/ders/d.en/pragma.html - Pragmas that allow interactions with the compiler, including the most recent pragma(inline). - Chapter - 17 Aug 2015 15:00 - - - - Operator Precedence - http://ddili.org/ders/d.en/operator_precedence.html - The rules that specify the execution order of chained operators and the expressions that they use. - Chapter - 17 Aug 2015 15:00 - - - - Foreword by Andrei Alexandrescu - http://ddili.org/ders/d.en/foreword2.html - "Instead of falling for getting things done quickly, 'Programming in D' focuses on getting things done properly, to the lasting benefit of its reader." - Chapter - 17 Aug 2015 15:00 - - - - Ebook versions - http://ddili.org/ders/d.en/index.html - In addition to the PDF version, now there are EPUB and AZW3 versions as well. - Ebook - 15 Dec 2014 02:00 - - - - '.offsetof' property and 'align' attribute - http://ddili.org/ders/d.en/memory.html - The .offsetof property to get the offsets of and the align attribute to specify the alignments of struct members. - Chapter - 25 Nov 2014 17:00 - - - - Contract inheritance - http://ddili.org/ders/d.en/invariant.html - Inheriting 'in' and 'out' blocks of interface and class functions. - Chapter - 25 Nov 2014 17:00 - - - - The special keywords - http://ddili.org/ders/d.en/templates_more.html - __MODULE__, __FILE__, __LINE__, __FUNCTION__, and __PRETTY_FUNCTION__ - Chapter - 25 Nov 2014 17:00 - - - - pragma - http://ddili.org/ders/d.en/templates.html - The pragma directive - Chapter - 25 Nov 2014 17:00 - - - - Nested Functions, Structs, and Classes - http://ddili.org/ders/d.en/nested.html - Defining functions, structs, and classes inside existing scopes. - Chapter - 25 Nov 2014 17:00 - - - - Lvalues and Rvalues - http://ddili.org/ders/d.en/lvalue_rvalue.html - Lvalues, rvalues, their differences, and 'auto ref' parameter type that can accept either kind. - Chapter - 25 Nov 2014 17:00 - - - - The 'Index' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated index section for the PDF version of the book. - Chapter - 25 Nov 2014 17:00 - - - - The 'Table of Contents' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated TOC section for the PDF version of the book. - Chapter - 28 Sep 2014 23:00 - - - - The attributes of Throwable - http://ddili.org/ders/d.en/exceptions.html - The attributes of the Throwable interface, collateral exceptions, and an example of accessing collateral exceptions through the .next property. - Chapter - 18 Sep 2014 16:00 - - - - Selective, local, renamed, and package imports - http://ddili.org/ders/d.en/modules.html - Importing modules selectively, locally, under a different name, and as a package. - Chapter - 17 Sep 2014 00:30 - - - - @disable - http://ddili.org/ders/d.en/special_functions.html - The @disable attribute to disable special functions of structs. - Chapter - 12 Sep 2014 00:15 - - - - Operator overloading for multi-dimensional indexing and slicing - http://ddili.org/ders/d.en/templates_more.html - The templated versions of opDollar, opIndex, and opSlice, and examples of using them to support multiple indexes inside square brackets. - Chapter - 11 Sep 2014 00:15 - - - - static this, static ~this, shared static this, and shared static ~this - http://ddili.org/ders/d.en/index.html - Specifying the initial and final operations of threads and programs. - Chapter - 26 Aug 2014 23:30 - - - - User Defined Attributes (UDA) - http://ddili.org/ders/d.en/uda.html - Assigning user defined attributes to type and variable declarations, testing the attributes at compile time, and compiling the program according to those attributes. - Chapter - 26 Aug 2014 22:30 - - - - Memory Management - http://ddili.org/ders/d.en/memory.html - The garbage collector, allocating memory, and placing objects at specific locations in memory. - Chapter - 24 Jul 2014 01:00 - - - - Type traits - http://ddili.org/ders/d.en/cond_comp.html - Using type traits in conditional compilation. - Chapter - 29 May 2014 20:30 - - - - Data Sharing Concurrency - http://ddili.org/ders/d.en/concurrency_shared.html - Multi-threaded programming in D by data sharing. - Chapter - 20 May 2014 12:00 - - - - More Ranges - http://ddili.org/ders/d.en/ranges_more.html - Useful range templates of the std.range module. Providing range capabilities depending on the capabilities on dependent ranges. - Chapter - 01 November 2013 17:00 - - - - Mixins - http://ddili.org/ders/d.en/mixin.html - Template and string mixins that allow inserting compile-time generated code into the source code. - Chapter - 01 November 2013 17:00 - - - - More Functions - http://ddili.org/ders/d.en/functions_more.html - More features of D functions: auto, ref, auto ref, and inout return attributes; pure and nothrow behavioral attributes; and @safe, @trusted, and @system memory safety attributes. - Chapter - 01 November 2013 17:00 - - - - More Templates - http://ddili.org/ders/d.en/templates_more.html - More information about templates: Templates can define any kind of code; template parameters can be of type, value, alias, this, and tuple; template constraints enable template definitions only for template arguments that satisfy that template's requirements. - Chapter - 19 September 2013 00:30 - - - - Tuples - http://ddili.org/ders/d.en/tuples.html - Tuples, which combine values of different types and make them available similar to struct objects, and TypeTuple, which represents the concept of 'list of values' as seen in parameter lists, template argument lists, and array literal initialization lists. - Chapter - 19 September 2013 00:30 - - - - Ranges - http://ddili.org/ders/d.en/ranges.html - The code examples in the Ranges chapter are made const-correct and they take advantage of UFCS. - Chapter - 09 July 2013 19:30 - - - - Labels and goto - http://ddili.org/ders/d.en/goto.html - Labels that give names to code lines and the goto statement that make program execution go to a label. - Chapter - 28 June 2013 19:00 - - - - Unions - http://ddili.org/ders/d.en/union.html - Sharing the same memory area for multiple members. - Chapter - 26 June 2013 10:00 - - - - foreach with Structs and Classes - http://ddili.org/ders/d.en/foreach_opapply.html - Providing foreach support for structs and classes. - Chapter - 14 June 2013 18:30 - - - - Function Pointers, Delegates, and Lambdas - http://ddili.org/ders/d.en/lambda.html - Function pointers and delegates allow storing how the program should behave at a later time. Lambdas (anonymous function or function literals) make code more readable and reduce boilerplate code. - Chapter - 9 June 2013 18:00 - - - - is Expression - http://ddili.org/ders/d.en/is_expr.html - is expression, one of the most powerful compile-time features of the D programming language. - Chapter - 3 June 2013 22:00 - - - - Conditional Compilation - http://ddili.org/ders/d.en/cond_comp.html - Compiling parts of programs in special ways depending on conditions that are checked at compile time. - Chapter - 3 June 2013 21:30 - - - - Bit Operations - http://ddili.org/ders/d.en/bit_operations.html - The D features that enable manipulating data bit-by-bit. - Chapter - 21 May 2013 23:30 - - - - Pointers - http://ddili.org/ders/d.en/pointers.html - Pointers are variables that provide access to other variables. They are low-level capabilities of the microprocessor. - Chapter - 31 Jan 2013 20:30 - - - - alias this - http://ddili.org/ders/d.en/alias_this.html - 'alias this' enables automatic type conversions of user-defined types. - Chapter - 31 Jan 2013 20:30 - - - - alias - http://ddili.org/ders/d.en/alias.html - 'alias' assigns aliases to existing names - Chapter - 31 Jan 2013 20:30 - - - - Contract Programming for Structs and Classes - http://ddili.org/ders/d.en/invariant.html - The 'invariant' keyword and the use of the 'in' and 'out' blocks with structs and classes. - Chapter - 31 Jan 2013 20:30 - - - - Properties - http://ddili.org/ders/d.en/property.html - Properties allow using member functions like member variables. - Chapter - 31 Jan 2013 20:30 - - - - Universal Function Call Syntax (UFCS) - http://ddili.org/ders/d.en/ufcs.html - UFCS enables the member function syntax even for regular functions. - Chapter - 31 Jan 2013 20:30 - - - - Encapsulation and Protection Attributes - http://ddili.org/ders/d.en/encapsulation.html - Preserving class invariants by limiting access to class members. - Chapter - 2 Nov 2012 21:00 - - - - Modules and Libraries - http://ddili.org/ders/d.en/modules.html - Organizing D programs and libraries as modules and packages. - Chapter - 2 Nov 2012 21:00 - - - - destroy and scoped - http://ddili.org/ders/d.en/destroy.html - 'destroy()' to call destructors explicitly and 'scoped()' to destroy objects automatically. - Chapter - 2 Nov 2012 21:00 - - - - Interfaces - http://ddili.org/ders/d.en/interface.html - The 'interface' keyword to define class interfaces. - Chapter - 2 Nov 2012 21:00 - - - - Object - http://ddili.org/ders/d.en/object.html - The Object class that is at the top of class hierarchies and its member functions toString(), opEquals(), opCmp(), and toHash(). - Chapter - 2 Nov 2012 21:00 - - - - Inheritance - http://ddili.org/ders/d.en/inheritance.html - Inheriting the members of existing classes. - Chapter - 2 Nov 2012 21:00 - - - - Classes - http://ddili.org/ders/d.en/class.html - The 'class' feature of the D programming language, which supports the object oriented programming (OOP) paradigm. - Chapter - 2 Nov 2012 21:00 - - - - Operator Overloading - http://ddili.org/ders/d.en/operator_overloading.html - Defining the behaviors of operators for structs to allow their uses as convenient as the fundamental types. - Chapter - 15 Sep 2012 23:30 - - - - Constructor and Other Special Functions - http://ddili.org/ders/d.en/special_functions.html - The four special functions of structs: constructor, destructor, postblit, and assignment. - Chapter - 15 Sep 2012 23:30 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Receiving LinkTerminated and OwnerTerminated exceptions as messages. - Chapter - 09 Aug 2012 23:45 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - Explain the parameters of the functions of the std.parallelism module: Work unit size and buffer size. - Chapter - 09 Aug 2012 23:30 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The consequences of marking function parameters const or immutable. - Chapter - 09 Aug 2012 22:00 - - - - const ref Parameters and const Member Functions - http://ddili.org/ders/d.en/const_member_functions.html - Marking parameters as 'const ref' and member functions as 'const' in order to be able to use them with immutable variables as well. - Chapter - Sun, 10 Jun 2012 17:40 - - - - Member Functions - http://ddili.org/ders/d.en/member_functions.html - Defining functions that are closely related to a struct inside the curly brackets of that struct definition. - Chapter - Sun, 10 Jun 2012 16:15 - - - - Function Overloading - http://ddili.org/ders/d.en/function_overloading.html - The function overloading feature that enables defining multiple functions having the same name. - Chapter - Sun, 10 Jun 2012 14:50 - - - - Variable Number of Parameters - http://ddili.org/ders/d.en/parameter_flexibility.html - Default parameter values and variadic functions. - Chapter - Sun, 10 Jun 2012 13:20 - - - - Structs - http://ddili.org/ders/d.en/struct.html - The 'struct' feature for defining higher-level concepts as user-defined types. - Chapter - Sun, 10 Jun 2012 12:20 - - - - Type Conversions - http://ddili.org/ders/d.en/cast.html - Automatic and explicit type conversions of D: integer promotions, arithmetic conversions, the to() and assumeUnique() functions, and the cast operator. - Chapter - Sun, 10 Jun 2012 00:05 - - - - The null Value and the is Operator - http://ddili.org/ders/d.en/null_is.html - The null value and the is and !is operators. - Chapter - Sat, 09 Jun 2012 18:30 - - - - Value Types and Reference Types - http://ddili.org/ders/d.en/value_vs_reference.html - Introducing value types, reference variables, and reference types; and their differences. - Chapter - Sat, 09 Jun 2012 17:45 - - - - Lifetimes and Fundamental Operations - http://ddili.org/ders/d.en/lifetimes.html - Object lifetimes: Initialization and finalization of variables. - Chapter - Sat, 09 Jun 2012 15:15 - - - - Contract Programming - http://ddili.org/ders/d.en/contracts.html - Contract programming in D: the 'in' and 'out' blocks of functions. - Chapter - Tue, 27 Apr 2012 23:15 - - - - Unit Testing - http://ddili.org/ders/d.en/unit_testing.html - Unit tests for reducing the risk of bugs and test driven development (TDD). - Chapter - Tue, 24 Apr 2012 22:15 - - - - Installing dmd and compiling programs - http://ddili.org/ders/d.en/hello_world.html - Added how to install dmd and how to compile programs on the command line. - Chapter - Sat, 21 Apr 2012 22:15 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Multi-threaded programming in D by message passing, provided by the std.concurrency module. - Chapter - 15 Apr 2012 23:15 - - - - assert and enforce - http://ddili.org/ders/d.en/assert.html - The assert checks and the enforce() function that help with program correctness. - Chapter - 12 April 2012 23:30 - - - - scope - http://ddili.org/ders/d.en/scope.html - The scope(success), scope(failure), and scope(exit) statements of D, which in many cases obviate the need for try-catch-finally blocks and RAII classes. - Chapter - 28 Mar 2012 23:20 - - - - Program Environment - http://ddili.org/ders/d.en/main.html - The environment that starts a D program and the ways the program can interact with its environment: return value, parameters, environment variables. - Chapter - 28 Mar 2012 23:10 - - - - Lazy Operators - http://ddili.org/ders/d.en/lazy_operators.html - The shortcut evaluations of three operators: logical or, logical and, and the ternary operator. - Chapter - 28 Mar 2012 01:00 - - - - Function Parameters - http://ddili.org/ders/d.en/function_parameters.html - Different kinds of function parameters and how they affect the functions and the arguments. - Chapter - 28 Mar 2012 01:00 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The concept of immutability in the D programming language, the const and immutable keywords, and recommendations on how to take advantage of immutability when defining variables and function parameters. - Chapter - 22 Mar 2012 22:50 - - - - Functions - http://ddili.org/ders/d.en/functions.html - The functions that define the building blocks of program behavior. - Chapter - 14 Mar 2012 00:30 - - - - enum - http://ddili.org/ders/d.en/enum.html - The enum feature that enables defining named constant values. - Chapter - 13 Mar 2012 18:30 - - - - switch and case - http://ddili.org/ders/d.en/switch_case.html - The switch and final switch statements, their case sections, and the use of the goto statement under the case sections. - Chapter - 27 Feb 2012 18:10 - - - - The foreach Loop - http://ddili.org/ders/d.en/foreach.html - The foreach loop, one of the most common statements of D. Its use with arrays, strings, and associative arrays. - Chapter - 27 Feb 2012 18:00 - - - - Associative Arrays - http://ddili.org/ders/d.en/aa.html - Associative arrays, the hash table implementation of the D programming language. - Chapter - 26 Feb 2012 22:40 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - The std.parallelism module to make programs run faster by taking advantage of multiple cores of the system. - Chapter - 19 Feb 2012 23:10 - - - - The do-while Loop - http://ddili.org/ders/d.en/do_while.html - The do-while loop and its comparison to the while loop. - Chapter - 11 Feb 2012 20:10 - - - - Formatted Input - http://ddili.org/ders/d.en/formatted_input.html - Reading data that match specific formats. - Chapter - 11 Feb 2012 20:00 - - - - Formatted Output - http://ddili.org/ders/d.en/formatted_output.html - Determining the format of printed values. - Chapter - 31 Jan 2012 00:00 - - - - Literals - http://ddili.org/ders/d.en/literals.html - The syntax of literals of different D types. - Chapter - 31 Jan 2012 00:00 - - - - The Ternary Operator ?: - http://ddili.org/ders/d.en/ternary.html - The ternary operator and comparing it to the if-else statement. - Chapter - 31 Jan 2012 00:00 - - - - The for Loop - http://ddili.org/ders/d.en/for.html - The for loop and its comparison to the while loop. - Chapter - 31 Jan 2012 00:00 - - - - PDF version of the book - http://ddili.org/ders/d.en/index.html - The PDF version of the book is now available through a link in chapter headers - News - 21 Jan 2012 18:00 - - - - Name Space - http://ddili.org/ders/d.en/name_space.html - The lifetime and accessibility of names of variables and other program constructs - Chapter - 21 Jan 2012 18:00 - - - - auto and typeof - http://ddili.org/ders/d.en/auto_and_typeof.html - The 'auto' keyword and its use during type inference, and the typeof keyword to get the type of expressions - Chapter - 21 Jan 2012 18:00 - - - - Files - http://ddili.org/ders/d.en/files.html - Reading from and writing to files using the std.stdio.File struct - Chapter - 21 Jan 2012 18:00 - - - - Redirecting Standard Input and Output Streams - http://ddili.org/ders/d.en/stream_redirect.html - How to redirect standard input and output streams of program to files and other programs - Chapter - 21 Jan 2012 18:00 - - - - Templates - http://ddili.org/ders/d.en/templates.html - The 'Templates' chapter - Chapter - 12 Jan 2012 00:40 - - - - Strings - http://ddili.org/ders/d.en/strings.html - The 'Strings' chapter - Chapter - 03 Jan 2012 18:00 - - - - Slices and Other Array Features - http://ddili.org/ders/d.en/slices.html - The 'Slices and Other Array Features' chapter - Chapter - 31 Dec 2011 19:15 - - - - Characters - http://ddili.org/ders/d.en/characters.html - The 'Characters' chapter - Chapter - 18 Dec 2011 19:15 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter has been proofread. - Proofreading - 18 Dec 2011 19:00 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter - Chapter - 11 Dec 2011 14:00 - - - - Floating Point Types - http://ddili.org/ders/d.en/floating_point.html - The 'Floating Point Types' chapter - Chapter - 09 Dec 2011 22:10 - - - - Index - http://ddili.org/ders/d.en/index.html - The index of the book - Book - 13 Nov 2011 23:00 - - - - diff --git a/ddili/src/ders/d.de/title.html b/ddili/src/ders/d.de/title.html deleted file mode 100644 index f189df7..0000000 --- a/ddili/src/ders/d.de/title.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - -

    -Programming in D -

    - -
    - -

    -First Edition -

    - -
    - -

    -Ali Çehreli -

    - -
    - -
    -

    -Edited by Luís Marques -

    -
    - -
    diff --git a/ddili/src/ders/d.de/to_be_continued.d b/ddili/src/ders/d.de/to_be_continued.d deleted file mode 100644 index d327691..0000000 --- a/ddili/src/ders/d.de/to_be_continued.d +++ /dev/null @@ -1,17 +0,0 @@ -Ddoc - -$(B) -$(H4 The End) - -$(P -You can use $(LINK2 /ders/d.de/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/ddili/src/ders/d.de/toc_head.html b/ddili/src/ders/d.de/toc_head.html deleted file mode 100644 index 345f048..0000000 --- a/ddili/src/ders/d.de/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    Contents

    diff --git a/ddili/src/ders/d.de/toc_tail.html b/ddili/src/ders/d.de/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/d.de/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/d.en/Makefile.in b/ddili/src/ders/d.en/Makefile.in deleted file mode 100644 index acf5e0c..0000000 --- a/ddili/src/ders/d.en/Makefile.in +++ /dev/null @@ -1,150 +0,0 @@ -DERS_SON_D=to_be_continued.d - -# Taken out for the draft release -# foreword1.d \ - -DERS_D_BOLUMLER= \ - foreword2.d \ - preface.d \ - hello_world.d \ - writeln.d \ - compiler.d \ - types.d \ - assignment.d \ - variables.d \ - io.d \ - input.d \ - logical_expressions.d \ - if.d \ - while.d \ - arithmetic.d \ - floating_point.d \ - arrays.d \ - characters.d \ - slices.d \ - strings.d \ - stream_redirect.d \ - files.d \ - auto_and_typeof.d \ - name_space.d \ - for.d \ - ternary.d \ - literals.d \ - formatted_output.d \ - formatted_input.d \ - do_while.d \ - aa.d \ - foreach.d \ - switch_case.d \ - enum.d \ - functions.d \ - const_and_immutable.d \ - value_vs_reference.d \ - function_parameters.d \ - lvalue_rvalue.d \ - lazy_operators.d \ - main.d \ - exceptions.d \ - scope.d \ - assert.d \ - unit_testing.d \ - contracts.d \ - lifetimes.d \ - null_is.d \ - cast.d \ - struct.d \ - parameter_flexibility.d \ - function_overloading.d \ - member_functions.d \ - const_member_functions.d \ - special_functions.d \ - operator_overloading.d \ - class.d \ - inheritance.d \ - object.d \ - interface.d \ - destroy.d \ - modules.d \ - encapsulation.d \ - ufcs.d \ - property.d \ - invariant.d \ - templates.d \ - pragma.d \ - alias.d \ - alias_this.d \ - pointers.d \ - bit_operations.d \ - cond_comp.d \ - is_expr.d \ - lambda.d \ - foreach_opapply.d \ - nested.d \ - union.d \ - goto.d \ - tuples.d \ - templates_more.d \ - functions_more.d \ - mixin.d \ - ranges.d \ - ranges_more.d \ - parallelism.d \ - concurrency.d \ - concurrency_shared.d \ - fibers.d \ - memory.d \ - uda.d \ - operator_precedence.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - hello_world.cozum.d \ - writeln.cozum.d \ - types.cozum.d \ - assignment.cozum.d \ - variables.cozum.d \ - io.cozum.d \ - input.cozum.d \ - logical_expressions.cozum.d \ - if.cozum.d \ - while.cozum.d \ - arithmetic.cozum.d \ - floating_point.cozum.d \ - arrays.cozum.d \ - slices.cozum.d \ - strings.cozum.d \ - stream_redirect.cozum.d \ - files.cozum.d \ - auto_and_typeof.cozum.d \ - for.cozum.d \ - ternary.cozum.d \ - literals.cozum.d \ - formatted_output.cozum.d \ - formatted_input.cozum.d \ - do_while.cozum.d \ - aa.cozum.d \ - foreach.cozum.d \ - switch_case.cozum.d \ - enum.cozum.d \ - functions.cozum.d \ - function_parameters.cozum.d \ - main.cozum.d \ - assert.cozum.d \ - unit_testing.cozum.d \ - contracts.cozum.d \ - struct.cozum.d \ - parameter_flexibility.cozum.d \ - function_overloading.cozum.d \ - member_functions.cozum.d \ - operator_overloading.cozum.d \ - inheritance.cozum.d \ - object.cozum.d \ - pointers.cozum.d \ - bit_operations.cozum.d \ - foreach_opapply.cozum.d \ - -include Makefile.ders.in -$(eval $(call derse_ozel,d.en,Programming_in_D,english)) diff --git a/ddili/src/ders/d.en/aa.cozum.d b/ddili/src/ders/d.en/aa.cozum.d deleted file mode 100644 index 0997594..0000000 --- a/ddili/src/ders/d.en/aa.cozum.d +++ /dev/null @@ -1,117 +0,0 @@ -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/ddili/src/ders/d.en/aa.d b/ddili/src/ders/d.en/aa.d deleted file mode 100644 index 6219a37..0000000 --- a/ddili/src/ders/d.en/aa.d +++ /dev/null @@ -1,323 +0,0 @@ -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 $(IX remove) 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) $(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/ddili/src/ders/d.en/arrays.cozum.d b/ddili/src/ders/d.en/arrays.cozum.d deleted file mode 100644 index 3e0d3fa..0000000 --- a/ddili/src/ders/d.en/arrays.cozum.d +++ /dev/null @@ -1,133 +0,0 @@ -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/ddili/src/ders/d.en/arrays.d b/ddili/src/ders/d.en/arrays.d deleted file mode 100644 index e57befb..0000000 --- a/ddili/src/ders/d.en/arrays.d +++ /dev/null @@ -1,552 +0,0 @@ -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 ~, 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/ddili/src/ders/d.en/class.d b/ddili/src/ders/d.en/class.d deleted file mode 100644 index 426ac8a..0000000 --- a/ddili/src/ders/d.en/class.d +++ /dev/null @@ -1,449 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX class) Classes) - -$(P -$(IX OOP) $(IX object oriented programming) Similar to structs, $(C class) is a feature for defining new 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 have seen 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()): -) - ---- -class Foo { -// ... - 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/ddili/src/ders/d.en/code/README b/ddili/src/ders/d.en/code/README deleted file mode 100644 index 028b9e5..0000000 --- a/ddili/src/ders/d.en/code/README +++ /dev/null @@ -1,22 +0,0 @@ -This directory contains most of the code samples that appear in the book -"Programming in D": - - http://ddili.org/ders/d.en/ - -The names of the sample programs are in chapterName.N.d format. The programs -are extracted from the chapter files and named automatically. - - chapterName - The name of the chapter file without the .html extension, from - which the program was extracted (e.g. hello_world for - hello_world.html) - - N - The order in which the program was extracted from that chapter - file (e.g. 1) - -For example, hello_world.1.d is the first program that was extracted from -hello_world.html. - -The word 'cozum' (transliteration of Turkish "çözüm") that appears in some of -the chapter file names has been replaced with its English translation -(solution). Accordingly, unit_testing.solution.2.d is the second program that -was extracted from unit_testing.cozum.html. diff --git a/ddili/src/ders/d.en/compiler.d b/ddili/src/ders/d.en/compiler.d deleted file mode 100644 index 3b9765e..0000000 --- a/ddili/src/ders/d.en/compiler.d +++ /dev/null @@ -1,101 +0,0 @@ -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/ddili/src/ders/d.en/concurrency.d b/ddili/src/ders/d.en/concurrency.d deleted file mode 100644 index a01965d..0000000 --- a/ddili/src/ders/d.en/concurrency.d +++ /dev/null @@ -1,1343 +0,0 @@ -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/ddili/src/ders/d.en/concurrency_shared.d b/ddili/src/ders/d.en/concurrency_shared.d deleted file mode 100644 index 217a136..0000000 --- a/ddili/src/ders/d.en/concurrency_shared.d +++ /dev/null @@ -1,748 +0,0 @@ -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: -) - ---- - 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: -) - ---- -void transferMoney(shared BankAccount from, - shared BankAccount to) { - 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 operation is based on $(I mutating a variable as long as it still has its currently known value). The way it is used is 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/ddili/src/ders/d.en/cover_CreateSpace.pdf b/ddili/src/ders/d.en/cover_CreateSpace.pdf deleted file mode 100644 index 91e5544..0000000 Binary files a/ddili/src/ders/d.en/cover_CreateSpace.pdf and /dev/null differ diff --git a/ddili/src/ders/d.en/cover_IngramSpark_hardcover.pdf b/ddili/src/ders/d.en/cover_IngramSpark_hardcover.pdf deleted file mode 100644 index dcd6ff0..0000000 Binary files a/ddili/src/ders/d.en/cover_IngramSpark_hardcover.pdf and /dev/null differ diff --git a/ddili/src/ders/d.en/cover_IngramSpark_paperback.pdf b/ddili/src/ders/d.en/cover_IngramSpark_paperback.pdf deleted file mode 100644 index 7e69500..0000000 Binary files a/ddili/src/ders/d.en/cover_IngramSpark_paperback.pdf and /dev/null differ diff --git a/ddili/src/ders/d.en/cover_ebook.png b/ddili/src/ders/d.en/cover_ebook.png deleted file mode 100644 index f9ae6c6..0000000 Binary files a/ddili/src/ders/d.en/cover_ebook.png and /dev/null differ diff --git a/ddili/src/ders/d.en/cover_thumb.png b/ddili/src/ders/d.en/cover_thumb.png deleted file mode 100644 index e7f4b7e..0000000 Binary files a/ddili/src/ders/d.en/cover_thumb.png and /dev/null differ diff --git a/ddili/src/ders/d.en/derse_ozel.ddoc b/ddili/src/ders/d.en/derse_ozel.ddoc deleted file mode 100644 index b1ce982..0000000 --- a/ddili/src/ders/d.en/derse_ozel.ddoc +++ /dev/null @@ -1,47 +0,0 @@ -MAIN_TITLE=Programming in D -SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference -SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english - -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= -
    Other D Resources
    -$(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)) -) - -$(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)) -) - -MINIBASLIK = $0 - - -BASLIK =

    $0

    - -ALTBASLIK =
    $0
    - - -MINI_SOZLUK= - -GERI_METIN=Prev -ILERI_METIN=Next -PROBLEM_METIN=Exercise -PROBLEM_COK_METIN=Exercises -PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... -PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... -COZUM_METIN=the solution -COZUMLER_METIN=the solutions - -DERLEME_HATASI_METIN=compilation ERROR diff --git a/ddili/src/ders/d.en/fibers.d b/ddili/src/ders/d.en/fibers.d deleted file mode 100644 index 530e306..0000000 --- a/ddili/src/ders/d.en/fibers.d +++ /dev/null @@ -1,1105 +0,0 @@ -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 have seen 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) { - alias RangeType = typeof(byNode(tree.root)); - - return (tree.root - ? byNode(tree.root) - : 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 = formattedRead(line, " %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/ddili/src/ders/d.en/foreword2.d b/ddili/src/ders/d.en/foreword2.d deleted file mode 100644 index 1444030..0000000 --- a/ddili/src/ders/d.en/foreword2.d +++ /dev/null @@ -1,40 +0,0 @@ -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/ddili/src/ders/d.en/frontispiece.d b/ddili/src/ders/d.en/frontispiece.d deleted file mode 100644 index 27f5200..0000000 --- a/ddili/src/ders/d.en/frontispiece.d +++ /dev/null @@ -1,9 +0,0 @@ -Ddoc - -
    - -
    - -
    - -
    diff --git a/ddili/src/ders/d.en/halftitle.html b/ddili/src/ders/d.en/halftitle.html deleted file mode 100644 index bbfcfd9..0000000 --- a/ddili/src/ders/d.en/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -Programming in D -

    - -

    -Ali Çehreli -

    - -
    diff --git a/ddili/src/ders/d.en/hello_world.cozum.d b/ddili/src/ders/d.en/hello_world.cozum.d deleted file mode 100644 index 2adc68b..0000000 --- a/ddili/src/ders/d.en/hello_world.cozum.d +++ /dev/null @@ -1,52 +0,0 @@ -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/ddili/src/ders/d.en/hello_world.d b/ddili/src/ders/d.en/hello_world.d deleted file mode 100644 index 259d82e..0000000 --- a/ddili/src/ders/d.en/hello_world.d +++ /dev/null @@ -1,222 +0,0 @@ -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.071. -) - -$(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 only the name of the compiler: -) - -$(SHELL -$(SHELL_OBSERVED $) dmd $(SHELL_NOTE enter only the name) -DMD64 D Compiler v2.069.0 -Copyright (c) 1999-2015 by Digital Mars written by Walter Bright -Documentation: http://dlang.org/ -Config file: /etc/dmd.conf -Usage: - dmd files.d ... { -switch } - - files.d D source files -... - -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: -) - -$(SHELL -$(SHELL_OBSERVED $) dmd $(HILITE -run) hello.d -w -unittest -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 __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 in the upcoming chapters with the exception of these keywords: $(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/ddili/src/ders/d.en/index.d b/ddili/src/ders/d.en/index.d deleted file mode 100644 index 59f365a..0000000 --- a/ddili/src/ders/d.en/index.d +++ /dev/null @@ -1,143 +0,0 @@ -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://gum.co/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, Book Index)) - -$(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 <> !<>= (and more))) -$(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__ __PRETTY_FUNCTION__)) -$(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/ddili/src/ders/d.en/index_section_head.html b/ddili/src/ders/d.en/index_section_head.html deleted file mode 100644 index 862063a..0000000 --- a/ddili/src/ders/d.en/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Index

    diff --git a/ddili/src/ders/d.en/index_section_tail.html b/ddili/src/ders/d.en/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.en/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.en/ix.d b/ddili/src/ders/d.en/ix.d deleted file mode 100644 index b85c3ae..0000000 --- a/ddili/src/ders/d.en/ix.d +++ /dev/null @@ -1,14 +0,0 @@ -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/ddili/src/ders/d.en/parallelism.d b/ddili/src/ders/d.en/parallelism.d deleted file mode 100644 index d82234d..0000000 --- a/ddili/src/ders/d.en/parallelism.d +++ /dev/null @@ -1,1217 +0,0 @@ -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/ddili/src/ders/d.en/pdf.derse_ozel.css b/ddili/src/ders/d.en/pdf.derse_ozel.css deleted file mode 100644 index fe51491..0000000 --- a/ddili/src/ders/d.en/pdf.derse_ozel.css +++ /dev/null @@ -1,31 +0,0 @@ -a.xref:after { - content: " (page " target-counter(attr(href, url), page) ")"; -} - -div.cozum_link_cok a.xref { - content: "The solutions are on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_cok a.xref:after { - content: normal; -} - -div.cozum_link_tek a.xref { - content: "The solution is on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_tek a.xref:after { - content: normal; -} - -div.cozum_link_cok, div.cozum_link_tek { - padding-top: .1em; - page-break-before: avoid; -} - -body -{ - counter-reset: h4 -2; -} diff --git a/ddili/src/ders/d.en/pdf_cozum_head.html b/ddili/src/ders/d.en/pdf_cozum_head.html deleted file mode 100644 index d7ebaae..0000000 --- a/ddili/src/ders/d.en/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Exercise Solutions

    diff --git a/ddili/src/ders/d.en/pdf_cozum_tail.html b/ddili/src/ders/d.en/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d.en/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d.en/pdf_html_head.html b/ddili/src/ders/d.en/pdf_html_head.html deleted file mode 100644 index d75ecee..0000000 --- a/ddili/src/ders/d.en/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - Programming in D - - - diff --git a/ddili/src/ders/d.en/pdf_html_tail.html b/ddili/src/ders/d.en/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/d.en/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/d.en/pdf_sozluk_head.html b/ddili/src/ders/d.en/pdf_sozluk_head.html deleted file mode 100644 index f5edc52..0000000 --- a/ddili/src/ders/d.en/pdf_sozluk_head.html +++ /dev/null @@ -1,3 +0,0 @@ - -

    Sözlük

    - diff --git a/ddili/src/ders/d.en/property.d b/ddili/src/ders/d.en/property.d deleted file mode 100644 index b4bda24..0000000 --- a/ddili/src/ders/d.en/property.d +++ /dev/null @@ -1,351 +0,0 @@ -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/ddili/src/ders/d.en/rss.xml b/ddili/src/ders/d.en/rss.xml deleted file mode 100644 index 1e35711..0000000 --- a/ddili/src/ders/d.en/rss.xml +++ /dev/null @@ -1,812 +0,0 @@ - - - - - Programming in D - http://ddili.org/ders/d.en/ - Programming with the D programming language - en - D Programming Language Book - - - 'deprecated', 'extern', and 'extern()' - http://ddili.org/ders/d.en/modules.html - Added 'deprecated' that helps with deprecation process of library features, 'extern' that is for declaring external symbols, and 'extern()' that allows interacting with libraries of other languages (extern(C), extern(C++, std), etc.). - Chapter - 24 Oct 2015 22:00 - - - - Code Samples .zip File - http://ddili.org/ders/d.en/index.html - You can download most of the code samples that appear in the book as a .zip file. - Code - 16 Oct 2015 20:00 - - - - Book Index - http://ddili.org/ders/d.en/ix.html - The index section of the book. - Chapter - 21 Aug 2015 02:00 - - - - Fibers - http://ddili.org/ders/d.en/fibers.html - Cooperative multitasking in D with fibers. - Chapter - 17 Aug 2015 15:00 - - - - Pragmas - http://ddili.org/ders/d.en/pragma.html - Pragmas that allow interactions with the compiler, including the most recent pragma(inline). - Chapter - 17 Aug 2015 15:00 - - - - Operator Precedence - http://ddili.org/ders/d.en/operator_precedence.html - The rules that specify the execution order of chained operators and the expressions that they use. - Chapter - 17 Aug 2015 15:00 - - - - Foreword by Andrei Alexandrescu - http://ddili.org/ders/d.en/foreword2.html - "Instead of falling for getting things done quickly, 'Programming in D' focuses on getting things done properly, to the lasting benefit of its reader." - Chapter - 17 Aug 2015 15:00 - - - - Ebook versions - http://ddili.org/ders/d.en/index.html - In addition to the PDF version, now there are EPUB and AZW3 versions as well. - Ebook - 15 Dec 2014 02:00 - - - - '.offsetof' property and 'align' attribute - http://ddili.org/ders/d.en/memory.html - The .offsetof property to get the offsets of and the align attribute to specify the alignments of struct members. - Chapter - 25 Nov 2014 17:00 - - - - Contract inheritance - http://ddili.org/ders/d.en/invariant.html - Inheriting 'in' and 'out' blocks of interface and class functions. - Chapter - 25 Nov 2014 17:00 - - - - The special keywords - http://ddili.org/ders/d.en/templates_more.html - __MODULE__, __FILE__, __LINE__, __FUNCTION__, and __PRETTY_FUNCTION__ - Chapter - 25 Nov 2014 17:00 - - - - pragma - http://ddili.org/ders/d.en/templates.html - The pragma directive - Chapter - 25 Nov 2014 17:00 - - - - Nested Functions, Structs, and Classes - http://ddili.org/ders/d.en/nested.html - Defining functions, structs, and classes inside existing scopes. - Chapter - 25 Nov 2014 17:00 - - - - Lvalues and Rvalues - http://ddili.org/ders/d.en/lvalue_rvalue.html - Lvalues, rvalues, their differences, and 'auto ref' parameter type that can accept either kind. - Chapter - 25 Nov 2014 17:00 - - - - The 'Index' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated index section for the PDF version of the book. - Chapter - 25 Nov 2014 17:00 - - - - The 'Table of Contents' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated TOC section for the PDF version of the book. - Chapter - 28 Sep 2014 23:00 - - - - The attributes of Throwable - http://ddili.org/ders/d.en/exceptions.html - The attributes of the Throwable interface, collateral exceptions, and an example of accessing collateral exceptions through the .next property. - Chapter - 18 Sep 2014 16:00 - - - - Selective, local, renamed, and package imports - http://ddili.org/ders/d.en/modules.html - Importing modules selectively, locally, under a different name, and as a package. - Chapter - 17 Sep 2014 00:30 - - - - @disable - http://ddili.org/ders/d.en/special_functions.html - The @disable attribute to disable special functions of structs. - Chapter - 12 Sep 2014 00:15 - - - - Operator overloading for multi-dimensional indexing and slicing - http://ddili.org/ders/d.en/templates_more.html - The templated versions of opDollar, opIndex, and opSlice, and examples of using them to support multiple indexes inside square brackets. - Chapter - 11 Sep 2014 00:15 - - - - static this, static ~this, shared static this, and shared static ~this - http://ddili.org/ders/d.en/index.html - Specifying the initial and final operations of threads and programs. - Chapter - 26 Aug 2014 23:30 - - - - User Defined Attributes (UDA) - http://ddili.org/ders/d.en/uda.html - Assigning user defined attributes to type and variable declarations, testing the attributes at compile time, and compiling the program according to those attributes. - Chapter - 26 Aug 2014 22:30 - - - - Memory Management - http://ddili.org/ders/d.en/memory.html - The garbage collector, allocating memory, and placing objects at specific locations in memory. - Chapter - 24 Jul 2014 01:00 - - - - Type traits - http://ddili.org/ders/d.en/cond_comp.html - Using type traits in conditional compilation. - Chapter - 29 May 2014 20:30 - - - - Data Sharing Concurrency - http://ddili.org/ders/d.en/concurrency_shared.html - Multi-threaded programming in D by data sharing. - Chapter - 20 May 2014 12:00 - - - - More Ranges - http://ddili.org/ders/d.en/ranges_more.html - Useful range templates of the std.range module. Providing range capabilities depending on the capabilities on dependent ranges. - Chapter - 01 November 2013 17:00 - - - - Mixins - http://ddili.org/ders/d.en/mixin.html - Template and string mixins that allow inserting compile-time generated code into the source code. - Chapter - 01 November 2013 17:00 - - - - More Functions - http://ddili.org/ders/d.en/functions_more.html - More features of D functions: auto, ref, auto ref, and inout return attributes; pure and nothrow behavioral attributes; and @safe, @trusted, and @system memory safety attributes. - Chapter - 01 November 2013 17:00 - - - - More Templates - http://ddili.org/ders/d.en/templates_more.html - More information about templates: Templates can define any kind of code; template parameters can be of type, value, alias, this, and tuple; template constraints enable template definitions only for template arguments that satisfy that template's requirements. - Chapter - 19 September 2013 00:30 - - - - Tuples - http://ddili.org/ders/d.en/tuples.html - Tuples, which combine values of different types and make them available similar to struct objects, and TypeTuple, which represents the concept of 'list of values' as seen in parameter lists, template argument lists, and array literal initialization lists. - Chapter - 19 September 2013 00:30 - - - - Ranges - http://ddili.org/ders/d.en/ranges.html - The code examples in the Ranges chapter are made const-correct and they take advantage of UFCS. - Chapter - 09 July 2013 19:30 - - - - Labels and goto - http://ddili.org/ders/d.en/goto.html - Labels that give names to code lines and the goto statement that make program execution go to a label. - Chapter - 28 June 2013 19:00 - - - - Unions - http://ddili.org/ders/d.en/union.html - Sharing the same memory area for multiple members. - Chapter - 26 June 2013 10:00 - - - - foreach with Structs and Classes - http://ddili.org/ders/d.en/foreach_opapply.html - Providing foreach support for structs and classes. - Chapter - 14 June 2013 18:30 - - - - Function Pointers, Delegates, and Lambdas - http://ddili.org/ders/d.en/lambda.html - Function pointers and delegates allow storing how the program should behave at a later time. Lambdas (anonymous function or function literals) make code more readable and reduce boilerplate code. - Chapter - 9 June 2013 18:00 - - - - is Expression - http://ddili.org/ders/d.en/is_expr.html - is expression, one of the most powerful compile-time features of the D programming language. - Chapter - 3 June 2013 22:00 - - - - Conditional Compilation - http://ddili.org/ders/d.en/cond_comp.html - Compiling parts of programs in special ways depending on conditions that are checked at compile time. - Chapter - 3 June 2013 21:30 - - - - Bit Operations - http://ddili.org/ders/d.en/bit_operations.html - The D features that enable manipulating data bit-by-bit. - Chapter - 21 May 2013 23:30 - - - - Pointers - http://ddili.org/ders/d.en/pointers.html - Pointers are variables that provide access to other variables. They are low-level capabilities of the microprocessor. - Chapter - 31 Jan 2013 20:30 - - - - alias this - http://ddili.org/ders/d.en/alias_this.html - 'alias this' enables automatic type conversions of user-defined types. - Chapter - 31 Jan 2013 20:30 - - - - alias - http://ddili.org/ders/d.en/alias.html - 'alias' assigns aliases to existing names - Chapter - 31 Jan 2013 20:30 - - - - Contract Programming for Structs and Classes - http://ddili.org/ders/d.en/invariant.html - The 'invariant' keyword and the use of the 'in' and 'out' blocks with structs and classes. - Chapter - 31 Jan 2013 20:30 - - - - Properties - http://ddili.org/ders/d.en/property.html - Properties allow using member functions like member variables. - Chapter - 31 Jan 2013 20:30 - - - - Universal Function Call Syntax (UFCS) - http://ddili.org/ders/d.en/ufcs.html - UFCS enables the member function syntax even for regular functions. - Chapter - 31 Jan 2013 20:30 - - - - Encapsulation and Protection Attributes - http://ddili.org/ders/d.en/encapsulation.html - Preserving class invariants by limiting access to class members. - Chapter - 2 Nov 2012 21:00 - - - - Modules and Libraries - http://ddili.org/ders/d.en/modules.html - Organizing D programs and libraries as modules and packages. - Chapter - 2 Nov 2012 21:00 - - - - destroy and scoped - http://ddili.org/ders/d.en/destroy.html - 'destroy()' to call destructors explicitly and 'scoped()' to destroy objects automatically. - Chapter - 2 Nov 2012 21:00 - - - - Interfaces - http://ddili.org/ders/d.en/interface.html - The 'interface' keyword to define class interfaces. - Chapter - 2 Nov 2012 21:00 - - - - Object - http://ddili.org/ders/d.en/object.html - The Object class that is at the top of class hierarchies and its member functions toString(), opEquals(), opCmp(), and toHash(). - Chapter - 2 Nov 2012 21:00 - - - - Inheritance - http://ddili.org/ders/d.en/inheritance.html - Inheriting the members of existing classes. - Chapter - 2 Nov 2012 21:00 - - - - Classes - http://ddili.org/ders/d.en/class.html - The 'class' feature of the D programming language, which supports the object oriented programming (OOP) paradigm. - Chapter - 2 Nov 2012 21:00 - - - - Operator Overloading - http://ddili.org/ders/d.en/operator_overloading.html - Defining the behaviors of operators for structs to allow their uses as convenient as the fundamental types. - Chapter - 15 Sep 2012 23:30 - - - - Constructor and Other Special Functions - http://ddili.org/ders/d.en/special_functions.html - The four special functions of structs: constructor, destructor, postblit, and assignment. - Chapter - 15 Sep 2012 23:30 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Receiving LinkTerminated and OwnerTerminated exceptions as messages. - Chapter - 09 Aug 2012 23:45 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - Explain the parameters of the functions of the std.parallelism module: Work unit size and buffer size. - Chapter - 09 Aug 2012 23:30 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The consequences of marking function parameters const or immutable. - Chapter - 09 Aug 2012 22:00 - - - - const ref Parameters and const Member Functions - http://ddili.org/ders/d.en/const_member_functions.html - Marking parameters as 'const ref' and member functions as 'const' in order to be able to use them with immutable variables as well. - Chapter - Sun, 10 Jun 2012 17:40 - - - - Member Functions - http://ddili.org/ders/d.en/member_functions.html - Defining functions that are closely related to a struct inside the curly brackets of that struct definition. - Chapter - Sun, 10 Jun 2012 16:15 - - - - Function Overloading - http://ddili.org/ders/d.en/function_overloading.html - The function overloading feature that enables defining multiple functions having the same name. - Chapter - Sun, 10 Jun 2012 14:50 - - - - Variable Number of Parameters - http://ddili.org/ders/d.en/parameter_flexibility.html - Default parameter values and variadic functions. - Chapter - Sun, 10 Jun 2012 13:20 - - - - Structs - http://ddili.org/ders/d.en/struct.html - The 'struct' feature for defining higher-level concepts as user-defined types. - Chapter - Sun, 10 Jun 2012 12:20 - - - - Type Conversions - http://ddili.org/ders/d.en/cast.html - Automatic and explicit type conversions of D: integer promotions, arithmetic conversions, the to() and assumeUnique() functions, and the cast operator. - Chapter - Sun, 10 Jun 2012 00:05 - - - - The null Value and the is Operator - http://ddili.org/ders/d.en/null_is.html - The null value and the is and !is operators. - Chapter - Sat, 09 Jun 2012 18:30 - - - - Value Types and Reference Types - http://ddili.org/ders/d.en/value_vs_reference.html - Introducing value types, reference variables, and reference types; and their differences. - Chapter - Sat, 09 Jun 2012 17:45 - - - - Lifetimes and Fundamental Operations - http://ddili.org/ders/d.en/lifetimes.html - Object lifetimes: Initialization and finalization of variables. - Chapter - Sat, 09 Jun 2012 15:15 - - - - Contract Programming - http://ddili.org/ders/d.en/contracts.html - Contract programming in D: the 'in' and 'out' blocks of functions. - Chapter - Tue, 27 Apr 2012 23:15 - - - - Unit Testing - http://ddili.org/ders/d.en/unit_testing.html - Unit tests for reducing the risk of bugs and test driven development (TDD). - Chapter - Tue, 24 Apr 2012 22:15 - - - - Installing dmd and compiling programs - http://ddili.org/ders/d.en/hello_world.html - Added how to install dmd and how to compile programs on the command line. - Chapter - Sat, 21 Apr 2012 22:15 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Multi-threaded programming in D by message passing, provided by the std.concurrency module. - Chapter - 15 Apr 2012 23:15 - - - - assert and enforce - http://ddili.org/ders/d.en/assert.html - The assert checks and the enforce() function that help with program correctness. - Chapter - 12 April 2012 23:30 - - - - scope - http://ddili.org/ders/d.en/scope.html - The scope(success), scope(failure), and scope(exit) statements of D, which in many cases obviate the need for try-catch-finally blocks and RAII classes. - Chapter - 28 Mar 2012 23:20 - - - - Program Environment - http://ddili.org/ders/d.en/main.html - The environment that starts a D program and the ways the program can interact with its environment: return value, parameters, environment variables. - Chapter - 28 Mar 2012 23:10 - - - - Lazy Operators - http://ddili.org/ders/d.en/lazy_operators.html - The shortcut evaluations of three operators: logical or, logical and, and the ternary operator. - Chapter - 28 Mar 2012 01:00 - - - - Function Parameters - http://ddili.org/ders/d.en/function_parameters.html - Different kinds of function parameters and how they affect the functions and the arguments. - Chapter - 28 Mar 2012 01:00 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The concept of immutability in the D programming language, the const and immutable keywords, and recommendations on how to take advantage of immutability when defining variables and function parameters. - Chapter - 22 Mar 2012 22:50 - - - - Functions - http://ddili.org/ders/d.en/functions.html - The functions that define the building blocks of program behavior. - Chapter - 14 Mar 2012 00:30 - - - - enum - http://ddili.org/ders/d.en/enum.html - The enum feature that enables defining named constant values. - Chapter - 13 Mar 2012 18:30 - - - - switch and case - http://ddili.org/ders/d.en/switch_case.html - The switch and final switch statements, their case sections, and the use of the goto statement under the case sections. - Chapter - 27 Feb 2012 18:10 - - - - The foreach Loop - http://ddili.org/ders/d.en/foreach.html - The foreach loop, one of the most common statements of D. Its use with arrays, strings, and associative arrays. - Chapter - 27 Feb 2012 18:00 - - - - Associative Arrays - http://ddili.org/ders/d.en/aa.html - Associative arrays, the hash table implementation of the D programming language. - Chapter - 26 Feb 2012 22:40 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - The std.parallelism module to make programs run faster by taking advantage of multiple cores of the system. - Chapter - 19 Feb 2012 23:10 - - - - The do-while Loop - http://ddili.org/ders/d.en/do_while.html - The do-while loop and its comparison to the while loop. - Chapter - 11 Feb 2012 20:10 - - - - Formatted Input - http://ddili.org/ders/d.en/formatted_input.html - Reading data that match specific formats. - Chapter - 11 Feb 2012 20:00 - - - - Formatted Output - http://ddili.org/ders/d.en/formatted_output.html - Determining the format of printed values. - Chapter - 31 Jan 2012 00:00 - - - - Literals - http://ddili.org/ders/d.en/literals.html - The syntax of literals of different D types. - Chapter - 31 Jan 2012 00:00 - - - - The Ternary Operator ?: - http://ddili.org/ders/d.en/ternary.html - The ternary operator and comparing it to the if-else statement. - Chapter - 31 Jan 2012 00:00 - - - - The for Loop - http://ddili.org/ders/d.en/for.html - The for loop and its comparison to the while loop. - Chapter - 31 Jan 2012 00:00 - - - - PDF version of the book - http://ddili.org/ders/d.en/index.html - The PDF version of the book is now available through a link in chapter headers - News - 21 Jan 2012 18:00 - - - - Name Space - http://ddili.org/ders/d.en/name_space.html - The lifetime and accessibility of names of variables and other program constructs - Chapter - 21 Jan 2012 18:00 - - - - auto and typeof - http://ddili.org/ders/d.en/auto_and_typeof.html - The 'auto' keyword and its use during type inference, and the typeof keyword to get the type of expressions - Chapter - 21 Jan 2012 18:00 - - - - Files - http://ddili.org/ders/d.en/files.html - Reading from and writing to files using the std.stdio.File struct - Chapter - 21 Jan 2012 18:00 - - - - Redirecting Standard Input and Output Streams - http://ddili.org/ders/d.en/stream_redirect.html - How to redirect standard input and output streams of program to files and other programs - Chapter - 21 Jan 2012 18:00 - - - - Templates - http://ddili.org/ders/d.en/templates.html - The 'Templates' chapter - Chapter - 12 Jan 2012 00:40 - - - - Strings - http://ddili.org/ders/d.en/strings.html - The 'Strings' chapter - Chapter - 03 Jan 2012 18:00 - - - - Slices and Other Array Features - http://ddili.org/ders/d.en/slices.html - The 'Slices and Other Array Features' chapter - Chapter - 31 Dec 2011 19:15 - - - - Characters - http://ddili.org/ders/d.en/characters.html - The 'Characters' chapter - Chapter - 18 Dec 2011 19:15 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter has been proofread. - Proofreading - 18 Dec 2011 19:00 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter - Chapter - 11 Dec 2011 14:00 - - - - Floating Point Types - http://ddili.org/ders/d.en/floating_point.html - The 'Floating Point Types' chapter - Chapter - 09 Dec 2011 22:10 - - - - Index - http://ddili.org/ders/d.en/index.html - The index of the book - Book - 13 Nov 2011 23:00 - - - - diff --git a/ddili/src/ders/d.en/title.html b/ddili/src/ders/d.en/title.html deleted file mode 100644 index f189df7..0000000 --- a/ddili/src/ders/d.en/title.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - -

    -Programming in D -

    - -
    - -

    -First Edition -

    - -
    - -

    -Ali Çehreli -

    - -
    - -
    -

    -Edited by Luís Marques -

    -
    - -
    diff --git a/ddili/src/ders/d.en/toc_head.html b/ddili/src/ders/d.en/toc_head.html deleted file mode 100644 index 345f048..0000000 --- a/ddili/src/ders/d.en/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    Contents

    diff --git a/ddili/src/ders/d.en/toc_tail.html b/ddili/src/ders/d.en/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/d.en/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/d.en/uda.d b/ddili/src/ders/d.en/uda.d deleted file mode 100644 index 9c0bb33..0000000 --- a/ddili/src/ders/d.en/uda.d +++ /dev/null @@ -1,421 +0,0 @@ -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 diff --git a/ddili/src/ders/d/Makefile.in b/ddili/src/ders/d/Makefile.in deleted file mode 100644 index 942d922..0000000 --- a/ddili/src/ders/d/Makefile.in +++ /dev/null @@ -1,150 +0,0 @@ -DERS_SON_D=devami_gelecek.d - -# Taslak surum icin cikartildilar -# onsoz1.d \ - -DERS_D_BOLUMLER= \ - onsoz2.d \ - onsoz_yazar.d \ - merhaba_dunya.d \ - writeln.d \ - derleyici.d \ - temel_turler.d \ - atama_ve_sira.d \ - degiskenler.d \ - giris_cikis.d \ - standart_giris.d \ - mantiksal_ifadeler.d \ - if_kosulu.d \ - while_dongusu.d \ - aritmetik_islemler.d \ - kesirli_sayilar.d \ - diziler.d \ - karakterler.d \ - dilimler.d \ - dizgiler.d \ - standart_akim_baglamak.d \ - dosyalar.d \ - auto.d \ - isim_alani.d \ - for_dongusu.d \ - uclu_islec.d \ - hazir_degerler.d \ - cikti_duzeni.d \ - giris_duzeni.d \ - do_while.d \ - esleme_tablolari.d \ - foreach_dongusu.d \ - switch_case.d \ - enum.d \ - islevler.d \ - const_ve_immutable.d \ - deger_referans.d \ - islev_parametreleri.d \ - deger_sol_sag.d \ - tembel_degerlendirmeler.d \ - main.d \ - hatalar.d \ - scope.d \ - assert.d \ - birim_testler.d \ - sozlesmeli.d \ - yasam_surecleri.d \ - null_ve_is.d \ - tur_donusumleri.d \ - yapilar.d \ - parametre_serbestligi.d \ - islev_yukleme.d \ - uye_islevler.d \ - const_uye_islevler.d \ - ozel_islevler.d \ - islec_yukleme.d \ - siniflar.d \ - tureme.d \ - object.d \ - interface.d \ - clear.d \ - moduller.d \ - sarma.d \ - ufcs.d \ - nitelikler.d \ - invariant.d \ - sablonlar.d \ - pragma.d \ - alias.d \ - alias_this.d \ - gostergeler.d \ - bit_islemleri.d \ - kosullu_derleme.d \ - is_ifadesi.d \ - kapamalar.d \ - foreach_opapply.d \ - ic_tanimlar.d \ - birlikler.d \ - etiketler.d \ - cokuzlular.d \ - sablonlar_ayrintili.d \ - islevler_diger.d \ - katmalar.d \ - araliklar.d \ - araliklar_baska.d \ - kosut_islemler.d \ - es_zamanli.d \ - es_zamanli_shared.d \ - fiberler.d \ - bellek_yonetimi.d \ - uda.d \ - islec_oncelikleri.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - merhaba_dunya.cozum.d \ - writeln.cozum.d \ - temel_turler.cozum.d \ - atama_ve_sira.cozum.d \ - degiskenler.cozum.d \ - giris_cikis.cozum.d \ - standart_giris.cozum.d \ - mantiksal_ifadeler.cozum.d \ - if_kosulu.cozum.d \ - while_dongusu.cozum.d \ - aritmetik_islemler.cozum.d \ - kesirli_sayilar.cozum.d \ - diziler.cozum.d \ - dilimler.cozum.d \ - dizgiler.cozum.d \ - standart_akim_baglamak.cozum.d \ - dosyalar.cozum.d \ - auto.cozum.d \ - for_dongusu.cozum.d \ - uclu_islec.cozum.d \ - hazir_degerler.cozum.d \ - cikti_duzeni.cozum.d \ - giris_duzeni.cozum.d \ - do_while.cozum.d \ - enum.cozum.d \ - islevler.cozum.d \ - esleme_tablolari.cozum.d \ - foreach_dongusu.cozum.d \ - switch_case.cozum.d \ - islev_parametreleri.cozum.d \ - main.cozum.d \ - assert.cozum.d \ - birim_testler.cozum.d \ - sozlesmeli.cozum.d \ - yapilar.cozum.d \ - parametre_serbestligi.cozum.d \ - islev_yukleme.cozum.d \ - uye_islevler.cozum.d \ - islec_yukleme.cozum.d \ - tureme.cozum.d \ - object.cozum.d \ - gostergeler.cozum.d \ - bit_islemleri.cozum.d \ - foreach_opapply.cozum.d \ - -include Makefile.ders.in -$(eval $(call derse_ozel,d,D_Programlama_Dili,turkish)) diff --git a/ddili/src/ders/d/Tennessee_Valley.jpg b/ddili/src/ders/d/Tennessee_Valley.jpg deleted file mode 100644 index edb7bad..0000000 Binary files a/ddili/src/ders/d/Tennessee_Valley.jpg and /dev/null differ diff --git a/ddili/src/ders/d/alias.d b/ddili/src/ders/d/alias.d deleted file mode 100644 index 9cef7dd..0000000 --- a/ddili/src/ders/d/alias.d +++ /dev/null @@ -1,462 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX alias) $(CH4 alias) ve $(CH4 with)) - -$(H5 $(C alias)) - -$(P -$(C alias) anahtar sözcüğü programda geçen isimlere takma isim vermek için kullanılır. $(C alias), farklı bir olanak olan $(C alias this) ile karıştırılmamalıdır. -) - -$(H6 Uzun bir ismi kısaltmak) - -$(P -Önceki bölümde gördüğümüz şablonlarda olduğu gibi, programda geçen bazı isimler kullanışsız derecede uzun olabilirler. Daha önce tanımladığımız şu işlevi hatırlayalım: -) - ---- -Yığın!(Nokta!double) rasgeleNoktalar(int adet) { - auto noktalar = new Yığın!(Nokta!double); - - // ... -} ---- - -$(P -Programda açıkça $(C Yığın!(Nokta!double)) yazmanın bir kaç sakıncası görülebilir: -) - -$(UL -$(LI Okumayı güçleştirecek derecede karmaşıktır.) - -$(LI Onun bir yığın veri yapısı olduğunun ve elemanlarının $(C Nokta) şablonunun $(C double) türü ile kullanılmalarından oluştuğunun her noktada görülmesi gereksiz bir bilgi olarak kabul edilebilir.) - -$(LI Programın ihtiyaçlarının değişmesi durumunda örneğin $(C double) yerine artık $(C real) kullanılması gerektiğinde, veya yığın veri yapısı yerine bir ikili ağaç veri yapısı gerektiğinde, türün açıkça yazıldığı her yerde değişiklik yapılması gerekecektir.) - -) - -$(P -Bu sakıncalar $(C Yığın!(Nokta!double)) ismine tek noktada yeni bir isim vererek giderilebilir: -) - ---- -alias $(HILITE Noktalar) = Yığın!(Nokta!double); - -// ... - -$(HILITE Noktalar) rasgeleNoktalar(int adet) { - auto noktalar = new $(HILITE Noktalar); - - // ... -} ---- - -$(P -Bir adım daha ileri giderek yukarıdaki $(C alias)'ı iki parça halinde de tanımlayabiliriz: -) - ---- -alias HassasNokta = Nokta!double; -alias Noktalar = Yığın!HassasNokta; ---- - -$(P -$(C alias)'ın söz dizimi şöyledir: -) - ---- - alias $(I takma_isim) = $(I var_olan_isim); ---- - -$(P -O tanımdan sonra takma isim daha önceden var olan ismin eşdeğeri haline gelir ve artık aynı biçimde kullanılır. -) - -$(P -Bazı D programlarında bu olanağın eski söz dizimine de rastlayabilirsiniz: -) - -$(MONO - // Eski söz dizimini kullanmaya gerek yok: - alias $(I var_olan_isim) $(I takma_isim); -) - -$(P -Türlerin isimlerini modülleriyle birlikte uzun uzun yazmak yerine de $(C alias)'tan yararlanabiliriz. Örneğin $(C okul) ve $(C firma) isimli iki modülde $(C Müdür) isminde iki farklı tür tanımlı olduğunu varsayalım. Bu iki modülün de programa eklendikleri bir durumda yalnızca $(C Müdür) yazıldığında program derlenemez: -) - ---- -import okul; -import firma; - -// ... - - Müdür kişi; $(DERLEME_HATASI) ---- - -$(P -Derleyici hangi $(C Müdür) türünü kasdettiğimizi anlayamaz: -) - -$(SHELL_SMALL -Error: $(HILITE okul.Müdür) at [...]/okul.d(1) conflicts with -$(HILITE firma.Müdür) at [...]/firma.d(1) -) - -$(P -Bunun önüne geçmenin bir yolu, programda kullanmak istediğimiz $(C Müdür)'e bir takma isim vermektir. Böylece her seferinde modülüyle birlikte örneğin $(C okul.Müdür) yazmak zorunda kalmadan birden fazla yerde kullanabiliriz: -) - ---- -import okul; - -alias $(HILITE OkulMüdürü) = okul.Müdür; - -void main() { - $(HILITE OkulMüdürü) kişi; - - // ... - - $(HILITE OkulMüdürü) başkaKişi; -} ---- - -$(P -$(C alias) programdaki başka çeşit isimlerle de kullanılabilir. Aşağıdaki kod bir değişkene nasıl takma isim verildiğini gösteriyor: -) - ---- - int uzunBirDeğişkenİsmi = 42; - - alias değişken = uzunBirDeğişkenİsmi; - değişken = 43; - - assert(uzunBirDeğişkenİsmi == 43); ---- - -$(H6 Tasarım esnekliği) - -$(P -Her ne kadar ileride değişmeyecek olduğundan emin bile olunsa, tasarımın esnek olması için $(C int) gibi temel türlere bile anlamlı yeni isimler verilebilir: -) - ---- -alias MüşteriNumarası = int; -alias Şirketİsmi = string; -// ... - -struct Müşteri { - MüşteriNumarası numara; - Şirketİsmi şirket; - // ... -} ---- - -$(P -Sırasıyla $(C int)'in ve $(C string)'in aynıları olsalar da, eğer o yapıyı kullanan kodlar her zaman için $(C MüşteriNumarası) ve $(C Şirketİsmi) yazarlarsa, yapı tanımında $(C int) veya $(C string) yerine başka bir tür kullanıldığında daha az satırda değişiklik gerekmiş olur. -) - -$(P -Bu yöntem kodun anlaşılır olmasına da yardım eder. Bir değerin türünün $(C int) yerine $(C MüşteriNumarası) olması, kod okunurken o değerin anlamı konusunda hiçbir şüphe bırakmaz. -) - -$(P -Bazı durumlarda böyle tür isimleri bir yapı veya sınıfın içinde de tanımlanabilir. Böylece o yapının veya sınıfın arayüzünde bu takma isimleriyle kullanılırlar. Örnek olarak ağırlık niteliğine sahip bir sınıfa bakalım: -) - ---- -class Kutu { -private: - - double ağırlık_; - -public: - - double ağırlık() const @property { - return ağırlık_; - } - - // ... -} ---- - -$(P -Bu sınıfın üyesinin ve niteliğinin açıkça $(C double) yazılarak tanımlanmış olması kullanıcıların da ağırlığı $(C double) olarak kullanmalarına neden olacaktır: -) - ---- - $(HILITE double) toplamAğırlık = 0; - - foreach (kutu; kutular) { - toplamAğırlık += kutu.ağırlık; - } ---- - -$(P -Bunun karşıtı olarak, ağırlığın türünün sınıf içindeki bir $(C alias) ile tanımlandığı duruma bakalım: -) - ---- -class Kutu { -private: - - $(HILITE Ağırlık) ağırlık_; - -public: - - alias $(HILITE Ağırlık) = double; - - $(HILITE Ağırlık) ağırlık() const @property { - return ağırlık_; - } - - // ... -} ---- - -$(P -Kullanıcı kodu da sınıfın arayüzüne bağlı kalarak artık $(C Ağırlık) yazacaktır: -) - ---- - $(HILITE Kutu.Ağırlık) toplamAğırlık = 0; - - foreach (kutu; kutular) { - toplamAğırlık += kutu.ağırlık; - } ---- - -$(P -$(C Kutu) sınıfının tasarımcısı $(C Ağırlık)'ı daha sonradan başka şekilde tanımlarsa kodda değiştirilmesi gereken yerlerin sayısı bu sayede azalmış olur. -) - -$(H6 $(IX isim gizleme) $(IX gizleme, isim) Üst sınıfın gizlenen isimlerini alt sınıfta görünür yapmak) - -$(P -Aynı ismin hem üst sınıfta hem de alt sınıfta bulunması isim çakışmasına neden olur. Alt sınıfta aynı isimde tek bir işlev bile bulunsa, üst sınıfın işlevlerinin isimleri $(I gizlenirler) ve alt sınıf arayüzünde görünmezler: -) - ---- -class GenelHesap { - void hesapla(int x) { - // ... - } -} - -class ÖzelHesap : GenelHesap { - void hesapla() { - // ... - } -} - -void main() { - auto hesap = new ÖzelHesap; - hesap.hesapla(42); $(DERLEME_HATASI) -} ---- - -$(P -O çağrıda 42 değeri kullanıldığından, $(C ÖzelHesap) nesnesinin kalıtım yoluyla edindiği ve $(C int) türünde parametre alan $(C GenelHesap.hesapla) işlevinin çağrılacağını bekleyebiliriz. Oysa, her ne kadar parametre listeleri farklı olsa da $(C ÖzelHesap.hesapla) işlevi, aynı isme sahip olduğu için $(C GenelHesap.hesapla) işlevini gizler ve program derlenmez. -) - -$(P $(I Not: Üst sınıf işlevinin alt sınıfta değişik olarak yeniden tanımlanmasından bahsetmediğimize dikkat edin. Öyle olsaydı, $(LINK2 /ders/d/tureme.html, Türeme bölümünde) anlatıldığı gibi, parametre listesini üst sınıftakiyle aynı yapar ve $(C override) anahtar sözcüğünü kullanırdık. Burada, alt sınıfa eklenen yeni bir işlev isminin üst sınıftaki bir isimle aynı olduğu durumdan bahsediyoruz.) -) - -$(P -Derleyici, $(C GenelHesap.hesapla)'yı bu gizleme nedeniyle dikkate bile almaz ve $(C ÖzelHesap.hesapla)'nın bir $(C int) ile çağrılamayacağını belirten bir hata verir: -) - -$(SHELL_SMALL -Error: function $(HILITE deneme.ÖzelHesap.hesapla ()) is not callable -using argument types $(HILITE (int)) -) - -$(P -Bunun geçerli bir nedeni vardır: İsim gizleme olmasa, ileride bu sınıflara eklenen veya onlardan çıkartılan $(C hesapla) işlevleri hiçbir uyarı verilmeden kodun istenenden farklı bir işlevi çağırmasına neden olabilirler. İsim gizleme, nesne yönelimli programlamayı destekleyen başka dillerde de bulunan ve bu tür hataları önleyen bir olanaktır. -) - -$(P -Gizlenen isimlerin alt sınıf arayüzünde de görünmeleri istendiğinde yine $(C alias)'tan yararlanılır: -) - ---- -class GenelHesap { - void hesapla(int x) { - // ... - } -} - -class ÖzelHesap : GenelHesap { - void hesapla() { - // ... - } - - alias $(HILITE hesapla) = GenelHesap.hesapla; -} ---- - -$(P -Yukarıdaki $(C alias), üst sınıftaki $(C hesapla) ismini alt sınıf arayüzüne getirir ve böylece gizlenmesini önlemiş olur. -) - -$(P -O eklemeden sonra kod artık derlenir ve istenmiş olduğu gibi üst sınıfın $(C hesapla) işlevi çağrılır. -) - -$(P -Eğer daha uygun olduğu düşünülürse, üst sınıfın işlevi farklı bir isimle bile görünür hale getirilebilir: -) - ---- -class GenelHesap { - void hesapla(int x) { - // ... - } -} - -class ÖzelHesap : GenelHesap { - void hesapla() { - // ... - } - - alias $(HILITE genelHesapla) = GenelHesap.hesapla; -} - -void main() { - auto hesap = new ÖzelHesap; - hesap.$(HILITE genelHesapla(42)); -} ---- - -$(P -İsim gizleme üye değişkenler için de geçerlidir. İstendiğinde onların alt sınıf arayüzünde görünmeleri de $(C alias) ile sağlanır. -) - ---- -class ÜstSınıf { - int şehir; -} - -class AltSınıf : ÜstSınıf { - string şehir() const @property { - return "Kayseri"; - } -} ---- - -$(P -Her ne kadar birisi üye değişken ve diğeri üye işlev olsa da, alt sınıftaki $(C şehir), üst sınıfın aynı isimdeki üyesini gizler ve bu yüzden aşağıdaki kod derlenemez: -) - ---- -void main() { - auto nesne = new AltSınıf; - nesne.şehir = 42; $(DERLEME_HATASI) -} ---- - -$(P -Üst sınıfın üye değişkeni $(C alias) ile alt sınıf arayüzüne getirildiğinde kod artık derlenir. Aşağıdaki kod değişkenlerin de yeni isimle kullanılabileceklerini gösteriyor: -) - ---- -class ÜstSınıf { - int şehir; -} - -class AltSınıf : ÜstSınıf { - string şehir() const @property { - return "Kayseri"; - } - - alias $(HILITE şehirKodu) = ÜstSınıf.şehir; -} - -void main() { - auto nesne = new AltSınıf; - nesne.$(HILITE şehirKodu) = 42; -} ---- - - -$(H5 $(IX with) $(C with)) - -$(P -$(C with), bir nesnenin veya başka bir ismin tekrarlanmasını önler. Parantez içinde bir ifade veya isim alır ve kendi kapsamı içinde geçen isimleri belirlerken o ifade veya ismi de göz önünde bulundurur: -) - ---- -struct S { - int i; - int j; -} - -void main() { - auto s = S(); - - with ($(HILITE s)) { - $(HILITE i) = 1; // s.i ile aynı anlamda - $(HILITE j) = 2; // s.j ile aynı anlamda - } -} ---- - -$(P -Parantez içinde geçici bir nesne de oluşturulabilir. Oluşturulan nesne bir $(LINK2 /ders/d/deger_sol_sag.html, sol değer) haline gelir. Doğal olarak, bu geçici nesnenin yaşam süreci $(C with) kapsamı ile sınırlıdır: -) - ---- - with (S()) { - i = 1; // geçici nesnenin i üyesi - j = 2; // geçici nesnenin j üyesi - } ---- - -$(P -Daha sonra $(LINK2 /ders/d/gostergeler.html, Göstergeler bölümünde) göreceğimiz gibi, bu geçici nesnenin yaşamı $(C new) anahtar sözcüğü ile uzatılabilir. -) - -$(P -$(C with) özellikle $(C enum) gibi tür isimlerinin $(C case) bloklarında tekrarlanmalarını önlemek için yararlıdır: -) - ---- -enum Renk { kırmızı, turuncu } - -// ... - - final switch (r) $(HILITE with (Renk)) { - - case kırmızı: // Renk.kırmızı anlamında - // ... - - case turuncu: // Renk.turuncu anlamında - // ... - } ---- - -$(H5 Özet) - -$(UL - -$(LI $(C alias) var olan isimlere takma isimler verir.) - -$(LI $(C with) aynı nesnenin veya ismin tekrarlanmasını önler.) - -) - -Macros: - SUBTITLE=alias - - DESCRIPTION=Kodun okunurluğunu arttırma amacıyla takma isimler verme olanağı, alias. - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial alias takma isim alias - -SOZLER= -$(arayuz) -$(gecici) -$(ifade) -$(isim_gizleme) -$(kalitim) -$(sablon) -$(takma_isim) diff --git a/ddili/src/ders/d/alias_this.d b/ddili/src/ders/d/alias_this.d deleted file mode 100644 index a1664f2..0000000 --- a/ddili/src/ders/d/alias_this.d +++ /dev/null @@ -1,175 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX alias this) $(CH4 alias this)) - -$(P -Başka bağlamlarda başka anlamlara gelen $(C alias) ve $(C this) anahtar sözcükleri bir arada kullanıldıklarında farklı bir anlam kazanırlar. Bu yüzden, ikisi bir arada kullanıldığında tek bir anahtar sözcük olarak kabul edilmelidirler. -) - -$(P -$(IX otomatik tür dönüşümü) $(IX tür dönüşümü, otomatik) $(C alias this), bir yapının veya sınıfın otomatik tür dönüşümü yoluyla başka türler yerine geçmesini sağlar. Tür dönüşümü için başka bir seçenek $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünde) gördüğümüz $(C opCast) işlecidir. Farkları, $(C opCast)'in açıkça yapılan tür dönüşümleri için, $(C alias this)'in ise otomatik tür dönüşümleri için kullanılmasıdır. -) - -$(P -Bu iki sözcük birbirlerinden ayrı olarak yazılırlar; aralarına yapının veya sınıfın bir üyesi gelir: -) - ---- - alias $(I üye_değişken_veya_işlev) this; ---- - -$(P -$(C alias this) yapının veya sınıfın türünü gerektiğinde belirtilen üyenin türüne otomatik olarak dönüştürmeyi sağlar. Dönüşüm sonucunda üretilen değer o üyenin değeridir. -) - -$(P -Aşağıdaki $(C Kesir) örneği $(C alias this)'i bir $(I üye işlev) ile kullanıyor. Daha aşağıdaki $(C AraştırmaGörevlisi) örneğinde ise $(C alias this)'in $(I üye değişkenlerle) kullanımlarını göreceğiz. -) - -$(P -$(C değeri) işlevinin dönüş değeri $(C double) olduğundan, aşağıdaki $(C alias this) bildirimi $(C Kesir) nesnelerinin $(C double) değerler yerine kullanılabilmelerini sağlar: -) - ---- -import std.stdio; - -struct Kesir { - long pay; - long payda; - - $(HILITE double değeri()) const @property { - return double(pay) / payda; - } - - alias $(HILITE değeri) this; - - // ... -} - -double hesap(double soldaki, double sağdaki) { - return 2 * soldaki + sağdaki; -} - -void main() { - auto kesir = Kesir(1, 4); // 1/4 anlamında - writeln(hesap($(HILITE kesir), 0.75)); -} ---- - -$(P -Yukarıdaki yapının nesneleri $(C double) türünde değer beklenen ifadelerde geçtiklerinde $(C değeri) işlevi çağrılır ve o işlevin döndürdüğü değer kullanılır. Yukarıdaki kodda aslında $(C double) bekleyen $(C hesap) işlevine bir $(C Kesir) nesnesi gönderilebilmiş ve o hesapta $(C değeri) işlevinin döndürdüğü 0.25 kullanılmıştır. Program, 2 * 0.25 + 0.75 hesabının sonucunu yazdırır: -) - -$(SHELL -1.25 -) - -$(H5 $(IX çoklu kalıtım) $(IX kalıtım, çoklu) Çoklu kalıtım) - -$(P -Sınıfların en fazla bir $(C class)'tan türetilebildiklerini daha önce $(LINK2 /ders/d/tureme.html, Türeme bölümünde) görmüştük. (Hatırlayacağınız gibi, $(C interface)'ten türeme konusundan böyle bir kısıtlama yoktur.) Nesne yönelimli programlama dillerinden bazıları birden fazla sınıftan türetmeye de izin verirler. Birden fazla sınıftan türetmeye $(I çoklu kalıtım) denir. -) - -$(P -$(C alias this) sınıfların çoklu kalıtıma uygun olarak kullanılabilmelerini de sağlar. Birden fazla $(C alias this) bildirimi kullanıldığında o yapı veya sınıf o bildirimlerin sağladıkları bütün otomatik tür dönüşümlerinde kullanılabilir. -) - -$(P -$(HILITE $(I Not: Bu bölümdeki kodların en son denendikleri derleyici olan dmd 2.071 yalnızca tek $(C alias this) bildirimine izin veriyordu.)) -) - -$(P -Aşağıdaki $(C AraştırmaGörevlisi) sınıfı kendi içinde $(C Öğrenci) ve $(C Öğretmen) türlerini üye değişkenler olarak tutuyor. Bu türün nesneleri $(C alias this) bildirimleri sayesinde hem öğretmen hem de öğrenci gereken yerlerde kullanılabilirler: -) - ---- -import std.stdio; - -class Öğrenci { - string isim; - uint[] notlar; - - this(string isim) { - this.isim = isim; - } -} - -class Öğretmen { - string isim; - string ders; - - this(string isim, string ders) { - this.isim = isim; - this.ders = ders; - } -} - -class AraştırmaGörevlisi { - Öğrenci öğrenciKimliği; - Öğretmen öğretmenKimliği; - - this(string isim, string ders) { - this.öğrenciKimliği = new Öğrenci(isim); - this.öğretmenKimliği = new Öğretmen(isim, ders); - } - - /* Bu iki bildirim sayesinde bu tür hem Öğretmen hem de - * Öğrenci olarak kullanılabilir. - * - * Not: dmd 2.071 birden fazla 'alias this' tanımını - * desteklemez. */ - alias $(HILITE öğretmenKimliği) this; - $(CODE_COMMENT_OUT compiler limitation)alias $(HILITE öğrenciKimliği) this; -} - -void dersSaati(Öğretmen öğretmen, Öğrenci[] öğrenciler) -in { - assert(öğretmen !is null); - assert(öğrenciler.length > 0); - -} body { - writef("%s öğretmen şu öğrencilere %s anlatıyor:", - öğretmen.isim, öğretmen.ders); - - foreach (öğrenci; öğrenciler) { - writef(" %s", öğrenci.isim); - } - - writeln(); -} - -void main() { - auto öğrenciler = [ new Öğrenci("Özlem"), - new Öğrenci("Özgür") ]; - - // Hem Öğretmen hem de Öğrenci olabilen bir nesne: - auto arkan = new AraştırmaGörevlisi("Arkan", "matematik"); - - // 'arkan' bu kullanımda Öğretmen kimliğindedir: - dersSaati($(HILITE arkan), öğrenciler); - - // Aşağıdaki kullanımda ise öğrencilerden birisidir: - auto ayşeHoca = new Öğretmen("Ayşe", "fizik"); - $(CODE_COMMENT_OUT compiler limitation)dersSaati(ayşeHoca, öğrenciler ~ $(HILITE arkan)); -} ---- - -$(P -Programın çıktısı aynı nesnenin otomatik olarak farklı türler yerine kullanılabildiğini gösteriyor: -) - -$(SHELL -$(HILITE Arkan) öğretmen şu öğrencilere matematik anlatıyor: Özlem Özgür -Ayşe öğretmen şu öğrencilere fizik anlatıyor: Özlem Özgür $(HILITE Arkan) -) - -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 bölümler öğrenmek tutorial alias takma isim alias this - -SOZLER= -$(kalitim) - diff --git a/ddili/src/ders/d/araliklar.d b/ddili/src/ders/d/araliklar.d deleted file mode 100644 index 31e0fd3..0000000 --- a/ddili/src/ders/d/araliklar.d +++ /dev/null @@ -1,2016 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX aralık) Aralıklar) - -$(P -Aralıklar, topluluk elemanlarına erişim işlemini soyutlarlar. Bu soyutlama, çok sayıdaki veri yapısının çok sayıdaki algoritma ile uyumlu olarak kullanılmasını sağlar. Veri yapılarının nasıl gerçekleştirilmiş oldukları önemsizleşir, elemanlarına nasıl erişildiği ön plana çıkar. -) - -$(P -Aralıklar, türlerin belirli isimdeki işlevleri sunmaları ilkesi üzerine kurulu olan aslında çok basit bir kavramdır. Bu kavramla daha önce $(LINK2 /ders/d/foreach_opapply.html, Yapı ve Sınıflarda $(C foreach) bölümünde) de karşılaşmıştık: $(C empty), $(C front) ve $(C popFront()) üye işlevlerini tanımlayan her tür, $(C foreach) döngüsü ile kullanılabiliyordu. O üç işlev, $(C InputRange) aralık çeşidinin gerektirdiği işlevlerdir. -) - -$(P -Aralıklarla ilgili kavramları en basit aralık çeşidi olan $(C InputRange) ile göstereceğim. Diğer aralıkların farkları, başka işlevler de gerektirmeleridir. -) - -$(P -Daha ileri gitmeden önce aralıklarla doğrudan ilgili olan topluluk ve algoritma tanımlarını hatırlatmak istiyorum. -) - -$(P -$(IX topluluk) $(IX veri yapısı) $(B Topluluk (veri yapısı):) Topluluk, neredeyse bütün programlarda karşılaşılan çok yararlı bir kavramdır. Değişkenler belirli amaçlarla bir araya getirilirler ve sonradan bir topluluğun elemanları olarak kullanılırlar. D'deki topluluklar; diziler, eşleme tabloları, ve $(C std.container) modülünde tanımlanmış olan topluluk türleridir. Her topluluk belirli bir $(I veri yapısı) olarak gerçekleştirilir. Örneğin eşleme tabloları bir $(I hash table) veri yapısı gerçekleştirmesidir. -) - -$(P -Her veri yapısı türü, elemanları o veri yapısına özel biçimde barındırır ve elemanlara o veri yapısına özel biçimde eriştirir. Örneğin dizi veri yapısında elemanlar yan yana dururlar ve sıra numarası ile erişilirler; bağlı liste yapısında elemanlar düğümlerde saklanırlar ve bu düğümler aracılığıyla erişilirler; ikili ağaç veri yapısında düğümler kendilerinden sıralamada önceki ve sonraki elemanlara farklı dallar yoluyla erişim sağlarlar; vs. -) - -$(P -Ben bu bölümde $(I topluluk) ve $(I veri yapısı) deyimlerini aynı anlamda kullanacağım. -) - -$(P -$(IX algoritma) $(B Algoritma (işlev):) Veri yapılarının belirli amaçlarla ve belirli adımlar halinde işlenmelerine algoritma denir. Örneğin $(I sıralı arama) algoritması, aranan değeri topluluktaki bütün elemanları başından sonuna kadar ilerleyerek arayan bir algoritmadır; $(I ikili arama) algoritması, her adımda elemanların yarısını eleyerek arayan bir algoritmadır; vs. -) - -$(P -Ben bu bölümde $(I algoritma) ve $(I işlev) deyimlerini aynı anlamda kullanacağım. -) - -$(P -Aşağıdaki çoğu örnekte eleman türü olarak $(C int), topluluk türü olarak da $(C int[]) kullanacağım. Aslında aralıkların gücü şablonlarla birlikte kullanıldıklarında ortaya çıkar. Aralıkların birbirlerine uydurduğu çoğu topluluk ve çoğu algoritma şablondur. Bunların örneklerini bir sonraki bölüme bırakacağım. -) - -$(H5 Tarihçe) - -$(P -Algoritmalarla veri yapılarını birbirlerinden başarıyla soyutlayan bir kütüphane, C++ dilinin standart kütüphanesinin de bir parçası olan STL'dir (Standard Template Library). STL bu soyutlamayı C++'ın şablon olanağından yararlanarak gerçekleştirdiği $(I erişici) (iterator) kavramı ile sağlar. -) - -$(P -Çok güçlü bir soyutlama olmasına rağmen erişici kavramının bazı zayıflıkları da vardır. Aralıklar, erişicilerin bu zayıflıklarını gidermeye yönelik olarak Andrei Alexandrescu tarafından tasarlanmıştır. Phobos, aralıkları kullanan ilk ve bilinen tek kütüphanedir. -) - -$(P -Andrei Alexandrescu, $(LINK2 http://ddili.org/makale/eleman_erisimi_uzerine.html, Eleman Erişimi Üzerine) isimli makalesinde aralıkları tanıtır ve aralıkların erişicilerden neden daha üstün olduklarını gösterir. -) - -$(H5 Aralıklar D'de kaçınılmazdır) - -$(P -Aralıklar D'ye özgü bir kavramdır. Dilimler en işlevsel aralık çeşidi olan $(C RandomAccessRange)'e uyarlar ve Phobos, aralıklarla ilgili çok sayıda olanak içerir. Çoğu programda kendi aralık türlerimizi veya aralık işlevlerimizi yazmamız gerekmez. Buna rağmen aralıkların Phobos'ta nasıl kullanıldığını bilmek önemlidir. -) - -$(P -Phobos'taki çok sayıda algoritma, kullanımları sırasında farkedilmese bile aslında geçici aralık nesneleri döndürürler. Örneğin elemanların 10'dan büyük olanlarını seçmek için kullanılan aşağıdaki $(C filter()) dizi değil, aralık nesnesi döndürür: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - int[] sayılar = [ 1, 20, 7, 11 ]; - writeln(sayılar.filter!(sayı => sayı > 10)); -} ---- - -$(P -$(C writeln), $(C filter())'ın döndürmüş olduğu aralık nesnesini gerektikçe tembel olarak kullanır. Sonuçta, belirtilen kıstasa uyan elemanlar yazdırılırlar: -) - -$(SHELL -[20, 11] -) - -$(P -O sonuca bakarak $(C filter())'ın $(C int) dizisi döndürdüğü düşünülebilir; ancak bu doğru değildir. Döndürülen nesne bir dizi olmadığı için örneğin aşağıdaki satır derlenemez: -) - ---- - int[] seçilenler = sayılar.filter!(sayı => sayı > 10); $(DERLEME_HATASI) ---- - -$(P -Döndürülen nesnenin türünü hata mesajında görüyoruz: -) - -$(SHELL -Error: cannot implicitly convert expression (filter(sayılar)) -of type $(HILITE FilterResult!(__lambda2, int[])) to int[] -) - -$(P -$(I Not: O tür sizin denediğiniz Phobos sürümünde farklı olabilir.) -) - -$(P -O geçici aralık nesnesinin istendiğinde bir diziye de dönüştürülebileceğini aşağıda göstereceğim. -) - -$(H5 Algoritmaların geleneksel gerçekleştirmeleri) - -$(P -Geleneksel olarak algoritmalar işlemekte oldukları veri yapılarının nasıl gerçekleştirildiklerini bilmek zorundadırlar. Örneğin bir bağlı listenin elemanlarını sırayla çıkışa yazdıran aşağıdaki işlev, kullandığı bağlı listenin düğümlerinin $(C eleman) ve $(C sonraki) isminde iki üyesi bulunduğunu bilmek zorundadır: -) ---- -struct Düğüm { - int eleman; - Düğüm * sonraki; -} - -void yazdır(const(Düğüm) * liste) { - for ( ; liste; liste = liste.$(HILITE sonraki)) { - write(' ', liste.$(HILITE eleman)); - } -} ---- - -$(P -Benzer şekilde, bir diziyi yazdıran işlev de dizilerin $(C length) isminde niteliklerinin bulunduğunu ve elemanlarına $(C []) işleci ile erişildiğini bilmek zorundadır: -) - ---- -void yazdır(const int[] dizi) { - for (int i = 0; i != dizi.$(HILITE length); ++i) { - write(' ', dizi$(HILITE [i])); - } -} ---- - -$(P -$(I Not: Dizilerde ilerlerken $(C foreach)'in daha uygun olduğunu biliyoruz. Amacım algoritmaların geleneksel olarak veri yapılarına doğrudan bağlı olduklarını göstermek olduğu için, $(C for)'un gerçekten gerektiği bir durum olduğunu kabul edelim.) -) - -$(P -Algoritmaların veri yapılarına bu biçimde bağlı olmaları, onların her veri yapısı için özel olarak yazılmalarını gerektirir. Örneğin; dizi, bağlı liste, eşleme tablosu, ikili ağaç, yığın, vs. gibi veri yapılarının her birisi için ara(), sırala(), ortakOlanlarınıBul(), değiştir(), vs. gibi algoritmaların ayrı ayrı yazılmaları gerekir. Bunun sonucunda da A adet algoritmanın V adet veri yapısı ile kullanılabilmesi için gereken işlev sayısı A çarpı V'dir. (Not: Her algoritma her veri yapısı ile kullanılamadığı için gerçekte bu sayı daha düşüktür. Örneğin eşleme tabloları sıralanamazlar.) -) - -$(P -Öte yandan, aralıklar veri yapılarıyla algoritmaları birbirlerinden soyutladıkları için yalnızca A adet algoritma ve V adet veri yapısı yazmak yeterli olur. Yeni yazılan bir veri yapısı, onun sunduğu aralık çeşidini destekleyen bütün algoritmalarla kullanılmaya hazırdır; yeni yazılan bir algoritma da onun gerektirdiği aralık çeşidine uyan bütün veri yapıları ile işlemeye hazırdır. -) - -$(H5 Phobos aralıkları) - -$(P -Bu bölümün konusu olan aralıklar, $(C baş..son) biçiminde yazılan sayı aralıklarından farklıdır. Sayı aralıklarını $(C foreach) döngüsündeki ve dilimlerdeki kullanımlarından tanıyoruz: -) - ---- - int[] dilim = dizi[$(HILITE 5..10)]; // sayı aralığı, - // Phobos aralığı DEĞİL - - foreach (sayı; $(HILITE 3..7)) { // sayı aralığı, - // Phobos aralığı DEĞİL ---- - -$(P -Ben bu bölümde $(I aralık) yazdığım yerlerde Phobos aralıklarını kastedeceğim. -) - -$(P -Aralıklar bir $(I aralık sıradüzeni) oluştururlar. Bu sıradüzen en basit aralık olan $(C InputRange) ile başlar. Diğer aralıklar, temel aldıkları aralığın gerektirdiği işlevlere ek olarak başka işlevler de gerektirirler. Aralık çeşitleri, en temelden en işlevsele doğru ve gerektirdikleri işlevlerle birlikte şunlardır: -) - -$(UL - -$(LI $(C InputRange), $(I giriş aralığı): $(C empty), $(C front) ve $(C popFront()) işlevleri) - -$(LI $(C ForwardRange), $(I ilerleme aralığı): ek olarak $(C save) işlevi) - -$(LI $(C BidirectionalRange), $(I çift uçlu aralık): ek olarak $(C back) ve $(C popBack()) işlevleri) - -$(LI $(C RandomAccessRange), $(I rastgele erişimli aralık): ek olarak $(C []) işleci (sonlu veya sonsuz olmasına göre başka koşullar da gerektirir)) - -) - -$(P -Bu sıradüzeni aşağıdaki gibi gösterebiliriz. $(C RandomAccessRange), sonlu ve sonsuz olarak iki çeşittir: -) - -$(MONO - InputRange - $(ASIL giriş aralığı) - ↑ - ForwardRange - $(ASIL ilerleme aralığı) - ↗ ↖ - BidirectionalRange RandomAccessRange (sonsuz) - $(ASIL çift uçlu aralık) $(ASIL rastgele erişimli aralık) - ↑ - RandomAccessRange (sonlu) - $(ASIL rastgele erişimli aralık) -) - -$(P -Yukarıdaki aralıklar eleman erişimine yönelik aralıklardır. Onlara ek olarak eleman $(I çıkışı) ile ilgili olan bir aralık daha vardır: -) - -$(UL -$(LI $(C OutputRange), $(I çıkış aralığı): $(C put(aralık, eleman)) işlemini desteklemek) -) - -$(P -Bu beş aralık, algoritmaların veri yapılarından soyutlanmaları için yeterlidir. -) - -$(H6 Aralığı daraltarak ilerlemek) - -$(P -Şimdiye kadar çoğu örnekte kullandığımız ilerleme yönteminde aralığın kendi durumunda değişiklik olmaz. Örneğin bir dilimde $(C foreach) veya $(C for) ile ilerlendiğinde dilimin kendisi değişmez: -) - ---- - int[] dilim = [ 10, 11, 12 ]; - - for (int i = 0; i != dilim.length; ++i) { - write(' ', dilim[i]); - } - - assert(dilim.length == 3); // uzunluğu değişmez ---- - -$(P -Burada, salt ilerleme işleminin dilimde bir değişiklik oluşturmadığını belirtmek istiyorum. -) - -$(P -Farklı bir bakış açısı getiren bir yöntem, aralığı başından daraltarak ilerlemektir. Bu yöntemde aralığın hep ilk elemanına erişilir. İlerleme, her seferinde baştaki eleman çıkartılarak sağlanır: -) - ---- - for ( ; dilim.length; dilim = dilim[1..$]) { - write(' ', $(HILITE dilim[0])); // hep ilk elemana erişilir - } ---- - -$(P -Yukarıdaki döngünün $(I ilerlemesi), $(C dilim = dilim[1..$]) ifadesinin baştaki elemanı dilimden çıkartması ile sağlanmaktadır. Dilim, o ifadenin etkisiyle aşağıdaki aşamalardan geçerek daralır ve sonunda boşalır: -) - -$(MONO -[ 10, 11, 12 ] - [ 11, 12 ] - [ 12 ] - [ ] -) - -$(P -İşte Phobos aralıklarındaki ilerleme kavramı, aralığı bu şekilde başından daraltma düşüncesi üzerine kuruludur. ($(C BidirectionalRange) ve sonlu $(C RandomAccessRange) aralıkları son taraftan da daralabilirler.) -) - -$(P -O örneği yalnızca bu tür ilerleme kavramını göstermek için verdim; $(C for) döngülerinin o şekilde yazılması normal kabul edilmemelidir. -) - -$(P -Salt ilerlemiş olmak için elemanların dilimden bu şekilde çıkartılmaları çoğu durumda istenmeyeceğinden; asıl topluluğun kendisi değil, yalnızca ilerlemek için oluşturulan başka bir aralık tüketilir. Bu örnekteki asıl dilimi korumak için örneğin başka bir dilimden yararlanılabilir: -) - ---- - int[] dilim = [ 10, 11, 12 ]; - int[] dilim2 = dilim; - - for ( ; dilim2.length; $(HILITE dilim2 = dilim2[1..$])) { - write(' ', dilim2[0]); - } - - assert(dilim2.length == 0); // ← dilim2 boşalır - assert(dilim.length == 3); // ← dilim değişmez ---- - -$(P -Phobos işlevleri de asıl topluluğun değişmemesi için özel aralık nesneleri döndürürler. -) - -$(H5 $(IX InputRange) $(IX giriş aralığı) $(C InputRange), $(I giriş aralığı)) - -$(P -Bu çeşit aralık, yukarıdaki geleneksel $(C yazdır()) işlevlerinde de olduğu gibi elemanların art arda erişildikleri aralık çeşidini ifade eder. Bu erişim hep ileri yöndedir; tekrar başa dönülemez. Buna rağmen, çok sayıda algoritma yalnızca $(C InputRange) kullanarak yazılabilir; çünkü çoğu algoritma yalnızca $(I ileri yönde ilerleme) üzerine kuruludur. Programların standart girişlerinde olduğu gibi, okundukça elemanların tüketildikleri akımlar da bu tür aralık tanımına girerler. -) - -$(P -$(C InputRange) aralıklarının gerektirdiği üç işlevi bütünlük amacıyla bir kere daha hatırlatıyorum: -) - -$(UL - -$(LI $(IX empty) $(C empty): "boş mu" anlamına gelir ve aralığın sonuna gelinip gelinmediğini bildirir; aralık boş kabul edildiğinde $(C true), değilse $(C false) döndürmelidir) - -$(LI $(IX front) $(C front): "öndeki" anlamına gelir ve aralığın başındaki elemana erişim sağlar) - -$(LI $(IX popFront) $(C popFront()): "öndekini çıkart" anlamına gelir ve aralığın başındaki elemanı çıkartarak aralığı baş tarafından daraltır) - -) - -$(P -$(I Not: $(C empty) ve $(C front) işlevlerini nitelik olarak kullanılmaya uygun oldukları için parantezsiz, $(C popFront()) işlevini ise yan etkisi olan bir işlev olduğu için parametre listesi ile yazmaya karar verdim.) -) - -$(P -$(C yazdır()) işlevini bir kere de bu üç işlevden yararlanacak şekilde gerçekleştirelim: -) - ---- -void yazdır(T)(T aralık) { - for ( ; !aralık$(HILITE .empty); aralık$(HILITE .popFront())) { - write(' ', aralık$(HILITE .front)); - } - - writeln(); -} ---- - -$(P -Aralığın elemanlarının türü konusunda bir kısıtlama getirmiş olmamak için işlevi ayrıca şablon olarak tanımladığıma dikkat edin. $(C yazdır()) böylece topluluğun asıl türünden de bağımsız hale gelir ve $(C InputRange)'in gerektirdiği üç işlevi sunan her toplulukla kullanılabilir. -) - -$(H6 Bir $(C InputRange) örneği) - -$(P -Daha önce de karşılaşmış olduğumuz $(C Okul) türünü $(C InputRange) tanımına uygun olarak tekrar tasarlayalım. $(C Okul)'u bir $(C Öğrenci) topluluğu olarak düşünelim ve onu elemanlarının türü $(C Öğrenci) olan bir aralık olarak tanımlamaya çalışalım. -) - -$(P -Örneği kısa tutmuş olmak için bazı önemli konularla ilgilenmeyeceğim: -) - -$(UL - -$(LI yalnızca bu bölümü ilgilendiren üyeleri yazacağım) - -$(LI bütün türleri yapı olarak tasarlayacağım) - -$(LI $(C private), $(C public), $(C const) gibi aslında yararlı olan belirteçler kullanmayacağım) - -$(LI sözleşmeli programlama veya birim testi olanaklarından yararlanmayacağım) - -) - ---- -import std.string; - -struct Öğrenci { - string isim; - int numara; - - string toString() const { - return format("%s(%s)", isim, numara); - } -} - -struct Okul { - Öğrenci[] öğrenciler; -} - -void main() { - auto okul = Okul( [ Öğrenci("Ebru", 1), - Öğrenci("Derya", 2) , - Öğrenci("Damla", 3) ] ); -} ---- - -$(P -$(C Okul) türünü bir $(C InputRange) olarak kullanabilmek için, $(C InputRange)'in gerektirdiği üç üye işlevi tanımlamamız gerekiyor. -) - -$(P -$(C empty) işlevinin aralık boş olduğunda $(C true) döndürmesini sağlamak için doğrudan $(C öğrenciler) dizisinin uzunluğunu kullanabiliriz. Dizinin uzunluğu 0 olduğunda aralık da boş kabul edilmelidir: -) - ---- -struct Okul { - // ... - - @property bool empty() const { - return öğrenciler.length == 0; - } -} ---- - -$(P -Programda kullanırken $(C okul.empty) biçiminde parantezsiz olarak yazabilmek için işlevi $(C @property) belirteci ile tanımladım. -) - -$(P -$(C front) işlevinin aralıktaki ilk elemanı döndürmesi, dizinin ilk elemanı döndürülerek sağlanabilir: -) - ---- -struct Okul { - // ... - - @property ref Öğrenci front() { - return öğrenciler[0]; - } -} ---- - -$(P -$(I Not: Dizideki asıl elemana erişim sağlamış olmak için $(C ref) dönüş türü kullandığımıza dikkat edin. Öyle yazmasaydık, $(C Öğrenci) bir yapı türü olduğu için ilk elemanın kopyası döndürülürdü.) -) - -$(P -$(C popFront()) işlevinin aralığı başından daraltması, $(C öğrenciler) dizisini başında daraltarak sağlanabilir: -) - ---- -struct Okul { - // ... - - void popFront() { - öğrenciler = öğrenciler[1 .. $]; - } -} ---- - -$(P -$(I Not: Yukarıda da değindiğim gibi, salt ilerlemiş olmak için aralıktan öğrenci çıkartılıyor olması çoğu duruma uygun değildir. Bu sorunu daha sonra özel bir aralık türü yardımıyla gidereceğiz.) -) - -$(P -Bu üç işlev $(C Okul) türünün $(C InputRange) olarak kullanılması için yeterlidir. $(C Okul) nesnelerini artık başka hiçbir şey gerekmeden örneğin $(C yazdır()) şablonuna gönderebiliriz: -) - ---- - yazdır(okul); ---- - -$(P -$(C yazdır()), $(C InputRange) tanımına uyan $(C Okul)'u aralık işlevleri aracılığıyla kullanır. Sonuçta aralığın elemanları teker teker çıkışa yazdırılırlar: -) - -$(SHELL - Ebru(1) Derya(2) Damla(3) -) - -$(P -$(IX dilim, InputRange olarak) Böylece kendi yazdığımız bir türü $(C InputRange) tanımına uydurmuş ve $(C InputRange)'lerle işleyen bir işleve gönderebilmiş olduk. $(C Okul), Phobos veya başka kütüphanelerin $(C InputRange) alan algoritmalarıyla da kullanılmaya hazırdır. Bunu biraz aşağıda göreceğiz. -) - -$(H6 Dilimleri aralık olarak kullanabilmek için $(C std.array) modülü) - -$(P -En sık kullanılan topluluk çeşidi olan dilimler, en işlevsel aralık çeşidi olan $(C RandomAccessRange) olarak kullanılabilirler. Bunun için $(C std.array) modülünün eklenmesi yeterlidir. -) - -$(P -$(C std.array) modülü; $(C empty), $(C front), $(C popFront()) ve diğer aralık işlevlerini dilimler için özel olarak tanımlar. Böylece dilimler örneğin $(C yazdır()) işlevine gönderilmeye hazırdırlar: -) - ---- -import $(HILITE std.array); - -// ... - - yazdır([ 1, 2, 3, 4 ]); ---- - -$(P -$(I Not: Biraz aşağıda göreceğimiz $(C std.range) modülü eklendiğinde $(C std.array)'in ayrıca eklenmesine gerek yoktur.) -) - -$(P -Sabit uzunluklu dizilerden eleman çıkartılması mümkün olmadığından $(C popFront()) onlar için tanımlanamaz. Bu yüzden sabit uzunluklu diziler kendileri aralık olarak kullanılamazlar: -) - ---- -void yazdır(T)(T aralık) { - for ( ; !aralık.empty; aralık.popFront()) { $(DERLEME_HATASI) - write(' ', aralık.front); - } - - writeln(); -} - -void main() { - int[$(HILITE 4)] dizi = [ 1, 2, 3, 4 ]; - yazdır(dizi); -} ---- - -$(P -$(I Not: Derleme hatasının $(C yazdır())'ın çağrıldığı satırda oluşması hatanın kaynağını göstermesi açısından daha yararlı olurdu. Bunun için $(C yazdır())'a bir sonraki bölümde göreceğimiz $(C isInputRange)'den yararlanan bir şablon kısıtlaması eklenebilir.) -) - ---- -void yazdır(T)(T aralık) - if (isInputRange!T) { $(CODE_NOTE şablon kısıtlaması) - // ... -} -// ... - yazdır(dizi); $(DERLEME_HATASI) ---- - -$(P -Sabit uzunluklu bir dizinin elemanlarına aralık işlevleriyle erişmek yine de mümkündür. Yapılması gereken, dizinin kendisini değil, bütün diziye erişim sağlayan bir dilim kullanmaktır: -) - ---- - yazdır(dizi$(HILITE [])); // şimdi derlenir ---- - -$(P -Her dilimin aralık olarak kullanılabilmesinin aksine, aralıklar dizi olarak kullanılamazlar. Aralık elemanlarından dizi oluşturmak gerektiğinde elemanlar teker teker açıkça kopyalanmalıdır. Bunun için $(C std.array.array) işlevi kullanılabilir. $(C array()), $(C InputRange) aralığını başından sonuna kadar ilerler, her elemanı kopyalar, ve yeni bir dizi döndürür: -) - ---- -import std.array; - -// ... - - // Not: UFCS'ten de yararlanılıyor - auto öğrencilerinKopyaları = okul.$(HILITE array); - writeln(öğrencilerinKopyaları); ---- - -$(P -Çıktısı: -) - -$(SHELL -[Ebru(1), Derya(2), Damla(3)] -) - -$(P -$(IX string, InputRange olarak) $(IX dchar, dizgi aralığı) Kodda UFCS'ten de yararlanıldığına dikkat edin. UFCS kodun yazımı ile işleyişini birbirine uygun hale getirdiğinden özellikle aralık algoritmalarında çok yararlanılan bir olanaktır. -) - -$(H6 Dizgilerin $(C dchar) aralığına dönüşmeleri) - -$(P -Tanım gereği olarak zaten $(I karakter dizisi) olan dizgiler de $(C std.array) modülü sayesinde hemen hemen bütün aralık çeşitleri olarak kullanılabilirler. Bunun istisnaları, $(C char) ve $(C wchar) dizgilerinin $(C RandomAccessRange) tanımına giremiyor olmalarıdır. -) - -$(P -Ancak, $(C std.array) modülünün dizgilere özel önemli bir yararı daha vardır: Dizgilerde ileri veya geri yönde ilerlendiğinde elemanlara UTF kod birimleri olarak değil, Unicode karakterleri olarak erişilir. Bunun anlamı, ne tür dizgi olursa olsun dizgi elemanlarının $(I harf harf) ilerlenmesidir. -) - -$(P -Aşağıdaki dizgilerde $(C char)'a sığmadıklarını bildiğimiz ç ve ğ harflerinden başka $(C wchar)'a sığmayan $(I çift çizgili matematik A harfi) (𝔸) de bulunuyor. Bu ortamda desteklenmiyorsa bir soru işareti olarak görünüyor olabilir: -) - ---- -import std.array; - -// ... - - yazdır("abcçdefgğ𝔸"c); - yazdır("abcçdefgğ𝔸"w); - yazdır("abcçdefgğ𝔸"d); ---- - -$(P -Buna rağmen, programın çıktısı çoğu durumda zaten istemiş olacağımız gibidir: -) - -$(XXX 𝔸 harfi doğru çıksın diye SHELL yerine MONO_NOBOLDkullanıyoruz.) -$(MONO_NOBOLD - a b c ç d e f g ğ 𝔸 - a b c ç d e f g ğ 𝔸 - a b c ç d e f g ğ 𝔸 -) - -$(P -Bu çıktının $(LINK2 /ders/d/karakterler.html, Karakterler) ve $(LINK2 /ders/d/dizgiler.html, Dizgiler) bölümlerinde gördüğümüz davranışlara uymadığına dikkat edin. Hatırlarsanız, $(C char) ve $(C wchar) dizgilerinin elemanları UTF kod birimleridir. -) - -$(P -Yukarıdaki çıktılarda kod birimleri yerine Unicode karakterlerinin belirmesinin nedeni, aralık olarak kullanıldıklarında dizgilerin elemanlarının otomatik olarak Unicode karakterlerine dönüştürülmeleridir. Aşağıda göreceğimiz gibi, Unicode karakteri olarak beliren $(C dchar) değerleri dizgilerin asıl elemanları değil, onlardan oluşturulan $(LINK2 /ders/d/deger_sol_sag.html, $(I sağ değerlerdir)). -) - -$(P -Bunu hatırlamak için dizgilerin elemanlarını tek tek indeksleyerek yazdıralım: -) - ---- -void $(HILITE elemanlarınıYazdır)(T)(T dizgi) { - for (int i = 0; i != dizgi.length; ++i) { - write(' ', dizgi$(HILITE [i])); - } - - writeln(); -} - -// ... - - elemanlarınıYazdır("abcçdefgğ𝔸"c); - elemanlarınıYazdır("abcçdefgğ𝔸"w); - elemanlarınıYazdır("abcçdefgğ𝔸"d); ---- - -$(P -Doğrudan dizgi elemanlarına erişildiğinde Unicode harflerine değil, UTF kod birimlerine erişilmiş olunur: -) - -$(XXX 𝔸 harfi doğru çıksın diye SHELL yerine MONO_NOBOLD kullanıyoruz.) -$(MONO_NOBOLD - a b c � � d e f g � � � � � � - a b c ç d e f g ğ ��� ��� - a b c ç d e f g ğ 𝔸 -) - - -$(P -$(IX representation, std.string) Bu otomatik dönüşüm her duruma uygun değildir. Örneğin, bir dizginin ilk elemanına atamaya çalışan aşağıdaki program derlenemez çünkü $(C .front)'un dönüş değeri bir $(LINK2 /ders/d/deger_sol_sag.html, $(I sağ değerdir)): -) - ---- -import std.array; - -void main() { - char[] s = "merhaba".dup; - s.front = 'M'; $(DERLEME_HATASI) -} ---- - -$(SHELL -Error: front(s) is $(HILITE not an lvalue) -) - -$(P -Bir aralık algoritması dizginin asıl elemanlarını değiştirmek istediğinde (ve bu değişikliğin dizginin UTF kodlamasını bozmayacağı bir durumda), $(C std.string.represention) çağrılarak dizgi bir $(C ubyte) aralığı olarak kullanılabilir: -) - ---- -import std.array; -import std.string; - -void main() { - char[] s = "merhaba".dup; - s$(HILITE .representation).front = 'M'; // derlenir - assert(s == "Merhaba"); -} ---- - -$(P -$(C representation); $(C char), $(C wchar), ve $(C dchar) dizgilerinin asıl elemanlarını sırasıyla $(C ubyte), $(C ushort), ve $(C uint) aralıkları olarak sunar. -) - -$(H6 Kendi elemanları bulunmayan aralıklar) - -$(P -Yukarıda aralık örneği olarak kullandığımız dizilerde ve $(C Okul) nesnelerinde hep gerçek elemanlar bulunuyordu. Örneğin $(C Okul.front), var olan bir $(C Öğrenci) nesnesine referans döndürüyordu. -) - -$(P -Aralıkların bir üstünlüğü, bu konuda da esneklik getirmeleridir: $(C front)'un döndürdüğü elemanın bir topluluğun gerçek bir elemanı olması gerekmez. O $(I sözde eleman), örneğin $(C popFront()) her çağrıldığında hesaplanarak oluşturulabilir ve $(C front) her çağrıldığında döndürülebilir. -) - -$(P -Gerçek elemanları bulunmayan bir aralık örneğiyle aslında biraz yukarıda da karşılaştık: Dizgiler aralık olarak kullanıldıklarında UTF kod birimlerine değil, Unicode karakterlerine erişildiğini gördük. Oysa; $(C char) ve $(C wchar) Unicode karakteri ifade edemeyeceklerinden, aralık olarak kullandığımızda elde edilen Unicode karakterleri o dizgilerin gerçek elemanları olamazlar. $(C front)'un döndürdüğü karakter, dizgideki UTF kod birimlerinin bir araya getirilmelerinden $(I oluşturulan) bir $(C dchar)'dır: -) - ---- -import std.array; - -void main() { - dchar harf = "şu".front; // front'un döndürdüğü dchar, - // ş'yi oluşturan iki char'ın - // bileşimidir -} ---- - -$(P -Dizginin eleman türü $(C char) olduğu halde yukarıdaki $(C front)'un dönüş türü $(C dchar)'dır. O $(C dchar), dizgi içindeki iki UTF kod biriminden oluşmuştur ama kendisi dizginin elemanı değil, onlardan oluşan bir $(LINK2 /ders/d/deger_sol_sag.html, $(I sağ değerdir)). -) - -$(P -Buna benzer olarak, bazı aralıkların ise hiç elemanları yoktur; böyle aralıklar yalnızca başka aralıkların elemanlarına erişim sağlamak için kullanılırlar. Bu, yukarıda $(C Okul) aralığında ilerlerken karşılaştığımız eleman kaybedilmesi sorununu da ortadan kaldırır. Bunun için örneğin $(C Okul) türünün kendisi değil, tek amacı okuldaki öğrencilere erişim sağlamak olan özel bir tür $(C InputRange) olarak tanımlanır. -) - -$(P -Daha önce $(C Okul) içinde tanımlamış olduğumuz bütün aralık işlevlerini yeni $(C ÖğrenciAralığı) türüne taşıyalım. Dikkat ederseniz bu değişiklik sonrasında $(C Okul) artık kendisi bir aralık olarak kabul edilemez: -) - ---- -struct Okul { - Öğrenci[] öğrenciler; -} - -struct ÖğrenciAralığı { - Öğrenci[] öğrenciler; - - this(Okul okul) { - $(HILITE this.öğrenciler = okul.öğrenciler); - } - - @property bool empty() const { - return öğrenciler.length == 0; - } - - @property ref Öğrenci front() { - return öğrenciler[0]; - } - - void popFront() { - öğrenciler = öğrenciler[1 .. $]; - } -} ---- - -$(P -Yeni aralık, kendisine verilen $(C Okul)'un öğrencilerini gösteren bir dilim oluşturur ve $(C popFront()) içinde o dilimi tüketir. Bunun sonucunda da asıl dizi değişmemiş olur: -) - ---- - auto okul = Okul( [ Öğrenci("Ebru", 1), - Öğrenci("Derya", 2) , - Öğrenci("Damla", 3) ] ); - - yazdır($(HILITE ÖğrenciAralığı)(okul)); - - assert(okul.öğrenciler.length == 3); // asıl dizi değişmez ---- - -$(P -$(I Not: Bütün işlerini doğrudan üyesi olan dilime yaptırdığı için $(C ÖğrenciAralığı)'nın iyi bir örnek olmadığını düşünebiliriz. Çünkü nasıl olsa $(C Okul.öğrenciler) dizisinin bir dilimini kendimiz de doğrudan kullanabilirdik. Öte yandan, $(C öğrenciler) dizisi $(C Okul)'un özel bir üyesi de olabilirdi ve $(C ÖğrenciAralığı) en azından o özel üyeye erişim sağlamak için yararlı olabilirdi.) -) - -$(H6 $(IX sonsuz aralık) Sonsuz aralıklar) - -$(P -Kendi elemanları bulunmayan aralıkların başka bir yararı, sonsuz uzunlukta aralıklar oluşturabilmektir. -) - -$(P -Bir aralığın hiç sonlanmaması, $(C empty) işlevinin her zaman için $(C false) değerinde olması ile sağlanır. Her zaman için $(C false) değerinde olan $(C empty)'nin işlev olması da gerekmeyeceğinden bir $(C enum) değer olarak tanımlanır: -) - ---- - enum empty = false; // ← sonsuz aralık ---- - -$(P -Başka bir seçenek, değişmez bir $(C static) üye kullanmaktır: -) - ---- - static immutable bool empty = false; // üsttekiyle aynı ---- - -$(P -Bunun bir örneğini görmek için Fibonacci serisini üreten bir aralık düşünelim. Aşağıdaki aralık, yalnızca iki adet $(C int) üyesi bulunmasına rağmen sonsuz uzunluktaki Fibonacci serisi olarak kullanılabilir: -) - ---- -$(CODE_NAME FibonacciSerisi_InputRange)$(CODE_COMMENT_OUT)struct FibonacciSerisi -$(CODE_COMMENT_OUT){ - int baştaki = 0; - int sonraki = 1; - - enum empty = false; // ← sonsuz aralık - - @property int front() const { - return baştaki; - } - - void popFront() { - const ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } -$(CODE_COMMENT_OUT)} ---- - -$(P -$(I Not: Her ne kadar sonsuz olsa da, sayı türü olarak $(C int) kullandığı için $(C int.max)'tan daha büyük değerlere gelindiğinde $(C FibonacciSerisi) yanlış çalışır.) -) - -$(P -$(C FibonacciSerisi) nesneleri için $(C empty)'nin değeri hep $(C false) olduğundan, parametre olarak gönderildiğinde $(C yazdır())'ın içindeki $(C for) döngüsü hiç sonlanmaz: -) - ---- - yazdır(FibonacciSerisi()); // hiç sonlanmaz ---- - -$(P -Sonsuz aralıklar ancak sonuna kadar ilerlemenin gerekmediği durumlarda kullanılabilirler. $(C FibonacciSerisi)'nin yalnızca belirli adet elemanının nasıl kullanılabildiğini aşağıda göreceğiz. -) - -$(H6 $(IX aralık döndüren işlev) Aralık döndüren işlevler) - -$(P -Bir $(C ÖğrenciAralığı) nesnesini yukarıda açıkça $(C ÖğrenciAralığı(okul)) yazarak oluşturmuş ve kullanmıştık. -) - -$(P -Bazı durumlarda ise $(C ÖğrenciAralığı) gibi türleri açıkça yazmak yerine, o türün nesnelerini döndüren işlevlerden yararlanılır. Örneğin bütün işi bir $(C ÖğrenciAralığı) nesnesi döndürmek olan aşağıdaki işlev, kodlamayı kolaylaştırabilir: -) - ---- -ÖğrenciAralığı öğrencileri(ref Okul okul) { - return ÖğrenciAralığı(okul); -} - -// ... - - // Not: Burada da UFCS'ten yararlanılıyor - yazdır(okul.$(HILITE öğrencileri)); ---- - -$(P -Böylece kullanıcılar bazı durumlarda çok karmaşık olabilen özel aralık türlerinin isimlerini ve şablon parametrelerini bilmek ve açıkça yazmak yerine, onları döndüren işlevlerin kısa isimlerini hatırlayabilirler. -) - -$(P -$(IX take, std.range) Bunun bir örneğini çok basit olan $(C std.range.take) işlevinde görebiliriz. "Al" anlamına gelen $(C take()), kendisine verilen bir aralığın başındaki belirli adet elemana teker teker erişim sağlar. Aslında bu işlem $(C take()) işlevi tarafından değil, onun döndürmüş olduğu özel bir aralık türü tarafından gerçekleştirilir. Yine de biz $(C take())'i kullanırken bunu bilmek zorunda değilizdir: -) - ---- -import std.range; - -// ... - - auto okul = Okul( [ Öğrenci("Ebru", 1), - Öğrenci("Derya", 2) , - Öğrenci("Damla", 3) ] ); - - yazdır(okul.öğrencileri.$(HILITE take(2))); ---- - -$(P -Yukarıdaki kullanımda $(C take()), $(C okul) nesnesinin başındaki 2 elemana erişim sağlayacak olan geçici bir aralık nesnesi döndürür. $(C yazdır()) da $(C take())'in döndürmüş olduğu bu geçici aralık nesnesini kullanır: -) - -$(SHELL - Ebru(1) Derya(2) -) - -$(P -Yukarıdaki işlemin sonucunda $(C okul) nesnesinde hiçbir değişiklik olmaz; onun hâlâ 3 elemanı vardır: -) - ---- - yazdır(okul.öğrencileri.take(2)); - assert(okul.öğrenciler.length == 3); ---- - -$(P -$(C take()) gibi işlevlerin kendi amaçları için döndürdükleri aralıkların türleri çoğu durumda bizi ilgilendirmez. Onların isimleriyle bazen hata mesajlarında karşılaşabiliriz; veya daha önce de yararlanmış olduğumuz $(C typeof) ve $(C stringof) ile kendimiz de yazdırabiliriz: -) - ---- - writeln(typeof(okul.öğrencileri.take(2)).stringof); ---- - -$(P -Çıktısı, $(C take())'in döndürdüğü türün $(C Take) isminde bir şablon olduğunu gösteriyor: -) - -$(SHELL -Take!(ÖğrenciAralığı) -) - -$(H6 $(IX std.range) $(IX std.algorithm) $(C std.range) ve $(C std.algorithm) modülleri) - -$(P -Kendi türlerimizi aralık olarak tanımlamanın çok büyük bir yararı; onları yalnızca kendi işlevlerimizle değil, Phobos ve başka kütüphanelerin aralık algoritmalarıyla da kullanabilmemizdir. -) - -$(P -$(C std.range) modülünde özellikle aralıklarla ilgili olan çok sayıda olanak bulunur. $(C std.algorithm) modülü ise başka dillerin kütüphanelerinde de bulunan çok sayıda tanınmış algoritma içerir. -) - -$(P -$(IX swapFront, std.algorithm) Bir örnek olarak $(C std.algorithm.swapFront) algoritmasını $(C Okul) türü ile kullanalım. "Öndekini değiş tokuş et" anlamına gelen $(C swapFront), kendisine verilen iki $(C InputRange) aralığının ilk elemanlarını değiş tokuş eder. -) - -$(P - -) - ---- -import std.algorithm; - -// ... - - auto türkOkulu = Okul( [ Öğrenci("Ebru", 1), - Öğrenci("Derya", 2) , - Öğrenci("Damla", 3) ] ); - - auto amerikanOkulu = Okul( [ Öğrenci("Mary", 10), - Öğrenci("Jane", 20) ] ); - - $(HILITE swapFront)(türkOkulu.öğrencileri, - amerikanOkulu.öğrencileri); - - yazdır(türkOkulu.öğrencileri); - yazdır(amerikanOkulu.öğrencileri); ---- - -$(P -İki okuldaki ilk öğrenciler değişmiştir: -) - -$(SHELL - $(HILITE Mary(10)) Derya(2) Damla(3) - $(HILITE Ebru(1)) Jane(20) -) - -$(P -$(IX filter, std.algorithm) Başka bir örnek olarak $(C std.algorithm.filter) algoritmasına bakalım. $(C filter()), elemanların belirli bir kıstasa uymayanlarını elemekle görevli olan özel bir aralık döndürür. Bu işlem sırasında asıl aralıkta hiçbir değişiklik olmaz. -) - -$(P -$(IX kıstas) $(C filter())'a verilen kıstas çok genel olarak $(I uyanlar için $(C true), uymayanlar için $(C false)) üreten bir ifadedir. $(C filter())'a şablon parametresi olarak verilen kıstası bildirmenin bir kaç yolu vardır. Bir yol, daha önce de karşılaştığımız gibi isimsiz bir işlev kullanmaktır. Kısa olması için $(C ö) olarak adlandırdığım parametre aralıktaki her öğrenciyi temsil eder: -) - ---- - okul.öğrencileri.filter!(ö => ö.numara % 2) ---- - -$(P -Yukarıdaki ifadedeki kıstas, $(C okul.öğrencileri) aralığındaki elemanların numarası tek olanlarını seçer. -) - -$(P -$(C take()) işlevinde olduğu gibi, $(C filter()) da özel bir aralık nesnesi döndürür. Böylece, döndürülen aralık nesnesini de doğrudan başka işlevlere gönderebiliriz. Örneğin, seçilmiş olan elemanları üretecek olan aralık nesnesi $(C yazdır())'a gönderilebilir: -) - ---- - yazdır(okul.öğrencileri.filter!(ö => ö.numara % 2)); ---- - -$(P -O kodu sağdan sola doğru okuyarak şöyle açıklayabiliriz: $(I $(C okul.öğrencileri) aralığındaki elemanların tek numaralı olanlarını seçen bir aralık oluştur ve $(C yazdır()) işlevine gönder). -) - -$(P -Çıktısı yalnızca tek numaralı öğrencilerden oluşur: -) - -$(SHELL - Ebru(1) Damla(3) -) - -$(P -Seçilecek olan elemanlar için $(C true) üretmesi koşuluyla, kıstas $(C filter())'a bir işlev olarak da bildirilebilir: -) - ---- -import std.array; - -// ... - - bool başHarfiD_mi(Öğrenci öğrenci) { - return öğrenci.isim.front == 'D'; - } - - yazdır(okul.öğrencileri.filter!başHarfiD_mi); ---- - -$(P -Yukarıdaki örnekteki kıstas işlevi, aldığı $(C Öğrenci) nesnesinin baş harfi D olanları için $(C true), diğerleri için $(C false) döndürmektedir. -) - -$(P -$(I Not: O ifadede baş harf için $(C öğrenci.isim[0]) yazmadığıma dikkat edin. Öyle yazsaydım baş harfini değil, ilk UTF-8 kod birimini elde ederdim. Yukarıda da belirttiğim gibi; $(C front), $(C isim)'i bir aralık olarak kullanır ve her zaman için ilk Unicode karakterini, yani ilk harfini döndürür.) -) - -$(P -O kodun sonucunda da baş harfi D olan öğrenciler seçilir ve yazdırılır: -) - -$(SHELL - Derya(2) Damla(3) -) - -$(P -$(IX generate, std.range) $(C std.range) modülündeki $(C generate), bir işlevin döndürdüğü değerlerin bir $(C InputRange)'in elemanlarıymış gibi kullanılmalarını sağlar. İşlev gibi çağrılabilen herhangi bir değişken (işlev göstergesi, isimsiz işlev, vs.) alır ve kavramsal olarak o işlevin döndürdüğü değerlerden oluşan bir $(C InputRange) nesnesi döndürür. -) - -$(P -Döndürülen aralık nesnesi sonsuzdur. Bu nesnenin $(C front) niteliğine her erişildiğinde asıl işlev işletilir ve onun döndürdüğü değer, aralığın $(I elemanı) olarak sunulur. Bu nesnenin $(C popFront) işlevi ise hiç iş yapmaz. -) - -$(P -Örneğin, aşağıdaki $(C zarAtıcı) nesnesi sonsuz bir aralık olarak kullanılabilmektedir: -) - ---- -import std.stdio; -import std.range; -import std.random; - -void main() { - auto zarAtıcı = $(HILITE generate)!(() => uniform(0, 6)); - writeln(zarAtıcı.take(10)); -} ---- - -$(SHELL -[1, 0, 3, 5, 5, 1, 5, 1, 0, 4] -) - -$(H6 $(IX tembel aralık) Tembellik) - -$(P -Aralık döndüren işlevlerin başka bir yararı, o aralıkların tembel olarak kullanılabilmeleridir. Bu hem program hızı ve bellek kullanımı açısından çok yararlıdır, hem de sonsuz aralıkların var olabilmeleri zaten bütünüyle tembellik olanağı sayesindedir. -) - -$(P -Tembel aralıklar işlerini gerektikçe ve parça parça gerçekleştirirler. Bunun bir örneğini $(C FibonacciSerisi) aralığında görüyoruz: Elemanlar ancak gerektikçe $(C popFront()) işlevinde teker teker hesaplanırlar. $(C FibonacciSerisi) eğer tembel yerine hevesli bir aralık olsaydı, yani kullanılmadan önce bütün aralığı üretmeye çalışsaydı, sonsuza kadar işlemeye devam ederdi. Ürettiği elemanları saklaması da gerekeceği için sonsuz sayıdaki elemana da yer bulamazdı. -) - -$(P -Hevesli aralıkların başka bir sakıncası, sonlu sayıda bile olsalar belki de hiç kullanılmayacak olan elemanlar için bile gereksizce yer harcayacak olmalarıdır. -) - -$(P -Phobos'taki çoğu algoritma gibi $(C take()) ve $(C filter()) da tembellikten yararlanırlar. Örneğin $(C FibonacciSerisi)'ni $(C take())'e vererek bu sonsuz aralığın belirli sayıdaki elemanını kullanabiliriz: -) - ---- - yazdır(FibonacciSerisi().take(10)); ---- - -$(P -Çıktısı yalnızca ilk 10 sayıyı içerir: -) - -$(SHELL - 0 1 1 2 3 5 8 13 21 34 -) - -$(H5 $(IX ForwardRange) $(IX ilerleme aralığı) $(C ForwardRange), $(I ilerleme aralığı)) - -$(P -$(C InputRange), elemanları çıkartıldıkça tükenen aralık kavramını ifade ediyordu. -) - -$(P -Bazı aralıklar ise $(C InputRange) gibi işleyebilmelerinin yanında, aralığın belirli bir durumunu hatırlama yeteneğine de sahiptirler. $(C FibonacciSerisi) nesneleri bunu sağlayabilirler, çünkü $(C FibonacciSerisi) nesneleri serbestçe kopyalanabilirler ve bu kopyalar birbirlerinden bağımsız aralıklar olarak yaşamlarına devam edebilirler. -) - -$(P -$(IX save) $(C ForwardRange) aralıkları, aralığın belirli bir andaki kopyasını döndüren $(C save) işlevini de sunan aralıklardır. $(C save)'in döndürdüğü kopyanın asıl aralıktan bağımsız olarak kullanılabilmesi şarttır. Örneğin bir kopya üzerinde ilerlemek diğer kopyayı ilerletmemelidir. -) - -$(P -$(C std.array) modülünün eklenmiş olması dilimleri de otomatik olarak $(C ForwardRange) tanımına sokar. -) - -$(P -$(C save) işlevini $(C FibonacciSerisi) için gerçekleştirmek istediğimizde nesnenin bir kopyasını döndürmek yeterlidir: -) - ---- -$(CODE_NAME FibonacciSerisi)struct FibonacciSerisi { -// ... - - @property FibonacciSerisi save() const { - return this; - } -$(CODE_XREF FibonacciSerisi_InputRange)} ---- - -$(P -Döndürülen kopya, bu nesnenin kopyalandığı yerden devam edecek olan bağımsız bir aralıktır. -) - -$(P -$(IX popFrontN, std.range) $(C save)'in döndürdüğü nesnenin asıl aralıktan bağımsız olduğunu aşağıdaki gibi bir program yardımıyla görebiliriz. Programda yararlandığım $(C std.range.popFrontN()), kendisine verilen aralığın başından belirtilen sayıda eleman çıkartır. $(C bilgiVer()) işlevi de çıkışı kısa tutmak için yalnızca ilk beş elemanı gösteriyor: -) - ---- -$(CODE_XREF FibonacciSerisi)import std.range; - -// ... - -void bilgiVer(T)(const dchar[] başlık, const ref T aralık) { - writefln("%40s: %s", başlık, aralık.take(5)); -} - -void main() { - auto aralık = FibonacciSerisi(); - bilgiVer("Başlangıçtaki aralık", aralık); - - aralık.popFrontN(2); - bilgiVer("İki eleman çıkartıldıktan sonra", aralık); - - auto kopyası = $(HILITE aralık.save); - bilgiVer("Kopyası", kopyası); - - aralık.popFrontN(3); - bilgiVer("Üç eleman daha çıkartıldıktan sonra", aralık); - bilgiVer("Kopyası", kopyası); -} ---- - -$(P -O kodun çıktısı, $(C aralıktan)'tan eleman çıkartılmış olmasının $(C kopyası)'nı etkilemediğini gösterir.: -) - -$(SHELL - Başlangıçtaki aralık: [0, 1, 1, 2, 3] - İki eleman çıkartıldıktan sonra: [1, 2, 3, 5, 8] - $(HILITE Kopyası: [1, 2, 3, 5, 8]) - Üç eleman daha çıkartıldıktan sonra: [5, 8, 13, 21, 34] - $(HILITE Kopyası: [1, 2, 3, 5, 8]) -) - -$(P -$(C bilgiVer()) içinde aralıkları doğrudan $(C writefln)'e gönderdiğime ayrıca dikkat edin. Kendi yazdığımız $(C yazdır()) işlevinde olduğu gibi, $(C stdio) modülünün çıkış işlevleri de $(C InputRange) aralıklarını kullanabilirler. Bundan sonraki örneklerde $(C yazdır()) yerine $(C stdio)'nun çıkış işlevlerini kullanacağım. -) - -$(P -$(IX cycle, std.range) $(C ForwardRange) aralıklarıyla işleyen bir algoritma örneği olarak $(C std.range.cycle)'a bakabiliriz. $(C cycle()), kendisine verilen aralığı sürekli olarak tekrarlar. Başından tekrarlayabilmesi için aralığın ilk durumunu saklaması gerekeceğinden, bu aralığın bir $(C ForwardRange) olması şarttır. -) - -$(P -Artık bir $(C ForwardRange) de kabul edilen $(C FibonacciSerisi) nesnelerini $(C cycle()) işlevine gönderebiliriz: -) - ---- - writeln(FibonacciSerisi().take(5).cycle.take(20)); ---- - -$(P -Hem $(C cycle())'a verilen aralığın hem de $(C cycle())'ın döndürdüğü aralığın sonlu olmaları için iki noktada $(C take())'ten yararlanıldığına dikkat edin. Çıktısı, $(I $(C FibonacciSerisi) aralığının ilk beş elemanının tekrarlanmasından oluşan aralığın ilk yirmi elemanıdır): -) - -$(SHELL -[0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3] -) - -$(P -Kodun anlaşılmasını kolaylaştırmak için ara değişkenler de tanımlanabilir. Yukarıdaki tek satırlık kodun bir eşdeğeri şudur: -) - ---- - auto seri = FibonacciSerisi(); - auto başTarafı = seri.take(5); - auto tekrarlanmışı = başTarafı.cycle; - auto tekrarlanmışınınBaşTarafı = tekrarlanmışı.take(20); - - writeln(tekrarlanmışınınBaşTarafı); ---- - -$(P -Tembelliğin yararını burada bir kere daha hatırlatmak istiyorum: İlk dört satırda yalnızca asıl işlemleri gerçekleştirecek olan geçici aralık nesneleri oluşturulur. Bütün ifadenin üretmiş olduğu sayılar, $(C FibonacciSerisi.popFront()) işlevi içinde ve ancak gerektikçe hesaplanırlar. -) - -$(P -$(I Not: $(C ForwardRange) olarak $(C FibonacciSerisi) türünü kullanacağımızı söylediğimiz halde $(C cycle())'a $(C FibonacciSerisi.take(5)) ifadesini verdik. $(C take())'in döndürdüğü aralığın türü parametresine uyar: parametre olarak $(C ForwardRange) verildiğinde döndürdüğü aralık da $(C ForwardRange) türündedir. Bunu sağlayan $(C isForwardRange) olanağını bir sonraki bölümde göstereceğim.) -) - -$(H5 $(IX BidirectionalRange) $(IX çift uçlu aralık) $(C BidirectionalRange), $(I çift uçlu aralık)) - -$(P -$(IX back) $(IX popBack) $(C BidirectionalRange) aralıkları, $(C ForwardRange) işlevlerine ek olarak iki işlev daha sunarlar. $(C back), $(C front)'un benzeri olarak aralığın sonundaki elemanı döndürür. $(C popBack()) de $(C popFront())'un benzeri olarak aralığı sonundan daraltır. -) - -$(P -$(C std.array) modülü eklendiğinde dilimler $(C BidirectionalRange) tanımına da girerler. -) - -$(P -$(IX retro, std.range) Örnek olarak $(C BidirectionalRange) aralığı gerektiren $(C std.range.retro) işlevini göstermek istiyorum. $(C retro()), kendisine verilen aralığın $(C front)'unu $(C back)'ine, $(C popFront())'unu da $(C popBack())'ine bağlayarak aralıktaki elemanlara ters sırada erişilmesini sağlar: -) - ---- - writeln([ 1, 2, 3 ].retro); ---- - -$(P -Çıktısı: -) - -$(SHELL -[3, 2, 1] -) - -$(P -$(C retro())'nun döndürdüğü özel aralığın bir benzerini çok basit olarak aşağıdaki gibi tanımlayabiliriz. Yalnızca $(C int) dizileriyle işlediği için çok kısıtlı olsa da aralıkların gücünü göstermeye yetiyor: -) - ---- -import std.array; -import std.stdio; - -struct TersSırada { - int[] aralık; - - this(int[] aralık) { - this.aralık = aralık; - } - - @property bool empty() const { - return aralık.empty; - } - - @property int $(HILITE front)() const { - return aralık.$(HILITE back); // ← ters - } - - @property int back() const { - return aralık.front; // ← ters - } - - void popFront() { - aralık.popBack(); // ← ters - } - - void popBack() { - aralık.popFront(); // ← ters - } -} - -void main() { - writeln(TersSırada([ 1, 2, 3])); -} ---- - -$(P -Aralığı $(I ters sırada) kullandığı için $(C retro()) ile aynı sonuç elde edilir: -) - -$(SHELL -[3, 2, 1] -) - -$(H5 $(IX RandomAccessRange) $(IX rastgele erişimli aralık) $(C RandomAccessRange), $(I rastgele erişimli aralık)) - -$(P -$(IX opIndex) $(C RandomAccessRange), belirli sıradaki elemanlarına $(C []) işleci ile erişilebilen aralıkları ifade eder. $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünden) hatırlayacağınız gibi, $(C []) işleci $(C opIndex()) üye işlevi ile tanımlanır. -) - -$(P -$(C std.array) modülü genel olarak dilimleri de $(C RandomAccessRange) tanımına sokar. Ancak; UTF-8 ve UFT-16 kodlamaları harflere sıra numarasıyla erişimi desteklemedikleri için, $(C char) ve $(C wchar) dizgileri harf erişimi açısından $(C RandomAccessRange) aralığı olarak kullanılamazlar. Öte yandan, UTF-32 kodlamasında kodlarla harfler bire bir karşılık geldiklerinden, $(C dchar) dizgileri harf erişiminde $(C RandomAccessRange) olarak kullanılabilirler. -) - -$(P -$(IX sabit zamanda erişim) Her türün $(C opIndex()) işlevini kendisine en uygun biçimde tanımlayacağı doğaldır. Ancak, bilgisayar biliminin algoritma karmaşıklıkları ile ilgili olarak bu konuda bir beklentisi vardır: Rastgele erişim, $(I sabit zamanda) gerçekleşmelidir. Sabit zamanda erişim, erişim için gereken işlemlerin aralıktaki eleman adedinden bağımsız olması anlamına gelir. Aralıkta ne kadar eleman olursa olsun, hiçbirisinin erişimi aralığın uzunluğuna bağlı olmamalıdır. -) - -$(P -$(C RandomAccessRange) tanımına girebilmek için ek olarak aşağıdaki koşullardan $(I birisinin) daha sağlanmış olması gerekir: -) - -$(UL -$(LI sonsuz bir $(C ForwardRange) olmak) -) - -$(P -veya -) - -$(UL -$(LI $(IX length, BidirectionalRange) $(C length) niteliğini de sunan bir $(C BidirectionalRange) olmak) -) - -$(H6 Sonsuz $(C RandomAccessRange)) - -$(P -Önce $(I sonsuz $(C ForwardRange)) tanımı üzerine kurulu olan bir $(C RandomAccessRange) örneğine bakalım. Bu tanıma girebilmek için gereken işlevler şunlardır: -) - -$(UL -$(LI $(C InputRange)'in gerektirdiği $(C empty), $(C front) ve $(C popFront())) -$(LI $(C ForwardRange)'in gerektirdiği $(C save)) -$(LI $(C RandomAccessRange)'in gerektirdiği $(C opIndex())) -$(LI sonsuz olabilmek için $(C empty)'nin değerinin derleme zamanında $(C false) olarak belirlenmiş olması) -) - -$(P -$(C FibonacciSerisi)'nin en son tanımı onu bir $(C ForwardRange) yapmaya yetiyordu. Ancak, $(C opIndex()) işlevi $(C FibonacciSerisi) için sabit zamanda işleyecek şekilde gerçekleştirilemez; çünkü belirli bir elemana erişebilmek için o elemandan önceki elemanların da hesaplanmaları gerekir. Bunun anlamı; N'inci sıradaki elemanın hesaplanması için ondan önceki N-1 elemanın hesaplanması gerektiği, bu yüzden de işlem adedinin N'ye bağlı olduğudur. -) - -$(P -$(C opIndex()) işlevinin sabit zamanda işletilebildiği bir örnek olarak tamsayıların karelerinden oluşan sonsuz bir aralık tanımlayalım. Böyle bir aralık sonsuz olduğu halde bütün elemanlarının değerlerine sabit zamanda erişilebilir: -) - ---- -class KareAralığı { - int baştaki; - - this(int baştaki = 0) { - this.baştaki = baştaki; - } - - enum empty = false; - - @property int front() const { - return opIndex(0); - } - - void popFront() { - ++baştaki; - } - - @property KareAralığı save() const { - return new KareAralığı(baştaki); - } - - int opIndex(size_t sıraNumarası) const { - /* Bu işlev sabit zamanda işler */ - immutable tamsayıDeğeri = baştaki + cast(int)sıraNumarası; - return tamsayıDeğeri * tamsayıDeğeri; - } -} ---- - -$(P -$(I Not: $(C KareAralığı)'nın bir $(C struct) olarak tanımlanması daha uygun olurdu.) -) - -$(P -Hiçbir eleman için yer ayrılmadığı halde bu aralığın bütün elemanlarına $(C []) işleci ile erişilebilir: -) - ---- - auto kareler = new KareAralığı(); - - writeln(kareler$(HILITE [5])); - writeln(kareler$(HILITE [10])); ---- - -$(P -Çıktısı 5 ve 10 sıra numaralı elemanları içerir: -) - -$(SHELL -25 -100 -) - -$(P -Sıfırıncı eleman her zaman için aralığın ilk elemanını temsil etmelidir. Bunu denemek için yine $(C popFrontN())'den yararlanabiliriz: -) - ---- - kareler.popFrontN(5); - writeln(kareler$(HILITE [0])); ---- - -$(P -Aralığın ilk 5 elemanı sırasıyla 0, 1, 2, 3 ve 4'ün kareleri olan 0, 1, 4, 9 ve 16'dır. Onlar çıkartıldıktan sonraki ilk eleman artık bir sonraki sayının karesi olan 25'tir: -) - -$(SHELL -25 -) - -$(P -$(C KareAralığı) en işlevsel aralık olan $(C RandomAccessRange) olarak tanımlandığı için diğer aralık çeşitleri olarak da kullanılabilir. Örneğin $(C InputRange) olarak: -) - ---- - bool sonİkiHaneAynı_mı(int sayı) { - /* Doğru olabilmesi için en az iki rakamı bulunmalı */ - if (sayı < 10) { - return false; - } - - /* Son iki hanesi 11'e tam olarak bölünmeli */ - immutable sonİkiHane = sayı % 100; - return (sonİkiHane % 11) == 0; - } - - writeln(kareler.take(50).filter!sonİkiHaneAynı_mı); ---- - -$(P -Çıktısı, ilk 50 elemanın son iki hanesi aynı olanlarını içerir: -) - -$(SHELL -[100, 144, 400, 900, 1444, 1600] -) - -$(H6 Sonlu $(C RandomAccessRange)) - -$(P -Şimdi de $(I sonlu uzunluklu $(C BidirectionalRange)) tanımı üzerine kurulu olan bir $(C RandomAccessRange) örneğine bakalım. Bu çeşit bir aralık olarak kabul edilmek için gereken işlevler şunlardır: -) - -$(UL -$(LI $(C InputRange)'in gerektirdiği $(C empty), $(C front) ve $(C popFront())) -$(LI $(C ForwardRange)'in gerektirdiği $(C save)) -$(LI $(C BidirectionalRange)'in gerektirdiği $(C back) ve $(C popBack())) -$(LI $(C RandomAccessRange)'in gerektirdiği $(C opIndex())) -$(LI aralığın uzunluğunu bildiren $(C length)) -) - -$(P -$(IX chain, std.range) Bu örnekte, kendisine verilen bütün aralıklardaki bütün elemanları sanki tek bir aralığın elemanlarıymış gibi sunan $(C std.range.chain)'in bir benzerini tasarlayalım. $(C chain()) her tür elemanla ve farklı aralıklarla işleyebilir. Bu örneği kısa tutabilmek için biz yalnızca $(C int) dizileriyle işleyecek şekilde tanımlayacağız. -) - -$(P -Önce adına $(C BirArada) diyeceğimiz bu türün nasıl kullanılacağını göstermek istiyorum: -) - ---- - auto aralık = BirArada([ 1, 2, 3 ], - [ 101, 102, 103]); - writeln(aralık[4]); ---- - -$(P -İki farklı diziyle ilklenen $(C aralık), $(C [ 1, 2, 3, 101, 102, 103 ]) elemanlarından oluşan tek bir diziymiş gibi kullanılacak. Örneğin dizilerin ikisinde de 4 numaralı eleman bulunmadığı halde diziler art arda düşünüldüklerinde 102, 4 numaralı eleman olarak kabul edilecek: -) - -$(SHELL -102 -) - -$(P -Bütün aralık nesnesi yazdırıldığında da elemanlar tek bir dizi gibi görünecekler: -) - ---- - writeln(aralık); ---- - -$(P -Çıktısı: -) - -$(SHELL -[1, 2, 3, 101, 102, 103] -) - -$(P -$(C BirArada) türünün bir yararı, bu işlemler gerçekleştirilirken elemanların yeni bir diziye kopyalanmayacak olmalarıdır. Bütün elemanlar kendi dizilerinde durmaya devam edecekler. -) - -$(P -Belirsiz sayıda dilim ile ilklenecek olan bu aralık, $(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği bölümünde) gördüğümüz $(I belirsiz sayıda parametre) olanağından yararlanabilir: -) - ---- -struct BirArada { - const(int)[][] aralıklar; - - this(const(int)[][] aralıklar$(HILITE ...)) { - this.aralıklar = aralıklar.dup; - - başıTemizle(); - sonuTemizle(); - } - -// ... -} ---- - -$(P -Bu yapının elemanlarda değişiklik yapmayacağının bir göstergesi olarak eleman türünün $(C const(int)) olarak tanımlandığına dikkat edin. Öte yandan, ilerleme kavramını sağlayabilmek için dilimlerin kendileri $(C popFront()) tarafından değiştirilmek zorundadır. -) - -$(P -Kurucu içinde çağrıldığını gördüğümüz $(C başıTemizle()) ve $(C sonuTemizle()) işlevleri, aralıkların baştaki ve sondaki boş olanlarını çıkartmak için kullanılıyorlar. Aralığa zaten bir katkıları bulunmayan boş aralıkların işlemleri karmaşıklaştırmaları böylece önlenmiş olacak: -) - ---- -struct BirArada { -// ... - - private void başıTemizle() { - while (!aralıklar.empty && aralıklar.front.empty) { - aralıklar.popFront(); - } - } - - private void sonuTemizle() { - while (!aralıklar.empty && aralıklar.back.empty) { - aralıklar.popBack(); - } - } -} ---- - -$(P -O işlevleri daha sonra $(C popFront()) ve $(C popBack()) içinden de çağıracağız. -) - -$(P -$(C başıTemizle()) ve $(C sonuTemizle()) işlevlerinin başta ve sonda boş aralık bırakmayacaklarını bildiğimizden, tek bir alt aralığın bile kalmış olması bütün aralığın henüz tükenmediği anlamına gelir: -) - ---- -struct BirArada { -// ... - - @property bool empty() const { - return aralıklar.empty; - } -} ---- - -$(P -İlk alt aralığın ilk elemanı bu aralığın da ilk elemanıdır: -) - ---- -struct BirArada { -// ... - - @property int front() const { - return aralıklar.front.front; - } -} ---- - -$(P -İlk aralığın ilk elemanını çıkartmak, bu aralığın ilk elemanını çıkartmış olur. Bu işlem sonucunda ilk aralık boşalmış olabileceğinden, gerektiğinde o aralığın ve onu izleyen olası boş aralıkların da çıkartılmaları için $(C başıTemizle()) işlevinin çağrılması gerekir: -) - ---- -struct BirArada { -// ... - - void popFront() { - aralıklar.front.popFront(); - başıTemizle(); - } -} ---- - -$(P -Aralığın belirli bir durumunun kopyası, elimizde bulunan alt aralıklarla ilklenen yeni bir $(C BirArada) nesnesi döndürerek sağlanabilir: -) - ---- -struct BirArada { -// ... - - @property BirArada save() const { - return BirArada(aralıklar.dup); - } -} ---- - -$(P -Aralığın son tarafındaki işlemler baş tarafındakilerin benzerleridir: -) - ---- -struct BirArada { -// ... - - @property int back() const { - return aralıklar.back.back; - } - - void popBack() { - aralıklar.back.popBack(); - sonuTemizle(); - } -} ---- - -$(P -Bütün aralığın uzunluğu, alt aralıkların uzunluklarının toplamı olarak hesaplanabilir: -) - ---- -struct BirArada { -// ... - - @property size_t length() const { - size_t uzunluk = 0; - - foreach (aralık; aralıklar) { - uzunluk += aralık.length; - } - - return uzunluk; - } -} ---- - -$(P -$(IX fold, std.algorithm) Aynı işlem $(C std.algorithm.fold) işlevi ile daha kısa olarak da gerçekleştirilebilir. $(C fold()), şablon parametresi olarak aldığı işlemi kendisine verilen aralıktaki bütün elemanlara uygular. -) - ---- -import std.algorithm; - -// ... - - @property size_t length() const { - return aralıklar.fold!((a, b) => a + b.length)(size_t.init); - } ---- - -$(P -Şablon parametresindeki $(C a) şimdiye kadarki toplamı, $(C b) de aralıktaki her bir elemanı temsil eder. İlk işlev parametresi hesabın hangi aralıktaki elemanlara uygulanacağını, ikinci işlev parametresi de toplamın ilk değerini (burada 0) belirler. ($(C aralıklar)'ın $(LINK2 /ders/d/ufcs.html, UFCS'ten) yararlanılarak $(C fold)'dan önce yazıldığına dikkat edin.) -) - -$(P -$(I Not: $(C length) her çağrıldığında uzunluğun böyle baştan hesaplanması yerine $(C uzunluk) isminde bir üyeden de yararlanılabilir. Bu üyenin değeri kurucu işlev içinde bir kere baştan hesaplanabilir, ve ondan sonra $(C popFront()) ve $(C popBack()) işlevleri her çağrıldıklarında teker teker azaltılabilir.) -) - -$(P -Belirli bir sıra numarasındaki elemanın döndürülebilmesi için bütün alt aralıklara baştan sona doğru bakılması ve sıra numarasının hangi aralıktaki bir elemana denk geldiğinin bulunması gerekir: -) - ---- -struct BirArada { -// ... - - int opIndex(size_t sıraNumarası) const { - /* Hata mesajı için saklıyoruz */ - immutable baştakiSıraNumarası = sıraNumarası; - - foreach (aralık; aralıklar) { - if (aralık.length > sıraNumarası) { - return aralık[sıraNumarası]; - - } else { - sıraNumarası -= aralık.length; - } - } - - throw new Exception( - format("Geçersiz sıra numarası: %s (uzunluk: %s)", - baştakiSıraNumarası, this.length)); - } -} ---- - -$(P -$(I Not: $(C opIndex), yukarıdaki uyarının aksine sabit zamanda gerçekleşemez. Bu aralığın kabul edilir derecede hızlı işleyebilmesi için $(C aralıklar) üyesinin fazla uzun olmaması gerekir.) -) - -$(P -Tanımladığımız bu aralık, istediğimiz sayıda $(C int) dizisiyle kullanılmaya hazırdır. Kendisine vereceğimiz dizileri $(C take()) ve $(C array()) işlevleri yardımıyla bu bölümde tanımladığımız türlerden bile edinebiliriz: -) - ---- - auto aralık = BirArada(FibonacciSerisi().take(10).array, - [ 777, 888 ], - (new KareAralığı()).take(5).array); - - writeln(aralık.save); ---- - -$(P -Çıktısı, üç aralığın tek aralıkmış gibi kullanılabildiğini gösterir: -) - -$(SHELL -[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16] -) - -$(P -Bu aralığı başka çeşit aralık kullanan algoritmalara da gönderebiliriz. Örneğin $(C BidirectionalRange) gerektiren $(C retro())'ya: -) - ---- - writeln(aralık.save.retro); ---- - -$(SHELL -[16, 9, 4, 1, 0, 888, 777, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0] -) - -$(P -$(C BirArada)'yı bu bölümde öğrendiklerimizin bir uygulaması olarak tasarladık. Programlarınızda daha kapsamlı olan $(C std.range.chain)'i kullanmanızı öneririm. -) - -$(H5 $(IX OutputRange) $(IX çıkış aralığı) $(C OutputRange), $(I çıkış aralığı)) - -$(P -Şimdiye kadar gördüğümüz aralıklar hep elemanlara erişimle ilgili olan aralıklardır. $(C OutputRange) ise çıkış aralığıdır. $(C stdout)'ta olduğu gibi elemanların belirli bir hedefe yazıldıkları akımları temsil ederler. -) - -$(P -$(IX put) $(C OutputRange) aralıklarının gerektirdiği işlemi yukarıda kısaca $(C put(aralık, eleman)) olarak belirtmiştim. $(C put()), $(C std.range) modülünde tanımlanmış olan bir işlevdir; çıkış aralığının hangi olanaklara sahip olduğunu $(C static if) yardımıyla derleme zamanında belirler ve $(I elemanı aralığa gönderirken) elemana ve aralığa en uygun olan yöntemi kullanır. -) - -$(P -$(C put())'un sırayla denediği durumlar ve seçtiği yöntemler aşağıdaki tablodaki gibidir. Tablodaki durumlara yukarıdan aşağıya doğru bakılır ve uygun olan ilk durum seçilir. Tabloda $(C A), aralığın türünü; $(C aralık), bir aralık nesnesini; $(C E), eleman türünü; ve $(C e) de bir eleman nesnesini temsil ediyor: -) - -$(TABLE full, -$(HEAD2 Olası Durum, Seçilen Yöntem) -$(ROW2 -$(C A) türünün parametre olarak $(C E) alan$(BR) -$(C put) isminde bir üye işlevi varsa, - -$(C aralık.put(e);) - -) -$(ROW2 -$(C A) türünün parametre olarak $(C E[]) alan$(BR) -$(C put) isminde bir üye işlevi varsa, - -$(C aralık.put([ e ]);) - -) -$(ROW2 -$(C A) bir $(C InputRange) aralığıysa$(BR) -ve $(C e), $(C aralık.front)'a atanabiliyorsa, - -$(C aralık.front = e;) -$(BR) -$(C aralık.popFront();) - -) -$(ROW2 -$(C E) bir $(C InputRange) aralığıysa$(BR) -ve $(C A) aralığına kopyalanabiliyorsa, - -$(C for (; !e.empty; e.popFront())) -$(BR) -$(C put(aralık, e.front);) - -) -$(ROW2 -$(C A), parametre olarak $(C E) alabiliyorsa$(BR) -($(C A) örneğin bir $(C delegate) olabilir), - -$(C aralık(e);) - -) -$(ROW2 -$(C A), parametre olarak $(C E[]) alabiliyorsa$(BR) -($(C A) örneğin bir $(C delegate) olabilir), - -$(C aralık([ e ]);) - -) -) - -$(P -Ben bu kullanımlardan birincisinin bir örneğini göstereceğim: Tanımlayacağımız aralık türünün $(C put) isminde bir işlevi olacak ve bu işlev çıkış aralığının eleman türünü parametre olarak alacak. -) - -$(P -Tanımlayacağımız çıkış aralığı, kurulurken belirsiz sayıda dosya ismi alsın. Daha sonradan $(C put()) işlevi ile yazdırılan elemanları hem bu dosyaların hepsine, hem de $(C stdout)'a yazdırsın. Ek olarak, her elemandan sonra yine kurucusunda aldığı ayracı yazdırsın. -) - ---- -$(CODE_NAME ÇokHedefeYazan)struct ÇokHedefeYazan { - string ayraç; - File[] dosyalar; - - this(string ayraç, string[] dosyaİsimleri...) { - this.ayraç = ayraç; - - /* stdout her zaman dahil */ - this.dosyalar ~= stdout; - - /* Belirtilen her dosya ismi için yeni bir dosya */ - foreach (dosyaİsmi; dosyaİsimleri) { - this.dosyalar ~= File(dosyaİsmi, "w"); - } - } - - /* Dilimlerle kullanılan put() (dizgiler hariç) */ - void put(T)(T dilim) - if (isArray!T && !isSomeString!T) { - foreach (eleman; dilim) { - // Bu, aşağıdaki put()'u çağırmaktadır - put(eleman); - } - } - - /* Dilim olmayan türlerle ve dizgilerle kullanılan put() */ - void put(T)(T değer) - if (!isArray!T || isSomeString!T) { - foreach (dosya; dosyalar) { - dosya.write(değer, ayraç); - } - } -} ---- - -$(P -Her türden çıkış aralığı yerine geçebilmesi için $(C put()) işlevini de şablon olarak tanımladım. Bu sayede aşağıda hem $(C int) hem de $(C string) aralığı olarak kullanabiliyoruz. -) - -$(P -$(IX copy, std.algorithm) Phobos'ta $(C OutputRange) kullanan bir algoritma $(C std.algorithm.copy)'dir. $(C copy()), bir $(C InputRange) aralığının elemanlarını bir $(C OutputRange) aralığına kopyalayan çok basit bir işlevdir. -) - ---- -import std.traits; -import std.stdio; -import std.algorithm; - -$(CODE_XREF ÇokHedefeYazan)// ... - -void main() { - auto çıkış = ÇokHedefeYazan("\n", "deneme_0", "deneme_1"); - copy([ 1, 2, 3], çıkış); - copy([ "kırmızı", "mavi", "yeşil" ], çıkış); -} ---- - -$(P -Yukarıdaki kod, giriş aralıklarındaki elemanları hem $(C stdout)'a, hem de "deneme_0" ve "deneme_1" isimli dosyalara yazar: -) - -$(SHELL -1 -2 -3 -kırmızı -mavi -yeşil -) - -$(H6 $(IX dilim, OutputRange olarak) Dilimlerin $(C OutputRange) olarak kullanılmaları) - -$(P -$(C std.range), dilimleri $(C OutputRange) tanımına da sokar. ($(C std.array) ise yalnızca giriş aralıkları tanımına sokar). Ancak, dilimlerin $(C OutputRange) olarak kullanılmalarının beklenmedik bir etkisi vardır: $(C OutputRange) olarak kullanılan dilim, her $(C put()) işlemine karşılık bir eleman kaybeder. Üstelik kaybedilen eleman, yeni atanmış olan baştaki elemandır. -) - -$(P -Bunun nedeni, $(C put()) üye işlevleri bulunmayan dilimlerin yukarıdaki tablodaki şu yönteme uymalarıdır: -) - ---- - aralık.front = e; - aralık.popFront(); ---- - -$(P -Her bir $(C put()) için yukarıdaki kod işletildiğinde hem baştaki elemana yeni değer atanır, hem de $(C popFront())'un etkisiyle baştaki eleman dilimden çıkartılır: -) - ---- -import std.stdio; -import std.range; - -void main() { - int[] dilim = [ 1, 2, 3 ]; - $(HILITE put(dilim, 100)); - writeln(dilim); -} ---- - -$(P -Bir $(C OutputRange) olarak kullanıldığı halde dilim eleman kaybetmiştir: -) - -$(SHELL -[2, 3] -) - -$(P -Bu yüzden dilimin kendisi değil, başka bir dilim $(C OutputRange) olarak kullanılmalıdır: -) - ---- -import std.stdio; -import std.range; - -void main() { - int[] dilim = [ 1, 2, 3 ]; - int[] dilim2 = dilim; - - put($(HILITE dilim2), 100); - - writeln(dilim2); - writeln(dilim); -} ---- - -$(P -Bu sefer ikinci dilim tükendiği halde asıl dilim istediğimiz elemanlara sahiptir: -) - -$(SHELL -[2, 3] -[100, 2, 3] $(SHELL_NOTE istenen sonuç) -) - -$(P -Burada önemli bir noktaya dikkat etmek gerekir: $(C OutputRange) olarak kullanılan dilimin uzunluğu otomatik olarak artmaz. Dilimde yeterli yer olması programcının sorumluluğundadır: -) - ---- - int[] dilim = [ 1, 2, 3 ]; - int[] dilim2 = dilim; - - foreach (i; 0 .. 4) { // ← dilimde 4 elemana yer yok - put(dilim2, i * 100); - } ---- - -$(P -$(C popFront()) nedeniyle boşalan dilimde yer kalmadığı için program boş dilimin ilk elemanı bulunmadığını bildiren bir hatayla sonlanır: -) - -$(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) ve onun kolaylık işlevi $(C appender) dilimleri $(I sonuna eklenen bir $(C OutputRange)) olarak kullanmaya yarar. $(C appender)'ın döndürdüğü özel aralık nesnesinin kendi $(C put()) işlevi, verilen elemanı dilimin sonuna ekler: -) - ---- -import std.array; - -// ... - - auto sonunaEkleyen = appender([ 1, 2, 3 ]); - - foreach (i; 0 .. 4) { - sonunaEkleyen.put(i * 100); - } ---- - -$(P -Yukarıdaki koddaki $(C appender) bir dizi ile çağrılıyor, ve onun döndürmüş olduğu nesne $(C put()) işlevi çağrılarak bir $(C OutputRange) olarak kullanılıyor. $(C appender)'ın bir çıkış olarak kullanıldığında edindiği elemanlara $(C .data) niteliği ile erişilir: -) - ---- - writeln(sonunaEkleyen.data); ---- - -$(P -Çıktısı: -) - -$(SHELL -[1, 2, 3, 0, 100, 200, 300] -) - -$(P -$(C Appender) dizilerin $(C ~=) işlecini de destekler: -) - ---- - sonunaEkleyen $(HILITE ~=) 1000; - writeln(sonunaEkleyen.data); ---- - -$(P -Çıktısı: -) - -$(SHELL -[1, 2, 3, 0, 100, 200, 300, 1000] -) - -$(H5 Aralık şablonları) - -$(P -Bu bölümde kendi yazdığımız çoğu örnekte $(C int) aralıkları kullandık. Oysa aralıkların ve aralık kullanan algoritmaların şablon olarak tasarlanmaları kullanışlılıklarını büyük ölçüde arttırır. -) - -$(P -$(C std.range) modülü aralıklarla ilgili olan çok sayıda yardımcı şablon da tanımlar. Bunların nasıl kullanıldıklarını bir sonraki bölümde göstereceğim. -) - -$(H5 Özet) - -$(UL - -$(LI Aralıklar veri yapılarıyla algoritmaları birbirlerinden soyutlayan ve birbirleriyle uyumlu olarak kullanılmalarını sağlayan olanaktır.) - -$(LI Aralıklar D'ye özgü bir kavramdır ve Phobos'ta çok kullanılır.) - -$(LI Phobos'taki çoğu algoritma kendisi işlem yapmak yerine özel bir aralık nesnesi döndürür ve tembellikten yararlanır.) - -$(LI UFCS aralık algoritmaları ile çok uyumludur.) - -$(LI Dizgiler $(C InputRange) olarak kullanıldıklarında elemanlarına $(I harf harf) erişilir.) - -$(LI $(C InputRange)'in gerektirdiği işlevler $(C empty), $(C front) ve $(C popFront())'tur.) - -$(LI $(C ForwardRange)'in gerektirdiği ek işlev $(C save)'dir.) - -$(LI $(C BidirectionalRange)'in gerektirdiği ek işlevler $(C back) ve $(C popBack())'tir.) - -$(LI Sonsuz $(C RandomAccessRange)'in $(C ForwardRange)'e ek olarak gerektirdiği işlev $(C opIndex())'tir.) - -$(LI Sonlu $(C RandomAccessRange)'in $(C BidirectionalRange)'e ek olarak gerektirdiği işlevler $(C opIndex()) ve $(C length)'tir.) - -$(LI $(C std.array.appender) dilimlerin sonuna ekleyen bir $(C OutputRange) nesnesi döndürür. ) - -$(LI Dilimler sonlu $(C RandomAccessRange) aralıklarıdır) - -$(LI Sabit uzunluklu diziler aralık değillerdir.) - -) - -macros: - SUBTITLE=Aralıklar - - DESCRIPTION=Topluluk elemanlarına erişimi soyutlayarak her algoritmanın o algoritmaya uyan her toplulukla çalışabilmesini sağlayan Phobos'un aralıkları. - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial aralık range OutputRange InputRange ForwardRange BidirectionalRange RandomAccessRange - -SOZLER= -$(algoritma) -$(aralik) -$(bagli_liste) -$(degisken) -$(hevesli) -$(islev) -$(phobos) -$(sag_deger) -$(sol_deger) -$(tembel_degerlendirme) -$(topluluk) -$(veri_yapilari) diff --git a/ddili/src/ders/d/araliklar_baska.d b/ddili/src/ders/d/araliklar_baska.d deleted file mode 100644 index 8276d21..0000000 --- a/ddili/src/ders/d/araliklar_baska.d +++ /dev/null @@ -1,446 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX aralık) Başka Aralık Olanakları) - -$(P -Bundan önceki bölümdeki çoğu aralık örneğinde $(C int) aralıkları kullandık. Aslında topluluklar, algoritmalar, ve aralıklar, hep şablonlar olarak gerçekleştirilirler. Biz de bir önceki bölümde $(C yazdır()) işlevini şablon olarak tanımladığımız için farklı $(C InputRange) aralıklarıyla kullanabilmiştik: -) - ---- -void yazdır$(HILITE (T))(T aralık) { - // ... -} ---- - -$(P -$(C yazdır())'ın bir eksiği, şablon parametresinin bir $(C InputRange) olması gerektiği halde bunu bir şablon kısıtlaması ile belirtmiyor olmasıdır. (Şablon kısıtlamalarını $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar) bölümünde görmüştük.) -) - -$(P -$(C std.range) modülü, hem şablon kısıtlamalarında hem de $(C static if) deyimlerinde yararlanılmak üzere çok sayıda yardımcı şablon içerir. -) - -$(H5 Aralık çeşidi şablonları) - -$(P -Bu şablonların "öyle midir" anlamına gelen $(C is) ile başlayanları, belirli bir türün o aralık çeşidinden olup olmadığını belirtir. Örneğin $(C isInputRange!T), "$(C T) bir $(C InputRange) midir" sorusunu yanıtlar. Aralık çeşidini sorgulayan şablonlar şunlardır: -) - -$(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 -$(C yazdır()) işlevinin şablon kısıtlaması $(C isInputRange)'den yararlanarak şöyle yazılır: -) - ---- -void yazdır(T)(T aralık) - if ($(HILITE isInputRange!T)) { - // ... -} ---- - -$(P -Bunlar arasından $(C isOutputRange), diğerlerinden farklı olarak iki şablon parametresi alır: Birincisi desteklenmesi gereken aralık türünü, ikincisi ise desteklenmesi gereken eleman türünü belirler. Örneğin aralığın $(C double) türü ile uyumlu olan bir çıkış aralığı olması gerektiği şöyle belirtilir: -) - ---- -void birİşlev(T)(T aralık) - if (isOutputRange!($(HILITE T, double))) { - // ... -} ---- - -$(P -O kısıtlamayı $(I $(C T) bir $(C OutputRange) ise ve $(C double) türü ile kullanılabiliyorsa) diye okuyabiliriz. -) - -$(P -$(C static if) ile birlikte kullanıldıklarında bu şablonlar kendi yazdığımız aralıkların ne derece yetenekli olabileceklerini de belirlerler. Örneğin asıl aralık $(C ForwardRange) olduğunda $(C save()) işlevine de sahip olacağından, kendi yazdığımız özel aralık türünün de $(C save()) işlevini sunmasını o işlevden yararlanarak sağlayabiliriz. -) - -$(P -Bunu görmek için kendisine verilen aralıktaki değerlerin ters işaretlilerini üreten bir aralığı önce bir $(C InputRange) olarak tasarlayalım: -) - ---- -import std.range; - -struct Tersi(T) - if (isInputRange!T) { - T aralık; - - @property bool empty() { - return aralık.empty; - } - - @property auto front() { - return $(HILITE -aralık.front); - } - - void popFront() { - aralık.popFront(); - } -} ---- - -$(P -$(I Not: $(C front)'un dönüş türü olarak $(C auto) yerine biraz aşağıda göreceğimiz $(C ElementType!T) de yazılabilir.) -) - -$(P -Bu aralığın tek özelliği, $(C front) işlevinde asıl aralığın başındaki değerin ters işaretlisini döndürmesidir. -) - -$(P -Çoğu aralık türünde olduğu gibi, kullanım kolaylığı açısından bir de yardımcı işlev yazalım: -) - ---- -Tersi!T tersi(T)(T aralık) { - return Tersi!T(aralık); -} ---- - -$(P -Bu aralık örneğin bir önceki bölümde gördüğümüz $(C FibonacciSerisi) aralığıyla birlikte kullanılmaya hazırdır: -) - ---- -struct FibonacciSerisi { - int baştaki = 0; - int sonraki = 1; - - enum empty = false; - - @property int front() const { - return baştaki; - } - - void popFront() { - const ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } - - @property FibonacciSerisi save() const { - return this; - } -} - -// ... - - writeln(FibonacciSerisi().take(5).$(HILITE tersi)); ---- - -$(P -Çıktısı, aralığın ilk 5 elemanının ters işaretlilerini içerir: -) - -$(SHELL -[0, -1, -1, -2, -3] -) - -$(P -Doğal olarak, $(C Tersi) bu tanımıyla yalnızca bir $(C InputRange) olarak kullanılabilir ve örneğin $(C ForwardRange) gerektiren $(C cycle()) gibi algoritmalara gönderilemez: -) - ---- - writeln(FibonacciSerisi() - .take(5) - .tersi - .cycle $(DERLEME_HATASI) - .take(10)); ---- - -$(P -Oysa, asıl aralık $(C FibonacciSerisi) gibi bir $(C ForwardRange) olduğunda $(C Tersi)'nin de $(C save()) işlevini sunamaması için bir neden yoktur. Bu durum derleme zamanında $(C static if) ile denetlenir ve ek işlevler asıl aralığın yetenekleri doğrultusunda tanımlanırlar. Bu durumda, asıl aralığın bir kopyası ile kurulmuş olan yeni bir $(C Tersi) nesnesi döndürmek yeterlidir: -) - ---- -struct Tersi(T) - if (isInputRange!T) { -// ... - - $(HILITE static if) (isForwardRange!T) { - Tersi save() { - return Tersi(aralık.save()); - } - } -} ---- - -$(P -Yukarıdaki ek işlev sayesinde $(C Tersi!FibonacciSerisi) de artık bir $(C ForwardRange) olarak kabul edilir ve yukarıdaki $(C cycle()) satırı artık derlenir: -) - ---- - writeln(FibonacciSerisi() - .take(5) - .tersi - .cycle // ← artık derlenir - .take(10)); ---- - -$(P -Çıktısı, $(I Fibonacci serisinin ilk 5 elemanının ters işaretlilerinin sürekli olarak tekrarlanmasından oluşan aralığın ilk 10 elemanıdır): -) - -$(SHELL -[0, -1, -1, -2, -3, 0, -1, -1, -2, -3] -) - -$(P -$(C Tersi) aralığının duruma göre $(C BidirectionalRange) ve $(C RandomAccessRange) olabilmesi de aynı yöntemle sağlanır: -) - ---- -struct Tersi(T) - if (isInputRange!T) { -// ... - - static if (isBidirectionalRange!T) { - @property auto back() { - return -aralık.back; - } - - void popBack() { - aralık.popBack(); - } - } - - static if (isRandomAccessRange!T) { - auto opIndex(size_t sıraNumarası) { - return -aralık[sıraNumarası]; - } - } -} ---- - -$(P -Böylece örneğin dizilerle kullanıldığında elemanlara $(C []) işleci ile erişilebilir: -) - ---- - auto d = [ 1.5, 2.75 ]; - auto e = tersi(d); - writeln(e$(HILITE [1])); ---- - -$(P -Çıktısı: -) - -$(SHELL --2.75 -) - -$(H5 $(IX ElementType) $(IX ElementEncodingType) $(C ElementType) ve $(C ElementEncodingType)) - -$(P -$(C ElementType), aralıktaki elemanların türünü bildiren bir şablondur. $(C ElementType!T), "$(C T) aralığının eleman türü" anlamına gelir. -) - -$(P -Örneğin, belirli türden iki aralık alan ve bunlardan birincisinin elemanlarının türü ile uyumlu olan bir çıkış aralığı gerektiren bir işlevin şablon kısıtlaması şöyle belirtilebilir: -) - ---- -void işle(G1, G2, Ç)(G1 giriş1, G2 giriş2, Ç çıkış) - if (isInputRange!G1 && - isForwardRange!G2 && - isOutputRange!(Ç, $(HILITE ElementType!G1))) { - // ... -} ---- - -$(P -Yukarıdaki şablon kısıtlamasını şöyle açıklayabiliriz: $(C G1) bir $(C InputRange) ve $(C G2) bir $(C ForwardRange) olmalıdır; ek olarak, $(C Ç) de $(C G1)'in elemanlarının türü ile kullanılabilen bir $(C OutputRange) olmalıdır. -) - -$(P -$(IX dchar, dizgi aralığı) Dizgiler aralık olarak kullanıldıklarında elemanlarına harf harf erişildiği için dizgi aralıklarının eleman türü her zaman için $(C dchar)'dır. Bu yüzden dizgilerin UTF kodlama türü $(C ElementType) ile belirlenemez. UTF kodlama türünü belirlemek için $(C ElementEncodingType) kullanılır. Örneğin bir $(C wchar) dizgisinin $(C ElementType)'ı $(C dchar), $(C ElementEncodingType)'ı da $(C wchar)'dır. -) - -$(H5 Başka aralık nitelikleri) - -$(P -$(C std.range) modülü aralıklarla ilgili başka şablon olanakları da sunar. Bunlar da şablon kısıtlamalarında ve $(C static if) deyimlerinde kullanılırlar. -) - -$(UL - -$(LI $(IX isInfinite) $(C isInfinite): Aralık sonsuzsa $(C true) üretir.) - -$(LI $(IX hasLength) $(C hasLength): Aralığın $(C length) niteliği varsa $(C true) üretir.) - -$(LI $(IX hasSlicing) $(C hasSlicing): Aralığın $(C a[x..y]) biçiminde dilimi alınabiliyorsa $(C true) üretir.) - -$(LI $(IX hasAssignableElements) $(C hasAssignableElements): Aralığın elemanlarına değer atanabiliyorsa $(C true) üretir.) - -$(LI $(IX hasSwappableElements) $(C hasSwappableElements): Aralığın elemanları $(C std.algorithm.swap) ile değiş tokuş edilebiliyorsa $(C true) üretir.) - -$(LI $(IX hasMobileElements) $(IX move, std.algorithm) $(C hasMobileElements): Aralığın elemanları $(C std.algorithm.move) ile aktarılabiliyorsa $(C true) üretir. - -$(P -$(IX moveFront) $(IX moveBack) $(IX moveAt) Bu, aralık çeşidine bağlı olarak baştaki elemanı aktaran $(C moveFront())'un, sondaki elemanı aktaran $(C moveBack())'in, veya rastgele bir elemanı aktaran $(C moveAt())'in mevcut olduğunu belirtir. Aktarma işlemi kopyalama işleminden daha hızlı olduğundan $(C hasMobileElements) niteliğinin sonucuna bağlı olarak bazı işlemler $(C move()) ile daha hızlı gerçekleştirilebilirler. -) - -) - -$(LI $(IX hasLvalueElements) $(IX sol değer) $(C hasLvalueElements): Aralığın elemanları $(I sol değer) olarak kullanılabiliyorsa $(C true) üretir. Bu kavramı, $(I aralığın elemanları gerçekte var olan elemanlara referans iseler) diye düşünebilirsiniz. - -$(P -Örneğin $(C hasLvalueElements!FibonacciSerisi)'nin değeri $(C false)'tur çünkü $(C FibonacciSerisi) aralığının elemanları gerçekte var olan elemanlar değillerdir; hesaplanarak oluşturulurlar. Benzer şekilde $(C hasLvalueElements!(Tersi!(int[])))'in değeri de $(C false)'tur çünkü o aralığın da gerçek elemanları yoktur. Öte yandan, $(C hasLvalueElements!(int[]))'in değeri $(C true)'dur çünkü dilimler gerçekte var olan elemanlara erişim sağlarlar. -) - -) - -) - -$(P -Örneğin $(C empty), $(C isInfinite!T)'nin değerine bağlı olarak farklı biçimde tanımlanabilir. Böylece, asıl aralık sonsuz olduğunda $(C Tersi!T)'nin de derleme zamanında sonsuz olması sağlanmış olur: -) - ---- -struct Tersi(T) - if (isInputRange!T) { -// ... - - static if (isInfinite!T) { - // Tersi!T de sonsuz olur - enum empty = false; - - } else { - @property bool empty() { - return aralık.empty; - } - } - -// ... -} - -static assert( isInfinite!(Tersi!FibonacciSerisi)); -static assert(!isInfinite!(int[])); ---- - -$(H5 $(IX çok şekillilik, çalışma zamanı) $(IX inputRangeObject) $(IX outputRangeObject) Çalışma zamanı çok şekilliliği için $(C inputRangeObject()) ve $(C outputRangeObject())) - -$(P -Aralıklar, şablonların getirdiği $(I derleme zamanı çok şekilliliğine) sahiptirler. Biz de bir önceki ve bu bölümdeki çoğu örnekte bu olanaktan yararlandık. ($(I Not: Derleme zamanı çok şekilliliği ile çalışma zamanı çok şekilliliğinin farklarını $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar bölümündeki) "Derleme zamanı çok şekilliliği" başlığında görmüştük.)) -) - -$(P -Derleme zamanı çok şekilliliğinin bir etkisi, şablonun her farklı kullanımının farklı bir şablon türü oluşturmasıdır. Örneğin $(C take()) algoritmasının döndürdüğü özel aralık nesnesinin türü $(C take())'e gönderilen aralık türüne göre değişir: -) - ---- - writeln(typeof([11, 22].tersi.take(1)).stringof); - writeln(typeof(FibonacciSerisi().take(1)).stringof); ---- - -$(P -Çıktısı: -) - -$(SHELL -Take!(Tersi!(int[])) -Take!(FibonacciSerisi) -) - -$(P -Bunun doğal sonucu, farklı türlere sahip olan aralık nesnelerinin uyumsuz oldukları için birbirlerine atanamamalarıdır. Bu uyumsuzluk iki $(C InputRange) nesnesi arasında daha açık olarak da gösterilebilir: -) - ---- - auto aralık = [11, 22].tersi; - // ... sonraki bir zamanda ... - aralık = FibonacciSerisi(); $(DERLEME_HATASI) ---- - -$(P -Bekleneceği gibi, derleme hatası $(C FibonacciSerisi) türünün $(C Tersi!(int[])) türüne otomatik olarak dönüştürülemeyeceğini bildirir: -) - -$(SHELL -Error: cannot implicitly convert expression (FibonacciSerisi(0,1)) -of type $(HILITE FibonacciSerisi) to $(HILITE Tersi!(int[])) -) - -$(P -Buna rağmen, her ne kadar türleri uyumsuz olsalar da aslında her ikisi de $(C int) aralığı olan bu aralık nesnelerinin birbirlerinin yerine kullanılabilmelerini bekleyebiliriz. Çünkü kullanım açısından bakıldığında, bütün işleri $(C int) türünden elemanlara eriştirmek olduğundan, o elemanların hangi düzenek yoluyla üretildikleri veya eriştirildikleri önemli olmamalıdır. -) - -$(P -Phobos, bu sorunu $(C inputRangeObject()) ve $(C outputRangeObject()) işlevleriyle giderir. $(C inputRangeObject()), aralıkları $(I belirli türden elemanlara sahip belirli çeşit aralık) tanımıyla kullandırmaya yarar. Aralık nesnelerini türlerinden bağımsız olarak, örneğin $(I elemanları $(C int) olan $(C InputRange) aralığı) genel tanımı ile kullanabiliriz. -) - -$(P -$(C inputRangeObject()) bütün erişim aralıklarını destekleyecek kadar esnektir. Bu yüzden nesnelerin tanımı $(C auto) ile yapılamaz; aralık nesnesinin nasıl kullanılacağının açıkça belirtilmesi gerekir: -) - ---- - // "int giriş aralığı" anlamında - $(HILITE InputRange!int) aralık = [11, 22].tersi.$(HILITE inputRangeObject); - - // ... sonraki bir zamanda ... - - // Bu atama artık derlenir - aralık = FibonacciSerisi().$(HILITE inputRangeObject); ---- - -$(P -$(C inputRangeObject())'in döndürdüğü nesnelerin ikisi de $(C InputRange!int) olarak kullanılabilmektedir. -) - -$(P -Aralığın örneğin $(I $(C int) elemanlı bir $(C ForwardRange)) olarak kullanılacağı durumda ise açıkça $(C ForwardRange!int) yazmak gerekir: -) - ---- - $(HILITE ForwardRange!int) aralık = [11, 22].tersi.inputRangeObject; - - auto kopyası = aralık.$(HILITE save); - - aralık = FibonacciSerisi().inputRangeObject; - writeln(aralık.$(HILITE save).take(10)); ---- - -$(P -Nesnelerin $(C ForwardRange) olarak kullanılabildiklerini göstermek için $(C save()) işlevlerini çağırarak kullandım. -) - -$(P -$(C outputRangeObject()) de $(C OutputRange) aralıkları ile kullanılır ve onları $(I belirli tür elemanlarla kullanılabilen $(C OutputRange) aralığı) genel tanımına uydurur. -) - -$(H5 Özet) - -$(UL - -$(LI $(C std.range) modülü şablon kısıtlamalarında yararlı olan bazı şablonlar içerir.) - -$(LI $(C std.range) modülündeki şablonlar, tanımladığımız aralıkların başka aralıkların yeteneklerinin el verdiği ölçüde yetenekli olabilmelerini sağlarlar.) - -$(LI $(C inputRangeObject()) ve $(C outputRangeObject()), farklı türden aralık nesnelerinin $(I elemanları belirli türden olan belirli çeşitten aralık) genel tanımına uymalarını sağlarlar.) -) - -macros: - SUBTITLE=Başka Aralık Olanakları - - DESCRIPTION=Aralıklarla kullanılmaya elverişli yardımcı şablonlar - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial aralık range isOutputRange isInputRange isForwardRange isBidirectionalRange isRandomAccessRange ElementType ElementEncodingType isInfinite hasLength hasSlicing hasAssignableElements hasSwappableElements hasMobileElements hasLvalueElements inputRangeObject outputRangeObject - -SOZLER= -$(aralik) -$(cok_sekillilik) -$(otomatik) -$(phobos) -$(sol_deger) -$(tasima) diff --git a/ddili/src/ders/d/aritmetik_islemler.cozum.d b/ddili/src/ders/d/aritmetik_islemler.cozum.d deleted file mode 100644 index 60cd6d4..0000000 --- a/ddili/src/ders/d/aritmetik_islemler.cozum.d +++ /dev/null @@ -1,159 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Tamsayılar ve Aritmetik İşlemler) - -$(OL - -$(LI -$(C /) işlecini bölüm için, $(C %) işlecini de kalan için kullanabiliriz: - ---- -import std.stdio; - -void main() { - int birinci_sayı; - write("Birinci sayı: "); - readf(" %s", &birinci_sayı); - - int ikinci_sayı; - write("İkinci sayı : "); - readf(" %s", &ikinci_sayı); - - int bölüm = birinci_sayı / ikinci_sayı; - int kalan = birinci_sayı % ikinci_sayı; - - writeln(birinci_sayı, " = ", - ikinci_sayı, " * ", bölüm, " + ", kalan); -} ---- - -) - -$(LI -Kalanın 0 olup olmadığını $(C if) koşulu ile denetleyebiliriz: - ---- -import std.stdio; - -void main() { - int birinci_sayı; - write("Birinci sayı: "); - readf(" %s", &birinci_sayı); - - int ikinci_sayı; - write("İkinci sayı : "); - readf(" %s", &ikinci_sayı); - - int bölüm = birinci_sayı / ikinci_sayı; - int kalan = birinci_sayı % ikinci_sayı; - - // Burada artık writeln kullanamayacağımıza dikkat - // edin. Satırı daha sonra sonlandırmak zorundayız. - write(birinci_sayı, " = ", ikinci_sayı, " * ", bölüm); - - // Bu kısmını ancak kalan 0 olmadığı zaman yazdırıyoruz - if (kalan != 0) { - write(" + ", kalan); - } - - // Artık satırı sonlandırıyoruz - writeln(); -} ---- - -) - -$(LI - ---- -import std.stdio; - -void main() { - while (true) { - write("0: Çık, 1: Toplama, 2: Çıkarma, ", - "3: Çarpma, 4: Bölme - İşlem? "); - - int işlem; - readf(" %s", &işlem); - - // Önce işlemi denetleyelim - if ((işlem < 0) || (işlem > 4)) { - writeln("Bu işlemi daha öğrenmedim"); - continue; - } - - if (işlem == 0){ - writeln("Güle güle!"); - break; - } - - // Eğer bu noktaya gelmişsek, bildiğimiz 4 işlemden - // birisi ile ilgilendiğimizden eminiz. Artık - // kullanıcıdan 2 sayıyı isteyebiliriz: - - int birinci; - int ikinci; - - write("Birinci sayı? "); - readf(" %s", &birinci); - - write(" İkinci sayı? "); - readf(" %s", &ikinci); - - // İşlemin sonucunu bu değişkene yerleştireceğiz - int sonuç; - - if (işlem == 1) { - sonuç = birinci + ikinci; - - } else if (işlem == 2) { - sonuç = birinci - ikinci; - - } else if (işlem == 3) { - sonuç = birinci * ikinci; - - } else if (işlem == 4) { - sonuç = birinci / ikinci; - - } else { - writeln( - "Programda bir hata var! ", - "Bu noktaya kesinlikle gelmemeliydik..."); - break; - } - - writeln(" Sonuç: ", sonuç); - } -} ---- - -) - -$(LI - ---- -import std.stdio; - -void main() { - int sayı = 1; - - while (sayı <= 10) { - if (sayı != 7) { - writeln(sayı); - } - - ++sayı; - } -} ---- - -) - -) - -Macros: - SUBTITLE=Aritmetik İşlemler Problem Çözümü - - DESCRIPTION=Aritmetik İşlemler Problem Çözümü - - KEYWORDS=d programlama dili bölümler öğrenmek tutorial aritmetik işlemler problem çözüm diff --git a/ddili/src/ders/d/aritmetik_islemler.d b/ddili/src/ders/d/aritmetik_islemler.d deleted file mode 100644 index b0ccc32..0000000 --- a/ddili/src/ders/d/aritmetik_islemler.d +++ /dev/null @@ -1,874 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX tamsayı) $(IX aritmetik işlemler) Tamsayılar ve Aritmetik İşlemler) - -$(P -D'nin karar verme ile ilgili yapılarından $(C if)'i ve $(C while)'ı gördük. Bu bölümde temel türlerin sayısal olanlarıyla yapılan aritmetik işlemlere bakacağız. Böylece bundan sonraki bölümlerde çok daha becerikli ve ilginç programlar yazabileceksiniz. -) - -$(P -Aritmetik işlemler aslında son derece basittirler çünkü zaten günlük hayatımızda her zaman karşımıza çıkarlar. Buna rağmen, temel türlerle ilgilenirken mutlaka bilinmesi gereken çok önemli kavramlar da vardır. $(I Tür uzunluğu), $(I taşma), ve $(I kırpılma) kavramlarını anlıyorsanız bütün konuyu bu tabloya bakarak geçebilirsiniz: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    İşleç Etkisi Örnek kullanım
    ++değerini bir arttırır++değişken
    --değerini bir azaltır--değişken
    +iki değerin toplamıbirinci + ikinci
    -birinciden ikincinin çıkarılmışıbirinci - ikinci
    *iki değerin çarpımıbirinci * ikinci
    /birincinin ikinciye bölümübirinci / ikinci
    %birincinin ikinciye bölümününden kalanbirinci % ikinci
    ^^birincinin ikinci'nin değeri kadar üssü$(BR)(birincinin ikinci kere kendisiyle çarpımı)birinci ^^ ikinci
    - -$(P -$(IX +=) $(IX -=) $(IX *=) $(IX /=) $(IX %=) $(IX ^^=) Tablodaki ikili işleçlerin yanına $(C =) karakteri gelenleri de vardır: $(C +=), $(C -=), $(C *=), $(C /=), $(C %=), ve $(C ^^=). Bunlar işlemin sonucunu soldaki değişkene atarlar: -) - ---- - sayı += 10; ---- - -$(P -O ifade $(C sayı)'ya 10 ekler ve sonucu yine $(C sayı)'ya atar; sonuçta değerini 10 arttırmış olur. Şu ifadenin eşdeğeridir: -) - ---- - sayı = sayı + 10; ---- - -$(P $(B Taşma:) Her değer her türe sığmaz ve taşabilir. Örneğin, 0 ile 255 arasında değerler tutabilen $(C ubyte)'a 260 değeri verilmeye kalkışılırsa değeri 4 olur. ($(I Not: C ve C++ gibi bazı dillerin tersine, taşma D'de işaretli türler için de yasaldır ve işaretsiz türlerle aynı davranışa sahiptir.)) -) - -$(P $(B Kırpılma:) Tamsayılar virgülden sonrasını tutamazlar. Örneğin 3/2 ifadesinin değeri 1 olur.) - -$(P -Eğer bu kavramları örneğin başka dillerden biliyorsanız, bu kadarı yetebilir. İsterseniz geri kalanını okumayabilirsiniz, ama yine de sondaki problemleri atlamayın. -) - -$(P -Bu bölüm ilgisiz bilgiler veriyor gibi gelebilir; çünkü aritmetik işlemler hepimizin günlük hayatta sürekli olarak karşılaştığımız kavramlardır: Tanesi 10 lira olan bir şeyden iki tane alırsak 20 lira veririz, veya üçü 45 lira olan şeylerin tanesi 15 liradır. -) - -$(P -Ne yazık ki işler bilgisayarda bu kadar basit olmayabilir. Sayıların bilgisayarda nasıl saklandıklarını bilmezsek, örneğin 3 milyar borcu olan bir firmanın 3 milyar daha borç alması sonucunda borcunun 1.7 milyara $(I düştüğünü) görebiliriz. Başka bir örnek olarak, 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2 tane yetecek diye hesaplayabiliriz. -) - -$(P -Bu bölüm size öncekilerden daha teknik gelebilir ama tamsayıların bilgisayarda nasıl ifade edildiklerinin bir programcı tarafından mutlaka bilinmesi gerekir. -) - -$(H6 Tamsayılar) - -$(P -Tamsayılar ancak tam değerler alabilen türlerdir: -2, 0, 10, vs. Bu türler 2.5 gibi kesirli değerler tutamazlar. Daha önce temel türler tablosunda da gördüğünüz tamsayı türleri şunlardır: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $(BR)Tür Bit$(BR)Uzunluğu İlk$(BR)Değeri
    byte80
    ubyte80
    short160
    ushort160
    int320
    uint320
    long640L
    ulong640L
    - -$(P -Hatırlarsanız, tür isimlerinin başındaki $(C u) karakteri "unsigned"dan geliyordu ve "işaretsiz" demekti. O türler $(I eksi işareti olmayan) türlerdir ve yalnızca sıfır ve daha büyük değerler alabilirler. -) - -$(H6 $(IX bit) Bitler ve tür uzunlukları) - -$(P -Günümüzdeki bilgisayar sistemlerinde en küçük bilgi parçası bittir. Bit, elektronik düzeyde ve devrelerin belirli noktalarında $(I elektrik geriliminin var olup olmaması) kavramıyla belirlendiği için, ancak iki durumdan birisinde bulunabilir. Bu durumlar 0 ve 1 değerleri olarak kabul edilmişlerdir. Yani sonuçta bir bit, iki değişik değer saklayabilir. -) - -$(P -Yalnızca iki durumla ifade edilebilen kavramlarla fazla karşılaşmadığımız için bitin kullanışlılığı da azdır: yazı veya tura, odada ışıkların açık olup olmadığı, vs. gibi iki durumu olan kavramlar... -) - -$(P -Biraz ileri giderek iki biti bir araya getirirsek, ikisinin birlikte saklayabilecekleri toplam değer adedi artar. İkisinin ayrı ayrı 0 veya 1 durumunda olmalarına göre toplam 4 olasılık vardır. Soldaki rakam birinci biti, sağdaki rakam da ikinci biti gösteriyor olsun: 00, 01, 10, ve 11. Yani bir bit eklemeyle toplam durum sayısı ikiye katlanmış olur. Bit eklemenin etkisini daha iyi görebilmek için bir adım daha atabiliriz: Üç bit, toplam 8 değişik durumda bulunabilir: 000, 001, 010, 011, 100, 101, 110, 111. -) - -$(P -Bu sekiz durumun hangi tamsayı değerlerine karşılık gelecekleri tamamen anlaşmalara ve geleneklere kalmıştır. Yoksa örneğin 000 durumu 42 değerini, 001 durumu 123 değerini, vs. gösteriyor da olabilirdi. Tabii bu kadar ilgisiz değerler kullanışlı olmayacaklarından, 3 bitlik bir türü örnek alırsak, bu 8 durumun işaretli ve işaretsiz olarak kullanılmasında aldığı değerler şu tablodakine benzer: -) - - - - - - - - - - - -
    Bitlerin$(BR)Durumu İşaretsiz$(BR)Değer İşaretli$(BR)Değer
    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 -Burada görmenizi istediğim, 3 bitten nasıl 8 farklı değer elde edilebildiğidir. -) - -$(P -Görüldüğü gibi, eklenen her bit, saklanabilecek bilgi miktarını iki katına çıkartmaktadır. Bunu devam ettirirsek; bitlerin bir araya getirilmelerinden oluşturulan değişik uzunluktaki türlerin saklayabildikleri farklı değer miktarlarını, bir önceki bit uzunluğunun saklayabileceği değer miktarını 2 ile çarparak şöyle görebiliriz: -) - - - - - - - - - - - - - - - - - - -
    Bit$(BR)Adedi Saklanabilecek$(BR)Farklı Değer Adedi$(BR)D TürüEn Küçük$(BR)DeğeriEn Büyük$(BR)Değeri
    12
    24
    38
    416
    532
    664
    7128
    8256 byte$(BR)ubyte-128$(BR)0127$(BR)255
    ......
    1665,536 short$(BR)ushort-32768$(BR)032767$(BR)65535
    ......
    324,294,967,296 int$(BR)uint-2147483648$(BR)02147483647$(BR)4294967295
    ......
    6418,446,744,073,709,551,616 long$(BR)ulong-9223372036854775808$(BR)09223372036854775807$(BR)18446744073709551615
    ......
    - -$(P -Bazı tablo satırlarını atladım ve aynı sayıda bitten oluşan D türlerinin işaretli ve işaretsiz olanlarını aynı satırda gösterdim (örneğin $(C int) ve $(C uint) 32 bitlik satırdalar). -) - -$(H6 Hangi durumda hangi tür) - -$(P -Üç bitlik bir tür toplam 8 değer taşıyabildiği için örneğin ancak $(I atılan zarın sonucu) veya $(I haftanın gün sayısı) gibi kavramları ifade etmek için kullanılabilir. (D'de 3 bitlik tür yoktur; örnek olarak kullanıyorum.) -) - -$(P -Öte yandan, $(C uint) çok büyük bir tür olsa da, dünyadaki bütün insanları kapsayacak bir kimlik kartı numarası gibi bir kavram için kullanılamaz, çünkü $(C uint) dünyadaki insan nüfusu olan 7 milyardan daha az sayıda değer saklayabilir. $(C long) ve $(C ulong)'un Türkçe'de nasıl okunduğunu bile bilemeyeceğim toplam değer adedi ise çoğu kavram için fazlasıyla yeterlidir. -) - -$(P -Temel bir kural olarak, özel bir neden yoksa, tamsayılar için öncelikle $(C int)'i düşünebilirsiniz. -) - -$(H6 $(IX taşma) Taşma) - -$(P -Türlerin bit sayılarıyla belirlenen bu kısıtlamaları, onlarla yapılan işlemlerde beklenmedik sonuçlar verebilir. Örneğin değerleri 3 milyar olan iki $(C uint)'in toplamı gerçekte 6 milyar olsa da, en fazla 4 milyar kadar değer saklayabilen $(C uint)'e sığmaz. Bu durumda sonuç $(C uint)'ten $(I taşmış) olur; programda hiçbir uyarı verilmeden 6 milyarın ancak 4 milyardan geri kalanı, yani 2 milyar kadarı sonuç değişkeninde kalır. (Aslında 6 milyar eksi 4.3 milyar, yani yaklaşık olarak 1.7 milyar...) -) - -$(H6 $(IX kırpılma) Kırpılma) - -$(P -Tamsayılar kesirli değerler tutamadıkları için ne kadar önemli olsa da, virgülden sonraki bilgiyi kaybederler. Örneğin 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2.75 kutu gerekiyor olsa bile, bu değer bir tamsayı tür içinde ancak 2 olarak saklanabilir. -) - -$(P -Taşmaya ve kırpılmaya karşı alabileceğiniz bazı önlemleri işlemlerin tanıtımından sonra vereceğim. Önce aritmetik işlemleri tanıyalım. -) - -$(H6 Tür nitelikleri hatırlatması) - -$(P -Temel türlerin tanıtıldığı bölümde tür niteliklerini görmüştük: $(C .min), türün alabileceği en küçük değeri; $(C .max) da en büyük değeri veriyordu. -) - -$(H6 $(IX ++, arttırma) $(IX arttırma) Arttırma: $(C ++)) - -$(P -Tek bir değişkenle kullanılır. Değişkenin isminden önce yazılır ve o değişkenin değerini 1 arttırır: -) - ---- -import std.stdio; - -void main() { - int sayı = 10; - ++sayı; - writeln("Yeni değeri: ", sayı); -} ---- - -$(SHELL -Yeni değeri: 11 -) - -$(P -Arttırma işleci, biraz aşağıda göreceğiniz $(I atamalı toplama) işlecinin 1 değeri ile kullanılmasının eşdeğeridir: -) - ---- - sayı += 1; // ++sayı ifadesinin aynısı ---- - -$(P -Arttırma işleminin sonucu; eğer türün taşıyabileceği en yüksek değeri aşıyorsa, o zaman $(I taşar) ve türün alabildiği en düşük değere dönüşür. Bunu denemek için önceki değeri $(C int.max) olan bir değişkeni arttırırsak, yeni değerinin $(C int.min) olduğunu görürüz: -) - ---- -import std.stdio; - -void main() { - writeln("en düşük int değeri : ", int.min); - writeln("en yüksek int değeri : ", int.max); - - int sayı = int.max; - writeln("sayının önceki değeri : ", sayı); - ++sayı; - writeln("sayının sonraki değeri: ", sayı); -} ---- - -$(SHELL -en düşük int değeri : -2147483648 -en yüksek int değeri : 2147483647 -sayının önceki değeri : 2147483647 -sayının sonraki değeri: -2147483648 -) - -$(P -Bu çok önemli bir konudur; çünkü sayı hiçbir uyarı verilmeden, en yüksek değerinden en düşük değerine geçmektedir; hem de $(I arttırma) işlemi sonucunda! -) - -$(P -Buna $(I taşma) denir. Benzer taşma davranışlarını azaltma, toplama, ve çıkarma işlemlerinde de göreceğiz. -) - -$(H6 $(IX --, azaltma) $(IX azaltma) Azaltma: $(C --)) - -$(P -Tek bir değişkenle kullanılır. Değişkenin isminden önce yazılır ve o değişkenin değerini 1 azaltır: -) - ---- - --sayı; // değeri bir azalır ---- - -$(P -Azaltma işleci, biraz aşağıda göreceğiniz $(I atamalı çıkarma) işlecinin 1 değeri ile kullanılmasının eşdeğeridir: -) - ---- - sayı -= 1; // --sayı ifadesinin aynısı ---- - - -$(P -$(C ++) işlecine benzer şekilde, eğer değişkenin değeri baştan o türün en düşük değerindeyse, yeni değeri o türün en yüksek değeri olur. Buna da $(I taşma) denir. -) - -$(H6 $(IX +, toplama) $(IX toplama) Toplama: +) - -$(P -İki ifadeyle kullanılır ve aralarına yazıldığı iki ifadenin toplamını verir: -) - ---- -import std.stdio; - -void main() { - int birinci = 12; - int ikinci = 100; - - writeln("Sonuç: ", birinci + ikinci); - writeln("Sabit ifadeyle: ", 1000 + ikinci); -} ---- - -$(SHELL -Sonuç: 112 -Sabit ifadeyle: 1100 -) - -$(P -Eğer iki ifadenin toplamı o türde saklanabilecek en yüksek değerden fazlaysa, yine $(I taşma) oluşur ve değerlerin ikisinden de daha küçük bir sonuç elde edilir: -) - ---- -import std.stdio; - -void main() { - // İki tane 3 milyar - uint birinci = 3000000000; - uint ikinci = 3000000000; - - writeln("uint'in en yüksek değeri: ", uint.max); - writeln(" birinci: ", birinci); - writeln(" ikinci: ", ikinci); - writeln(" toplam: ", birinci + ikinci); - writeln("TAŞMA! Sonuç 6 milyar olmadı!"); -} ---- - -$(SHELL -uint'in en yüksek değeri: 4294967295 - birinci: 3000000000 - ikinci: 3000000000 - toplam: 1705032704 -TAŞMA! Sonuç 6 milyar olmadı! -) - -$(H6 $(IX -, çıkarma) $(IX çıkarma) Çıkarma: $(C -)) - -$(P -İki ifadeyle kullanılır ve birinci ile ikincinin farkını verir: -) - ---- -import std.stdio; - -void main() { - int sayı_1 = 10; - int sayı_2 = 20; - - writeln(sayı_1 - sayı_2); - writeln(sayı_2 - sayı_1); -} ---- - -$(SHELL --10 -10 -) - -$(P -Eğer sonucu tutan değişken işaretsizse ve sonuç eksi bir değer alırsa, yine garip sonuçlar doğar. Yukarıdaki programı $(C uint) için tekrar yazarsak: -) - ---- -import std.stdio; - -void main() { - uint sayı_1 = 10; - uint sayı_2 = 20; - - writeln("SORUN! uint eksi değer tutamaz:"); - writeln(sayı_1 - sayı_2); - writeln(sayı_2 - sayı_1); -} ---- - -$(SHELL -SORUN! uint eksi değer tutamaz: -4294967286 -10 -) - -$(P -Eninde sonunda farkları alınacak kavramlar için hep işaretli türlerden seçmek iyi bir karardır. Yine, özel bir neden yoksa normalde $(C int)'i seçebilirsiniz. -) - -$(H6 $(IX *, çarpma) $(IX çarpma) Çarpma: $(C *)) - -$(P -İki ifadenin değerlerini çarpar. Yine taşmaya maruzdur: -) - ---- -import std.stdio; - -void main() { - uint sayı_1 = 6; - uint sayı_2 = 7; - - writeln(sayı_1 * sayı_2); -} ---- - -$(SHELL -42 -) - -$(H6 $(IX /) $(IX bölme) Bölme: $(C /)) - -$(P -Birinci ifadeyi ikinci ifadeye böler. Tamsayılar kesirli sayı tutamayacakları için, eğer varsa sonucun kesirli kısmı atılır. Buna $(I kırpılma) denir. Örneğin bu yüzden aşağıdaki program 3.5 değil, 3 yazmaktadır: -) - ---- -import std.stdio; - -void main() { - writeln(7 / 2); -} ---- - -$(SHELL -3 -) - -$(P -Virgülden sonrasının önemli olduğu hesaplarda tamsayı türleri değil, $(I kesirli sayı türleri) kullanılır. Kesirli sayı türlerini bir sonraki bölümde göreceğiz. -) - -$(H6 $(IX %) $(IX kalan) Kalan: %) - -$(P -Birinci ifadeyi ikinci ifadeye böler ve kalanını verir. Örneğin 10'un 6'ya bölümünden kalan 4'tür: -) - ---- -import std.stdio; - -void main() { - writeln(10 % 6); -} ---- - -$(SHELL -4 -) - -$(P -Bu işleç bir sayının tek veya çift olduğunu anlamada kullanılır. Tek sayıların ikiye bölümünden kalan her zaman için 1 olduğundan, kalanın 0 olup olmadığına bakarak sayının tek veya çift olduğu kolayca anlaşılır: -) - ---- - if ((sayı % 2) == 0) { - writeln("çift sayı"); - } else { - writeln("tek sayı"); - } ---- - -$(H6 $(IX ^^) $(IX üs alma) Üs alma: ^^) - -$(P -Birinci ifadenin ikinci ifade ile belirtilen üssünü alır. Örneğin 3 üssü 4, 3'ün 4 kere kendisiyle çarpımıdır: -) - ---- -import std.stdio; - -void main() { - writeln(3 ^^ 4); -} ---- - -$(SHELL -81 -) - -$(H6 $(IX atamalı aritmetik işleci) Atamalı aritmetik işleçleri) - -$(P -Yukarıda gösterilen ve iki ifade alan aritmetik işleçlerin atamalı olanları da vardır. Bunlar işlemi gerçekleştirdikten sonra ek olarak sonucu sol taraftaki değişkene atarlar: -) - ---- -import std.stdio; - -void main() { - int sayı = 10; - - sayı += 20; // sayı = sayı + 20 ile aynı şey; şimdi 30 - sayı -= 5; // sayı = sayı - 5 ile aynı şey; şimdi 25 - sayı *= 2; // sayı = sayı * 2 ile aynı şey; şimdi 50 - sayı /= 3; // sayı = sayı / 3 ile aynı şey; şimdi 16 - sayı %= 7; // sayı = sayı % 7 ile aynı şey; şimdi 2 - sayı ^^= 6; // sayı = sayı ^^ 6 ile aynı şey; şimdi 64 - - writeln(sayı); -} ---- - -$(SHELL -64 -) - -$(H6 $(IX -, eksi işareti) $(IX eksi işareti) Eksi işareti: $(C -)) - -$(P -Önüne yazıldığı ifadenin değerini artıysa eksi, eksiyse artı yapar: -) - ---- -import std.stdio; - -void main() { - int sayı_1 = 1; - int sayı_2 = -2; - - writeln(-sayı_1); - writeln(-sayı_2); -} ---- - -$(SHELL --1 -2 -) - -$(P -Bu işlecin sonucunun türü, değişkenin türü ile aynıdır. $(C uint) gibi işaretsiz türler eksi değerler tutamadıkları için, bu işlecin onlarla kullanılması şaşırtıcı sonuçlar doğurabilir: -) - ---- - $(HILITE uint) sayı = 1; - writeln("eksi işaretlisi: ", -sayı); ---- - -$(P -$(C -sayı) ifadesinin türü de $(C uint)'tir ve o yüzden eksi değer alamaz: -) - -$(SHELL -eksi işaretlisi: 4294967295 -) - -$(H6 $(IX +, artı işareti) $(IX artı işareti) Artı işareti: $(C +)) - -$(P -Matematikte sayıların önüne yazılan + işareti gibi bunun da hiçbir etkisi yoktur. İfadenin değeri eksiyse yine eksi, artıysa yine artı kalır: -) - ---- -import std.stdio; - -void main() { - int sayı_1 = 1; - int sayı_2 = -2; - - writeln(+sayı_1); - writeln(+sayı_2); -} ---- - -$(SHELL -1 --2 -) - -$(H6 $(IX ++, arttırma, önceki değerli) $(IX önceki değerli arttırma) $(IX sonek arttırma) $(IX arttırma, sonek) Önceki değerli (sonek) arttırma: $(C ++)) - -$(P -$(I Not: Özel bir nedeni yoksa normal arttırma işlecini kullanmanızı öneririm.) -) - -$(P -Normal arttırma işlecinden farklı olarak ifadeden sonra yazılır. Yukarıda anlatılan $(C ++) işlecinde olduğu gibi ifadenin değerini bir arttırır, ama içinde geçtiği ifadede $(I önceki değeri) olarak kullanılır. Bunun etkisini görmek için normal $(C ++) işleciyle karşılaştıralım: -) - ---- -import std.stdio; - -void main() { - int normal_arttırılan = 1; - writeln(++normal_arttırılan); // 2 yazılır - writeln(normal_arttırılan); // 2 yazılır - - int önceki_değerli_arttırılan = 1; - - // Değeri arttırılır ama ifadede önceki değeri kullanılır: - writeln(önceki_değerli_arttırılan++); // 1 yazılır - writeln(önceki_değerli_arttırılan); // 2 yazılır -} ---- - -$(SHELL -2 -2 -1 -2 -) - -$(P -Yukarıdaki arttırma işleminin olduğu satır şunun eşdeğeridir: -) - ---- - int önceki_değeri = önceki_değerli_arttırılan; - ++önceki_değerli_arttırılan; - writeln(önceki_değeri); // 1 yazılır ---- - -$(P -Yani bir anlamda, sayı arttırılmıştır, ama içinde bulunduğu ifadede $(I önceki değeri) kullanılmıştır. -) - -$(H6 $(IX --, azaltma, önceki değerli) $(IX önceki değerli azaltma) $(IX sonek azaltma) $(IX azaltma, sonek) Önceki değerli (sonek) azaltma: $(C --)) - -$(P -$(I Not: Özel bir nedeni yoksa normal azaltma işlecini kullanmanızı öneririm.) -) - -$(P -Önceki değerli arttırma $(C ++) ile aynı şekilde davranır ama arttırmak yerine azaltır. -) - -$(H6 İşleç öncelikleri) - -$(P -Yukarıdaki işleçleri hep tek başlarına ve bir veya iki ifade ile gördük. Oysa mantıksal ifadelerde olduğu gibi, birden fazla aritmetik işleci bir arada kullanarak daha karmaşık işlemler oluşturabiliriz: -) - ---- -int sayı = 77; -int sonuç = (((sayı + 8) * 3) / (sayı - 1)) % 5; ---- - -$(P -Mantıksal ifadelerde olduğu gibi, bu işleçlerin de D tarafından belirlenmiş olan öncelikleri vardır. Örneğin, $(C *) işlecinin önceliği $(C +) işlecininkinden yüksek olduğundan, parantezler kullanılmadığında $(C sayı + 8 * 3) ifadesi önce $(C *) işlemi uygulanacağı için $(C sayı + 24) olarak hesaplanır. Bu da yukarıdakinden farklı bir işlemdir. -) - -$(P -O yüzden, parantezler kullanarak hem işlemleri doğru sırada uygulatmış olursunuz, hem de kodu okuyan kişilere kodu anlamalarında yardımcı olmuş olursunuz. -) - -$(P -İşleç öncelikleri tablosunu $(LINK2 /ders/d/islec_oncelikleri.html, ilerideki bir bölümde) göreceğiz. -) - -$(H6 Taşma olduğunu belirlemek) - -$(P -$(IX core.checkedint) $(IX checkedint) $(IX adds) $(IX addu) $(IX subs) $(IX subu) $(IX muls) $(IX mulu) $(IX negs) Her ne kadar henüz görmediğimiz $(LINK2 /ders/d/islevler.html, işlev) ve $(LINK2 /ders/d/islev_parametreleri.html, $(C ref) parametre) olanaklarını kullansa da, taşma durumunu bildirebilen $(LINK2 http://dlang.org/phobos/core_checkedint.html, $(C core.checkedint) modülünü) burada tanıtmak istiyorum. Bu modül $(C +) ve $(C -) gibi işleçleri değil, şu işlevleri kullanır: işaretli ve işaretsiz toplama için $(C adds) ve $(C addu), işaretli ve işaretsiz çıkarma için $(C subs) ve $(C subu), işaretli ve işaretsiz çarpma için $(C muls) ve $(C mulu), ve ters işaretlisini almak için $(C negs). -) - -$(P -Örneğin, $(C a) ve $(C b)'nin $(C int) türünde iki değişken olduklarını varsayarsak, toplamlarının taşıp taşmadığını aşağıdaki kod ile denetleyebiliriz: -) - ---- - // Aşağıdaki 'adds' işlevi sırasında sonuç taşmışsa bu - // değişkenin değeri 'true' olur: - bool taştı_mı = false; - int sonuç = adds(a, b, $(HILITE taştı_mı)); - - if (taştı_mı) { - // 'sonuç'u kullanamayız çünkü taşmış - // ... - - } else { - // 'sonuç'u kullanabiliriz - // ... - } ---- - -$(H6 Taşmaya karşı önlemler) - -$(P -Eğer bir işlemin sonucu seçilen türe sığmıyorsa, zaten yapılacak bir şey yoktur. Ama bazen sonuç sığacak olsa da ara işlemler sırasında oluşabilecek taşmalar nedeniyle yanlış sonuçlar elde edilebilir. -) - -$(P -Bir örneğe bakalım: kenarları 40'a 60 kilometre olan bir alanın her 1000 metre karesine bir elma ağacı dikmek istiyoruz. Kaç ağaç gerekir? -) - -$(P -Bu problemi kağıt kalemle çözünce sonucun 40000 çarpı 60000 bölü 1000 olarak 2.4 milyon olduğunu görürüz. Bunu hesaplayan bir programa bakalım: -) - ---- -import std.stdio; - -void main() { - int en = 40000; - int boy = 60000; - int ağaç_başına_yer = 1000; - - int gereken_ağaç = en * boy / ağaç_başına_yer; - - writeln("Gereken elma ağacı: ", gereken_ağaç); -} ---- - -$(SHELL -Gereken elma ağacı: -1894967 -) - -$(P -Bırakın yakın olmayı, bu sonuç sıfırdan bile küçüktür! Bu sorunun nedeni, programdaki $(C en * boy) alt işleminin bir $(C int)'e sığamayacak kadar büyük olduğu için taşması, ve bu yüzden de geri kalan $(C / ağaç_başına_yer) işleminin de yanlış çıkmasıdır. -) - -$(P -Buradaki ara işlem sırasında oluşan taşmayı değişken sıralarını değiştirerek giderebiliriz: -) - ---- -int gereken_ağaç = en / ağaç_başına_yer * boy; ---- - -$(P -Şimdi hesap doğru çıkar: -) - -$(SHELL -Gereken elma ağacı: 2400000 -) - -$(P -Bu ifadenin doğru çalışmasının nedeni, şimdiki ara işlem olan $(C en / ağaç_başına_yer) ifadesinin değerinin 40 olduğu için artık $(C int)'ten taşmamasıdır. -) - -$(P -Aslında böyle bir durumda en doğru çözüm; bir tamsayı türü değil, kesirli sayı türlerinden birisini kullanmaktır: $(C float), $(C double), veya $(C real). -) - -$(H6 Kırpılmaya karşı önlemler) - -$(P -Benzer şekilde, ara işlemlerin sırasını değiştirerek kırpılmanın da etkisini azaltabiliriz. Bunun ilginç bir örneğini, aynı sayıya bölüp yine aynı sayıyla çarptığımızda görebiliriz: 10/9*9 işleminin sonucunun 10 çıkmasını bekleriz. Oysa: -) - ---- -import std.stdio; - -void main() { - writeln(10 / 9 * 9); -} ---- - -$(SHELL -9 -) - -$(P -Yine, işlemlerin sırasını değiştirince kırpılma olmayacağı için sonuç doğru çıkar: -) - ---- -writeln(10 * 9 / 9); ---- - -$(SHELL -10 -) - -$(P -Burada da en iyi çözüm belki de bir kesirli sayı türü kullanmaktır. -) - -$(PROBLEM_COK -$(PROBLEM -Yazacağınız program kullanıcıdan iki tamsayı alsın ve birincinin içinde ikinciden kaç tane bulunduğunu ve artanını versin. Örneğin 7 ve 3 değerleri girilince çıkışa şunu yazsın: - -$(SHELL -7 = 3 * 2 + 1 -) -) - -$(PROBLEM -Aynı programı, kalan 0 olduğunda daha kısa sonuç verecek şekilde değiştirin. Örneğin 10 ve 5 verince gereksizce "10 = 5 * 2 + 0" yazmak yerine, yalnızca yeterli bilgiyi versin: - -$(SHELL -10 = 5 * 2 -) -) - -$(PROBLEM -Dört işlemi destekleyen basit bir hesap makinesi yazın. İşlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın. Bu programda taşma ve kırpılma sorunlarını gözardı edebilirsiniz. -) - -$(PROBLEM -Yazacağınız program 1'den 10'a kadar bütün sayıları ayrı satırlarda olacak şekilde yazdırsın. Ama, bir istisna olarak 7 değerini yazdırmasın. Programda şu şekilde tekrarlanan $(C writeln) ifadeleri kullan$(B ma)yın: - ---- -import std.stdio; - -void main() { - // Böyle yapmayın! - writeln(1); - writeln(2); - writeln(3); - writeln(4); - writeln(5); - writeln(6); - writeln(8); - writeln(9); - writeln(10); -} ---- - -$(P -Onun yerine, bir döngü içinde değeri arttırılan bir değişken düşünün ve 7'yi yazdırmama koşuluna da dikkat edin. Burada herhalde $(I eşit olmama) koşulunu denetleyen $(C !=) işlecini kullanmak zorunda kalacaksınız. -) -) - -) - - -Macros: - SUBTITLE=Aritmetik İşlemler - - DESCRIPTION=D dilinde aritmetik işlemlerin tanıtılması - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial aritmetik işlemler - -SOZLER= -$(arttirma) -$(atama) -$(azaltma) -$(bit) -$(ifade) -$(isaretli_tur) -$(isaretsiz_tur) -$(islec) -$(kalan) -$(kirpilma) -$(nitelik) -$(onceki_degerli_arttirma) -$(onceki_degerli_azaltma) -$(tasma) diff --git a/ddili/src/ders/d/assert.cozum.d b/ddili/src/ders/d/assert.cozum.d deleted file mode 100644 index 315fbd3..0000000 --- a/ddili/src/ders/d/assert.cozum.d +++ /dev/null @@ -1,211 +0,0 @@ -Ddoc -$(COZUM_BOLUMU $(CH4 assert) İfadesi ve $(CH4 enforce)) - -$(OL - -$(LI -Bu programı $(C 06:09) ve $(C 1:2) vererek çalıştırdığınızda hata atmadığını göreceksiniz. Buna rağmen, sonucun doğru olmadığını da farkedebilirsiniz: - -$(SHELL -09:06'da başlayan ve 1 saat 2 dakika süren işlem -10:08'de sonlanır.) - -$(P -Görüldüğü gibi, $(C 06:09) girildiği halde, çıkışa $(C 09:06) yazdırılmaktadır. Bu hata, bir sonraki problemde bir $(C assert) denetimi yardımıyla yakalanacak. -) - -) - -$(LI -Programa $(C 06:09) ve $(C 15:2) verildiğinde atılan hata, bizi şu satıra götürür: - ---- -string zamanDizgisi(in int saat, in int dakika) { - $(HILITE assert((saat >= 0) && (saat <= 23));) - // ... -} ---- - -$(P -Saat bilgisinin 0 ile 23 arasında bir değerde olmasını denetleyen bu $(C assert) denetiminin başarısız olması, ancak bu işlev programın başka yerinden yanlış $(C saat) değeriyle çağrıldığında mümkündür. -) - -$(P -$(C zamanDizgisi) işlevinin çağrıldığı $(C sonucuYazdır) işlevine baktığımızda bir yanlışlık göremiyoruz: -) - ---- -void sonucuYazdır( - in int başlangıçSaati, in int başlangıçDakikası, - in int işlemSaati, in int işlemDakikası, - in int bitişSaati, in int bitişDakikası) { - writef("%s'%s başlayan", - $(HILITE zamanDizgisi)(başlangıçSaati, - başlangıçDakikası), - daEki(başlangıçDakikası)); - - writef(" ve %s saat %s dakika süren işlem", - işlemSaati, - işlemDakikası); - - writef(" %s'%s sonlanır.", - $(HILITE zamanDizgisi)(bitişSaati, bitişDakikası), - daEki(bitişDakikası)); - - writeln(); -} ---- - -$(P -Bu durumda $(C sonucuYazdır) işlevini çağıran noktalardan şüphelenir ve onun programda $(C main) içinden ve tek bir noktadan çağrıldığını görürüz: -) - ---- -void $(CODE_DONT_TEST)main() { - // ... - $(HILITE sonucuYazdır)(başlangıçSaati, başlangıçDakikası, - işlemSaati, işlemDakikası, - bitişSaati, bitişDakikası); -} ---- - -$(P -Çağıran noktada da bir sorun yok gibi görünüyor. Biraz daha dikkat ve zaman harcayarak sonunda başlangıç zamanının ters sırada okunduğunu farkederiz: -) - ---- - zamanOku("Başlangıç zamanı", - başlangıç$(HILITE Dakikası), - başlangıç$(HILITE Saati)); ---- - -$(P -Programcının yaptığı o dikkatsizlik nedeniyle $(C 06:09) olarak girilen bilgi aslında $(C 09:06) olarak algılanmakta ve daha sonra buna $(C 15:2) süresi eklenmektedir. $(C zamanDizgisi) işlevindeki $(C assert) de saat değerini 24 olarak görür ve bu yüzden hata atılmasına neden olur. -) - -$(P -Burada çözüm, başlangıç zamanının okunduğu noktada parametreleri doğru sırada yazmaktır: -) - ---- - zamanOku("Başlangıç zamanı", - başlangıçSaati, - başlangıçDakikası); ---- - -$(P -Çıktısı: -) - -$(SHELL -Başlangıç zamanı? (SS:DD) 06:09 -İşlem süresi? (SS:DD) 15:2 -06:09'da başlayan ve 15 saat 2 dakika süren işlem -21:11'de sonlanır. -) - -) - -$(LI -Bu seferki hata, $(C daEki) işlevindeki $(C assert) ile ilgili: - ---- - assert(ek.length != 0); ---- - -$(P -O denetimin hatalı çıkması, $(I da) ekinin uzunluğunun 0 olduğunu, yani ekin boş olduğunu gösteriyor. Dikkat ederseniz, $(C 06:09) ve $(C 1:1) zamanlarını toplayınca sonuç $(C 07:10) olur. Yani bu sonucun dakika değerinin son hanesi 0'dır. $(C daEki) işlevine dikkat ederseniz, 0'ın hangi eki alacağı bildirilmemiştir. Çözüm, 0'ın $(C case) bloğunu da $(C switch) ifadesine eklemektir: -) - ---- - case 6, 9, 0: - ek = "da"; - break; ---- - -$(P -Bu hatayı da bir $(C assert) sayesinde yakalamış ve gidermiş olduk: -) - -$(SHELL -Başlangıç zamanı? (SS:DD) 06:09 -İşlem süresi? (SS:DD) 1:1 -06:09'da başlayan ve 1 saat 1 dakika süren işlem -07:10'da sonlanır. -) - -) - -$(LI -Daha önce de karşılaştığımız $(C assert) yine doğru çıkmıyor: - ---- - assert((saat >= 0) && (saat <= 23)); ---- - -$(P -Bunun nedeni, $(C zamanEkle) işlevinin saat değerini 23'ten büyük yapabilmesidir. Bu işlevin sonuna, saat değerinin her zaman için 0 ve 23 aralığında olmasını sağlayan bir $(I kalan) işlemi ekleyebiliriz: -) - ---- -void zamanEkle( - in int başlangıçSaati, in int başlangıçDakikası, - in int eklenecekSaat, in int eklenecekDakika, - out int sonuçSaati, out int sonuçDakikası) { - sonuçSaati = başlangıçSaati + eklenecekSaat; - sonuçDakikası = başlangıçDakikası + eklenecekDakika; - - if (sonuçDakikası > 59) { - ++sonuçSaati; - } - - $(HILITE sonuçSaati %= 24;) -} ---- - -$(P -Yukarıdaki işlevdeki diğer hatayı da görüyor musunuz? $(C sonuçDakikası) 59'dan büyük bir değer olduğunda $(C sonuçSaati) bir arttırılıyor, ama $(C sonuçDakikası)'nın değeri 59'dan büyük olarak kalıyor. -) - -$(P -Belki de şu daha doğru bir işlev olur: -) - ---- -void zamanEkle( - in int başlangıçSaati, in int başlangıçDakikası, - in int eklenecekSaat, in int eklenecekDakika, - out int sonuçSaati, out int sonuçDakikası) { - sonuçSaati = başlangıçSaati + eklenecekSaat; - sonuçDakikası = başlangıçDakikası + eklenecekDakika; - - $(HILITE sonuçSaati += sonuçDakikası / 60;) - $(HILITE sonuçSaati %= 24;) - - $(HILITE assert((sonuçSaati >= 0) && (sonuçSaati <= 23));) - $(HILITE assert((sonuçDakikası >= 0) && (sonuçDakikası <= 59));) -} ---- - -$(P -Aslında $(C sonuçDakikası) hâlâ hatalıdır çünkü ona da 60'tan kalanı atamak gerekir. Ama şimdi işin güzel tarafı, artık bu işlevin hatalı saat ve dakika değerleri üretmesi $(C assert) denetimleri nedeniyle olanaksızdır. -) - -$(P -Yukarıdaki işlevi örneğin $(C 06:09) ve $(C 1:55) değerleriyle çağırırsanız, $(C sonuçDakikası)'nı denetleyen $(C assert) denetiminin hata vereceğini göreceksiniz. -) - -) - -$(LI -Burada sorun, son hanenin 0 olmasından kaynaklanıyor. Son hane sıfır olunca onlar hanesini de katarak "on", "kırk", "elli", vs. diye okuyunca 0'a verilmiş olan "da" eki her durumda doğru çalışmıyor. Bu problemin çözümünü size bırakıyorum. -) - -) - -Macros: - SUBTITLE=assert İfadesi ve enforce - - DESCRIPTION=D dilinin kod varsayımlarını denetleyen olanağı assert ifadesi problem çözümleri - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial güvenlik kod güvenliği assert varsayım problem çözüm diff --git a/ddili/src/ders/d/assert.d b/ddili/src/ders/d/assert.d deleted file mode 100644 index 3bbfdc5..0000000 --- a/ddili/src/ders/d/assert.d +++ /dev/null @@ -1,496 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX assert) $(IX enforce) $(CH4 assert) ve $(CH4 enforce)) - -$(P -Programları yazarken çok sayıda varsayımda bulunuruz ve bazı beklentilerin doğru çıkmalarını umarız. Programlar ancak bu varsayımlar ve beklentiler doğru çıktıklarında doğru çalışırlar. $(C assert), programın dayandığı bu varsayımları ve beklentileri denetlemek için kullanılır. Programcının en etkili yardımcılarındandır. -) - -$(P -Bazen hata atmakla $(C assert)'ten yararlanmak arasında karar vermek güçtür. Aşağıdaki örneklerde fazla açıklamaya girmeden $(C assert)'ler kullanacağım. Hangi durumda hangi yöntemin daha uygun olduğunu ise daha aşağıda açıklayacağım. -) - -$(P -Çoğu zaman programdaki varsayımların farkına varılmaz. Örneğin iki kişinin yaşlarının ortalamasını alan aşağıdaki işlevde kullanılan hesap, yaş parametrelerinin ikisinin de sıfır veya daha büyük olacakları varsayılarak yazılmıştır: -) - ---- -double ortalamaYaş(double birinciYaş, double ikinciYaş) { - return (birinciYaş + ikinciYaş) / 2; -} ---- - -$(P -Yaşlardan en az birisinin eksi bir değer olarak gelmesi hatalı bir durumdur. Buna rağmen, işlev mantıklı bir ortalama üretebilir ve program bu hata hiç farkedilmeden işine yanlış da olsa devam edebilir. -) - -$(P -Başka bir örnek olarak, aşağıdaki işlev yalnızca iki komuttan birisi ile çağrılacağını varsaymaktadır: "şarkı söyle" ve "dans et": -) - ---- -void komutİşlet(string komut) { - if (komut == "şarkı söyle") { - robotaŞarkıSöylet(); - - } else { - robotuDansEttir(); - } -} ---- - -$(P -Böyle bir varsayımda bulunduğu için, "şarkı söyle" dışındaki geçerli olsun olmasın her komuta karşılık $(C robotuDansEttir) işlevini çağıracaktır. -) - - - -$(P -Bu varsayımları kendimize sakladığımızda sonuçta ortaya çıkan program hatalı davranabilir. $(C assert), bu varsayımlarımızı dile getirmemizi sağlayan ve varsayımlar hatalı çıktığında işlemlerin durdurulmalarına neden olan bir olanaktır. -) - -$(P -$(C assert), bir anlamda programa "böyle olduğunu varsayıyorum, eğer yanlışsa işlemi durdur" dememizi sağlar. -) - -$(H5 Söz dizimi) - -$(P -$(C assert) iki biçimde yazılabilir: -) - ---- - assert($(I mantıksal_ifade)); - assert($(I mantıksal_ifade), $(I mesaj)); ---- - -$(P -$(C assert), kendisine verilen mantıksal ifadeyi işletir. İfadenin değeri $(C true) ise varsayım doğru çıkmış kabul edilir ve $(C assert) denetiminin hiçbir etkisi yoktur. İfadenin değeri $(C false) olduğunda ise varsayım yanlış çıkmış kabul edilir ve bir $(C AssertError) hatası atılır. İsminden de anlaşılabileceği gibi, bu hata $(C Error)'dan türemiştir ve $(LINK2 /ders/d/hatalar.html, Hatalar bölümünde) gördüğümüz gibi, yakalanmaması gereken bir hata türüdür. Böyle bir hata atıldığında programın hemen sonlanması önemlidir çünkü programın yanlış varsayımlara dayanarak yanlış olabilecek sonuçlar üretmesi böylece önlenmiş olur. -) - -$(P -Yukarıdaki $(C ortalamaYaş) işlevindeki varsayımlarımızı iki $(C assert) ile şöyle ifade edebiliriz: -) - ---- -double ortalamaYaş(double birinciYaş, double ikinciYaş) { - assert(birinciYaş >= 0); - assert(ikinciYaş >= 0); - - return (birinciYaş + ikinciYaş) / 2; -} - -void main() { - auto sonuç = ortalamaYaş($(HILITE -1), 10); -} ---- - -$(P -O $(C assert)'ler "birinciYaş'ın 0 veya daha büyük olduğunu varsayıyorum" ve "ikinciYaş'ın 0 veya daha büyük olduğunu varsayıyorum" anlamına gelir. Başka bir bakış açısıyla, "assert" sözcüğünün "emin olarak öne sürmek" karşılığını kullanarak, "birinciYaş'ın 0 veya daha büyük olduğundan eminim" gibi de düşünülebilir. -) - -$(P -$(C assert) bu varsayımları denetler ve yukarıdaki programda olduğu gibi, varsayımın yanlış çıktığı durumda programı bir $(C AssertError) hatasıyla sonlandırır: -) - -$(SHELL -core.exception.AssertError@deneme(3): Assertion failure -) - -$(P -Hatanın $(C @) karakterinden sonra gelen bölümü hangi dosyanın hangi satırındaki varsayımın doğru çıkmadığını gösterir. Bu örnekteki $(C deneme(3))'e bakarak hatanın $(C deneme.d) dosyasının üçüncü satırında olduğu anlaşılır. -) - -$(P -$(C assert) beklentisinin yanlış çıktığı durumda açıklayıcı bir mesaj yazdırılmak istendiğinde $(C assert) denetiminin ikinci kullanımından yararlanılır: -) - ---- - assert(birinciYaş >= 0, "Yaş sıfırdan küçük olamaz"); ---- - -$(P -Çıktısı: -) - -$(SHELL -core.exception.AssertError@deneme.d(3): $(HILITE Yaş sıfırdan küçük olamaz) -) - -$(P -Programda kesinlikle gelinmeyeceği düşünülen veya gelinmemesi gereken noktalarda, özellikle başarısız olsun diye mantıksal ifade olarak bilerek $(C false) sabit değeri kullanılır. Örneğin yukarıdaki "şarkı söyle" ve "dans et" örneğinde başka komutların geçersiz olduklarını belirtmek ve bu durumlarda hata atılmasını sağlamak için şöyle bir $(C assert) denetimi kullanılabilir: -) - ---- -void komutİşlet(in char[] komut) { - if (komut == "şarkı söyle") { - robotaŞarkıSöylet(); - - } else if (komut == "dans et") { - robotuDansEttir(); - - } else { - $(HILITE assert(false)); - } -} ---- - -$(P -Artık işlev yalnızca o iki komutu kabul eder ve başka komut geldiğinde $(C assert(false)) nedeniyle işlem durdurulur. ($(I Not: Burada aynı amaç için bir $(LINK2 /ders/d/switch_case.html, $(C final switch) deyimi) de kullanılabilir.)) -) - - -$(H5 $(IX static assert) $(C static assert)) - -$(P -$(C assert) denetimleri programın çalışması sırasında işletilirler çünkü programın doğru işleyişi ile ilgilidirler. Bazı denetimler ise daha çok programın yapısı ile ilgilidirler ve derleme zamanında bile işletilebilirler. -) - -$(P -$(C static assert), derleme zamanında işletilebilecek olan denetimler içindir. Bunun bir yararı, belirli koşulların sağlanamaması durumunda programın derlenmesinin önlenebilmesidir. Doğal olarak, bütün ifadenin derleme zamanında işletilebiliyor olması şarttır. -) - -$(P -Örneğin, çıkış aygıtının genişliği gibi bir kısıtlama nedeniyle menü başlığının belirli bir uzunluktan kısa olması gereken bir durumda $(C static assert)'ten yararlanılabilir: -) - ---- - enum dstring menüBaşlığı = "Komut Menüsü"; - static assert(menüBaşlığı.length <= 16); ---- - -$(P -İfadenin derleme zamanında işletilebilmesi için dizginin $(C enum) olarak tanımlandığına dikkat edin. Yalnızca $(C dstring) olsaydı bir derleme hatası oluşurdu. -) - -$(P -Bir programcının o başlığı daha açıklayıcı olduğunu düşündüğü için değiştirdiğini düşünelim: -) - ---- - enum dstring menüBaşlığı = "Yön Komutları Menüsü"; - static assert(menüBaşlığı.length <= 16); ---- - -$(P -Program artık $(C static assert) denetimini geçemediği için derlenemez: -) - -$(SHELL -Error: static assert (20LU <= 16LU) is false -) - -$(P -Programcı da böylece programın uyması gereken bu kısıtlamayı farketmiş olur. -) - -$(P -$(C static assert)'ün yararı, yukarıda olduğu gibi türlerin ve değerlerin açıkça belli oldukları örneklerde anlaşılamıyor. $(C static assert) özellikle şablon ve koşullu derleme olanakları ile kullanıldığında yararlıdır. Bu olanakları ilerideki bölümlerde göreceğiz. -) - -$(H5 $(I Kesinlikle doğru olan) (!) varsayımlar için bile $(C assert)) - -$(P -"Kesinlikle doğru olan"ın özellikle üzerine basıyorum. Hiçbir varsayım bilerek yanlış olmayacağı için, zaten çoğu hata $(I kesinlikle doğru olan) varsayımlara dayanır. -) - -$(P -Bu yüzden bazen kesinlikle gereksizmiş gibi duran $(C assert) denetimleri de kullanılır. Örneğin belirli bir senenin aylarının kaç gün çektikleri bilgisini bir dizi olarak döndüren bir işlev ele alalım: -) - ---- -int[] ayGünleri(in int yıl) { - int[] günler = [ - 31, şubatGünleri(yıl), - 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - ]; - - assert((diziToplamı(günler) == 365) || - (diziToplamı(günler) == 366)); - - return günler; -} ---- - -$(P -Doğal olarak bu işlevin döndürdüğü dizideki gün toplamları ya 365 olacaktır, ya da 366. Bu yüzden yukarıdaki $(C assert) denetiminin gereksiz olduğu düşünülebilir. Oysa, her ne kadar gereksiz gibi görünse de, o denetim $(C şubatGünleri) işlevinde ilerideki bir zamanda yapılabilecek bir hataya karşı bir güvence sağlar. $(C şubatGünleri) işlevi bir hata nedeniyle örneğin 30 değerini döndürse, o $(C assert) sayesinde bu hata hemen farkedilecektir. -) - -$(P -Hatta biraz daha ileri giderek dizinin uzunluğunun her zaman için 12 olacağını da denetleyebiliriz: -) - ---- - assert(günler.length == 12); ---- - -$(P -Böylece kodu diziden yanlışlıkla silinebilecek veya diziye yanlışlıkla eklenebilecek bir elemana karşı da güvence altına almış oluruz. -) - -$(P -Böyle denetimler her ne kadar gereksizmiş gibi görünseler de son derece yararlıdırlar. Kodun sağlamlığını arttıran ve kodu ilerideki değişiklikler karşısında güvencede tutan çok etkili yapılardır. -) - -$(P -Kodun sağlamlığını arttıran ve programın yanlış sonuçlar doğuracak işlemlerle devam etmesini önleyen bir olanak olduğu için, $(C assert) bundan sonraki bölümlerde göreceğimiz $(I birim testleri) ve $(I sözleşmeli programlama) olanaklarının da temelini oluşturur. -) - -$(H5 Değer üretmez ve yan etkisi yoktur) - -$(P -İfadelerin değer üretebildiklerini ve yan etkilerinin olabildiğini görmüştük. $(C assert) değer üretmeyen bir denetimdir. -) - -$(P -Ek olarak, $(C assert) denetiminin kendisinin bir yan etkisi de yoktur. Ona verilen mantıksal ifadenin yan etkisinin olmaması da D standardı tarafından şart koşulmuştur. $(C assert), programın durumunu değiştirmeyen ve yalnızca varsayımları denetleyen bir yapı olarak kalmak zorundadır. -) - -$(H5 $(C assert) denetimlerini etkisizleştirmek) - -$(P -$(C assert) programın doğruluğu ile ilgilidir. Programın yeterince denenip amacı doğrultusunda doğru olarak işlediğine karar verildikten sonra programda başkaca yararı yoktur. Üstelik, ne değerleri ne de yan etkileri olduğundan, $(C assert) denetimleri programdan bütünüyle kaldırılabilmelidirler ve bu durumda programın işleyişinde hiçbir değişiklik olmamalıdır. -) - -$(P -$(IX -release, derleyici seçeneği) Derleyici seçeneği $(C -release), $(C assert) denetimlerinin sanki programa hiç yazılmamışlar gibi gözardı edilmelerini sağlar: -) - -$(SHELL -dmd deneme.d -release -) - -$(P -Böylece olasılıkla uzun süren denetimlerin programı yavaşlatmaları önlenmiş olur. -) - -$(P -Bir istisna olarak, $(C false) veya ona otomatik olarak dönüşen bir hazır değerle çağrılan $(C assert)'ler $(C ‑release) ile derlendiklerinde bile programdan çıkartılmazlar. Bunun nedeni, $(C assert(false)) denetimlerinin hiçbir zaman gelinmemesi gereken satırları belirliyor olmaları ve o satırlara gelinmesinin her zaman için hatalı olacağıdır. -) - -$(H5 Hata atmak için $(C enforce)) - -$(P -Programın çalışması sırasında karşılaşılan her beklenmedik durum programdaki bir yanlışlığı göstermez. Beklenmedik durumlar programın elindeki verilerle veya çevresiyle de ilgili olabilir. Örneğin, kullanıcının girmiş olduğu geçersiz bir değerin $(C assert) ile denetlenmesi doğru olmaz çünkü kullanıcının girdiği yanlış değerin $(I programın doğruluğu) ile ilgisi yoktur. Bu gibi durumlarda $(C assert)'ten yararlanmak yerine daha önceki bölümlerde de yaptığımız gibi $(C throw) ile hata atmak doğru olur. -) - -$(P -$(C std.exception) modülünde tanımlanmış olan ve buradaki kullanımında "şart koşuyorum" anlamına gelen $(C enforce), hata atarken daha önce de kullandığımız $(C throw) ifadesinin yerine geçer. -) - -$(P -Örneğin, belirli bir koşula bağlı olarak bir hata atıldığını varsayalım: -) - ---- - if (adet < 3) { - throw new Exception("En az 3 tane olmalı."); - } ---- - -$(P -$(C enforce) bir anlamda $(C if) denetimini ve $(C throw) deyimini sarmalar. Aynı kod $(C enforce) ile aşağıdaki gibi yazılır: -) - ---- -import std.exception; -// ... - enforce(adet >= 3, "En az 3 tane olmalı."); ---- - -$(P -Mantıksal ifadenin öncekinin tersi olduğuna dikkat edin. Bunun nedeni, $(C enforce)'un "bunu şart koşuyorum" anlamını taşımasıdır. Görüldüğü gibi, $(C enforce) koşul denetimine ve $(C throw) deyimine gerek bırakmaz. -) - -$(H5 Nasıl kullanmalı) - -$(P -$(C assert) $(I programcı hatalarını) yakalamak için kullanılır. Örneğin, yukarıdaki $(C ayGünleri) işlevinde ve $(C menüBaşlığı) değişkeniyle ilgili olarak kullanılan $(C assert)'ler tamamen programcılıkla ilgili hatalara karşı bir güvence olarak kullanılmışlardır. -) - -$(P -Bazı durumlarda $(C assert) kullanmakla hata atmak arasında karar vermek güç olabilir. Böyle durumlarda beklenmedik durumun programın kendisi ile mi ilgili olduğuna bakmak gerekir. Eğer denetim programın kendisi ile ilgili ise $(C assert) kullanılmalıdır. -) - -$(P -Herhangi bir işlem gerçekleştirilemediğinde ise hata atılmalıdır. Bu iş için daha kullanışlı olduğu için $(C enforce)'tan yararlanmanızı öneririm. -) - -$(P -Bu konudaki başka bir kıstas, karşılaşılan durumun giderilebilen bir hata çeşidi olup olmadığıdır. Eğer giderilebilen bir durumsa hata atmak uygun olabilir. Böylece daha üst düzeydeki bir işlev atılan bu hatayı yakalayabilir ve duruma göre farklı davranabilir. -) - -$(PROBLEM_COK - -$(PROBLEM -Bu problemde size önceden yazılmış bir program göstermek istiyorum. Bu programın hata olasılığını azaltmak için bazı noktalarına $(C assert) denetimleri yerleştirilmiş. Amacım, bu $(C assert) denetimlerinin programdaki hataları ortaya çıkartma konusunda ne kadar etkili olduklarını göstermek. - -$(P -Program kullanıcıdan bir başlangıç zamanı ve bir işlem süresi alıyor ve o işlemin ne zaman sonuçlanacağını hesaplıyor. Program, sayılardan sonra gelen 'da' eklerini de doğru olarak yazdırıyor: -) - -$(SHELL -09:06'da başlayan ve 1 saat 2 dakika süren işlem -10:08'de sonlanır. -) - ---- -import std.stdio; -import std.string; -import std.exception; - -/* Verilen mesajı kullanıcıya gösterir ve girilen zaman - * bilgisini saat ve dakika olarak okur. */ -void zamanOku(in string mesaj, out int saat, out int dakika) { - write(mesaj, "? (SS:DD) "); - - readf(" %s:%s", &saat, &dakika); - - enforce((saat >= 0) && (saat <= 23) && - (dakika >= 0) && (dakika <= 59), - "Geçersiz zaman!"); -} - -/* Zamanı dizgi düzeninde döndürür. */ -string zamanDizgisi(in int saat, in int dakika) { - assert((saat >= 0) && (saat <= 23)); - assert((dakika >= 0) && (dakika <= 59)); - - return format("%02s:%02s", saat, dakika); -} - -/* İki zaman bilgisini birbirine ekler ve üçüncü parametre - * çifti olarak döndürür. */ -void zamanEkle( - in int başlangıçSaati, in int başlangıçDakikası, - in int eklenecekSaat, in int eklenecekDakika, - out int sonuçSaati, out int sonuçDakikası) { - sonuçSaati = başlangıçSaati + eklenecekSaat; - sonuçDakikası = başlangıçDakikası + eklenecekDakika; - - if (sonuçDakikası > 59) { - ++sonuçSaati; - } -} - -/* Sayılardan sonra kesme işaretiyle ayrılarak kullanılacak - * olan "de, da" ekini döndürür. */ -string daEki(in int sayı) { - string ek; - - immutable int sonHane = sayı % 10; - - switch (sonHane) { - - case 1, 2, 7, 8: - ek = "de"; - break; - - case 3, 4, 5: - ek = "te"; - break; - - case 6, 9: - ek = "da"; - break; - - default: - break; - } - - assert(ek.length != 0); - - return ek; -} - -void main() { - int başlangıçSaati; - int başlangıçDakikası; - zamanOku("Başlangıç zamanı", - başlangıçDakikası, başlangıçSaati); - - int işlemSaati; - int işlemDakikası; - zamanOku("İşlem süresi", işlemSaati, işlemDakikası); - - int bitişSaati; - int bitişDakikası; - zamanEkle(başlangıçSaati, başlangıçDakikası, - işlemSaati, işlemDakikası, - bitişSaati, bitişDakikası); - - sonucuYazdır(başlangıçSaati, başlangıçDakikası, - işlemSaati, işlemDakikası, - bitişSaati, bitişDakikası); -} - -void sonucuYazdır( - in int başlangıçSaati, in int başlangıçDakikası, - in int işlemSaati, in int işlemDakikası, - in int bitişSaati, in int bitişDakikası) { - writef("%s'%s başlayan", - zamanDizgisi(başlangıçSaati, başlangıçDakikası), - daEki(başlangıçDakikası)); - - writef(" ve %s saat %s dakika süren işlem", - işlemSaati, işlemDakikası); - - writef(" %s'%s sonlanır.", - zamanDizgisi(bitişSaati, bitişDakikası), - daEki(bitişDakikası)); - - writeln(); -} ---- - -$(P -Bu programı çalıştırın ve girişine başlangıç olarak $(C 06:09) ve süre olarak $(C 1:2) verin. Programın normal olarak sonlandığını göreceksiniz. -) - -$(P $(I Not: Aslında çıktının hatalı olduğunu farkedebilirsiniz. Bunu şimdilik görmezden gelin; çünkü az sonra $(C assert)'lerin yardımıyla bulacaksınız.) -) - -) - -$(PROBLEM -Bu sefer programa $(C 06:09) ve $(C 15:2) zamanlarını girin. Bir $(C AssertError) atıldığını göreceksiniz. Hatada belirtilen satıra gidin ve programla ilgili olan hangi beklentinin gerçekleşmediğine bakın. Bu hatanın kaynağını bulmanız zaman alabilir. -) - -$(PROBLEM -Bu sefer programa $(C 06:09) ve $(C 1:1) zamanlarını girin. Yeni bir hata ile karşılaşacaksınız. O satıra da gidin ve o hatayı da giderin. -) - -$(PROBLEM -Bu sefer programa $(C 06:09) ve $(C 20:0) bilgilerini girin. Yine $(C assert) tarafından yakalanan bir program hatası ile karşılaşacaksınız. O hatayı da giderin. -) - -$(PROBLEM -Bu sefer programa $(C 06:09) ve $(C 1:41) bilgilerini girin. Programın $(I da) ekinin doğru çalışmadığını göreceksiniz: - -$(SHELL -Başlangıç zamanı? (SS:DD) 06:09 -İşlem süresi? (SS:DD) 1:41 -06:09'da başlayan ve 1 saat 41 dakika süren işlem -$(HILITE 07:50'da) sonlanır -) - -$(P -Bunu düzeltin ve duruma göre doğru ek yazmasını sağlayın: 7:10'da, 7:50'de, 7:40'ta, vs. -) - -) - -) - -Macros: - SUBTITLE=assert ve enforce - - DESCRIPTION=D dilinin kod varsayımlarını denetleyen olanağı assert ve hata atmayı kolaşlaştıran işlevi enforce. - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial güvenlik kod güvenliği assert varsayım - -SOZLER= -$(donus_degeri) -$(ic_olanak) -$(ifade) -$(yan_etki) diff --git a/ddili/src/ders/d/atama_ve_sira.cozum.d b/ddili/src/ders/d/atama_ve_sira.cozum.d deleted file mode 100644 index dbb37b6..0000000 --- a/ddili/src/ders/d/atama_ve_sira.cozum.d +++ /dev/null @@ -1,25 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Atama ve İşlem Sıraları) - -$(P -$(C a), $(C b), ve $(C c)'nin değerlerini her işlem adımının sağ tarafında ve değişen değeri sarı ile işaretleyerek gösteriyorum: -) - -$(MONO -başlangıçta → a 1, b 2, c önemsiz -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 -Sonuçta $(C a) ve $(C b)'nin değerleri değiş tokuş edilmişlerdir. -) - -Macros: - SUBTITLE=Atama ve İşlem Sıraları Problem Çözümü - - DESCRIPTION=Atama ve işlem sıraları bölümü problem çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial atam ve işlem sıraları problem çözüm diff --git a/ddili/src/ders/d/atama_ve_sira.d b/ddili/src/ders/d/atama_ve_sira.d deleted file mode 100644 index 80f5961..0000000 --- a/ddili/src/ders/d/atama_ve_sira.d +++ /dev/null @@ -1,80 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Atama ve İşlem Sıraları) - -$(P -Programcılık öğrenirken karşılaşılan engellerden ilk ikisini bu bölümde göreceğiz. -) - -$(H5 Atama işlemi) - -$(P -Program içinde -) - ---- - a = 10; ---- - -$(P -gibi bir satır gördüğünüzde bu, "a'nın değeri 10 olsun" demektir. Benzer şekilde, aşağıdaki satırın anlamı da "b'nin değeri 20 olsun" demektir: -) - ---- - b = 20; ---- - -$(P -Bu bilgilere dayanarak o iki satırdan sonra aşağıdaki satırı gördüğümüzde ne düşünebiliriz? -) - ---- - a = b; ---- - -$(P -Ne yazık ki matematikten alıştığımız kuralı burada uygulayamayız. O ifade, "a ile b eşittir" demek $(B değildir)! Baştaki iki ifadeyle aynı mantığı yürütünce, o ifadenin "a'nın değeri b olsun" demek olduğunu görürüz. "a'nın b olması" demek, "b'nin değeri ne ise, a'nın değeri de o olsun" demektir. -) - -$(P -Matematikten alıştığımız $(C =) işareti programcılıkta bambaşka bir anlamda kullanılmaktadır: Sağ tarafın değeri ne ise, sol tarafın değerini de o yapmak. -) - -$(H5 İşlem sıraları) - -$(P -Programlarda işlemler adım adım ve belirli bir sırada uygulanırlar. Yukarıdaki üç ifadenin program içinde alt alta bulunduklarını düşünelim: -) - ---- - a = 10; - b = 20; - a = b; ---- - -$(P -Onların toplu halde anlamları şudur: "a'nın değeri 10 olsun, $(I sonra) b'nin değeri 20 olsun, $(I sonra) a'nın değeri b'nin değeri olsun". Yani oradaki üç işlem adımından sonra hem a'nın hem de b'nin değerleri 20 olur. -) - -$(PROBLEM_TEK - -$(P -Aşağıdaki işlemlerin $(C a)'nın ve $(C b)'nin değerlerini değiş tokuş ettiklerini gözlemleyin. Eğer değerler başlangıçta sırasıyla 1 ve 2 iseler, işlemlerden sonra 2 ve 1 olurlar: -) - ---- - c = a; - a = b; - b = c; ---- - -) - -Macros: - SUBTITLE=Atama ve İşlem Sıraları - - DESCRIPTION=Programcılık öğrenirken karşılaşılan ilk engellerden olan atama ve işlem sırası kavramlarının açıklanmaları - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial atama işlem sırası programcılık - -MINI_SOZLUK= diff --git a/ddili/src/ders/d/auto.cozum.d b/ddili/src/ders/d/auto.cozum.d deleted file mode 100644 index c1b4fe8..0000000 --- a/ddili/src/ders/d/auto.cozum.d +++ /dev/null @@ -1,31 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C auto) ve $(C typeof)) - -$(P -Türünü bulmak istediğimiz hazır değeri $(C typeof)'a vererek türünü üretebiliriz, ve o türün $(C .stringof) niteliği ile de türün ismini yazdırabiliriz: -) - ---- -import std.stdio; - -void main() { - writeln(typeof(1.2).stringof); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -double -) - - -Macros: - SUBTITLE=auto ve typeof Problem Çözümü - - DESCRIPTION=auto ve typeof bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial auto typeof problem çözüm diff --git a/ddili/src/ders/d/auto.d b/ddili/src/ders/d/auto.d deleted file mode 100644 index 24a249a..0000000 --- a/ddili/src/ders/d/auto.d +++ /dev/null @@ -1,114 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(CH4 auto) ve $(CH4 typeof)) - -$(H5 $(IX auto, değişken) $(C auto)) - -$(P -Bazen aynı ismin iki veya daha fazla modülde birden tanımlı olduğu durumlarla karşılaşılabilir. Örneğin birbirlerinden farklı iki kütüphanenin iki modülünde de $(C File) isminde bir tür bulunabilir. O ismi tanımlayan iki modülün birden eklenmesi durumunda da yalnızca $(C File) yazmak karışıklığa neden olur; derleyici hangi türün kullanılacağını bilemez. -) - -$(P -Böyle durumlarda hangi modüldeki ismin kastedildiğini belirtmek için modülün ismini de yazmak gerekir. Örneğin $(C File) türü ile ilgili böyle bir isim çakışması olduğunu varsayarsak: -) - ---- -std.stdio.File dosya = std.stdio.File("ogrenci_bilgisi", "r"); ---- - -$(P -O kullanımda uzun ismin hem de iki kere yazılması gerekmiştir: sol tarafta $(C dosya) nesnesinin türünü belirtmek için, sağ tarafta ise $(C File) nesnesini kurmak için. -) - -$(P -Oysa derleyiciler çoğu durumda sol tarafın türünü sağ tarafın türüne bakarak anlayabilirler. Örneğin 42 gibi bir tamsayı değerle ilklenen bir değişkenin $(C int) olduğu, veya $(C std.stdio.File) kurularak oluşturulan bir nesnenin yine $(C std.stdio.File) türünden olduğu kolayca anlaşılabilir. -) - -$(P -D'nin $(C auto) anahtar sözcüğü, sol tarafın türünün sağ taraftan anlaşılabildiği durumlarda sol tarafın yazımını kolaylaştırmak için kullanılır: -) - ---- - $(HILITE auto) dosya = std.stdio.File("ogrenci_bilgisi", "r"); ---- - -$(P -$(C auto)'yu her türle kullanabilirsiniz: -) - ---- - auto sayı = 42; - auto kesirliSayı = 1.2; - auto selam = "Merhaba"; - auto vida = BisikletVitesDüzeneğininAyarVidası(10); ---- - -$(P -"auto", otomatik anlamına gelen "automatic"in kısaltmasıdır. Buna rağmen $(I türün otomatik olarak anlaşılması) kavramı ile ilgili değildir. Aslında değişkenlerin yaşam süreçleri ile ilgili olan $(C auto), tanım sırasında başka belirteç bulunmadığı zaman kullanılır. -) - -$(P -Başka belirteçler de türün otomatik olarak anlaşılması için yeterlidir: -) - ---- - immutable i = 42; ---- - -$(P -Zaten $(C immutable) yazılmış olduğu için türün değişmez bir $(C int) olduğu o yazımdan da otomatik olarak anlaşılır. ($(C immutable) anahtar sözcüğünü daha sonra göreceğiz.) -) - -$(H5 $(IX typeof) $(C typeof)) - -$(P -Bu anahtar sözcük, "türü" anlamına gelen "type of" deyiminden türemiştir. Kendisine verilen ifadenin (değişken, nesne, hazır değer, vs.) türünü o ifadeyi hiç işletmeden üretir. -) - -$(P -Örneğin zaten tanımlanmış olan $(C int) türünde $(C sayı) isminde bir değişken olduğunu varsayarsak: -) - ---- - int sayı = 100; // bu zaten 'int' olarak tanımlanmış - - typeof(sayı) sayı2; // "sayı'nın türü" anlamında - typeof(100) sayı3; // "100 hazır değerinin türü" anlamında ---- - -$(P -Yukarıdaki son iki ifade, şu ikisinin eşdeğeridir: -) - ---- - int sayı2; - int sayı3; ---- - -$(P -Türlerin zaten bilindiği yukarıdaki gibi durumlarda $(C typeof)'un kullanılmasına gerek olmadığı açıktır. Bu anahtar sözcük özellikle daha sonra anlatılacak olan $(LINK2 /ders/d/sablonlar.html, şablon) ve $(LINK2 /ders/d/katmalar.html, katma $(ASIL mixin)) olanaklarının kullanımında yararlıdır. -) - -$(PROBLEM_TEK - -$(P -42 gibi bir hazır değerin D'nin tamsayı türlerinden $(C int) türünde olduğunu yukarıda okudunuz. (Yani $(C short), $(C long), vs. değil.) Bir program yazarak 1.2 gibi bir hazır değerin türünün D'nin kesirli sayı türlerinden hangisinden olduğunu bulun: $(C float) mu, $(C double) mı, yoksa $(C real) mi? Yeni öğrendiğiniz $(C typeof) ve $(LINK2 /ders/d/temel_turler.html, Temel Türler bölümünde) öğrendiğiniz $(C .stringof) işinize yarayabilir. -) - -) - -Macros: - SUBTITLE=auto ve typeof Anahtar Sözcükleri - - DESCRIPTION=D dilinin yazım kolaylığı sağlayan auto anahtar sözcüğü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial auto - -SOZLER= -$(degisken) -$(hazir_veri) -$(ilklemek) -$(kurma) -$(nesne) -$(sinif) -$(sablon) diff --git a/ddili/src/ders/d/bellek_yonetimi.d b/ddili/src/ders/d/bellek_yonetimi.d deleted file mode 100644 index 15a1592..0000000 --- a/ddili/src/ders/d/bellek_yonetimi.d +++ /dev/null @@ -1,1360 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX bellek yönetimi) Bellek Yönetimi) - -$(P -Şimdiye kadar yazdığımız programlarda hiç bellek yönetimiyle ilgilenmek zorunda kalmadık çünkü D bellek yönetimi gerektirmeyen bir dildir. O yüzden, burada anlatılanlara büyük olasılıkla hiç ihtiyaç duymayacaksınız. Buna rağmen, D gibi sistem dillerinde alt düzey bellek işlemleri ile ilgilenmek gerekebilir. -) - -$(P -Bellek yönetimi çok kapsamlı bir konudur. Bu bölümde yalnızca çöp toplayıcıyı tanıyacağız, çöp toplayıcıdan nasıl bellek ayrıldığını ve belirli bellek bölgelerine değişkenlerin nasıl yerleştirildiklerini göreceğiz. Farklı bellek yönetimi yöntemlerini ve özellikle $(C std.allocator) modülünü kendiniz araştırmanızı öneririm. ($(C std.allocator) bu kitap yazıldığı sırada henüz deneysel aşamadaydı.) -) - -$(P -Önceki bazı bölümlerde olduğu gibi, aşağıda kısaca yalnızca $(I değişken) yazdığım yerlerde yapı ve sınıf nesneleri de dahil olmak üzere her türden değişkeni kastediyorum. -) - -$(H5 $(IX bellek) Bellek) - -$(P -Bellek hem programın kendisini hem de kullandığı verileri barındırır. Bu yüzden diğer bilgisayar kaynaklarından daha önemlidir. Bu kaynak temelde işletim sistemine aittir. İşletim sistemi belleği ihtiyaçlar doğrultusunda programlara paylaştırır. Her programın kullanmakta olduğu bellek o programın belirli zamanlardaki ihtiyaçları doğrultusunda artabilir veya azalabilir. Belirli bir programın kullandığı bellek o program sonlandığında tekrar işletim sistemine geçer. -) - -$(P -Bellek, değişken değerlerinin yazıldığı bir defter gibi düşünülebilir. Her değişken bellekte belirli bir yere yazılır. Her değişkenin değeri gerektikçe aynı yerden okunur ve kullanılır. Yaşamı sona eren değişkenlerin yerleri daha sonradan başka değişkenler için kullanılır. -) - -$(P -$(IX &, adres) Bellekle ilgili deneyler yaparken değişkenlerin adres değerlerini veren $(C &) işlecinden yararlanabiliriz: -) - ---- -import std.stdio; - -void main() { - int i; - int j; - - writeln("i: ", $(HILITE &)i); - writeln("j: ", $(HILITE &)j); -} ---- - -$(P -$(I Not: Adresler programın her çalıştırılışında büyük olasılıkla farklı olacaktır. Ek olarak, adres değerini edinmiş olmak, normalde bir mikro işlemci yazmacında yaşayacak olan bir değişkenin bile bellekte yaşamasına neden olur.) -) - -$(P -Çıktısı: -) - -$(SHELL -i: 7FFF2B633E2$(HILITE 8) -j: 7FFF2B633E2$(HILITE C) -) - -$(P -Adreslerdeki tek fark olan son hanelere bakarak $(C i)'nin bellekte $(C j)'den hemen önce bulunduğunu görebiliyoruz: 8'e $(C int)'in büyüklüğü olan 4'ü eklersek on altılı sayı düzeninde C elde edilir. -) - -$(H5 $(IX çöp toplayıcı) $(IX GC, çöp toplayıcı) Çöp toplayıcı) - -$(P -D programlarındaki dinamik değişkenler çöp toplayıcıya ait olan bellek bölgelerinde yaşarlar. Yaşamları sona eren değişkenler çöp toplayıcının işlettiği bir algoritma ile sonlandırılırlar. Bu değişkenlerin yerleri tekrar kullanılmak üzere geri alınır. Bu işleme aşağıda bazen $(I çöp toplama), bazen de $(I temizlik) diyeceğim. -) - -$(P -Çöp toplayıcının işlettiği algoritma çok kabaca şöyle açıklanabilir: Çağrı yığıtı da dahil olmak üzere $(I kök) olarak adlandırılan bölgeler taranır. O bölgelerdeki değişkenler yoluyla doğrudan veya dolaylı olarak erişilebilen bütün bellek bölgeleri belirlenir ve program tarafından herhangi bir yolla erişilebilen bütün bölgelerin hâlâ kullanımda olduklarına karar verilir. Kullanımda olmadıkları görülen diğer bellek bölgelerindeki değişkenlerin sonlandırıcıları işletilir ve o bellek bölgeleri sonradan başka değişkenler için kullanılmak üzere geri alınır. Kökler; her iş parçacığının çağrı yığıtından, bütün evrensel değişkenlerden, ve $(C GC.addRoot) veya $(C GC.addRange) ile tanıtılmış olan bölgelerden oluşur. -) - -$(P -Bazı çöp toplayıcılar kullanımda olan bütün değişkenleri bellekte yan yana dursunlar diye başka yerlere taşıyabilirler. Programın tutarlılığı bozulmasın diye de o değişkenleri gösteren bütün göstergelerin değerlerini otomatik olarak değiştirirler. (D'nin bu kitabın yazıldığı sırada kullandığı çöp toplayıcısı nesne taşıyan çeşitten değildi.) -) - -$(P -Hangi bellek bölgelerinde gösterge bulunduğunun ve hangilerinde bulunmadığının hesabını tutan çöp toplayıcılarına $(I hassas) $(ASIL precise) denir. Bunun aksine, her bellek bölgesindeki değerlerin gösterge olduklarını varsayan çöp toplayıcılarına ise $(I korunumlu) $(ASIL conservative) denir. Bu kitabın yazıldığı sırada kullanılan D çöp toplayıcısının yarı korunumlu olduğunu söyleyebiliriz: yalnızca gösterge içeren bellek bölgelerini, ama o bölgelerin tamamını tarar. Bunun bir etkisi, bazı bellek bölgelerinin hiç toplanmayarak $(I bellek sızıntısı) oluşturabilmesidir. $(I Yalancı göstergelerin) neden olduğu bu durumdan kaçınmak için artık kullanılmadığı bilinen bellek bölgelerinin programcı tarafından açıkça geri verilmesi önerilir. -) - -$(P -Temizlik işlemlerinin hangi sırada işletildikleri belirsizdir. Örneğin, nesnelerin referans türündeki (göstergeler dahil) üyeleri kendilerini barındıran nesneden daha önce sonlanmış olabilirler. Bu yüzden, yaşamları çöp toplayıcıya ait olan ve kendileri referans türünden olan üyelerin sonlandırıcı işlevler içinde kullanılmaları hatalıdır. Bu kavram sonlanma sıralarının tam olarak belirli olduğu C++ gibi bazı dillerden farklıdır. -) - -$(P -Temizlik işlemleri boş yerin azalmaya başlaması gibi nedenlerle ve önceden kestirilemeyecek zamanlarda işletilebilir. Temizlik işlemleri devam ederken yeni yer ayrılması çöp toplama düzeneğinde karışıklık yaratabileceğinden programa ait olan bütün iş parçacıkları temizlik sırasında kısa süreliğine durdurulabilirler. Bu işlem sırasında programın tutukluk yaptığı hissedilebilir. -) - -$(P -Programcının çöp toplayıcının işine karışması çoğu durumda gerekmese de temizlik işlemlerinin hemen işletilmeleri veya ertelenmeleri gibi bazı işlemler $(C core.memory) modülünün olanakları ile sağlanabilir. -) - -$(H6 $(IX GC.enable) $(IX GC.disable) $(IX GC.collect) Temizlik başlatmak ve ertelemek) - -$(P -Programın tutukluk yapmadan çalışması gereken yerlerde temizlik işlemlerinin ertelenmesi mümkündür. $(C GC.disable) temizlik işlemlerini erteler, $(C GC.enable) da tekrar etkinleştirir: -) - ---- - GC.disable(); - -// ... tutukluk hissedilmeden işlemesi gereken işlemler ... - - GC.enable(); ---- - -$(P -Ancak, temizlik işlemlerinin kesinlikle işletilmeyecekleri garantili değildir: Çöp toplayıcı belleğin çok azaldığını farkettiği durumlarda boş yer bulmak için yine de işletebilir. -) - -$(P -Temizlik işlemleri programın tutukluk yapmasının sorun oluşturmadığının bilindiği bir zamanda programcı tarafından $(C GC.collect()) ile başlatılabilir: -) - ---- -import core.memory; - -// ... - - GC.collect(); // temizlik başlatır ---- - -$(P -Normalde, çöp toplayıcı boş kalan bellek bölgelerini işletim sistemine geri vermez ve ileride oluşturulacak olan değişkenler için elinde tutmaya devam eder. Bunun bir sorun oluşturduğunun bilindiği programlarda boş bellek bölgeleri $(C GC.minimize()) ile işletim sistemine geri verilebilir: -) - ---- - GC.minimize(); ---- - -$(H5 Bellekten yer ayırmak) - -$(P -Bellekten herhangi bir amaç için bellek bölgesi ayrılabilir. Böyle bir bölge örneğin üzerinde değişkenler kurmak için kullanılabilir. -) - -$(P -Belirli sayıda bayttan oluşan bir bellek bölgesi sabit uzunluklu bir dizi olarak ayrılabilir: -) - ---- - ubyte[100] yer; // 100 baytlık yer ---- - -$(P -$(IX ilklenmeyen dizi) $(IX dizi, ilklenmeyen) $(IX = void) Yukarıdaki dizi 100 baytlık bellek bölgesi olarak kullanılmaya hazırdır. Bazen bu bölgenin $(C uybte) gibi bir türle ilgisi olması yerine $(I hiçbir türden) olması istenebilir. Bunun için eleman türü olarak $(C void) seçilir ve $(C void) türü herhangi bir değer alamadığından böyle dizilerin özel olarak $(C =void) ile ilklenmeleri gerekir: -) - ---- - void[100] yer = void; // 100 baytlık yer ---- - -$(P -$(IX GC.calloc) Bu bölümde bellek ayırmak için yalnızca $(C core.memory) modülündeki $(C GC.calloc) işlevini kullanacağız. Aynı modüldeki diğer bellek ayırma işlevlerini kendiniz araştırmak isteyebilirsiniz. Ek olarak, C standart kütüphanesinin olanaklarını içeren $(C std.c.stdlib) modülündeki $(C calloc()) ve diğer işlevler de kullanılabilir. -) - -$(P -$(C GC.calloc) bellekten kaç bayt istendiğini parametre olarak alır ve ayırdığı bellek bölgesinin başlangıç adresini döndürür: -) - ---- -import core.memory; -// ... - void * yer = GC.calloc(100); // 100 baytlık yer ---- - -$(P -$(IX void*) $(C void*) ile gösterilen bir bölgenin hangi tür için kullanılacağı o türün göstergesine dönüştürülerek belirlenebilir: -) - ---- - int * intYeri = $(HILITE cast(int*))yer; ---- - -$(P -Ancak, o ara adım çoğunlukla atlanır ve $(C GC.calloc)'un döndürdüğü adres istenen türe doğrudan dönüştürülür: -) - ---- - int * intYeri = cast(int*)GC.calloc(100); ---- - -$(P -Öylesine seçmiş olduğum 100 gibi hazır değerler kullanmak yerine örneğin türün uzunluğu ile nesne adedi çarpılabilir: -) - ---- - // 25 int için yer - int * yer = cast(int*)GC.calloc($(HILITE int.sizeof * 25)); ---- - -$(P -$(IX classInstanceSize) $(IX .sizeof, class) Sınıf nesnelerinin uzunluğu konusunda önemli bir fark vardır: $(C .sizeof) sınıf nesnesinin değil, sınıf değişkeninin uzunluğudur. Sınıf nesnesinin uzunluğu $(C __traits(classInstanceSize)) ile öğrenilir: -) - ---- - // 10 Sınıf nesnesi için yer - Sınıf * yer = - cast(Sınıf*)GC.calloc( - $(HILITE __traits(classInstanceSize, Sınıf)) * 10); ---- - -$(P -$(IX OutOfMemoryError) İstenen büyüklükte bellek ayrılamadığı zaman $(C core.exception.OutOfMemoryError) türünde bir hata atılır: -) - ---- - void * yer = GC.calloc(10_000_000_000); ---- - -$(P -O kadar bellek ayrılamayan durumdaki çıktısı: -) - -$(SHELL -core.exception.OutOfMemoryError -) - -$(P -$(IX GC.free) Ayrılan bellek işi bittiğinde $(C GC.free) ile geri verilebilir: -) - ---- - GC.free(yer); ---- - -$(P -Ancak, açıkça çağrılan $(C free()), sonlandırıcıları işletmez. Sonlanmaları gereken nesnelerin bellek geri verilmeden önce $(C destroy()) ile teker teker sonlandırılmaları gerekir. Çöp toplayıcı $(C struct) ve $(C class) nesnelerini sonlandırma kararını verirken çeşitli etkenleri gözden geçirir. Bu yüzden, sonlandırıcının kesinlikle çağrılması gereken bir durumda en iyisi nesneyi $(C new) işleci ile kurmaktır. O zaman $(C GC.free()) sonlandırıcıyı işletir. -) - -$(P -$(IX GC.realloc) Daha önce çöp toplayıcıdan alınmış olan bir bellek bölgesinin $(I uzatılması) mümkündür. $(C GC.realloc()), daha önce edinilmiş olan adres değerini ve istenen yeni uzunluğu parametre olarak alır ve yeni uzunlukta bir yer döndürür. Aşağıdaki kod önceden 100 bayt olarak ayrılmış olan bellek bölgesini 200 bayta uzatıyor: -) - ---- - void * eskiYer = GC.calloc(100); -// ... - void * yeniYer = $(HILITE GC.realloc)(eskiYer, 200); ---- - -$(P -$(C realloc()) gerçekten gerekmedikçe yeni yer ayırmaz: -) - -$(UL - -$(LI Eski yerin hemen sonrası yeni uzunluğu karşılayacak kadar boşsa orayı da eski yere ekleyerek bir anlamda eski belleği uzatır.) - -$(LI Eski yerin hemen sonrası boş değilse veya yeni büyüklük için yeterli değilse, istenen miktarı karşılayacak yeni bir bellek bölgesi ayırır ve eski belleğin içeriğini oraya kopyalar.) - -$(LI Eski yer olarak $(C null) gönderilebilir; o durumda yalnızca yeni bir yer ayırır.) - -$(LI Yeni uzunluk olarak eski uzunluktan daha küçük bir değer gönderilebilir; o durumda yalnızca bellek bölgesinin geri kalanı çöp toplayıcıya geri verilmiş olur.) - -$(LI Yeni uzunluk 0 ise eski bellek $(C free()) çağrılmış gibi geri verilir.) - -) - -$(P -$(C GC.realloc) C kütüphanesindeki aynı isimli işlevden gelmiştir. Görevi hem fazla çeşitli hem de fazla karmaşık olduğundan hatalı tasarlanmış bir işlev olarak kabul edilir. $(C GC.realloc)'un şaşırtıcı özelliklerinden birisi, asıl bellek $(C GC.calloc) ile ayrılmış bile olsa uzatılan bölümün sıfırlanmamasıdır. Bu yüzden, belleğin sıfırlanmasının önemli olduğu durumlarda aşağıdaki gibi bir işlevden yararlanılabilir ($(C bellekNitelikleri) parametresinin anlamını biraz aşağıda göreceğiz): -) - ---- -$(CODE_NAME boşOlarakUzat)import core.memory; - -/* GC.realloc gibi işler. Ondan farklı olarak, belleğin - * uzatıldığı durumda eklenen baytları sıfırlar. */ -void * boşOlarakUzat( - void * yer, - size_t eskiUzunluk, - size_t yeniUzunluk, - GC.BlkAttr bellekNitelikleri = GC.BlkAttr.NONE, - const TypeInfo türBilgisi = null) { - /* Asıl işi GC.realloc'a yaptırıyoruz. */ - yer = GC.realloc(yer, yeniUzunluk, - bellekNitelikleri, türBilgisi); - - /* Eğer varsa, yeni eklenen bölümü sıfırlıyoruz. */ - if (yeniUzunluk > eskiUzunluk) { - import std.c.string; - - auto eklenenYer = yer + eskiUzunluk; - auto eklenenUzunluk = yeniUzunluk - eskiUzunluk; - - memset(eklenenYer, 0, eklenenUzunluk); - } - - return yer; -} ---- - -$(P -$(IX memset, std.c.string) $(C std.c.string) modülünde tanımlı olan $(C memset()) belirtilen adresteki belirtilen sayıdaki bayta belirtilen değeri atar. Örneğin, yukarıdaki çağrı $(C eklenenYer)'deki $(C eklenenUzunluk) adet baytı $(C 0) yapar. -) - -$(P -$(C boşOlarakUzat()) işlevini aşağıdaki bir örnekte kullanacağız. -) - -$(P -$(IX GC.extend) $(C GC.realloc) ile benzer amaçla kullanılan $(C GC.extend)'in davranışı çok daha basittir çünkü yalnızca yukarıdaki ilk maddeyi uygular: Eski yerin hemen sonrası yeni uzunluğu karşılayamıyorsa hiçbir işlem yapmaz ve bu durumu 0 döndürerek bildirir. -) - -$(H6 $(IX bellek bölgesi niteliği) $(IX BlkAttr) Ayrılan belleğin temizlik işlemlerinin belirlenmesi) - -$(P -Çöp toplayıcı algoritmasında geçen kavramlar ve adımlar bir $(C enum) türü olan $(C BlkAttr)'ın değerleri ile her bellek bölgesi için ayrı ayrı ayarlanabilir. $(C BlkAttr), $(C GC.calloc) ve diğer bellek ayırma işlevlerine parametre olarak gönderilebilir ve bellek bölgelerinin niteliklerini belirlemek için kullanılır. $(C BlkAttr) türünün değerleri şunlardır: -) - -$(UL - -$(LI $(C NONE): Sıfır değeri; hiçbir niteliğin belirtilmediğini belirler.) - -$(LI $(C FINALIZE): Bölgedeki nesnelerin temizlik sırasında çöp toplayıcı tarafından sonlandırılmaları gerektiğini belirler. - -$(P -Normalde, çöp toplayıcı kendisinden ayrılmış olan bellekteki nesnelerin yaşam süreçlerinin artık programcının sorumluluğuna girdiğini düşünür ve bu bölgelerdeki nesnelerin sonlandırıcılarını işletmez. $(C GC.BlkAttr.FINALIZE) değeri, çöp toplayıcının sonlandırıcıları yine de işletmesinin istendiğini belirtir: -) - ---- - Sınıf * yer = - cast(Sınıf*)GC.calloc( - __traits(classInstanceSize, Sınıf) * 10, - GC.BlkAttr.FINALIZE); ---- - -$(P -$(C FINALIZE), çöp toplayıcının bellek bloğuna yazdığı kendi özel ayarlarıyla ilgili bir belirteçtir. O yüzden, bu belirtecin normalde programcı tarafından değil, çöp toplayıcı tarafından kullanılması önerilir. -) - -) - -$(LI $(C NO_SCAN): Bölgenin çöp toplayıcı tarafından taran$(I ma)ması gerektiğini belirler. - -$(P -Ayrılan bölgedeki bayt değerleri tesadüfen ilgisiz başka değişkenlerin adreslerine karşılık gelebilirler. Öyle bir durumda çöp toplayıcı hâlâ kullanımda olduklarını sanacağından, aslında yaşamları sona ermiş bile olsa o başka değişkenleri sonlandırmaz. -) - -$(P -Başka değişken referansları taşımadığı bilinen bellek bölgelerinin taranması $(C GC.BlkAttr.NO_SCAN) niteliği ile engellenir: -) - ---- - int * intYeri = - cast(int*)GC.calloc(100, GC.BlkAttr.NO_SCAN); ---- - -$(P -Yukarıdaki bellek bölgesine yerleştirilecek olan $(C int) değerlerinin tesadüfen başka değişkenlerin adreslerine eşit olmaları böylece artık sorun oluşturmaz. -) - -) - -$(LI $(C NO_MOVE): Bölgedeki nesnelerin başka bölgelere taşın$(I ma)maları gerektiğini belirler.) - -$(LI $(C APPENDABLE): Bu, D $(I çalışma ortamına) ait olan ve dizilere daha hızlı eleman eklenmesini sağlayan bir belirteçtir. Programcı tarafından kullanılmaz.) - -$(LI $(C NO_INTERIOR): Bu bölgenin $(I iç tarafındaki) değişkenleri gösteren gösterge bulunmadığını belirtir (olası göstergeler bölgenin yalnızca ilk adresini gösterirler). Bu, $(I yalancı gösterge) olasılığını düşürmeye yarayan bir belirteçtir.) - -) - -$(P -$(IX |) Bu değerler $(LINK2 /ders/d/bit_islemleri.html, Bit İşlemleri bölümünde) gördüğümüz işleçlerle birlikte kullanılabilecek biçimde seçilmişlerdir. Örneğin, iki değer $(C |) işleci ile aşağıdaki gibi birleştirilebilir: -) - ---- - const bellekAyarları = - GC.BlkAttr.NO_SCAN $(HILITE |) GC.BlkAttr.NO_INTERIOR; ---- - -$(P -Doğal olarak, çöp toplayıcı yalnızca kendi ayırdığı bellek bölgelerini tanır ve temizlik işlemleri sırasında yalnızca o bölgeleri tarar. Örneğin, $(C std.c.stdlib.calloc) ile ayrılmış olan bellek bölgelerinden çöp toplayıcının normalde haberi olmaz. -) - -$(P -$(IX GC.addRange) $(IX GC.removeRange) $(IX GC.addRoot) Kendisinden alınmamış olan bir bölgenin çöp toplayıcının yönetimine geçirilmesi için $(C GC.addRange()) işlevi kullanılır. Bunun karşıtı olarak, bellek geri verilmeden önce de $(C GC.removeRange())'in çağrılması gerekir. -) - -$(P -Bazı durumlarda çöp toplayıcı kendisinden ayrılmış olan bir bölgeyi gösteren hiçbir referans bulamayabilir. Örneğin, ayrılan belleğin tek referansı bir C kütüphanesi içinde tutuluyor olabilir. Böyle bir durumda çöp toplayıcı o bölgenin kullanımda olmadığını düşünecektir. -) - -$(P -$(C GC.addRoot()), belirli bir bölgeyi çöp toplayıcıya tanıtır ve oradan dolaylı olarak erişilebilen bütün nesneleri de yönetmesini sağlar. Bunun karşıtı olarak, bellek geri verilmeden önce de $(C GC.removeRoot()) işlevinin çağrılması gerekir. -) - -$(H6 Bellek uzatma örneği) - -$(P -$(C realloc())'un kullanımını göstermek için dizi gibi işleyen çok basit bir yapı tasarlayalım. Çok kısıtlı olan bu yapıda yalnızca eleman ekleme ve elemana erişme olanakları bulunsun. D dizilerinde olduğu gibi bu yapının da sığası olsun. Aşağıdaki yapı sığayı gerektikçe yukarıda tanımladığımız ve kendisi $(C GC.realloc)'tan yararlanan $(C boşOlarakUzat()) ile arttırıyor: -) - ---- -$(CODE_NAME Dizi)$(CODE_XREF boşOlarakUzat)struct Dizi(T) { - T * yer; // Elemanların bulunduğu yer - size_t sığa; // Toplam kaç elemanlık yer olduğu - size_t uzunluk; // Eklenmiş olan eleman adedi - - /* Belirtilen numaralı elemanı döndürür */ - T eleman(size_t numara) { - import std.string; - enforce(numara < uzunluk, - format("%s numara yasal değil", numara)); - - return *(yer + numara); - } - - /* Elemanı dizinin sonuna ekler */ - void ekle(T eleman) { - writefln("%s numaralı eleman ekleniyor", uzunluk); - - if (uzunluk == sığa) { - /* Yeni eleman için yer yok; sığayı arttırmak - * gerekiyor. */ - size_t yeniSığa = sığa + (sığa / 2) + 1; - sığaArttır(yeniSığa); - } - - /* Elemanı en sona yerleştiriyoruz */ - *(yer + uzunluk) = eleman; - ++uzunluk; - } - - void sığaArttır(size_t yeniSığa) { - writefln("Sığa artıyor: %s -> %s", - sığa, yeniSığa); - - auto eskiUzunluk = sığa * T.sizeof; - auto yeniUzunluk = yeniSığa * T.sizeof; - - /* Bu bölgeye yerleştirilen bayt değerlerinin - * tesadüfen başka değişkenlerin göstergeleri - * sanılmalarını önlemek için NO_SCAN belirtecini - * kullanıyoruz. */ - yer = cast(T*)$(HILITE boşOlarakUzat)( - yer, eskiUzunluk, yeniUzunluk, GC.BlkAttr.NO_SCAN); - - sığa = yeniSığa; - } -} ---- - -$(P -Bu dizinin sığasi her seferinde yaklaşık olarak %50 oranında arttırılıyor. Örneğin, 100 elemanlık yer tükendiğinde yeni sığa 151 oluyor. ($(I Yeni sığa hesaplanırken eklenen 1 değeri, başlangıç durumunda sıfır olan sığa için özel bir işlem gerekmesini önlemek içindir. Öyle olmasaydı, sıfırın %50 fazlası da sıfır olacağından sığa hiç artamazdı.)) -) - -$(P -Bu yapıyı $(C double) türünde elemanlarla şöyle deneyebiliriz: -) - ---- -$(CODE_XREF Dizi)import std.stdio; -import core.memory; -import std.exception; - -// ... - -void main() { - auto dizi = Dizi!double(); - - size_t adet = 10; - - foreach (i; 0 .. adet) { - double elemanDeğeri = i * 1.1; - dizi.ekle(elemanDeğeri); - } - - writeln("Bütün elemanlar:"); - - foreach (i; 0 .. adet) { - write(dizi.eleman(i), ' '); - } - - writeln(); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -0 numaralı eleman ekleniyor -Sığa artıyor: 0 -> 1 -1 numaralı eleman ekleniyor -Sığa artıyor: 1 -> 2 -2 numaralı eleman ekleniyor -Sığa artıyor: 2 -> 4 -3 numaralı eleman ekleniyor -4 numaralı eleman ekleniyor -Sığa artıyor: 4 -> 7 -5 numaralı eleman ekleniyor -6 numaralı eleman ekleniyor -7 numaralı eleman ekleniyor -Sığa artıyor: 7 -> 11 -8 numaralı eleman ekleniyor -9 numaralı eleman ekleniyor -Bütün elemanlar: -0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 -) - -$(H5 $(IX hizalama) Hizalama birimi) - -$(P -Değişkenler normalde kendi türlerine özgü bir değerin katı olan adreslerde bulunurlar. Bu değere o türün $(I hizalama birimi) denir. Örneğin, $(C int) türünün hizalama birimi 4'tür çünkü $(C int) değişkenler ancak dördün katı olan adreslerde (4, 8, 12, vs.) bulunabilirler. -) - -$(P -Hizalama, hem mikro işlemci işlemlerinin hızlı olması için istenen hem de mikro işlemcinin nesne adresleyebilmesi için gereken bir kavramdır. Ek olarak, bazı değişkenler yalnızca kendi türlerinin hizalama birimine uyan adreslerde iseler kullanılabilirler. -) - -$(H6 $(IX .alignof) Türlerin $(C .alignof) niteliği) - -$(P -$(IX classInstanceAlignment) Bir türün $(C .alignof) niteliği o türün $(I varsayılan) hizalama birimini döndürür. Ancak, sınıflarda $(C .alignof) sınıf nesnesinin değil, sınıf değişkeninin hizalama birimidir. Sınıf nesnesinin hizalama birimi için $(C std.traits.classInstanceAlignment) kullanılmalıdır. -) - -$(P -Aşağıdaki program çeşitli türün hizalama birimini yazdırıyor. -) - ---- -import std.stdio; -import std.meta; -import std.traits; - -struct BoşYapı { -} - -struct Yapı { - char c; - double d; -} - -class BoşSınıf { -} - -class Sınıf { - char karakter; -} - -void main() { - alias Türler = AliasSeq!(char, short, int, long, - double, real, - string, int[int], int*, - BoşYapı, Yapı, BoşSınıf, Sınıf); - - writeln(" Uzunluk Hizalama Tür\n", - "========================"); - - foreach (Tür; Türler) { - static if (is (Tür == class)) { - size_t uzunluk = __traits(classInstanceSize, Tür); - size_t hizalama = $(HILITE classInstanceAlignment!Tür); - - } else { - size_t uzunluk = Tür.sizeof; - size_t hizalama = $(HILITE Tür.alignof); - } - - writefln("%6s%9s %s", - uzunluk, hizalama, Tür.stringof); - } -} ---- - -$(P -Bu programın çıktısı farklı ortamlarda farklı olabilir: -) - -$(SHELL - Uzunluk Hizalama Tür -======================== - 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 BoşYapı - 16 8 Yapı - 16 8 BoşSınıf - 17 8 Sınıf -) - -$(P -Biraz aşağıda nesnelerin belirli adreslerde de kurulabildiklerini göreceğiz. Bunun güvenle yapılabilmesi için hizalama birimlerinin gözetilmeleri gerekir. -) - -$(P -Bunun örneğini görmek için yukarıdaki 17 bayt uzunluğundaki $(C Sınıf) türünün iki nesnesinin bellekte $(I yan yana) nasıl durabileceklerine bakalım. Her ne kadar yasal bir adres olmasa da, örneği kolaylaştırmak için birinci nesnenin 0 adresinde bulunduğunu varsayalım. Bu nesneyi oluşturan baytlar 0'dan 16'ya kadar olan adreslerdedir: -) - -$(MONO - $(HILITE 0) 1 16 - ┌────┬────┬─ ... ─┬────┬─ ... - │$(HILITE <───birinci nesne────>)│ - └────┴────┴─ ... ─┴────┴─ ... -) - -$(P -$(IX doldurma baytı) Bir sonraki boş yerin adresi 17 olduğu halde o adres değeri $(C Sınıf)'ın hizalama birimi olan 8'in katı olmadığından ikinci nesne orada kurulamaz. İkinci nesnenin 8'in katı olan bir sonraki adrese, yani 24 adresine yerleştirilmesi gerekir. Aradaki kullanılmayan baytlara $(I doldurma) baytları denir: -) - -$(P -) - -$(MONO - $(HILITE 0) 1 16 17 23 $(HILITE 24) 25 30 - ┌────┬────┬─ ... ─┬────┬────┬─ ... ─┬────┬────┬────┬─ ... ─┬────┬─ ... - │$(HILITE <───birinci nesne────>)│ <──$(I doldurma)───> │$(HILITE <────ikinci nesne────>)│ - └────┴────┴─ ... ─┴────┴────┴─ ... ─┴────┴────┴────┴─ ... ─┴────┴─ ... -) - -$(P -Bir nesnenin belirli bir aday adresten sonra yasal olarak kurulabileceği ilk adresi elde etmek için şu hesap kullanılabilir: -) - ---- - (adayAdres + hizalamaBirimi - 1) - / hizalamaBirimi - * hizalamaBirimi ---- - -$(P -Yukarıdaki hesabın doğru olarak işlemesi için bölme işleminden kalanın gözardı edilmesi şarttır. O yüzden o hesapta tamsayı türleri kullanılır. -) - -$(P -Aşağıda $(C emplace())'in örneklerini gösterirken yukarıdaki hesabı uygulayan şu işlevden yararlanacağız: -) - ---- -$(CODE_NAME hizalıAdres)T * hizalıAdres(T)(T * adayAdres) { - import std.traits; - - static if (is (T == class)) { - const hizalama = classInstanceAlignment!T; - - } else { - const hizalama = T.alignof; - } - - const sonuç = (cast(size_t)adayAdres + hizalama - 1) - / hizalama * hizalama; - return cast(T*)sonuç; -} ---- - -$(P -Yukarıdaki işlev nesnenin türünü şablon parametresinden otomatik olarak çıkarsamaktadır. Onun $(C void*) adresleri ile işleyen yüklemesini de şöyle yazabiliriz: -) - ---- -$(CODE_NAME hizalıAdres_void)void * hizalıAdres(T)(void * adayAdres) { - return hizalıAdres(cast(T*)adayAdres); -} ---- - -$(P -Bu işlev de aşağıda $(C emplace()) ile $(I sınıf) nesneleri oluştururken yararlı olacak. -) - -$(P -Son olarak, yukarıdaki işlevden yararlanan yardımcı bir işlev daha tanımlayalım. Bu işlev, nesnenin boşluklarla birlikte kaç bayt yer tuttuğunu döndürür: -) - ---- -$(CODE_NAME boşlukluUzunluk)size_t boşlukluUzunluk(T)() { - static if (is (T == class)) { - size_t uzunluk = __traits(classInstanceSize, T); - - } else { - size_t uzunluk = T.sizeof; - } - - return cast(size_t)hizalıAdres(cast(T*)uzunluk); -} ---- - -$(H6 $(IX .offsetof) $(C .offsetof) niteliği) - -$(P -Hizalama üye değişkenlerle de ilgili olan bir kavramdır. Üyeleri kendi türlerinin hizalama birimlerine uydurmak için üyeler arasına da doldurma baytları yerleştirilir. Örneğin, aşağıdaki yapının büyüklüğü bekleneceği gibi 6 değil, 12'dir: -) - ---- -struct A { - byte b; // 1 bayt - int i; // 4 bayt - ubyte u; // 1 bayt -} - -static assert($(HILITE A.sizeof == 12)); // 1 + 4 + 1'den daha fazla ---- - -$(P -Bunun nedeni, hem $(C int) üye dördün katı olan bir adrese denk gelsin diye ondan önceye yerleştirilen, hem de bütün yapı nesnesi yapı türünün hizalama birimine uysun diye en sona yerleştirilen doldurma baytlarıdır. -) - -$(P -$(C .offsetof) niteliği bir üyenin nesnenin başlangıç adresinden kaç bayt sonra olduğunu bildirir. Aşağıdaki işlev belirli bir türün bellekteki yerleşimini doldurma baytlarını $(C .offsetof) ile belirleyerek yazdırır: -) - ---- -$(CODE_NAME nesneYerleşiminiYazdır)void nesneYerleşiminiYazdır(T)() - if (is (T == struct) || is (T == union)) { - import std.stdio; - import std.string; - - writefln("=== '%s' nesnelerinin yerleşimi" ~ - " (.sizeof: %s, .alignof: %s) ===", - T.stringof, T.sizeof, T.alignof); - - /* Tek satır bilgi yazar. */ - void satırYazdır(size_t uzaklık, string bilgi) { - writefln("%4s: %s", uzaklık, bilgi); - } - - /* Doldurma varsa miktarını yazdırır. */ - void doldurmaBilgisiYazdır(size_t beklenenUzaklık, - size_t gözlemlenenUzaklık) { - if (beklenenUzaklık < gözlemlenenUzaklık) { - /* Gözlemlenen uzaklık beklenenden fazlaysa - * doldurma baytı var demektir. */ - - const doldurmaMiktarı = - gözlemlenenUzaklık - beklenenUzaklık; - - satırYazdır(beklenenUzaklık, - format("... %s bayt DOLDURMA", - doldurmaMiktarı)); - } - } - - /* Bir sonraki üyenin doldurma olmayan durumda nerede - * olacağı bilgisini tutar. */ - size_t doldurmasızUzaklık = 0; - - /* Not: __traits(allMembers) bir türün üyelerinin - * isimlerinden oluşan bir 'string' topluluğudur. */ - foreach (üyeİsmi; __traits(allMembers, T)) { - mixin (format("alias üye = %s.%s;", - T.stringof, üyeİsmi)); - - const uzaklık = üye$(HILITE .offsetof); - doldurmaBilgisiYazdır(doldurmasızUzaklık, uzaklık); - - const türİsmi = typeof(üye).stringof; - satırYazdır(uzaklık, format("%s %s", türİsmi, üyeİsmi)); - - doldurmasızUzaklık = uzaklık + üye.sizeof; - } - - doldurmaBilgisiYazdır(doldurmasızUzaklık, T.sizeof); -} ---- - -$(P -Aşağıdaki program, büyüklüğü yukarıda 12 bayt olarak bildirilen $(C A) yapısının yerleşimini yazdırır: -) - ---- -$(CODE_XREF nesneYerleşiminiYazdır)struct A { - byte b; - int i; - ubyte u; -} - -void main() { - nesneYerleşiminiYazdır!A(); -} ---- - -$(P -Programın çıktısı 6 doldurma baytının nesnenin nerelerinde olduğunu gösteriyor. Çıktıda soldaki sütun nesnenin başından olan uzaklığı göstermektedir: -) - -$(SHELL -=== 'A' nesnelerinin yerleşimi (.sizeof: $(HILITE 12), .alignof: 4) === - 0: byte b - 1: ... 3 bayt DOLDURMA - 4: int i - 8: ubyte u - 9: ... 3 bayt DOLDURMA -) - -$(P -Doldurma baytlarını olabildiğince azaltmanın bir yolu, üyeleri yapı içinde büyükten küçüğe doğru sıralamaktır. Örneğin, $(C int) üyeyi diğerlerinden önceye alınca yapının büyüklüğü azalır: -) - ---- -$(CODE_XREF nesneYerleşiminiYazdır)struct B { - $(HILITE int i;) // Üye listesinin başına getirildi - byte b; - ubyte u; -} - -void main() { - nesneYerleşiminiYazdır!B(); -} ---- - -$(P -Bu sefer yalnızca en sonda 2 doldurma baytı bulunduğundan yapının büyüklüğü 8'e inmiştir: -) - -$(SHELL -=== 'B' nesnelerinin yerleşimi (.sizeof: $(HILITE 8), .alignof: 4) === - 0: int i - 4: byte b - 5: ubyte u - 6: ... 2 bayt DOLDURMA -) - -$(H6 $(IX align) $(C align) niteliği) - -$(P -$(C align) niteliği değişkenlerin, kullanıcı türlerinin, ve üyelerin hizalama birimlerini belirler. Parantez içinde belirtilen değer hizalama birimidir. Her tanımın hizalama birimi ayrı ayrı belirlenebilir. Örneğin, aşağıdaki tanımda $(C S) nesnelerinin hizalama birimi 2, ve özellikle $(C i) üyesinin hizalama birimi 1 olur (hizalama birimi 1, hiç doldurma baytı olmayacak demektir): -) - ---- -$(CODE_XREF nesneYerleşiminiYazdır)$(HILITE align (2)) // 'S' nesnelerinin hizalama birimi -struct S { - byte b; - $(HILITE align (1)) int i; // 'i' üyesinin hizalama birimi - ubyte u; -} - -void main() { - nesneYerleşiminiYazdır!S(); -} ---- - -$(P -$(C int) üyenin hizalama birimi 1 olduğunda onun öncesinde hiç doldurma baytına gerek kalmaz ve yapının büyüklüğü üyelerinin büyüklüğü olan 6'ya eşit olur: -) - -$(SHELL -=== 'S' nesnelerinin yerleşimi (.sizeof: $(HILITE 6), .alignof: 4) === - 0: byte b - 1: int i - 5: ubyte u -) - -$(P -Ancak, varsayılan hizalama birimleri gözardı edildiğinde programın hızında önemli derecede yavaşlama görülebilir. Ek olarak, yanlış hizalanmış olan değişkenler bazı mikro işlemcilerde programın çökmesine neden olabilirler. -) - -$(P -$(C align) ile değişkenlerin hizalamaları da belirlenebilir: -) - ---- - $(HILITE align (32)) double d; // Bu değişkenin hizalama birimi ---- - -$(P -Ancak, çöp toplayıcı $(C new) ile ayrılmış olan nesnelerin hizalama birimlerinin $(C size_t) türünün uzunluğunun bir tam katı olduğunu varsayar. Çöp toplayıcıya ait olan değişkenlerin hizalama birimlerinin buna uymaması tanımsız davranışa neden olur. Örneğin, $(C size_t) 8 bayt ise $(C new) ile ayrılmış olan nesnelerin hizalama birimleri 8'in katı olmalıdır. -) - -$(H5 $(IX emplace, nesne kurma) $(IX kurma, emplace) $(IX emplace) Değişkenleri belirli bir yerde kurmak) - -$(P -$(IX new) $(C new) ifadesi üç işlem gerçekleştirir: -) - -$(OL - -$(LI Bellekten nesnenin sığacağı kadar yer ayırır. Bu bellek bölgesi henüz hiçbir nesneyle ilgili değildir. -) - -$(LI Nesnenin kurucu işlevini o bellek bölgesi üzerinde işletir. Nesne ancak bu işlemden sonra o bölgeye $(I yerleştirilmiş) olur. -) - -$(LI Nesne daha sonradan sonlandırılırken kullanılmak üzere bellek bölgesi belirteçlerini ayarlar. -) - -) - -$(P -Bu işlemlerden birincisinin $(C GC.calloc) ve başka işlevlerle gerçekleştirilebildiğini yukarıda gördük. Bir sistem dili olan D, normalde otomatik olarak işletilen ikinci adımın da programcı tarafından belirlenmesine olanak verir. -) - -$(P -Nesnelerin belirli bir adreste kurulması için "yerleştir" anlamına gelen $(C std.conv.emplace) kullanılır. -) - -$(H6 $(IX emplace, struct) Yapı nesnelerini belirli bir yerde kurmak) - -$(P -$(C emplace()), nesnenin kurulacağı adresi parametre olarak alır ve o adreste bir nesne kurar. Eğer varsa, nesnenin kurucu işlevinin parametreleri bu adresten sonra bildirilir: -) - ---- -import std.conv; -// ... - emplace($(I adres), /* ... kurucu parametreleri ... */); ---- - -$(P -Yapı nesneleri kurarken türün ayrıca belirtilmesi gerekmez; $(C emplace()) hangi türden nesne kuracağını kendisine verilen göstergenin türünden anlar. Örneğin, aşağıdaki $(C emplace()) çağrısında $(C öğrenciAdresi)'nin türü bir $(C Öğrenci*) olduğundan $(C emplace()) o adreste bir $(C Öğrenci) nesnesi kurar: -) - ---- - Öğrenci * öğrenciAdresi = hizalıAdres(adayAdres); -// ... - emplace(öğrenciAdresi, isim, numara); ---- - -$(P -Yukarıdaki işlevlerden yararlanan aşağıdaki program bütün nesneleri alabilecek büyüklükte bir bölge ayırıyor ve nesneleri o bölge içindeki hizalı adreslerde kuruyor: -) - ---- -$(CODE_XREF boşlukluUzunluk)$(CODE_XREF hizalıAdres)import std.stdio; -import std.string; -import core.memory; -import std.conv; - -// ... - -struct Öğrenci { - string isim; - int numara; - - string toString() { - return format("%s(%s)", isim, numara); - } -} - -void main() { - /* Önce bu türle ilgili bilgi yazdırıyoruz. */ - writefln("Öğrenci.sizeof: %#x (%s) bayt", - Öğrenci.sizeof, Öğrenci.sizeof); - writefln("Öğrenci.alignof: %#x (%s) bayt", - Öğrenci.alignof, Öğrenci.alignof); - - string[] isimler = [ "Deniz", "Pınar", "Irmak" ]; - auto toplamBayt = - boşlukluUzunluk!Öğrenci() * isimler.length; - - /* Bütün Öğrenci nesnelerine yetecek kadar yer ayırıyoruz. - * - * UYARI! Bu dilimin eriştirdiği nesneler henüz - * kurulmamışlardır. */ - Öğrenci[] öğrenciler = - (cast(Öğrenci*)GC.calloc(toplamBayt)) - [0 .. isimler.length]; - - foreach (int i, isim; isimler) { - Öğrenci * adayAdres = öğrenciler.ptr + i; - Öğrenci * öğrenciAdresi = hizalıAdres(adayAdres); - writefln("adres %s: %s", i, öğrenciAdresi); - - auto numara = 100 + i; - $(HILITE emplace)(öğrenciAdresi, isim, numara); - } - - /* Bütün elemanları kurulmuş olduğundan bir Öğrenci dilimi - * olarak kullanmakta artık bir sakınca yoktur. */ - writeln(öğrenciler); -} ---- - -$(P -Yukarıdaki program $(C Öğrenci) türünün uzunluğunu, hizalama birimini, ve her öğrencinin kurulduğu adresi de yazdırıyor: -) - -$(SHELL -Öğrenci.sizeof: 0x18 (24) bayt -Öğrenci.alignof: 0x8 (8) bayt -adres 0: 7FCF0B0F2F00 -adres 1: 7FCF0B0F2F18 -adres 2: 7FCF0B0F2F30 -[Deniz(100), Pınar(101), Irmak(102)] -) - -$(H6 $(IX emplace, class) Sınıf nesnelerini belirli bir yerde kurmak) - -$(P -Sınıf değişkenlerinin nesnenin tam türünden olması gerekmez. Örneğin, $(C Hayvan) değişkenleri $(C Kedi) nesnelerine de erişim sağlayabilirler. Bu yüzden $(C emplace()), kuracağı nesnenin türünü kendisine verilen göstergenin türünden anlayamaz ve asıl türün $(C emplace())'e şablon parametresi olarak bildirilmesini gerektirir. ($(I Not: Ek olarak, sınıf göstergesi nesnenin değil, değişkenin adresi olduğundan türün açıkça belirtilmesi nesne mi yoksa değişken mi yerleştirileceği seçimini de programcıya bırakmış olur.)) -) - -$(P -$(IX void[]) Sınıf nesnelerinin kurulacağı yer $(C void[]) türünde bir dilim olarak belirtilir. Bunlara göre sınıf nesneleri kurarken şu söz dizimi kullanılır: -) - ---- - Tür değişken = - emplace!$(I Tür)($(I voidDilimi), - /* ... kurucu parametreleri ... */); ---- - -$(P -$(C emplace()), belirtilen yerde bir nesne kurar ve o nesneye erişim sağlayan bir sınıf $(I değişkeni) döndürür. -) - -$(P -Bunları denemek için bir $(C Hayvan) sıradüzeninden yararlanalım. Bu sıradüzene ait olan nesneleri $(C GC.calloc) ile ayrılmış olan bir belleğe yan yana yerleştireceğiz. Alt sınıfları özellikle farklı uzunlukta seçerek her nesnenin yerinin bir öncekinin uzunluğuna bağlı olarak nasıl hesaplanabileceğini göreceğiz. -) - ---- -$(CODE_NAME Hayvan)interface Hayvan { - string şarkıSöyle(); -} - -class Kedi : Hayvan { - string şarkıSöyle() { - return "miyav"; - } -} - -class Papağan : Hayvan { - string[] sözler; - - this(string[] sözler) { - this.sözler = sözler; - } - - string şarkıSöyle() { - /* std.algorithm.joiner, belirtilen aralıktaki - * elemanları belirtilen ayraçla birleştirir. */ - return sözler.joiner(", ").to!string; - } -} ---- - -$(P -Nesnelerin yerleştirilecekleri bölgeyi $(C GC.calloc) ile ayıracağız: -) - ---- - auto sığa = 10_000; - void * boşYer = GC.calloc(sığa); ---- - -$(P -Normalde, nesneler kuruldukça o bölgenin tükenmediğinden de emin olunması gerekir. Örneği kısa tutmak için bu konuyu gözardı edelim ve kurulacak olan iki nesnenin on bin bayta sığacaklarını varsayalım. -) - -$(P -O bölgede önce bir $(C Kedi) nesnesi sonra da bir $(C Papağan) nesnesi kuracağız: -) - ---- - Kedi kedi = emplace!Kedi(kediYeri); -// ... - Papağan papağan = - emplace!Papağan(papağanYeri, [ "merrba", "aloo" ]); ---- - -$(P -Dikkat ederseniz $(C Papağan)'ın kurucusunun gerektirdiği parametreler nesnenin yerinden sonra belirtiliyorlar. -) - -$(P -$(C emplace()) çağrılarının döndürdükleri değişkenler bir $(C Hayvan) dizisine eklenecekler ve daha sonra bir $(C foreach) döngüsünde kullanılacaklar: -) - ---- - Hayvan[] hayvanlar; -// ... - hayvanlar ~= kedi; -// ... - hayvanlar ~= papağan; - - foreach (hayvan; hayvanlar) { - writeln(hayvan.şarkıSöyle()); - } ---- - -$(P -Diğer açıklamaları programın içine yazıyorum: -) - ---- -$(CODE_XREF Hayvan)$(CODE_XREF hizalıAdres)$(CODE_XREF hizalıAdres_void)import std.stdio; -import std.algorithm; -import std.conv; -import core.memory; - -// ... - -void main() { - /* Bu bir Hayvan değişkeni dizisidir; Hayvan nesnesi - * dizisi değildir. */ - Hayvan[] hayvanlar; - - /* On bin baytın bu örnekte yeterli olduğunu varsayalım. - * Normalde nesnelerin buraya gerçekten sığacaklarının da - * denetlenmesi gerekir. */ - auto sığa = 10_000; - void * boşYer = GC.calloc(sığa); - - /* İlk önce bir Kedi nesnesi yerleştireceğiz. */ - void * kediAdayAdresi = boşYer; - void * kediAdresi = hizalıAdres!Kedi(kediAdayAdresi); - writeln("Kedi adresi : ", kediAdresi); - - /* Sınıflarda emplace()'e void[] verildiğinden adresten - * dilim elde etmek gerekiyor. */ - size_t kediUzunluğu = __traits(classInstanceSize, Kedi); - void[] kediYeri = kediAdresi[0..kediUzunluğu]; - - /* Kedi'yi o yerde kuruyoruz ve döndürülen değişkeni - * diziye ekliyoruz. */ - Kedi kedi = $(HILITE emplace!Kedi)(kediYeri); - hayvanlar ~= kedi; - - /* Papağan'ı Kedi nesnesinden sonraki ilk uygun adreste - * kuracağız. */ - void * papağanAdayAdresi = kediAdresi + kediUzunluğu; - void * papağanAdresi = - hizalıAdres!Papağan(papağanAdayAdresi); - writeln("Papağan adresi: ", papağanAdresi); - - size_t papağanUzunluğu = - __traits(classInstanceSize, Papağan); - void[] papağanYeri = papağanAdresi[0..papağanUzunluğu]; - - Papağan papağan = - $(HILITE emplace!Papağan)(papağanYeri, [ "merrba", "aloo" ]); - hayvanlar ~= papağan; - - /* Nesneleri kullanıyoruz. */ - foreach (hayvan; hayvanlar) { - writeln(hayvan.şarkıSöyle()); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Kedi adresi : 7F869469E000 -Papağan adresi: 7F869469E018 -miyav -merrba, aloo -) - -$(P -Programın adımlarını açıkça gösterebilmek için bütün işlemleri $(C main) içinde ve belirli türlere bağlı olarak yazdım. O işlemlerin iyi yazılmış bir programda $(C yeniNesne(T)) gibi bir şablon içinde bulunmalarını bekleriz. -) - -$(H5 Nesneyi belirli bir zamanda sonlandırmak) - -$(P -$(C new) işlecinin tersi, sonlandırıcı işlevin işletilmesi ve nesne için ayrılmış olan belleğin çöp toplayıcı tarafından geri alınmasıdır. Bu işlemler normalde belirsiz bir zamanda otomatik olarak işletilir. -) - -$(P -Bazı durumlarda sonlandırıcı işlevin programcının istediği bir zamanda işletilmesi gerekebilir. Örneğin, açmış olduğu bir dosyayı sonlandırıcı işlevinde kapatan bir nesnenin sonlandırıcısının hemen işletilmesi gerekebilir. -) - -$(P -$(IX destroy) Buradaki kullanımında "ortadan kaldır" anlamına gelen $(C destroy()), nesnenin sonlandırıcı işlevinin hemen işletilmesini sağlar: -) - ---- - destroy(değişken); ---- - -$(P -$(IX .init) Sonlandırıcı işlevi işlettikten sonra $(C destroy()) değişkene türünün $(C .init) değerini atar. Sınıf değişkenlerinin ilk değeri $(C null) olduğundan nesne o noktadan sonra kullanılamaz. $(C destroy()) yalnızca sonlandırıcı işlevi işletir; belleğin gerçekten ne zaman geri verileceği yine de çöp toplayıcının kararına kalmıştır. -) - -$(P -$(B Uyarı:) $(I Yapı) göstergesiyle kullanıldığında $(C destroy())'a göstergenin kendisi değil, gösterdiği nesne verilmelidir. Yoksa nesnenin sonlandırıcısı çağrılmaz, göstergenin kendisi $(C null) değerini alır: -) - ---- -import std.stdio; - -struct S { - int i; - - this(int i) { - this.i = i; - writefln("%s değerli nesne kuruluyor", i); - } - - ~this() { - writefln("%s değerli nesne sonlanıyor", i); - } -} - -void main() { - auto g = new S(42); - - writeln("destroy()'dan önce"); - destroy($(HILITE g)); // ← YANLIŞ KULLANIM - writeln("destroy()'dan sonra"); - - writefln("g: %s", g); - - writeln("main'den çıkılıyor"); -} ---- - -$(P -$(C destroy())'a gösterge verildiğinde sonlandırılan (yani, türünün $(C .init) değeri verilen) göstergenin kendisidir: -) - -$(SHELL -42 değerli nesne kuruluyor -destroy()'dan önce -destroy()'dan sonra $(SHELL_NOTE_WRONG Bu satırdan önce nesne sonlanmamıştır) -g: null $(SHELL_NOTE_WRONG Onun yerine gösterge null olmuştur) -main'den çıkılıyor -42 değerli nesne sonlanıyor -) - -$(P -Bu yüzden, yapı göstergesiyle kullanıldığında $(C destroy())'a gösterilen nesne verilmelidir: -) - ---- - destroy($(HILITE *g)); // ← Doğru kullanım ---- - -$(P -Sonlandırıcı işlevin bu sefer doğru noktada işletildiğini ve göstergenin değerinin $(C null) olmadığını görüyoruz: -) - -$(SHELL -42 değerli nesne kuruluyor -destroy()'dan önce -42 değerli nesne sonlanıyor $(SHELL_NOTE Nesne doğru noktada sonlanmıştır) -destroy()'dan sonra -g: 7FC5EB4EE200 $(SHELL_NOTE Gösterge null olmamıştır) -main'den çıkılıyor -0 değerli nesne sonlanıyor $(SHELL_NOTE Bir kere de S.init değeriyle) -) - -$(P -Son satır, artık $(C S.init) değerine sahip olan nesnenin sonlandırıcısı bir kez de kapsamdan çıkılırken işletilirken yazdırılmıştır. -) - -$(H5 $(IX kurma, isimle) Nesneyi çalışma zamanında ismiyle kurmak) - -$(P -$(IX factory) $(IX Object) $(C Object) sınıfının $(C factory()) isimli üye işlevi türün ismini parametre olarak alır, o türden bir nesne kurar, ve adresini döndürür. $(C factory()), türün kurucusu için parametre almaz; bu yüzden türün parametresiz olarak kurulabilmesi şarttır: -) - ---- -$(HILITE module deneme;) - -import std.stdio; - -interface Hayvan { - string ses(); -} - -class Kedi : Hayvan { - string ses() { - return "miyav"; - } -} - -class Köpek : Hayvan { - string ses() { - return "hav"; - } -} - -void main() { - string[] kurulacaklar = [ "Kedi", "Köpek", "Kedi" ]; - - Hayvan[] hayvanlar; - - foreach (türİsmi; kurulacaklar) { - /* "Sözde değişken" __MODULE__, her zaman için içinde - * bulunulan modülün ismidir ve bir string olarak - * derleme zamanında kullanılabilir. */ - const tamİsim = __MODULE__ ~ '.' ~ türİsmi; - writefln("%s kuruluyor", tamİsim); - hayvanlar ~= cast(Hayvan)$(HILITE Object.factory)(tamİsim); - } - - foreach (hayvan; hayvanlar) { - writeln(hayvan.ses()); - } -} ---- - -$(P -O programda hiç $(C new) kullanılmadığı halde üç adet $(C Hayvan) nesnesi oluşturulmuş ve $(C hayvanlar) dizisine eklenmiştir: -) - -$(SHELL -deneme.Kedi kuruluyor -deneme.Köpek kuruluyor -deneme.Kedi kuruluyor -miyav -hav -miyav -) - -$(P -$(C Object.factory())'ye türün tam isminin verilmesi gerekir. O yüzden yukarıdaki tür isimleri $(STRING "Kedi") ve $(STRING "Köpek") gibi kısa olarak değil, modülün ismi ile birlikte $(STRING "deneme.Kedi") ve $(STRING "deneme.Köpek") olarak belirtiliyorlar. -) - -$(P -$(C factory)'nin dönüş türü $(C Object)'tir; bu türün yukarıdaki $(C cast(Hayvan)) kullanımında gördüğümüz gibi doğru türe açıkça dönüştürülmesi gerekir. -) - -$(H5 Özet) - -$(UL -$(LI Çöp toplayıcı belleği belirsiz zamanlarda tarar, artık kullanılmayan nesneleri belirler, onları sonlandırır, ve yerlerini geri alır.) - -$(LI Çöp toplayıcının temizlik işlemleri $(C GC.collect), $(C GC.disable), $(C GC.enable), $(C GC.minimize), vs. ile bir ölçüye kadar yönetilebilir.) - -$(LI Çöp toplayıcıdan yer ayırmak için $(C GC.calloc) (ve başka işlevler), ayrılmış olan belleği uzatmak için $(C GC.realloc), geri vermek için de $(C GC.free) kullanılır.) - -$(LI Çöp toplayıcıdan ayrılan belleğin $(C GC.BlkAttr.NO_SCAN), $(C GC.BlkAttr.NO_INTERIOR), vs. olarak işaretlenmesi gerekebilir.) - -$(LI $(C .alignof) türün varsayılan hizalama birimini verir. Sınıf $(I nesneleri) için $(C classInstanceAlignment) kullanılır.) - -$(LI $(C .offsetof) bir üyenin nesnenin başlangıç adresinden kaç bayt sonra olduğunu bildirir.) - -$(LI $(C align) niteliği değişkenlerin, kullanıcı türlerinin, ve üyelerin hizalama birimlerini belirler.) - -$(LI $(C emplace) yapı nesnesi kurarken gösterge, sınıf nesnesi kurarken $(C void[]) alır.) - -$(LI $(C destroy) nesnenin sonlandırıcısını işletir. ($(C destroy)'a yapı göstergesi değil, gösterilen yapı nesnesi verilmelidir.)) - -$(LI $(C Object.factory) uzun ismiyle belirtilen türde nesne kurar.) - -) - -macros: - SUBTITLE=Bellek Yönetimi - - DESCRIPTION=Bellek, ve derleyici veya programcı tarafından kullanılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial bellek new destroy clear - -SOZLER= -$(bellek_sizintisi) -$(cagri_yigiti) -$(calisma_ortami) -$(cikarsama) -$(cokme) -$(cop_toplayici) -$(doldurma_bayti) -$(evrensel) -$(hizalama) -$(isletim_dizisi) -$(kapsam) -$(mikro_islemci) -$(siga) -$(sonlandirma) -$(statik) -$(tanimsiz_davranis) -$(yasam_sureci) -$(yazmac) diff --git a/ddili/src/ders/d/birim_testler.cozum.d b/ddili/src/ders/d/birim_testler.cozum.d deleted file mode 100644 index 6a1aacc..0000000 --- a/ddili/src/ders/d/birim_testler.cozum.d +++ /dev/null @@ -1,190 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Birim Testleri) - -$(P -Programı önce bu haliyle başlatıyor ve hata atıldığından emin oluyoruz: -) - -$(SHELL -$ dmd deneme.d -ofdeneme -w -unittest -$ ./deneme -$(DARK_GRAY core.exception.AssertError@deneme(11): unittest failure) -) - -$(P -Böylece testlerin çalıştığından eminiz; bizi ileride yapılabilecek hatalara karşı koruyacaklar. Bu durumdaki hata mesajındaki satır numarasına (11) bakarak, birim testlerinden ilkinin başarısız olduğunu görüyoruz. -) - -$(P -Şimdi, göstermek amacıyla bilerek hatalı bir gerçekleştirmesini deneyelim. Bu gerçekleştirme, özel harfe hiç dikkat etmez ve girilen dizinin aynısını döndürür: -) - ---- -dstring harfBaşa(dstring dizgi, in dchar harf) { - dstring sonuç; - - foreach (eleman; dizgi) { - sonuç ~= eleman; - } - - return sonuç; -} - -unittest { - immutable dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} - -void main() { -} ---- - -$(P -Bu sefer birinci $(C assert) denetimi $(I tesadüfen) başarılı olur, ama ikincisi hata atar: -) - -$(SHELL -$ ./deneme -$(DARK_GRAY core.exception.AssertError@deneme($(HILITE 17)): unittest failure) -) - -$(P -Tesadüfün nedeni, o hatalı gerçekleştirmede "merhaba" girildiği zaman yine "merhaba" döndürülmesi ve birim testinin beklentisine uymasıdır. Tekrar deneyelim: -) - ---- -dstring harfBaşa(dstring dizgi, in dchar harf) { - dstring başTaraf; - dstring sonTaraf; - - foreach (eleman; dizgi) { - if (eleman == harf) { - başTaraf ~= eleman; - - } else { - sonTaraf ~= eleman; - } - } - - return başTaraf ~ sonTaraf; -} - -unittest { - immutable dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} - -void main() { -} ---- - -$(P -Şimdi testlerin tümü geçer: -) - -$(SHELL -$ ./deneme -$ -) - -$(P -Artık bu noktadan sonra güvendeyiz; işlevi, testlerine güvenerek istediğimiz gibi değiştirebiliriz. Aşağıda iki farklı gerçekleştirmesini daha görüyorsunuz. Bunların ikisi de aynı testlerden geçerler. -) - -$(UL - -$(LI -$(C std.algorithm) modülündeki $(C partition) işlevini kullanan bir gerçekleştirme: - ---- -import std.algorithm; - -dstring harfBaşa(dstring dizgi, in dchar harf) { - dchar[] sonuç = dizgi.dup; - partition!(e => e == harf, SwapStrategy.stable)(sonuç); - - return sonuç.idup; -} - -unittest { - immutable dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} - -void main() { -} ---- - -$(P -$(I Not: Yukarıdaki programda kullanılan ve isimsiz işlev oluşturmaya yarayan $(C =>) söz dizimini daha sonraki bölümlerde göreceğiz.) -) - -) - -$(LI -Önce özel harften kaç tane bulunduğunu sayan bir gerçekleştirme... Bu, sonucun baş tarafını daha sonra $(C tekrarlıDizi) isimli başka bir işleve yaptırıyor. Sağlam programcılık gereği, o işlevin de kendi birim testleri yazılmış: - ---- -dstring tekrarlıDizi(int adet, dchar harf) { - dstring dizi; - - foreach (i; 0..adet) { - dizi ~= harf; - } - - return dizi; -} - -unittest { - assert(tekrarlıDizi(3, 'z') == "zzz"); - assert(tekrarlıDizi(10, 'ğ') == "ğğğğğğğğğğ"); -} - -dstring harfBaşa(dstring dizgi, in dchar harf) { - int özelHarfAdedi; - dstring sonTaraf; - - foreach (eleman; dizgi) { - if (eleman == harf) { - ++özelHarfAdedi; - - } else { - sonTaraf ~= eleman; - } - } - - return tekrarlıDizi(özelHarfAdedi, harf) ~ sonTaraf; -} - -unittest { - immutable dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} - -void main() { -} ---- - -) - -) - -Macros: - SUBTITLE=Birim Testleri - - DESCRIPTION=D dilinin kod güvenilirliğini arttıran olanağı 'birim testleri' [unit tests] problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial birim test testler unit test unittest problem çözüm diff --git a/ddili/src/ders/d/birim_testler.d b/ddili/src/ders/d/birim_testler.d deleted file mode 100644 index beee1ee..0000000 --- a/ddili/src/ders/d/birim_testler.d +++ /dev/null @@ -1,519 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX birim testleri) $(IX test) Birim Testleri) - -$(P -Programcılığın kaçınılmaz uğraşlarından birisi hata ayıklamaktır. -) - -$(P -$(IX hata, nedenleri) Her kullanıcının yakından tanıdığı gibi, içinde bilgisayar programı çalışan her cihaz yazılım hataları içerir. Yazılım hataları, kol saati gibi basit elektronik aletlerden uzay aracı gibi büyük sistemlere kadar her yerde bulunur. -) - -$(H5 Hata nedenleri) - -$(P -Yazılım hatalarının çok çeşitli nedenleri vardır. Programın fikir aşamasından başlayarak kodlanmasına doğru kabaca sıralarsak: -) - -$(UL - -$(LI -Programdan istenenler açık bir şekilde ortaya konmamış olabilir. Hatta, belki de programın tam olarak ne yapacağı başından belli değildir. -) - -$(LI -Programcı programdan istenenleri yanlış anlamış olabilir. -) - -$(LI -Programlama dili programdan istenenleri ifade etmekte yetersiz kalabilir. Bir insana Türkçe anlatırken bile anlaşmazlıklar yaşandığını göz önüne alırsak, bilgisayar dilinin karmaşık söz dizimleri ve kuralları istenenlerin tam olarak ifade edilmesi için yeterli olmayabilir. -) - -$(LI -Programcının varsayımları yanlış çıkabilir. Örneğin, pi sayısı olarak 3.14 değerinin yeterli olduğu varsayılmış olabilir. -) - -$(LI -Programcının bilgisi herhangi bir konuda yetersiz veya yanlış olabilir. Örneğin, kesirli sayıların eşitlik karşılaştırmalarında kullanılmalarının güvensiz olduğunu bilmiyordur. -) - -$(LI -Program baştan düşünülmemiş olan bir durumla karşılaşabilir. Örneğin, bir klasördeki dosyalardan birisi program o listeyi bir döngüde kullanırken silinmiş veya o dosyanın ismi değiştirilmiş olabilir. -) - -$(LI -Programcı kodu yazarken dikkatsizlik yapabilir. Örneğin, bir işlem sırasında $(C toplamFiyat) yerine $(C toptanFiyat) yazabilir. -) - -$(LI vs.) - -) - -$(P -Ne yazık ki, günümüzde henüz tam olarak sağlam kod üreten yazılım geliştirme yöntemleri bulunamamıştır. Bu konu, sürekli olarak çözüm bulunmaya çalışılan ve her beş on yılda bir ümit verici yöntemlerin ortaya çıktığı bir konudur. -) - -$(H5 Hatanın farkedildiği zaman) - -$(P -Yazılım hatasının ne zaman farkına varıldığı da çeşitlilik gösterir. En erkenden en geçe doğru sıralayarak: -) - -$(UL -$(LI Kod yazılırken - -$(UL -$(LI Programı yazan kişi tarafından -) - -$(LI Başka bir programcı tarafından; örneğin $(I çiftli programlama) $(ASIL pair programming) yöntemi uygulandığında, yapılan bir yazım hatasını programı yazan kişinin yanındaki programcı farkedebilir -) - -$(LI Derleyici tarafından; derleyicinin verdiği hata mesajları veya uyarılar çoğunlukla programcı hatalarını gösterirler -) - -$(LI Programın programcı tarafından oluşturulması sırasında $(B birim testleri) tarafından -) -) - -) - -$(LI Kod incelenirken - -$(UL -$(LI Kaynak kodu inceleyen araç programlar tarafından -) - -$(LI Kodu inceleyen başka programcılar tarafından $(I kod incelemesi) $(ASIL code review) sırasında -) -) - -) - -$(LI Program kullanımdayken - -$(UL -$(LI Programın işleyişini inceleyen araç programlar tarafından (örneğin Linux ortamlarındaki açık kodlu 'valgrind' programı ile) -) - -$(LI Sürümden önce test edilirken, ya $(C assert) denetimlerinin başarısızlığından ya da programın gözlemlenen davranışından -) - -$(LI Sürümden önce $(I beta) kullanıcıları tarafından test edilirken -) - -$(LI Sürümdeyken son kullanıcılar tarafından) - -) - -) - -) - -$(P -Hata ne kadar erken farkedilirse hem zararı o kadar az olur, hem de o kadar az sayıda insanın zamanını almış olur. Bu yüzden en iyisi, hatanın kodun yazıldığı sırada yakalanmasıdır. Geç farkedilen hata ise başka programcıların, programı test edenlerin, ve çok sayıdaki kullanıcının da zamanını alır. -) - -$(P -Son kullanıcıya gidene kadar farkedilmemiş olan bir hatanın kodun hangi noktasından kaynaklandığını bulmak da çoğu durumda oldukça zordur. Bu noktaya kadar farkedilmemiş olan bir hata, bazen aylarca sürebilen uğraşlar sonucunda temizlenebilir. -) - -$(H5 Hata yakalamada birim testleri) - -$(P -Kodu yazan programcı olmazsa zaten kod olmaz. Ayrıca, derlemeli bir dil olduğu için D programları zaten derleyici kullanmadan oluşturulamazlar. Bunları bir kenara bıraktığımızda, program hatalarını yakalamada en erken ve bu yüzden de en etkin yöntem olarak birim testleri kalır. -) - -$(P -Birim testleri, modern programcılığın ayrılmaz araçlarındandır. Kod hatalarını azaltma konusunda en etkili yöntemlerdendir. Birim testleri olmayan kod, hatalı kod olarak kabul edilir. -) - -$(P -Ne yazık ki bunun tersi doğru değildir: birim testlerinin olması, kodun hatasız olduğunu kanıtlamaz; ama hata oranını çok büyük ölçüde azaltır. -) - -$(P -Birim testleri ayrıca kodun rahatça ve güvenle geliştirilebilmesini de sağlarlar. Kod üzerinde değişiklik yapmak, örneğin yeni olanaklar eklemek, doğal olarak o kodun eski olanaklarının artık hatalı hale gelmelerine neden olabilir. Kodun geliştirilmesi sırasında ortaya çıkan böyle hatalar, ya çok sonraki sürüm testleri sırasında farkedilirler, ya da daha kötüsü, program son kullanıcılar tarafından kullanılırken. -) - -$(P -Bu tür hatalar kodun yeniden düzenlenmesinden çekinilmesine ve kodun gittikçe $(I çürümesine) $(ASIL code rot) neden olurlar. Örneğin bazı satırların aslında yeni bir işlev olarak yazılmasının gerektiği bir durumda, yeni hatalardan korkulduğu için koda dokunulmaz ve $(I kod tekrarı) gibi zararlı durumlara düşülebilir. -) - -$(P -Programcı kültüründe duyulan "bozuk değilse düzeltme" ("if it isn't broken, don't fix it") gibi sözler, hep bu korkunun ürünüdür. Bu gibi sözler, yazılmış olan koda dokunmamayı erdem olarak gösterdikleri için zaman geçtikçe kodun çürümesine ve üzerinde değişiklik yapılamaz hale gelmesine neden olurlar. -) - -$(P -Modern programcılıkta bu düşüncelerin yeri yoktur. Tam tersine, kod çürümesinin önüne geçmek için kodun gerektikçe serbestçe geliştirilmesi önerilir: "acımasızca geliştir" ("refactor mercilessly"). İşte bu yararlı yaklaşımın en güçlü silahı birim testleridir. -) - -$(P -Birim testi, programı oluşturan en alt birimlerin birbirlerinden olabildiğince bağımsız olarak test edilmeleri anlamına gelir. Alt birimlerin bağımsız olarak testlerden geçmeleri, o birimlerin birlikte çalışmaları sırasında oluşacak hataların olasılığını büyük ölçüde azaltır. Eğer parçalar doğru çalışıyorsa, bütünün de doğru çalışma olasılığı artar. -) - -$(P -Birim testleri başka bazı dillerde JUnit, CppUnit, Unittest++, vs. gibi kütüphane olanakları olarak gerçekleştirilmişlerdir. D'de ise birim testleri dilin iç olanakları arasındadır. Her iki yaklaşımın da üstün olduğu yanlar gösterilebilir. D birim testleri konusunda bazı kütüphanelerin sunduğu bazı olanakları içermez. Bu yüzden birim testleri için ayrı bir kütüphaneden yararlanmak da düşünülebilir. -) - -$(P -D'de birim testleri, önceki bölümde gördüğümüz $(C assert) denetimlerinin $(C unittest) blokları içinde kullanılmalarından oluşurlar. Ben burada yalnızca D'nin bu iç olanağını göstereceğim. -) - -$(H5 $(IX -unittest, derleyici seçeneği) Birim testlerini başlatmak) - -$(P -Programın asıl işleyişi ile ilgili olmadıkları için, birim testlerinin yalnızca programın geliştirilmesi aşamasında çalıştırılmaları gerekir. Birim testleri derleyici veya geliştirme ortamı tarafından, ve ancak özellikle istendiğinde başlatılır. -) - -$(P -Birim testlerinin nasıl başlatıldıkları kullanılan derleyiciye ve geliştirme ortamına göre değişir. Ben burada örnek olarak Digital Mars'ın derleyicisi olan $(C dmd)'nin $(C ‑unittest) seçeneğini göstereceğim. -) - -$(P -Programın $(C deneme.d) isimli bir kaynak dosyaya yazıldığını varsayarsak komut satırına $(C ‑unittest) seçeneğini eklemek birim testlerini etkinleştirmek için yeterlidir: -) - -$(SHELL -dmd deneme.d -w $(HILITE -unittest) -) - -$(P -Bu şekilde oluşturulan program çalıştırıldığında önce birim testleri işletilir ve ancak onlar başarıyla tamamlanmışsa programın işleyişi $(C main) ile devam eder. -) - -$(H5 $(IX unittest) $(C unittest) blokları) - -$(P -Birim testlerini oluşturan kodlar bu blokların içine yazılır. Bu kodların programın normal işleyişi ile ilgileri yoktur; yalnızca programı ve özellikle işlevleri denemek için kullanılırlar: -) - ---- -unittest { - /* ... birim testleri ve testler için gereken kodlar ... */ -} ---- - -$(P -$(C unittest) bloklarını sanki işlev tanımlıyor gibi kendi başlarına yazabilirsiniz. Ama daha iyisi, bu blokları denetledikleri işlevlerin hemen altına yazmaktır. -) - -$(P -Örnek olarak, bir önceki bölümde gördüğümüz ve kendisine verilen sayıya Türkçe ses uyumuna uygun olarak $(I da eki) döndüren işleve bakalım. Bu işlevin doğru çalışmasını denetlemek için, $(C unittest) bloğuna bu işlevin döndürmesini beklediğimiz koşullar yazarız: -) - ---- -dstring daEki(in int sayı) { - // ... -} - -$(HILITE unittest) { - assert(daEki(1) == "de"); - assert(daEki(5) == "te"); - assert(daEki(9) == "da"); -} ---- - -$(P -Oradaki üç koşul; 1, 5, ve 9 sayıları için sırasıyla "de", "te", ve "da" döndürüldüğünü denetler. -) - -$(P -Her ne kadar testlerin temeli $(C assert) denetimleri olsa da, $(C unittest) bloklarının içinde her türlü D olanağını kullanabilirsiniz. Örneğin, bir dizgi içindeki belirli bir harfi o dizginin en başında olacak şekilde döndüren bir işlevin testleri şöyle yazılabilir: -) - ---- -dstring harfBaşa(dstring dizgi, in dchar harf) { - // ... -} - -unittest { - immutable dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} ---- - -$(P -Oradaki üç $(C assert) denetimi $(C harfBaşa) işlevinin nasıl çalışmasının beklendiğini denetliyorlar. -) - -$(P -Bu örneklerde görüldüğü gibi, birim testleri aynı zamanda işlevlerin belgeleri ve örnek kodları olarak da kullanışlıdırlar. Yalnızca birim testine bakarak işlevin kullanılışı hakkında hızlıca fikir edinebiliriz. -) - -$(H5 $(IX assertThrown, std.exception) $(IX assertNotThrown, std.exception) Hata atılıp atılmadığının denetlenmesi) - -$(P -Kodun belirli durumlar karşısında hata atıp atmadığının da denetlenmesi gerekebilir. $(C std.exception) modülü bu konuda yardımcı olan iki işlev içerir: -) - -$(UL - -$(LI $(C assertThrown): Belirli bir hata türünün atıldığını denetler) - -$(LI $(C assertNotThrown): Belirli bir hata türünün atıl$(I ma)dığını denetler) - -) - -$(P -Örneğin, iki dilim parametresinin eşit uzunlukta olduğunu şart koşan ve boş dilimlerle de hatasız çalışması gereken bir işlev aşağıdaki gibi denetlenebilir: -) - ---- -import std.exception; - -int[] ortalama(int[] a, int[] b) { - // ... -} - -unittest { - /* Eşit uzunluklu olmayan dilimlerde hata atılmalıdır */ - assertThrown(ortalama([1], [1, 2])); - - /* Boş dilimlerde hata atılmamalıdır */ - assertNotThrown(ortalama([], [])); -} ---- - -$(P -$(C assertThrown) normalde türüne bakmaksızın herhangi bir hatanın atıldığını denetler; gerektiğinde özel bir hata türünün atıldığını da denetleyebilir. Benzer biçimde, $(C assertNotThrown) da normalde hiçbir hatanın atılmadığını denetler ama gerektiğinde o da belirli bir hata türünün atılmadığını denetleyebilir. Özel hata türü bu işlevlere şablon parametresi olarak bildirilir: -) - ---- - /* Eşit uzunluklu olmayan dilimlerde UzunlukHatası - * atılmalıdır */ - assertThrown$(HILITE !UzunlukHatası)(ortalama([1], [1, 2])); - - /* Boş dilimlerde RangeError atılmamalıdır (yine de başka - * türden hata atılabilir) */ - assertNotThrown$(HILITE !RangeError)(ortalama([], [])); ---- - -$(P -Şablonları $(LINK2 /ders/d/sablonlar.html, ilerideki bir bölümde) göreceğiz. -) - -$(P -Bu işlevlerin temel amacı kodu kısaltmak ve okunurluğu arttırmaktır. Yoksa, aşağıdaki $(C assertThrown) satırı aslında hemen altındaki uzun kodun eşdeğeridir: -) - ---- - assertThrown(ortalama([1], [1, 2])); - -// ... - - /* Yukarıdaki satırın eşdeğeri */ - { - auto atıldı_mı = false; - - try { - ortalama([1], [1, 2]); - - } catch (Exception hata) { - atıldı_mı = true; - } - - assert(atıldı_mı); - } ---- - -$(H5 $(IX TDD) $(IX test yönelimli programlama) Test yönelimli programlama: $(I önce test, sonra kod)) - -$(P -Modern programcılık yöntemlerinden olan $(I test yönelimli programlama) ("test driven development" - TDD), birim testlerinin kod yazılmadan $(I önce) yazılmasını öngörür. Bu yöntemde asıl olan birim testleridir. Kodun yazılması, birim testlerinin başarıya ulaşmalarını sağlayan ikincil bir uğraştır. -) - -$(P -Yukarıdaki $(C daEki) işlevine bu bakış açısıyla yaklaşarak onu önce birim testleriyle şöyle yazmamız gerekir: -) - ---- -dstring daEki(in int sayı) { - return "bilerek hatalı"; -} - -unittest { - assert(daEki(1) == "de"); - assert(daEki(5) == "te"); - assert(daEki(9) == "da"); -} - -void main() { -} ---- - -$(P -Her ne kadar o işlevin hatalı olduğu açık olsa da, önce programın birim testlerinin doğru olarak çalıştıklarını, yani beklendiği gibi hata attıklarını görmek isteriz: -) - -$(SHELL -$ dmd deneme.d -w -O -unittest -$ ./deneme -$(DARK_GRAY core.exception.AssertError@deneme(8): $(HILITE unittest failure)) -) - -$(P -İşlev ancak ondan sonra ve bu testleri geçecek şekilde yazılır: -) - ---- -$(CODE_NAME daEki)dstring daEki(in int sayı) { - dstring ek; - - immutable sonHane = sayı % 10; - - final switch (sonHane) { - - case 1: - case 2: - case 7: - case 8: - ek = "de"; - break; - - case 3: - case 4: - case 5: - ek = "te"; - break; - - case 6: - case 9: - case 0: - ek = "da"; - break; - } - - return ek; -} - -unittest { - assert(daEki(1) == "de"); - assert(daEki(5) == "te"); - assert(daEki(9) == "da"); -} - -void main() { -} ---- - -$(P -Artık program bu testleri geçer, ve bizim de $(C daEki) işlevi konusunda güvenimiz gelişir. Bu işlevde daha sonradan yapılacak olası geliştirmeler, $(C unittest) bloğuna yazdığımız koşulları korumak zorundadırlar. Böylelikle kodu geliştirmeye güvenle devam edebiliriz. -) - -$(H5 Bazen de $(I önce hata, sonra test, ve en sonunda kod)) - -$(P -Birim testleri bütün durumları kapsayamazlar. Örneğin yukarıdaki testlerde üç farklı eki üreten üç sayı değeri seçilmiş, ve $(C daEki) işlevi bu üç testten geçtiği için başarılı kabul edilmiştir. -) - -$(P -Bu yüzden, her ne kadar çok etkili yöntemler olsalar da, birim testleri bütün hataları yakalayamazlar ve bazı hatalar bazen son kullanıcılara kadar saklı kalabilir. -) - -$(P -$(C daEki) işlevi için bunun örneğini $(C assert) bölümünün problemlerinde de görmüştük. O problemde olduğu gibi, bu işlev 50 gibi bir değer geldiğinde hatalıdır: -) - ---- -$(CODE_XREF daEki)import std.stdio; - -void main() { - writefln("%s'%s", 50, daEki(50)); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -$ ./deneme -$(DARK_GRAY 50'da) -) - -$(P -İşlev yalnızca son haneye baktığı için 50 için "de" yerine hatalı olarak "da" döndürmektedir. -) - -$(P -Test yönelimli programlama işlevi hemen düzeltmek yerine öncelikle bu hatalı durumu yakalayan bir birim testinin eklenmesini önerir. Çünkü hatanın birim testlerinin gözünden kaçarak programın kullanımı sırasında ortaya çıkmış olması, birim testlerinin bir yetersizliği olarak görülür. Buna uygun olarak bu durumu yakalayan bir test örneğin şöyle yazılabilir: -) - ---- -unittest { - assert(daEki(1) == "de"); - assert(daEki(5) == "te"); - assert(daEki(9) == "da"); - $(HILITE assert(daEki(50) == "de");) -} ---- - -$(P -Program bu sefer bu birim testi denetimi nedeniyle sonlanır: -) - -$(SHELL -$ ./deneme -$(DARK_GRAY core.exception.AssertError@deneme(39): unittest failure) -) - -$(P -Artık bu hatalı durumu denetleyen bir test bulunduğu için, işlevde ileride yapılabilecek geliştirmelerin tekrardan böyle bir hataya neden olmasının önüne geçilmiş olur. -) - -$(P -Kod ancak bu birim testi yazıldıktan sonra, ve o testi geçirmek için yazılır. -) - -$(P $(I Not: Bu işlev, sonu "bin" ve "milyon" gibi okunarak biten başka sayılarla da sorunlu olduğu için burada kapsamlı bir çözüm bulmaya çalışmayacağım.) -) - -$(PROBLEM_TEK - -$(P -Yukarıda sözü geçen $(C harfBaşa) işlevini, birim testlerini geçecek şekilde gerçekleştirin: -) - ---- -dstring harfBaşa(dstring dizgi, in dchar harf) { - dstring sonuç; - return sonuç; -} - -unittest { - dstring dizgi = "merhaba"d; - - assert(harfBaşa(dizgi, 'm') == "merhaba"); - assert(harfBaşa(dizgi, 'e') == "emrhaba"); - assert(harfBaşa(dizgi, 'a') == "aamerhb"); -} - -void main() { -} ---- - -$(P -O tanımdan başlayın; ilk test yüzünden hata atıldığını görün; ve işlevi hatayı giderecek şekilde yazın. -) - -) - -Macros: - SUBTITLE=Birim Testleri - - DESCRIPTION=D dilinin kod güvenilirliğini arttıran olanağı 'birim testleri' [unit tests] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial birim testi testler unit test unittest - -SOZLER= -$(birim_testi) -$(blok) -$(derleyici) -$(ic_olanak) -$(ifade) -$(kutuphane) diff --git a/ddili/src/ders/d/birlikler.d b/ddili/src/ders/d/birlikler.d deleted file mode 100644 index 0a1a3bc..0000000 --- a/ddili/src/ders/d/birlikler.d +++ /dev/null @@ -1,414 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX union) $(IX birlik) Birlikler) - -$(P -Birlikler, birden fazla üyenin aynı bellek alanını paylaşmalarını sağlarlar. D'ye C dilinden geçmiş olan alt düzey bir olanaktır. -) - -$(P -İki fark dışında yapılarla aynı şekilde kullanılır: -) - -$(UL - -$(LI $(C struct) yerine $(C union) anahtar sözcüğü ile tanımlanır) - -$(LI üyeleri aynı bellek alanını paylaşırlar; birbirlerinden bağımsız değillerdir) - -) - -$(P -Yapılar gibi, birliklerin de üye işlevleri bulunabilir. -) - -$(P -$(IX -m32, derleyici seçeneği) Aşağıdaki örnek programlar derlendikleri ortamın 32 bit veya 64 bit olmasına bağlı olarak farklı sonuçlar üreteceklerdir. Bu yüzden, bu bölümdeki programları derlerken $(C -m32) derleyici seçeneğini kullanmanızı öneririm. Aksi taktirde sizin sonuçlarınız aşağıda gösterilenlerden farklı olabilir. -) - -$(P -Şimdiye kadar çok karşılaştığımız yapı türlerinin kullandıkları bellek alanı bütün üyelerini barındıracak kadar büyüktü: -) - ---- -struct Yapı { - int i; - double d; -} - -// ... - - writeln(Yapı.sizeof); ---- - -$(P -Dört baytlık $(C int)'ten ve sekiz baytlık $(C double)'dan oluşan o yapının büyüklüğü 12'dir: -) - -$(SHELL_SMALL -12 -) - -$(P -Aynı şekilde tanımlanan bir birliğin büyüklüğü ise, üyeleri aynı bellek bölgesini paylaştıkları için, üyelerden en büyüğü için gereken yer kadardır: -) - ---- -$(HILITE union) Birlik { - int i; - double d; -} - -// ... - - writeln(Birlik.sizeof); ---- - -$(P -Dört baytlık $(C int) ve sekiz baytlık $(C double) aynı alanı paylaştıkları için bu birliğin büyüklüğü en büyük üye için gereken yer kadardır: -) - -$(SHELL_SMALL -8 -) - -$(P -Bunun bellek kazancı sağlayan bir olanak olduğunu düşünmeyin. Aynı bellek alanına birden fazla veri sığdırmak olanaksızdır. Birliklerin yararı, aynı bölgenin farklı zamanlarda farklı türden veriler için kullanılabilmesidir. Belirli bir anda ancak tek üyenin değerine güvenilebilir. Buna rağmen, her ortamda aynı şekilde çalışmasa da, birliklerin yararlarından birisi, geçerli olan verinin parçalarına diğer üyeler yoluyla erişilebilmesidir. -) - -$(P -Aşağıdaki örneklerden birisi, geçerli üye dışındakilere erişimin $(C typeid)'den yararlanılarak nasıl engellenebileceğini göstermektedir. -) - -$(P -Yukarıdaki birliği oluşturan sekiz baytın bellekte nasıl durduklarını ve üyeler için nasıl kullanıldıklarını şöyle gösterebiliriz: -) - -$(MONO - 0 1 2 3 4 5 6 7 -───┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬─── - │$(HILITE <─── int için 4 bayt ───>) │ - │$(HILITE <─────────────── double için 8 bayt ────────────────>)│ -───┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴─── -) - -$(P -Ya sekiz baytın hepsi birden $(C double) üye için kullanılır, ya da ilk dört bayt $(C int) üye için kullanılır ve gerisine dokunulmaz. -) - -$(P -Ben örnek olarak iki üye kullandım; birlikleri istediğiniz kadar üye ile tanımlayabilirsiniz. Üyelerin hepsi aynı alanı paylaşırlar. -) - -$(P -Aynı bellek bölgesinin kullanılıyor olması ilginç sonuçlar doğurabilir. Örneğin, birliğin bir $(C int) ile ilklenmesi ama bir $(C double) olarak kullanılması, baştan kestirilemeyecek $(C double) değerleri verebilir: -) - ---- - auto birlik = Birlik(42); // int üyenin ilklenmesi - writeln(birlik.d); // double üyenin kullanılması ---- - -$(P -$(C int) üyeyi oluşturan dört baytın 42 değerini taşıyacak şekilde kurulmaları, $(C double) üyenin değerini de etkiler: -) - -$(SHELL_SMALL -4.9547e-270 -) - -$(P -Mikro işlemcinin bayt sıralarına bağlı olarak $(C int) üyeyi oluşturan dört bayt bellekte 0|0|0|42, 42|0|0|0, veya daha başka bir düzende bulunabilir. Bu yüzden yukarıdaki $(C double) üyenin değeri başka ortamlarda daha farklı da olabilir. -) - -$(H5 $(IX isimsiz birlik) İsimsiz birlikler) - -$(P -İsimsiz birlikler, içinde bulundukları bir yapının hangi üyelerinin paylaşımlı olarak kullanıldıklarını belirlerler: -) - ---- -struct BirYapı { - int birinci; - - union { - int ikinci; - int üçüncü; - } -} - -// ... - - writeln(BirYapı.sizeof); ---- - -$(P -Yukarıdaki yapının son iki üyesi aynı alanı paylaşırlar ve bu yüzden yapı, toplam iki $(C int)'in büyüklüğü kadar yer tutar. Birlik üyesi olmayan $(C birinci) için gereken 4 bayt, ve $(C ikinci) ile $(C üçüncü)'nün paylaştıkları 4 bayt: -) - -$(SHELL_SMALL -8 -) - -$(H5 Başka bir türün baytlarını ayrıştırmak) - -$(P -Birlikler, türleri oluşturan baytlara teker teker erişmek için kullanılabilirler. Örneğin aslında 32 bitten oluşan IPv4 adreslerinin 4 bölümünü elde etmek için bu 32 biti paylaşan 4 baytlık bir dizi kullanılabilir. Adres değerini oluşturan üye ve dört bayt bir birlik olarak şöyle bir araya getirilebilir: -) - ---- -$(CODE_NAME IpAdresi)union IpAdresi { - uint değer; - ubyte[4] baytlar; -} ---- - -$(P -O birliği oluşturan iki üye, aynı belleği şu şekilde paylaşırlar: -) - -$(MONO - 0 1 2 3 -───┬────────────┬────────────┬────────────┬────────────┬─── - │$(HILITE <─────── IPv4 adresini oluşturan 32 bit ────────>)│ - │ baytlar[0] │ baytlar[1] │ baytlar[2] │ baytlar[3] │ -───┴────────────┴────────────┴────────────┴────────────┴─── -) - -$(P -Bu birlik, daha önceki bölümlerde 192.168.1.2 adresinin değeri olarak karşılaştığımız 0xc0a80102 ile ilklendiğinde, $(C baytlar) dizisinin elemanları teker teker adresin dört bölümüne karşılık gelirler: -) - ---- -$(CODE_XREF IpAdresi)void main() { - auto adres = IpAdresi(0xc0a80102); - writeln(adres$(HILITE .baytlar)); -} ---- - -$(P -Adresin bölümleri, bu programı denediğim ortamda alışık olunduğundan ters sırada çıkmaktadır: -) - -$(SHELL_SMALL -[2, 1, 168, 192] -) - -$(P -Bu, programı çalıştıran mikro işlemcinin küçük soncul olduğunu gösterir. Başka ortamlarda başka sırada da çıkabilir. -) - -$(P -Bu örnekte özellikle belirtmek istediğim, birlik üyelerinin değerlerinin belirsiz olabilecekleridir. Birlikler, ancak ve ancak tek bir üyeleri ile kullanıldıklarında beklendiği gibi çalışırlar. Hangi üyesi ile kurulmuşsa, birlik nesnesinin yaşamı boyunca o üyesi ile kullanılması gerekir. O üye dışındaki üyelere erişildiğinde ne tür değerlerle karşılaşılacağı ortamdan ortama farklılık gösterebilir. -) - -$(P -$(IX bswap, std.bitop) $(IX endian, std.system) Bu bölümle ilgisi olmasa da, $(C core.bitop) modülünün $(C bswap) işlevinin bu konuda yararlı olabileceğini belirtmek istiyorum. $(C bswap), kendisine verilen $(C uint)'in baytları ters sırada olanını döndürür. $(C std.system) modülündeki $(C endian) değerinden de yararlanırsak, küçük soncul bir ortamda olduğumuzu şöyle belirleyebilir ve yukarıdaki IPv4 adresini oluşturan baytları tersine çevirebiliriz: -) - ---- -import std.system; -import core.bitop; - -// ... - - if (endian == Endian.littleEndian) { - adres.değer = bswap(adres.değer); - } ---- - -$(P -$(C Endian.littleEndian) değeri sistemin küçük soncul olduğunu, $(C Endian.BigEndian) değeri de büyük soncul olduğunu belirtir. Yukarıdaki dönüşüm sonucunda IPv4 adresinin bölümleri alışık olunan sırada çıkacaktır: -) - -$(SHELL_SMALL -[192, 168, 1, 2] -) - -$(P -Bunu yalnızca birliklerle ilgili bir kullanım örneği olarak gösterdim. Normalde IPv4 adresleriyle böyle doğrudan ilgilenmek yerine, o iş için kullanılan bir kütüphanenin olanaklarından yararlanmak daha doğru olur. -) - -$(H5 Örnekler) - -$(H6 Haberleşme protokolü) - -$(P -Bazı protokollerde, örneğin ağ protokollerinde, bazı baytların anlamı başka bir üye tarafından belirleniyor olabilir. Ağ pakedinin daha sonraki bir bölümü, o üyenin değerine göre farklı bir şekilde kullanılıyor olabilir: -) - ---- -struct Adres { - // ... -} - -struct BirProtokol { - // ... -} - -struct BaşkaProtokol { - // ... -} - -enum ProtokolTürü { birTür, başkaTür } - -struct AğPakedi { - Adres hedef; - Adres kaynak; - ProtokolTürü $(HILITE tür); - - $(HILITE union) { - BirProtokol birProtokol; - BaşkaProtokol başkaProtokol; - } - - ubyte[] geriKalanı; -} ---- - -$(P -Yukarıdaki $(C AğPakedi) yapısında hangi protokol üyesinin geçerli olduğu $(C tür)'ün değerinden anlaşılabilir, programın geri kalanı da yapıyı o değere göre kullanır. -) - -$(H6 $(IX korumalı birlik) Korumalı birlik) - -$(P -Korumalı birlik, $(C union) kullanımını güvenli hale getiren bir veri yapısıdır. $(C union)'ın aksine, yalnızca belirli bir anda geçerli olan üyeye erişilmesine izin verir. -) - -$(P -Aşağıdaki, yalnızca $(C int) ve $(C double) türlerini kullanan basit bir korumalı birlik örneğidir. Veri saklamak için kullandığı $(C union) üyesine ek olarak bir de o birliğin hangi üyesinin geçerli olduğunu bildiren bir $(LINK2 /ders/d/object.html, $(C TypeInfo)) üyesi vardır. -) - ---- -$(CODE_NAME Korumalı)import std.stdio; -import std.exception; - -struct Korumalı { -private: - - TypeInfo geçerliTür_; - - union { - int i_; - double d_; - } - -public: - - this(int değer) { - // Bu atama, aşağıdaki nitelik işlevini çağırır - i = değer; - } - - // 'int' üyeyi değiştirir - @property void i(int değer) { - i_ = değer; - geçerliTür_ $(HILITE = typeid(int)); - } - - // 'int' veriyi döndürür - @property int i() const { - enforce(geçerliTür_ $(HILITE == typeid(int)), - "Veri 'int' değil."); - return i_; - } - - this(double değer) { - // Bu atama, aşağıdaki nitelik işlevini çağırır - d = değer; - } - - // 'double' üyeyi değiştirir - @property void d(double değer) { - d_ = değer; - geçerliTür_ $(HILITE = typeid(double)); - } - - // 'double' veriyi döndürür - @property double d() const { - enforce(geçerliTür_ $(HILITE == typeid(double)), - "Veri 'double' değil." ); - return d_; - } - - // Geçerli verinin türünü bildirir - @property const(TypeInfo) tür() const { - return geçerliTür_; - } -} - -unittest { - // 'int' veriyle başlayalım - auto k = Korumalı(42); - - // Geçerli tür 'int' olarak bildirilmelidir - assert(k.tür == typeid(int)); - - // 'int' veri okunabilmelidir - assert(k.i == 42); - - // 'double' veri okunamamalıdır - assertThrown(k.d); - - // 'int' yerine 'double' veri kullanalım - k.d = 1.5; - - // Geçerli tür 'double' olarak bildirilmelidir - assert(k.tür == typeid(double)); - - // Bu sefer 'double' veri okunabilmelidir ... - assert(k.d == 1.5); - - // ... ve 'int' veri okunamamalıdır - assertThrown(k.i); -} ---- - -$(P -$(IX Variant, std.variant) $(IX Algebraic, std.variant) Bunu basit bir örnek olarak kabul edin. Kendi programlarınızda $(C std.variant) modülünde tanımlı olan $(C Algebraic) ve $(C Variant) türlerini kullanmanızı öneririm. Ek olarak, bu örnek $(LINK2 /ders/d/sablonlar.html, şablonlar) ve $(LINK2 /ders/d/katmalar.html, katmalar) gibi diğer D olanaklarından yararlanabilir ve en azından kod tekrarının önüne geçebilirdi. -) - -$(P -Dikkat ederseniz, içinde tuttuğu verinin türünden bağımsız olarak $(C Korumalı) diye tek tür bulunmaktadır. (Öte yandan, şablon kullanan bir gerçekleştirme verinin türünü bir şablon parametresi olarak alabilir ve bunun sonucunda şablonun her farklı parametre değeri için kullanımının farklı bir tür olmasına neden olabilirdi.) $(C Korumalı), bunun sayesinde dizi elemanı türü olarak kullanılabilir ve bunun sonucunda da farklı türden verilerin aynı dizide bir araya getirilmeleri sağlanmış olur. Ancak, kullanıcılar yine de veriye erişmeden önce hangi verinin geçerli olduğundan emin olmak zorundadırlar. Örneğin, aşağıdaki işlev bunun için $(C Korumalı) türünün $(C tür) niteliğinden yararlanmaktadır: -) - ---- -$(CODE_XREF Korumalı)void main() { - Korumalı[] dizi = [ Korumalı(1), Korumalı(2.5) ]; - - foreach (değer; dizi) { - if (değer.tür $(HILITE == typeid(int))) { - writeln("'int' veri kullanıyoruz : ", değer$(HILITE .i)); - - } else if (değer.tür $(HILITE == typeid(double))) { - writeln("'double' veri kullanıyoruz: ", değer$(HILITE .d)); - - } else { - assert(0); - } - } -} ---- - -$(SHELL -'int' veri kullanıyoruz : 1 -'double' veri kullanıyoruz: 2.5 -) - -Macros: - SUBTITLE=Birlikler - - DESCRIPTION=D'nin farklı türden değişkenleri aynı bellek bölgesinde depolamaya olanak veren birlik (union) olanağı - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial birlik union - -SOZLER= -$(alt_duzey) -$(bayt_sirasi) -$(birlik) -$(buyuk_soncul) -$(korumali_birlik) -$(kucuk_soncul) -$(veri_yapilari) -$(yapi) diff --git a/ddili/src/ders/d/bit_islemleri.cozum.d b/ddili/src/ders/d/bit_islemleri.cozum.d deleted file mode 100644 index ada881a..0000000 --- a/ddili/src/ders/d/bit_islemleri.cozum.d +++ /dev/null @@ -1,86 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Bit İşlemleri) - -$(OL - -$(LI IPv4 adreslerinin her zaman 4 parçadan oluştuğu bilindiğinden bu kadar kısa bir işlevde sihirli sabitler kullanılabilir. Bunun nedeni, aksi taktirde aralara nokta karakterlerinin yerleştirilmesinin ek bir karmaşıklık getireceğidir. - - ---- -string noktalıOlarak(uint ipAdresi) { - return format("%s.%s.%s.%s", - (ipAdresi >> 24) & 0xff, - (ipAdresi >> 16) & 0xff, - (ipAdresi >> 8) & 0xff, - (ipAdresi >> 0) & 0xff); -} ---- - -$(P -Kullanılan tür işaretsiz bir tür olduğu için soldan her zaman için 0 değerli bitler geleceğini hatırlarsak, 24 bit kaydırıldığında ayrıca maskelemeye gerek yoktur. Ek olarak, sıfır kere kaydırmanın da hiçbir etkisi olmadığından o işlevi biraz daha kısa olarak yazabiliriz: -) - ---- -string noktalıOlarak(uint ipAdresi) { - return format("%s.%s.%s.%s", - ipAdresi >> 24, - (ipAdresi >> 16) & 0xff, - (ipAdresi >> 8) & 0xff, - ipAdresi & 0xff); -} ---- - -$(P -Buna rağmen daha okunaklı olduğu için birinci işlev yeğlenebilir çünkü etkisiz olan işlemler bazı durumlarda zaten derleyici tarafından elenebilir. -) - -) - -$(LI Her bayt IPv4 adresinde bulunduğu yere kaydırılabilir ve bu değerler "$(I veya)"lanabilir: - ---- -uint ipAdresi(ubyte bayt3, // en yüksek değerli bayt - ubyte bayt2, - ubyte bayt1, - ubyte bayt0) { // en düşük değerli bayt - return - (bayt3 << 24) | - (bayt2 << 16) | - (bayt1 << 8) | - (bayt0 << 0); -} ---- - -) - -$(LI Aşağıdaki yöntem bütün bitlerin 1 olduğu değerle başlıyor. Önce bitleri sağa kaydırarak üst bitlerin 0 olmalarını, daha sonra da sola kaydırarak alt bitlerin 0 olmalarını sağlıyor: - ---- -uint maskeYap(int düşükBit, int uzunluk) { - uint maske = uint.max; - maske >>= (uint.sizeof * 8) - uzunluk; - maske <<= düşükBit; - return maske; -} ---- - -$(P -$(C uint.max), bütün bitlerin 1 olduğu değerdir. Onun yerine 0 değerinin tümleyeni de kullanılabilir: -) - ---- - uint maske = ~0; - // ... ---- - -) - -) - -Macros: - SUBTITLE=Bit İşlemleri Problem Çözümleri - - DESCRIPTION=Bit İşlemleri Problem Çözümleri - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial bit işlemleri problem çözüm diff --git a/ddili/src/ders/d/bit_islemleri.d b/ddili/src/ders/d/bit_islemleri.d deleted file mode 100644 index 287d9c6..0000000 --- a/ddili/src/ders/d/bit_islemleri.d +++ /dev/null @@ -1,1096 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX bit işlemi) Bit İşlemleri) - -$(P -Bu bölümde mikro işlemcinin en küçük bilgi birimi olan bitlerle yapılan işlemleri tanıyacağız. Bit işlemleri mikro işlemcinin en temel olanaklarındandır. -) - -$(P -Bu işlemler hem alt düzey programcılık açısından bilinmelidir, hem de parametre olarak $(I bayrak) alan işlevler için gereklidir. Bayrak alan işlevlere özellikle C kütüphanelerinden uyarlanmış olan D kütüphanelerinde rastlanabilir. -) - -$(H5 Verinin en alt düzeyde gerçekleştirilmesi) - -$(P -D gibi bir programlama dili aslında bir soyutlamadır. Program içinde tanımladığımız $(C Öğrenci) gibi bir kullanıcı türünün bilgisayarın iç yapısı ile doğrudan bir ilgisi yoktur. Programlama dillerinin amaçlarından birisi, donanımın anladığı dil ile insanın anladığı dil arasında aracılık yapmaktır. -) - -$(P -Bu yüzden her ne kadar D dilini kullanırken donanımla ilgili kavramlarla ilgilenmek gerekmese de, üst düzey kavramların en alt düzeyde elektronik devre elemanlarına nasıl bağlı olduklarını anlamak önemlidir. Bu konularda başka kaynaklarda çok miktarda bilgi bulabileceğinizi bildiğim için bu başlığı olabildiğince kısa tutacağım. -) - -$(H6 $(IX transistör) Transistör) - -$(P -Modern elektronik aletlerin işlem yapma yetenekleri büyük ölçüde transistör denen elektronik devre elemanı ile sağlanır. Transistörün bir özelliği, devrenin başka tarafındaki sinyallerle kontrol edilebilmesidir. Bir anlamda elektronik devrenin kendi durumundan haberinin olmasını ve kendi durumunu değiştirebilmesini sağlar. -) - -$(P -$(IX bit) Transistörler hem mikro işlemcinin içinde hem de bilgisayarın ana belleğinde çok büyük sayılarda bulunurlar. Programlama dili aracılığıyla ifade ettiğimiz işlemleri ve verileri en alt düzeyde gerçekleştiren elemanlardır. -) - -$(H6 Bit) - -$(P -Bilgisayarlarda en küçük bilgi birimi bittir. Bit en alt düzeyde bir kaç tane transistörün belirli bir düzende bir araya getirilmesi ile gerçekleştirilir ve veri olarak iki farklı değerden birisini depolayabilir: 0 veya 1. Depoladığı veriyi tekrar değiştirilene kadar veya enerji kaynağı kesilene kadar korur. -) - -$(P -$(IX bayt) Bilgisayarlar veriye bit düzeyinde doğrudan erişim sağlamazlar. Bunun nedeni, her bitin adreslenebilmesinin bilgisayarın karmaşıklığını ve maliyetini çok arttıracak olması ve tek bitlik kavramların desteklenmeye değmeyecek kadar nadir olmalarıdır. -) - -$(H6 Bayt) - -$(P -Bayt, birbirleriyle ilişkilendirilen 8 bitin bir araya gelmesinden oluşur. Bilgisayarlarda adreslenebilen, yani ayrı ayrı erişilebilen en küçük veri bayttır. Bellekten tek seferde en az bir bayt veri okunabilir ve belleğe en az bir bayt veri yazılabilir. -) - -$(P -Bu yüzden, yalnızca $(C false) ve $(C true) diye iki farklı değer alan ve aslında tek bitlik bilgi taşıyan $(C bool) türü bile 1 bayt olarak gerçekleştirilir. Bunu $(C bool.sizeof) değerine bakarak kolayca görebiliriz: -) - ---- - writeln(bool.stringof, ' ', bool.sizeof, " bayttır"); ---- - -$(SHELL_SMALL -bool 1 bayttır -) - -$(H6 $(IX yazmaç) Yazmaç) - -$(P -Mikro işlemcinin kendi içinde bulunan depolama ve işlem birimleri yazmaçlardır. Yazmaçlar oldukça kısıtlı ama çok hızlı işlemler sunarlar. -) - -$(P -Yazmaçlar her işlemcinin bit genişliğine bağlı olan sayıda bayttan oluşurlar. Örneğin, 32 bitlik işlemcilerde yazmaçlar 4 bayttan, 64 bitlik işlemcilerde de 8 bayttan oluşur. Yazmaç büyüklüğü hem mikro işlemcinin etkin olarak işleyebildiği bilgi miktarını hem de en fazla ne kadar bellek adresleyebildiğini belirler. -) - -$(P -Programlama dili aracılığıyla gerçekleştirilen her iş eninde sonunda bir veya daha fazla yazmaç tarafından halledilir. -) - -$(H5 $(IX ikili sayı sistemi) İkili sayı sistemi) - -$(P -Günlük hayatta kullandığımız onlu sayı sisteminde 10 rakam vardır: 0123456789. Bilgisayar donanımlarında kullanılan ikili sayı sisteminde ise iki rakam vardır: 0 ve 1. Bu, bitin iki değer alabilmesinden gelir. Bitler örneğin üç farklı değer alabilseler, bilgisayarlar üçlü sayı sistemini kullanırlardı. -) - -$(P -Günlük hayatta kullandığımız sayıların basamakları birler, onlar, yüzler, binler, vs. diye artarak adlandırılır. Örneğin, 1023 gibi bir sayı şöyle ifade edilebilir: -) - -$(MONO -1023 == 1 adet 1000, 0 adet 100, 2 adet 10, ve 3 adet 1 -) - -$(P -Dikkat ederseniz, sola doğru ilerlendiğinde her basamağın değeri 10 kat artmaktadır: 1, 10, 100, 1000, vs. -) - -$(P -Aynı tanımı ikili sayı sistemine taşıyınca, ikili sistemde yazılmış olan sayıların basamaklarının da birler, ikiler, dörtler, sekizler, vs. şeklinde artması gerektiğini görürüz. Yani sola doğru ilerlendiğinde her basamağın değeri 2 kat artmalıdır: 1, 2, 4, 8, vs. Örneğin, 1011 gibi bir $(I ikili) sayı şöyle ifade edilebilir: -) - -$(MONO -1011 == 1 adet 8, 0 adet 4, 1 adet 2, ve 1 adet 1 -) - -$(P -Basamaklar numaralanırken, en sağdaki basamağa (en düşük değerli olan basamağa) $(I 0 numaralı basamak) denir. Buna göre, ikili sayı sisteminde yazılmış olan 32 bitlik işaretsiz bir değerin bütün basamaklarını ve basamak değerlerini şöyle gösterebiliriz: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Basamak
    Numarası

    Değeri
    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 -Yüksek değerli bitlere $(I üst) bit, düşük değerli bitlere $(I alt) bit denir. -) - -$(P -İkili sistemde yazılan hazır değerlerin $(C 0b) ile başladıklarını $(LINK2 /ders/d/hazir_degerler.html, Hazır Değerler bölümünde) görmüştük. İkili sistemde değerler yazarak bu tabloya nasıl uyduklarına bakabiliriz. Okumayı kolaylaştırmak için alt çizgi karakterlerinden de yararlanarak: -) - ---- -import std.stdio; - -void main() { - // 1073741824 4 1 - // ↓ ↓ ↓ - int sayı = 0b_01000000_00000000_00000000_00000101; - writeln(sayı); -} ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -1073741829 -) - -$(P -Dikkat ederseniz, o hazır değerin içinde rakamı 1 olan yalnızca 3 adet basamak vardır. Yazdırılan değerin bu basamakların yukarıdaki tablodaki değerlerinin toplamı olduğunu görüyoruz: 1073741824 + 4 + 1 == 1073741829. -) - -$(H6 $(IX işaret biti) İşaretli türlerin $(I işaret) biti) - -$(P -En üst bit işaretli türlerde sayının artı veya eksi olduğunu bildirmek için kullanılır: -) - ---- - int sayı = 0b_10000000_00000000_00000000_00000000; - writeln(sayı); ---- - -$(SHELL_SMALL --2147483648 -) - -$(P -En üst bitin diğerlerinden bağımsız olduğunu düşünmeyin. Örneğin, yukarıdaki sayı diğer bitlerinin 0 olmalarına bakarak -0 değeri olarak düşünülmemelidir (zaten tamsayılarda -0 diye bir değer yoktur). Bunun ayrıntısına burada girmeyeceğim ve bunun D'nin de kullandığı $(I ikiye tümleyen) sayı gösterimi ile ilgili olduğunu söylemekle yetineceğim. -) - -$(P -Burada önemli olan, yukarıdaki tabloda gösterilen en yüksek 2,147,483,648 değerinin yalnızca $(I işaretsiz) türlerde geçerli olduğunu bilmenizdir. Aynı deneyi $(C uint) ile yaptığımızda tablodaki değeri görürüz: -) - ---- - $(HILITE uint) sayı = 0b_10000000_00000000_00000000_00000000; - writeln(sayı); ---- - -$(SHELL_SMALL -2147483648 -) - -$(P -Bu yüzden, aksine bir neden olmadığı sürece aşağıda gösterilenler gibi bit işlemlerinde her zaman için işaretsiz türler kullanılır: $(C ubyte), $(C uint), ve $(C ulong). -) - -$(H5 $(IX on altılı sayı sistemi) On altılı sayı sistemi) - -$(P -Yukarıdaki hazır değerlerden de görülebileceği gibi, ikili sayı sistemi okunaklı değildir. Hem çok yer kaplar hem de yalnızca 0 ve 1'lerden oluştuğu için okunması ve anlaşılması zordur. -) - -$(P -Daha kullanışlı olduğu için on altılı sayı sistemi yaygınlaşmıştır. -) - -$(P -On altılı sayı sisteminde toplam 16 rakam vardır. Alfabelerde 10'dan fazla rakam bulunmadığı için Latin alfabesinden de 6 harf alınmış ve bu sistemin rakamları olarak 0123456789abcdef kabul edilmiştir. O sıralamadan bekleneceği gibi; a, b, c, d, e, ve f harfleri sırasıyla 10, 11, 12, 13, 14, ve 15 değerlerindedir. abcdef harfleri yerine isteğe bağlı olarak ABCDEF harfleri de kullanılabilir. -) - -$(P -Yukarıdaki sayı sistemlerine benzer biçimde, bu sistemde sola doğru ilerlendiğinde her basamağın değeri 16 kat artar: 1, 16, 256, 4096, vs. Örneğin, on altılı sistemdeki 8 basamaklı bir sayının basamak değerleri şöyledir: -) - - - - - - - - - - -
    Basamak
    Numarası

    Değeri
    7268,435,456
    616,777,216
    51,048,576
    465,536
    34,096
    2256
    116
    01
    - -$(P -On altılı hazır değerlerin $(C 0x) ile yazıldıklarını hatırlayarak bir deneme: -) - ---- - // 1048576 4096 1 - // ↓ ↓ ↓ - uint sayı = 0x_0030_a00f; - writeln(sayı); ---- - -$(SHELL_SMALL -3186703 -) - -$(P -Bunun nedenini sayı içindeki sıfır olmayan basamakların katkılarına bakarak anlayabiliriz: 3 adet 1048576, a adet 4096, ve f adet 1. a'nın 10 ve f'nin 15 olduklarını hatırlayarak hesaplarsak: 3145728 + 40960 + 15 == 3186703. -) - -$(P -On altılı ve ikili sistemde yazılan sayılar kolayca birbirlerine dönüştürülebilirler. On altılı sistemdeki bir sayıyı ikili sisteme dönüştürmek için, sayının her basamağı ikili sistemde dört basamak olarak yazılır. Birbirlerine karşılık gelen değerler şöyledir: -) - - - - - - - - - - - - - - - - - - -
    On altılı İkili Onlu
    000000
    100011
    200102
    300113
    401004
    501015
    601106
    701117
    810008
    910019
    a101010
    b101111
    c110012
    d110113
    e111014
    f111115
    - -$(P -Örneğin, yukarıda kullandığımız 0x0030a00f on altılı değerini ikili olarak şöyle yazabiliriz: -) - ---- - // on altılı: 0 0 3 0 a 0 0 f - uint ikili = 0b_0000_0000_0011_0000_1010_0000_0000_1111; ---- - -$(P -İkili sistemden on altılı sisteme dönüştürmek için de ikili sayının her dört basamağı on altılı sistemde tek basamak olarak yazılır. Yukarıda ilk kullandığımız ikili değer için: -) ---- - // ikili: 0100 0000 0000 0000 0000 0000 0000 0101 - uint on_altılı = 0x___4____0____0____0____0____0____0____5; ---- - -$(H5 Bit işlemleri) - -$(P -Değerlerin bitlerle nasıl ifade edildiklerini ve ikili veya on altılı olarak nasıl yazıldıklarını gördük. Şimdi değerleri bit düzeyinde değiştiren işlemlere geçebiliriz. -) - -$(P -Her ne kadar bit düzeyindeki işlemlerden bahsediyor olsak da, bitlere doğrudan erişilemediğinden bu işlemler en az 8 biti birden etkilemek zorundadırlar. Örneğin, $(C ubyte) türündeki bir ifadenin 8 bitinin hepsi de, ama ayrı ayrı olarak bit işlemine dahil edilir. -) - -$(P -Ben üst bitin özel anlamı nedeniyle işaretli türleri gözardı edeceğim ve bu örneklerde $(C uint) türünü kullanacağım. Siz buradaki işlemleri $(C ubyte) ve $(C ulong) türleriyle, ve işaret bitinin önemini hatırlamak şartıyla $(C byte), $(C int), ve $(C long) türleriyle de deneyebilirsiniz. -) - -$(P -Önce aşağıdaki işlemleri açıklamada yardımcı olacak bir işlev yazalım. Kendisine verilen sayıyı ikili, on altılı, ve onlu sistemde göstersin: -) - ---- -import std.stdio; - -void göster(uint sayı) { - writefln("%032b %08x %10s", sayı, sayı, sayı); -} - -void main() { - göster(123456789); -} ---- - -$(P -Sırasıyla ikili, on altılı, ve onlu: -) - -$(SHELL_SMALL -00000111010110111100110100010101 075bcd15 123456789 -) - -$(H6 $(IX ~, tersini alma) $(IX tersi, bit işleci) Tersini alma işleci $(C ~)) - -$(P -Bu işleç önüne yazıldığı ifadenin bitleri ters olanını üretir. 1 olan bitler 0, 0 olanlar 1 olur: -) - ---- - uint değer = 123456789; - write(" "); göster(değer); - writeln("~ --------------------------------"); - write(" "); göster($(HILITE ~)değer); ---- - -$(P -Bu işlecin etkisi ikili gösteriminde çok kolay anlaşılıyor. Her bit tersine dönmüştür: -) - -$(SHELL_SMALL - 00000111010110111100110100010101 075bcd15 123456789 -~ -------------------------------- - 11111000101001000011001011101010 f8a432ea 4171510506 -) - -$(P -Bu işlecin bit düzeyindeki etkisini şöyle özetleyebiliriz: -) - -$(MONO -~0 → 1 -~1 → 0 -) - -$(H6 $(IX &, ve) $(IX ve, bit işleci) $(I Ve) işleci $(C &)) - -$(P -İki ifadenin arasına yazılır. İki ifadenin aynı numaralı bitlerine sırayla bakılır. Sonuç olarak her iki ifadede de 1 olan bitler için 1 değeri, diğerleri için 0 değeri üretilir. -) - ---- - uint soldaki = 123456789; - uint sağdaki = 987654321; - - write(" "); göster(soldaki); - write(" "); göster(sağdaki); - writeln("& --------------------------------"); - write(" "); göster(soldaki $(HILITE &) sağdaki); ---- - -$(P -Mikro işlemci bu işlemde her iki ifadenin 31, 30, 29, vs. numaralı bitlerini ayrı ayrı kullanır. -) - -$(P -Çıktıda önce soldaki ifadeyi, sonra da sağdaki ifadeyi görüyoruz. Kesikli çizginin altında da bit işleminin sonucu yazdırılıyor: -) - -$(SHELL_SMALL - 00000111010110111100110100010101 075bcd15 123456789 - 00111010110111100110100010110001 3ade68b1 987654321 -& -------------------------------- - 00000010010110100100100000010001 025a4811 39471121 -) - -$(P -Dikkat ederseniz, kesikli çizginin altına yazdırdığım sonuç değerde 1 olan bitler çizginin üstündeki her iki ifadede de 1 değerine sahip olan bitlerdir. -) - -$(P -Bu işleç bu yüzden $(I ve işleci) olarak isimlendirilmiştir: soldaki $(I ve) sağdaki bit 1 olduğunda 1 değerini üretir. Bunu bir tablo ile gösterebiliriz. İki bitin 0 ve 1 oldukları dört farklı durumda ancak iki bitin de 1 oldukları durum 1 sonucunu verir: -) - -$(MONO -0 & 0 → 0 -0 & 1 → 0 -1 & 0 → 0 -1 & 1 → 1 -) - -$(P -Gözlemler: -) - -$(UL -$(LI Bir taraf 0 ise diğer taraftan bağımsız olarak sonuç 0'dır; 0 ile $(I "ve"lemek), $(I sıfırlamak) anlamına gelir.) -$(LI Bir taraf 1 ise sonuç diğerinin değeridir; 1 ile $(I "ve"lemek) etkisizdir.) -) - -$(H6 $(IX |) $(IX veya, bit işleci) $(I Veya) işleci $(C |)) - -$(P -İki ifadenin arasına yazılır. İki ifadenin aynı numaralı bitlerine sırayla bakılır. Her iki ifadede de 0 olan bitlere karşılık 0 değeri üretilir; diğerlerinin sonucu 1 olur: -) - ---- - uint soldaki = 123456789; - uint sağdaki = 987654321; - - write(" "); göster(soldaki); - write(" "); göster(sağdaki); - writeln("| --------------------------------"); - write(" "); göster(soldaki $(HILITE |) sağdaki); ---- - -$(SHELL_SMALL - 00000111010110111100110100010101 075bcd15 123456789 - 00111010110111100110100010110001 3ade68b1 987654321 -| -------------------------------- - 00111111110111111110110110110101 3fdfedb5 1071639989 -) - -$(P -Dikkat ederseniz, sonuçta 0 olan bitler her iki ifadede de 0 olan bitlerdir. Bitin soldaki $(I veya) sağdaki ifadede 1 olması, sonucun da 1 olması için yeterlidir: -) - -$(MONO -0 | 0 → 0 -0 | 1 → 1 -1 | 0 → 1 -1 | 1 → 1 -) - -$(P -Gözlemler: -) - -$(UL -$(LI Bir taraf 0 ise sonuç diğerinin değeridir; 0 ile $(I "veya"lamak) etkisizdir.) -$(LI Bir taraf 1 ise diğer taraftan bağımsız olarak sonuç 1'dir; 1 ile $(I "veya"lamak) 1 yapmak anlamına gelir.) -) - -$(H6 $(IX ^, ya da) $(IX ya da, bit işleci) $(I Ya da) işleci $(C ^)) - -$(P -İki ifadenin arasına yazılır. İki ifadenin aynı numaralı bitlerine sırayla bakılır. İki ifadede farklı olan bitlere karşılık 1 değeri üretilir; diğerlerinin sonucu 0 olur: -) - ---- - uint soldaki = 123456789; - uint sağdaki = 987654321; - - write(" "); göster(soldaki); - write(" "); göster(sağdaki); - writeln("^ --------------------------------"); - write(" "); göster(soldaki $(HILITE ^) sağdaki); ---- - -$(SHELL_SMALL - 00000111010110111100110100010101 075bcd15 123456789 - 00111010110111100110100010110001 3ade68b1 987654321 -^ -------------------------------- - 00111101100001011010010110100100 3d85a5a4 1032168868 -) - -$(P -Dikkat ederseniz, sonuçta 1 olan bitler soldaki ve sağdaki ifadelerde farklı olan bitlerdir. İkisinde de 0 veya ikisinde de 1 olan bitlere karşılık 0 üretilir. -) - -$(MONO -0 ^ 0 → 0 -0 ^ 1 → 1 -1 ^ 0 → 1 -1 ^ 1 → 0 -) - -$(P -Gözlem: -) - -$(UL -$(LI Kendisiyle $(I "ya da"lamak) sıfırlamak anlamına gelir) -) - -$(P -Değeri ne olursa olsun, aynı değişkenin kendisiyle $(I "ya da")lanması 0 sonucunu üretir: -) - ---- - uint değer = 123456789; - - göster(değer ^ değer); ---- - -$(SHELL_SMALL -00000000000000000000000000000000 00000000 0 -) - -$(H6 $(IX >>) $(IX sağa kaydırma, bit işleci) Sağa kaydırma işleci $(C >>)) - -$(P -İfadenin değerini oluşturan bitleri belirtilen sayıda basamak kadar sağa kaydırır. Kaydırılacak yerleri olmayan en sağdaki bitler $(I düşerler) ve değerleri kaybedilir. Sol taraftan yeni gelen bitler işaretsiz türlerde 0 olur. -) - -$(P -Bu örnek bitleri 2 basamak kaydırıyor: -) - ---- - uint değer = 123456789; - göster(değer); - göster(değer $(HILITE >>) 2); ---- - -$(P -Hem sağdan kaybedilecek olan bitleri hem de soldan yeni gelecek olan bitleri işaretli olarak gösteriyorum: -) - -$(SHELL_SMALL -000001110101101111001101000101$(HILITE 01) 075bcd15 123456789 -$(HILITE 00)000001110101101111001101000101 01d6f345 30864197 -) - -$(P -Dikkat ederseniz, alt satırdaki bitler üst satırdaki bitlerin iki bit sağa kaydırılması ile elde edilmiştir. -) - -$(P -$(IX işaret genişletilmesi) Bitler sağa kaydırılırken sol tarafa yeni gelenlerin 0 olduklarını gördünüz. Bu, işaretsiz türlerde böyledir. İşaretli türlerde ise $(I işaret genişletilmesi) (sign extension) denen bir yöntem uygulanır ve sayının en soldaki biti ne ise soldan hep o bitin değerinde bitler gelir. -) - -$(P -Bu etkiyi göstermek için $(C int) türünde ve özellikle üst biti 1 olan bir değer seçelim: -) - ---- - $(HILITE int) değer = 0x80010300; - göster(değer); - göster(değer >> 3); ---- - -$(P -Asıl sayıda üst bit 1 olduğu için yeni gelen bitler de 1 olur: -) - -$(SHELL_SMALL -$(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 111)10000000000000010000001100000 f0002060 4026540128 -) - -$(P -Üst bitin 0 olduğu bir değerde yeni gelen bitler de 0 olur: -) - ---- - $(HILITE int) değer = 0x40010300; - göster(değer); - göster(değer >> 3); ---- - -$(SHELL_SMALL -$(U 0)1000000000000010000001100000$(HILITE 000) 40010300 1073808128 -$(HILITE 000)01000000000000010000001100000 08002060 134226016 -) - -$(H6 $(IX >>>) $(IX işaretsiz sağa kaydırma, bit işleci) İşaretsiz sağa kaydırma işleci $(C >>>)) - -$(P -Bu işleç sağa kaydırma işlecine benzer biçimde çalışır. Tek farkı $(I işaret genişletilmesinin) uygulanmamasıdır. Türden ve en soldaki bitten bağımsız olarak soldan her zaman için 0 gelir: -) - ---- - int değer = 0x80010300; - göster(değer); - göster(değer $(HILITE >>>) 3); ---- - -$(SHELL_SMALL -10000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 000)10000000000000010000001100000 10002060 268443744 -) - -$(H6 $(IX <<) $(IX sola kaydırma, bit işleci) Sola kaydırma işleci $(C <<)) - -$(P -Sağa kaydırma işlecinin tersi olarak bitleri belirtilen basamak kadar sola kaydırır: -) - ---- - uint değer = 123456789; - göster(değer); - göster(değer $(HILITE <<) 4); ---- - -$(P -En soldaki bit değerleri kaybedilir ve sağ taraftan 0 değerli bitler gelir: -) - -$(SHELL_SMALL -$(HILITE 0000)0111010110111100110100010101 075bcd15 123456789 -0111010110111100110100010101$(HILITE 0000) 75bcd150 1975308624 -) - -$(H6 $(IX atamalı bit işleci) Atamalı bit işleçleri) - -$(P -Yukarıdaki işleçlerin ikili olanlarının atamalı karşılıkları da vardır: $(C &=), $(C |=), $(C ^=), $(C >>=), $(C >>>=), ve $(C <<=). $(LINK2 /ders/d/aritmetik_islemler.html, Tamsayılar ve Aritmetik İşlemler bölümünde) gördüğümüz atamalı aritmetik işleçlerine benzer biçimde, bunlar işlemi gerçekleştirdikten sonra sonucu soldaki ifadeye atarlar. -) - -$(P -Örnek olarak $(C &=) işlecini kullanalım: -) - ---- - değer = değer & 123; - değer &= 123; // üsttekiyle aynı şey ---- - -$(H5 Anlamları) - -$(P -Bu işleçlerin bit düzeyinde nasıl işledikleri işlemlerin hangi anlamlarda görülmeleri gerektiği konusunda yeterli olmayabilir. Burada bu anlamlara dikkat çekmek istiyorum. -) - -$(H6 $(C |) işleci birleşim kümesidir) - -$(P -İki ifadenin 1 olan bitlerinin birleşimini verir. Uç bir örnek olarak, bitleri birer basamak atlayarak 1 olan ve birbirlerini tutmayan iki ifadenin birleşimi, sonucun bütün bitlerinin 1 olmasını sağlar: -) - ---- - uint soldaki = 0xaaaaaaaa; - uint sağdaki = 0x55555555; - - write(" "); göster(soldaki); - write(" "); göster(sağdaki); - writeln("| --------------------------------"); - write(" "); göster(soldaki | sağdaki); ---- - -$(SHELL_SMALL - 10101010101010101010101010101010 aaaaaaaa 2863311530 - 01010101010101010101010101010101 55555555 1431655765 -| -------------------------------- - 11111111111111111111111111111111 ffffffff 4294967295 -) - -$(H6 $(C &) işleci kesişim kümesidir) - -$(P -İki ifadenin 1 olan bitlerinin kesişimini verir. Uç bir örnek olarak, yukarıdaki iki ifadenin 1 olan hiçbir biti diğerini tutmadığı için, kesişimlerinin bütün bitleri 0'dır: -) - ---- - uint soldaki = 0xaaaaaaaa; - uint sağdaki = 0x55555555; - - write(" "); göster(soldaki); - write(" "); göster(sağdaki); - writeln("& --------------------------------"); - write(" "); göster(soldaki & sağdaki); ---- - -$(SHELL_SMALL - 10101010101010101010101010101010 aaaaaaaa 2863311530 - 01010101010101010101010101010101 55555555 1431655765 -& -------------------------------- - 00000000000000000000000000000000 00000000 0 -) - -$(H6 $(C |=) işleci belirli bitleri 1 yapar) - -$(P -İfadelerden bir taraftakini $(I asıl değişken) olarak düşünürsek, diğer ifadeyi de $(I 1 yapılacak olan bitleri seçen) ifade olarak görebiliriz: -) - ---- - uint ifade = 0x00ff00ff; - uint birYapılacakBitler = 0x10001000; - - write("önce : "); göster(ifade); - write("1 olacaklar: "); göster(birYapılacakBitler); - - ifade $(HILITE |=) birYapılacakBitler; - write("sonra : "); göster(ifade); ---- - -$(P -Etkilenen bitlerin önceki ve sonraki durumlarını işaretli olarak gösterdim: -) - -$(SHELL_SMALL -önce : 000$(HILITE 0)000011111111000$(HILITE 0)000011111111 00ff00ff 16711935 -1 olacaklar: 00010000000000000001000000000000 10001000 268439552 -sonra : 000$(HILITE 1)000011111111000$(HILITE 1)000011111111 10ff10ff 285151487 -) - -$(P -$(C birYapılacakBitler) değeri, bir anlamda hangi bitlerin 1 yapılacakları bilgisini taşımış ve asıl ifadenin o bitlerini 1 yapmış ve diğerlerine dokunmamıştır. -) - -$(H6 $(C &=) işleci belirli bitleri siler) - -$(P -İfadelerden bir taraftakini $(I asıl değişken) olarak düşünürsek, diğer ifadeyi de $(I silinecek olan bitleri seçen) ifade olarak görebiliriz: -) - ---- - uint ifade = 0x00ff00ff; - uint sıfırYapılacakBitler = 0xffefffef; - - write("önce : "); göster(ifade); - write("silinecekler: "); göster(sıfırYapılacakBitler); - - ifade $(HILITE &=) sıfırYapılacakBitler; - write("sonra : "); göster(ifade); ---- - -$(P -Etkilenen bitlerin önceki ve sonraki durumlarını yine işaretli olarak gösteriyorum: -) - -$(SHELL_SMALL -önce : 00000000111$(HILITE 1)111100000000111$(HILITE 1)1111 00ff00ff 16711935 -silinecekler: 11111111111011111111111111101111 ffefffef 4293918703 -sonra : 00000000111$(HILITE 0)111100000000111$(HILITE 0)1111 00ef00ef 15663343 -) - -$(P -$(C sıfırYapılacakBitler) değeri, hangi bitlerin sıfırlanacakları bilgisini taşımış ve asıl ifadenin o bitlerini sıfırlamıştır. -) - -$(H6 $(C &) işleci belirli bir bitin 1 olup olmadığını sorgular) - -$(P -Eğer ifadelerden birisinin tek bir biti 1 ise diğer ifadede o bitin 1 olup olmadığı sorgulanabilir: -) - ---- - uint ifade = 123456789; - uint sorgulananBit = 0x00010000; - - göster(ifade); - göster(sorgulananBit); - writeln(ifade $(HILITE &) sorgulananBit ? "evet, 1" : "1 değil"); ---- - -$(P -Asıl ifadenin hangi bitinin sorgulandığını işaretli olarak gösteriyorum: -) - -$(SHELL_SMALL -000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 -00000000000000010000000000000000 00010000 65536 -evet, 1 -) - -$(P -Başka bir bitini sorgulayalım: -) - ---- - uint sorgulananBit = 0x00001000; ---- - -$(SHELL_SMALL -0000011101011011110$(HILITE 0)110100010101 075bcd15 123456789 -00000000000000000001000000000000 00001000 4096 -1 değil -) - -$(P -Sorgulama ifadesinde birden fazla 1 kullanarak o bitlerin $(I hepsinin birden) asıl ifadede 1 olup olmadıkları da sorgulanabilir. -) - -$(H6 Sağa kaydırmak ikiye bölmektir) - -$(P -Sağa bir bit kaydırmak değerin yarıya inmesine neden olur. Bunu yukarıdaki basamak değerleri tablosunda görebilirsiniz: bir sağdaki bit her zaman için soldakinin yarısı değerdedir. -) - -$(P -Sağa birden fazla sayıda kaydırmak o kadar sayıda yarıya bölmek anlamına gelir. Örneğin 3 bit kaydırmak, 3 kere 2'ye bölmek, yani sonuçta 8'e bölmek anlamına gelir: -) - ---- - uint değer = 8000; - - writeln(değer >> 3); ---- - -$(SHELL_SMALL -1000 -) - -$(P -Ayrıntısına girmediğim $(I ikiye tümleyen) sisteminde sağa kaydırmak işaretli türlerde de ikiye bölmektir: -) - ---- - $(HILITE int) değer = -8000; - - writeln(değer >> 3); ---- - -$(SHELL_SMALL --1000 -) - -$(H6 Sola kaydırmak iki katını almaktır) - -$(P -Basamaklar tablosundaki her bitin, bir sağındakinin iki katı olması nedeniyle, bir bit sola kaydırmak 2 ile çarpmak anlamına gelir: -) - ---- - uint değer = 10; - - writeln(değer << 5); ---- - -$(P -Beş kere 2 ile çarpmak 32 ile çarpmanın eşdeğeridir: -) - -$(SHELL_SMALL -320 -) - -$(H5 Bazı kullanımları) - -$(H6 $(IX bayrak) Bayraklar) - -$(P -Bayraklar birbirlerinden bağımsız olarak bir arada tutulan tek bitlik verilerdir. Tek bitlik oldukları için var/yok, olsun/olmasın, geçerli/geçersiz gibi iki değerli kavramları ifade ederler. -) - -$(P -Her ne kadar böyle tek bitlik bilgilerin yaygın olmadıklarını söylemiş olsam da bazen bir arada kullanılırlar. Bayraklar özellikle C kütüphanelerinde yaygındır. C'den uyarlanan D kütüphanelerinde bulunmaları da beklenebilir. -) - -$(P -Bayraklar bir $(C enum) türünün birbirleriyle örtüşmeyen tek bitlik değerleri olarak tanımlanırlar. -) - -$(P -Bir örnek olarak araba yarışıyla ilgili bir oyun programı düşünelim. Bu programın gerçekçiliği kullanıcı seçimlerine göre belirlensin: -) - -$(UL -$(LI Benzin, kullanıma göre azalabilsin.) -$(LI Çarpışmalar hasar bırakabilsin.) -$(LI Lastikler kullanıma göre eskiyebilsin.) -$(LI Lastikler yolda iz bırakabilsin.) -) - -$(P -Oyun sırasında bunlardan hangilerinin etkin olacakları bayrak değerleriyle belirtilebilir: -) - ---- -enum Gerçekçilik { - benzinBiter = 1 << 0, - hasarOluşur = 1 << 1, - lastiklerEskir = 1 << 2, - lastikİzleriOluşur = 1 << 3 -} ---- - -$(P -Dikkat ederseniz, o $(C enum) değerlerinin hepsi de birbirleriyle çakışmayan tek bitten oluşmaktadırlar. Her değer 1'in farklı sayıda sola ötelenmesi ile elde edilmiştir. Bit değerlerinin şöyle olduklarını görebiliriz: -) - -$(MONO -benzinBiter : ...0001 -hasarOluşur : ...0010 -lastiklerEskir : ...0100 -lastikİzleriOluşur : ...1000 -) - -$(P -Hiçbir bit diğerlerininkiyle çakışmadığı için bu değerler $(C |) ile birleştirilebilir ve hep birden tek bir değişkende bulundurulabilir. Örneğin, yalnızca lastiklerle ilgili ayarların etkin olmaları istendiğinde değer şöyle kurulabilir: -) - ---- - Gerçekçilik ayarlar = Gerçekçilik.lastiklerEskir - | - Gerçekçilik.lastikİzleriOluşur; - writefln("%b", ayarlar); ---- - -$(P -Bu iki bayrağın bitleri aynı değer içinde yan yana bulunurlar: -) - -$(SHELL_SMALL -1100 -) - -$(P -Daha sonradan, programın asıl işleyişi sırasında bu bayrakların etkin olup olmadıkları $(C &) işleci ile denetlenir: -) - ---- - if (ayarlar & Gerçekçilik.benzinBiter) { - // ... benzinin azalmasıyla ilgili kodlar ... - } - - if (ayarlar & Gerçekçilik.lastiklerEskir) { - // ... lastiklerin eskimesiyle ilgili kodlar ... - } ---- - -$(P -$(C &) işlecinin sonucu, ancak belirtilen bayrak $(C ayarlar) içinde de 1 ise 1 sonucunu verir. -) - -$(P -$(C if) koşuluyla kullanılabilmesinin bir nedeni de $(LINK2 /ders/d/tur_donusumleri.html, Tür Dönüşümleri bölümünden) hatırlayacağınız gibi, sıfır olmayan değerlerin otomatik olarak $(C true)'ya dönüşmesidir. $(C &) işlecinin sonucu 0 olduğunda $(C false), farklı bir değer olduğunda da $(C true) değerine dönüşür ve bayrağın etkin olup olmadığı böylece anlaşılmış olur. -) - -$(H6 $(IX maske) Maskeleme) - -$(P -Bazı kütüphanelerde ve sistemlerde belirli bir tamsayı değer içine birden fazla bilgi yerleştirilmiş olabilir. Örneğin, 32 bitlik bir değerin üst 3 bitinin belirli bir anlamı ve alt 29 bitinin başka bir anlamı bulunabilir. Bu veriler maskeleme yöntemiyle birbirlerinden ayrılabilirler. -) - -$(P -Bunun bir örneğini IPv4 adreslerinde görebiliriz. IPv4 adresleri ağ paketleri içinde 32 bitlik tek bir değer olarak bulunurlar. Bu 32 bitin 8'er bitlik 4 parçası günlük kullanımdan alışık olduğumuz noktalı adres gösterimi değerleridir. Örneğin, 192.168.1.2 gibi bir adres, 32 bit olarak 0xc0a80102 değeridir: -) - -$(MONO -c0 == 12 * 16 + 0 = 192 -a8 == 10 * 16 + 8 = 168 -01 == 0 * 16 + 1 = 1 -02 == 0 * 16 + 2 = 2 -) - -$(P -Maske, ilgilenilen veri ile örtüşen sayıda 1'lerden oluşur. Asıl değişken bu maske ile $(I "ve"lendiğinde), yani $(C &) işleci ile kullanıldığında verinin değerleri elde edilir. Örneğin, 0x000000ff gibi bir maske değeri ifadenin alt 8 bitini olduğu gibi korur, diğer bitlerini sıfırlar: -) - ---- - uint değer = 123456789; - uint maske = 0x000000ff; - - write("değer: "); göster(değer); - write("maske: "); göster(maske); - write("sonuç: "); göster(değer & maske); ---- - -$(P -Maskenin seçerek koruduğu bitleri işaretli olarak gösteriyorum. Diğer bütün bitler sıfırlanmıştır: -) - -$(SHELL_SMALL -değer: 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 -maske: 00000000000000000000000011111111 000000ff 255 -sonuç: 000000000000000000000000$(HILITE 00010101) 00000015 21 -) - -$(P -Bu yöntemi 0xc0a80102 IPv4 adresine ve en üst 8 biti seçecek bir maskeyle uyguladığımızda noktalı gösterimdeki ilk adres değerini elde ederiz: -) - ---- - uint değer = 0xc0a80102; - uint maske = 0xff000000; - - write("değer: "); göster(değer); - write("maske: "); göster(maske); - write("sonuç: "); göster(değer & maske); ---- - -$(P -Maskenin üst bitleri 1 olduğundan, değerin de üst bitleri seçilmiş olur: -) - -$(SHELL_SMALL -değer: $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 -maske: 11111111000000000000000000000000 ff000000 4278190080 -sonuç: $(HILITE 11000000)000000000000000000000000 c0000000 3221225472 -) - -$(P -Ancak, sonucun onlu gösterimi beklediğimiz gibi 192 değil, 3221225472 olmuştur. Bunun nedeni, maskelenen 8 bitin değerin en sağ tarafına kaydırılmalarının da gerekmesidir. O 8 biti 24 bit sağa kaydırırsak birlikte ifade ettikleri değeri elde ederiz: -) - ---- - uint değer = 0xc0a80102; - uint maske = 0xff000000; - - write("değer: "); göster(değer); - write("maske: "); göster(maske); - write("sonuç: "); göster((değer & maske) $(HILITE >> 24)); ---- - -$(SHELL_SMALL -değer: $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 -maske: 11111111000000000000000000000000 ff000000 4278190080 -sonuç: 000000000000000000000000$(HILITE 11000000) 000000c0 $(HILITE 192) -) - -$(PROBLEM_COK - -$(PROBLEM -Verilen IPv4 adresinin noktalı gösterimini döndüren bir işlev yazın: - ---- -string noktalıOlarak(uint ipAdresi) { - // ... -} - -unittest { - assert(noktalıOlarak(0xc0a80102) == "192.168.1.2"); -} ---- - -) - -$(PROBLEM -Verilen 4 değeri 32 bitlik IPv4 adresine dönüştüren bir işlev yazın: - ---- -uint ipAdresi(ubyte bayt3, // en yüksek değerli bayt - ubyte bayt2, - ubyte bayt1, - ubyte bayt0) { // en düşük değerli bayt - // ... -} - -unittest { - assert(ipAdresi(192, 168, 1, 2) == 0xc0a80102); -} ---- - -) - -$(PROBLEM -Maske oluşturan bir işlev yazın. Belirtilen bit ile başlayan ve belirtilen uzunlukta olan maske oluştursun: - ---- -uint maskeYap(int düşükBit, int uzunluk) { - // ... -} - -unittest { - assert(maskeYap(2, 5) == - 0b_0000_0000_0000_0000_0000_0000_0111_1100); - // ↑ - // başlangıç biti 2 - // ve 5 bitten oluşuyor -} ---- - -) - -) - -Macros: - SUBTITLE=Bit İşlemleri - - DESCRIPTION=D'nin en alt düzey işlem olanaklarından olan bit işlemleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial bit bayt bit işlemleri - -SOZLER= -$(bayrak) -$(bayt) -$(bit) -$(ifade) -$(ikili_sistem) -$(isaretli_tur) -$(isaretsiz_tur) -$(on_altili_sistem) -$(yazmac) diff --git a/ddili/src/ders/d/blurbs.d b/ddili/src/ders/d/blurbs.d deleted file mode 100644 index e78348c..0000000 --- a/ddili/src/ders/d/blurbs.d +++ /dev/null @@ -1,29 +0,0 @@ -DDoc - -$(COMMENTED_OUT - - -"Üniversitede D öğretirken Ali'nin internetteki kitabını kullanıyorum. Güncel, eksiksiz, ve en önemlisi, son derece okunaklı. Basılı halinin olması daha da iyi! D programcılığı öğrenirken akla ilk gelen kitap bu." -- Chuck Allison, Utah Valley University, Bilgisayar Bölüm Başkanı ve Profesörü - - -"Gördüğüm en iyi D başvuru kitaplarından biri." -- Andrew Wray, D Programcısı - - -"D'yi merak edenlerin bu kitabı okumalarını öneririm. [...] Derlemeli dillerde deneyimi olmayanların bile kolayca takip edebilecekleri bir kitap." -- bachmeier, Reddit üyesi - - -"Bu kitaptan çalışmış birisi olarak takip edilmesi en kolay ve hiç dikkat dağıtmadan okunabilen kitaplardan biri olduğunu söyleyebilirim. Yeni bir dil öğrenmeyi bu kadar kolaylaştırmış olması etkileyici." -- Imran Khan, Öğrenci - - -"Ali'nin açıklamaları hem öz hem de tam amaca yönelik. Özellikle D'nin tasarım nedenlerini ve etkin kullanımını açıklıyor olmasını beğendim. Okuduğum en iyi programlama dili kitabı." -- Robbin Carlson, Yaylı çalgılar yapımcısı ve Enterprise Architect - - -"CS2 Veri Yapıları dersini öğretirken C++ veya Java yerine D'yi seçtim. Bunun sonucunda hem kendim daha başarılı oldum hem de öğrenciler daha memnun kaldılar. Bunun nedeni, D'nin her ölçekteki kavramları ifade etmede mükemmel bir dil olması ve bunu gereksiz karmaşıklığa düşmeden başarmasıdır. - -Ali Çehreli'nin kitabı özellikle dersin ilk yarısında öğrencilere destek olarak temel bir rol oynadı. Bu kitap olmasa bu dersin başarılı olması mümkün değildi ("çok teşekkürler Ali"). Bu başarının nedeni, kitabın başından sonuna doğru sırayla okunacak biçimde yazılmış olması; kitap yalnızca daha önce anlatılanlara bağlı kalınarak okunabiliyor. Bilgilerini henüz geliştirmemiş olan öğrenciler bile çok çalışmak kaydıyla kısa sürede diğer öğrencilerle aynı düzeye erişebildiler. Kitabın bu özelliğini ne kadar övsem azdır. Bu kitabı herkese bütün samimiyetimle öneririm." -- Dr. Carl Sturtivant, University of Minnesota, Bilgisayar Bilimleri ve Mühendisliği Bölümü - - -"D saf, temiz, son derece güçlü, ve en gelişmiş kabul ettiğim programlama dili. Ali'nin kitabı tam bir cevher. Açık, öz, ve eksiksiz." -- Olivier Henley - - -) diff --git a/ddili/src/ders/d/breadcrumbs.ddoc b/ddili/src/ders/d/breadcrumbs.ddoc deleted file mode 100644 index 9d2b233..0000000 --- a/ddili/src/ders/d/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > D - -BREADCRUMBS_FULL=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > $(LINK2 /ders/d/index.html, D) diff --git a/ddili/src/ders/d/cikti_duzeni.cozum.d b/ddili/src/ders/d/cikti_duzeni.cozum.d deleted file mode 100644 index 8f3c503..0000000 --- a/ddili/src/ders/d/cikti_duzeni.cozum.d +++ /dev/null @@ -1,55 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Çıktı Düzeni) - -$(OL - -$(LI Bunun düzen belirteciyle nasıl yapıldığını zaten gördünüz. Hiçbir hesap yapmaya gerek kalmadan: - ---- -import std.stdio; - -void main() { - writeln("(Programdan çıkmak için 0 giriniz.)"); - - while (true) { - write("Lütfen bir sayı giriniz: "); - long sayı; - readf(" %s", &sayı); - - if (sayı == 0) { - break; - } - - writefln("%1$d <=> %1$#x", sayı); - } -} ---- - -) - -$(LI -$(C %) karakterinin kendisini yazdırmak için çift yazmak gerektiğini hatırlayarak: - ---- -import std.stdio; - -void main() { - write("Yüzde değeri? "); - double yüzde; - readf(" %s", &yüzde); - - writefln("%%%.2f", yüzde); -} ---- - -) - -) - -Macros: - SUBTITLE=Çıktı Düzeni Problem Çözümü - - DESCRIPTION=Çıktı Düzeni bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial çıktı düzeni format çözüm diff --git a/ddili/src/ders/d/cikti_duzeni.d b/ddili/src/ders/d/cikti_duzeni.d deleted file mode 100644 index 2e484a7..0000000 --- a/ddili/src/ders/d/cikti_duzeni.d +++ /dev/null @@ -1,639 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX çıktı düzeni) $(IX düzen, çıktı) Çıktı Düzeni) - -$(P -Diğer bölümlerden farklı olarak, bu bölüm D dilinin iç olanaklarından birisini değil, çıktı düzeni için kullanılan $(C std.format) modülünü anlatmaktadır. -) - -$(P -$(IX std) $(IX Phobos) Adı $(C std) ile başlayan bütün modüller gibi $(C std.format) da D'nin standart kütüphanesi olan Phobos'un bir parçasıdır. Çok büyük bir kütüphane olan Phobos bu kitapta bütünüyle kapsanamamaktadır. -) - -$(P -D'nin giriş ve çıkış için kullandığı düzen belirteçlerinin temelde C dilindekiler gibi olduğunu ama bazı farkları bulunduğunu göreceksiniz. -) - -$(P -Bir $(I ön hatırlatma) olarak bütün düzen dizgisi karakterleri aşağıdaki tablodaki gibidir: -) - -$(MONO -$(B Ayar Karakterleri) (birden fazla kullanılabilir) - - sola dayalı - + işaretli - # $(I diğer) şekilde - 0 solda 0'lı - $(I boşluk) solda boşluklu - -$(B Düzen Karakterleri) - s belirteçsiz gibi - b ikili - d onlu - o sekizli - x,X on altılı - f,F kesirli - e,E on üzerili kesirli - a,A on altılı kesirli - g,G e veya f gibi - - ( eleman düzeni başı - ) eleman düzeni sonu - | eleman ayracı -) - -$(P -Şimdiye kadar çıktı için $(C writeln) gibi işlevleri gerektiğinde birden fazla parametreyle kullanmıştık. Bu parametreler otomatik olarak karakter eşdeğerlerine dönüştürülerek sırayla çıkışa gönderiliyorlardı. -) - -$(P -Bazen bu yeterli değildir. Çıktının belirli bir düzene uyması gerekebilir. Örneğin bir faturanın maddelerini yazdıran şu koda bakalım: -) - ---- - faturaMaddeleri ~= 1.23; - faturaMaddeleri ~= 45.6; - - for (int i = 0; i != faturaMaddeleri.length; ++i) { - writeln("Madde ", i + 1, ": ", faturaMaddeleri[i]); - } ---- - -$(P -Çıktısı: -) - -$(SHELL -Madde 1: 1.23 -Madde 2: 45.6 -) - -$(P -Oysa faturadaki değerlerin belirli bir düzende, örneğin her zaman için virgülden sonra iki haneyle ve geniş bir alanda sağa dayalı olarak yazılmaları okuma açısından önemli olabilir. ($(I Not: Ben bu bölümde günlük kullanıma uygun olarak "virgül" diyeceğim; ama kesirli sayılarda virgül yerine nokta karakteri kullanılır.)): -) - -$(SHELL -Madde 1: 1.23 -Madde 2: 45.60 -) - -$(P -İşte çıktı düzeni, böyle konularda yarar sağlar. Şimdiye kadar kullandığımız çıktı işlevlerinin isminde $(C f) harfi geçen karşılıkları da vardır: $(C writef()) ve $(C writefln()). İsimlerindeki $(C f) harfi "düzen, biçim" anlamına gelen "format"ın kısaltmasıdır. Bu işlevlerin ilk parametresi diğer parametrelerin nasıl yazdırılacaklarını belirleyen $(I düzen dizgisidir). -) - -$(P -Örneğin, $(C writefln) yukarıdaki çıktıyı aşağıdaki gibi bir düzen dizgisi ile üretebilir: -) - ---- - writefln("Madde %d:%9.02f", i + 1, faturaMaddeleri[i]); ---- - -$(P -Düzen dizgisi, normal karakterlerden ve özel düzen belirteçlerinden oluşur. Her düzen belirteci $(C %) karakteriyle başlar ve bir $(I düzen karakteri) ile biter. Yukarıdaki dizgide iki tane düzen belirteci var: $(C %d) ve $(C %9.02f). -) - -$(P -Her belirteç, düzen dizgisinden sonra verilen parametrelerle sıra ile eşleşir. Örneğin $(C %d) ile $(C i + 1), ve $(C %9.02f) ile $(C faturaMaddeleri[i])... Her belirteç, eşleştiği parametrenin çıktı düzenini belirler. (Düzen belirteçlerinde parametre numaraları da kullanılabilir. Bunu aşağıda göstereceğim.) -) - -$(P -Düzen dizgisi içinde bulunan ve belirteçlere ait olmayan karakterler, oldukları gibi yazdırılırlar. Yukarıdaki dizgi içindeki $(I normal) karakterleri işaretli olarak şöyle gösterebiliriz: $(C "$(HILITE Madde )%d$(HILITE :)%9.02f"). -) - -$(P -Düzen belirteci, çoğunun belirtilmesi gerekmeyen altı parçadan oluşur. Bu bölümlerden birisi olan $(I numara)'yı daha aşağıda göstereceğim. Diğer beş bölüm şunlardır ($(I Not: okumayı kolaylaştırmak için aralarında boşluk kullanıyorum; bu bölümler aslında bitişik olarak yazılırlar)): -) - -$(MONO - % $(I$(C ayar_karakterleri genişlik duyarlık düzen_karakteri)) -) - -$(P -Baştaki $(C %) karakterinin ve sondaki düzen karakterinin yazılması şarttır, diğerleri ise isteğe bağlıdır. -) - -$(P -% karakterinin böyle özel bir anlamı olduğu için, çıktıda % karakterinin kendisi yazdırılmak istendiğinde $(C %%) şeklinde çift olarak yazılır. -) - -$(H5 $(I düzen_karakteri)) - -$(P $(IX %b) $(C b): Tamsayı, ikili sayı düzeninde yazdırılır. -) - -$(P $(IX %o, çıkış) $(C o): Tamsayı, sekizli sayı düzeninde yazdırılır. -) - -$(P $(IX %x, çıkış) $(IX %X) $(C x) ve $(C X): Tamsayı, on altılı sayı düzeninde yazdırılır; $(C x) için küçük harfler, $(C X) için büyük harfler kullanılır. -) - -$(P $(IX %d, çıkış) $(C d): Tamsayı, onlu sistemde yazdırılır; eğer işaretli bir türse ve değeri sıfırdan küçükse, başına eksi işareti gelir; aksi durumda işaretsiz bir tür gibi yazdırılır. -) - ---- - int değer = 12; - - writefln("İkili : %b", değer); - writefln("Sekizli : %o", değer); - writefln("On altılı: %x", değer); - writefln("Ondalık : %d", değer); ---- - -$(SHELL -İkili : 1100 -Sekizli : 14 -On altılı: c -Ondalık : 12 -) - -$(P $(IX %e) $(C e): Kesirli sayı, aşağıdaki bölümlerden oluşacak şekilde yazdırılır. -) - -$(UL -$(LI virgülden önce tek hane) -$(LI $(I duyarlık) 0 değilse virgül) -$(LI virgülden sonra $(I duyarlık) adet hane (varsayılan duyarlık 6'dır)) -$(LI $(C e) karakteri ("10 üzeri" anlamında)) -$(LI üs sıfırdan küçükse $(C -), değilse $(C +) karakteri) -$(LI en az iki hane olarak üs değeri) -) - -$(P $(IX %E) $(C E): $(C e) ile aynı düzende, ama çıktıda $(C E) harfiyle -) - -$(P $(IX %f, çıkış) $(IX %F) $(C f) ve $(C F): Kesirli sayı, onlu sistemde yazdırılır; virgülden önce en az bir hane bulunur; varsayılan duyarlık 6'dır. -) - -$(P $(IX %g) $(C g): Kesirli sayı, eğer üs değeri -5 ile $(I duyarlık) arasında olacaksa, $(C f) gibi; değilse $(C e) gibi yazdırılır. $(I duyarlık) virgülden sonrasını değil, belirgin hane sayısını belirtir; virgülden sonra belirgin hane yoksa virgül de yazdırılmaz; virgülden sonra en sağdaki sıfırlar yazdırılmazlar. -) - -$(P $(IX %G) $(C G): $(C g) ile aynı düzende, ama $(C E) veya $(C F) kullanılmış gibi yazdırılır -) - -$(P $(IX %a) $(C a): Kesirli sayı, on altılı sistemde ve aşağıdaki bölümlerden oluşacak şekilde yazdırılır: -) - -$(UL -$(LI $(C 0x) karakterleri) -$(LI tek on altılı hane) -$(LI $(I duyarlık) 0 değilse virgül) -$(LI virgülden sonra $(I duyarlık) adet hane, veya $(I duyarlık) belirtilmemişse gerektiği kadar hane) -$(LI $(C p) karakteri ("2 üzeri" anlamında)) -$(LI üssün değerine göre $(C -) veya $(C +) karakteri) -$(LI en az bir hane olarak üs değeri; (0 değerinin üs değeri 0'dır)) -) - -$(P $(IX %A) $(C A): $(C a) ile aynı düzende, ama çıktıda $(C 0X) ve $(C P) karakterleriyle -) - ---- - double değer = 123.456789; - - writefln("e ile: %e", değer); - writefln("f ile: %f", değer); - writefln("g ile: %g", değer); - writefln("a ile: %a", değer); ---- - -$(SHELL -e ile: 1.234568e+02 -f ile: 123.456789 -g ile: 123.457 -a ile: 0x1.edd3c07ee0b0bp+6 -) - -$(P $(IX %s, çıkış) $(C s): Parametrenin değeri; düzen dizgisi kullanılmadığı zamandaki gibi, türüne uygun olan şekilde yazdırılır: -) - -$(UL - -$(LI $(C bool) türler $(C true) veya $(C false) olarak -) -$(LI tamsayılar $(C %d) gibi -) -$(LI kesirli sayılar $(C %g) gibi -) -$(LI dizgiler UTF-8 kodlamasıyla; $(I duyarlık), en fazla kaç bayt kullanılacağını belirler (UTF-8 kodlamasında karakter sayısıyla bayt sayısının eşit olmayabileceklerini hatırlayın; örneğin "ağ" dizgisi toplam 3 bayt uzunluğunda 2 karakterden oluşur) -) -$(LI yapı ve sınıf nesneleri, türün $(C toString()) üye işlevinin ürettiği dizgi olarak; $(I duyarlık), en fazla kaç bayt kullanılacağını belirler -) -$(LI diziler, elemanları yan yana sıralanarak -) - -) - ---- - bool b = true; - int i = 365; - double d = 9.87; - string s = "düzenli"; - auto n = File("deneme_dosyasi", "r"); - int[] dz = [ 2, 4, 6, 8 ]; - - writefln("bool : %s", b); - writefln("int : %s", i); - writefln("double: %s", d); - writefln("string: %s", s); - writefln("nesne : %s", n); - writefln("dizi : %s", dz); ---- - -$(SHELL -bool : true -int : 365 -double: 9.87 -string: düzenli -nesne : File(55738FA0) -dizi : [2, 4, 6, 8] -) - -$(H5 $(IX genişlik, çıktı) $(I genişlik)) - -$(P -$(IX *, çıktı düzeni) Değer için çıktıda ayrılan alanın genişliğini belirler. Eğer genişlik olarak $(C *) kullanılmışsa, genişlik değeri bir sonraki parametrenin değeri olarak alınır. Eğer eksi bir sayıysa, $(C -) ayar karakteri kullanılmış gibi çalışır. -) - ---- - int değer = 100; - - writefln("On karakterlik alanda :%10s", değer); - writefln("Beş karakterlik alanda:%5s", değer); ---- - -$(SHELL -On karakterlik alanda : 100 -Beş karakterlik alanda: 100 -) - -$(H5 $(IX duyarlık, çıktı) $(I duyarlık)) - -$(P -Eğer belirtilmişse, nokta karakterinden sonra yazılır. Kesirli sayı türünden olan değerlerin çıktıda kullanılacak olan duyarlığını belirler. Eğer duyarlık olarak $(C *) kullanılmışsa, duyarlık değeri bir sonraki parametrenin değeri olarak alınır (o değer $(C int) olmak zorundadır). Duyarlık eksi bir sayıysa gözardı edilir. -) - ---- - double kesirli = 1234.56789; - - writefln("%.8g", kesirli); - writefln("%.3g", kesirli); - writefln("%.8f", kesirli); - writefln("%.3f", kesirli); ---- - -$(SHELL -1234.5679 -1.23e+03 -1234.56789000 -1234.568 -) - ---- - auto sayı = 0.123456789; - writefln("Sayı: %.*g", 4, sayı); ---- - -$(SHELL -Sayı: 0.1235 -) - -$(H5 $(IX ayar karakteri, çıktı) $(I ayar_karakterleri)) - -$(P -Birden fazla ayar karakteri kullanabilirsiniz. -) - -$(P $(C -): parametre değeri; kendisine ayrılan alanda sola dayalı olarak yazdırılır; bu ayar, $(C 0) ayar karakterini geçersiz kılar -) - ---- - int değer = 123; - - writefln("normalde sağa dayalı:|%10d|", değer); - writefln("sola dayalı :|%-10d|", değer); ---- - -$(SHELL -normalde sağa dayalı:| 123| -sola dayalı :|123 | -) - -$(P $(C +): değer artı ise başına $(C +) karakteri yazdırılır; bu ayar, $(I boşluk) ayar karakterini geçersiz kılar -) - ---- - writefln("eksi değerde etkili değil: %+d", -50); - writefln("artı değer, + ile : %+d", 50); - writefln("artı değer, + olmadan : %d", 50); ---- - -$(SHELL -eksi değerde etkili değil: -50 -artı değer, + ile : +50 -artı değer, + olmadan : 50 -) - -$(P $(C #): kullanılan $(I düzen_karakteri)'ne bağlı olarak, değeri $(I başka şekilde) yazdırır -) - -$(UL -$(LI $(C o) için: sekizli sayının ilk karakteri her zaman için 0 olarak yazdırılır) -$(LI $(C x) ve $(C X) için: sayı sıfır değilse, başına $(C 0x) veya $(C 0X) gelir) -$(LI kesirli sayılarda: virgülden sonra hane olmasa da virgül yazdırılır) -$(LI $(C g) ve $(C G) için: virgülden sonra sağdaki sıfırlar atılmaz) -) - ---- - writefln("Sekizli sıfırla başlar : %#o", 1000); - writefln("On altılının başına 0x gelir : %#x", 1000); - - writefln("Gerekmese de virgüllü olur : %#g", 1f); - writefln("Sağdaki sıfırlar da yazdırılır: %#g", 1.2); ---- - -$(SHELL -Sekizli sıfırla başlar : 01750 -On altılının başına 0x gelir : 0x3e8 -Gerekmese de virgüllü olur : 1.00000 -Sağdaki sıfırlar da yazdırılır: 1.20000 -) - -$(P $(C 0): sayılarda (değer $(C nan) veya $(C infinity) değilse), sol tarafa değer için ayrılan alan dolacak kadar 0 yazdırılır; $(I duyarlık) da belirtilmişse bu ayar etkisizdir -) - ---- - writefln("Sekiz genişlikte: %08d", 42); ---- - -$(SHELL -Sekiz genişlikte: 00000042 -) - -$(P $(I boşluk) karakteri: değer artı ise, eksi değerlerle alt alta düzgün dursun diye başına tek bir boşluk karakteri yazdırılır) - ---- - writefln("Eksi değerde etkisi yok: % d", -34); - writefln("Artı değer, boşluklu : % d", 56); - writefln("Artı değer, boşluksuz : %d", 56); ---- - -$(SHELL -Eksi değerde etkisi yok: -34 -Artı değer, boşluklu : 56 -Artı değer, boşluksuz : 56 -) - -$(H5 $(IX %1$) $(IX parametre numaraları, çıktı) $(IX numaralı parametre, çıktı) $(IX $, çıktı düzeni) Parametre numaraları) - -$(P -Yukarıda düzen dizgisi içindeki düzen belirteçlerinin parametrelerle teker teker ve sırayla eşleştirildiklerini gördük. Aslında düzen belirtecinde parametre numarası da kullanılabilir. Bu, belirtecin hangi parametre ile ilgili olduğunu belirler. Parametreler 1'den başlayarak artan sırada numaralanırlar. Parametre numarası $(C %) karakterinden hemen sonra ve $(C $) karakteri ile birlikte yazılır: -) - -$(MONO - % $(I$(C $(HILITE numara$) ayar_karakterleri genişlik duyarlık düzen_karakteri)) -) - -$(P -Bunun bir yararı, aynı parametrenin birden fazla yerde yazdırılabilmesidir: -) - ---- - writefln("%1$d %1$x %1$o %1$b", 42); ---- - -$(P -Yukarıdaki düzen dizgisi 1 numaralı parametreyi dört düzen belirteci yoluyla onlu, on altılı, sekizli, ve ikili sayı sistemlerinde yazdırmaktadır: -) - -$(SHELL -42 2a 52 101010 -) - -$(P -Parametre numaralarının bir diğer kullanım alanı, aynı parametrelerin farklı düzen dizgileriyle kullanılabilmesi ve bu sayede mesajların farklı konuşma dillerinin yazım kurallarına uydurulabilmesidir. Örneğin belirli bir dersteki öğrenci sayısı Türkçe olarak şöyle bildiriliyor olsun: -) - ---- - writefln("%s sınıfında %s öğrenci var.", sınıf, adet); ---- - -$(SHELL -1A sınıfında 20 öğrenci var. -) - -$(P -Programın örneğin İngilizce'yi de desteklemesi gerektiğini düşünelim. Bu durumda düzen dizgisinin dile uygun olarak daha önceden seçilmiş olması gerekir. Aşağıdaki yöntem bu iş için üçlü işleçten yararlanıyor: -) - ---- - auto düzenDizgisi = (dil == "tr" - ? "%s sınıfında %s öğrenci var." - : "There are %s students in room %s."); - - writefln(düzenDizgisi, sınıf, adet); ---- - -$(P -Ne yazık ki, parametreler düzen belirteçleriyle birer birer eşleştirildiklerinde sınıf ve adet bilgileri İngilizce mesajda ters sırada çıkarlar. Sınıf bilgisi adet yerinde, adet bilgisi de sınıf yerindedir: -) - -$(SHELL -There are 1A students in room 20. $(SHELL_NOTE_WRONG Yanlış: Adet 1A, sınıf 20!) -) - -$(P -Bunun önüne geçmek için düzen dizgisinde hangi belirtecin hangi parametreye karşılık geldiği $(C 1$) ve $(C 2$) biçiminde parametre numaralarıyla belirtilebilir: -) - ---- - auto düzenDizgisi = (dil == "tr" - ? "%1$s sınıfında %2$s öğrenci var." - : "There are %2$s students in room %1$s."); - - writefln(düzenDizgisi, sınıf, adet); ---- - -$(P -Artık mesajın hem Türkçesi hem de İngilizcesi düzgündür: -) - -$(SHELL -1A sınıfında 20 öğrenci var. -) - -$(SHELL -There are 20 students in room 1A. -) - -$(H5 $(IX %$(PARANTEZ_AC)) $(IX %$(PARANTEZ_KAPA)) Eleman düzeni) - -$(P -$(STRING %$(PARANTEZ_AC)) ve $(STRING %$(PARANTEZ_KAPA)) arasındaki düzen belirteçleri bir topluluktaki (veya aralıktaki) elemanlara teker teker uygulanır: -) - ---- - auto sayılar = [ 1, 2, 3, 4 ]; - writefln("%(%s%)", sayılar); ---- - -$(P -Yukarıdaki düzen dizgisi üç parçadan oluşuyor: -) - -$(UL -$(LI $(STRING %$(PARANTEZ_AC)): Eleman düzeni başı) -$(LI $(STRING %s): Her elemanın düzeni) -$(LI $(STRING %$(PARANTEZ_KAPA)): Eleman düzeni sonu) -) - -$(P -Her birisine $(STRING %s) düzeni uygulandığında bütün elemanlar çıktıda art arda belirirler: -) - -$(SHELL -1234 -) - -$(P -Eleman düzeninin başı ile sonu arasındaki $(I normal) karakterler her eleman için tekrarlanırlar. Örneğin, $(STRING {%s},) belirteci her elemanın küme parantezleri arasında ve virgüllerle ayrılarak yazdırılmasını sağlar: -) - ---- - writefln("%({%s},%)", sayılar); ---- - -$(P -Ancak, düzen belirtecinin sağındaki $(I normal) karakterlerin ayraç oldukları kabul edilir ve onlar normalde yalnızca elemanlar arasına yazdırılırlar. Bu yüzden, yukarıdaki örnekteki $(C },) karakterleri sonuncu elemandan sonra yazdırılmazlar: -) - -$(SHELL -{1},{2},{3},{4 $(SHELL_NOTE '}' ve ',' karakterleri son eleman için yazdırılmamış) -) - -$(P -$(IX %|) Sağdaki karakterlerin hangilerinin ayraç oldukları ve hangilerinin sonuncu elemandan sonra da yazdırılmalarının gerektiği $(STRING %|) ile belirtilir. Bu belirtecin solundaki karakterler sonuncu eleman için de yazdırılırlar, sağındaki karakterler ise yazdırılmazlar. Örneğin, aşağıdaki düzen dizgisi $(C }) karakterini sonuncu elemandan sonra da yazdırır ama $(C ,) karakterini yazdırmaz: -) ---- - writefln("%({%s}%|,%)", sayılar); ---- - -$(SHELL -{1},{2},{3},{4} $(SHELL_NOTE '}' karakteri son eleman için de yazdırılmış) -) - -$(P -Tek başlarına yazdırılan dizgilerden farklı olarak, eleman olarak yazdırılan dizgiler normalde çift tırnaklar arasında yazdırılırlar: -) - ---- - auto sebzeler = [ "ıspanak", "kuşkonmaz", "enginar" ]; - writefln("%(%s, %)", sebzeler); ---- - -$(SHELL -"ıspanak", "kuşkonmaz", "enginar" -) - -$(P -$(IX %-$(PARANTEZ_AC)) Bunun istenmediği durumlarda eleman düzeni $(STRING %$(PARANTEZ_AC)) ile değil, $(STRING %-$(PARANTEZ_AC)) ile başlatılır: -) - ---- - writefln("%-(%s, %)", sebzeler); ---- - -$(SHELL -ıspanak, kuşkonmaz, enginar -) - -$(P -Aynısı karakterler için de geçerlidir. $(STRING %$(PARANTEZ_AC)) kullanıldığında karakterler tek tırnak içinde yazdırılır: -) - ---- - writefln("%(%s%)", "merhaba"); ---- - -$(SHELL -'m''e''r''h''a''b''a' -) - -$(P -$(STRING %-$(PARANTEZ_AC)) kullanıldığında ise tırnaksız olarak yazdırılır: -) - ---- - writefln("%-(%s%)", "merhaba"); ---- - -$(SHELL -merhaba -) - -$(P -Eşleme tablolarında eleman düzeninde iki belirteç kullanılmalıdır: Birincisi anahtarı, ikincisi de değeri temsil eder. Örneğin, aşağıdaki $(STRING %s (%s)) belirteci önce anahtarın parantezsiz olarak, sonra da değerin parantez içinde yazdırılmasını sağlar: -) - ---- - auto yazıyla = [ 1 : "bir", 10 : "on", 100 : "yüz" ]; - writefln("%-(%s (%s)%|, %)", yazıyla); ---- - -$(P -$(STRING %|) belirtecinin sağında belirtilen virgülün son eleman için yazdırılmadığına da dikkat edin: -) - -$(SHELL -1 (bir), 100 (yüz), 10 (on) -) - -$(H5 $(IX format, std.string) $(C format)) - -$(P -Yukarıda anlatılan bütün olanaklar $(C std.string) modülünün $(C format) işlevi için de geçerlidir. $(C format) aynı $(C writef) gibi işler ama oluşturduğu bilgiyi çıkışa yazdırmak yerine bir dizgi olarak döndürür: -) - ---- -import std.stdio; -import std.string; - -void main() { - write("Adınız ne? "); - auto isim = strip(readln()); - - auto sonuç = $(HILITE format)("Merhaba %s!", isim); -} ---- - -$(P -Böylece, oluşturulan dizgi daha sonraki ifadelerde kullanılabilir. -) - -$(PROBLEM_COK - -$(PROBLEM -Girilen tamsayıyı on altılı düzende yazdıran bir program yazın. -) - -$(PROBLEM -Girilen kesirli sayıyı bir $(I yüzde) değeri olarak ve virgülden sonra 2 haneyle yazdıran bir program yazın. Örneğin 1.2345 girildiğinde ekrana yalnızca $(C %1.23) yazsın. -) - -) - -Macros: - SUBTITLE=Çıktı Düzeni - - DESCRIPTION=Phobos'un std.format modülünün çıktı düzeni için nasıl kullanıldığı - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial çıktı düzen format - -SOZLER= -$(duzen) -$(ic_olanak) -$(isaretli_tur) -$(isaretsiz_tur) -$(islev) -$(parametre) -$(parametre_degeri) -$(phobos) diff --git a/ddili/src/ders/d/clear.d b/ddili/src/ders/d/clear.d deleted file mode 100644 index 83f5826..0000000 --- a/ddili/src/ders/d/clear.d +++ /dev/null @@ -1,483 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(CH4 destroy) ve $(CH4 scoped)) - -$(P -$(LINK2 /ders/d/yasam_surecleri.html, Yaşam Süreçleri ve Temel İşlemler bölümünde) değişkenlerin kurma işlemiyle başlayan ve sonlandırma işlemiyle biten yaşam süreçlerini görmüştük. -) - -$(P -Daha sonraki bölümlerde de nesnelerin kurulması sırasında gereken işlemlerin $(C this) isimli kurucu işlevde, sonlandırılması sırasında gereken işlemlerin de $(C ~this) isimli sonlandırıcı işlevde tanımlandıklarını öğrenmiştik. -) - -$(P -Sonlandırıcı işlev, yapılarda ve başka $(I değer türlerinde) nesnenin yaşamı sona ererken $(I hemen) işletilir. Sınıflarda ve başka referans türlerinde ise çöp toplayıcı tarafından $(I sonraki bir zamanda) işletilir. -) - -$(P -Burada önemli bir ayrım vardır: bir sınıf nesnesinin yaşamının sona ermesi ile sonlandırıcı işlevinin işletilmesi aynı zamanda gerçekleşmez. Nesnenin yaşamı, örneğin geçerli olduğu kapsamdan çıkıldığı an sona erer. Sonlandırıcı işlevi ise çöp toplayıcı tarafından belirsiz bir zaman sonra otomatik olarak işletilir. -) - -$(P -Sonlandırıcı işlevlerin görevlerinden bazıları, nesne için kullanılmış olan sistem kaynaklarını geri vermektir. Örneğin $(C std.stdio.File) yapısı, işletim sisteminden kendi işi için almış olduğu dosya kaynağını sonlandırıcı işlevinde geri verir. Artık sonlanmakta olduğu için zaten o kaynağı kullanması söz konusu değildir. -) - -$(P -Sınıfların sonlandırıcılarının çöp toplayıcı tarafından tam olarak ne zaman çağrılacakları belli olmadığı için, bazen kaynakların sisteme geri verilmeleri gecikebilir ve yeni nesneler için kaynak kalmayabilir. -) - -$(H5 Sınıf sonlandırıcı işlevlerinin geç işletilmesini gösteren bir örnek) - -$(P -Sınıfların sonlandırıcı işlevlerinin ilerideki belirsiz bir zamanda işletildiklerini göstermek için bir sınıf tanımlayalım. Bu sınıfın kurucu işlevi sınıfın $(C static) bir sayacını arttırsın ve sonlandırıcı işlevi de o sayacı azaltsın. Hatırlarsanız, $(C static) üyelerden bir tane bulunur: Sınıfın bütün nesneleri o tek üyeyi ortaklaşa kullanırlar. Böylece o sayacın değerine bakarak sınıfın nesnelerinden kaç tanesinin henüz sonlandırılmadıklarını anlayabileceğiz. -) - ---- -$(CODE_NAME YaşamıGözlenen)class YaşamıGözlenen { - int[] dizi; // ← her nesnenin kendisine aittir - - static int sayaç; // ← bütün nesneler tarafından - // paylaşılır - - this() { - /* Her nesne bellekte çok yer tutsun diye bu diziyi - * çok sayıda int'lik hale getiriyoruz. Nesnelerin - * böyle büyük olmalarının sonucunda çöp - * toplayıcının bellek açmak için onları daha sık - * sonlandıracağını umuyoruz. */ - dizi.length = 30_000; - - /* Bir nesne daha kurulmuş olduğundan nesne sayacını - * bir arttırıyoruz. */ - ++sayaç; - } - - ~this() { - /* Bir nesne daha sonlandırılmış olduğundan nesne - * sayacını bir azaltıyoruz. */ - --sayaç; - } -} ---- - -$(P -O sınıfın nesnelerini bir döngü içinde oluşturan bir program: -) - ---- -$(CODE_XREF YaşamıGözlenen)import std.stdio; - -void main() { - foreach (i; 0 .. 20) { - auto değişken = new YaşamıGözlenen; // ← baş - write(YaşamıGözlenen.sayaç, ' '); - } // ← son - - writeln(); -} ---- - -$(P -O programda oluşan her $(C YaşamıGözlenen) nesnesinin yaşamı aslında çok kısadır: $(C new) anahtar sözcüğüyle başlar, ve $(C foreach) döngüsünün kapama parantezinde son bulur. Yaşamları sona eren bu nesneler çöp toplayıcının sorumluluğuna girerler. -) - -$(P -Programdaki $(COMMENT baş) ve $(COMMENT son) açıklamaları her nesnenin yaşamının başladığı ve sona erdiği noktayı gösteriyor. Nesnelerin sonlandırıcı işlevlerinin, yaşamlarının sona erdiği an işletilmediklerini sayacın değerine bakarak görebiliyoruz: -) - -$(SHELL -1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 -) - -$(P -Yukarıdaki çıktıdan anlaşıldığına göre, çöp toplayıcının bellek ayırma algoritması, bu deneyde bu sınıfın sonlandırıcısını en fazla 7 nesne için ertelemiştir. ($(I Not: Bu çıktı çöp toplayıcının yürüttüğü algoritmaya, boş bellek miktarına ve başka etkenlere bağlı olarak farklı olabilir.)) -) - -$(H5 $(IX destroy) $(IX sonlandırıcı, işletilmesi) Nesnenin sonlandırıcısını işletmek için $(C destroy())) - -$(P -"Ortadan kaldır" anlamına gelen $(C destroy()) nesnenin sonlandırıcı işlevini çağırır: -) - ---- -$(CODE_XREF YaşamıGözlenen)void main() { - foreach (i; 0 .. 20) { - auto değişken = new YaşamıGözlenen; - write(YaşamıGözlenen.sayaç, ' '); - $(HILITE destroy(değişken)); - } - - writeln(); -} ---- - -$(P -$(C YaşamıGözlenen.sayaç)'ın değeri $(C new) satırında kurucu işlevin işletilmesi sonucunda arttırılır ve 1 olur. Değerinin yazdırıldığı satırdan hemen sonraki $(C destroy()) satırında da sonlandırıcı işlev tarafından tekrar sıfıra indirilir. O yüzden yazdırıldığı satırda hep 1 olduğunu görüyoruz: -) - -$(SHELL -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -) - -$(P -Açıkça sonlandırılan nesneler geçersiz kabul edilmelidirler ve artık kullanılmamalıdırlar: -) - ---- - destroy(değişken); - // ... - // Dikkat: Geçersiz bir nesneye erişiliyor - writeln(değişken.dizi); ---- - -$(P -Normalde referans türleri ile kullanılan $(C destroy), gerektiğinde $(C struct) nesnelerinin erkenden sonlandırılmaları için de kullanılabilir. -) - -$(H5 Ne zaman kullanmalı) - -$(P -Yukarıdaki örnekte gördüğümüz gibi, kaynakların çöp toplayıcının kararına kalmadan hemen geri verilmesi gerektiğinde kullanılır. -) - -$(H5 Örnek) - -$(P -$(LINK2 /ders/d/ozel_islevler.html, Kurucu ve Diğer Özel İşlevler bölümünde) $(C XmlElemanı) isminde bir yapı tanımlamıştık. O yapı, XML elemanlarını $(C <etiket>değer</etiket>) şeklinde yazdırmak için kullanılıyordu. XML elemanlarının kapama etiketlerinin yazdırılması sonlandırıcı işlevin göreviydi: -) - ---- -struct XmlElemanı { - // ... - - ~this() { - writeln(girinti, "'); - } -} ---- - -$(P -O yapıyı kullanan bir programla aşağıdaki çıktıyı elde etmiştik: -) - -$(SHELL_SMALL -<dersler> - <ders0> - <not> - 72 - </not> $(SHELL_NOTE Kapama etiketleri doğru satırlarda beliriyor) - <not> - 97 - </not> $(SHELL_NOTE) - <not> - 90 - </not> $(SHELL_NOTE) - </ders0> $(SHELL_NOTE) - <ders1> - <not> - 77 - </not> $(SHELL_NOTE) - <not> - 87 - </not> $(SHELL_NOTE) - <not> - 56 - </not> $(SHELL_NOTE) - </ders1> $(SHELL_NOTE) -</dersler> $(SHELL_NOTE) -) - -$(P -O çıktının doğru belirmesinin nedeni, $(C XmlElemanı)'nın bir yapı olmasıdır. Yapıların sonlandırıcıları hemen çağrıldıklarından, istenen çıktı, nesneleri uygun kapsamlara yerleştirerek elde edilir: -) - ---- -void $(CODE_DONT_TEST)main() { - const $(HILITE dersler) = XmlElemanı("dersler", 0); - - foreach (dersNumarası; 0 .. 2) { - const $(HILITE ders) = - XmlElemanı("ders" ~ to!string(dersNumarası), 1); - - foreach (i; 0 .. 3) { - const $(HILITE not) = XmlElemanı("not", 2); - const rasgeleNot = uniform(50, 101); - - writeln(girintiDizgisi(3), rasgeleNot); - - } // ← not sonlanır - - } // ← ders sonlanır - -} // ← dersler sonlanır ---- - -$(P -Nesneler açıklama satırları ile belirtilen noktalarda sonlandıkça XML kapama etiketlerini de çıkışa yazdırırlar. -) - -$(P -Sınıfların farkını görmek için aynı programı bu sefer $(C XmlElemanı) bir sınıf olacak şekilde yazalım: -) - ---- -import std.stdio; -import std.array; -import std.random; -import std.conv; - -string girintiDizgisi(in int girintiAdımı) { - return replicate(" ", girintiAdımı * 2); -} - -$(HILITE class) XmlElemanı { - string isim; - string girinti; - - this(in string isim, in int düzey) { - this.isim = isim; - this.girinti = girintiDizgisi(düzey); - - writeln(girinti, '<', isim, '>'); - } - - ~this() { - writeln(girinti, "'); - } -} - -void main() { - const dersler = $(HILITE new) XmlElemanı("dersler", 0); - - foreach (dersNumarası; 0 .. 2) { - const ders = $(HILITE new) XmlElemanı( - "ders" ~ to!string(dersNumarası), 1); - - foreach (i; 0 .. 3) { - const not = $(HILITE new) XmlElemanı("not", 2); - const rasgeleNot = uniform(50, 101); - - writeln(girintiDizgisi(3), rasgeleNot); - } - } -} ---- - -$(P -Referans türleri olan sınıfların sonlandırıcı işlevleri çöp toplayıcıya bırakılmış olduğu için programın çıktısı artık istenen düzende değildir: -) - -$(SHELL_SMALL -<dersler> - <ders0> - <not> - 57 - <not> - 98 - <not> - 87 - <ders1> - <not> - 84 - <not> - 60 - <not> - 99 - </not> $(SHELL_NOTE Kapama etiketlerinin hepsi en sonda beliriyor) - </not> $(SHELL_NOTE) - </not> $(SHELL_NOTE) - </ders1> $(SHELL_NOTE) - </not> $(SHELL_NOTE) - </not> $(SHELL_NOTE) - </not> $(SHELL_NOTE) - </ders0> $(SHELL_NOTE) -</dersler> $(SHELL_NOTE) -) - -$(P -Bütün sonlandırıcı işlevler işletilmişlerdir ama kapama etiketleri beklenen yerlerde değildir. ($(I Not: Aslında çöp toplayıcı bütün nesnelerin sonlandırılacakları garantisini vermez. Örneğin programın çıktısında hiçbir kapama parantezi bulunmayabilir.)) -) - -$(P -$(C XmlElemanı)'nın sonlandırıcı işlevinin doğru noktalarda işletilmesini sağlamak için $(C destroy()) çağrılır: -) - ---- -void $(CODE_DONT_TEST)main() { - const dersler = new XmlElemanı("dersler", 0); - - foreach (dersNumarası; 0 .. 2) { - const ders = new XmlElemanı( - "ders" ~ to!string(dersNumarası), 1); - - foreach (i; 0 .. 3) { - const not = new XmlElemanı("not", 2); - const rasgeleNot = uniform(50, 101); - - writeln(girintiDizgisi(3), rasgeleNot); - - $(HILITE destroy(not)); - } - - $(HILITE destroy(ders)); - } - - $(HILITE destroy(dersler)); -} ---- - -$(P -Sonuçta, nesneler kapsamlardan çıkılırken sonlandırıldıkları için programın çıktısı yapı tanımında olduğu gibi düzgündür: -) - -$(SHELL_SMALL -<dersler> - <ders0> - <not> - 66 - </not> $(SHELL_NOTE Kapama etiketleri doğru satırlarda belirmiş) - <not> - 75 - </not> $(SHELL_NOTE) - <not> - 68 - </not> $(SHELL_NOTE) - </ders0> $(SHELL_NOTE) - <ders1> - <not> - 73 - </not> $(SHELL_NOTE) - <not> - 62 - </not> $(SHELL_NOTE) - <not> - 100 - </not> $(SHELL_NOTE) - </ders1> $(SHELL_NOTE) -</dersler> $(SHELL_NOTE) -) - -$(H5 $(IX scoped) Sonlandırıcı işlevi otomatik olarak çağırmak için $(C scoped)) - -$(P -Yukarıdaki programın bir yetersizliği vardır: Kapsamlardan daha $(C destroy()) satırlarına gelinemeden atılmış olan bir hata nedeniyle çıkılmış olabilir. Eğer $(C destroy()) satırlarının kesinlikle işletilmeleri gerekiyorsa, bunun bir çözümü $(LINK2 /ders/d/hatalar.html, Hatalar bölümünde) gördüğümüz $(C scope) ve diğer olanaklardan yararlanmaktır. -) - -$(P -Başka bir yöntem, sınıf nesnesini $(C new) yerine $(C std.typecons.scoped) ile kurmaktır. $(C scoped()), sınıf değişkenini perde arkasında bir yapı nesnesi ile sarmalar. O yapı nesnesinin sonlandırıcısı kapsamdan çıkılırken otomatik olarak çağrıldığında sınıf nesnesinin sonlandırıcısını da çağırır. -) - -$(P -$(C scoped)'un etkisi, yaşam süreçleri açısından sınıf nesnelerini yapı nesnelerine benzetmesidir. -) - -$(P -Aşağıdaki değişikliklerden sonra program yine beklenen sonucu üretir: -) - ---- -$(HILITE import std.typecons;) -// ... -void $(CODE_DONT_TEST)main() { - const dersler = $(HILITE scoped!)XmlElemanı("dersler", 0); - - foreach (dersNumarası; 0 .. 2) { - const ders = $(HILITE scoped!)XmlElemanı( - "ders" ~ to!string(dersNumarası), 1); - - foreach (i; 0 .. 3) { - const not = $(HILITE scoped!)XmlElemanı("not", 2); - const rasgeleNot = uniform(50, 101); - - writeln(girintiDizgisi(3), rasgeleNot); - } - } -} ---- - -$(P -$(C destroy()) satırlarının çıkartılmış olduklarına dikkat edin. -) - -$(P -$(IX RAII) $(IX vekil) $(C scoped()), asıl sınıf nesnesini sarmalayan özel bir yapı nesnesi döndürür. Döndürülen nesne asıl nesnenin $(I vekili) $(ASIL proxy) olarak görev görür. (Aslında, yukarıdaki $(C dersler) nesnesinin türü $(C XmlElemanı) değil, $(C Scoped)'dur.) -) - -$(P -Kendisi otomatik olarak sonlandırılırken vekil nesne sarmaladığı sınıf nesnesini de $(C destroy()) ile sonlandırır. (Bu, RAII yönteminin bir uygulamasıdır. $(C scoped()) bunu ilerideki bölümlerde göreceğimiz $(LINK2 /ders/d/sablonlar.html, şablon) ve $(LINK2 /ders/d/alias_this.html, $(C alias this)) olanaklarından yararlanarak gerçekleştirir.) -) - -$(P -Vekil nesnelerinin kullanımlarının asıl nesne kadar doğal olması istenir. Bu yüzden, $(C scoped())'un döndürdüğü nesne sanki asıl türdenmiş gibi kullanılabilir. Örneğin, asıl türün üye işlevleri vekil nesne üzerinde çağrılabilirler: -) - ---- -import std.typecons; - -class C { - void foo() { - } -} - -void main() { - auto v= scoped!C(); - v$(HILITE .foo()); // Vekil nesnesi v, C gibi kullanılıyor -} ---- - -$(P -Ancak, bu kolaylığın bir bedeli vardır: Vekil nesnesi asıl nesneye referans döndürdükten hemen sonra sonlanmış ve döndürülen referans o yüzden geçersiz kalmış olabilir. Bu durum asıl sınıf türü sol tarafta açıkça belirtildiğinde ortaya çıkabilir: -) - ---- - $(HILITE C) c = scoped!C(); $(CODE_NOTE_WRONG HATALI) - c.foo(); $(CODE_NOTE_WRONG Sonlanmış bir nesneye erişir) ---- - -$(P -Yukarıdaki $(C c) vekil nesnesi değil, açıkça $(C C) olarak tanımlandığından asıl nesneye erişim sağlamakta olan bir sınıf değişkenidir. Ne yazık ki bu durumda sağ tarafta kurulmuş olan vekil nesnesi kurulduğu ifadenin sonunda sonlandırılacaktır. Sonuçta, geçersiz bir nesneye erişim sağlamakta olan $(C c)'nin kullanılması tanımsız davranıştır. Örneğin, program bir çalışma zamanı hatasıyla çökebilir: -) - -$(SHELL -Segmentation fault -) - -$(P -O yüzden, $(C scoped()) değişkenlerini asıl tür ile tanımlamayın: -) - ---- - $(HILITE C) a = scoped!C(); $(CODE_NOTE_WRONG HATALI) - auto b = scoped!C(); $(CODE_NOTE doğru) - const c = scoped!C(); $(CODE_NOTE doğru) - immutable d = scoped!C(); $(CODE_NOTE doğru) ---- - -$(H5 Özet) - -$(UL - -$(LI Bir sınıf nesnesinin sonlandırıcı işlevinin istenen bir anda çağrılması için $(C destroy()) işlevi kullanılır.) - -$(LI $(C scoped()) ile kurulan sınıf nesnelerinin sonlandırıcıları kapsamdan çıkılırken otomatik olarak çağrılır.) - -$(LI $(C scoped()) değişkenlerini asıl türün ismiyle tanımlamak hatalıdır.) - -) - -Macros: - SUBTITLE=destroy ve scoped - - DESCRIPTION=Sınıf nesnelerinin sonlandırıcılarını çağıran destroy() işlevi ve destroy()'u sınıf nesneleri için otomatik olarak çağıran std.typecons.scoped - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial sınıf class sınıflar clear destroy scoped sonlandırma destructor - -SOZLER= -$(cop_toplayici) -$(deger_turu) -$(kurma) -$(kurucu_islev) -$(referans_turu) -$(sonlandirici_islev) -$(sonlandirma) -$(vekil) diff --git a/ddili/src/ders/d/code/BENIOKU b/ddili/src/ders/d/code/BENIOKU deleted file mode 100644 index 457e8ff..0000000 --- a/ddili/src/ders/d/code/BENIOKU +++ /dev/null @@ -1,15 +0,0 @@ -Bu klasör "D Programlama Dili" kitabındaki örnek programların çoğunu içerir: - - http://ddili.org/ders/d/ - -Örnek programların isimleri bölümİsmi.N.d düzenindedir. Bu programlar bölüm -dosyalarından otomatik olarak çıkartılmışlardır. - - bölümİsmi - Kitabın web sürümünde programın ilgili olduğu bölüm dosyasının - .html'siz bölümü (örneğin, merhaba_dunya.html'e karşılık - merhaba_dunya) - - N - Programın o bölümden çıkartılma sıra numarası (örneğin, 1) - -Örneğin, merhaba_dunya.1.d dosyası merhaba_dunya.html dosyasından çıkartılan -ilk örnek programdır. diff --git a/ddili/src/ders/d/cokuzlular.d b/ddili/src/ders/d/cokuzlular.d deleted file mode 100644 index b05a4d9..0000000 --- a/ddili/src/ders/d/cokuzlular.d +++ /dev/null @@ -1,568 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX çokuzlu) Çokuzlular) - -$(P -Çokuzlu birden fazla değeri bir araya getirerek hep birden bir yapı nesnesi gibi kullanılmalarını sağlayan olanaktır. Bazı dillerin iç olanağı olan çokuzlular D'de $(C std.typecons) modülündeki $(C Tuple) ile bir kütüphane olanağı olarak gerçekleştirilmiştir. -) - -$(P -$(C Tuple) bazı işlemleri için $(C std.meta) modülündeki $(C AliasSeq)'ten da yararlanır. -) - -$(H5 $(IX Tuple, std.typecons) $(IX tuple, std.typecons) $(C Tuple) ve $(C tuple())) - -$(P -Çokuzlular $(C Tuple) şablonu ile gerçekleştirilmişlerdir. Çoğunlukla kolaylık işlevi olan $(C tuple()) ile oluşturulurlar: -) - ---- -import std.stdio; -import std.typecons; - -void main() { - auto çokuzlu = $(HILITE tuple(42, "merhaba")); - writeln(çokuzlu); -} ---- - -$(P -Yukarıdaki $(C tuple()) işlevi 42 değerindeki $(C int)'in ve $(STRING "merhaba") değerindeki $(C string)'in bir araya gelmesinden oluşan bir nesne oluşturur. Bu nesnenin türünü ve üyelerinin değerlerini programın çıktısında görüyoruz: -) - -$(SHELL -Tuple!(int, string)(42, "merhaba") -) - -$(P -O çokuzlu türünün aşağıdaki sözde yapının eşdeğeri olduğunu ve perde arkasında da öyle gerçekleştirildiğini düşünebilirsiniz: -) - ---- -// Tuple!(int, string)'in eşdeğeri -struct __Çokuzlu_int_string { - int __üye_0; - string __üye_1; -} ---- - -$(P -Çokuzluların üyelerine normalde sıra numarasıyla erişilir. Bu açıdan bakıldığında her bir üyesi farklı türden olabilen bir dizi gibi düşünülebilir: -) - ---- - writeln(çokuzlu$(HILITE [0])); - writeln(çokuzlu$(HILITE [1])); ---- - -$(P -Çıktısı: -) - -$(SHELL -42 -merhaba -) - -$(H6 Üye isimleri) - -$(P -Çokuzlu üyelerine sıra numarasıyla erişilmesi başka dillerde de yaygındır. Phobos çokuzlularında ise üyelere nitelik isimleriyle de erişilebilir. Bunun için çokuzlunun $(C tuple()) kolaylık işlevi ile değil, $(C Tuple) şablonu ile açıkça oluşturulması gerekir. Üyelerin türleri ve nitelik isimleri çiftler halinde belirtilirler: -) - ---- - auto çokuzlu = Tuple!(int, "sayı", - string, "mesaj")(42, "merhaba"); ---- - -$(P -Yukarıdaki tanım, $(C int) türündeki 0 numaralı üyeye ayrıca $(C .sayı) niteliğiyle ve $(C string) türündeki 1 numaralı üyeye ayrıca $(C .mesaj) niteliğiyle erişilmesini sağlar: -) - ---- - writeln("0 sıra numarasıyla : ", çokuzlu[0]); - writeln(".sayı niteliği olarak : ", çokuzlu$(HILITE .sayı)); - writeln("1 sıra numarasıyla : ", çokuzlu[1]); - writeln(".mesaj niteliği olarak: ", çokuzlu$(HILITE .mesaj)); ---- - -$(P -Çıktısı: -) - -$(SHELL -0 sıra numarasıyla : 42 -.sayı niteliği olarak : 42 -1 sıra numarasıyla : merhaba -.mesaj niteliği olarak: merhaba -) - -$(H6 $(IX .expand) Üyelerin değer listesi olarak açılmaları) - -$(P -Çokuzlu nesneleri üyelerinin değerlerinden oluşan liste olarak açılabilir ve örneğin o türlere uyan bir işlevi çağırırken kullanılabilir. Bu, $(C .expand) niteliği ile veya çokuzlu nesnesi dilimlenerek sağlanır: -) - ---- -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 ç = tuple(1, "2", 3.3, '4'); - - // İkisi de foo(1, "2", 3.3, '4')'ün eşdeğeridir: - foo(ç$(HILITE.expand)); - foo(ç$(HILITE[])); - - // bar(1, 3.3, '4')'ün eşdeğeridir: - bar(ç$(HILITE[0]), ç$(HILITE[$-2..$])); -} ---- - -$(P -Yukarıdaki çokuzlu $(C int), $(C string), $(C double), ve $(C char) türündeki değerlerden oluşmaktadır. Bu yüzden, bütün üyelerinin açılmasından oluşan liste $(C foo())'nun parametre listesine uyar ve o yüzden $(C foo()) çağrılırken kullanılabilir. $(C bar()) çağrılırken ise yalnızca ilk üyesinin ve son iki üyesinin değerlerinden oluşan üç değer gönderilmektedir. -) - -$(P -Üyelerin türleri aynı dizinin elemanı olabilecek kadar uyumlu olduklarında çokuzlunun açılımı bir diziyi ilklerken de kullanılabilir: -) - ---- -import std.stdio; -import std.typecons; - -void main() { - auto çokuzlu = tuple(1, 2, 3); - auto dizi = [ çokuzlu$(HILITE .expand), çokuzlu$(HILITE []) ]; - writeln(dizi); -} ---- - -$(P -Yukarıdaki örnek dizi üç $(C int)'ten oluşan çokuzlunun iki kere açılmasından oluşmaktadır: -) - -$(SHELL -[1, 2, 3, 1, 2, 3] -) - -$(H6 $(IX foreach, derleme zamanı) $(IX derleme zamanı foreach) Derleme zamanı $(C foreach)'i) - -$(P -Hem dizi gibi düşünülebildiklerinden hem de değerleri liste olarak açılabildiğinden çokuzlular $(C foreach) ile de kullanılabilirler: -) - ---- - auto çokuzlu = tuple(42, "merhaba", 1.5); - - foreach (i, üye; $(HILITE çokuzlu)) { - writefln("%s: %s", i, üye); - } ---- - -$(P -Çıktısı: -) - -$(SHELL -0: 42 -1: merhaba -2: 1.5 -) - -$(P -$(IX döngü açılımı) Yukarıdaki koddaki $(C foreach)'in çalışma zamanında işletildiği düşünülebilir; ancak, bu doğru değildir. Çokuzlu üyeleri üzerinde işletilen $(C foreach)'ler aslında döngü değil, döngünün içeriğinin üye adedi kadar tekrarlanmasından oluşan bir $(I döngü açılımıdır). Dolayısıyla, yukarıdaki $(C foreach) döngüsü aşağıdaki üç kod bloğunun eşdeğeridir: -) - ---- - { - enum size_t i = 0; - $(HILITE int) üye = çokuzlu[i]; - writefln("%s: %s", i, üye); - } - { - enum size_t i = 1; - $(HILITE string) üye = çokuzlu[i]; - writefln("%s: %s", i, üye); - } - { - enum size_t i = 2; - $(HILITE double) üye = çokuzlu[i]; - writefln("%s: %s", i, üye); - } ---- - -$(P -Bunun nedeni, her çokuzlu üyesinin farklı türden olabilmesi ve dolayısıyla döngünün her ilerletilişinde döngü kapsamındaki kodların farklı olarak derlenmesinin gerekmesidir. -) - -$(H6 Birden fazla değer döndürmek) - -$(P -$(IX findSplit, std.algorithm) Çokuzlular işlevlerin tek değer döndürebilme yetersizliklerine karşı basit bir çözüm olarak görülebilirler. Örneğin, $(C std.algorithm) modülündeki $(C findSplit()) bir aralığı başka bir aralık içinde arar ve arama sonucunda üç bilgi üretir: bulunan aralıktan öncesi, bulunan aralık, ve bulunan aralıktan sonrası. $(C findSplit()), bu üç parça bilgiyi bir çokuzlu olarak döndürür: -) - ---- -import std.algorithm; - -// ... - - auto bütünAralık = "merhaba"; - auto aranan = "er"; - - auto sonuç = findSplit(bütünAralık, aranan); - - writeln("öncesi : ", sonuç[0]); - writeln("bulunan: ", sonuç[1]); - writeln("sonrası: ", sonuç[2]); ---- - -$(P -Çıktısı: -) - -$(SHELL -öncesi : m -bulunan: er -sonrası: haba -) - -$(P -Birden fazla değer döndürmek için bir yapı nesnesi de döndürülebileceğini biliyorsunuz: -) - ---- -struct Sonuç { - // ... -} - -$(HILITE Sonuç) işlev() { - // ... -} ---- - -$(H5 $(IX AliasSeq, std.meta) $(C AliasSeq)) - -$(P -$(C std.meta) modülünde tanımlı olan $(C AliasSeq) normalde derleyiciye ait olan ve hep üstü kapalı olarak geçen ve yukarıda da rastladığımız bir kavramı programcının kullanımına sunar: virgüllerle ayrılmış değer listesi. Aşağıda bunun üç örneğini görmekteyiz: -) - -$(UL -$(LI İşlev parametre değeri listesi) -$(LI Şablon parametre değeri listesi) -$(LI Dizi hazır değeri eleman listesi) -) - -$(P -Bu üç farklı listenin örnekleri şöyle gösterilebilir: -) - ---- - işlev($(HILITE 1, "merhaba", 2.5)); // işlev parametreleri - auto n = YapıŞablonu!($(HILITE char, long))(); // şablon parametreleri - auto d = [ $(HILITE 1, 2, 3, 4) ]; // dizi elemanları ---- - -$(P -Daha yukarıda örneklerini gördüğümüz $(C Tuple) üyelerinin değer listesi olarak açılabilmeleri de aslında $(C AliasSeq) tarafından sağlanır. -) - -$(P -$(IX TypeTuple, std.typetuple) $(C AliasSeq)'in adı "sıralanmış isimler" olarak açıklanabilen "alias sequence"tan gelir ve türler, değerler, ve isimler içerir. ($(C AliasSeq) ve $(C std.meta)'nın eski isimleri sırasıyla $(C TypeTuple) ve $(C std.typetuple) idi.) -) - -$(P -Bu bölümde $(C AliasSeq)'in yalnızca ya bütünüyle türlerden ya da bütünüyle değerlerden oluşan örneklerini göreceğiz. Hem türlerden hem değerlerden oluşan örneklerini bir sonraki bölüme ayıracağız. $(C AliasSeq) bir sonraki bölümde göreceğimiz $(I belirsiz sayıda parametre) alan şablonlarda da yararlıdır. -) - -$(H6 Değerlerden oluşan $(C AliasSeq)) - -$(P -Bir derleme zamanı olanağı olan $(C AliasSeq), ifade ettiği parametre listesini kendi şablon parametreleri olarak alır. Bunu üç parametre alan bir işlev çağrısında görelim: -) - ---- -import std.stdio; - -void foo(int i, string s, double d) { - writefln("foo çağrıldı: %s %s %s", i, s, d); -} ---- - -$(P -Normalde o işlevin açıkça üç parametre değeri ile çağrıldığını biliyoruz: -) - ---- - foo(1, "merhaba", 2.5); ---- - -$(P -$(C AliasSeq) o parametre değerlerini tek değişken olarak bir arada tutabilir ve işlev çağrıları sırasında otomatik olarak parametre listesi olarak açılabilir: -) - ---- -import std.meta; - -// ... - - alias parametreler = AliasSeq!(1, "merhaba", 2.5); - foo($(HILITE parametreler)); ---- - -$(P -Her ne kadar bu sefer tek değer alıyormuş gibi görünse de, yukarıdaki $(C foo) çağrısı öncekinin eşdeğeridir ve her iki yöntem de aynı çıktıyı üretir: -) - -$(SHELL -foo çağrıldı: 1 merhaba 2.5 -) - -$(P -$(C parametreler)'in $(C auto) anahtar sözcüğü ile değişken olarak değil, $(C alias) sözcüğü ile belirli bir $(C AliasSeq)'in takma ismi olarak tanımlandığına dikkat edin. $(C auto) anahtar sözcüğünün kullanılabildiği durumlar olsa da bu bölümdeki örneklerde yalnızca takma isim olarak göreceğiz. -) - -$(P -Yukarıda $(C Tuple) başlığı altında da gördüğümüz gibi, değerlerin hepsi aynı türden veya daha genel olarak $(I uygun) türlerden olduklarında, $(C AliasSeq) bir dizi hazır değerinin elemanlarını da temsil edebilir: -) - ---- - alias elemanlar = AliasSeq!(1, 2, 3, 4); - auto dizi = [ $(HILITE elemanlar) ]; - assert(dizi == [ 1, 2, 3, 4 ]); ---- - -$(H6 Türlerden oluşan $(C AliasSeq)) - -$(P -$(C AliasSeq)'in parametreleri türlerin $(I kendilerinden) de oluşabilir. Yani, belirli bir türün belirli bir değeri değil, $(C int) gibi bir türün $(I kendisi) olabilir. -) - -$(P -Tür içeren $(C AliasSeq)'ler şablonlarla kullanılmaya elverişlidir. Bunun bir örneğini görmek için iki parametreli bir yapı şablonu düşünelim. Bu şablonun ilk parametresi yapının bir dizisinin eleman türünü, ikincisi de yapının bir işlevinin dönüş türünü belirliyor olsun: -) - ---- -import std.conv; - -struct S($(HILITE ElemanTürü, SonuçTürü)) { - ElemanTürü[] dizi; - - SonuçTürü uzunluk() { - return to!SonuçTürü(dizi.length); - } -} - -void main() { - auto s = S!$(HILITE (double, int))([ 1, 2, 3 ]); - auto u = s.uzunluk(); -} ---- - -$(P -Yukarıda bu şablonun $(C (double, int)) türleri ile kullanıldığını görüyoruz. Aynı amaç için iki tür içeren bir $(C AliasSeq)'ten de yararlanılabilir: -) - ---- -import std.meta; - -// ... - - alias Türler = AliasSeq!(double, int); - auto s = S!$(HILITE Türler)([ 1, 2, 3 ]); ---- - -$(P -Yukarıda $(C S) şablonu her ne kadar tek şablon parametresi ile kullanılıyor gibi görünse de, $(C Türler) otomatik olarak açılır ve sonuçta $(C S!(double, int)) türünün aynısı elde edilir. -) - -$(P -$(C AliasSeq) özellikle $(I belirsiz sayıda parametre) alan şablonlarda yararlıdır. Bunun örneklerini bir sonraki bölümde göreceğiz. -) - -$(H6 Dizi gibi kullanılması) - -$(P -$(C AliasSeq)'in kurulduğu şablon parametrelerine dizi erişim işleci ile erişilebilir: -) - ---- - alias parametreler = AliasSeq!(1, "merhaba", 2.5); - assert(parametreler$(HILITE [0]) == 1); - assert(parametreler$(HILITE [1]) == "merhaba"); - assert(parametreler$(HILITE [2]) == 2.5); ---- - -$(P -$(C AliasSeq) yine dizilerde olduğu gibi dilimleme işleminde de kullanılabilir. Yukarıdaki örneklerde kullanılan $(C AliasSeq)'in son iki parametresine uyan bir işlev olduğunu düşünelim. Böyle bir işlev yukarıdaki $(C parametreler)'in son iki değeri dilimlenerek çağrılabilir: -) - ---- -void bar(string s, double d) { - // ... -} - -// ... - - bar(parametreler$(HILITE [$-2 .. $])); ---- - -$(H6 $(C foreach) ile kullanılması) - -$(P -Yukarıda gördüğümüz $(C Tuple)'da olduğu gibi, $(C AliasSeq)'in $(C foreach) ile kullanılmasında da çalışma zamanında işletilen bir döngü $(I oluşmaz); döngünün içeriği parametre listesindeki her eleman için derleme zamanında kod olarak açılır ve sonuçta o açılım derlenir. -) - -$(P -Bunun örneğini yukarıdaki $(C S) yapı şablonu için yazılmış olan bir birim testinde görelim. Aşağıdaki kod bu yapı şablonunun eleman türü olarak $(C int), $(C long), ve $(C float) kullanılabildiğini test ediyor ($(C SonuçTürü) ise hep $(C size_t)): -) - ---- -unittest { - alias Türler = AliasSeq!($(HILITE int, long, float)); - - foreach (Tür; $(HILITE Türler)) { - auto s = S!(Tür, size_t)([ Tür.init, Tür.init ]); - assert(s.uzunluk() == 2); - } -} ---- - -$(P -Yukarıdaki koddaki $(C Tür) değişkeni sırasıyla $(C int), $(C long), ve $(C float) türünü temsil eder ve sonuçta $(C foreach) döngüsü aşağıdaki eşdeğer döngü açılımı olarak derlenir: -) - ---- - { - auto s = S!($(HILITE int), size_t)([ $(HILITE int).init, $(HILITE int).init ]); - assert(s.uzunluk() == 2); - } - { - auto s = S!($(HILITE long), size_t)([ $(HILITE long).init, $(HILITE long).init ]); - assert(s.uzunluk() == 2); - } - { - auto s = S!($(HILITE float), size_t)([ $(HILITE float).init, $(HILITE float).init ]); - assert(s.uzunluk() == 2); - } ---- - -$(H5 $(IX .tupleof) $(C .tupleof) niteliği) - -$(P -$(C .tupleof) bir türün veya bir nesnenin bütün üyelerini bir çokuzlu olarak elde etmeye yarar. Aşağıdaki örnek $(C .tupleof) niteliğini bir türe uyguluyor: -) - ---- -import std.stdio; - -struct Yapı { - int numara; - string dizgi; - double kesirli; -} - -void main() { - foreach (i, ÜyeTürü; typeof($(HILITE Yapı.tupleof))) { - writefln("Üye %s:", i); - writefln(" tür : %s", ÜyeTürü.stringof); - - string isim = $(HILITE Yapı.tupleof)[i].stringof; - writefln(" isim: %s", isim); - } -} ---- - -$(P -$(C Yapı.tupleof)'un yukarıda iki yerde geçtiğine dikkat edin. İlkinde eleman türleri $(C typeof) ile elde edilmekte ve her tür $(C foreach)'in $(C ÜyeTürü) değişkeni olarak belirmektedir. İkincisinde ise yapı üyesinin ismi $(C Yapı.tupleof[i].stringof) ile elde edilmektedir. -) - -$(SHELL -Üye 0: - tür : int - isim: numara -Üye 1: - tür : string - isim: dizgi -Üye 2: - tür : double - isim: kesirli -) - -$(P -$(C .tupleof) bir nesneye uygulandığında ise o nesnenin üyelerinin değerlerini çokuzlu olarak elde etmeye yarar: -) - ---- - auto nesne = Yapı(42, "merhaba", 1.5); - - foreach (i, üye; $(HILITE nesne.tupleof)) { - writefln("Üye %s:", i); - writefln(" tür : %s", typeof(üye).stringof); - writefln(" değer: %s", üye); - } ---- - -$(P -Bu durumda $(C üye) isimli döngü değişkeni sırasıyla üyelerin değerlerini temsil eder: -) - -$(SHELL -Üye 0: - tür : int - değer: 42 -Üye 1: - tür : string - değer: merhaba -Üye 2: - tür : double - değer: 1.5 -) - -$(P -Buradaki önemli bir ayrıntı, nesneye uygulanan $(C .tupleof) çokuzlusunun üyelerin değerlerinden değil, üyelerin kendilerinden oluşmasıdır. Bir anlamda, her çokuzlu üyesi temsil ettiği asıl üyenin referansıdır. -) - -$(H5 Özet) - -$(UL - -$(LI $(C tuple()) yapı benzeri değişkenler oluşturur.) - -$(LI Açıkça $(C Tuple) kullanıldığında üyelere isim verilebilir.) - -$(LI Üyeler $(C .expand) niteliği ile veya dilimlenerek değer listesi olarak açılabilirler.) - -$(LI Çokuzluya uygulanan $(C foreach) çalışma zamanında işleyen döngü değildir, döngü açılımıdır.) - -$(LI $(C AliasSeq) parametre değer listesi gibi kavramları temsil eder.) - -$(LI $(C AliasSeq) hem değerleri hem türlerin kendilerini içerebilir.) - -$(LI $(C Tuple) ve $(C AliasSeq) dizi erişim ve dilimleme işleçlerini destekler.) - -$(LI $(C .tupleof) türlerin veya nesnelerin üyeleri ile ilgili bilgi verir.) - -) - -macros: - SUBTITLE=Çokuzlular - - DESCRIPTION=Birden fazla değişkeni hızlıca bir araya getiren tuple olanağı - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial tuple çokuzlu - -SOZLER= -$(belirsiz_sayida_parametre) -$(cokuzlu) -$(dongu_acilimi) -$(ic_olanak) -$(phobos) diff --git a/ddili/src/ders/d/const_uye_islevler.d b/ddili/src/ders/d/const_uye_islevler.d deleted file mode 100644 index 2590cf8..0000000 --- a/ddili/src/ders/d/const_uye_islevler.d +++ /dev/null @@ -1,302 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(CH4 const ref) Parametreler ve $(CH4 const) Üye İşlevler) - -$(P -Bu bölümde üye işlevlerin $(C immutable) nesnelerle de kullanılabilmeleri için nasıl $(C const) olarak işaretlenmeleri gerektiğini göreceğiz. Bu bölümde her ne kadar yalnızca yapıları kullanıyor olsak da $(C const) üye işlevler sınıflar için de aynen geçerlidir. -) - -$(H5 $(C immutable) nesneler) - -$(P -Şimdiye kadarki bazı örneklerde $(C immutable) değişkenler ve nesneler tanımlamış ve $(C immutable) anahtar sözcüğünün nesnelerin değiştirilemez olmalarını sağladığını görmüştük: -) - ---- - immutable okumaSaati = GününSaati(15, 0); ---- - -$(P -$(C okumaSaati) değiştirilemez: -) - ---- - okumaSaati = GününSaati(16, 0); $(DERLEME_HATASI) - okumaSaati.dakika += 10; $(DERLEME_HATASI) ---- - -$(P -Derleyici $(C immutable) nesneye yeni bir değer atanmasına veya bir üyesinin değiştirilmesine izin vermez. Zaten $(C immutable) olarak işaretlemenin amacı da budur: Bazı nesnelerin değerlerinin değişmemesi program doğruluğu açısından önemli olabilir. -) - -$(H5 $(C const) olmayan $(C ref) parametreler) - -$(P -Bu kavramı daha önce $(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) görmüştük. $(C ref) parametrelerin işlev içinde değiştirilmemeleri yönünde bir kısıtlama yoktur. $(C ref) bir parametresini değiştirmiyor bile olsa, bunun garantisini vermediği için böyle bir işleve $(C immutable) nesne gönderilemez: -) - ---- -// süre'yi değiştirmediği halde const olarak işaretlenmemiş -int toplamSaniye(ref Süre süre) { - return 60 * süre.dakika; -} -// ... - $(HILITE immutable) ısınmaSüresi = Süre(3); - toplamSaniye(ısınmaSüresi); $(DERLEME_HATASI) ---- - -$(P -Derleyici $(C immutable) olan $(C ısınmaSüresi) nesnesinin $(C toplamSaniye) işlevine gönderilmesine izin vermez, çünkü $(C toplamSaniye) işlevi parametresinde değişiklik yapmayacağı garantisini vermemektedir. -) - -$(H5 $(IX const ref) $(IX ref const) $(IX parametre, const ref) $(C const ref) parametreler) - -$(P -$(C const ref) olarak işaretlenen bir parametre, $(I o işlev içinde değiştirilmeyecek) demektir: -) - ---- -int toplamSaniye(const ref Süre süre) { - return 60 * süre.dakika; -} -// ... - immutable ısınmaSüresi = Süre(3); - toplamSaniye(ısınmaSüresi); // ← şimdi derlenir ---- - -$(P -Parametresini $(C const) olarak işaretleyen işlev $(I o parametrede değişiklik yapmayacağı) garantisini vermiş olduğu için işleve $(C immutable) değişkenler de gönderilebilir. -) - -$(P -Derleyici $(C const) parametrenin değiştirilmesine izin vermez: -) - ---- -int toplamSaniye(const ref Süre süre) { - süre.dakika = 7; $(DERLEME_HATASI) -// ... -} ---- - -$(P -$(IX in ref) $(IX ref in) $(IX parametre, in ref) $(C const ref) yerine $(C in ref) de kullanılabilir. $(LINK2 /ders/d/islev_parametreleri.html, İlerideki bir bölümde) göreceğimiz gibi, $(C in) parametrenin yalnızca giriş bilgisi olarak kullanıldığını ve bu yüzden değiştirilemeyeceğini bildirir: -) - ---- -int toplamSaniye($(HILITE in ref) Süre süre) { - // ... -} ---- - -$(H5 $(C const) olmayan üye işlevler) - -$(P -Nesneleri değiştirmenin başka bir yolu üye işlevlerdir. Bunu daha önce $(C GününSaati.ekle) işlevinde görmüştük. O üye işlev, üzerinde çağrıldığı nesneyi ona bir $(C Süre) ekleyerek değiştiriyordu: -) - ---- -struct GününSaati { -// ... - void ekle(in Süre süre) { - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - } -// ... -} -// ... - auto başlangıç = GününSaati(5, 30); - başlangıç.ekle(Süre(30)); // başlangıç değişir ---- - -$(H5 $(IX const, üye işlev) $(C const) üye işlevler) - -$(P -Bazı üye işlevler ise üzerinde çağrıldıkları nesnede değişiklik yapmazlar: -) - ---- -struct GününSaati { -// ... - string toString() { - return format("%02s:%02s", saat, dakika); - } -// ... -} ---- - -$(P -$(C toString)'in tek işi nesneyi $(C string) olarak ifade etmektir ve zaten o kadar olmalıdır; nesnenin kendisini değiştirmez. -) - -$(P -Üye işlevlerin nesnede bir değişiklik yapmayacakları garantisi parametre listesinden sonra yazılan $(C const) sözcüğü ile verilir: -) - ---- -struct GününSaati { -// ... - string toString() $(HILITE const) { - return format("%02s:%02s", saat, dakika); - } -} ---- - -$(P -O $(C const), nesnenin o işlev içinde değiştirilmeyeceği anlamına gelir. -) - -$(P -Böylece $(C toString) üye işlevi $(C immutable) nesneler üzerinde de çağrılabilir. Aksi halde nesnenin değiştirilmeyeceğinin garantisi bulunmadığından, $(C immutable) nesneler üzerinde çağrılamama gibi yapay bir kısıtlamayla karşı karşıya kalınırdı: -) - ---- -struct GününSaati { -// ... - // const olarak işaretlenmemiş (yanlış tasarım) - string toString() { - return format("%02s:%02s", saat, dakika); - } -} -// ... - $(HILITE immutable) başlangıç = GününSaati(5, 30); - writeln(başlangıç); // GününSaati.toString() çağrılmaz! ---- - -$(P -Çıktısı beklenendiği gibi $(C 05:30) değil, derleyicinin çağırdığı genel bir işlevin çıktısıdır: -) - -$(SHELL -immutable(GününSaati)(5, 30) -) - -$(P -$(C toString) $(C immutable) bir nesne üzerinde açıkça çağrıldığında ise bir derleme hatası oluşur: -) - ---- - auto dizgiOlarak = başlangıç.toString(); $(DERLEME_HATASI) ---- - -$(P -Bu açıdan bakıldığında şimdiye kadarki bölümlerde gördüğümüz $(C toString) üye işlevleri yanlış tasarlanmışlardır; aslında onların da $(C const) olarak işaretlenmeleri gerekirdi. -) - -$(P $(I Not: İşlevin nesnede değişiklik yapmayacağını garanti eden $(C const) anahtar sözcüğü aslında işlevin tanımından önce de yazılabilir:) -) - ---- - // üsttekiyle aynı anlamda - $(HILITE const) string toString() { - return format("%02s:%02s", saat, dakika); - } ---- - -$(P -$(I Öyle yazıldığında dönüş türüne aitmiş gibi yanlış bir anlam verebildiği için $(C const) anahtar sözcüğünü bu biçimde değil, daha yukarıda gösterildiği gibi parametre listesinden sonra yazmanızı öneririm.) -) - - -$(H5 $(IX inout, üye işlev) $(C inout) üye işlevler) - -$(P -$(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) gördüğümüz gibi, $(C inout) parametrenin değişmezlik bilgisini işlevin çıkış türüne aktarır. -) - -$(P -Benzer biçimde, $(C inout) olarak tanımlanmış olan bir üye işlev de $(I nesnenin) değişmezlik bilgisini işlevin çıkış türüne aktarır: -) - ---- -import std.stdio; - -struct Topluluk { - int[] elemanlar; - - $(HILITE inout)(int)[] başTarafı(size_t n) $(HILITE inout) { - return elemanlar[0 .. n]; - } -} - -void main() { - { - // immutable bir Topluluk nesnesi - auto topluluk = $(HILITE immutable)(Topluluk)([ 1, 2, 3 ]); - auto dilim = topluluk.başTarafı(2); - writeln(typeof(dilim).stringof); - } - { - // const bir Topluluk nesnesi - auto topluluk = $(HILITE const)(Topluluk)([ 1, 2, 3 ]); - auto dilim = topluluk.başTarafı(2); - writeln(typeof(dilim).stringof); - } - { - // Değişebilen bir Topluluk nesnesi - auto topluluk = Topluluk([ 1, 2, 3 ]); - auto dilim = topluluk.başTarafı(2); - writeln(typeof(dilim).stringof); - } -} ---- - -$(P -Farklı değişmezliğe sahip üç nesnenin döndürdüğü üç dilim o nesnelerin değişmezliklerine sahiptir: -) - -$(SHELL -$(HILITE immutable)(int)[] -$(HILITE const)(int)[] -int[] -) - -$(P -$(C const) ve $(C immutable) nesneler üzerinde de çağrılabilmeleri gerektiğinden $(C inout) üye işlevler derleyici tarafından $(C const) olarak derlenirler. -) - -$(H5 Ne zaman kullanmalı) - -$(UL - -$(LI -İşlev içinde değiştirilmeyecek olan parametreleri $(C const) olarak işaretleyin. Böylece o işlevlere $(C immutable) değişkenler de gönderilebilir. -) - -$(LI -$(C toString) gibi nesnede değişiklik yapmayan üye işlevleri her zaman için $(C const) olarak işaretleyin: - ---- -struct GününSaati { -// ... - string toString() $(HILITE const) { - return format("%02s:%02s", saat, dakika); - } -} ---- - -$(P -Böylece yapının ve sınıfın kullanışlılığı gereksizce kısıtlanmamış olur. Bundan sonraki bölümlerdeki kodları buna uygun olarak tasarlayacağız. -) - -) - -) - -Macros: - SUBTITLE=const ref Parametreler ve const Üye İşlevler - - DESCRIPTION=D dilinde const ref parametreler; ve yapıların ve sınıfların üye işlevlerinin const nesnelerle de çalışabilmek için const olarak işaretlenmeleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial yapı yapılar struct const üye işlev üye fonksiyon const ref parametre - -SOZLER= -$(degismez) -$(referans) -$(sabit) -$(sinif) -$(uye_islev) -$(yapi) diff --git a/ddili/src/ders/d/const_ve_immutable.d b/ddili/src/ders/d/const_ve_immutable.d deleted file mode 100644 index ae5eae2..0000000 --- a/ddili/src/ders/d/const_ve_immutable.d +++ /dev/null @@ -1,783 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX const) $(IX immutable) $(IX değişmezlik) Değişmezlik) - -$(P -Değişkenlerin programla ilgili olan kavramları temsil ettiklerini gördük. Temsil ettikleri kavramlar arasındaki etkileşimleri bu değişkenlerin değerlerini değiştirerek sağlarız: -) - ---- - // Parayı öde - toplamFiyat = fiyatıHesapla(fiyatListesi); - cüzdandakiMiktar $(HILITE -=) toplamFiyat; - bakkaldakiMiktar $(HILITE +=) toplamFiyat; ---- - -$(P -Bu açıdan bakıldığında değişkenler olmadan olmaz; $(I değişebilme) kavramı programların iş yapabilmeleri için önemlidir. Buna rağmen, değişimin uygun olmadığı durumlar da vardır: -) - -$(UL - -$(LI Bazı kavramlar zaten $(I değişmezdirler). Örneğin haftadaki gün sayısı 7'dir, matematikteki $(I pi) (π) sabittir, bir programın desteklediği dil sayısı programın çalıştığı sürece değişmeyecektir (örneğin yalnızca Türkçe ve İngilizce'dir), vs. -) - -$(LI Koddaki bütün işlemlerin her değişkeni değiştirebilecek kadar esnek olmaları, hangi işlemlerin hangi değişkenleri değiştirdiklerini fazla serbest bıraktığı için kodun okunması ve geliştirilmesi güçleşir. - -$(P -Örneğin $(C emekliEt(banka, çalışan)) gibi bir işlev çağrısı sonucunda $(C çalışan)'ın $(C banka)'dan emekli edildiğini anlayabiliriz. Ancak, bu işlevden dönüldüğünde bu iki değişkenin değişip değişmeyeceklerini bilmek de önemlidir. Yoksa her işlev çağrısına şüpheyle bakmaya başlarız. -) - -$(P -Herhalde $(C banka)'nın eleman sayısı azalacaktır; peki $(C çalışan) değişkeni de değişecek midir; örneğin bu işlev $(C çalışan) değişkeninin içindeki bir $(C enum) değişkeni de $(C ÇalışmaDurumu.emekli) olarak değiştirecek midir? -) - -) - -) - -$(P -Bazı kavramların bazı işlemler sırasında değişmeyecekleri güvencesine gerek duyarız. Bazı başka dillerde bulunmayan bu $(I kesinlikle değişmezlik) kavramı hata çeşitlerinden bazılarının olasılığını düşüren yararlı bir olanaktır. -) - -$(P -$(I Değişmezlik) kavramlarını belirleyen iki anahtar sözcüğün İngilizce'deki anlamları da birbirlerine çok yakındır: $(C const), "sabit, değişmez" anlamına gelen "constant"ın kısasıdır. $(C immutable) ise, "değişebilen" anlamına gelen "mutable"ın karşıt anlamlısıdır. İkisi de "değişmezlik" anlamını taşıyor olsalar da, $(C immutable) ve $(C const) sözcüklerinin görevleri farklıdır ve bazı durumlarda birbirleriyle uyumsuzdurlar. -) - -$(P -$(IX tür nitelendirici) $(IX nitelendirici, tür) $(C const), $(C immutable), $(C inout), ve $(C shared) $(I tür nitelendiricisidirler). ($(LINK2 /ders/d/islev_parametreleri.html, $(C inout)) ve $(LINK2 /ders/d/es_zamanli_shared.html, $(C shared)) anahtar sözcüklerini ilerideki bölümlerde göreceğiz.) -) - -$(H5 Değişmezler) - -$(P -Her ne kadar kulağa anlamsız gelse de, bu başlık yerine "Değişmez değişken" de düşünebilirdi. Böyle anlamsız ifadeler İngilizce kaynaklarda da bulunur: "constant variable" veya "immutable variable" da kulağa aynı derecede yanlış gelen terimlerdir. -) - -$(P -Kesinlikle değişmeyecek olan değişkenler üç farklı biçimde tanımlanabilirler. -) - -$(H6 $(IX enum) $(C enum) değişkenler) - -$(P -Bazı sabit değişkenlerin $(C enum) olarak tanımlanabildiklerini $(LINK2 /ders/d/enum.html, $(C enum) bölümünde) görmüştük: -) - ---- - enum dosyaİsmi = "liste.txt"; ---- - -$(P -Derleme zamanında hesaplanabildikleri sürece $(C enum) değişkenler işlev çağrılarının sonuçları ile de ilklenebilirler: -) - ---- -int satırAdedi() { - return 42; -} - -int sütunAdedi() { - return 7; -} - -string isim() { - return "liste"; -} - -void main() { - enum dosyaİsmi = isim() ~ ".txt"; - enum toplamKare = satırAdedi() * sütunAdedi(); -} ---- - -$(P -Derleyici $(C enum) değişkenlerin değiştirilmelerine izin vermez: -) - ---- - ++toplamKare; $(DERLEME_HATASI) ---- - -$(P -Değişmezlik kavramını sağlayan çok etkili bir olanak olmasına karşın $(C enum) ancak değerleri derleme zamanında bilinen veya hesaplanabilen sabitler için kullanılabilir. -) - -$(P -Bekleneceği gibi, program derlenirken $(C enum) değişkenlerin yerlerine onların değerleri kullanılır. Örneğin, şöyle bir $(C enum) tanımı ve onu kullanan iki ifade olsun: -) - ---- - enum i = 42; - writeln(i); - foo(i); ---- - -$(P -Yukarıdaki kod, $(C i)'nin yerine onun değeri olan $(C 42)'nin yazılmasının eşdeğeridir: -) - ---- - writeln(42); - foo(42); ---- - -$(P -Bir $(C enum) değişkenin yerine değerinin kullanılıyor olması $(C int) gibi basit türler için normal olarak kabul edilmelidir. Ancak, $(C enum) değişkenlerin dizi veya eşleme tablosu olarak kullanılmalarının gizli bir bedeli vardır: -) - ---- - enum a = [ 42, 100 ]; - writeln(a); - foo(a); ---- - -$(P -$(C a)'nın yerine değerini yerleştirdiğimizde derleyicinin derleyeceği asıl kodun aşağıdaki gibi olduğunu görürüz: -) - ---- - writeln([ 42, 100 ]); // bir dizi oluşturulur - foo([ 42, 100 ]); // başka bir dizi oluşturulur ---- - -$(P -Yukarıdaki koddaki gizli bedel, her ifade için farklı bir dizi oluşturuluyor olmasıdır. Bu yüzden, birden fazla yerde kullanılacak olan dizilerin ve eşleme tablolarının $(C immutable) değişkenler olarak tanımlanmaları çoğu duruma daha uygundur. -) - -$(P -$(C enum) değişkenler işlev çağrılarının sonuçları ile de ilklenebilirler: -) - ---- - enum a = diziYap(); // derleme zamanında çağrılır ---- - -$(P -Bu, D'nin $(I derleme zamanında işlev işletme (CTFE)) olanağı sayesindedir. Bu olanağı $(LINK2 /ders/d/islevler_diger.html, ilerideki bir bölümde) göreceğiz. -) - - -$(H6 $(IX değişken, immutable) $(C immutable) değişkenler) - -$(P -Bu anahtar sözcük de programın çalışması sırasında değişkenin $(I kesinlikle) değişmeyeceğini bildirir. $(C enum)'dan farklı olarak, $(C immutable) olarak işaretlenen değişkenlerin değerleri çalışma zamanında da hesaplanabilir. -) - -$(P -Aşağıdaki program $(C enum) ve $(C immutable) anahtar sözcüklerinin kullanımlarının farklarını gösteriyor. Tuttuğu sayıyı kullanıcının tahmin etmesini bekleyen bu programda tutulan sayı derleme zamanında bilinemediği için $(C enum) olarak tanımlanamaz. Ancak, bir kere seçildikten sonra değerinin değişmesi istenmeyeceğinden ve hatta değişmesi bir hata olarak kabul edileceğinden bu değişkenin $(C immutable) olarak işaretlenmesi uygun olur. -) - -$(P -Aşağıdaki program kullanıcının tahminini okurken yine bir önceki bölümde tanımladığımız $(C sayıOku) işlevinden yararlanıyor: -) - ---- -import std.stdio; -import std.random; - -int sayıOku(string mesaj) { - int sayı; - write(mesaj, "? "); - readf(" %s", &sayı); - return sayı; -} - -void main() { - enum enAz = 1; - enum enÇok = 10; - - $(HILITE immutable) sayı = uniform(enAz, enÇok + 1); - - writefln("%s ile %s arasında bir sayı tuttum.", - enAz, enÇok); - - auto doğru_mu = false; - while (!doğru_mu) { - $(HILITE immutable) tahmin = sayıOku("Tahmininiz"); - doğru_mu = (tahmin == sayı); - } - - writeln("Doğru!"); -} ---- - -$(P -Gözlemler: -) - -$(UL - -$(LI $(C enAz)'ın ve $(C enÇok)'un değerleri programın derlenmesi sırasında bilindiklerinden ve bir anlamda bu programın davranışının değişmez parçaları olduklarından $(C enum) olarak tanımlanmışlardır. -) - -$(LI Rasgele seçilmiş olan $(C sayı) değerinin ve kullanıcıdan okunan her $(C tahmin) değerinin programın işleyişi sırasında değişmeleri doğru olmayacağından onlar $(C immutable) olarak tanımlanmışlardır. -) - -$(LI O değişkenlerin tanımları sırasında türlerinin açıkça belirtilmediğine dikkat edin. $(C auto)'da olduğu gibi, $(C enum) ve $(C immutable) anahtar sözcükleri de türün sağ tarafın değerinden çıkarsanması için yeterlidir. -) - -) - -$(P -Program içinde açıkça $(C immutable(int)) diye parantezle yazılması gerekmese de $(C immutable) türün bir parçasıdır. Aşağıdaki program üç farklı biçimde tanımlanmış olan değişkenlerin türlerinin tam isimlerinin aynı olduklarını gösteriyor: -) - ---- -import std.stdio; - -void main() { - immutable çıkarsanarak = 0; - immutable int türüyle = 1; - immutable(int) tamOlarak = 2; - - writeln(typeof(çıkarsanarak).stringof); - writeln(typeof(türüyle).stringof); - writeln(typeof(tamOlarak).stringof); -} ---- - -$(P -Üçünün de asıl tür ismi $(C immutable)'ı da içerir ve parantezlidir: -) - -$(SHELL -immutable(int) -immutable(int) -immutable(int) -) - -$(P -Parantezlerin içindeki tür önemlidir. Bunu aşağıda dilimin veya elemanlarının değişmezliği konusunda göreceğiz. -) - -$(H6 $(IX değişken, const) $(C const) değişkenler) - -$(P -Bu anahtar sözcüğün değişkenler üzerinde $(C immutable)'dan bir farkı yoktur. $(C const) değişkenler de değiştirilemezler: -) - ---- - $(HILITE const) yarısı = toplam / 2; - yarısı = 10; $(DERLEME_HATASI) ---- - -$(P -Değişkenlerin değişmezliğini belirlerken $(C const) yerine $(C immutable) kullanmanızı öneririm çünkü $(C immutable) değişkenler, parametrenin özellikle $(C immutable) olmasını isteyen işlevlere de gönderilebilirler. Bunu aşağıda göreceğiz. -) - -$(H5 Değişmez parametreler) - -$(P -İşlevlerin parametrelerinde değişiklik yapmayacakları sözünü vermeleri ve derleyicinin bunu garanti etmesi programcılık açısından çok yararlıdır. Bunun nasıl sağlanacağına geçmeden önce dilim elemanlarının işlevler tarafından değiştirilebildiklerini görelim. -) - -$(P -$(LINK2 /ders/d/dilimler.html, Başka Dizi Olanakları bölümünden) hatırlayacağınız gibi, dilimler kendi elemanlarına sahip değillerdir, o elemanlara yalnızca erişim sağlarlar. Belirli bir anda aynı elemana erişim sağlamakta olan birden fazla dilim bulunabilir. -) - -$(P -Bu başlık altındaki örneklerde dilimlerden yararlanıyorum. Burada anlatılanlar eşleme tabloları için de geçerlidir çünkü onlar da $(I referans türleridir). -) - -$(P -İşlev parametresi olan bir dilim, işlevin çağrıldığı yerdeki dilimin kendisi değil, bir kopyasıdır: -) - ---- -import std.stdio; - -void main() { - int[] dilim = [ 10, 20, 30, 40 ]; // 1 - yarıla(dilim); - writeln(dilim); -} - -void yarıla(int[] sayılar) { // 2 - foreach (ref sayı; sayılar) { - sayı /= 2; - } -} ---- - -$(P -Yukarıdaki $(C yarıla) işlevinin işletildiği sırada aynı dört elemana erişim sağlamakta olan iki farklı dilim vardır: -) - -$(OL - -$(LI $(C main)'in içinde tanımlanmış olan ve $(C yarıla)'ya parametre olarak gönderilen $(C dilim) isimli dilim -) - -$(LI $(C yarıla)'nın parametre değeri olarak almış olduğu ve $(C main)'deki dilimle aynı dört elemana erişim sağlamakta olan $(C sayılar) isimli dilim -) - -) - -$(P -$(C foreach) döngüsünde $(C ref) anahtar sözcüğü de kullanılmış olduğundan o dört elemanın değerleri yarılanmış olur: -) - -$(SHELL -[5, 10, 15, 20] -) - -$(P -Bu örnekte de görüldüğü gibi, $(C yarıla) gibi işlevlerin kendilerine gönderilen dilimlerin elemanlarını değiştirebilmeleri kullanışlıdır çünkü zaten elemanları değiştirmek için yazılmışlardır. -) - -$(P -Derleyici, $(C immutable) değişkenlerin böyle işlevlere gönderilmelerine izin vermez: -) - ---- - $(HILITE immutable) int[] dilim = [ 10, 20, 30, 40 ]; - yarıla(dilim); $(DERLEME_HATASI) ---- - -$(P -Derleme hatası, $(C immutable(int[])) türündeki bir değişkenin $(C int[]) türündeki bir parametre değeri olarak kullanılamayacağını bildirir: -) - -$(SHELL -Error: function deneme.yarıla ($(HILITE int[]) sayılar) is not callable -using argument types ($(HILITE immutable(int[]))) -) - -$(H6 $(IX parametre, const) $(C const) parametreler) - -$(P -$(C immutable) değişkenlerin $(C yarıla)'da olduğu gibi parametrelerinde değişiklik yapan işlevlere gönderilmelerinin engellenmesi önemlidir. Ancak, parametrelerinde değişiklik yapmayan ve hatta yapmaması gereken başka işlevlere gönderilememeleri büyük bir kısıtlama olarak görülmelidir: -) - ---- -import std.stdio; - -void main() { - immutable int[] dilim = [ 10, 20, 30, 40 ]; - yazdır(dilim); $(DERLEME_HATASI) -} - -void yazdır(int[] dilim) { - writefln("%s eleman: ", dilim.length); - - foreach (i, eleman; dilim) { - writefln("%s: %s", i, eleman); - } -} ---- - -$(P -Elemanların değiştirilemiyor olmaları onların yazdırılmalarına engel olmamalıdır. $(C const) parametreler bu konuda yararlıdırlar. -) - -$(P -$(C const) anahtar sözcüğü bir değişkenin $(I belirli bir referans) (örneğin dilim) yoluyla değiştirilmeyeceğini belirler. Parametreyi $(C const) olarak işaretlemek, o dilimin elemanlarının işlev içerisinde değiştirilemeyeceğini garanti eder. Böyle bir garanti sağlandığı için program artık derlenir: -) - ---- - yazdır(dilim); // şimdi derlenir -// ... -void yazdır($(HILITE const) int[] dilim) ---- - -$(P -İşlev nasıl olsa değiştirmeyeceğine söz vermiş olduğundan, hem $(I değişebilen) değişkenler hem de $(C immutable) değişkenler işlevlere $(C const) parametreler olarak gönderilebilirler: -) - ---- - immutable int[] dilim = [ 10, 20, 30, 40 ]; - yazdır(dilim); // şimdi derlenir - - int[] değişebilenDilim = [ 7, 8 ]; - yazdır(değişebilenDilim); // bu satır da derlenir ---- - -$(P -İşlev tarafından değiştirilmediği halde $(C const) olarak tanımlanmayan bir parametre işlevin kullanışlılığını düşürür. Ek olarak, işlev parametrelerinin $(C const) olarak işaretlenmeleri programcı açısından yararlı bir bilgidir. Değişkenin belirli bir kapsam içinde değişmeyecek olduğunu bilmek kodun anlaşılmasını kolaylaştırır. Olası hataları da önler: Değişmeyecek olduğu düşünüldüğü için $(C const) olarak tanımlanmış olan bir değişkeni sonradan değiştirmeye çalışmaya derleyici izin vermez: -) - ---- -void yazdır($(HILITE const) int[] dilim) { - dilim[0] = 42; $(DERLEME_HATASI) ---- - -$(P -O durumda programcı ya yanlışlığı farkeder ya da tasarımı gözden geçirerek $(C const)'ın kaldırılması gerektiğine karar verir. -) - -$(P -$(C const) parametrelerin hem değişebilen hem de $(C immutable) değişkenleri kabul edebilmelerinin ilginç bir etkisi vardır. Bunu aşağıdaki "$(C const) parametre mi, $(C immutable) parametre mi?" başlığı altında göreceğiz. -) - -$(H6 $(IX parametre, immutable) $(C immutable) parametreler) - -$(P -$(C const) parametre yerine kullanılan asıl değişkenin $(I değişebilen) veya $(C immutable) olabildiğini gördük. $(C const) parametreler bu anlamda esneklik getirirler. -) - -$(P -Parametrenin $(C immutable) olarak işaretlenmesi ise asıl değişkenin de kesinlikle $(C immutable) olması şartını getirir. Bu açıdan bakıldığında $(C immutable) parametreler işlevin çağrıldığı nokta üzerinde kuvvetli bir talepte bulunmaktadırlar: -) - ---- -void birİşlem($(HILITE immutable) int[] dilim) { - // ... -} - -void main() { - immutable int[] değişmezDilim = [ 1, 2 ]; - int[] değişebilenDilim = [ 8, 9 ]; - - birİşlem(değişmezDilim); // bu derlenir - birİşlem(değişebilenDilim); $(DERLEME_HATASI) -} ---- - -$(P -O yüzden $(C immutable) parametreleri ancak gerçekten gereken durumlarda düşünmenizi öneririm. Şimdiye kadar öğrendiklerimiz arasında $(C immutable) parametreler yalnızca dizgi türlerinde üstü kapalı olarak geçerler. Bunu biraz aşağıda göstereceğim. -) - -$(P -$(C const) veya $(C immutable) olarak işaretlenmiş olan parametrelerin, işlevin çağrıldığı yerdeki asıl değişkeni değiştirmeme sözü verdiklerini gördük. Bu konu yalnızca referans türünden olan değişkenlerle ilgilidir. -) - -$(P -Referans ve değer türlerini bir sonraki bölümde daha ayrıntılı olarak göreceğiz. Bu bölüme kadar gördüğümüz türler arasında dilimler ve eşleme tabloları referans türleri, diğerleri ise değer türleridir. -) - -$(H6 $(IX parametre, const veya immutable) $(C const) parametre mi, $(C immutable) parametre mi?) - -$(P -Yukarıdaki iki başlığa bakıldığında esneklik getirdiği için $(C const) belirtecinin yeğlenmesinin doğru olacağı sonucuna varılabilir. Bu her zaman doğru değildir. -) - -$(P -$(C const) belirteci asıl değişkenin $(I değişebilen) mi yoksa $(C immutable) mı olduğu bilgisini işlev içerisinde belirsiz hale getirir. Bunu derleyici de bilemez. -) - -$(P -Bunun bir etkisi, $(C const) parametrelerin $(C immutable) parametre alan başka işlevlere doğrudan gönderilemeyecekleridir. Örneğin, aşağıdaki koddaki $(C foo) işlevi $(C const) parametresini $(C bar)'a gönderemez: -) - ---- -void main() { - /* Asıl değişken immutable */ - immutable int[] dilim = [ 10, 20, 30, 40 ]; - foo(dilim); -} - -/* Daha kullanışlı olabilmek için parametresini const olarak - * alan bir işlev. */ -void foo(const int[] dilim) { - bar(dilim); $(DERLEME_HATASI) -} - -/* Parametresini belki de geçerli bir nedenle immutable olarak - * alan bir işlev. */ -void bar(immutable int[] dilim) { - // ... -} ---- - -$(P -$(C bar), parametresinin $(C immutable) olmasını şart koşmaktadır. Öte yandan, $(C foo)'nun $(C const) parametresi olan $(C dilim)'in aslında $(C immutable) bir değişkene mi yoksa değişebilen bir değişkene mi bağlı olduğu bilinemez. -) - -$(P -$(I Not: Yukarıdaki kullanıma bakıldığında $(C main) içindeki asıl değişkenin $(C immutable) olduğu açıktır. Buna rağmen, derleyici her işlevi ayrı ayrı derlediği için $(C foo)'nun $(C const) parametresinin aslında $(C immutable) olduğunu bilmesi olanaksızdır. Derleyicinin gözünde $(C dilim) değişebilen de olabilir $(C immutable) da. -) -) - -$(P -Böyle bir durumda bir çözüm, $(C bar)'ı parametrenin değişmez bir kopyası ile çağırmaktır: -) - ---- -void foo(const int[] dilim) { - bar(dilim$(HILITE .idup)); -} ---- - -$(P -Kod artık derlenebiliyor olsa da asıl değişkenin zaten $(C immutable) olduğu durumda bile kopyasının alınıyor olması gereksizdir. -) - -$(P -Bütün bunlara bakıldığında belki de $(C foo)'nun parametresini $(C const) olarak almasının her zaman için doğru olmadığı düşünülebilir. Çünkü parametresini baştan $(C immutable) olarak seçmiş olsa kod kopyaya gerek kalmadan derlenebilir: -) - ---- -void foo(immutable int[] dilim) { // Bu sefer immutable - bar(dilim); // Artık kopya gerekmez -} ---- - -$(P -Ancak, bir üstteki başlıkta belirtildiği gibi, asıl değişkenin $(C immutable) olmadığı durumlarda $(C foo)'nun çağrılabilmesi için bu sefer de $(C .idup) ile kopyalanması gerekecekti: -) - ---- - foo(değişebilenDilim$(HILITE .idup)); ---- - -$(P -Görüldüğü gibi, değişmeyecek olan parametrenin türünün $(C const) veya $(C immutable) olarak belirlenmesinin kararı kolay değildir. -) - -$(P -İleride göreceğimiz şablonlar bu konuda da yararlı olabilirler. Aşağıdaki kodları kitabın bu aşamasında anlamanızı beklemesem de parametrenin $(C const) veya $(C immutable) olması kararını ortadan kaldırdığını belirtmek istiyorum. Aşağıdaki $(C foo) hem $(I değişebilen) hem de $(C immutable) değişkenlerle çağrılabilir ve yalnızca asıl değişken $(I değişebilen) olduğunda kopya bedeli öder: -) - ---- -import std.conv; -// ... - -/* Şablon olduğu için hem değişebilen hem de immutable - * değişkenlerle çağrılabilir. */ -void foo(T)(T[] dilim) { - /* Asıl değişken zaten immutable olduğunda 'to' ile - * kopyalamanın bedeli yoktur. */ - bar(to!(immutable T[])(dilim)); -} ---- - -$(H5 Bütün dilime karşılık elemanlarının değişmezliği) - -$(P -$(C immutable) bir dilimin türünün $(C .stringof) ile $(C immutable(int[])) olarak yazdırıldığını yukarıda gördük. $(C immutable)'dan sonra kullanılan parantezlerden anlaşılabileceği gibi, değişmez olan dilimin bütünüdür; o dilimde hiçbir değişiklik yapılamaz. Örneğin dilime eleman eklenemez, dilimden eleman çıkartılamaz, var olan elemanların değerleri değiştirilemez, veya dilimin başka elemanları göstermesi sağlanamaz: -) - ---- - immutable int[] değişmezDilim = [ 1, 2 ]; - değişmezDilim ~= 3; $(DERLEME_HATASI) - değişmezDilim[0] = 3; $(DERLEME_HATASI) - değişmezDilim.length = 1; $(DERLEME_HATASI) - - immutable int[] değişmezBaşkaDilim = [ 10, 11 ]; - değişmezDilim = değişmezBaşkaDilim; $(DERLEME_HATASI) ---- - -$(P -Değişmezliğin bu derece ileri götürülmesi bazı durumlara uygun değildir. Çoğu durumda önemli olan, yalnızca elemanların değiştirilmeyecekleri güvencesidir. Dilim nasıl olsa elemanlara erişim sağlayan bir olanak olduğundan o elemanlar değiştirilmedikleri sürece dilimin kendisinde oluşan değişiklikler bazı durumlarda önemli değildir. -) - -$(P -Bir dilimin yalnızca elemanlarının değişmeyeceği, $(C immutable)'dan sonraki parantezin yalnızca elemanın türünü içermesi ile sağlanır. Yukarıdaki kod buna uygun olarak değiştirilirse artık yalnızca elemanı değiştiren satır derlenemez; dilimin kendisi değiştirilebilir: -) - ---- - immutable$(HILITE (int))[] değişmezDilim = [ 1, 2 ]; - değişmezDilim ~= 3; // şimdi derlenir - değişmezDilim[0] = 3; $(DERLEME_HATASI) - değişmezDilim.length = 1; // şimdi derlenir - - immutable int[] değişmezBaşkaDilim = [ 10, 11 ]; - değişmezDilim = değişmezBaşkaDilim; // şimdi derlenir ---- - -$(P -Birbirlerine çok yakın olan bu söz dizimlerini şöyle karşılaştırabiliriz: -) - ---- - immutable int[] a = [1]; /* Ne elemanları ne kendisi - değiştirilebilen dilim */ - - immutable(int[]) b = [1]; /* Üsttekiyle aynı anlam */ - - immutable(int)[] c = [1]; /* Elemanları değiştirilemeyen - ama kendisi değiştirilebilen - dilim */ ---- - -$(P -Daha önceki bölümlerde bu konuyla üstü kapalı olarak karşılaştık. Hatırlarsanız, dizgi türlerinin asıl türlerinin $(C immutable) olduklarından bahsetmiştik: -) - -$(UL -$(LI $(C string), $(C immutable(char)[])'ın takma ismidir) -$(LI $(C wstring), $(C immutable(wchar)[])'ın takma ismidir) -$(LI $(C dstring), $(C immutable(dchar)[])'ın takma ismidir) -) - -$(P -Benzer şekilde, dizgi hazır değerleri de değişmezdirler: -) - -$(UL -$(LI $(STRING "merhaba"c) hazır dizgisinin türü $(C string)'dir) -$(LI $(STRING "merhaba"w) hazır dizgisinin türü $(C wstring)'dir) -$(LI $(STRING "merhaba"d) hazır dizgisinin türü $(C dstring)'dir) -) - -$(P -Bunlara bakarak D dizgilerinin normalde $(I değiştirilemeyen karakterlerden) oluştuklarını söyleyebiliriz. -) - -$(H6 $(IX değişmezlik, geçişli) $(C const) ve $(C immutable) geçişlidir) - -$(P -Yukarıdaki $(C a) ve $(C b) dilimlerinin kod açıklamalarında da değinildiği gibi, o dilimlerin ne kendileri ne de elemanları değiştirilebilir. -) - -$(P -Bu, ilerideki bölümlerde göreceğimiz $(LINK2 /ders/d/yapilar.html, yapılar) ve $(LINK2 /ders/d/siniflar.html, sınıflar) için de geçerlidir. Örneğin, $(C const) olan bir yapı değişkeninin bütün üyeleri de $(C const)'tır ve $(C immutable) olan bir yapı değişkeninin bütün üyeleri de $(C immutable)'dır. (Aynısı sınıflar için de geçerlidir.) -) - -$(H6 $(IX .dup) $(IX .idup) $(C .dup) ve $(C .idup)) - -$(P -Karakterleri değişmez olduklarından dizgiler işlevlere parametre olarak geçirilirken uyumsuz durumlarla karşılaşılabilir. Bu durumlarda dizilerin $(C .dup) ve $(C .idup) nitelikleri yararlıdır: -) - -$(UL -$(LI $(C .dup) dizinin değişebilen bir kopyasını oluşturur; ismi, "kopyasını al" anlamındaki "duplicate"ten gelir) -$(LI $(C .idup) dizinin değişmez bir kopyasını oluşturur; ismi "immutable duplicate"ten gelir) -) - -$(P -Örneğin, parametresinin programın çalışması süresince kesinlikle değişmeyecek olmasını isteyen ve bu yüzden onu $(C immutable) olarak belirlemiş olan bir işlevi $(C .idup) ile alınan bir kopya ile çağırmak gerekebilir: -) - ---- -void foo($(HILITE string) dizgi) { - // ... -} - -void main() { - char[] selam; - foo(selam); $(DERLEME_HATASI) - foo(selam$(HILITE .idup)); // ← derlenir -} ---- - - -$(H5 Nasıl kullanmalı) - -$(UL - -$(LI -Genel bir kural olarak, olabildiği kadar değişmezliği yeğleyin. -) - -$(LI -Değişken tanımlarken, programın çalışması sırasında kesinlikle değişmeyecek olan ve değerleri derleme zamanında bilinen veya hesaplanabilen değerleri $(C enum) olarak tanımlayın. Örneğin, dakikadaki saniye sayısı değişmez: - ---- - enum int dakikaBaşınaSaniye = 60; ---- - -$(P -Türün sağ taraftan çıkarsanabildiği durumlarda değişkenin türü belirtilmeyebilir: -) - ---- - enum dakikaBaşınaSaniye = 60; ---- - -) - -$(LI -$(C enum) dizisi ve $(C enum) eşleme tablosu kullanmanın gizli bedelini göz önünde bulundurun. Fazla büyük olduklarında ve programda birden fazla yerde kullanıldıklarında onları $(C immutable) değişkenler olarak tanımlayın. -) - -$(LI -Kesinlikle değişmeyecek olan ama değerleri derleme zamanında bilinmeyen veya hesaplanamayan değişkenleri $(C immutable) olarak tanımlayın. Türün belirtilmesi yine isteğe bağlıdır: - ---- - immutable tahmin = sayıOku("Tahmininiz"); ---- - -) - -$(LI -Parametre tanımlarken, eğer işlev parametrede bir değişiklik yapmayacaksa parametreyi $(C const) olarak tanımlayın. Öyle yaptığınızda parametreyi değiştirmeme sözü verdiğiniz için $(I değişebilen) ve $(C immutable) değişkenleri o işleve gönderebilirsiniz: - ---- -void foo(const char[] dizgi) { - // ... -} - -void main() { - char[] değişebilenDizgi; - string immutableDizgi; - - foo(değişebilenDizgi); // ← derlenir - foo(immutableDizgi); // ← derlenir -} ---- - -) - -$(LI -Bir üstteki maddeyi uyguladığınızda, $(C const) olarak aldığınız parametreyi $(C immutable) alan işlevlere gönderemeyeceğinizi unutmayın. Bu konunun ayrıntılarını yukarıdaki "$(C const) parametre mi, $(C immutable) parametre mi?" başlığında gördük. -) - -$(LI -Eğer parametrede bir değişiklik yapacaksanız o parametreyi değişebilen şekilde tanımlayın ($(C const) veya $(C immutable) olarak tanımlansa zaten derleyici izin vermez): - ---- -import std.stdio; - -void tersÇevir(dchar[] dizgi) { - foreach (i; 0 .. dizgi.length / 2) { - immutable geçici = dizgi[i]; - dizgi[i] = dizgi[$ - 1 - i]; - dizgi[$ - 1 - i] = geçici; - } -} - -void main() { - dchar[] selam = "merhaba"d.dup; - tersÇevir(selam); - writeln(selam); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -abahrem -) - -) - -) - -$(H5 Özet) - -$(UL - -$(LI $(C enum) değişkenler, değerleri derleme zamanında bilinen ve kesinlikle değişmeyecek olan kavramları temsil ederler.) - -$(LI $(C immutable) değişkenler, değerleri derleme zamanında bilinemeyen ama kesinlikle değişmeyecek olan kavramları temsil ederler.) - -$(LI $(C const) parametreler, işlevin değiştirmeyeceği parametrelerdir. Hem $(I değişebilen) hem de $(C immutable) değişkenler o parametrenin değeri olarak kullanılabilirler.) - -$(LI $(C immutable) parametreler, işlevin özellikle $(C immutable) olmasını talep ettiği parametrelerdir. İşlev çağrılırken bu parametrelere karşılık yalnızca $(C immutable) değişkenler gönderilebilir.) - -$(LI $(C immutable(int[])), dilimin de elemanlarının da değişmez olduklarını belirler.) - -$(LI $(C immutable(int)[]), yalnızca elemanların değişmez olduklarını belirler.) - -) - -Macros: - SUBTITLE=Değişmezlik - - DESCRIPTION=D dilinde değişmezlik kavramlarıyla ilgili olan const ve immutable anahtar sözcüklerinin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial const immutable sabit değişmez - -SOZLER= -$(degisken) -$(degismez) -$(islev) -$(kapsam) -$(parametre) -$(sabit) -$(takma_isim) -$(tur_nitelendirici) diff --git a/ddili/src/ders/d/copyright.d b/ddili/src/ders/d/copyright.d deleted file mode 100644 index 9e2e75f..0000000 --- a/ddili/src/ders/d/copyright.d +++ /dev/null @@ -1,91 +0,0 @@ -Ddoc - -
    - -$(P -D Programlama Dili, Birinci Yayım -) - -$(BR) - -$(P -Sürüm: $(LINK2 https://bitbucket.org/acehreli/ddili, ) -) - -$(BR) - -$(P -Bu kitabın en güncel elektronik sürümlerini $(LINK2 http://ddili.org/ders/d, sitesinden) edinebilirsiniz. -) - -$(BR) -$(BR) - -$(P -Copyleft (ɔ) 2009-2015 Ali Çehreli -) - -$(BR) - -$(P -Creative Commons License -) - -$(BR) -

    -Bu eser, Creative Commons'ın $(I Atıf-Gayriticari-LisansDevam 4.0 Uluslararası lisansı) ile lisanslanmıştır. Bu lisansın tam metni için http://creativecommons.org/licenses/by-nc-sa/4.0/deed.tr/ adresine gidiniz. -

    - -$(BR) -$(BR) - -$(P -Teknik düzenleme: $(LINK2 http://www.luismarques.eu, Luís Marques) -) -$(BR) -$(P -Kapak tasarımı: $(LINK2 http://izgiyapici.com, İzgi Yapıcı) -) -$(BR) -$(P -Kapak resmi: $(LINK2 mailto:sarah@reeceweb.com, Sarah Reece) -) -$(BR) -$(P -Basımcı: $(LINK2 mailto:acehreli@yahoo.com, Ali Çehreli) -) - -$(BR) -$(BR) - -$(P -Fontlar: -) -
      -
    • -Andada (tasarımcı: Huerta Tipográfica için Carolina Giovagnoli) -
    • -
    • -Open Sans (tasarımcı: Steve Matteson) -
    • -
    • -DejaVu Mono (tasarımcı: DejaVu Fonts) -
    • -
    - -$(BR) -$(P -PDF sürümü Prince XML ile oluşturulmuştur -) -$(P -Diğer ekitap sürümleri Calibre ile oluşturulmuştur -) - -
    - -Macros: - SUBTITLE=Copyleft - - DESCRIPTION=D Programlama Dili'nin copyleft sayfası - - KEYWORDS=copyright diff --git a/ddili/src/ders/d/cover.pdf b/ddili/src/ders/d/cover.pdf deleted file mode 100644 index e4ca8c1..0000000 Binary files a/ddili/src/ders/d/cover.pdf and /dev/null differ diff --git a/ddili/src/ders/d/cover_ebook.png b/ddili/src/ders/d/cover_ebook.png deleted file mode 100644 index 49e1ec4..0000000 Binary files a/ddili/src/ders/d/cover_ebook.png and /dev/null differ diff --git a/ddili/src/ders/d/cover_thumb.png b/ddili/src/ders/d/cover_thumb.png deleted file mode 100644 index 2c9362c..0000000 Binary files a/ddili/src/ders/d/cover_thumb.png and /dev/null differ diff --git a/ddili/src/ders/d/cozum_ozel.ddoc b/ddili/src/ders/d/cozum_ozel.ddoc deleted file mode 100644 index c56fd1f..0000000 --- a/ddili/src/ders/d/cozum_ozel.ddoc +++ /dev/null @@ -1,6 +0,0 @@ -MAIN_TITLE=D.ershane Çözümleri -CLASS=solution -COZUM_BOLUMU = $(H4 $0) -LANG=tr -LANGUAGE=turkish -SUB_AUTHOR= diff --git a/ddili/src/ders/d/deger_referans.d b/ddili/src/ders/d/deger_referans.d deleted file mode 100644 index 07ad4f9..0000000 --- a/ddili/src/ders/d/deger_referans.d +++ /dev/null @@ -1,708 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX değer türü) $(IX referans türü) Değerler ve Referanslar) - -$(P -Değer türü ile referans türü arasındaki fark, özellikle daha sonra göreceğimiz yapıları ve sınıfları anlamada yararlı olacak. -) - -$(P -Bu bölümde ayrıca değişkenlerin adreslerini bildiren $(C &) işlecini de tanıtacağım. -) - -$(P -En sonda da şu iki önemli kavramı gösteren bir tablo vereceğim: -) - -$(UL -$(LI değer karşılaştırması) -$(LI adres karşılaştırması) -) - -$(H5 Değer türü) - -$(P -Bunun tanımı son derece basittir: Değişkenleri değer taşıyan türlere değer türü denir. Örneğin bütün tamsayı ve kesirli sayı türleri değer türleridir çünkü bu türlerden olan her değişkenin kendi değeri vardır. Pek açık olmasa da sabit uzunluklu diziler de değer türleridir. -) - -$(P -Örneğin, $(C int) türünden olan bir değişken bir tamsayı değer taşır: -) - ---- - int hız = 123; ---- - -$(P -$(C hız) değişkeninin büyüklüğü $(C int)'in büyüklüğü olan 4 bayttır. Belleği soldan sağa doğru bir şerit halinde devam ediyormuş gibi gösterirsek, o değişkenin bellekte şu şekilde yaşadığını düşünebiliriz: -) - -$(MONO - hız - ───┬─────┬─── - │ 123 │ - ───┴─────┴─── -) - -$(P -Değer türlerinin değişkenleri kopyalandıklarında kendi özel değerlerini edinirler: -) - ---- - int yeniHız = hız; ---- - -$(P -Yeni değişkene bellekte kendisine ait bir yer ayrılır ve $(C yeniHız)'ın da kendi değeri olur: -) - -$(MONO - hız yeniHız - ───┬─────┬─── ───┬─────┬─── - │ 123 │ │ 123 │ - ───┴─────┴─── ───┴─────┴─── -) - -$(P -Doğal olarak, artık bu değişkenlerde yapılan değişiklikler birbirlerinden bağımsızdır: -) - ---- - hız = 200; ---- - -$(P -Diğer değişkenin değeri değişmez: -) - -$(MONO - hız yeniHız - ───┬─────┬─── ───┬─────┬─── - │ 200 │ │ 123 │ - ───┴─────┴─── ───┴─────┴─── -) - -$(H6 $(C assert) hatırlatması) - -$(P -Bu bölümde kavramların doğruluklarını göstermek için $(LINK2 /ders/d/assert.html, $(C assert) ve $(C enforce) bölümünde) gördüğümüz $(C assert) denetimlerinden yararlanacağım. Aşağıdaki örneklerde kullandığım $(C assert) denetimlerini "bu doğrudur" demişim gibi kabul edin. -) - -$(P -Örneğin aşağıdaki $(C assert(hız == yeniHız)) ifadesi, "hız, yeniHız'a eşittir" anlamına geliyor. -) - -$(H6 Değer kimliği) - -$(P -Yukarıdaki gösterimlerden de anlaşılabileceği gibi, değişkenlerin eşitlikleri iki anlamda ele alınabilir: -) - -$(UL -$(LI $(B Değer eşitliği): Şimdiye kadar bir çok örnekte kullandığımız $(C ==) işleci, değişkenlerin değerlerini karşılaştırır. Birbirlerinden farklı olan iki değişkenin bu açıdan $(I eşit olmaları), onların değerlerinin eşit olmaları anlamına gelir. -) -$(LI $(B Değer kimliği): Kendi değerlerine sahip olmaları açısından bakıldığında, $(C hız) ve $(C yeniHız)'ın ayrı kimlikleri vardır. Değerleri eşit olsalar bile, birisinde yapılan değişiklik diğerini etkilemez. -) - -) - - ---- - int hız = 123; - int yeniHız = hız; - assert(hız == yeniHız); - hız = 200; - assert(hız != yeniHız); ---- - -$(H6 Adres alma işleci $(C &)) - -$(P -Daha önce $(C readf) kullanımında gördüğümüz gibi, bu işleç değişkenin adresini döndürür. Okuduğu bilgiyi hangi değişkene yazacağını $(C readf)'e o değişkenin adresini vererek bildiriyorduk. -) - -$(P -Değişkenlerin adreslerini başka amaçlar için de kullanabiliriz. Bir örnek olarak iki farklı değişkenin adresini yazdıran bir kod şöyle yazılabilir: -) - ---- - int hız = 123; - int yeniHız = hız; - - writeln("hız : ", hız, " adresi: ", $(HILITE &)hız); - writeln("yeniHız: ", yeniHız, " adresi: ", $(HILITE &)yeniHız); ---- - -$(P -$(C hız) ve $(C yeniHız) değişkenlerinin değerleri aynıdır, ama yukarıda da gösterildiği gibi bu değerler belleğin farklı adreslerinde bulunmaktadırlar: -) - -$(SHELL -hız : 123 adresi: BF9A78F0 -yeniHız: 123 adresi: BF9A78F4 -) - -$(P $(I Not: Programı her çalıştırdığınızda farklı adresler görmeniz normaldir. Bu değişkenler işletim sisteminden alınan belleğin boş yerlerine yerleştirilirler.) -) - -$(P -Değişken adresleri normalde on altılı sayı sisteminde yazdırılır. -) - -$(P -Ayrıca, adreslerin $(C int)'in uzunluğu olan 4 kadar farklı olmalarına bakarak o değişkenlerin bellekte yan yana durduklarını da anlayabiliriz. -) - -$(H5 $(IX değişken, referans) Referans değişkenleri) - -$(P -$(I Referans türlerini) anlatmaya geçmeden önce referans değişkenlerini tanıtmam gerekiyor. -) - -$(P -Referans değişkenleri, başka değişkenlerin takma isimleri gibi kullanılan değişkenlerdir. Her ne kadar kendileri değişken gibi olsalar da, kendi özel değerleri yoktur. Böyle bir değişkende yapılan bir değişiklik asıl değişkeni etkiler. -) - -$(P -Referans değişkenlerini aslında şimdiye kadar iki konuda görmüş ama üzerinde fazla durmamıştık: -) - -$(UL - -$(LI $(B $(C foreach) döngüsünde $(C ref) ile): Bir grup elemana $(C foreach) döngüsünde sırayla erişilirken $(C ref) anahtar sözcüğü kullanıldığında; eleman, dizi elemanının $(I kendisi) anlamına geliyordu. Kullanılmadığında ise dizi elemanının $(I kopyası) oluyordu. - -$(P -Bunu, $(C &) işleci ile de gösterebiliriz. Adresleri aynı ise, iki farklı değişken aslında belleğin aynı yerindeki bir değere erişim sağlıyorlar demektir: -) - ---- - int[] dilim = [ 0, 1, 2, 3, 4 ]; - - foreach (i, $(HILITE ref) eleman; dilim) { - assert(&eleman == &dilim[i]); - } ---- - -$(P -Her ne kadar farklı iki değişken olsalar da, $(C &) işleci ile alınan adreslerinin eşit olmaları, döngünün her tekrarında tanımlanan $(C eleman) ve $(C dilim[i])'nin değer kimliği açısından aslında aynı olduklarını gösterir. -) - -$(P -Bir başka deyişle, $(C eleman), $(C dilim[i])'nin takma ismidir. Daha başka bir deyişle, $(C eleman) ile $(C dilim[i]) aynı değere erişim sağlarlar. Birisinde yapılan değişiklik, asıl değeri etkiler. -) - -$(P -$(C eleman) takma ismi, sırayla asıl dizi elemanlarının takma ismi haline gelir. Bu durumu döngünün $(C i)'nin örneğin 3 olduğu tekrarı için şöyle gösterebiliriz: -) - -$(MONO - dilim[0] dilim[1] dilim[2] dilim[3] dilim[4] - ⇢ ⇢ ⇢ (eleman) -──┬────────┬────────┬────────┬────────┬─────────┬── - │ 0 │ 1 │ 2 │ 3 │ 4 │ -──┴────────┴────────┴────────┴────────┴─────────┴── -) - -) - -$(LI $(B $(C ref) ve $(C out) işlev parametrelerinde): İşlev parametreleri $(C ref) veya $(C out) olarak tanımlandıklarında işleve gönderilen asıl değişkenin takma ismi gibi işlem görürler. - -$(P -Bunu görmek için böyle iki parametre alan bir işlevin iki parametresine de aynı değişkeni gönderelim. Aynı değer kimliğine sahip olduklarını yine $(C &) işleci ile gösterebiliriz: -) - ---- -import std.stdio; - -void main() { - int asılDeğişken; - writeln("asılDeğişken'in adresi : ", &asılDeğişken); - işlev(asılDeğişken, asılDeğişken); -} - -void işlev($(HILITE ref) int refParametre, $(HILITE out) int outParametre) { - writeln("refParametre'nin adresi: ", &refParametre); - writeln("outParametre'nin adresi: ", &outParametre); - assert($(HILITE &)refParametre == $(HILITE &)outParametre); -} ---- - -$(P -Her ne kadar farklı parametre olarak tanımlansalar da, $(C refParametre) ve $(C outParametre) aslında aynı değere erişim sağlarlar çünkü zaten ikisi de $(C main) içindeki $(C asılDeğişken)'in takma ismidir: -) - -$(SHELL -asılDeğişken'in adresi : 7FFF1DC7D7D8 -refParametre'nin adresi: 7FFF1DC7D7D8 -outParametre'nin adresi: 7FFF1DC7D7D8 -) - -) - -) - -$(H5 $(IX tür, referans) Referans türü) - -$(P -Bazı türlerden olan değişkenler kendi kimlikleri olduğu halde kendileri değer taşımazlar; değer taşıyan başka değişkenlere erişim sağlarlar. Böyle türlere referans türü denir. -) - -$(P -Bu kavramı daha önce dizi dilimlerinde görmüştük. Dilimler, var olan başka bir dizinin elemanlarına erişim sağlayan türlerdir; kendi elemanları yoktur: -) - ---- - // İsmi burada 'dizi' olarak yazılmış olsa da aslında bu - // değişken de dilimdir; bütün elemanlara erişim sağlar. - int[] dizi = [ 0, 1, 2, 3, 4 ]; - - // Baştaki ve sondaki elemanı dışlayarak ortadaki üçüne - // erişim sağlayan bir dilim: - int[] dilim = dizi[1 .. $ - 1]; - - // Şimdi dilim[0] ile dizi[1] aynı değere erişirler: - assert($(HILITE &)dilim[0] == $(HILITE &)dizi[1]); - - // Gerçekten de dilim[0]'da yapılan değişiklik dizi[1]'i - // etkiler: - dilim[0] = 42; - assert(dizi[1] == 42); ---- - -$(P -Referans değişkenlerinin tersine, referans türleri yalnızca takma isim değildirler. Bunu görmek için aynı dilimin kopyası olan bir dilim daha oluşturalım: -) - ---- - int[] dilim2 = dilim; ---- - -$(P -Bu iki dilim kendi adresleri olan, bir başka deyişle kendi kimlikleri olan değişkenlerdir; $(C dilim2) ile $(C dilim) farklı adreslerde yaşarlar: -) - ---- - assert(&dilim != &dilim2); ---- - -$(P -İşte $(I referans değişkenleri) ile $(I referans türlerinin) ayrımı buna dayanır: -) - -$(UL - -$(LI Referans değişkenlerinin kendi kimlikleri yoktur, başka b değişkenlerin takma isimleridirler.) - -$(LI Referans türünden olan değişkenler ise kendi kimlikleri olan değişkenlerdir ama yine başka değerlere erişim sağlarlar.) - -) - -$(P -Örneğin yukarıdaki $(C dilim) ve $(C dilim2)'yi bellek üzerinde şöyle gösterebiliriz: -) - -$(MONO - dilim dilim2 - ───┬───┬───┬───┬───┬───┬─── ───┬───┬─── ───┬───┬─── - │ 0 │$(HILITE  1 )│$(HILITE  2 )│$(HILITE  3 )│ 4 │ │ o │ │ o │ - ───┴───┴───┴───┴───┴───┴─── ───┴─│─┴─── ───┴─│─┴─── - ▲ │ │ - │ │ │ - └────────────────────┴────────────┘ -) - -$(P -İki dilimin erişim sağladıkları asıl elemanlar işaretli olarak gösteriliyor. -) - -$(P -D'nin güçlü bir olanağı olan sınıfları daha ilerideki bölümlerde göreceğiz. D'yi C++'dan ayıran önemli farklardan birisi, D'nin sınıflarının referans türleri olmalarıdır. Yalnızca bunu göstermiş olmak için çok basit bir sınıf tanımlayacağım: -) - ---- -class BirSınıf { - int üye; -} ---- - -$(P -Sınıf nesneleri, daha önce hata atarken de kullandığımız $(C new) ile oluşturulurlar: -) - ---- - auto değişken = new BirSınıf; ---- - -$(P -Bu durumda $(C değişken), $(C new) işleciyle oluşturulmuş olan isimsiz bir $(C BirSınıf) nesnesine erişim sağlayan bir referanstır: -) - -$(MONO - (isimsiz BirSınıf nesnesi) değişken - ───┬───────────────────┬─── ───┬───┬─── - │ ... │ │ o │ - ───┴───────────────────┴─── ───┴─│─┴─── - ▲ │ - │ │ - └────────────────────┘ -) - -$(P -Dilimlere benzer şekilde, $(C değişken) kopyalandığında kopyası da aynı nesneye erişim sağlar ama kopyanın farklı adresi vardır: -) - ---- - auto değişken = new BirSınıf; - auto değişken2 = değişken; - assert(değişken == değişken2); - assert(&değişken != &değişken2); ---- - -$(P -Yukarıda görüldüğü gibi, erişim sağlama açısından eşit olsalar da, adresleri farklı olduğu için farklı değişkenlerdir: -) - -$(MONO - (isimsiz BirSınıf nesnesi) değişken değişken2 - ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── - │ ... │ │ o │ │ o │ - ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── - ▲ │ │ - │ │ │ - └────────────────────┴────────────┘ -) - -$(P -Böyle iki farklı $(C BirSınıf) nesnesinin gerçekten de aynı nesneye erişim sağladıklarını bir de şöyle gösterebiliriz: -) - ---- - auto değişken = new BirSınıf; - değişken.üye = 1; - - auto değişken2 = değişken; // aynı nesneyi paylaşırlar - değişken2.üye = 2; - - assert(değişken.üye == 2); // değişken'in de erişim - // sağladığı nesne - // değişmiştir ---- - -$(P -$(C değişken2) yoluyla 2 değerini alan üye, $(C değişken)'in de erişim sağladığı nesnenin üyesidir. -) - -$(P -Başka bir referans türü, eşleme tablolarıdır. Eşleme tabloları da atandıklarında aynı asıl tabloya erişim sağlarlar: -) - ---- - string[int] isimleSayılar = - [ - 1 : "bir", - 10 : "on", - 100 : "yüz", - ]; - - // Aynı asıl tabloyu paylaşmaya başlarlar: - string[int] isimleSayılar2 = isimleSayılar; - - // Örneğin ikincisine eklenen eleman ... - isimleSayılar2[4] = "dört"; - - // ... birincisinde de görünür. - assert(isimleSayılar[4] == "dört"); ---- - -$(P -Bir sonraki bölümde göreceğimiz gibi, baştaki eşleme tablosu $(C null) ise eleman paylaşımı yoktur. -) - -$(H6 Atama işleminin farkı) - -$(P -Değer türlerinde ve referans değişkenlerinde atama işleminin sonucunda $(I asıl değer) değişir: -) - ---- -void main() { - int sayı = 8; - - yarıyaBöl(sayı); // asıl değer değişir - assert(sayı == 4); -} - -void yarıyaBöl($(HILITE ref) int bölünen) { - bölünen /= 2; -} ---- - -$(P -Referans türlerinde ise atama işlemi, $(I hangi asıl nesneye erişim sağlandığını) değiştirir. Örneğin aşağıdaki kodda $(C dilim3)'e yapılan atama işlemi onun eriştirdiği elemanların değerlerini değiştirmez; $(C dilim3)'ün başka elemanları göstermesini sağlar: -) - ---- -void main() { - int[] dilim1 = [ 10, 11, 12, 13, 14 ]; - int[] dilim2 = [ 20, 21, 22 ]; - - int[] dilim3 = dilim1[1 .. 3]; // 1 ve 2 indeksli elemanlara - // eriştirir - - dilim3[0] = 777; - assert(dilim1 == [ 10, 777, 12, 13, 14 ]); - - // Bu atama işlemi dilim3'ün eriştirdiği elemanları - // değiştirmez, dilim3'ün artık başka elemanlara - // erişim sağlamasına neden olur - $(HILITE dilim3 =) dilim2[$ - 1 .. $]; // sonuncu elemana eriştirir - - dilim3[0] = 888; - assert(dilim2 == [ 20, 21, 888 ]); -} ---- - -$(P -Atama işlecinin referans türlerindeki bu etkisini bir de $(C BirSınıf) türünde görelim: -) - ---- - auto değişken1 = new BirSınıf; - değişken1.üye = 1; - - auto değişken2 = new BirSınıf; - değişken2.üye = 2; - - auto $(HILITE kopya = değişken1); - kopya.üye = 3; - - $(HILITE kopya = değişken2); - kopya.üye = 4; - - assert(değişken1.üye == 3); - assert(değişken2.üye == 4); ---- - -$(P -Oradaki atama işlemleri sonucunda $(C kopya) önce $(C değişken1)'in nesnesine, sonra da $(C değişken2)'nin nesnesine erişim sağlar. $(C kopya) yoluyla değeri değiştirilen $(C üye) ilk seferde $(C değişken1)'inkidir, sonra ise $(C değişken2)'ninkidir. -) - -$(H6 Referans türleri hiçbir değere erişim sağlamıyor olabilirler) - -$(P -Referans $(I değişkenlerinde) mutlaka bir asıl değer vardır; onların yaşam süreçleri erişim sağladıkları bir asıl değer olmadan başlamaz. Referans $(I türlerinin) değişkenleri ise, henüz hiçbir değere erişim sağlamayacak şekilde oluşturulabilirler.) - -$(P -Örneğin bir $(C BirSınıf) değişkeni, erişim sağladığı nesne henüz belli olmadan şöyle tanımlanabilir: -) - ---- - BirSınıf değişken; ---- - -$(P -Böyle değişkenler $(C null) özel değerine eşittirler. Bu özel değeri ve $(C is) anahtar sözcüğünü $(LINK2 /ders/d/null_ve_is.html, daha sonraki bir bölümde) göreceğiz. -) - -$(H5 Sabit uzunluklu diziler $(I değer türü), dinamik diziler $(I referans türü)) - -$(P -D'nin iki dizi türü bu konuda farklılık gösterir. -) - -$(P -Dinamik diziler (dilimler), yukarıdaki örneklerde de görüldüğü gibi referans türleridir. Dinamik diziler kendilerine ait olmayan elemanlara erişim sağlarlar. Temel işlemler açısından referans olarak davranırlar. -) - -$(P -Sabit uzunluklu diziler ise değer türleridir. Kendi elemanlarına sahiptirler ve değer türü olarak davranırlar: -) - ---- - int[3] dizi1 = [ 10, 20, 30 ]; - - // dizi2'nin elemanları dizi1'inkilerden farklı olur - auto dizi2 = dizi1; - dizi2[0] = 11; - - // İlk dizi değişmez - assert(dizi1[0] == 10); ---- - -$(P -Tanımlandığı zaman uzunluğu da belirlendiği için $(C dizi1) sabit uzunluklu bir dizidir. $(C auto) anahtar sözcüğü nedeniyle $(C dizi2) de aynı türü edinir. Her ikisi de kendi elemanlarına sahiptirler. Birisinde yapılan değişiklik diğerini etkilemez. -) - -$(H5 Deney) - -$(P -Yukarıda anlatılan farklı türlerin değişkenlerine ve onların adreslerine $(C ==) işlecini uygulayınca ortaya şöyle bir tablo çıkıyor: -) - -$(MONO - Değişken Türü a == b &a == &b -===================================================================== - aynı değerli değişkenler (değer türü) true false - farklı değerli değişkenler (değer türü) false false - ref değişkenli foreach true true - ref olmayan değişkenli foreach true false - out parametreli işlev true true - ref parametreli işlev true true - in parametreli işlev true false - aynı elemanlara erişen dilimler true false - farklı elemanlara erişen dilimler false false - aynı nesneye erişen BirSınıf'lar (referans türü) true false -farklı nesneye erişen BirSınıf'lar (referans türü) false false -) - -$(P -O tablo aşağıdaki programla üretilmiştir: -) - ---- -import std.stdio; -import std.array; - -int modülDeğişkeni = 9; - -class BirSınıf { - int üye; -} - -void başlıkÇiz() { - immutable dchar[] başlık = - " Değişken Türü" - " a == b &a == &b"; - - writeln(); - writeln(başlık); - writeln(replicate("=", başlık.length)); -} - -void bilgiSatırı(const dchar[] başlık, - bool değerEşitliği, - bool adresEşitliği) { - writefln("%50s%9s%9s", - başlık, değerEşitliği, adresEşitliği); -} - -void main() { - başlıkÇiz(); - - int sayı1 = 12; - int sayı2 = 12; - bilgiSatırı("aynı değerli değişkenler (değer türü)", - sayı1 == sayı2, - &sayı1 == &sayı2); - - int sayı3 = 3; - bilgiSatırı("farklı değerli değişkenler (değer türü)", - sayı1 == sayı3, - &sayı1 == &sayı3); - - int[] dilim = [ 4 ]; - foreach (i, ref eleman; dilim) { - bilgiSatırı("ref değişkenli foreach", - eleman == dilim[i], - &eleman == &dilim[i]); - } - - foreach (i, eleman; dilim) { - bilgiSatırı("ref olmayan değişkenli foreach", - eleman == dilim[i], - &eleman == &dilim[i]); - } - - outParametre(modülDeğişkeni); - refParametre(modülDeğişkeni); - inParametre(modülDeğişkeni); - - int[] uzunDilim = [ 5, 6, 7 ]; - int[] dilim1 = uzunDilim; - int[] dilim2 = dilim1; - bilgiSatırı("aynı elemanlara erişen dilimler", - dilim1 == dilim2, - &dilim1 == &dilim2); - - int[] dilim3 = dilim1[0 .. $ - 1]; - bilgiSatırı("farklı elemanlara erişen dilimler", - dilim1 == dilim3, - &dilim1 == &dilim3); - - auto değişken1 = new BirSınıf; - auto değişken2 = değişken1; - bilgiSatırı( - "aynı nesneye erişen BirSınıf'lar (referans türü)", - değişken1 == değişken2, - &değişken1 == &değişken2); - - auto değişken3 = new BirSınıf; - bilgiSatırı( - "farklı nesneye erişen BirSınıf'lar (referans türü)", - değişken1 == değişken3, - &değişken1 == &değişken3); -} - -void outParametre(out int parametre) { - bilgiSatırı("out parametreli işlev", - parametre == modülDeğişkeni, - ¶metre == &modülDeğişkeni); -} - -void refParametre(ref int parametre) { - bilgiSatırı("ref parametreli işlev", - parametre == modülDeğişkeni, - ¶metre == &modülDeğişkeni); -} - -void inParametre(in int parametre) { - bilgiSatırı("in parametreli işlev", - parametre == modülDeğişkeni, - ¶metre == &modülDeğişkeni); -} ---- - -$(P -Notlar: -) - -$(UL - -$(LI -$(IX modül değişkeni) $(IX değişken, modül) Programda işlev parametrelerini karşılaştırmak için bir de modül değişkeni kullanılıyor. Modül değişkenleri işlevlerin dışında tanımlanırlar ve içinde tanımlandıkları modüldeki bütün kodlar tarafından erişilebilirler. -) - -$(LI -$(IX replicate, std.array) $(C std.array) modülünün $(C replicate()) işlevi kendisine verilen aralığı (yukarıdaki $(STRING "=")) belirtilen sayıda tekrarlar. -) - -) - -$(H5 Özet) - -$(UL -$(LI Değer türlerinden olan her değişkenin kendi değeri ve kendi adresi vardır.) -$(LI Referans değişkenlerinin ne değerleri vardır ne de adresleri; takma isim gibidirler.) -$(LI Referans türlerinden olan değişkenlerin kendi adresleri vardır; ama erişim sağladıkları değer kendilerinin değildir.) -$(LI Referans türlerinde atama işlemi değer değiştirmez, hangi asıl nesneye erişildiğini değiştirir.) -$(LI Referans türlerinden olan değişkenler $(C null) olabilirler.) -) - -Macros: - SUBTITLE=Değerler ve Referanslar - - DESCRIPTION=D dilinde değer türlerinin ve referans türlerinin karşılaştırılmaları; ve adres alma işleci olan & - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial nesne değer türü referans türü - -SOZLER= -$(cop_toplayici) -$(deger) -$(deger_turu) -$(dilim) -$(esleme_tablosu) -$(nesne) -$(referans) -$(referans_turu) -$(sinif) -$(yapi) diff --git a/ddili/src/ders/d/deger_sol_sag.d b/ddili/src/ders/d/deger_sol_sag.d deleted file mode 100644 index c8a7fe3..0000000 --- a/ddili/src/ders/d/deger_sol_sag.d +++ /dev/null @@ -1,216 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX sol değer) $(IX sağ değer) Sol Değerler ve Sağ Değerler) - -$(P -$(IX ifade, sol değer ve sağ değer) Her ifadenin değeri ya $(I sol değerdir) ya da $(I sağ değerdir). Bu iki kavramı ayırt etmenin kolay bir yolu, dizi ve eşleme tablosu elemanları dahil olmak üzere bütün değişkenlerin sol değer, hazır değerler dahil olmak üzere bütün geçici değerlerin de sağ değer olduklarını kabul etmektir. -) - -$(P -Örneğin, aşağıdaki $(C writeln()) çağrılarından birincisinin bütün parametreleri sol değerdir, ikincisindekiler ise sağ değerdir: -) - ---- -import std.stdio; - -void main() { - int i; - immutable(int) imm; - auto dizi = [ 1 ]; - auto tablo = [ 10 : "on" ]; - - /* Aşağıdaki parametre değerlerinin hepsi sol değerdir. */ - - writeln(i, // değişken - imm, // immutable değişken - dizi, // dizi - dizi[0], // dizi elemanı - tablo[10]); // eşleme tablosu elemanı - // vs. - - enum mesaj = "merhaba"; - - /* Aşağıdaki parametre değerlerinin hepsi sağ değerdir. */ - - writeln(42, // hazır değer - mesaj, // enum sabiti (manifest constant) - i + 1, // geçici değer - hesapla(i)); // işlevin dönüş değeri - // vs. -} - -int hesapla(int i) { - return i * 2; -} ---- - -$(H5 Sağ değerlerin yetersizlikleri) - -$(P -Sol değerlerle karşılaştırıldıklarında sağ değerler aşağıdaki üç konuda yetersizdir. -) - -$(H6 Sağ değerlerin adresleri yoktur) - -$(P -Sol değerlerin bellekte yerleri olabilir, sağ değerlerin olamaz. -) - -$(P -Örneğin, aşağıdaki programdaki $(C a + b) sağ değerinin adresi alınamaz: -) - ---- -import std.stdio; - -void main() { - int a; - int b; - - readf(" %s", &a); $(CODE_NOTE derlenir) - readf(" %s", &(a + b)); $(DERLEME_HATASI) -} ---- - -$(SHELL -Error: a + b $(HILITE is not an lvalue) -) - -$(H6 Sağ değerlere yeni değer atanamaz) - -$(P -Değişmez olmadıkları sürece sol değerlere yeni değer atanabilir, sağ değerlere atanamaz. -) - ---- - a = 1; $(CODE_NOTE derlenir) - (a + b) = 2; $(DERLEME_HATASI) ---- - -$(SHELL -Error: a + b $(HILITE is not an lvalue) -) - -$(H6 Sağ değerler işlevlere referans olarak geçirilemezler) - -$(P -Sol değerler referans olarak geçirilebilirler, sağ değerler geçirilemezler. -) - ---- -void onArttır($(HILITE ref int) değer) { - değer += 10; -} - -// ... - - onArttır(a); $(CODE_NOTE derlenir) - onArttır(a + b); $(DERLEME_HATASI) ---- - -$(SHELL -Error: function deneme.onArttır (ref int değer) -$(HILITE is not callable) using argument types (int) -) - -$(P -Bu kısıtlamanın temel nedeni, işlevlerin referans türündeki parametrelerini sonradan kullanmak üzere bir kenara kaydedebilecekleri, oysa sağ değerlerin yaşamlarının sonradan kullanılmaya çalışıldıklarında çoktan sonlanmış olacağıdır. -) - -$(P -C++ gibi bazı dillerden farklı olarak, D'de sağ değerler referansı $(C const) olarak alan işlevlere de geçirilemezler: -) - ---- -void yazdır($(HILITE ref const(int)) değer) { - writeln(değer); -} - -// ... - - yazdır(a); $(CODE_NOTE derlenir) - yazdır(a + b); $(DERLEME_HATASI) ---- - -$(SHELL -Error: function deneme.yazdır (ref const(int) değer) -$(HILITE is not callable) using argument types (int) -) - -$(H5 $(IX auto ref, parametre) $(IX parametre, auto ref) Hem sol değer hem sağ değer alabilen $(C auto ref) parametreler) - -$(P -Önceki bölümde gördüğümüz gibi, $(LINK2 /ders/d/sablonlar.html, işlev şablonlarının) $(C auto ref) parametreleri hem sol değer hem sağ değer alabilirler. -) - -$(P -$(C auto ref), bir sol değer ile çağrıldığında $(I referans olarak) geçirme anlamına gelir; sağ değer ile çağrıldığında ise $(I kopyalayarak) geçirme anlamına gelir. Derleyicinin bu farklı iki durum için farklı kod üretebilmesi için işlevin bir şablon olması gerekir. -) - -$(P -Şablonları daha sonra göreceğiz. Şimdilik aşağıda işaretli olarak gösterilen boş parantezlerin $(C onArttır)'ı bir $(I işlev şablonu) haline getirdiğini kabul edin. -) - ---- -void onArttır$(HILITE ())($(HILITE auto ref) int değer) { - /* UYARI: Asıl parametre bir sağ değer ise buradaki - * 'değer' adlı parametre çağıran taraftaki değerin - * kopyasıdır. O yüzden, parametrede yapılan aşağıdaki - * değişiklik çağıran tarafta gözlemlenemez. */ - - değer += 10; -} - -void main() { - int a; - int b; - - onArttır(a); $(CODE_NOTE sol değer; referans olarak geçirilir) - onArttır(a + b); $(CODE_NOTE sağ değer; kopyalanarak geçirilir) -} ---- - -$(P -Yukarıdaki kod açıklamasında da belirtildiği gibi, parametrede yapılan değişiklik işlevi çağıran tarafta görülemeyebilir. O yüzden, $(C auto ref) genellikle parametrenin değişmediği durumlarda $(C auto ref const) olarak kullanılır. -) - -$(H5 Terimler) - -$(P -"Sol değer" ve "sağ değer" anlamına gelen "lvalue" ve "rvalue" ne yazık ki bu iki çeşit değeri yeterince ifade edemez. Başlarındaki $(I l) ve $(I r) harfleri "sol" anlamındaki "left"ten ve "sağ" anlamındaki "right"tan gelir. Bu sözcükler atama işlecinin sol ve sağ tarafını ifade ederler: -) - -$(UL - -$(LI -Değişmez olmadıkları sürece, sol değerler atama işlecinin sol tarafındaki ifade olarak kullanılabilirler. -) - -$(LI -Sağ değerler atama işlecinin sol tarafındaki ifade olarak kullanılamazlar. -) - -) - -$(P -Bu terimlerin açık olmamalarının bir nedeni, hem sol değerlerin hem de sağ değerlerin aslında atama işlecinin her iki tarafında da yer alabilmeleridir: -) - ---- - /* Bir sağ değer olan 'a + b' solda, - * bir sol değer olan 'a' sağda: */ - array[a + b] = a; ---- - -Macros: - SUBTITLE=Sol Değerler ve Sağ Değerler - - DESCRIPTION=Sol değerler, sağ değerler, ve aralarındaki farklar. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sol sağ lvalue rvalue - -SOZLER= -$(atama) -$(ifade) -$(sag_deger) -$(sol_deger) diff --git a/ddili/src/ders/d/degiskenler.cozum.d b/ddili/src/ders/d/degiskenler.cozum.d deleted file mode 100644 index 917c631..0000000 --- a/ddili/src/ders/d/degiskenler.cozum.d +++ /dev/null @@ -1,22 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Değişkenler) - ---- -import std.stdio; - -void main() { - double kur = 2.11; - int adet = 20; - - writeln(kur, " kurundan ", adet, " avro bozdurdum."); -} ---- - - -Macros: - SUBTITLE=Değişkenler Problem Çözümü - - DESCRIPTION=İlk D programlama dili dersi çözümleri: Değişkenler - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial değişkenler problem çözüm diff --git a/ddili/src/ders/d/degiskenler.d b/ddili/src/ders/d/degiskenler.d deleted file mode 100644 index 407bea1..0000000 --- a/ddili/src/ders/d/degiskenler.d +++ /dev/null @@ -1,112 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX değişken) Değişkenler) - -$(P -Programda kullanılan kavramları temsil eden yapılara $(I değişken) denir. Örnek olarak $(I hava sıcaklığı) gibi bir değeri veya $(I yarış arabası motoru) gibi karmaşık bir nesneyi düşünebilirsiniz. -) - -$(P -Bir değişkenin temel amacı bir değeri ifade etmektir. Değişkenin değeri, ona en son atanan değerdir. Her değerin belirli bir türünün olması gerektiği gibi, her değişken de belirli bir türdendir. Değişkenlerin çoğunun isimleri de olur ama programda açıkça anılmaları gerekmeyen değişkenlerin isimleri olmayabilir de. -) - -$(P -Örnek olarak bir okuldaki öğrenci sayısı $(I kavramını) ifade eden bir değişken düşünebiliriz. Öğrenci sayısı bir tamsayı olduğu için, türünü $(C int) olarak seçebiliriz. Açıklayıcı bir isim olarak da $(C öğrenci_sayısı) uygun olur. -) - -$(P -D'nin yazım kuralları gereği, değişkenler önce türleri sonra isimleri yazılarak tanıtılırlar. Bir değişkenin bu şekilde tanıtılmasına, o değişkenin $(I tanımı), ve bu eyleme o değişkenin $(I tanımlanması) denir. Değişkenin ismi, programda geçtiği her yerde değerine dönüşür. -) - ---- -import std.stdio; - -void main() { - // Değişkenin tanımlanması; öğrenci_sayısı'nın int - // türünde bir değişken olduğunu belirtir: - int öğrenci_sayısı; - - // Değişkenin isminin kullanıldığı yerde değerine - // dönüşmesi: - writeln("Bu okulda ", öğrenci_sayısı, " öğrenci var."); -} ---- - -$(P -Bu programın çıktısı şudur: -) - -$(SHELL -Bu okulda 0 öğrenci var. -) - -$(P -Programın çıktısından anlaşıldığına göre, $(C öğrenci_sayısı)'nın değeri 0'dır. Bunun nedeni, $(C int)'in ilk değerinin temel türler tablosundan hatırlayacağınız gibi 0 olmasıdır. -) - -$(P -Dikkat ederseniz, $(C öğrenci_sayısı) çıktıda ismi olarak değil, değeri olarak belirmiştir; yani programın çıktısı $(C Bu okulda öğrenci_sayısı öğrenci var.) şeklinde olmamıştır. -) - -$(P -Değişkenlerin değerleri $(C =) işleci ile değiştirilir. Yaptığı iş $(I değer atamak) olduğu için, bu işlece $(I atama işleci) denir: -) - ---- -import std.stdio; - -void main() { - int öğrenci_sayısı; - writeln("Bu okulda ", öğrenci_sayısı, " öğrenci var."); - - // öğrenci_sayısı'na 200 değerinin atanması: - öğrenci_sayısı = 200; - writeln("Bu okulda şimdi ", öğrenci_sayısı, " öğrenci var."); -} ---- - -$(SHELL -Bu okulda 0 öğrenci var. -Bu okulda şimdi 200 öğrenci var. -) - -$(P -Eğer değişkenin değeri tanımlandığı sırada biliniyorsa, tanımlanmasıyla değerinin atanması aynı anda yapılabilir, ve hata riskini azalttığı için de önerilen bir yöntemdir: -) - ---- -import std.stdio; - -void main() { - // Hem tanım, hem atama: - int öğrenci_sayısı = 100; - - writeln("Bu okulda ", öğrenci_sayısı, " öğrenci var."); -} ---- - -$(SHELL -Bu okulda 100 öğrenci var. -) - -$(PROBLEM_TEK - -$(P -İki değişken kullanarak ekrana "2.11 kurundan 20 avro bozdurdum." yazdırın. Değişkenlerden kesirli sayı olanı için $(C double) türünü kullanabilirsiniz. -) - -) - -Macros: - SUBTITLE=Değişkenler - - DESCRIPTION=D dilinde değişkenler - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial değişkenler - -SOZLER= -$(atama) -$(cikti) -$(degisken) -$(islec) -$(tanim) diff --git a/ddili/src/ders/d/derleyici.d b/ddili/src/ders/d/derleyici.d deleted file mode 100644 index e2a285e..0000000 --- a/ddili/src/ders/d/derleyici.d +++ /dev/null @@ -1,112 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX derleme) Derleme) - -$(P -D programcılığında en çok kullanılan iki aracın $(I metin düzenleyici) ve $(I derleyici) olduklarını gördük. Programlar metin düzenleyicilerde yazılırlar. -) - -$(P -D gibi dillerde derleme kavramı ve derleyicinin işlevi de hiç olmazsa kaba hatlarıyla mutlaka bilinmelidir. -) - -$(H5 $(IX makine kodu) Makine kodu) - -$(P -$(IX CPU) $(IX mikro işlemci) Bilgisayarın beyni CPU denen mikro işlemcidir. Mikro işlemciye ne işler yapacağını bildirmeye $(I kodlama), bu bildirimlere de $(I kod) denir. -) - -$(P -Her mikro işlemci modelinin kendisine has kodları vardır. Her mikro işlemcinin nasıl kodlanacağına mimari tasarımı aşamasında ve donanım kısıtlamaları gözetilerek karar verilir. Bu kodlar çok alt düzeyde elektronik sinyaller olarak gerçekleştirilirler. Bu tasarımda kodlama kolaylığı geri planda kaldığı için, doğrudan mikro işlemciyi kodlayarak program yazmak çok güç bir iştir. -) - -$(P -Mikro işlemcinin adının parçası olan $(I işlem) kavramı, özel sayılar olarak belirlenmiştir. Örneğin kodları 8 bit olan hayalî bir işlemcide 4 sayısı yükleme işlemini, 5 sayısı depolama işlemini, 6 sayısı da arttırma işlemini gösteriyor olabilir. Bu hayalî işlemcide soldaki 3 bitin işlem sayısı ve sağdaki 5 bitin de o işlemde kullanılacak değer olduğunu düşünürsek, bu mikro işlemcinin makine kodu şöyle olabilir: -) - -$(MONO -$(B -İşlem Değer Anlamı) - 100 11110 YÜKLE 11110 - 101 10100 DEPOLA 10100 - 110 10100 ARTTIR 10100 - 000 00000 BEKLE -) - -$(P -Makine kodunun donanıma bu kadar yakın olması, $(I oyun kağıdı) veya $(I öğrenci kayıtları) gibi üst düzey kavramların bilgisayarda temsil edilmelerini son derece güç hale getirir. -) - -$(H5 $(IX programlama dili) Programlama dili) - -$(P -Mikro işlemcileri kullanmanın daha etkin yolları aranmış, ve çözüm olarak üst düzey kavramları ifade etmeye elverişli programlama dilleri geliştirilmiştir. Bu dillerin donanım kaygıları olmadığı için, özellikle kullanım kolaylığı ve ifade gücü gözetilerek tasarlanmışlardır. Programlama dilleri insanlara uygun dillerdir ve çok kabaca konuşma dillerine benzerler: -) - -$(MONO -if (ortaya_kağıt_atılmış_mı()) { - oyun_kağıdını_göster(); -} -) - -$(P -Buna rağmen, programlama dilleri çok daha sıkı kurallara sahiptirler. -) - -$(P -Programlama dillerinin bir sorunu, anahtar sözcüklerinin geleneksel olarak İngilizce olmasıdır. Neyse ki bunlar kolayca öğrenebilecek kadar az sayıdadır. Örneğin $(C if)'in "eğer" anlamına geldiğini bir kere öğrenmek yeter. -) - -$(H5 $(IX yorumlayıcı) Yorumlayıcı) - -$(P -Yorumlayıcı, programın kaynak kodunu okuyan ve yazıldığı amaca uygun olarak işleten bir araçtır. Örneğin, bir yorumlayıcı yukarıdaki kod verildiğinde önce $(C ortaya_kağıt_atılmış_mı())'yı işleteceğini, sonra da onun sonucuna bağlı olarak $(C oyun_kağıdını_göster())'i işletir. Programcının bakış açısından, programı yorumlayıcı ile işletmek yalnızca iki adımdan oluşur: programı yazmak ve yorumlayıcıya vermek. -) - -$(P -Yorumlayıcı programı her işletişinde kaynak kodu baştan okumak zorundadır. Bu yüzden, yorumlayıcı ile işletilen program genel olarak o programın derlenmiş halinden daha yavaş çalışır. Ek olarak, yorumlayıcılar genelde kodu kapsamlıca incelemeden işletirler. Bu yüzden, çeşitli program hatası ancak program çalışmaya başladıktan sonra yakalanabilir. -) - -$(P -Perl, Python, ve Ruby gibi esnek ve dinamik dillerde yazılmış programların işletilmeden önce incelenmeleri güçtür. Bu yüzden, böyle diller geleneksel olarak yorumlayıcı ile kullanılırlar. -) - -$(H5 $(IX derleyici) Derleyici) - -$(P -Derleyici, programın kaynak kodunu okuyan başka bir araçtır. Yorumlayıcıdan farkı, kodu işletmemesi ama işletecek olan başka bir program oluşturmasıdır. Programcının yazdığı kodları işletme görevi oluşturulan bu programa aittir. (Bu program genelde makine kodu içerir.) Programcının bakış açısından, programı derleyici ile işletmek üç adımdan oluşur: programı yazmak, derlemek, ve oluşturulan programı çalıştırmak. -) - -$(P -Yorumlayıcının aksine, derleyici kaynak kodu tek kere ve derleme sırasında okur. Bu yüzden, derlenmiş program yorumlayıcı tarafından işletilen programdan genelde daha hızlıdır. Derleyiciler kodu genelde kapsamlı olarak da inceleyebildiklerinden hem daha hızlı işleyen programlar üretebilirler hem de program hatalarının çoğunu daha program işlemeye başlamadan önce yakalayabilirler. Öte yandan, yazılan programın her değişiklikten sonra derlenmesinin gerekmesi ek bir külfet olarak görülebilir ve bazı insan hatalarının kaynağıdır. Dahası, derleyicinin ürettiği program ancak belirli bir platformda çalışabilir; programın başka bir mikro işlemci veya işletim sistemi üzerinde çalışabilmesi için tekrar derlenmesi gerekir. Ek olarak, derlenmeleri kolay olan diller yorumlayıcı kullananlardan daha az esnektirler. -) - -$(P -Aralarında Ada, C, C++, ve D'nin de bulunduğu bazı diller güvenlik ve performans gibi nedenlerle derlenebilecek biçimde tasarlanmışlardır. -) - -$(H6 $(IX derleme hatası) $(IX hata, derleme) Derleme hatası) - -$(P -Derleyiciler programı dilin kurallarına uygun olarak derledikleri için, kural dışı kodlar gördüklerinde bir hata mesajı vererek sonlanırlar. Örneğin kapanmamış bir parantez, unutulmuş bir noktalı virgül, yanlış yazılmış bir anahtar sözcük, vs. derleme hatasına yol açar. -) - -$(P -Derleyici bazen de kod açıkça kural dışı olmadığı halde, programcının yanlış yapmasından şüphelenebilir ve bu konuda uyarı mesajı verebilir. Program derlenmiş bile olsa, her zaman için uyarıları da hata gibi kabul edip, uyarıya neden olan kodu değiştirmek iyi olur. -) - -Macros: - SUBTITLE=Derleyici - - DESCRIPTION=Derleyici kavramının tanıtılması - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial derleyici makina - -SOZLER= -$(anahtar_sozcuk) -$(bit) -$(derleyici) -$(makine_kodu) -$(metin_duzenleyici) -$(mikro_islemci) -$(program) diff --git a/ddili/src/ders/d/derse_ozel.ddoc b/ddili/src/ders/d/derse_ozel.ddoc deleted file mode 100644 index 0514600..0000000 --- a/ddili/src/ders/d/derse_ozel.ddoc +++ /dev/null @@ -1,12 +0,0 @@ -MAIN_TITLE=D Programlama Dili -SUB_MAIN_TITLE_DERSE_OZEL=– Programlama dersleri ve D referansı -SUB_AUTHOR=Ali Çehreli -LANG=tr -LANGUAGE=turkish - -HORIZNAV_CONTENT_DERSE_OZEL= -$(LINK2 /ders/d/rss.xml, -Kitap RSS D Kitabı RSS Beslemesi) -$(BR) -$(LINK2 /ders/d/index.html, -Ekitap sürümleri $(IMG pdficon_small.gif)) diff --git a/ddili/src/ders/d/devami_gelecek.d b/ddili/src/ders/d/devami_gelecek.d deleted file mode 100644 index 746bb91..0000000 --- a/ddili/src/ders/d/devami_gelecek.d +++ /dev/null @@ -1,24 +0,0 @@ -Ddoc - -$(B) -$(H3 Şimdilik bu kadar!) - -$(P -Yeni bölümlerden haberdar olmak için: -) - -$(UL -$(LI Kitabın $(LINK2 /ders/d/rss.xml, RSS beslemesini) kullanabilirsiniz -) -$(LI $(LINK2 http://ddili.org/forum/forum/4, Ddili Forum'un Duyurular Bölümüne) arada bir göz atabilirsiniz -) -) - -Macros: - SUBTITLE=Devamı Gelecek - - DESCRIPTION= - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial - -MINI_SOZLUK= diff --git a/ddili/src/ders/d/dilimler.cozum.d b/ddili/src/ders/d/dilimler.cozum.d deleted file mode 100644 index c1dc0da..0000000 --- a/ddili/src/ders/d/dilimler.cozum.d +++ /dev/null @@ -1,40 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Başka Dizi Olanakları) - -$(P -Aşağıdaki dilimdeki gibi $(I başından kısaltarak tüketmek), D'de çok yaygındır. Bu yöntem, daha ileride göreceğimiz Phobos aralıklarının da temelini oluşturur. -) - ---- -import std.stdio; - -void main() { - double[] dizi = [ 1, 20, 2, 30, 7, 11 ]; - - double[] dilim = dizi; // işimize dizinin bütün - // elemanlarına erişim - // sağlayan bir dilimle - // başlıyoruz - - while (dilim.length) { // o dilimde eleman bulunduğu - // sürece ... - - if (dilim[0] > 10) { // işlemlerde yalnızca ilk - dilim[0] /= 2; // elemanı kullanıyoruz - } - - dilim = dilim[1 .. $]; // dilimi başından kısaltıyoruz - } - - writeln(dizi); // asıl dizi değişmiş oluyor -} ---- - - -Macros: - SUBTITLE=Başka Dizi Olanakları Problem Çözümü - - DESCRIPTION=Dizi dilimleri problem çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial dizi dilim problem çözüm diff --git a/ddili/src/ders/d/dilimler.d b/ddili/src/ders/d/dilimler.d deleted file mode 100644 index 6727b61..0000000 --- a/ddili/src/ders/d/dilimler.d +++ /dev/null @@ -1,853 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX dilim) $(IX dizi) Başka Dizi Olanakları) - -$(P -Elemanları bir araya getirmeye yarayan dizileri $(LINK2 /ders/d/diziler.html, Diziler bölümünde) görmüştük. O bölümü kısa tutmak için özellikle sonraya bıraktığım başka dizi olanaklarını burada göstereceğim. -) - -$(P -Ama önce karışıklığa neden olabileceğini düşündüğüm bazı terimleri listelemek istiyorum: -) - -$(UL - -$(LI $(B Dizi:) Yan yana duran ve sıra numarasıyla erişilen elemanlardan oluşan topluluktur; bundan başkaca anlam taşımaz. -) - -$(LI -$(B Sabit uzunluklu dizi (statik dizi):) Eleman adedi değiştirilemeyen dizidir; kendi elemanlarına sahiptir. -) - -$(LI -$(B Dinamik dizi:) Eleman adedi değiştirilebilen dizidir; kendi elemanları yoktur, sahip olmadığı elemanlara erişim sağlar. -) - -$(LI $(B Dilim:) Dinamik dizilerin başka bir ismidir. -) - -) - -$(P -Bu bölümde özellikle $(I dilim) dediğim zaman dilimleri (yani dinamik dizileri), yalnızca $(I dizi) dediğim zaman da fark gözetmeden dilimleri ve sabit uzunluklu dizileri kasdetmiş olacağım. -) - -$(H5 Dilimler) - -$(P -Dilimler aslında dinamik dizilerle aynı olanaktır. Bu olanağa; dizi gibi kullanılabilme özelliği nedeniyle bazen $(I dinamik dizi), başka dizinin bir parçasına erişim sağlama özelliği nedeniyle de bazen $(I dilim) denir. Var olan başka bir dizinin elemanlarının bir bölümünü sanki daha küçük farklı bir diziymiş gibi kullandırmaya yarar. -) - -$(P -$(IX .., dilim elemanı aralığı) Dilimler, elemanları bir başlangıç indeksinden bir bitiş indeksine kadar belirlemeye yarayan $(I aralık) söz dizimiyle tanımlanırlar: -) - ---- - $(I aralığın_başı) .. $(I aralığın_sonundan_bir_sonrası) ---- - -$(P -Başlangıç indeksi aralığa dahildir; bitiş indeksi aralığın dışındadır: -) - ---- -/* ... */ = ayGünleri[0 .. 3]; // 0, 1, ve 2 dahil; 3 hariç ---- - -$(P $(I Not: Burada anlatılan aralıklar Phobos kütüphanesinin aralık kavramından farklıdır. Sınıf ve yapı arayüzleriyle ilgili olan Phobos aralıklarını daha ilerideki bir bölümde göstereceğim. -)) - -$(P -Örnek olarak $(C ayGünleri) dizisini dörde $(I dilimleyerek) birbirinden farklı dört çeyrek diziymiş gibi şöyle kullanabiliriz: -) - ---- - int[12] ayGünleri = - [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; - - int[] ilkÇeyrek = ayGünleri[0 .. 3]; - int[] ikinciÇeyrek = ayGünleri[3 .. 6]; - int[] üçüncüÇeyrek = ayGünleri[6 .. 9]; - int[] sonÇeyrek = ayGünleri[9 .. 12]; ---- - -$(P -O kodda tanımlanan son dört değişken dilimdir; her birisi asıl dizinin dört değişik bölgesine erişim sağlamaktadır. Buradaki önemli nokta, o dilimlerin kendilerine ait elemanlarının bulunmadığıdır. Onlar asıl dizinin elemanlarına erişim sağlarlar. Bir dilimdeki bir elemanın değiştirilmesi asıl dizideki asıl elemanı etkiler. Bunu görmek için dört çeyreğin ilk elemanlarına dört farklı değer verelim ve asıl diziyi yazdıralım: -) - ---- - ilkÇeyrek[0] = 1; - ikinciÇeyrek[0] = 2; - üçüncüÇeyrek[0] = 3; - sonÇeyrek[0] = 4; - - writeln(ayGünleri); ---- - -$(P -Değişen elemanları sarıyla gösteriyorum: -) - -$(SHELL -[$(HILITE 1), 28, 31, $(HILITE 2), 31, 30, $(HILITE 3), 31, 30, $(HILITE 4), 30, 31] -) - -$(P -Dikkat ederseniz, her dilim kendisinin 0 numaralı elemanını değiştirdiğinde o dilimin asıl dizide erişim sağladığı ilk eleman değişmiştir. -) - -$(P -Dizi indekslerinin 0'dan başladıklarını ve dizinin uzunluğundan bir eksiğine kadar olduklarını daha önce görmüştük. Örneğin 3 elemanlı bir dizinin yasal indeksleri 0, 1, ve 2'dir. Dilim söz diziminde bitiş indeksi $(I aralığın sonundan bir sonrası) anlamına gelir. Bu yüzden, dizinin son elemanını da aralığa dahil etmek gerektiğinde ikinci indeks olarak dizinin uzunluğu kullanılır. Örneğin uzunluğu 3 olan bir dizinin bütün elemanlarına erişim sağlamak için $(C dizi[0..3]) yazılır. -) - -$(P -Aralık söz dizimindeki doğal bir kısıtlama, başlangıç indeksinin bitiş indeksinden büyük olamayacağıdır: -) - ---- - int[3] dizi = [ 0, 1, 2 ]; - int[] dilim = dizi[2 .. 1]; // ← çalışma zamanı HATASI ---- - -$(P -Başlangıç indeksinin bitiş indeksine eşit olması ise yasaldır ve $(I boş dilim) anlamına gelir: -) - ---- - int[] dilim = birDizi[indeks .. indeks]; - writeln("Dilimin uzunluğu: ", dilim.length); ---- - -$(P -$(C indeks)'in yasal bir indeks değeri olduğunu kabul edersek, çıktısı: -) - -$(SHELL -Dilimin uzunluğu: 0 -) - -$(H5 $(C dizi.length) yerine $(C $)) - -$(P -$(IX $, dilim uzunluğu) Dizi elemanlarını $(C []) işleci ile indekslerken bazen dizinin uzunluğundan da yararlanmak gerekebilir. Bu konuda kolaylık olarak ve yalnızca $(C []) işleci içindeyken, $(C dizi.length) yazmak yerine kısaca $(C $) karakteri kullanılabilir: -) - ---- - writeln(dizi[dizi.length - 1]); // dizinin son elemanı - writeln(dizi[$ - 1]); // aynı şey ---- - -$(H5 Kopyasını almak için $(C .dup)) - -$(P -$(IX .dup) $(IX kopyalama, dizi) $(IX dizi kopyalama) İsmi "kopyala" anlamına gelen "duplicate"in kısası olan $(C .dup) niteliği, var olan bir dizinin elemanlarının kopyasından oluşan yeni bir dizi üretir: -) - ---- - double[] dizi = [ 1.25, 3.75 ]; - double[] kopyası = dizi.dup; ---- - -$(P -Bir örnek olarak Şubat'ın 29 gün çektiği senelerdeki ayların gün sayılarını tutan bir dizi oluşturmak isteyelim. Bir yöntem, önce normal senelerdeki $(C ayGünleri)'nin bir kopyasını almak ve o kopya dizideki Şubat'ın gün sayısını bir arttırmaktır: -) - ---- -import std.stdio; - -void main() { - int[12] ayGünleri = - [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; - - int[] artıkYıl = ayGünleri$(HILITE .dup); - - ++artıkYıl[1]; // yeni dizideki Şubat'ın gün sayısını - // arttırır - - writeln("Normal: ", ayGünleri); - writeln("Artık : ", artıkYıl); -} ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -Normal: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] -Artık : [31, $(HILITE 29), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] -) - -$(H5 $(IX atama, dilim ve dizi) Atama işlemi) - -$(P -$(I Değerini değiştirme) olarak bildiğimiz atama işlemi, sabit uzunluklu dizilerde de aynı anlamdadır; elemanların değerleri değişir: -) - ---- - int[3] a = [ 1, 1, 1 ]; - int[3] b = [ 2, 2, 2 ]; - - a = b; // a'nın elemanları da 2 olur - writeln(a); ---- - -$(P -Çıktısı: -) - -$(SHELL -[2, 2, 2] -) - -$(P -Dilimlerle kullanıldığında ise atama işleminin anlamı çok farklıdır: Dilimin, erişim sağlamakta olduğu elemanları bırakmasına ve yeni elemanlara erişim sağlamaya başlamasına neden olur: -) - ---- - int[] tekler = [ 1, 3, 5, 7, 9, 11 ]; - int[] çiftler = [ 2, 4, 6, 8, 10 ]; - - int[] dilim; // henüz hiçbir elemana erişim sağlamıyor - - $(HILITE dilim = )tekler[2 .. $ - 2]; - writeln(dilim); - - $(HILITE dilim = )çiftler[1 .. $ - 1]; - writeln(dilim); ---- - -$(P -Yukarıdaki koddaki $(C dilim) başlangıçta hiçbir dizinin elemanına erişim sağlamazken önce $(C tekler)'in bazı elemanlarına, sonra da $(C çiftler)'in bazı elemanlarına erişim sağlar: -) - -$(SHELL -[5, 7] -[4, 6, 8] -) - -$(H5 Uzunluğun artması paylaşımı sonlandırabilir) - -$(P -Sabit dizilere eleman eklenemediği için bu konu yalnızca dilimlerle ilgilidir. -) - -$(P -Aynı elemana aynı anda birden fazla dilimle erişilebilir. Örneğin aşağıdaki sekiz elemanın ilk ikisi üç dilim tarafından paylaşılmaktadır: -) - ---- -import std.stdio; - -void main() { - int[] dilim = [ 1, 3, 5, 7, 9, 11, 13, 15 ]; - int[] yarısı = dilim[0 .. $ / 2]; - int[] çeyreği = dilim[0 .. $ / 4]; - - çeyreği[1] = 0; // tek dilimde değişiklik - - writeln(çeyreği); - writeln(yarısı); - writeln(dilim); -} ---- - -$(P -$(C çeyreği) diliminin ikinci elemanında yapılan değişiklik asıl elemanı değiştirdiği için, bu etki dilimlerin hepsi tarafından görülür: -) - -$(SHELL -[1, $(HILITE 0)] -[1, $(HILITE 0), 5, 7] -[1, $(HILITE 0), 5, 7, 9, 11, 13, 15] -) - -$(P -Bu açıdan bakıldığında dilimlerin elemanlara $(I paylaşımlı) olarak erişim sağladıkları söylenebilir. Bu paylaşımın getirdiği bir soru işareti, dilimlerden birisine eleman eklendiğinde ne olacağıdır. Dilimler aynı asıl elemanlara erişim sağladıklarından, kısa olan dilime eklenecek elemanlar için yer yoktur. (Aksi taktirde, yeni elemanlar başka dilimlerin elemanları üzerine yazılırlar.) -) - -$(P -$(IX eleman üzerine yazma) D, yeni eklenen bir elemanın başka dilimlerin üzerine yazılmasına izin vermez ve uzunluğun artması için yer bulunmadığında paylaşımı sona erdirir. Yeri olmayan dilim paylaşımdan ayrılır. Bu işlem sırasında o dilimin erişim sağlamakta olduğu bütün elemanlar otomatik olarak kopyalanırlar ve uzayan dilim artık bu yeni elemanlara erişim sağlamaya başlar. -) - -$(P -Bunu görmek için yukarıdaki programdaki $(C çeyreği) diliminin elemanını değiştirmeden önce ona yeni bir eleman ekleyelim: -) - ---- - çeyreği ~= 42; // sonunda yeni elemana yer olmadığı - // için bu dilim bu noktada paylaşımdan - // ayrılır - - çeyreği[1] = 0; // o yüzden bu işlem diğer dilimleri - // etkilemez ---- - -$(P -Eklenen eleman dilimin uzunluğunu arttırdığı için dilim artık kopyalanan yeni elemanlara erişim sağlamaya başlar. $(C çeyreği)'nin elemanında yapılan değişikliğin $(C dilim) ve $(C yarısı) dilimlerini artık etkilemediği programın şimdiki çıktısında görülüyor: -) - -$(SHELL -[1, $(HILITE 0), 42] -[1, 3, 5, 7] -[1, 3, 5, 7, 9, 11, 13, 15] -) - -$(P -Dilimin uzunluğunun açıkça arttırılması da eleman paylaşımından ayrılmasına neden olur: -) - ---- - ++çeyreği.length; // paylaşımdan ayrılır ---- - -$(P -veya -) - ---- - çeyreği.length += 5; // paylaşımdan ayrılır ---- - -$(P -Öte yandan, bir dilimin uzunluğunun kısaltılması eleman paylaşımını sonlandırmaz. Uzunluğun kısaltılması, yalnızca $(I artık daha az elemana erişim sağlama) anlamına gelir: -) - ---- - int[] a = [ 1, 11, 111 ]; - int[] d = a; - - d = d[1 .. $]; // başından kısaltıyoruz - d[0] = 42; // elemanı dilim yoluyla değiştiriyoruz - - writeln(a); // diğer dilimi yazdırıyoruz ---- - -$(P -Çıktısından görüldüğü gibi, $(C d) yoluyla yapılan değişiklik $(C a)'nın eriştirdiği elemanı da etkilemiştir; yani paylaşım devam etmektedir: -) - -$(SHELL -[1, $(HILITE 42), 111] -) - -$(P -Uzunluğun başka ifadeler yoluyla kısaltılması da paylaşımı sonlandırmaz: -) - ---- - d = d[0 .. $ - 1]; // sonundan kısaltmak - --d.length; // aynı şey - d.length = d.length - 1; // aynı şey ---- - -$(P -Eleman paylaşımı devam eder. -) - -$(H6 $(IX .capacity) $(IX sığa) $(IX kapasite) Paylaşımın sonlanıp sonlanmayacağını belirlemek için $(C capacity)) - -$(P -Bu konuda dikkat edilmesi gereken bir karışıklık, uzunluğun artmasının paylaşımı her zaman için sonlandırmamasıdır. En uzun olan dilimin sonunda yeni elemanlara yer bulunduğu zaman paylaşım sonlanmaz: -) - ---- -import std.stdio; - -void main() { - int[] dilim = [ 1, 3, 5, 7, 9, 11, 13, 15 ]; - int[] yarısı = dilim[0 .. $ / 2]; - int[] çeyreği = dilim[0 .. $ / 4]; - - dilim ~= 42; // en uzun dilime ekleniyor - $(HILITE dilim[1] = 0); - - writeln(çeyreği); - writeln(yarısı); - writeln(dilim); -} ---- - -$(P -Çıktıda görüldüğü gibi, uzunluğu artmış olmasına rağmen en uzun olan dilime eleman eklenmesi paylaşımı sonlandırmamıştır. Yapılan değişiklik bütün dilimleri etkilemiştir: -) - -$(SHELL -[1, $(HILITE 0)] -[1, $(HILITE 0), 5, 7] -[1, $(HILITE 0), 5, 7, 9, 11, 13, 15, 42] -) - -$(P -Bir dilime yeni bir eleman eklendiğinde paylaşımın sonlanıp sonlanmayacağı $(C capacity) niteliği ile belirlenir. (Aslında $(C capacity) bir işlev olarak gerçekleştirilmiştir ancak bu ayrımın burada önemi yoktur.) -) - -$(P -Dilime ileride eklenecek olan yeni elemanlar için önceden ayrılmış olan alana o dilimin $(I sığası) denir. $(C capacity) değerinin anlamı aşağıdaki gibidir: -) - -$(UL - -$(LI -Değeri 0 ise, bu dilim en uzun dilim değildir. Bu durumda yeni bir eleman eklendiğinde dilimin bütün elemanları başka yere kopyalanırlar ve paylaşım sonlanır. -) - -$(LI -Değeri sıfırdan farklı ise bu en uzun dilimdir. Bu durumda $(C capacity)'nin anlamı, başka yere kopyalanmaları gerekmeden dilimin en fazla kaç eleman barındıracağıdır. Dilime eklenebilecek yeni eleman sayısı, $(C capacity)'den mevcut eleman adedi çıkartılarak bulunur. Dilimin uzunluğu $(C capacity) değerine eşit ise, bir eleman daha eklendiğinde elemanlar başka yere kopyalanacaklar demektir. -) - -) - -$(P -Buna uygun olarak, eleman eklendiğinde paylaşımın sonlanıp sonlanmayacağı aşağıdaki gibi bir kodla belirlenebilir: -) - ---- - if (dilim.capacity == 0) { - /* Yeni bir eleman eklendiğinde bu dilimin bütün - * elemanları başka bir yere kopyalanacaklar - * demektir. */ - - // ... - - } else { - /* Bu dilimde yeni elemanlar için yer olabilir. Kaç - * elemanlık yer olduğunu hesaplayalım: */ - auto kaçElemanDaha = dilim.capacity - dilim.length; - - // ... - } ---- - -$(P -Sığayla ilgili ilginç bir durum, $(I bütün elemanlara) erişim sağlayan birden fazla dilim olduğunda ortaya çıkar. Böyle bir durumda her dilim sığası olduğunu bildirir: -) - ---- -import std.stdio; - -void main() { - // Bütün elemanlara eriştiren üç dilim - int[] d0 = [ 1, 2, 3, 4 ]; - int[] d1 = d0; - int[] d2 = d0; - - writeln(d0.capacity); - writeln(d1.capacity); - writeln(d2.capacity); -} ---- - -$(P -Üçünün de sığası vardır: -) - -$(SHELL -7 -7 -7 -) - -$(P -Ancak, dilimlerden birisine eleman eklendiği an diğerleri sığalarını yitirirler: -) - ---- - $(HILITE d1 ~= 42); $(CODE_NOTE artık d1 en uzundur) - - writeln(d0.capacity); - writeln(d1.capacity); - writeln(d2.capacity); ---- - -$(P -Eleman eklenen dilim en uzun dilim haline geldiğinden artık yalnızca onun sığası vardır: -) - -$(SHELL -0 -7 $(SHELL_NOTE artık yalnızca d1'in sığası var) -0 -) - -$(H6 $(IX .reserve) Elemanlar için yer ayırmak) - -$(P -Hem eleman kopyalamanın hem de elemanlar için yeni yer ayırmanın az da olsa bir süre bedeli vardır. Bu yüzden, eleman eklemek pahalı bir işlem olabilir. Eleman adedinin baştan bilindiği durumlarda böyle bir bedelin önüne geçmek için tek seferde yer ayırmak mümkündür: -) - ---- -import std.stdio; - -void main() { - int[] dilim; - - dilim$(HILITE .reserve(20)); - writeln(dilim.capacity); - - foreach (eleman; 0 .. $(HILITE 17)) { - dilim ~= eleman; $(CODE_NOTE bu elemanlar taşınmazlar) - } -} ---- - -$(SHELL -31 $(SHELL_NOTE En az 20 elemanlık sığa) -) - -$(P -$(C dilim)'in elemanları ancak 31'den fazla eleman olduğunda başka bir yere taşınacaklardır. -) - -$(H5 $(IX bütün elemanlar üzerinde işlem) $(IX eleman, hepsi ile işlem) Bütün elemanlar üzerindeki işlemler) - -$(P -Bu olanak hem sabit uzunluklu dizilerle hem de dilimlerle kullanılabilir. -) - -$(P -Dizi isminden sonra yazılan içi boş $(C []) karakterleri $(I bütün elemanlar) anlamına gelir. Bu olanak, elemanların her birisiyle yapılması istenen işlemlerde büyük kolaylık sağlar. -) - -$(P -$(I Not: Bu bölümdeki kodların en son denendikleri derleyici olan dmd 2.071 bu işlemleri henüz dilimler için tam olarak desteklemiyordu. O yüzden, bu başlık altındaki bazı örneklerde yalnızca sabit uzunluklu diziler kullanılmaktadır.) -) - ---- -import std.stdio; - -void main() { - double[3] a = [ 10, 20, 30 ]; - double[3] b = [ 2, 3, 4 ]; - - double[3] sonuç = $(HILITE a[] + b[]); - - writeln(sonuç); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[12, 23, 34] -) - -$(P -O programdaki toplama işlemi, $(C a) ve $(C b) dizilerinin birbirlerine karşılık gelen elemanlarını ayrı ayrı toplar: önce ilk elemanlar kendi aralarında, sonra ikinci elemanlar kendi aralarında, vs. O yüzden böyle işlemlerde kullanılan dizilerin uzunluklarının eşit olmaları şarttır. -) - -$(P -Yukarıdaki programdaki $(C +) işleci yerine; daha önce gördüğünüz $(C +), $(C -), $(C *), $(C /), $(C %), ve $(C ^^) aritmetik işleçlerini; ilerideki bölümlerde karşılaşacağınız $(C ^), $(C &), ve $(C |) ikili bit işleçlerini; ve bir dizinin önüne yazılan tekli $(C -) ve $(C ~) işleçlerini kullanabilirsiniz. -) - -$(P -Bu işleçlerin atamalı olanları da kullanılabilir: $(C =), $(C +=), $(C -=), $(C *=), $(C /=), $(C %=), $(C ^^=), $(C ^=), $(C &=), ve $(C |=). -) - -$(P -Bu olanak yalnızca iki diziyi ilgilendiren işlemler için değildir; bir dizi yanında onun elemanlarıyla uyumlu olan bir ifade de kullanılabilir. Örneğin bir dizinin bütün elemanlarını dörde bölmek için: -) - ---- - double[3] a = [ 10, 20, 30 ]; - $(HILITE a[]) /= 4; - - writeln(a); ---- - -$(P -Çıktısı: -) - -$(SHELL -[2.5, 5, 7.5] -) - -$(P -Bütün elemanlarını belirli bir değere eşitlemek için: -) - ---- - $(HILITE a[]) = 42; - writeln(a); ---- - -$(P -Çıktısı: -) - -$(SHELL -[42, 42, 42] -) - -$(P -Bu olanağın dilimlerle kullanımında hataya açık bir durum vardır. Sonuçta eleman değerlerinde bir fark görülmese bile aşağıdaki iki ifade aslında anlamsal açıdan çok farklıdır: -) - ---- - dilim2 = dilim1; // ← dilim1'in elemanlarına erişim - // sağlamaya başlar - - dilim3[] = dilim1; // ← zaten erişim sağlamakta olduğu - // elemanların değerleri değişir ---- - -$(P -$(C dilim2)'nin doğrudan atama işleciyle kullanılıyor olması, onun artık $(C dilim1)'in elemanlarına erişim sağlamaya başlamasına neden olur. Oysa $(C dilim3[]) ifadesi $(I dilim3'ün bütün elemanları) anlamını taşıdığı için, onun bütün elemanlarının değerleri $(C dilim1)'in elemanlarının değerlerini alırlar. Bu yüzden, unutulan bir $(C []) işlecinin etkisi çok büyük olabilir. -) - -$(P -Bunu aşağıdaki programda görebiliriz: -) - ---- -import std.stdio; - -void main() { - double[] dilim1 = [ 1, 1, 1 ]; - double[] dilim2 = [ 2, 2, 2 ]; - double[] dilim3 = [ 3, 3, 3 ]; - - dilim2 = dilim1; // ← dilim1'in elemanlarına erişim - // sağlamaya başlar - - dilim3[] = dilim1; // ← zaten erişim sağlamakta olduğu - // elemanların değerleri değişir - - writeln("dilim1 önce : ", dilim1); - writeln("dilim2 önce : ", dilim2); - writeln("dilim3 önce : ", dilim3); - - $(HILITE dilim2[0] = 42); // ← erişimini dilim1'le paylaşmakta - // olduğu eleman değişir - - dilim3[0] = 43; // ← kendi elemanı değişir - - writeln("dilim1 sonra: ", dilim1); - writeln("dilim2 sonra: ", dilim2); - writeln("dilim3 sonra: ", dilim3); -} ---- - -$(P -$(C dilim2)'de yapılan değişiklik $(C dilim1)'i de etkilemiştir: -) - -$(SHELL -dilim1 önce : [1, 1, 1] -dilim2 önce : [1, 1, 1] -dilim3 önce : [1, 1, 1] -dilim1 sonra: [$(HILITE 42), 1, 1] -dilim2 sonra: [$(HILITE 42), 1, 1] -dilim3 sonra: [43, 1, 1] -) - -$(P -Buradaki tehlike; $(C dilim2) atanırken $(C []) işlecinin belki de unutulmuş olmasının etkisinin, belki de o yüzden istenmeden paylaşılmaya başlanmış olan eleman değişene kadar farkedilememiş olmasıdır. -) - -$(P -Bu gibi tehlikeler yüzünden bu işlemleri dilimlerle kullanırken dikkatli olmak gerekir. -) - -$(H5 $(IX çok boyutlu dizi) Çok boyutlu diziler) - -$(P -Şimdiye kadar gördüğümüz dizi işlemlerinde eleman türü olarak hep $(C int) ve $(C double) gibi temel türler kullandık. Eleman türü olarak aslında başka türler, örneğin diziler de kullanılabilir. Böylece $(I dizi dizisi) gibi daha karmaşık topluluklar tanımlayabiliriz. Elemanlarının türü dizi olan dizilere $(I çok boyutlu dizi) denir. -) - -$(P -Şimdiye kadar gördüğümüz dizilerin elemanlarını hep soldan sağa doğru yazmıştık. İki boyutlu dizi kavramını anlamayı kolaylaştırmak için bir diziyi bir kere de yukarıdan aşağıya doğru yazalım: -) - ---- - int[] dizi = [ - 10, - 20, - 30, - 40 - ]; ---- - -$(P -Kodu güzelleştirmek için kullanılan boşlukların ve fazladan satırların derleyicinin gözünde etkisiz olduklarını biliyorsunuz. Yukarıdaki dizi önceden olduğu gibi tek satırda da yazılabilirdi ve aynı anlama gelirdi. -) - -$(P -Şimdi o dizinin her bir elemanını $(C int[]) türünde bir değerle değiştirelim: -) - ---- - /* ... */ dizi = [ - [ 10, 11, 12 ], - [ 20, 21, 22 ], - [ 30, 31, 32 ], - [ 40, 41, 42 ] - ]; ---- - -$(P -Yaptığımız tek değişiklik, $(C int) yerine $(C int[]) türünde elemanlar yazmak oldu. Kodun yasal olması için eleman türünü artık $(C int) olarak değil, $(C int[]) olarak belirlememiz gerekir: -) - ---- - $(HILITE int[])[] dizi = [ - [ 10, 11, 12 ], - [ 20, 21, 22 ], - [ 30, 31, 32 ], - [ 40, 41, 42 ] - ]; ---- - -$(P -Satır ve sütunlardan oluştukları için yukarıdaki gibi dizilere $(I iki boyutlu dizi) denir. -) - -$(P -Elemanları $(I int dizisi) olan yukarıdaki dizinin kullanımı şimdiye kadar öğrendiklerimizden farklı değildir. Her bir elemanının $(C int[]) türünde olduğunu hatırlamak ve $(C int[]) türüne uyan işlemlerde kullanmak yeter: -) - ---- -dizi ~= [ 50, 51 ]; // yeni bir eleman (yani dilim) ekler -dizi[0] ~= 13; // ilk elemanına (yani ilk dilimine) ekler ---- - -$(P -Aynı dizinin şimdiki hali: -) - -$(SHELL_SMALL -[[10, 11, 12, $(HILITE 13)], [20, 21, 22], [30, 31, 32], [40, 41, 42], $(HILITE [50, 51])] -) - -$(P -Dizinin kendisi veya elemanları sabit uzunluklu da olabilir: -) - ---- - int[2][3][4] dizi; // 2 sütun, 3 satır, 4 düzlem ---- - -$(P -Yukarıdaki tanımı $(I iki sütunlu üç satırdan oluşan dört düzlem) diye düşünebilirsiniz. Öyle bir dizi, örneğin bir macera oyununda ve her katında 2x3=6 oda bulunan 4 katlı bir bina ile ilgili bir kavram için kullanılıyor olabilir. -) - -$(P -Örneğin öyle bir binanın ikinci katının ilk odasında bulunan eşyaların sayısı şöyle arttırılabilir: -) - ---- - // ikinci katın indeksi 1'dir ve o katın ilk odasına - // [0][0] ile erişilir - ++eşyaSayıları[1][0][0]; ---- - -$(P -Yukarıdaki söz dizimlerine ek olarak, $(I dilim dilimi) oluşturmak için $(C new) ifadesi de kullanılabilir. Aşağıdaki örnek yalnızca iki boyut belirtiyor: -) - ---- -import std.stdio; - -void main() { - int[][] d = new int[][](2, 3); - writeln(d); -} ---- - -$(P -Yukarıdaki $(C new) ifadesi 2 adet 3 elemanlı dizi oluşturur ve onlara erişim sağlayan bir dilim döndürür. Çıktısı: -) - -$(SHELL -[[0, 0, 0], [0, 0, 0]] -) - -$(H5 Özet) - -$(UL - -$(LI -Sabit uzunluklu dizilerin kendi elemanları vardır; dilimler ise kendilerine ait olmayan elemanlara erişim sağlarlar. -) - -$(LI -$(C []) işleci içindeyken $(C $(I dizi_ismi).length) yazmak yerine kısaca $(C $) yazılabilir. -) - -$(LI -$(C .dup) niteliği, elemanların kopyalarından oluşan yeni bir dizi üretir. -) - -$(LI -Atama işlemi, sabit dizilerde eleman değerlerini değiştirir; dilimlerde ise başka elemanlara erişim sağlanmasına neden olur. -) - -$(LI -Uzayan dilim paylaşımdan $(I ayrılabilir) ve yeni kopyalanmış olan elemanlara erişim sağlamaya başlayabilir. Bunun olup olmayacağı $(C capacity()) ile belirlenir. -) - -$(LI -$(C dizi[]) yazımı $(I dizinin bütün elemanları) anlamına gelir; kendisine uygulanan işlem her bir elemanına ayrı ayrı uygulanır. -) - -$(LI -Elemanları dizi olan dizilere çok boyutlu dizi denir. -) - -) - -$(PROBLEM_TEK - -$(P -Bir $(C double) dizisini başından sonuna doğru ilerleyin ve değerleri 10'dan büyük olanların değerlerini yarıya indirin. Örneğin elinizde şu dizi varsa: -) - ---- - double[] dizi = [ 1, 20, 2, 30, 7, 11 ]; ---- - -$(P -elemanlarının değerleri şuna dönüşsün: -) - -$(SHELL -[1, $(HILITE 10), 2, $(HILITE 15), 7, $(HILITE 5.5)] -) - -$(P -Çeşitli çözümleri olsa da, bunu yalnızca dilim olanakları ile başarmaya çalışın. İşe bütün diziye erişim sağlayan bir dilimle başlayabilirsiniz. Ondan sonra o dilimi her seferinde baş tarafından tek eleman kısaltabilir ve dilimin hep ilk elemanını kullanabilirsiniz. -) - -$(P -Şu ifade dilimi başından tek eleman kısaltır: -) - ---- - dilim = dilim[1 .. $]; ---- - -) - -Macros: - SUBTITLE=Başka Dizi Olanakları - - DESCRIPTION=D dilim ve dizilerinin tanıtılması - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial dizgi dilim - -SOZLER= -$(aralik) -$(atama) -$(dilim) -$(dinamik) -$(dizi) -$(ifade) -$(indeks) -$(nitelik) -$(phobos) -$(referans) -$(siga) -$(topluluk) diff --git a/ddili/src/ders/d/dizgiler.cozum.d b/ddili/src/ders/d/dizgiler.cozum.d deleted file mode 100644 index 2443d5b..0000000 --- a/ddili/src/ders/d/dizgiler.cozum.d +++ /dev/null @@ -1,62 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Dizgiler) - -$(OL - -$(LI -Kütüphane başvuru belgelerinin amaçları öğretmek değildir. Kütüphane belgelerini caydırıcı derecede kısa ve anlaşılmaz bulabilirsiniz. Siz de zamanla alışacaksınız ve uzun yazılardan çok öz belgeler yeğleyeceksiniz. -) - -$(LI - ---- -import std.stdio; -import std.string; - -void main() { - write("Adınız? "); - string ad = capitalize(strip(readln())); - - write("Soyadınız? "); - string soyad = capitalize(strip(readln())); - - string adSoyad = ad ~ " " ~ soyad; - writeln(adSoyad); -} ---- - -) - -$(LI - ---- -import std.stdio; -import std.string; - -void main() { - write("Satırı giriniz: "); - string satır = strip(readln()); - - ptrdiff_t ilk_a = indexOf(satır, 'a'); - - if (ilk_a == -1) { - writeln("Bu satırda a harfi yok."); - - } else { - ptrdiff_t son_a = lastIndexOf(satır, 'a'); - writeln(satır[ilk_a .. son_a + 1]); - } -} ---- - -) - -) - -Macros: - SUBTITLE=Dizgiler Problem Çözümü - - DESCRIPTION=D programlama dili dersi çözümleri: Dizgiler - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial dizgiler dizgi problem çözüm diff --git a/ddili/src/ders/d/dizgiler.d b/ddili/src/ders/d/dizgiler.d deleted file mode 100644 index 0b17b35..0000000 --- a/ddili/src/ders/d/dizgiler.d +++ /dev/null @@ -1,558 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Dizgiler) - -$(P -"merhaba" gibi metin parçalarının dizgi olduklarını zaten öğrenmiş ve şimdiye kadarki kodlarda çok yerde kullanmıştık. Dizgileri anlamaya yarayan iki olanağı da bundan önceki üç bölümde gördük: diziler ve karakterler. -) - -$(P -Dizgiler o iki olanağın bileşiminden başka bir şey değildir: elemanlarının türü $(I karakter) olan $(I dizilere) dizgi denir. Örneğin $(C char[]) bir dizgi türüdür. Ancak, $(LINK2 /ders/d/karakterler.html, Karakterler bölümünde) gördüğümüz gibi, D'de üç değişik karakter türü olduğu için, üç değişik dizgi türünden ve bunların bazen şaşırtıcı olabilecek etkileşimlerinden söz etmek gerekir. -) - -$(H5 $(IX readln) $(IX strip) $(C readf) yerine $(C readln) ve $(C strip)) - -$(P -Konsoldan satır okuma ile ilgili bazı karışıklıklara burada değinmek istiyorum. -) - -$(P -Dizgiler karakter dizileri oldukları için $(I satır sonu) anlamına gelen $(STRING '\n') gibi kontrol karakterlerini de barındırabilirler. O yüzden, girdiğimiz bilgilerin sonunda bastığımız Enter tuşunu temsil eden kodlar da okunurlar ve dizginin parçası haline gelirler. -) - -$(P -Dahası, girişten kaç karakter okunmak istendiği de bilinmediği için $(C readf) $(I giriş tükenene kadar) gelen bütün karakterleri dizginin içine okur. -) - -$(P -Bunun sonucunda da şimdiye kadar kullanmaya alıştığımız $(C readf) istediğimiz gibi işlemez: -) - ---- -import std.stdio; - -void main() { - char[] isim; - - write("İsminiz nedir? "); - readf(" %s", &isim); - - writeln("Çok memnun oldum ", isim, "!"); -} ---- - -$(P -Yazılan isimden sonra basılan Enter girişi sonlandırmaz, ve $(C readf) dizgiye eklemek için karakter beklemeye devam eder: -) - -$(SHELL -İsminiz nedir? Mert - $(SHELL_NOTE Enter'a basıldığı halde giriş sonlanmaz) - $(SHELL_NOTE (bir kere daha basıldığını varsayalım)) -) - -$(P -Konsolda girişi sonlandırmak için Linux ortamlarında Ctrl-D'ye, Windows ortamlarında da Ctrl-Z'ye basılır. Girişi o şekilde sonlandırdığınızda Enter'lar nedeniyle oluşan satır sonu kodlarının bile dizginin parçası haline geldiklerini görürsünüz: -) - -$(SHELL -Çok memnun oldum Mert - $(SHELL_NOTE_WRONG isimden sonra $(I satır sonu karakteri) var) -! $(SHELL_NOTE_WRONG (bir tane daha)) -) - -$(P -İsimden hemen sonra yazdırılmak istenen ünlem işareti satır sonu kodlarından sonra belirmiştir. -) - -$(P -Bu yüzden $(C readf) çoğu durumda girişten dizgi okumaya uygun değildir. Onun yerine ismi "satır oku" anlamındaki "read line"dan türemiş olan $(C readln) kullanılabilir. -) - -$(P -$(C readln)'ın kullanımı $(C readf)'ten farklıdır; $(STRING " %s") düzen dizgisini ve $(C &) işlecini gerektirmez: -) - ---- -import std.stdio; - -void main() { - char[] isim; - - write("İsminiz nedir? "); - $(HILITE readln(isim)); - - writeln("Çok memnun oldum ", isim, "!"); -} ---- - -$(P -Buna rağmen satır sonunu belirleyen kodu o da barındırır: -) - -$(SHELL -İsminiz nedir? Mert -Çok memnun oldum Mert -! $(SHELL_NOTE_WRONG isimden sonra yine "satır sonu" var) -) - -$(P -Dizgilerin sonundaki satır sonu kodları ve bütün boşluk karakterleri $(C std.string) modülünde tanımlanmış olan $(C strip) işlevi ile silinebilir: -) - ---- -import std.stdio; -$(HILITE import std.string;) - -void main() { - char[] isim; - - write("İsminiz nedir? "); - readln(isim); - $(HILITE isim = strip(isim);) - - writeln("Çok memnun oldum ", isim, "!"); -} ---- - -$(P -Yukarıdaki $(C strip) ifadesi $(C isim)'in sonundaki satır sonu kodlarının silinmiş halini döndürür. O halinin tekrar $(C isim)'e atanması da $(C isim)'i değiştirmiş olur: -) - -$(SHELL -İsminiz nedir? Mert -Çok memnun oldum Mert! $(SHELL_NOTE "satır sonu" kodlarından arınmış olarak) -) - -$(P -$(C readln) ve $(C strip) zincirleme biçimde daha kısa olarak da yazılabilirler: -) - ---- - string isim = strip(readln()); ---- - -$(P -O yazımı $(C string) türünü tanıttıktan sonra kullanmaya başlayacağım. -) - -$(H5 $(IX formattedRead) Dizgiden veri okumak için $(C formattedRead)) - -$(P -Girişten veya herhangi başka bir kaynaktan edinilmiş olan bir dizginin içeriği $(C std.format) modülündeki $(C formattedRead()) ile okunabilir. Bu işlevin ilk parametresi veriyi içeren dizgidir. Sonraki parametreler ise aynı $(C readf)'teki anlamdadır: -) - ---- -import std.stdio; -import std.string; -$(HILITE import std.format;) - -void main() { - write("İsminizi ve yaşınızı aralarında boşluk" ~ - " karakteriyle girin: "); - - string satır = strip(readln()); - - string isim; - int yaş; - $(HILITE formattedRead)(satır, " %s %s", &isim, &yaş); - - writeln("İsminiz ", isim, ", yaşınız ", yaş, '.'); -} ---- - -$(SHELL -İsminizi ve yaşınızı aralarında boşluk karakteriyle girin: $(HILITE Mert 30) -İsminiz $(HILITE Mert), yaşınız $(HILITE 30). -) - -$(P -Aslında, hem $(C readf) hem de $(C formattedRead) başarıyla okuyup dönüştürdükleri veri adedini $(I döndürürler). Dizginin geçerli düzende olup olmadığı bu değer beklenen adet ile karşılaştırılarak belirlenir. Örneğin, yukarıdaki $(C formattedRead) çağrısı $(C string) türündeki isimden ve $(C int) türündeki yaştan oluşan $(I iki) adet veri beklediğinden, dizginin geçerliliği aşağıdaki gibi denetlenebilir: -) - ---- - $(HILITE uint adet) = formattedRead(satır, " %s %s", &isim, &yaş); - - if ($(HILITE adet != 2)) { - writeln("Hata: Geçersiz satır."); - - } else { - writeln("İsminiz ", isim, ", yaşınız ", yaş, '.'); - } ---- - -$(P -Girilen veri $(C isim) ve $(C yaş) değişkenlerine dönüştürülemiyorsa program hata verir: -) - -$(SHELL -İsminizi ve yaşınızı aralarında boşluk karakteriyle girin: $(HILITE Mert) -Hata: Geçersiz satır. -) - -$(H5 $(IX ") Tek tırnak değil, çift tırnak) - -$(P -Tek tırnakların karakter sabiti tanımlarken kullanıldıklarını görmüştük. Dizgi sabitleri için ise çift tırnaklar kullanılır: $(STRING 'a') karakterdir, $(STRING "a") tek karakterli bir dizgidir. -) - -$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string), $(C wstring), ve $(C dstring) değişmezdirler) - -$(P -D'de üç karakter türüne karşılık gelen üç farklı karakter dizisi türü vardır: $(C char[]), $(C wchar[]), ve $(C dchar[]). -) - -$(P -Bu üç dizi türünün $(I değişmez) olanlarını göstermek için üç tane de $(I takma isim) vardır: $(C string), $(C wstring), ve $(C dstring). Bu takma isimler kullanılarak tanımlanan dizgilerin karakterleri $(I değişmezdirler). Bir örnek olarak, bir $(C wchar[]) değişkeninin karakterleri değişebilir ama bir $(C wstring) değişkeninin karakterleri değişemez. (D'nin $(I değişmezlik) kavramının ayrıntılarını daha sonraki bölümlerde göreceğiz.) -) - -$(P -Örneğin bir $(C string)'in baş harfini büyütmeye çalışan şu kodda bir derleme hatası vardır: -) - ---- - string değişmez = "merhaba"; - değişmez[0] = 'M'; $(DERLEME_HATASI) ---- - -$(P -Buna bakarak, değiştirilmesi istenen dizgilerin dizi yazımıyla yazılabileceklerini düşünebiliriz ama o da derlenemez. Sol tarafı dizi yazımıyla yazarsak: -) - ---- - char[] bir_dilim = "merhaba"; $(DERLEME_HATASI) ---- - -$(P -O kod da derlenemez. Bunun iki nedeni vardır: -) - -$(OL -$(LI $(STRING "merhaba") gibi kodun içine hazır olarak yazılan dizgilerin türü $(C string)'dir ve bu yüzden değişmezdirler. -) -$(LI Türü $(C char[]) olan sol taraf, sağ tarafın bir $(I dilimidir). -) -) - -$(P -Bir önceki bölümden hatırlayacağınız gibi, sol taraf sağ tarafı gösteren bir dilim olarak algılanır. $(C char[]) değişebilir ve $(C string) değişmez olduğu için de burada bir uyumsuzluk oluşur: derleyici, değişebilen bir dilim ile değişmez bir diziye erişilmesine izin vermemektedir. -) - -$(P -Bu durumda yapılması gereken, değişmez dizinin bir kopyasını almaktır. Bir önceki bölümde gördüğümüz $(C .dup) niteliğini kullanarak: -) - ---- -import std.stdio; - -void main() { - char[] dizgi = "merhaba"$(HILITE .dup); - dizgi[0] = 'M'; - writeln(dizgi); -} ---- - -$(P -Derlenir ve dizginin baş harfi değişir: -) - -$(SHELL -Merhaba -) - -$(P -Benzer şekilde, örneğin $(C string) gereken yerde de $(C char[]) kullanılamaz. Değişebilen $(C char[]) türünden, değiştirilemeyen $(C string) türünü üretmek için de $(C .idup) niteliğini kullanmak gerekebilir. $(C s)'nin türü $(C char[]) olduğunda aşağıdaki satır derlenemez: -) - ---- - string sonuç = s ~ '.'; $(DERLEME_HATASI) ---- - -$(P -$(C s)'nin türü $(C char[]) olduğu için sağ taraftaki sonucun türü de $(C char[])'dır. Bütün o sonuç kullanılarak değişmez bir dizgi elde etmek için $(C .idup) kullanılabilir: -) - ---- - string sonuç = (s ~ '.')$(HILITE .idup); ---- - -$(H5 $(IX length, dizgi) Dizgilerin şaşırtıcı olabilen uzunlukları) - -$(P -Unicode karakterlerinin bazılarının birden fazla baytla gösterildiklerini ve Türk alfabesine özgü harflerin iki baytlık olduklarını görmüştük. Bu bazen şaşırtıcı olabilir: -) - ---- - writeln("aĞ".length); ---- - -$(P -"aĞ" dizgisi 2 harf içerdiği halde dizinin uzunluğu 3'tür: -) - -$(SHELL -3 -) - -$(P -Bunun nedeni, "merhaba" şeklinde yazılan hazır dizgilerin eleman türünün $(C char) olmasıdır. $(C char) da UTF-8 kod birimi olduğu için, o dizginin uzunluğu 3'tür (a için tek, Ğ için iki kod birimi). -) - -$(P -Bunun görünür bir etkisi, iki baytlık bir harfi tek baytlık bir harfle değiştirmeye çalıştığımızda karşımıza çıkar: -) - ---- - char[] d = "aĞ".dup; - writeln("Önce: ", d); - d[1] = 't'; - writeln("Sonra:", d); ---- - -$(SHELL -Önce: aĞ -Sonra:at� $(SHELL_NOTE_WRONG YANLIŞ) -) - -$(P -O kodda dizginin 'Ğ' harfinin 't' harfi ile değiştirilmesi istenmiş, ancak 't' harfi tek bayttan oluştuğu için 'Ğ'yi oluşturan baytlardan ancak birincisinin yerine geçmiş ve ikinci bayt çıktıda belirsiz bir karaktere dönüşmüştür. -) - -$(P -O yüzden, bazı başka programlama dillerinin normal karakter türü olan $(C char)'ı D'de bu amaçla kullanamayız. (Aynı sakınca $(C wchar)'da da vardır.) Unicode'un tanımladığı anlamda harflerle, imlerle, ve diğer simgelerle ilgilendiğimiz durumlarda $(C dchar) türünü kullanmamız gerekir: -) - ---- - dchar[] d = "aĞ"d.dup; - writeln("Önce: ", d); - d[1] = 't'; - writeln("Sonra:", d); ---- - -$(SHELL -Önce: aĞ -Sonra:at -) - -$(P -Doğru çalışan kodda iki değişiklik yapıldığına dikkat edin: -) -$(OL -$(LI Dizginin türü $(C dchar[]) olarak belirlenmiştir.) -$(LI $(STRING "aĞ"d) hazır dizgisinin sonunda $(C d) belirteci kullanılmıştır.) -) - -$(P -Buna rağmen, $(C dchar[]) ve $(C dstring) kullanımı karakterlerle ilgili bütün sorunları çözemez. Örneğin, kullanıcının girdiği "aĞ" dizgisinin uzunluğu 2 olmayabilir çünkü örneğin 'Ğ' tek Unicode karakteri olarak değil, 'G' ve sonrasında gelen $(I birleştirici) $(ASIL combining) breve şapkası olarak kodlanmış olabilir. Unicode ile ilgili bu tür sorunlardan kaçınmanın en kolay yolu bir Unicode kütüphanesi kullanmaktır. -) - -$(H5 $(IX hazır dizgi) Hazır dizgiler) - -$(P -Hazır dizgilerin özellikle belirli bir karakter türünden olmasını sağlamak için sonlarına belirleyici karakterler eklenir: -) - ---- -import std.stdio; - -void main() { - string s = "aĞ"c; // bu, "aĞ" ile aynı şeydir - wstring w = "aĞ"w; - dstring d = "aĞ"d; - - writeln(s.length); - writeln(w.length); - writeln(d.length); -} ---- - -$(SHELL -3 -2 -2 -) - -$(P -a ve Ğ harflerinin her ikisi de $(C wchar) ve $(C dchar) türlerinden tek bir elemana sığabildiklerinden son iki dizginin uzunlukları 2 olmaktadır. -) - -$(H5 $(IX birleştirmek, dizgi) Dizgi birleştirmek) - -$(P -Dizgiler aslında dizi olduklarından, dizi işlemleri onlar için de geçerlidir. İki dizgiyi birleştirmek için $(C ~) işleci, bir dizginin sonuna başka dizgi eklemek için de $(C ~=) işleci kullanılır: -) - ---- -import std.stdio; -import std.string; - -void main() { - write("İsminiz? "); - string isim = strip(readln()); - - // Birleştirme örneği: - string selam = "Merhaba " ~ isim; - - // Sonuna ekleme örneği: - selam ~= "! Hoşgeldin..."; - - writeln(selam); -} ---- - -$(SHELL -İsminiz? Can -Merhaba Can! Hoşgeldin... -) - -$(H5 Dizgileri karşılaştırmak) - -$(P -$(I Not: Unicode bütün yazı sistemlerindeki harfleri tanımlasa da onların o yazı sistemlerinde nasıl sıralandıklarını belirlemez. Aşağıdaki işlevleri kullanırken bu konuda beklenmedik sonuçlarla karşılaşabilirsiniz.) -) - -$(P -Daha önce sayıların küçüklük büyüklük karşılaştırmalarında kullanılan $(C <), $(C >=), vs. işleçlerini görmüştük. Aynı işleçleri dizgilerle de kullanabiliriz. Bu işleçlerin $(I küçüklük) kavramı dizgilerde alfabetik sırada $(I önce) anlamındadır. Benzer şekilde, büyüklük de alfabetik sırada $(I sonra) demektir: -) - ---- -import std.stdio; -import std.string; - -void main() { - write(" Bir dizgi giriniz: "); - string dizgi_1 = strip(readln()); - - write("Bir dizgi daha giriniz: "); - string dizgi_2 = strip(readln()); - - if (dizgi_1 $(HILITE ==) dizgi_2) { - writeln("İkisi aynı!"); - - } else { - string önceki; - string sonraki; - - if (dizgi_1 $(HILITE <) dizgi_2) { - önceki = dizgi_1; - sonraki = dizgi_2; - - } else { - önceki = dizgi_2; - sonraki = dizgi_1; - } - - writeln("Sıralamada önce '", önceki, - "', sonra '", sonraki, "' gelir."); - } -} ---- - -$(H5 Büyük küçük harfler farklıdır) - -$(P -Harflerin büyük ve küçük hallerinin farklı karakter kodlarına sahip olmaları onların birbirlerinden farklı oldukları gerçeğini de getirir. Örneğin 'A' ile 'a' farklı harflerdir. -) - -$(P -Ek olarak, ASCII tablosundaki kodlarının bir yansıması olarak, büyük harflerin hepsi, sıralamada küçük harflerin hepsinden önce gelir. Örneğin büyük olduğu için 'B', sıralamada 'a'dan önce gelir. Aynı şekilde, "aT" dizgisi, 'T' harfi 'ç'den önce olduğu için "aç" dizgisinden önce sıralanır. -) - -$(P -Bazen dizgileri harflerin küçük veya büyük olmalarına bakmaksızın karşılaştırmak isteriz. Böyle durumlarda yukarıda gösterilen aritmetik işleçler yerine, $(C std.string.icmp) işlevi kullanılabilir. -) - -$(H5 std.string modülü) - -$(P -$(C std.string) modülü dizgilerle ilgili işlevler içerir. Bu işlevlerin tam listesini $(LINK2 http://dlang.org/phobos/std_string.html, kendi belgesinde) bulabilirsiniz. -) - -$(P -Oradaki işlevler arasından bir kaç tanesi: -) - -$(UL - -$(LI $(C indexOf): Verilen karakteri bir dizgi içinde baştan sona doğru arar ve bulursa bulduğu yerin indeksini, bulamazsa -1 değerini döndürür. Seçime bağlı olarak bildirilebilen üçüncü parametre, küçük büyük harf ayrımı olmadan aranmasını sağlar) - -$(LI $(C lastIndexOf): $(C indexOf)'a benzer şekilde çalışır. Farkı, sondan başa doğru aramasıdır -) - -$(LI $(C countChars): Birinci dizgi içinde ikinci dizgiden kaç tane bulunduğunu sayar -) - -$(LI $(C toLower): Verilen dizginin, bütün harfleri küçük olan eşdeğerini döndürür -) - -$(LI $(C toUpper): $(C toLower)'a benzer şekilde çalışır. Farkı, büyük harf kullanmasıdır -) - -$(LI $(C strip): Dizginin başındaki ve sonundaki boşlukları siler -) - -$(LI $(C insert): Dizginin içine başka dizgi yerleştirir -) - -) - -$(P -Dizgiler de aslında dizi olduklarından, diziler için yararlı işlevler içeren $(C std.array), $(C std.algorithm) ve $(C std.range) modüllerindeki işlevler de dizgilerle kullanılabilir. -) - -$(PROBLEM_COK - -$(PROBLEM -$(LINK2 http://dlang.org/phobos/std_string.html, std.string modülünün belgesini) inceleyin. -) - -$(PROBLEM -$(C ~) işlecini de kullanan bir program yazın: Kullanıcı bütünüyle küçük harflerden oluşan ad ve soyad girsin; program önce bu iki kelimeyi aralarında boşluk olacak şekilde birleştirsin ve sonra baş harflerini büyütsün. Örneğin "ebru" ve "domates" girildiğinde programın çıktısı "Ebru Domates" olsun. -) - -$(PROBLEM -Kullanıcıdan bir satır alın. Satırın içindeki ilk 'a' harfinden, satırın içindeki son 'a' harfine kadar olan bölümü yazdırsın. Örneğin kullanıcı "balıkadam" dizgisini girdiğinde ekrana "alıkada" yazdırılsın. - -$(P -Bu programda $(C indexOf) ve $(C lastIndexOf) işlevlerini kullanarak iki değişik indeks bulmanız, ve bu indekslerle bir dilim oluşturmanız işe yarayabilir. -) - -$(P -$(C indexOf) ve $(C lastIndexOf) işlevlerinin dönüş türleri $(C int) değil, $(C ptrdiff_t)'dir. İlk 'a' harfini bulmak için şöyle bir satır kullanabilirsiniz: -) - ---- - ptrdiff_t ilk_a = indexOf(satır, 'a'); ---- - -$(P -Bir kaç bölüm sonra göreceğimiz $(C auto) anahtar sözcüğü ile daha da kısa olabilir: -) - ---- - auto ilk_a = indexOf(satır, 'a'); ---- - -) - -) - -Macros: - SUBTITLE=Dizgiler - - DESCRIPTION=D dilinde dizgilerin ve dizgi işlemlerinin tanıtılması - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial dizgi string - -SOZLER= -$(akim) -$(degismez) -$(dilim) -$(dizgi) -$(dizi) -$(hazir_veri) -$(islev) -$(karakter) -$(parametre) -$(phobos) -$(takma_isim) diff --git a/ddili/src/ders/d/diziler.cozum.d b/ddili/src/ders/d/diziler.cozum.d deleted file mode 100644 index 1a4aae1..0000000 --- a/ddili/src/ders/d/diziler.cozum.d +++ /dev/null @@ -1,132 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Diziler) - -$(OL - -$(LI - ---- -import std.stdio; -import std.algorithm; - -void main() { - write("Kaç sayı var? "); - int adet; - readf(" %s", &adet); - - double[] sayılar; - sayılar.length = adet; - - int sayaç; - while (sayaç < adet) { - write("Sayı ", sayaç, ": "); - readf(" %s", &sayılar[sayaç]); - ++sayaç; - } - - writeln("Sıralı olarak:"); - sort(sayılar); - - sayaç = 0; - while (sayaç < adet) { - write(sayılar[sayaç], " "); - ++sayaç; - } - writeln(); - - writeln("Ters sırada:"); - reverse(sayılar); - - sayaç = 0; - while (sayaç < adet) { - write(sayılar[sayaç], " "); - ++sayaç; - } - writeln(); -} ---- - -) - -$(LI Açıklamalar kodun içinde: - ---- -import std.stdio; -import std.algorithm; - -void main() { - // Kaç tane sayı geleceğini baştan bilmediğimiz için - // dinamik diziler kullanıyoruz - int[] tekler; - int[] çiftler; - - writeln("Lütfen tamsayılar girin (sonlandırmak için -1)"); - - while (true) { - - // Sayıyı okuyoruz - int sayı; - readf(" %s", &sayı); - - // Sayı özellikle -1 olduğunda döngüden çıkıyoruz - if (sayı == -1) { - break; - } - - // Tek veya çift olması durumuna göre farklı dizinin - // sonuna yerleştiriyoruz; ikiye bölümünden kalan 0 - // ise çifttir, değilse tektir - if ((sayı % 2) == 0) { - çiftler ~= sayı; - - } else { - tekler ~= sayı; - } - } - - // Önce tekleri ve çiftleri ayrı ayrı sıralıyoruz - sort(tekler); - sort(çiftler); - - // Ondan sonra birleştiriyoruz - int[] sonuç; - sonuç = tekler ~ çiftler; - - writeln("Önce tekler, sonra çiftler; sıralı olarak:"); - - // Daha önce gördüğümüz gibi bir döngü kurarak dizinin - // bütün elemanlarını çıkışa yazdırıyoruz - int i; - while (i < sonuç.length) { - write(sonuç[i], " "); - ++i; - } - - writeln(); -} ---- - -) - -$(LI -Bu programda üç hata var. İki hata $(C while) döngüleriyle ilgili: her ikisinde de $(C <) işleci yerine $(C <=) kullanılmış. O yüzden program yasal olmayan bir indeks kullanarak dizinin dışına taşıyor. - -$(P -Üçüncü hatayı kendiniz uğraşarak gidermeniz önemli olduğundan çözümü hemen vermek istemiyorum. Yukarıdaki iki hatayı giderdikten sonra programı tekrar derleyin ve neden sonucu yazdırmadığını bir sonraki paragrafı okumadan kendiniz çözmeye çalışın. -) - -$(P -$(C i) sayacı hâlâ bir önceki döngüden çıkıldığındaki değerinde olduğundan, ikinci $(C while) döngüsünün koşulu hiçbir zaman sağlanmaz ve ikinci döngü bir kere bile tekrarlanmaz. Çözüm olarak ikinci döngüden önce bir $(C i = 0;) ifadesi yazmanız gerekir. -) - -) - -) - -Macros: - SUBTITLE=Diziler Problem Çözümü - - DESCRIPTION=Diziler Problem Çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial diziler problem çözüm diff --git a/ddili/src/ders/d/diziler.d b/ddili/src/ders/d/diziler.d deleted file mode 100644 index 144920a..0000000 --- a/ddili/src/ders/d/diziler.d +++ /dev/null @@ -1,571 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX dizi) Diziler) - -$(P -Bir önceki bölümün problemlerinden birisinde 5 tane değişken tanımlamış ve onlarla belirli işlemler yapmıştık: önce iki katlarını almıştık, sonra da beşe bölmüştük. O değişkenleri ayrı ayrı şöyle tanımlamıştık: -) - ---- - double sayı_1; - double sayı_2; - double sayı_3; - double sayı_4; - double sayı_5; ---- - -$(P -Bu yöntem her duruma uygun değildir, çünkü değişken sayısı arttığında onları teker teker tanımlamak, içinden çıkılmaz bir hal alır. Bin tane sayıyla işlem yapmak gerektiğini düşünün... Bin tane değişkeni ayrı ayrı sayı_1, sayı_2, ..., sayı_1000 diye tanımlamak hemen hemen olanaksız bir iştir. -) - -$(P -Dizilerin bir yararı böyle durumlarda ortaya çıkar: diziler bir seferde birden fazla değişken tanımlamaya yarayan olanaklardır. Birden fazla değişkeni bir araya getirmek için en çok kullanılan veri yapısı da dizidir. -) - -$(P -Bu bölüm dizi olanaklarının yalnızca bir bölümünü kapsar. Diğer olanaklarını daha ilerideki bir bölümde göreceğiz. -) - -$(H5 Tanımlanması) - -$(P -Dizi tanımı değişken tanımına çok benzer. Tek farkı, dizide kaç değişken bulunacağının, yani bir seferde kaç değişken tanımlanmakta olduğunun, türün isminden sonraki köşeli parantezler içinde belirtilmesidir. Tek bir değişkenin tanımlanması ile bir dizinin tanımlanmasını şöyle karşılaştırabiliriz: -) - ---- - int tekDeğişken; - int[10] onDeğişkenliDizi; ---- - -$(P -O iki tanımdan birincisi, şimdiye kadarki kodlarda gördüklerimiz gibi tek değişken tanımıdır; ikincisi ise 10 değişkenden oluşan bir dizidir. -) - -$(P -Yukarıda sözü geçen problemdeki 5 ayrı değişkeni 5 elemanlı bir dizi halinde hep birden tanımlamak için şu söz dizimi kullanılır: -) - ---- - double[5] sayılar; ---- - -$(P -Bu tanım, "double türünde 5 tane sayı" diye okunabilir. Daha sonra kod içinde kullanıldığında tek bir sayı değişkeni sanılmasın diye ismini de çoğul olarak seçtiğime dikkat edin. -) - -$(P -Özetle; dizi tanımı, tür isminin yanına köşeli parantezler içinde yazılan dizi uzunluğundan ve bunları izleyen dizi isminden oluşur: -) - ---- - $(I tür_ismi)[$(I dizi_uzunluğu)] $(I dizi_ismi); ---- - -$(P -Tür ismi olarak temel türler kullanılabileceği gibi, programcının tanımladığı daha karmaşık türler de kullanılabilir (bunları daha sonra göreceğiz). Örnekler: -) - ---- - // Bütün şehirlerdeki hava durumlarını tutan bir dizi - // Burada örneğin - // false: "kapalı hava" - // true : "açık hava" - // anlamında kullanılabilir - bool[şehirAdedi] havaDurumları; - - // Yüz kutunun ağırlıklarını ayrı ayrı tutan bir dizi - double[100] kutuAğırlıkları; - - // Bir okuldaki bütün öğrencilerin kayıtları - ÖğrenciBilgisi[öğrenciAdedi] öğrenciKayıtları; ---- - -$(H5 $(IX topluluk) $(IX eleman) Topluluklar ve elemanlar) - -$(P -Aynı türden değişkenleri bir araya getiren veri yapılarına $(I topluluk) adı verilir. Bu tanıma uydukları için diziler de toplulukturlar. Örneğin Temmuz ayındaki günlük hava sıcaklıklarını tutmak için kullanılacak bir dizi 31 tane $(C double) değişkenini bir araya getirebilir ve $(I $(C double) türünde elemanlardan oluşan bir topluluk) oluşturur. -) - -$(P -Topluluk değişkenlerinin her birisine $(I eleman) denir. Dizilerin barındırdıkları eleman adedine dizilerin $(I uzunluğu) denir. "Eleman adedi" ve "dizi uzunluğu" ifadelerinin ikisi de sık kullanılır. -) - -$(H5 $(IX []) Eleman erişimi) - -$(P -Problemdeki değişkenleri ayırt etmek için isimlerinin sonuna bir alt çizgi karakteri ve bir sıra numarası eklemiştik: $(C sayı_1) gibi... Sayıları hep birden bir dizi halinde ve $(C sayılar) isminde tanımlayınca elemanlara farklı isimler verme şansımız kalmaz. Onun yerine, elemanlara dizinin erişim işleci olan $(C []) ile ve bir sıra numarasıyla erişilir: -) - ---- - sayılar[0] ---- - -$(P -O yazım, "sayıların 0 numaralı elemanı" diye okunabilir. Bu şekilde yazınca $(C sayı_1) ifadesinin yerini $(C sayılar[0]) ifadesi almış olur. -) - -$(P -Burada dikkat edilmesi gereken iki nokta vardır: -) - -$(UL - -$(LI $(B Numara sıfırdan başlar:) Biz insanlar nesneleri 1'den başlayacak şekilde numaralamaya alışık olduğumuz halde, dizilerde numaralar 0'dan başlar. Bizim 1, 2, 3, 4, ve 5 olarak numaraladığımız sayılar dizi içinde 0, 1, 2, 3, ve 4 olarak numaralanırlar. Programcılığa yeni başlayanların bu farklılığa dikkat etmeleri gerekir. -) - -$(LI $(B $(C[]) karakterlerinin iki farklı kullanımı:) Dizi tanımlarken kullanılan $(C []) karakterleri ile erişim işleci olarak kullanılan $(C []) karakterlerini karıştırmayın. Dizi tanımlarken kullanılan $(C []) karakterleri elemanların türünden sonra yazılır ve dizide kaç eleman bulunduğunu belirler; erişim için kullanılan $(C []) karakterleri ise dizinin isminden sonra yazılır ve elemanın sıra numarasını belirler: - ---- - // Bu bir tanımdır. 12 tane int'ten oluşmaktadır ve her - // ayda kaç gün bulunduğu bilgisini tutmaktadır - int[12] ayGünleri; - - // Bu bir erişimdir. Aralık ayına karşılık gelen elemana - // erişir ve değerini 31 olarak belirler - ayGünleri[11] = 31; - - // Bu da bir erişimdir. Ocak ayındaki gün sayısını - // writeln'a göndermek için kullanılmaktadır. - writeln("Ocak'ta ", ayGünleri[0], " gün var."); ---- - -$(P -$(B Hatırlatma:) Ocak ayının sıra numarasının 0, Aralık ayının sıra numarasının 11 olduğuna dikkat edin. -) - -) - -) - -$(H5 $(IX indeks) İndeks) - -$(P -Elemanlara erişirken kullanılan sıra numaralarına $(I indeks), elemanlara erişme işine de $(I indeksleme) denir. -) - -$(P -İndeks sabit bir değer olmak zorunda değildir; indeks olarak değişken değerleri de kullanılabilir. Bu olanak dizilerin kullanışlılığını büyük ölçüde arttırır. Örneğin aşağıdaki kodda hangi aydaki gün sayısının yazdırılacağını $(C ayNumarası) değişkeni belirlemektedir: -) - ---- - writeln("Bu ay ", ayGünleri[ayNumarası], " gün çeker"); ---- - -$(P -$(C ayNumarası)'nın 2 olduğu bir durumda yukarıdaki ifadede $(C ayGünleri[2])'nin değeri, yani Mart ayındaki gün adedi yazdırılır. $(C ayNumarası)'nın başka bir değerinde de o aydaki gün sayısı yazdırılır. -) - -$(P -Yasal olan indeksler, 0'dan dizinin uzunluğundan bir eksiğine kadar olan değerlerdir. Örneğin 3 elemanlı bir dizide yalnızca 0, 1, ve 2 indeksleri yasaldır. Bunun dışında indeks kullanıldığında program bir hata ile sonlanır. -) - -$(P -Dizileri, elemanları yan yana duran bir topluluk olarak düşünebilirsiniz. Örneğin ayların günlerini tutan bir dizinin elemanları ve indeksleri şu şekilde gösterilebilir (Şubat'ın 28 gün çektiğini varsayarak): -) - -$(MONO - indeksler → 0 1 2 3 4 5 6 7 8 9 10 11 - elemanlar → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | -) - -$(P -$(I Not: Yukarıdaki indeksleri yalnızca gösterim amacıyla kullandım; indeksler belleğe yazılmazlar.) -) - -$(P -İlk elemanın indeksi 0, ve Ocak ayındaki gün sayısı olan 31 değerine sahip; ikinci elemanın indeksi 1, ve Şubat ayındaki gün sayısı olan 28 değerine sahip; vs. -) - -$(H5 $(IX sabit uzunluklu dizi) $(IX dinamik dizi) $(IX statik dizi) Sabit uzunluklu diziler ve dinamik diziler) - -$(P -Kaç eleman barındıracakları programın yazıldığı sırada bilinen dizilere $(I sabit uzunluklu dizi); elemanlarının sayısı programın çalışması sırasında değişebilen dizilere $(I dinamik dizi) denir. -) - -$(P -Yukarıda 5 sayı tanımlamak için kullandığımız $(C sayılar) dizisi ve 12 aydaki gün sayılarını tutmak için kullandığımız $(C ayGünleri) dizileri sabit uzunluklu dizilerdir; çünkü eleman sayıları baştan belirlenmiştir. O dizilerin uzunlukları programın çalışması sırasında değiştirilemez. Uzunluklarının değişmesi gerekse, bu ancak kaynak koddaki sabit olan değerin elle değiştirilmesi ve programın tekrar derlenmesi ile mümkündür. -) - -$(P -Dinamik dizi tanımlamak sabit uzunluklu dizi tanımlamaktan daha kolaydır; dizinin uzunluğunu boş bırakmak diziyi dinamik yapmaya yeter: -) - ---- - int[] dinamikDizi; ---- - -$(P -Böyle dizilerin uzunlukları programın çalışması sırasında gerektikçe arttırılabilir veya azaltılabilir. -) - -$(H5 $(IX .length) Eleman adedini edinmek ve değiştirmek için $(C .length)) - -$(P -Türlerin olduğu gibi dizilerin de nitelikleri vardır. Burada yalnızca bir tanesini göreceğiz. $(C .length) dizideki eleman adedini bildirir: -) - ---- - writeln("Dizide ", dizi.length, " tane eleman var"); ---- - -$(P -Ek olarak, $(C .length) dinamik dizilerde dizinin uzunluğunu değiştirmeye de yarar: -) - ---- - int[] dizi; // boştur - dizi.length = 5; // uzunluğu 5 olur ---- - -$(H5 Bir dizi örneği) - -$(P -Bu bilgiler ışığında 5 değişkenli probleme dönelim ve onu dizi kullanacak şekilde tekrar yazalım: -) - ---- -import std.stdio; - -void main() { - // Bu değişkeni döngüleri kaç kere tekrarladığımızı saymak - // için kullanacağız - int sayaç; - - // double türündeki beş elemandan oluşan sabit uzunluklu - // bir dizi tanımlıyoruz - double[5] sayılar; - - // Sayıları bir döngü içinde girişten alıyoruz - while (sayaç < sayılar.length) { - write("Sayı ", sayaç + 1, ": "); - readf(" %s", &sayılar[sayaç]); - ++sayaç; - } - - writeln("İki katları:"); - sayaç = 0; - while (sayaç < sayılar.length) { - writeln(sayılar[sayaç] * 2); - ++sayaç; - } - - // Beşte birlerini hesaplayan döngü de bir önceki - // döngünün benzeridir... -} ---- - -$(P $(B Gözlemler:) Döngülerin kaç kere tekrarlanacaklarını $(C sayaç) belirliyor: döngüleri, o değişkenin değeri $(C sayılar.length)'ten küçük olduğu sürece tekrarlıyoruz. Sayacın değeri her tekrarda bir arttıkça, $(C sayılar[sayaç]) ifadesi de sırayla dizinin elemanlarını göstermiş oluyor: $(C sayılar[0]), $(C sayılar[1]), vs. -) - -$(P -Bu programın yararını görmek için girişten 5 yerine örneğin 20 sayı alınacağını düşünün... Dizi kullanan bu programda tek bir yerde küçük bir değişiklik yapmak yeter: 5 değerini 20 olarak değiştirmek... Oysa dizi kullanmayan programda 15 tane daha değişken tanımlamak ve kullanıldıkları kod satırlarını 15 değişken için tekrarlamak gerekirdi. -) - -$(H5 $(IX ilkleme, dizi) Elemanları ilklemek) - -$(P -D'de her türde olduğu gibi dizi elemanları da otomatik olarak ilklenirler. Elemanlar için kullanılan ilk değer, elemanların türüne bağlıdır: $(C int) için 0, $(C double) için $(C double.nan), vs. -) - -$(P -Yukarıdaki programdaki $(C sayılar) dizisinin beş elemanı da dizi tanımlandığı zaman $(C double.nan) değerine sahiptir: -) - ---- - double[5] sayılar; // dizinin bütün elemanlarının - // ilk değeri double.nan olur ---- - -$(P -Elemanların bu ilk değerleri dizi kullanıldıkça değişebilir. Bunun örneklerini yukarıdaki programlarda gördük. Örneğin $(C ayGünleri) dizisinin 11 indeksli elemanına 31 değerini atadık: -) - ---- - ayGünleri[11] = 31; ---- - -$(P -Daha sonra da girişten gelen değeri, $(C sayılar) isimli dizinin $(C sayaç) indeksli elemanının değeri olarak okuduk: -) - ---- - readf(" %s", &sayılar[sayaç]); ---- - -$(P -Bazen elemanların değerleri, dizi kurulduğu anda bilinir. Öyle durumlarda dizi, $(I atama) söz dizimiyle ve elemanların ilk değerleri sağ tarafta belirtilerek tanımlanır. Kullanıcıdan ay numarasını alan ve o ayın kaç gün çektiğini yazan bir program düşünelim: -) - ---- -import std.stdio; - -void main() { - // Şubat'ın 28 gün çektiğini varsayıyoruz - int[12] ayGünleri = - [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; - - write("Kaçıncı ay? "); - int ayNumarası; - readf(" %s", &ayNumarası); - - int indeks = ayNumarası - 1; - writeln(ayNumarası, ". ay ", - ayGünleri[indeks], " gün çeker"); -} ---- - -$(P -O programda $(C ayGünleri) dizisinin elemanlarının dizinin tanımlandığı anda ilklendiklerini görüyorsunuz. Ayrıca, kullanıcıdan alınan ve değeri 1-12 aralığında olan ay numarasının indekse nasıl dönüştürüldüğüne dikkat edin. Böylece kullanıcının 1-12 aralığında verdiği numara, programda 0-11 aralığına dönüştürülmüş olur. Kullanıcı 1-12 aralığının dışında bir değer girdiğinde, program dizinin dışına erişildiğini bildiren bir hata ile sonlanır. -) - -$(P -Dizileri ilklerken sağ tarafta tek bir eleman değeri de kullanılabilir. Bu durumda dizinin bütün elemanları o değeri alır: -) - ---- - int[10] hepsiBir = 1; // Bütün elemanları 1 olur ---- - -$(H5 Temel dizi işlemleri) - -$(P -Diziler, bütün elemanlarını ilgilendiren bazı işlemlerde büyük kolaylık sağlarlar. -) - -$(H6 $(IX kopyalama, dizi) Sabit uzunluklu dizileri kopyalama) - -$(P -Atama işleci, sağdaki dizinin elemanlarının hepsini birden soldaki diziye kopyalar: -) ---- - int[5] kaynak = [ 10, 20, 30, 40, 50 ]; - int[5] hedef; - - hedef = kaynak; ---- - -$(P -$(I Not: Atama işleminin anlamı dinamik dizilerde çok farklıdır; bunu ilerideki bir bölümde göreceğiz.) -) - -$(H6 Dinamik dizilere eleman ekleme) - -$(P -$(IX ~=) $(IX ekleme, dizi) $(IX eleman ekleme, dizi) $(C ~=) işleci, dinamik dizinin sonuna yeni bir eleman veya yeni bir dizi ekler: -) - ---- - int[] dizi; // dizi boştur - dizi ~= 7; // dizide tek eleman vardır - dizi ~= 360; // dizide iki eleman olur - dizi ~= [ 30, 40 ]; // dizide dört eleman olur ---- - -$(P -Sabit uzunluklu dizilere eleman eklenemez: -) - ---- - int[$(HILITE 10)] dizi; - dizi ~= 7; $(DERLEME_HATASI) ---- - -$(H6 Birleştirme) - -$(P -$(IX ~, birleştirme) $(IX birleştirme, dizi) $(C ~) işleci iki diziyi uç uca birleştirerek yeni bir dizi oluşturur. Aynı işlecin atamalı olanı da vardır ($(C ~=)) ve sağdaki diziyi soldaki dizinin sonuna ekler: -) - ---- -import std.stdio; - -void main() { - int[10] birinci = 1; - int[10] ikinci = 2; - int[] sonuç; - - sonuç = birinci ~ ikinci; - writeln(sonuç.length); // 20 yazar - - sonuç ~= birinci; - writeln(sonuç.length); // 30 yazar -} ---- - -$(P -Eğer sol tarafta sabit uzunluklu bir dizi varsa, dizinin uzunluğu değiştirilemeyeceği için $(C ~=) işleci kullanılamaz: -) - ---- - int[20] sonuç; - // ... - sonuç $(HILITE ~=) birinci; $(DERLEME_HATASI) ---- - -$(P -Atama işleminde de, sağ tarafın uzunluğu sol tarafa uymazsa program çöker: -) - ---- - int[10] birinci = 1; - int[10] ikinci = 2; - int[$(HILITE 21)] sonuç; - - sonuç = birinci ~ ikinci; ---- - -$(P -O kod, programın "dizi kopyası sırasında uzunluklar aynı değil" anlamına gelen hata ile çökmesine neden olur: -) - -$(SHELL -object.Error@(0): Array lengths don't match for copy: $(HILITE 20 != 21) -) - -$(H6 $(IX sort) Elemanları sıralama) - -$(P -$(C std.algorithm.sort) işlevi elemanları küçükten büyüğe doğru sıralar. $(C sort())'tan yararlanabilmek için $(C std.algorithm) modülünün eklenmesi gerekir. (İşlevleri daha sonraki bir bölümde göreceğiz.) -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - int[] dizi = [ 4, 3, 1, 5, 2 ]; - $(HILITE sort)(dizi); - writeln(dizi); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[1, 2, 3, 4, 5] -) - -$(H6 $(IX reverse) Elemanları ters çevirmek) - -$(P -$(C std.algorithm.reverse) elemanların yerlerini aynı dizi içinde ters çevirir; ilk eleman sonuncu eleman olur, vs.: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - int[] dizi = [ 4, 3, 1, 5, 2 ]; - $(HILITE reverse)(dizi); - writeln(dizi); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[2, 5, 1, 3, 4] -) - -$(PROBLEM_COK - -$(PROBLEM -Yazacağınız program önce kullanıcıdan kaç tane sayı girileceğini öğrensin ve girişten o kadar kesirli sayı alsın. Daha sonra bu sayıları önce küçükten büyüğe, sonra da büyükten küçüğe doğru sıralasın. - -$(P -Burada $(C sort) ve $(C reverse) işlevlerini kullanabilirsiniz. -) - -) - -$(PROBLEM -Başka bir program yazın: girişten aldığı sayıların önce tek olanlarını sırayla, sonra da çift olanlarını sırayla yazdırsın. Özel olarak -1 değeri girişi sonlandırmak için kullanılsın: bu değer geldiğinde artık girişten yeni sayı alınmasın. - -$(P -Örneğin girişten -) - -$(SHELL -1 4 7 2 3 8 11 -1 -) - -$(P -geldiğinde çıkışa şunları yazdırsın: -) - -$(SHELL -1 3 7 11 2 4 8 -) - -$(P -$(B İpucu:) Sayıları iki ayrı diziye yerleştirmek işinize yarayabilir. Girilen sayıların tek veya çift olduklarını da aritmetik işlemler sayfasında öğrendiğiniz $(C %) (kalan) işlecinin sonucuna bakarak anlayabilirsiniz. -) - -) - -$(PROBLEM -Bir arkadaşınız yazdığı bir programın doğru çalışmadığını söylüyor. - -$(P -Girişten beş tane sayı alan, bu sayıların karelerini bir diziye yerleştiren, ve sonunda da dizinin elemanlarını çıkışa yazdıran bir program yazmaya çalışmış ama programı doğru çalışmıyor. -) - -$(P -Bu programın hatalarını giderin ve beklendiği gibi çalışmasını sağlayın: -) - ---- -import std.stdio; - -void main() { - int[5] kareler; - - writeln("5 tane sayı giriniz"); - - int i = 0; - while (i <= 5) { - int sayı; - write(i + 1, ". sayı: "); - readf(" %s", &sayı); - - kareler[i] = sayı * sayı; - ++i; - } - - writeln("=== Sayıların Kareleri ==="); - while (i <= kareler.length) { - write(kareler[i], " "); - ++i; - } - - writeln(); -} ---- - -) - -) - -Macros: - SUBTITLE=Diziler - - DESCRIPTION=D dilinde dizilerin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial diziler - -SOZLER= -$(atama) -$(cokme) -$(degisken) -$(dinamik) -$(dizi) -$(eleman) -$(indeks) -$(kalan) -$(tanim) -$(topluluk) diff --git a/ddili/src/ders/d/do_while.cozum.d b/ddili/src/ders/d/do_while.cozum.d deleted file mode 100644 index 8121754..0000000 --- a/ddili/src/ders/d/do_while.cozum.d +++ /dev/null @@ -1,23 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C do-while) Döngüsü) - -$(P -Bu programın $(C do-while) ile özellikle bir ilgisi yok; ama $(C while) yerine kesinlikle $(C do-while) ile yapılması gereken bir örnek de bulunamaz. -) - -$(P -Program, tuttuğunuz sayıyı üstten ve alttan kıstırarak bulur. Örneğin ilk tahmini 50 olsa, ve siz "çık" diye yanıt verseniz; artık sayının [51,100] aralığında olduğunu öğrenir. Ondan sonra bu iki sayının tam ortasında bir değer tutsa, ve "in" deseniz; bu sefer de örneğin [51,75] aralığında olduğunu bilir. -) - -$(P -O şekilde kıstıra kıstıra tek bir sayı kaldığında tuttuğunuz sayıyı da bulmuş olur. -) - - -Macros: - SUBTITLE=do-while Döngüsü Problem Çözümü - - DESCRIPTION=do-while döngüsü bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial do-while döngü problem çözüm diff --git a/ddili/src/ders/d/do_while.d b/ddili/src/ders/d/do_while.d deleted file mode 100644 index aaa931f..0000000 --- a/ddili/src/ders/d/do_while.d +++ /dev/null @@ -1,94 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX do-while) $(IX döngü, do-while) $(CH4 do-while) Döngüsü) - -$(P -$(LINK2 /ders/d/for_dongusu.html, $(C for) döngüsü) bölümünde $(LINK2 /ders/d/while_dongusu.html, $(C while))'ın işleyiş adımlarını da görmüştük: -) - -$(MONO -hazırlık - -koşul denetimi -asıl işlemler -ilerletilmesi - -koşul denetimi -asıl işlemler -ilerletilmesi - -... -) - -$(P -$(C do-while)'ın $(C while)'dan farkı, koşul denetiminin sonda olması ve bu sayede işlemlerin en az bir kere işletilmeleridir: -) - -$(MONO -hazırlık (while'dan daha az durumda gerekir) - -asıl işlemler -ilerletilmesi -koşul denetimi $(SHELL_NOTE koşul denetimi sonda) - -asıl işlemler -ilerletilmesi -koşul denetimi $(SHELL_NOTE koşul denetimi sonda) - -... -) - -$(P -Örneğin, tuttuğu sayının tahmin edilmesini bekleyen bir programda $(C do-while) döngüsü daha doğal gelebilir: -) - ---- -import std.stdio; -import std.random; - -void main() { - int sayı = uniform(1, 101); - - writeln("1'den 100'e kadar bir sayı tuttum."); - - int tahmin; - - do { - write("Tahmininiz nedir? "); - - readf(" %s", &tahmin); - - if (sayı < tahmin) { - write("tuttuğum sayı daha küçük; "); - - } else if (sayı > tahmin) { - write("tuttuğum sayı daha büyük; "); - } - - } while (tahmin != sayı); - - writeln("Doğru!"); -} ---- - -$(P -$(C uniform), $(C std.random) modülünde bulunan bir işlevdir. Belirtilen aralıkta eşit dağılımlı rasgele sayılar üretir. Yukarıdaki kullanımında; aralığı belirleyen ikinci değer, çıkacak sayılar arasında değildir. Diğer kullanımlarını öğrenmek için $(LINK2 http://dlang.org/phobos/std_random.html, std.random modülünün belgesine) bakabilirsiniz. -) - -$(PROBLEM_TEK - -$(P -Aynı oyunu bilgisayara oynatın; tuttuğunuz sayıyı en fazla 7 tahminde bulacaktır. -) - -) - -Macros: - SUBTITLE=do-while Döngüsü - - DESCRIPTION=do-while döngüsünün tanıtılması ve while döngüsüne benzerliğinin gösterilmesi - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial do-while while - -SOZLER= -$(dongu) diff --git a/ddili/src/ders/d/dosyalar.cozum.d b/ddili/src/ders/d/dosyalar.cozum.d deleted file mode 100644 index bf358b7..0000000 --- a/ddili/src/ders/d/dosyalar.cozum.d +++ /dev/null @@ -1,35 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Dosyalar) - ---- -import std.stdio; -import std.string; - -void main() { - write("Lütfen dosya ismini yazınız: "); - string girişDosyasıİsmi = strip(readln()); - File giriş = File(girişDosyasıİsmi, "r"); - - string çıkışDosyasıİsmi = girişDosyasıİsmi ~ ".bak"; - File çıkış = File(çıkışDosyasıİsmi, "w"); - - while (!giriş.eof()) { - string satır = strip(giriş.readln()); - - if (satır.length != 0) { - çıkış.writeln(satır); - } - } - - writeln(çıkışDosyasıİsmi, " dosyasını oluşturdum."); -} ---- - - -Macros: - SUBTITLE=Dosyalar Problem Çözümü - - DESCRIPTION=Dosyalar bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial dosya problem çözüm diff --git a/ddili/src/ders/d/dosyalar.d b/ddili/src/ders/d/dosyalar.d deleted file mode 100644 index 3daa3b2..0000000 --- a/ddili/src/ders/d/dosyalar.d +++ /dev/null @@ -1,240 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX dosya) Dosyalar) - -$(P -Ne kadar güçlü olsalar da, önceki bölümde uç birimlerde kullanıldıklarını gördüğümüz $(C >), $(C <), ve $(C |) karakterleri her duruma uygun değildir. Çünkü her program işini yalnızca standart giriş ve çıkışla etkileşerek yapamaz. -) - -$(P -Örneğin öğrenci kayıtları ile ilgilenen bir program, standart çıkışını kullanıcıya bir komut menüsü göstermek için kullanıyor olabilir. Standart girişini de kullanıcıdan komut almak için kullandığını düşünürsek, böyle bir programın kayıtlarını tuttuğu öğrenci bilgilerini yazmak için en az bir dosyaya ihtiyacı olacaktır. -) - -$(P -Bu bölümde dosya sisteminin klasörlerde barındırdığı dosyalara yazmayı ve dosyalardan okumayı öğreneceğiz. -) - -$(H5 Temel kavramlar) - -$(P -Dosya işlemleri için $(C std.stdio) modülünde tanımlanmış olan $(C File) $(I yapısı) kullanılır. Henüz yapıları göstermediğim için $(C File) nesnelerinin $(I kurulma) söz diziminin ayrıntısına girmeyeceğim ve şimdilik bir kalıp olarak kabul etmenizi bekleyeceğim. -) - -$(P -Kullanımlarına geçmeden önce dosyalarla ilgili temel kavramların açıklanması gerekir. -) - -$(H6 Karşı taraf) - -$(P -Bu bölümdeki bilgilerle oluşturulan dosyaların başka ortamlarda hemen okunabileceklerini düşünmeyin. Dosyayı oluşturan taraf ile dosyayı kullanan tarafın en azından dosya düzeni konusundan anlaşmış olmaları gerekir. Örneğin öğrenci numarasının ve isminin dosyaya yazıldıkları sırada okunmaları gerekir. -) - -$(P -Bir dosya oluşturup içine bilgiler yazmak, o dosyanın başka bir ortamda açılıp okunması için yeterli olmayabilir. Dosyayı yazan tarafla okuyan tarafın belirli konularda anlaşmış olmaları gerekir. Örneğin dosyaya $(C char[]) olarak yazılmış olan bir bilginin $(C wchar[]) olarak okunması yanlış sonuç doğurur. -) - -$(P -Ek olarak, aşağıdaki kodlar dosyaların başına BOM belirtecini yazmazlar. Bu, dosyalarınızın BOM belirteci gerektiren ortamlarda doğru olarak açılamamasına neden olabilir. ("Byte order mark"ın kısası olan BOM, karakterleri oluşturan UTF kod birimlerinin dosyaya hangi sırada yazılmış olduklarını belirtir.) -) - -$(H6 Dosya erişim hakları) - -$(P -Dosya sistemi dosyaları programlara çeşitli erişim haklarıyla sunar. Erişim hakları hem performans hem de dosya sağlığı açısından önemlidir. -) - -$(P -Konu $(I dosyadan okumak) olunca; aynı dosyadan okumak isteyen birden fazla programa aynı anda okuma izni verilmesi, programlar birbirlerini beklemeyecekleri için hız kazancı sağlar. Öte yandan, konu $(I dosyaya yazmak) olunca; dosyanın içeriğinin tutarlılığı açısından dosyaya belirli bir anda ancak tek bir programın yazmasına izin verilmelidir; yoksa iki programın birbirlerinden habersiz olarak yazmaları sonucunda dosyanın içeriği tutarsız hale gelebilir. -) - -$(H6 Dosya açmak) - -$(P -Programın standart giriş ve çıkış akımları olan $(C stdin) ve $(C stdout), program başladığında zaten $(I açılmış) ve kullanıma hazır olarak gelirler; onları kullanmaya başlamadan önce özel bir işlem gerekmez. -) - -$(P -Dosyaların ise diskteki isimleri ve istenen erişim hakları bildirilerek program tarafından açılmaları gerekir. Aşağıdaki örneklerde de göreceğimiz gibi, oluşturulan bir $(C File) nesnesi, belirtilen isimdeki dosyanın açılması için yeterlidir: -) - ---- - File dosya = File("ogrenci_bilgisi", "r"); ---- - -$(H6 Dosya kapatmak) - -$(P -Açılan dosyaların mutlaka kapatılmaları da gerekir. Ancak, $(C File) nesneleri kendileri sonlanırken erişim sağlamakta oldukları asıl dosyaları da kapattıkları için, normalde bu işin programda açıkça yapılması gerekmez. Dosya, $(C File) nesnesinin içinde bulunduğu kapsamdan çıkılırken kendiliğinden kapatılır: -) - ---- -if (bir_koşul) { - - // File nesnesi burada oluşturulmuş ve kullanılmış olsun - // ... - -} // ← Dosya bu kapsamdan çıkılırken otomatik olarak - // kapatılır. Açıkça kapatmaya gerek yoktur. ---- - -$(P -Bazen aynı $(C File) nesnesinin başka dosyayı veya aynı dosyayı farklı erişim haklarıyla kullanması istenir. Böyle durumlarda dosyanın kapatılıp tekrar açılması gerekir: -) - ---- - dosya.close(); // dosyayı kapatır - dosya.open("ogrenci_bilgisi", "r"); // dosyayı açar ---- - -$(H6 Dosyaya yazmak ve dosyadan okumak) - -$(P -Dosyalar da karakter akımları olduklarından, $(C writeln) ve $(C readf) gibi işlevler onlarla da kullanılabilir. Farklı olan, dosya değişkeninin isminin ve bir noktanın da yazılmasının gerekmesidir: -) - ---- - writeln("merhaba"); // standart çıkışa yazar - stdout.writeln("merhaba"); // yukarıdakinin uzun yazımıdır - $(HILITE dosya.)writeln("merhaba"); // dosyaya yazar ---- - -$(H6 $(IX eof) Dosya sonu için $(C eof())) - -$(P -Bir dosyadan okurken dosyanın sonuna gelinip gelinmediği, "dosya sonu" anlamına gelen "end of file"ın kısaltması olan $(C eof()) üye işleviyle denetlenir. Bu işlev dosya sonuna gelindiğinde $(C true) döndürür. -) - -$(P -Örneğin, aşağıdaki döngü dosyanın sonuna gelene kadar devam eder: -) - ---- - while (!dosya.eof()) { - // ... - } ---- - -$(H6 $(IX std.file) Klasör işlemleri için $(C std.file) modülü) - -$(P -Klasör işlemleri ile ilgili olan $(C std.file) modülünün $(LINK2 http://dlang.org/phobos/std_file.html, belgesinde) işinize yarayacak işlevler bulabilirsiniz. Örneğin, $(C exists) belirtilen isimdeki dosyanın mevcut olup olmadığını bildirir: -) - ---- - if (exists(dosya_ismi)) { - // dosya mevcut - - } else { - // dosya mevcut değil - } ---- - -$(H5 $(IX File) $(C std.stdio.File) yapısı) - -$(P -$(IX erişim belirteci, dosya) $(C File), C dilindeki standart $(C fopen) işlevinin kullandığı erişim belirteçlerini kullanır: -) - - - - - - - - - - - - - - - -
     Belirteç  Anlamı
    r$(B okuma) erişimi$(BR)dosya başından okunacak şekilde hazırlanır
    r+$(B okuma ve yazma) erişimi$(BR)dosya başından okunacak ve başına yazılacak şekilde hazırlanır
    w$(B yazma) erişimi$(BR)dosya yoksa: boş olarak oluşturulur$(BR)dosya zaten varsa: içi boşaltılır
    w+$(B okuma ve yazma) erişimi$(BR)dosya yoksa: boş olarak oluşturulur$(BR)dosya zaten varsa: içi boşaltılır
    a$(B sonuna yazma) erişimi$(BR)dosya yoksa: boş olarak oluşturulur$(BR)dosya zaten varsa: içeriği korunur ve sonuna yazılacak şekilde hazırlanır
    a+$(B okuma ve sonuna yazma) erişimi$(BR)dosya yoksa: boş olarak oluşturulur$(BR)dosya zaten varsa: içeriği korunur; başından okunacak ve sonuna yazılacak şekilde hazırlanır
    - -$(P -Yukarıdaki erişim haklarının sonuna 'b' karakteri de gelebilir ("rb" gibi). O karakter $(I binary mode) açma durumunu destekleyen platformlarda etkili olabilir ama POSIX ortamlarında gözardı edilir. -) - -$(H6 Dosyaya yazmak) - -$(P -Dosyanın önce yazma erişimi ile açılmış olması gerekir: -) - ---- -import std.stdio; - -void main() { - File dosya = File("ogrenci_bilgisi", $(HILITE "w")); - - dosya.writeln("İsim : ", "Zafer"); - dosya.writeln("Numara: ", 123); - dosya.writeln("Sınıf : ", "1A"); -} ---- - -$(P -$(LINK2 /ders/d/dizgiler.html, Dizgiler bölümünden) hatırlayacağınız gibi, "ogrenci_bilgisi" gibi bir dizginin türü $(C string)'dir ve $(I değişmezdir). Yani $(C File), dosya ismini ve erişim hakkını $(C string) türü olarak kabul eder. Bu yüzden, yine o bölümden hatırlayacağınız gibi, $(C File) örneğin $(C char[]) türünde bir dizgi ile kurulamaz; kurmak gerektiğinde o dizginin $(C .idup) niteliğinin çağrılması gerekir. -) - -$(P -Yukarıdaki program, çalıştırıldığı klasör içinde ismi $(C ogrenci_bilgisi) olan bir dosya oluşturur veya var olan dosyanın üzerine yazar. -) - -$(P -$(I Not: Dosya ismi olarak dosya sisteminin izin verdiği her karakteri kullanabilirsiniz. Ben bu kitapta dosya isimlerinde yalnızca ASCII harfler kullanacağım.) -) - -$(H6 Dosyadan okumak) - -$(P -Dosyanın önce okuma erişimi ile açılmış olması gerekir: -) - ---- -import std.stdio; -import std.string; - -void main() { - File dosya = File("ogrenci_bilgisi", $(HILITE "r")); - - while (!dosya.eof()) { - string satır = strip(dosya.readln()); - writeln("Okuduğum satır -> |", satır); - } -} ---- - -$(P -Yukarıdaki program, çalıştırıldığı klasör içindeki $(C ogrenci_bilgisi) isimli dosyanın içindeki satırları başından sonuna kadar okur ve standart çıkışa yazdırır. -) - -$(PROBLEM_TEK - -$(P -Yazacağınız program kullanıcıdan bir dosya ismi alsın, o dosyanın içindeki $(I boş olmayan) bütün satırları, dosyanın ismine $(C .bak) eklenmiş başka bir dosyaya yazsın. Örneğin, verilen dosyanın ismi $(C deneme.txt) ise, boş olmayan satırlarını $(C deneme.txt.bak) dosyasına yazsın. -) - -) - -Macros: - SUBTITLE=Dosyalar - - DESCRIPTION=D dilinde dosyaların ve dosya işlemlerinin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial dosya - -SOZLER= -$(akim) -$(bom) -$(degismez) -$(kapsam) -$(klasor) -$(kurma) -$(nesne) -$(standart_cikis) -$(standart_giris) -$(uc_birim) -$(uye_islev) -$(yapi) diff --git a/ddili/src/ders/d/enum.cozum.d b/ddili/src/ders/d/enum.cozum.d deleted file mode 100644 index be6577c..0000000 --- a/ddili/src/ders/d/enum.cozum.d +++ /dev/null @@ -1,97 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C enum)) - -$(P -Açıklamalar kodun içerisinde: -) - ---- -import std.stdio; -import std.conv; - -enum İşlem { çıkış, toplama, çıkarma, çarpma, bölme } - -void main() { - // Programın desteklediği işlemleri yazdırıyoruz - write("İşlemler - "); - for (İşlem işlem; işlem <= İşlem.max; ++işlem) { - writef("%d:%s ", işlem, işlem); - } - writeln(); - - // Kullanıcı isteyene kadar programda kalmak için sonsuz - // döngü kullanıyoruz. - while (true) { - write("İşlem? "); - - // Girişten yine de enum'un asıl türü olan int olarak - // okumak zorundayız - int işlemKodu; - readf(" %s", &işlemKodu); - - /* Bu noktadan sonra sihirli sabitler yerine enum - * değerler kullanacağız. - * - * Girişten int olarak okuduğumuz için bu int değerin - * türünü İşlem'e dönüştürüyoruz - * - * (Tür dönüşümlerini ayrıntılı olarak daha sonraki - * bir bölümde göreceğiz.) */ - İşlem işlem = cast(İşlem)işlemKodu; - - if ((işlem < İşlem.min) || (işlem > İşlem.max)) { - writeln("HATA: Geçersiz işlem"); - continue; - } - - if (işlem == İşlem.çıkış) { - writeln("Güle güle!"); - break; - } - - double birinci; - double ikinci; - double sonuç; - - write("Birinci sayı? "); - readf(" %s", &birinci); - - write(" İkinci sayı? "); - readf(" %s", &ikinci); - - switch (işlem) { - - case İşlem.toplama: - sonuç = birinci + ikinci; - break; - - case İşlem.çıkarma: - sonuç = birinci - ikinci; - break; - - case İşlem.çarpma: - sonuç = birinci * ikinci; - break; - - case İşlem.bölme: - sonuç = birinci / ikinci; - break; - - default: - throw new Exception( - "HATA: Bu satıra hiç gelinmemeliydi."); - } - - writeln(" Sonuç: ", sonuç); - } -} ---- - - -Macros: - SUBTITLE=enum Çözümü - - DESCRIPTION=enum Problem Çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial enum numaralandırma numara problem çözüm diff --git a/ddili/src/ders/d/enum.d b/ddili/src/ders/d/enum.d deleted file mode 100644 index 05ef05a..0000000 --- a/ddili/src/ders/d/enum.d +++ /dev/null @@ -1,335 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX enum) $(CH4 enum)) - -$(P -$(C enum), "numaralandırmak" anlamına gelen "enumerate"in kısaltılmışıdır. İsimli sabit değerler üretmek için kullanılır. -) - -$(H5 $(IX sihirli sabit) Sihirli sabitler) - -$(P -Tamsayılar ve Aritmetik İşlemler bölümünün $(LINK2 /ders/d/aritmetik_islemler.cozum.html, problem çözümlerinden) birisinde şöyle bir koşul kullanmıştık: -) - ---- - if (işlem == 1) { - sonuç = birinci + ikinci; - - } else if (işlem == 2) { - sonuç = birinci - ikinci; - - } else if (işlem == 3) { - sonuç = birinci * ikinci; - - } else if (işlem == 4) { - sonuç = birinci / ikinci; - } ---- - -$(P -O kod parçasındaki 1, 2, 3, ve 4 değerlerine $(I sihirli sabit) denir. Kodu okuyan birisinin onların ne anlama geldiklerini bir bakışta anlaması olanaksızdır. Örneğin yukarıdaki kodda 1'in $(I toplama işlemi), 2'nin $(I çıkarma işlemi), vs. anlamlarına geldiklerini ancak kapsamlarındaki kodları okuduktan sonra anlayabiliyoruz. Bu durumda şanslıyız, çünkü her kapsamda yalnızca tek satır var; daha karmaşık kodlarda kodu anlamak çok güç olabilir. -) - -$(P -Programcılıkta sihirli sabitlerden kaçınılır çünkü onlar iyi yazılmış kodun en önemli niteliklerinden olan $(I okunurluğunu) azaltırlar. -) - -$(P -$(C enum) olanağı işte bu tür sabitlere isimler vermeyi ve bu sayede kodun okunurluğunu arttırmayı sağlar. Aynı kod $(C enum) değerleriyle yazıldığında her bir $(C if) koşulunun hangi işlemle ilgili olduğu açıkça anlaşılır: -) - ---- - if (işlem == İşlem.toplama) { - sonuç = birinci + ikinci; - - } else if (işlem == İşlem.çıkarma) { - sonuç = birinci - ikinci; - - } else if (işlem == İşlem.çarpma) { - sonuç = birinci * ikinci; - - } else if (işlem == İşlem.bölme) { - sonuç = birinci / ikinci; - - } ---- - -$(P -Artık 1 gibi anlamı açık olmayan bir değer yerine $(C İşlem.toplama) gibi isimli bir değer kullanılmaktadır. Bundan sonraki bölümlerdeki kodlarda sihirli sabitler yerine hep isimli sabitler kullanacağım. -) - -$(P -Yukarıdaki 1, 2, 3, ve 4 değerlerine karşılık gelen $(C enum) tanımı şöyle yazılır: -) - ---- - enum İşlem { toplama = 1, çıkarma, çarpma, bölme } ---- - -$(H5 Söz dizimi) - -$(P -$(C enum) yaygın olarak şu söz dizimiyle kullanılır: -) - ---- - enum $(I Türİsmi) { $(I değerİsmi_1), $(I değerİsmi_2), /* vs. */ } ---- - -$(P -Bazen değerlerin asıl türlerini de belirtmek gerekebilir. Bunun nasıl kullanıldığını bir sonraki başlıkta göreceğiz: -) - ---- - enum $(I Türİsmi) $(HILITE : $(I asıl_tür)) { $(I değerİsmi_1), $(I değerİsmi_2), /* vs. */ } ---- - -$(P -$(C enum) anahtar sözcüğünden sonra bütün değerlerin toplu olarak ne anlama geldiğini belirten bir tür ismi verilir. Bütün olası değerler isimler halinde $(C enum) kapsamı içinde sıralanırlar. -) - -$(P -Bir kaç örnek: -) - ---- - enum ParaAtışıSonucu { yazı, tura } - enum OyunKağıdıRengi { maça, kupa, karo, sinek } - enum BiletTürü { normal, çocuk, öğrenci, emekli } ---- - -$(P -Bu değerler aynı zamanda yeni bir türün parçaları haline de gelirler. Örneğin $(C yazı) ve $(C tura) artık $(C ParaAtışıSonucu) diye tanımlanmış olan yeni bir türün değerleridir. Bu yeni tür de başka türler gibi değişken tanımlamak için kullanılabilir: -) - ---- - ParaAtışıSonucu sonuç; // otomatik ilklenerek - auto yt = ParaAtışıSonucu.yazı; // türü çıkarsanarak ---- - -$(P -Yukarıdaki kodlarda da olduğu gibi, $(C enum) türlerinin değerleri kod içinde sabit olarak belirtilecekleri zaman ait oldukları türün ismiyle birlikte ve ondan bir nokta ile ayrılarak yazılırlar: -) - ---- - if (sonuç == ParaAtışıSonucu.yazı) { - // ... - } ---- - -$(H5 Asıl değerler ve türleri) - -$(P -$(C enum) türlerin değerleri arka planda normalde $(C int) olarak gerçekleştirilirler. Yani her ne kadar $(C yazı) ve $(C tura) gibi isimleri olsa da, arka planda birer $(C int) değeridirler. ($(C int)'ten başka türlerin de kullanılabileceğini aşağıda göreceğiz.) -) - -$(P -Bu değerler programcı özellikle belirtmediği sürece 0'dan başlar ve her isimli değer için bir tane arttırılır. Örneğin yukarıda tanımlanan $(C ParaAtışıSonucu)'nun iki değerinin sırasıyla 0 ve 1'e eşit olduklarını şöyle gösterebiliriz: -) - ---- - writefln("yazı'nın değeri 0: %s", (ParaAtışıSonucu.yazı == 0)); - writefln("tura'nın değeri 1: %s", (ParaAtışıSonucu.tura == 1)); ---- - -$(P -Çıktısı: -) - -$(SHELL -yazı'nın değeri 0: true -tura'nın değeri 1: true -) - -$(P -Normalde 0'dan başlayan bu değerleri istediğimiz noktadan itibaren $(C =) işareti ile kendimiz de belirleyebiliriz. Yukarıda $(C İşlem.toplama) değerini 1 olarak belirken bundan yararlanmıştık. Belirlediğimiz değerden sonrakilerin değerleri de yine derleyici tarafından birer birer arttırılarak verilir: -) - ---- - enum Deneme { a, b, c, ç = 100, d, e, f = 222, g, ğ } - writefln("%d %d %d", Deneme.b, Deneme.ç, Deneme.ğ); ---- - -$(P -Çıktısı: -) - -$(SHELL -1 100 224 -) - -$(P -$(C enum) değerlerinin perde arkasında tamsayılardan başka bir tür olması gerektiğinde o tür $(C enum) isminden sonra belirtilir: -) - ---- - enum DoğalSabit $(HILITE : double) { pi = 3.14, e = 2.72 } - enum IsıBirimi $(HILITE : string) { C = "Celcius", F = "Fahrenheit" } ---- - -$(H5 Bir $(C enum) türüne ait olmayan $(C enum) değerleri) - -$(P -Sihirli sabitlerden kurtulmanın önemli olduğunu ve bu amaçla $(C enum)'lardan yararlanabileceğimizi gördük. -) - -$(P -Ancak, sihirli sabitlerden kurtulabilmek için ayrıca bir $(C enum) türü belirlemek doğal olmayabilir. Örneğin tek amacımızın 24 saatteki toplam saniye sayısını tutan bir sabit tanımlamak olduğunu düşünelim. Böyle tek sabitin tanımlanmasında ayrıca $(C enum) türü belirlemeye gerek yoktur. Böyle durumlarda $(C enum) türü ve $(C enum) kapsam parantezleri yazılmayabilir: -) - ---- - enum günBaşınaSaniye = 60 * 60 * 24; ---- - -$(P -Artık o sabiti hesaplarda ismiyle kullanabiliriz: -) - ---- - toplamSaniye = günAdedi * günBaşınaSaniye; ---- - -$(P -$(C enum), başka türden hazır değerler tanımlamak için de kullanılabilir. Örneğin isimli bir $(C string) hazır değeri şöyle tanımlanabilir: -) - ---- - enum dosyaİsmi = "liste.txt"; ---- - -$(P -$(IX manifest constant) $(IX sabit, manifest) Böyle sabitler $(LINK2 /ders/d/deger_sol_sag.html, $(I sağ değerdirler)) ve İngilizce'de "manifest constant" diye anılırlar. -) - -$(H5 Nitelikleri) - -$(P -$(C .min) ve $(C .max) nitelikleri $(C enum) türünün sırasıyla en küçük ve en büyük değerleridir. Bunları bir $(C for) döngüsünde kullanarak bütün değerleri sırayla gezebiliriz: -) - ---- - enum OyunKağıdıRengi { maça, kupa, karo, sinek } - - for (auto renk = OyunKağıdıRengi.min; - renk <= OyunKağıdıRengi.max; - ++renk) { - - writefln("%s: %d", renk, renk); - } ---- - -$(P -$(STRING "%s") ve $(STRING "%d") düzen belirteçlerinin çıktılarının farklı olduklarına dikkat edin: -) - -$(SHELL -maça: 0 -kupa: 1 -karo: 2 -sinek: 3 -) - -$(P -Bunun için $(C foreach) döngüsünün uygun olmadığına dikkat edin. $(C foreach) değer aralığı ile kullanılsaydı $(C .max) değeri aralığın dışında kalırdı: -) - ---- - foreach (renk; OyunKağıdıRengi.min .. OyunKağıdıRengi.max) { - writefln("%s: %d", renk, renk); - } ---- - -$(P -Çıktısı: -) - -$(SHELL -maça: 0 -kupa: 1 -karo: 2 - $(SHELL_NOTE_WRONG sinek eksik) -) - -$(P -$(IX EnumMembers, std.traits) Bu yüzden, bir $(C enum)'ın bütün değerleri üzerinde ilerlemenin doğru bir yolu $(C std.traits) modülünde tanımlı olan $(C EnumMembers) şablonundan yararlanmaktır: -) - ---- -import std.traits; -// ... - foreach (renk; $(HILITE EnumMembers!OyunKağıdıRengi)) { - writefln("%s: %d", renk, renk); - } ---- - -$(P -$(I Not: Yukarıdaki $(C !) karakteri şablon parametre değeri bildirmek içindir. Şablonları $(LINK2 /ders/d/sablonlar.html, ilerideki bir bölümde) göreceğiz.) -) - -$(SHELL -maça: 0 -kupa: 1 -karo: 2 -sinek: 3 $(SHELL_NOTE sinek mevcut) -) - -$(H5 Asıl türden dönüştürmek) - -$(P -Yukarıdaki yazdırma örneklerinde görüldüğü gibi, bir $(C enum) değer perde arkasında kullanılan asıl türe (örneğin $(C int)'e) otomatik olarak dönüşür. Bunun tersi doğru değildir: -) - ---- - OyunKağıdıRengi renk = 1; $(DERLEME_HATASI) ---- - -$(P -Bunun nedeni, $(C enum) değişkenlerine yanlışlıkla geçersiz değerlerin atanmasını önlemektir: -) - ---- - renk = 100; // ← geçerli bir değer olmadığı için - // anlamsız olurdu ---- - -$(P -Geçerli olduğunu bildiğimiz bir değeri bir $(C enum) değerine dönüştürmek istiyorsak, bunu açıkça bir $(I tür dönüşümü) olarak yazmamız gerekir: -) - ---- - renk = cast(OyunKağıdıRengi)1; // şimdi kupa ---- - -$(P -Tür dönüşümlerini $(LINK2 /ders/d/tur_donusumleri.html, ilerideki bir bölümde) göreceğiz. -) - -$(PROBLEM_TEK - -$(P -$(LINK2 /ders/d/aritmetik_islemler.html, Tamsayılar ve Aritmetik İşlemler bölümünün) problemlerindeki hesap makinesini değiştirin: Dört işlemi destekleyen basit bir hesap makinesi, işlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın. -) - -$(P -Programı bu sefer şu farklarla yazın: -) - -$(UL -$(LI Hangi işlem olduğunu sihirli sabitlerden değil, $(C enum) değerlerden anlasın.) -$(LI $(C int) yerine $(C double) kullansın.) -$(LI "if else if" zinciri yerine $(C switch) kullansın.) -) -) - -Macros: - SUBTITLE=enum - - DESCRIPTION=D dilinin isimli değerler üretme olanağı olan enum'un tanıtılması - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial enum numaralandırma numara - -SOZLER= -$(sag_deger) -$(sihirli_sabit) -$(sablon) diff --git a/ddili/src/ders/d/es_zamanli.d b/ddili/src/ders/d/es_zamanli.d deleted file mode 100644 index 3f4a7e2..0000000 --- a/ddili/src/ders/d/es_zamanli.d +++ /dev/null @@ -1,1338 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX eş zamanlı programlama, mesajlaşarak) $(IX mesajlaşarak eş zamanlı programlama) Mesajlaşarak Eş Zamanlı Programlama) - -$(P -Eş zamanlı programlama bir önceki bölümde gördüğümüz koşut işlemlere çok benzer. İkisi de işlemlerin farklı iş parçacıkları üzerinde aynı anda işletilmeleri ile ilgilidir ve aslında koşut işlemler de perde arkasında eş zamanlı programlama ile gerçekleştirilir. Bu iki kavram bu yüzden çok karıştırılır. -) - -$(P -$(IX koşut işlemler ve eş zamanlı programlama) $(IX eş zamanlı programlama ve koşut işlemler) Koşut işlemlerle eş zamanlı programlama arasındaki farklar şunlardır: -) - -$(UL - -$(LI Koşut işlemlerin temel amacı mikro işlemci çekirdeklerinden yararlanarak programın hızını arttırmaktır. Eş zamanlı programlama ise yalnızca tek çekirdeği bulunan ortamlarda bile gerekebilir ve programın aynı anda birden fazla iş yürütmesini sağlar. Örneğin bir sunucu program her istemcinin işini farklı bir iş parçacığında yürütebilir.) - -$(LI Koşut işlemler birbirlerinden bağımsızdırlar. Hatta, birbirlerine bağlı olan işlemlerin koşut olarak işletilmeleri hata olarak kabul edilir. Eş zamanlı programlamada ise çoğu zaman iş parçacıkları birbirlerine bağlıdırlar. Örneğin, devam edebilmek için başka iş parçacıklarının ürettikleri verilere gerek duyarlar.) - -$(LI Her iki yöntem de işletim sisteminin iş parçacıklarını kullanırlar. Koşut işlemler iş parçacıklarını $(I görev) kavramının arkasına gizlerler; eş zamanlı programlama ise doğrudan iş parçacıklarını kullanır.) - -$(LI Koşut işlemler çok kolay kullanılırlar ve görevler bağımsız oldukları sürece program doğruluğu açısından güvenlidirler. Eş zamanlı programlama ise ancak mesajlaşma yöntemi kullanıldığında güvenlidir. Veri paylaşımına dayanan geleneksel eş zamanlı programlamada programın doğru çalışacağı kanıtlanamayabilir.) - -) - -$(P -D hem mesajlaşmaya dayanan hem de veri paylaşımına dayanan eş zamanlı programlamayı destekler. Veri paylaşımı ile hatasız programlar üretmek çok zor olduğundan modern programcılıkta mesajlaşma yöntemi benimsenmiştir. Bu bölümde $(C std.concurrency) modülünün sağladığı mesajlaşma olanaklarını, bir sonraki bölümde ise veri paylaşımına dayalı eş zamanlı programlama olanaklarını göreceğiz. -) - -$(H5 Kavramlar) - -$(P$(IX iş parçacığı) $(B İş parçacığı $(ASIL thread)): İşletim sistemi bütün programları $(I iş parçacığı) adı verilen işlem birimleri ile işletir. Çalıştırılan her D programının $(C main()) ile başlayan işlemleri işletim sisteminin o programı çalıştırmak için seçmiş olduğu bir iş parçacığı üzerinde başlatılır. $(C main())'in işlettiği bütün işlemler normalde hep aynı iş parçacığı üzerinde işletilirler. Program, gerektiğinde kendisi başka iş parçacıkları başlatabilir ve böylece aynı anda birden fazla iş yürütebilir. Örneğin bir önceki bölümde gördüğümüz her görev, $(C std.parallelism)'in olanakları tarafından başlatılmış olan bir iş parçacığını kullanır. - -) - -$(P -İşletim sistemi iş parçacıklarını önceden kestirilemeyecek anlarda duraksatır ve tekrar başlatır. Bunun sonucunda örneğin aşağıdaki kadar basit işlemler bile bir süre yarım kalmış olabilirler: -) - ---- - ++i; ---- - -$(P -Yukarıdaki işlem aslında üç adımdan oluşur: Değişkenin değerinin okunması, değerin arttırılması ve tekrar değişkene atanması. İşletim sisteminin bu iş parçacığını duraksattığı bir anda bu adımlar sonradan devam edilmek üzere yarım kalmış olabilirler. -) - -$(P $(IX mesaj) $(B Mesaj $(ASIL message)): İş parçacıklarının işleyişleri sırasında birbirlerine gönderdikleri bilgilere mesaj denir. Mesaj her türden ve her sayıda değişkenden oluşabilir. -) - -$(P $(IX kimlik) $(B İş parçacığı kimliği $(ASIL Tid)): Her iş parçacığının bir kimliği vardır. Kimlik, gönderilen mesajın alıcısı olan iş parçacığını belirler. -) - -$(P $(IX sahip) $(B Sahip $(ASIL owner)): İş parçacığı başlatan her iş parçacığı, başlatılan iş parçacığının sahibi olarak anılır. -) - -$(P $(IX işçi) $(B İşçi $(ASIL worker)): Başlatılan iş parçacığına işçi denir. -) - -$(H5 $(IX spawn) İş parçacıklarını başlatmak) - -$(P -Yeni bir iş parçacığı başlatmak için $(C spawn()) kullanılır. $(C spawn()) parametre olarak bir işlev alır ve yeni iş parçacığını o işlevden başlatır. O işlevin belki de başka işlevlere de dallanarak devam eden işlemleri artık yeni iş parçacığı üzerinde işletilir. $(C spawn()) ile $(LINK2 /ders/d/kosut_islemler.html, $(C task())) arasındaki bir fark, $(C spawn()) ile başlatılan iş parçacıklarının birbirlerine mesaj gönderebilmeleridir. -) - -$(P -İşçinin başlatılmasından sonra sahip ve işçi birbirlerinden bağımsız iki alt program gibi işlemeye devam ederler: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -void işçi() { - foreach (i; 0 .. 5) { - Thread.sleep(500.msecs); - writeln(i, " (işçi)"); - } -} - -void main() { - $(HILITE spawn(&işçi)); - - foreach (i; 0 .. 5) { - Thread.sleep(300.msecs); - writeln(i, " (main)"); - } - - writeln("main tamam"); -} ---- - -$(P -İşlemlerin aynı anda işletildiklerini gösterebilmek için buradaki örneklerde de $(C Thread.sleep)'ten yararlanıyorum. Programın çıktısı $(C main())'den ve $(C işçi())'den başlamış olan iki iş parçacığının diğerinden bağımsız olarak işlediğini gösteriyor: -) - -$(SHELL -0 (main) -0 (işçi) -1 (main) -2 (main) -1 (işçi) -3 (main) -2 (işçi) -4 (main) -main tamam -3 (işçi) -4 (işçi) -) - -$(P -Program bütün iş parçacıklarının tamamlanmasını otomatik olarak bekler. Bunu yukarıdaki çıktıda görüyoruz: $(C main())'in sonundaki "main tamam" yazdırıldığı halde $(C işçi()) işlevinin de tamamlanması beklenmiştir. -) - -$(P -İş parçacığını başlatan işlevin aldığı parametreler $(C spawn())'a işlev isminden sonra verilirler. Aşağıdaki programdaki iki işçi, çıkışa dörder tane sayı yazdırıyor. Hangi değerden başlayacağını parametre olarak alıyor: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -void işçi($(HILITE int başlangıçDeğeri)) { - foreach (i; 0 .. 4) { - Thread.sleep(500.msecs); - writeln(başlangıçDeğeri + i); - } -} - -void main() { - foreach (i; 1 .. 3) { - spawn(&işçi, $(HILITE i * 10)); - } -} ---- - -$(P -İş parçacıklarından birisinin yazdırdıklarını işaretli olarak belirtiyorum. İşletim sisteminin iş parçacıklarını başlatmasına ve duraklatmasına bağlı olarak bu çıktıdaki satırlar farklı sırada da olabilirler: -) - -$(SHELL -10 -$(HILITE 20) -11 -$(HILITE 21) -12 -$(HILITE 22) -13 -$(HILITE 23) -) - -$(P -$(IX iş parçacığı performansı) $(IX mikro işlemciye bağlı) $(IX giriş/çıkış'a bağlı) İşletim sistemleri belirli bir anda işlemekte olan iş parçacıklarının sayısı konusunda kısıtlama getirir. Bu kısıtlamalar kullanıcı başına olabileceği gibi, bütün sistem başına veya herhangi başka bir kavramla ilgili olabilir. Mikro işlemciyi meşgul ederek işleyen iş parçacıklarının sayısı sistemdeki çekirdek sayısından fazla olduğunda bütün sistemin performansı düşebilir. Belirli bir anda mikro işlemciyi meşgul ederek işlemekte olan iş parçacıklarına $(I mikro işlemciye bağlı) denir. Öte yandan, bazı iş parçacıkları zamanlarının çoğunu iş yaparak değil, belirli bir olayın gerçekleşmesini bekleyerek geçirirler. Örneğin, kullanıcıdan veya ağ üzerinden bilgi gelmesinin veya bir $(C Thread.sleep) çağrısının sonlanmasının beklenmesi sırasında mikro işlemci meşgul değildir. Böyle durumdaki iş parçacıklarına $(I giriş/çıkış'a bağlı) denir. İş parçacıklarının çoğunluğunun giriş/çıkış'a bağlı olarak işlediği bir programın sistemdeki çekirdek sayısından daha fazla iş parçacığı başlatmasında bir sakınca yoktur. Program hızıyla ilgili her tasarım kararında olması gerektiği gibi, bu konudaki kararlarınızı da ölçümler yaparak vermenizi öneririm. -) - -$(H5 $(IX Tid) $(IX thisTid) $(IX ownerTid) İş parçacıklarının kimlikleri) - -$(P -$(C thisTid()) iş parçacığının kendi kimliğini döndürür. İsmi "bu iş parçacığının kimliği" anlamına gelen "this thread's identifier"dan türemiştir. Bir işlev olmasına rağmen daha çok parantezsiz kullanılır: -) - ---- -import std.stdio; -import std.concurrency; - -void kimlikBilgisi(string açıklama) { - writefln("%s: %s", açıklama, $(HILITE thisTid)); -} - -void işçi() { - kimlikBilgisi("işçi "); -} - -void main() { - spawn(&işçi); - kimlikBilgisi("sahip"); -} ---- - -$(P -$(C Tid) türündeki kimliğin değerinin program açısından bir önemi olmadığından bu türün $(C toString) işlevi bile tanımlanmamıştır. Bu yüzden programın aşağıdaki çıktısında yalnızca türün ismini görüyoruz: -) - -$(SHELL -sahip: Tid(std.concurrency.MessageBox) -işçi : Tid(std.concurrency.MessageBox) -) - -$(P -Çıktıları aynı olsa da sahip ve işçinin kimlikleri farklıdır. -) - -$(P -$(C spawn())'ın bu noktaya kadar gözardı etmiş olduğum dönüş değeri de işçinin kimliğini sahibe bildirir: -) - ---- - $(HILITE Tid işçim) = spawn(&işçi); ---- - -$(P -Her işçinin sahibinin kimliği ise $(C ownerTid()) işlevi ile elde edilir. -) - -$(P -Özetle, sahibin kimliği $(C ownerTid) değişkeni ile, işçinin kimliği de $(C spawn)'ın dönüş değeri ile elde edilmiş olur. -) - -$(H5 $(IX send) $(IX receiveOnly) Mesajlaşma) - -$(P -Mesaj göndermek için $(C send()), belirli türden mesaj beklemek için de $(C receiveOnly()) kullanılır. (Çeşitli türlerden mesaj bekleyen $(C receive())'i ve belirli süreye kadar bekleyen $(C receiveTimeout())'u daha aşağıda göstereceğim.) -) - -$(P -Aşağıdaki programdaki sahip iş parçacığı işçisine $(C int) türünde bir mesaj göndermekte ve ondan $(C double) türünde bir mesaj beklemektedir. Bu iş parçacıkları sahip sıfırdan küçük bir değer gönderene kadar mesajlaşmaya devam edecekler. Önce sahip iş parçacığını gösteriyorum: -) - ---- -void $(CODE_DONT_TEST)main() { - Tid işçi = spawn(&işçiİşlevi); - - foreach (değer; 1 .. 5) { - $(HILITE işçi.send)(değer); - double sonuç = $(HILITE receiveOnly!double)(); - writefln("gönderilen: %s, alınan: %s", değer, sonuç); - } - - /* Sonlanmasını sağlamak için işçiye sıfırdan küçük bir - * değer gönderiyoruz */ - $(HILITE işçi.send)(-1); -} ---- - -$(P -$(C main()), $(C spawn())'ın döndürdüğü iş parçacığının kimliğini $(C işçi) ismiyle saklamakta ve bu kimliği $(C send()) ile mesaj gönderirken kullanmaktadır. -) - -$(P -İşçi ise kullanacağı $(C int)'i bir mesaj olarak alıyor, onu bir hesapta kullanıyor ve ürettiği $(C double)'ı yine bir mesaj olarak sahibine gönderiyor: -) - ---- -void işçiİşlevi() { - int değer = 0; - - while (değer >= 0) { - değer = $(HILITE receiveOnly!int)(); - double sonuç = cast(double)değer / 5; - $(HILITE ownerTid.send)(sonuç); - } -} ---- - -$(P -Yukarıdaki iş parçacığı mesajdaki değerin beşte birini hesaplar. Programın çıktısı şöyle: -) - -$(SHELL -gönderilen: 1, alınan: 0.2 -gönderilen: 2, alınan: 0.4 -gönderilen: 3, alınan: 0.6 -gönderilen: 4, alınan: 0.8 -) - -$(P -Birden fazla değer aynı mesajın parçası olarak gönderilebilir: -) - ---- - ownerTid.send($(HILITE thisTid, 42, 1.5)); ---- - -$(P -Aynı mesajın parçası olarak gönderilen değerler alıcı tarafta bir çokuzlunun üyeleri olarak belirirler. $(C receiveOnly())'nin şablon parametrelerinin mesajı oluşturan türlere uymaları şarttır: -) - ---- - /* Tid, int, ve double türlerinden oluşan bir mesaj - * bekliyoruz */ - auto mesaj = receiveOnly!($(HILITE Tid, int, double))(); - - /* Mesaj bir çokuzlu olarak alınır */ - auto gönderen = mesaj$(HILITE [0]); // Tid türünde - auto tamsayı = mesaj$(HILITE [1]); // int türünde - auto kesirli = mesaj$(HILITE [2]); // double türünde ---- - -$(P -$(IX MessageMismatch) Türler uymadığında "mesaj uyumsuzluğu" anlamına gelen $(C MessageMismatch) hatası atılır: -) - ---- -import std.concurrency; - -void işçiİşlevi() { - ownerTid.send("merhaba"); $(CODE_NOTE $(HILITE string) gönderiyor) -} - -void main() { - spawn(&işçiİşlevi); - - auto mesaj = receiveOnly!double(); $(CODE_NOTE $(HILITE double) bekliyor) -} ---- - -$(P -Çıktısı: -) - -$(SHELL -std.concurrency.$(HILITE MessageMismatch)@std/concurrency.d(202): -Unexpected message type: expected 'double', got 'immutable(char)[]' -) - -$(H6 Örnek) - -$(P -Şimdiye kadar gördüğümüz kavramları kullanan basit bir benzetim programı tasarlayalım. -) - -$(P -Bu örnek iki boyutlu düzlemdeki robotların birbirlerinden bağımsız ve rasgele hareketlerini belirliyor. Her robotu farklı bir iş parçacığı yönetiyor. Her iş parçacığı başlatılırken üç bilgi alıyor: -) - -$(UL - -$(LI Robotun numarası: Gönderilen mesajın hangi robotla ilgili olduğu -) - -$(LI Başlangıç noktası: Robotun hareketinin başlangıç noktası -) - -$(LI Robotun dinlenme süresi: Robotun ne kadar zamanda bir yer değiştireceği -) - -) - -$(P -Yukarıdaki üç bilgiyi bir arada tutan bir $(C İş) yapısı şöyle tanımlanabilir: -) - ---- -struct İş { - size_t robotNumarası; - Yer başlangıç; - Duration dinlenmeSüresi; -} ---- - -$(P -Bu iş parçacığının yaptığı tek iş, robotun numarasını ve hareketini bir sonsuz döngü içinde sahibine göndermek: -) - ---- -void gezdirici(İş iş) { - Yer nereden = iş.başlangıç; - - while (true) { - Thread.sleep(iş.dinlenmeSüresi); - - Yer nereye = rasgeleKomşu(nereden); - Hareket hareket = Hareket(nereden, nereye); - nereden = nereye; - - ownerTid.send($(HILITE HareketMesajı)(iş.robotNumarası, hareket)); - } -} ---- - -$(P -Sahip de sonsuz bir döngü içinde bu mesajları bekliyor. Aldığı mesajların hangi robotla ilgili olduğunu her mesajın parçası olan robot numarasından anlıyor: -) - ---- - while (true) { - auto mesaj = receiveOnly!$(HILITE HareketMesajı)(); - - writefln("%s %s", - robotlar[mesaj.robotNumarası], - mesaj.hareket); - } ---- - -$(P -Bu örnekteki bütün mesajlar işçilerden sahibe gönderiliyor. Daha karmaşık programlarda her iki yönde ve çok çeşitli türlerden mesajlar da gönderilebilir. Programın tamamı şöyle: -) - ---- -import std.stdio; -import std.random; -import std.string; -import std.concurrency; -import core.thread; - -struct Yer { - int satır; - int sütun; - - string toString() { - return format("%s,%s", satır, sütun); - } -} - -struct Hareket { - Yer nereden; - Yer nereye; - - string toString() { - return ((nereden == nereye) - ? format("%s (durgun)", nereden) - : format("%s -> %s", nereden, nereye)); - } -} - -class Robot { - string görünüm; - Duration dinlenmeSüresi; - - this(string görünüm, Duration dinlenmeSüresi) { - this.görünüm = görünüm; - this.dinlenmeSüresi = dinlenmeSüresi; - } - - override string toString() { - return format("%s(%s)", görünüm, dinlenmeSüresi); - } -} - -/* 0,0 noktası etrafında rasgele bir yer döndürür */ -Yer rasgeleYer() { - return Yer(uniform!"[]"(-10, 10), uniform!"[]"(-10, 10)); -} - -/* Verilen değerin en fazla bir adım ötesinde bir değer - * döndürür */ -int rasgeleAdım(int şimdiki) { - return şimdiki + uniform!"[]"(-1, 1); -} - -/* Verilen Yer'in komşusu olan bir Yer döndürür; çapraz - * komşusu olabileceği gibi tesadüfen aynı yer de olabilir. */ -Yer rasgeleKomşu(Yer yer) { - return Yer(rasgeleAdım(yer.satır), - rasgeleAdım(yer.sütun)); -} - -struct İş { - size_t robotNumarası; - Yer başlangıç; - Duration dinlenmeSüresi; -} - -struct HareketMesajı { - size_t robotNumarası; - Hareket hareket; -} - -void gezdirici(İş iş) { - Yer nereden = iş.başlangıç; - - while (true) { - Thread.sleep(iş.dinlenmeSüresi); - - Yer nereye = rasgeleKomşu(nereden); - Hareket hareket = Hareket(nereden, nereye); - nereden = nereye; - - ownerTid.send(HareketMesajı(iş.robotNumarası, hareket)); - } -} - -void main() { - /* Farklı hızlardaki robotlar */ - Robot[] robotlar = [ new Robot("A", 600.msecs), - new Robot("B", 2000.msecs), - new Robot("C", 5000.msecs) ]; - - /* Her birisi için bir iş parçacığı başlatılıyor */ - foreach (robotNumarası, robot; robotlar) { - spawn(&gezdirici, İş(robotNumarası, - rasgeleYer(), - robot.dinlenmeSüresi)); - } - - /* Artık hareket bilgilerini işçilerden toplamaya - * başlayabiliriz */ - while (true) { - auto mesaj = receiveOnly!HareketMesajı(); - - /* Bu robotla ilgili yeni bilgiyi çıkışa - * yazdırıyoruz */ - writefln("%s %s", - robotlar[mesaj.robotNumarası], - mesaj.hareket); - } -} ---- - -$(P -Program sonlandırılana kadar robotların konumlarını çıkışa yazdırır: -) - -$(SHELL -A(600 ms) -3,3 -> -4,4 -A(600 ms) -4,4 -> -4,3 -A(600 ms) -4,3 -> -3,2 -B(2 secs) -6,9 (durgun) -A(600 ms) -3,2 -> -2,2 -A(600 ms) -2,2 -> -3,1 -A(600 ms) -3,1 -> -2,0 -B(2 secs) -6,9 -> -5,9 -A(600 ms) -2,0 (durgun) -A(600 ms) -2,0 -> -3,-1 -C(5 secs) -6,6 -> -6,7 -A(600 ms) -3,-1 -> -4,-1 -... -) - -$(P -Mesajlaşmaya dayanan eş zamanlı programlamanın yararını bu örnekte görebiliyoruz. Her robotun hareketi aynı anda ve diğerlerinden bağımsız olarak hesaplanıyor. Bu basit örnekteki sahip yalnızca robotların hareketlerini çıkışa yazdırıyor; bütün robotları ilgilendiren başka işlemler de uygulanabilir. -) - -$(H5 $(IX temsilci, mesajlaşma) Farklı çeşitlerden mesaj beklemek) - -$(P -$(C receiveOnly()) yalnızca belirtilen türden mesaj bekleyebilir. $(C receive()) ise farklı çeşitlerden mesajlar beklemek için kullanılır. Parametre olarak belirsiz sayıda $(I mesajcı işlev) alır. Gelen mesaj bu mesajcı işlevlere sırayla uydurulmaya çalışılır ve mesaj, mesajın türünün uyduğu ilk işleve gönderilir. -) - -$(P -Örneğin aşağıdaki $(C receive()) çağrısı ilki $(C int), ikincisi de $(C string) bekleyen iki mesajcı işlev kullanmaktadır: -) - ---- -$(CODE_NAME işçiİşlevi)void işçiİşlevi() { - bool tamam_mı = false; - - while (!tamam_mı) { - void intİşleyen($(HILITE int) mesaj) { - writeln("int mesaj: ", mesaj); - - if (mesaj == -1) { - writeln("çıkıyorum"); - tamam_mı = true; - } - } - - void stringİşleyen($(HILITE string) mesaj) { - writeln("string mesaj: ", mesaj); - } - - receive($(HILITE &intİşleyen), $(HILITE &stringİşleyen)); - } -} ---- - -$(P -Gönderilen $(C int) mesajlar $(C intİşleyen())'e, $(C string) mesajlar da $(C stringİşleyen())'e uyarlar. O iş parçacığını şöyle bir kodla deneyebiliriz: -) - ---- -$(CODE_XREF işçiİşlevi)import std.stdio; -import std.concurrency; - -// ... - -void main() { - auto işçi = spawn(&işçiİşlevi); - - işçi.send(10); - işçi.send(42); - işçi.send("merhaba"); - işçi.send(-1); // ← işçinin sonlanması için -} ---- - -$(P -Mesajlar alıcı taraftaki uygun mesajcı işlevlere gönderilirler: -) - -$(SHELL -int mesaj: 10 -int mesaj: 42 -string mesaj: merhaba -int mesaj: -1 -çıkıyorum -) - -$(P -$(C receive()), yukarıdaki normal işlevler yerine isimsiz işlevler veya $(C opCall()) üye işlevi tanımlanmış olan türlerin nesnelerini de kullanabilir. Bunun bir örneğini görmek için programı isimsiz işlevler kullanacak şekilde değiştirelim. Ek olarak, işçinin sonlanmasını da -1 gibi özel bir değer yerine ismi açıkça $(C Sonlan) olan özel bir türle bildirelim. -) - -$(P -Aşağıda $(C receive())'e parametre olarak üç isimsiz işlev gönderildiğine dikkat edin. Bu işlevlerin açma ve kapama parantezlerini sarı ile belirtiyorum: -) - ---- -import std.stdio; -import std.concurrency; - -struct Sonlan { -} - -void işçiİşlevi() { - bool devam_mı = true; - - while (devam_mı) { - receive( - (int mesaj) $(HILITE {) - writeln("int mesaj: ", mesaj); - $(HILITE }), - - (string mesaj) $(HILITE {) - writeln("string mesaj: ", mesaj); - $(HILITE }), - - (Sonlan mesaj) $(HILITE {) - writeln("çıkıyorum"); - devam_mı = false; - $(HILITE })); - } -} - -void main() { - auto işçi = spawn(&işçiİşlevi); - - işçi.send(10); - işçi.send(42); - işçi.send("merhaba"); - işçi.send($(HILITE Sonlan())); -} ---- - -$(H6 Beklenmeyen mesaj almak) - -$(P -$(IX Variant, std.variant) $(C std.variant) modülünde tanımlanmış olan $(C Variant) her türden veriyi sarmalayabilen bir türdür. $(C receive())'e verilen diğer mesajcı işlevlere uymayan mesajlar $(C Variant) türünü bekleyen bir mesajcı tarafından yakalanabilirler: -) - ---- -import std.stdio; -import std.concurrency; - -void işçiİşlev() { - receive( - (int mesaj) { /* ... */ }, - - (double mesaj) { /* ... */ }, - - ($(HILITE Variant) mesaj) { - writeln("Beklemediğim bir mesaj aldım: ", mesaj); - }); -} - -struct ÖzelMesaj { - // ... -} - -void main() { - auto işçi = spawn(&işçiİşlev); - işçi.send(ÖzelMesaj()); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Beklemediğim bir mesaj aldım: ÖzelMesaj() -) - -$(P -Bu bölümün konusu dışında kaldığı için $(C Variant)'ın ayrıntılarına girmeyeceğim. -) - -$(H5 $(IX receiveTimeout) Mesajları belirli süreye kadar beklemek) - -$(P -Mesajların belirli bir süreden daha fazla beklenmesi istenmeyebilir. Gönderen iş parçacığı geçici olarak meşgul olmuş olabilir veya bir hata ile sonlanmış olabilir. Mesaj bekleyen iş parçacığının belki de hiç gelmeyecek olan bir mesajı sonsuza kadar beklemesini önlemek için $(C receiveTimeout()) çağrılır. -) - -$(P -$(C receiveTimeout())'un ilk parametresi mesajın en fazla ne kadar bekleneceğini bildirir. Dönüş değeri de mesajın o süre içinde gelip gelmediğini belirtir: Mesaj alındığında $(C true), alınmadığında ise $(C false) değeridir. -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -void işçi() { - Thread.sleep(3.seconds); - ownerTid.send("merhaba"); -} - -void main() { - spawn(&işçi); - - writeln("mesaj bekliyorum"); - bool alındı = false; - while (!alındı) { - alındı = $(HILITE receiveTimeout)(600.msecs, - (string mesaj) { - writeln("geldi: ", mesaj); - }); - - if (!alındı) { - writeln("... henüz yok"); - - /* ... burada başka işlere devam edilebilir ... */ - } - } -} ---- - -$(P -Yukarıdaki sahip, gereken mesajı en fazla 600 milisaniye bekliyor. Mesaj o süre içinde gelmezse başka işlerine devam edebilir: -) - -$(SHELL -mesaj bekliyorum -... henüz yok -... henüz yok -... henüz yok -... henüz yok -geldi: merhaba -) - -$(P -Mesajın belirli süreden uzun sürmesi sonucunda çeşitli durumlarda başka kararlar da verilebilir. Örneğin, mesaj geç geldiğinde artık bir anlamı yoktur. -) - -$(H5 $(IX hata atma, eş zamanlı programlama) İşçide atılan hatalar) - -$(P -Hatırlayacağınız gibi, $(C std.parallelism) modülünün çoğu olanağı görevler sırasında atılan hataları yakalar ve görevle ilgili bir sonraki işlem sırasında tekrar atmak üzere saklar. Böylece örneğin bir görevin işlemesi sırasında atılmış olan hata daha sonra $(C yieldForce()) çağrıldığında görevi başlatan tarafta yakalanabilir: -) - ---- - try { - görev.yieldForce(); - - } catch (Exception hata) { - writefln("görev sırasında bir hata olmuş: '%s'", - hata.msg); - } ---- - -$(P -$(C std.concurrency) genel hata türleri konusunda kolaylık sağlamaz. Atılan olası bir hatanın sahip iş parçacığına iletilebilmesi için açıkça yakalanması ve bir mesaj halinde gönderilmesi gerekir. Bir kolaylık olarak, biraz aşağıda göreceğimiz gibi, $(C OwnerTerminated) ve $(C LinkTerminated) hataları mesaj olarak da alınabilirler. -) - -$(P -Aşağıdaki $(C hesapçı()) işlevi $(C string) türünde mesajlar alıyor; onları $(C double) türüne çeviriyor, 0.5 değerini ekliyor ve sonucu bir mesaj olarak gönderiyor: -) - ---- -$(CODE_NAME hesapçı)void hesapçı() { - while (true) { - auto mesaj = receiveOnly!string(); - ownerTid.send(to!double(mesaj) + 0.5); - } -} ---- - -$(P -Yukarıdaki $(C to!double()), "merhaba" gibi $(C double)'a dönüştürülemeyen bir dizgi ile çağrıldığında hata atar. Atılan o hata $(C hesapçı())'dan hemen çıkılmasına neden olacağından aşağıdaki üç mesajdan yalnızca birincisinin yanıtı alınabilir: -) - ---- -$(CODE_XREF hesapçı)import std.stdio; -import std.concurrency; -import std.conv; - -// ... - -void main() { - Tid hesapçı = spawn(&hesapçı); - - hesapçı.send("1.2"); - hesapçı.send("merhaba"); // ← hatalı veri - hesapçı.send("3.4"); - - foreach (i; 0 .. 3) { - auto mesaj = receiveOnly!double(); - writefln("sonuç %s: %s", i, mesaj); - } -} ---- - -$(P -Bu yüzden, sahip "1.2"nin sonucunu 1.7 olarak alır ama işçi sonlanmış olduğundan bir sonraki mesajı alamaz: -) - -$(SHELL -sonuç 0: 1.7 - $(SHELL_NOTE hiç gelmeyecek olan mesajı bekleyerek takılır) -) - -$(P -Hesapçı iş parçacığının bu konuda yapabileceği bir şey, kendi işlemleri sırasında atılabilecek olan hatayı $(C try-catch) ile yakalamak ve özel bir mesaj olarak iletmektir. Programı hatanın nedenini bir $(C HesapHatası) nesnesi olarak gönderecek şekilde aşağıda değiştiriyoruz. Ek olarak, iş parçacığının sonlanması da özel $(C Sonlan) türü ile sağlanıyor: -) - ---- -import std.stdio; -import std.concurrency; -import std.conv; - -struct HesapHatası { - string neden; -} - -struct Sonlan { -} - -void hesapçı() { - bool devam_mı = true; - - while (devam_mı) { - receive( - (string mesaj) { - try { - ownerTid.send(to!double(mesaj) + 0.5); - - } $(HILITE catch) (Exception hata) { - ownerTid.send(HesapHatası(hata.msg)); - } - }, - - (Sonlan mesaj) { - devam_mı = false; - }); - } -} - -void main() { - Tid hesapçı = spawn(&hesapçı); - - hesapçı.send("1.2"); - hesapçı.send("merhaba"); // ← hatalı veri - hesapçı.send("3.4"); - hesapçı.send(Sonlan()); - - foreach (i; 0 .. 3) { - writef("sonuç %s: ", i); - - receive( - (double mesaj) { - writeln(mesaj); - }, - - (HesapHatası hata) { - writefln("HATA! '%s'", hata.neden); - }); - } -} ---- - -$(P -Hatanın nedeninin "rakam bulunamadı" anlamına gelen "no digits seen" olduğunu bu sefer görebiliyoruz: -) - -$(SHELL -sonuç 0: 1.7 -sonuç 1: HATA! 'no digits seen' -sonuç 2: 3.9 -) - -$(P -Bu konuda diğer bir yöntem, işçinin yakaladığı hatanın olduğu gibi sahibe gönderilmesidir. Aynı hata sahip tarafından kullanılabileceği gibi tekrar atılabilir de: -) - ---- -// ... işçi tarafta ... - try { - // ... - - } catch ($(HILITE shared(Exception)) hata) { - ownerTid.send(hata); - }}, - -// ... sahip tarafta ... - receive( - // ... - - ($(HILITE shared(Exception)) hata) { - throw hata; - }); ---- - -$(P -Yukarıdaki $(C shared) belirteçlerine neden gerek olduğunu bir sonraki bölümde göreceğiz. -) - -$(H5 İş parçacıklarının sonlandıklarını algılamak) - -$(P -İş parçacıkları alıcı tarafın herhangi bir nedenle sonlanmış olduğunu algılayabilirler. -) - -$(H6 $(IX OwnerTerminated) $(C OwnerTerminated) hatası) - -$(P -"Sahip sonlandı" anlamına gelen bu hata işçinin bu durumdan haberinin olmasını sağlar. Aşağıdaki programdaki aracı iş parçası iki mesaj gönderdikten sonra sonlanıyor. Bunun sonucunda işçi tarafta bir $(C OwnerTerminated) hatası atılıyor: -) - ---- -import std.stdio; -import std.concurrency; - -void main() { - spawn(&aracıİşlev); -} - -void aracıİşlev() { - auto işçi = spawn(&işçiİşlev); - işçi.send(1); - işçi.send(2); -} // ← İki mesajdan sonra sonlanıyor. - -void işçiİşlev() { - while (true) { - auto mesaj = receiveOnly!int(); // ← Sahip sonlanmışsa - // hata atılır. - writeln("Mesaj: ", mesaj); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Mesaj: 1 -Mesaj: 2 -std.concurrency.$(HILITE OwnerTerminated)@std/concurrency.d(248): -Owner terminated -) - -$(P -İstendiğinde o hata işçi tarafından yakalanabilir ve böylece işçinin de hatasız olarak sonlanması sağlanabilir: -) - ---- -void işçiİşlev() { - bool devam_mı = true; - - while (devam_mı) { - try { - auto mesaj = receiveOnly!int(); - writeln("Mesaj: ", mesaj); - - } catch ($(HILITE OwnerTerminated) hata) { - writeln("Sahibim sonlanmış."); - devam_mı = false; - } - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Mesaj: 1 -Mesaj: 2 -Sahibim sonlanmış. -) - -$(P -Bu hatanın mesaj olarak da alınabileceğini biraz aşağıda göreceğiz. -) - -$(H6 $(IX LinkTerminated) $(IX spawnLinked) $(C LinkTerminated) hatası) - -$(P -$(C spawnLinked()) ile başlatılmış olan bir iş parçacığı sonlandığında sahibin tarafında $(C LinkTerminated) hatası atılır. $(C spawnLinked()), $(C spawn()) ile aynı biçimde kullanılır: -) - ---- -import std.stdio; -import std.concurrency; - -void main() { - auto işçi = $(HILITE spawnLinked)(&işçiİşlev); - - while (true) { - auto mesaj = receiveOnly!int(); // ← İşçi sonlanmışsa - // hata atılır. - writeln("Mesaj: ", mesaj); - } -} - -void işçiİşlev() { - ownerTid.send(10); - ownerTid.send(20); -} // ← İki mesajdan sonra sonlanıyor. ---- - -$(P -İşçi yalnızca iki mesaj gönderdikten sonra sonlanıyor. İşçisini $(C spawnLinked()) ile başlatmış olduğu için sahip bu durumu bir $(C LinkTerminated) hatası ile öğrenir: -) - -$(SHELL -Mesaj: 10 -Mesaj: 20 -std.concurrency.$(HILITE LinkTerminated)@std/concurrency.d(263): -Link terminated -) - -$(P -$(C OwnerTerminated) hatasında olduğu gibi bu hata da yakalanabilir ve sahip de bu durumda düzenli olarak sonlanabilir: -) - ---- - bool devam_mı = true; - - while (devam_mı) { - try { - auto mesaj = receiveOnly!int(); - writeln("Mesaj: ", mesaj); - - } catch ($(HILITE LinkTerminated) hata) { - writeln("İşçi sonlanmış."); - devam_mı = false; - } - } ---- - -$(P -Çıktısı: -) - -$(SHELL -Mesaj: 10 -Mesaj: 20 -İşçi sonlanmış. -) - -$(P -Bu hata mesaj olarak da alınabilir. -) - -$(H6 Hataları mesaj olarak almak) - -$(P -$(C OwnerTerminated) ve $(C LinkTerminated) hataları karşı tarafta mesaj olarak da alınabilirler. Aşağıdaki kod bunu $(C OwnerTerminated) hatası üzerinde gösteriyor: -) - ---- - bool devam_mı = true; - - while (devam_mı) { - receive( - (int mesaj) { - writeln("Mesaj: ", mesaj); - }, - - ($(HILITE OwnerTerminated hata)) { - writeln("Sahip sonlanmış; çıkıyorum."); - devam_mı = false; - } - ); - } ---- - -$(H5 Posta kutusu yönetimi) - -$(P -İş parçacıklarına gönderilen mesajlar her iş parçacığına özel bir posta kutusunda dururlar. Posta kutusundaki mesajların sayısı alıcının mesajları işleyiş hızına bağlı olarak zamanla artabilir ve azalabilir. Posta kutusunun aşırı büyümesi hem sistem belleğine fazla yük getirir hem de programın tasarımındaki bir hataya işaret eder. Posta kutusunun sürekli olarak büyümesi bazı mesajların hiçbir zaman alınamayacaklarını da gösteriyor olabilir. -) - -$(P -$(IX setMaxMailboxSize) Posta kutusunun uzunluğu $(C setMaxMailboxSize()) işlevi ile kısıtlanır. Bu işlevin ilk parametresi hangi iş parçacığına ait posta kutusunun kısıtlanmakta olduğunu, ikinci parametresi posta kutusunun en fazla kaç mesaj alabileceğini, üçüncü parametresi de posta kutusu dolu olduğunda ne olacağını belirler. Üçüncü parametre için dört seçenek vardır: -) - -$(UL - -$(LI $(IX OnCrowding) $(C OnCrowding.block): Gönderen taraf posta kutusunda yer açılana kadar bekler.) - -$(LI $(C OnCrowding.ignore): Mesaj gözardı edilir.) - -$(LI $(IX MailboxFull) $(C OnCrowding.throwException): Mesaj gönderilirken $(C MailboxFull) hatası atılır.) - -$(LI $(C bool function(Tid)) türünde işlev göstergesi: Belirtilen işlev çağrılır.) - -) - -$(P -Bunun bir örneğini görmek için önce posta kutusunun sürekli olarak büyümesini sağlayalım. Aşağıdaki programdaki işçi hiç zaman geçirmeden art arda mesaj gönderdiği halde sahip iş parçacığı her mesaj için bir saniye zaman harcamaktadır: -) - ---- -/* UYARI: Bu program çalışırken sisteminiz aşırı derecede - * yavaşlayabilir. */ -import std.concurrency; -import core.thread; - -void işçiİşlev() { - while (true) { - ownerTid.send(42); // ← Sürekli olarak mesaj üretiyor. - } -} - -void main() { - spawn(&işçiİşlev); - - while (true) { - receive( - (int mesaj) { - // Her mesajda zaman geçiriyor. - Thread.sleep(1.seconds); - }); - } -} ---- - -$(P -Mesajları tüketen taraf üreten taraftan yavaş kaldığı için yukarıdaki programın kullandığı bellek sürekli olarak artacaktır. Bunun önüne geçmek için ana iş parçacığının posta kutusu daha işçi başlatılmadan önce belirli bir mesaj sayısı ile kısıtlanabilir: -) - ---- -void $(CODE_DONT_TEST)main() { - setMaxMailboxSize(thisTid, 1000, OnCrowding.block); - - spawn(&işçiİşlev); -// ... -} ---- - -$(P -Yukarıdaki $(C setMaxMailboxSize()) çağrısı ana iş parçacığının posta kutusunun uzunluğunu 1000 ile kısıtlamaktadır. $(C OnCrowding.block), gönderen tarafın mesaja yer açılana kadar beklemesine neden olur. -) - -$(P -$(C OnCrowding.throwException) kullanılan aşağıdaki örnekte ise mesajı gönderen taraf posta kutusunun dolu olduğunu atılan $(C MailboxFull) hatasından anlamaktadır: -) - ---- -import std.concurrency; -import core.thread; - -void işçiİşlev() { - while (true) { - try { - ownerTid.send(42); - - } catch ($(HILITE MailboxFull) hata) { - /* Gönderemedim; biraz sonra tekrar denerim. */ - Thread.sleep(1.msecs); - } - } -} - -void main() { - setMaxMailboxSize(thisTid, 1000, $(HILITE OnCrowding.throwException)); - - spawn(&işçiİşlev); - - while (true) { - receive( - (int mesaj) { - Thread.sleep(1.seconds); - }); - } -} ---- - -$(H5 $(IX prioritySend) $(IX PriorityMessageException) Öncelikli mesajlar) - -$(P -$(C prioritySend()) ile gönderilen mesajlar önceliklidir. Bu mesajlar posta kutusunda beklemekte olan mesajlardan daha önce alınırlar: -) - ---- - prioritySend(ownerTid, ÖnemliMesaj(100)); ---- - -$(P -Alıcı tarafta $(C prioritySend()) ile gönderilmiş olan mesajın türünü bekleyen mesajcı işlev yoksa $(C PriorityMessageException) hatası atılır: -) - -$(SHELL -std.concurrency.$(HILITE PriorityMessageException)@std/concurrency.d(280): -Priority message -) - -$(H5 İş parçacığı isimleri) - -$(P -Şimdiye kadar kullandığımız basit örneklerde sahip ve işçinin birbirlerinin kimliklerini kolayca edindiklerini gördük. Çok sayıda iş parçacığının görev aldığı programlarda ise iş parçacıklarının $(C Tid) değerlerini birbirlerini tanısınlar diye elden ele geçirmek karmaşık olabilir. Bunun önüne geçmek için iş parçacıklarına bütün program düzeyinde isimler atanabilir. -) - -$(P -Aşağıdaki üç işlev bütün iş parçacıkları tarafından erişilebilen bir eşleme tablosu gibi düşünülebilirler: -) - -$(UL - -$(LI $(IX register) $(C register()): İş parçacığını bir isimle eşleştirir.) - -$(LI $(IX locate) $(C locate()): Belirtilen isme karşılık gelen iş parçacığını döndürür. O isme karşılık gelen iş parçacığı yoksa $(C Tid.init) değerini döndürür.) - -$(LI $(IX unregister) $(C unregister()): İş parçacığı ile ismin ilişkisini kaldırır.) - -) - -$(P -Aşağıdaki program birbirlerini isimleriyle bulan iki eş iş parçacığı başlatıyor. Bu iş parçacıkları sonlanmalarını bildiren $(C Sonlan) mesajını alana kadar birbirlerine mesaj gönderiyorlar: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -struct Sonlan { -} - -void main() { - // Eşinin ismi "ikinci" olan bir iş parçacığı - auto birinci = spawn(&oyuncu, "ikinci"); - $(HILITE register)("birinci", birinci); - scope(exit) $(HILITE unregister)("birinci"); - - // Eşinin ismi "birinci" olan bir iş parçacığı - auto ikinci = spawn(&oyuncu, "birinci"); - $(HILITE register)("ikinci", ikinci); - scope(exit) $(HILITE unregister)("ikinci"); - - Thread.sleep(2.seconds); - - prioritySend(birinci, Sonlan()); - prioritySend(ikinci, Sonlan()); - - // unregister() çağrıları iş parçacıkları sonlandıktan - // sonra işletilsinler diye main() beklemelidir. - thread_joinAll(); -} - -void oyuncu(string eşİsmi) { - Tid eş; - - while (eş == Tid.init) { - Thread.sleep(1.msecs); - eş = $(HILITE locate)(eşİsmi); - } - - bool devam_mı = true; - - while (devam_mı) { - eş.send("merhaba " ~ eşİsmi); - receive( - (string mesaj) { - writeln("Mesaj: ", mesaj); - Thread.sleep(500.msecs); - }, - - (Sonlan mesaj) { - writefln("%s, ben çıkıyorum.", eşİsmi); - devam_mı = false; - }); - } -} ---- - -$(P -$(IX thread_joinAll) $(C main)'in sonunda çağrıldığını gördüğümüz $(C thread_joinAll), sahip iş parçacığının işçilerinin hepsinin sonlanmalarını beklemesini sağlar. -) - -$(P -Çıktısı: -) - -$(SHELL -Mesaj: merhaba birinci -Mesaj: merhaba ikinci -Mesaj: merhaba birinci -Mesaj: merhaba ikinci -Mesaj: merhaba birinci -Mesaj: merhaba ikinci -Mesaj: merhaba ikinci -Mesaj: merhaba birinci -birinci, ben çıkıyorum. -ikinci, ben çıkıyorum. -) - -$(H5 Özet) - -$(UL - -$(LI İş parçacıklarının birbirlerine bağlı olmadıkları durumlarda bir önceki bölümün konusu olan $(C std.parallelism) modülünün sunduğu $(I koşut programlamayı) yeğleyin. Ancak iş parçacıkları birbirlerine bağlı olduklarında $(C std.concurrency)'nin sunduğu $(I eş zamanlı programlamayı) düşünün.) - -$(LI Veri paylaşımı çeşitli program hatalarına açık olduğundan eş zamanlı programlama gerçekten gerektiğinde bu bölümün konusu olan mesajlaşmayı yeğleyin.) - -$(LI $(C spawn()) ve $(C spawnLinked()) iş parçacığı başlatır.) - -$(LI $(C thisTid) bu iş parçacığının kimliğidir.) - -$(LI $(C ownerTid) bu iş parçacığının sahibinin kimliğidir.) - -$(LI $(C send()) ve $(C prioritySend()) mesaj gönderir.) - -$(LI $(C receiveOnly()), $(C receive()) ve $(C receiveTimeout()) mesaj bekler.) - -$(LI $(C Variant) her mesaja uyar.) - -$(LI $(C setMaxMailboxSize()) posta kutusunun büyüklüğünü kısıtlar.) - -$(LI $(C register()), $(C unregister()) ve $(C locate()) iş parçacıklarını isimle kullanma olanağı sağlar.) - -$(LI Mesajlaşma sırasında hata atılabilir: $(C MessageMismatch), $(C OwnerTerminated), $(C LinkTerminated), $(C MailboxFull) ve $(C PriorityMessageException).) - -$(LI Sahip, işçiden atılan hataları otomatik olarak yakalayamaz.) - -) - -macros: - SUBTITLE=Mesajlaşarak Eş Zamanlı Programlama - - DESCRIPTION=Programın birden fazla iş parçacığı üzerinde işletilmesi ve iş parçacıklarının mesajlaşarak iletişimleri (concurrency) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial eş zamanlı programlama iş parçacığı concurrency mesajlaşma - -SOZLER= -$(cokuzlu) -$(degismez) -$(es_zamanli) -$(giris_cikisa_bagli) -$(gorev) -$(is_parcacigi) -$(kosut_islemler) -$(mesajlasma) -$(mikro_islemciye_bagli) diff --git a/ddili/src/ders/d/es_zamanli_shared.d b/ddili/src/ders/d/es_zamanli_shared.d deleted file mode 100644 index f4958d2..0000000 --- a/ddili/src/ders/d/es_zamanli_shared.d +++ /dev/null @@ -1,764 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX veri paylaşarak eş zamanlı programlama) $(IX eş zamanlı programlama, veri paylaşarak) Veri Paylaşarak Eş Zamanlı Programlama) - -$(P -Bir önceki bölümdeki yöntemler iş parçacıklarının mesajlaşarak bilgi alış verişinde bulunmalarını sağlıyordu. Daha önce de söylediğim gibi, eş zamanlı programlamada güvenli olan yöntem odur. -) - -$(P -Diğer yöntem, iş parçacıklarının aynı verilere doğrudan erişmelerine dayanır; iş parçacıkları aynı veriyi doğrudan okuyabilirler ve değiştirebilirler. Örneğin, sahip işçiyi $(C bool) bir değişkenin adresi ile başlatabilir ve işçi de sonlanıp sonlanmayacağını doğrudan o değişkenin değerini okuyarak karar verebilir. Başka bir örnek olarak, sahip bir kaç tane iş parçacığını hesaplarının sonuçlarını ekleyecekleri bir değişkenin adresi ile başlatabilir ve işçiler de o değişkenin değerini doğrudan arttırabilirler. -) - -$(P -Veri paylaşımı ancak paylaşılan veri değişmez olduğunda güvenilirdir. Verinin değişebildiği durumda ise iş parçacıkları birbirlerinden habersizce yarış halinde bulunurlar. İşletim sisteminin iş parçacıklarını ne zaman duraksatacağı ve ne zaman tekrar başlatacağı konusunda hiçbir tahminde bulunulamadığından programın davranışı bu yüzden şaşırtıcı derecede karmaşıklaşabilir. -) - -$(P -Bu başlık altındaki örnekler fazlaca basit ve anlamsız gelebilir. Buna rağmen, veri paylaşımının burada göreceğimiz sorunlarıyla gerçek programlarda da çok daha büyük ölçeklerde karşılaşılır. Ek olarak, buradaki örnekler iş parçacığı başlatırken kolaylık olarak $(C std.concurrency.spawn)'dan yararlanıyor olsalar da, burada anlatılan kavramlar $(C core.thread) modülünün olanakları ile başlatılmış olan iş parçacıkları için de geçerlidir. -) - -$(H5 Paylaşım otomatik değildir) - -$(P -Çoğu dilin aksine, D'de değişkenler iş parçacıklarına özeldir. Örneğin, her ne kadar aşağıdaki programdaki $(C değişken) tekmiş gibi görünse de her iş parçacığı o değişkenin kendi kopyasını edinir: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -int $(HILITE değişken); - -void bilgiVer(string mesaj) { - writefln("%s: %s (@%s)", mesaj, değişken, &değişken); -} - -void işçi() { - değişken = $(HILITE 42); - bilgiVer("İşçi sonlanırken"); -} - -void main() { - spawn(&işçi); - thread_joinAll(); - bilgiVer("İşçi sonlandıktan sonra"); -} ---- - -$(P -$(C işçi)'de değiştirilen değişkenin $(C main)'in kullandığı değişkenin aynısı olmadığı hem $(C main)'deki değerinin sıfır olmasından hem de adreslerinin farklı olmasından anlaşılıyor: -) - -$(SHELL -İşçi sonlanırken: 42 (@7F299DEF5660) -İşçi sonlandıktan sonra: 0 (@7F299DFF67C0) -) - -$(P -Değişkenlerin iş parçacıklarına özel olmalarının doğal bir sonucu, onların iş parçacıkları tarafından otomatik olarak paylaşılamamalarıdır. Örneğin, sonlanmasını bildirmek için işçiye $(C bool) türündeki bir değişkenin adresini göndermeye çalışan aşağıdaki kod D'de derlenemez: -) - ---- -import std.concurrency; - -void işçi($(HILITE bool * devam_mı)) { - while (*devam_mı) { - // ... - } -} - -void main() { - bool devam_mı = true; - spawn(&işçi, $(HILITE &devam_mı)); $(DERLEME_HATASI) - - // ... - - // Daha sonra işçi'nin sonlanmasını sağlamak için - devam_mı = false; - - // ... -} ---- - -$(P -$(C std.concurrency) modülündeki bir $(C static assert), bir iş parçacığının değişebilen $(ASIL mutable) yerel verisine başka iş parçacığı tarafından erişilmesini engeller: -) - -$(SHELL -src/phobos/std/concurrency.d(329): Error: static assert -"Aliases to $(HILITE mutable thread-local data) not allowed." -) - -$(P -$(C main()) içindeki $(C devam_mı) değişebilen yerel bir veri olduğundan ona erişim sağlayan adresi hiçbir iş parçacığına geçirilemez. -) - -$(P -$(IX __gshared) Verinin iş parçacıklarına özel olmasının bir istisnası, $(C __gshared) olarak işaretlenmiş olan değişkenlerdir: -) - ---- -__gshared int bütünProgramdaTek; ---- - -$(P -Bu çeşit değişkenlerden bütün programda tek adet bulunur ve o değişken bütün iş parçacıkları tarafından paylaşılır. $(C __gshared) değişkenler paylaşımın otomatik olduğu C ve C++ gibi dillerin kütüphaneleri ile etkileşirken gerekirler. -) - -$(H5 $(IX shared) Veri paylaşımı için $(C shared)) - -$(P -Değişebilen yerel verilerin iş parçacıkları tarafından paylaşılabilmeleri için "paylaşılan" anlamına gelen $(C shared) olarak işaretlenmeleri gerekir: -) - ---- -import std.concurrency; - -void işçi($(HILITE shared(bool)) * devam_mı) { - while (*devam_mı) { - // ... - } -} - -void main() { - $(HILITE shared(bool)) devam_mı = true; - spawn(&işçi, &devam_mı); - - // ... - - // Daha sonra işçi'nin sonlanmasını sağlamak için - devam_mı = false; - - // ... -} ---- - -$(P -($(I Not: İş parçacıklarının haberleşmeleri için bu örnekteki gibi veri paylaşımını değil, bir önceki bölümdeki mesajlaşmayı yeğleyin.)) -) - -$(P -$(IX immutable, eş zamanlı programlama) Öte yandan, $(C immutable) verilerin değiştirilmeleri olanaksız olduğundan onların paylaşılmalarında bir sakınca yoktur. O yüzden $(C immutable) veriler açıkça belirtilmeseler de $(C shared)'dirler: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -void işçi($(HILITE immutable(int)) * veri) { - writeln("veri: ", *veri); -} - -void main() { - $(HILITE immutable(int)) bilgi = 42; - spawn(&işçi, &bilgi); // ← derlenir - - thread_joinAll(); -} ---- - -$(P -Yukarıdaki program bu sefer derlenir ve beklenen çıktıyı üretir: -) - -$(SHELL -veri: 42 -) - -$(P -$(C bilgi)'nin yaşamı $(C main()) ile sınırlı olduğundan, ona erişmekte olan iş parçacığı sonlanmadan $(C main())'den çıkılmamalıdır. Bu yüzden, yukarıdaki programda $(C main())'den çıkılması programın sonundaki $(C thread_joinAll()) çağrısı ile engellenmekte ve $(C bilgi) değişkeni $(C işçi()) işlediği sürece geçerli kalmaktadır. -) - -$(H5 Veri değiştirirken yarış halinde olma örneği) - -$(P -Değişebilen verilerin paylaşıldığı durumda programın davranışının doğruluğunu sağlamak programcının sorumluluğundadır. -) - -$(P -Bunun önemini görmek için aynı değişebilen veriyi paylaşan birden fazla iş parçacığına bakalım. Aşağıdaki programdaki iş parçacıkları iki değişkenin adreslerini alıyorlar ve o değişkenlerin değerlerini değiş tokuş ediyorlar: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -void değişTokuşçu($(HILITE shared(int)) * birinci, $(HILITE shared(int)) * ikinci) { - foreach (i; 0 .. 10_000) { - int geçici = *ikinci; - *ikinci = *birinci; - *birinci = geçici; - } -} - -void main() { - $(HILITE shared(int)) i = 1; - $(HILITE shared(int)) j = 2; - - writefln("önce : %s ve %s", i, j); - - foreach (adet; 0 .. 10) { - spawn(&değişTokuşçu, &i, &j); - } - - // Bütün işlemlerin bitmesini bekliyoruz - thread_joinAll(); - - writefln("sonra: %s ve %s", i, j); -} ---- - -$(P -Ne yazık ki, yukarıdaki program büyük olasılıkla yanlış sonuç üretir. Bunun nedeni, 10 iş parçacığının $(C main()) içindeki $(C i) ve $(C j) isimli aynı değişkenlere erişmeleri ve farkında olmadan yarış halinde birbirlerinin işlerini bozmalarıdır. -) - -$(P -Yukarıdaki programdaki toplam değiş tokuş adedi 10 çarpı 10 bindir. Bu değer bir çift sayı olduğundan $(C i)'nin ve $(C j)'nin değerlerinin program sonunda yine başlangıçtaki gibi 1 ve 2 olmalarını bekleriz: -) - -$(SHELL -önce : 1 ve 2 -sonra: 1 ve 2 $(SHELL_NOTE beklenen sonuç) -) - -$(P -Program farklı zamanlarda ve ortamlarda bazen o sonucu üretebilse de aşağıdaki yanlış sonuçların çıkma olasılığı daha yüksektir: -) - -$(SHELL -önce : 1 ve 2 -sonra: 1 ve 1 $(SHELL_NOTE_WRONG yanlış sonuç) -) - -$(SHELL -önce : 1 ve 2 -sonra: 2 ve 2 $(SHELL_NOTE_WRONG yanlış sonuç) -) - -$(P -Bazı durumlarda sonuç "2 ve 1" bile çıkabilir. -) - -$(P -Bunun nedenini A ve B olarak isimlendireceğimiz yalnızca iki iş parçacığının işleyişiyle bile açıklayabiliriz. İşletim sistemi iş parçacıklarını belirsiz zamanlarda duraksatıp tekrar başlattığından bu iki iş parçacığı verileri birbirlerinden habersiz olarak aşağıda gösterildiği biçimde değiştirebilirler. -) - -$(P -$(C i)'nin ve $(C j)'nin değerlerinin sırasıyla 1 ve 2 olduğu duruma bakalım. Aynı $(C değişTokuşçu()) işlevini işlettikleri halde A ve B iş parçacıklarının yerel $(C geçici) değişkenleri farklıdır. Ayırt edebilmek için onları aşağıda geçiciA ve geçiciB olarak belirtiyorum. -) - -$(P -Her iki iş parçacığının işlettiği aynı 3 satırlık kodun zaman ilerledikçe nasıl işletildiklerini yukarıdan aşağıya doğru gösteriyorum: 1 numaralı işlem ilk işlem, 6 numaralı işlem de son işlem. Her işlemde $(C i) ve $(C j)'den hangisinin değiştiğini de sarıyla işaretliyorum: -) - -$(MONO -$(B İşlem İş parçacığı A İş parçacığı B) -───────────────────────────────────────────────────────────────────────────── - - 1: int geçici = *ikinci; (geçiciA==2) - 2: *ikinci = *birinci; (i==1, $(HILITE j==1)) - - $(I (A duraksatılmış ve B başlatılmış olsun)) - - 3: int geçici = *ikinci; (geçiciB==1) - 4: *ikinci = *birinci; (i==1, $(HILITE j==1)) - - $(I (B duraksatılmış ve A tekrar başlatılmış olsun)) - - 5: *birinci = geçici; ($(HILITE i==2), j==1) - - $(I (A duraksatılmış ve B tekrar başlatılmış olsun)) - - 6: *birinci = geçici; ($(HILITE i==1), j==1) -) - -$(P -Görüldüğü gibi, yukarıdaki gibi bir durumda hem $(C i) hem de $(C j) 1 değerini alırlar. Artık programın sonuna kadar başka değer almaları mümkün değildir. -) - -$(P -Yukarıdaki işlem sıraları bu programdaki hatayı açıklamaya yeten yalnızca bir durumdur. Onun yerine 10 iş parçacığının etkileşimlerinden oluşan çok sayıda başka karmaşık durum da gösterilebilir. -) - -$(H5 $(IX synchronized) Veri korumak için $(C synchronized)) - -$(P -Yukarıdaki hatalı durum aynı verinin birden fazla iş parçacığı tarafından serbestçe okunması ve yazılması nedeniyle oluşmaktadır. Bu tür hataları önlemenin bir yolu, belirli bir anda yalnızca tek iş parçacığı tarafından işletilmesi gereken kod bloğunu $(C synchronized) olarak işaretlemektir. Yapılacak tek değişiklik programın artık doğru sonuç üretmesi için yeterlidir: -) - ---- - foreach (i; 0 .. 10_000) { - $(HILITE synchronized {) - int geçici = *ikinci; - *ikinci = *birinci; - *birinci = geçici; - $(HILITE }) - } ---- - -$(P -Çıktısı: -) - -$(SHELL -önce : 1 ve 2 -sonra: 1 ve 2 $(SHELL_NOTE doğru sonuç) -) - -$(P -$(IX kilit) $(C synchronized), isimsiz bir kilit oluşturur ve bu kilidi belirli bir anda yalnızca tek iş parçacığına verir. Yukarıdaki kod bloğu da bu sayede belirli bir anda tek iş parçacığı tarafından işletilir ve $(C i) ve $(C j)'nin değerleri her seferinde doğru olarak değiş tokuş edilmiş olur. Değişkenler $(C foreach) döngüsünün her adımında ya "1 ve 2" ya da "2 ve 1" durumundadırlar. -) - -$(P -$(I Not: Bir iş parçacığının bir kilidin açılmasını beklemesi ve tekrar kilitlemesi masraflı bir işlemdir ve programın farkedilir derecede yavaş işlemesine neden olabilir. Bazı programlarda veri paylaşımı $(C synchronized) ile kilit kullanılmasına gerek kalmadan $(I kesintisiz işlemlerden) yararlanılarak da sağlanabilir ve program bu sayede daha hızlı işleyebilir. Bunun örneklerini biraz aşağıda göreceğiz.) -) - -$(P -Kullanacağı kilit veya kilitler $(C synchronized)'a açıkça da verilebilir. Bu, belirli bir anda birden fazla bloktan yalnızca birisinin işlemesini sağlar. -) - -$(P -Bunun bir örneğini görmek için aşağıdaki programa bakalım. Bu programda paylaşılan veriyi değiştiren iki kod bloğu bulunuyor. Bu blokları $(C shared(int)) türündeki aynı değişkenin adresi ile çağıracağız. Birisi bu değişkenin değerini arttıracak, diğeri ise azaltacak: -) - ---- -void arttırıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - *değer = *değer + 1; - } -} - -void azaltıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - *değer = *değer - 1; - } -} ---- - -$(P -$(I Not: Yukarıdaki ifadeler yerine daha kısa olan $(C ++(*değer)) ve $(C ‑‑(*değer)) ifadeleri kullanıldığında derleyici o ifadelerin $(C shared) değişkenler üzerinde işletilmelerinin emekliye ayrıldığını bildiren bir uyarı mesajı verir.) -) - -$(P -Aynı veriyi değiştirdikleri için bu iki bloğun da $(C synchronized) olarak işaretlenmeleri düşünülebilir, ancak bu yeterli olmaz. Bu bloklar farklı olduklarından her birisi farklı bir kilit ile korunacaktır ve değişkenin tek iş parçacığı tarafından değiştirilmesi yine sağlanamayacaktır: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -enum adet = 1000; - -void arttırıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - $(HILITE synchronized) { // ← bu kilit aşağıdakinden farklıdır - *değer = *değer + 1; - } - } -} - -void azaltıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - $(HILITE synchronized) { // ← bu kilit yukarıdakinden farklıdır - *değer = *değer - 1; - } - } -} - -void main() { - shared(int) ortak = 0; - - foreach (i; 0 .. 100) { - spawn(&arttırıcı, &ortak); - spawn(&azaltıcı, &ortak); - } - - thread_joinAll(); - writeln("son değeri: ", ortak); -} ---- - -$(P -Eşit sayıda arttırıcı ve azaltıcı iş parçacığı başlatılmış olduğundan $(C ortak) isimli değişkenin son değerinin sıfır olmasını bekleriz ama büyük olasılıkla sıfırdan farklı çıkar: -) - -$(SHELL -son değeri: -3325 $(SHELL_NOTE_WRONG sıfır değil) -) - -$(P -Farklı blokların aynı kilidi veya kilitleri paylaşmaları gerektiğinde kilit veya kilitler $(C synchronized)'a parantez içinde bildirilir: -) - ---- - synchronized ($(I kilit_nesnesi), $(I başka_kilit_nesnesi), ...) ---- - -$(P -D'de özel bir kilit nesnesi yoktur; herhangi bir sınıf türünün herhangi bir nesnesi kilit olarak kullanılabilir. Yukarıdaki programdaki iş parçacıklarının aynı kilidi kullanmaları için $(C main()) içinde bir nesne oluşturulabilir ve iş parçacıklarına parametre olarak o nesne gönderilebilir. Programın değişen yerlerini işaretliyorum: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -enum adet = 1000; - -$(HILITE class Kilit { -}) - -void arttırıcı(shared(int) * değer, $(HILITE shared(Kilit) kilit)) { - foreach (i; 0 .. adet) { - synchronized $(HILITE (kilit)) { - *değer = *değer + 1; - } - } -} - -void azaltıcı(shared(int) * değer, $(HILITE shared(Kilit) kilit)) { - foreach (i; 0 .. adet) { - synchronized $(HILITE (kilit)) { - *değer = *değer - 1; - } - } -} - -void main() { - $(HILITE shared(Kilit) kilit = new shared(Kilit)()); - shared(int) ortak = 0; - - foreach (i; 0 .. 100) { - spawn(&arttırıcı, &ortak, $(HILITE kilit)); - spawn(&azaltıcı, &ortak, $(HILITE kilit)); - } - - thread_joinAll(); - writeln("son değeri: ", ortak); -} ---- - -$(P -Bütün iş parçacıkları $(C main()) içinde tanımlanmış olan aynı kilidi kullandıklarından belirli bir anda bu iki $(C synchronized) bloğundan yalnızca bir tanesi işletilir ve beklenen sonuç elde edilir: -) - -$(SHELL -son değeri: 0 $(SHELL_NOTE doğru sonuç) -) - -$(P -Sınıflar da $(C synchronized) olarak tanımlanabilirler. Bunun anlamı, o türün bütün üye işlevlerinin aynı kilidi kullanacaklarıdır: -) - ---- -$(HILITE synchronized) class Sınıf { - void foo() { - // ... - } - - void bar() { - // ... - } -} ---- - -$(P -$(C synchronized) olarak işaretlenen türlerin bütün üye işlevleri nesnenin kendisini kilit olarak kullanırlar. Yukarıdaki sınıfın eşdeğeri aşağıdaki sınıftır: -) - ---- -class Sınıf { - void foo() { - synchronized (this) { - // ... - } - } - - void bar() { - synchronized (this) { - // ... - } - } -} ---- - -$(P -Birden fazla nesnenin kilitlenmesi gerektiğinde bütün nesneler aynı $(C synchronized) deyimine yazılmalıdırlar. Aksi taktirde farklı iş parçacıkları farklı nesnelerin kilitlerini ele geçirmiş olabileceklerinden sonsuza kadar birbirlerini bekleyerek takılıp kalabilirler. -) - -$(P -Bunun tanınmış bir örneği, bir banka hesabından diğerine para aktaran işlevdir. Böyle bir işlemin hatasız gerçekleşmesi için her iki banka hesabının da kilitlenmesinin gerekeceği açıktır. Bu durumda yukarıda gördüğümüz $(C synchronized) kullanımını aşağıdaki gibi uygulamak hatalı olur: -) - ---- -void paraAktar(shared(BankaHesabı) kimden, - shared(BankaHesabı) kime) { - synchronized (kimden) { $(CODE_NOTE_WRONG HATALI) - synchronized (kime) { - // ... - } - } -} ---- - -$(P -Yanlışlığın nedenini şöyle basit bir durumla açıklayabiliriz: Bir iş parçacığının A hesabından B hesabına para aktardığını, başka bir iş parçacığının da B hesabından A hesabına para aktardığını düşünelim. İşletim sisteminin iş parçacıklarını belirsiz zamanlarda duraksatması sonucunda; $(C kimden) olarak A hesabını işlemekte olan iş parçacığı A nesnesini, $(C kimden) olarak B nesnesini işlemekte olan iş parçacığı da B nesnesini kilitlemiş olabilir. Bu durumda her ikisi de diğerinin elinde tuttuğu nesneyi kilitlemeyi bekleyeceklerinden sonsuza kadar takılıp kalacaklardır. -) - -$(P -Bu sorunun çözümü $(C synchronized) deyiminde birden fazla nesne belirtmektir: -) - ---- -void paraAktar(shared(BankaHesabı) kimden, - shared(BankaHesabı) kime) { - synchronized (kimden, kime) { // ← doğru - // ... - } -} ---- - -$(P -Derleyici ya nesnelerin ikisinin birden kilitleneceğini ya da hiçbirisinin kilitlenmeyeceğini garanti eder. -) - -$(H5 $(IX shared static this) $(IX shared static ~this) $(IX this, shared static) $(IX ~this, shared static) Tek ilkleme için $(C shared static this()) ve tek sonlandırma için $(C shared static ~this())) - -$(P -$(C static this())'in modül değişkenlerini ilklerken kullanıldığını görmüştük. D'de veri iş parçacıklarına özel olduğundan $(C static this()) her iş parçacığı için ayrıca işletilir: -) - ---- -import std.stdio; -import std.concurrency; -import core.thread; - -static this() { - writeln("static this() işletiliyor"); -} - -void işçi() { -} - -void main() { - spawn(&işçi); - - thread_joinAll(); -} ---- - -$(P -Yukarıdaki programdaki $(C static this()) bir kere ana iş parçacığında bir kere de $(C spawn()) ile başlatılan iş parçacığında işletilir: -) - -$(SHELL -static this() işletiliyor -static this() işletiliyor -) - -$(P -Bu durum $(C shared) olarak işaretlenmiş olan modül değişkenleri ($(C immutable) dahil) için bir sorun oluşturur çünkü aynı değişkenin birden fazla ilklenmesi $(I yarış hali) nedeniyle özellikle eş zamanlı programlamada yanlış olacaktır. Bunun çözümü $(C shared static this()) bloklarıdır. Bu bloklar bütün programda tek kere işletilirler: -) - ---- -int a; // her iş parçacığına özel -immutable int b; // bütün programda paylaşılan - -static this() { - writeln("İş parçacığı değişkeni ilkleniyor; adresi: ", &a); - a = 42; -} - -$(HILITE shared) static this() { - writeln("Program değişkeni ilkleniyor; adresi: ", &b); - b = 43; -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Program değişkeni ilkleniyor; adresi: 6B0140 $(SHELL_NOTE programda tek) -İş parçacığı değişkeni ilkleniyor; adresi: 7F80E22667D0 -İş parçacığı değişkeni ilkleniyor; adresi: 7F80E2165670 -) - -$(P -Benzer biçimde, $(C shared static ~this()) de bütün programda tek kere işletilmesi gereken sonlandırma işlemleri içindir. -) - -$(H5 $(IX kesintisiz işlemler) Kesintisiz işlemler) - -$(P -İşlemlerin başka iş parçacıkları araya girmeden kesintisiz olarak işletilmesini sağlamanın bir yolu; mikro işlemci, derleyici, veya işletim sistemi tarafından sunulmuş olan kesintisiz işlemlerden yararlanmaktır. -) - -$(P -Phobos bu olanakları $(C core.atomic) modülünde sunar. Bu bölümde bu olanaklardan yalnızca ikisini göstereceğim: -) - -$(H6 $(IX atomicOp, core.atomic) $(C atomicOp)) - -$(P -Bu işlev, şablon parametresi olarak belirtilen işleci parametrelerine uygular. Şablon parametresinin $(C +), $(C +=), vs. gibi bir $(I ikili işleç) olması şarttır: -) - ---- -import core.atomic; - -// ... - - atomicOp!"+="(*değer, 1); // kesintisiz ---- - -$(P -Yukarıdaki satır, aşağıdakinin kesintiye uğratılmadan işletilmesinin eşdeğeridir: -) - ---- - *değer += 1; // kesintili ---- - -$(P -Dolayısıyla, eğer kesintiye uğratılmadan işletilmesi gereken işlem bir ikili işleç ise $(C synchronized) bloğuna gerek kalmaz ve kod daha hızlı işleyebilir. Yukarıdaki $(C arttırıcı) ve $(C azaltıcı) işlevlerinin aşağıdaki eşdeğerleri de programın doğru çalışmasını sağlar. Bu çözümde $(C Kilit) türüne de gerek yoktur: -) - ---- -import core.atomic; - -//... - -void arttırıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - $(HILITE atomicOp!"+="(*değer, 1)); - } -} - -void azaltıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - $(HILITE atomicOp!"-="(*değer, 1)); - } -} ---- - -$(P -$(C atomicOp) başka ikili işleçlerle de kullanılabilir. -) - -$(H6 $(IX cas, core.atomic) $(C cas)) - -$(P -Bu işlevin ismi "karşılaştır ve değiş tokuş et" anlamına gelen İngilizce $(I compare and swap)'ın kısasıdır. İşleyişi, $(I değişkenin hâlâ belirli bir değere eşit ise değiştirilmesi) temeline dayanır. Önce değişkenin mevcut değeri okunur ve o değer $(C cas)'a yeni değerle birlikte verilir: -) - ---- - bool değişti_mi = cas(değişken_adresi, mevcutDeğer, yeniDeğer); ---- - -$(P -Değişkenin mevcut değerinin $(C cas)'ın işleyişi sırasında aynı kalmış olması başka bir iş parçacığının araya girmediğinin göstergesidir. Bu durumda $(C cas) değişkene yeni değerini atar ve değişimin başarıyla gerçekleştiğini belirtmek için $(C true) döndürür. Değişkenin eski değerine eşit olmadığını gördüğünde $(C cas) işleyişine devam etmez ve $(C false) döndürür. -) - -$(P -Aşağıdaki işlevler $(C cas) başarısız olduğunda (yani, dönüş değeri $(C false) olduğunda) mevcut değeri tekrar okumakta ve işlemi hemen tekrar denemekteler. Bu çağrıların anlamı $(I değeri $(C mevcutDeğer)'e eşit ise yeni değerle değiştir) diye açıklanabilir: -) - ---- -void arttırıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - int mevcutDeğer; - - do { - mevcutDeğer = *değer; - } while (!$(HILITE cas(değer, mevcutDeğer, mevcutDeğer + 1))); - } -} - -void azaltıcı(shared(int) * değer) { - foreach (i; 0 .. adet) { - int mevcutDeğer; - - do { - mevcutDeğer = *değer; - } while (!$(HILITE cas(değer, mevcutDeğer, mevcutDeğer - 1))); - } -} ---- - -$(P -Yukarıdaki işlevler de $(C synchronized) bloğuna gerek kalmadan doğru sonuç üretirler. -) - -$(P -$(C core.atomic) modülünün olanakları çoğu durumda $(C synchronized) bloklarından kat kat hızlıdır. Probleme uygun olduğu sürece öncelikle bu modülden yararlanmanızı öneririm. -) - -$(P -Bu olanaklar bu kitabın konusu dışında kalan $(I kilitsiz veri yapılarının) gerçekleştirilmelerinde de kullanılırlar. -) - -$(P -Klasik eş zamanlı programlamada çok karşılaşılan başka olanakları da $(C core.sync) pakedinin modüllerinde bulabilirsiniz: -) - -$(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 Özet) - -$(UL - -$(LI İş parçacıklarının birbirlerine bağlı olmadıkları durumlarda iki önceki bölümün konusu olan $(C std.parallelism) modülünün sunduğu $(I koşut programlamayı) yeğleyin. Ancak iş parçacıkları birbirlerine bağlı olduklarında $(C std.concurrency)'nin sunduğu $(I eş zamanlı programlamayı) düşünün.) - -$(LI Eş zamanlı programlama gerçekten gerektiğinde bir önceki bölümün konusu olan mesajlaşmayı yeğleyin çünkü veri paylaşımı çeşitli program hatalarına açıktır.) - -$(LI Yalnızca $(C shared) veriler paylaşılabilir; $(C immutable) otomatik olarak $(C shared)'dir.) - -$(LI $(C __gshared) C ve C++ anlamında veri paylaşımı sağlar.) - -$(LI $(C synchronized) belirli bir kod bloğunun belirli bir anda tek iş parçacığı tarafından işletilmesini sağlar.) - -$(LI Bir sınıf türü $(C synchronized) olarak tanımlandığında belirli bir nesnesi üzerinde belirli bir anda üye işlevlerinden yalnızca birisi işletilir.) - -$(LI $(C static this()) her iş parçacığı için ayrıca işletilir; $(C shared static this()) bütün programda tek kere işletilir.) - -$(LI $(C core.atomic) modülünün olanakları $(C synchronized)'dan çok daha hızlı işleyen programlar üretir; ancak, her duruma uygun değildir.) - -$(LI $(C core.sync) pakedi başka eş zamanlı programlama olanakları içerir.) - -) - -macros: - SUBTITLE=Veri Paylaşarak Eş Zamanlı Programlama - - DESCRIPTION=Programın birden fazla iş parçacığı üzerinde işletilmesi ve iş parçacıklarının veri paylaşımı yoluyla iletişimleri (concurrency) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial eş zamanlı programlama iş parçacığı concurrency immutable shared synchronized veri paylaşımı - -SOZLER= -$(cokuzlu) -$(degismez) -$(emekli) -$(es_zamanli) -$(gorev) -$(is_parcacigi) -$(kesintisiz_islem) -$(kilitsiz_veri_yapisi) -$(kosut_islemler) -$(yaris_hali) diff --git a/ddili/src/ders/d/esleme_tablolari.cozum.d b/ddili/src/ders/d/esleme_tablolari.cozum.d deleted file mode 100644 index e5c0ab6..0000000 --- a/ddili/src/ders/d/esleme_tablolari.cozum.d +++ /dev/null @@ -1,120 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Eşleme Tabloları) - -$(OL - -$(LI - -$(UL - -$(LI -Eşleme tablosunun $(C .keys) niteliği, bütün indeksleri içeren bir dizi döndürür. Bu dizinin elemanlarını bir $(C for) döngüsünde gezersek, ve her birisi için eşleme tablosunun $(C .remove) niteliğini kullanırsak bütün elemanlar eşleme tablosundan silinmiş olurlar ve sonuçta tablo boşalır: - ---- -import std.stdio; - -void main() { - string[int] isimleSayılar = - [ - 1 : "bir", - 10 : "on", - 100 : "yüz", - ]; - - writeln("Başlangıçtaki tablo büyüklüğü : ", - isimleSayılar.length); - - int[] indeksler = isimleSayılar.keys; - - /* foreach for'a benzer ama ondan daha kullanışlıdır. - * foreach'i bir sonraki bölümde göreceğiz. */ - foreach (indeks; indeksler) { - writeln(indeks, " indeksinin elemanını siliyorum"); - isimleSayılar.remove(indeks); - } - - writeln("Sildikten sonraki tablo büyüklüğü: ", - isimleSayılar.length); -} ---- - -$(P -O çözüm özellikle büyük tablolarda yavaş olacaktır. Aşağıdaki çözümlerin ikisi de tabloyu bir seferde boşaltırlar. -) - -) - -$(LI -Başka bir çözüm, eşleme tablosuna kendisiyle aynı türden boş bir tablo atamaktır: - ---- - string[int] boşTablo; - isimleSayılar = boşTablo; ---- - -) - -$(LI -Her türün $(C .init) niteliği, o türün $(I ilk değeri) anlamındadır. Bir eşleme tablosunun ilk değeri de boş tablo olduğu için, bir önceki çözümün de eşdeğeri olan şunu kullanabiliriz: - ---- - isimleSayılar = isimleSayılar.init; ---- - -) - -) - -) - -$(LI -Burada öğrenci ismine karşılık birden fazla not tutmak istiyoruz. Yani bir $(I dizi) not... Eğer eşleme tablomuzu $(C string)'den $(C int[]) türüne eşleyecek şekilde tanımlarsak, isimle eriştiğimiz eleman, bir $(C int) dizisi olur. O dizinin sonuna not ekleyerek de amacımıza erişiriz: - ---- -import std.stdio; - -void main() { - /* Eşleme tablosunun indeks türü string; eleman türü ise - * int[], yani bir int dizisi. Belirginleştirmek için - * aralarında boşlukla tanımlıyorum: */ - int[] [string] notlar; - - /* Artık "emre" indeksine karşılık gelen elemanı bir int - * dizisi gibi kullanabiliriz. */ - - // Diziye notlar eklemek: - notlar["emre"] ~= 90; - notlar["emre"] ~= 85; - - // Diziyi yazdırmak - writeln(notlar["emre"]); -} ---- - -$(P -Notları teker teker eklemek yerine hepsini bir dizi olarak da atayabiliriz: -) - ---- -import std.stdio; - -void main() { - int[][string] notlar; - - notlar["emre"] = [ 90, 85, 95 ]; - - writeln(notlar["emre"]); -} ---- - -) - -) - -Macros: - SUBTITLE=Eşleme Tabloları Çözümleri - - DESCRIPTION=D'nin dil olanaklarından olan eşleme tabloları (hash tables) dersinin problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial eşleme tablosu hash table çözüm diff --git a/ddili/src/ders/d/esleme_tablolari.d b/ddili/src/ders/d/esleme_tablolari.d deleted file mode 100644 index 3f70154..0000000 --- a/ddili/src/ders/d/esleme_tablolari.d +++ /dev/null @@ -1,330 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX eşleme tablosu) Eşleme Tabloları) - -$(P -Eşleme tabloları üst düzey dillerin hepsinde bulunan veri yapılarıdır. Onları program içine gömülen minik veri tabanları olarak düşünülebilirsiniz. Programlarda çok kullanılan ve çok hızlı veri yapılarıdır. -) - -$(P -$(LINK2 /ders/d/diziler.html, Dizileri) "elemanları yan yana duran topluluk" olarak tanımlamış ve elemanlarına $(I indeksle) erişildiğini görmüştük. Örneğin haftanın günlerinin isimlerini tutan bir dizi şöyle tanımlanabilir: -) - ---- - string[] günİsimleri = - [ "Pazartesi", "Salı", "Çarşamba", "Perşembe", - "Cuma", "Cumartesi", "Pazar" ]; ---- - -$(P -Belirli bir günün ismi o diziyi kullanarak şöyle yazdırılabilir: -) - ---- - writeln(günİsimleri[1]); // "Salı" yazar ---- - -$(P -Dizilerin elemanları sıra numarasıyla (indeksle) eriştiriyor olmaları, onların indekslerle elemanları $(I eşleştirdikleri) olarak açıklanabilir. -) - -$(P -Ancak, diziler indeks türü olarak yalnızca tamsayı türler kullanabilirler. Örneğin "Salı" dizgisi bulunduğunda onun haftanın 1 numaralı günü olduğunu söyleyemezler çünkü "Salı" gibi bir dizgiyi indeks olarak kullanamazlar. -) - -$(P -Eşleme tablolarının kullanışlılığı işte bu gibi durumlarda ortaya çıkar. Eşleme tabloları elemanlara yalnızca numara ile değil, herhangi bir türle erişilen veri yapılarıdır. Görevleri, herhangi bir indeks türündeki bir değeri herhangi başka bir türdeki değer ile $(I eşlemektir). Eşleme tabloları elemanlarını $(I indeks-değer) çiftleri olarak tutarlar. Aşağıda $(I eleman) yazdığım her yerde bir indeks-değer çiftini kastedeceğim. -) - -$(P -Eşleme tabloları arka planda $(I hash table) veri yapısını kullandıkları için algoritma karmaşıklığı açısından dizilerden geri kalmazlar: son derece hızlı topluluklardır. Bunun anlamı, içlerindeki eleman sayısından bağımsız olarak, hemen her zaman için sabit zamanda erişim sağlamalarıdır. -) - -$(P -Bu kadar hızlı çalışmalarının bedeli, içlerindeki elemanların sıraları konusunda bir şey bilinemiyor olmasıdır. Elemanların ne dizilerdeki gibi $(I yan yana) olduklarını, ne de örneğin $(I küçükten büyüğe doğru) sıralandıklarını söyleyebiliriz. -) - -$(P -Diziler indeks değerleri için yer harcamazlar. Dizi elemanları bellekte yan yana durduklarından her elemanın indeks değeri onun başlangıçtan kaç eleman ötede olduğudur. -) - -$(P -Öte yandan, eşleme tabloları hem indeksleri hem de değerleri saklamak zorundadırlar. Bu fark eşleme tablolarının bellekte daha fazla yer tutmalarına neden olsa da, onların $(I seyrek) indeks değerleri kullanabilmelerini de sağlar. Örneğin, 0 ve 999 gibi iki değer için diziler 1000 eleman saklamak zorunda oldukları halde eşleme tabloları yalnızca iki eleman saklarlar. -) - -$(H5 Tanımlama) - -$(P -Eşleme tablosu tanımı dizi tanımına çok benzer. Tek farkı, köşeli parantezler içine dizinin uzunluğu yerine dizinin indeks türünün gelmesidir. Söz dizimi aşağıdaki gibidir: -) - ---- - $(I değer_türü)[$(I indeks_türü)] tablo_ismi; ---- - -$(P -Örneğin türü $(C string) olan gün isminden türü $(C int) olan gün sıra numarasına eşleyen bir eşleme tablosu şöyle tanımlanır: -) - ---- - int[string] günSıraları; ---- - -$(P -O tanım gün ismine karşılık olarak gün numarasını veren, yani yukarıdaki $(C günİsimleri) dizisinin tersi olarak işleyen bir tablo olarak kullanılabilir. Bunu aşağıdaki kod örneklerinde göreceğiz. -) - -$(P -Eşleme tablolarının en kullanışlı taraflarından birisi, indeks ve değer türü olarak daha sonra öğreneceğimiz $(I yapı) ve $(I sınıf) türleri de dahil olmak üzere her türün kullanılabilmesidir. -) - -$(P -Dinamik dizilerde olduğu gibi, eşleme tablolarının uzunlukları da tanımlandıkları zaman belirlenmez. Tablo otomatik olarak büyür. -) - -$(P -$(I Not: Baştan elemansız olarak tanımlanan bir eşleme tablosu boş değil, $(LINK2 /ders/d/null_ve_is.html, $(C null))'dır. Bu ayrımın $(LINK2 /ders/d/islev_parametreleri.html, işlevlere parametre olarak geçirilen eşleme tabloları) açısından büyük önemi vardır. Bu kavramları ilerideki bölümlerde göreceğiz.) -) - -$(H5 Tabloya eleman ekleme) - -$(P -Belirli bir indeks değerine karşılık gelen değer atama işleci ile belirlenir: -) - ---- - günSıraları["Pazartesi"] $(HILITE =) 0; // "Pazartesi"yi 0 ile eşler - günSıraları["Salı"] $(HILITE =) 1; // "Salı"yı 1 ile eşler ---- - -$(P -Eşleme ilişkisi tablonun otomatik olarak büyümesi için de yeterlidir. Yukarıdaki işlemler sonucunda tabloda artık iki eleman vardır. Bunu bütün tabloyu yazdırarak görebiliriz: -) - ---- - writeln(günSıraları); ---- - -$(P -Çıktısı, "Pazartesi" ve "Salı" indekslerine karşılık 0 ve 1 değerlerinin bulunduğunu gösterir: -) - -$(SHELL -["Pazartesi":0, "Salı":1] -) - -$(P -Her indeks değerine karşılık tek değer bulunabilir. Bu yüzden, var olan bir indekse karşılık yeni bir değer atandığında tablo büyümez, var olan elemanın değeri değişir: -) - ---- - günSıraları["Salı"] = 222; - writeln(günSıraları); ---- - -$(P -Çıktısı: -) - -$(SHELL -["Pazartesi":0, "Salı":222] -) - -$(H5 İlkleme) - -$(P -$(IX :, eşleme tablosu) Gün sıraları kavramında olduğu gibi, eşleme bilgisi bazen tablo kurulduğu sırada bilinir. Eşlemeleri teker teker atayarak kurmak yerine bu bilgiyi tabloyu tanımladığımız zaman da verebiliriz. Eşleme tabloları da dizi söz diziminde olduğu gibi ilklenir. Farklı olarak, indeks ile değeri arasına $(C :) karakteri yazılır: -) - ---- - int[string] günSıraları = - [ "Pazartesi" : 0, "Salı" : 1, "Çarşamba" : 2, - "Perşembe" : 3, "Cuma" : 4, "Cumartesi": 5, - "Pazar" : 6 ]; - - writeln(günSıraları["Salı"]); // "1" yazar ---- - -$(H5 $(IX remove) Tablodan eleman çıkartma) - -$(P -Elemanlar, buradaki kullanımında "çıkart, at" anlamına gelen $(C .remove) ile çıkartılırlar: -) - ---- - günSıraları.remove("Salı"); - writeln(günSıraları["Salı"]); // ← çalışma zamanı HATASI ---- - -$(P -Son satır, tabloda artık bulunmayan bir elemana erişmeye çalıştığı için çalışma zamanında bir hata atılmasına ve o hatanın yakalanmaması durumunda da programın sonlanmasına neden olur. Hata düzeneğini $(LINK2 /ders/d/hatalar.html, ilerideki bir bölümde) göreceğiz. -) - -$(P -$(IX clear) Elemanların hepsini birden çıkartmak gerektiğinde $(C .clear) kullanılır: -) - ---- - günSıraları.clear; // Tablo boşalır ---- - -$(H5 $(IX in, eşleme tablosu) Eleman sorgulama) - -$(P -Tabloda bulunmayan bir elemana erişmek bir hata atılmasına neden olduğundan, sorgulamak için $(C in) işleci kullanılır. Bu kullanım "içinde var mı?" sorusunu yanıtlar: -) - ---- - if ("mor" $(HILITE in) renkKodları) { - // evet, renkKodları'nda "mor" indeksli değer var - - } else { - // hayır, yok - } ---- - -$(P -Bazen elemanın bulunup bulunmadığını açıkça sorgulamak yerine eleman bulunmadığı durumda standart bir değer kullanmak istenebilir. Örneğin, $(C renkKodları) tablosunda bulunmayan renklere karşılık -1 gibi bir değer kabul edilmiş olabilir. Bu gibi durumlarda $(C .get()) kullanılır. Tabloda varsa mevcut değeri, yoksa $(C .get())'e verilen ikinci parametrenin değerini döndürür: -) - ---- - int[string] renkKodları = [ "mavi" : 10, "yeşil" : 20 ]; - writeln(renkKodları.get("mor", $(HILITE -1))); ---- - -$(P -Tabloda $(STRING "mor") indeksli eleman bulunmadığından $(C .get()) ikinci parametresinin değeri olan -1'i döndürür: -) - -$(SHELL --1 -) - -$(H5 Nitelikler) - -$(UL - -$(LI $(IX .length) $(C .length) eleman sayısını verir.) - -$(LI $(IX .keys) $(C .keys) bütün indeksleri dinamik dizi olarak verir.) - -$(LI $(IX .byKey) $(C .byKey) bütün indeksleri bir aralık olarak sunar; bunun kullanımını bir sonraki bölümde göreceğiz.) - -$(LI $(IX .values) $(C .values) bütün eleman değerlerini dinamik dizi olarak verir.) - -$(LI $(IX .byValue) $(C .byValue) bütün eleman değerlerini bir aralık olarak sunar; bunun kullanımını bir sonraki bölümde göreceğiz.) - -$(LI $(IX .byKeyValue) $(C .byKeyValue) bütün indeksleri ve değerleri bir aralık olarak sunar.) - -$(LI $(IX .rehash) $(C .rehash) ancak gerçekten gereken durumlarda tablonun daha etkin çalışmasını sağlayabilir. Örneğin, tabloya çok sayıda eleman eklendikten sonra ve daha tablonun asıl kullanımı başlamadan önce bu nitelik çağrılırsa tablonun erişim işlemleri bazı programlarda daha hızlı olabilir.) - -$(LI $(IX .sizeof, eşleme tablosu) $(C .sizeof) tablonun $(I referansının) büyüklüğüdür (tablodaki eleman adediyle ilgisi yoktur ve her tablo için aynıdır).) - -$(LI $(IX .get) $(C .get) varsa elemanın değerini, yoksa ikinci parametresinin değerini döndürür.) - -$(LI $(IX .remove) $(C .remove) belirtilen indeksli elemanı tablodan çıkartır.) - -$(LI $(IX .clear) $(C .clear) tabloyu boşaltır.) - -) - -$(H5 Örnek) - -$(P -Girilen rengin İngilizcesini veren bir program şöyle yazılabilir: -) - ---- -import std.stdio; -import std.string; - -void main() { - string[string] renkler = [ "siyah" : "black", - "beyaz" : "white", - "kırmızı" : "red", - "yeşil" : "green", - "mavi" : "blue" ]; - - writeln("Ben bu ", renkler.length, - " rengin İngilizcelerini öğrendim: ", - renkler.keys); - - write("Haydi sorun: "); - string türkçesi = strip(readln()); - - if (türkçesi in renkler) { - writefln("İngilizcesi \"%s\"", renkler[türkçesi]); - - } else { - writeln("Onu bilmiyorum."); - } -} ---- - -$(PROBLEM_COK - -$(PROBLEM -Bir eşleme tablosunu bütünüyle boşaltmak için $(C .clear)'den başka ne yöntemler düşünülebilir? (Bunun en doğal yolu $(C .clear)'dir.) En az üç yöntem düşünülebilir: - -$(UL - -$(LI Elemanları bir döngü içinde teker teker tablodan çıkartmak) - -$(LI Boş bir eşleme tablosu atamak) - -$(LI Bir öncekine benzer şekilde, tablonun $(C .init) niteliğini atamak - -$(P -$(IX .init, değişkeni sıfırlamak) $(I Not: Her türün $(C .init) niteliği, o türün ilk değeri olarak kullanılan değerdir:) -) - ---- - sayı = int.init; // int için 0 olur ---- -) - -) - -) - -$(PROBLEM -Dizilerde olduğu gibi, eşleme tablolarında da her indekse karşılık tek değer bulunabilir. Bu, bazı durumlarda kısıtlayıcıdır. - -$(P -Her öğrenci için birden fazla not tutmak istiyor olalım. Örneğin "emre" için 90, 85, 95, vs. notlarını barındırmak isteyelim. -) - -$(P -Bir eşleme tablosu kullanmak, notlara $(C notlar["emre"]) şeklinde öğrencinin ismiyle erişme konusunda yardımcı olur. Ancak, notları tabloya aşağıdaki şekilde yerleştirmek işe yaramaz: -) ---- - int[string] notlar; - notlar["emre"] = 90; - notlar["emre"] = 85; // ← Olmaz: öncekinin üstüne yazar ---- - -$(P -Ne yapabilirsiniz? Her öğrenci için birden fazla not tutabilen bir eşleme tablosu tanımlayın. -) - -) - -) - -Macros: - SUBTITLE=Eşleme Tabloları - - DESCRIPTION=D'nin dil olanaklarından olan eşleme tablolarının (hash tables) tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial eşleme tablosu hash table - -SOZLER= -$(dizi) -$(eleman) -$(esleme_tablosu) -$(ilklemek) -$(indeks) -$(islec) -$(referans) -$(topluluk) -$(ust_duzey) diff --git a/ddili/src/ders/d/etiketler.d b/ddili/src/ders/d/etiketler.d deleted file mode 100644 index f00e4c1..0000000 --- a/ddili/src/ders/d/etiketler.d +++ /dev/null @@ -1,268 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX etiket) $(IX goto) Etiketler ve $(CH4 goto)) - -$(P -$(IX :, etiket) Etiketler kod satırlarına isimler vermeye ve program akışını bu isimli satırlara yöneltmeye yararlar. -) - -$(P -Etiketin isminden ve $(C :) karakterinden oluşurlar: -) - ---- -bitiş: // ← bir etiket ---- - -$(P -Yukarıdaki etiket, tanımlandığı satıra $(I bitiş) ismini verir. -) - -$(P -$(I Not: Aslında etiketler herhangi iki deyimin arasında da bulunabilirler ve bulundukları o noktayı isimlendirmiş olurlar. Ancak bu kullanım yaygın değildir:) -) - ---- - birİfade(); $(HILITE bitiş:) başkaİfade(); ---- - -$(H5 $(C goto)) - -$(P -İngilizce'de "git" anlamına gelen $(C goto), program akışını ismi belirtilen satıra yönlendirir: -) - ---- -void birİşlev(bool koşul) { - writeln("birinci"); - - if (koşul) { - $(HILITE goto) bitiş; - } - - writeln("ikinci"); - -bitiş: - - writeln("üçüncü"); -} ---- - -$(P -Yukarıdaki işlev, $(C koşul)'un $(C true) olduğu durumlarda doğrudan $(C bitiş) isimli satıra gider, ve "ikinci" yazdırılmaz: -) - -$(SHELL_SMALL -birinci -üçüncü -) - -$(P -Etiketler ve $(C goto) D'ye C'den geçmiştir. $(C goto), yapısal programlamaya aykırı olduğu için C'de bile kaçınılması önerilen bir olanaktır. Doğrudan belirli satırlara yönlendiren $(C goto)'lar yerine $(C while), $(C for), ve diğer yapısal deyimlerin kullanılması önerilir. -) - -$(P -Örneğin yukarıdaki kodun eşdeğeri, şimdiye kadar çoğu kodda gördüğümüz gibi, $(C goto) kullanmadan şöyle yazılabilir: -) - ---- -void birİşlev(bool koşul) { - writeln("birinci"); - - if (!koşul) { - writeln("ikinci"); - } - - writeln("üçüncü"); -} ---- - -$(P -Buna rağmen $(C goto)'nun C dilinde iki tane geçerli kullanımı vardır. Bu kullanımların ikisi de D'de gereksizdir. -) - -$(H6 D'de gerekmeyen, sonlandırıcı bölge) - -$(P -$(C goto)'nun C'deki geçerli bir kullanımı, işlevlerin sonlarına yazılan ve o işlevde ayrılmış olan kaynakların geri verilmesi gibi işlemleri içeren sonlandırıcı bölgedir: -) - -$(C_CODE -// --- C kodu --- - -int birIslev() { - // ... - - if (hata) { - goto bitis; - } - - // ... - -bitis: - $(COMMENT // ... sonlandirma islemleri buraya yazilir ...) - - return hata; -} -) - -$(P -D'de kaynak yönetimi için başka olanaklar bulunduğu için bu kullanım D'de gereksizdir. D'de sonlandırma işlemleri; çöp toplayıcı, sonlandırıcı işlevler, hata atma düzeneğinin $(C catch) ve $(C finally) blokları, $(C scope()) deyimleri, vs. gibi olanaklarla sağlanır. -) - -$(P $(I Not: Bu kullanıma C++'ta da gerek yoktur.) -) - -$(H6 D'de gerekmeyen, iç içe döngülerde kullanımı) - -$(P -$(C goto)'nun C'deki diğer geçerli kullanımı, iç içe döngülerin daha dışta olanlarını etkilemektir. -) - -$(P -Döngüyü kırmak için kullanılan $(C break), ve döngüyü hemen ilerletmek için kullanılan $(C continue), yalnızca en içteki döngüyü etkiler. C'de ve C++'ta dıştaki döngüyü kırmanın bir yolu, döngüden sonraki bir etikete gitmektir; dıştaki döngüyü ilerletmenin bir yolu da, onun hemen içindeki bir etikete gitmektir: -) - -$(C_CODE -// --- C kodu --- - - while (birKosul) { - - while (baskaKosul) { - - $(COMMENT // yalnizca icteki donguyu etkiler) - continue; - - $(COMMENT // yalnizca icteki donguyu etkiler) - break; - - $(COMMENT // distaki icin 'continue' gibi calisir) - goto distakiniIlerlet; - - $(COMMENT // distaki icin 'break' gibi calisir) - goto distakindenCik; - } - - distakiniIlerlet: - ; - } -distakindenCik: -) - -$(P $(I Not: Bu kullanıma C++ programlarında da rastlanabilir.) -) - -$(P -Aynı durum iç içe bulunan $(C switch) deyimlerinde de vardır; $(C break) yalnızca içteki $(C switch)'i etkilediğinden dıştakinden de çıkmak için $(C goto) kullanılabilir. -) - -$(P -D'de $(C goto)'nun bu kullanımına da gerek yoktur. Onun yerine biraz aşağıda göstereceğim döngü etiketleri kullanılır. -) - -$(H6 $(C goto)'nun kurucu işlevleri atlama sorunu) - -$(P -Kurucu işlevler nesnelerin kuruldukları satırlarda çağrılırlar. Bunun nedenlerinden birisi, nesnenin kurulması için gereken bilginin henüz mevcut olmaması olabilir. Bir başka neden, belki de hiç kullanılmayacak olan bir nesneyi kurmak için gereksizce zaman ve kaynak harcamamaktır. -) - -$(P -Nesnelerin kuruldukları satırlar $(C goto) ile atlandığında, henüz kurulmadıklarından hatalı sonuçlar doğuran nesnelerle karşılaşılabilir: -) - ---- - if (koşul) { - goto birEtiket; // kurucu işlevi atlar - } - - auto nesne = Yapı(42); // nesnenin kurulduğu satır - -birEtiket: - - nesne.birİşlem(); // HATA: belki de hazır olmayan nesne ---- - -$(P -Derleyici bu hatayı önler: -) - -$(SHELL -Error: goto skips declaration of variable deneme.main.s -) - -$(H5 $(IX döngü etiketi) Döngü etiketleri) - -$(P -D'de döngülerden hemen önce etiketler tanımlanabilir. $(C continue) ve $(C break) anahtar sözcüklerinde de etiket belirtilebilir ve o döngülerin etkilenmeleri sağlanabilir: -) - ---- -$(HILITE dışDöngü:) - while (birKoşul) { - - while (başkaKoşul) { - - // içteki döngüyü ilerletir - continue; - - // içteki döngüden çıkar - break; - - // dıştaki döngüyü ilerletir - continue $(HILITE dışDöngü); - - // dıştaki döngüden çıkar - break $(HILITE dışDöngü); - } - } ---- - -$(P -Aynısı $(C switch) deyimleri için de geçerlidir. $(C break) deyimlerinin dıştaki bir $(C switch)'i etkilemesi için o $(C switch) deyiminden önce de etiket tanımlanabilir. -) - -$(H5 $(C case) bölümlerinde kullanımı) - -$(P -$(C goto)'nun $(C case) bölümlerinde nasıl kullanıldıklarını $(LINK2 /ders/d/switch_case.html, $(C switch) ve $(C case) bölümünde) görmüştük: -) - -$(UL - -$(LI $(IX goto case) $(IX case, goto) $(C goto case), bir sonraki $(C case)'e atlanmasını sağlar.) - -$(LI $(IX goto default) $(IX default, goto) $(C goto default), $(C default) bölümüne atlanmasını sağlar.) - -$(LI $(C goto case $(I ifade)), ifadeye uyan $(C case)'e atlanmasını sağlar.) - -) - -$(H5 Özet) - -$(UL - -$(LI -$(C goto)'nun riskli kullanımlarına D'de gerek yoktur. -) - -$(LI -İç içe döngülerden veya $(C switch) deyimlerinden hangisinin etkileneceğini belirtmek için $(C break) ve $(C continue) deyimlerinde etiket kullanılabilir. -) - -$(LI -$(C case) bölümlerindeki $(C goto)'lar diğer $(C case) ve $(C default) bölümlerine atlanmasını sağlarlar. -) - -) - -Macros: - SUBTITLE=Etiketler - - DESCRIPTION=D'nin kod satırlarına isimler vermeye yarayan etiketleri, ve program akışını o satırlara yönlendiren goto anahtar sözcüğü, ve goto'nun kullanılmasına D'de gerek olmadığının gösterilmesi - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial etiket label goto while for foreach döngü break continue - -SOZLER= -$(etiket) -$(kurucu_islev) -$(sonlandirici_islev) diff --git a/ddili/src/ders/d/fiberler.d b/ddili/src/ders/d/fiberler.d deleted file mode 100644 index 6e0dacc..0000000 --- a/ddili/src/ders/d/fiberler.d +++ /dev/null @@ -1,1135 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX fiber) Fiberler) - -$(P -$(IX coroutine) $(IX yeşil iş parçacığı) $(IX iş parçacığı, yeşil) Fiber, tek iş parçacığının birden fazla görev yürütmesini sağlayan bir $(I işlem birimidir). Koşut işlemlerde ve eş zamanlı programlamada normalde yararlanılan iş parçacıklarıyla karşılaştırıldığında bir fiberin duraksatılması ve tekrar başlatılması çok daha hızlıdır. Fiberler $(I ortak işlevlere) $(ASIL coroutines) ve $(I yeşil iş parçacıklarına) $(ASIL green threads) çok benzerler ve bu terimler bazen aynı anlamda kullanılır. -) - -$(P -Fiberler temelde iş parçacıklarının birden fazla çağrı yığıtı kullanmalarını sağlarlar. Bu yüzden, fiberlerin yararını tam olarak görebilmek için önce $(I çağrı yığıtının) getirdiği kolaylığı anlamak gerekir. -) - -$(H5 $(IX çağrı yığıtı) $(IX program yığıtı) Çağrı yığıtı) - -$(P -$(IX yerel durum) Bir işlevin parametreleri, $(C static) olmayan yerel değişkenleri, dönüş değeri, geçici ifadeleri, ve işletilmesi sırasında gereken başka her türlü bilgi o işlevin $(I yerel durumudur) $(ASIL local state). Yerel durumu oluşturan değişkenler için kullanılan alan her işlev çağrısında otomatik olarak ayrılır ve bu değişkenler otomatik olarak ilklenirler. -) - -$(P -$(IX yığıt çerçevesi) Her çağrı için ayrılan bu alan o çağrının $(I çerçevesi) olarak adlandırılır. İşlevler başka işlevleri çağırdıkça bu çerçeveler kavramsal olarak yığıt biçiminde $(I üst üste) yerleştirilirler. Belirli bir andaki bütün etkin işlev çağrılarının çerçevelerinden oluşan alana o işlevleri işletmekte olan iş parçacığının $(I çağrı yığıtı) denir. -) - -$(P -Örneğin, aşağıdaki programın ana iş parçacığında $(C main)'in $(C foo)'yu çağırmasının ardından $(C foo)'nun da $(C bar)'ı çağırdığı durumda toplam üç etkin işlev çağrısı vardır: -) - ---- -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 parametre) { - string[] dizi; - // ... -} ---- - -$(P -O çağrılar sonucunda $(C bar)'ın işletilmesi sırasında çağrı yığıtı üç çerçeveden oluşur: -) - -$(MONO -Çağrı yığıtı işlev çağrıları -derinleştikçe yukarıya doğru büyür. - ▲ ▲ - │ │ -Çağrı yığıtının tepesi → ┌───────────────┐ - │ int parametre │ ← bar'ın çerçevesi - │ string[] dizi │ - ├───────────────┤ - │ int x │ - │ int y │ ← foo'nun çerçevesi - │ dönüş değeri │ - ├───────────────┤ - │ int a │ - │ int b │ ← main'in çerçevesi - Çağrı yığıtının dibi → └───────────────┘ -) - -$(P -İşlevlerin başka işlevleri çağırarak daha derine dallanmalarına ve bu işlevlerden üst düzeylere dönülmelerine bağlı olarak çağrı yığıtının büyüklüğü buna uygun olarak artar veya azalır. Örneğin, $(C bar)'dan dönüldüğünde artık çerçevesine gerek kalmadığından o alan ilerideki başka bir çağrı için kullanılmak üzere boş kalır: -) - -$(MONO -$(LIGHT_GRAY ┌───────────────┐) -$(LIGHT_GRAY │ int parametre │) -$(LIGHT_GRAY │ string[] dizi │) -Çağrı yığıtının tepesi → ├───────────────┤ - │ int x │ - │ int y │ ← foo'nun çerçevesi - │ dönüş değeri │ - ├───────────────┤ - │ int a │ - │ int b │ ← main'in çerçevesi - Çağrı yığıtının dibi → └───────────────┘ -) - -$(P -Bu kitapta yazdığımız her programda üzerinde durmasak da hep çağrı yığıtından yararlandık. Özyinelemeli işlevlerin basit olabilmelerinin nedeni de çağrı yığıtıdır. -) - -$(H6 $(IX özyineleme) Özyineleme) - -$(P -Özyineleme, bir işlevin doğrudan veya dolaylı olarak kendisini çağırması durumudur. Özyineleme, aralarında $(I böl ve fethet) $(ASIL divide-and-conquer) diye tanımlananlar da bulunan bazı algoritmaları büyük ölçüde kolaylaştırır. -) - -$(P -Bunun bir örneğini görmek için bir dilimin elemanlarının toplamını döndüren aşağıdaki işleve bakalım. Bu işlev görevini yerine getirirken yine kendisini, ama farklı parametre değerleriyle çağırmaktadır. Her çağrı, parametre olarak alınan dilimin bir eksik elemanlısını kullanmaktadır. Bu özyineleme dilim boş kalana kadar devam eder. Belirli bir ana kadar hesaplanmış olan toplam değer ise işlevin ikinci parametresi olarak geçirilmektedir: -) - ---- -import std.array; - -int topla(int[] dilim, int anlıkToplam = 0) { - if (dilim.empty) { - /* Ekleyecek eleman yok. Bu ana kadar hesaplanmış olan - * toplamı döndürelim. */ - return anlıkToplam; - } - - /* Baştaki elemanın değerini bu andaki toplama ekleyelim - * ve kendimizi dilimin geri kalanı ile çağıralım. */ - return $(HILITE topla)(dilim[1..$], anlıkToplam + dilim.front); -} - -void main() { - assert(topla([1, 2, 3]) == 6); -} ---- - -$(P -$(IX sum, std.algorithm) $(I Not: Yukarıdaki işlev yalnızca gösterim amacıyla yazılmıştır. Bir aralıktaki elemanların toplamı gerektiğinde $(C std.algorithm.sum) işlevini kullanmanızı öneririm. O işlev kesirli sayıları toplarken özel algoritmalardan yararlanır ve daha doğru sonuçlar üretir.) -) - -$(P -$(C topla)'nın yukarıdaki gibi $(C [1, 2, 3]) dilimiyle çağrıldığını düşünürsek, özyinelemenin son adımında çağrı yığıtı aşağıdaki çerçevelerden oluşacaktır. Her parametrenin değerini $(C ==) işlecinden sonra gösteriyorum. Çağrı sırasına uygun olması için çerçeveleri aşağıdan yukarıya doğru okumanızı öneririm: -) - -$(MONO - ┌──────────────────────────┐ - │ dilim == [] │ ← topla'nın son çağrılışı - │ anlıkToplam == 6 │ - ├──────────────────────────┤ - │ dilim == [3] │ ← topla'nın üçüncü çağrılışı - │ anlıkToplam == 3 │ - ├──────────────────────────┤ - │ dilim == [2, 3] │ ← topla'nın ikinci çağrılışı - │ anlıkToplam == 1 │ - ├──────────────────────────┤ - │ dilim == [1, 2, 3] │ ← topla'nın ilk çağrılışı - │ anlıkToplam == 0 │ - ├──────────────────────────┤ - │ ... │ ← main'in çerçevesi - └──────────────────────────┘ -) - -$(P -$(I Not: Eğer özyinelemeli işlev $(C topla)'da olduğu gibi kendisini çağırmasının sonucunu döndürüyorsa, derleyiciler "kuyruk özyinelemesi" denen bir eniyileştirme $(ASIL tail-call optimization) yönteminden yararlanırlar ve her çağrı için ayrı çerçeve kullanımını önlerler.) -) - -$(P -Birden fazla iş parçacığı kullanılan durumda her iş parçacığı diğerlerinden bağımsızca kendi görevini yürüttüğünden, her iş parçacığı için ayrı çağrı yığıtı vardır. -) - -$(P -Bir fiberin gücü, kendisi iş parçacığı olmadığı halde kendi çağrı yığıtına sahip olmasından kaynaklanır. Fiberler, normalde tek çağrı yığıtına sahip olan iş parçacıklarının birden fazla çağrı yığıtı kullanabilmelerini sağlarlar. Tek çağrı yığıtı ancak tek görevin durumunu saklayabildiğinden birden fazla çağrı yığıtı bir iş parçacığının birden fazla görev yürütmesini sağlar. -) - -$(H5 Kullanım) - -$(P -Fiberlerin genel kullanımı aşağıdaki işlemlerden oluşur. Bunların örneklerini biraz aşağıda göreceğiz. -) - -$(UL - -$(LI $(IX fiber işlevi) Bir fiberin işleyişi, çağrılabilen herhangi bir $(I birimden) (işlev göstergesi, temsilci, vs.) başlar. Bu başlangıç çağrısı parametre almaz ve değer döndürmez. Örneğin, fiberin başlangıcının türü $(C void function()) olabilir: - ---- -void fiberİşlevi() { - // ... -} ---- - -) - -$(LI $(IX Fiber, core.thread) Bir fiber temelde $(C core.thread.Fiber) sınıfının bir nesnesi olarak kurulur: - ---- -import core.thread; - -// ... - - auto fiber = new Fiber($(HILITE &)fiberİşlevi); ---- - -$(P -Gerektiğinde $(C Fiber) sınıfından türemiş olan bir sınıf da kullanılabilir. Bu durumda başlangıç işlevi üst sınıfın kurucusuna parametre olarak geçirilir: -) - ---- -class ÖzelFiber : $(HILITE Fiber) { - this() { - super($(HILITE &)başlangıç); - } - - void başlangıç() { - // ... - } -} - -// ... - - auto fiber = new ÖzelFiber(); ---- - -) - -$(LI $(IX call, Fiber) Bir fiber $(C call()) üye işlevi ile başlatılır: - ---- - fiber.call(); ---- - -$(P -İş parçacıklarının tersine, fiber işlerken onu çağıran kod durur. -) - -) - -$(LI $(IX yield, Fiber) Bir fiber kendisini $(C Fiber.yield()) ile duraksatır: - ---- -void fiberİşlevi() { - // ... - - Fiber.yield(); - - // ... -} ---- - -$(P -Fiber duraksadığında onu çağıran kod kaldığı yerden tekrar işlemeye başlar. -) - -) - -$(LI $(IX .state, Fiber) Bir fiberin çalışma durumu $(C .state) niteliği ile öğrenilir: - ---- - if (fiber.state == Fiber.State.TERM) { - // ... - } ---- - -$(P -$(IX State, Fiber) $(C Fiber.State) aşağıdaki değerlerden oluşan bir $(C enum) türüdür: -) - -$(UL - -$(LI $(IX HOLD, Fiber.State) $(C HOLD): Fiber duraksamış (yani, başlatılabilir) durumdadır.) - -$(LI $(IX EXEC, Fiber.State) $(C EXEC): Fiber işlemektedir.) - -$(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): Fiber işlemini tamamlamış durumdadır. Yeniden başlatılması isteniyorsa önce $(C reset()) üye işlevinin çağrılması gerekir.) - -) - -) - -) - -$(H5 Fiberlerin aralıklara yararı) - -$(P -Hemen hemen her aralık en son nerede kaldığı bilgisini saklamak üzere üye değişkenlerden yararlanır. Bu bilgi, aralık nesnesi $(C popFront) ile $(I ilerletilirken) kullanılır. Hem $(LINK2 /ders/d/araliklar.html, Aralıklar bölümünde) hem de daha sonraki bölümlerde gördüğümüz çoğu aralığın da üye değişkenleri bulunuyordu. -) - -$(P -Örneğin, daha önce tanımlamış olduğumuz $(C FibonacciSerisi), serinin iki sonraki sayısını hesaplamak için iki üye değişkenden yararlanıyordu: -) - ---- -struct FibonacciSerisi { - int $(HILITE baştaki) = 0; - int $(HILITE sonraki) = 1; - - enum empty = false; - - @property int front() const { - return baştaki; - } - - void popFront() { - const ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } -} ---- - -$(P -İlerleme durumu için böyle değişkenler tanımlamak $(C FibonacciSerisi) gibi bazı aralıklar için basit olsa da, ikili ağaç gibi bazı özyinelemeli veri yapılarında şaşırtıcı derecede güçtür. Şaşırtıcılığın nedeni, aynı algoritmaların özyinelemeli olarak yazıldıklarında ise çok basit olmalarıdır. -) - -$(P -Örneğin, özyinelemeli olarak tanımlanmış olan aşağıdaki $(C ekle) ve $(C yazdır) işlevleri hiç değişken tanımlamaları gerekmeden ve ağaçtaki eleman sayısından bağımsız olarak çok basitçe yazılabilmişlerdir. Özyinelemeli çağrıları işaretli olarak gösteriyorum. (Dikkat ederseniz, $(C ekle)'nin özyinelemesi $(C ekleVeyaİlkle) üzerindendir.) -) - ---- -import std.stdio; -import std.string; -import std.conv; -import std.random; -import std.range; -import std.algorithm; - -/* İkili ağacın düğümlerini temsil eder. Aşağıdaki Ağaç - * yapısının gerçekleştirilmesinde kullanılmak üzere - * tanımlanmıştır. */ -struct Düğüm { - int eleman; - Düğüm * sol; // Sol alt ağaç - Düğüm * sağ; // Sağ alt ağaç - - void $(HILITE ekle)(int eleman) { - if (eleman < this.eleman) { - /* Küçük elemanlar sol alt ağaca */ - ekleVeyaİlkle(sol, eleman); - - } else if (eleman > this.eleman) { - /* Büyük elemanlar sağ alt ağaca */ - ekleVeyaİlkle(sağ, eleman); - - } else { - throw new Exception(format("%s mevcut", eleman)); - } - } - - void $(HILITE yazdır)() const { - /* Önce sol alt ağacı yazdırıyoruz. */ - if (sol) { - sol.$(HILITE yazdır)(); - write(' '); - } - - /* Sonra bu düğümün elemanını yazdırıyoruz. */ - write(eleman); - - /* En sonunda da sağ alt ağacı yazdırıyoruz. */ - if (sağ) { - write(' '); - sağ.$(HILITE yazdır)(); - } - } -} - -/* Elemanı belirtilen alt ağaca ekler. Eğer 'null' ise düğümü - * ilkler. */ -void ekleVeyaİlkle(ref Düğüm * düğüm, int eleman) { - if (!düğüm) { - /* Bu alt ağacı ilk elemanıyla ilkliyoruz. */ - düğüm = new Düğüm(eleman); - - } else { - düğüm.$(HILITE ekle)(eleman); - } -} - -/* Ağaç veri yapısını temsil eder. 'kök' üyesi 'null' ise ağaç - * boş demektir. */ -struct Ağaç { - Düğüm * kök; - - /* Elemanı bu ağaca ekler. */ - void ekle(int eleman) { - ekleVeyaİlkle(kök, eleman); - } - - /* Elemanları sıralı olarak yazdırır. */ - void yazdır() const { - if (kök) { - kök.yazdır(); - } - } -} - -/* '10 * n' sayı arasından rasgele seçilmiş olan 'n' sayı ile - * bir ağaç oluşturur. */ -Ağaç rasgeleAğaç(size_t n) { - /* '10 * n' sayı arasından 'n' tane seç. */ - auto sayılar = iota((n * 10).to!int) - .randomSample(n, Random(unpredictableSeed)) - .array; - - /* 'n' sayıyı karıştır. */ - randomShuffle(sayılar); - - /* Ağacı o sayılarla doldur. */ - auto ağaç = Ağaç(); - sayılar.each!(e => ağaç.ekle(e)); - - return ağaç; -} - -void main() { - auto ağaç = rasgeleAğaç(10); - ağaç.yazdır(); -} ---- - -$(P -$(I Not: Yukarıdaki program aşağıdaki Phobos olanaklarından da yararlanmaktadır:) -) - -$(UL - -$(LI -$(IX iota, std.range) $(C std.range.iota), verilen değer aralığındaki elemanları tembel olarak üretir. (İlk eleman belirtilmediğinde $(C .init) değeri varsayılır.) Örneğin, $(C iota(10)) 0 ile 9 arasındaki değerlerden oluşan bir $(C int) aralığıdır. -) - -$(LI -$(IX each, std.algorithm) $(IX map ve each) $(C std.algorithm.each), $(C std.algorithm.map)'e çok benzer. $(C map) her elemana karşılık yeni bir sonuç ürettiği halde $(C each) her elemana karşılık yan etki üretir. Ek olarak, $(C map) tembeldir ama $(C each) heveslidir. -) - -$(LI -$(IX randomSample, std.random) $(C std.random.randomSample), verilen aralıktan sıralarını değiştirmeden rasgele elemanlar seçer. -) - -$(LI -$(IX randomShuffle, std.random) $(C std.random.randomShuffle), bir aralıktaki elemanların sıralarını rasgele değiştirir. -) - -) - -$(P -Her toplulukta olduğu gibi, aralık algoritmalarıyla kullanılabilmesi için bu ağaç topluluğunun da bir aralık arayüzü sunmasını isteriz. Bunu $(C opSlice) üye işlevini tanımlayarak gerçekleştirebileceğimizi biliyoruz: -) - ---- -struct Ağaç { -// ... - - /* Ağacın elemanlarına sıralı erişim sağlar. */ - struct SıralıAralık { - $(HILITE ... Gerçekleştirmesi nasıl olmalıdır? ...) - } - - SıralıAralık opSlice() const { - return SıralıAralık(kök); - } -} ---- - -$(P -Yukarıda tanımlanan $(C yazdır) üye işlevi de temelde elemanlara sırayla eriştiği halde, bir ağacın elemanlarına erişim sağlayan bir $(C InputRange) tanımlamak göründüğünden çok daha güç bir iştir. Ben burada $(C SıralıAralık) yapısını tanımlamaya çalışmayacağım. Ağaç erişicilerinin nasıl gerçekleştirildiklerini kendiniz araştırmanızı ve geliştirmeye çalışmanızı öneririm. (Bazı erişici gerçekleştirmeleri $(C sol) ve $(C sağ) üyelerine ek olarak üstteki $(ASIL parent) düğümü gösteren $(C Node*) türünde bir üye daha olmasını gerektirirler.) -) - -$(P -$(C yazdır) gibi özyinelemeli ağaç algoritmalarının o kadar basit yazılabilmelerinin nedeni çağrı yığıtıdır. Çağrı yığıtı, belirli bir andaki elemanın hangisi olduğunun yanında o elemana hangi alt ağaçlar izlenerek erişildiği (hangi düğümlerde sola veya sağa dönüldüğü) bilgisini de otomatik olarak saklar. -) - -$(P -Örneğin, özyinelemeli $(C sol.yazdır()) çağrısından soldaki elemanlar yazdırılıp dönüldüğünde, şu anda işlemekte olan $(C yazdır) işlevi sırada boşluk karakteri olduğunu zaten bilir: -) - ---- - void yazdır() const { - if (sol) { - sol.yazdır(); - write(' '); // ← Çağrı yığıtına göre sıra bundadır - } - - // ... - } ---- - -$(P -Fiberler özellikle çağrı yığıtının büyük kolaylık sağladığı bu gibi durumlarda yararlıdır. -) - -$(P -Fiberlerin sağladığı kolaylık Fibonacci serisi gibi basit türler üzerinde gösterilemese de, fiber işlemlerini özellikle böyle basit bir yapı üzerinde tanıtmak istiyorum. Daha aşağıda bir ikili ağaç aralığı da tanımlayacağız. -) - ---- -import core.thread; - -/* Elemanları üretir ve 'ref' parametresine atar. */ -void fibonacciSerisi($(HILITE ref) int baştaki) { // (1) - baştaki = 0; // Not: 'baştaki' parametrenin kendisidir - int sonraki = 1; - - while (true) { - $(HILITE Fiber.yield()); // (2) - /* Bir sonraki call() çağrısı tam bu noktadan - * devam eder. */ // (3) - - const ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } -} - -void main() { - int baştaki; // (1) - // (4) - Fiber fiber = new Fiber(() => fibonacciSerisi(baştaki)); - - foreach (_; 0 .. 10) { - fiber$(HILITE .call()); // (5) - - import std.stdio; - writef("%s ", baştaki); - } -} ---- - -$(OL - -$(LI Yukarıdaki fiber işlevi parametre olarak $(C int) türünde bir değişken referansı almakta ve ürettiği elemanları kendisini çağırana bu parametre aracılığıyla iletmektedir. (Bu parametre $(C ref) yerine $(C out) olarak da tanımlanabilir.)) - -$(LI Fiber, yeni eleman hazır olduğunda kendisini $(C Fiber.yield()) ile duraksatır.) - -$(LI Bir sonraki $(C call()) çağrısı, fiberi en son duraksatmış olan $(C Fiber.yield())'in hemen sonrasından devam ettirir. (İlk $(C call()) ise fiber işlevini başlatır.)) - -$(LI Fiber işlevleri parametre almadıklarından $(C fibonacciSerisi()) doğrudan kullanılamaz. O yüzden, $(C Fiber) nesnesi kurulurken parametresiz bir $(LINK2 /ders/d/kapamalar.html, isimsiz işlev) kullanılmıştır.) - -$(LI Çağıran, fiberi $(C call()) üye işlevi ile başlatır ve devam ettirir.) - -) - -$(P -Sonuçta, $(C main) eleman değerlerini $(C baştaki) değişkeni üzerinden elde eder ve yazdırır: -) - -$(SHELL -0 1 1 2 3 5 8 13 21 34 -) - -$(H6 $(IX Generator, std.concurrency) Fiberlerin $(C std.concurrency.Generator) ile aralık olarak kullanılmaları) - -$(P -Fibonacci serisinin yukarıdaki fiber gerçekleştirmesinin bazı yetersizlikleri vardır: -) - -$(UL - -$(LI Bu seri, aralık arayüzü sunmadığından mevcut aralık algoritmalarıyla kullanılamaz.) - -$(LI $(C ref) çeşidinden bir parametrenin değiştirilmesi yerine elemanların çağırana $(I kopyalandıkları) bir tasarım tercih edilmelidir.) - -$(LI Fiberi böyle $(I alt düzey) olanaklarıyla açıkça kurmak ve kullanmak yerine kullanım kolaylığı getiren başka çözümler tasarlanabilir.) - -) - -$(P -$(C std.concurrency.Generator) sınıfı bu yetersizliklerin hepsini giderir. Aşağıdaki $(C fibonacciSerisi)'nin nasıl basit bir işlev olarak yazılabildiğine dikkat edin. Tek farkı, işlevden tek eleman döndürmek yerine $(C yield) ile birden fazla eleman üretmesidir. (Bu örnekte sonsuz sayıda eleman üretilmektedir.) -) - -$(P -$(IX yield, std.concurrency) Ek olarak, aşağıdaki $(C yield) daha önce kullandığımız $(C Fiber.yield) üye işlevi değil, $(C std.concurrency) modülündeki $(C yield) işlevidir. -) - ---- -import std.stdio; -import std.range; -import std.concurrency; - -/* Bu alias std.range.Generator ile olan bir isim çakışmasını - * gidermek içindir. */ -alias FiberAralığı = std.concurrency.Generator; - -void fibonacciSerisi() { - int baştaki = 0; - int sonraki = 1; - - while (true) { - $(HILITE yield(baştaki)); - - const ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } -} - -void main() { - auto seri = new $(HILITE FiberAralığı!int)(&fibonacciSerisi); - writefln("%(%s %)", seri.take(10)); -} ---- - -$(P -Sonuçta, bir fiber işlevinin ürettiği elemanlar kolayca bir $(C InputRange) aralığı olarak kullanılabilmektedir: -) - -$(SHELL -0 1 1 2 3 5 8 13 21 34 -) - -$(P -Ağaç elemanlarına $(C InputRange) arayüzü vermek için de $(C Generator)'dan yararlanılabilir. Dahası, $(C InputRange) arayüzü bulunan bir ağacın $(C yazdır) işlevine de artık gerek kalmaz. Aşağıdaki $(C düğümleri) işlevinin $(C sonrakiDüğüm)'ü çağıran bir isimsiz işlev oluşturduğuna ve $(C Generator)'a o isimsiz işlevi verdiğine dikkat edin: -) - ---- -import std.concurrency; - -alias FiberAralığı = std.concurrency.Generator; - -struct Düğüm { -// ... - - /* Not: Gerekmeyen yazdır() işlevi çıkartılmıştır. */ - - auto opSlice() const { - return düğümleri(&this); - } -} - -/* Bu fiber işlevi eleman değerine göre sıralı olarak bir - * sonraki düğümü üretir. */ -void sonrakiDüğüm(const(Düğüm) * düğüm) { - if (!düğüm) { - /* Bu düğümün kendisinde veya altında eleman yok */ - return; - } - - sonrakiDüğüm(düğüm.sol); // Önce soldaki elemanlar - $(HILITE yield(düğüm)); // Şimdi bu eleman - sonrakiDüğüm(düğüm.sağ); // Sonra sağdaki elemanlar -} - -/* Ağacın düğümlerinden oluşan bir InputRange döndürür. */ -auto düğümleri(const(Düğüm) * düğüm) { - return new FiberAralığı!(const(Düğüm)*)( - () => sonrakiDüğüm(düğüm)); -} - -// ... - -struct Ağaç { -// ... - - /* Not: Gerekmeyen yazdır() işlevi çıkartılmıştır. */ - - auto opSlice() const { - /* Düğümlerden eleman değerlerine dönüşüm. */ - return düğümleri(this).map!(d => d.eleman); - } -} - -/* Ağacın düğümlerinden oluşan bir InputRange döndürür. Ağaçta - * eleman bulunmadığında (yani, 'kök' 'null' olduğunda) boş - * aralık döndürür. */ -auto düğümleri(const(Ağaç) ağaç) { - alias AralıkTürü = typeof(düğümleri(ağaç.kök)); - - return (ağaç.kök - ? düğümleri(ağaç.kök) - : new AralıkTürü(() {})); // ← Boş aralık -} ---- - -$(P -Artık $(C Ağaç) nesneleri $(C []) işleciyle dilimlenebilirler ve $(C InputRange) olarak kullanılabilirler: -) - ---- - writefln("%(%s %)", ağaç$(HILITE [])); ---- - -$(H5 $(IX zaman uyumsuz giriş/çıkış, fiber) Fiberlerin zaman uyumsuz giriş/çıkış işlemlerinde kullanılmaları) - -$(P -Fiberlerin çağrı yığıtları zaman uyumsuz giriş/çıkış işlemlerini de kolaylaştırır. -) - -$(P -Bunun bir örneğini görmek için kullanıcıların sırayla $(I isim), $(I e-posta), ve $(I yaş) bilgilerini girerek kayıt oldukları bir servis düşünelim. Bu örneği bir internet sitesinin $(I üye kayıt iş akışına $(ASIL flow)) benzetebiliriz. Örneği kısa tutmak için bir internet sunucusu yerine kullanıcılarla komut satırı üzerinden etkileşen bir program yazalım. Bu etkileşim girilen bilgilerin işaretli olarak gösterildikleri aşağıdaki protokolü kullanıyor olsun: -) - -$(UL -$(LI $(HILITE $(C merhaba)): Bir kullanıcı bağlansın ve kendi akışının numarasını edinsin.) -$(LI $(HILITE $(C $(I numara veri))): Belirtilen numaralı akışın kullanıcısı bir sonraki veriyi girsin. Örneğin, $(C 42) numaralı akışın bir sonraki verisi $(I isim) ise ve kullanıcısının adı Ayşe ise, giriş $(C 42 Ayşe) olsun.) -$(LI $(HILITE $(C son)): Program sonlansın.) -) - -$(P -Örneğin, Ayşe ve Barış adlı iki kullanıcının etkileşimleri aşağıdaki gibi olabilir. Kullanıcıların girdikleri veriler işaretli olarak gösterilmiştir. Her kullanıcı bağlandıktan sonra $(I isim), $(I e-posta), ve $(I yaş) bilgisini girmektedir: -) - -$(SHELL -> $(HILITE merhaba) $(SHELL_NOTE Ayşe bağlanır) -0 numaralı akış başladı. -> $(HILITE 0 Ayşe) -> $(HILITE 0 ayse@example.com) -> $(HILITE 0 20) $(SHELL_NOTE Ayşe kaydını tamamlar) -Akış 0 tamamlandı. -'Ayşe' eklendi. -> $(HILITE merhaba) $(SHELL_NOTE Barış bağlanır) -1 numaralı akış başladı. -> $(HILITE 1 Barış) -> $(HILITE 1 baris@example.com) -> $(HILITE 1 30) $(SHELL_NOTE Barış kaydını tamamlar) -Akış 1 tamamlandı. -'Barış' eklendi. -> $(HILITE son) -Güle güle. -Kullanıcılar: - Kullanıcı("Ayşe", "ayse@example.com", 20) - Kullanıcı("Barış", "baris@example.com", 30) -) - -$(P -Bu programı $(C merhaba) komutunu bekleyen ve kullanıcı verileri için bir işlev çağıran bir tasarımla gerçekleştirebiliriz: -) - ---- - if (giriş == "merhaba") { - yeniKullanıcıKaydet(); $(CODE_NOTE_WRONG UYARI: Giriş tıkayan tasarım) - } ---- - -$(P -Eğer program eş zamanlı programlama yöntemleri kullanmıyorsa, yukarıdaki gibi bir tasarım $(I girişi tıkayacaktır) $(ASIL block) çünkü bağlanan kullanıcının verileri tamamlanmadan program başka kullanıcı kabul edemez. Verilerini dakika mertebesinde giren kullanıcılar fazla yüklü olmayan bir sunucuyu bile kullanışsız hale getirecektir. -) - -$(P -Böyle bir servisin tıkanmadan işlemesini (yani, birden fazla kullanıcının kayıt işlemlerinin aynı anda sürdürülebilmesini) sağlayan çeşitli tasarımlar düşünülebilir: -) - -$(UL - -$(LI Görevlerin açıkça yönetilmesi: Ana iş parçacığı her bağlanan kullanıcı için $(C spawn) ile farklı bir iş parçacığı oluşturabilir ve bilgileri o iş parçacığına mesajlar halinde iletebilir. Bu çözümde veri geçerliliğinin $(C synchronized) gibi yöntemlerle korunması gerekebilir. Ek olarak, aşağıda $(I işbirlikli çoklu görev) bölümünde açıklanacağı gibi, iş parçacıkları fiberlerden genelde daha yavaş işlerler.) - -$(LI Akış durumunun açıkça yönetilmesi: Program birden fazla akış kabul edebilir ve her akışın durumunu açıkça yönetebilir. Örneğin, Ayşe henüz yalnızca ismini girmişse, onun akışının durum bilgisi bir sonraki verinin e-posta olduğunu belirtir.) - -) - -$(P -Her kayıt akışı için ayrı fiber kullanan bir yöntem de düşünülebilir. Bunun yararı, akışın doğrusal olarak ve kullanıcı protokolüne tam uygun olarak yazılabilmesidir: önce isim, sonra e-posta, ve son olarak yaş. Aşağıdaki $(C başlangıç) işlevinin akışın durumunu saklamak için değişken tanımlamak zorunda kalmadığına dikkat edin. Her $(C call) çağrısı bir önceki $(C Fiber.yield)'in kaldığı yerden devam eder; bir sonra işletilecek olan işlem, çağrı yığıtı tarafından üstü kapalı olarak saklanmaktadır. -) - -$(P -Önceki örneklerden farklı olarak, aşağıdaki programdaki fiber, $(C Fiber)'in alt sınıfı olarak tanımlanmıştır: -) - ---- -import std.stdio; -import std.string; -import std.format; -import std.exception; -import std.conv; -import std.array; -import core.thread; - -struct Kullanıcı { - string isim; - string eposta; - uint yaş; -} - -/* Bu alt sınıf kullanıcı kayıt akışını temsil eder. */ -class KayıtAkışı : Fiber { - /* Bu akış için en son okunmuş olan veri. */ - string veri_; - - /* Kullanıcı nesnesi kurmak için gereken bilgi. */ - string isim; - string eposta; - uint yaş; - - this() { - /* Fiberin başlangıç noktası olarak 'başlangıç' üye - * işlevini belirtiyoruz. */ - super(&başlangıç); - } - - void başlangıç() { - /* İlk girilen veri isimdir. */ - isim = veri_; - $(HILITE Fiber.yield()); - - /* İkinci girilen veri e-postadır. */ - eposta = veri_; - $(HILITE Fiber.yield()); - - /* Sonuncu veri yaştır. */ - yaş = veri_.to!uint; - - /* Bu noktada Kullanıcı nesnesi oluşturacak bütün - * veriyi toplamış bulunuyoruz. 'Fiber.yield()' ile - * duraksamak yerine artık işlevin sonlanmasını - * istiyoruz. (Burada açıkça 'return' deyimi de - * olabilirdi.) Bunun sonucunda bu fiberin durumu - * Fiber.State.TERM değerini alır. */ - } - - /* Bu nitelik işlevi çağıranın veri girmesi içindir. */ - @property void veri(string yeniVeri) { - veri_ = yeniVeri; - } - - /* Bu nitelik işlevi kurulan nesneyi çağırana vermek - * içindir. */ - @property Kullanıcı kullanıcı() const { - return Kullanıcı(isim, eposta, yaş); - } -} - -/* Belirli bir akış için girişten okunmuş olan veriyi temsil - * eder. */ -struct AkışVerisi { - size_t numara; - string yeniVeri; -} - -/* Belirtilen satırdan akış verisi okur. */ -AkışVerisi akışVerisiOku(string satır) { - size_t numara; - string yeniVeri; - - const adet = - formattedRead(satır, " %s %s", &numara, ¥iVeri); - - enforce(adet == 2, - format("Geçersiz veri: '%s'.", satır)); - - return AkışVerisi(numara, yeniVeri); -} - -void main() { - Kullanıcı[] kullanıcılar; - KayıtAkışı[] akışlar; - - bool bitti_mi = false; - - while (!bitti_mi) { - write("> "); - string satır = readln.strip; - - switch (satır) { - case "merhaba": - /* Yeni bağlanan kullanıcı için yeni akış - * oluşturalım. */ - akışlar ~= new KayıtAkışı(); - - writefln("%s numaralı akış başladı.", - akışlar.length - 1); - break; - - case "son": - /* Programdan çıkalım. */ - bitti_mi = true; - break; - - default: - /* Girilen satırı akış verisi olarak kullanmaya - * çalışalım. */ - try { - auto kullanıcı = veriİşle(satır, akışlar); - - if (!kullanıcı.isim.empty) { - kullanıcılar ~= kullanıcı; - writefln("'%s' eklendi.", kullanıcı.isim); - } - - } catch (Exception hata) { - writefln("Hata: %s", hata.msg); - } - break; - } - } - - writeln("Güle güle."); - writefln("Kullanıcılar:\n%( %s\n%)", kullanıcılar); -} - -/* Girilen verinin ait olduğu fiberi belirler, yeni verisini - * bildirir, ve o fiberin işleyişini kaldığı yerden devam - * ettirir. Eğer girilen son veri üzerine akış sonlanmışsa, - * üyeleri geçerli değerlerden oluşan bir Kullanıcı nesnesi - * döndürür. */ -Kullanıcı veriİşle(string satır, KayıtAkışı[] akışlar) { - const akışVerisi = akışVerisiOku(satır); - const numara = akışVerisi.numara; - - enforce(numara < akışlar.length, - format("Geçersiz numara: %s.", numara)); - - auto akış = akışlar[numara]; - - enforce(akış.state == Fiber.State.HOLD, - format("Akış %s işletilebilir durumda değil.", - numara)); - - /* Akışa yeni verisini bildir. */ - akış.veri = akışVerisi.yeniVeri; - - /* Akışı kaldığı yerden devam ettir. */ - akış$(HILITE .call()); - - Kullanıcı kullanıcı; - - if (akış.state == Fiber.State.TERM) { - writefln("Akış %s tamamlandı.", numara); - - /* Dönüş değerine yeni oluşturulan kullanıcıyı ata. */ - kullanıcı = akış.kullanıcı; - - /* Sonrası için fikir: 'akışlar' dizisinin artık işi - * bitmiş olan bu elemanı yeni bağlanacak olan - * kullanıcılar için kullanılabilir. Ancak, önce - * 'akış.reset()' ile tekrar başlatılabilir duruma - * getirilmesi gerekir. */ - } - - return kullanıcı; -} ---- - -$(P -$(C main) işlevi girişten satırlar okur, onları ayrıştırır, ve veriyi işlenmek üzere ilgili akışa bildirir. Her akışın durumu kendi çağrı yığıtı tarafından otomatik olarak bilinmektedir. Yeni kullanıcılar bilgileri tamamlandıkça sisteme eklenirler. -) - -$(P -Yukarıdaki programı çalıştırdığınızda kullanıcıların bilgi girme hızlarından bağımsız olarak sistemin her zaman için yeni kullanıcı kabul ettiğini göreceksiniz. Aşağıdaki örnekte Ayşe'nin etkileşimi işaretlenmiştir: -) - -$(SHELL -> $(HILITE merhaba) $(SHELL_NOTE Ayşe bağlanır) -0 numaralı akış başladı. -> $(HILITE 0 Ayşe) -> merhaba $(SHELL_NOTE Barış bağlanır) -1 numaralı akış başladı. -> merhaba $(SHELL_NOTE Can bağlanır) -2 numaralı akış başladı. -> $(HILITE 0 ayse@example.com) -> 1 Barış -> 2 Can -> 2 can@example.com -> 2 40 $(SHELL_NOTE Can kaydını tamamlar) -Akış 2 tamamlandı. -'Can' eklendi. -> 1 baris@example.com -> 1 30 $(SHELL_NOTE Barış kaydını tamamlar) -Akış 1 tamamlandı. -'Barış' eklendi. -> $(HILITE 0 20) $(SHELL_NOTE Ayşe kaydını tamamlar) -Akış 0 tamamlandı. -'Ayşe' eklendi. -> son -Güle güle. -Kullanıcılar: - Kullanıcı("Can", "can@example.com", 40) - Kullanıcı("Barış", "baris@example.com", 30) - Kullanıcı("Ayşe", "ayse@example.com", 20) -) - -$(P -Önce Ayşe, sonra Barış, ve en son Can bağlandıkları halde kayıt işlemlerini farklı sürelerde tamamlamışlardır. Sonuçta $(C kullanıcılar) dizisinin elemanları tamamlanan akış sırasına göre eklenmiştir. -) - -$(P -Fiberlerin bu programa bir yararı, $(C KayıtAkışı.başlangıç) işlevinin kullanıcı giriş hızlarından bağımsız olarak basitçe yazılabilmiş olmasıdır. Ek olarak, başka akışlardan bağımsız olarak her zaman için yeni kullanıcı kabul edilebilmektedir. -) - -$(P -$(IX vibe.d) $(LINK2 http://vibed.org, vibe.d) gibi çok sayıdaki $(I zaman uyumsuz giriş/çıkış çatısı) da fiberler üzerine kurulu tasarımlardan yararlanır. -) - -$(H5 $(IX hata atma, fiber) Fiberler ve hata yönetimi) - -$(P -$(LINK2 /ders/d/hatalar.html, Hata Yönetimi bölümünde) "alt düzey bir işlevden atılan bir hatanın teker teker o işlevi çağıran üst düzey işlevlere geçtiğini" görmüştük. Hiçbir düzeyde yakalanmayan bir hatanın ise "$(C main)'den de çıkılmasına ve programın sonlanmasına" neden olduğunu görmüştük. O bölümde hiç çağrı yığıtından bahsedilmemiş olsa da hata atma düzeneği de çağrı yığıtından yararlanır. -) - -$(P -$(IX yığıt çözülmesi) Bu bölümün ilk örneğinden devam edersek, $(C bar) içinde bir hata atıldığında çağrı yığıtından önce $(C bar)'ın çerçevesi çıkartılır, ondan sonra $(C foo)'nunki, ve en sonunda da $(C main)'inki. İşlevler sonlanırken çerçevelerinin çağrı yığıtından çıkartılması sırasında o işlevlerin yerel değişkenlerinin sonlandırıcı işlevleri de işletilir. İşlevlerden hata atılması üzerine çıkılması ve sonlandırıcıların işletilmesine $(I yığıt çözülmesi) denir. -) - -$(P -Fiberlerin kendi çağrı yığıtları olduğundan, atılan hata da fiberin kendi çağrı yığıtını etkiler, fiberi çağıran kodun çağrı yığıtını değil. Hata yakalanmadığında ise fiber işlevinden de çıkılmış olur ve fiberin durumu $(C Fiber.State.TERM) değerini alır. -) - -$(P -$(IX yieldAndThrow, Fiber) Bu, bazı durumlarda tam da istenen davranış olabileceği gibi, bazen fiberin kaldığı yeri kaybetmeden hata durumunu bildirmesi istenebilir. $(C Fiber.yieldAndThrow), fiberin kendisini duraksatmasını ve hemen ardından çağıranın kapsamında bir hata atmasını sağlar. -) - -$(P -Bundan nasıl yararlanılabileceğini görmek için yukarıdaki kayıt programına geçersiz yaş bilgisi verelim: -) - -$(SHELL -> merhaba -0 numaralı akış başladı. -> 0 Ayşe -> 0 ayse@example.com -> 0 $(HILITE selam) $(SHELL_NOTE_WRONG kullanıcı geçersiz yaş bilgisi girer) -Hata: Unexpected 's' when converting from type string to type uint -> 0 $(HILITE 20) $(SHELL_NOTE hatasını düzeltmeye çalışır) -Hata: Akış 0 işletilebilir durumda değil. $(SHELL_NOTE ama fiber sonlanmıştır) -) - -$(P -Fiberin sonlanması nedeniyle bütün kullanıcı akışının kaybedilmesi yerine, fiber atılan dönüşüm hatasını yakalayabilir ve kendisini çağırana $(C yieldAndThrow) ile bildirebilir. Bunun için yaş bilgisinin dönüştürüldüğü aşağıdaki satırın değiştirilmesi gerekir: -) - ---- - yaş = veri_.to!uint; ---- - -$(P -O satırın sonsuz döngüdeki bir $(C try-catch) deyimi içine alınması, $(C uint)'e dönüştürülebilecek veri gelene kadar fiberi canlı tutacaktır: -) - ---- - while (true) { - try { - yaş = veri_.to!uint; - break; // ← Dönüştürüldü; döngüden çıkalım - - } catch (ConvException hata) { - Fiber.yieldAndThrow(hata); - } - } ---- - -$(P -Bu sefer, geçerli veri gelene kadar döngü içinde kalınır: -) - -$(SHELL -> merhaba -0 numaralı akış başladı. -> 0 Ayşe -> 0 ayse@example.com -> 0 $(HILITE selam) $(SHELL_NOTE_WRONG kullanıcı geçersiz yaş bilgisi girer) -Hata: Unexpected 's' when converting from type string to type uint -> 0 $(HILITE dünya) $(SHELL_NOTE_WRONG tekrar geçersiz yaş bilgisi girer) -Hata: Unexpected 'd' when converting from type string to type uint -> 0 $(HILITE 20) $(SHELL_NOTE sonunda doğru bilgi girer) -Akış 0 tamamlandı. -'Ayşe' eklendi. -> son -Güle güle. -Kullanıcılar: - Kullanıcı("Ayşe", "ayse@example.com", 20) -) - -$(P -Programın çıktısında görüldüğü gibi, artık akış hata nedeniyle sonlanmaz ve kullanıcı sisteme eklenmiş olur. -) - -$(H5 $(IX çoklu görev) $(IX iş parçacığı performansı) İşbirlikli çoklu görevler) - -$(P -İşletim sisteminin sunduğu çoklu görev olanağı iş parçacıklarını belirsiz zamanlarda duraksatmaya ve tekrar başlatmaya dayanır. Fiberler ise kendilerini istedikleri zaman duraksatırlar ve çağıranları tarafından tekrar başlatılırlar. Bu ayrıma göre, işletim sisteminin sunduğu çoklu görev sistemine $(I geçişli çoklu görev), fiberlerin sunduğuna ise $(I işbirlikli çoklu görev) denir. -) - -$(P -$(IX bağlam değiştirme) Geçişli çoklu görev sistemlerinde işletim sistemi başlattığı her iş parçacığına belirli bir süre ayırır. O süre dolduğunda iş parçacığı duraksatılır ve başka bir iş parçacığına $(I geçilir). Bir iş parçacığından başkasına geçmeye $(I bağlam değiştirme) denir. Bağlam değiştirme göreceli olarak masraflı bir işlemdir. -) - -$(P -Sistemler genelde çok sayıda iş parçacığı işlettiklerinden bağlam değiştirme hem kaçınılmazdır hem de programların kesintisiz işlemeleri açısından istenen bir durumdur. Ancak, bazı iş parçacıkları ayrılan süreleri daha dolmadan kendilerini duraksatma gereği duyarlar. Bu durum, bir iş parçacığının başka bir iş parçacığından veya bir cihazdan veri beklediği zamanlarda oluşabilir. Bir iş parçacığı kendisini durdurduğunda işletim sistemi başka bir iş parçacığına geçmek için yeniden bağlam değiştirmek zorundadır. Sonuçta, mikro işlemcinin iş gerçekleştirmek amacıyla ayırdığı sürenin bir bölümü bağlam değiştirmek için harcanmıştır. -) - -$(P -Fiberlerde ise fiber ve onu çağıran kod aynı iş parçacığı üzerinde işletilirler. (Fiber ve çağıranının aynı anda işletilmemelerinin nedeni budur.) Bunun bir yararı, ikisi arasındaki geçişlerde bağlam değiştirme masrafının bulunmamasıdır. (Yine de işlev çağırma masrafı kadar küçük olan bir masraf vardır.) -) - -$(P -İşbirlikli çoklu görevlerin başka bir yararı, fiberle çağıranı arasında iletilen verinin mikro işlemcinin önbelleğinde bulunma olasılığının daha yüksek olmasıdır. Önbelleğe erişmek sistem belleğine erişmekten yüzlerce kat hızlı olduğundan, fiberler iş parçacıklarından çok daha hızlı işleyebilirler. -) - -$(P -Dahası, fiber ve çağıranı aynı anda işlemediklerinden, veri erişiminde $(I yarış hali) de söz konusu değildir. Dolayısıyla, $(C synchronized) gibi olanaklar kullanılması da gerekmez. Ancak, programcı yine de fiberin gereğinden erken duraksatılmadığından emin olmalıdır. Örneğin, aşağıdaki $(C işlev()) çağrısı sırasında $(C Fiber.yield) çağrılmamalıdır çünkü $(C paylaşılanVeri)'nin değeri o sırada henüz ikiye katlanmamıştır: -) - ---- -void fiberİşlevi() { - // ... - - işlev(); $(CODE_NOTE fiberi duraksatmamalıdır) - paylaşılanVeri *= 2; - Fiber.yield(); $(CODE_NOTE istenen duraksatma noktası) - - // ... -} ---- - -$(P -$(IX M:N, iş parçacıkları) Fiberlerin bariz bir yetersizliği, fiber ve çağıranının tek çekirdek üzerinde işliyor olmalarıdır. Mikro işlemcinin boşta bekleyen çekirdekleri olduğunda bu durum kaynak savurganlığı anlamına gelir. Bunun önüne geçmek için $(I M:N iş parçacığı modeli) $(ASIL M:N threading model) gibi çeşitli yöntemlere başvurulabilir. Bu yöntemleri kendiniz araştırmanızı öneririm. -) - -$(H5 Özet) - -$(UL - -$(LI Çağrı yığıtı işlev yerel durumu için kullanılan alanın çok hızlıca ayrılmasını sağlar ve aralarında özyinelemelilerin de bulunduğu bazı algoritmaları çok basitleştirir.) - -$(LI Fiberler normalde tek çağrı yığıtına sahip olan iş parçacıklarının birden fazla çağrı yığıtı kullanmalarını sağlarlar.) - -$(LI Fiber ve çağıranı aynı iş parçacığı üzerinde işletilirler (aynı anda değil).) - -$(LI Fiber kendisini $(C yield) ile duraksatır ve çağıranı tarafından $(C call) ile tekrar başlatılır.) - -$(LI $(C Generator) fiberi $(C InputRange) olarak sunar.) - -$(LI Fiberler çağrı yığıtına dayanan algoritmaları basitleştirirler.) - -$(LI Fiberler zaman uyumsuz giriş/çıkış işlemlerini basitleştirirler.) - -$(LI Fiberler $(I geçişli çoklu görev) sistemlerinden farklı artıları ve eksileri bulunan $(I işbirlikli çoklu görev) sistemleridirler.) - -) - -macros: - SUBTITLE=Fiberler - - DESCRIPTION=İş parçacıklarının birden fazla çağrı yığıtı aracılığıyla işbirlikli çoklu görev yürütmeleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial fiber işbirlikli çoklu görev - -SOZLER= -$(baglam_degistirme) -$(cagri_yigiti) -$(cerceve) -$(coklu_gorev) -$(erisici) -$(gecisli_coklu_gorev) -$(gorev) -$(hevesli) -$(is_parcacigi) -$(isbirlikli_coklu_gorev) -$(mikro_islemci_cekirdegi) -$(ortak_islev) -$(onbellek) -$(ozyineleme) -$(tembel_degerlendirme) -$(yaris_hali) -$(yigit_cozulmesi) -$(zaman_uyumsuz) diff --git a/ddili/src/ders/d/for_dongusu.cozum.d b/ddili/src/ders/d/for_dongusu.cozum.d deleted file mode 100644 index 1ef65e9..0000000 --- a/ddili/src/ders/d/for_dongusu.cozum.d +++ /dev/null @@ -1,85 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C for) Döngüsü) - -$(OL - -$(LI - ---- -import std.stdio; - -void main() { - for (int satır = 0; satır != 9; ++satır) { - for (int sütun = 0; sütun != 9; ++sütun) { - write(satır, ',', sütun, ' '); - } - - writeln(); - } -} ---- - -) - -$(LI Üçgen: - ---- -import std.stdio; - -void main() { - for (int satır = 0; satır != 5; ++satır) { - int uzunluk = satır + 1; - - for (int i = 0; i != uzunluk; ++i) { - write('*'); - } - - writeln(); - } -} ---- - -$(P -Paralelkenar: -) - ---- -import std.stdio; - -void main() { - for (int satır = 0; satır != 5; ++satır) { - for (int i = 0; i != satır; ++i) { - write(' '); - } - - writeln("********"); - } -} ---- - -$(P -Baklava dilimi çizdirebilir misiniz? -) - -$(SHELL - * - *** - ***** -******* - ***** - *** - * -) - -) - -) - - -Macros: - SUBTITLE=for Döngüsü Problem Çözümü - - DESCRIPTION=for döngüsü bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial for döngü problem çözüm diff --git a/ddili/src/ders/d/for_dongusu.d b/ddili/src/ders/d/for_dongusu.d deleted file mode 100644 index 5ed0b63..0000000 --- a/ddili/src/ders/d/for_dongusu.d +++ /dev/null @@ -1,267 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX for) $(IX döngü, for) $(CH4 for) Döngüsü) - -$(P -$(LINK2 /ders/d/while_dongusu.html, $(C while) döngüsü) ile aynı işe yarar. Yararı, döngü ile ilgili bütün tanımların tek satırda yapılmasıdır. -) - -$(P -$(C for) döngüsü $(C foreach) döngüsünden çok daha az kullanılır. Buna rağmen, $(C for) döngüsünün nasıl işlediği de iyi bilinmelidir. $(C foreach) döngüsünü daha sonraki bir bölümde göreceğiz. -) - -$(H5 $(C while)'ın bölümleri) - -$(P -Hatırlarsak, $(C while) döngüsü tek bir koşul denetler ve o koşul doğru olduğu sürece döngüye devam eder. Örneğin 1'den 10'a kadar olan bütün tamsayıları yazdıran bir döngü "sayı 11'den küçük olduğu sürece" şeklinde kodlanabilir: -) - ---- - while (sayı < 11) ---- - -$(P -O döngünün $(I ilerletilmesi), $(C sayı)'nın döngü içinde bir arttırılması ile sağlanabilir: -) - ---- - ++sayı; ---- - -$(P -Kodun derlenebilmesi için $(C sayı)'nın $(C while)'dan önce tanımlanmış olması gerekir: -) - ---- - int sayı = 1; ---- - -$(P -Döngünün asıl işlemlerini de sayarsak, bütün bölümlerine değinmiş oluruz: -) - ---- - writeln(sayı); ---- - -$(P -Bu dört işlemi $(I döngünün hazırlığı), $(I devam etme koşulunun denetimi), $(I asıl işlemleri), ve $(I ilerletilmesi) olarak açıklayabiliriz: -) - ---- - int sayı = 1; // ← hazırlık - - while (sayı < 11) { // ← devam koşulu - writeln(sayı); // ← asıl işlemler - ++sayı; // ← döngünün ilerletilmesi - } ---- - -$(P -$(C while) döngüsü sırasında bu bölümler şu sırada işletilirler: -) - -$(MONO -hazırlık - -koşul denetimi -asıl işlemler -ilerletilmesi - -koşul denetimi -asıl işlemler -ilerletilmesi - -... -) - -$(P -Hatırlayacağınız gibi, bir $(C break) deyimi veya atılmış olan bir hata da döngünün sonlanmasını sağlayabilir. -) - -$(H5 $(C for)'un bölümleri) - -$(P -$(C for) döngüsü bu dört işlemden üçünü tek bir tanıma indirgeyen deyimdir. Bu işlemlerin üçü de $(C for) deyiminin parantezi içinde, ve aralarında noktalı virgül olacak şekilde yazılırlar. Asıl işlemler ise kapsam içindedir: -) - ---- -for (/* hazırlık */; /* devam koşulu */; /* ilerletilmesi */) { - /* asıl işlemler */ -} ---- - -$(P -Yukarıdaki $(C while) döngüsü $(C for) ile yazıldığında çok daha düzenli bir hale gelir: -) - ---- - for (int sayı = 1; sayı < 11; ++sayı) { - writeln(sayı); - } ---- - -$(P -Bu, özellikle döngü kapsamının kalabalık olduğu durumlarda çok yararlıdır: döngüyü ilerleten işlem, kapsam içindeki diğer ifadeler arasında kaybolmak yerine, $(C for) ile aynı satırda durur ve kolayca görülür. -) - -$(P -$(C for) döngüsünün bölümleri de $(C while)'ın bölümleriyle aynı sırada işletilirler. -) - -$(P -$(C break) ve $(C continue) deyimleri $(C for) döngüsünde de aynı şekilde çalışırlar. -) - -$(P -$(C while) ve $(C for) döngüleri arasındaki tek fark, $(C for)'un hazırlık bölümünde tanımlanmış olan değişkenin isim alanıdır. Bunu aşağıda açıklıyorum. -) - -$(P -Çok sık olarak döngüyü ilerletmek için bir tamsayı kullanılır, ama öyle olması gerekmez. Ayrıca, döngü değişkeni arttırılmak yerine başka bir biçimde de değiştirilebilir. Örneğin belirli bir değer aralığındaki kesirli sayıların sürekli olarak yarılarını gösteren bir döngü şöyle yazılabilir: -) - ---- - for (double sayı = 1; sayı > 0.001; sayı /= 2) { - writeln(sayı); - } ---- - -$(P -$(B Not:) Bu başlık altında yukarıda anlatılanlar teknik açıdan doğru değildir ama $(C for) döngüsünün hemen hemen bütün kullanımlarını karşılar. Bu, özellikle C'den veya C++'tan gelen programcıların kodları için geçerlidir. Aslında $(C for) döngüsünün noktalı virgüllerle ayrılmış olan üç bölgesi $(I yoktur): İlkini hazırlık ve koşul bölümlerinin paylaştıkları yalnızca iki bölgesi vardır. Bu söz diziminin ayrıntılarına burada girmek yerine, hazırlık bölümünde iki değişken tanımlayan aşağıdaki kodu göstermekle yetineceğim: -) - ---- - for ($(HILITE {) int i = 0; double d = 0.5; $(HILITE }) i < 10; ++i) { - writeln("i: ", i, ", d: ", d); - d /= 2; - } ---- - -$(P -Hazırlık bölümü sarı küme parantezleri arasındaki bölgedir. Dikkat ederseniz onunla koşulun arasında noktalı virgül yoktur. -) - -$(H5 Döngünün üç bölümü de boş bırakılabilir) - -$(P -Gereken durumlarda isteğe bağlı olarak, bu bölümler boş bırakılabilir: -) - -$(UL -$(LI Bazen hazırlık için bir değişken tanımlamak gerekmez çünkü zaten tanımlanmış olan bir değişken kullanılacaktır -) -$(LI Bazen döngüyü sonlandırmak için döngü koşulu yerine döngü içindeki $(C break) satırlarından yararlanılır -) -$(LI Bazen döngüyü ilerletme adımı belirli koşullara bağlı olarak döngü içinde yapılabilir -) -) - -$(P -Bütün bölümler boş bırakıldığında, $(C for) döngüsü $(I sonsuza kadar) anlamına gelir: -) - ---- - for ( ; ; ) { - // ... - } ---- - -$(P -Öyle bir döngü, örneğin ya hiç çıkılmayacak şekilde, veya belirli bir koşul gerçekleştiğinde $(C break) ile çıkılacak şekilde tasarlanmış olabilir. -) - -$(H5 Döngü değişkeninin geçerli olduğu kapsam) - -$(P -$(C for) ile $(C while)'ın tek farkı, döngü hazırlığı sırasında tanımlanan ismin geçerlilik alanıdır: $(C for) döngüsünün hazırlık bölgesinde tanımlanan isim, yalnızca döngü içindeki kapsamda geçerlidir (ve onun içindekilerde), dışarıdaki kapsamda değil: -) - ---- - for (int i = 0; i < 5; ++i) { - // ... - } - - writeln(i); $(DERLEME_HATASI) - // i burada geçerli değildir ---- - - -$(P -$(C while) döngüsünde ise, isim $(C while)'ın da içinde bulunduğu kapsamda tanımlanmış olduğu için, $(C while)'dan çıkıldığında da geçerliliğini korur: -) - ---- - int i = 0; - - while (i < 5) { - // ... - ++i; - } - - writeln(i); // ← 'i' burada hâlâ geçerlidir ---- - -$(P -$(C for) döngüsünün bu ismin geçerlilik alanını küçük tutuyor olması, bir önceki bölümün sonunda anlatılanlara benzer şekilde, programcılık hatası risklerini de azaltır. -) - -$(PROBLEM_COK - -$(PROBLEM - -İç içe iki $(C for) döngüsü kullanarak, ekrana satır ve sütun numaralarını gösteren 9'a 9'luk bir tablo yazdırın: - -$(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 -Bir veya daha fazla $(C for) döngüsü kullanarak ve $(C *) karakterini gereken sayıda yazdırarak geometrik şekiller çizdirin: - -$(SHELL -* -** -*** -**** -***** -) - -$(SHELL -******** - ******** - ******** - ******** - ******** -) - -$(P -vs. -) - -) - -) - - -Macros: - SUBTITLE=for Döngüsü - - DESCRIPTION=D dilinin for döngüsünün tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial for döngüsü döngü - -SOZLER= -$(deyim) -$(dongu) -$(kapsam) diff --git a/ddili/src/ders/d/foreach_dongusu.cozum.d b/ddili/src/ders/d/foreach_dongusu.cozum.d deleted file mode 100644 index 82529aa..0000000 --- a/ddili/src/ders/d/foreach_dongusu.cozum.d +++ /dev/null @@ -1,31 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C foreach) Döngüsü) - -$(P -$(C isimle) tablosunun tersi olarak çalışabilmesi için indeks türü yerine eleman türü, eleman türü yerine de indeks türü kullanmak gerekir. Yani $(C int[string])... Asıl dizginin elemanlarını $(C foreach) ile gezerek indeks olarak eleman değerini, eleman olarak da indeks değerini kullanırsak, ters yönde çalışan bir eşleme tablosu elde ederiz: -) - ---- -import std.stdio; - -void main() { - string[int] isimle = [ 1:"bir", 7:"yedi", 20:"yirmi" ]; - - int[string] rakamla; - - foreach (indeks, eleman; isimle) { - rakamla[eleman] = indeks; - } - - writeln(rakamla["yirmi"]); -} ---- - - -Macros: - SUBTITLE=foreach Döngüsü Problem Çözümleri - - DESCRIPTION=D dilinin foreach döngüsü bölümünün problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial foreach döngüsü döngü problem çözüm diff --git a/ddili/src/ders/d/foreach_dongusu.d b/ddili/src/ders/d/foreach_dongusu.d deleted file mode 100644 index f710896..0000000 --- a/ddili/src/ders/d/foreach_dongusu.d +++ /dev/null @@ -1,462 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX foreach) $(IX döngü, foreach) $(CH4 foreach) Döngüsü) - -$(P -$(C foreach) D'nin en kullanışlı deyimlerinden birisidir. "Her birisi için" anlamına gelir. Belirli işlemleri bir topluluktaki (veya bir aralıktaki) elemanların her birisi ile yapmayı sağlar. -) - - - -$(P -Topluluk elemanlarının tümüyle yapılan işlemler programcılıkta çok yaygındır. $(LINK2 /ders/d/for_dongusu.html, $(C for) döngüsünün) bir dizinin bütün elemanlarına erişmek için nasıl kullanıldığını görmüştük: -) - ---- - for (int i = 0; i != dizi.length; ++i) { - writeln(dizi[i]); - } ---- - -$(P -Bu iş için gereken adımları şöyle özetleyebiliriz: -) - -$(UL -$(LI İsmi geleneksel olarak $(C i) olan bir sayaç tanımlamak (aslında biz önceki örneklerde hep $(C sayaç) dedik)) -$(LI Döngüyü topluluğun $(C .length) niteliğine kadar ilerletmek) -$(LI $(C i)'yi arttırmak) -$(LI Elemana erişmek) -) - -$(P -Bu adımlar ayrı ayrı elle yapılmak yerine $(C foreach) ile çok daha basit olarak şöyle ifade edilir: -) - ---- - foreach (eleman; dizi) { - writeln(eleman); - } ---- - -$(P -$(C foreach)'in güçlü yanlarından birisi, eşleme tabloları ile de aynı biçimde kullanılabilmesidir. $(C for) döngüsünde ise, örneğin bir eşleme tablosunun bütün elemanlarına erişmek için tablo'nun $(C .values) niteliği çağrılır: -) - ---- - auto elemanlar = tablo$(HILITE .values); - for (int i = 0; i != elemanlar.length; ++i) { - writeln(elemanlar[i]); - } ---- - -$(P -$(C foreach) eşleme tabloları için özel bir kullanım gerektirmez; eşleme tabloları da dizilerle aynı biçimde kullanılır: -) - ---- - foreach (eleman; tablo) { - writeln(eleman); - } ---- - -$(H5 Söz dizimi) - -$(P -$(C foreach) üç bölümden oluşur: -) - ---- - foreach ($(I isimler); $(I topluluk_veya_aralık)) { - $(I işlem_bloğu) - } ---- - -$(UL -$(LI $(B $(I topluluk_veya_aralık)): döngünün işletileceği elemanları belirler -) - -$(LI $(B $(I işlem_bloğu)): her elemanla yapılacak işlemleri belirler -) - -$(LI $(B $(I isimler)): erişilen elemanın ve varsa başka nesnelerin isimlerini belirler; seçilen isimler programcıya bağlı olsa da, bunların anlamı ve adedi topluluk çeşidine göre değişir -) -) - -$(H5 $(C continue) ve $(C break)) - -$(P -Bu anahtar sözcüklerin ikisi de burada da aynı anlama gelirler: $(C continue) döngünün erkenden ilerletilmesini, $(C break) de döngünün sonlandırılmasını bildirir. -) - -$(H5 Dizilerle kullanımı) - -$(P -$(I isimler) bölümüne yazılan tek isim, dizinin elemanını ifade eder: -) - ---- - foreach (eleman; dizi) { - writeln(eleman); - } ---- - -$(P -Eğer iki isim yazılırsa birincisi otomatik bir sayaçtır, ikincisi yine elemanı ifade eder: -) - ---- - foreach (sayaç, eleman; dizi) { - writeln(sayaç, ": ", eleman); - } ---- - -$(P -Sayacın değeri $(C foreach) tarafından otomatik olarak arttırılır. Sayaç değişkeninin ismi programcıya kalmış olsa da isim olarak $(C i) de çok yaygındır. -) - -$(H5 $(IX stride, std.range) Dizgilerle kullanımı ve $(C std.range.stride)) - -$(P -Dizilerle aynı şekilde kullanılır. Tek isim yazılırsa dizginin karakterini ifade eder, çift isim yazılırsa sayaç ve karakterdir: -) - ---- - foreach (karakter; "merhaba") { - writeln(karakter); - } - - foreach (sayaç, karakter; "merhaba") { - writeln(sayaç, ": ", karakter); - } ---- - -$(P -$(C char) ve $(C wchar) türlerinin Unicode karakterlerini barındırmaya genel olarak uygun olmadıklarını hatırlayın. $(C foreach) bu türlerle kullanıldığında karakterlere değil, kod birimlerine erişilir: -) - ---- - foreach (sayaç, kod; "abcçd") { - writeln(sayaç, ": ", kod); - } ---- - -$(P -Örneğin ç'yi oluşturan kodlara ayrı ayrı erişilir: -) - -$(SHELL -0: a -1: b -2: c -3: -4: � -5: d -) - -$(P -UTF kodlamasından bağımsız olarak her tür dizginin $(C foreach) ile $(I karakter karakter) erişilmesini sağlayan olanak, $(C std.range) modülündeki $(C stride)'dır. $(C stride) "adım" anlamına gelir ve karakterlerin kaçar kaçar atlanacağı bilgisini de alır: -) - ---- -import std.range; - -// ... - - foreach (harf; stride("abcçd", 1)) { - writeln(harf); - } ---- - -$(P -$(C stride) kullanıldığında UTF kodlarına değil Unicode karakterlerine erişilir: -) - -$(SHELL -a -b -c -ç -d -) - -$(P -Bu kodda neden sayaç kullanılamadığını biraz aşağıda açıklayacağım. -) - -$(H5 Eşleme tablolarıyla kullanımı) - -$(P -Tek isim yazılırsa eleman değerini, iki isim yazılırsa indeks ve eleman değerini ifade eder: -) - ---- - foreach (eleman; tablo) { - writeln(eleman); - } - - foreach (indeks, eleman; tablo) { - writeln(indeks, ": ", eleman); - } ---- - -$(P -$(I Not: Eşleme tablolarında indeksin de herhangi bir türden olabileceğini hatırlayın. O yüzden bu döngüde $(C sayaç) yazmadım.) -) - -$(P -$(IX .byKey, foreach) $(IX .byValue, foreach) $(IX .byKeyValue, foreach) Eşleme tabloları indekslerini ve elemanlarını $(I aralıklar) olarak da sunabilirler. Aralıkları daha $(LINK2 /ders/d/araliklar.html, ilerideki bir bölümde) göreceğiz. Eşleme tablolarının $(C .byKey), $(C .byValue), ve $(C .byKeyValue) nitelikleri $(C foreach) döngülerinden başka ortamlarda da kullanılabilen hızlı aralık nesneleri döndürürler. -) - -$(P -$(C .byValue), $(C foreach) döngülerinde yukarıdaki elemanlı döngü ile karşılaştırıldığında fazla bir yarar sağlamaz. $(C .byKey) ise bir eşleme tablosunun $(I yalnızca) indeksleri üzerinde ilerlemenin en hızlı yoludur: -) - ---- - foreach (indeks; tablo$(HILITE .byKey)) { - writeln(indeks); - } ---- - -$(P -$(C .byKeyValue) $(LINK2 /ders/d/cokuzlular.html, çokuzlu) gibi kullanılan bir değişken döndürür. İndeks ve eleman değerleri o değişkenin $(C .key) ve $(C .value) nitelikleri ile elde edilir: -) - ---- - foreach (eleman; tablo$(HILITE .byKeyValue)) { - writefln("%s indeksinin değeri: %s", - eleman$(HILITE .key), eleman$(HILITE .value)); - } ---- - -$(H5 $(IX sayı aralığı) $(IX .., sayı aralığı) Sayı aralıklarıyla kullanımı) - -$(P -Sayı aralıklarını $(LINK2 /ders/d/dilimler.html, Başka Dizi Olanakları bölümünde) görmüştük. $(C foreach)'in $(I topluluk_veya_aralık) bölümüne bir sayı aralığı da yazılabilir: -) - ---- - foreach (sayı; 10..15) { - writeln(sayı); - } ---- - -$(P -Hatırlarsanız; yukarıdaki kullanımda 10 aralığa dahildir, 15 değildir. -) - -$(H5 Yapılarla, sınıflarla, ve aralıklarla kullanımı) - -$(P -$(C foreach), bu desteği veren yapı, sınıf, ve aralık nesneleriyle de kullanılabilir. Nasıl kullanıldığı hakkında burada genel bir şey söylemek olanaksızdır, çünkü tamamen o tür tarafından belirlenir. $(C foreach)'in nasıl işlediğini ancak söz konusu yapının, sınıfın, veya aralığın belgesinden öğrenebiliriz. -) - -$(P -Yapılar ve sınıflar $(C foreach) desteğini ya $(C opApply()) isimli üye işlevleri ya da $(I aralık (range) üye işlevleri) aracılığıyla verirler; aralıklar ise bu iş için aralık üye işlevleri tanımlarlar. Bu olanakları daha sonraki bölümlerde göreceğiz. -) - -$(H5 $(IX sayaç, foreach) Sayaç yalnızca dizilerde otomatiktir) - -$(P -Sayaç olanağı yalnızca dizilerde bulunur. $(C foreach) dizilerden başka türlerle kullanıldığında ve sayaç gerektiğinde, açıkça değişken tanımlanabilir ve arttırılabilir: -) - ---- - size_t sayaç = 0; - foreach (eleman; topluluk) { - // ... - ++sayaç; - } ---- - -$(P -Böyle bir değişken, sayacın döngünün her ilerletilişinde değil, belirli bir koşul sağlandığında arttırılması gerektiğinde de yararlı olur. Örneğin aşağıdaki döngü yalnızca 10'a tam olarak bölünen sayıları sayar: -) - ---- -import std.stdio; - -void main() { - auto dizi = [ 1, 0, 15, 10, 3, 5, 20, 30 ]; - - size_t sayaç = 0; - foreach (sayı; dizi) { - if ((sayı % 10) == 0) { - $(HILITE ++sayaç); - write(sayaç); - - } else { - write(' '); - } - - writeln(": ", sayı); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL - : 1 -1: 0 - : 15 -2: 10 - : 3 - : 5 -3: 20 -4: 30 -) - -$(H5 Elemanın kopyası, kendisi değil) - -$(P -$(C foreach) döngüsü; normalde elemanın kendisine değil, bir kopyasına erişim sağlar. Topluluk elemanlarının yanlışlıkla değiştirilmelerini önlemek amacıyla böyle tasarlandığını düşünebilirsiniz. -) - -$(P -Bir dizinin elemanlarının her birisini iki katına çıkartmaya çalışan şu koda bakalım: -) - ---- -import std.stdio; - -void main() { - double[] sayılar = [ 1.2, 3.4, 5.6 ]; - - writefln("Önce : %s", sayılar); - - foreach (sayı; sayılar) { - sayı *= 2; - } - - writefln("Sonra: %s", sayılar); -} ---- - -$(P -Programın çıktısı, $(C foreach) kapsamında $(C sayı)'ya yapılan atamanın etkisi olmadığını gösteriyor: -) - -$(SHELL -Önce : 1.2 3.4 5.6 -Sonra: 1.2 3.4 5.6 -) - -$(P -$(IX ref, foreach) Bunun nedeni, $(C sayı)'nın dizi elemanının kendisi değil, onun bir kopyası olmasıdır. Dizi elemanının kendisinin ifade edilmesi istendiğinde, isim bir $(I referans) olarak tanımlanır: -) - ---- - foreach ($(HILITE ref) sayı; sayılar) { - sayı *= 2; - } ---- - -$(P -Yeni çıktıda görüldüğü gibi, $(C ref) anahtar sözcüğü dizideki asıl elemanın etkilenmesini sağlamıştır: -) - -$(SHELL -Önce : 1.2 3.4 5.6 -Sonra: 2.4 6.8 11.2 -) - -$(P -Oradaki $(C ref) anahtar sözcüğü, $(C sayı)'yı asıl elemanın bir $(I takma ismi) olarak tanımlar. $(C sayı)'da yapılan değişiklik artık elemanın kendisini etkilemektedir. -) - -$(H5 Topluluğun kendisi değiştirilmemelidir) - -$(P -Topluluk elemanlarını $(C ref) olarak tanımlanmış olan değişkenler aracılığıyla değiştirmekte bir sakınca yoktur. Ancak, $(C foreach) döngüsü kapsamında topluluğun kendi yapısını etkileyecek hiçbir işlem yapılmamalıdır. Örneğin diziden eleman silinmemeli veya diziye eleman eklenmemelidir. -) - -$(P -Bu tür işlemler topluluğun yapısını değiştireceklerinden, ilerlemekte olan $(C foreach) döngüsünün işini bozarlar. O noktadan sonra programın davranışının ne olacağı bilinemez. -) - -$(H5 $(IX foreach_reverse) $(IX döngü, foreach_reverse) Ters sırada ilerlemek için $(C foreach_reverse)) - -$(P -$(C foreach_reverse) $(C foreach) ile aynı biçimde işler ama aralığı ters sırada ilerler: -) - ---- - auto elemanlar = [ 1, 2, 3 ]; - - foreach_reverse (eleman; elemanlar) { - writefln("%s ", eleman); - } ---- - -$(P -Çıktısı: -) - -$(SHELL -3 -2 -1 -) - -$(P -$(C foreach_reverse)'ün kullanımı yaygın değildir. Çoğunlukla onun yerine daha sonra göreceğimiz $(C retro()) isimli aralık işlevi kullanılır. -) - -$(PROBLEM_TEK - -$(P -Eşleme tablolarının indeks değerleri ile eleman değerlerini $(I eşlediklerini) görmüştük. Bu tek yönlüdür: indeks verildiğinde eleman değerini elde ederiz, ama eleman değeri verildiğinde indeks değerini elde edemeyiz. -) - -$(P -Elinizde hazırda şöyle bir eşleme tablosu olsun: -) - ---- - string[int] isimle = [ 1:"bir", 7:"yedi", 20:"yirmi" ]; ---- - -$(P -O tablodan ve tek bir $(C foreach) döngüsünden yararlanarak, $(C rakamla) isminde başka bir eşleme tablosu oluşturun. Bu yeni tablo, $(C isimle) tablosunun tersi olarak çalışsın: isime karşılık rakam elde edebilelim. Örneğin -) - ---- - writeln(rakamla["yirmi"]); ---- - -$(P -yazdığımızda çıktı şöyle olsun: -) - -$(SHELL -20 -) - -) - - - -Macros: - SUBTITLE=foreach Döngüsü - - DESCRIPTION=D dilinin foreach döngüsünün tanıtılması - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial foreach döngüsü döngü - -SOZLER= -$(aralik) -$(blok) -$(deyim) -$(dilim) -$(dongu) -$(eleman) -$(nesne) -$(referans) -$(sinif) -$(soz_dizimi) -$(topluluk) -$(uye_islev) -$(yapi) diff --git a/ddili/src/ders/d/foreach_opapply.cozum.d b/ddili/src/ders/d/foreach_opapply.cozum.d deleted file mode 100644 index 75a0a89..0000000 --- a/ddili/src/ders/d/foreach_opapply.cozum.d +++ /dev/null @@ -1,206 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Yapı ve Sınıflarda $(C foreach)) - -$(OL - -$(LI Aralığın başı ve sonuna ek olarak adım miktarının da saklanması gerekir. $(C opApply) içindeki döngüdeki değer bu durumda $(C adım) kadar arttırılır: - ---- -struct Aralık { - int baş; - int son; - $(HILITE int adım); - - int opApply(int delegate(ref int) işlemler) const { - int sonuç; - - for (int sayı = baş; sayı != son; $(HILITE sayı += adım)) { - sonuç = işlemler(sayı); - if (sonuç) { - break; - } - } - - return sonuç; - } -} - -import std.stdio; - -void main() { - foreach (eleman; Aralık(0, 10, 2)) { - write(eleman, ' '); - } - - writeln(); -} ---- - -) - -$(LI - ---- -import std.stdio; -import std.string; - -class Öğrenci { - string isim; - int numara; - - this(string isim, int numara) { - this.isim = isim; - this.numara = numara; - } - - override string toString() { - return format("%s(%s)", isim, numara); - } -} - -class Öğretmen { - string isim; - string ders; - - this(string isim, string ders) { - this.isim = isim; - this.ders = ders; - } - - override string toString() { - return format("%s dersine %s Öğretmen", ders, isim); - } -} - -class Okul { -private: - - Öğrenci[] öğrenciler; - Öğretmen[] öğretmenler; - -public: - - this(Öğrenci[] öğrenciler, - Öğretmen[] öğretmenler) { - this.öğrenciler = öğrenciler.dup; - this.öğretmenler = öğretmenler.dup; - } - - /* Parametresi Öğrenci olduğundan, bu 'delegate'i - * kullanan opApply, foreach döngü değişkeninin Öğrenci - * olduğu durumda çağrılır. */ - int opApply(int delegate(ref $(HILITE Öğrenci)) işlemler) { - int sonuç; - - foreach (öğrenci; öğrenciler) { - sonuç = işlemler(öğrenci); - - if (sonuç) { - break; - } - } - - return sonuç; - } - - /* Benzer şekilde, bu opApply da foreach döngü değişkeni - * Öğretmen olduğunda çağrılır. */ - int opApply(int delegate(ref $(HILITE Öğretmen)) işlemler) { - int sonuç; - - foreach (öğretmen; öğretmenler) { - sonuç = işlemler(öğretmen); - - if (sonuç) { - break; - } - } - - return sonuç; - } -} - -void girintiliYazdır(T)(T nesne) { - writeln(" ", nesne); -} - -void main() { - auto okul = new Okul( - [ new Öğrenci("Can", 1), - new Öğrenci("Canan", 10), - new Öğrenci("Cem", 42), - new Öğrenci("Cemile", 100) ], - - [ new Öğretmen("Nazmiye", "Matematik"), - new Öğretmen("Makbule", "Türkçe") ]); - - writeln("Öğrenci döngüsü"); - foreach ($(HILITE Öğrenci) öğrenci; okul) { - girintiliYazdır(öğrenci); - } - - writeln("Öğretmen döngüsü"); - foreach ($(HILITE Öğretmen) öğretmen; okul) { - girintiliYazdır(öğretmen); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Öğrenci döngüsü - Can(1) - Canan(10) - Cem(42) - Cemile(100) -Öğretmen döngüsü - Matematik dersine Nazmiye Öğretmen - Türkçe dersine Makbule Öğretmen -) - -$(P -İki işlevin dizi türleri dışında aynı olduklarını görüyoruz. Buradaki ortak işlemleri dizinin türüne göre değişen bir işlev şablonu olarak yazabilir ve iki $(C opApply)'dan bu ortak işlevi çağırabiliriz: -) - ---- -class Okul { -// ... - - int opApplyOrtak$(HILITE (T))(T[] dizi, int delegate(ref T) işlemler) { - int sonuç; - - foreach (eleman; dizi) { - sonuç = işlemler(eleman); - - if (sonuç) { - break; - } - } - - return sonuç; - } - - int opApply(int delegate(ref Öğrenci) işlemler) { - return opApplyOrtak(öğrenciler, işlemler); - } - - int opApply(int delegate(ref Öğretmen) işlemler) { - return opApplyOrtak(öğretmenler, işlemler); - } -} ---- - -) - -) - -Macros: - SUBTITLE=Yapı ve Sınıflarda foreach Problem Çözümleri - - DESCRIPTION=Yapı ve Sınıflarda foreach Problem Çözümleri - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial yapı sınıf foreach problem çözüm diff --git a/ddili/src/ders/d/foreach_opapply.d b/ddili/src/ders/d/foreach_opapply.d deleted file mode 100644 index ab3462a..0000000 --- a/ddili/src/ders/d/foreach_opapply.d +++ /dev/null @@ -1,686 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX foreach, sınıf ve yapı) $(IX struct, foreach) $(IX class, foreach) Yapı ve Sınıflarda $(C foreach)) - -$(P -$(LINK2 /ders/d/foreach_dongusu.html, $(C foreach) Döngüsü bölümünden) hatırlayacağınız gibi, bu döngü uygulandığı türe göre değişik şekillerde işler. Nasıl kullanıldığına bağlı olarak farklı elemanlara erişim sağlar: dizilerde, sayaçlı veya sayaçsız olarak dizi elemanlarına; eşleme tablolarında, indeksli veya indekssiz olarak tablo elemanlarına; sayı aralıklarında, değerlere; kütüphane türlerinde, o türe özel bir şekilde, örneğin $(C File) için dosya satırlarına... -) - -$(P -$(C foreach)'in nasıl işleyeceğini kendi türlerimiz için de belirleyebiliriz. Bunun için iki farklı yöntem kullanılabilir: -) - -$(UL -$(LI Türün aralık algoritmalarıyla da kullanılmasına olanak veren $(I aralık işlevleri) tanımlamak) - -$(LI Tür için $(C opApply) üye işlevleri tanımlamak) -) - -$(P -Bu iki yöntemden $(C opApply) işlevleri önceliklidir: Tanımlanmışlarsa derleyici o üye işlevleri kullanır; tanımlanmamışlarsa $(I aralık işlevlerine) başvurur. Öte yandan, $(I aralık işlevleri) yöntemi çoğu durumda yeterli, daha basit, ve daha kullanışlıdır. -) - -$(P -Bu yöntemlere geçmeden önce, $(C foreach)'in her türe uygun olamayacağını vurgulamak istiyorum. Bir nesne üzerinde $(C foreach) ile ilerlemek, ancak o tür herhangi bir şekilde bir $(I topluluk) olarak kabul edilebiliyorsa anlamlıdır. -) - -$(P -Örneğin, $(C Öğrenci) gibi bir sınıfın $(C foreach) ile kullanılmasında ne tür değişkenlere erişileceği açık değildir. O yüzden $(C Öğrenci) sınıfının böyle bir konuda destek vermesi beklenmeyebilir. Öte yandan, başka bir bakış açısı ile, $(C foreach) döngüsünün $(C Öğrenci) nesnesinin notlarına erişmek için kullanılacağı da düşünülebilir. -) - -$(P -Kendi türlerinizin $(C foreach) desteği verip vermeyeceklerine ve vereceklerse ne tür değişkenlere erişim sağlayacaklarına siz karar vermelisiniz. -) - -$(H5 $(IX aralık, foreach) $(C foreach) desteğini aralık işlevleri ile sağlamak) - -$(P -$(IX empty) $(IX front) $(IX popFront) $(C foreach)'in $(C for)'un daha kullanışlısı olduğunu biliyoruz. Şöyle bir $(C foreach) döngüsü olsun: -) - ---- - foreach (eleman; aralık) { - // ... ifadeler ... - } ---- - -$(P -O döngü, derleyici tarafından arka planda bir $(C for) döngüsü olarak şöyle gerçekleştirilir: -) - ---- - for ( ; /* bitmediği sürece */; /* başından daralt */) { - - auto eleman = /* aralığın başındaki */; - - // ... ifadeler ... - } ---- - -$(P -$(C foreach)'in kendi türlerimizle de çalışabilmesi için yukarıdaki üç özel bölümde kullanılacak olan üç özel üye işlev tanımlamak gerekir. Bu üç işlev; döngünün sonunu belirlemek, sonrakine geçmek (aralığı baş tarafından daraltmak), ve en baştakine erişim sağlamak için kullanılır. -) - -$(P -Bu üç üye işlevin isimleri sırasıyla $(C empty), $(C popFront), ve $(C front)'tur. Derleyicinin arka planda ürettiği kod bu üye işlevleri kullanır: -) - ---- - for ( ; !aralık.empty(); aralık.popFront()) { - - auto eleman = aralık.front(); - - // ... ifadeler ... - } ---- - -$(P -Bu üç işlev aşağıdaki gibi işlemelidir: -) - -$(UL - -$(LI $(C .empty()): Aralık tükenmişse $(C true), değilse $(C false) döndürür) - -$(LI $(C .popFront()): Bir sonrakine geçer (aralığı baş tarafından daraltır)) - -$(LI $(C .front()): Baştaki elemanı döndürür) - -) - -$(P -O şekilde işleyen böyle üç üye işleve sahip olması, türün $(C foreach) ile kullanılabilmesi için yeterlidir. -) - -$(H6 Örnek) - -$(P -Belirli aralıkta değerler üreten bir yapı tasarlayalım. Aralığın başını ve sonunu belirleyen değerler, nesne kurulurken belirlensinler. Geleneklere uygun olarak, son değer aralığın $(I dışında) kabul edilsin. Bir anlamda, D'nin $(C baş..son) şeklinde yazılan aralıklarının eşdeğeri olarak çalışan bir tür tanımlayalım: -) - ---- -struct Aralık { - int baş; - int son; - - invariant() { - // baş'ın hiçbir zaman son'dan büyük olmaması gerekir - assert(baş <= son); - } - - bool empty() const { - // baş, son'a eşit olduğunda aralık tükenmiş demektir - return baş == son; - } - - void popFront() { - // Bir sonrakine geçmek, baş'ı bir arttırmaktır. Bu - // işlem, bir anlamda aralığı baş tarafından kısaltır. - ++baş; - } - - int front() const { - // Aralığın başındaki değer, baş'ın kendisidir - return baş; - } -} ---- - -$(P -$(I Not: Ben güvenlik olarak yalnızca $(C invariant) bloğundan yararlandım. Ona ek olarak, $(C popFront) ve $(C front) işlevleri için $(C in) blokları da düşünülebilirdi; o işlevlerin doğru olarak çalışabilmesi için ayrıca aralığın boş olmaması gerekir.) -) - -$(P -O yapının nesnelerini artık $(C foreach) ile şöyle kullanabiliriz: -) - ---- - foreach (eleman; Aralık(3, 7)) { - write(eleman, ' '); - } ---- - -$(P -$(C foreach), o üç işlevden yararlanarak aralıktaki değerleri sonuna kadar, yani $(C empty)'nin dönüş değeri $(C true) olana kadar kullanır: -) - -$(SHELL_SMALL -3 4 5 6 -) - -$(H6 $(IX retro, std.range) Ters sırada ilerlemek için $(C std.range.retro)) - -$(P -$(IX save) $(IX back) $(IX popBack) $(C std.range) modülü aralıklarla ilgili çeşitli olanaklar sunar. Bunlar arasından $(C retro), kendisine verilen aralığı ters sırada kullanır. Türün $(C retro) ile kullanılabilmesi için bu amaca yönelik iki üye işlev daha gerekir: -) - -$(UL - -$(LI $(C .popBack()): Bir öncekine geçer (aralığı son tarafından daraltır)) - -$(LI $(C .back()): Sondaki elemanı döndürür) - -) - -$(P -Ancak, $(C retro)'nun o iki işlevi kullanabilmesi için bir işlevin daha tanımlanmış olması gerekir: -) - -$(UL -$(LI $(C .save()): Aralığın şu andaki durumunun kopyasını döndürür) -) - -$(P -Bu üye işlevler hakkında daha ayrıntılı bilgiyi daha sonra $(LINK2 /ders/d/araliklar.html, Aralıklar bölümünde) göreceğiz. -) - -$(P -Bu üç işlevi $(C Aralık) yapısı için şöyle tanımlayabiliriz: -) - ---- -struct Aralık { -// ... - - void popBack() { - // Bir öncekine geçmek, son'u bir azaltmaktır. Bu - // işlem, bir anlamda aralığı son tarafından kısaltır. - --son; - } - - int back() const { - // Aralığın sonundaki değer, son'dan bir önceki - // değerdir; çünkü gelenek olarak aralığın sonu, - // aralığa dahil değildir. - return son - 1; - } - - Aralık save() const @property { - // Aralık nesnesinin şu andaki durumu bir kopyası - // döndürülerek sağlanabilir. - return this; - } -} ---- - -$(P -Bu türün nesneleri $(C retro) ile kullanılmaya hazırdır: -) - ---- -import std.range; - -// ... - - foreach (eleman; Aralık(3, 7)$(HILITE .retro)) { - write(eleman, ' '); - } ---- - - -$(P -Kodun çıktısından anlaşıldığı gibi, $(C retro) yukarıdaki üye işlevlerden yararlanarak bu aralığı ters sırada kullanır: -) - -$(SHELL_SMALL -6 5 4 3 -) - -$(H5 $(IX opApply) $(IX opApplyReverse) $(C foreach) desteğini $(C opApply) ve $(C opApplyReverse) işlevleri ile sağlamak) - -$(P -$(IX foreach_reverse) Bu başlık altında $(C opApply) için anlatılanlar $(C opApplyReverse) için de geçerlidir. $(C opApplyReverse), nesnenin $(C foreach_reverse) döngüsüyle kullanımını belirler. -) - -$(P -Yukarıdaki üye işlevler, nesneyi sanki bir aralıkmış gibi kullanmayı sağlarlar. O yöntem, nesnelerin $(C foreach) ile tek bir şekilde kullanılmaları durumuna daha uygundur. Örneğin $(C Öğrenciler) gibi bir türün nesnelerinin, öğrencilere $(C foreach) ile teker teker erişim sağlaması, o yöntemle kolayca gerçekleştirilebilir. -) - -$(P -Öte yandan, bazen bir nesne üzerinde farklı şekillerde ilerlemek istenebilir. Bunun örneklerini eşleme tablolarından biliyoruz: Döngü değişkenlerinin tanımına bağlı olarak ya yalnızca elemanlara, ya da hem elemanlara hem de indekslere erişilebiliyordu: -) - ---- - string[string] ingilizcedenTürkçeye; - - // ... - - foreach (türkçesi; ingilizcedenTürkçeye) { - // ... yalnızca elemanlar ... - } - - foreach (ingilizcesi, türkçesi; ingilizcedenTürkçeye) { - // ... indeksler ve elemanlar ... - } ---- - -$(P -$(C opApply) işlevleri, kendi türlerimizi de $(C foreach) ile birden fazla şekilde kullanma olanağı sağlarlar. $(C opApply)'ın nasıl tanımlanması gerektiğini görmeden önce $(C opApply)'ın nasıl çağrıldığını anlamamız gerekiyor. -) - -$(P -Programın işleyişi, $(C foreach)'in kapsamına yazılan işlemler ile $(C opApply) işlevinin işlemleri arasında, belirli bir $(I anlaşmaya) uygun olarak gider gelir. Önce $(C opApply)'ın içi işletilir; $(C opApply) kendi işi sırasında $(C foreach)'in işlemlerini çağırır; ve bu karşılıklı gidiş geliş döngü sonuna kadar devam eder. -) - -$(P -Bu $(I anlaşmayı) açıklamadan önce $(C foreach) döngüsünün yapısını tekrar hatırlatmak istiyorum: -) - ---- -// Programcının yazdığı döngü: - - foreach (/* döngü değişkenleri */; nesne) { - // ... işlemler ... - } ---- - -$(P -$(IX temsilci, foreach) Eğer döngü değişkenlerine uyan bir $(C opApply) işlevi tanımlanmışsa; derleyici, döngü değişkenlerini ve döngü kapsamını kullanarak bir $(I temsilci) oluşturur ve nesnenin $(C opApply) işlevini o temsilci ile çağırır. -) - -$(P -Buna göre, yukarıdaki döngü derleyici tarafından arka planda aşağıdaki koda dönüştürülür. Temsilciyi oluşturan kapsam parantezlerini sarı ile işaretliyorum: -) - ---- -// Derleyicinin arka planda kullandığı kod: - - nesne.opApply(delegate int(/* döngü değişkenleri */) $(HILITE {) - // ... işlemler ... - return sonlandı_mı; - $(HILITE })); ---- - -$(P -Yani, $(C foreach) döngüsü ortadan kalkar; onun yerine nesnenin $(C opApply) işlevi derleyicinin oluşturduğu bir temsilci ile çağrılır. Derleyicinin oluşturduğu bir temsilcinin kullanılıyor olması $(C opApply) işlevinin yazımı konusunda bazı zorunluluklar getirir. -) - -$(P -Bu dönüşümü ve uyulması gereken zorunlulukları şu maddelerle açıklayabiliriz: -) - -$(OL - -$(LI $(C foreach)'in işlemleri temsilciyi oluşturan işlemler haline gelirler. Bu temsilci $(C opApply) tarafından çağrılmalıdır.) - -$(LI Döngü değişkenleri temsilcinin parametreleri haline gelirler. Bu parametrelerin $(C opApply)'ın tanımında $(C ref) olarak işaretlenmeleri gerekir.) - -$(LI Temsilcinin dönüş türü $(C int)'tir. Buna uygun olarak, temsilcinin sonuna derleyici tarafından bir $(C return) satırı eklenir. $(C return)'ün döndürdüğü bilgi, döngünün $(C break) veya $(C return) ile sonlanıp sonlanmadığını anlamak için kullanılır. Eğer sıfır ise döngü devam etmelidir; sıfırdan farklı ise döngü sonlanmalıdır.) - -$(LI Asıl döngü $(C opApply)'ın içinde programcı tarafından gerçekleştirilir.) - -$(LI $(C opApply), temsilcinin döndürmüş olduğu $(C sonlandı_mı) değerini döndürmelidir.) - -) - -$(P -$(C Aralık) yapısını bu anlaşmaya uygun olarak aşağıdaki gibi tanımlayabiliriz. Yukarıdaki maddeleri, ilgili oldukları yerlerde açıklama satırları olarak belirtiyorum: -) - ---- -struct Aralık { - int baş; - int son; - - // (2) (1) - int opApply(int delegate(ref int) işlemler) const { - int sonuç = 0; - - for (int sayı = baş; sayı != son; ++sayı) { // (4) - sonuç = işlemler(sayı); // (1) - - if (sonuç) { - break; // (3) - } - } - - return sonuç; // (5) - } -} ---- - -$(P -Bu yapıyı da $(C foreach) ile aynı şekilde kullanabiliriz: -) - ---- - foreach (eleman; Aralık(3, 7)) { - write(eleman, ' '); - } ---- - -$(P -Çıktısı, aralık işlevleri kullanıldığı zamanki çıktının aynısı olacaktır: -) - -$(SHELL_SMALL -3 4 5 6 -) - -$(H6 Farklı biçimlerde ilerlemek için $(C opApply)'ın yüklenmesi) - -$(P -Nesne üzerinde farklı şekillerde ilerleyebilmek, $(C opApply)'ın değişik türlerdeki temsilcilerle yüklenmesi ile sağlanır. Derleyici, $(C foreach) değişkenlerinin uyduğu bir $(C opApply) yüklemesi bulur ve onu çağırır. -) - -$(P -Örneğin, $(C Aralık) nesnelerinin iki $(C foreach) değişkeni ile de kullanılabilmelerini isteyelim: -) - ---- - foreach ($(HILITE birinci, ikinci); Aralık(0, 15)) { - writef("%s,%s ", birinci, ikinci); - } ---- - -$(P -O kullanım, eşleme tablolarının hem indekslerine hem de elemanlarına $(C foreach) ile erişildiği duruma benzer. -) - -$(P -Bu örnekte, $(C Aralık) yukarıdaki gibi iki değişkenle kullanıldığında art arda iki değere erişiliyor olsun; ve döngünün her ilerletilişinde değerler beşer beşer artsın. Yani yukarıdaki döngünün çıktısı şöyle olsun: -) - -$(SHELL_SMALL -0,1 5,6 10,11 -) - -$(P -Bunu sağlamak için iki değişkenli bir temsilci ile çalışan yeni bir $(C opApply) tanımlamak gerekir. O temsilci $(C opApply) tarafından ve bu kullanıma uygun olan iki değerle çağrılmalıdır: -) - ---- - int opApply(int delegate($(HILITE ref int, ref int)) işlemler) const { - int sonuç = 0; - - for (int i = baş; (i + 1) < son; i += 5) { - int birinci = i; - int ikinci = i + 1; - - sonuç = işlemler($(HILITE birinci, ikinci)); - - if (sonuç) { - break; - } - } - - return sonuç; - } ---- - -$(P -İki değişkenli döngü kullanıldığında üretilen temsilci bu $(C opApply) yüklemesine uyduğu için, derleyici bu tanımı kullanır. -) - -$(P -Tür için anlamlı olduğu sürece başka $(C opApply) işlevleri de tanımlanabilir. -) - -$(P -Hangi $(C opApply) işlevinin seçileceği döngü değişkenlerinin adedi yanında, türleri ile de belirlenebilir. Değişkenlerin türleri $(C foreach) döngüsünde açıkça yazılabilir ve böylece ne tür elemanlar üzerinde ilerlenmek istendiği açıkça belirtilebilir. -) - -$(P -Buna göre, $(C foreach) döngüsünün hem öğrencilere hem de öğretmenlere erişmek için kullanılabileceği bir $(C Okul) sınıfı şöyle tanımlanabilir: -) - ---- -class Okul { - int opApply(int delegate(ref $(HILITE Öğrenci)) işlemler) const { - // ... - } - - int opApply(int delegate(ref $(HILITE Öğretmen)) işlemler) const { - // ... - } -} ---- - -$(P -Bu $(C Okul) türünü kullanan programlar, hangi elemanlar üzerinde ilerleneceğini döngü değişkenini açık olarak yazarak seçebilirler: -) - ---- - foreach ($(HILITE Öğrenci) öğrenci; okul) { - // ... - } - - foreach ($(HILITE Öğretmen) öğretmen; okul) { - // ... - } ---- - -$(P -Derleyici, değişkenin türüne uyan bir temsilci üretecek ve o temsilciye uyan $(C opApply) işlevini çağıracaktır. -) - -$(H5 $(IX döngü sayacı) $(IX sayaç, döngü) Döngü sayacı) - -$(P -$(C foreach)'in dizilerle kullanımında kolaylık sağlayan döngü sayacı bütün türler için otomatik değildir. İstendiğinde kendi türlerimiz için açıkça programlamamız gerekir. -) - -$(H6 $(IX enumerate, std.range) Aralık işlevleriyle döngü sayacı) - -$(P -Eğer $(C foreach) aralık işlevleriyle sağlanmışsa sayaç elde etmenin en kolay yolu $(C std.range) modülünde tanımlı olan ve "numaralandır" anlamına gelen $(C enumerate)'ten yararlanmaktır: -) - ---- -import std.range; - -// ... - - foreach ($(HILITE i), eleman; Aralık(42, 47)$(HILITE .enumerate)) { - writefln("%s: %s", i, eleman); - } ---- - -$(P -$(C enumerate) sıfırdan başlayan sayılar üretir ve bu sayıları asıl aralığın elemanları ile eşleştirir. (Sıfırdan farklı başlangıç değeri de seçilebilir.) Sonuçta, sayaç ve asıl aralıktaki değerler $(C foreach)'in iki döngü değişkeni olarak elde edilirler: -) - -$(SHELL_SMALL -0: 42 -1: 43 -2: 44 -3: 45 -4: 46 -) - -$(H6 $(C opApply) ile döngü sayacı) - -$(P -$(C foreach) desteğinin $(C opApply) ile sağlandığı durumda ise sayaç değişkeninin $(C size_t) türünde ek bir değişken olarak tanımlanması gerekir. Bunu göstermek için noktalardan oluşan ve kendi rengine sahip olan bir poligon yapısı tasarlayalım. -) - -$(P -Bu yapının noktalarını sunan $(I sayaçsız) bir $(C opApply) yukarıdakilere benzer biçimde şöyle tanımlanabilir: -) - ---- -import std.stdio; - -enum Renk { mavi, yeşil, kırmızı } - -struct Nokta { - int x; - int y; -} - -struct Poligon { - Renk renk; - Nokta[] noktalar; - - int $(HILITE opApply)(int delegate(ref const(Nokta)) işlemler) const { - int sonuç = 0; - - foreach (nokta; noktalar) { - sonuç = işlemler(nokta); - - if (sonuç) { - break; - } - } - - return sonuç; - } -} - -void main() { - auto poligon = Poligon(Renk.mavi, - [ Nokta(0, 0), Nokta(1, 1) ] ); - - foreach (nokta; poligon) { - writeln(nokta); - } -} ---- - -$(P -$(C opApply)'ın tanımında da $(C foreach)'ten yararlanıldığına dikkat edin. $(C main) içinde $(C poligon) nesnesi üzerinde işleyen $(C foreach), poligonun $(C noktalar) üyesi üzerinde işletilen bir $(C foreach)'ten yararlanmış olur. -) - -$(P -$(C delegate)'in parametresinin $(C ref const(Nokta)) olduğuna dikkat edin. Bu, bu $(C opApply)'ın elemanların $(C foreach) içinde değiştirilmelerine izin vermediği anlamına gelir. Elemanların değiştirilmelerine izin verilmesi için hem $(C opApply)'ın hem de parametresinin $(C const) belirteci olmadan tanımlanmaları gerekir. -) - -$(P -Çıktısı: -) - -$(SHELL -const(Nokta)(0, 0) -const(Nokta)(1, 1) -) - -$(P -$(C Poligon) türünü bu tanımı ile sayaçlı olarak kullanmaya çalıştığımızda bu kullanım $(C opApply) yüklemesine uymayacağından doğal olarak bir derleme hatasıyla karşılaşırız: -) - ---- - foreach ($(HILITE sayaç), nokta; poligon) { $(DERLEME_HATASI) - writefln("%s: %s", sayaç, nokta); - } ---- - -$(P -Derleme hatası $(C foreach) değişkenlerinin anlaşılamadıklarını bildirir: -) - -$(SHELL -Error: cannot uniquely infer foreach argument types -) - -$(P -Böyle bir kullanımı destekleyen bir $(C opApply) yüklemesi, $(C opApply)'ın aldığı temsilcinin $(C size_t) ve $(C Nokta) türlerinde iki parametre alması ile sağlanmalıdır: -) - ---- - int opApply(int delegate($(HILITE ref size_t), - ref const(Nokta)) işlemler) const { - int sonuç = 0; - - foreach ($(HILITE sayaç), nokta; noktalar) { - sonuç = işlemler($(HILITE sayaç), nokta); - - if (sonuç) { - break; - } - } - - return sonuç; - } ---- - -$(P -Program $(C foreach)'in son kullanımını bu $(C opApply) yüklemesine uydurur ve artık derlenir: -) - -$(SHELL -0: const(Nokta)(0, 0) -1: const(Nokta)(1, 1) -) - -$(P -Bu $(C opApply)'ın tanımında $(C noktalar) üyesi üzerinde işleyen $(C foreach) döngüsünün otomatik sayacından yararlanıldığına dikkat edin. ($(I Temsilci parametresi $(C ref size_t) olarak tanımlanmış olduğu halde, $(C main) içindeki $(C foreach) döngüsü $(C noktalar) üzerinde ilerleyen otomatik sayacı değiştiremez.)) -) - -$(P -Gerektiğinde sayaç değişkeni açıkça tanımlanabilir ve arttırılabilir. Örneğin, aşağıdaki $(C opApply) bu sefer bir $(C while) döngüsünden yararlandığı için sayacı kendisi tanımlıyor ve arttırıyor: -) - ---- - int opApply(int delegate(ref size_t, - ref Eleman) işlemler) const { - int sonuç = 0; - bool devam_mı = true; - - $(HILITE size_t sayaç = 0;) - while (devam_mı) { - // ... - - sonuç = işlemler(sayaç, sıradakiEleman); - - if (sonuç) { - break; - } - - ++sayaç; - } - - return sonuç; - } ---- - -$(H5 Uyarı: $(C foreach)'in işleyişi sırasında topluluk değişmemelidir) - -$(P -Hangi yöntemle olursa olsun, $(C foreach) desteği veren bir tür, döngünün işleyişi sırasında sunduğu $(I topluluk) kavramında bir değişiklik yapmamalıdır: döngünün işleyişi sırasında yeni elemanlar eklememeli ve var olan elemanları silmemelidir. (Var olan elemanların değiştirilmelerinde bir sakınca yoktur.) -) - -$(P -Bu kurala uyulmaması tanımsız davranıştır. -) - -$(PROBLEM_COK - -$(PROBLEM -Yukarıdaki $(C Aralık) gibi çalışan, ama aralıktaki değerleri birer birer değil, belirtilen adım kadar ilerleten bir yapı tanımlayın. Adım bilgisini kurucu işlevinin üçüncü parametresi olarak alsın: - ---- - foreach (sayı; Aralık(0, 10, $(HILITE 2))) { - write(sayı, ' '); - } ---- - -$(P -Sıfırdan 10'a kadar ikişer ikişer ilerlemesi beklenen o $(C Aralık) nesnesinin çıktısı şöyle olsun: -) - -$(SHELL_SMALL -0 2 4 6 8 -) - -) - -$(PROBLEM -Yazı içinde geçen $(C Okul) sınıfını, $(C foreach)'in döngü değişkenlerine göre öğrencilere veya öğretmenlere erişim sağlayacak şekilde yazın. -) - -) - -Macros: - SUBTITLE=Yapı ve Sınıflarda foreach - - DESCRIPTION=D'nin foreach döngüsünün işleyişinin yapılar ve sınıflar için o türlere uygun olacak şekilde belirlenmesi - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial foreach foreach_reverse struct class yapı sınıf opApply opApplyReverse empty popFront front popBack back - -SOZLER= -$(aralik) -$(kapsam) -$(tanimsiz_davranis) -$(temsilci) -$(topluluk) -$(yukleme) diff --git a/ddili/src/ders/d/frontispiece.d b/ddili/src/ders/d/frontispiece.d deleted file mode 100644 index 27f5200..0000000 --- a/ddili/src/ders/d/frontispiece.d +++ /dev/null @@ -1,9 +0,0 @@ -Ddoc - -
    - -
    - -
    - -
    diff --git a/ddili/src/ders/d/giris_cikis.cozum.d b/ddili/src/ders/d/giris_cikis.cozum.d deleted file mode 100644 index 391439f..0000000 --- a/ddili/src/ders/d/giris_cikis.cozum.d +++ /dev/null @@ -1,22 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Standart Giriş ve Çıkış Akımları) - ---- -import std.stdio; - -void main() { - stdout.write(1, ",", 2); - - // Gerektiğinde satırı sonlandırmak için: - writeln(); -} ---- - - -Macros: - SUBTITLE=Standart Giriş ve Çıkış Akımları Problem Çözümü - - DESCRIPTION=Standart Giriş ve Çıkış Akımları Problem Çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial standart giriş ve çıkış problem çözüm diff --git a/ddili/src/ders/d/giris_cikis.d b/ddili/src/ders/d/giris_cikis.d deleted file mode 100644 index 2ba2eeb..0000000 --- a/ddili/src/ders/d/giris_cikis.d +++ /dev/null @@ -1,56 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX standart giriş) $(IX standart çıkış) Standart Giriş ve Çıkış Akımları) - -$(P -Bizim $(I ekran) olarak algıladığımız çıkış, aslında D programının $(I standart çıkışıdır). Standart çıkış $(I karakter) temellidir: yazdırılan bütün bilgi önce karakter karşılığına dönüştürülür ve ondan sonra art arda karakterler olarak standart çıkışa gönderilir. Önceki bölümlerde çıkışa gönderilen tamsayılar, örneğin öğrenci sayısı olan 100 değeri, ekrana aslında tamsayı 100 değeri olarak değil; $(C 1), $(C 0), ve $(C 0) şeklinde üç karakter olarak gönderilmiştir. -) - -$(P -Normalde $(I klavye) olarak algıladığımız standart giriş de bunun tersi olarak çalışır: bilgi art arda karakterler olarak gelir ve ondan sonra programda kullanılacak değerlere dönüştürülür. Örneğin girişten okunan 42 gibi bir değer, aslında $(C 4) ve $(C 2) karakterleri olarak okunur. -) - -$(P -Bu dönüşümler bizim özel bir şey yapmamıza gerek olmadan, otomatik olarak gerçekleşirler. -) - -$(P -$(IX stdin) $(IX stdout) Art arda gelen karakterler kavramına $(I karakter akımı) adı verilir. Bu tanıma uydukları için D programlarının standart girişi ve çıkışı birer karakter akımıdır. Standart giriş akımının ismi $(C stdin), standart çıkış akımının ismi de $(C stdout)'tur. -) - -$(P -Akımları kullanırken normalde akımın ismi, bir nokta, ve o akımla yapılacak işlem yazılır: $(C akım.işlem) gibi. Buna rağmen, çok kullanılan akımlar oldukları için, $(C stdin) ve $(C stdout)'un özellikle belirtilmeleri gerekmez. -) - -$(P -Önceki bölümlerde kullandığımız $(C writeln), aslında $(C stdout.writeln)'in kısaltmasıdır. Benzer şekilde, $(C write) da $(C stdout.write)'in kısaltmasıdır. $(I Merhaba dünya) programını böyle bir kısaltma kullanmadan şöyle de yazabiliriz: -) - ---- -import std.stdio; - -void main() { - stdout.writeln("Merhaba dünya!"); -} ---- - -$(PROBLEM_TEK - -$(P -Yukarıdaki programda $(C stdout)'u yine $(C writeln) işlemiyle kullanın ama bir seferde birden fazla değişken yazdırın. -) - -) - -Macros: - SUBTITLE=Standart Giriş ve Çıkış Akımları - - DESCRIPTION=D dilinde standart giriş ve çıkış akımları stdin ve stdout'un tanıtılmaları - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial standart giriş çıkış stdin stdout - -SOZLER= -$(akim) -$(karakter) -$(standart_cikis) -$(standart_giris) diff --git a/ddili/src/ders/d/giris_duzeni.cozum.d b/ddili/src/ders/d/giris_duzeni.cozum.d deleted file mode 100644 index b47c9b5..0000000 --- a/ddili/src/ders/d/giris_duzeni.cozum.d +++ /dev/null @@ -1,29 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Giriş Düzeni) - -$(P -Tarihin yazımındaki her bir tamsayının yerine $(C %s) yerleştirmek işimize yarayan düzen dizgisini oluşturmaya yeter: -) - ---- -import std.stdio; - -void main() { - int yıl; - int ay; - int gün; - - readf("%s.%s.%s", &yıl, &ay, &gün); - - writeln("Ay: ", ay); -} ---- - - -Macros: - SUBTITLE=Giriş Düzeni Problem Çözümü - - DESCRIPTION=Giriş Düzeni bölümü problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial giriş düzeni format çözüm diff --git a/ddili/src/ders/d/giris_duzeni.d b/ddili/src/ders/d/giris_duzeni.d deleted file mode 100644 index d8c425d..0000000 --- a/ddili/src/ders/d/giris_duzeni.d +++ /dev/null @@ -1,165 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX giriş düzeni) Giriş Düzeni) - -$(P -$(LINK2 /ders/d/cikti_duzeni.html, Çıktı Düzeni) bölümünde anlatılanlara benzer şekilde, girişten gelen verilerin düzeni de belirtilebilir. Bu düzen; hem okunması istenen bilgiyi, hem de gözardı edilmesi istenen bilgiyi belirtebilir. -) - -$(P -Giriş için kullanılan düzen dizgisi C'deki $(C scanf) işlevinin düzen dizgisine benzer. -) - -$(P -Düzen dizgisi olarak şimdiye kadar yaptığımız gibi $(STRING " %s") kullanıldığında, okunmakta olan değişkenin türüne en uygun olan düzende okunur. Örneğin aşağıdaki $(C readf) çağrısında değişkenin türü $(C double) olduğu için girişteki karakterler kesirli sayı olarak okunurlar: -) - ---- - double sayı; - - readf(" %s", &sayı); ---- - -$(P -Düzen dizgisi içinde üç tür bilgi bulunabilir: -) - -$(UL -$(LI $(B Boşluk karakteri): Girişteki $(I sıfır) veya daha fazla boşluk karakteri anlamına gelir ve onların okunup gözardı edilmelerini sağlar.) - -$(LI $(B Düzen belirteci): Önceki bölümdekilere benzer şekilde $(C %) karakteriyle başlar ve girişten gelen karakterlerin hangi türde okunacaklarını belirler.) - -$(LI $(B Başka herhangi karakter): Girişte aynen bulunması beklenen bir karakteri ifade eder ve onun okunup gözardı edilmesini sağlar.) -) - -$(P -O bilgiler sayesinde, girişten gelen veri içerisinden bizim için önemli olanlarını seçip çıkartmak ve geri kalanını gözardı etmek son derece kolaydır. -) - -$(P -Ayrıntıya girmeden önce, bu üç tür bilgiyi kullanan bir örneğe bakalım. Girişte tek satır halinde şöyle bir bilgi bulunsun: -) - -$(SHELL -numara:123 not:90 -) - -$(P -O satır içerisinden bizim için önemli olan iki bilgi, öğrencinin numarası ve notu olsun; yani girişteki $(C numara:) ve $(C not:) gibi karakterlerin bizim için bir önemi bulunmasın. İşte o satır içinden öğrencinin numarasını ve notunu $(I seçen) ve geri kalanını gözardı eden bir düzen dizgisi şöyle yazılabilir: -) - ---- - int numara; - int not; - readf("numara:%s not:%s", &numara, ¬); ---- - -$(P -$(STRING "$(HILITE numara:)%s $(HILITE not:)%s") düzen dizgisinde işaretli olarak gösterilen bütün karakterler girişte aynen bulunmalıdırlar; onlar $(C readf) tarafından girişten okunup gözardı edilirler. -) - -$(P -O düzen dizgisinde kullanılan tek boşluk karakteri, girişte o noktada bulunan bütün boşluk karakterlerinin gözardı edilmelerine neden olur. -) - -$(P -$(C %) karakterinin özel anlamı nedeniyle, girişte $(C %) karakterinin kendisinin gözardı edilmesi istendiğinde $(C %%) şeklinde çift olarak yazılır. -) - -$(P -Tek satırlık bilgi okumak için $(LINK2 /ders/d/dizgiler.html, Dizgiler bölümünde) $(C strip(readln())) yöntemi önerilmişti. Düzen dizgisinin sonuna yazılan $(C \n) karakteri sayesinde $(C readf) de bu amaçla kullanılabilir: -) - ---- -import std.stdio; - -void main() { - write("Adınız : "); - string ad; - readf(" %s\n", &ad); // ← sonda \n - - write("Soyadınız: "); - string soyad; - readf(" %s\n", &soyad); // ← sonda \n - - write("Yaşınız : "); - int yaş; - readf(" %s", &yaş); - - writefln("%s %s (%s)", ad, soyad, yaş); -} ---- - -$(P -Yukarıda $(C ad) ve $(C soyad) okunurken kullanılan düzen dizgileri satır sonunda basılan Enter tuşunun oluşturduğu $(C \n) karakterinin de okunmasını ve gözardı edilmesini sağlarlar. Buna rağmen, satır sonlarındaki olası boşluk karakterlerinden kurtulmak için yine de $(C strip())'i çağırmak gerekebilir. -) - -$(H5 Düzen karakterleri) - -$(P -Verinin nasıl okunacağı aşağıdaki düzen karakterleriyle belirtilir: -) - -$(P $(IX %d, giriş) $(C d): Onlu sistemde tamsayı oku) - -$(P $(IX %o, giriş) $(C o): Sekizli sistemde tamsayı oku) - -$(P $(IX %x, giriş) $(C x): On altılı sistemde tamsayı oku) - -$(P $(IX %f, giriş) $(C f): Kesirli sayı oku) - -$(P $(IX %s, giriş) $(C s): Değişkenin türüne uygun olan düzende oku; en yaygın kullanılan belirteç budur) - -$(P $(IX %c) $(C c): Tek karakter oku; bu belirteç boşlukları da okur (gözardı edilmelerini önler)) - -$(P -Örneğin, girişte 3 tane "23" bulunduğunu varsayarsak, her birisi aşağıdaki farklı düzen karakterlerine göre farklı olarak okunur: -) - ---- - int sayı_d; - int sayı_o; - int sayı_x; - - readf(" %d %o %x", &sayı_d, &sayı_o, &sayı_x); - - writeln("onlu olarak okununca : ", sayı_d); - writeln("sekizli olarak okununca : ", sayı_o); - writeln("on altılı olarak okununca: ", sayı_x); ---- - -$(P -3 defa "23" girildiği halde her birisi farklı okunur: -) - -$(SHELL -onlu olarak okununca : 23 -sekizli olarak okununca : 19 -on altılı olarak okununca: 35 -) - -$(P -$(I Not: "23", sekizli düzende 2x8+3=19 değerinde, ve on altılı düzende 2x16+3=35 değerindedir.) -) - -$(PROBLEM_TEK - -$(P -Girişten $(I yıl.ay.gün) düzeninde bir tarih bilgisi gelsin. Ekrana kaçıncı ay olduğunu yazdırın. Örneğin 2009.09.30 geldiğinde 9 yazılsın. -) - -) - -Macros: - SUBTITLE=Giriş Düzeni - - DESCRIPTION=Girişten bilginin belirli bir düzende okunması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial giriş düzen format - -SOZLER= -$(cokme) -$(duzen) -$(islev) -$(parametre) -$(phobos) diff --git a/ddili/src/ders/d/gostergeler.cozum.d b/ddili/src/ders/d/gostergeler.cozum.d deleted file mode 100644 index 34e9f77..0000000 --- a/ddili/src/ders/d/gostergeler.cozum.d +++ /dev/null @@ -1,202 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Göstergeler) - -$(OL - -$(LI Parametre türleri yalnızca $(C int) olduğunda işleve $(C main()) içindeki değişkenlerin kopyalarının gönderildiklerini biliyorsunuz. $(C main()) içindeki değişkenlerin referanslarını edinmenin bir yolu, parametreleri $(C ref int) olarak tanımlamaktır. - -$(P -Diğer bir yol, o değişkenlere erişim sağlayan göstergeler göndermektir. Programın değişen yerlerini sarı ile işaretliyorum: -) - ---- -void değişTokuş(int $(HILITE *) birinci, int $(HILITE *) ikinci) { - int geçici = $(HILITE *)birinci; - $(HILITE *)birinci = $(HILITE *)ikinci; - $(HILITE *)ikinci = geçici; -} - -void main() { - int i = 1; - int j = 2; - - değişTokuş($(HILITE &)i, $(HILITE &)j); - - assert(i == 2); - assert(j == 1); -} ---- - -) - -$(LI Hem $(C Düğüm) hem de $(C Liste) $(C int) türüne bağlı olarak yazılmışlardı. Bu iki yapıyı şablona dönüştürmenin yolu, tanımlanırken isimlerinden sonra $(C (T)) eklemek ve tanımlarındaki $(C int)'leri $(C T) ile değiştirmektir. Değişen yerlerini sarıyla işaretliyorum: - ---- -$(CODE_NAME Liste)struct Düğüm$(HILITE (T)) { - $(HILITE T) eleman; - Düğüm * sonraki; - - string toString() const { - string sonuç = to!string(eleman); - - if (sonraki) { - sonuç ~= " -> " ~ to!string(*sonraki); - } - - return sonuç; - } -} - -struct Liste$(HILITE (T)) { - Düğüm$(HILITE !T) * baş; - - void başınaEkle($(HILITE T) eleman) { - baş = new Düğüm$(HILITE !T)(eleman, baş); - } - - string toString() const { - return format("(%s)", baş ? to!string(*baş) : ""); - } -} ---- - -$(P -$(C Liste)'yi artık $(C int)'ten başka türlerle de deneyebiliriz: -) - ---- -$(CODE_XREF Liste)import std.stdio; -import std.conv; -import std.string; - -// ... - -struct Nokta { - double x; - double y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} - -void main() { - $(HILITE Liste!Nokta) noktalar; - - noktalar.başınaEkle(Nokta(1.1, 2.2)); - noktalar.başınaEkle(Nokta(3.3, 4.4)); - noktalar.başınaEkle(Nokta(5.5, 6.6)); - - writeln(noktalar); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -((5.5,6.6) -> (3.3,4.4) -> (1.1,2.2)) -) - -) - -$(LI Bu durumda sondaki düğümü gösteren bir üyeye daha ihtiyacımız olacak. Açıklamaları programın içine yerleştirdim: - ---- -struct Liste(T) { - Düğüm!T * baş; - $(HILITE Düğüm!T * son); - - void sonunaEkle(T eleman) { - /* Sona eklenen elemandan sonra düğüm bulunmadığından - * 'sonraki' düğüm olarak 'null' gönderiyoruz. */ - auto yeniDüğüm = new Düğüm!T(eleman, null); - - if (!baş) { - /* Liste boşmuş. Şimdi 'baş' bu düğümdür. */ - baş = yeniDüğüm; - } - - if (son) { - /* Şu andaki 'son'dan sonraya bu düğümü - * yerleştiriyoruz. */ - son.sonraki = yeniDüğüm; - } - - /* Bu düğüm yeni 'son' oluyor. */ - son = yeniDüğüm; - } - - void başınaEkle(T eleman) { - auto yeniDüğüm = new Düğüm!T(eleman, baş); - - /* Bu düğüm yeni 'baş' oluyor. */ - baş = yeniDüğüm; - - if (!son) { - /* Liste boşmuş. Şimdi 'son' bu düğümdür. */ - son = yeniDüğüm; - } - } - - string toString() const { - return format("(%s)", baş ? to!string(*baş) : ""); - } -} ---- - -$(P -$(C başınaEkle()) işlevi aslında daha kısa olarak da yazılabilir: -) - ---- - void başınaEkle(T eleman) { - baş = new Düğüm!T(eleman, baş); - - if (!son) { - son = baş; - } - } ---- - -$(P -Yukarıdaki $(C Nokta) nesnelerinin tek değerli olanlarını başa, çift değerli olanlarını sona ekleyen bir deneme: -) - ---- -void $(CODE_DONT_TEST)main() { - Liste!Nokta noktalar; - - foreach (i; 1 .. 7) { - if (i % 2) { - noktalar.başınaEkle(Nokta(i, i)); - - } else { - noktalar.sonunaEkle(Nokta(i, i)); - } - } - - writeln(noktalar); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -((5,5) -> (3,3) -> (1,1) -> (2,2) -> (4,4) -> (6,6)) -) - -) - -) - -Macros: - SUBTITLE=Göstergeler Problem Çözümleri - - DESCRIPTION=Göstergeler Problem Çözümleri - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial gösterge problem çözüm diff --git a/ddili/src/ders/d/gostergeler.d b/ddili/src/ders/d/gostergeler.d deleted file mode 100644 index 2b371c1..0000000 --- a/ddili/src/ders/d/gostergeler.d +++ /dev/null @@ -1,1647 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX gösterge) Göstergeler) - -$(P -Göstergeler başka değişkenlere erişim sağlamak için kullanılırlar. Değerleri, erişim sağladıkları değişkenlerin adresleridir. -) - -$(P -Göstergeler her türden değişkeni, nesneyi, ve hatta başka göstergeleri de gösterebilirler. Ben bu bölümde kısa olsun diye, bunların hepsinin yerine $(I değişken) sözünü kullanacağım. -) - -$(P -Göstergeler mikro işlemcilerin en temel olanaklarındandır ve sistem programcılığının önemli bir parçasıdır. -) - -$(P -D'nin gösterge kavramı ve kullanımı C'den geçmiştir. C öğrenenlerin anlamakta en çok zorlandıkları olanak göstergeler olduğu halde, D'de göstergelerin çok daha kolay öğrenileceğini düşünüyorum. Bunun nedeni, göstergelerin amaçlarından bazılarının D'nin başka olanakları tarafından zaten karşılanıyor olmasıdır. Bu yüzden, hem bir çok durumda gösterge kullanılması gerekmez - hem de başka D olanaklarının zaten anlaşılmış olması göstergelerin anlaşılmalarını da kolaylaştırır. -) - -$(P -Bu bölümde özellikle basit olarak seçtiğim örnekler göstergelerin kullanım amaçlarını anlatma konusunda yetersiz kalabilirler. Yazımlarını ve kullanımlarını öğrenirken bunu gözardı edebilirsiniz. En sonda vereceğim örneklerin daha anlamlı olacaklarını düşünüyorum. -) - -$(P -Ek olarak, örneklerde basitçe $(C gösterge) diye seçtiğim isimlerin kullanışsız olduklarını aklınızda bulundurun. Kendi programlarınızda her ismi anlamlı ve açıklayıcı olarak seçmeye özen gösterin. -) - -$(H5 $(IX referans kavramı) Referans kavramı) - -$(P -Göstergelere geçmeden önce göstergelerin temel amacı olan $(I referans) kavramını şimdiye kadarki bölümlerden tanıdığımız D olanakları ile kısaca hatırlayalım. -) - -$(H6 $(C foreach)'in $(C ref) değişkenleri) - -$(P -$(LINK2 /ders/d/foreach_dongusu.html, $(C foreach) Döngüsü bölümünde) gördüğümüz gibi, döngü değişkenleri normalde elemanların $(I kopyalarıdır): -) - ---- -import std.stdio; - -void main() { - int[] dizi = [ 1, 11, 111 ]; - - foreach (sayı; dizi) { - sayı = 0; // ← kopya değişir; asıl eleman değişmez - } - - writeln("Döngüden sonra elemanlar: ", dizi); -} ---- - -$(P -Yukarıdaki döngü içinde sıfırlanmakta olan $(C sayı) değişkeni her seferinde dizi elemanlarından birisinin $(I kopyasıdır). Onun değiştirilmesi dizideki asıl elemanı etkilemez: -) - -$(SHELL_SMALL -Döngüden sonra elemanlar: 1 11 111 -) - -$(P -Dizideki elemanların kendilerinin değişmeleri istendiğinde $(C foreach) değişkeni $(C ref) olarak tanımlanır: -) - ---- - foreach ($(HILITE ref) sayı; dizi) { - sayı = 0; // ← asıl eleman değişir - } ---- - -$(P -$(C sayı) bu sefer dizideki asıl elemanın takma ismi gibi işlem görür ve dizideki asıl elemanlar değişir: -) - -$(SHELL_SMALL -Döngüden sonra elemanlar: 0 0 0 -) - -$(H6 $(C ref) işlev parametreleri) - -$(P -$(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) gördüğümüz gibi, $(I değer türünden) olan işlev parametreleri normalde başka değişkenlerin kopyalarıdır: -) - ---- -import std.stdio; - -void yarımEkle(double değer) { - değer += 0.5; // ← main'deki değer değişmez -} - -void main() { - double değer = 1.5; - - yarımEkle(değer); - - writeln("İşlevden sonraki değer: ", değer); -} ---- - -$(P -İşlev parametresi $(C ref) olarak tanımlanmadığından, işlev içindeki atama yalnızca işlevin yerel değişkeni olan $(C değer)'i etkiler. $(C main)'deki $(C değer) değişmez: -) - -$(SHELL_SMALL -İşlevden sonraki değer: 1.5 -) - -$(P -İşlev parametresinin, işlevin çağrıldığı yerdeki değişkenin takma ismi olması için $(C ref) anahtar sözcüğü kullanılır: -) - ---- -void yarımEkle($(HILITE ref) double değer) { - değer += 0.5; -} ---- - -$(P -Bu sefer $(C main) içindeki $(C değer) etkilenmiş olur: -) - -$(SHELL_SMALL -İşlevden sonraki değer: 2 -) - -$(H6 Referans türleri) - -$(P -D'de bazı türler referans türleridir. Bu türlerden olan değişkenler sahip olmadıkları başka değerlere erişim sağlarlar: -) - -$(UL -$(LI Sınıf değişkenleri) -$(LI Dinamik diziler) -$(LI Eşleme tabloları) -) - -$(P -Referans kavramını $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar bölümünde) görmüştük. Burada o bölüme dahil etmediğim sınıflar üzerinde bir örnek göstermek istiyorum: -) - ---- -import std.stdio; - -class TükenmezKalem { - double mürekkep; - - this() { - mürekkep = 15; - } - - void kullan(double miktar) { - mürekkep -= miktar; - } -} - -void main() { - auto kalem = new TükenmezKalem; - auto başkaKalem = kalem; // ← şimdi ikisi de aynı nesneye - // erişim sağlarlar - - writefln("Önce : %s %s", - kalem.mürekkep, başkaKalem.mürekkep); - - kalem.kullan(1); // ← aynı nesne kullanılır - başkaKalem.kullan(2); // ← aynı nesne kullanılır - - writefln("Sonra: %s %s", - kalem.mürekkep, başkaKalem.mürekkep); -} ---- - -$(P -Sınıflar referans türleri olduklarından, farklı sınıf değişkenleri olan $(C kalem) ve $(C başkaKalem) tek $(C TükenmezKalem) nesnesine erişim sağlamaktadır. Sonuçta, iki değişkenin kullanılması da aynı nesneyi etkiler: -) - -$(SHELL_SMALL -Önce : 15 15 -Sonra: 12 12 -) - -$(P -Bu sınıf nesnesinin ve ona erişim sağlayan iki sınıf değişkeninin bellekte şu şekilde durduklarını düşünebiliriz: -) - -$(MONO - (TükenmezKalem nesnesi) kalem başkaKalem - ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── - │ mürekkep │ │ o │ │ o │ - ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── - ▲ │ │ - │ │ │ - └────────────────────┴────────────┘ -) - -$(P -Referans kavramı yukarıdaki şekildeki gibidir: Referanslar asıl değişkenleri $(I gösterirler). -) - -$(P -Programlama dillerindeki referans ve gösterge kavramları perde arkasında mikro işlemcilerin $(I gösterme) amacıyla kullanılan yazmaçları ile gerçekleştirilir. -) - -$(P -D'nin yukarıda hatırlattığım üst düzey olanakları da perde arkasında göstergelerle gerçekleştirilmiştir. Bu yüzden hem zaten çok etkin çalışırlar hem de açıkça gösterge kullanmaya gerek bırakmazlar. Buna rağmen, başka sistem programlama dillerinde de olduğu gibi, göstergeler D programcılığında da mutlaka bilinmelidir. -) - -$(H5 $(IX *, gösterge tanımı) Tanımlanması) - -$(P -D'nin gösterge söz dizimi aynı C'de olduğu gibidir. Bu, C bilen programcılar için bir kolaylık olarak görülse de, özellikle $(C *) işlecinin farklı anlamlara sahip olması C'de olduğu gibi D'de de öğrenmeyi güçleştirebilir. -) - -$(P -Biraz aşağıda anlatacağım $(I her türü gösterebilen gösterge) dışındaki göstergeler ancak belirli türden bir değişkeni gösterebilirler. Örneğin bir $(C int) göstergesi yalnızca $(C int) türünden olan değişkenleri gösterebilir. -) - -$(P -Bir gösterge tanımlanırken, önce hangi türden değer göstereceği sonra da bir $(C *) karakteri yazılır: -) - ---- - $(I $(D_KEYWORD göstereceği_tür)) * $(I göstergenin_ismi); ---- - -$(P -Bir $(C int)'i gösterecek olan bir gösterge şöyle tanımlanabilir: -) - ---- - int * benimGöstergem; ---- - -$(P -Böyle bir tanımda $(C *) karakterini "göstergesi" diye okuyabilirsiniz. $(C benimGöstergem)'in türü bir $(C int*)'dır; yani bir "int göstergesidir". $(C *) karakterinden önceki ve sonraki boşlukların yazılmaları isteğe bağlıdır ve aşağıdaki gibi kullanımlar da çok yaygındır: -) - ---- - int* benimGöstergem; - int *benimGöstergem; ---- - -$(P -Tek başına tür ismi olarak "int göstergesi" anlamında kullanıldığında, boşluksuz olarak $(C int*) olarak yazılması da çok yaygındır. -) - -$(H5 $(IX &, adres) Göstergenin değeri ve adres alma işleci $(C &)) - -$(P -Göstergeler de değişkendir ve her değişkenin olduğu gibi onların da değerleri vardır. Değer atanmayan göstergelerin varsayılan değeri, hiçbir değişkene erişim sağlamama değeri olan $(C null)'dır. -) - -$(P -Bir göstergenin hangi değişkeni gösterdiği (yani $(I erişim sağladığı)), göstergenin değer olarak o değişkenin adresini taşıması ile sağlanır. Başka bir deyişle, gösterge o adresteki değişkeni gösterir. -) - -$(P -Şimdiye kadar $(C readf) işlevi ile çok kullandığımız $(C &) işlecini $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar bölümünden) de hatırlayacaksınız. Bu işleç, önüne yazıldığı değişkenin adresini alır. Bu adres değeri, gösterge değeri olarak kullanılabilir: -) - ---- - int beygirGücü = 180; - int * benimGöstergem = $(HILITE &)beygirGücü; ---- - -$(P -Yukarıdaki ifadede göstergenin $(C beygirGücü)'nün adresi ile ilklenmesi, $(C benimGöstergem)'in $(C beygirGücü)'nü $(I göstermesini) sağlar. -) - -$(P -Göstergenin değeri $(C beygirGücü)'nün adresi ile aynıdır: -) - ---- - writeln("beygirGücü'nün adresi : ", &beygirGücü); - writeln("benimGöstergem'in değeri: ", benimGöstergem); ---- - -$(SHELL_SMALL -beygirGücü'nün adresi : 7FFF2CE73F10 -benimGöstergem'in değeri: 7FFF2CE73F10 -) - -$(P $(I Not: Adres değeri siz denediğinizde farklı olacaktır. $(C beygirGücü), programın işletim sisteminden aldığı daha büyük bir belleğin bir yerinde bulunur. Bu yer programın her çalıştırılışında büyük olasılıkla farklı bir adreste bulunacaktır.) -) - -$(P -Bir göstergenin değerinin erişim sağladığı değişkenin adresi olduğunu ve böylece o değişkeni $(I gösterdiğini) referanslara benzer biçimde şöyle düşünebiliriz: -) - -$(MONO - 7FFF2CE73F10 adresindeki başka bir adresteki - beygirGücü benimGöstergem -───┬──────────────────┬─── ───┬────────────────┬─── - │ 180 │ │ 7FFF2CE73F10 │ -───┴──────────────────┴─── ───┴────────│───────┴─── - ▲ │ - │ │ - └─────────────────────────────┘ -) - -$(P -$(C beygirGücü)'nün değeri 180, $(C benimGöstergem)'in değeri de $(C beygirGücü)'nün adresidir. -) - -$(P -Göstergeler de değişken olduklarından, onların adreslerini de $(C &) işleci ile öğrenebiliriz: -) - ---- - writeln("benimGöstergem'in adresi: ", &benimGöstergem); ---- - -$(SHELL_SMALL -benimGöstergem'in adresi: 7FFF2CE73F18 -) - -$(P -$(C beygirGücü) ile $(C benimGöstergem)'in adreslerinin arasındaki farkın bu örnekte 8 olduğuna bakarak ve $(C beygirGücü)'nün türü olan $(C int)'in büyüklüğünün 4 bayt olduğunu hatırlayarak bu iki değişkenin bellekte 4 bayt ötede bulundukları sonucunu çıkartabiliriz. -) - -$(P -$(I Gösterme) kavramını belirtmek için kullandığım oku da kaldırırsak, bir şerit gibi soldan sağa doğru uzadığını hayal ettiğimiz belleği şimdi şöyle düşünebiliriz: -) - - -$(MONO - 7FFF2CE73F10 7FFF2CE73F14 7FFF2CE73F18 - : : : : - ───┬────────────────┬────────────────┬────────────────┬─── - │ 180 │ (boş) │ 7FFF2CE73F10 │ - ───┴────────────────┴────────────────┴────────────────┴─── -) - -$(P -Kaynak kodda geçen değişken ismi, işlev ismi, anahtar sözcük, vs. gibi isimler D gibi derlemeli diller ile oluşturulan programların içinde bulunmazlar. Örneğin, programcının isim vererek tanımladığı ve kullandığı değişkenler program içinde mikro işlemcinin anladığı adreslere ve değerlere dönüşürler. -) - -$(P $(I Not: Programda kullanılan isimler hata ayıklayıcıda yararlanılmak üzere programın) debug $(I halinde de bulunurlar ama o isimlerin programın işleyişiyle ilgileri yoktur.) -) - -$(H5 $(IX *, erişim işleci) Erişim işleci $(C *)) - -$(P -Çarpma işleminden tanıdığımız $(C *) karakterinin gösterge tanımlarken tür isminden sonra yazıldığını yukarıda gördük. Göstergeleri öğrenirken karşılaşılan bir güçlük, bu karakterin göstergenin gösterdiği değişkene erişmek için de kullanılmasıdır. -) - -$(P -Bir göstergenin isminden önce yazıldığında, $(I göstergenin erişim sağladığı değer) anlamına gelir: -) - ---- - writeln("Gösterdiği değer: ", $(HILITE *)benimGöstergem); ---- - -$(SHELL_SMALL -Gösterdiği değer: 180 -) - -$(H5 $(IX ., gösterge) Gösterdiğinin üyesine erişim için $(C .) (nokta) işleci) - -$(P $(I Not: Eğer göstergeleri C'den tanıyorsanız, bu işleç C'deki $(C ->) işleci ile aynıdır.) -) - -$(P -$(C *) işlecinin gösterilen değişkene erişim için kullanıldığını gördük. Bu, temel türleri gösteren göstergeler için yeterli derecede kullanışlıdır: $(C *benimGöstergem) yazılarak gösterilen değere kolayca erişilir. -) - -$(P -Gösterilen değişken yapı veya sınıf nesnesi olduğunda ise bu yazım sıkıntılı hale gelir. Örnek olarak $(C x) ve $(C y) üyeleri ile iki boyutlu düzlemdeki bir noktayı ifade eden bir yapıya bakalım: -) - ---- -struct Konum { - int x; - int y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} ---- - -$(P -O türden bir değişkeni gösteren bir göstergeyi aşağıdaki gibi tanımlayabiliriz ve gösterdiğine erişebiliriz: -) - ---- - auto merkez = Konum(0, 0); - Konum * gösterge = $(HILITE &)merkez; // tanım - writeln($(HILITE *)gösterge); // erişim ---- - -$(P -$(C toString) işlevi tanımlanmış olduğundan, o kullanım $(C Konum) nesnesini yazdırmak için yeterlidir: -) - -$(SHELL_SMALL -(0,0) -) - -$(P -Ancak, gösterilen nesnenin bir üyesine erişmek için $(C *) işleci kullanıldığında kod karmaşıklaşır: -) - ---- - // 10 birim sağa ötele - (*gösterge).x += 10; ---- - -$(P -O ifade $(C merkez) nesnesinin $(C x) üyesinin değerini değiştirmektedir. Bunu şu adımlarla açıklayabiliriz: -) - -$(UL -$(LI $(C gösterge): $(C merkez)'i gösteren gösterge) - -$(LI $(C $(HILITE *)gösterge): Nesneye erişim; yani $(C merkez)'in kendisi) - -$(LI $(C $(HILITE ()*gösterge$(HILITE ))): Nokta karakteri gösterge'ye değil, onun gösterdiğine uygulansın diye gereken parantezler) - -$(LI $(C (*gösterge)$(HILITE .x)): Gösterdiği nesnenin $(C x) üyesi) -) - -$(P -Gösterilen nesnenin üyesine erişim böyle karışık bir şekilde yazılmak zorunda kalınmasın diye, $(C .) (nokta) işleci göstergenin kendisine uygulanır ama $(I gösterdiğinin üyesine) erişim sağlar. Yukarıdaki ifadeyi çok daha kısa olarak şöyle yazabiliriz: -) - ---- - $(HILITE gösterge.x) += 10; ---- - -$(P -Daha basit olan $(C gösterge.x) ifadesi yine $(C merkez)'in $(C x) üyesine eriştirmiştir: -) - -$(SHELL_SMALL -(10,0) -) - -$(P -Bunun sınıflardaki kullanımla aynı olduğuna dikkat edin. Bir sınıf $(I değişkenine) doğrudan uygulanan $(C .) (nokta) işleci aslında sınıf $(I nesnesinin) üyesine erişim sağlar: -) - ---- -class SınıfTürü { - int üye; -} - -// ... - - // Solda değişken, sağda nesne - SınıfTürü değişken = new SınıfTürü; - - // Değişkene uygulanır ama nesnenin üyesine erişir - değişken.üye = 42; ---- - -$(P -$(LINK2 /ders/d/siniflar.html, Sınıflar bölümünden) hatırlayacağınız gibi, yukarıdaki koddaki nesne, $(C new) ile sağda isimsiz olarak oluşturulur. $(C değişken), o nesneye erişim sağlayan bir sınıf değişkenidir. Değişkene uygulanan $(C .) (nokta) işleci aslında asıl nesnenin üyesine erişim sağlar. -) - -$(P -Aynı durumun göstergelerde de bulunması sınıf değişkenleri ile göstergelerin temelde benzer biçimde gerçekleştirildiklerini ortaya koyar. -) - -$(P -Bu kullanımın hem sınıflarda hem de göstergelerde bir istisnası vardır. $(C .) (nokta) işleciyle erişilen $(C .sizeof) gibi tür nitelikleri türün kendisine uygulanır, nesneye değil: -) - ---- - char c; - char * g; - - writeln(g.sizeof); // göstergenin uzunluğu, char'ın değil ---- - -$(SHELL_SMALL -8 -) - -$(H5 Gösterge değerinin değiştirilmesi) - -$(P -Göstergelerin değerleri arttırılabilir ve azaltılabilir, ve göstergeler toplama ve çıkarma işlemlerinde kullanılabilir: -) - ---- - ++birGösterge; - --birGösterge; - birGösterge += 2; - birGösterge -= 2; - writeln(birGösterge + 3); - writeln(birGösterge - 3); ---- - -$(P -Aritmetik işlemlerden alıştığımızdan farklı olarak, bu işlemler göstergenin değerini belirtilen miktar kadar değiştirmezler. Göstergenin değeri, $(I belirtilen miktar kadar sonraki (veya önceki)) değişkeni gösterecek biçimde değişir. -) - -$(P -Örneğin, göstergenin değerinin $(C ++) işleciyle arttırılması o göstergenin bellekte bir sonra bulunan değişkeni göstermesini sağlar: -) - ---- - ++birGösterge; // daha önce gösterdiğinden bir sonraki - // değişkeni göstermeye başlar ---- - -$(P -Bunun sağlanabilmesi için göstergenin değerinin türün büyüklüğü kadar arttırılması gerekir. Örneğin, $(C int)'in büyüklüğü 4 olduğundan $(C int*) türündeki bir göstergenin değeri $(C ++) işlemi sonucunda 4 artar. -) - -$(P $(B Uyarı): Göstergelerin programa ait olmayan adresleri göstermeleri tanımsız davranıştır. Erişmek için kullanılmasa bile, bir göstergenin var olmayan bir değişkeni göstermesi hatalıdır. ($(I Not: Bunun tek istisnası, bir dizinin sonuncu elemanından sonraki hayali elemanın gösterilebilmesidir. Bunu aşağıda açıklıyorum.)) -) - -$(P -Örneğin, yukarıda tek $(C int) olarak tanımlanmış olan $(C beygirGücü) değişkenini gösteren göstergenin arttırılması yasal değildir: -) - ---- - ++benimGöstergem; $(CODE_NOTE_WRONG tanımsız davranış) ---- - -$(P -Tanımsız davranış, o işlemin sonucunda ne olacağının belirsiz olması anlamına gelir. O işlem sonucunda programın çökeceği sistemler bulunabilir. Modern bilgisayarlardaki mikro işlemcilerde ise göstergenin değeri büyük olasılıkla 4 sonraki bellek adresine sahip olacak ve gösterge yukarıda "(boş)" olarak işaretlenmiş olan alanı gösterecektir. -) - -$(P -O yüzden, göstergelerin değerlerinin arttırılması veya azaltılması ancak yan yana bulunduklarından emin olunan değişkenler gösterildiğinde kullanılmalıdır. Diziler (ve dizgiler) bu tanıma uyarlar: Bir dizinin elemanları bellekte yan yanadır (yani $(I art ardadır)). -) - -$(P -Dizi elemanını gösteren bir göstergenin değerinin $(C ++) işleci ile artırılması onun bir sonraki elemanı göstermesini sağlar: -) - ---- -import std.stdio; -import std.string; -import std.conv; - -enum Renk { kırmızı, sarı, mavi } - -struct KurşunKalem { - Renk renk; - double uzunluk; - - string toString() const { - return format("%s santimlik %s bir kalem", - uzunluk, renk); - } -} - -void main() { - writeln("KurşunKalem nesnelerinin büyüklüğü: ", - KurşunKalem.sizeof, " bayt"); - - KurşunKalem[] kalemler = [ KurşunKalem(Renk.kırmızı, 11), - KurşunKalem(Renk.sarı, 12), - KurşunKalem(Renk.mavi, 13) ]; - - $(HILITE KurşunKalem * gösterge) = $(HILITE &)kalemler[0]; // (1) - - for (int i = 0; i != kalemler.length; ++i) { - writeln("gösterge değeri: ", $(HILITE gösterge)); // (2) - - writeln("kalem: ", $(HILITE *gösterge)); // (3) - $(HILITE ++gösterge); // (4) - } -} ---- - -$(OL -$(LI Tanımlanması: Dizinin ilk elemanının adresi ile ilklenmektedir) -$(LI Değerinin kullanılması: Değeri, gösterdiği elemanın adresidir) -$(LI Gösterdiği nesneye erişim) -$(LI Bir sonraki nesneyi göstermesi) -) - -$(P -Çıktısı: -) - -$(SHELL -KurşunKalem nesnelerinin büyüklüğü: 12 bayt -gösterge değeri: 114FC0 -kalem: 11 santimlik kırmızı bir kalem -gösterge değeri: 114FCC -kalem: 12 santimlik sarı bir kalem -gösterge değeri: 114FD8 -kalem: 13 santimlik mavi bir kalem -) - -$(P -Dikkat ederseniz, yukarıdaki döngü $(C kalemler.length) kere tekrarlanmakta ve o yüzden gösterge hep var olan bir elemanı göstermektedir. -) - -$(H5 Göstergeler risklidir) - -$(P -Göstergelerin doğru olarak kullanılıp kullanılmadıkları konusunda denetim sağlanamaz. Ne derleyici, ne de çalışma zamanındaki denetimler bunu garantileyebilirler. Bir göstergenin değerinin her zaman için geçerli olması programcının sorumluluğundadır. -) - -$(P -O yüzden, göstergeleri kullanmayı düşünmeden önce D'nin üst düzey ve güvenli olanaklarının yeterli olup olmadıklarına bakmanızı öneririm. -) - -$(H5 $(IX eleman, sonuncudan sonraki) Dizinin son elemanından bir sonrası) - -$(P -Dizinin sonuncu elemanından hemen sonraki hayali elemanın gösterilmesi yasaldır. -) - -$(P -Bu, dilimlerden alışık olduğumuz aralık kavramına benzeyen yöntemlerde kullanışlıdır. Hatırlarsanız, dilim aralıklarının ikinci indeksi işlem yapılacak olan elemanlardan $(I bir sonrasını) gösterir: -) - ---- - int[] sayılar = [ 0, 1, 2, 3 ]; - writeln(sayılar[1 .. 3]); // 1 ve 2 dahil, 3 hariç ---- - -$(P -Bu yöntem göstergelerle de kullanılabilir. Başlangıç göstergesinin ilk elemanı göstermesi ve bitiş göstergesinin son elemandan sonraki elemanı göstermesi yaygın bir işlev tasarımıdır. -) - -$(P -Bunu bir işlevin parametrelerinde görelim: -) - ---- -import std.stdio; - -// Kendisine verilen aralıktaki değerleri 10 katına çıkartır -void onKatı(int * baş, int * son) { - while (baş != son) { - *baş *= 10; - ++baş; - } -} - -void main() { - int[] sayılar = [ 0, 1, 2, 3 ]; - int * baş = &sayılar[1]; // ikinci elemanın adresi - onKatı(baş, baş + 2); // ondan iki sonrakinin adresi - writeln(sayılar); -} ---- - -$(P -$(C baş + 2) değeri, $(C baş)'ın gösterdiğinden 2 sonraki elemanın, yani indeksi 3 olan elemanın adresi anlamına gelir. -) - -$(P -Yukarıdaki $(C onKatı) işlevi, iki gösterge almaktadır; bunlardan ilkinin gösterdiği $(C int)'i kullanmakta ama ikincisinin gösterdiği $(C int)'e hiçbir zaman erişmemektedir. İkinci göstergeyi, işlem yapacağı $(C int)'lerin dışını belirten bir değer olarak kullanmaktadır. $(C son)'un gösterdiği elemanı kullanmadığı için de dizinin yalnızca 1 ve 2 numaralı indeksli elemanları değişmiştir: -) - -$(SHELL_SMALL -0 10 20 3 -) - -$(P -Yukarıdaki gibi işlevler $(C for) döngüleri ile de gerçekleştirilebilir: -) - ---- - for ( ; baş != son; ++baş) { - *baş *= 10; - } ---- - -$(P -Dikkat ederseniz, $(C for) döngüsünün hazırlık bölümü boş bırakılmıştır. Bu işlev yeni bir gösterge kullanmak yerine doğrudan $(C baş) parametresini arttırmaktadır. -) - -$(P -Aralık bildiren çift göstergeler $(C foreach) deyimi ile de uyumlu olarak kullanılabilir: -) - ---- - foreach (gösterge; baş .. son) { - *gösterge *= 10; - } ---- - -$(P -Bu gibi bir yöntemde bir dizinin elemanlarının $(I hepsinin birden) kullanılabilmesi için ikinci göstergenin dizinin sonuncu elemanından bir sonrayı göstermesi gerekir: -) - ---- - // ikinci gösterge dizinin sonuncu elemanından sonraki - // hayali bir elemanı gösteriyor: - onKatı(baş, baş + sayılar.length); ---- - -$(P -Dizilerin son elemanlarından sonraki $(I aslında var olmayan) bir elemanın gösterilmesi işte bu yüzden yasaldır. -) - -$(H5 $(IX []) Dizi erişim işleci $(C []) ile kullanımı) - -$(P -D'de hiç gerekmese de göstergeler bir dizinin elemanlarına erişir gibi de kullanılabilirler: -) - ---- - double[] kesirliler = [ 0.0, 1.1, 2.2, 3.3, 4.4 ]; - - double * gösterge = &kesirliler[2]; - - *gösterge = -100; // gösterdiğine erişim - gösterge$(HILITE [1]) = -200; // dizi gibi erişim - - writeln(kesirliler); ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -0 1.1 -100 -200 4.4 -) - -$(P -Böyle bir kullanımda göstergenin göstermekte olduğu değişken sanki bir dilimin ilk elemanıymış gibi düşünülür ve $(C []) işleci o hayali dilimin belirtilen elemanına erişim sağlar. Yukarıdaki programdaki $(C gösterge), $(C kesirliler) dizisinin 2 indeksli elemanını göstermektedir. $(C gösterge[1]) kullanımı, sanki hayali bir dilim varmış gibi o dilimin 1 indeksli elemanına, yani asıl dizinin 3 indeksli elemanına erişim sağlar. -) - -$(P -Karışık görünse de bu kullanımın temelinde çok basit bir dönüşüm yatar. Derleyici $(C gösterge[indeks]) gibi bir yazımı perde arkasında $(C *(gösterge + indeks)) ifadesine dönüştürür: -) - ---- - gösterge[1] = -200; // dizi gibi erişim - *(gösterge + 1) = -200; // üsttekiyle aynı elemana erişim ---- - -$(P -Yukarıda da belirttiğim gibi, bu kullanımın geçerli bir değişkeni gösterip göstermediği denetlenemez. Güvenli olabilmesi için bunun yerine dilim kullanılmalıdır: -) - ---- - double[] dilim = kesirliler[2 .. 4]; - dilim[0] = -100; - dilim[1] = -200; ---- - -$(P -O dilimin yalnızca iki elemanı bulunduğuna dikkat edin. Dilim, asıl dizinin 2 ve 3 indeksli elemanlarına erişim sağlamaktadır. İndeksi 4 olan eleman dilimin dışındadır. -) - -$(P -Dilimler güvenlidir; eleman erişimi hataları çalışma zamanında yakalanır: -) - ---- - dilim[2] = -300; // HATA: dilimin dışına erişim ---- - -$(P -Dilimin 2 indeksli elemanı bulunmadığından bir hata atılır ve böylece programın yanlış sonuçlarla devam etmesi önlenmiş olur: -) - -$(SHELL_SMALL -core.exception.RangeError@deneme(8391): Range violation -) - -$(H5 $(IX dilim, gösterge) Göstergeden dilim elde etmek) - -$(P -Dizi erişim işleciyle sorunsuz olarak kullanılabiliyor olmaları göstergelerin dilimlerle eşdeğer oldukları düşüncesini doğurabilir ancak bu doğru değildir. Göstergeler hem dilimlerin aksine eleman adedini bilmezler hem de aslında tek değişken gösterebildiklerinden dilimler kadar kullanışlı ve güvenli değillerdir. -) - -$(P -Buna rağmen, art arda kaç eleman bulunduğunun bilindiği durumlarda göstergelerden dilim oluşturulabilir. Böylece riskli göstergeler yerine kullanışlı ve güvenli dilimlerden yararlanılmış olur. -) - -$(P -Aşağıdaki koddaki $(C nesnelerOluştur)'un bir C kütüphanesinin bir işlevi olduğunu varsayalım. Bu işlev $(C Yapı) türünden belirtilen adet nesne oluşturuyor olsun ve bu nesnelerden ilkinin adresini döndürüyor olsun: -) - ---- - Yapı * gösterge = nesnelerOluştur(10); ---- - -$(P -Belirli bir göstergenin göstermekte olduğu elemanlara erişim sağlayacak olan dilim oluşturan söz dizimi aşağıdaki gibidir: -) - ---- - /* ... */ dilim = gösterge[0 .. adet]; ---- - -$(P -Buna göre, $(C nesnelerOluştur)'un oluşturduğu ve ilkinin adresini döndürdüğü 10 elemana erişim sağlayan bir dilim aşağıdaki gibi oluşturulur: -) - ---- - Yapı[] dilim = gösterge[0 .. 10]; ---- - -$(P -Artık $(C dilim) programda normal bir D dilimi gibi kullanılmaya hazırdır: -) - ---- - writeln(dilim[1]); // İkinci elemanı yazdırır ---- - -$(H5 $(IX void*) Her türü gösterebilen $(C void*)) - -$(P -D'de hemen hemen hiç gerekmese de, yine C'den gelen bir olanak, $(I herhangi türden) değişkenleri gösterebilen göstergelerdir. Bunlar $(I void göstergesi) olarak tanımlanırlar: -) - ---- - int tamsayı = 42; - double kesirli = 1.25; - void * herTürüGösterebilen; - - herTürüGösterebilen = &tamsayı; - herTürüGösterebilen = &kesirli; ---- - -$(P -Yukarıdaki koddaki $(C void*) türünden olan gösterge hem bir $(C int)'i hem de bir $(C double)'ı gösterebilmektedir. O satırların ikisi de yasaldır ve hatasız olarak derlenir. -) - -$(P -$(C void*) türünden olan göstergeler kısıtlıdır. Getirdikleri esnekliğin bir sonucu olarak, gösterdikleri değişkenlere kendileri erişim sağlayamazlar çünkü gösterilen asıl tür bilinmediğinden gösterilen elemanın kaç baytlık olduğu da bilinemez: -) - ---- - *herTürüGösterebilen = 43; $(DERLEME_HATASI) ---- - -$(P -Böyle işlemlerde kullanılabilmesi için, $(C void*)'nin değerinin önce doğru türü gösteren bir göstergeye aktarılması gerekir: -) - ---- - int tamsayı = 42; // (1) - void * herTürüGösterebilen = &tamsayı; // (2) - - // ... - - int * tamsayıGöstergesi = cast(int*)herTürüGösterebilen; // (3) - *tamsayıGöstergesi = 43; // (4) ---- - -$(P -Yukarıdaki örnek kodu şu adımlarla açıklayabiliriz: -) - -$(OL -$(LI Asıl değişken) -$(LI Değişkenin değerinin bir $(C void*) içinde saklanması) -$(LI Daha sonra o değerin doğru türü gösteren bir göstergeye aktarılması) -$(LI Değişkenin değerinin doğru türü gösteren gösterge ile erişilerek değiştirilmesi) -) - -$(P -$(C void*) türündeki bir göstergenin değeri arttırılabilir veya azaltılabilir. $(C void*) aritmetik işlemlerde $(C ubyte) gibi tek baytlık bir türün göstergesiymiş gibi işlem görür: -) - ---- - ++herTürüGösterebilen; // değeri 1 artar ---- - -$(P -D'de $(C void*) çoğunlukla C kütüphaneleri kullanılırken gerekir. $(C interface), sınıf, şablon, vs. gibi üst düzey olanakları bulunmayan C kütüphaneleri $(C void*) türünden yararlanmış olabilirler. -) - -$(H5 Mantıksal ifadelerde kullanılmaları) - -$(P -Göstergeler otomatik olarak $(C bool) türüne dönüşebilirler. Bu onların değerlerinin mantıksal ifadelerde kullanılabilmesini sağlar. $(C null) değere sahip olan göstergeler mantıksal ifadelerde $(C false) değerini alırlar, diğerleri de $(C true) değerini. Yani hiçbir değişkeni göstermeyen göstergeler $(C false)'tur. -) - -$(P -Çıkışa nesne yazdıran bir işlev düşünelim. Bu işlev, kaç bayt yazdığını da bir çıkış parametresi ile bildiriyor olsun. Ancak, o bilgiyi yalnızca özellikle istendiğinde veriyor olsun. Bunun isteğe bağlı olması işleve gönderilen göstergenin $(C null) olup olmaması ile sağlanabilir: -) - ---- -void bilgiVer(KurşunKalem kalem, size_t * baytAdedi) { - immutable bilgi = format("Kalem: %s", kalem); - writeln(bilgi); - - $(HILITE if (baytAdedi)) { - *baytAdedi = bilgi.length; - } -} ---- - -$(P -Kaç bayt yazıldığı bilgisinin gerekmediği durumlarda gösterge olarak $(C null) değeri gönderilebilir: -) - ---- - bilgiVer(KurşunKalem(Renk.sarı, 7), $(HILITE null)); ---- - -$(P -Bayt adedinin önemli olduğu durumlarda ise $(C null) olmayan bir değer: -) - ---- - size_t baytAdedi; - bilgiVer(KurşunKalem(Renk.mavi, 8), $(HILITE &baytAdedi)); - writeln("Çıkışa ", baytAdedi, " bayt yazılmış"); ---- - -$(P -Bunu yalnızca bir örnek olarak kabul edin. Bayt adedinin işlevden her durumda döndürülmesi daha uygun bir tasarım olarak kabul edilebilir: -) - ---- -size_t bilgiVer(KurşunKalem kalem) { - immutable bilgi = format("Kalem: %s", kalem); - writeln(bilgi); - - return bilgi.length; -} ---- - -$(H5 $(IX new) $(C new) bazı türler için adres döndürür) - -$(P -Şimdiye kadar sınıf nesneleri oluştururken karşılaştığımız $(C new)'ü yapı nesneleri, diziler, ve temel tür değişkenleri oluşturmak için de kullanabiliriz. $(C new) ile oluşturulan değişkenlere $(I dinamik değişken) denir.) - -$(P -$(C new) önce bellekten değişken için gereken büyüklükte bir yer ayırır. Ondan sonra bu yerde bir değişken $(I kurar). Bu değişkenlerin kendi isimleri bulunmadığından onlara ancak $(C new)'ün döndürmüş olduğu referans ile erişilir. -) - -$(P -Bu referans değişkenin türüne bağlı olarak farklı çeşittendir: -) - -$(UL - -$(LI Sınıf nesnelerinde şimdiye kadar çok gördüğümüz gibi bir $(I sınıf değişkenidir): - ---- - Sınıf sınıfDeğişkeni = new Sınıf; ---- - -) - -$(LI Yapı nesnelerinde ve temel türlerde bir $(I göstergedir): - ---- - Yapı $(HILITE *) yapıGöstergesi = new Yapı; - int $(HILITE *) intGöstergesi = new int; ---- - -) - -$(LI Dizilerde ise bir $(I dinamik dizidir): - ---- - int[] dinamikDizi = new int[100]; ---- - -) - -) - -$(P -$(LINK2 /ders/d/auto.html, $(C auto) ve $(C typeof) bölümünden) hatırlayacağınız gibi, sol taraftaki tür isimleri yerine normalde $(C auto) anahtar sözcüğü kullanıldığından çoğunlukla bu ayrıma dikkat etmek gerekmez: -) - ---- - auto sınıfDeğişkeni = new Sınıf; - auto yapıGöstergesi = new Yapı; - auto intGöstergesi = new int; - auto dinamikDizi = new int[100]; ---- - -$(P -Herhangi bir ifadenin tür isminin $(C typeof(Tür).stringof) yöntemiyle yazdırılabildiğini hatırlarsanız, $(C new)'ün değişik türler için ne döndürdüğü küçük bir programla şöyle görülebilir: -) - ---- -import std.stdio; - -struct Yapı { -} - -class Sınıf { -} - -void main() { - writeln(typeof(new int ).stringof); - writeln(typeof(new int[5]).stringof); - writeln(typeof(new Yapı ).stringof); - writeln(typeof(new Sınıf ).stringof); -} ---- - -$(P -Çıktıdan anlaşıldığı gibi, $(C new) temel tür ve yapılar için gösterge türünde bir değer döndürmektedir: -) - -$(SHELL_SMALL -int* -int[] -Yapı* -Sınıf -) - -$(P -Eğer $(C new) ile oluşturulan dinamik değişkenin türü bir $(LINK2 /ders/d/deger_referans.html, değer türü) ise, o değişkenin yaşam süreci, programda ona eriştiren en az bir referans (örneğin, bir gösterge) bulunduğu sürece uzar. (Bu, referans türleri için varsayılan durumdur.) -) - -$(H5 $(IX .ptr, dizi elemanı) $(IX gösterge, dizi elemanı) Dizilerin $(C .ptr) niteliği) - -$(P -Dizilerin (ve dilimlerin) $(C .ptr) niteliği ilk elemanın adresini döndürür. Bu değerin türü eleman türünü gösteren bir göstergedir: -) - ---- - int[] sayılar = [ 7, 12 ]; - - int * ilkElemanınAdresi = sayılar$(HILITE .ptr); - writeln("İlk eleman: ", *ilkElemanınAdresi); ---- - -$(P -Bu değer de C kütüphanelerini kullanırken yararlı olabilir. Bazı C işlevleri bellekte art arda bulunan elemanların ilkinin adresini alırlar. -) - -$(P -Dizgilerin de dizi olduklarını hatırlarsanız, onların $(C .ptr) niteliği de ilk karakterlerinin adresini verir. Burada dikkat edilmesi gereken bir konu, dizgi elemanlarının $(I harf) değil, o harflerin Unicode kodlamasındaki karşılıkları olduklarıdır. Örneğin, ş harfi bir $(C char[]) veya $(C string) içinde iki tane $(C char) olarak bulunur. -) - -$(P -$(C .ptr) niteliğinin döndürdüğü adres ile erişildiğinde, Unicode kodlamasında kullanılan karakterler ayrı ayrı gözlemlenebilirler. Bunu örnekler bölümünde göreceğiz. -) - -$(H5 $(IX in, işleç) Eşleme tablolarının $(C in) işleci) - -$(P -Aslında göstergeleri $(LINK2 /ders/d/esleme_tablolari.html, Eşleme Tabloları bölümünde) gördüğümüz $(C in) işleci ile de kullanmıştık. Orada henüz göstergeleri anlatmamış olduğumdan $(C in) işlecinin dönüş türünü $(I geçiştirmiş) ve o değeri üstü kapalı olarak bir mantıksal ifadede kullanmıştım: -) - ---- - if ("mor" in renkKodları) { - // evet, renkKodları'nda "mor" indeksli eleman varmış - - } else { - // hayır, yokmuş... - } ---- - -$(P -Aslında $(C in) işleci tabloda bulunuyorsa elemanın adresini, bulunmuyorsa $(C null) değerini döndürür. Yukarıdaki koşul da bu değerin $(C false)'a veya $(C true)'ya otomatik olarak dönüşmesi temeline dayanır. -) - -$(P -$(C in)'in dönüş değerini bir göstergeye atarsak, tabloda bulunduğu durumlarda o elemana etkin biçimde erişebiliriz: -) - ---- -import std.stdio; - -void main() { - // Tamsayıdan string'e dönüşüm tablosu - string[int] sayılar = - [ 0 : "sıfır", 1 : "bir", 2 : "iki", 3 : "üç" ]; - - int sayı = 2; - auto $(HILITE eleman) = sayı in sayılar; // (1) - - if ($(HILITE eleman)) { // (2) - writeln("Biliyorum: ", $(HILITE *eleman)); // (3) - - } else { - writeln(sayı, " sayısının yazılışını bilmiyorum"); - } -} ---- - -$(P -Yukarıdaki koddaki $(C eleman) göstergesi $(C in) işleci ile ilklenmekte (1) ve değeri bir mantıksal ifadede kullanılmaktadır (2). Değeri $(C null) olmadığında da gösterdiği değişkene erişilmektedir (3). Hatırlarsanız, $(C null) değerinin gösterdiği geçerli bir nesne olmadığı için, değeri $(C null) olan bir göstergenin gösterdiğine erişilemez. -) - -$(P -Orada $(C eleman)'ın türü, eşleme tablosunun $(I değer türünde) bir göstergedir. Bu tablodaki değerler $(C string) olduklarından $(C in)'in dönüş türü $(C string*)'dir. Dolayısıyla, $(C auto) yerine tür açık olarak aşağıdaki gibi de yazılabilir: -) - ---- - $(HILITE string *) eleman = sayı in sayılar; ---- - -$(H5 Ne zaman kullanmalı) - -$(H6 Kütüphaneler gerektirdiğinde) - -$(P -$(C readf) işlevinde de gördüğümüz gibi, kullandığımız bir kütüphane bizden bir gösterge bekliyor olabilir. Her ne kadar D kütüphanelerinde az sayıda olacaklarını düşünsek de, bu tür işlevlerle karşılaştığımızda onlara istedikleri türde gösterge göndermemiz gerekir. -) - -$(P -Örneğin, bir C kütüphanesi olan gtk'den uyarlanmış olan gtkD'nin bazı işlevlerinin bazı parametreleri göstergedir: -) - ---- - GdkGeometry boyutlar; - // ... boyutlar nesnesinin üyelerinin kurulması ... - - pencere.setGeometryHints(/* ... */, $(HILITE &)boyutlar, /* ... */); ---- - -$(H6 Değer türünden değişkenleri göstermek için) - -$(P -Yine kesinlikle gerekmese de, değer türünden olan bir değişkenin hangisiyle işlem yapılacağını bir gösterge ile belirleyebiliriz. Örnek olarak yazı-tura deneyi yapan bir programa bakalım: -) - ---- -import std.stdio; -import std.random; - -void main() { - size_t yazıAdedi = 0; - size_t turaAdedi = 0; - - foreach (i; 0 .. 100) { - size_t * hangisi = (uniform(0, 2) == 1) - ? &yazıAdedi - : &turaAdedi; - ++(*hangisi); - } - - writefln("yazı: %s tura: %s", yazıAdedi, turaAdedi); -} ---- - -$(P -Tabii aynı işlemi gösterge kullanmadan da gerçekleştirebiliriz: -) - ---- - uniform(0, 2) ? ++yazıAdedi : ++turaAdedi; ---- - -$(P -Veya bir $(C if) koşuluyla: -) - ---- - if (uniform(0, 2)) { - ++yazıAdedi; - - } else { - ++turaAdedi; - } ---- - -$(H6 Veri yapılarının üyelerinde) - -$(P -Bazı veri yapılarının temeli göstergelere dayanır. -) - -$(P -Dizilerin elemanlarının yan yana bulunmalarının aksine, bazı veri yapılarının elemanları bellekte birbirlerinden ayrı olarak dururlar. Bunun bir nedeni, elemanların veri yapısına farklı zamanlarda eklenmeleri olabilir. Böyle veri yapıları elemanların birbirlerini $(I göstermeleri) temeli üzerine kuruludur. -) - -$(P -Örneğin, bağlı liste veri yapısının her düğümü kendisinden bir sonraki düğümü $(I gösterir). İkili ağaç veri yapısının düğümleri de sol ve sağ dallardaki düğümleri $(I gösterirler). Başka veri yapılarında da gösterge kullanımına çok rastlanır. -) - -$(P -D'de veri yapıları referans türleri kullanarak da gerçekleştirilebilseler de göstergeler bazı durumlarda daha doğal olabilirler. -) - -$(P -Gösterge üye örneklerini biraz aşağıda göreceğiz. -) - -$(H6 Belleğe doğrudan erişmek gerektiğinde) - -$(P -Göstergeler belleğe doğrudan ve bayt düzeyinde erişim sağlarlar. Hataya açık olduklarını akılda tutmak gerekir. Ek olarak, programa ait olmayan belleğe erişmek tanımsız davranıştır. -) - -$(H5 Örnekler) - -$(H6 Basit bir bağlı liste) - -$(P -Bağlı liste veri yapısının elemanları $(I düğümler) halinde tutulurlar. Liste, her düğümün kendisinden bir sonraki düğümü $(I göstermesi) düşüncesi üzerine kuruludur. Sonuncu düğüm hiçbir düğümü göstermez (değeri $(C null)'dır): -) - -$(MONO - ilk düğüm düğüm son düğüm - ┌────────┬───┐ ┌────────┬───┐ ┌────────┬────┐ - │ eleman │ o────▶ │ eleman │ o────▶ ... │ eleman │null│ - └────────┴───┘ └────────┴───┘ └────────┴────┘ -) - -$(P -Yukarıdaki şekil yanıltıcı olabilir: Düğümlerin bellekte art arda bulundukları sanılmamalıdır; düğümler normalde belleğin herhangi bir yerinde bulunabilirler. Önemli olan, her düğümün kendisinden bir sonraki düğümü gösteriyor olmasıdır. -) - -$(P -Bu şekle uygun olarak, bir $(C int) listesinin düğümünü şöyle tanımlayabiliriz: -) - ---- -struct Düğüm { - int eleman; - Düğüm * sonraki; - - // ... -} ---- - -$(P $(IX özyinelemeli tür) $(I Not: Kendi türünden nesneleri gösterdiği için bunun) özyinelemeli $(I bir yapı olduğunu söyleyebiliriz.) -) - -$(P -Bütün düğümlerin bir liste olarak düşünülmesi de yalnızca başlangıç düğümünü gösteren bir gösterge ile sağlanabilir: -) - ---- -struct Liste { - Düğüm * baş; - - // ... -} ---- - -$(P -Bu bölümün amacından fazla uzaklaşmamak için burada yalnızca listenin başına eleman ekleyen işlevi göstermek istiyorum: -) - ---- -struct Liste { - Düğüm * baş; - - void başınaEkle(int eleman) { - baş = new Düğüm(eleman, baş); - } - - // ... -} ---- - -$(P -Bu kodun en önemli noktası $(C başınaEkle) işlevini oluşturan satırdır. O satır yeni elemanı listenin başına ekler ve böylece bu yapının bir $(I bağlı liste) olmasını sağlar. ($(I Not: Aslında sonuna ekleme işlemi daha doğal ve kullanışlıdır. Bunu problemler bölümünde göreceğiz.)) -) - -$(P -Yukarıdaki satırda sağ tarafta dinamik bir $(C Düğüm) nesnesi oluşturuluyor. Bu yeni nesne kurulurken, $(C sonraki) üyesi olarak listenin şu andaki başı kullanılıyor. Listenin yeni başı olarak da bu yeni düğümün adresi kullanılınca, listenin başına eleman eklenmiş oluyor. -) - -$(P -Bu küçük veri yapısını deneyen küçük bir program: -) - ---- -import std.stdio; -import std.conv; -import std.string; - -struct Düğüm { - int eleman; - Düğüm * sonraki; - - string toString() const { - string sonuç = to!string(eleman); - - if (sonraki) { - sonuç ~= " -> " ~ to!string(*sonraki); - } - - return sonuç; - } -} - -struct Liste { - Düğüm * baş; - - void başınaEkle(int eleman) { - baş = new Düğüm(eleman, baş); - } - - string toString() const { - return format("(%s)", baş ? to!string(*baş) : ""); - } -} - -void main() { - Liste sayılar; - - writeln("önce : ", sayılar); - - foreach (sayı; 0 .. 10) { - sayılar.başınaEkle(sayı); - } - - writeln("sonra: ", sayılar); -} ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -önce : () -sonra: (9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0) -) - -$(H6 $(IX bellek erişimi, gösterge) $(C ubyte) göstergesi ile belleğin incelenmesi) - -$(P -Belleğin adresleme birimi bayttır. Her adreste tek baytlık bilgi bulunur. Her değişken, kendi türü için gereken sayıda bayt $(I üzerinde) kurulur. Göstergeler belleğe bayt bayt erişme olanağı sunarlar. -) - -$(P -Belleğe bayt olarak erişmek için en uygun tür $(C ubyte*)'dir. Bir değişkenin adresi bir $(C ubyte) göstergesine atanır ve bu gösterge ilerletilerek o değişkeni oluşturan baytların tümü gözlemlenebilir. -) - -$(P -Burada açıklayıcı olsun diye değeri on altılı düzende yazılmış olan bir tamsayı olsun: -) - ---- - int birSayı = 0x01_02_03_04; ---- - -$(P -Bu değişkeni gösteren bir göstergenin şu şekilde tanımlandığını gördük: -) - ---- - int * adresi = &birSayı; ---- - -$(P -O göstergenin değeri, $(C birSayı)'nın bellekte bulunduğu yerin adresidir. Göstergenin değerini tür dönüşümü ile bir $(C ubyte) göstergesine de atayabiliriz: -) - ---- - ubyte * baytGöstergesi = cast(ubyte*)adresi; ---- - -$(P -Bu adresteki $(C int)'i oluşturan 4 baytı şöyle yazdırabiliriz: -) - ---- - writeln(baytGöstergesi[0]); - writeln(baytGöstergesi[1]); - writeln(baytGöstergesi[2]); - writeln(baytGöstergesi[3]); ---- - -$(P -Eğer sizin mikro işlemciniz de benimki gibi $(I küçük soncul) ise, $(C int)'i oluşturan baytların bellekte $(I ters) sırada durduklarını görebilirsiniz: -) - -$(SHELL_SMALL -4 -3 -2 -1 -) - -$(P -Değişkenleri oluşturan baytları gözlemleme işini kolaylaştırmak için bir işlev şablonu yazabiliriz: -) - ---- -$(CODE_NAME baytlarınıGöster)import std.stdio; - -void baytlarınıGöster(T)(ref T değişken) { - const ubyte * baş = cast(ubyte*)&değişken; // (1) - - writefln("tür : %s", T.stringof); - writefln("değer : %s", değişken); - writefln("adres : %s", baş); // (2) - writef ("baytlar: "); - - writefln("%(%02x %)", baş[0 .. T.sizeof]); // (3) - - writeln(); -} ---- - -$(OL -$(LI Değişkenin adresinin bir $(C ubyte) göstergesine atanması) -$(LI Göstergenin değerinin, yani değişkenin başlangıç adresinin yazdırılması) -$(LI Türün büyüklüğünün $(C .sizeof) niteliği ile edinilmesi ve göstergenin gösterdiği baytların yazdırılması ($(C baş) göstergesinden dilim elde edildiğine ve o dilimin yazdırıldığına dikkat edin.)) -) - -$(P -Baytlar $(C *) işleci ile erişerek şöyle de yazılabilirdi: -) - ---- - foreach (bayt; baş .. baş + T.sizeof) { - writef("%02x ", *bayt); - } ---- - -$(P -$(C bayt) göstergesinin değeri o döngüde $(C baş .. baş + T.sizeof) aralığında değişir. $(C baş + T.sizeof) değerinin aralık dışında kaldığına ve ona hiçbir zaman erişilmediğine dikkat edin. -) - -$(P -O işlev şablonunu değişik türlerle çağırabiliriz: -) - ---- -$(CODE_XREF baytlarınıGöster)struct Yapı { - int birinci; - int ikinci; -} - -class Sınıf { - int i; - int j; - - this(int i, int j) { - this.i = i; - this.j = j; - } -} - -void main() { - int tamsayı = 0x11223344; - baytlarınıGöster(tamsayı); - - double kesirli = double.nan; - baytlarınıGöster(kesirli); - - string dizgi = "merhaba dünya"; - baytlarınıGöster(dizgi); - - int[3] dizi = [ 1, 2, 3 ]; - baytlarınıGöster(dizi); - - auto yapıNesnesi = Yapı(0xaa, 0xbb); - baytlarınıGöster(yapıNesnesi); - - auto sınıfDeğişkeni = new Sınıf(1, 2); - baytlarınıGöster(sınıfDeğişkeni); -} ---- - -$(P -Çıktısı aydınlatıcı olabilir: -) - -$(SHELL_SMALL -tür : int -değer : 287454020 -adres : BFFD6D0C -baytlar: 44 33 22 11 $(SHELL_NOTE (1)) - -tür : double -değer : nan -adres : BFFD6D14 -baytlar: 00 00 00 00 00 00 f8 7f $(SHELL_NOTE (2)) - -tür : string -değer : merhaba dünya -adres : BFFD6D1C -baytlar: 0e 00 00 00 e8 c0 06 08 $(SHELL_NOTE (3)) - -tür : int[3u] -değer : 1 2 3 -adres : BFFD6D24 -baytlar: 01 00 00 00 02 00 00 00 03 00 00 00 $(SHELL_NOTE (1)) - -tür : Yapı -değer : Yapı(170, 187) -adres : BFFD6D34 -baytlar: aa 00 00 00 bb 00 00 00 $(SHELL_NOTE (1)) - -tür : Sınıf -değer : deneme.Sınıf -adres : BFFD6D3C -baytlar: c0 ec be 00 $(SHELL_NOTE (4)) -) - -$(P $(B Gözlemler:) -) - -$(OL -$(LI Bazı türlerin baytları beklediğimiz gibidir: $(C int)'in, sabit uzunluklu dizinin ($(C int[3u])), ve yapı nesnesinin değerlerinin baytları bellekte ters sırada bulunmaktadır.) - -$(LI $(C double.nan) özel değerini oluşturan baytları ters sırada düşününce bu değerin 0x7ff8000000000000 özel bit dizisi ile ifade edildiğini öğreniyoruz.) - -$(LI $(C string) 8 bayttan oluşmaktadır; onun değeri olan "merhaba dünya"nın o kadar küçük bir alana sığması olanaksızdır. Bu, $(C string) türünün perde arkasında bir yapı gibi tanımlanmış olmasından gelir. Derleyicinin bir iç türü olduğunu vurgulamak için ismini $(C __) ile başlatarak, örneğin şöyle bir yapı olduğunu düşünebiliriz: - ---- -struct __string { - size_t uzunluk; - char * ptr; // asıl karakterler -} ---- - -$(P -Bu tahmini destekleyen bulguyu $(C string)'i oluşturan baytlarda görüyoruz: Dikkat ederseniz, "merhaba dünya" dizgisindeki toplam 13 harf, içlerindeki ü'nün UTF-8 kodlamasında iki baytla ifade edilmesi nedeniyle 14 bayttan oluşur. $(C string)'in yukarıda görülen ilk 4 baytı olan 0x0000000e'nin değerinin onlu sistemde 14 olması bu gözlemi doğruluyor. -) - -) - -$(LI Benzer şekilde, sınıf nesnesini oluşturan $(C i) ve $(C j) üyelerinin 4 bayta sığmaları olanaksızdır; iki $(C int) için 8 bayt gerektiğini biliyoruz. O çıktı, sınıf değişkenlerinin sınıf nesnesini gösterecek şekilde tek bir göstergeden oluştuğu şüphesini uyandırır: - ---- -struct __Sınıf_DeğişkenTürü { - __Sınıf_AsılNesneTürü * nesne; -} ---- - -) - -) - -$(P -Şimdi biraz daha esnek bir işlev düşünelim. Belirli bir değişkenin baytları yerine, belirli bir adresteki belirli sayıdaki baytı gösteren bir işlev yazalım: -) - ---- -$(CODE_NAME belleğiGöster)import std.stdio; -import std.ascii; - -void belleğiGöster(T)(T * bellek, size_t uzunluk) { - const ubyte * baş = cast(ubyte*)bellek; - - foreach (adres; baş .. baş + uzunluk) { - char karakter = (isPrintable(*adres) ? *adres : '.'); - - writefln("%s: %02x %s", adres, *adres, karakter); - } -} ---- - -$(P -$(C std.ascii) modülünde tanımlı olan $(C isPrintable), kendisine verilen bayt değerinin ASCII tablosunun görüntülenebilen bir karakteri olup olmadığını bildirir. Bazı bayt değerlerinin tesadüfen uç birimin kontrol karakterlerine karşılık gelerek uç birimin çalışmasını bozmalarını önlemek için "$(C isPrintable) olmayan" karakterler yerine $(C '.') karakterini yazdırıyoruz. -) - -$(P -Bu işlevi $(C string)'in $(C .ptr) niteliğinin gösterdiği karakterlere erişmek için kullanabiliriz: -) - ---- -$(CODE_XREF belleğiGöster)import std.stdio; - -void main() { - string dizgi = "merhaba dünya"; - belleğiGöster(dizgi.ptr, dizgi.length); -} ---- - -$(P -Çıktıdan anlaşıldığına göre ü harfi için iki bayt kullanılmaktadır: -) - -$(SHELL_SMALL -8067F18: 6d m -8067F19: 65 e -8067F1A: 72 r -8067F1B: 68 h -8067F1C: 61 a -8067F1D: 62 b -8067F1E: 61 a -8067F1F: 20 -8067F20: 64 d -8067F21: c3 . -8067F22: bc . -8067F23: 6e n -8067F24: 79 y -8067F25: 61 a -) - -$(PROBLEM_COK - -$(PROBLEM -Kendisine verilen iki $(C int)'in değerlerini değiş tokuş etmeye çalışan şu işlevi parametrelerinde $(C ref) kullanmadan düzeltin: - ---- -void değişTokuş(int birinci, int ikinci) { - int geçici = birinci; - birinci = ikinci; - ikinci = geçici; -} - -void main() { - int i = 1; - int j = 2; - - değişTokuş(i, j); - - // Değerleri değişsin - assert(i == 2); - assert(j == 1); -} ---- - -$(P -O programı çalıştırdığınızda $(C assert) denetimlerinin başarısız olduklarını göreceksiniz. -) - -) - -$(PROBLEM -Bu bölümde gösterilen liste yapısını şablona dönüştürün ve böylece $(C int)'ten başka türlerle de kullanılabilmesini sağlayın. -) - -$(PROBLEM -Bağlı listede yeni elemanların sona eklenmeleri daha doğal bir işlemdir. Ben daha kısa olduğu için bu bölümde başına eklemeyi seçtim. Yeni elemanların listenin başına değil, sonuna eklenmelerini sağlayın. - -$(P -Bunun için listenin sonuncu elemanını gösteren bir gösterge yararlı olabilir. -) - -) - -) - -Macros: - SUBTITLE=Göstergeler - - DESCRIPTION=Başka değişkenlere erişim sağlamak amacıyla kullanılan göstergeler (pointer) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial gösterge göstergeler pointer - -SOZLER= -$(bagli_liste) -$(bayt_sirasi) -$(buyuk_soncul) -$(gosterge) -$(kucuk_soncul) -$(referans) -$(tanimsiz_davranis) -$(yazmac) diff --git a/ddili/src/ders/d/halftitle.html b/ddili/src/ders/d/halftitle.html deleted file mode 100644 index 359b52f..0000000 --- a/ddili/src/ders/d/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -D Programlama Dili -

    - -

    -Ali Çehreli -

    - -
    diff --git a/ddili/src/ders/d/hatalar.d b/ddili/src/ders/d/hatalar.d deleted file mode 100644 index 713e314..0000000 --- a/ddili/src/ders/d/hatalar.d +++ /dev/null @@ -1,1014 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX hata atma) Hata Yönetimi) - -$(P -Beklenmedik durumlar programların yaşamlarının doğal parçalarıdır. Kullanıcı hataları, programcı hataları, ortamdaki beklenmedik değişiklikler, vs. programların çalışmaları sırasında her zaman karşılaşılan durumlardır. -) - -$(P -Bu durumlar bazen normal işleyişe devam edilemeyecek kadar vahim olabilir. Örneğin gereken bir bilgi elde edilemiyordur, eldeki bilgi geçersizdir, bir çevre aygıtı çalışmıyordur, vs. Böyle çaresiz kalınan durumlarda D'nin hata atma düzeneği kullanılarak işleme son verilir. -) - -$(P -Devam edilemeyecek kadar kötü bir durum örneği olarak yalnızca dört aritmetik işlemi destekleyen bir işlevin bunların dışındaki bir işlemle çağrılması durumunu düşünebilirsiniz. Önceki bölümün problem çözümlerinde de olduğu gibi: -) - ---- - switch (işlem) { - - case "+": - writeln(birinci + ikinci); - break; - - case "-": - writeln(birinci - ikinci); - break; - - case "x": - writeln(birinci * ikinci); - break; - - case "/": - writeln(birinci / ikinci); - break; - - default: - throw new Exception(format("Geçersiz işlem: %s", işlem)); - } ---- - -$(P -Yukarıdaki $(C switch) deyiminde $(C case)'lerle belirtilmiş olan dört işlem dışında ne yapılacağı bilinmemektedir. O yüzden deyimin $(C default) kapsamında bir hata atılmaktadır. -) - -$(P -Çaresiz durumlarda atılan hata örnekleriyle Phobos'ta da karşılaşırız. Örneğin bir dizgiyi $(C int) türüne dönüştürmek için kullanılan $(C to!int), $(C int) olamayacak bir dizgiyle çağrıldığında hata atar: -) - ---- -import std.conv; - -void main() { - const int sayı = to!int("merhaba"); -} ---- - -$(P -"merhaba" dizgisi bir tamsayı değer ifade etmediği için; o program, $(C to!int)'in attığı bir hatayla sonlanır. -) - -$(SHELL -# ./deneme -$(DARK_GRAY std.conv.ConvException@std/conv.d(37): std.conv(1161): Can't convert -value `merhaba' of type const(char)[] to type int) -) - -$(P -$(C to!int)'in attığı yukarıdaki hatayı şu şekilde çevirebiliriz: "const(char)[] türündeki `merhaba' değeri int türüne dönüştürülemez". -) - -$(P -Hata mesajının baş tarafındaki std.conv.ConvException da hatanın türünü belirtir. Bu hatanın ismine bakarak onun $(C std.conv) modülü içinde tanımlanmış olduğunu anlayabiliyoruz. İsmi de "dönüşüm hatası" anlamına gelen "conversion exception"dan türemiş olan $(C ConvException)'dır. -) - -$(H5 $(IX throw) Hata atmak için $(C throw)) - -$(P -Bunun örneklerini hem yukarıdaki $(C switch) deyiminde, hem de daha önceki bölümlerde gördük. -) - -$(P -Anlamı "at, fırlat" olan $(C throw) deyimi, kendisinden sonra yazılan ifadenin değerini bir $(I hata nesnesi olarak atar) ve işleme hemen son verilmesine neden olur. $(C throw) deyiminden sonraki adımlar işletilmez. Bu, hata kavramına uygun bir davranıştır: hatalar işlemlere devam edilemeyecek durumlarda atıldıkları için, zaten devam etmek söz konusu olmamalıdır. -) - -$(P -Başka bir bakış açısıyla; eğer işleme devam edilebilecek gibi bir durumla karşılaşmışsak, hata atılacak kadar çaresiz bir durum yok demektir. O durumda hata atılmaz ve işlev bir çaresini bulur ve işine devam edebilir. -) - -$(H6 $(IX Exception) $(IX Error) $(IX Throwable) $(C Exception) ve $(C Error) hata türleri) - -$(P -$(C throw) deyimi ile yalnızca $(C Throwable) türünden türemiş olan nesneler atılabilir. Buna rağmen, programlarda ondan da türemiş olan $(C Exception) ve $(C Error) türleri kullanılır. Örneğin Phobos'taki hatalar ya $(C Exception) sınıfından, ya da $(C Error) sınıfından türemişlerdir. $(C Error), giderilemez derecede hatalı durumları ifade eder. O hatanın yakalanması önerilmez. Bu yüzden, atacağınız hataları ya doğrudan $(C Exception)'dan, ya da ondan türeteceğiniz daha belirgin türlerden atmanız gerekir. ($(I Not: Sınıflarla ilgili bir konu olan türemeyi daha sonra göreceğiz.)) -) - -$(P -$(C Exception) nesneleri, kurulurlarken hata mesajını $(C string) olarak alırlar. Bu mesajı, $(C std.string) modülündeki $(C format) işlevi ile oluşturmak kolaylık sağlar: -) - ---- -import std.stdio; -import std.string; -import std.random; - -int[] rasgeleZarlarAt(int adet) { - if (adet < 0) { - $(HILITE throw new Exception)( - format("Geçersiz 'adet' değeri: %s", adet)); - } - - int[] sayılar; - - foreach (i; 0 .. adet) { - sayılar ~= uniform(1, 7); - } - - return sayılar; -} - -void main() { - writeln(rasgeleZarlarAt(-5)); -} ---- - -$(SHELL -# ./deneme -$(DARK_GRAY object.Exception: Geçersiz 'adet' değeri: -5) -) - -$(P -Çoğu durumda, $(C new) ile açıkça hata nesnesi oluşturmak ve $(C throw) ile açıkça atmak yerine bu adımları kapsayan $(C enforce()) işlevi kullanılır. Örneğin, yukarıdaki denetimin eşdeğeri aşağıdaki $(C enforce()) çağrısıdır: -) - ---- - enforce(adet >= 0, format("Geçersiz 'adet' değeri: %s", adet)); ---- - -$(P -$(C enforce()) ve $(C assert()) işlevlerinin farklarını daha sonraki bir bölümde göreceğiz. -) - -$(H6 Hata atıldığında bütün kapsamlardan çıkılır) - -$(P -Programın, $(C main) işlevinden başlayarak başka işlevlere, onlardan da daha başka işlevlere dallandığını görmüştük. İşlevlerin birbirlerini katmanlar halinde çağırmaları, çağrılan işlevlerin kendilerini çağıran işlevlere dönmeleri, ardından başka işlevlerin çağrılmaları, vs. bir ağacın dalları halinde gösterilebilir. -) - -$(P -Örneğin $(C main)'den çağrılan $(C yumurtaYap) adlı bir işlev, kendisi $(C malzemeleriHazırla) adlı başka bir işlevi çağırabilir, ve o işlev de $(C yumurtaHazırla) adlı başka bir işlevi çağırabilir. Okların işlev çağrıları anlamına geldiklerini kabul edersek, böyle bir programın dallanmasını şu şekilde gösterebiliriz: -) - -$(MONO -main - │ - ├──▶ yumurtaYap - │ │ - │ ├──▶ malzemeleriHazırla - │ │ │ - │ │ ├─▶ yumurtaHazırla - │ │ ├─▶ yağHazırla - │ │ └─▶ tavaHazırla - │ │ - │ ├──▶ yumurtalarıPişir - │ └──▶ malzemeleriKaldır - │ - └──▶ yumurtaYe -) - -$(P -Toplam 3 alt düzeye dallanan bu programı, dallanma düzeylerini değişik miktarlarda girintiyle gösterecek şekilde aşağıdaki gibi yazabiliriz. Tabii bu programda işlevler yararlı işler yapmıyorlar; burada amaç, yalnızca programın dallanmasını göstermek: -) - ---- -$(CODE_NAME butun_islevler)import std.stdio; - -void girinti(in int miktar) { - foreach (i; 0 .. miktar * 2) { - write(' '); - } -} - -void başlıyor(in char[] işlev, in int girintiMiktarı) { - girinti(girintiMiktarı); - writeln("▶ ", işlev, " ilk satır"); -} - -void bitiyor(in char[] işlev, in int girintiMiktarı) { - girinti(girintiMiktarı); - writeln("◁ ", işlev, " son satır"); -} - -void main() { - başlıyor("main", 0); - yumurtaYap(); - yumurtaYe(); - bitiyor("main", 0); -} - -void yumurtaYap() { - başlıyor("yumurtaYap", 1); - malzemeleriHazırla(); - yumurtalarıPişir(); - malzemeleriKaldır(); - bitiyor("yumurtaYap", 1); -} - -void yumurtaYe() { - başlıyor("yumurtaYe", 1); - bitiyor("yumurtaYe", 1); -} - -void malzemeleriHazırla() { - başlıyor("malzemeleriHazırla", 2); - yumurtaHazırla(); - yağHazırla(); - tavaHazırla(); - bitiyor("malzemeleriHazırla", 2); -} - -void yumurtalarıPişir() { - başlıyor("yumurtalarıPişir", 2); - bitiyor("yumurtalarıPişir", 2); -} - -void malzemeleriKaldır() { - başlıyor("malzemeleriKaldır", 2); - bitiyor("malzemeleriKaldır", 2); -} - -void yumurtaHazırla() { - başlıyor("yumurtaHazırla", 3); - bitiyor("yumurtaHazırla", 3); -} - -void yağHazırla() { - başlıyor("yağHazırla", 3); - bitiyor("yağHazırla", 3); -} - -void tavaHazırla() { - başlıyor("tavaHazırla", 3); - bitiyor("tavaHazırla", 3); -} ---- - -$(P -Normal işleyişi sırasında program şu çıktıyı üretir: -) - -$(SHELL -▶ main ilk satır - ▶ yumurtaYap ilk satır - ▶ malzemeleriHazırla ilk satır - ▶ yumurtaHazırla ilk satır - ◁ yumurtaHazırla son satır - ▶ yağHazırla ilk satır - ◁ yağHazırla son satır - ▶ tavaHazırla ilk satır - ◁ tavaHazırla son satır - ◁ malzemeleriHazırla son satır - ▶ yumurtalarıPişir ilk satır - ◁ yumurtalarıPişir son satır - ▶ malzemeleriKaldır ilk satır - ◁ malzemeleriKaldır son satır - ◁ yumurtaYap son satır - ▶ yumurtaYe ilk satır - ◁ yumurtaYe son satır -◁ main son satır -) - -$(P -$(C başlıyor) ve $(C bitiyor) işlevleri sayesinde $(C ▶) işareti ile işlevin ilk satırını, $(C ◁) işareti ile de son satırını gösterdik. Program $(C main)'in ilk satırıyla başlıyor, başka işlevlere dallanıyor, ve en son $(C main)'in son satırıyla sonlanıyor. -) - -$(P -Şimdi, programı $(C yumurtaHazırla) işlevine dolaptan kaç yumurta çıkartacağını parametre olarak belirtecek şekilde değiştirelim; ve bu işlev birden az bir değer geldiğinde hata atsın: -) - ---- -$(CODE_NAME yumurtaHazırla_int)import std.string; - -// ... - -void yumurtaHazırla($(HILITE int adet)) { - başlıyor("yumurtaHazırla", 3); - - if (adet < 1) { - throw new Exception( - format("Dolaptan %s yumurta çıkartılamaz", adet)); - } - - bitiyor("yumurtaHazırla", 3); -} ---- - -$(P -Programın doğru olarak derlenebilmesi için tabii başka işlevleri de değiştirmemiz gerekir. Dolaptan kaç yumurta çıkartılacağını işlevler arasında $(C main)'den başlayarak elden elden iletebiliriz. Bu durumda programın diğer tarafları da aşağıdaki gibi değiştirilebilir. Bu örnekte, $(C main)'den bilerek geçersiz olan -8 değerini gönderiyoruz; amaç, programın dallanmasını bir kere de hata atıldığında görmek: -) - ---- -$(CODE_NAME yumurtaYap_int_etc)$(CODE_XREF butun_islevler)$(CODE_XREF yumurtaHazırla_int)// ... - -void main() { - başlıyor("main", 0); - yumurtaYap($(HILITE -8)); - yumurtaYe(); - bitiyor("main", 0); -} - -void yumurtaYap($(HILITE int adet)) { - başlıyor("yumurtaYap", 1); - malzemeleriHazırla($(HILITE adet)); - yumurtalarıPişir(); - malzemeleriKaldır(); - bitiyor("yumurtaYap", 1); -} - -// ... - -void malzemeleriHazırla($(HILITE int adet)) { - başlıyor("malzemeleriHazırla", 2); - yumurtaHazırla($(HILITE adet)); - yağHazırla(); - tavaHazırla(); - bitiyor("malzemeleriHazırla", 2); -} - -// ... ---- - -$(P -Programın bu halini çalıştırdığımızda, $(C throw) ile hata atıldığı yerden sonraki hiçbir satırın işletilmediğini görürüz: -) - -$(SHELL -▶ main ilk satır - ▶ yumurtaYap ilk satır - ▶ malzemeleriHazırla ilk satır - ▶ yumurtaHazırla ilk satır -object.Exception: Dolaptan -8 yumurta çıkartılamaz -) - -$(P -Hata oluştuğu an; en alt düzeyden en üst düzeye doğru, önce $(C yumurtaHazırla) işlevinden, sonra $(C malzemeleriHazırla) işlevinden, daha sonra $(C yumurtaYap) işlevinden, ve en sonunda da $(C main) işlevinden çıkılır. Bu çıkış sırasında, işlevlerin henüz işletilmemiş olan adımları işletilmez. -) - -$(P -İşlemlere devam etmeden bütün işlevlerden çıkılmasının mantığı; en alt düzeydeki $(C yumurtaHazırla) işlevinin başarısızlıkla sonuçlanmış olmasının, onu çağıran daha üst düzeydeki işlevlerin de başarısız olacakları anlamına gelmesidir. -) - -$(P -Alt düzey bir işlevden atılan hata, teker teker o işlevi çağıran üst düzey işlevlere geçer ve en sonunda $(C main)'den de çıkarak programın sonlanmasına neden olur. Hatanın izlediği yolu işaretli olarak aşağıdaki gibi gösterebiliriz: -) - -$(MONO - $(HILITE ▲) - $(HILITE │) - $(HILITE │) -main $(HILITE  ◀───────────┐) - │ $(HILITE │) - │ $(HILITE │) - ├──▶ yumurtaYap $(HILITE  ◀─────────────┐) - │ │ $(HILITE │) - │ │ $(HILITE │) - │ ├──▶ malzemeleriHazırla $(HILITE  ◀─────┐) - │ │ │ $(HILITE │) - │ │ │ $(HILITE │) - │ │ ├─▶ yumurtaHazırla $(HILITE X) $(I atılan hata) - │ │ ├─▶ yağHazırla - │ │ └─▶ tavaHazırla - │ │ - │ ├──▶ yumurtalarıPişir - │ └──▶ malzemeleriKaldır - │ - └──▶ yumurtaYe -) - -$(P -Hata atma düzeneğinin yararı, hatalı bir durumla karşılaşıldığında dallanılmış olan bütün işlevlerin derhal terkedilmelerini sağlamasıdır. -) - -$(P -Bazı durumlarda, atılan hatanın $(I yakalanması) ve programın devam edebilmesi de mümkündür. Bunu sağlayan $(C catch) anahtar sözcüğünü biraz aşağıda göreceğiz. -) - -$(H6 $(C throw)'u ne zaman kullanmalı) - -$(P -$(C throw)'u gerçekten işe devam edilemeyecek durumlarda kullanın. Örneğin kayıtlı öğrenci adedini bir dosyadan okuyan bir işlev, bu değer sıfırdan küçük çıktığında hata atabilir. Çünkü örneğin eksi adet öğrenci ile işine devam etmesi olanaksızdır. -) - -$(P -Öte yandan; eğer devam edilememesinin nedeni kullanıcının girdiği bir bilgiyse, kullanıcının girdiği bu bilgiyi denetlemek daha uygun olabilir. Kullanıcıya bir hata mesajı gösterilebilir ve bilgiyi geçerli olacak şekilde tekrar girmesi istenebilir. Kullanıcıyla etkileşilen böyle bir durum, programın atılan bir hata ile sonlanmasından daha uygun olabilir. -) - -$(H5 $(IX try) $(IX catch) Hata yakalamak için $(C try-catch) deyimi) - -$(P -Yukarıda, atılan hatanın bütün işlevlerden ve en sonunda da programdan hemen çıkılmasına neden olduğunu anlattım. Aslında atılan bu hata $(I yakalanabilir) ve hatanın türüne veya duruma göre davranılarak programın sonlanması önlenebilir. -) - -$(P -Hata, atıldığı işlevden üst düzey işlevlere doğru adım adım ilerlerken, onunla ilgilenen bir noktada $(C try-catch) deyimi ile yakalanabilir. "try"ın anlamı "dene", "catch"in anlamı da "yakala"dır. $(C try-catch) deyimini, bu anlamları göze alarak "çalıştırmayı $(I dene), eğer hata atılırsa $(I yakala)" olarak anlatabiliriz. Söz dizimi şöyledir: -) - ---- - try { - // çalıştırılması istenen ve belki de - // hata atacak olan kod bloğu - - } catch (ilgilenilen_bir_hata_türü_nesnesi) { - // bu türden hata atıldığında - // işletilecek olan işlemler - - } catch (ilgilenilen_diğer_bir_hata_türü_nesnesi) { - // bu diğer türden hata atıldığında - // işletilecek olan işlemler - - // ... seçime bağlı olarak başka catch blokları ... - - } finally { - // hata atılsa da atılmasa da; - // mutlaka işletilmesi gereken işlemler - } ---- - -$(P -Bu bloğu anlamak için önce aşağıdaki $(C try-catch) kullanmayan programa bakalım. Bu program, zar değerini bir dosyadan okuyor ve çıkışa yazdırıyor: -) - ---- -import std.stdio; - -int dosyadanZarOku() { - auto dosya = File("zarin_yazili_oldugu_dosya", "r"); - - int zar; - dosya.readf(" %s", &zar); - - return zar; -} - -void main() { - const int zar = dosyadanZarOku(); - - writeln("Zar: ", zar); -} ---- - -$(P -Dikkat ederseniz, $(C dosyadanZarOku) işlevi hiç hatalarla ilgilenmeden ve sanki dosya başarıyla açılacakmış ve içinden bir zar değeri okunacakmış gibi yazılmış. O, yalnızca kendi işini yapıyor. Bu, hata atma düzeneğinin başka bir yararıdır: işlevler her şey yolunda gidecekmiş gibi yazılabilirler. -) - -$(P -Şimdi o programı klasörde $(C zarin_yazili_oldugu_dosya) isminde bir dosya bulunmadığı zaman başlatalım: -) - -$(SHELL -# ./deneme -$(DARK_GRAY std.exception.ErrnoException@std/stdio.d(286): Cannot open file -$(BACK_TICK)zarin_yazili_oldugu_dosya' in mode $(BACK_TICK)r' (No such file or directory)) -) - -$(P -Klasörde dosya bulunmadığı zaman, mesajı "'zarin_yazili_oldugu_dosya' 'r' modunda açılamıyor" olan bir $(C ErrnoException) atılmıştır. Yukarıda gördüğümüz diğer örneklere uygun olarak, program çıkışına "Zar: " yazdıramamış ve hemen sonlanmıştır. -) - -$(P -Şimdi programa $(C dosyadanZarOku) işlevini bir $(C try) bloğu içinde çağıran bir işlev ekleyelim, ve $(C main)'den bu işlevi çağıralım: -) - ---- -import std.stdio; - -int dosyadanZarOku() { - auto dosya = File("zarin_yazili_oldugu_dosya", "r"); - - int zar; - dosya.readf(" %s", &zar); - - return zar; -} - -int $(HILITE dosyadanZarOkumayıDene)() { - int zar; - - $(HILITE try) { - zar = dosyadanZarOku(); - - } $(HILITE catch) (std.exception.ErrnoException hata) { - writeln("(Dosyadan okuyamadım; 1 varsayıyorum)"); - zar = 1; - } - - return zar; -} - -void main() { - const int zar = $(HILITE dosyadanZarOkumayıDene)(); - - writeln("Zar: ", zar); -} ---- - -$(P -Eğer programı yine aynı şekilde klasörde $(C zarin_yazili_oldugu_dosya) dosyası olmadan başlatırsak, bu sefer programın hata ile sonlanmadığını görürüz: -) - -$(SHELL -$ ./deneme -$(DARK_GRAY (Dosyadan okuyamadım; 1 varsayıyorum) -Zar: 1) -) - -$(P -Bu kodda, $(C dosyadanZarOku) işlevinin işleyişi bir $(C try) bloğu içinde $(I denenmektedir). Eğer hatasız çalışırsa, işlev ondan sonra $(C return zar;) satırı ile normal olarak sonlanır. Ama eğer özellikle belirtilmiş olan $(C std.exception.ErrnoException) hatası atılırsa, işlevin işleyişi o $(C catch) bloğuna geçer ve o bloğun içindeki kodları çalıştırır. Bunu programın yukarıdaki çıktısında görüyoruz. -) - -$(P -Özetle, klasörde zar dosyası bulunmadığı için -) - -$(UL -$(LI önceki programdaki gibi bir $(C std.exception.ErrnoException) hatası atılmakta, (bunu bizim kodumuz değil, $(C File) atıyor)) -$(LI bu hata $(C catch) ile yakalanmakta,) -$(LI $(C catch) bloğunun normal işleyişi sırasında zar için 1 değeri varsayılmakta,) -$(LI ve programın işleyişine devam edilmektedir.) -) - -$(P -İşte $(C catch), atılabilecek olan hataları yakalayarak o durumlara uygun olarak davranılmasını, ve programın işleyişine devam etmesini sağlar. -) - -$(P -Başka bir örnek olarak, yumurtalı programa dönelim ve onun $(C main) işlevine bir $(C try-catch) deyimi ekleyelim: -) - ---- -$(CODE_XREF yumurtaYap_int_etc)void main() { - başlıyor("main", 0); - - try { - yumurtaYap(-8); - yumurtaYe(); - - } catch (Exception hata) { - write("Yumurta yiyemedim: "); - writeln('"', hata.msg, '"'); - writeln("Komşuda yiyeceğim..."); - } - - bitiyor("main", 0); -} ---- - -$(P -($(I Not: $(C .msg) niteliğini biraz aşağıda göreceğiz.)) -) - -$(P -Yukarıdaki $(C try) bloğunda iki satır kod bulunuyor. $(C catch), bu satırların herhangi birisinden atılacak olan hatayı yakalar. -) - -$(SHELL -▶ main ilk satır - ▶ yumurtaYap ilk satır - ▶ malzemeleriHazırla ilk satır - ▶ yumurtaHazırla ilk satır -Yumurta yiyemedim: "Dolaptan -8 yumurta çıkartılamaz" -Komşuda yiyeceğim... -◁ main son satır -) - -$(P -Görüldüğü gibi, bu program bir hata atıldı diye artık hemen sonlanmamaktadır. Program; hataya karşı önlem almakta, işleyişine devam etmekte, ve $(C main) işlevi normal olarak sonuna kadar işletilmektedir. -) - -$(H6 $(C catch) blokları sırayla taranır) - -$(P -Örneklerde kendimiz hata atarken kullandığımız $(C Exception), $(I genel) bir hata türüdür. Bu hatanın atılmış olması, programda bir hata olduğunu belirtir; ve hatanın içinde saklanmakta olan mesaj, o mesajı okuyan insanlara da hatayla ilgili bilgi verir. Ancak, $(C Exception) sınıfı hatanın $(I türü) konusunda bir bilgi taşımaz. -) - -$(P -Bu bölümde daha önce gördüğümüz $(C ConvException) ve $(C ErrnoException) ise $(I daha özel) hata türleridir: birincisi, atılan hatanın bir dönüşüm ile ilgili olduğunu; ikincisi ise sistem işlemleriyle ilgili olduğunu anlatır. -) - -$(P -Phobos'taki çoğu başka hata gibi $(C ConvException) ve $(C ErrnoException), $(C Exception) sınıfından türemişlerdir. Atılan hata türleri, $(C Error) ve $(C Exception) genel hata türlerinin daha özel halleridir. $(C Error) ve $(C Exception) da kendilerinden daha genel olan $(C Throwable) sınıfından türemişlerdir. ("Throwable"ın anlamı "atılabilen"dir.) -) - -$(P -Her ne kadar $(C catch) ile yakalanabiliyor olsa da, $(C Error) türünden veya ondan türemiş olan hataların yakalanmaları önerilmez. $(C Error)'dan daha genel olduğu için $(C Throwable)'ın yakalanması da önerilmez. Yakalanmasının doğru olduğu sıradüzen, $(C Exception) sıradüzenidir. -) - -$(MONO - Throwable $(I (yakalamayın)) - ↗ ↖ - Exception Error $(I (yakalamayın)) - ↗ ↖ ↗ ↖ - ... ... ... ... -) - -$(P $(I Not: Sıradüzen gösterimini daha sonraki $(LINK2 /ders/d/tureme.html, Türeme bölümünde) göstereceğim. Yukarıdaki şekil, $(C Throwable)'ın en genel, $(C Exception) ve $(C Error)'ın daha özel türler olduklarını ifade eder.) -) - -$(P -Atılan hataları özellikle belirli bir türden olacak şekilde yakalayabiliriz. Örneğin $(C ErrnoException) türünü yakalayarak dosya açma sorunu ile karşılaşıldığını anlayabilir ve programda buna göre hareket edebiliriz. -) - -$(P -Atılan hata, ancak eğer $(C catch) bloğunda belirtilen türe uyuyorsa yakalanır. Örneğin $(C ÖzelBirHata) türünü yakalamaya çalışan bir $(C catch) bloğu, $(C ErrnoException) hatasını yakalamaz. -) - -$(P -Bir $(C try) deyimi içerisindeki kodların (veya onların çağırdığı başka kodların) attığı hata, o $(C try) deyiminin $(C catch) bloklarında belirtilen hata türlerine $(I sırayla) uydurulmaya çalışılır. Eğer atılan hatanın türü sırayla bakılan $(C catch) bloğunun hata türüne uyuyorsa, o hata yakalanmış olur ve o $(C catch) bloğunun içerisindeki kodlar işletilir. Uyan bir $(C catch) bloğu bulunursa, artık diğer $(C catch) bloklarına bakılmaz. -) - -$(P -$(C catch) bloklarının böyle sırayla taranmalarının doğru olarak çalışması için $(C catch) bloklarının daha özel hata türlerinden daha genel hata türlerine doğru sıralanmış olmaları gerekir. Buna göre; genel bir kural olarak eğer yakalanması uygun bulunuyorsa, yakalanması önerilen en genel hata türü olduğu için $(C Exception) her zaman en sondaki $(C catch) bloğunda belirtilmelidir. -) - -$(P -Örneğin öğrenci kayıtlarıyla ilgili hataları yakalamaya çalışan bir $(C try) deyimi, $(C catch) bloklarındaki hata türlerini özelden genele doğru şu şekilde yazabilir: -) - ---- - try { - // ... hata atabilecek kayıt işlemleri ... - - } catch (KayıtNumarasıHanesiHatası hata) { - - // özellikle kayıt numarasının bir hanesiyle ilgili - // olan bir hata - - } catch (KayıtNumarasıHatası hata) { - - // kayıt numarasıyla ilgili olan, ama hanesi ile - // ilgili olmayan daha genel bir hata - - } catch (KayıtHatası hata) { - - // kayıtla ilgili daha genel bir hata - - } catch (Exception hata) { - - // kayıtla ilgisi olmayan genel bir hata - - } ---- - -$(H6 $(IX finally) $(C finally) bloğu) - -$(P -$(C try-catch) deyiminin son bloğu olan $(C finally), hata atılsa da atılmasa da mutlaka işletilecek olan işlemleri içerir. $(C finally) bloğu isteğe bağlıdır; gerekmiyorsa yazılmayabilir. -) - -$(P -$(C finally)'nin etkisini görmek için %50 olasılıkla hata atan şu programa bakalım: -) - ---- -import std.stdio; -import std.random; - -void yüzdeElliHataAtanİşlev() { - if (uniform(0, 2) == 1) { - throw new Exception("hata mesajı"); - } -} - -void deneme() { - writeln("ilk satır"); - - try { - writeln("try'ın ilk satırı"); - yüzdeElliHataAtanİşlev(); - writeln("try'ın son satırı"); - - // ... isteğe bağlı olarak catch blokları da olabilir ... - - } $(HILITE finally) { - writeln("finally işlemleri"); - } - - writeln("son satır"); -} - -void main() { - deneme(); -} ---- - -$(P -O işlev hata atmadığında programın çıktısı şöyledir: -) - -$(SHELL -ilk satır -try'ın ilk satırı -try'ın son satırı -$(HILITE finally işlemleri) -son satır -) - -$(P -Hata attığında ise şöyle: -) - -$(SHELL -ilk satır -try'ın ilk satırı -$(HILITE finally işlemleri) -object.Exception@deneme.d(7): hata mesajı -) - -$(P -Görüldüğü gibi, hata atıldığında "try'ın son satırı" ve "son satır" yazdırılmamış, ama $(C finally) bloğunun içi iki durumda da işletilmiştir. -) - -$(H6 $(C try-catch)'i ne zaman kullanmalı) - -$(P -$(C try-catch) deyimi, atılmış olan hataları yakalamak ve bu durumlarda özel işlemler yapmak için kullanılır. -) - -$(P -Dolayısıyla, $(C try-catch) deyimini ancak ve ancak atılan bir hata ile ilgili özel işlemler yapmanız gereken veya yapabildiğiniz durumlarda kullanın. Başka durumlarda hatalara karışmayın. Hataları, onları yakalamaya çalışan işlevlere bırakın. -) - -$(H5 Hata nitelikleri) - -$(P -Program hata ile sonlandığında çıktıya otomatik olarak yazdırılan bilgiler yakalanan hata nesnelerinin niteliklerinden de edinilebilir. Bu nitelikler $(C Throwable) arayüzü tarafından sunulur: -) - -$(UL - -$(LI $(IX .file) $(C .file): Hatanın atıldığı kaynak dosya) - -$(LI $(IX .line) $(C .line): Hatanın atıldığı satır) - -$(LI $(IX .msg) $(C .msg): Hata mesajı) - -$(LI $(IX .info) $(C .info): Çağrı yığıtının hata atıldığındaki durumu) - -$(LI $(IX .next) $(C .next): Bir sonraki ikincil hata) - -) - -$(P -$(C finally) bloğunun hata atılan durumda otomatik olarak işletildiğini gördük. (Bir sonraki bölümde göreceğimiz $(C scope) deyimi ve daha ilerideki bir bölümde göreceğimiz $(I sonlandırıcı işlevler) de kapsamlardan çıkılırken otomatik olarak işletilirler.) -) - -$(P -$(IX ikincil hata) Doğal olarak, kapsamlardan çıkılırken işletilen kodlar da hata atabilirler. $(I İkincil) olarak adlandırılan bu hatalar birbirlerine bir bağlı liste olarak bağlanmışlardır; her birisine asıl hatadan başlayarak $(C .next) niteliği ile erişilir. Sonuncu hatanın $(C .next) niteliğinin değeri $(C null)'dır. ($(C null) değerini ilerideki bir bölümde göreceğiz.) -) - -$(P -Aşağıdaki örnekte toplam üç adet hata atılmaktadır: $(C foo()) içinde atılan asıl hata ve $(C foo())'nun ve onu çağıran $(C bar())'ın $(C finally) bloklarında atılan ikincil hatalar. Program, ikincil hatalara $(C .next) nitelikleri ile nasıl erişildiğini gösteriyor. -) - -$(P -Bu programdaki bazı kavramları daha sonraki bölümlerde göreceğiz. Örneğin, $(C for) döngüsünün yalnızca $(C hata) ifadesinden oluşan devam koşulu $(I $(C hata) $(C null) olmadığı sürece) anlamına gelir. -) - ---- -import std.stdio; - -void foo() { - try { - throw new Exception("foo'daki asıl hata"); - - } finally { - throw new Exception("foo'daki finally hatası"); - } -} - -void bar() { - try { - foo(); - - } finally { - throw new Exception("bar'daki finally hatası"); - } -} - -void main() { - try { - bar(); - - } catch (Exception yakalananHata) { - - for (Throwable hata = yakalananHata; - hata; $(CODE_NOTE Anlamı: null olmadığı sürece) - hata = hata$(HILITE .next)) { - - writefln("mesaj: %s", hata$(HILITE .msg)); - writefln("dosya: %s", hata$(HILITE .file)); - writefln("satır: %s", hata$(HILITE .line)); - writeln(); - } - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -mesaj: foo'daki asıl hata -dosya: deneme.d -satır: 6 - -mesaj: foo'daki finally hatası -dosya: deneme.d -satır: 9 - -mesaj: bar'daki finally hatası -dosya: deneme.d -satır: 19 -) - -$(H5 $(IX hata çeşitleri) Hata çeşitleri) - -$(P -Hata atma düzeneğinin ne kadar yararlı olduğunu gördük. Hem alt düzeydeki işlemlerin, hem de o işleme bağımlı olan daha üst düzey işlemlerin hemen sonlanmalarına neden olur. Böylece program yanlış bilgiyle veya eksik işlemle devam etmemiş olur. -) - -$(P -Buna bakarak her hatalı durumda hata atılmasının uygun olduğunu düşünmeyin. Hatanın çeşidine bağlı olarak farklı davranmak gerekebilir. -) - -$(H6 Kullanıcı hataları) - -$(P -Hataların bazıları kullanıcıdan gelir. Yukarıda da gördüğümüz gibi, örneğin bir sayı beklenen durumda "merhaba" gibi bir dizgi girilmiş olabilir. Programın kullanıcıyla etkileştiği bir durumda programın hata ile sonlanması uygun olmayacağı için, böyle durumlarda kullanıcıya bir hata mesajı göstermek ve doğru bilgi girmesini istemek daha uygun olabilir. -) - -$(P -Yine de, kullanıcının girdiği bilginin doğrudan işlenmesinde ve o işlemler sırasında bir hata atılmasında da bir sakınca olmayabilir. Önemli olan, bu tür bir hatanın programın sonlanmasına neden olmak yerine, kullanıcıya geçerli bilgi girmesini söylemesidir. -) - -$(P -Bir örnek olarak, kullanıcıdan dosya ismi alan bir programa bakalım. Aldığımız dosya isminin geçerli olup olmadığı konusunda iki yol izleyebiliriz: -) - -$(UL -$(LI $(B Bilgiyi denetlemek): $(C std.file) modülündeki $(C exists) işlevini kullanarak verilen isimde bir dosya olup olmadığına bakabiliriz: - ---- - if (exists(dosya_ismi)) { - // dosya mevcut - - } else { - // dosya mevcut değil - } ---- - -$(P -Dosyayı ancak dosya mevcut olduğunda açarız. Ancak; dosya, program bu denetimi yaptığı anda mevcut olduğu halde, az sonra $(C File) ile açılmaya çalışıldığında mevcut olmayabilir. Çünkü örneğin sistemde çalışmakta olan başka bir program tarafından silinmiş veya ismi değiştirilmiş olabilir. -) - -$(P -Bu yüzden, belki de aşağıdaki diğer yöntem daha uygundur. -) - -) - -$(LI $(B Bilgiyi doğrudan kullanmak): Kullanıcıdan alınan bilgiye güvenebilir ve doğrudan işlemlere geçebiliriz. Eğer verilen bilgi geçersizse, zaten $(C File) bir hata atacaktır: - ---- -import std.stdio; -import std.string; - -void dosyayıKullan(string dosyaİsmi) { - auto dosya = File(dosyaİsmi, "r"); - // ... -} - -string dizgiOku(in char[] soru) { - write(soru, ": "); - string dizgi = strip(readln()); - - return dizgi; -} - -void main() { - bool dosyaKullanılabildi = false; - - while (!dosyaKullanılabildi) { - try { - dosyayıKullan( - dizgiOku("Dosyanın ismini giriniz")); - - /* Eğer bu noktaya gelebildiysek, dosyayıKullan - * işlevi başarıyla sonlanmış demektir. Yani, - * verilen dosya ismi geçerlidir. - * - * Bu yüzden bu noktada bu değişkenin değerini - * 'true' yaparak while'ın sonlanmasını - * sağlıyoruz. */ - dosyaKullanılabildi = true; - writeln("Dosya başarıyla kullanıldı"); - - } catch (std.exception.ErrnoException açmaHatası) { - stderr.writeln("Bu dosya açılamadı"); - } - } -} ---- - -) - -) - -$(H6 Programcı hataları) - -$(P -Bazı hatalar programcının kendisinden kaynaklanır. Örneğin yazılan bir işlevin programda kesinlikle sıfırdan küçük bir değerle çağrılmayacağından eminizdir. Programın tasarımına göre bu işlev kesinlikle eksi bir değerle çağrılmıyordur. İşlevin buna rağmen eksi bir değer alması; ya programın mantığındaki bir hatadan kaynaklanıyordur, ya da o mantığın gerçekleştirilmesindeki bir hatadan. Bunların ikisi de programcı hatası olarak kabul edilir. -) - -$(P -Böyle, programın yazımıyla ilgili olan, yani programcının kendisinden kaynaklanan hatalı durumlarda hata atmak yerine bir $(C assert) kullanmak daha uygun olabilir ($(I Not: $(C assert)'ü daha sonraki bir bölümde göreceğiz.)): -) - ---- -void menüSeçeneği(int sıraNumarası) { - assert(sıraNumarası >= 0); - // ... -} - -void main() { - menüSeçeneği(-1); -} ---- - -$(P -Program bir $(C assert) hatası ile sonlanır: -) - -$(SHELL_SMALL -core.exception.AssertError@$(HILITE deneme.d(3)): Assertion failure -) - -$(P -$(C assert) hangi kaynak dosyanın hangi satırındaki beklentinin gerçekleşmediğini de bildirir. (Bu mesajda deneme.d dosyasının üçüncü satırı olduğu anlaşılıyor.) -) - -$(H6 Beklenmeyen durumlar) - -$(P -Yukarıdaki iki durumun dışında kalan her türlü hatalı durumda hata atmak uygundur. Zaten başka çare kalmamıştır: ne bir kullanıcı hatasıyla ne de bir programcı hatasıyla karşı karşıyayızdır. Eğer işimize devam edemiyorsak, hata atmaktan başka çare yoktur. -) - -$(P -Bizim attığımız hatalar karşısında ne yapacakları bizi çağıran üst düzey işlevlerin görevidir. Eğer uygunsa, attığımız hatayı yakalayarak bir çare bulabilirler. -) - -$(H5 Özet) - -$(UL - -$(LI -Eğer bir kullanıcı hatasıyla karşılaşmışsanız ya kullanıcıyı uyarın ya da yine de işlemlere devam ederek nasıl olsa bir hata atılacağına güvenin. -) - -$(LI -Programın mantığında veya gerçekleştirilmesinde hata olmadığını garantilemek için $(C assert)'ü kullanın. ($(I Not: $(C assert)'ü ilerideki bir bölümde göreceğiz.)) -) - -$(LI -Bunların dışındaki durumlarda $(C throw) veya $(C enforce()) ile hata atın. ($(I Not: $(C enforce())'u ilerideki bir bölümde göreceğiz.)) -) - -$(LI -Hataları ancak ve ancak yakaladığınızda yararlı bir işlem yapabilecekseniz yakalayın. Yoksa hiç $(C try-catch) deyimi içine almayın; belki de işlevinizi çağıran daha üst düzeydeki bir işlev yakalayacaktır. -) - -$(LI -$(C catch) bloklarını özelden genele doğru sıralayın. -) - -$(LI -İşletilmeleri mutlaka gereken işlemleri $(C finally) bloğuna yazın. -) - -) - -Macros: - SUBTITLE=Hata Yönetimi - - DESCRIPTION=D dilinde işlevlerin işlerine devam edememe durumlarında kullanılan hata atma düzeneği [exceptions] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev hata atma throw exception catch finally aykırı durum - -SOZLER= -$(bagli_liste) -$(blok) -$(cagri_yigiti) -$(deyim) -$(hata_atma) -$(ifade) -$(ikincil_hata) -$(islev) -$(kapsam) -$(klasor) -$(kurma) -$(nesne) -$(nitelik) -$(phobos) -$(sinif) -$(sonlandirici_islev) -$(turetmek) diff --git a/ddili/src/ders/d/hazir_degerler.cozum.d b/ddili/src/ders/d/hazir_degerler.cozum.d deleted file mode 100644 index 6d260cc..0000000 --- a/ddili/src/ders/d/hazir_degerler.cozum.d +++ /dev/null @@ -1,76 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Hazır Değerler) - -$(OL - -$(LI -Buradaki sorun, sağ taraftaki hazır değerin bir $(C int)'e sığmayacak kadar büyük olması ve o yüzden de türünün derleyici tarafından $(C long) olarak belirlenmesidir. Bu yüzden soldaki $(C int) türündeki değişkene uymaz. Burada en az iki çözüm vardır. - -$(P -Bir çözüm, açıkça $(C int) yazmak yerine, değişkenin türü için $(C auto) kullanmak ve tür seçimini derleyiciye bırakmaktır: -) - ---- - auto miktar = 10_000_000_000; ---- - -$(P -Böylece $(C miktar) değişkeninin değeri de $(C long) olarak seçilir. -) - -$(P -Diğer çözüm, değişkenin türünü de açıkça $(C long) yazmaktır: -) - ---- - long miktar = 10_000_000_000; ---- - -) - -$(LI -Burada satırın başına götüren $(C '\r') karakteri kullanılabilir. Böylece hep aynı satırın üstüne yazılır. - ---- -import std.stdio; - -void main() { - for (int sayı = 0; ; ++sayı) { - write("\rSayı: ", sayı); - } -} ---- - -$(P -Yukarıdaki programın çıktısı hem fazla hızlı hem de $(C stdout)'un ara belleğinin dolup boşalmasına bağlı olarak tutarsız olabilir. Aşağıdaki program her yazmadan sonra hem $(C flush()) ile çıkış ara belleğini boşaltır, hem de 10 milisaniye bekler: -) - ---- -import std.stdio; -import core.thread; - -void main() { - for (int sayı = 0; ; ++sayı) { - write("\rSayı: ", sayı); - stdout.flush(); - Thread.sleep(10.msecs); - } -} ---- - -$(P -Normalde çıkış ara belleğinin açıkça boşaltılmasına gerek yoktur. Ara bellek yeni satıra geçmeden önce veya girişten bilgi okunmadan önce de otomatik olarak boşaltılır. -) - -) - -) - - -Macros: - SUBTITLE=Hazır Değerler Problem Çözümü - - DESCRIPTION=Hazır değerler bölümünün problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial hazır değer problem çözüm diff --git a/ddili/src/ders/d/hazir_degerler.d b/ddili/src/ders/d/hazir_degerler.d deleted file mode 100644 index 72638f8..0000000 --- a/ddili/src/ders/d/hazir_degerler.d +++ /dev/null @@ -1,379 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX hazır değer) Hazır Değerler) - -$(P -Programlar işlerini değişkenlerin ve nesnelerin değerlerini kullanarak yaparlar. Değişkenleri ve nesneleri işleçlerle ve işlevlerle kullanarak yeni değerler üretirler ve yeni nesneler oluştururlar. -) - -$(P -Bazı değerlerin ise hesaplanmaları gerekmez; onlar kaynak kod içine doğrudan hazır olarak yazılırlar. Örneğin şu kod parçasındaki işlemler sırasında kullanılan $(C 0.75) kesirli sayı değeri ve $(C "Toplam fiyat: ") sabit dizgisi kod içine programcı tarafından hazır olarak yazılmıştır: -) - ---- - öğrenciFiyatı = biletFiyatı * 0.75; - fiyat += öğrenciSayısı * öğrenciFiyatı; - writeln("Toplam fiyat: ", fiyat); ---- - -$(P -Bu tür değerlere $(I hazır değer) denir. Şimdiye kadar gördüğümüz programlarda zaten çok sayıda hazır değer kullandık. Bu bölümde hazır değerlerin bütün çeşitlerini ve söz dizimlerini göreceğiz. -) - -$(H5 Tamsayılar) - -$(P -Tamsayıları dört değişik sayı sisteminde yazabilirsiniz: Günlük hayatımızda kullandığımız $(I onlu) sayı sisteminde, bazı durumlarda daha uygun olan $(I on altılı) veya $(I ikili) sayı sistemlerinde, ve nadir olarak $(I sekizli) sayı sisteminde. -) - -$(P -Bütün tamsayı değerlerinin rakamlarının aralarına, istediğiniz sayıda, istediğiniz yerlerine, ve herhangi amaçla; örneğin okumayı kolaylaştırmak için $(C _) karakterleri yerleştirebilirsiniz. Örneğin, rakamları üçer üçer ayırmak için: $(C 1_234_567). Bu karakterler tamamen programcının isteğine bağlıdır ve derleyici tarafından gözardı edilirler. -) - -$(P $(B Onlu sayı sisteminde:) Günlük hayatımızda kullandığımız gibi, onlu rakamlarla yazılır. Örnek: $(C 12). Onlu değerlerin ilk rakamı 0 olamaz. Bunun nedeni, 0 ile başlayan hazır değerlerin çoğu başka dilde sekizli sayı sistemine ayrılmış olmasıdır. Bu konudaki karışıklıklardan doğabilecek olan hataları önlemek için D'de tamsayı hazır değerleri 0 ile başlayamaz. -) - -$(P $(B On altılı sayı sisteminde:) $(C 0x) veya $(C 0X) ile başlayarak ve on altılı sayı sisteminin rakamları olan "0123456789abcdef" ve "ABCDEF" ile yazılır. Örnek: $(C 0x12ab00fe). -) - -$(P $(B Sekizli sayı sisteminde:) $(C std.conv) modülündeki $(C octal) ile ve sekizli sayı sisteminin rakamları olan "01234567" ile yazılır. Örnek: $(C octal!576). -) - -$(P $(B İkili sayı sisteminde:) $(C 0b) veya $(C 0B) ile başlayarak ve ikili sayı sisteminin rakamları olan 0 ve 1 ile yazılır. Örnek: $(C 0b01100011). -) - -$(H6 Tamsayı değerlerin türleri) - -$(P -Her değerin olduğu gibi, D'de hazır değerlerin de türleri vardır. Hazır değerlerin türleri $(C int), $(C double), vs. gibi açıkça yazılmaz; derleyici, türü hazır değerin yazımından anlar. -) - -$(P -Hazır değerlerin türlerinin aslında programcı açısından çok büyük bir önemi yoktur. Bazen tür, hazır değerin içinde kullanıldığı ifadeye uymayabilir ve derleyici uyarı verir. Öyle durumlarda aşağıdaki bilgilerden yararlanarak hazır değerin türünü açıkça belirtmeniz gerekebilir. -) - -$(P -Tamsayı hazır değerlerin öncelikle $(C int) türünde oldukları varsayılır. Eğer değer bir $(C int)'e sığmayacak kadar büyükse, derleyici şu şekilde karar verir: -) - -$(UL -$(LI $(C int)'e sığmayacak kadar büyük olan değer onlu sistemde yazılmışsa, $(C long)'dur -) - -$(LI $(C int)'e sığmayacak kadar büyük olan değer başka bir sayı sisteminde yazılmışsa, öncelikle $(C uint)'tir, ona da sığmıyorsa $(C long)'dur, ona da sığmıyorsa $(C ulong)'dur -) -) - -$(P -Bunu görmek için daha önce öğrendiğimiz $(C typeof)'tan ve $(C stringof)'tan yararlanan şu programı kullanabiliriz: -) - ---- -import std.stdio; - -void main() { - writeln("\n--- bunlar onlu olarak yazıldılar ---"); - - // int'e sığdığı için int - writeln( 2_147_483_647, "\t\t", - typeof(2_147_483_647).stringof); - - // int'e sığmadığı ve onlu olarak yazıldığı için long - writeln( 2_147_483_648, "\t\t", - typeof(2_147_483_648).stringof); - - writeln("\n--- bunlar onlu olarak yazılMAdılar ---"); - - // int'e sığdığı için int - writeln( 0x7FFF_FFFF, "\t\t", - typeof(0x7FFF_FFFF).stringof); - - // int'e sığmadığı ve onlu olarak yazılmadığı için uint - writeln( 0x8000_0000, "\t\t", - typeof(0x8000_0000).stringof); - - // uint'e sığmadığı ve onlu olarak yazılmadığı için long - writeln( 0x1_0000_0000, "\t\t", - typeof(0x1_0000_0000).stringof); - - // long'a sığmadığı ve onlu olarak yazılmadığı için ulong - writeln( 0x8000_0000_0000_0000, "\t\t", - typeof(0x8000_0000_0000_0000).stringof); -} ---- - -$(P -Çıktısı: -) - -$(SHELL ---- bunlar onlu olarak yazıldılar --- -2147483647 int -2147483648 long - ---- bunlar onlu olarak yazılMAdılar --- -2147483647 int -2147483648 uint -4294967296 long -9223372036854775808 ulong -) - -$(H6 $(IX L, son ek) $(C L) son eki) - -$(P -Değerin büyüklüğünden bağımsız olarak, eğer değerin sonunda bir $(C L) karakteri varsa, türü $(C long)'dur. Örnek: $(C 10L). -) - -$(H6 $(IX U, son ek) $(C U) son eki) - -$(P -Değerin büyüklüğünden bağımsız olarak, eğer değerin sonunda bir $(C U) karakteri varsa, işaretsiz bir türdür. Örnek: $(C 10U)'nun türü $(C uint)'tir. Küçük harf $(C u) da kullanılabilir. -) - -$(P -$(IX LU, son ek) $(IX UL, son ek) $(C L) ve $(C U) karakterleri birlikte ve sıralarının önemi olmadan da kullanılabilirler. Örneğin $(C 7UL)'nin ve $(C 8LU)'nun ikisinin de türleri $(C ulong)'dur. -) - -$(H5 Kesirli sayılar) - -$(P -Kesirli sayılar onlu sayı sisteminde veya on altılı sayı sisteminde yazılabilirler. Örneğin onlu olarak $(C 1.234) veya on altılı olarak $(C 0x9a.bc). -) - -$(P $(B Onlu sayı sisteminde:) Sayının yanına, $(C e) veya $(C E) belirtecinden sonra "çarpı 10 üzeri" anlamına gelen bir çarpan eklenebilir. Örneğin $(C 3.4e5), "3.4 çarpı 10 üzeri 5" anlamındadır. Bu belirteçten sonra bir $(C +) karakteri de yazılabilir ama onun bir etkisi yoktur. Örneğin, $(C 5.6e2) ile $(C 5.6e+2) aynı anlamdadır. -) - -$(P -Belirteçten sonra gelen $(C -) karakterinin etkisi vardır ve "10 üzeri o kadar değere bölünecek" anlamına gelir. Örneğin $(C 7.8e-3), "7.8 bölü 10 üzeri 3" anlamındadır. -) - -$(P $(B On altılı sayı sisteminde:) Sayı $(C 0x) veya $(C 0X) ile başlar; noktadan önceki ve sonraki bölümleri on altılı sayı sisteminin rakamlarıyla yazılır. $(C e) ve $(C E) de on altılı sistemde geçerli rakamlar olduklarından, üs belirteci olarak başka bir harf kullanılır: $(C p) (veya $(C P)). -) - -$(P -Başka bir fark, bu belirteçten sonra gelen değerin "10 üzeri" değil, "2 üzeri" anlamına gelmesidir. Örneğin $(C 0xabc.defP4)'ün sonundaki belirteç, "2 üzeri 4 ile çarpılacak" anlamına gelir. -) - -$(P -Kesirli sayı değerler hemen hemen her zaman için bir nokta içerirler, ama belirteç varsa noktaya gerek yoktur. Örneğin $(C 2e3), 2000 değerinde bir kesirli sayıdır. -) - -$(P -Noktadan önceki değer $(C 0) ise yazılmayabilir. Örneğin $(C .25), "çeyrek" anlamında bir kesirli sayı değeridir. -) - -$(P -Gözardı edilen $(C _) karakterlerini kesirli sayılarla da kullanabilirsiniz: $(C 1_000.5) -) - -$(H6 Kesirli sayı değerlerin türleri) - -$(P -Kesirli değerler özellikle belirtilmemişse $(C double) türündedir. Sonlarına $(C f) veya $(C F) eklenirse, $(C float); $(C L) eklenirse $(C real) olurlar. Örneğin $(C 1.2) $(C double)'dır, $(C 3.4f) $(C float)'tur, ve $(C 5.6L) $(C real)'dir. -) - -$(H5 Karakterler) - -$(P -Karakter türündeki hazır değerler her zaman için tek tırnaklar arasında yazılırlar. Örneğin $(C 'a'), $(C '\n'), $(C '\x21'). -) - -$(P $(B Karakterin kendisi olarak:) Tek tırnaklar arasına karakterin kendisi klavyeden yazılabilir veya başka bir metinden kopyalanabilir: $(C 'a'), $(C 'ş'), vs. -) - -$(P $(IX kontrol karakteri) $(B Kontrol karakteri olarak:) Ters bölü işaretinden sonra bir karakter belirteci kullanılabilir. Örneğin ters bölü karakterinin kendisi $(C '\\') şeklinde yazılır. Kontrol karakterleri şunlardır: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     Yazımı  Anlamı
    \'tek tırnak
    \"çift tırnak
    \?soru işareti
    \\ters bölü
    \auyarı karakteri (bazı ortamlarda zil sesi)
    \bsilme karakteri
    \fsayfa sonu
    \nsatır sonu
    \raynı satırın başına götürür
    \tbir sonraki sekme adımına götürür
    \vbir sonraki düşey sekme adımına götürür
    - -$(P $(B Genişletilmiş ASCII karakter kodu olarak:) Karakterleri doğrudan kodları ile belirtebilirsiniz. Yukarıda tamsayılar başlığında anlatılanlara uygun olarak, kodu $(C \x) ile başlayan 2 haneli on altılı sayı olarak veya $(C \) ile başlayan 3 haneye kadar sekizli sayı olarak yazabilirsiniz. Örneğin $(C '\x21') ve $(C '\41') ünlem işareti karakterinin iki farklı yazımıdır. -) - -$(P $(B Unicode karakter kodu olarak:) $(C u) karakterinden sonra 4 on altılı rakam olarak yazılırsa türü $(C wchar) olur; $(C U) karakterinden sonra 8 on altılı rakam olarak yazılırsa türü $(C dchar) olur. Örneğin $(C '\u011e') ve $(C '\U0000011e') Ğ karakterinin sırasıyla $(C wchar) ve $(C dchar) türünde olan değeridir. -) - -$(P $(B İsimli karakter olarak:) İsimleri olan karakterleri isimleriyle ve $(C '\&$(I karakter_ismi);') söz dizimiyle yazabilirsiniz. D, $(LINK2 http://dlang.org/entity.html, HTML 5 karakter isimlerinin hepsini destekler). Örneğin $(C '\&euro;') €, $(C '\&hearts;') ♥, ve $(C '\&copy;') de © karakteridir. -) - - $(H5 $(IX dizgi hazır değeri) $(IX hazır değer, dizgi) Dizgiler) - -$(P -Hazır dizgiler sabit karakterlerin bileşimlerinden oluşurlar ve çok sayıda farklı söz dizimiyle yazılabilirler. -) - -$(H6 Çift tırnaklar arasında yazılan dizgiler) - -$(P -Dizgilerin başka dillerde de bulunan en yaygın yazımı, çift tırnaklar arasında yazılmalarıdır: örneğin $(C "merhaba"). Bu şekilde yazıldığında, içindeki karakterler yukarıdaki karakter yazımlarına uygun olarak yazılırlar. Örneğin, göstermek amacıyla yukarıdaki karakter sabitlerinden bazılarını içeren $(C "A4 ka\u011fıt: 3\&frac12;TL") dizgisi, $(C "A4 kağıt: 3½TL")nin eşdeğeridir. -) - -$(H6 $(IX göründüğü gibi dizgi) $(IX dizgi, göründüğü gibi) $(IX `) $(I Göründüğü gibi çıkan) dizgiler) - -$(P -Ters tırnak işaretleri arasında yazılan dizgilerin içindeki karakterler, yukarıda karakter sabitleriyle ilgili olarak anlatılan kurallar işletilmeden, görüldükleri anlama gelirler. Örneğin $(STRING $(BACK_TICK)c:\nurten$(BACK_TICK)) şeklinde yazılan dizgi, Windows işletim sisteminde bir klasör ismi olabilir. Oysa çift tırnaklar arasında yazılmış olsa, dizginin içinde geçen $(C '\n'), $(I satır sonu) anlamına gelirdi: -) - ---- - writeln(`c:\nurten`); - writeln("c:\nurten"); ---- - -$(SHELL -c:\nurten $(SHELL_NOTE göründüğü gibi) -c: $(SHELL_NOTE_WRONG satır sonu olarak anlaşılan karakter) -urten -) - -$(P -Göründüğü gibi çıkan dizgilerin diğer bir yazım şekli, çift tırnaklar kullanmak, ama öncesine bir $(C r) belirteci eklemektir: $(C r"c:\nurten") de göründüğü gibi anlaşılır. -) - -$(H6 $(IX on altılı dizgi) On altılı sistemde yazılan dizgiler) - -$(P -Karakterlerinin kodları on altılı sayı sisteminde yazılacak olan dizgilerin her karakterinin başına $(C \x) yazmak yerine, başına $(C x) belirteci gelen dizgiler kullanılabilir. Hatta bu dizgilerin içine okumayı kolaylaştırmak amacıyla boşluklar da yazılabilir. Bu boşluklar derleyici tarafından gözardı edilirler. Örneğin $(C "\x44\x64\x69\x6c\x69") yerine $(C x"44 64 69 6c 69") yazılabilir. -) - -$(H6 $(IX q"") $(IX ayraçlı dizgi) Ayraçlı dizgiler) - -$(P -Çift tırnakların hemen içine gelmek koşuluyla, dizginin parçası olmayan ayraçlar yerleştirebilirsiniz. Ayraçlı dizgilerde çift tırnaklardan önce $(C q) karakteri gelir: $(C q".merhaba.") dizgisinin değeri "merhaba"dır; noktalar değere ait değillerdir. Hemen sonrası satır sonuna gelmek koşuluyla, ayraçları sözcükler olarak da belirleyebilirsiniz: -) - ---- -writeln(q"AYRAÇ -birinci satır -ikinci satır -AYRAÇ"); ---- - -$(P -Bu örnekteki AYRAÇ sözcüğü dizginin parçası değildir: -) - -$(SHELL -birinci satır -ikinci satır -) - -$(H6 $(IX q{}) $(IX kod dizgisi) $(IX hazır değer, kod dizgisi) D kodu dizgileri) - -$(P -Yine başında $(C q) karakteri olmak üzere, $(C {) ve $(C }) karakterleri arasında yasal D kodu içeren dizgiler yazılabilir: -) - ---- - auto dizgi = q{int sayı = 42; ++sayı;}; - writeln(dizgi); ---- - -$(P -Çıktısı: -) - -$(SHELL -int sayı = 42; ++sayı; -) - -$(H6 $(IX string) $(IX wstring) $(IX dstring) Dizgi değerlerin türleri) - -$(P -Dizgiler özellikle belirtilmediğinde $(C immutable(char)[]) türündedirler. Sonlarına eklenen $(C c), $(C w), ve $(C d) karakterleri dizginin türünü sırasıyla $(C immutable(char)[]), $(C immutable(wchar)[]), ve $(C immutable(dchar)[]) olarak belirler. Örneğin $(C "merhaba"d) dizgisinin karakterleri $(C immutable(dchar)) türündedirler. -) - -$(P -Bu üç türün sırasıyla $(C string), $(C wstring), ve $(C dstring) olan takma isimlerini $(LINK2 /ders/d/dizgiler.html, Dizgiler bölümünde) öğrenmiştiniz. -) - -$(H5 Hazır değerler derleme zamanında hesaplanırlar) - -$(P -Hazır değerleri işlem halinde de yazabilirsiniz. Örneğin Ocak ayındaki toplam saniye değerini $(C 2678400) veya $(C 2_678_400) olarak yazmak yerine, değerin doğruluğundan emin olmamıza yarayan $(C 60 * 60 * 24 * 31) şeklinde de yazabilirsiniz. İçinde çarpma işleçleri olsa da, o işlem programınızın çalışma hızını düşürmez; hazır değer, derleme zamanında yine de 2678400 olarak hesaplanır ve sanki siz öyle yazmışsınız gibi derlenir. -) - -$(P -Aynı durum dizgi hazır değerleri için de geçerlidir. Örneğin $(C "merhaba " ~ "dünya") yazımındaki $(I dizgi birleştirme) işlemi çalışma zamanında değil, derleme zamanında yapıldığı için programınız sanki "merhaba dünya" yazılmış gibi derlenir ve çalışır. -) - -$(PROBLEM_COK - -$(PROBLEM -Aşağıdaki satır derlenemez: - ---- - int miktar = 10_000_000_000; $(DERLEME_HATASI) ---- - -$(P -Derleme hatasını giderin ve $(C miktar)'ın on milyara eşit olmasını sağlayın. -) - -) - -$(PROBLEM -Bir tamsayının değerini sonsuz bir döngüde arttıran ve bunu ekrana yazdıran bir program yazın. Döngünün her tekrarında sayının değeri ekrana yazdırıldığı halde, yazılan değer hep aynı satırda çıksın: - -$(SHELL -Sayı: 25774 $(SHELL_NOTE hep aynı satırın üstüne yazılsın) -) - -$(P -Bunun için yukarıdaki kontrol karakterlerinden birisi işinize yarayacak. -) - -) - -) - -Macros: - SUBTITLE=Hazır Değerler - - DESCRIPTION=D dilinde kaynak kod içine hazır olarak yazılan sabit değerlerin tanıtılması ve söz dizimleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial hazır değer literal - -SOZLER= -$(degisken) -$(hazir_deger) -$(isaretli_tur) -$(isaretsiz_tur) -$(islec) -$(islev) -$(nesne) -$(sekme) diff --git a/ddili/src/ders/d/ic_tanimlar.d b/ddili/src/ders/d/ic_tanimlar.d deleted file mode 100644 index de5ef12..0000000 --- a/ddili/src/ders/d/ic_tanimlar.d +++ /dev/null @@ -1,287 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX işlev, iç) $(IX struct, iç) $(IX class, iç) $(IX iç tanım) İç İşlevler, Yapılar, ve Sınıflar) - -$(P -İşlevler, yapılar, ve sınıflar iç kapsamlarda tanımlanabilirler. Bu hem isimlerin daha dar kapsamlarda geçerli olmalarını ve böylece bir anlamda o isimlerin sarmalanmalarını sağlar hem de $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler bölümünde) gördüğümüz kapamaların başka bir gerçekleştirmesidir. -) - -$(P -Bir örnek olarak, aşağıdaki $(C dışİşlev()) işlevinin kapsamında bir işlev, bir yapı, ve bir de sınıf tanımlanmaktadır: -) - ---- -void dışİşlev(int parametre) { - int yerel; - - $(HILITE void içİşlev()) { - yerel = parametre * 2; - } - - $(HILITE struct İçYapı) { - void üyeİşlev() { - yerel /= parametre; - } - } - - $(HILITE class İçSınıf) { - void üyeİşlev() { - yerel += parametre; - } - } - - // İşlev içindeki kullanımları: - - içİşlev(); - - auto y = İçYapı(); - y.üyeİşlev(); - - auto s = new İçSınıf(); - s.üyeİşlev(); -} - -void main() { - dışİşlev(42); -} ---- - -$(P -Beklenebileceği gibi, iç tanımlar dış kapsamlarındaki değişkenlere erişebilirler. Örneğin, yukarıdaki koddaki iç tanımların üçü de $(C parametre) ve $(C yerel) adlı değişkenlere erişebilmektedir. -) - -$(P -İşlev içinde tanımlanan değişkenlerde olduğu gibi, işlev içinde tanımlanan isimler de yalnızca tanımlandıkları kapsamda geçerlidir. Örneğin; $(C içİşlev()), $(C İçYapı), ve $(C İçSınıf) isimleri $(C main()) içinde kullanılamaz: -) - ---- -void main() { - auto a = İçYapı(); $(DERLEME_HATASI) - auto b = dışİşlev.İçYapı(); $(DERLEME_HATASI) -} ---- - -$(P -Ancak, isimleri kullanılamasalar da iç tanımlar başka kapsamlarda kullanılabilirler. Örneğin, bir çok Phobos algoritması görevini kendi içinde tanımladığı bir yapı aracılığıyla gerçekleştirir. -) - -$(P -Bunun bir örneğini görmek için kendisine verilen dilimi bir baştan bir sondan tüketerek kullanan bir işlev tanımlayalım: -) - ---- -import std.stdio; -import std.array; - -auto baştanSondan(T)(T[] dilim) { - bool baştan_mı = true; - - $(HILITE struct BaştanSondanAralığı) { - bool empty() @property const { - return dilim.empty; - } - - T front() @property const { - return baştan_mı ? dilim.front : dilim.back; - } - - void popFront() { - if (baştan_mı) { - dilim.popFront(); - baştan_mı = false; - - } else { - dilim.popBack(); - baştan_mı = true; - } - } - } - - return BaştanSondanAralığı(); -} - -void main() { - auto a = baştanSondan([ 1, 2, 3, 4, 5 ]); - writeln(a); -} ---- - -$(P -Her ne kadar ismine erişemese de, $(C main()) $(C baştanSondan()) işlevinin kurduğu ve döndürdüğü iç yapı nesnesini kullanabilir: -) - -$(SHELL -[1, 5, 2, 4, 3] -) - -$(P -$(IX Voldemort) $(I Not: İsimlerinin $(I söylenemiyor) olması Harry Potter karakterlerinden Voldemort'u çağrıştırdığından bu çeşit türlere $(I Voldemort türü) denir.) -) - -$(P -$(IX kapama) $(IX kapsam) Dikkat ederseniz, $(C baştanSondan()) işlevinin döndürdüğü iç yapının hiçbir üyesi bulunmamaktadır. O yapı görevini yalnızca işlev parametresi olan $(C dilim)'i ve yerel değişken olan $(C baştan_mı)'yı kullanarak gerçekleştirmektedir. Bu değişkenlerin normalde işlevden çıkılırken sonlanacak olan yaşamları iç yapı nesnesi yaşadığı sürece uzatılır. Bu; $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler bölümünde) gördüğümüz $(I kapsam saklama) kavramının aynısıdır: İşlevlerden döndürülen iç tanımlar tanımlandıkları kapsamların yaşam süreçlerini kendileri yaşadıkları sürece uzatırlar ve böylece fonksiyonel programlamadaki $(I kapama) kavramını oluştururlar. -) - -$(H6 $(IX static, iç tanım) Kapama gerekmeyen durumlarda $(C static)) - -$(P -Tanımlandıkları kapsamı da barındırdıklarından iç tanımlar modül düzeyinde tanımlanmış olan benzerlerinden daha masraflıdır. Ek olarak, bu türlerin nesneleri işledikleri kapsamın hangisi olduğunu bildiren gizli bir $(I kapsam göstergesi) de barındırmak zorundadırlar. İç tanım nesneleri bu yüzden daha fazla yer de kaplarlar. Örneğin, aynı sayıda üye değişkene sahip oldukları halde aşağıdaki iki yapının boyutları farklıdır: -) - ---- -import std.stdio; - -$(HILITE struct Dış) { - int i; - - void işlev() { - } -} - -void foo() { - $(HILITE struct İç) { - int i; - - void işlev() { - } - } - - writefln("Dıştaki %s bayt, içteki %s bayt", - Dış.sizeof, İç.sizeof); -} - -void main() { - foo(); -} ---- - -$(P -Büyüklükler farklı ortamlarda farklı olabilir. Benim ortamımdaki çıktısı aşağıdaki gibi: -) - -$(SHELL -Dıştaki $(HILITE 4) bayt, içteki $(HILITE 16) bayt -) - -$(P -$(IX static class) $(IX static struct) İç tanımlar bazen yalnızca kodu olabildiğince yerel tanımlamak amacıyla kullanılırlar; kapsamdaki değişkenlere erişmeyle ve dolayısıyla kapama oluşturmayla ilgileri yoktur. Getirdikleri masraf böyle durumlarda gereksiz olacağından iç tanımların normal tanımlara eşdeğer olmaları istendiğinde $(C static) anahtar sözcüğü kullanılır. Bunun doğal sonucu olarak $(C static) iç tanımlar kapsamdaki değişkenlere erişemezler: -) - ---- -void dışİşlev(int parametre) { - $(HILITE static) class İçSınıf { - int i; - - this() { - i = parametre; $(DERLEME_HATASI) - } - } -} ---- - -$(P -$(IX .outer, void*) Bir iç sınıf nesnesinin kapsam göstergesi $(C .outer) niteliği ile $(C void*) türünde elde edilebilir. Örneğin, aynı kapsamda oluşturulan iki sınıf değişkeninin kapsam göstergeleri bekleneceği gibi aynıdır: -) - ---- -void foo() { - class C { - } - - auto a = new C(); - auto b = new C(); - - assert(a$(HILITE .outer) is b$(HILITE .outer)); -} ---- - -$(P -Sınıf içinde tanımlanan sınıflarda kapsam göstergesinin türü $(C void*) değil, dış sınıfın türüdür. Bunu biraz aşağıda göreceğiz. -) - -$(H6 $(IX class, sınıf içinde) Sınıf içinde tanımlanan sınıflar) - -$(P -Bir sınıf başka bir sınıf içinde tanımlandığında iç sınıfın kapsamı dış sınıf nesnesinin kendisidir. -) - -$(P -$(IX .new) $(IX .outer, class) Bu çeşit iç sınıf nesneleri özel $(C this.new) söz dizimi ile oluşturulurlar. Dış kapsamı oluşturan nesneye gerektiğinde $(C this.outer) ile erişilebilir: -) - ---- -class Dış { - int dışÜye; - - $(HILITE class İç) { - int işlev() { - /* İç sınıf dış sınıfın üyelerine erişebilir. */ - return dışÜye * 2; - } - - Dış dışNesne() { - /* İç nesne kendi kapsamı olan dış nesnesine - * 'outer' anahtar sözcüğüyle erişebilir. Bu - * örnekte yalnızca dönüş değeri olarak - * kullanıyor. */ - return $(HILITE this.outer); - } - } - - İç algoritma() { - /* Dış kendisini kapsam olarak kullanacak olan bir İç - * nesnesini özel 'this.new' söz dizimi ile kurar. */ - return $(HILITE this.new) İç(); - } -} - -void main() { - auto dış = new Dış(); - - /* Dış'ın bir işlevinin bir İç nesnesi döndürmesi: */ - auto iç = dış.algoritma(); - - /* Döndürülen nesnenin kullanılması: */ - iç.işlev(); - - /* Doğal olarak 'iç'in kapsamı 'dış'tır: */ - assert(iç.dışNesne() is dış); -} ---- - -$(P -Bu örnekteki $(C this.new) ve $(C this.outer) söz dizimleri yerine $(C .new) ve $(C .outer) var olan değişkenlere de uygulanabilir: -) - ---- - auto dış = new Dış(); - auto iç = $(HILITE dış.new) Dış.İç(); - auto dış2 = $(HILITE iç.outer); ---- - -$(H5 Özet) - -$(UL - -$(LI İç kapsamlarda tanımlanan işlevler, yapılar, ve sınıflar o kapsamlardaki isimlere doğrudan erişebilirler.) - -$(LI İç tanımlar tanımlandıkları kapsamları canlı tutarak kapama oluştururlar.) - -$(LI İç tanımlar normal tanımlardan daha masraflıdır. Bu masraf kapama gerekmeyen durumlarda $(C static) anahtar sözcüğü ile önlenir.) - -$(LI Sınıf içinde tanımlanan sınıfın kapsamı, dışındaki sınıf nesnesidir. Sınıf içi sınıflar $(C this.new) veya $(C değişken.new) ile kurulurlar; kapsamlarına $(C this.outer) veya $(C değişken.outer) ile erişilir.) - -) - -Macros: - SUBTITLE=İç İşlevler, Yapılar, ve Sınıflar - - DESCRIPTION=İşlevlerin, yapıların, ve sınıfların iç kapsamlarda tanımlanmaları. - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial işlev yapı sınıf iç - -SOZLER= -$(gosterge) -$(ic_tanim) -$(kapama) -$(sarma) diff --git a/ddili/src/ders/d/if_kosulu.cozum.d b/ddili/src/ders/d/if_kosulu.cozum.d deleted file mode 100644 index 800a682..0000000 --- a/ddili/src/ders/d/if_kosulu.cozum.d +++ /dev/null @@ -1,110 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU if Koşulu) - -$(OL - -$(LI -Bu programda $(C writeln("Tabağı kaldırıyorum")) ifadesi sanki $(C else) kapsamındaymış gibi içerletilerek yazılmış. Oysa $(C else)'ten sonra küme parantezleri kullanılmadığı için, kurallar gereği bu $(C else) kapsamında tek bir ifade vardır: $(C writeln("Baklava yiyorum")). - -$(P -Programdaki boşlukların önemi de olmadığı için (yazım hatalarına neden olmadıkları sürece) tabaklı ifade aslında $(C main) içinde serbest bir ifadedir ve hiçbir koşula bağlı olmadan her zaman için işletilir. İçerletildiği için okuyanı yanıltabiliyor. Eğer tabaklı ifade de $(C else) kapsamında olacaksa, o zaman küme parantezlerini unutmamak gerekir: -) - ---- -import std.stdio; - -void main() { - bool limonata_var = true; - - if (limonata_var) { - writeln("Limonata içiyorum"); - writeln("Bardağı yıkıyorum"); - - } else $(HILITE {) - writeln("Baklava yiyorum"); - writeln("Tabağı kaldırıyorum"); - $(HILITE }) -} ---- - -) - -$(LI -Bu oyundaki koşulları tasarlamak için birden çok çözüm düşünebiliriz. Ben iki tane göstereceğim. Önce soruda verilen bilgiyi bire bir uygulayarak: - ---- -import std.stdio; - -void main() { - write("Zar kaç geldi? "); - int zar; - readf(" %s", &zar); - - if (zar == 1) { - writeln("Siz kazandınız"); - - } else if (zar == 2) { - writeln("Siz kazandınız"); - - } else if (zar == 3) { - writeln("Siz kazandınız"); - - } else if (zar == 4) { - writeln("Ben kazandım"); - - } else if (zar == 5) { - writeln("Ben kazandım"); - - } else if (zar == 6) { - writeln("Ben kazandım"); - - } else { - writeln("HATA: Geçersiz değer: ", zar); - } -} ---- - -$(P -Ne yazık ki o programda çok tekrar bulunuyor. Aynı sonucu başka biçimde de elde edebiliriz. Bir tanesi: -) - ---- -import std.stdio; - -void main() { - write("Zar kaç geldi? "); - int zar; - readf(" %s", &zar); - - if ((zar == 1) || (zar == 2) || (zar == 3)) { - writeln("Siz kazandınız"); - - } else if ((zar == 4) || (zar == 5) || (zar == 6)) { - writeln("Ben kazandım"); - - } else { - writeln("HATA: Geçersiz değer: ", zar); - } -} ---- - -) - -$(LI -Artık yukarıda gösterilen çözümleri kullanamayız. Kimse 1000 değişik değeri öyle açıkça yazmaz: aşırı emek gerektirir, doğruluğundan emin olunamaz, okuyan bir şey anlamaz, vs. O yüzden burada "bu sayı iki sınırın arasında mı" karşılaştırmasını kullanırız: - ---- - if ((sayı >= 1) && (sayı <= 500)) ---- - -) - -) - -Macros: - SUBTITLE=if Koşulu Problem Çözümleri - - DESCRIPTION=D.ershane D programlama dili dersi çözümleri: if Koşulu - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial if koşulu deyimi problem çözüm diff --git a/ddili/src/ders/d/if_kosulu.d b/ddili/src/ders/d/if_kosulu.d deleted file mode 100644 index 006e574..0000000 --- a/ddili/src/ders/d/if_kosulu.d +++ /dev/null @@ -1,310 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX if) $(CH4 if) Koşulu) - -$(P -Programda asıl işlerin ifadeler tarafından yapıldığını öğrendik. Şimdiye kadar gördüğümüz programlarda işlemler $(C main) isimli işlev içinde baştan sona doğru ve yazıldıkları sırada işletiliyorlardı. -) - -$(P -$(IX deyim) D'de deyimler, ifadelerin işletilme kararlarını veren ve ifadelerin işletilme sıralarını etkileyen program yapılarıdır. Kendileri değer üretmezler ve yan etkileri yoktur. Deyimler, ifadelerin işletilip işletilmeyeceklerini ve bu ifadelerin hangi sırada işletileceklerini belirlerler. Bu kararları verirken de yine ifadelerin değerlerinden yararlanırlar. -) - -$(P $(I $(B Not:) İfade ve deyim kavramlarının burada öğrendiğiniz tanımları D dilindeki tanımlarıdır. Başka dillerdeki tanımları farklılıklar gösterir ve hatta bazı dillerde böyle bir ayrım yoktur.) -) - -$(H5 $(C if) bloğu ve kapsamı) - -$(P -$(C if) deyimi, ifadelerin işletilip işletilmeyeceğine belirli bir mantıksal ifadenin sonucuna bakarak karar veren yapıdır. "if", İngilizce'de "eğer" anlamındadır; "eğer tatlı varsa" kullanımında olduğu gibi... -) - -$(P -Parantez içinde bir mantıksal ifade alır, eğer o ifade doğruysa (yani değeri $(C true) ise), küme parantezleri içindeki ifadeleri işletir. Bunun tersi olarak, mantıksal ifade doğru değilse küme parantezleri içindeki ifadeleri işletmez. -) - -$(P -Söz dizimi şöyledir: -) - ---- - if (bir_mantıksal_ifade) { - // işletilecek bir ifade - // işletilecek başka bir ifade - // vs. - } ---- - -$(P -Örneğin "eğer baklava varsa baklava ye ve sonra tabağı kaldır" gibi bir program yapısını şöyle yazabiliriz: -) - ---- -import std.stdio; - -void main() { - bool baklava_var = true; - - if (baklava_var) { - writeln("Baklava yiyorum..."); - writeln("Tabağı kaldırıyorum..."); - } -} ---- - -$(P -O programda $(C baklava_var)'ın değeri $(C false) yapılırsa çıkışa hiçbir şey yazdırılmaz, çünkü $(C if) deyimi kendisine verilen mantıksal ifade $(C false) olduğunda küme parantezi içindeki ifadeleri işletmez. -) - -$(P -Küme parantezleriyle gruplanmış ifadelerin tümüne $(I blok), o bölgeye de $(I kapsam) adı verilir. -) - -$(P -Yazım yanlışlarına yol açmadığı sürece, okumayı kolaylaştırmak için programda istediğiniz gibi boşluklar kullanabilirsiniz. ) - -$(H5 $(IX else) $(C else) bloğu ve kapsamı) - -$(P -Çoğu zaman $(C if)'e verilen mantıksal ifadenin doğru olmadığı durumda da bazı işlemler yapmak isteriz. Örneğin "eğer ekmek varsa yemek ye, yoksa bakkala git" gibi bir kararda ekmek olsa da olmasa da bir eylem vardır. -) - -$(P -D'de ifade $(C false) olduğunda yapılacak işler $(C else) anahtar sözcüğünden sonraki küme parantezleri içinde belirtilir. "else", "değilse" demektir. Söz dizimi şöyledir: -) - ---- - if (bir_mantıksal_ifade) { - // doğru olduğunda işletilen ifadeler - - } else { - // doğru olMAdığında işletilen ifadeler - } ---- - -$(P -Örnek olarak: -) - ---- - if (ekmek_var) { - writeln("Yemek yiyorum"); - - } else { - writeln("Bakkala yürüyorum"); - } ---- - -$(P -O örnekte $(C ekmek_var)'ın değerine göre ya birinci dizgi ya da ikinci dizgi yazdırılır. -) - -$(P -$(C else) kendisi bir deyim değildir, $(C if) deyiminin seçime bağlı bir parçasıdır; tek başına kullanılamaz. -) - -$(P -Yukarıdaki $(C if) ve $(C else) bloklarının küme parantezlerinin hangi noktalara yazıldıklarına dikkat edin. $(LINK2 http://dlang.org/dstyle.html, Kabul edilen D kodlama standardına) göre küme parantezleri aslında kendi satırlarına yazılırlar. Bu kitap yaygın olan başka bir kodlama standardına uygun olarak deyim küme parantezlerini deyimlerle aynı satırlara yazar. -) - -$(H5 Kapsam parantezlerini hep kullanın) - -$(P -Hiç tavsiye edilmez ama bunu bilmenizde yarar var: Eğer $(C if)'in veya $(C else)'in altındaki ifade tekse, küme parantezleri gerekmez. Yukarıdaki ifade küme parantezleri kullanılmadan aşağıdaki gibi de yazılabilir: -) - ---- - if (ekmek_var) - writeln("Yemek yiyorum"); - - else - writeln("Bakkala yürüyorum"); ---- - -$(P -Çoğu deneyimli programcı tek ifadelerde bile küme parantezi kullanır. (Bununla ilgili bir hatayı problemler bölümünde göreceksiniz.) Mutlaka küme parantezleri kullanmanızı bu noktada önermemin bir nedeni var: Bu öneriye hemen hemen hiçbir zaman uyulmayan tek durumu da şimdi anlatacağım. -) - -$(H5 $(IX else if) "if, else if, else" zinciri) - -$(P -Dilin bize verdiği güçlerden birisi, ifade ve deyimleri serbestçe karıştırarak kullanma imkanıdır. İfade ve deyimleri kapsamlar içinde de kullanabiliriz. Örneğin bir $(C else) kapsamında bile $(C if) deyimi bulunabilir. Programların $(I akıllı) olarak algılanmaları, hep bizim ifade ve deyimleri doğru sonuçlar verecek şekilde birbirlerine bağlamamızdan doğar. Bisiklete binmeyi yürümekten daha çok sevdiğimizi varsayarsak: -) - ---- - if (ekmek_var) { - writeln("Yemek yiyorum"); - - } else { - - if (bisiklet_var) { - writeln("Uzaktaki fırına gidiyorum"); - - } else { - writeln("Yakındaki bakkala yürüyorum"); - } - - } ---- - -$(P -Oradaki $(C if) deyimlerinin anlamı şudur: "eğer ekmek varsa: yemek yiyorum; eğer ekmek yoksa: bisiklet varsa fırına gidiyorum, yoksa bakkala yürüyorum". -) - -$(P -Biraz daha ileri gidelim ve bisiklet olmadığında hemen bakkala yürümek yerine, komşunun evde olup olmamasına göre davranalım: -) - ---- - if (ekmek_var) { - writeln("Yemek yiyorum"); - - } else { - - if (bisiklet_var) { - writeln("Uzaktaki fırına gidiyorum"); - - } else { - - if (komşu_evde) { - writeln("Komşudan istiyorum"); - - } else{ - writeln("Yakındaki bakkala yürüyorum"); - } - } - } ---- - -$(P -Burada görüldüğü gibi "eğer böyleyse bunu yap, değilse ama öyleyse onu yap, o da değilse ama şöyleyse şunu yap, vs." gibi yapılar programcılıkta çok kullanılır. Ne yazık ki böyle yazınca kodda fazla boşluklar oluşur: buradaki 3 $(C if) deyimi ve 4 $(C writeln) ifadesi için toplam 13 satır yazmış olduk (boş satırları saymadan). -) - -$(P -Sık karşılaşılan bu yapıyı daha düzenli olarak yazmak için, böyle zincirleme kararlarda bir istisna olarak $(I içlerinde tek bir $(C if) deyimi bulunan) $(C else)'lerin kapsam parantezlerini yazmayız. -) - -$(P -Hiçbir zaman kodu aşağıdaki gibi düzensiz bırakmamanızı öneririm. Ben bir sonraki adıma geçme aşaması olarak gösteriyorum. İçlerinde tek bir $(C if) olan $(C else)'lerin küme parantezlerini kaldırınca kod aşağıdaki gibi olur: -) - ---- - if (ekmek_var) { - writeln("Yemek yiyorum"); - - } else - - if (bisiklet_var) { - writeln("Uzaktaki fırına gidiyorum"); - - } else - - if (komşu_evde) { - writeln("Komşudan istiyorum"); - - } else{ - writeln("Yakındaki bakkala yürüyorum"); - } ---- - -$(P -Bir adım daha ileri giderek $(C if) anahtar sözcüklerini de üstlerindeki $(C else) satırlarına çeker ve biraz da hizalarsak, son derece okunaklı bir yapı oluşur: -) - ---- - if (ekmek_var) { - writeln("Yemek yiyorum"); - - } else if (bisiklet_var) { - writeln("Uzaktaki fırına gidiyorum"); - - } else if (komşu_evde) { - writeln("Komşudan istiyorum"); - - } else{ - writeln("Yakındaki bakkala yürüyorum"); - } ---- - -$(P -Böylece hem satır sayısı azalmış olur, hem de kararlara göre işletilecek olan bütün ifadeler alt alta gelmiş olurlar. Dört koşulun hangi sırada denetlendiği ve her koşulda ne yapıldığı bir bakışta anlaşılır. -) - -$(P -Çok sık karşılaşılan bu kod yapısına "if, else if, else" denir. -) - -$(PROBLEM_COK - -$(PROBLEM - -Aşağıdaki programdaki mantıksal ifadenin $(C true) olduğunu görüyoruz. Dolayısıyla programın $(I limonata içip bardağı yıkamasını) bekleriz: - ---- -import std.stdio; - -void main() { - bool limonata_var = true; - - if (limonata_var) { - writeln("Limonata içiyorum"); - writeln("Bardağı yıkıyorum"); - - } else - writeln("Baklava yiyorum"); - writeln("Tabağı kaldırıyorum"); -} ---- - -Oysa programı çalıştırırsanız, çıktısında bir de $(I tabak kaldırıldığını) göreceksiniz: - -$(SHELL -Limonata içiyorum -Bardağı yıkıyorum -Tabağı kaldırıyorum -) - -Neden? Programı düzelterek beklenen çıktıyı vermesini sağlayın. - -) - -$(PROBLEM -Kullanıcıyla oyun oynayan (ve ona fazlasıyla güvenen) bir program yazın. Kullanıcı attığı zarın değerini girsin. Zarın değerine göre ya kullanıcı kazansın, ya da program: - -$(MONO -$(B Zar Değeri Program Çıktısı) - 1 Siz kazandınız - 2 Siz kazandınız - 3 Siz kazandınız - 4 Ben kazandım - 5 Ben kazandım - 6 Ben kazandım -Başka bir değer HATA: Geçersiz değer -) - -Ek puan: Hatalı giriş oluştuğunda değeri de yazsın. Örneğin: - -$(SHELL -HATA: Geçersiz değer: 7 -) -) - -$(PROBLEM -Aynı oyunu şöyle değiştirelim: Kullanıcı 1'den 1000'e kadar bir sayı girsin ve 1-500 aralığında siz kazanın, 501-1000 aralığında bilgisayar kazansın. Hâlâ bir önceki problemdeki çözümleri uygulayabilir misiniz? -) - -) - -Macros: - SUBTITLE=if Koşulu - - DESCRIPTION=D dilinin koşul deyimlerinden if'in tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial if koşul deyim - -SOZLER= -$(blok) -$(deyim) -$(ifade) -$(kapsam) -$(mantiksal_ifade) diff --git a/ddili/src/ders/d/index.d b/ddili/src/ders/d/index.d deleted file mode 100644 index 5ca1abb..0000000 --- a/ddili/src/ders/d/index.d +++ /dev/null @@ -1,139 +0,0 @@ -Ddoc - -$(DERS_BOLUMU D Programlama Dili) - -
    - - - -$(H5 Ekitap sürümleri) - -$(UL - -$(LI -$(I İstediğiniz kadar) ödeme olanağı veren $(LINK2 https://gum.co/DDili, Gumroad'dan indirebilirsiniz.) -) - -$(LI Ücretsiz olarak buradan $(LINK_DOWNLOAD http://ddili.org/ders/d/D_Programlama_Dili.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d/D_Programlama_Dili.epub, EPUB) (çoğu ekitap okuyucusu için), $(LINK_DOWNLOAD http://ddili.org/ders/d/D_Programlama_Dili.azw3, AZW3) (yeni Kindle'lar için), veya $(LINK_DOWNLOAD http://ddili.org/ders/d/D_Programlama_Dili.mobi, MOBI) (eski Kindle'lar için) düzeninde indirebilirsiniz. -) - -) - -
    - -$(P -$(LINK_DOWNLOAD /ders/d/D_Programlama_Dili_kod_ornekleri.zip, Kod örneklerini $(C .zip) dosyası halinde indirmek için buraya tıklayın.) -) - -$(H5 Web sürümü) - -$(P $(LINK2 /ders/d/ix.html, Kitap Dizini)) - -$(UL - -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d/onsoz1.html, Walter Bright'ın Önsözü)) -) -$(LI $(LINK2 /ders/d/onsoz2.html, Önsöz – Andrei Alexandrescu)) -$(LI $(LINK2 /ders/d/onsoz_yazar.html, Yazarın Önsözü)) -$(LI $(LINK2 /ders/d/merhaba_dunya.html, "Merhaba Dünya" Programı) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d/writeln.html, writeln ve write)) -$(LI $(LINK2 /ders/d/derleyici.html, Derleme)) -$(LI $(LINK2 /ders/d/temel_turler.html, Temel Türler) $(INDEX_KEYWORDS char int double (ve daha başka))) -$(LI $(LINK2 /ders/d/atama_ve_sira.html, Atama ve İşlem Sıraları) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d/degiskenler.html, Değişkenler)) -$(LI $(LINK2 /ders/d/giris_cikis.html, Standart Giriş ve Çıkış Akımları) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d/standart_giris.html, Girişten Bilgi Almak)) -$(LI $(LINK2 /ders/d/mantiksal_ifadeler.html, Mantıksal İfadeler) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d/if_kosulu.html, if Koşulu) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d/while_dongusu.html, while Döngüsü) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d/aritmetik_islemler.html, Tamsayılar ve Aritmetik İşlemler) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d/kesirli_sayilar.html, Kesirli Sayılar) $(INDEX_KEYWORDS .nan .infinity isNaN <> !<>= (ve daha başka))) -$(LI $(LINK2 /ders/d/diziler.html, Diziler) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d/karakterler.html, Karakterler) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d/dilimler.html, Başka Dizi Olanakları) $(INDEX_KEYWORDS .. $ .dup capacity)) -$(LI $(LINK2 /ders/d/dizgiler.html, Dizgiler) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d/standart_akim_baglamak.html, Standart Akımları Dosyalara Bağlamak)) -$(LI $(LINK2 /ders/d/dosyalar.html, Dosyalar) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d/auto.html, auto ve typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d/isim_alani.html, İsim Alanı)) -$(LI $(LINK2 /ders/d/for_dongusu.html, for Döngüsü) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d/uclu_islec.html, Üçlü İşleç ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d/hazir_degerler.html, Hazır Değerler)) -$(LI $(LINK2 /ders/d/cikti_duzeni.html, Çıktı Düzeni) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d/giris_duzeni.html, Giriş Düzeni)) -$(LI $(LINK2 /ders/d/do_while.html, do-while Döngüsü) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d/esleme_tablolari.html, Eşleme Tabloları) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d/foreach_dongusu.html, foreach Döngüsü) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d/switch_case.html, switch ve case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d/islevler.html, İşlevler) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d/const_ve_immutable.html, Değişmezlik) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d/deger_sol_sag.html, Sol Değerler ve Sağ Değerler) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d/tembel_degerlendirmeler.html, Tembel İşleçler)) -$(LI $(LINK2 /ders/d/main.html, Programın Çevresiyle Etkileşimi) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d/hatalar.html, Hata Yönetimi) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d/assert.html, assert ve enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d/birim_testler.html, Birim Testleri) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d/sozlesmeli.html, Sözleşmeli Programlama) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d/yasam_surecleri.html, Yaşam Süreçleri ve Temel İşlemler)) -$(LI $(LINK2 /ders/d/null_ve_is.html, null Değeri ve is İşleci) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d/tur_donusumleri.html, Tür Dönüşümleri) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d/yapilar.html, Yapılar) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ __PRETTY_FUNCTION__)) -$(LI $(LINK2 /ders/d/islev_yukleme.html, İşlev Yükleme)) -$(LI $(LINK2 /ders/d/uye_islevler.html, Üye İşlevler) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d/const_uye_islevler.html, const ref Parametreler ve const Üye İşlevler) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d/ozel_islevler.html, Kurucu ve Diğer Özel İşlevler) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (ve daha başka))) -$(LI $(LINK2 /ders/d/siniflar.html, Sınıflar) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d/tureme.html, Türeme) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d/interface.html, Arayüzler) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d/clear.html, destroy ve scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d/moduller.html, Modüller ve Kütüphaneler) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d/sarma.html, Sarma ve Erişim Hakları) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d/ufcs.html, İşlev Çağırma Ortak Söz Dizimi (UFCS))) -$(LI $(LINK2 /ders/d/nitelikler.html, Nitelikler) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d/invariant.html, Yapı ve Sınıflarda Sözleşmeli Programlama) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d/sablonlar.html, Şablonlar)) -$(LI $(LINK2 /ders/d/pragma.html, Pragmalar)) -$(LI $(LINK2 /ders/d/alias.html, alias ve with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d/gostergeler.html, Göstergeler) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d/bit_islemleri.html, Bit İşlemleri) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d/kosullu_derleme.html, Koşullu Derleme) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d/is_ifadesi.html, is İfadesi) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d/foreach_opapply.html, Yapı ve Sınıflarda foreach) $(INDEX_KEYWORDS opApply empty popFront front (ve daha başka))) -$(LI $(LINK2 /ders/d/ic_tanimlar.html, İç İşlevler, Yapılar, ve Sınıflar) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d/birlikler.html, Birlikler) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d/etiketler.html, Etiketler ve goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d/cokuzlular.html, Çokuzlular) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d/islevler_diger.html, Diğer İşlev Olanakları) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d/katmalar.html, Katmalar) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d/araliklar.html, Aralıklar) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d/araliklar_baska.html, Başka Aralık Olanakları) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (ve daha başka))) -$(LI $(LINK2 /ders/d/kosut_islemler.html, Koşut İşlemler) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d/es_zamanli.html, Mesajlaşarak Eş Zamanlı Programlama) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (ve daha başka))) -$(LI $(LINK2 /ders/d/es_zamanli_shared.html, Veri Paylaşarak Eş Zamanlı Programlama) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d/fiberler.html, Fiberler) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d/bellek_yonetimi.html, Bellek Yönetimi) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d/uda.html, Kullanıcı Nitelikleri (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d/islec_oncelikleri.html, İşleç Öncelikleri)) -) - -Macros: - SUBTITLE=D Programlama Dili - - DESCRIPTION=D programlama dili kitabı - - KEYWORDS=d programlama dili bölümleri öğrenmek tutorial - - BREADCRUMBS=$(BREADCRUMBS_INDEX) - -SOZLER= diff --git a/ddili/src/ders/d/index_section_head.html b/ddili/src/ders/d/index_section_head.html deleted file mode 100644 index 1a79845..0000000 --- a/ddili/src/ders/d/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Dizin

    diff --git a/ddili/src/ders/d/index_section_tail.html b/ddili/src/ders/d/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d/interface.d b/ddili/src/ders/d/interface.d deleted file mode 100644 index 10f276a..0000000 --- a/ddili/src/ders/d/interface.d +++ /dev/null @@ -1,658 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX interface) $(IX arayüz) Arayüzler) - -$(P -Sınıf sıradüzenlerinde arayüz tanımlamak için $(C class) anahtar sözcüğü yerine $(C interface) kullanılır. $(C interface), bazı olanakları kısıtlanmış soyut sınıf gibidir: -) - -$(UL - -$(LI -Bildirdiği ama tanımını vermediği bütün üye işlevleri soyuttur; $(C abstract) anahtar sözcüğü bile gerekmez. -) - -$(LI -Tanımını da verdiği üye işlevler içeriyorsa o işlevlerin $(C static) veya $(C final) olmaları şarttır. ($(C static) ve $(C final) işlevleri aşağıda açıklayacağım.) -) - -$(LI -Eğer varsa, üye değişkenleri ancak $(C static) olabilirler. -) - -$(LI -Arayüzler ancak başka arayüzlerlerden türeyebilirler. -) - -) - -$(P -Bu kısıtlamalarına rağmen arayüzlerin getirdikleri önemli bir yarar vardır: Her sınıf en fazla bir $(C class)'tan türeyebildiği halde $(C interface)'ten türemenin sınırı yoktur. -) - -$(H5 Tanımlanması) - -$(P -$(C class) yerine $(C interface) yazılarak tanımlanır: -) - ---- -$(HILITE interface) SesliAlet { - // ... -} ---- - -$(P -$(C interface) o arayüzün gerektirdiği işlevleri bildirir ama tanımlarını vermez: -) - ---- -interface SesliAlet { - string ses(); // Yalnızca bildirilir (tanımı verilmez) -} ---- - -$(P -O arayüz ile kullanılabilmeleri için, $(C interface)'ten türeyen sınıfların $(C interface)'in bildirdiği işlevleri tanımlamaları gerekir. -) - -$(P -Arayüz işlevlerinin $(C in) ve $(C out) blokları bulunabilir: -) - ---- -interface I { - int işlev(int i) - $(HILITE in) { - /* Bu işlevi çağıranların uyması gereken en ağır - * koşullar. (Alt arayüzler ve sınıflar bu koşulları - * hafifletebilirler.) */ - - } $(HILITE out) { // ((sonuç) parametresi de bulunabilir) - /* Bu işlevin gerçekleştirmelerinin vermeleri gereken - * garantiler. (Alt arayüzler ve sınıflar ek - * garantiler de verebilirler.) */ - } -} ---- - -$(P -Sözleşmeli programlamanın türemedeki kullanımını daha sonra $(LINK2 /ders/d/invariant.html, Yapı ve Sınıflarda Sözleşmeli Programlama bölümünde) göreceğiz. -) - -$(H5 $(C interface)'ten türetme) - -$(P -Türeme söz dizimi $(C class)'tan farklı değildir: -) - ---- -class Keman : $(HILITE SesliAlet) { - string ses() { - return "♩♪♪"; - } -} - -class Çan : $(HILITE SesliAlet) { - string ses() { - return "çın"; - } -} ---- - -$(P -Üst sınıflarda da olduğu gibi, parametre olarak $(C interface) alan işlevler onları asıl türlerini bilmeden kullanabilirler. Örneğin, işlemleri sırasında bir $(C SesliAlet) kullanan aşağıdaki işlev hangi tür bir sesli alet olduğunu bilmeden onun $(C ses) işlevinden yararlanabilir: -) - ---- -void sesliAletKullan(SesliAlet alet) { - // ... bazı işlemler ... - writeln($(HILITE alet.ses())); - // ... başka işlemler ... -} ---- - -$(P -Sınıflarda da olduğu gibi, o işlev $(C SesliAlet) arayüzünden türeyen her sınıf ile çağrılabilir: -) - ---- - sesliAletKullan(new Keman); - sesliAletKullan(new Çan); ---- - -$(P -Her aletin kendi asıl türünün tanımladığı $(C ses) işlevi çağrılır ve sonuçta sırasıyla $(C Keman.ses) ve $(C Çan.ses) üye işlevlerinin çıktıları görülür: -) - -$(SHELL -♩♪♪ -çın -) - -$(H5 Birden fazla $(C interface)'ten türetme) - -$(P -Bir sınıf ancak tek bir $(C class)'tan türetilebilir. $(C interface)'ten türemede ise böyle bir kısıtlama yoktur. -) - -$(P -Örnek olarak, haberleşme aletlerini temsil eden aşağıdaki arayüzü ele alalım: -) - ---- -interface HaberleşmeAleti { - void konuş(string mesaj); - string dinle(); -} ---- - -$(P -$(C Telefon) sınıfını hem bir sesli alet, hem de bir haberleşme aleti olarak kullanabilmek için onu bu iki arayüzden birden türeterek tanımlayabiliriz: -) - ---- -class Telefon : $(HILITE SesliAlet, HaberleşmeAleti) { - // ... -} ---- - -$(P -O tanım şu iki ilişkiyi birden sağlar: "telefon bir sesli alettir" ve "telefon bir haberleşme aletidir". -) - -$(P -$(C Telefon) sınıfının nesnelerinin oluşturulabilmesi için bu iki arayüzün gerektirdiği bütün işlevleri tanımlamış olması gerekir: -) - ---- -class Telefon : SesliAlet, HaberleşmeAleti { - string ses() { // SesliAlet için - return "zırrr zırrr"; - } - - void konuş(string mesaj) { // HaberleşmeAleti için - // ... mesajı hatta ilet ... - } - - string dinle() { // HaberleşmeAleti için - string hattaDuyulanSes; - // ... sesi hattan oku ... - return hattaDuyulanSes; - } -} ---- - -$(P -Programın gerekleri doğrultusunda sınırsız sayıda $(C interface)'ten türetilebilir. -) - -$(H5 $(C interface)'ten ve $(C class)'tan türetme) - -$(P -Bir sınıf, bir veya daha fazla $(C interface)'ten türetilmenin yanında, bir adet olduğu sürece aynı zamanda bir sınıftan da türetilebilir: -) - ---- -$(HILITE class) Saat { - // ... kendi gerçekleştirmesi ... -} - -class ÇalarSaat : $(HILITE Saat), SesliAlet { - string ses() { - return "bi bi biip"; - } -} ---- - -$(P -$(C ÇalarSaat), $(C Saat)'in bütün üyelerini ve üye işlevlerini türeme yoluyla edinmektedir. Bunun yanında, $(C SesliAlet) arayüzünün gerektirdiği $(C ses) işlevini tanımlamak zorundadır. -) - -$(H5 $(C interface)'ten $(C interface) türetme) - -$(P -Arayüzden türetilen bir arayüz, alt sınıfların tanımlamaları gereken işlevlerin sayısını arttırmış olur: -) - ---- -interface MüzikAleti : SesliAlet { - void akortEt(); -} ---- - -$(P -Yukarıdaki tanıma göre, bir $(C MüzikAleti) olabilmek için hem $(C SesliAlet)'in gerektirdiği $(C ses) işlevinin, hem de $(C MüzikAleti)'nin gerektirdiği $(C akortEt) işlevinin tanımlanması gerekir. -) - -$(P -Örneğin, yukarıdaki $(C Keman) sınıfı doğrudan $(C SesliAlet) arayüzünden türetilmek yerine aradaki $(C MüzikAleti)'nden türetilse, $(C akortEt) işlevini de tanımlaması gerekir: -) - ---- -class Keman : MüzikAleti { - string ses() { // SesliAlet için - return "♩♪♪"; - } - - void akortEt() { // MüzikAleti için - // ... akort işlemleri ... - } -} ---- - -$(H5 $(IX static, üye işlev) $(C static) üye işlevler) - -$(P -$(C static) üye işlevler yapılar, sınıflar, ve arayüzler için tanımlanabilir. Önceki bölümleri gereğinden fazla karmaşıklaştırmamak için bu olanağı bu bölüme bıraktım. -) - -$(P -Hatırlayacağınız gibi, normal üye işlevler her zaman için bir nesne üzerinde çağrılırlar. Üye işlev içinde kullanılan üyeler hep o nesnenin üyeleridir: -) - ---- -struct Yapı { - int i; - - void değiştir(int değer) { - $(HILITE i) = değer; - } -} - -void main() { - auto nesne0 = Yapı(); - auto nesne1 = Yapı(); - - nesne0.değiştir(10); // nesne0.i değişir - nesne1.değiştir(10); // nesne1.i değişir -} ---- - -$(P -Ek olarak, üyeler üye işlev içindeyken "bu nesne" anlamına gelen $(C this) ile de belirtilebilirler: -) - ---- - void değiştir(int değer) { - this.i = değer; // üsttekinin eşdeğeri - } ---- - -$(P -$(C static) üye işlevler ise hiçbir nesne üzerinde işlemezler; üye işlev içindeyken $(C this) anahtar sözcüğünün karşılık geldiği bir nesne yoktur. O yüzden, $(C static) üye işlev içindeyken hiçbir $(I normal üye değişken) geçerli değildir: -) - ---- -struct Yapı { - int i; - - $(HILITE static) void ortakİşlev(int değer) { - i = değer; $(DERLEME_HATASI) - this.i = değer; $(DERLEME_HATASI) - } -} ---- - -$(P -$(C static) işlevler ancak türlerin ortak üyeleri olan $(C static) üyeleri kullanabilirler. -) - -$(P -$(LINK2 /ders/d/yapilar.html, Yapılar bölümünde) gördüğümüz $(C Nokta) türünü $(C static) üye işlevi olacak biçimde tekrar tanımlayalım. Hatırlarsanız, $(C Nokta) türünün her nesnesine farklı bir numara veriliyordu. Numaralar bu sefer $(C static) bir üye işlev tarafından belirleniyor: -) - ---- -import std.stdio; - -struct Nokta { - size_t numara; // Nesnenin kendi numarası - int satır; - int sütun; - - // Bundan sonra oluşturulacak olan nesnenin numarası - $(HILITE static) size_t sonrakiNumara; - - this(int satır, int sütun) { - this.satır = satır; - this.sütun = sütun; - this.numara = yeniNumaraBelirle(); - } - - $(HILITE static) size_t yeniNumaraBelirle() { - immutable yeniNumara = sonrakiNumara; - ++sonrakiNumara; - return yeniNumara; - } -} - -void main() { - auto üstteki = Nokta(7, 0); - auto ortadaki = Nokta(8, 0); - auto alttaki = Nokta(9, 0); - - writeln(üstteki.numara); - writeln(ortadaki.numara); - writeln(alttaki.numara); -} ---- - -$(P -$(C static) $(C yeniNumaraBelirle) işlevi, türün ortak değişkeni olan $(C sonrakiNumara)'yı kullanabilir. Sonuçta her nesnenin farklı bir numarası olur: -) - -$(SHELL -0 -1 -2 -) - -$(P -Yukarıda bir yapı üzerinde gösterdiğim $(C static) üye işlevler sınıflarla ve arayüzlerle de kullanılabilir. -) - -$(H5 $(IX final) $(C final) üye işlevler) - -$(P -$(C final) üye işlevler sınıflar ve arayüzler için tanımlanabilir. (Yapılarda türeme olmadığı için yapılarla ilgili bir olanak değildir.) Önceki bölümleri gereğinden fazla karmaşıklaştırmamak için bu olanağı bu bölüme bıraktım. -) - -$(P -"Son" anlamına gelen $(C final) anahtar sözcüğü bir üye işlevin tanımının daha alttaki sınıflar tarafından değiştirilemeyeceğini bildirir; bir anlamda, algoritmanın son tanımı bu sınıf veya arayüz tarafından verilmektedir. Bir algoritmanın ana hatlarının üst sınıf veya arayüz tarafından belirlendiği ve ayrıntılarının alt sınıflara bırakıldığı durumlarda yararlıdır. -) - -$(P -Bunun örneğini bir $(C Oyun) arayüzünde görelim. Bir oyunun nasıl oynatıldığının ana hatları bu arayüzün $(C oynat) işlevi tarafından belirlenmektedir: -) - ---- -$(CODE_NAME Oyun)interface Oyun { - $(HILITE final) void oynat() { - string isim = oyunİsmi(); - writefln("%s oyunu başlıyor", isim); - - oyuncularıTanı(); - hazırlan(); - başlat(); - sonlandır(); - - writefln("%s oyunu bitti", isim); - } - - string oyunİsmi(); - void oyuncularıTanı(); - void hazırlan(); - void başlat(); - void sonlandır(); -} ---- - -$(P -$(C final) işlevin tanımladığı adımların alt sınıflar tarafından değiştirilmesi mümkün değildir. Alt sınıflar ancak aynı arayüz tarafından şart koşulmuş olan beş işlevi tanımlayabilirler ve böylece algoritmayı tamamlamış olurlar: -) - ---- -$(CODE_XREF Oyun)import std.stdio; -import std.string; -import std.random; -import std.conv; - -class ZarToplamıOyunu : Oyun { - string oyuncu; - size_t adet; - size_t toplam; - - string oyunİsmi() { - return "Zar Toplamı"; - } - - void oyuncularıTanı() { - write("İsminiz nedir? "); - oyuncu = strip(readln()); - } - - void hazırlan() { - write("Kaç kere zar atılsın? "); - readf(" %s", &adet); - toplam = 0; - } - - void başlat() { - foreach (i; 0 .. adet) { - immutable zar = uniform(1, 7); - writefln("%s: %s", i, zar); - toplam += zar; - } - } - - void sonlandır() { - writefln("Oyuncu: %s, Zar toplamı: %s, Ortalama: %s", - oyuncu, toplam, to!double(toplam) / adet); - } -} - -void kullan(Oyun oyun) { - oyun.oynat(); -} - -void main() { - kullan(new ZarToplamıOyunu()); -} ---- - -$(P -Yukarıda bir arayüz üzerinde gösterdiğim $(C final) üye işlevler sınıflarla da kullanılabilir. -) - -$(H5 Nasıl kullanmalı) - -$(P -$(C interface) çok kullanılan bir olanaktır. Hemen hemen bütün sıradüzenlerin en üstünde bir veya daha fazla $(C interface) bulunur. En sık karşılaşılan sıradüzenlerden birisi, tek bir $(C interface)'ten türeyen basit gerçekleştirme sınıflarından oluşan sıradüzendir: -) - -$(MONO - $(I MüzikAleti - (interface)) - / | \ \ - Kemençe Saz Kaval ... -) - -$(P -Çok daha karmaşık sıradüzenlerle de karşılaşılır ama bu basit yapı çoğu programın ihtiyacı için yeterlidir. -) - -$(P -Bazı alt sınıfların ortak gerçekleştirmelerinin bir ara sınıfta tanımlandığı durumlarla da sık karşılaşılır. Alt sınıflar bu ortak sınıftan türerler. Aşağıdaki sıradüzende $(C TelliMüzikAleti) ve $(C NefesliMüzikAleti) sınıfları kendi alt türlerinin ortak üyelerini içeriyor olabilirler: -) - -$(MONO - $(I MüzikAleti - (interface)) - / \ - TelliMüzikAleti NefesliMüzikAleti - / | \ / | \ - Kemençe Saz ... Kaval Ney ... -) - -$(P -O ortak sınıflardan türeyen alt sınıflar da kendi daha özel tanımlarını içerebilirler. -) - -$(H5 $(IX soyutlama) Soyutlama) - -$(P -Arayüzler programların alt bölümlerini birbirlerinden bağımsızlaştırmaya yararlar. Buna $(I soyutlama) denir. Örneğin, müzik aletleri kullanan bir programın büyük bir bölümü yalnızca $(C MüzikAleti) arayüzünden haberi olacak biçimde ve yalnızca onu kullanarak yazılabilir. -) - -$(P -$(C Müzisyen) gibi bir sınıf asıl türünü bilmeden bir $(C MüzikAleti) içerebilir: -) - ---- -class Müzisyen { - MüzikAleti alet; - // ... -} ---- - -$(P -Birden fazla müzik aletini bir araya getiren türler o aletlerin asıl türlerini bilmek zorunda değillerdir: -) - ---- - MüzikAleti[] orkestradakiAletler; ---- - -$(P -Programın çoğu işlevi yalnızca bu arayüzü kullanarak yazılabilir: -) - ---- -bool akortGerekiyor_mu(MüzikAleti alet) { - bool karar; - // ... - return karar; -} - -void güzelÇal(MüzikAleti alet) { - if (akortGerekiyor_mu(alet)) { - alet.akortEt(); - } - - writeln(alet.ses()); -} ---- - -$(P -Bu şekilde bir $(I soyutlama) kullanarak programın bölümlerinin birbirlerinden bağımsız hale getirilmeleri, alt sınıflarda ileride gerekebilecek kod düzenlemelerinin serbestçe yapılabilmesini sağlar. Alt sınıfların gerçekleştirmeleri bu arayüzün $(I arkasında) oldukları için, bu arayüzü kullanan kodlar o değişikliklerden etkilenmemiş olurlar. -) - -$(H5 Örnek) - -$(P -$(C SesliAlet), $(C MüzikAleti), ve $(C HaberleşmeAleti) arayüzlerini içeren bir program şöyle yazılabilir: -) - ---- -import std.stdio; - -/* Bu arayüz 'ses' işlevini gerektirir. */ -interface SesliAlet { - string ses(); -} - -/* Bu sınıfın yalnızca 'ses' işlevini tanımlaması gerekir. */ -class Çan : SesliAlet { - string ses() { - return "çın"; - } -} - -/* Bu arayüz 'ses' işlevine ek olarak 'akortEt' işlevini de - * gerektirir. */ -interface MüzikAleti : SesliAlet { - void akortEt(); -} - -/* Bu sınıfın 'ses' ve 'akortEt' işlevlerini tanımlaması - * gerekir. */ -class Keman : MüzikAleti { - string ses() { - return "♩♪♪"; - } - - void akortEt() { - // ... kemanın akort işlemleri ... - } -} - -/* Bu arayüz 'konuş' ve 'dinle' işlevlerini gerektirir. */ -interface HaberleşmeAleti { - void konuş(string mesaj); - string dinle(); -} - -/* Bu sınıfın 'ses', 'konuş', ve 'dinle' işlevlerini - * tanımlaması gerekir. */ -class Telefon : SesliAlet, HaberleşmeAleti { - string ses() { - return "zırrr zırrr"; - } - - void konuş(string mesaj) { - // ... mesajın hatta iletilmesi ... - } - - string dinle() { - string hattaDuyulanSes; - // ... sesin hattan okunması ... - return hattaDuyulanSes; - } -} - -class Saat { - // ... Saat'in gerçekleştirilmesi ... -} - -/* Bu sınıfın yalnızca 'ses' işlevini tanımlaması gerekir. */ -class ÇalarSaat : Saat, SesliAlet { - string ses() { - return "bi bi biip"; - } - - // ... ÇalarSaat'in gerçekleştirilmesi ... -} - -void main() { - SesliAlet[] aletler; - - aletler ~= new Çan; - aletler ~= new Keman; - aletler ~= new Telefon; - aletler ~= new ÇalarSaat; - - foreach (alet; aletler) { - writeln(alet.ses()); - } -} ---- - -$(P -$(C main)'in içindeki $(C aletler) bir $(C SesliAlet) dizisi olduğu için, o diziye $(C SesliAlet)'ten türeyen her tür eklenebiliyor. Sonuçta programın çıktısı bütün aletlerin ürettikleri sesleri içerir: -) - -$(SHELL -çın -♩♪♪ -zırrr zırrr -bi bi biip -) - -$(H5 Özet) - -$(UL - -$(LI $(C interface) bir arayüz tanımlar; bütün işlevleri soyut olan bir sınıf gibidir. Gerçekleştirme olarak yalnızca $(C static) üye değişkenleri ve $(C static) ve $(C final) üye işlevleri olabilir.) - -$(LI Bir sınıfın nesnelerinin oluşturulabilmesi için, türetildiği bütün arayüzlerin bütün işlevlerinin tanımlanmış olmaları gerekir.) - -$(LI Tek $(C class)'tan türetebilme kısıtlaması $(C interface)'lerde yoktur; sınıflar ve arayüzler sınırsız sayıda $(C interface)'ten türetilebilirler.) - -$(LI Sık karşılaşılan bir sıradüzen, üstte bir arayüz ($(C interface)) ve alttaki gerçekleştirmeleridir ($(C class)).) -) - -Macros: - SUBTITLE=Arayüzler - - DESCRIPTION=D dilinin arayüz türeme olanağı olan 'interface' anahtar sözcüğü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar kullanıcı türleri türeme arayüz türemesi interface - -SOZLER= -$(alt_sinif) -$(arayuz) -$(bildirim) -$(gerceklestirme) -$(siraduzen) -$(soyut) -$(ust_sinif) diff --git a/ddili/src/ders/d/invariant.d b/ddili/src/ders/d/invariant.d deleted file mode 100644 index 3b62995..0000000 --- a/ddili/src/ders/d/invariant.d +++ /dev/null @@ -1,465 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX sözleşmeli programlama) Yapı ve Sınıflarda Sözleşmeli Programlama) - -$(P -Sözleşmeli programlama, kod hatalarını azaltmaya yarayan çok etkili bir olanaktır. D'nin sözleşmeli programlama olanaklarından ikisini $(LINK2 /ders/d/sozlesmeli.html, Sözleşmeli Programlama bölümünde) görmüştük. $(C in) ve $(C out) blokları, işlevlerin giriş ve çıkış garantilerini denetlemek için kullanılıyordu. -) - -$(P -$(I Not: -O bölümdeki "in bloğu mu enforce mu" başlığı altındaki ilkeleri gözetmeniz önemlidir. Bu bölümdeki örnekler nesnelerin ve parametrelerin tutarlılıkları ile ilgili sorunların programcı hatalarına bağlı olduğu durumlarla ilgilidir. Diğer durumlarda işlevin kodlarından $(C enforce)'u çağırmanız doğru olur.) -) - -$(P -Hatırlamak amacıyla, üçgen alanını Heron formülünü kullanarak kenar uzunluklarından hesaplayan bir işlev yazalım. Üçgenin alanının doğru olarak hesaplanabilmesi için üç kenar uzunluğunun da sıfır veya daha büyük olması gerekir. Ek olarak, bir üçgenin hiçbir kenarının diğer ikisinin toplamından uzun olmaması da gerekir. -) - -$(P -O giriş koşulları sağlandığında, üçgenin alanı da sıfır veya daha büyük olacaktır. Bu koşulları ve bu garantiyi sağlayan bir işlev şöyle yazılabilir: -) - ---- -private import std.math; - -double üçgenAlanı(in double a, in double b, in double c) -in { - // Kenarlar sıfırdan küçük olamaz - assert(a >= 0); - assert(b >= 0); - assert(c >= 0); - - // Hiçbir kenar diğer ikisinin toplamından uzun olamaz - assert(a <= (b + c)); - assert(b <= (a + c)); - assert(c <= (a + b)); - -} out (sonuç) { - assert(sonuç >= 0); - -} body { - immutable yarıÇevre = (a + b + c) / 2; - - return sqrt(yarıÇevre - * (yarıÇevre - a) - * (yarıÇevre - b) - * (yarıÇevre - c)); -} ---- - -$(H5 $(IX in, sözleşme) $(IX out, sözleşme) $(IX giriş koşulu) $(IX çıkış garantisi) Üye işlevlerin $(C in) ve $(C out) blokları) - -$(P -$(C in) ve $(C out) blokları üye işlevlerle de kullanılabilir ve aynı biçimde işlevin giriş koşullarını ve çıkış garantisini denetler. -) - -$(P -Yukarıdaki alan hesabı işlevini bir üye işlev olarak yazalım: -) - ---- -import std.stdio; -import std.math; - -struct Üçgen { -private: - - double a; - double b; - double c; - -public: - - double alan() const @property - $(HILITE out (sonuç)) { - assert(sonuç >= 0); - - } body { - const double yarıÇevre = (a + b + c) / 2; - return sqrt(yarıÇevre - * (yarıÇevre - a) - * (yarıÇevre - b) - * (yarıÇevre - c)); - } -} - -void main() { - auto üçDörtBeşÜçgeni = Üçgen(3, 4, 5); - writeln(üçDörtBeşÜçgeni.alan); -} ---- - -$(P -Üçgenin kenarları zaten yapının üye değişkenleri oldukları için bu işlevin parametreleri yok. O yüzden bu işlevin $(C in) bloğunu yazmadım. Üye değişkenlerin tutarlılıkları için aşağıdaki bilgileri kullanmanız gerekir. -) - -$(H5 Nesnelerin geçerliliği için $(C in) ve $(C out) blokları) - -$(P -Yukarıdaki üye işlev parametre almadığı için $(C in) bloğunu yazmadık. İşlevdeki hesabı da nesnenin üyelerini kullanarak yaptık. Yani bir anlamda üyelerin geçerli değerlere sahip olduklarını varsaydık. Bu varsayımın doğru olmasını sağlamanın bir yolu, sınıfın kurucu işlevine $(C in) bloğu eklemektir. Böylece kurucunun aldığı parametrelerin geçerli olduklarını en baştan nesne kurulurken denetleyebiliriz: -) - ---- -struct Üçgen { -// ... - - this(in double a, in double b, in double c) - $(HILITE in) { - // Kenarlar sıfırdan küçük olamaz - assert(a >= 0); - assert(b >= 0); - assert(c >= 0); - - // Hiçbir kenar diğer ikisinin toplamından uzun olamaz - assert(a <= (b + c)); - assert(b <= (a + c)); - assert(c <= (a + b)); - - } body { - this.a = a; - this.b = b; - this.c = c; - } - -// ... -} ---- - -$(P -Üçgen nesnelerinin geçersiz değerlerle oluşturulmaları en başından engellenmiş olur. Artık programın geçersiz değerlerle kurulmuş olan bir üçgen nesnesi kullanması olanaksızdır: -) - ---- - auto eksiKenarUzunluklu = Üçgen(-1, 1, 1); - auto birKenarıFazlaUzun = Üçgen(1, 1, 10); ---- - -$(P -Kurucu işlevin $(C in) bloğu, yukarıdaki geçersiz nesnelerin oluşturulmalarına izin vermez: -) - -$(SHELL -core.exception.AssertError@deneme.d: Assertion failure -) - -$(P -Bu sefer de $(C out) bloğunu yazmadığıma dikkat edin. Eğer gerekirse, daha karmaşık türlerde kurucu işlevin $(C out) bloğu da yazılabilir. O da nesnenin üyeleri kurulduktan sonra gerekebilecek denetimler için kullanılabilir. -) - -$(H5 $(IX invariant) Nesnelerin tutarlılığı için $(C invariant) blokları) - -$(P -Kurucuya eklenen $(C in) ve $(C out) blokları nesnenin yaşamının geçerli değerlerle başlayacağını, üyelere eklenen $(C in) ve $(C out) blokları da işlevlerin doğru işlediklerini garanti eder. -) - -$(P -Ancak, bu denetimler nesnenin üyelerinin $(I her zaman için) geçerli veya tutarlı olacaklarını garanti etmeye elverişli değillerdir. Nesnenin üyeleri, üye işlevler içinde programcı hataları sonucunda tutarsız değerler edinebilirler. -) - -$(P -Nesnenin tutarlılığını tarif eden koşullara "mutlak değişmez" denir. Örneğin, bir müşteri takip sınıfında her siparişe karşılık bir fatura bulunacağını varsayarsak, fatura adedinin sipariş adedinden fazla olamayacağı bu sınıfın bir mutlak değişmezidir. Eğer bu koşulun geçerli olmadığı bir müşteri takip nesnesi varsa, o nesnenin tutarlı durumda olduğunu söyleyemeyiz. -) - -$(P -Bunun bir örneği olarak $(LINK2 /ders/d/sarma.html, Sarma ve Erişim Hakları bölümünde) kullandığımız $(C Okul) sınıfını ele alalım: -) - ---- -class Okul { -private: - - Öğrenci[] öğrenciler; - int kızToplamı; - int erkekToplamı; - -// ... -} ---- - -$(P -Bu sınıftan olan nesnelerin tutarlı olarak kabul edilmeleri için, üç üyesi arasındaki bir mutlak değişmezin sağlanması gerekir. Öğrenci dizisinin uzunluğu, her zaman için kız öğrencilerin toplamı ile erkek öğrencilerin toplamına eşit olmalıdır: -) - ---- - assert(öğrenciler.length == (kızToplamı + erkekToplamı)); ---- - -$(P -O koşulun bozulmuş olması, bu sınıf kodlarında yapılan bir hatanın göstergesidir. -) - -$(P -Yapı ve sınıf nesnelerinin tutarlılıkları o türün $(C invariant) bloklarında denetlenir. Bir veya daha fazla olabilen bu bloklar yapı veya sınıf tanımı içine yazılırlar ve sınıf nesnelerinin tutarlılık koşullarını belirlerler. $(C in) ve $(C out) bloklarında olduğu gibi, burada da $(C assert) denetimleri kullanılır: -) - ---- -class Okul { -private: - - Öğrenci[] öğrenciler; - int kızToplamı; - int erkekToplamı; - - $(HILITE invariant()) { - assert(öğrenciler.length == (kızToplamı + erkekToplamı)); - } - -// ... -} ---- - -$(P -$(C invariant) bloklarındaki kodlar aşağıdaki zamanlarda otomatik olarak işletilir, ve bu sayede programın yanlış verilerle devam etmesi önlenmiş olur: -) - -$(UL - -$(LI Kurucu işlev sonunda: Böylece nesnenin yaşamına tutarlı olarak başladığı garanti edilir.) - -$(LI Sonlandırıcı işlev çağrılmadan önce: Böylece sonlandırma işlemlerinin tutarlı üyeler üzerinde yapılacakları garanti edilir.) - -$(LI $(C public) bir işlev işletilmeden önce ve sonra: Böylece üye işlevlerdeki kodların nesneyi bozmadıkları garanti edilir. - -$(P -$(IX export) $(I Not: Burada $(C public) işlevler için söylenen, $(C export) işlevler için de geçerlidir. ($(C export) işlevleri kısaca "dinamik kütüphalerin sundukları işlevler" olarak tanımlayabiliriz.)) -) - -) - -) - -$(P -$(C invariant) bloklarındaki denetimlerin başarısız olmaları da $(C in) ve $(C out) bloklarında olduğu gibi $(C AssertError) atılmasına neden olur. Bu sayede programın tutarsız nesnelerle devam etmesi önlenmiş olur. -) - -$(P -$(IX -release, derleyici seçeneği) $(C in) ve $(C out) bloklarında olduğu gibi, $(C invariant) blokları da $(C -release) seçeneği ile iptal edilebilir: -) - -$(SHELL -dmd deneme.d -w -release -) - -$(H5 $(IX sözleşmeli programlama, kalıtım) $(IX kalıtım, sözleşmeli programlama) $(IX türeme, sözleşmeli programlama) $(IX giriş koşulu, türeme) $(IX çıkış garantisi, türeme) $(IX in, türeme) $(IX out, türeme) Sözleşmeli programlama ve türeme) - -$(P -Arayüz ve sınıf üye işlevlerinin $(C in) ve $(C out) blokları olabilir. Böylece hem alt sınıflarının güvenebilecekleri giriş koşulları hem de kullanıcılarının güvenebilecekleri çıkış garantileri tanımlamış olurlar. Üye işlevlerin alt sınıflardaki tanımları da $(C in) ve $(C out) blokları içerebilirler. Alt sınıflardaki $(C in) blokları giriş koşullarını hafifletebilirler ve $(C out) blokları da ek çıkış garantileri verebilirler. -) - -$(P -Normalde bir arayüzle etkileşecek biçimde $(I soyutlanmış) olarak yazıldığından kullanıcı kodunun çoğu durumda alt sınıflardan haberi yoktur. Kullanıcı kodu bir arayüzün sözleşmesine uygun olarak yazıldığından, bir alt sınıfın bu sözleşmenin giriş koşullarını ağırlaştırması da doğru olmaz. O yüzden alt sınıflar giriş koşullarını ancak hafifletebilirler. -) - -$(P -$(C in) blokları üst sınıftan alt sınıfa doğru otomatik olarak işletilir. $(I Herhangi bir) $(C in) bloğunun başarılı olması (bütün $(C assert)'lerin doğru çıkması), giriş koşullarının sağlanmış olduğu anlamına gelir ve işlev çağrısı başarıyla devam eder. (Aşağıda anlatılacağı gibi, bunun bir etkisi, giriş koşullarının istenmeden etkisizleştirilebilmeleridir.) -) - -$(P -Benzer biçimde, alt sınıflar $(C out) blokları da tanımlayabilirler. Çıkış garantileri bir işlevin verdiği garantileri tanımladığından alt sınıf üye işlevi üst sınıfın garantilerini de sağlamak zorundadır. Alt sınıf ek garantiler de getirebilir. -) - -$(P -$(C out) blokları üst sınıftan alt sınıfa doğru otomatik olarak işletilir. Bir işlevin çıkış garantilerinin sağlanmış olması için $(I bütün) $(C out) bloklarının başarıyla işletilmeleri gerekir. -) - -$(P -Bu kuralları gösteren aşağıdaki yapay program bir $(C interface) ve ondan türeyen bir $(C class) tanımlamaktadır. Buradaki alt sınıf hem daha az koşul gerektirmekte hem de daha fazla garanti vermektedir: -) - ---- -interface Arayüz { - int[] işlev(int[] a, int[] b) - $(HILITE in) { - writeln("Arayüz.işlev.in"); - - /* Bu arayüz işlevi parametrelerinin aynı uzunlukta - * olmalarını gerektirmektedir. */ - assert(a.length == b.length); - - } $(HILITE out) (sonuç) { - writeln("Arayüz.işlev.out"); - - /* Bu arayüz işlevi dönüş değerinin çift sayıda - * elemandan oluşacağını garanti etmektedir. - * (Not: Boş dilimin çift sayıda elemanı olduğu kabul - * edilir.) */ - assert((sonuç.length % 2) == 0); - } -} - -class Sınıf : Arayüz { - int[] işlev(int[] a, int[] b) - $(HILITE in) { - writeln("Sınıf.işlev.in"); - - /* Bu sınıf işlevi üst türdeki giriş koşullarını - * hafifletmektedir: Birisi boş olmak kaydıyla - * parametrelerin uzunluklarının eşit olmaları - * gerekmemektedir. */ - assert((a.length == b.length) || - (a.length == 0) || - (b.length == 0)); - - } $(HILITE out) (sonuç) { - writeln("Sınıf.işlev.out"); - - /* Bu sınıf ek garantiler vermektedir: Sonuç boş - * olmayacaktır ve ilk ve sonuncu elemanların - * değerleri eşit olacaktır. */ - assert((sonuç.length != 0) && - (sonuç[0] == sonuç[$ - 1])); - - } body { - writeln("Sınıf.işlev.body"); - - /* Bu yalnızca 'in' ve 'out' bloklarının işleyişini - * gösteren yapay bir gerçekleştirme. */ - - int[] sonuç; - - if (a.length == 0) { - a = b; - } - - if (b.length == 0) { - b = a; - } - - foreach (i; 0 .. a.length) { - sonuç ~= a[i]; - sonuç ~= b[i]; - } - - sonuç[0] = sonuç[$ - 1] = 42; - - return sonuç; - } -} - -import std.stdio; - -void main() { - auto c = new Sınıf(); - - /* Aşağıdaki çağrı Arayüz'ün gerektirdiği koşulu - * sağlamadığı halde kabul edilir çünkü Sınıf'ın giriş - * koşulunu sağlamaktadır. */ - writeln(c.işlev([1, 2, 3], $(HILITE []))); -} ---- - -$(P -$(C Sınıf.işlev)'in $(C in) bloğu $(C Arayüz.işlev)'in giriş koşulu sağlanmadığı için işletilmiştir: -) - -$(SHELL -Arayüz.işlev.in -Sınıf.işlev.in $(SHELL_NOTE Arayüz.işlev.in başarılı olsa bu işletilmezdi) -Sınıf.işlev.body -Arayüz.işlev.out -Sınıf.işlev.out -[42, 1, 2, 2, 3, 42] -) - -$(H6 Giriş koşullarının istenmeden etkisizleştirilmeleri) - -$(P -$(C in) bloğu olmayan bir işlev hiçbir giriş koşulu gerektirmiyor demektir. Bunun bir etkisi olarak, üst sınıfta giriş koşulu bulunan alt sınıf işlevleri kendileri giriş koşulu tanımlamazlarsa üst sınıftaki giriş koşullarını etkisiz hale getirmiş olurlar. (Yukarıda anlatıldığı gibi, üye işlevin tanımlarından birisinin $(C in) bloğunun başarılı olması giriş koşullarının sağlanmış olduğu anlamına gelir.) -) - -$(P -Bu yüzden, genel bir kural olarak, üst sınıfta $(C in) bloğu bulunan bir üye işlevin alt sınıfta da $(C in) bloğu bulunmalıdır. Örneğin, alt sınıf işlevine her zaman için başarısız olan bir $(C in) bloğu eklenebilir. -) - -$(P -Bunu görmek için üst sınıfının giriş koşulunu etkisiz hale getiren bir alt sınıfa bakalım: -) - ---- -class Protokol { -// ... - - void hesapla(double d) - in { - assert($(HILITE d > 42)); - - } body { - // ... - } -} - -class ÖzelProtokol : Protokol { - /* 'in' bloğu bulunmadığından, bu işlev - * 'Protokol.hesapla'nın giriş koşulunu belki de - * istemeden etkisizleştirir. */ - override void hesapla(double d) { - // ... - } -} - -void main() { - auto ö = new ÖzelProtokol(); - ö.hesapla($(HILITE 10)); /* HATA: Parametre değeri olan 10 üst - * sınıfın giriş koşulunu sağlamadığı - * halde bu çağrı başarılı olur. */ -} ---- - -$(P -Bir çözüm, $(C ÖzelProtokol.hesapla)'ya her zaman için başarısız olan bir giriş koşulu eklemektir: -) - ---- -class ÖzelProtokol : Protokol { - override void hesapla(double d) - in { - $(HILITE assert(false)); - - } body { - // ... - } -} ---- - -$(P -Bu sefer üst sınıfın giriş koşulu etkili olacak ve yanlış parametre değeri yakalanacaktır: -) - -$(SHELL -core.exception.AssertError@deneme.d: Assertion failure -) - -$(H5 Özet) - -$(UL - -$(LI -$(C in) ve $(C out) bloklarını üye işlevlerle de kullanabilirsiniz; kurucu işleve ekleyerek nesnelerin geçersiz parametrelerle kurulmalarını önleyebilirsiniz. -) - -$(LI -Nesnelerin yaşamları boyunca her zaman için tutarlı olmalarını garantilemek için $(C invariant) bloklarını kullanabilirsiniz. -) - -$(LI -Alt türlerin üye işlevlerinin de $(C in) blokları olabilir. Alt sınıfların giriş koşulları üst sınıftakilerden daha ağır olmamalıdır. ($(I $(C in) bloğunun olmaması "hiç giriş koşulu gerektirmemek" anlamına gelir.)) -) - -$(LI -Alt türlerin üye işlevlerinin de $(C out) blokları olabilir. Alt sınıf işlevleri kendi garantilerinden başka üst sınıfların garantilerini de sağlamak zorundadırlar. -) - -) - -Macros: - SUBTITLE=Yapı ve Sınıflarda Sözleşmeli Programlama - - DESCRIPTION=D yapılarının ve sınıflarının sözleşmeli programlama olanaklarından olan ve nesnelerin tutarlılıklarını denetleyen invariant anahtar sözcüğü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar yapı struct kullanıcı türleri sözleşmeli programlama design by contract in out invariant mutlak değişmez - -SOZLER= -$(acikca_elle_yapilan) -$(mutlak_degismez) -$(otomatik) -$(sozlesmeli_programlama) diff --git a/ddili/src/ders/d/is_ifadesi.d b/ddili/src/ders/d/is_ifadesi.d deleted file mode 100644 index a44e7f1..0000000 --- a/ddili/src/ders/d/is_ifadesi.d +++ /dev/null @@ -1,561 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX is, ifade) $(CH4 is) İfadesi) - -$(P -Bu ifade, daha önce $(LINK2 /ders/d/null_ve_is.html, $(CH4 null) değeri ve $(CH4 is) işleci bölümünde) gördüğümüz $(C is) işlecinden anlam ve yazım açısından farklıdır: -) - ---- - a is b // daha önce gördüğümüz is işleci - is (/* ... */) // is ifadesi ---- - -$(P -Bu bölümün konusu olan $(C is) ifadesi derleme zamanında işletilir ve parantez içindeki ifadeye bağlı olan bir değer üretir. Ürettiği değerin türü $(C int)'tir; koşul geçerli olduğunda 1, geçersiz olduğunda 0 değerini alır. -) - -$(P -$(C is)'in aldığı koşul bir mantıksal ifade değildir ama $(C is)'in kendi değeri bir mantıksal ifadede kullanılmaya elverişlidir. Örneğin $(C if) deyimiyle, ve derleme zamanında işletildiği için daha da uygun olarak $(C static if) deyimiyle kullanılabilir. -) - -$(P -Aldığı koşul türlerle ilgilidir ve bir kaç özel biçimden birisi olarak yazılmak zorundadır. En çok şablon parametrelerini denetlemede ve şablon parametre türleriyle ilgili bilgi toplamada yararlıdır. -) - -$(H5 $(C is ($(I Tür)))) - -$(P -$(C Tür)'ün $(I anlamsal) olarak geçerli bir tür olup olmadığını denetler. -) - -$(P -$(C is)'in bu kullanımı için bu noktada tek başına örnekler bulmak oldukça zor. Bunun yararını daha sonraki bölümlerde şablon parametreleri ile kullanırken göreceğiz. -) - ---- - static if (is (int)) { - writeln("geçerli"); - - } else { - writeln("geçersiz"); - } ---- - -$(P -Yukarıdaki koşulda kullanılan $(C int), geçerli bir türdür: -) - -$(SHELL_SMALL -geçerli -) - -$(P -Başka bir örnek olarak, eşleme tablosu indeks türü olarak $(C void) kullanmak geçersiz olduğu için bu örnekte $(C else) bloğu işletilir: -) - ---- - static if (is (string[void])) { - writeln("geçerli"); - - } else { - writeln("geçersiz"); - } ---- - -$(SHELL_SMALL -geçersiz -) - - -$(H5 $(C is ($(I Tür Takmaİsim)))) - -$(P -Yukarıdaki ile aynı şekilde çalışır. Ek olarak, koşul geçerli olduğunda $(C Takmaİsim)'i türün yeni takma ismi olarak tanımlar: -) - ---- - static if (is (int Yeniİsim)) { - writeln("geçerli"); - Yeniİsim değişken = 42; // int ve Yeniİsim aynı anlamda - - } else { - writeln("geçersiz"); - } ---- - -$(P -Takma ismin bu şekilde $(C is) ifadesinin içinde tanımlanabilmesi, daha sonra göreceğimiz karmaşık $(C is) ifadelerinde yararlıdır. -) - -$(H5 $(C is ($(I Tür) : $(I ÖzelTür)))) - -$(P -$(C Tür)'ün belirtilen özel türe otomatik olarak dönüşüp dönüşemediğini denetler. -) - -$(P -$(LINK2 /ders/d/tur_donusumleri.html, Tür Dönüşümleri bölümünde) gördüğümüz temel tür dönüşümlerini, veya $(LINK2 /ders/d/tureme.html, Türeme bölümünde) gördüğümüz "bu alt sınıf, o üst sınıfın türündendir" ilişkilerini denetlemede kullanılır. -) - ---- -import std.stdio; - -interface Saat { - void zamanıOku(); -} - -class ÇalarSaat : Saat { - override void zamanıOku() { - writeln("10:00"); - } -} - -void birİşlev(T)(T nesne) { - static if ($(HILITE is (T : Saat))) { - // Eğer buraya geldiysek, şablon parametresi olan T - // Saat yerine kullanılabilen bir türdür - writeln("bu bir Saat; zamanı söyleyebiliriz"); - nesne.zamanıOku(); - - } else { - writeln("bu bir Saat değil"); - } -} - -void main() { - auto değişken = new ÇalarSaat; - birİşlev(değişken); - birİşlev(42); -} ---- - -$(P -O kod, $(C birİşlev) şablonu $(C Saat)'e dönüşebilen bir tür ile çağrıldığında $(C nesne)'nin $(C zamanıOku) işlevini de çağırmaktadır. Tür $(C int) olduğunda ise $(C else) bloğu derlenmektedir: -) - -$(SHELL_SMALL -bu bir Saat; zamanı söyleyebiliriz $(SHELL_NOTE ÇalarSaat için) -10:00 $(SHELL_NOTE ÇalarSaat için) -bu bir Saat değil $(SHELL_NOTE int için) -) - -$(H5 $(C is ($(I Tür Takmaİsim) : $(I ÖzelTür)))) - -$(P -Yukarıdakiyle aynı şekilde çalışır. Ek olarak, koşul geçerli olduğunda $(C Takmaİsim)'i koşulu sağlayan türün yeni takma ismi olarak tanımlar. -) - -$(H5 $(C is ($(I Tür) == $(I ÖzelTür)))) - -$(P -$(C Tür)'ün belirtilen özel türün $(I aynısı) olup olmadığını, veya $(I aynı belirtece sahip) olup olmadığını denetler. -) - -$(H6 $(I Aynı tür) anlamında) - -$(P -Yukarıdaki örnek kodu değiştirsek ve $(C :) yerine $(C ==) kullansak, bu sefer $(C ÇalarSaat) için de geçersiz olacaktır: -) - ---- - static if (is (T $(HILITE ==) Saat)) { - writeln("bu bir Saat; zamanı söyleyebiliriz"); - nesne.zamanıOku(); - - } else { - writeln("bu bir Saat değil"); - } ---- - -$(P -$(C ÇalarSaat) $(C Saat)'ten türediği için bir $(C Saat)'tir, ama $(C Saat)'in aynısı değildir. O yüzden koşul hem $(C ÇalarSaat) için, hem de $(C int) için geçersizdir: -) - -$(SHELL_SMALL -bu bir Saat değil -bu bir Saat değil -) - -$(H6 $(I Aynı belirtece sahip) anlamında) - -$(P -$(C ÖzelTür) yerine bir belirteç kullanıldığında türün o belirtece uyup uymadığını denetler. Bu kullanımda belirteç olarak aşağıdaki anahtar sözcükler kullanılabilir (bu sözcüklerden bazılarını daha sonraki bölümlerde göreceğiz): -) - -$(UL -$(LI $(IX struct, is ifadesi) $(C struct)) -$(LI $(IX union, is ifadesi) $(C union)) -$(LI $(IX class, is ifadesi) $(C class)) -$(LI $(IX interface, is ifadesi) $(C interface)) -$(LI $(IX enum, is ifadesi) $(C enum)) -$(LI $(IX function, is ifadesi) $(C function)) -$(LI $(IX delegate, is ifadesi) $(C delegate)) -$(LI $(IX const, is ifadesi) $(C const)) -$(LI $(IX immutable, is ifadesi) $(C immutable)) -$(LI $(IX shared, is ifadesi) $(C shared)) -) - ---- -void birİşlev(T)(T nesne) { - static if (is (T == class)) { - writeln("bu bir sınıf türü"); - - } else static if (is (T == enum)) { - writeln("bu bir enum"); - - } else static if (is (T == const)) { - writeln("bu 'const' bir tür"); - - } else { - writeln("bu başka bir tür"); - } -} ---- - -$(P -İşlev şablonları çağrıldıkları türe göre değişik davranacak şekilde kodlanabilirler. Koşulun değişik bloklarının etkinleştiğini göstermek için şöyle deneyebiliriz: -) - ---- - auto değişken = new ÇalarSaat; - birİşlev(değişken); - - // (enum HaftaGünleri biraz aşağıda tanımlanıyor) - birİşlev(HaftaGünleri.Pazartesi); - - const double sayı = 1.2; - birİşlev(sayı); - - birİşlev(42); ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -bu bir sınıf türü -bu bir enum -bu 'const' bir tür -bu başka bir tür -) - -$(H5 $(C is ($(I Tür isim) == $(I Belirteç)))) - -$(P -$(IX super, is ifadesi) -$(IX return, is ifadesi) -$(IX __parameters, is ifadesi) -Yukarıdaki ile aynı şekilde çalışır. Ek olarak, koşul geçerli olduğunda $(C isim)'i duruma göre farklı anlamlarda tanımlar. $(C isim), yukarıdaki takma isimli kullanımlardaki gibi doğrudan türün takma ismi olabileceği gibi, belirtece bağlı olarak başka bir bilgi de olabilir: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $(C Belirteç)$(C isim)'in anlamı
    $(C struct)koşulu sağlayan tür
    $(C union)koşulu sağlayan tür
    $(C class)koşulu sağlayan tür
    $(C interface)koşulu sağlayan tür
    $(C super)üst tür ve arayüzlerden oluşan $(I çokuzlu)
    $(C enum)$(C enum)'un gerçekleştirildiği $(I temel tür)
    $(C function)işlev parametrelerinden oluşan $(I çokuzlu)
    $(C delegate)$(C delegate)'in $(I türü)
    $(C return)işlevin, $(C delegate)'in, veya işlev göstergesinin dönüş $(I türü)
    $(C __parameters)işlevin, $(C delegate)'in, veya işlev göstergesinin parametrelerinden oluşan $(I çokuzlu)
    $(C const)koşulu sağlayan tür
    $(C immutable)koşulu sağlayan tür
    $(C shared)koşulu sağlayan tür
    - -$(P -Bu olanağın nasıl çalıştığını göstermek için önce bazı türler tanımlayalım: -) - ---- -struct Nokta { - // ... -} - -interface Saat { - // ... -} - -class ÇalarSaat : Saat { - // ... -} - -enum HaftaGünleri { - Pazartesi, Salı, Çarşamba, Perşembe, Cuma, - Cumartesi, Pazar -} - -char foo(double kesirli, int tamsayı, Saat saat) { - return 'a'; -} ---- - -$(P -$(C is) ifadesinin bu değişik türlerle kullanımlarını göstermek için aşağıdaki gibi bir işlev şablonu yazılabilir. İşlevin çağrıldığı türlerin, nesnelerin, ve $(C isim)'in ne anlamlara geldiklerini açıklama satırları olarak yazdım: -) - ---- -void birİşlev(T)(T nesne) { - static if (is (T YerelTür == struct)) { - writefln("\n--- struct ---"); - // T ve YerelTür aynı anlamdadır; 'nesne', bu işleve - // gelen yapı nesnesidir - - writeln("Yeni bir ", YerelTür.stringof, - " nesnesini kopyalayarak oluşturuyorum"); - YerelTür yeniNesne = nesne; - } - - static if (is (T üstTürler == super)) { - writeln("\n--- super ---"); - // 'üstTürler' çokuzlusu bütün üst türleri içerir; - // 'nesne', bu işleve gelen sınıf nesnesidir - - writeln(T.stringof, " sınıfının ", üstTürler.length, - " adet üst türü var"); - - writeln("hepsi birden: ", üstTürler.stringof); - writeln("en üstteki: ", üstTürler[0].stringof); - } - - static if (is (T AsılTür == enum)) { - writeln("\n--- enum ---"); - // 'AsılTür', enum değerlerini gerçekleştirmek için - // kullanılan asıl türdür; 'nesne', bu işleve gelen - // enum değeridir - - writeln(T.stringof, " enum türü, perde arkasında ", - AsılTür.stringof, - " olarak gerçekleştirilmiştir"); - } - - static if (is (T DönüşTürü == return)) { - writeln("\n--- return ---"); - // 'DönüşTürü', işlevin dönüş türüdür; bu işleve - // parametre olarak gelen 'nesne', bir işlev - // göstergesidir - - writeln("Bu, dönüş türü ", DönüşTürü.stringof, - " olan bir işlev:"); - writeln(" ", T.stringof); - write("çağırıyoruz... "); - - // Not: İşlev göstergeleri işlev gibi çağrılabilirler - DönüşTürü sonuç = nesne(1.5, 42, new ÇalarSaat); - writeln("ve sonuç: ", sonuç); - } -} ---- - -$(P -O işlevi yukarıdaki farklı türlerle şöyle çağırabiliriz: -) - ---- - // Yapı nesnesiyle - birİşlev(Nokta()); - - // Sınıf nesnesiyle - birİşlev(new ÇalarSaat); - - // enum değerle - birİşlev(HaftaGünleri.Pazartesi); - - // İşlev göstergesiyle - birİşlev(&foo); ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL ---- struct --- -Yeni bir Nokta nesnesini kopyalayarak oluşturuyorum - ---- super --- -ÇalarSaat sınıfının 2 adet üst türü var -hepsi birden: (in Object, in Saat) -en üstteki: Object - ---- enum --- -HaftaGünleri enum türü, perde arkasında int olarak -gerçekleştirilmiştir - ---- return --- -Bu, dönüş türü char olan bir işlev: - char function(double kesirli, int tamsayı, Saat saat) -çağırıyoruz... ve sonuç: a -) - -$(H5 $(C is (/* ... */ $(I Belirteç), $(I ŞablonParametreListesi)))) - -$(P -Şablon parametre listesi içeren $(C is) ifadesinin dört farklı kullanımı vardır: -) - -$(UL - -$(LI $(C is ($(I Tür) : $(I Belirteç), $(I ŞablonParametreListesi)))) - -$(LI $(C is ($(I Tür) == $(I Belirteç), $(I ŞablonParametreListesi)))) - -$(LI $(C is ($(I Tür isim) : $(I Belirteç), $(I ŞablonParametreListesi)))) - -$(LI $(C is ($(I Tür isim) == $(I Belirteç), $(I ŞablonParametreListesi)))) - -) - -$(P -Bu dört kullanım çok daha karmaşık ifadeler yazmaya olanak verir. -) - -$(P -$(C isim), $(C Belirteç), $(C :), ve $(C ==) hep yukarıdaki kullanımlarıyla aynı anlamdadırlar. -) - -$(P -$(C ŞablonParametreListesi) ise hem koşulun parçası olarak işlem görür hem de bütün koşul sağlandığında otomatik olarak uygun tür isimleri tanımlamaya yarar. Şablonların tür çıkarsama olanağı ile aynı biçimde işler. -) - -$(P -Örnek olarak, indeks değeri $(C string) olan eşleme tabloları kullanıldığında özel işlemler yapılması gereksin. Yalnızca böyle türlere uymaya çalışan bir $(C is) ifadesi şöyle yazılabilir: -) - ---- - static if (is (T == Değer[İndeks], // (1) - Değer, // (2) - İndeks : string)) { // (3) ---- - -$(P -O koşulu üç bölüm olarak açıklayabiliriz. Bunların son ikisi $(C ŞablonParametreListesi)'ni oluşturmaktadır: -) - -$(OL -$(LI $(C T), $(C Değer[İndeks]) yazımına uygunsa) -$(LI $(C Değer) herhangi bir tür ise) -$(LI $(C İndeks) bir $(C string) ise (şablon özellemesi söz dizimi)) -) - -$(P -$(C Belirteç) olarak $(C Değer[İndeks]) kullanılmış olması şablon parametresi olan $(C T)'nin bir eşleme tablosu türü olmasını gerektirir. $(C Değer) için hiçbir koşul belirtilmemiş olması onun herhangi bir tür olmasının yeterli olduğu anlamına gelir. Ek olarak, eşleme tablosunun indeks türünün de özellikle $(C string) olması gerekmektedir. Dolayısıyla, yukarıdaki $(C is) ifadesi, "$(C T), indeks türü $(C string) olan bir eşleme tablosu ise" anlamına gelmektedir. -) - -$(P -Bu $(C is) ifadesini kullanan ve dört farklı türle çağrılan bir örnek şöyle yazılabilir: -) - ---- -import std.stdio; - -void birİşlev(T)(T nesne) { - writeln("\n--- ", T.stringof, " ile çağrıldık ---"); - - static if (is (T == Değer[İndeks], - Değer, - İndeks : string)) { - - writeln("Evet, koşul sağlandı."); - - writeln("değer türü : ", Değer.stringof); - writeln("indeks türü: ", İndeks.stringof); - - } else { - writeln("Hayır, koşul sağlanmadı."); - } -} - -void main() { - int sayı; - birİşlev(sayı); - - int[string] intTablosu; - birİşlev(intTablosu); - - double[string] doubleTablosu; - birİşlev(doubleTablosu); - - dchar[long] dcharTablosu; - birİşlev(dcharTablosu); -} ---- - -$(P -Koşul, yalnızca indeks türü $(C string) olan eşleme tabloları için sağlanmaktadır: -) - -$(SHELL_SMALL ---- int ile çağrıldık --- -Hayır, koşul sağlanmadı. - ---- int[string] ile çağrıldık --- -Evet, koşul sağlandı. -değer türü : int -indeks türü: string - ---- double[string] ile çağrıldık --- -Evet, koşul sağlandı. -değer türü : double -indeks türü: string - ---- dchar[long] ile çağrıldık --- -Hayır, koşul sağlanmadı. -) - -Macros: - SUBTITLE=is İfadesi - - DESCRIPTION=İç gözlem olanaklarından olan is ifadesi - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial koşullu derleme is ifadesi - -SOZLER= -$(statik) diff --git a/ddili/src/ders/d/isim_alani.d b/ddili/src/ders/d/isim_alani.d deleted file mode 100644 index bd16592..0000000 --- a/ddili/src/ders/d/isim_alani.d +++ /dev/null @@ -1,149 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX isim alanı) İsim Alanı) - -$(P -D'de her isim, tanımlandığı noktadan başlayarak hem içinde tanımlandığı kapsamda, hem de o kapsamın içindeki kapsamlarda geçerlidir. Her kapsam bir $(I isim alanı) tanımlar. -) - -$(P -İçinde tanımlandığı kapsamdan çıkıldığında, isim artık geçersiz hale gelir ve derleyici tarafından tanınmaz: -) - ---- -void main() { - int dışSayı; - - if (birKoşul) { // ← yeni bir kapsam başlatır - int içSayı = 1; - dışSayı = 2; // ← dışSayı içeride de geçerlidir - - } // ← içSayı'nın geçerliliği burada son bulur - - içSayı = 3; $(DERLEME_HATASI) - // içSayı'nın geçerli olduğu kapsamdan - // çıkılmıştır -} ---- - -$(P -$(C if) koşulunun kapsamı içinde tanımlanmış olan $(C içSayı) o kapsamın dışında geçersizdir. Öte yandan, $(C dışSayı) hem dışarıdaki hem de içerideki kapsamda geçerlidir. -) - -$(P -Bir kapsamda tanımlanmış bir ismin içerdeki bir kapsamda tekrar tanımlanması yasal değildir: -) - ---- - int $(HILITE uzunluk) = tekSayılar.length; - - if (birKoşul) { - int $(HILITE uzunluk) = asalSayılar.length; $(DERLEME_HATASI) - } ---- - -$(H5 İsimleri kullanıldıkları ilk noktada tanımlamak) - -$(P -Şimdiye kadarki örneklerde de gördüğünüz gibi, isimlerin kullanıldıkları ilk noktadan daha $(I önce) tanımlanmış olmaları gerekir: -) - ---- - writeln(sayı); $(DERLEME_HATASI) - // sayı henüz bilinmiyor - int sayı = 42; ---- - -$(P -O kodun çalışabilmesi için $(C sayı)'nın $(C writeln) işleminden daha önce tanımlanmış olması gerekir. Kaç satır önce tanımlanacağı programcıya bağlı olsa da, her ismin $(I kullanıldığı ilk noktaya en yakın yerde) tanımlanması programcılık açısından daha iyi kabul edilir. -) - -$(P -Bunu kullanıcıdan aldığı sayıların ortalamalarını yazdıran bir programın $(C main) işlevinde görelim. Özellikle C dilinden gelen programcılar, kullanılan bütün isimleri kapsamların en başında tanımlamaya alışmışlardır: -) - ---- - int adet; // ← BURADA - int[] sayılar; // ← BURADA - double ortalamaDeğer; // ← BURADA - - write("Kaç sayı gireceksiniz? "); - - readf(" %s", &adet); - - if (adet >= 1) { - sayılar.length = adet; - - // ... burada asıl işlemler yapılıyor olsun... - - } else { - writeln("HATA: En az bir sayı girmelisiniz!"); - } ---- - -$(P -Bunun karşıtı olarak, isimleri olabildiğince geç tanımlamak önerilir. Aynı programı bu tavsiyeye uyarak şöyle yazabiliriz: -) - ---- - write("Kaç sayı gireceksiniz? "); - - int adet; // ← BURADA - readf(" %s", &adet); - - if (adet >= 1) { - int[] sayılar; // ← BURADA - sayılar.length = adet; - - double ortalamaDeğer; // ← BURADA - // ... burada asıl işlemler yapılıyor olsun... - - } else { - writeln("HATA: En az bir sayı girmelisiniz!"); - } ---- - -$(P -Bütün değişkenleri bir arada en başta tanımlamak yapısal olarak daha iyi olsa da, değişkenleri geç tanımlamanın da bir kaç önemli yararı vardır: -) - -$(UL -$(LI $(B Hız): Her değişken tanımının program hızı açısından bir bedeli vardır. D'de bütün değişkenler ilklendikleri için, belki de hiç kullanılmayacak olan değişkenleri en baştan ilklemek, o işlem için geçen zamanın boşa gitmesine neden olabilir. -) - -$(LI $(B Hata riski): Değişkenlerin tanımları ile kullanımları arasına giren her satır, program hataları açısından ufak da olsa bir risk taşır: bir örnek olarak, $(C uzunluk) gibi genel bir ismi olan bir değişken aradaki satırlarda yanlışlıkla başka bir uzunluk kavramı için kullanılmış, ve asıl kullanılacağı yere gelindiğinde değeri çoktan değişmiş olabilir. -) - -$(LI $(B Okuma kolaylığı): Kapsamdaki satırlar çoğaldıkça, alttaki satırlarda kullanılan bir değişkenin tanımının programın yazıldığı ekranın dışında kalma olasılığı artar; değişkenlerin tanımlarını görmek veya hatırlamak için sık sık metnin üst tarafına gitmek ve tekrar geri gelmek gerekebilir. -) - -$(LI $(B Kod değişikliği): Program kodları sürekli olarak gelişim halindedirler: programa ekler yapılır, programın bazı olanakları silinir, farkedilen hataları giderilir, vs. Bu işlemler sırasında çoğu zaman bir grup satırın hep birden başka bir işlev olarak tanımlanması istenebilir. - -$(P Böyle durumlarda, o kod satırlarında kullanılan bütün değişkenlerin kullanıldıkları ilk yerde tanımlanmış olmaları, hepsinin birden başka bir yere taşınmalarına olanak sağlar. -) - -$(P -Örneğin yukarıdaki bu tavsiyeye uyan programın $(C if) kapsamındaki bütün satırlar hep birden programın başka bir noktasına taşınabilirler. -) - -$(P -Oysa değişkenlerini C'deki gibi tanımlayan bir programda, taşınacak olan kod satırlarında kullanılan değişkenlerin de teker teker seçilerek ayrı ayrı taşınmaları gerekir. -) - -) - -) - -Macros: - SUBTITLE=İsim alanı - - DESCRIPTION=D dilinde kullanılan isimlerin geçerlilik alanları (kapsamları) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial isim alanı kapsam - -SOZLER= -$(degisken) -$(emekli) -$(ilklemek) -$(isim_alani) -$(kapsam) diff --git a/ddili/src/ders/d/islec_oncelikleri.d b/ddili/src/ders/d/islec_oncelikleri.d deleted file mode 100644 index 616bd51..0000000 --- a/ddili/src/ders/d/islec_oncelikleri.d +++ /dev/null @@ -1,325 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX işleç öncelikleri) $(IX öncelik, işleç) İşleç Öncelikleri) - -$(P -Kitabın başından beri yaptığımız gibi, ikiden fazla ifade birden fazla işleç ile bir araya getirilebilir. Örneğin, aşağıdaki satır üç işleç ile bağlanmış olan dört ifade içerir: -) - ---- - a = b + c * d // üç işleç: =, +, ve * ---- - -$(P -İşleçlerin öncelik kuralları, birden fazla işleç bulunan durumda o işleçlerin hangi sırada işletileceklerini ve hangi ifadeleri kullanacaklarını belirler. İşleçler önceliklerine göre işletilirler: önce yüksek öncelikli olanlar, sonra düşük öncelikli olanlar. -) - -$(P -Aşağıdaki tablo D'nin işleç önceliklerini yüksek öncelikliden düşük öncelikliye doğru sıralanmış olarak verir. Aynı tablo satırında bulunan işleçler aynı önceliğe sahiptir. (Aynı tablo hücresi içindeki satırların önemi yoktur; örneğin, $(C ==) ile $(C !is) aynı önceliğe sahiptir.) Özellikle belirtilmediği sürece bütün işleçler $(I sol birleşimlidir). -) - -$(P -Tabloda kullanılan terimlerin bazıları aşağıda açıklanıyor. -) - - - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    İşleçlerAçıklamaNotlar
    $(C !) Şablon gerçekleştirmesiZincirlenemez -
    $(C =>) İsimsiz işlev tanımıGerçek bir işleç değildir; tabloda iki yerde bulunur; bu satır $(I sol) tarafıyla ilgilidir -
    $(C . ++ -- $(PARANTEZ_AC) [) Sonek işleçler$(C $(PARANTEZ_AC)) ve $(C [) parantezleri sırasıyla $(C $(PARANTEZ_KAPA)) ve $(C ]) ile kapatılmalıdır -
    $(C ^^) Üs alma işleciSağ birleşimli -
    $(C ++ -- * + - ! & ~ cast) Tekli işleçler -
    $(C * / %) İkili işleçler -
    $(C + - ~) İkili işleçler
    $(C << >> >>>) Bit kaydırma işleçleri -
    $(C == != > < >= <= !> !< !>= !<= <> !<> <>= !<>= in !in is !is) Karşılaştırma işleçleriBit işleçleri ile aralarında öncelik tanımlı değildir; Zincirlenemezler -
    $(C &) Bit işlemi $(I ve)Karşılaştırma işleçleri ile aralarında öncelik tanımlı değildir -
    $(C ^) Bit işlemi $(I ya da)Karşılaştırma işleçleri ile aralarında öncelik tanımlı değildir -
    $(C |) Bit işlemi $(I veya)Karşılaştırma işleçleri ile aralarında öncelik tanımlı değildir -
    $(C &&) Mantıksal $(I ve)İkinci ifade işletilmeyebilir
    $(C ||)Mantıksal $(I veya)İkinci ifade işletilmeyebilir
    $(C ?:)Üçlü işleçSağ birleşimli -
    $(C = -= += = *= %= ^= ^^= ~= <<= >>= >>>=) - Atamalı işleçlerSağ birleşimli -
    $(C =>) İsimsiz işlev tanımıGerçek bir işleç değildir; tabloda iki yerde bulunur; bu satır $(I sağ) tarafıyla ilgilidir
    $(C ,) Virgül işleciAyraç olarak kullanılan virgül ile karıştırılmamalıdır
    $(C ..) Sayı aralığıGerçek bir işleç değildir; söz diziminin özel bir parçasıdır -
    - - -$(H6 $(IX işleç zincirleme) $(IX zincirleme, işleç) İşleç zincirleme) - -$(P -Bölümün başındaki ifadeye tekrar bakalım: -) - ---- - a = b + c * d ---- - -$(P -İkili $(C *) işlecinin önceliği ikili $(C +) işlecininkinden ve ikili $(C +) işlecinin önceliği $(C =) işlecininkinden yüksek olduklarından o ifade aşağıdaki parantezli eşdeğeri gibi işletilir: -) - ---- - a = (b + (c * d)) // önce *, sonra +, sonra = ---- - -$(P -Başka bir örnek olarak, sonek $(C .) işlecinin önceliği tekli $(C *) işlecininkinden yüksek olduğundan aşağıdaki ifade önce $(C n) nesnesinin $(C gösterge) üyesine erişir, sonra da onun gösterdiğine: -) - ---- - *n.gösterge // ← n.gösterge'nin gösterdiğine erişir - *(n.gösterge) // ← üsttekinin eşdeğeri - (*n).gösterge $(CODE_NOTE_WRONG üsttekinin eşdeğeri DEĞİL) ---- - -$(P -Bazı işleçler zincirlenemezler: -) - ---- - if (a > b == c) { $(DERLEME_HATASI) - // ... - } ---- - -$(SHELL -Error: found '==' when expecting '$(PARANTEZ_KAPA)' -) - -$(P -İşlem sırasını programcının örneğin parantezlerle belirtmesi gerekir: -) - ---- - if ((a > b) == c) { $(CODE_NOTE derlenir) - // ... - } ---- - -$(H6 $(IX sol birleşimli) $(IX sağ birleşimli) $(IX birleşim, işleç önceliği) $(IX işleç birleşimi) Birleşim) - -$(P -Aynı önceliğe sahip işleçler bulunduğunda hangisinin önce işletileceği işleç birleşimlerine göre belirlenir: ya soldaki ya da sağdaki. -) - -$(P -Çoğu işleç sol birleşimlidir; önce soldaki işletilir: -) - ---- - 10 - 7 - 3; - (10 - 7) - 3; $(CODE_NOTE üsttekinin eşdeğeri (== 0)) - 10 - (7 - 3); $(CODE_NOTE_WRONG üsttekinin eşdeğeri DEĞİL (== 6)) ---- - -$(P -Bazı işleçler sağ birleşimlidir; önce sağdaki işletilir: -) - ---- - 4 ^^ 3 ^^ 2; - 4 ^^ (3 ^^ 2); $(CODE_NOTE üsttekinin eşdeğeri (== 262144)) - (4 ^^ 3) ^^ 2; $(CODE_NOTE_WRONG üsttekinin eşdeğeri DEĞİL (== 4096)) ---- - -$(H6 Öncelikleri tanımsız işleç grupları) - -$(P -Bit işleçleriyle karşılaştırma işleçleri arasında öncelik tanımlı değildir: -) - ---- - if (a & b == c) { $(DERLEME_HATASI) - // ... - } ---- - -$(SHELL -Error: b == c must be parenthesized when next to operator & -) - -$(P -İşlem sırasını programcının örneğin parantezlerle belirtmesi gerekir: -) - ---- - if ((a & b) == c) { $(CODE_NOTE derlenir) - // ... - } ---- - -$(H6 $(IX =>, işleç önceliği) $(C =>) işaretinin önceliği) - -$(P -Aslında bir işleç olmayan $(C =>), solundaki ve sağındaki işleçlerle farklı önceliklere sahip olduğundan tabloda iki satırda yer alır. -) - ---- - l = a => a = 1; ---- - -$(P -Yukarıda $(C =>) işaretinin her iki tarafında da $(C =) işleci olduğu halde, $(C =>) sol tarafta $(C =) işlecinden daha öncelikli olduğundan soldaki $(C a)'ya kendisi bağlanır ve sanki programcı aşağıdaki parantezleri yazmış gibi kabul edilir: -) - ---- - l = (a => a = 1); ---- - -$(P -Öte yandan, $(C =>) sağ tarafta $(C =) işlecinden daha düşük öncelikli olduğundan, sağdaki $(C a) $(C =) işlecine bağlanır ve sanki aşağıdaki parantezler de yazılmış gibi kabul edilir: -) - ---- - l = (a => $(HILITE $(PARANTEZ_AC))a = 1$(HILITE $(PARANTEZ_KAPA))); ---- - -$(P -Dolayısıyla, isimsiz işlevin tanımı $(C a => a) haline $(I gelmez); ifadenin geri kalanını da içerir: $(C a => a = 1) (anlamı: $(I a'ya karşılık a = 1 değerini üret)). O isimsiz işlev de sonuçta $(C l) değişkenine atanmaktadır. -) - -$(P -$(I Not: Bunu yalnızca bir örnek olarak kabul edin. $(C a = 1) ifadesi bir isimsiz işlevin içeriği olarak anlamlı değildir çünkü parametresine yapılan o atama normalde etkisizdir ve işlev hep 1 değerini üretir. (Burada "normalde" denmesinin nedeni, aslında $(C a)'nın atama işlecinin yüklenmiş olabileceği ve işletildiğinde yan etki üretebileceğidir.)) -) - -$(H6 $(IX , (virgül), işleç) $(IX virgül işleci) Virgül işleci) - -$(P -Virgül işleci ikili bir işleçtir. Önce sol tarafındaki ifadeyi sonra sağ tarafındaki ifadeyi işletir. Sol tarafın ürettiği değer göz ardı edilir ama sağ tarafın değeri virgül işlecinin değeri olarak kullanılır: -) - ---- - int a = 1; - int b = (foo()$(HILITE ,) bar()$(HILITE ,) ++a + 10); - - assert(a == 2); - assert(b == 12); ---- - -$(P -Yukarıdaki iki virgül işleci üç ifadeyi bağlamaktadır. İlk iki ifade ($(C foo()) ve $(C bar())) işletildikleri halde, onların değerleri kullanılmaz ve virgül işlecinin değeri son ifadenin ($(C ++a + 10)) değeri olur. -) - -Macros: - SUBTITLE=İşleç Öncelikleri - - DESCRIPTION=D işleçlerinin öncelikleri ve birleşimleri (sol veya sağ) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işleç öncelik birleşim - -SOZLER= -$(ifade) -$(isimsiz_islev) -$(islec) -$(islec_birlesimi) -$(sonek_islec) diff --git a/ddili/src/ders/d/islec_yukleme.cozum.d b/ddili/src/ders/d/islec_yukleme.cozum.d deleted file mode 100644 index 8ee76e1..0000000 --- a/ddili/src/ders/d/islec_yukleme.cozum.d +++ /dev/null @@ -1,290 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU İşleç Yükleme) - -$(P -Aşağıdaki gerçekleştirme bütün birim testlerinden geçiyor. Tasarım kararlarını kod açıklamaları içine yazdım. -) - -$(P -Bu yapının bazı işleçleri daha etkin olarak tasarlanabilirdi. Ek olarak, payı ve paydayı sadeleştirmek de gerekir. Pay ve payda değerleri örneğin 20 ve 60 olarak kalmak yerine en büyük ortak bölenlerine bölündükten sonra 1 ve 3 olarak saklanmalıdır. Yoksa, çoğu işlem payın ve paydanın gittikçe büyümelerine neden olmakta. -) - ---- -import std.exception; -import std.conv; - -struct Kesir { - long pay; - long payda; - - /* Kurucu işlev kolaylık olsun diye paydanın - * belirtilmesini gerektirmiyor ve 1 varsayıyor. */ - this(long pay, long payda = 1) { - enforce(payda != 0, "Payda sıfır olamaz"); - - this.pay = pay; - this.payda = payda; - - /* Paydanın eksi değer almasını başından önlemek daha - * sonraki işlemleri basitleştirecek. */ - if (this.payda < 0) { - this.pay = -this.pay; - this.payda = -this.payda; - } - } - - /* Tekli - işleci: Kesirin eksi değerli olanını - * döndürür. */ - Kesir opUnary(string işleç)() const - if (işleç == "-") { - /* İsimsiz bir nesne üretiyor ve döndürüyor. */ - return Kesir(-pay, payda); - } - - /* ++ işleci: Kesirin değerini bir arttırır. */ - ref Kesir opUnary(string işleç)() - if (işleç == "++") { - /* Burada 'this += Kesir(1)' de kullanılabilirdi. */ - pay += payda; - return this; - } - - /* -- işleci: Kesirin değerini bir azaltır. */ - ref Kesir opUnary(string işleç)() - if (işleç == "--") { - /* Burada 'this -= Kesir(1)' de kullanılabilirdi. */ - pay -= payda; - return this; - } - - /* += işleci: Kesirin değerini arttırır. */ - ref Kesir opOpAssign(string işleç)(in Kesir sağdaki) - if (işleç == "+") { - /* Toplama formülü: a/b + c/d = (a*d + c*b)/(b*d) */ - pay = (pay * sağdaki.payda) + (sağdaki.pay * payda); - payda *= sağdaki.payda; - return this; - } - - /* -= işleci: Kesirin değerini azaltır. */ - ref Kesir opOpAssign(string işleç)(in Kesir sağdaki) - if (işleç == "-") { - /* Burada zaten tanımlanmış olan += ve tekli - - * işleçlerinden yararlanılıyor. Bunun yerine bu işleç - * de += işlecine benzer biçimde ve çıkarma formülü - * açıkça gerçekleştirilerek tanımlanabilirdi: - * - * Çıkarma formülü: a/b - c/d = (a*d - c*b)/(b*d) - */ - this += -sağdaki; - return this; - } - - /* *= işleci: Kesiri sağdaki ile çarpar. */ - ref Kesir opOpAssign(string işleç)(in Kesir sağdaki) - if (işleç == "*") { - /* Çarpma formülü: a/b * c/d = (a*c)/(b*d) */ - pay *= sağdaki.pay; - payda *= sağdaki.payda; - return this; - } - - /* /= işleci: Kesiri sağdakine böler. */ - ref Kesir opOpAssign(string işleç)(in Kesir sağdaki) - if (işleç == "/") { - enforce(sağdaki.pay != 0, "Sıfırla bölme hatası"); - - /* Bölme formülü: (a/b) / (c/d) = (a*d)/(b*c) */ - pay *= sağdaki.payda; - payda *= sağdaki.pay; - return this; - } - - /* + işleci: Bu kesirle sağdakinin toplamını üretir. */ - Kesir opBinary(string işleç)(in Kesir sağdaki) const - if (işleç == "+") { - /* Önce bu nesnenin bir kopyası alınıyor ve zaten - * tanımlanmış olan += işleci o kopyaya - * uygulanıyor. */ - Kesir sonuç = this; - sonuç += sağdaki; - return sonuç; - } - - /* - işleci: Bu kesirle sağdakinin farkını üretir. */ - Kesir opBinary(string işleç)(in Kesir sağdaki) const - if (işleç == "-") { - /* Zaten tanımlanmış olan -= işleci kullanılıyor. */ - Kesir sonuç = this; - sonuç -= sağdaki; - return sonuç; - } - - /* * işleci: Bu kesirle sağdakinin çarpımını üretir. */ - Kesir opBinary(string işleç)(in Kesir sağdaki) const - if (işleç == "*") { - /* Zaten tanımlanmış olan *= işleci kullanılıyor. */ - Kesir sonuç = this; - sonuç *= sağdaki; - return sonuç; - } - - /* / işleci: Bu kesirin sağdakine bölümünü üretir. */ - Kesir opBinary(string işleç)(in Kesir sağdaki) const - if (işleç == "/") { - /* Zaten tanımlanmış olan /= işleci kullanılıyor. */ - Kesir sonuç = this; - sonuç /= sağdaki; - return sonuç; - } - - /* double türünde eşdeğer üretme işleci. */ - double opCast(T : double)() const { - /* Basit bir bölme işlemi. Ancak, long türünde bölme - * işlemi virgülden sonrasını kırpacağından burada - * pay/payda yazılamazdı. */ - return to!double(pay) / payda; - } - - /* Sıra karşılaştırması: Bu kesir önce ise eksi, sonra ise - * artı, ikisi de eşit iseler sıfır üretir. */ - int opCmp(const ref Kesir sağdaki) const { - immutable sonuç = this - sağdaki; - /* long türündeki pay dönüş türü olan int'e otomatik - * olarak dönüştürülemeyeceğinden 'to' ile açıkça tür - * dönüşümü gerekir. */ - return to!int(sonuç.pay); - } - - /* Eşitlik karşılaştırması: Eşit iseler true üretir. - * - * Eşitlik karşılaştırması işlecinin bu tür için özel - * olarak tanımlanması gerekmektedir çünkü derleyicinin - * otomatik olarak işlettiği ve üyelerin teker teker - * karşılaştırılmalarından oluşan opEquals Kesir türü için - * yeterli değildir. - * - * Örneğin, derleyicinin opEquals'ı her ikisinin değeri de - * 0.5 olan Kesir(1,2) ve Kesir(2,4)'ün eşit olmadıklarına - * karar verirdi. */ - bool opEquals(const ref Kesir sağdaki) const { - /* opCmp'ın değerinin 0 olup olmadığına bakmak - * yeterlidir. */ - return opCmp(sağdaki) == 0; - } -} - -unittest { - /* Payda 0 olduğunda hata atılmalı. */ - assertThrown(Kesir(42, 0)); - - /* 1/3 değeriyle başlayacağız. */ - auto a = Kesir(1, 3); - - /* -1/3 */ - assert(-a == Kesir(-1, 3)); - - /* 1/3 + 1 == 4/3 */ - ++a; - assert(a == Kesir(4, 3)); - - /* 4/3 - 1 == 1/3 */ - --a; - assert(a == Kesir(1, 3)); - - /* 1/3 + 2/3 == 3/3 */ - a += Kesir(2, 3); - assert(a == Kesir(1)); - - /* 3/3 - 2/3 == 1/3 */ - a -= Kesir(2, 3); - assert(a == Kesir(1, 3)); - - /* 1/3 * 8 == 8/3 */ - a *= Kesir(8); - assert(a == Kesir(8, 3)); - - /* 8/3 / 16/9 == 3/2 */ - a /= Kesir(16, 9); - assert(a == Kesir(3, 2)); - - /* double türünde bir değere dönüştürülebilmeli. - * - * Hatırlarsanız, double türü her değeri tam olarak ifade - * edemez. 1.5 değeri tam olarak ifade edilebildiği için - * bu testi bu noktada uyguladım. */ - assert(to!double(a) == 1.5); - - /* 1.5 + 2.5 == 4 */ - assert(a + Kesir(5, 2) == Kesir(4, 1)); - - /* 1.5 - 0.75 == 0.75 */ - assert(a - Kesir(3, 4) == Kesir(3, 4)); - - /* 1.5 * 10 == 15 */ - assert(a * Kesir(10) == Kesir(15, 1)); - - /* 1.5 / 4 == 3/8 */ - assert(a / Kesir(4) == Kesir(3, 8)); - - /* Sıfırla bölmek hata atmalı. */ - assertThrown(Kesir(42, 1) / Kesir(0)); - - /* Payı az olan öncedir. */ - assert(Kesir(3, 5) < Kesir(4, 5)); - - /* Paydası büyük olan öncedir. */ - assert(Kesir(3, 9) < Kesir(3, 8)); - assert(Kesir(1, 1_000) > Kesir(1, 10_000)); - - /* Değeri küçük olan öncedir. */ - assert(Kesir(10, 100) < Kesir(1, 2)); - - /* Eksi değer öncedir. */ - assert(Kesir(-1, 2) < Kesir(0)); - assert(Kesir(1, -2) < Kesir(0)); - - /* Aynı değerler hem <= hem de >= olmalı. */ - assert(Kesir(-1, -2) <= Kesir(1, 2)); - assert(Kesir(1, 2) <= Kesir(-1, -2)); - assert(Kesir(3, 7) <= Kesir(9, 21)); - assert(Kesir(3, 7) >= Kesir(9, 21)); - - /* Değerleri aynı olanlar eşit olmalı. */ - assert(Kesir(1, 3) == Kesir(20, 60)); - - /* Karışık işaretler aynı sonucu üretmeli. */ - assert(Kesir(-1, 2) == Kesir(1, -2)); - assert(Kesir(1, 2) == Kesir(-1, -2)); -} - -void main() { -} ---- - -$(P -Bölümde de kısaca değinildiği gibi, $(C mixin) olanağı bazı işleçlerin tanımlarını birleştirmek için kullanılabilir. Örneğin, aşağıdaki tanım dört aritmetik işlecin hepsini birden tanımlar: -) - ---- - /* İkili aritmetik işleçleri. */ - Kesir opBinary(string işleç)(in Kesir sağdaki) const - if ((işleç == "+") || (işleç == "-") || - (işleç == "*") || (işleç == "/")) { - /* Önce bu nesnenin bir kopyası alınıyor ve zaten - * tanımlanmış olan atamalı işleç o kopyaya - * uygulanıyor. */ - Kesir sonuç = this; - mixin ("sonuç " ~ işleç ~ "= sağdaki;"); - return sonuç; - } ---- - - -Macros: - SUBTITLE=İşleç Yükleme - - DESCRIPTION=D'de türlerin kullanışlılığını arttıran olanaklarından işleç yükleme [operator overloading] ile ilgili problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işleç yükleme overloading problem çözüm diff --git a/ddili/src/ders/d/islec_yukleme.d b/ddili/src/ders/d/islec_yukleme.d deleted file mode 100644 index b297d75..0000000 --- a/ddili/src/ders/d/islec_yukleme.d +++ /dev/null @@ -1,1850 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX işleç yükleme) $(IX yükleme, işleç) İşleç Yükleme) - -$(P -İşleç yükleme olanağını bu bölümde yapılar üzerinde göreceğiz. Burada anlatılanlar daha sonra göreceğimiz sınıflar için de hemen hemen aynen geçerlidir. En belirgin fark, $(LINK2 /ders/d/ozel_islevler.html, Kurucu ve Diğer Özel İşlevler bölümünde) gördüğümüz atama işleci $(C opAssign)'ın sınıflar için tanımlanamıyor olmasıdır. -) - -$(P -İşleç yükleme çok sayıda kavram içerir (şablonlar, $(C auto ref), vs.). Bu kavramların bazılarını kitabın ilerideki bölümlerinde göreceğimizden bu bölüm size bu aşamada diğer bölümlerden daha zor gelebilir. -) - -$(P -İşleç yükleme, işleçlerin kendi türlerimizle nasıl çalışacaklarını belirleme olanağıdır. -) - -$(P -Yapıların ve üye işlevlerin yararlarını önceki bölümlerde görmüştük. Bunun bir örneği, $(C GününSaati) nesnelerine $(C Süre) nesnelerini ekleyebilmekti. Kodu kısa tutmak için yalnızca bu bölümü ilgilendiren üyelerini gösteriyorum: -) - ---- -struct Süre { - int dakika; -} - -struct GününSaati { - int saat; - int dakika; - - void $(HILITE ekle)(in Süre süre) { - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - } -} - -void main() { - auto yemekZamanı = GününSaati(12, 0); - yemekZamanı$(HILITE .ekle)(Süre(10)); -} ---- - -$(P -Üye işlevlerin yararı, yapıyı ilgilendiren işlemlerin yapının içinde tanımlanabilmeleridir. Üye değişkenler ve üye işlevler bir arada tanımlanınca yapının üyelerinin bütün işlevler tarafından doğru olarak kullanıldıkları daha kolay denetlenebilir. -) - -$(P -Yapıların bütün bu yararlarına karşın, temel türlerin işleç kullanımı konusunda yapılara karşı üstünlükleri vardır: Temel türler özel tanımlar gerekmeden işleçlerle rahatça kullanılabilirler: -) - ---- - int ağırlık = 50; - ağırlık $(HILITE +=) 10; // işleçle ---- - -$(P -Şimdiye kadar öğrendiğimiz bilgiler doğrultusunda benzer işlemleri yapılar için ancak üye işlevlerle gerçekleştirebiliyoruz: -) - ---- - auto yemekZamanı = GününSaati(12, 0); - yemekZamanı$(HILITE .ekle)(Süre(10)); // üye işlevle ---- - -$(P -İşleç yükleme, yapıları da temel türler gibi işleçlerle kullanabilme olanağı sunar. Örneğin, $(C GününSaati) yapısı için tanımlayabileceğimiz $(C +=) işleci, yukarıdaki işlemin yazımını kolaylaştırır ve daha okunaklı hale getirir: -) - ---- - yemekZamanı $(HILITE +=) Süre(10); // yapı için de işleçle ---- - -$(P -Yüklenebilecek bütün işleçlere geçmeden önce bu kullanımın nasıl sağlandığını göstermek istiyorum. Daha önce ismini $(C ekle) olarak yazdığımız işlevi D'nin özel olarak belirlediği $(C opOpAssign(string işleç)) ismiyle tanımlamak ve bu tanımın $(STRING "+") karakteri için yapılmakta olduğunu belirtmek gerekir. Biraz aşağıda açıklanacağı gibi, bu aslında $(C +=) işleci içindir. -) - -$(P -Aşağıdaki tanımın şimdiye kadar gördüğümüz işlev tanımlarına benzemediğini farkedeceksiniz. Bunun nedeni, $(C opOpAssign)'ın aslında bir $(I işlev şablonu) olmasıdır. Şablonları daha ilerideki bölümlerde göreceğiz; şimdilik işleç yükleme konusunda bu söz dizimini bir kalıp olarak uygulamak gerektiğini kabul etmenizi rica ediyorum: -) - ---- -struct GününSaati { -// ... - ref GününSaati opOpAssign(string işleç)(in Süre süre) // (1) - if (işleç == "+") { // (2) - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - - return this; - } -} ---- - -$(P -Yukarıdaki şablon tanımı iki parçadan oluşur: -) - -$(OL - -$(LI $(C opOpAssign(string işleç)): Bunun aynen yazılması gerekir. ($(C opOpAssign)'dan başka işleç işlevlerinin de bulunduğunu aşağıda göreceğiz.) -) - -$(LI $(C if (işleç == $(STRING "+"))): $(C opOpAssign) birden fazla işlecin tanımında kullanılabildiği için hangi işleç karakterinin tanımlanmakta olduğu bu söz dizimiyle belirtilir. Aslında bir $(I şablon kısıtlaması) olan bu söz diziminin ayrıntılarını da daha sonraki bir bölümde göreceğiz. -) - -) - -$(P -$(C ekle) işlevinden farklı olarak, dönüş türünün bu tanımda $(C void) olmadığına dikkat edin. İşleçlerin dönüş türlerini biraz aşağıda açıklayacağım. -) - -$(P -Derleyici, $(C GününSaati) nesnelerinin $(C +=) işleciyle kullanıldığı yerlerde perde arkasında $(C opOpAssign!$(STRING "+")) işlevini çağırır: -) - ---- - yemekZamanı += Süre(10); - - // Aşağıdaki satır üsttekinin eşdeğeridir: - yemekZamanı.opOpAssign!"+"(Süre(10)); ---- - -$(P -$(C opOpAssign)'dan sonra yazılan $(C !$(STRING "+")), $(C opOpAssign)'ın $(C +) karakteri için tanımı olan işlevin çağrılmakta olduğu anlamına gelir. Şablonlarla ilgili olan bu söz dizimini de daha sonraki bir bölümde göreceğiz. -) - -$(P -Kod içindeki $(C +=) işlecine karşılık gelen yukarıdaki üye işlevde $(STRING "+=") değil, $(STRING "+") kullanıldığına dikkat edin. $(C opOpAssign)'ın isminde geçen ve "değer ata" anlamına gelen "assign" zaten atama kavramını içerir. Sonuçta, $(C opOpAssign!$(STRING "+")), atamalı toplama işlemi olan $(C +=) işlecinin tanımıdır. -) - -$(P -İşleçlerin davranışlarını bizim belirleyebiliyor olmamız bize çoğu işleç için istediğimiz şeyi yapma serbestisi verir. Örneğin, yukarıdaki işleci süre ekleyecek şekilde değil, tam tersine süre azaltacak şekilde de tanımlayabilirdik. Oysa kodu okuyanlar $(C +=) işlecini gördüklerinde doğal olarak $(I değerin artmasını) bekleyeceklerdir. -) - -$(P -Genel beklentilere uyulması işleçlerin dönüş türleri için de önemlidir. -) - -$(P -İşleçleri doğal davranışları dışında yazdığınızda bunun herkesi yanıltacağını ve programda hatalara neden olacağını aklınızda bulundurun. -) - -$(H5 Yüklenebilen işleçler) - -$(P -İşleçler kullanım çeşitlerine göre farklı yüklenirler. -) - -$(H6 $(IX tekli işleç) $(IX işleç, tekli) $(IX opUnary) Tekli işleçler) - -$(P -Tek nesneyle işleyen işleçlere tekli işleç denir: -) - ---- - ++ağırlık; ---- - -$(P -Yukarıdaki $(C ++) işleci tekli işleçtir çünkü işini yaparken tek değişken kullanmaktadır ve onun değerini bir arttırmaktadır. -) - -$(P -Bu işleçler $(C opUnary) üye işlev ismiyle tanımlanırlar. $(C opUnary) parametre almaz çünkü yalnızca işlecin üzerinde uygulandığı nesneyi (yani $(C this)'i) etkiler. -) - -$(P -İşlev tanımında kullanılması gereken işleç dizgileri şunlardır: -) - -$(TABLE full, -$(HEAD3 İşleç, Anlamı, İşleç Dizgisi) -$(ROW3 $(IX -, eksi işareti) -nesne, ters işaretlisini üret, "-") -$(ROW3 $(IX +, artı işareti) +nesne, aynı işaretlisini üret, "+") -$(ROW3 $(IX ~, tersini alma) ~nesne, bit düzeyinde tersini al, "~") -$(ROW3 $(IX *, erişim işleci) *nesne, gösterdiğine eriş, "*") -$(ROW3 $(IX ++, arttırma) ++nesne, bir arttır, "++") -$(ROW3 $(IX --, azaltma) --nesne, bir azalt, "--") -) - -$(P -Örneğin $(C ++) işlecini $(C Süre) için şöyle tanımlayabiliriz: -) - ---- -struct Süre { - int dakika; - - ref Süre opUnary(string işleç)() - if (işleç == "++") { - ++dakika; - return this; - } -} ---- - -$(P -İşlecin dönüş türünün $(C ref) olarak işaretlendiğine dikkat edin. İşleçlerin dönüş türlerini aşağıda açıklayacağım. -) - -$(P -$(C Süre) nesneleri bu sayede artık $(C ++) işleci ile arttırılabilirler: -) - ---- - auto süre = Süre(20); - ++süre; ---- - -$(P -$(IX ++, arttırma, önceki değerli) $(IX --, azaltma, önceki değerli) $(IX önceki değerli arttırma) $(IX önceki değerli azaltma) $(IX arttırma, sonek) $(IX azaltma, sonek) Önceki değerli (sonek) arttırma ve önceki değerli (sonek) azaltma işleçleri olan $(C nesne++) ve $(C nesne--) kullanımları yüklenemez. O kullanımlardaki önceki değerleri derleyici otomatik olarak üretir. Örneğin, $(C süre++) kullanımının eşdeğeri şudur: -) - ---- - /* Önceki değer derleyici tarafından otomatik olarak - * kopyalanır: */ - Süre __öncekiDeğer__ = süre; - - /* Tanımlanmış olan normal ++ işleci çağrılır: */ - ++süre; - - /* Daha sonra bütün ifadede __öncekiDeğer__ kullanılır. */ ---- - -$(P -Bazı programlama dillerinden farklı olarak, $(I önceki değerin) programda kullanılmadığı durumlarda yukarıdaki kopyanın D'de bir masrafı yoktur. İfadenin kullanılmadığı durumlarda $(I önceki değerli arttırma) işleçleri normal arttırma işleçleriyle değiştirilirler: -) - ---- - /* Aşağıdaki ifadenin değeri programda kullanılmamaktadır. - * İfadenin tek etkisi, 'i'nin değerini arttırmaktır. */ - i++; ---- - -$(P -$(C i)'nin $(I önceki değeri) programda kullanılmadığından derleyici o ifadenin yerine aşağıdakini yerleştirir: -) - ---- - /* Derleyicinin kullandığı ifade: */ - ++i; ---- - -$(P -Ek olarak, eğer aşağıda göreceğimiz $(C opBinary) yüklemesi $(C süre += 1) kullanımını destekliyorsa, $(C ++süre) ve $(C süre++) kullanımları için $(C opUnary) gerekmez; derleyici onların yerine $(C süre += 1) ifadesinden yararlanır. Benzer biçimde, $(C süre -= 1) yüklemesi de $(C --süre) ve $(C süre--) kullanımlarını karşılar. -) - -$(H6 $(IX ikili işleç) $(IX işleç, ikili) İkili işleçler) - -$(P -İki nesne kullanan işleçlere ikili işleç denir: -) - ---- - toplamAğırlık $(HILITE =) kutuAğırlığı $(HILITE +) çikolataAğırlığı; ---- - -$(P -Yukarıdaki satırda iki farklı ikili işleç görülüyor: $(C +) işleci solundaki ve sağındaki değerleri toplar, $(C =) işleci de sağındakinin değerini solundakine atar. -) - -$(P -$(IX +, toplama) -$(IX -, çıkarma) -$(IX *, çarpma) -$(IX /) -$(IX %) -$(IX ^^) -$(IX &, ve) -$(IX |) -$(IX ^, ya da) -$(IX <<) -$(IX >>) -$(IX >>>) -$(IX ~, birleştirme) -$(IX in, işleç) -$(IX ==) -$(IX !=) -$(IX <, küçüktür) -$(IX <=) -$(IX >, büyüktür) -$(IX >=) -$(IX =) -$(IX +=) -$(IX -=) -$(IX *=) -$(IX /=) -$(IX %=) -$(IX ^^=) -$(IX &=) -$(IX |=) -$(IX ^=) -$(IX <<=) -$(IX >>=) -$(IX >>>=) -$(IX ~=) -$(IX opBinary) -$(IX opAssign) -$(IX opOpAssign) -$(IX opBinaryRight) -İşleçleri gruplandırmak için aşağıdaki tabloda işleçlerin çeşitlerini de belirttim. "=" ile işaretli olanlar sağ tarafın değerini sol tarafa atarlar. -) - -$(TABLE full, -$(HEAD5 $(BR)İşleç, $(BR)Anlamı, $(BR)İşlev İsmi, Sağ Taraf için$(BR)İşlev İsmi, $(BR)Çeşit) -$(ROW5 +, topla, opBinary, opBinaryRight, aritmetik) -$(ROW5 -, çıkar, opBinary, opBinaryRight, aritmetik) -$(ROW5 *, çarp, opBinary, opBinaryRight, aritmetik) -$(ROW5 /, böl, opBinary, opBinaryRight, aritmetik) -$(ROW5 %, kalanını hesapla, opBinary, opBinaryRight, aritmetik) -$(ROW5 ^^, üssünü al, opBinary, opBinaryRight, aritmetik) -$(ROW5 &, bit işlemi $(I ve), opBinary, opBinaryRight, bit) -$(ROW5 |, bit işlemi $(I veya), opBinary, opBinaryRight, bit) -$(ROW5 ^, bit işlemi $(I ya da), opBinary, opBinaryRight, bit) -$(ROW5 <<, sola kaydır, opBinary, opBinaryRight, bit) -$(ROW5 >>, sağa kaydır, opBinary, opBinaryRight, bit) -$(ROW5 >>>, işaretsiz sağa kaydır, opBinary, opBinaryRight, bit) -$(ROW5 ~, birleştir, opBinary, opBinaryRight, ) -$(ROW5 in, içinde mi?, opBinary, opBinaryRight, ) -$(ROW5 ==, eşittir, opEquals, -, mantıksal) -$(ROW5 !=, eşit değildir, opEquals, -, mantıksal) -$(ROW5 <, öncedir, opCmp, -, sıralama) -$(ROW5 <=, sonra değildir, opCmp, -, sıralama) -$(ROW5 >, sonradır, opCmp, -, sıralama) -$(ROW5 >=, önce değildir, opCmp, -, sıralama) -$(ROW5 =, ata, opAssign, -, =) -$(ROW5 +=, arttır, opOpAssign, -, =) -$(ROW5 -=, azalt, opOpAssign, -, =) -$(ROW5 *=, katını ata, opOpAssign, -, =) -$(ROW5 /=, bölümünü ata, opOpAssign, -, =) -$(ROW5 %=, kalanını ata, opOpAssign, -, =) -$(ROW5 ^^=, üssünü ata, opOpAssign, -, =) -$(ROW5 &=, & sonucunu ata, opOpAssign, -, =) -$(ROW5 |=, | sonucunu ata, opOpAssign, -, =) -$(ROW5 ^=, ^ sonucunu ata, opOpAssign, -, =) -$(ROW5 <<=, << sonucunu ata, opOpAssign, -, =) -$(ROW5 >>=, >> sonucunu ata, opOpAssign, -, =) -$(ROW5 >>>=, >>> sonucunu ata, opOpAssign, -, =) -$(ROW5 ~=, sonuna ekle, opOpAssign, -, =) -) - -$(P -Tabloda $(I sağ taraf için) olarak belirtilen işlev isimleri, nesne işlecin sağ tarafında da kullanılabildiğinde işletilir. Kodda şöyle bir ikili işleç bulunduğunu düşünelim: -) - ---- - x $(I işleç) y ---- - -$(P -Derleyici hangi üye işlevi işleteceğine karar vermek için yukarıdaki ifadeyi şu iki üye işlev çağrısına dönüştürür: -) - ---- - x.opBinary!"işleç"(y); // x'in solda olduğu durumun tanımı - y.opBinaryRight!"işleç"(x); // y'nin sağda olduğu durumun tanımı ---- - -$(P -O işlev çağrılarından daha uygun olanı seçilir ve işletilir. -) - -$(P -Çoğu durumda $(C opBinaryRight)'ın tanımlanmasına gerek yoktur. Bu durum $(C in) işlecinde tam tersidir: $(C in) için çoğunlukla $(C opBinaryRight) tanımlanır. -) - -$(P -Aşağıdaki örneklerde üye işlevleri tanımlarken parametre ismini $(C sağdaki) olarak seçtim. Bununla parametrenin $(I işlecin sağındaki nesne) olduğunu vurguluyorum: -) - ---- - x $(I işleç) y ---- - -$(P -O ifade kullanıldığında üye işlevin $(C sağdaki) ismindeki parametresi $(C y)'yi temsil edecektir. -) - -$(P -İkili işleçlerin nasıl yüklendiklerini daha aşağıdaki başlıklarda açıklayacağım. -) - -$(H6 Dizi ve dilim işleçleri) - -$(P -Aşağıdaki işleçler bir türün topluluk olarak kullanılabilmesini sağlarlar. -) - -$(TABLE full, -$(HEAD3 Anlamı, İşlev İsmi, Örnek kullanım) -$(ROW3 eleman erişimi, opIndex, topluluk[i]) -$(ROW3 elemana atama, opIndexAssign, topluluk[i] = 7) -$(ROW3 eleman üzerinde tekli işlem, opIndexUnary, ++topluluk[i]) -$(ROW3 atamalı eleman işlemi, opIndexOpAssign, topluluk[i] *= 2) -$(ROW3 eleman adedi, opDollar, topluluk[$ - 1]) -$(ROW3 bütün elemanlara eriştiren dilim, opSlice, topluluk[]) -$(ROW3 bazı elemanlara eriştiren dilim, opSlice(size_t, size_t), topluluk[i..j]) -) - -$(P -Bu işleçleri aşağıda kendi başlıkları altında göreceğiz. -) - -$(P -Aşağıdaki tablodaki işleçler D'nin önceki sürümlerinden kalma olduklarından kullanımları $(I önerilmez). Onların yerine yukarıdaki tablodaki işleçler kullanılır. -) - -$(TABLE full, -$(HEAD3 Anlamı, İşlev İsmi, Örnek kullanım) -$(ROW3 bütün elemanlar üzerinde tekli işlem, opSliceUnary (önerilmez), ++topluluk[]) -$(ROW3 bazı elemanlar üzerinde tekli işlem, opSliceUnary (önerilmez), ++topluluk[i..j]) -$(ROW3 bütün elemanlara atama, opSliceAssign (önerilmez), topluluk[] = 42) -$(ROW3 bazı elemanlara atama, opSliceAssign (önerilmez), topluluk[i..j] = 7) -$(ROW3 bütün elemanlar üzerinde atamalı işlem, opSliceOpAssign (önerilmez), topluluk[] *= 2) -$(ROW3 bazı elemanlar üzerinde atamalı işlem, opSliceOpAssign (önerilmez), topluluk[i..j] *= 2) -) - -$(H6 Diğer işleçler) - -$(P -Yukarıdaki işleçlere ek olarak aşağıdaki işleçler de yüklenebilir: -) - -$(TABLE full, -$(HEAD3 Anlamı, İşlev İsmi, Örnek kullanım) -$(ROW3 işlev çağrısı, opCall, nesne(42)) -$(ROW3 tür dönüşümü işleci, opCast, to!int(nesne)) -$(ROW3 var olmayan üye işlev için sevk, opDispatch, nesne.varOlmayanİşlev()) -) - -$(P -Bu işleçleri daha aşağıda kendi başlıkları altında açıklayacağım. -) - -$(H5 Birden fazla işleci aynı zamanda tanımlamak) - -$(P -Örnekleri kısa tutmak için yukarıda yalnızca $(C ++), $(C +) ve $(C +=) işleçlerini kullandık. En az bir işlecinin yüklenmesi gereken bir türün başka işleçlerinin de yüklenmelerinin gerekeceği beklenebilir. Örneğin, aşağıdaki $(C Süre) türü için $(C --) ve $(C -=) işleçleri de tanımlanmaktadır: -) - ---- -struct Süre { - int dakika; - - ref Süre opUnary(string işleç)() - if (işleç == "++") { - $(HILITE ++)dakika; - return this; - } - - ref Süre opUnary(string işleç)() - if (işleç == "--") { - $(HILITE --)dakika; - return this; - } - - ref Süre opOpAssign(string işleç)(in int miktar) - if (işleç == "+") { - dakika $(HILITE +)= miktar; - return this; - } - - ref Süre opOpAssign(string işleç)(in int miktar) - if (işleç == "-") { - dakika $(HILITE -)= miktar; - return this; - } -} - -unittest { - auto süre = Süre(10); - - ++süre; - assert(süre.dakika == 11); - - --süre; - assert(süre.dakika == 10); - - süre += 5; - assert(süre.dakika == 15); - - süre -= 3; - assert(süre.dakika == 12); -} - -void main() { -} ---- - -$(P -Yukarıdaki işleç yüklemelerinde kod tekrarı bulunmaktadır. Benzer işlevlerin farklı olan karakterlerini sarı ile işaretledim. Bu kod tekrarı D'nin $(I dizgi katmaları) ($(C mixin)) olanağı ile giderilebilir. Daha ilerideki bölümlerde daha ayrıntılı olarak öğreneceğimiz $(C mixin) anahtar sözcüğünün işleç yüklemedeki yararını burada kısaca görelim. -) - -$(P -$(C mixin), kendisine verilen dizgiyi bulunduğu yere kaynak kod olarak yerleştirir. Örneğin, $(C işleç)'in $(STRING "++") olduğu durumda aşağıdaki iki satır eşdeğerdir: -) - ---- - mixin (işleç ~ "dakika;"); - ++dakika; // üsttekinin eşdeğeri ---- - -$(P -Dolayısıyla, bu olanaktan yararlanan aşağıdaki yapı yukarıdakinin eşdeğeridir: -) - ---- -struct Süre { - int dakika; - - ref Süre opUnary(string işleç)() - if ((işleç == "++") || (işleç == "--")) { - $(HILITE mixin) (işleç ~ "dakika;"); - return this; - } - - ref Süre opOpAssign(string işleç)(in int miktar) - if ((işleç == "+") || (işleç == "-")) { - $(HILITE mixin) ("dakika " ~ işleç ~ "= miktar;"); - return this; - } -} ---- - -$(P -$(C Süre) nesnelerinin belirli bir miktar ile çarpılmalarının veya bölünmelerinin de desteklenmesi istendiğinde yapılması gereken, o işleç karakterlerini de şablon kısıtlamalarına eklemektir: -) - ---- -struct Süre { -// ... - - ref Süre opOpAssign(string işleç)(in int miktar) - if ((işleç == "+") || (işleç == "-") || - $(HILITE (işleç == "*") || (işleç == "/"))) { - mixin ("dakika " ~ işleç ~ "= miktar;"); - return this; - } -} - -unittest { - auto süre = Süre(12); - - süre $(HILITE *=) 4; - assert(süre.dakika == 48); - - süre $(HILITE /=) 2; - assert(süre.dakika == 24); -} ---- - -$(P -Şablon kısıtlamaları bu durumda isteğe bağlıdır; özellikle gerekmedikçe yazılmayabilirler: -) - ---- - ref Süre opOpAssign(string işleç)(in int miktar) - /* kısıtlama yok */ { - mixin ("dakika " ~ işleç ~ "= miktar;"); - return this; - } ---- - -$(H5 $(IX dönüş türü, işleç) İşleçlerin dönüş türleri) - -$(P -İşleçleri kendi türleriniz için tanımlarken o işleçlerin hem davranışlarının hem de dönüş türlerinin temel türlerdeki gibi olmalarına dikkat edin. Bu, kodun anlaşılırlığı ve hataların önlenmesi açısından önemlidir. -) - -$(P -Temel türlerle kullanılan hiçbir işlecin dönüş türü $(C void) değildir. Bu, bazı işleçler için barizdir. Örneğin, iki $(C int) değerin $(C a + b) biçiminde toplanmalarının sonucunun yine $(C int) türünde bir değer olduğu açıktır: -) - ---- - int a = 1; - int b = 2; - int c = a + b; // c, işlecin değeri ile ilklenir ---- - -$(P -Başka bazı işleçlerin dönüş değerleri ve türleri ise bariz olmayabilir. Örneğin, $(C ++i) gibi bir işlecin bile değeri vardır: -) - ---- - int i = 1; - writeln(++i); // 2 yazar ---- - -$(P -$(C ++) işleci $(C i)'yi arttırmakla kalmaz, ayrıca $(C i)'nin yeni değerini de döndürür. Üstelik, $(C ++) işlecinin döndürdüğü değer $(C i)'nin yeni değeri değil, aslında $(C i)'nin $(I ta kendisidir). Bunu $(C ++i) işleminin sonucunun adresini yazdırarak gösterebiliriz: -) - ---- - int i = 1; - writeln("i'nin adresi : ", &i); - writeln("++i ifadesinin sonucunun adresi: ", &(++i)); ---- - -$(P -Çıktısı, iki adresin aynı olduklarını gösterir: -) - -$(SHELL -i'nin adresi : 7FFFAFECB2A8 -++i ifadesinin sonucunun adresi: 7FFFAFECB2A8 -) - -$(P -Tanımladığınız işleçlerin dönüş türlerinin aşağıdaki listedekilere uymalarına özen göstermenizi öneririm: -) - -$(UL - -$(LI $(B Nesneyi değiştiren işleçler) - -$(P -$(C opAssign) istisna olmak üzere, nesnede değişiklik yapan işleçlerin nesnenin kendisini döndürmeleri uygundur. Bunu yukarıdaki $(C GününSaati.opOpAssign!$(STRING "+")) ve $(C Süre.opUnary!$(STRING "++")) işleçlerinde gördük. -) - -$(P -Nesnenin kendisini döndürmek için şu adımlar uygulanır: -) - -$(OL -$(LI Dönüş türü olarak türün kendisi yazılır ve başına "referans" anlamına gelen $(C ref) anahtar sözcüğü eklenir.) - -$(LI İşlevden $(I bu nesneyi döndür) anlamında $(C return this) ile çıkılır.) -) - -$(P -Nesneyi değiştiren işleçler şunlardır: $(C opUnary!$(STRING "++")), $(C opUnary!$(STRING "--")), ve bütün $(C opOpAssign) yüklemeleri. -) - -) - -$(LI $(B Mantıksal işleçler) - -$(P -$(C ==) ve $(C !=) işleçlerini temsil eden $(C opEquals) $(C bool) döndürmelidir. $(C in) işleci ise normalde $(I içerilen nesneyi) döndürse de, istendiğinde o da basitçe $(C bool) döndürebilir. -) - -) - -$(LI $(B Sıralama işleçleri) - -$(P -Nesnelerin sıralanmalarında yararlanılan ve $(C <), $(C <=), $(C >), ve $(C >=) işleçlerinin davranışını belirleyen $(C opCmp) $(C int) döndürmelidir. -) - -) - -$(LI $(B Yeni nesne üreten işleçler) - -$(P -Bazı işleçlerin yeni nesne oluşturmaları ve o nesneyi döndürmeleri gerekir: -) - -$(UL - -$(LI Tekli işleçler $(C -), $(C +), ve $(C ~); ve ikili $(C ~) işleci.) - -$(LI Aritmetik işleçler $(C +), $(C -), $(C *), $(C /), $(C %), ve $(C ^^).) - -$(LI Bit işleçleri $(C &), $(C |), $(C ^), $(C <<), $(C >>), ve $(C >>>).) - -$(LI $(C opAssign), bir önceki bölümde de gösterildiği gibi, $(C return this) ile bu nesnenin bir kopyasını döndürür. - -$(P $(I Not: Bir eniyileştirme olarak bu işleç büyük yapılarda $(C const ref) de döndürebilir. Ben bu kitapta bu eniyileştirmeyi uygulamayacağım.) -) - -) - -) - -$(P -Yeni nesne üreten işleç örneği olarak $(C Süre) nesnelerini birbirleriyle toplamayı sağlayan $(C opBinary!$(STRING "+")) yüklemesine bakalım: -) - ---- -struct Süre { - int dakika; - - Süre opBinary(string işleç)(in Süre sağdaki) const - if (işleç == "+") { - return Süre(dakika + sağdaki.dakika); // yeni nesne - } -} ---- - -$(P -O tanımdan sonra programlarımızda artık $(C Süre) nesnelerini $(C +) işleciyle toplayabiliriz: -) - ---- - auto gitmeSüresi = Süre(10); - auto dönmeSüresi = Süre(11); - Süre toplamSüre; - // ... - toplamSüre = gitmeSüresi $(HILITE +) dönmeSüresi; ---- - -$(P -Derleyici o ifadeyi dönüştürür ve perde arkasında $(C gitmeSüresi) nesnesi üzerinde bir üye işlev olarak çağırır: -) - ---- - // üsttekinin eşdeğeridir: - toplamSüre = gitmeSüresi.opBinary!"+"(dönmeSüresi); ---- - -) - -$(LI $(C opDollar) - -$(P -Eleman adedi bilgisini döndürdüğünden en uygun tür $(C size_t)'dir. Buna rağmen, özellikle gerektiğinde $(C int) gibi başka tamsayı türlerini de döndürebilir. -) - -) - -$(LI $(B Serbest işleçler) - -$(P -Bazı işleçlerin dönüş türleri bütünüyle o yapının tasarımına bağlıdır: Tekli $(C *) işleci, $(C opCall), $(C opCast), $(C opDispatch), $(C opSlice), ve bütün $(C opIndex) işleçleri. -) - -) - -) - -$(H5 $(IX opEquals) Eşitlik karşılaştırmaları için $(C opEquals)) - -$(P -$(C ==) ve $(C !=) işleçlerinin davranışını belirler. -) - -$(P -$(C opEquals) işlecinin dönüş türü $(C bool)'dur. -) - -$(P -Yapılarda $(C opEquals) işlevinin parametresi $(C in) olarak işaretlenebilir. Ancak, çok büyük yapılarda hız kaybını önlemek için $(C opEquals), parametresi $(C auto ref const) olan bir şablon olarak da tanımlanabilir (boş parantezler bu tanımın bir şablon olmasını sağlarlar): -) - ---- - bool opEquals$(HILITE ())(auto ref const GününSaati sağdaki) const { - // ... - } ---- - -$(P -$(LINK2 /ders/d/deger_sol_sag.html, Sol Değerler ve Sağ Değerler bölümünde) gördüğümüz gibi, $(C auto ref) $(I sol değerlerin) referans olarak, $(I sağ değerlerin) ise kopyalanarak geçirilmelerini sağlar. Ancak, D'de $(I sağ değerler) kopyalanmak yerine taşındıklarından yukarıdaki işlev bildirimi hem $(I sol değerler) hem de $(I sağ değerler) için hızlı işler. -) - -$(P -Karışıklıklara önlemek için $(C opEquals) ve $(C opCmp) birbirleriyle tutarlı olmalıdır. $(C opEquals)'ın $(C true) döndürdüğü iki nesne için $(C opCmp) sıfır döndürmelidir. -) - -$(P -$(C opEquals) üye işlevi $(C ==) ve $(C !=) işleçlerinin ikisini de karşılar. Programcı işlevi $(C ==) işleci için tanımlar; derleyici de $(C !=) işleci için onun tersini kullanır: -) - ---- - x == y; - x.opEquals(y); // üsttekinin eşdeğeri - - x != y; - !(x.opEquals(y)); // üsttekinin eşdeğeri ---- - -$(P -Normalde $(C opEquals) işlevini yapılar için tanımlamaya gerek yoktur; derleyici bütün üyelerin eşitliklerini sırayla otomatik olarak karşılaştırır ve nesnelerin eşit olup olmadıklarına öyle karar verir. -) - -$(P -Bazen nesnelerin eşitliklerinin özel olarak belirlenmeleri gerekebilir. Örneğin bazı üyeler eşitlik karşılaştırması için önemsiz olabilirler veya bir türün nesnelerinin eşit kabul edilmeleri özel bir kurala bağlı olabilir, vs. -) - -$(P -Bunu göstermek için $(C GününSaati) yapısı için dakika bilgisini gözardı eden bir $(C opEquals) tanımlayalım: -) - ---- -struct GününSaati { - int saat; - int dakika; - - bool opEquals(in GününSaati sağdaki) const { - return saat == sağdaki.saat; - } -} -// ... - assert(GününSaati(20, 10) $(HILITE ==) GününSaati(20, 59)); ---- - -$(P -Eşitlik karşılaştırmasında yalnızca saat bilgisine bakıldığı için 20:10 ve 20:59 zamanları eşit çıkmaktadır. ($(I Not: Bunun karışıklık doğuracağı açıktır; gösterim amacıyla basit bir örnek olarak kabul edelim.)) -) - -$(H5 $(IX opCmp) Sıra karşılaştırmaları için $(C opCmp)) - -$(P -Sıralama işleçleri nesnelerin öncelik/sonralık ilişkilerini belirler. Sıralama ile ilgili olan $(C <), $(C <=), $(C >), ve $(C >=) işleçlerinin hepsi birden $(C opCmp) üye işlevi tarafından karşılanır. -) - -$(P -Yapılarda $(C opCmp) işlevinin parametresi $(C in) olarak işaretlenebilir. Ancak, $(C opEquals)'da olduğu gibi, çok büyük yapılarda hız kaybını önlemek için $(C opCmp), parametresi $(C auto ref const) olan bir şablon olarak da tanımlanabilir: -) - ---- - int opCmp$(HILITE ())(auto ref const GününSaati sağdaki) const { - // ... - } ---- - -$(P -Karışıklıkları önlemek için $(C opEquals) ve $(C opCmp) birbirleriyle tutarlı olmalıdır. $(C opEquals)'ın $(C true) döndürdüğü iki nesne için $(C opCmp) sıfır döndürmelidir. -) - -$(P -Bu dört işleçten birisinin şu şekilde kullanıldığını düşünelim: -) - ---- - if (x $(I işleç) y) { $(CODE_NOTE $(I işleç) <, <=, >, veya >= olabilir) ---- - -$(P -Derleyici o ifadeyi aşağıdaki mantıksal ifadeye dönüştürür ve onun sonucunu kullanır: -) - ---- - if (x.opCmp(y) $(I işleç) 0) { ---- - -$(P -Örnek olarak, -) - ---- - if (x $(HILITE <=) y) { ---- - -$(P -ifadesi şuna dönüştürülür: -) - ---- - if (x.opCmp(y) $(HILITE <=) 0) { ---- - -$(P -Kendi yazdığımız bu işlevin bu kurala göre doğru çalışabilmesi için işlevin şu değerleri döndürmesi gerekir: -) - -$(UL -$(LI Soldaki nesne önce olduğunda $(I eksi) bir değer.) -$(LI Sağdaki nesne önce olduğunda $(I artı) bir değer.) -$(LI İki nesne eşit olduklarında $(I sıfır) değeri.) -) - -$(P -Bu sonuçlardan anlaşılacağı gibi, $(C opCmp)'ın dönüş türü $(C bool) değil, $(C int)'tir. -) - -$(P -$(C GününSaati) nesnelerinin sıralama ilişkilerini öncelikle saat değerine, saatleri eşit olduğunda da dakika değerlerine bakacak şekilde şöyle belirleyebiliriz: -) - ---- - int opCmp(in GününSaati sağdaki) const { - /* Not: Buradaki çıkarma işlemleri sonucun alttan - * taşabileceği durumlarda hatalıdır. (Metin içindeki - * uyarıyı okuyunuz.) */ - - return (saat == sağdaki.saat - ? dakika - sağdaki.dakika - : saat - sağdaki.saat); - } ---- - -$(P -Saat değerleri aynı olduğunda dakika değerlerinin farkı, saatler farklı olduğunda da saatlerin farkı döndürülüyor. Bu tanım, zaman sıralamasında $(I soldaki) nesne önce olduğunda $(I eksi) bir değer, $(I sağdaki) nesne önce olduğunda $(I artı) bir değer döndürür. -) - -$(P -$(B Uyarı:) Yasal değerlerinin taşmaya neden olabildiği türlerde $(C opCmp) işlecinin çıkarma işlemi ile gerçekleştirilmesi hatalıdır. Örneğin, aşağıdaki $(C -2) değerine sahip olan nesne $(C int.max) değerine sahip olan nesneden daha $(I büyük) çıkmaktadır: -) - ---- -struct S { - int i; - - int opCmp(in S rhs) const { - return i - rhs.i; $(CODE_NOTE_WRONG HATA) - } -} - -void main() { - assert(S(-2) $(HILITE >) S(int.max)); $(CODE_NOTE_WRONG yanlış sonuç) -} ---- - -$(P -Öte yandan, çıkarma işleminin $(C GününSaati) yapısında kullanılmasında bir sakınca yoktur çünkü o türün hiçbir üyesinin yasal değerleri çıkarma işleminde taşmaya neden olmaz. -) - -$(P -$(IX cmp, std.algorithm) $(IX sıralama) Bütün dizgi türleri ve aralıklar dahil olmak üzere dilimleri karşılaştırırken $(C std.algorithm.cmp) işlevinden yararlanabilirsiniz. $(C cmp()) iki dilimin sıra değerlerine uygun olarak eksi bir değer, sıfır, veya artı bir değer döndürür. Bu değer doğrudan $(C opCmp) işlevinin dönüş değeri olarak kullanılabilir: -) - ---- -import std.algorithm; - -struct S { - string isim; - - int opCmp(in S rhs) const { - return $(HILITE cmp)(isim, rhs.isim); - } -} ---- - -$(P -$(C opCmp)'un tanımlanmış olması bu türün $(C std.algorithm.sort) gibi sıralama algoritmalarıyla kullanılabilmesini de sağlar. $(C sort) sıralamayı belirlemek için nesneleri karşılaştırırken perde arkasında hep $(C opCmp) işletilir. Aşağıdaki program 10 adet rasgele zaman değeri oluşturuyor ve onları $(C sort) ile sıralıyor: -) - ---- -import std.random; -import std.stdio; -import std.string; -import std.algorithm; - -struct GününSaati { - int saat; - int dakika; - - int opCmp(in GününSaati sağdaki) const { - return (saat == sağdaki.saat - ? dakika - sağdaki.dakika - : saat - sağdaki.saat); - } - - string toString() const { - return format("%02s:%02s", saat, dakika); - } -} - -void main() { - GününSaati[] zamanlar; - - foreach (i; 0 .. 10) { - zamanlar ~= GününSaati(uniform(0, 24), uniform(0, 60)); - } - - sort(zamanlar); - - writeln(zamanlar); -} ---- - -$(P -Beklendiği gibi, çıktıdaki saat değerleri zamana göre sıralanmışlardır: -) - -$(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 ()) İşlev gibi çağırmak için $(C opCall)) - -$(P -İşlev çağırırken kullanılan parantezler de işleçtir. Bu işlecin türün $(I ismi) ile kullanımını bir önceki bölümde $(C static opCall) olanağında görmüştük. O kullanım yapı nesnelerinin varsayılan olarak kurulmalarını sağlıyordu. -) - -$(P -$(C opCall) türün $(I nesnelerinin) de işlev gibi kullanılabilmelerini sağlar: -) - ---- - BirTür nesne; - nesne$(HILITE ()); ---- - -$(P -O kodda $(C nesne) bir işlev gibi çağrılmaktadır. Bu kullanım $(C static) $(I olmayan) $(C opCall) üye işlevleri tarafından belirlenir. -) - -$(P -Bunun bir örneği olarak bir doğrusal denklemin $(I x) değerlerine karşılık $(I y) değerlerini hesaplayan bir yapı düşünelim: -) - -$(MONO - $(I y) = $(I a)$(I x) + $(I b) -) - -$(P -O hesaptaki $(I y) değerlerini $(C opCall) işlevi içinde şöyle hesaplayabiliriz: -) - ---- -struct DoğrusalDenklem { - double a; - double b; - - double opCall(double x) const { - return a * x + b; - } -} ---- - -$(P -O işlev sayesinde yapının nesneleri işlev gibi kullanılabilir ve verilen $(I x) değerlerine karşılık $(I y) değerleri hesaplanabilir: -) - ---- - DoğrusalDenklem denklem = { 1.2, 3.4 }; - // nesne işlev gibi kullanılıyor: - double y = denklem(5.6); ---- - -$(P -$(I Not: $(C opCall) işlevi tanımlanmış olan yapıları $(C Tür(parametreler)) yazımıyla kuramayız çünkü o yazım da bir $(C opCall) çağrısı olarak kabul edilir. O yüzden, yukarıdaki nesnenin $(C { }) yazımıyla kurulması gerekmiştir. $(C DoğrusalDenklem(1.2, 3.4)) yazımı gerçekten gerektiğinde iki $(C double) parametre alan bir $(C static opCall) işlevi tanımlanmalıdır.) -) - -$(P -İlk satırda nesne kurulurken denklemin çarpanı olarak 1.2, ekleneni olarak da 3.4 değerinin kullanılacağı belirleniyor. Bunun sonucunda $(C denklem) nesnesi, $(I y = 1.2x + 3.4) denklemini ifade etmeye başlar. Ondan sonra nesneyi artık bir işlev gibi kullanarak $(I x) değerlerini parametre olarak gönderiyor ve dönüş değeri olarak $(I y) değerlerini elde ediyoruz. -) - -$(P -Bunun yararı, çarpan ve eklenen değerlerin baştan bir kere belirlenebilmesidir. Nesne o bilgiyi kendi içinde barındırır ve sonradan işlev gibi kullanıldığında yararlanır. -) - -$(P -Başka çarpan ve eklenen değerleri ile kurulan bir nesneyi bu sefer de bir döngü içinde kullanan bir örneğe bakalım: -) - ---- - DoğrusalDenklem denklem = { 0.01, 0.4 }; - - for (double x = 0.0; x <= 1.0; x += 0.125) { - writefln("%f: %f", x, denklem(x)); - } ---- - -$(P -O da $(I y = 0.01x + 0.4) denklemini $(I x)'in 0.0 ile 1.0 aralığındaki her 0.125 adımı için hesaplar. -) - -$(H5 $(IX opIndex) $(IX opIndexAssign) $(IX opIndexUnary) $(IX opIndexOpAssign)$(IX opDollar) Dizi erişim işleçleri) - -$(P -$(C opIndex), $(C opIndexAssign), $(C opIndexUnary), $(C opIndexOpAssign), ve $(C opDollar) nesneyi $(C nesne[konum]) biçiminde dizi gibi kullanma olanağı sağlarlar. -) - -$(P -Dizilerden farklı olarak, bu işleçler çok boyutlu indeksleri de desteklerler. Çok boyutlu indeksler köşeli parantezler içinde birden fazla konum değeri ile sağlanır. Örneğin, iki boyutlu dizi gibi işleyen bir tür $(C nesne[konum0, konum1]) söz dizimini destekleyebilir. Bu bölümde bu işleçleri yalnızca tek boyutlu olarak kullanacağız ve çok boyutlu örneklerini $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar bölümünde) göreceğiz. -) - -$(P -Aşağıdaki satırlardaki $(C kuyruk), biraz aşağıda tanıyacağımız $(C ÇiftUçluKuyruk) türünün bir nesnesi, $(C e) ise $(C int) türünde bir değişkendir. -) - -$(P -$(C opIndex) eleman erişimi amacıyla kullanılır. Köşeli parantezler içindeki konum değeri işlevin parametresi haline gelir: -) - ---- - e = kuyruk[3]; // 3 numaralı eleman - e = kuyruk.opIndex(3); // üsttekinin eşdeğeri ---- - -$(P -$(C opIndexAssign) atama amacıyla kullanılır. İlk parametresi atanan değer, sonraki parametresi de köşeli parantezler içindeki konum değeridir: -) - ---- - kuyruk[5] = 55; // 5 numaralı elemana 55 ata - kuyruk.opIndexAssign(55, 5); // üsttekinin eşdeğeri ---- - -$(P -$(C opIndexUnary), $(C opUnary)'nin benzeridir. Farkı, işlemin belirtilen konumdaki $(I eleman) üzerinde işleyecek olmasıdır: -) - ---- - ++kuyruk[4]; // 4 numaralı elemanı arttır - kuyruk.opIndexUnary!"++"(4); // üsttekinin eşdeğeri ---- - -$(P -$(C opIndexOpAssign), $(C opOpAssign)'ın benzeridir. Farkı, atamalı işlemin belirtilen konumdaki $(I eleman) üzerinde işleyecek olmasıdır: -) - ---- - kuyruk[6] += 66; // 6 numaralı elemana 66 ekle - kuyruk.opIndexOpAssign!"+"(66, 6); // üsttekinin eşdeğeri ---- - -$(P -$(C opDollar), dilimlerden tanınan $(C $) karakterini tanımlar. İçerilen eleman adedini döndürmek içindir: -) - ---- - e = kuyruk[$ - 1]; // sonuncu eleman - e = kuyruk[kuyruk.opDollar() - 1]; // üsttekinin eşdeğeri ---- - -$(H6 Eleman erişimi işleçleri örneği) - -$(P -$(I Çift uçlu kuyruk) $(ASIL double-ended queue, veya kısaca deque) bir dizi gibi işleyen ama başa eleman eklemenin de sona eleman eklemek kadar hızlı olduğu bir veri yapısıdır. (Dizilerde ise başa eleman eklemek bütün elemanların yeni bir diziye taşınmalarını gerektirdiğinden yavaş bir işlemdir.) -) - -$(P -Çift uçlu kuyruk veri yapısını gerçekleştirmenin bir yolu, perde arkasında iki adet diziden yararlanmak ama bunlardan birincisini ters sırada kullanmaktır. Başa eklenen eleman aslında birinci dizinin sonuna eklenir ve böylece o işlem de sona eklemek kadar hızlı olur. -) - -$(P -Bu veri yapısını gerçekleştiren aşağıdaki yapı bu bölümde gördüğümüz erişim işleçlerinin hepsini tanımlamaktadır: -) - ---- -$(CODE_NAME ÇiftUçluKuyruk)import std.stdio; -import std.string; -import std.conv; - -$(CODE_COMMENT_OUT)struct ÇiftUçluKuyruk -$(CODE_COMMENT_OUT){ -private: - - /* Elemanlar bu iki üyenin hayalî olarak uç uca - * gelmesinden oluşurlar. Ancak, 'baş' ters sırada - * kullanılır: İlk eleman baş[$-1]'dir, ikinci eleman - * baş[$-2]'dir, vs. - * - * baş[$-1], baş[$-2], ... baş[0], son[0], ... son[$-1] - */ - int[] baş; // baş taraftaki elemanlar - int[] son; // son taraftaki elemanlar - - /* Belirtilen konumdaki elemanın hangi dilimde olduğunu - * bulur ve o elemana bir referans döndürür. */ - ref inout(int) eleman(size_t konum) inout { - return (konum < baş.length - ? baş[$ - 1 - konum] - : son[konum - baş.length]); - } - -public: - - string toString() const { - string sonuç; - - foreach_reverse (eleman; baş) { - sonuç ~= format("%s ", to!string(eleman)); - } - - foreach (eleman; son) { - sonuç ~= format("%s ", to!string(eleman)); - } - - return sonuç; - } - - /* Not: Sonraki bölümlerde göreceğimiz olanaklardan - * yararlanıldığında toString() çok daha etkin olarak - * aşağıdaki gibi de yazılabilir: */ - version (none) { - void toString(void delegate(const(char)[]) hedef) const { - import std.format; - import std.range; - - formattedWrite( - hedef, "%(%s %)", chain(baş.retro, son)); - } - } - - /* Başa eleman ekler. */ - void başınaEkle(int değer) { - baş ~= değer; - } - - /* Sona eleman ekler. - * - * Örnek: kuyruk ~= değer - */ - ref ÇiftUçluKuyruk opOpAssign(string işleç)(int değer) - if (işleç == "~") { - son ~= değer; - return this; - } - - /* Belirtilen elemanı döndürür. - * - * Örnek: kuyruk[konum] - */ - inout(int) opIndex(size_t konum) inout { - return eleman(konum); - } - - /* Tekli işleci belirtilen elemana uygular. - * - * Örnek: ++kuyruk[konum] - */ - int opIndexUnary(string işleç)(size_t konum) { - mixin ("return " ~ işleç ~ "eleman(konum);"); - } - - /* Belirtilen elemana belirtilen değeri atar. - * - * Örnek: kuyruk[konum] = değer - */ - int opIndexAssign(int değer, size_t konum) { - return eleman(konum) = değer; - } - - /* Belirtilen değeri belirtilen işlemde kullanır ve sonucu - * belirtilen elemana atar. - * - * Örnek: kuyruk[konum] += değer - */ - int opIndexOpAssign(string işleç)(int değer, size_t konum) { - mixin ("return eleman(konum) " ~ işleç ~ "= değer;"); - } - - /* Uzunluk anlamına gelen $ karakterini tanımlar. - * - * Örnek: kuyruk[$ - 1] - */ - size_t opDollar() const { - return baş.length + son.length; - } -$(CODE_COMMENT_OUT)} - -void $(CODE_DONT_TEST)main() { - auto kuyruk = ÇiftUçluKuyruk(); - - foreach (i; 0 .. 10) { - if (i % 2) { - kuyruk.başınaEkle(i); - - } else { - kuyruk ~= i; - } - } - - writefln("Üç numaralı eleman: %s", - kuyruk[3]); // erişim - ++kuyruk[4]; // arttırım - kuyruk[5] = 55; // atama - kuyruk[6] += 66; // atamalı arttırım - - (kuyruk ~= 100) ~= 200; - - writeln(kuyruk); -} ---- - -$(P -$(C opOpAssign) işlevinin dönüş türünün de yukarıdaki ilkeler doğrultusunda $(C ref) olarak işaretlendiğine dikkat edin. $(C ~=) işleci bu sayede zincirleme olarak kullanılabilmektedir: -) - ---- - (kuyruk ~= 100) ~= 200; ---- - -$(P -O ifadelerin sonucunda 100 ve 200 değerleri aynı kuyruk nesnesine eklenmiş olurlar: -) - -$(SHELL -Üç numaralı eleman: 3 -9 7 5 3 2 55 68 4 6 8 100 200 -) - -$(H5 $(IX opSlice) Dilim işleçleri) - -$(P -$(C opSlice) nesneyi $(C []) işleciyle kullanma olanağı verir. -) - -$(P -$(IX opSliceUnary) $(IX opSliceAssign) $(IX opSliceOpAssign) Bu işlece ek olarak $(C opSliceUnary), $(C opSliceAssign), ve $(C opSliceOpAssign) işleçleri de vardır ama onların kullanımları önerilmez. -) - -$(P -D, birden fazla boyutta dilimlemeyi destekler. Çok boyutlu bir dizi dilimleme örneğini ilerideki $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar bölümünde) göreceğiz. O bölümde anlatılacak olan yöntemler tek boyutta da kullanılabilseler de, hem yukarıdaki indeksleme işleçlerine uymazlar hem de henüz görmediğimiz şablonlar olarak tanımlanırlar. Bu yüzden, bu bölümde $(C opSlice)'ın şablon olmayan ve yalnızca tek boyutta kullanılabilen bir kullanımını göreceğiz. ($(C opSlice)'ın bu kullanımı da önerilmez.) -) - -$(P -$(C opSlice)'ın iki farklı kullanımı vardır: -) - -$(UL - -$(LI $(I Bütün elemanlar) anlamına gelen $(C kuyruk[]) biçiminde köşeli parantezlerin içinin boş olduğu kullanım -) - -$(LI $(I Belirtilen aralıktaki elemanlar) anlamına gelen $(C kuyruk[baş .. son]) biçiminde köşeli parantezlerin içinde bir sayı aralığı belirtilen kullanım) - -) - -$(P -Hem elemanları bir araya getiren $(I topluluk) kavramıyla hem de o elemanlara erişim sağlayan $(I aralık) kavramıyla ilgili olduklarından bu işleçler diğerlerinden daha karmaşık gelebilirler. Topluluk ve aralık kavramlarını ilerideki bölümlerde daha ayrıntılı olarak göreceğiz. -) - -$(P -Şablon olmayan ve yalnızca tek boyutta işleyen $(C opSlice)'ın buradaki kullanımı, topluluktaki belirli bir aralıktaki elemanları temsil eden bir nesne döndürür. O aralıktaki elemanlara uygulanan işleçleri tanımlamak o nesnenin görevidir. Örneğin, aşağıdaki kullanım perde arkasında önce $(C opSlice) yüklemesinden yararlanarak bir aralık nesnesi üretir, sonra $(C opOpAssign!$(STRING "*")) işlecini o aralık nesnesi üzerinde işletir: -) - ---- - kuyruk[] *= 10; // bütün elemanları 10'la çarp - - // Üsttekinin eşdeğeri: - { - auto aralık = kuyruk.opSlice(); - aralık.opOpAssign!"*"(10); - } ---- - -$(P -Buna uygun olarak, $(C ÇiftUçluKuyruk) türünün $(C opSlice) işlevleri özel bir $(C Aralık) nesnesi döndürür: -) - ---- -import std.exception; - -struct ÇiftUçluKuyruk { -$(CODE_XREF ÇiftUçluKuyruk)// ... - - /* Bütün elemanları kapsayan bir aralık döndürür. - * ('Aralık' yapısı aşağıda tanımlanıyor.) - * - * Örnek: kuyruk[] - */ - inout(Aralık) $(HILITE opSlice)() inout { - return inout(Aralık)(baş[], son[]); - } - - /* Belirli elemanları kapsayan bir aralık döndürür. - * - * Örnek: kuyruk[ilkKonum .. sonKonum] - */ - inout(Aralık) $(HILITE opSlice)(size_t ilkKonum, size_t sonKonum) inout { - enforce(sonKonum <= opDollar()); - enforce(ilkKonum <= sonKonum); - - /* Belirtilen aralığın 'baş' ve 'son' dilimlerinin - * hangi bölgelerine karşılık geldiklerini hesaplamaya - * çalışıyoruz. */ - - if (ilkKonum < baş.length) { - if (sonKonum < baş.length) { - /* Aralık bütünüyle 'baş' içinde. */ - return inout(Aralık)( - baş[$ - sonKonum .. $ - ilkKonum], - []); - - } else { - /* Aralığın bir bölümü 'baş' içinde, geri - * kalanı 'son' içinde. */ - return inout(Aralık)( - baş[0 .. $ - ilkKonum], - son[0 .. sonKonum - baş.length]); - } - - } else { - /* Aralık bütünüyle 'son' içinde. */ - return inout(Aralık)( - [], - son[ilkKonum - baş.length .. sonKonum - baş.length]); - } - } - - /* Kuyruğun belirli bir aralığını temsil eder. opUnary, - * opAssign, ve opOpAssign işleçlerinin tanımları bu yapı - * içindedir. */ - struct $(HILITE Aralık) { - int[] başAralık; // 'baş' içindeki elemanlar - int[] sonAralık; // 'son' içindeki elemanlar - - /* Belirtilen tekli işleci elemanlara uygular. */ - Aralık opUnary(string işleç)() { - mixin (işleç ~ "başAralık[];"); - mixin (işleç ~ "sonAralık[];"); - return this; - } - - /* Belirtilen değeri elemanlara atar. */ - Aralık opAssign(int değer) { - başAralık[] = değer; - sonAralık[] = değer; - return this; - } - - /* Belirtilen değeri her eleman için belirtilen - * işlemde kullanır ve sonucu o elemana atar. */ - Aralık opOpAssign(string işleç)(int değer) { - mixin ("başAralık[] " ~ işleç ~ "= değer;"); - mixin ("sonAralık[] " ~ işleç ~ "= değer;"); - return this; - } - } -} - -void $(CODE_DONT_TEST)main() { - auto kuyruk = ÇiftUçluKuyruk(); - - foreach (i; 0 .. 10) { - if (i % 2) { - kuyruk.başınaEkle(i); - - } else { - kuyruk ~= i; - } - } - - writeln(kuyruk); - kuyruk$(HILITE []) *= 10; - kuyruk$(HILITE [3 .. 7]) = -1; - writeln(kuyruk); -} ---- - -$(P -Çıktısı: -) - -$(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 tür dönüşümü) Tür dönüşümü işleci $(C opCast)) - -$(P -$(C opCast) elle açıkça yapılan tür dönüşümünü belirler ve dönüştürülecek her tür için ayrı ayrı yüklenebilir. Daha önceki bölümlerden hatırlayacağınız gibi, açıkça tür dönüşümü hem $(C to) işlevi ile hem de $(C cast) işleciyle sağlanabilir. -) - -$(P -Bu işleç de şablon olarak tanımlanır ama kalıbı farklıdır: Hangi dönüşümün tanımlanmakta olduğu $(C (T : dönüştürülecek_tür)) söz dizimiyle belirtilir: -) - ---- - $(I dönüştürülecek_tür) opCast(T : $(I dönüştürülecek_tür))() { - // ... - } ---- - -$(P -Yine şimdilik bir kalıp olarak kabul etmenizi istediğim bu söz dizimini de daha sonraki $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) göreceğiz. -) - -$(P -$(C Süre)'nin saat ve dakikadan oluşan bir tür olduğunu kabul edelim. Bu türün nesnelerini $(C double) türüne dönüştüren işlev aşağıdaki gibi tanımlanabilir: -) - ---- -import std.stdio; -import std.conv; - -struct Süre { - int saat; - int dakika; - - double opCast(T : double)() const { - return saat + (to!double(dakika) / 60); - } -} - -void main() { - auto süre = Süre(2, 30); - double kesirli = to!double(süre); // cast(double)süre de olabilirdi - writeln(kesirli); -} ---- - -$(P -Yukarıdaki tür dönüşümü satırında derleyici üye işlevi perde arkasında şöyle çağırır: -) - ---- - double kesirli = süre.opCast!double(); ---- - -$(P -$(C double) türüne dönüştüren yukarıdaki işleç iki saat otuz dakikaya karşılık 2.5 değerini üretmektedir: -) - -$(SHELL -2.5 -) - -$(H5 $(IX opDispatch) $(IX sevk işleci) Sevk işleci $(C opDispatch)) - -$(P -Nesnenin var olmayan bir üyesine erişildiğinde çağrılacak olan üye işlevdir. Var olmayan üyelere yapılan bütün erişimler bu işlece $(I sevk edilir). -) - -$(P -Var olmayan üyenin ismi $(C opDispatch)'in bir şablon parametresi olarak belirir. -) - -$(P -Bu işleci çok basit olarak gösteren bir örnek: -) - ---- -import std.stdio; - -struct BirTür { - void opDispatch(string isim, T)(T parametre) { - writefln("BirTür.opDispatch - isim: %s, değer: %s", - isim, parametre); - } -} - -void main() { - BirTür nesne; - nesne.varOlmayanİşlev(42); - nesne.varOlmayanBaşkaİşlev(100); -} ---- - -$(P -Var olmayan üyelerine erişildiği halde derleme hatası alınmaz. Bütün o çağrılar $(C opDispatch) işlevinin çağrılmasını sağlarlar. Birinci şablon parametresi işlevin ismidir. Çağrılan noktada kullanılan parametreler de $(C opDispatch)'in parametreleri haline gelirler: -) - -$(SHELL -BirTür.opDispatch - isim: varOlmayanİşlev, değer: 42 -BirTür.opDispatch - isim: varOlmayanBaşkaİşlev, değer: 100 -) - -$(P -$(C isim) şablon parametre değeri normalde $(C opDispatch) içinde kullanılabilir ve işlemler onun değerine bağlı olarak seçilebilirler: -) - ---- - switch (isim) { - // ... - } ---- - -$(H5 $(IX in, işleç yükleme) $(IX !in) İçerme sorgusu için $(C opBinaryRight!"in")) - -$(P -Eşleme tablolarından tanıdığımız $(C in) işlecini nesneler için de tanımlama olanağı sağlar. -) - -$(P -Diğer işleçlerden farklı olarak, bu işleç için nesnenin sağda yazıldığı durum daha doğaldır: -) - ---- - if (zaman in öğleTatili) { ---- - -$(P -O yüzden bu işleç için daha çok $(C opBinaryRight!$(STRING "in")) yüklenir ve derleyici perde arkasında o üye işlevi çağırır: -) - ---- - // üsttekinin eşdeğeri - if (öğleTatili.opBinaryRight!"in"(zaman)) { ---- - -$(P -$(C !in) işleci ise bir değerin eşleme tablosunda $(I bulunmadığını) belirlemek için kullanılır: -) - ---- - if (a !in b) { ---- - -$(P -$(C !in) yüklenemez çünkü derleyici perde arkasında $(C in) işlecinin sonucunun tersini kullanır: -) - ---- - if (!(a in b)) { // üsttekinin eşdeğeri ---- - -$(H6 $(C in) işleci örneği) - -$(P -Bu örnek daha önce gördüğümüz $(C Süre) ve $(C GününSaati) yapılarına ek olarak bir de $(C ZamanAralığı) yapısı tanımlıyor. Bu yapı için tanımlanan $(C in) işleci belirli bir zamanın belirli bir aralıkta olup olmadığını bildirmek için kullanılacak. -) - -$(P -Bu örnekte de yalnızca gerektiği kadar üye işlev kullandım. -) - -$(P -$(C GününSaati) nesnesinin $(C for) döngüsünde nasıl temel türler kadar rahat kullanıldığına özellikle dikkat edin. O döngü işleç yüklemenin yararını gösteriyor. -) - ---- -import std.stdio; -import std.string; - -struct Süre { - int dakika; -} - -struct GününSaati { - int saat; - int dakika; - - ref GününSaati opOpAssign(string işleç)(in Süre süre) - if (işleç == "+") { - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - - return this; - } - - int opCmp(in GününSaati sağdaki) const { - return (saat == sağdaki.saat - ? dakika - sağdaki.dakika - : saat - sağdaki.saat); - } - - string toString() const { - return format("%02s:%02s", saat, dakika); - } -} - -struct ZamanAralığı { - GününSaati baş; - GününSaati son; // son aralığın dışında kabul edilir - - bool opBinaryRight(string işleç)(GününSaati zaman) const - if (işleç == "in") { - return (zaman >= baş) && (zaman < son); - } -} - -void main() { - auto öğleTatili = ZamanAralığı(GününSaati(12, 00), - GününSaati(13, 00)); - - for (auto zaman = GününSaati(11, 30); - zaman < GününSaati(13, 30); - zaman += Süre(15)) { - - if (zaman in öğleTatili) { - writeln(zaman, " öğle tatilinde"); - - } else { - writeln(zaman, " öğle tatili dışında"); - } - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL -11:30 öğle tatili dışında -11:45 öğle tatili dışında -12:00 öğle tatilinde -12:15 öğle tatilinde -12:30 öğle tatilinde -12:45 öğle tatilinde -13:00 öğle tatili dışında -13:15 öğle tatili dışında -) - -$(PROBLEM_TEK - -$(P -Payını ve paydasını $(C long) türünde iki üye olarak tutan bir kesirli sayı türü tanımlayın. Böyle bir yapının bir yararı, $(C float), $(C double), ve $(C real)'deki değer kayıplarının bulunmamasıdır. Örneğin, 1.0/3 gibi bir $(C double) değerin 3 ile çarpılmasının sonucu 1.0 olmadığı halde 1/3'ü temsil eden $(C Kesirli) bir nesnenin 3 ile çarpılmasının sonucu tam olarak 1'dir: - -) - ---- -struct Kesir { - long pay; - long payda; - - /* Kurucu işlev kolaylık olsun diye paydanın - * belirtilmesini gerektirmiyor ve 1 varsayıyor. */ - this(long pay, long payda = 1) { - enforce(payda != 0, "Payda sıfır olamaz"); - - this.pay = pay; - this.payda = payda; - - /* Paydanın eksi değer almasını başından önlemek daha - * sonraki işlemleri basitleştirecek. */ - if (this.payda < 0) { - this.pay = -this.pay; - this.payda = -this.payda; - } - } - - /* ... işleçleri siz tanımlayın ... */ -} ---- - -$(P -Bu yapı için işleçler tanımlayarak olabildiğince temel türler gibi işlemesini sağlayın. Yapının tanımı tamamlandığında aşağıdaki birim testi bloğu hatasız işletilebilsin. O blokta şu işlemler bulunuyor: -) - -$(UL - -$(LI Payda sıfır olduğunda hata atılıyor. (Bu, yukarıdaki kurucudaki $(C enforce) ile zaten sağlanıyor.)) - -$(LI Değerin eksi işaretlisini üretmek: Örneğin, 1/3 değerinin eksilisi olarak -1/3 değeri elde ediliyor.) - -$(LI $(C ++) ve $(C --) ile değer bir arttırılıyor veya azaltılıyor.) - -$(LI Dört işlem destekleniyor: Hem $(C +=), $(C -=), $(C *=), ve $(C /=) ile tek nesnenin değeri değiştirilebiliyor hem de iki nesne $(C +), $(C -), $(C *), ve $(C /) aritmetik işlemlerinde kullanılabiliyor. (Kurucuda olduğu gibi, sıfıra bölme işlemi de denetlenmeli ve önlenmelidir.) - -$(P -Hatırlatma olarak, a/b ve c/d gibi iki kesirli arasındaki aritmetik işlem formülleri şöyledir: -) - -$(UL -$(LI Toplama: a/b + c/d = (a*d + c*b)/(b*d)) -$(LI Çıkarma: a/b - c/d = (a*d - c*b)/(b*d)) -$(LI Çarpma: a/b * c/d = (a*c)/(b*d)) -$(LI Bölme: (a/b) / (c/d) = (a*d)/(b*c)) -) - -) - -$(LI Nesnenin değeri $(C double)'a dönüştürülebiliyor.) - -$(LI Sıralama ve eşitlik karşılaştırmaları pay ve paydaların tam değerlerine göre değil, o üyelerin ifade ettikleri değerlere göre uygulanıyorlar. Örneğin 1/3 ve 20/60 kesirli değerleri eşit kabul ediliyorlar. -) - -) - ---- -unittest { - /* Payda 0 olduğunda hata atılmalı. */ - assertThrown(Kesir(42, 0)); - - /* 1/3 değeriyle başlayacağız. */ - auto a = Kesir(1, 3); - - /* -1/3 */ - assert(-a == Kesir(-1, 3)); - - /* 1/3 + 1 == 4/3 */ - ++a; - assert(a == Kesir(4, 3)); - - /* 4/3 - 1 == 1/3 */ - --a; - assert(a == Kesir(1, 3)); - - /* 1/3 + 2/3 == 3/3 */ - a += Kesir(2, 3); - assert(a == Kesir(1)); - - /* 3/3 - 2/3 == 1/3 */ - a -= Kesir(2, 3); - assert(a == Kesir(1, 3)); - - /* 1/3 * 8 == 8/3 */ - a *= Kesir(8); - assert(a == Kesir(8, 3)); - - /* 8/3 / 16/9 == 3/2 */ - a /= Kesir(16, 9); - assert(a == Kesir(3, 2)); - - /* double türünde bir değere dönüştürülebilmeli. - * - * Hatırlarsanız, double türü her değeri tam olarak ifade - * edemez. 1.5 değeri tam olarak ifade edilebildiği için - * bu testi bu noktada uyguladım. */ - assert(to!double(a) == 1.5); - - /* 1.5 + 2.5 == 4 */ - assert(a + Kesir(5, 2) == Kesir(4, 1)); - - /* 1.5 - 0.75 == 0.75 */ - assert(a - Kesir(3, 4) == Kesir(3, 4)); - - /* 1.5 * 10 == 15 */ - assert(a * Kesir(10) == Kesir(15, 1)); - - /* 1.5 / 4 == 3/8 */ - assert(a / Kesir(4) == Kesir(3, 8)); - - /* Sıfırla bölmek hata atmalı. */ - assertThrown(Kesir(42, 1) / Kesir(0)); - - /* Payı az olan öncedir. */ - assert(Kesir(3, 5) < Kesir(4, 5)); - - /* Paydası büyük olan öncedir. */ - assert(Kesir(3, 9) < Kesir(3, 8)); - assert(Kesir(1, 1_000) > Kesir(1, 10_000)); - - /* Değeri küçük olan öncedir. */ - assert(Kesir(10, 100) < Kesir(1, 2)); - - /* Eksi değer öncedir. */ - assert(Kesir(-1, 2) < Kesir(0)); - assert(Kesir(1, -2) < Kesir(0)); - - /* Aynı değerler hem <= hem de >= olmalı. */ - assert(Kesir(-1, -2) <= Kesir(1, 2)); - assert(Kesir(1, 2) <= Kesir(-1, -2)); - assert(Kesir(3, 7) <= Kesir(9, 21)); - assert(Kesir(3, 7) >= Kesir(9, 21)); - - /* Değerleri aynı olanlar eşit olmalı. */ - assert(Kesir(1, 3) == Kesir(20, 60)); - - /* Karışık işaretler aynı sonucu üretmeli. */ - assert(Kesir(-1, 2) == Kesir(1, -2)); - assert(Kesir(1, 2) == Kesir(-1, -2)); -} ---- - -) - -Macros: - SUBTITLE=İşleç Yükleme - - DESCRIPTION=D'de türlerin kullanışlılığını arttıran olanaklarından işleç yükleme [operator overloading] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işleç yükleme overloading - -SOZLER= -$(atama) -$(eniyilestirme) -$(islec) -$(kisitlama) -$(kurma) -$(sag_deger) -$(sol_deger) -$(sablon) -$(yukleme) diff --git a/ddili/src/ders/d/islev_parametreleri.cozum.d b/ddili/src/ders/d/islev_parametreleri.cozum.d deleted file mode 100644 index 3da2a29..0000000 --- a/ddili/src/ders/d/islev_parametreleri.cozum.d +++ /dev/null @@ -1,39 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU İşlev Parametreleri) - -$(P -Bu işlevin parametreleri kopyalanan türden olduklarından işlev içindeki değiş tokuş işlemi yalnızca bu kopyaları değiş tokuş eder. -) - -$(P -Parametrelerin referans olarak gönderilmeleri gerekir: -) - ---- -void değişTokuş($(HILITE ref) int birinci, $(HILITE ref) int ikinci) { - const int geçici = birinci; - birinci = ikinci; - ikinci = geçici; -} ---- - -$(P -Artık $(C main) içindeki değişkenler etkilenirler: -) - -$(SHELL -2 1 -) - -$(P -Programdaki hatayla ilgisi olmasa da, bir kere ilklendikten sonra değeri değiştirilmeyeceğinden $(C geçici) de $(C const) olarak belirlenmiştir. -) - - -Macros: - SUBTITLE=İşlev Parametreleri ve Tembel Değerlendirmeler - - DESCRIPTION=D dilinde işlev (fonksiyon) [function] parametrelerinin çeşitleri ile ilgili problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function parametre in out ref lazy const immutable problem çözüm diff --git a/ddili/src/ders/d/islev_parametreleri.d b/ddili/src/ders/d/islev_parametreleri.d deleted file mode 100644 index 88cdee5..0000000 --- a/ddili/src/ders/d/islev_parametreleri.d +++ /dev/null @@ -1,1021 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX parametre) $(IX işlev parametresi) İşlev Parametreleri) - -$(P -Bu bölümde parametrelerin işlevlere gönderilmeleri konusundaki ayrıntıları göreceğiz ve D'deki parametre çeşitlerini tanıyacağız. -) - -$(P -Aslında bu bölümün konularının bazılarıyla önceki bölümlerde karşılaşmıştık. Örneğin, $(LINK2 /ders/d/foreach_dongusu.html, foreach Döngüsü bölümünde) $(C ref) anahtar sözcüğünün elemanların kopyalarını değil, $(I kendilerini) kullandırdığını görmüştük. -) - -$(P -Ek olarak, hem $(C const) ve $(C immutable) belirteçlerinin parametrelerle kullanımını hem de değer türleriyle referans türleri arasındaki farkları daha önceki bölümlerde görmüştük. -) - -$(P -Önceki programlarda işlevlerin nasıl parametrelerini kullanarak sonuçlar ürettiklerini gördük. Örneğin, hiçbir yan etkisi olmayan ve işi yalnızca değer üretmek olan bir işlev şöyle yazılabiliyordu: -) - ---- -double seneSonuNotu(double vizeNotu, double finalNotu) { - return vizeNotu * 0.4 + finalNotu * 0.6; -} ---- - -$(P -O işlevde vize notu ağırlığının %40, final notununkinin de %60 olarak hesaplandığını görebiliyoruz. O işlevi örneğin şu şekilde çağırabiliriz: -) - ---- - int vizeOrtalaması = 76; - int finalNotu = 80; - - writefln("Sene sonu notu: %2.0f", - seneSonuNotu(vizeOrtalaması, finalNotu)); ---- - -$(H5 $(IX kopyalanan parametre) $(IX parametre, kopyalanarak) Parametre her zaman kopyalanır) - -$(P -Yukarıdaki kodun $(C vizeOrtalaması) ve $(C finalNotu) değişkenlerini $(I kullandığını) söylediğimizde aslında temelde bir hataya düşmüş oluruz çünkü aslında işlev tarafından kullanılanlar değişkenlerin kendileri değil, $(I kopyalarıdır). -) - -$(P -Bu ayrım önemlidir çünkü parametrede yapılan değişiklik ancak kopyayı etkiler. Bunu yan etki üretmeye $(I çalışan) aşağıdaki işlevde görebiliriz. Bu işlev bir oyun karakterinin enerjisini azaltmak için yazılmış olsun: -) - ---- -void enerjisiniAzalt(double enerji) { - enerji /= 4; -} ---- - -$(P -O işlevi denemek için yazılmış olan şu programa bakalım: -) - ---- -import std.stdio; - -void enerjisiniAzalt(double enerji) { - enerji /= 4; -} - -void main() { - double enerji = 100; - - enerjisiniAzalt(enerji); - writeln("Yeni enerji: ", enerji); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Yeni enerji: 100 $(SHELL_NOTE_WRONG Değişmedi) -) - -$(P -$(C enerjisiniAzalt) işlevi parametresinin değerini dörtte birine düşürdüğü halde $(C main) içindeki $(C enerji) isimli değişkenin değeri aynı kalmaktadır. Bunun nedeni, $(C main) içindeki $(C enerji) ile $(C enerjisiniAzalt) işlevinin parametresi olan $(C enerji)'nin farklı değişkenler olmalarıdır. Parametre, $(C main) içindeki değişkenin $(I kopyasıdır). -) - -$(P -Bu olayı biraz daha yakından incelemek için programa bazı çıktı ifadeleri yerleştirebiliriz: -) - ---- -import std.stdio; - -void enerjisiniAzalt(double enerji) { - writeln("İşleve girildiğinde : ", enerji); - enerji /= 4; - writeln("İşlevden çıkılırken : ", enerji); -} - -void main() { - double enerji = 100; - - writeln("İşlevi çağırmadan önce : ", enerji); - enerjisiniAzalt(enerji); - writeln("İşlevden dönüldükten sonra: ", enerji); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -İşlevi çağırmadan önce : 100 -İşleve girildiğinde : 100 -İşlevden çıkılırken : 25 $(SHELL_NOTE parametre değişir,) -İşlevden dönüldükten sonra: 100 $(SHELL_NOTE asıl enerji değişmez) -) - -$(P -Çıktıdan anlaşıldığı gibi, isimleri aynı olsa da $(C main) içindeki $(C enerji) ile $(C enerjisiniAzalt) içindeki $(C enerji) farklı değişkenlerdir. İşleve $(C main) içindeki değişkenin değeri $(I kopyalanır) ve değişiklik bu kopyayı etkiler. -) - -$(P -Bu, ilerideki bölümlerde göreceğimiz yapı nesnelerinde de böyledir: Yapı nesneleri de işlevlere kopyalanarak gönderilirler. -) - -$(H5 $(IX referans parametre) $(IX parametre, referans olarak) Referans türlerinin eriştirdiği değişkenler kopyalanmazlar) - -$(P -Dilim, eşleme tablosu, ve sınıf gibi referans türleri de işlevlere kopyalanırlar. Ancak, bu türlerin erişim sağladığı değişkenler (dilim ve eşleme tablosu elemanları ve sınıf nesneleri) kopyalanmazlar. Bu çeşit değişkenler işlevlere $(I referans) olarak geçirilirler. Parametre, asıl nesneye eriştiren yeni bir reeferanstır ve dolayısıyla, parametrede yapılan değişiklik asıl nesneyi değiştirir. -) - -$(P -Dizgiler de dizi olduklarından bu durum onlar için de geçerlidir. Parametresinde değişiklik yapan şu işleve bakalım: -) - ---- -import std.stdio; - -void başHarfiniNoktaYap(dchar[] dizgi) { - dizgi[0] = '.'; -} - -void main() { - dchar[] dizgi = "abc"d.dup; - başHarfiniNoktaYap(dizgi); - writeln(dizgi); -} ---- - -$(P -Parametrede yapılan değişiklik $(C main) içindeki asıl nesneyi değiştirmiştir: -) - -$(SHELL -.bc -) - -$(P -Buna rağmen, dilim ve eşleme tablosu değişkenlerinin kendileri yine de kopyalanırlar. Bu durum, parametre özellikle $(C ref) belirteci ile tanımlanmamışsa şaşırtıcı sonuçlar doğurabilir. -) - -$(H6 Dilimlerin şaşırtıcı olabilen referans davranışları) - -$(P -$(LINK2 /ders/d/dilimler.html, Başka Dizi Olanakları bölümünde) belirtildiği gibi, $(I bir dilime eleman eklenmesi paylaşımı sonlandırabilir). Paylaşım sonlanmışsa yukarıdaki $(C dizgi) gibi bir parametre artık asıl elemanlara erişim sağlamıyor demektir. -) - ---- -import std.stdio; - -void sıfırEkle(int[] dilim) { - dilim $(HILITE ~= 0); - writefln("sıfırEkle() içindeyken: %s", dilim); -} - -void main() { - auto dilim = [ 1, 2 ]; - sıfırEkle(dilim); - writefln("sıfırEkle()'den sonra : %s", dilim); -} ---- - -$(P -Yeni eleman yalnızca parametreye eklenir, çağıran taraftaki dilime değil: -) - -$(SHELL -sıfırEkle() içindeyken: [1, 2, 0] -sıfırEkle()'den sonra : [1, 2] $(SHELL_NOTE_WRONG 0 elemanı yok) -) - -$(P -Yeni elemanların gerçekten de asıl dilime eklenmesi istendiğinde parametrenin $(C ref) olarak geçirilmesi gerekir: -) - ---- -void sıfırEkle($(HILITE ref) int[] dilim) { - // ... -} ---- - -$(P -$(C ref) belirtecini biraz aşağıda göreceğiz. -) - -$(H6 Eşleme tablolarının şaşırtıcı olabilen referans davranışları) - -$(P -Eşleme tablosu çeşidinden olan parametreler de şaşırtıcı sonuçlar doğurabilirler. Bunun nedeni, eşleme tablolarının yaşamlarına boş olarak değil, $(C null) olarak başlamalarıdır. -) - -$(P -$(C null), bu anlamda $(I ilklenmemiş eşleme tablosu) anlamına gelir. Eşleme tabloları ilk elemanları eklendiğinde otomatik olarak ilklenirler. Bunun bir etkisi olarak, eğer bir işlev $(C null) olan bir eşleme tablosuna bir eleman eklerse o eleman çağıran tarafta görülemez çünkü parametre ilklenmiştir ama çağıran taraftaki değişken yine $(C null)'dır: -) - ---- -import std.stdio; - -void elemanEkle(int[string] tablo) { - tablo$(HILITE ["kırmızı"] = 100); - writefln("elemanEkle() içindeyken: %s", tablo); -} - -void main() { - int[string] tablo; // ← null tablo - elemanEkle(tablo); - writefln("elemanEkle()'den sonra : %s", tablo); -} ---- - -$(P -Eklenen eleman çağıran taraftaki tabloya eklenmemiştir: -) - -$(SHELL -elemanEkle() içindeyken: ["kırmızı":100] -elemanEkle()'den sonra : [] $(SHELL_NOTE_WRONG Elemanı yok) -) - -$(P -Öte yandan, işleve gönderilen tablo $(C null) değilse, eklenen eleman o tabloda da görülür: -) - ---- - int[string] tablo; - tablo["mavi"] = 10; // ← Bu sefer null değil - elemanEkle(tablo); ---- - -$(P -Bu sefer, eklenen eleman çağıran taraftaki tabloda da görülür: -) - -$(SHELL -elemanEkle() içindeyken: ["mavi":10, "kırmızı":100] -elemanEkle()'den sonra : ["mavi":10, $(HILITE "kırmızı":100)] -) - -$(P -Bu yüzden, eşleme tablolarını da $(C ref) parametreler olarak geçirmek daha uygun olabilir. -) - -$(H5 Parametre çeşitleri) - -$(P -Parametrelerin işlevlere geçirilmeleri normalde yukarıdaki iki temel kurala uyar: -) - -$(UL - -$(LI Değer türleri kopyalanırlar. Asıl değişken ve parametre birbirlerinden bağımsızdır.) - -$(LI Referans türleri de kopyalanırlar ama hem asıl değişken hem de parametre aynı nesneye erişim sağlarlar.) - -) - -$(P -Bunlar bir belirteç kullanılmadığı zaman varsayılan kurallardır. Bu genel kurallar aşağıdaki anahtar sözcükler yardımıyla değiştirilebilir. -) - -$(H6 $(IX in, parametre) $(C in)) - -$(P -İşlevlerin değer veya yan etki üretebildiklerini görmüştük. $(C in) anahtar sözcüğü parametrenin işlev tarafından yalnızca giriş bilgisi olarak kullanıldığını belirtir. Bu tür parametreler değiştirilemezler. İngilizce'de "içeriye" anlamına gelen "in" parametrenin amacını daha açık ifade eder: -) - ---- -import std.stdio; - -double ağırlıklıToplam($(HILITE in) double şimdikiToplam, - $(HILITE in) double ağırlık, - $(HILITE in) double eklenecekDeğer) { - return şimdikiToplam + (ağırlık * eklenecekDeğer); -} - -void main() { - writeln(ağırlıklıToplam(1.23, 4.56, 7.89)); -} ---- - -$(P -$(C in) parametreler değiştirilemezler: -) - ---- -void deneme(in int değer) { - değer = 1; $(DERLEME_HATASI) -} ---- - -$(H6 $(IX out, parametre) $(C out)) - -$(P -İşlevin ürettiği bilginin işlevden $(C return) anahtar sözcüğü ile döndürüldüğünü görmüştük. İşlevlerden tek değer döndürülebiliyor olması bazen kısıtlayıcı olabilir çünkü bazı işlevlerin birden fazla sonuç üretmesi istenir. ($(I Not: Aslında dönüş türü $(C Tuple) veya $(C struct) olduğunda işlevler birden fazla değer döndürebilirler. Bu olanakları ilerideki bölümlerde göreceğiz.)) -) - -$(P -"Dışarıya" anlamına gelen $(C out) belirteci, işlevlerin parametreleri yoluyla da sonuç üretmelerini sağlar. İşlev bu çeşit parametrelerin değerlerini atama yoluyla değiştirdiğinde, o değerler işlevi çağıran ortamda da sonuç olarak görülürler. Bilgi bir anlamda işlevden $(I dışarıya) gönderilmektedir. -) - -$(P -Örnek olarak iki sayıyı bölen ve hem bölümü hem de kalanı üreten bir işleve bakalım. İşlevin dönüş değerini bölmenin sonucu için kullanırsak bölmeden kalanı da bir $(C out) parametre olarak döndürebiliriz: -) - ---- -import std.stdio; - -int kalanlıBöl(in int bölünen, in int bölen, $(HILITE out) int kalan) { - $(HILITE kalan = bölünen % bölen); - return bölünen / bölen; -} - -void main() { - int kalan; - int bölüm = kalanlıBöl(7, 3, kalan); - - writeln("bölüm: ", bölüm, ", kalan: ", kalan); -} ---- - -$(P -İşlevin $(C kalan) isimli parametresinin değiştirilmesi $(C main) içindeki $(C kalan)'ın değişmesine neden olur (isimlerinin aynı olması gerekmez): -) - -$(SHELL -bölüm: 2, kalan: 1 -) - -$(P -Değerleri çağıran tarafta ne olursa olsun, işleve girildiğinde $(C out) parametreler öncelikle türlerinin ilk değerine dönüşürler: -) - ---- -import std.stdio; - -void deneme(out int parametre) { - writeln("İşleve girildiğinde : ", parametre); -} - -void main() { - int değer = 100; - - writeln("İşlev çağrılmadan önce: ", değer); - deneme(değer); - writeln("İşlevden dönüldüğünde : ", değer); -} ---- - -$(P -O işlevde parametreye hiçbir değer atanmıyor bile olsa işleve girildiğinde parametrenin değeri $(C int)'in ilk değeri olmakta ve bu $(C main) içindeki değeri de etkilemektedir: -) - -$(SHELL -İşlev çağrılmadan önce: 100 -İşleve girildiğinde : 0 $(SHELL_NOTE int.init değerinde) -İşlevden dönüldüğünde : 0 -) - -$(P -Görüldüğü gibi, $(C out) parametreler dışarıdan bilgi alamazlar, yalnızca dışarıya bilgi gönderebilirler. -) - -$(P -$(C out) parametre yerine dönüş türü olarak $(C Tuple) veya $(C struct) kullanmak daha iyidir. Bunları ilerideki bölümlerde göreceğiz. -) - -$(H6 $(IX const, parametre) $(C const)) - -$(P -Daha önce de gördüğümüz gibi, bu belirteç parametrenin işlev içinde değiştirilmeyeceği garantisini verir. Bu sayede, işlevi çağıranlar hem parametrede değişiklik yapılmadığını bilmiş olurlar, hem de işlev $(C const) veya $(C immutable) olan değişkenlerle de çağrılabilir: -) - ---- -import std.stdio; - -dchar sonHarfi($(HILITE const) dchar[] dizgi) { - return dizgi[$ - 1]; -} - -void main() { - writeln(sonHarfi("sabit")); -} ---- - -$(H6 $(IX immutable, parametre) $(C immutable)) - -$(P -Daha önce de gördüğümüz gibi, bu belirteç parametrenin programın çalışması süresince değişmemesini şart koşar. Bu konuda ısrarlı olduğundan aşağıdaki işlevi ancak elemanları $(C immutable) olan dizgilerle çağırabiliriz (örneğin, dizgi hazır değerleriyle): -) - ---- -import std.stdio; - -dchar[] karıştır($(HILITE immutable) dchar[] birinci, - $(HILITE immutable) dchar[] ikinci) { - dchar[] sonuç; - int i; - - for (i = 0; (i < birinci.length) && (i < ikinci.length); ++i) { - sonuç ~= birinci[i]; - sonuç ~= ikinci[i]; - } - - sonuç ~= birinci[i..$]; - sonuç ~= ikinci[i..$]; - - return sonuç; -} - -void main() { - writeln(karıştır("MERHABA", "dünya")); -} ---- - -$(P -Kısıtlayıcı bir belirteç olduğundan, $(C immutable)'ı ancak değişmezliğin gerçekten gerekli olduğu durumlarda kullanmanızı öneririm. Öte yandan, $(C const) parametreler genelde daha kullanışlıdır çünkü bunlar $(C const), $(C immutable), ve $(I değişebilen) değişkenlerin hepsini kabul ederler. -) - -$(H6 $(IX ref, parametre) $(C ref)) - -$(P -İşleve normalde kopyalanarak geçirilecek olan bir değişkenin referans olarak geçirilmesini sağlar. -) - -$(P -Yukarıda parametresi normalde kopyalandığı için istediğimiz gibi çalışmayan $(C enerjisiniAzalt) işlevinin $(C main) içindeki asıl değişkeni değiştirebilmesi için parametresini referans olarak alması gerekir: -) - ---- -import std.stdio; - -void enerjisiniAzalt($(HILITE ref) double enerji) { - enerji /= 4; -} - -void main() { - double enerji = 100; - - enerjisiniAzalt(enerji); - writeln("Yeni enerji: ", enerji); -} ---- - -$(P -İşlev parametresinde yapılan değişiklik artık $(C main) içindeki $(C enerji)'nin değerini değiştirir: -) - -$(SHELL -Yeni enerji: 25 -) - -$(P -Görüldüğü gibi, $(C ref) parametreler işlev içinde hem kullanılmak üzere giriş bilgisidirler, hem de sonuç üretmek üzere çıkış bilgisidirler. $(C ref) parametreler asıl değişkenlerin takma isimleri olarak da düşünülebilirler. Yukarıdaki işlev parametresi olan $(C enerji), $(C main) içindeki $(C enerji)'nin bir takma ismi gibi işlem görür. $(C ref) yoluyla yapılan değişiklik asıl değişkeni değiştirir. -) - -$(P -$(C ref) parametreler işlevlerin yan etki üreten türden işlevler olmalarına neden olurlar: Dikkat ederseniz, $(C enerjisiniAzalt) işlevi değer üretmemekte, parametresinde bir değişiklik yapmaktadır. -) - -$(P -$(I Fonksiyonel programlama) denen programlama yönteminde yan etkilerin özellikle azaltılmasına çalışılır. Hatta, bazı programlama dillerinde yan etkilere hiç izin verilmez. Değer üreten işlevlerin yan etkisi olan işlevlerden programcılık açısından daha üstün oldukları kabul edilir. İşlevlerinizi olabildiğince değer üretecek şekilde tasarlamanızı öneririm. İşlevlerin yan etkilerini azaltmak, onların daha anlaşılır ve daha kolay olmalarını sağlar. -) - -$(P -Aynı işi fonksiyonel programlamaya uygun olacak şekilde gerçekleştirmek için (yani, değer üreten işlev kullanmak için) programı şöyle değiştirmek önerilir: -) - ---- -import std.stdio; - -$(HILITE double düşükEnerji)(double enerji) { - $(HILITE return enerji / 4); -} - -void main() { - double enerji = 100; - - $(HILITE enerji = düşükEnerji(enerji)); - writeln("Yeni enerji: ", enerji); -} ---- - -$(H6 $(C auto ref)) - -$(P -Bu belirteç yalnızca $(LINK2 /ders/d/sablonlar.html, şablonlarla) kullanılabilir. Bir sonraki bölümde göreceğimiz gibi, $(I sol değerler) $(C auto ref) parametrelere referans olarak, $(I sağ değerler) ise kopyalanarak geçirilirler. -) - -$(H6 $(IX inout, parametre) $(C inout)) - -$(P -İsminin $(C in) ve $(C out) sözcüklerinden oluştuğuna bakıldığında bu belirtecin $(I hem giriş hem çıkış) anlamına geldiği düşünebilir ancak bu doğru değildir. Hem giriş hem çıkış anlamına gelen belirtecin $(C ref) olduğunu yukarıda gördük. -) - -$(P -$(C inout), parametrenin $(I değişmezlik) bilgisini otomatik olarak çıkış değerine taşımaya yarar. Parametre $(C const), $(C immutable), veya $(I değişebilen) olduğunda dönüş değeri de $(C const), $(C immutable), veya $(I değişebilen) olur. -) - -$(P -Bu belirtecin yararını görmek için kendisine verilen dilimin ortadaki elemanlarını yine dilim olarak döndüren bir işleve bakalım: -) - ---- -import std.stdio; - -int[] ortadakileri(int[] dilim) { - if (dilim.length) { - --dilim.length; // sondan kırp - - if (dilim.length) { - dilim = dilim[1 .. $]; // baştan kırp - } - } - - return dilim; -} - -void main() { - int[] sayılar = [ 5, 6, 7, 8, 9 ]; - writeln(ortadakileri(sayılar)); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[6, 7, 8] -) - -$(P -Kitabın bu noktasına kadar anladıklarımız doğrultusunda bu işlevin parametresinin aslında $(C const(int)[]) olarak bildirilmiş olması gerekir çünkü kendisine verilen dilimin elemanlarında değişiklik yapmamaktadır. Dikkat ederseniz, dilimin kendisinin değiştirilmesinde bir sakınca yoktur çünkü değiştirilen dilim işlevin çağrıldığı yerdeki dilim değil, onun kopyasıdır. -) - -$(P -Ancak, işlev buna uygun olarak tekrar yazıldığında bir derleme hatası alınır: -) - ---- -int[] ortadakileri($(HILITE const(int)[]) dilim) { - // ... - return dilim; $(DERLEME_HATASI) -} ---- - -$(P -Derleme hatası, elemanları değiştirilemeyen bir dilimin elemanları $(I değiştirilebilen) bir dilim olarak döndürülemeyeceğini bildirir: -) - -$(SHELL -Error: cannot implicitly convert expression (dilim) of -type const(int)[] to int[] -) - -$(P -Bunun çözümü olarak dönüş türünün de $(C const(int)[]) olarak belirlenmesi düşünülebilir: -) - ---- -$(HILITE const(int)[]) ortadakileri(const(int)[] dilim) { - // ... - return dilim; // şimdi derlenir -} ---- - -$(P -Kod, yapılan o değişiklikle derlenir. Ancak, bu sefer ortaya farklı bir kısıtlama çıkmıştır: İşlev $(I değişebilen) elemanlardan oluşan bir dilimle bile çağrılmış olsa döndürdüğü dilim $(C const) elemanlardan oluşacaktır. Bunun zararını görmek için bir dilimin ortadaki elemanlarının on katlarını almaya çalışan şu koda bakalım: -) - ---- - int[] ortadakiler = ortadakileri(sayılar); $(DERLEME_HATASI) - ortadakiler[] *= 10; ---- - -$(P -İşlevin döndürdüğü $(C const(int)[]) türündeki dilimin $(C int[]) türündeki dilime atanamaması doğaldır: -) - -$(SHELL -Error: cannot implicitly convert expression -(ortadakileri(sayılar)) of type const(int)[] to int[] -) - -$(P -Asıl dilim değişebilen elemanlardan oluştuğu halde ortadaki bölümü üzerine böyle bir kısıtlama getirilmesi kullanışsızlıktır. İşte, $(C inout) değişmezlikle ilgili olan bu sorunu çözer. Bu anahtar sözcük hem parametrede hem de dönüş türünde kullanılır ve parametrenin değişebilme durumunu dönüş değerine taşır: -) - ---- -$(HILITE inout)(int)[] ortadakileri($(HILITE inout)(int)[] dilim) { - // ... - return dilim; -} ---- - -$(P -Aynı işlev artık $(C const), $(C immutable), ve $(I değişebilen) dilimlerle çağrılabilir: -) - ---- - { - $(HILITE int[]) sayılar = [ 5, 6, 7, 8, 9 ]; - // Dönüş türü değişebilen elemanlı dilimdir - $(HILITE int[]) ortadakiler = ortadakileri(sayılar); - ortadakiler[] *= 10; - writeln(ortadakiler); - } - - { - $(HILITE immutable int[]) sayılar = [ 10, 11, 12 ]; - // Dönüş türü immutable elemanlı dilimdir - $(HILITE immutable int[]) ortadakiler = ortadakileri(sayılar); - writeln(ortadakiler); - } - - { - $(HILITE const int[]) sayılar = [ 13, 14, 15, 16 ]; - // Dönüş türü const elemanlı dilimdir - $(HILITE const int[]) ortadakiler = ortadakileri(sayılar); - writeln(ortadakiler); - } ---- - -$(H6 $(IX lazy) $(C lazy)) - -$(P -Doğal olarak, parametre değerleri işlevler çağrılmadan $(I önce) işletilirler. Örneğin, $(C topla) gibi bir işlevi başka iki işlevin sonucu ile çağırdığımızı düşünelim: -) - ---- - sonuç = topla(birMiktar(), başkaBirMiktar()); ---- - -$(P -$(C topla)'nın çağrılabilmesi için öncelikle $(C birMiktar) ve $(C başkaBirMiktar) işlevlerinin çağrılmaları gerekir çünkü $(C topla) hangi iki değeri toplayacağını bilmek zorundadır. -) - -$(P -İşlemlerin bu şekilde doğal olarak işletilmeleri $(I hevesli) olarak tanımlanır. Program, işlemleri öncelik sıralarına göre hevesle işletir. -) - -$(P -Oysa bazı parametreler işlevin işleyişine bağlı olarak belki de hiçbir zaman kullanılmayacaklardır. Parametre değerlerinin hevesli olarak önceden işletilmeleri kullanılmayan parametrelerin gereksiz yere hesaplanmış olmalarına neden olacaktır. -) - -$(P -Bunun bilinen bir örneği, programın işleyişiyle ilgili mesajlar yazdırmaya yarayan $(I log) işlevleridir. Bu işlevler kullanıcı ayarlarına bağlı olarak yalnızca yeterince öneme sahip olan mesajları yazdırırlar: -) - ---- -enum Önem { düşük, orta, yüksek } -// Not: Önem, İngilizce'de 'log level' olarak bilinir. - -void logla(Önem önem, string mesaj) { - if (önem >= önemAyarı) { - writefln("%s", mesaj); - } -} ---- - -$(P -Örneğin, eğer kullanıcı yalnızca $(C Önem.yüksek) değerli mesajlarla ilgileniyorsa, $(C Önem.orta) değerindeki mesajlar yazdırılmazlar. Buna rağmen, parametre değeri işlev çağrılmadan önce yine de hesaplanacaktır. Örneğin, aşağıdaki mesajı oluşturan $(C format) ifadesinin tamamı ($(C bağlantıDurumunuÖğren()) çağrısı dahil) $(C logla) işlevi çağrılmadan önce işletilecek ama bu işlem mesaj yazdırılmadığı zaman boşa gitmiş olacaktır: -) - ---- - if (!bağlanıldı_mı) { - logla(Önem.orta, - format("Hata. Bağlantı durumu: '%s'.", - bağlantıDurumunuÖğren())); - } ---- - -$(P -$(C lazy) anahtar sözcüğü parametreyi oluşturan ifadenin yalnızca o parametre işlev içinde gerçekten kullanıldığında (ve her kullanıldığında) hesaplanacağını bildirir: -) - ---- -void logla(Önem önem, $(HILITE lazy) string mesaj) { - // ... işlevin tanımı öncekiyle aynı ... -} ---- - -$(P -Artık ifade $(C mesaj) gerçekten kullanıldığında hesaplanır. -) - -$(P -Dikkat edilmesi gereken bir nokta, $(C lazy) parametrenin değerinin o parametre $(I her kullanıldığında) hesaplanacağıdır. -) - -$(P -Örneğin, aşağıdaki işlevin $(C lazy) parametresi üç kere kullanıldığından onu hesaplayan işlev de üç kere çağrılmaktadır: -) - ---- -import std.stdio; - -int parametreyiHesaplayanİşlev() { - writeln("Hesap yapılıyor"); - return 1; -} - -void tembelParametreliİşlev(lazy int değer) { - int sonuç = $(HILITE değer + değer + değer); - writeln(sonuç); -} - -void main() { - tembelParametreliİşlev(parametreyiHesaplayanİşlev()); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Hesap yapılıyor -Hesap yapılıyor -Hesap yapılıyor -3 -) - -$(P -$(C lazy) belirtecini değeri ancak bazı durumlarda kullanılan parametreleri belirlemek için kullanabilirsiniz. Ancak, değerin birden fazla sayıda hesaplanabileceğini de unutmamak gerekir. -) - -$(H6 $(IX scope) $(C scope)) - -$(P -Bu anahtar sözcük, parametrenin işlev tarafından bir kenara kaydedilmeyeceğini bildirir. Bir anlamda, işlevin o parametreyle işinin kısa olacağını garanti eder: -) - -$(COMMENT_XXX Aşağıda DERLEME_HATASI makrosunu kullanmıyoruz çünkü 'scope' derleme hatasına neden olmadığından codetester hata veriyor.) - ---- -int[] modülDilimi; - -int[] işlev($(HILITE scope) int[] parametre) { - modülDilimi = parametre; // ← derleme HATASI - return parametre; // ← derleme HATASI -} - -void main() { - int[] dilim = [ 10, 20 ]; - int[] sonuç = işlev(dilim); -} ---- - -$(P -O işlev $(C scope) ile verdiği sözü iki yerde bozmaktadır çünkü onu hem modül kapsamındaki bir dilime atamakta hem de dönüş değeri olarak kullanmaktadır. Bu davranışların ikisi de parametrenin işlevin sonlanmasından sonra da kullanılabilmelerine neden olacağından derleme hatasına neden olur. -) - -$(P -($(I Not: Bu bölümdeki kodların en son denendikleri derleyici olan dmd 2.071 bu anahtar sözcüğü desteklemiyordu. -)) -) - -$(H6 $(IX shared, parametre) $(C shared)) - -$(P -Bu anahtar sözcük parametrenin iş parçacıkları arasında paylaşılabilen çeşitten olmasını gerektirir: -) - ---- -void işlev($(HILITE shared) int[] i) { - // ... -} - -void main() { - int[] sayılar = [ 10, 20 ]; - işlev(sayılar); $(DERLEME_HATASI) -} ---- - -$(P -Yukarıdaki program derlenemez çünkü parametre olarak kullanılan değişken $(C shared) değildir. Program aşağıdaki değişiklikle derlenebilir: -) - ---- - $(HILITE shared) int[] sayılar = [ 10, 20 ]; - işlev(sayılar); // şimdi derlenir ---- - -$(P -$(C shared) anahtar sözcüğünü ilerideki $(LINK2 /ders/d/es_zamanli_shared.html, Veri Paylaşarak Eş Zamanlı Programlama bölümünde) kullanacağız. -) - -$(H6 $(IX return, parametre) $(C return)) - -$(P -Bazı durumlarda bir işlevin $(C ref) parametrelerinden birisini doğrudan döndürmesi istenebilir. Örneğin, aşağıdaki $(C seç()) işlevi rasgele seçtiği bir parametresini döndürmekte ve böylece çağıran taraftaki bir değişkenin doğrudan değiştirilmesi sağlanmaktadır: -) - ---- -import std.stdio; -import std.random; - -$(HILITE ref) int seç($(HILITE ref) int soldaki, $(HILITE ref) int sağdaki) { - return uniform(0, 2) ? soldaki : sağdaki; -} - -void main() { - int a; - int b; - - seç(a, b) $(HILITE = 42); - - writefln("a: %s, b: %s", a, b); -} ---- - -$(P -Sonuçta $(C main()) içindeki $(C a) veya $(C b) değişkenlerinden birisinin değeri $(C 42) olur: -) - -$(SHELL -a: 42, b: 0 -) - -$(SHELL -a: 0, b: 42 -) - -$(P -Ancak, bazı durumlarda $(C seç())'e gönderilen parametrelerin yaşam süreçleri döndürülen referansın yaşam sürecinden daha kısa olabilir. Örneğin, aşağıdaki $(C foo()) işlevi $(C seç())'i iki yerel değişkenle çağırmakta ve sonuçta kendisi bunlardan birisine referans döndürmüş olmaktadır: -) - ---- -import std.random; - -ref int seç(ref int soldaki, ref int sağdaki) { - return uniform(0, 2) ? soldaki : sağdaki; -} - -ref int foo() { - int a; - int b; - - return seç(a, b); $(CODE_NOTE_WRONG HATA: geçersiz referans döndürülüyor) -} - -void main() { - foo() = 42; $(CODE_NOTE_WRONG HATA: yasal olmayan adrese yazılıyor) -} ---- - -$(P -$(C a) ve $(C b) değişkenlerinin yaşam süreçleri $(C foo())'dan çıkıldığında sona erdiğinden, $(C main()) içindeki atama işlemi yasal bir değişkene yapılamaz. Bu, $(I tanımsız davranıştır). -) - -$(P -$(IX tanımsız davranış) Tanımsız davranış, programın davranışının programlama dili tarafından belirlenmediğini ifade eder. Tanımsız davranış içeren bir programın davranışı hakkında hiçbir şey söylenemez. (Olasılıkla, $(C 42) değeri daha önceden $(C a) veya $(C b) için kullanılan ama belki de artık ilgisiz bir değişkene ait olan bir bellek bölgesine yazılacak ve o değişkenin değerini önceden kestirilemeyecek biçimde bozacaktır.) -) - -$(P -Parametreye uygulanan $(C return) anahtar sözcüğü böyle hataları önler. $(C return), o parametrenin döndürülen referanstan daha uzun yaşayan bir değişken olması gerektiğini bildirir: -) - ---- -import std.random; - -ref int seç($(HILITE return) ref int soldaki, $(HILITE return) ref int sağdaki) { - return uniform(0, 2) ? soldaki : sağdaki; -} - -ref int foo() { - int a; - int b; - - return seç(a, b); $(DERLEME_HATASI) -} - -void main() { - foo(); -} ---- - -$(P -Derleyici bu sefer $(C seç())'e gönderilen değişkenlerin $(C foo())'nun döndürmeye çalıştığı referanstan daha kısa yaşadıklarını farkeder ve $(I yerel değişkene referans döndürülmekte) olduğunu bildiren bir hata verir: -) - -$(SHELL -Error: escaping reference to local variable a -Error: escaping reference to local variable b -) - -$(P -$(I Not: Derleyicinin böyle bir hatayı $(C return) anahtar sözcüğüne gerek kalmadan da görmüş olabileceği düşünülebilir. Ancak, bu her durumda mümkün değildir çünkü derleyici her derleme sırasında her işlevin içeriğini görmüyor olabilir.) -) - -$(H5 Özet) - -$(UL - -$(LI $(I Parametre), işlevin işi için kullanılan bilgidir. -) - -$(LI $(I Parametre değeri), işleve parametre olarak gönderilen bir ifadedir (örneğin bir değişken). -) - -$(LI -Her parametre kopyalanarak gönderilir. Ancak, referans türlerinde kopyalanan asıl değişken değil, referansın kendisidir. -) - -$(LI $(C in), parametrenin yalnızca bilgi girişi için kullanıldığını bildirir.) - -$(LI $(C out), parametrenin yalnızca bilgi çıkışı için kullanıldığını bildirir. -) - -$(LI $(C ref), parametrenin hem bilgi girişi hem de bilgi çıkışı için kullanıldığını bildirir. -) - -$(LI $(C auto ref) yalnızca şablonlarla kullanılır. $(I Sol değerlerin) referans olarak, $(I sağ değerlerin) ise kopyalanarak geçirileceğini bildirir.) - -$(LI $(C const), parametrenin işlev içinde değiştirilmediğini garanti eder. (Hatırlarsanız, $(C const) geçişlidir: böyle bir değişken aracılığıyla erişilen başka veriler de değiştirilemez.) -) - -$(LI $(C immutable), parametre olarak kullanılan değişkenin $(C immutable) olması şartını getirir. -) - -$(LI $(C inout) hem paremetrede hem de dönüş türünde kullanılır ve parametrenin $(C const), $(C immutable), veya $(I değişebilme) özelliğini dönüş türüne taşır. -) - -$(LI $(C lazy), parametre olarak gönderilen ifadenin değerinin ancak o değer gerçekten kullanıldığında (ve her kullanıldığında) işletilmesini sağlar. -) - -$(LI $(C scope), parametreye eriştiren herhangi bir referansın işlevden dışarıya sızdırılmayacağını bildirir. -) - -$(LI $(C shared), parametre olarak kullanılan değişkenin $(C shared) olması şartını getirir. -) - -$(LI $(C return), parametrenin döndürülen referanstan daha uzun yaşaması gerektiğini bildirir. -) - -) - -$(PROBLEM_TEK - -$(P -Aşağıdaki işlev kendisine verilen iki parametrenin değerlerini değiş tokuş etmeye çalışmaktadır: -) - ---- -import std.stdio; - -void değişTokuş(int birinci, int ikinci) { - int geçici = birinci; - birinci = ikinci; - ikinci = geçici; -} - -void main() { - int birSayı = 1; - int başkaSayı = 2; - - değişTokuş(birSayı, başkaSayı); - - writeln(birSayı, ' ', başkaSayı); -} ---- - -$(P -Ancak, işlev istendiği gibi çalışmamaktadır: -) - -$(SHELL -1 2 $(SHELL_NOTE_WRONG değiş tokuş olmamış) -) - -$(P -İşlevi düzeltin ve değişkenlerin değerlerinin değiş tokuş edilmelerini sağlayın. -) - -) - -Macros: - SUBTITLE=İşlev Parametreleri - - DESCRIPTION=D dilinde işlev (fonksiyon) [function] parametrelerinin çeşitleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function parametre in out inout ref lazy const immutable scope shared - -SOZLER= -$(atama) -$(degisken) -$(donus_degeri) -$(fonksiyonel_programlama) -$(hevesli) -$(is_parcacigi) -$(islev) -$(nesne) -$(parametre) -$(parametre_degeri) -$(referans) -$(sag_deger) -$(sol_deger) -$(tanimsiz_davranis) -$(yapi) diff --git a/ddili/src/ders/d/islev_yukleme.cozum.d b/ddili/src/ders/d/islev_yukleme.cozum.d deleted file mode 100644 index 615cfce..0000000 --- a/ddili/src/ders/d/islev_yukleme.cozum.d +++ /dev/null @@ -1,146 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU İşlev Yükleme) - -$(P -Daha önce yazılan $(C bilgiVer) işlevlerinden yararlanan iki yüklemesi şöyle yazılabilir: -) - ---- -void bilgiVer(in Yemek yemek) { - bilgiVer(yemek.zaman); - write('-'); - bilgiVer(zamanEkle(yemek.zaman, GününSaati(1, 30))); - - write(" Yemek, Yer: ", yemek.adres); -} - -void bilgiVer(in GünlükPlan plan) { - bilgiVer(plan.sabahToplantısı); - writeln(); - bilgiVer(plan.öğleYemeği); - writeln(); - bilgiVer(plan.akşamToplantısı); -} ---- - -$(P -Bütün bu türleri kullanan programın tamamı: -) - ---- -import std.stdio; - -struct GününSaati { - int saat; - int dakika; -} - -void bilgiVer(in GününSaati zaman) { - writef("%02s:%02s", zaman.saat, zaman.dakika); -} - -GününSaati zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek) { - GününSaati sonuç; - - sonuç.dakika = başlangıç.dakika + eklenecek.dakika; - sonuç.saat = başlangıç.saat + eklenecek.saat; - sonuç.saat += sonuç.dakika / 60; - - sonuç.dakika %= 60; - sonuç.saat %= 24; - - return sonuç; -} - -struct Toplantı { - string konu; - int katılımcıSayısı; - GününSaati başlangıç; - GününSaati bitiş; -} - -void bilgiVer(in Toplantı toplantı) { - bilgiVer(toplantı.başlangıç); - write('-'); - bilgiVer(toplantı.bitiş); - - writef(" \"%s\" toplantısı (%s katılımcı)", - toplantı.konu, - toplantı.katılımcıSayısı); -} - -struct Yemek { - GününSaati zaman; - string adres; -} - -void bilgiVer(in Yemek yemek) { - bilgiVer(yemek.zaman); - write('-'); - bilgiVer(zamanEkle(yemek.zaman, GününSaati(1, 30))); - - write(" Yemek, Yer: ", yemek.adres); -} - -struct GünlükPlan { - Toplantı sabahToplantısı; - Yemek öğleYemeği; - Toplantı akşamToplantısı; -} - -void bilgiVer(in GünlükPlan plan) { - bilgiVer(plan.sabahToplantısı); - writeln(); - bilgiVer(plan.öğleYemeği); - writeln(); - bilgiVer(plan.akşamToplantısı); -} - -void main() { - immutable geziToplantısı = Toplantı("Bisiklet gezisi", 4, - GününSaati(10, 30), - GününSaati(11, 45)); - - immutable öğleYemeği = Yemek(GününSaati(12, 30), "Taksim"); - - immutable bütçeToplantısı = Toplantı("Bütçe", 8, - GününSaati(15, 30), - GününSaati(17, 30)); - - immutable bugününPlanı = GünlükPlan(geziToplantısı, - öğleYemeği, - bütçeToplantısı); - - bilgiVer(bugününPlanı); - writeln(); -} ---- - -$(P -Yukarıdaki $(C main), nesneler tanımlamak yerine yalnızca yapı hazır değerleri ile şöyle de yazılabilir: -) - ---- -void $(CODE_DONT_TEST)main() { - bilgiVer(GünlükPlan(Toplantı("Bisiklet gezisi", 4, - GününSaati(10, 30), - GününSaati(11, 45)), - - Yemek(GününSaati(12, 30), "Taksim"), - - Toplantı("Bütçe", 8, - GününSaati(15, 30), - GününSaati(17, 30)))); - writeln(); -} ---- - - -Macros: - SUBTITLE=İşlev Yükleme - - DESCRIPTION=D'nin işlevlerin kullanışlılığını arttıran olanaklarından işlev yükleme konusunun problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev yükleme overloading problem çözüm diff --git a/ddili/src/ders/d/islev_yukleme.d b/ddili/src/ders/d/islev_yukleme.d deleted file mode 100644 index 0132a6b..0000000 --- a/ddili/src/ders/d/islev_yukleme.d +++ /dev/null @@ -1,312 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX işlev yükleme) $(IX yükleme, işlev) İşlev Yükleme) - -$(P -Aynı isimde birden fazla işlev tanımlamaya $(I işlev yükleme) denir. İsimleri aynı olan bu işlevlerin ayırt edilebilmeleri için parametrelerinin birbirlerinden farklı olmaları gerekir. -) - -$(P -Bu kullanımda "yükleme" sözcüğünün "aynı isme yeni görev yükleme" anlamına geldiğini düşünebilirsiniz. -) - -$(P -Aşağıda aynı isimde ama parametreleri farklı işlevler görüyorsunuz: -) - ---- -import std.stdio; - -void bilgiVer(in $(HILITE double) sayı) { - writeln("Kesirli sayı: ", sayı); -} - -void bilgiVer(in $(HILITE int) sayı) { - writeln("Tamsayı : ", sayı); -} - -void bilgiVer(in $(HILITE char[]) dizgi) { - writeln("Dizgi : ", dizgi); -} - -void main() { - bilgiVer(1.2); - bilgiVer(3); - bilgiVer("merhaba"); -} ---- - -$(P -İşlevlerin hepsinin de ismi $(C bilgiVer) olduğu halde, derleyici parametrenin türüne uygun olan işlevi seçer ve onun çağrılmasını sağlar. Örneğin $(C 1.2) hazır değerinin türü $(C double) olduğu için, onun kullanıldığı durumda o işlevler arasından $(C double) parametre alanı çağrılır. -) - -$(P -Hangi işlevin çağrılacağı $(I derleme zamanında) seçilir. Bu seçim her zaman kolay veya açık olmayabilir. Örneğin şu kodda kullanılan $(C int) değer hem $(C real) hem de $(C double) türüne uyduğu için derleyici hangisini seçeceğine karar veremez: -) - ---- -real yediKatı(in real değer) { - return 7 * değer; -} - -double yediKatı(in double değer) { - return 7 * değer; -} - -void main() { - int sayı = 5; - auto sonuç = yediKatı(sayı); $(DERLEME_HATASI) -} ---- - -$(P $(I Not: Normalde aynı işi yapan böyle iki işlevin yazılması gereksizdir. Tek işlev tanımının nasıl birden fazla tür için kullanılabileceğini daha sonra $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) göreceğiz.) -) - -$(P -Öte yandan, bu işlevlerin $(C long) türünde parametre alan bir üçüncüsü tanımlansa derleme hatası ortadan kalkar çünkü yüklenen işlev seçimi konusunda $(C int) değerler $(C long) türüne kesirli türlerden $(I daha uyumludurlar): -) - ---- -long yediKatı(in long değer) { - return 7 * değer; -} - -// ... - - auto sonuç = yediKatı(sayı); // şimdi derlenir ---- - -$(H5 Parametre uyum kuralları) - -$(P -Aynı isimde birden fazla işlev bulunması, derleyicinin bir seçim yapmasını gerektirir. Yüklenen işlevler arasından, kullanılan parametrelere $(I daha çok uyan) işlev seçilir. -) - -$(P -Bu seçim çoğu durumda kolay ve $(I beklendiği gibi) olur; ama hangi işlevin daha çok uyduğu konusu bazen çok karışıktır. Bu yüzden uyum kuralları geliştirilmiştir. -) - -$(P -Parametreler için uyum konusunda dört durum vardır: -) - -$(UL -$(LI uyumsuzluk) -$(LI otomatik tür dönüşümü yoluyla uyum) -$(LI $(C const)'a dönüştürerek uyum) -$(LI tam uyum) -) - -$(P -Derleyici, yüklenmiş olan işlevlerden hangisini çağıracağına karar vermek için işlevleri gözden geçirir. Her işlevin parametrelerine teker teker bakar ve her parametrenin yukarıdaki dört uyum durumundan hangisinde olduğunu belirler. Bütün parametreler içindeki en az uyum, bu işlevin de uyumu olarak kabul edilir. -) - -$(P -Bu şekilde bütün işlevlerin uyum durumları belirlendikten sonra; eğer varsa, en çok uyan işlev seçilir. -) - -$(P -Eğer birden fazla işlev aynı derecede uymuşsa, onlardan hangisinin seçileceğine daha da karışık başka kurallar yoluyla karar verilir. -) - -$(P -Ben burada bu kuralların daha derinine inmeyeceğim; çünkü eğer bu kadar karışık kurallarla yüz yüze kalmışsanız, aslında programınızın tasarımında değişiklik yapma zamanı gelmiş demektir. Belki de işlev şablonlarını kullanmak daha doğru olacaktır. Hatta belki de aynı isimde işlev tanımlamak yerine, daha açıklayıcı isimler kullanarak hangisini çağırmak istediğinizi açıkça belirtmek bütün karışıklığı ortadan kaldıracaktır: $(C yediKatı_real) ve $(C yediKatı_double) gibi... -) - -$(H5 Yapılar için işlev yükleme) - -$(P -İşlev yükleme, yapılarda ve sınıflarda çok yararlıdır; üstelik o türlerde işlev seçimi konusunda uyum sorunları da çok daha azdır. Yukarıdaki $(C bilgiVer) işlevini $(LINK2 /ders/d/yapilar.html, Yapılar bölümünde) kullandığımız bazı türler için yükleyelim: -) - ---- -struct GününSaati { - int saat; - int dakika; -} - -void bilgiVer(in GününSaati zaman) { - writef("%02s:%02s", zaman.saat, zaman.dakika); -} ---- - -$(P -O tanım sayesinde artık $(C GününSaati) nesnelerini de $(C bilgiVer) işlevine gönderebiliriz. Böylece programımızda her tür nesneyi aynı isimle yazdırabileceğimiz alışılmış bir yöntemimiz olur: -) - ---- - auto kahvaltıZamanı = GününSaati(7, 0); - bilgiVer(kahvaltıZamanı); ---- - -$(P -Temel türlerde olduğu gibi, artık $(C GününSaati) nesneleri de kendilerine özgü çıktı düzenleri ile yazdırılmış olurlar: -) - -$(SHELL -07:00 -) - -$(P -$(C bilgiVer) işlevini yapılar bölümünde değinilen $(C Toplantı) yapısı için de yükleyelim: -) - ---- -struct Toplantı { - string konu; - int katılımcıSayısı; - GününSaati başlangıç; - GününSaati bitiş; -} - -void bilgiVer(in Toplantı toplantı) { - bilgiVer(toplantı.başlangıç); - write('-'); - bilgiVer(toplantı.bitiş); - - writef(" \"%s\" toplantısı (%s katılımcı)", - toplantı.konu, - toplantı.katılımcıSayısı); -} ---- - -$(P -Gördüğünüz gibi; $(C bilgiVer)'in $(C GününSaati) için yüklenmiş olanı, $(C Toplantı)'yı yazdıran işlev tarafından kullanılmaktadır. Artık $(C Toplantı) nesnelerini de alıştığımız isimdeki işlevle yazdırabiliriz: -) - ---- - auto geziToplantısı = - Toplantı("Bisikletle gezilecek yerler", 3, - GününSaati(9, 0), GününSaati(9, 10)); - bilgiVer(geziToplantısı); ---- - -$(P -Çıktısı: -) - -$(SHELL -09:00-09:10 "Bisikletle gezilecek yerler" toplantısı (3 katılımcı) -) - -$(H5 Eksiklikler) - -$(P -Yukarıdaki $(C bilgiVer) işlevi her ne kadar kullanım kolaylığı getirse de, bu yöntemin bazı eksiklikleri vardır: -) - -$(UL - -$(LI -$(C bilgiVer) işlevi yalnızca $(C stdout)'a yazdığı için fazla kullanışlı değildir. Oysa örneğin $(C File) türünden bir dosyaya da yazabiliyor olsa, kullanışlılığı artardı. Bunu sağlamanın yolu, çıktının yazdırılacağı akımı da işleve bir parametre olarak vermektir: - ---- -void bilgiVer(File akım, in GününSaati zaman) { - akım.writef("%02s:%02s", zaman.saat, zaman.dakika); -} ---- - -$(P -O sayede $(C GününSaati) nesnelerini istersek $(C stdout)'a, istersek de bir dosyaya yazdırabiliriz: -) - ---- - bilgiVer($(HILITE stdout), kahvaltıZamanı); - - auto dosya = File("bir_dosya", "w"); - bilgiVer($(HILITE dosya), kahvaltıZamanı); ---- - -$(P -$(I Not: $(C stdin), $(C stdout), ve $(C stderr) nesnelerinin türleri de aslında $(C File)'dır.) -) - -) - -$(LI -Daha önemlisi, $(C bilgiVer) gibi bir işlev, yapı nesnelerini temel türler kadar rahatça kullanabilmemiz için yeterli değildir. Temel türlerden alışık olduğumuz rahatlık yoktur: - ---- - writeln(kahvaltıZamanı); // Kullanışsız: Genel düzende yazar ---- - -$(P -O kod çalıştırıldığında $(C GününSaati) türünün ismi ve üyelerinin değerleri programa uygun biçimde değil, genel bir düzende yazdırılır: -) - -$(SHELL -GününSaati(7, 0) -) - -$(P -Bunun yerine, yapı nesnesinin değerini örneğin $(STRING "12:34") biçiminde bir $(C string)'e dönüştürebilen bir işlev olması çok daha yararlı olur. Yapı nesnelerinin de otomatik olarak $(C string)'e dönüştürülebileceklerini bundan sonraki bölümde göstereceğim. -) - -) - -) - -$(PROBLEM_TEK - -$(P -$(C bilgiVer) işlevini şu iki yapı için de yükleyin: -) - ---- -struct Yemek { - GününSaati zaman; - string adres; -} - -struct GünlükPlan { - Toplantı sabahToplantısı; - Yemek öğleYemeği; - Toplantı akşamToplantısı; -} ---- - -$(P -$(C Yemek) yapısı yalnızca başlangıç zamanını barındırdığı için; onun bitiş zamanını başlangıç zamanından bir buçuk saat sonrası olarak belirleyin. Bu işlem için yapılar bölümünde tanımladığımız $(C zamanEkle) işlevi yararlı olabilir: -) - ---- -GününSaati zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek) { - GününSaati sonuç; - - sonuç.dakika = başlangıç.dakika + eklenecek.dakika; - sonuç.saat = başlangıç.saat + eklenecek.saat; - sonuç.saat += sonuç.dakika / 60; - - sonuç.dakika %= 60; - sonuç.saat %= 24; - - return sonuç; -} ---- - -$(P -Yemek bitiş zamanları o işlev yardımıyla hesaplanınca $(C GünlükPlan) nesneleri çıkışa şuna benzer şekilde yazdırılabilirler: -) - -$(SHELL -10:30-11:45 "Bisiklet gezisi" toplantısı (4 katılımcı) -12:30-14:00 Yemek, Yer: Taksim -15:30-17:30 "Bütçe" toplantısı (8 katılımcı) -) - -) - - -Macros: - SUBTITLE=İşlev Yükleme - - DESCRIPTION=D'nin işlevlerin kullanışlılığını arttıran olanaklarından işlev yükleme olanağı [overloading] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev yükleme overloading - -SOZLER= -$(acikca_elle_yapilan) -$(otomatik) -$(tur_donusumu) -$(yukleme) diff --git a/ddili/src/ders/d/islevler.cozum.d b/ddili/src/ders/d/islevler.cozum.d deleted file mode 100644 index e6356cd..0000000 --- a/ddili/src/ders/d/islevler.cozum.d +++ /dev/null @@ -1,56 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU İşlevler) - -$(OL - -$(LI - ---- -import std.stdio; - -void menüyüGöster(string[] seçenekler, int ilkNumara) { - foreach (i, seçenek; seçenekler) { - writeln(' ', i + ilkNumara, ' ', seçenek); - } -} - -void main() { - string[] seçenekler = - [ "Siyah", "Kırmızı", "Yeşil", "Mavi", "Beyaz" ]; - menüyüGöster(seçenekler, 1); -} ---- -) - -$(LI -Bir kaç fikir: - -$(UL - -$(LI Yatay çizgi çizen $(C yatayÇizgiÇiz) adında bir işlev tanımlayın.) - -$(LI Kare çizen $(C kareÇiz) adında bir işlev tanımlayın. Bu işlev $(C düşeyÇizgiÇiz) ve $(C yatayÇizgiÇiz) işlevlerinden yararlanabilir.) - -$(LI Boyarken hangi karakteri kullanacaklarını çizim işlevlerine bir parametre olarak verin. Böylece her şekil farklı bir karakterle çizilebilir: - ---- -void benekKoy(Kağıt kağıt, int satır, int sütun$(HILITE, dchar boya)) { - kağıt[satır][sütun] = $(HILITE boya); -} ---- - -) - -) - -) - -) - -Macros: - SUBTITLE=İşlevler Dersi Çözümleri - - DESCRIPTION=D dilinde işlevler (fonksiyonlar) [functions] ders problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function çözüm diff --git a/ddili/src/ders/d/islevler.d b/ddili/src/ders/d/islevler.d deleted file mode 100644 index 36460fe..0000000 --- a/ddili/src/ders/d/islevler.d +++ /dev/null @@ -1,748 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX işlev) İşlevler) - -$(P -İşlevler $(I gerçek) programların temel taşlarıdır. Nasıl temel türler, bütün türlerin yapı taşları iseler, işlevler de program davranışlarının yapı taşlarıdır. -) - -$(P -İşlevlerin ustalıkla da ilgisi vardır. Usta programcıların yazdıkları işlevler kısa ve öz olur. Bunun tersi de doğrudur: kısa ve öz işlevler yazmaya çalışmak, ustalık yolunda ilerlemenin önemli adımlarındandır. İşlemleri oluşturan alt adımları görmeye çalışmak ve o adımları küçük işlevler halinde yazmak, programcılık konusunda gelişmenize yardım edecektir. -) - -$(P -Bundan önceki bölümlerde temel deyimler ve ifadeler öğrendik. Daha hepsini bitirmedik ama D'nin programlarda çok kullanılan, çok yararlı, ve çok önemli olanaklarını gördük. Yine de, hiçbirisi büyük programlar yazmak için yeterli değildir. Şimdiye kadar yazdığımız biçimdeki programlar, deneme programları gibi hiçbir karmaşıklığı olmayan çok basit programlar olabilirler. En ufak bir karmaşıklığı bulunan bir işi işlev kullanmadan yazmaya çalışmak çok zordur, ve ortaya çıkan program da hataya açık olur. -) - -$(P -İşlevler, ifade ve deyimleri bir araya getiren olanaklardır. Bir araya getirilen ifade ve deyimlere toplu olarak yeni bir isim verilir ve o işlemlerin hepsi birden bu isimle işletilir. -) - -$(P -$(I Bir araya getirerek yeni isim verme) kavramını günlük hayattan tanıyoruz. Örneğin $(I yağda yumurta yapma) işini şu adımlarla tarif edebiliriz: -) - -$(UL -$(LI tavayı çıkart) -$(LI yağı çıkart) -$(LI yumurtayı çıkart) -$(LI ateşi aç) -$(LI tavayı ateşe koy) -$(LI tava ısınınca yağı içine at) -$(LI yağ eriyince yumurtayı içine kır) -$(LI yumurtanın beyazı pişince tavayı ateşten al) -$(LI ateşi söndür) -) - -$(P -O kadar ayrıntıya girmek zamanla gereksiz ve içinden çıkılmaz bir hâl alacağı için, birbiriyle ilişkili adımların bazılarına tek bir isim verilebilir: -) - -$(UL -$(LI $(HILITE malzemeleri hazırla) (tavayı, yağı, yumurtayı çıkart)) -$(LI ateşi aç) -$(LI $(HILITE yumurtayı pişir) (tavayı ateşe koy, vs.)) -$(LI ateşi söndür) -) - -$(P -Daha sonra daha da ileri gidilebilir ve bütün o adımları içeren tek bir ifade de kullanılabilir: -) - -$(UL -$(LI $(HILITE yağda yumurta yap) (bütün adımlar)) -) - -$(P -İşlevlerin bundan farkı yoktur: Yaptıkları işlerin hepsine birden genel bir isim verilebilen adımlar tek bir işlev olarak tanımlanırlar. Örnek olarak kullanıcıya bir menü gösteren şu satırlara bakalım: -) - ---- - writeln(" 0 Çıkış"); - writeln(" 1 Toplama"); - writeln(" 2 Çıkarma"); - writeln(" 3 Çarpma"); - writeln(" 4 Bölme"); ---- - -$(P -Onların hepsine birden $(C menüyüGöster) gibi bir isim verilebileceği için onları bir işlev olarak şu şekilde bir araya getirebiliriz: -) - ---- -$(CODE_NAME menüyüGöster)void menüyüGöster() { - writeln(" 0 Çıkış"); - writeln(" 1 Toplama"); - writeln(" 2 Çıkarma"); - writeln(" 3 Çarpma"); - writeln(" 4 Bölme"); -} ---- - -$(P -Artık o işlevi $(C main) içinden kısaca ismiyle işletebiliriz: -) - ---- -$(CODE_XREF menüyüGöster)import std.stdio; - -void main() { - menüyüGöster(); - - // ... diğer işlemler ... -} ---- - -$(P -$(C menüyüGöster) ile $(C main)'in tanımlarındaki benzerliğe bakarak $(C main)'in de bir işlev olduğunu görebilirsiniz. İsmi İngilizce'de "ana işlev" kullanımındaki "ana" anlamına gelen $(C main), D programlarının ana işlevidir. D programlarının işleyişi bu işlevle başlar ve programcının istediği şekilde başka işlevlere dallanır. -) - -$(H5 $(IX parametre) Parametreler) - -$(P -İşlevlerin güçlü yanlarından birisi, yaptıkları işlerin belirli ölçüde ayarlanabiliyor olmasından gelir. -) - -$(P -Yine yumurta örneğine dönelim, ve bu sefer beş yumurta yapmak isteyelim. İzlenmesi gereken adımlar aslında bu durumda da aynıdır; tek farkları, yumurta sayısındadır. Daha önce dörde indirgediğimiz adımları beş yumurtaya uygun olarak şöyle değiştirebiliriz: -) - -$(UL -$(LI $(HILITE beş yumurtalık) malzeme hazırla) -$(LI ateşi aç) -$(LI yumurta$(HILITE lar)ı pişir) -$(LI ateşi söndür) -) - -$(P -Teke indirgediğimiz adım da şöyle değişir: -) - -$(UL -$(LI yağda $(HILITE beş) yumurta yap) -) - -$(P -Temelde aynı olan yumurta pişirme işiyle ilgili bir bilgi ek olarak belirtilmektedir: "beş yumurta çıkart" veya "beş yumurta yap" gibi. -) - -$(P -$(IX , (virgül), işlev parametre listesi) İşlevlerin davranışları da benzer şekilde ayarlanabilir. İşlevlerin işlerini bu şekilde etkileyen bilgilere $(I parametre) denir. Parametreler, parametre listesinde virgüllerle ayrılarak bildirilirler. Parametre listesi, işlevin isminden hemen sonra yazılan parantezin içidir. -) - -$(P -Daha önceki $(C menüyüGöster) işlevinde parametre parantezini boş olarak tanımlamıştık çünkü o işlev her zaman için aynı menüyü göstermekteydi. Menüdeki ilk seçeneğin hep "Çıkış" olması yerine, duruma göre değişen bir seçenek olmasını istesek, bunu bir parametreyle sağlayabiliriz. Örneğin ilk seçeneği bazı durumlarda "Geri Dön" olarak yazdırmak için bu bilgiyi bir parametre olarak tanımlayabiliriz: -) - ---- -void menüyüGöster($(HILITE string ilkSeçenek)) { - writeln(" 0 ", ilkSeçenek); - writeln(" 1 Toplama"); - writeln(" 2 Çıkarma"); - writeln(" 3 Çarpma"); - writeln(" 4 Bölme"); -} ---- - -$(P -$(C ilkSeçenek) parametresi bu örnekte bir dizgi olduğu için türünü de $(C string) olarak belirledik. Bu işlevi artık değişik dizgilerle işleterek menünün ilk satırının farklı olmasını sağlayabiliriz. Tek yapmamız gereken, parametre değerini parantez içinde belirtmektir: -) - ---- - menüyüGöster("Çıkış"); - menüyüGöster("Geri Dön"); ---- - -$(P -Not: Burada parametrenin türüyle ilgili bir sorunla karşılaşabilirsiniz: Bu işlev yukarıda yazıldığı haliyle $(C char[]) türünde dizgilerle kullanılamaz. Örneğin, $(C char[]) ve $(C string) uyumlu olmadıklarından aşağıdaki kod derleme hatasına neden olur: -) - ---- - char[] birSeçenek; - birSeçenek ~= "Kare Kök Al"; - menüyüGöster(birSeçenek); $(DERLEME_HATASI) ---- - -$(P -Öte yandan, $(C menüyüGöster)'in tanımında parametrenin türünü $(C char[]) olarak belirlediğinizde de işlevi $(STRING "Çıkış") gibi bir $(C string) değeriyle çağıramazsınız. $(C immutable) ile ilgili olan bu konuyu bir sonraki bölümde göreceğiz. -) - -$(P -Biraz daha ileri gidelim ve seçenek numaralarının hep 0 ile değil, duruma göre değişik bir değerle başlamasını istiyor olalım. Bu durumda başlangıç numarasını da parametre olarak verebiliriz. Parametreler virgüllerle ayrılırlar: -) - ---- -void menüyüGöster(string ilkSeçenek$(HILITE, int ilkNumara)) { - writeln(' ', ilkNumara, ' ', ilkSeçenek); - writeln(' ', ilkNumara + 1, " Toplama"); - writeln(' ', ilkNumara + 2, " Çıkarma"); - writeln(' ', ilkNumara + 3, " Çarpma"); - writeln(' ', ilkNumara + 4, " Bölme"); -} ---- - -$(P -O işleve hangi numarayla başlayacağını artık biz bildirebiliriz: -) - ---- - menüyüGöster("Geri Dön"$(HILITE , 1)); ---- - -$(H5 İşlev çağırmak) - -$(P -İşlevin işini yapması için başlatılmasına işlevin $(I çağrılması) denir. İşlev çağrısının söz dizimi şöyledir: -) - ---- - $(I işlevin_ismi)($(I parametre_değerleri)) ---- - -$(P -$(IX parametre değeri) İşini yaparken kullanması için işleve verilen bilgilere $(I parametre değeri) denir. Parametre değerleri işlevin tanımındaki parametrelerle bire bir eşleşirler. Örneğin, yukarıdaki $(C menüyüGöster()) işlev çağrısındaki $(C "Geri Dön") ve $(C 1) değerleri sırayla $(C ilkSeçenek) ve $(C ilkNumara) parametrelerine karşılık gelirler. -) - -$(P -Her parametre değerinin türü, karşılık geldiği parametrenin türüne uymalıdır. -) - -$(H5 İş yapmak) - -$(P -Hem daha önceki bölümlerde hem de bu bölümde $(I iş yapmaktan) söz ettim. Program adımlarının, ifadelerin, işlevlerin, $(I iş yaptıklarını) söyledim. İş yapmak, yan etki oluşturmak veya değer üretmek anlamına gelir: -) - -$(UL - -$(LI -$(IX yan etki) $(B Yan etki oluşturmak): Bazı işlemlerin yalnızca yan etkileri vardır. Örneğin çıkışa menü yazdıran $(C menüyüGöster) işlevi çıkışı $(I etkilemektedir); oluşturduğu bir değer yoktur. Başka bir örnek olarak, kendisine verilen bir $(C Öğrenci) nesnesini bir öğrenciler listesine ekleyen bir işlevin etkisi, listenin büyümesidir. Onun da ürettiği bir değer yoktur. - -$(P -Genel olarak, programın durumunda bir değişikliğe neden olan bir işlemin yan etkisinin olduğu söylenir. -) - -) - -$(LI -$(B Değer üretmek): Bazı işlemler yalnızca değer üretirler. Örneğin toplama işleminin sonucunu veren bir işlev, toplanan değerlerin toplamını $(I üretir). Başka bir örnek olarak; isim, adres, vs. gibi kendisine verilen bilgiyi bir araya getirerek bir $(C Öğrenci) nesnesi oluşturan bir işlevin de bir nesne $(I ürettiği) söylenir. - -$(P -Bu tür işlemlerin ayrıca yan etkileri yoktur; programın durumunda hiçbir değişikliğe neden olmazlar; yalnızca değer üretirler. -) - -) - -$(LI -$(B Hem değer üretmek, hem yan etki oluşturmak:) Bazı işlemler hem değer üretirler, hem de yan etkileri vardır. Örneğin girişten okuduğu sayıların toplamını hesaplayan bir işlev, hem toplamın sonucunu üretmektedir; hem de içinden karakterler çıkarttığı için girişi etkilemektedir. -) - -$(LI -$(B Etkisizlik:) Her işlevin normalde yukarıdaki üç gruptan birisine girdiğini söyleyebiliriz: değer üretirler veya yan etkileri vardır. Buna rağmen, bazı işlevler bazı koşullara bağlı olarak bazen hiç iş yapmayabilirler. -) - -) - -$(H5 $(IX dönüş değeri) İşlevin dönüş değeri) - -$(P -Değer üreten bir işlevin ürettiği değere o işlevin $(I dönüş değeri) denir. Bu terim, işlevin işini bitirdikten sonra bize geri dönmesi gibi bir düşünceden türemiştir. İşlevi "çağırırız" ve "döndürdüğü" değeri kullanırız. -) - -$(P -Her değerin olduğu gibi, dönüş değerinin de türü vardır. Bu tür işlevin isminden önce yazılır. Örneğin iki tane $(C int) değeri toplayan bir işlev, eğer sonuçta yine $(C int) türünde bir değer üretiyorsa, dönüş türü olarak $(C int) yazılır: -) - ---- -$(HILITE int) topla(int birinci, int ikinci) { - // ... yapılan işlemler ... -} ---- - -$(P -İşlevin döndürdüğü değer, sanki o değer işlev çağrısının yerine yazılmış gibi, onun yerine geçer. Örneğin $(C topla(5, 7)) çağrısının değer olarak 12 ürettiğini düşünürsek, şu iki satır birbirinin eşdeğeridir: -) - ---- - writeln("Toplam: ", topla(5, 7)); - writeln("Toplam: ", 12); ---- - -$(P -$(C writeln) çağrılmadan önce, $(C topla(5, 7)) işlevi çağrılır ve onun döndürdüğü değer olan 12, yazdırması için $(C writeln)'e parametre olarak verilir. -) - -$(P -Bu sayede işlevlerin değerlerini başka işlevlere parametre olarak verebilir ve daha karmaşık ifadeler oluşturabiliriz: -) - ---- - writeln("Sonuç: ", topla(5, böl(100, kişiSayısı()))); ---- - -$(P -O örnekte $(C kişiSayısı)'nın dönüş değeri $(C böl)'e, $(C böl)'ün dönüş değeri $(C topla)'ya, ve en sonunda da $(C topla)'nın dönüş değeri $(C writeln)'e parametre olarak verilmektedir. -) - -$(H5 $(IX return, deyim) $(C return) deyimi) - -$(P -İşlevin ürettiği değer, "döndür" anlamına gelen $(C return) anahtar sözcüğü ile bildirilir: -) - ---- -int topla(int birinci, int ikinci) { - int toplam = birinci + ikinci; - $(HILITE return) toplam; -} ---- - -$(P -İşlev, gereken işlemleri ve hesapları yaparak dönüş değerini üretir ve en son olarak $(C return) ile döndürür. İşlevin işleyişi de o noktada sona erer ve işlevden dönülmüş olur. -) - -$(P -İşlevlerde birden fazla $(C return) anahtar sözcüğü kullanılabilir. İfade ve deyimlere bağlı olarak önce hangi $(C return) işletilirse, işlevin dönüş değeri olarak o $(C return)'ün döndürdüğü değer kullanılır: -) - ---- -int karmaşıkHesap(int birParametre, int başkaParametre) { - if (birParametre == başkaParametre) { - return 0; - } - - return birParametre * başkaParametre; -} ---- - -$(P -O işlev; iki parametresi birbirlerine eşitse 0 değerini, değilse iki parametrenin çarpımını döndürür. -) - -$(H5 $(IX void, işlev) $(C void) işlevler) - -$(P -Eğer işlev değer üretmeyen bir işlevse, dönüş türü olarak "boşluk, yokluk" anlamına gelen $(C void) yazılır. O yüzden $(C main)'in ve $(C menüyüGöster)'in dönüş türlerini $(C void) olarak yazdık; şimdiye kadar gördüğümüz kadarıyla ikisi de değer üretmeyen işlevlerdir. -) - -$(P -$(I Not: $(C main) aslında $(C int) de döndürebilir. Bunu $(LINK2 /ders/d/main.html, sonraki bir bölümde) göreceğiz. -) -) - -$(H5 İşlevin ismi) - -$(P -İşlevin ismi, programcı tarafından işlevin yaptığı işi açıklayacak şekilde seçilmelidir. Örneğin iki sayıyı toplayan işlevin ismini $(C topla) olarak seçtik; veya menüyü gösteren işleve $(C menüyüGöster) dedik. -) - -$(P -İşlevlere isim verirken izlenen bir kural, isimleri $(C topla)'da ve $(C menüyüGöster)'de olduğu gibi ikinci tekil şahıs emir kipinde seçmektir. Yani $(C toplam)'da ve $(C menü)'de olduğu gibi isim halinde değil. Böylece işlevin bir eylemde bulunduğu isminden anlaşılır. -) - -$(P -Öte yandan hiçbir yan etkileri olmayan, yani yalnızca değer üreten işlevlere içinde eylem bulunmayan isimler de seçilebilir. Örneğin şu andaki hava sıcaklığını veren bir işlev için $(C havaSıcaklığınıVer) yerine $(C havaSıcaklığı) gibi bir ismin daha uygun olduğunu düşünebilirsiniz. -) - -$(P -İşlev, nesne, değişken, vs. isim seçimlerinin programcılığın $(I sanat) tarafında kaldığını düşünebilirsiniz. İşe yarar, yeterince kısa, ve programdaki diğer isimlerle tutarlı olan isimler bulmak bazen yaratıcılık gerektirir. -) - -$(H5 İşlevlerin kod kalitesine etkileri) - -$(P -İşlevlerin kod kalitesine etkileri büyüktür. İşlevlerin küçük olmaları ve sorumluluklarının az olması programların bakımlarını kolaylaştırır. -) - -$(P -Programı bir bütün halinde $(C main) içinde yazmak yerine küçük parçalara ayırmak bütün programı kolaylaştırır. Küçük işlevlerin birim olarak işlemleri de basit olacağından, teker teker yazılmaları çok daha kolay olur. Programın diğer işlemleri bu yapı taşları üzerine kurulunca bütün program daha kolay yazılır. Daha da önemlisi, programda daha sonradan gereken değişiklikler de çok daha kolay hale gelirler. -) - -$(H6 $(IX kod tekrarı) Kod tekrarından kaçının) - -$(P -Programcılıkta kaçınılması gereken bir eylem, kod tekrarıdır. Kod tekrarı, aynı işi yapan işlemlerin programda birden fazla yerde tekrarlanması anlamına gelir. -) - -$(P -Bu tekrar bazen bilinçli olarak satırların bir yerden başka bir yere kopyalanması ile yapılabilir. Bazen de farkında olmadan, aynı işlemlerin aynı şekilde kodlanmaları şeklinde ortaya çıkabilir. -) - -$(P -Kod tekrarının sakıncalarından birisi; tekrarlanan işlemlerdeki olası hataların bütün kopyalarda da bulunması, ve aynı hatanın her kopyada giderilmesinin gerekmesidir. Oysa; tekrarlanan kod tek bir işlev içinde bulunuyor olsa, hatayı yalnızca bir kere gidermek yeter. -) - -$(P -Yukarıda işlevlerin ustalıkla ilgili olduklarına değinmiştim. Usta programcılar koddaki işlemler arasındaki benzerlikleri yakalamaya ve kod tekrarını ortadan kaldırmaya çalışırlar. -) - -$(P -Bir örnek olarak, girişten aldığı sayıları önce girişten geldikleri sırada, sonra da sıralanmış olarak yazdıran şu programa bakalım: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - int[] sayılar; - - int adet; - write("Kaç sayı gireceksiniz? "); - readf(" %s", &adet); - - // Sayıları oku - foreach (i; 0 .. adet) { - int sayı; - write("Sayı ", i, "? "); - readf(" %s", &sayı); - - sayılar ~= sayı; - } - - // Diziyi çıkışa yazdır - writeln("Sıralamadan önce:"); - foreach (i, sayı; sayılar) { - writefln("%3d:%5d", i, sayı); - } - - sort(sayılar); - - // Diziyi çıkışa yazdır - writeln("Sıraladıktan sonra:"); - foreach (i, sayı; sayılar) { - writefln("%3d:%5d", i, sayı); - } -} ---- - -$(P -Kod tekrarını görüyor musunuz? Diziyi yazdırmak için kullanılan son iki $(C foreach) döngüsü birbirinin aynısı. $(C yazdır) ismiyle bir işlev tanımlasak ve yazdırmasını istediğimiz diziyi de parametre olarak versek, bu kod tekrarını ortadan kaldırmış oluruz: -) - ---- -void yazdır(int[] dizi) { - foreach (i, eleman; dizi) { - writefln("%3s:%5s", i, eleman); - } -} ---- - -$(P -Dikkat ederseniz, parametrenin ismi olarak $(C sayılar) yerine ondan daha genel olan $(C dizi) ismini seçtik. Bunun nedeni, bu işlev bağlamında dizi yazdırmak dışında bir şey bilmiyor olduğumuzdur. Dizinin elemanlarının ne olduklarından bu işlev içinde haberimiz yoktur. Dizideki $(C int) elemanların ne anlama geldiklerini ancak bu işlevi çağıran kapsam bilir: belki öğrenci kayıt numaralarıdır, belki bir şifrenin parçalarıdır, belki bir grup insanın yaşlarıdır... Ne olduklarını $(C yazdır) işlevi içinde bilemediğimiz için, ancak $(C dizi) ve $(C eleman) gibi genel isimler kullanabiliyoruz. -) - -$(P -Şimdi kod biraz daha düzenli bir hale gelir: -) - ---- -import std.stdio; -import std.algorithm; - -void yazdır(int[] dizi) { - foreach (i, eleman; dizi) { - writefln("%3s:%5s", i, eleman); - } -} - -void main() { - int[] sayılar; - - int adet; - write("Kaç sayı gireceksiniz? "); - readf(" %s", &adet); - - // Sayıları oku - foreach (i; 0 .. adet) { - int sayı; - write("Sayı ", i, "? "); - readf(" %s", &sayı); - - sayılar ~= sayı; - } - - // Diziyi çıkışa yazdır - writeln("Sıralamadan önce:"); - $(HILITE yazdır(sayılar)); - - sort(sayılar); - - // Diziyi çıkışa yazdır - writeln("Sıraladıktan sonra:"); - $(HILITE yazdır(sayılar)); -} ---- - -$(P -İşimiz bitmedi: iki $(C yazdır) çağrısından önce birer de başlık yazdırılıyor. Yazdırılan dizgi farklı olsa da işlem aynıdır. Eğer başlığı da $(C yazdır)'a parametre olarak verirsek, başlığı yazdırma tekrarından da kurtulmuş oluruz. Programın yalnızca değişen bölümlerini gösteriyorum: -) - ---- -void yazdır($(HILITE string başlık, )int[] dizi) { - writeln(başlık, ":"); - - foreach (i, eleman; dizi) { - writefln("%3s:%5s", i, eleman); - } -} - -// ... - - // Diziyi çıkışa yazdır - yazdır("Sıralamadan önce", sayılar); - -// ... - - // Diziyi çıkışa yazdır - yazdır("Sıraladıktan sonra", sayılar); ---- - -$(P -Bu işlemin $(C yazdır)'dan önceki açıklama satırlarını da gereksiz hale getirdiğini görebiliriz. İşlemlere $(C yazdır) diye açıklayıcı bir isim verdiğimiz için, ayrıca "Diziyi çıkışa yazdır" gibi bir açıklamaya da gerek kalmamış oluyor. Şimdi programın son satırları şöyle kısaltılabilir: -) - ---- - yazdır("Sıralamadan önce", sayılar); - sort(sayılar); - yazdır("Sıraladıktan sonra", sayılar); ---- - -$(P -Bu programda bir kod tekrarı daha var: girişten $(C adet) ve $(C sayı) için aynı şekilde tamsayı okunuyor. Tek farkları, kullanıcıya gösterilen mesaj ve değişkenin ismi: -) - ---- - int adet; - write("Kaç sayı gireceksiniz? "); - readf(" %s", &adet); - -// ... - - int sayı; - write("Sayı ", i, "? "); - readf(" %s", &sayı); ---- - -$(P -$(C sayıOku) diye bir işlev yazarsak ve kullanıcıya gösterilecek mesajı bir parametre olarak alırsak, kod çok daha temiz bir hale gelir. Bu sefer bu işlevin girişten okuduğu değeri döndürmesi gerektiğine dikkat edin: -) - ---- -int sayıOku(string mesaj) { - int sayı; - write(mesaj, "? "); - readf(" %s", &sayı); - return sayı; -} ---- - -$(P -Bu işlevi çağırarak $(C adet)'in değerini okumak kolay. $(C adet)'i işlevin dönüş değeriyle ilkleyebiliriz: -) - ---- - int adet = sayıOku("Kaç sayı gireceksiniz"); ---- - -$(P -$(C sayı)'yı okurken kullanılan mesaj döngü sayacı olan $(C i)'yi de içerdiğinden o mesaj $(C std.string) modülündeki $(C format)'tan yararlanılarak her $(C i) için farklı olarak oluşturulabilir: -) - ---- -import std.string; -// ... - int sayı = sayıOku(format("Sayı %s", i)); ---- - -$(P -$(C sayı)'nın $(C foreach) içinde tek bir yerde kullanıldığını görerek $(C sayı)'nın tanımını da tamamen kaldırabilir ve onun kullanıldığı tek yerde doğrudan $(C sayıOku) işlevini çağırabiliriz. Böylece döngü içindeki satırlar da azalmış olur: -) - ---- - foreach (i; 0 .. adet) { - sayılar ~= $(HILITE sayıOku)(format("Sayı %s", i)); - } ---- - -$(P -Ben bu programda son bir değişiklik daha yapacağım ve sayıların okunmasıyla ilgili bütün işlemleri tek bir işleve taşıyacağım. Böylece "Sayıları oku" açıklaması da ortadan kalkacak; çünkü yeni işlevin ismi zaten ne işlem yapılmakta olduğunu açıklayacak. -) - -$(P -$(C sayılarıOku) ismini verebileceğimiz bu işlevin hiçbir parametre alması gerekmez, ama değer olarak bütün diziyi üretirse ismi ile de uyumlu bir kullanımı olur.) - -$(P -Böylece bütün program son olarak şöyle yazılabilir: -) - ---- -import std.stdio; -import std.string; -import std.algorithm; - -void yazdır(string başlık, int[] dizi) { - writeln(başlık, ":"); - - foreach (i, eleman; dizi) { - writefln("%3s:%5s", i, eleman); - } -} - -int sayıOku(string mesaj) { - int sayı; - write(mesaj, "? "); - readf(" %s", &sayı); - return sayı; -} - -int[] $(HILITE sayılarıOku)() { - int[] sayılar; - - int adet = sayıOku("Kaç sayı gireceksiniz"); - - foreach (i; 0 .. adet) { - sayılar ~= sayıOku(format("Sayı %s", i)); - } - - return sayılar; -} - -void main() { - int[] sayılar = sayılarıOku(); - yazdır("Sıralamadan önce", sayılar); - sort(sayılar); - yazdır("Sıraladıktan sonra", sayılar); -} ---- - -$(P -Programın bu halini ilk haliyle karşılaştırın. Yeni programda $(C main) işlevi içinde programın ana adımları açık bir şekilde anlaşılmaktadır. Oysa ilk halinde programın ne yaptığını ancak kodları ve açıklamaları okuyarak anlamak zorunda kalıyorduk. -) - -$(P -Programın son halinde daha fazla satır bulunuyor olması sizi yanıltmasın. İşlevler aslında kodu küçültürler, ama bunu çok kısa olan bu programda göremiyoruz. Örneğin $(C sayıOku) işlevini yazmadan önce girişten tamsayı okumak için her seferinde 3 satır kod yazıyorduk. Şimdi ise $(C sayıOku)'yu çağırarak her noktadaki kod satırı sayısını 1'e indirmiş olduk. Hatta, $(C foreach) döngüsü içindeki $(C sayı)'nın tanımını da tamamen kaldırabildik. -) - -$(H6 Açıklamalı kod satırlarını işlevlere dönüştürün) - -$(P -Eğer programdaki işlemlerin bazılarının ne yaptıklarını açıklama satırları yazarak açıklama gereği duyuyorsanız, belki de o işlemlerin bir işleve taşınmaları zamanı gelmiştir. İşlevin ismini açıklayıcı olarak seçmek, açıklama satırının gereğini de ortadan kaldırır. -) - -$(P -Yukarıdaki programdaki üç açıklama satırından bu sayede kurtulmuş olduk. -) - -$(P -Açıklama satırlarından kurtulmanın önemli başka bir nedeni daha vardır: açıklama satırları zaman içinde kodun ne yaptığı hakkında yanlış bilgi vermeye başlarlar. Baştan iyi niyetle ve doğru olarak yazılan açıklama satırı, kod değiştiğinde unutulur ve zamanla koddan ilgisiz hale gelebilir. Artık açıklama satırı ya yanlış bilgi veriyordur, ya da tamamen işe yaramaz durumdadır. Bu yüzden programları açıklama satırlarına gerek bırakmadan yazmaya çalışmak önemlidir. -) - -$(PROBLEM_COK - -$(PROBLEM $(C menüyüGöster) işlevini bütün seçeneklerini bir dizi olarak alacak şekilde değiştirin. Örneğin şu şekilde çağırabilelim: - ---- - string[] seçenekler = - [ "Siyah", "Kırmızı", "Yeşil", "Mavi", "Beyaz" ]; - menüyüGöster(seçenekler, 1); ---- - -$(P -Çıktısı şöyle olsun: -) - -$(SHELL  1 Siyah - 2 Kırmızı - 3 Yeşil - 4 Mavi - 5 Beyaz -) - -) - -$(PROBLEM -İki boyutlu bir diziyi bir resim kağıdı gibi kullanan bir program yazdım. Siz bu programı istediğiniz şekilde değiştirin: - ---- -import std.stdio; - -enum satırAdedi = 20; -enum sütunAdedi = 60; - -/* alias, "takma isim" anlamına gelir. Programın geri - * kalanında hep dchar[sütunAdedi] yazmak yerine, daha - * açıklayıcı olarak 'Satır' yazabilmemizi sağlıyor. - * - * Dikkat ederseniz Satır "sabit uzunluklu dizi" türüdür. */ -alias Satır = dchar[sütunAdedi]; - -/* Bir Satır dilimine de kısaca 'Kağıt' takma ismini - * veriyoruz. */ -alias Kağıt = Satır[]; - -/* Verilen kağıdı satır satır ve kare kare çıkışa gönderir. */ -void kağıdıGöster(Kağıt kağıt) { - foreach (satır; kağıt) { - writeln(satır); - } -} - -/* Verilen kağıdın belirtilen yerine bir benek koyar; bir - * anlamda o kareyi "boyar". */ -void benekKoy(Kağıt kağıt, int satır, int sütun) { - kağıt[satır][sütun] = '#'; -} - -/* Kağıdın belirtilen yerinden aşağıya doğru, belirtilen - * uzunlukta çizgi çizer. */ -void düşeyÇizgiÇiz(Kağıt kağıt, int satır, - int sütun, int uzunluk) { - foreach (çizilecekSatır; satır .. satır + uzunluk) { - benekKoy(kağıt, çizilecekSatır, sütun); - } -} - -void main() { - Satır boşSatır = '.'; - - /* Hiç satırı bulunmayan bir kağıt */ - Kağıt kağıt; - - /* Ona boş satırlar ekliyoruz */ - foreach (i; 0 .. satırAdedi) { - kağıt ~= boşSatır; - } - - /* Ve kullanmaya başlıyoruz */ - benekKoy(kağıt, 7, 30); - düşeyÇizgiÇiz(kağıt, 5, 10, 4); - - kağıdıGöster(kağıt); -} ---- - -) - -) - -Macros: - SUBTITLE=İşlevler - - DESCRIPTION=D dilinde işlevler (fonksiyonlar) [functions] - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial işlev fonksiyon function - -SOZLER= -$(deger) -$(degisken) -$(degismez) -$(deyim) -$(dizgi) -$(donus_degeri) -$(ifade) -$(islev) -$(nesne) -$(parametre) -$(parametre_degeri) -$(yan_etki) diff --git a/ddili/src/ders/d/islevler_diger.d b/ddili/src/ders/d/islevler_diger.d deleted file mode 100644 index d846a0b..0000000 --- a/ddili/src/ders/d/islevler_diger.d +++ /dev/null @@ -1,984 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Diğer İşlev Olanakları) - -$(P -İşlevleri daha önce aşağıdaki bölümlerde görmüştük: -) - -$(UL -$(LI $(LINK2 /ders/d/islevler.html, İşlevler)) - -$(LI $(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri)) - -$(LI $(LINK2 /ders/d/islev_yukleme.html, İşlev Yükleme)) - -$(LI $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri ve Kapamalar)) - -) - -$(P -Burada o bölümlerde yer almayan başka işlev olanaklarını göreceğiz. -) - -$(H5 Dönüş türü olanakları) - -$(P -İşlevler $(C auto), $(C ref), $(C inout), ve $(C auto ref) olarak bildirilebilirler. Bunlar işlevlerin dönüş türleriyle ilgilidir. -) - -$(H6 $(IX auto, dönüş türü) $(IX auto işlev) $(C auto) işlevler) - -$(P -$(C auto) olarak bildirilen işlevlerin dönüş türlerinin açıkça yazılması gerekmez: -) - ---- -$(HILITE auto) topla(int birinci, double ikinci) { - double sonuç = birinci + ikinci; - return sonuç; -} ---- - -$(P -Derleyici dönüş türünü $(C return) satırından otomatik olarak çıkarsar. Yukarıdaki işlevin $(C return) ile döndürdüğü sonuç $(C double) olduğundan, o işlev sanki dönüş türü açıkça $(C double) yazılmış gibi derlenir. -) - -$(P -Birden fazla $(C return) deyimi bulunduğunda işlevin dönüş türü o dönüş ifadelerinin $(C ortak türüdür). (Ortak türü $(LINK2 /ders/d/uclu_islec.html, Üçlü İşleç ?:) bölümünde görmüştük.) Örneğin, $(C int) ve $(C double) türlerinin ortak türü $(C double) olduğundan aşağıdaki $(C auto) işlevin dönüş türü $(C double)'dır: -) - ---- -auto işlev(int i) { - if (i < 0) { - return i; // Burada 'int' döndürülüyor - } - - return i * 1.5; // Burada 'double' döndürülüyor -} - -void main() { - // İşlevin dönüş türü, 'int' ve 'double' türlerinin ortak - // türü olan 'double' türüdür - auto sonuç = işlev(42); - static assert(is (typeof(sonuç) == $(HILITE double))); -} ---- - -$(H6 $(IX ref, dönüş türü) $(C ref) işlevler) - -$(P -İşlevlerin döndürdükleri değerler normalde işlevi çağıran tarafa kopyalanırlar. $(C ref) belirteci, dönüş değerinin kopyalanmak yerine referans olarak döndürülmesini sağlar. -) - -$(P -Örneğin, aşağıdaki işlev kendisine verilen iki parametreden büyük olanını döndürmektedir: -) - ---- -$(CODE_NAME büyüğü)int büyüğü(int birinci, int ikinci) { - return (birinci > ikinci) ? birinci : ikinci; -} ---- - -$(P -O işlevin hem parametreleri hem de dönüş değeri normalde kopyalanır: -) - ---- -$(CODE_XREF büyüğü)import std.stdio; - -void main() { - int a = 1; - int b = 2; - int sonuç = büyüğü(a, b); - sonuç += 10; // ← ne a ne de b etkilenir - writefln("a: %s, b: %s, sonuç: %s", a, b, sonuç); -} ---- - -$(P -$(C büyüğü) işlevinin dönüş değeri $(C sonuç) değişkenine kopyalandığından, değişkenin arttırılması yalnızca $(C sonuç) isimli kopyayı etkiler. İşleve kendileri de zaten kopyalanarak geçirilmiş olan $(C a) ve $(C b) değişmezler: -) - -$(SHELL_SMALL -a: 1, b: 2, sonuç: 12 -) - -$(P -Parametrelerin kopyalanmak yerine referans olarak gönderilmeleri için $(C ref) anahtar sözcüğünün kullanıldığını biliyorsunuz. Aynı sözcük dönüş türü için de kullanılabilir ve işlevin dönüş değerinin de referans olarak döndürülmesini sağlar: -) - ---- -$(HILITE ref) int büyüğü($(HILITE ref) int birinci, $(HILITE ref) int ikinci) { - return (birinci > ikinci) ? birinci : ikinci; -} ---- - -$(P -Bu durumda döndürülen referans parametrelerden birisinin takma ismi yerine geçecek ve onda yapılan değişiklik artık ya $(C a)'yı ya da $(C b)'yi değiştirecektir: -) - ---- - int a = 1; - int b = 2; - büyüğü(a, b) += 10; // ← ya a ya b etkilenir - writefln("a: %s, b: %s", a, b); ---- - -$(P -Dikkat ederseniz, işlevin döndürdüğü referansı $(C sonuç) diye bir değişken kullanmadan doğrudan arttırıyoruz. O işlem, $(C a) ve $(C b)'den büyük olanını etkiler: -) - -$(SHELL_SMALL -a: 1, b: $(HILITE 12) -) - -$(P -$(IX gösterge) $(IX yerel değişken) $(IX değişken, yerel) $(B Yerel referans için gösterge gerekir:) Burada bir noktaya dikkatinizi çekmek istiyorum. $(C ref) kullanılmış olmasına rağmen, o dönüş değerinin yerel bir değişkene atanması $(C a) veya $(C b)'yi yine de değiştirmez: -) - ---- - int sonuç = büyüğü(a, b); - sonuç += 10; // ← yine yalnızca 'sonuç' değişir ---- - -$(P -$(C büyüğü) işlevi $(C a)'ya veya $(C b)'ye referans döndürüyor olsa da, o referans $(C sonuç) ismindeki yerel değişkene kopyalandığından $(C a) veya $(C b) değişmez: -) - -$(SHELL_SMALL -a: 1, b: 2, sonuç: 12 -) - -$(P -$(C sonuç)'un $(C a)'nın veya $(C b)'nin referansı olması istendiğinde gösterge olarak tanımlanması gerekir: -) - ---- - int $(HILITE *) sonuç = $(HILITE &)büyüğü(a, b); - $(HILITE *)sonuç += 10; - writefln("a: %s, b: %s, sonuç: %s", a, b, $(HILITE *)sonuç); ---- - -$(P -$(C sonuç) artık ya $(C a)'ya ya da $(C b)'ye erişim sağladığından, onun aracılığıyla yapılan değişiklik o ikisinin büyük olanını etkilerdi: -) - -$(SHELL_SMALL -a: 1, b: $(HILITE 12), sonuç: 12 -) - -$(P -$(B Yerel değişkene referans döndürülemez:) Yukarıdaki $(C ref) dönüş değeri daha işlev çağrılmadan önce yaşamaya başlayan iki değişkenden birisinin takma ismi gibi çalışmaktadır. Bir başka deyişle, $(C birinci) de döndürülse $(C ikinci) de döndürülse o dönüş değeri hep işlevin çağrıldığı noktada zaten yaşamakta olan $(C a)'nın veya $(C b)'nin referansıdır. -) - -$(P -Yaşam süresi işlevden çıkılırken sona erecek olan bir değişkene referans döndürülemez: -) - ---- -$(HILITE ref) string parantezİçinde(string söz) { - string sonuç = '(' ~ söz ~ ')'; - return sonuç; $(DERLEME_HATASI) -} // ← sonuç'un yaşamı burada sona erer ---- - -$(P -İşlev kapsamında tanımlanmış olan $(C sonuç)'un yaşamı o işlevden çıkıldığında sona erer. O yüzden, o değişkenin takma ismi gibi kullanılacak bir dönüş değeri olamaz. -) - -$(P -Derleme, yerel değişkene referans döndürüldüğünü bildiren bir hata ile sonlanır: -) - -$(SHELL_SMALL -Error: escaping $(HILITE reference to local variable) sonuç -) - -$(H6 $(IX auto ref, dönüş türü) $(C auto ref) işlevler) - -$(P -Yukarıdaki $(C parantezİçinde) işlevinin yaşam süresi sona eren yerel değişken nedeniyle derlenemediğini gördük. $(C auto ref) öyle durumlarda yararlıdır. -) - -$(P -$(C auto ref) olarak bildirilmiş olan bir işlevin dönüş türü $(C auto) işlevlerde olduğu gibi otomatik olarak çıkarsanır. Ek olarak, referans olamayacak bir değer döndürüldüğünde o değer referans olarak değil, kopyalanarak döndürülür. -) - -$(P -Aynı işlevi $(C auto ref) olarak yazdığımızda program derlenir: -) - ---- -$(HILITE auto ref) parantezİçinde(string söz) { - string sonuç = '(' ~ söz ~ ')'; - return sonuç; // ← derlenir -} ---- - -$(P -İşlevin değer mi yoksa referans mı döndüreceği içindeki ilk $(C return) deyimi tarafından belirlenir. -) - -$(P -$(C auto ref) daha çok parametrelerin duruma göre referans veya kopya olabildikleri işlev şablonlarında yararlıdır. -) - -$(H6 $(IX inout, dönüş türü) $(C inout) işlevler) - -$(P -Bu belirteç işlev parametrelerinde ve dönüş türünde kullanılır ve o işlevin parametrelerine bağlı olarak $(C const), $(C immutable), veya $(I değişebilen) anlamına gelir. -) - -$(P -Yukarıdaki işlevi $(C string) alacak ve $(C string) döndürecek şekilde tekrar yazalım: -) - ---- -string parantezİçinde(string söz) { - return '(' ~ söz ~ ')'; -} ---- - -$(P -O işleve $(C string) türünde parametre verilmesi gerektiğini ve sonucunun $(C string) olduğunu biliyoruz: -) - ---- - writeln(parantezİçinde("merhaba")); ---- - -$(P -$(STRING "merhaba") hazır değerinin türü $(C string), yani $(C immutable(char)[]) olduğundan o kod derlenir ve çalışır: -) - -$(SHELL_SMALL -(merhaba) -) - -$(P -Burada kullanışsız bir durum vardır: O işlev $(C string) türüne bağlı olarak yazıldığından $(C immutable) olmayan bir dizgi ile çağrılamaz: -) - ---- - char[] dizgi; // elemanları değişebilir - dizgi ~= "selam"; - writeln(parantezİçinde(dizgi)); $(DERLEME_HATASI) ---- - -$(P -Derleme hatası, $(I değişebilen) karakterlerden oluşan $(C char[]) türünün $(C string)'e dönüştürülemeyeceğini bildirir: -) - -$(SHELL_SMALL -Error: function deneme.parantezİçinde ($(HILITE string) söz) -is not callable using argument types ($(HILITE char[])) -) - -$(P -Aynı sorun $(C const(char)[]) dizgilerinde de vardır. -) - -$(P -Bu sorunu çözmek için bir kaç yöntem düşünülebilir. Bir yöntem, işlevi $(I değişebilen) ve $(C const) karakter dizileri için de yüklemektir: -) - ---- -char[] parantezİçinde(char[] söz) { - return '(' ~ söz ~ ')'; -} - -const(char)[] parantezİçinde(const(char)[] söz) { - return '(' ~ söz ~ ')'; -} ---- - -$(P -Bunun programcılıkta kaçınılması gereken $(I kod tekrarı) anlamına geldiğini görüyoruz. Bu işlevlerin ileride gelişebileceklerini veya olası hatalarının giderilebileceklerini düşünürsek, o değişikliklerin üçünde de yapılmasının unutulmaması gerekecektir. O yüzden bu riskli bir tasarımdır. -) - -$(P -Başka bir yöntem, işlevi şablon olarak tanımlamaktır: -) - ---- -T parantezİçinde(T)(T söz) { - return '(' ~ söz ~ ')'; -} ---- - -$(P -O çözüm şablon içindeki kullanıma uyan her tür ile kullanılabilir. Bunun bazen fazla esnek olabileceğini ve şablon kısıtlamaları kullanılmasının gerekebileceğini de önceki bölümde görmüştük. -) - -$(P -$(C inout) yöntemi şablon çözümüne çok benzer ama bütün türü değil, yalnızca türün $(C const), $(C immutable), veya $(I değişebilen) özelliğini esnek bırakır. O özelliği işlevin parametrelerinden otomatik olarak çıkarsar: -) - ---- -$(HILITE inout)(char)[] parantezİçinde($(HILITE inout)(char)[] söz) { - return '(' ~ söz ~ ')'; -} ---- - -$(P -$(C inout), parametreden otomatik olarak çıkarsanan özelliği dönüş türüne de aktarır. -) - -$(P -O işlev $(C char[]) türüyle çağrıldığında hiç $(C inout) yazılmamış gibi derlenir. $(C immutable(char)[]) veya $(C const(char)[]) türleriyle çağrıldığında ise $(C inout) sırasıyla $(C immutable) veya $(C const) yerine geçer. Bunu işlevin dönüş türünü yazdırarak görebiliriz: -) - ---- - char[] değişebilen; - writeln(typeof(parantezİçinde(değişebilen)).stringof); - - const(char)[] sabit; - writeln(typeof(parantezİçinde(sabit)).stringof); - - immutable(char)[] değişmez; - writeln(typeof(parantezİçinde(değişmez)).stringof); ---- - -$(P -Üç çağrının farklı dönüş türleri: -) - -$(SHELL_SMALL -char[] -const(char)[] -string -) - -$(P -Özetle, $(C inout) parametre türünün $(C const), $(C immutable), veya $(I değişebilen) özelliğini dönüş türüne aktarır. -) - -$(H5 Davranış olanakları) - -$(P -$(C pure), $(C nothrow), ve $(C @nogc) işlevlerin davranışlarıyla ilgilidir. -) - -$(H6 $(IX pure) $(C pure) işlevler) - -$(P -İşlevlerin değer üretebildiklerini ve yan etki oluşturabildiklerini $(LINK2 /ders/d/islevler.html, İşlevler bölümünde) görmüştük. Değer üretmenin yan etki oluşturmaktan daha yararlı olduğunun kabul edildiğini de o bölümde görmüştük. -) - -$(P -Buna benzeyen başka yararlı bir kavram, bir işlevin saflığıdır. Bu kavramın D'deki tanımı fonksiyonel programlamadaki genel tanımından farklıdır: D'deki tanıma göre, dönüş değerini üretirken veya olası yan etkilerini oluştururken $(I değişebilen) evrensel veya $(C static) değerlere erişmeyen bir işlev saftır. (Giriş çıkış aygıtları da değişebilen evrensel durum olarak kabul edildiklerinden saf işlevler giriş ve çıkış işlemleri de içeremezler.) -) - -$(P -Bir başka deyişle, dönüş değeri ve yan etkileri yalnızca parametrelerine, yerel değişkenlerine, ve değişmez evrensel değerlere bağlı olan bir işlev saftır. -) - -$(P -Dolayısıyla, saflığın D'deki önemli farkı saf işlevlerin parametrelerini değiştirebilmeleridir. -) - -$(P -Ek olarak, programın genel durumunu etkilediği için aslında izin verilmemesi gereken bazı işlemlere de D'nin bir sistem dili olması nedeniyle izin verilir. Bu yüzden, D'de saf işlevler aşağıdaki işlemleri de gerçekleştirebilirler: -) - -$(UL -$(LI $(C new) ifadesi ile nesne oluşturulabilirler.) -$(LI Programı sonlandırabilirler.) -$(LI İşlemcinin kesirli sayı bayraklarına erişebilirler.) -$(LI Hata atabilirler.) -) - -$(P -$(C pure) anahtar sözcüğü bir işlevin bu koşullara uyduğunu bildirir ve bu durumun derleyici tarafından denetlenmesini sağlar. -) - -$(P -Doğal olarak, aynı garantileri vermediklerinden saf olmayan işlevler saf işlevler tarafından çağrılamazlar. -) - -$(P -Aşağıdaki örnek program bu koşullardan bazılarını gösteriyor: -) - ---- -import std.stdio; -import std.exception; - -int değişebilenEvrensel; -const int constEvrensel; -immutable int immutableEvrensel; - -void safOlmayanİşlev() { -} - -int safİşlev(ref int i, int[] dilim) $(HILITE pure) { - // Hata atabilir: - enforce(dilim.length >= 1); - - // Parametrelerini değiştirebilir: - i = 42; - dilim[0] = 43; - - // Değişmez evrensel duruma erişebilir: - i = constEvrensel; - i = immutableEvrensel; - - // new ifadesini kullanabilir: - auto p = new int; - - // Değişebilen evrensel duruma erişemez: - i = değişebilenEvrensel; $(DERLEME_HATASI) - - // Giriş ve çıkış işlemleri uygulayamaz: - writeln(i); $(DERLEME_HATASI) - - static int değişebilenStatik; - - // Değişebilen statik duruma erişemez: - i = değişebilenStatik; $(DERLEME_HATASI) - - // Saf olmayan işlevleri çağıramaz: - safOlmayanİşlev(); $(DERLEME_HATASI) - - return 0; -} - -void main() { - int i; - int[] dilim = [ 1 ]; - safİşlev(i, dilim); -} ---- - -$(P -Bazı saf işlevler parametrelerinde değişiklik de yapmazlar. Dolayısıyla, bu gibi işlevlerin programdaki tek etkileri dönüş değerleridir. Bu açıdan bakıldığında, parametrelerinde değişiklik yapmayan saf işlevlerin belirli parametre değerlerine karşılık hep aynı değeri döndürecekleri belli demektir. Dolayısıyla, böyle bir işlevin dönüş değeri eniyileştirme amacıyla sonradan kullanılmak üzere saklanabilir. (Bu gözlem hem derleyici hem de programcı için yararlıdır.) -) - -$(P -$(IX çıkarsama, pure niteliği) $(IX nitelik çıkarsama, pure) Bir şablonun tam olarak ne çeşit kodlar içereceği ve derleneceği şablon parametrelerine bağlı olduğundan, saf olup olmadığı da şablon parametrelerine bağlı olabilir. Bu yüzden, şablonların saf olup olmadıkları derleyici tarafından otomatik olarak belirlenir. Şablonlarda gerekmese de $(C pure) anahtar sözcüğü istendiğinde yine de belirtilebilir. Benzer biçimde, $(C auto) işlevlerin saflıkları da otomatik olarak çıkarsanır. -) - -$(P -Örneğin, aşağıdaki şablon $(C N)'nin sıfır olduğu durumda saf olmadığından $(C şablon!0)'ın saf olan bir işlev tarafından çağrılması mümkün değildir: -) - ---- -import std.stdio; - -// Bu şablon N sıfır olduğunda saf değildir. -void şablon(size_t N)() { - static if (N == 0) { - // N sıfır olduğunda çıkışa yazdırmaya çalışmaktadır: - writeln("sıfır"); - } -} - -void foo() $(HILITE pure) { - şablon!0(); $(DERLEME_HATASI) -} - -void main() { - foo(); -} ---- - -$(P -Derleyici yukarıdaki şablonun $(C 0) kullanımının saf olmadığını otomatik olarak anlar ve onun saf olan $(C foo) tarafından çağrılmasına izin vermez: -) - -$(SHELL_SMALL -Error: pure function 'deneme.foo' $(HILITE cannot call impure function) -'deneme.şablon!0.şablon' -) - -$(P -Öte yandan, şablonun örneğin $(C 1) değeri ile kullanımı saftır ve kod derlenir: -) - ---- -void foo() $(HILITE pure) { - şablon!1(); // ← derlenir -} ---- - -$(P -$(C writeln) gibi işlevlerin evrensel durumu etkiledikleri için kullanılamadıklarını gördük. Bu gibi kısıtlamalar özellikle hata ayıklama gibi durumlarda büyük sıkıntıya neden olacaklarından $(C debug) olarak işaretlenmiş olan kodlara saf olmasalar bile $(C pure) işlevler içinde izin verilir: -) - ---- -import std.stdio; - -debug size_t fooÇağrılmaSayacı; - -void foo(int i) $(HILITE pure) { - $(HILITE debug) ++fooÇağrılmaSayacı; - - if (i == 0) { - $(HILITE debug) writeln("i sıfır"); - i = 42; - } - - // ... -} - -void main() { - foreach (i; 0..100) { - if ((i % 10) == 0) { - foo(i); - } - } - - debug writefln("foo %s kere çağrıldı", fooÇağrılmaSayacı); -} ---- - -$(P -Yukarıdaki koddaki saf işlev hem değişebilen evrensel bir değişkeni değiştirmekte hem de çıkışa bir mesaj yazdırmaktadır. Bunlara rağmen derlenebilmesinin nedeni, o işlemlerin $(C debug) olarak işaretlenmiş olmalarıdır. -) - -$(P -$(I Not: Hatırlarsanız, o deyimlerin etkinleşmesi için programın $(C -debug) seçeneği ile derlenmesi gerekir.) -) - -$(P -Üye işlevler de saf olabilirler. Saf olmayan bir üye işlevin alt sınıftaki tanımı saf olabilir ama bunun tersi doğru değildir. Bunların örneklerini aşağıdaki kodda görmekteyiz: -) - ---- -interface Arayüz { - void foo() pure; // Alt sınıflar foo'yu saf olarak - // tanımlamak zorundadırlar. - - void bar(); // Alt sınıflar bar'ı isterlerse saf - // olarak da tanımlayabilirler. -} - -class Sınıf : Arayüz { - void foo() pure { // Gerektiği için pure - // ... - } - - void bar() pure { // Gerekmediği halde pure - // ... - } -} ---- - -$(P -Temsilciler ve isimsiz işlevler de saf olabilirler. Hazır değer olarak tanımlandıklarında derleyici saf olup olmadıklarını otomatik olarak çıkarsar: -) - ---- -import std.stdio; - -void foo(int delegate(double) $(HILITE pure) temsilci) { - int i = temsilci(1.5); -} - -void main() { - foo(a => 42); // ← derlenir - - foo((a) { $(DERLEME_HATASI) - writeln("merhaba"); - return 42; - }); -} ---- - -$(P -Yukarıdaki koddaki $(C foo), parametresinin saf bir temsilci olmasını gerektirmektedir. Derleyici $(C a => 42) temsilcisinin saf olduğunu anlar ve birinci çağrıya izin verir. İkinci temsilci ise saf olmadığı için $(C foo)'ya parametre değeri olarak gönderilemez: -) - -$(SHELL_SMALL -Error: function deneme.foo (int delegate(double) $(HILITE pure) temsilci) -is $(HILITE not callable) using argument types (void) -) - - -$(H6 $(IX nothrow) $(IX throw) $(C nothrow) işlevler) - -$(P -D'nin hata düzeneğini $(LINK2 /ders/d/hatalar.html, Hata Yönetimi bölümünde) görmüştük. -) - -$(P -Her işlevin hangi durumda ne tür hata atacağı o işlevin belgesinde belirtilmelidir. Ancak, genel bir kural olarak her işlevin her türden hata atabileceği varsayılabilir. -) - -$(P -Bazı durumlarda ise çağırdığımız işlevlerin ne tür hatalar atabildiklerini değil, kesinlikle hata atmadıklarını bilmek isteriz. Örneğin, belirli adımlarının kesintisiz olarak devam etmesi gereken bir algoritma o adımlar sırasında hata atılmadığından emin olmak isteyebilir. -) - -$(P -$(C nothrow), işlevin hata atmadığını garanti eder: -) - ---- -int topla(int birinci, int ikinci) $(HILITE nothrow) { - // ... -} ---- - -$(P -$(I Not: Hatırlarsanız, "giderilemez derecede hatalı" durumları ifade eden $(C Error) sıradüzeni altındaki hataların yakalanmaları önerilmez. Burada bir işlevin hata atmadığından söz edilirken o işlevin "$(C Exception) sıradüzeni altındaki hatalardan atmadığı" kastediliyor. Yoksa, $(C nothrow) işlevler $(C Error) sıradüzeni altındaki hataları atabilirler.) -) - -$(P -Yukarıdaki işlev ne kendisi hata atabilir ne de hata atabilen bir işlevi çağırabilir: -) - ---- -int topla(int birinci, int ikinci) nothrow { - writeln("topluyorum"); $(DERLEME_HATASI) - return birinci + ikinci; -} ---- - -$(P -Derleme hatası, $(C topla)'nın bu koşula uymadığını bildirir: -) - -$(SHELL_SMALL -Error: function deneme.topla 'topla' is nothrow yet $(HILITE may throw) -) - -$(P -Bunun nedeni, $(C writeln)'in $(C nothrow) olarak bildirilmiş bir işlev olmamasıdır. -) - -$(P -$(IX çıkarsama, nothrow niteliği) $(IX nitelik çıkarsama, nothrow) Derleyici, işlevlerin kesinlikle hata atmayacaklarını da anlayabilir. $(C topla)'nın aşağıdaki tanımında her tür hata yakalandığından $(C nothrow)'un getirdiği garantiler geçerliliğini sürdürür ve işlev $(C nothrow) olmayan işlevleri bile çağırabilir: -) - ---- -int topla(int birinci, int ikinci) nothrow { - int sonuç; - - try { - writeln("topluyorum"); // ← derlenir - sonuç = birinci + ikinci; - - } catch (Exception hata) { // bütün hataları yakalar - // ... - } - - return sonuç; -} ---- - -$(P -Yukarıda belirtildiği gibi, $(C nothrow) belirteci $(C Error) sıradüzeni altındaki hataları kapsamaz. Örneğin, dilim elemanına $(C []) işleci ile erişilirken $(C RangeError) atılabileceği halde aşağıdaki işlev yine de $(C nothrow) olarak tanımlanabilir: -) - ---- -int foo(int[] arr, size_t i) $(HILITE nothrow) { - return 10 * arr$(HILITE [i]); -} ---- - -$(P -$(C pure)'da olduğu gibi, şablonların, temsilcilerin, isimsiz işlevlerin, ve $(C auto) işlevlerin hata atıp atmadıkları otomatik olarak çıkarsanır. -) - -$(H6 $(IX @nogc) $(C @nogc) işlevler) - -$(P -D çöp toplayıcılı bir dildir. Çoğu programda kullanılan çok sayıda değişken ve algoritma, çöp toplayıcıya ait olan dinamik bellek bölgelerini kullanır. Programda kullanımları sona eren dinamik bellek bölgeleri daha sonra $(I çöp toplama) adı verilen bir algoritma ile otomatik olarak sonlandırılır. -) - -$(P -Sık kullanılan bazı D olanakları da çöp toplayıcıdan yararlanır. Örneğin, dizi elemanları dinamik bellek bölgelerinde yaşarlar: -) - ---- -// Dolaylı olarak çöp toplayıcıdan yararlanan bir işlev -int[] ekle(int[] dizi) { - dizi $(HILITE ~=) 42; - return dizi; -} ---- - -$(P -Yukarıdaki $(C ~=) işleci de mevcut sığa yetersiz olduğunda çöp toplayıcıdan yeni bir bellek bölgesi ayırır. -) - -$(P -Hem veri yapıları hem de algoritmalar açısından büyük kolaylık getirmelerine rağmen, bellek ayırma ve çöp toplama işlemleri program hızını farkedilir derecede yavaşlatabilir. -) - -$(P -"No garbage collector operations"ın kısaltması olan ve "çöp toplayıcı işlemleri içermez" anlamına gelen $(C @nogc), işlevlerin hız kaybına neden olabilecek böyle işlemler içermediğini garanti etmek içindir: -) - ---- -void foo() $(HILITE @nogc) { - // ... -} ---- - -$(P -Derleyici bu garantiyi denetler. Örneğin, aşağıdaki işlev yukarıdaki $(C ekle()) işlevini çağıramaz çünkü $(C ekle()) bu garantiyi vermemektedir: -) - ---- -void foo() $(HILITE @nogc) { - int[] dizi; - // ... - ekle(dizi); $(DERLEME_HATASI) -} ---- - -$(SHELL_SMALL -Error: @nogc function 'deneme.foo' $(HILITE cannot call non-@nogc function) -'deneme.ekle' -) - -$(H5 Güvenilirlik olanakları) - -$(P -$(IX çıkarsama, @safe niteliği) $(IX nitelik çıkarsama, @safe) $(C @safe), $(C @trusted), ve $(C @system) belirteçleri işlevlerin güvenilirlikleri ile ilgilidir. Yine $(C pure)'da olduğu gibi, şablonların, temsilcilerin, isimsiz işlevlerin, ve $(C auto) işlevlerin güvenilirlikleri otomatik olarak çıkarsanır. -) - -$(H6 $(IX @safe) $(C @safe) işlevler) - -$(P -Programcı hatalarının önemli bir bölümü farkında olmadan belleğin yanlış yerlerine yazılması ve o yerlerdeki bilgilerin bu yüzden $(I bozulmaları) ile ilgilidir. Bu hatalar genellikle göstergelerin yanlış kullanılmaları ve güvensiz tür dönüşümleri sonucunda oluşur. -) - -$(P -"Güvenli" anlamına gelen $(C @safe) işlevler, belleği bozmayacağını garanti eden işlevlerdir. -) - -$(P -Bazılarına bu bölümlerde hiç değinmemiş olsam da derleyici $(C @safe) işlevlerde aşağıdaki işlemlere ve olanaklara izin vermez: -) - -$(UL - -$(LI Göstergeler $(C void*) dışındaki gösterge türlerine dönüştürülemezler.) - -$(LI Gösterge olmayan bir değer bir gösterge türüne dönüştürülemez.) - -$(LI Göstergelerin değerleri değiştirilemez.) - -$(LI Gösterge veya referans üyeleri bulunan birlikler kullanılamaz.) - -$(LI $(C @system) olarak bildirilmiş olan işlevler çağrılamaz.) - -$(LI $(C Exception) sınıfından türemiş olmayan bir hata yakalanamaz.) - -$(LI $(I Inline assembler) kullanılamaz.) - -$(LI $(I Değişebilen) değişkenler $(C immutable)'a dönüştürülemezler.) - -$(LI $(C immutable) değişkenler $(I değişebilen) türlere dönüştürülemezler.) - -$(LI İş parçacıklarının yerel değişkenleri $(C shared)'e dönüştürülemezler.) - -$(LI $(C shared) değişkenler iş parçacığının yerel değişkeni olacak şekilde dönüştürülemezler.) - -$(LI İşlevlerin yerel değişkenlerinin veya parametrelerinin adresleri alınamaz.) - -$(LI $(C __gshared) olarak tanımlanmış olan değişkenlere erişilemez.) - -) - -$(H6 $(IX @trusted) $(C @trusted) işlevler) - -$(P -"Güvenilir" anlamına gelen $(C @trusted) olarak bildirilmiş olan işlevler $(C @safe) olarak bildirilemeyecek oldukları halde tanımsız davranışa neden olmayan işlevlerdir. -) - -$(P -Böyle işlevler $(C @safe) işlevlerin yasakladığı işlemleri yapıyor oldukları halde hatalı olmadıkları programcı tarafından garantilenen işlevlerdir. Programcının derleyiciye "bu işleve güvenebilirsin" demesi gibidir. -) - -$(P -Derleyici programcının sözüne güvenir ve $(C @trusted) işlevlerin $(C @safe) işlevlerden çağrılmalarına izin verir. -) - -$(H6 $(IX @system) $(C @system) işlevler) - -$(P -$(C @safe) veya $(C @trusted) olarak bildirilmiş olmayan bütün işlevlerin $(C @system) oldukları varsayılır. Derleyici böyle işlevlerin doğru veya güvenilir olduklarını düşünemez. -) - -$(H5 $(IX CTFE) $(IX derleme zamanında işlev işletme, CTFE) Derleme zamanında işlev işletme (CTFE)) - -$(P -Derleme zamanında yapılabilen hesaplar çoğu programlama dilinde oldukça kısıtlıdır. Bu hesaplar genellikle sabit uzunluklu dizilerin uzunlukları veya hazır değerler kullanan aritmetik işlemler kadar basittir: -) - ---- - writeln(1 + 2); ---- - -$(P -Yukarıdaki $(C 1 + 2) işlemi derleme zamanında işletilir ve program doğrudan $(C writeln(3)) yazılmış gibi derlenir; o hesap için çalışma zamanında hiç süre harcanmaz. -) - -$(P -D'nin "compile time function execution (CTFE)" denen özelliği ise normal olarak çalışma zamanında işletildiklerini düşüneceğimiz işlevlerin bile derleme zamanında işletilmelerini sağlar. -) - -$(P -Çıktıya bir menü yazdıran bir programa bakalım: -) - ---- -import std.stdio; -import std.string; -import std.range; - -string menüSatırları(string[] seçenekler) { - string sonuç; - - foreach (i, seçenek; seçenekler) { - sonuç ~= format(" %s. %s\n", i + 1, seçenek); - } - - return sonuç; -} - -string menü(string başlık, string[] seçenekler, - size_t genişlik) { - return format("%s\n%s\n%s", - başlık.center(genişlik), - '='.repeat(genişlik), // yatay çizgi - menüSatırları(seçenekler)); -} - -void main() { - $(HILITE enum) tatlıMenüsü = - menü("Tatlılar", - [ "Baklava", "Kadayıf", "Muhallebi" ], 20); - - writeln(tatlıMenüsü); -} ---- - -$(P -Aynı iş çok farklı başka yollarla da yapılabilse de, yukarıdaki program bazı işlemler sonucunda bir dizgi üretmekte ve bu dizgiyi çıktıya yazdırmaktadır: -) - -$(SHELL_SMALL - Tatlılar -==================== - 1. Baklava - 2. Kadayıf - 3. Muhallebi -) - -$(P -$(C tatlıMenüsü) değişkeni $(C enum) olarak tanımlandığından değerinin derleme zamanında bilinmesi gerektiğini biliyoruz. Bu, $(C menü) işlevinin derleme zamanında işletilmesi için yeterlidir ve döndürdüğü değer $(C tatlıMenüsü)'nü ilklemek için kullanılır. Sonuçta, $(C tatlıMenüsü) değişkeni sanki o işlemlerin sonucunda oluşan dizgi programa açıkça yazılmış gibi derlenir: -) - ---- - // Yukarıdaki kodun eşdeğeri: - enum tatlıMenüsü = " Tatlılar \n" - "====================\n" - " 1. Baklava\n" - " 2. Kadayıf\n" - " 3. Muhallebi\n"; ---- - -$(P -Bir işlevin derleme zamanında işletilmesi için bunun gerektiği bir ifadede geçmesi yeterlidir: -) - -$(UL -$(LI $(C static) bir değişkenin ilklenmesi) -$(LI $(C enum) bir değişkenin ilklenmesi) -$(LI Sabit uzunluklu bir dizinin uzunluğunun hesaplanması) -$(LI Bir değer şablon parametresinin değerinin hesaplanması) -) - -$(P -Her işlev derleme zamanında işletilemez. Örneğin, evrensel bir değişkene erişen bir işlev o değişken ancak çalışma zamanında yaşamaya başlayacağından derleme zamanında işletilemez. Benzer biçimde, $(C stdout) da çalışma zamanında yaşamaya başlayacağından çıkışa yazdıran bir işlev de derleme zamanında işletilemez. -) - -$(H6 $(IX __ctfe) $(C __ctfe) değişkeni) - -$(P -CTFE'nin güçlü bir tarafı, aynı işlevin hem çalışma zamanında hem de derleme zamanında kullanılabilmesidir. İşlevin bunun için farklı yazılması gerekmese de bazı ifadelerin ancak çalışma zamanında etkinleştirilmeleri gerekebilir. Bunun için $(C __ctfe) değişkeninden yararlanılır: Bu değişken işlev derleme zamanında işletilirken $(C true), çalışma zamanında işletilirken $(C false) değerindedir: -) - ---- -import std.stdio; - -size_t sayaç; - -int foo() { - if (!$(HILITE __ctfe)) { - // Çalışma zamanında işletilmekteyiz - ++sayaç; - } - - return 42; -} - -void main() { - enum i = foo(); - auto j = foo(); - writefln("foo %s kere çağrıldı.", sayaç); -} ---- - -$(P -$(C sayaç) değişkeninin derleme zamanında arttırılması mümkün olmadığından yukarıdaki program onu yalnızca çalışma zamanında işletildiğinde arttırmaktadır. $(C i) derleme zamanında ve $(C j) çalışma zamanında ilklendiklerinden $(C foo) çalışma zamanında 1 kere çağrılmaktadır: -) - -$(SHELL_SMALL -foo 1 kere çağrıldı. -) - -$(H5 Özet) - -$(UL - -$(LI $(C auto) işlevin dönüş türü otomatik olarak çıkarsanır.) - -$(LI $(C ref) işlevin dönüş değeri var olan bir değişkene referanstır.) - -$(LI $(C auto ref) işlevin dönüş değeri referans olabiliyorsa referans, değilse kopyadır.) - -$(LI $(C inout), parametrenin $(C const), $(C immutable), veya $(I değişebilen) özelliğini dönüş türüne aktarır.) - -$(LI $(C pure) işlev $(I değişebilen) evrensel veya static değerlere erişemez. Şablonların, temsilcilerin, isimsiz işlevlerin, ve $(C auto) işlevlerin saf olup olmadıkları otomatik olarak çıkarsanır.) - -$(LI $(C nothrow) işlev hata atamaz. Şablonların, temsilcilerin, isimsiz işlevlerin, ve $(C auto) işlevlerin hata atıp atmadıkları otomatik olarak çıkarsanır.) - -$(LI $(C @nogc) işlev çöp toplayıcı işlemleri içeremez.) - -$(LI $(C @safe) işlev bellek hatalarına neden olamaz. Şablonların, temsilcilerin, isimsiz işlevlerin, ve $(C auto) işlevlerin $(C @safe) olup olmadıkları otomatik olarak çıkarsanır.) - -$(LI $(C @trusted) işlev güvenilir olduğu halde $(C @safe) olarak işaretlenmemiş olan işlevdir; $(C @safe) kabul edilerek derlenir.) - -$(LI $(C @system) işlev her D olanağını kullanabilir. $(C @system), varsayılan güvenilirlik belirtecidir.) - -$(LI İşlevler derleme zamanında işletilebilirler (CTFE). Bu durum $(C __ctfe) değişkeni ile denetlenebilir.) - -) - -Macros: - SUBTITLE=Diğer İşlev Olanakları - - DESCRIPTION=D dili işlevlerinin (fonksiyon) [function] şimdiye kadar gösterilenlerden başka olanakları - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function pure nothrow auto ref inout @safe @trusted @system ctfe __ctfe - -SOZLER= -$(cikarsama) -$(cop_toplayici) -$(degismez) -$(evrensel) -$(fonksiyonel_programlama) -$(hata_ayiklama) -$(is_parcacigi) -$(referans) -$(sabit) -$(statik) -$(tanimsiz_davranis) -$(yukleme) diff --git a/ddili/src/ders/d/ix.d b/ddili/src/ders/d/ix.d deleted file mode 100644 index fb7462c..0000000 --- a/ddili/src/ders/d/ix.d +++ /dev/null @@ -1,14 +0,0 @@ -Ddoc - -$(H4 Dizin) - -$(DIV_CLASS web_index_section, -$(INDEX_ENTRIES) -) - -Macros: - SUBTITLE=Dizin Bölümü - - DESCRIPTION=D Programlama Dili kitabının dizin bölümü - - KEYWORDS=dizin diff --git a/ddili/src/ders/d/kapamalar.d b/ddili/src/ders/d/kapamalar.d deleted file mode 100644 index 840c0c4..0000000 --- a/ddili/src/ders/d/kapamalar.d +++ /dev/null @@ -1,1219 +0,0 @@ -Ddoc - -$(DERS_BOLUMU İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler) - -$(P -İşlev göstergeleri işlevlerin adreslerinin saklanabilmelerini ve daha sonraki bir zamanda bu göstergeler yoluyla çağrılabilmelerini sağlarlar. İşlev göstergeleri D'ye C'den geçmiştir. -) - -$(P -Temsilciler hem işlev göstergelerini hem de o işlevlerin kullandıkları kapsamları bir arada saklayan olanaklardır. Saklanan kapsam o temsilcinin içinde oluşturulduğu ortam olabileceği gibi, bir yapı veya sınıf nesnesinin kendisi de olabilir. -) - -$(P -Temsilciler çoğu fonksiyonel dilde bulunan $(I kapama) olanağını da gerçekleştirirler. -) - -$(H5 $(IX işlev göstergesi) $(IX gösterge, işlev) İşlev göstergeleri) - -$(P -$(IX &, işlev adresi) Bundan önceki bölümde $(C is) ifadesini denerken $(C &) işleci ile işlevlerin adreslerinin de alınabildiğini görmüştük. O adresi bir işlev şablonuna parametre olarak göndermiştik. -) - -$(P -Şablonların çeşitli türlerle çağrılabilmelerinden ve türlerin $(C .stringof) niteliğinden yararlanarak, işlev göstergelerinin türleri hakkında bilgi edinebiliriz: -) - ---- -import std.stdio; - -int işlev(char c, double d) { - return 42; -} - -void main() { - şablon($(HILITE &işlev)); // adresinin alınması ve - // parametre olarak gönderilmesi -} - -void şablon(T)(T parametre) { - writeln("türü : ", T.stringof); - writeln("değeri: ", parametre); -} ---- - -$(P -O program çalıştırıldığında, $(C işlev) isimli işlevin adresinin türü konusunda bir fikir sahibi olabiliyoruz: -) - -$(SHELL -türü : int function(char c, double d) -değeri: 80495B4 -) - -$(H6 $(IX üye işlev göstergesi) $(IX gösterge, üye işlev) Üye işlev göstergeleri) - -$(P -Üye işlevlerin adresleri hem doğrudan tür üzerinden hem de o türün bir nesnesi üzerinden alınabilir. Bu iki yöntemin etkisi farklıdır: -) - ---- -struct Yapı { - void işlev() { - } -} - -void main() { - auto nesne = Yapı(); - - auto f = &$(HILITE Yapı).işlev; // tür üzerinden - auto d = &$(HILITE nesne).işlev; // nesne üzerinden - - static assert(is (typeof($(HILITE f)) == void $(HILITE function)())); - static assert(is (typeof($(HILITE d)) == void $(HILITE delegate)())); -} ---- - -$(P -Yukarıdaki $(C static assert) satırlarından da görüldüğü gibi, $(C f) bir $(C function), $(C d) ise bir $(C delegate)'tir. Daha aşağıda göreceğimiz gibi, $(C d) doğrudan çağrılabilir ama $(C f)'nin çağrılabilmesi için önce hangi nesne üzerinde çağrılacağının da belirtilmesi gerekir. -) - -$(H6 Tanımlanması) - -$(P -$(IX function) İşlev göstergeleri $(C function) anahtar sözcüğü ile tanımlanır. Bu sözcükten önce işlevin dönüş türü, sonra da işlevin aldığı parametreler yazılır: -) - ---- - $(I dönüş_türü) function($(I aldığı_parametreler)) gösterge; ---- - -$(P -Bu tanımda parametrelere isim verilmesi gerekmez; yukarıdaki çıktıda gördüğümüz parametre isimleri olan $(C c)'nin ve $(C d)'nin yazılmaları isteğe bağlıdır. Bir örnek olarak, yukarıdakı $(C işlev) isimli işlevi gösteren bir değişkeni şöyle tanımlayabiliriz: -) - ---- - int function(char, double) gösterge = &işlev; ---- - -$(P -İşlev göstergelerinin yazımı oldukça karmaşık olduğundan o türe $(C alias) ile yeni bir isim vermek kodun okunaklılığını arttırır: -) - ---- -alias Hesapİşlevi = int function(char, double); ---- - -$(P -Artık $(C function)'lı uzun yazım yerine kısaca $(C Hesapİşlevi) yazmak yeterlidir. -) - ---- - Hesapİşlevi gösterge = &işlev; ---- - -$(P -$(C auto)'dan da yararlanılabilir: -) - ---- - auto gösterge = &işlev; ---- - -$(H6 Çağrılması) - -$(P -İşlev göstergesi olarak tanımlanan değişken, sanki kendisi bir işlevmiş gibi isminden sonraki parametre listesiyle çağrılır ve dönüş değeri kullanılabilir: -) - ---- - int sonuç = $(HILITE gösterge)('a', 5.67); - assert(sonuç == 42); ---- - -$(P -Yukarıdaki çağrı, işlevin kendi ismiyle $(C işlev('a', 5.67)) olarak çağrılmasının eşdeğeridir. -) - -$(H6 Ne zaman kullanmalı) - -$(P -İşlev göstergeleri değerlerin saklanmalarına benzer şekilde, işlemlerin de saklanabilmelerini sağlar. Saklanan göstergeler programda daha sonradan işlev gibi kullanılabilirler. Bir anlamda, daha sonradan uygulanacak olan davranışları saklarlar. -) - -$(P -Aslında davranış farklılıklarının D'nin başka olanakları ile de sağlanabildiğini biliyorsunuz. Örneğin $(C Çalışan) gibi bir yapının ücretinin hesaplanması sırasında hangi işlevin çağrılacağı, bu yapının bir $(C enum) değeri ile belirlenebilir: -) - ---- - final switch (çalışan.tür) { - - case ÇalışanTürü.maaşlı: - maaşlıÜcretHesabı(); - break; - - case ÇalışanTürü.saatli: - saatliÜcretHesabı(); - break; - } ---- - -$(P -O yöntemin bir yetersizliği, o kod bir kütüphane içinde bulunduğu zaman ortaya çıkar: Bütün $(C enum) değerlerinin ve onlara karşılık gelen bütün işlevlerin kütüphane kodu yazıldığı sırada biliniyor olması gerekmektedir. Farklı bir ücret hesabı gerektiğinde, kütüphane içindeki ilgili $(C switch) deyimlerinin hepsinin yeni türü de içerecek şekilde değiştirilmeleri gerekir. -) - -$(P -Davranış farkı konusunda başka bir yöntem, nesne yönelimli programlama olanaklarından yararlanmak olabilir. $(C Çalışan) diye bir arayüz tanımlanabilir ve ücret hesabı ondan türeyen alt sınıflara yaptırılabilir: -) - ---- -interface Çalışan { - double ücretHesabı(); -} - -class MaaşlıÇalışan : Çalışan { - double ücretHesabı() { - double sonuç; - // ... - return sonuç; - } -} - -class SaatliÇalışan : Çalışan { - double ücretHesabı() { - double sonuç; - // ... - return sonuç; - } -} - -// ... - - double ücret = çalışan.ücretHesabı(); ---- - -$(P -Bu, nesne yönelimli programlama dillerine uygun olan yöntemdir. -) - -$(P -İşlev göstergeleri, davranış farklılığı konusunda kullanılan başkaca bir yöntemdir. İşlev göstergeleri, nesne yönelimli olanakları bulunmayan C dilinde yazılmış olan kütüphanelerde görülebilirler. -) - -$(H6 Parametre örneği) - -$(P -Kendisine verilen bir dizi sayı ile işlem yapan bir işlev tasarlayalım. Bu işlev, sayıların yalnızca sıfırdan büyük olanlarının on katlarını içeren bir dizi döndürsün: -) - ---- -$(CODE_NAME süz_ve_dönüştür)int[] süz_ve_dönüştür(const int[] sayılar) { - int[] sonuç; - - foreach (sayı; sayılar) { - if (sayı > 0) { // süzme, - immutable yeniDeğer = sayı * 10; // ve dönüştürme - sonuç ~= yeniDeğer; - } - } - - return sonuç; -} ---- - -$(P -O işlevi şöyle bir programla deneyebiliriz: -) - ---- -$(CODE_XREF süz_ve_dönüştür)import std.stdio; -import std.random; - -void main() { - int[] sayılar; - - // Rasgele 20 sayı - foreach (i; 0 .. 20) { - sayılar ~= uniform(0, 10) - 5; - } - - writeln("giriş: ", sayılar); - writeln("sonuç: ", süz_ve_dönüştür(sayılar)); -} ---- - -$(P -Çıktısından görüldüğü gibi, sonuç yalnızca sıfırdan büyük olanların on katlarını içermektedir: -) - -$(SHELL -giriş: -2 0 $(HILITE 3 2 4) -3 $(HILITE 2) -4 $(HILITE 4 2 2 4 2 1) -2 -1 0 $(HILITE 2) -2 $(HILITE 4) -sonuç: 30 20 40 20 40 20 20 40 20 10 20 40 -) - -$(P -$(C süz_ve_dönüştür) işlevinin bu haliyle fazla kullanışlı olduğunu düşünemeyiz çünkü her zaman için sıfırdan büyük değerlerin on katlarını üretmektedir. Oysa süzme ve dönüştürme işlemlerini nasıl uygulayacağını dışarıdan alabilse çok daha kullanışlı olabilir. -) - -$(P -Dikkat ederseniz, süzme işlemi $(C int)'ten $(C bool)'a bir dönüşüm, sayı dönüştürme işlemi de $(C int)'ten yine $(C int)'e bir dönüşümdür: -) - -$(UL -$(LI $(C sayı > 0), $(C int) olan sayıya bakarak $(C bool) sonuç elde ediyor.) -$(LI $(C sayı * 10), $(C int) olan sayı kullanarak yine $(C int) üretiyor.) -) - -$(P -Bu işlemleri işlev göstergeleri yoluyla yapmaya geçmeden önce, bu dönüşümleri sağlayacak olan işlev gösterge türlerini şöyle tanımlayabiliriz: -) - ---- -alias Süzmeİşlemi = bool function(int); // int'ten bool -alias Dönüşümİşlemi = int function(int); // int'ten int ---- - -$(P -$(C Süzmeİşlemi), "int alan ve bool döndüren" işlev göstergesi, $(C Dönüşümİşlemi) de "int alan ve int döndüren" işlev göstergesi anlamındadır. -) - -$(P -Bu türlerden olan işlev göstergelerini $(C süz_ve_dönüştür) işlevine dışarıdan parametre olarak verirsek süzme ve dönüştürme işlemlerini o işlev göstergelerine yaptırabiliriz. Böylece işlev daha kullanışlı hale gelir: -) - ---- -int[] süz_ve_dönüştür(const int[] sayılar, - $(HILITE Süzmeİşlemi süzücü), - $(HILITE Dönüşümİşlemi dönüştürücü)) { - int[] sonuç; - - foreach (sayı; sayılar) { - if ($(HILITE süzücü(sayı))) { - immutable yeniDeğer = $(HILITE dönüştürücü(sayı)); - sonuç ~= yeniDeğer; - } - } - - return sonuç; -} ---- - -$(P -Bu işlev artık asıl süzme ve dönüştürme işlemlerinden bağımsız bir hale gelmiştir çünkü o işlemleri kendisine verilen işlev göstergelerine yaptırmaktadır. Yukarıdaki gibi $(I sıfırdan büyük olanlarının on katlarını) üretebilmesi için şöyle iki küçük işlev tanımlayabiliriz ve $(C süz_ve_dönüştür) işlevini onların adresleri ile çağırabiliriz: -) - ---- -bool sıfırdanBüyük_mü(int sayı) { - return sayı > 0; -} - -int onKatı(int sayı) { - return sayı * 10; -} - -// ... - - writeln("sonuç: ", süz_ve_dönüştür(sayılar, - $(HILITE &sıfırdanBüyük_mü), - $(HILITE &onKatı))); ---- - -$(P -Bunun yararı, $(C süz_ve_dönüştür) işlevinin artık bambaşka süzücü ve dönüştürücü işlevleriyle de serbestçe çağrılacak hale gelmiş olmasıdır. Örneğin $(I çift olanlarının ters işaretlileri) şöyle elde edilebilir: -) - ---- -bool çift_mi(int sayı) { - return (sayı % 2) == 0; -} - -int tersİşaretlisi(int sayı) { - return -sayı; -} - -// ... - - writeln("sonuç: ", süz_ve_dönüştür(sayılar, - &çift_mi, - &tersİşaretlisi)); ---- - -$(P -Çıktısı: -) - -$(SHELL -giriş: 2 -3 -3 -2 4 4 3 1 4 3 -4 -1 -2 1 1 -5 0 2 -3 2 -sonuç: -2 2 -4 -4 -4 4 2 0 -2 -2 -) - -$(P -İşlevler $(C çift_mi) ve $(C tersİşaretlisi) gibi çok kısa olduklarında başlı başlarına tanımlanmaları gerekmeyebilir. Bunun nasıl gerçekleştirildiğini biraz aşağıda $(I İsimsiz işlevler) ve özellikle onların $(C =>) söz dizimini tanırken göreceğiz: -) - ---- - writeln("sonuç: ", süz_ve_dönüştür(sayılar, - sayı => (sayı % 2) == 0, - sayı => -sayı)); ---- - -$(H6 Üye örneği) - -$(P -İşlev göstergeleri değişken olarak kullanılabildikleri için yapı ve sınıf üyeleri de olabilirler. Yukarıdaki $(C süz_ve_dönüştür) işlevi yerine, süzme ve dönüştürme işlemlerini kurucu parametreleri olarak alan bir sınıf da yazılabilir: -) - ---- -class SüzücüDönüştürücü { - $(HILITE Süzmeİşlemi süzücü); - $(HILITE Dönüşümİşlemi dönüştürücü); - - this(Süzmeİşlemi süzücü, Dönüşümİşlemi dönüştürücü) { - this.süzücü = süzücü; - this.dönüştürücü = dönüştürücü; - } - - int[] işlemYap(const int[] sayılar) { - int[] sonuç; - - foreach (sayı; sayılar) { - if (süzücü(sayı)) { - immutable yeniDeğer = dönüştürücü(sayı); - sonuç ~= yeniDeğer; - } - } - - return sonuç; - } -} ---- - -$(P -Daha sonra o türden bir nesne oluşturulabilir ve yukarıdaki sonuçların aynıları şöyle elde edilebilir: -) - ---- - auto işlemci = new SüzücüDönüştürücü($(HILITE &çift_mi), $(HILITE &tersİşaretlisi)); - writeln("sonuç: ", işlemci.işlemYap(sayılar)); ---- - -$(H5 $(IX isimsiz işlev) $(IX işlev, isimsiz) $(IX işlev, lambda) $(IX işlev hazır değeri) $(IX hazır değer, işlev) $(IX lambda) İsimsiz işlevler) - -$(P -Yukarıdaki örnek programlarda $(C süz_ve_dönüştür) işlevinin esnekliğinden yararlanmak için küçük işlevler tanımlandığını ve $(C süz_ve_dönüştür) çağrılırken o küçük işlevlerin adreslerinin gönderildiğini gördük. -) - -$(P -Yukarıdaki örneklerde de görüldüğü gibi, işlevin asıl işi az olduğunda başlı başına işlevler tanımlamak külfetli olabilir. Örneğin, $(C sayı > 0) ve $(C sayı * 10) oldukça basit ve küçük işlemlerdir. -) - -$(P -$(I İşlev hazır değeri) olarak da adlandırabileceğimiz $(I isimsiz işlev) olanağı $(ASIL lambda), başka ifadelerin arasında küçük işlevler tanımlamaya yarar. İsimsiz işlevler işlev göstergesi kullanılabilen her yerde şu söz dizimiyle tanımlanabilirler: -) - ---- - function $(I dönüş_türü)($(I parametreleri)) { /* ... işlemleri ... */ } ---- - -$(P -Örneğin, yukarıdaki örnekte tanımladığımız sınıftan olan bir nesneyi $(I ikiden büyük olanlarının yedi katlarını) üretecek şekilde şöyle kullanabiliriz: -) - ---- - new SüzücüDönüştürücü( - function bool(int sayı) { return sayı > 2; }, - function int(int sayı) { return sayı * 7; }); ---- - -$(P -Böylece, hem bu kadar küçük işlemler için ayrıca işlevler tanımlamak zorunda kalmamış oluruz hem de istediğimiz davranışı tam da gereken noktada belirtmiş oluruz. -) - -$(P -Yukarıdaki söz dizimlerinin normal işlevlere ne kadar benzediğine dikkat edin. Normal işlevlerle isimsiz işlevlerin söz dizimlerinin bu derece yakın olmaları kolaylık olarak kabul edilebilir. Öte yandan, bu ağır söz dizimi isimsiz işlevlerin kullanım amaçlarıyla hâlâ çelişmektedir çünkü isimsiz işlevler özellikle kısa işlemleri kolayca tanımlama amacını taşırlar. -) - -$(P -Bu yüzden isimsiz işlevler çeşitli kısa söz dizimleri ile de tanımlanabilirler. -) - -$(H6 Kısa söz dizimi) - -$(P -İsimsiz işlevlerin yazımlarında bazı kolaylıklar da vardır. İşlevin dönüş türünün $(C return) satırından anlaşılabildiği durumlarda dönüş türü yazılmayabilir: -) - ---- - new SüzücüDönüştürücü( - function (int sayı) { return sayı > 2; }, - function (int sayı) { return sayı * 7; }); ---- - -$(P -İsimsiz işlevin parametre almadığı durumlarda da parametre listesi yazılmayabilir. Bunu görmek için işlev göstergesi alan bir işlev düşünelim: -) - ---- -void birİşlev(/* ... işlev göstergesi alsın ... */) { - // ... -} ---- - -$(P -O işlevin aldığı parametre, $(C double) döndüren ama parametre almayan bir işlev göstergesi olsun: -) - ---- -void birİşlev(double function() gösterge) { - // ... -} ---- - -$(P -O parametrenin tanımındaki $(C function)'dan sonraki parantezin boş olması, o göstergenin $(I parametre almayan bir işlev göstergesi) olduğunu ifade eder. Böyle bir durumda, isimsiz işlevin oluşturulduğu noktada boş parantez yazmaya da gerek yoktur. Şu üç isimsiz işlev tanımı birbirlerinin eşdeğeridir: -) - ---- - birİşlev(function double() { return 42.42; }); - birİşlev(function () { return 42.42; }); // üsttekiyle aynı - birİşlev(function { return 42.42; }); // üsttekiyle aynı ---- - -$(P -Birincisi hiçbir kısaltmaya başvurmadan yazılmıştır. İkincisi dönüş türünün $(C return) satırından çıkarsanmasından yararlanmıştır. Üçüncüsü de gereksiz olan boş parametre listesini de yazmamıştır. -) - -$(P -Bir adım daha atılabilir ve $(C function) da yazılmayabilir. O zaman bunun isimsiz bir işlev mi yoksa isimsiz bir temsilci mi olduğuna derleyici karar verir. Oluşturulduğu ortamdaki değişkenleri kullanıyorsa temsilcidir, kullanmıyorsa $(C function)'dır: -) - ---- - birİşlev({ return 42.42; }); // bu durumda 'function' çıkarsanır ---- - -$(P -Bazı isimsiz işlevler $(C =>) söz dizimiyle daha da kısa yazılabilirler. -) - -$(H6 $(IX =>) Tek $(C return) ifadesi yerine $(C =>) söz dizimi) - -$(P -Yukarıdaki en kısa söz dizimi bile gereğinden fazla karmaşık olarak görülebilir. İşlevin parametre listesinin hemen içindeki küme parantezleri okumayı güçleştirmektedirler. Üstelik çoğu isimsiz işlev tek $(C return) deyiminden oluşur. Öyle durumlarda ne $(C return) anahtar sözcüğüne gerek olmalıdır ne de sonundaki noktalı virgüle. D'nin isimsiz işlevlerinin en kısa söz dizimi başka dillerde de bulunan $(C =>) ile sağlanır. -) - -$(P -Yalnızca tek $(C return) deyimi içeren bir isimsiz işlevin söz dizimini hatırlayalım: -) - ---- - function $(I dönüş_türü)($(I parametreler)) { return $(I ifade); } ---- - -$(P -$(C function) anahtar sözcüğünün ve dönüş türünün belirtilmelerinin gerekmediğini yukarıda görmüştük: -) - ---- - ($(I parametreler)) { return $(I ifade); } ---- - -$(P -Aynı isimsiz işlev $(C =>) ile çok daha kısa olarak şöyle tanımlanabilir: -) - ---- - ($(I parametreler)) => $(I ifade) ---- - -$(P -Yukarıdaki söz diziminin anlamı, "o parametreler verildiğinde şu ifadeyi (değeri) üret" olarak açıklanabilir. -) - -$(P -Dahası, yalnızca tek parametre bulunduğunda etrafındaki parantezler de yazılmayabilir: -) - ---- - $(I tek_parametre) => $(I ifade) ---- - -$(P -Buna rağmen, D'nin gramerinin bir gereği olarak hiç parametre bulunmadığında parametre listesinin boş olarak verilmesi şarttır: -) - ---- - () => $(I ifade) ---- - -$(P -İsimsiz işlevleri başka dillerden tanıyan programcılar $(C =>) karakterlerinden sonra küme parantezleri yazma hatasına düşebilirler. O söz dizimi başka bir anlam taşır: -) - ---- - // 'a + 1' döndüren isimsiz işlev - auto l0 = (int a) => a + 1 - - // 'a + 1' döndüren isimsiz işlev döndüren isimsiz işlev - auto l1 = (int a) => $(HILITE {) return a + 1; $(HILITE }) - - assert(l0(42) == 43); - assert(l1(42)$(HILITE ()) == 43); // l1'in döndürdüğünün işletilmesi ---- - -$(P -$(IX filter, std.algorithm) Kısa söz diziminin bir örneğini $(C std.algorithm) modülündeki $(C filter) algoritmasının kullanımında görelim. $(C filter), şablon parametresi olarak bir kıstas, işlev parametresi olarak da bir $(I aralık) alır. Kıstası elemanlara teker teker uygular; $(C false) çıkan elemanları eler ve diğerlerini geçirir. Kıstas, isimsiz işlevler de dahil olmak üzere çeşitli yollarla bildirilebilir. -) - -$(P -($(I $(B Not:) Aralık kavramını daha sonraki bir bölümde göreceğiz. Şimdilik dilimlerin aralık olduklarını kabul edebilirsiniz.)) -) - -$(P -Örneğin, değerleri 10'dan büyük olan elemanları geçiren ve diğerlerini eleyen bir $(C filter) ifadesine şablon parametresi olarak aşağıdaki gibi bir isimsiz işlev verilebilir: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - int[] sayılar = [ 20, 1, 10, 300, -2 ]; - writeln(sayılar.filter!($(HILITE sayı => sayı > 10))); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[20, 300] -) - -$(P -O kıstası şöyle açıklayabiliriz: $(I bir sayı verildiğinde o sayı 10'dan büyük ise $(C true) üret). Bu açıdan bakıldığında $(C =>) söz diziminin $(I solundaki değere karşılık sağındaki ifadeyi üreten) bir söz dizimi olduğunu düşünebiliriz. -) - -$(P -O kısa söz diziminin yerine bir kere de onun eşdeğeri olan en uzun söz dizimini yazalım. İsimsiz işlevin tanımını belirleyen küme parantezlerini sarı ile işaretliyorum: -) - ---- - writeln(sayılar.filter!(function bool(int sayı) $(HILITE {) - return sayı > 10; - $(HILITE }))); ---- - -$(P -Görüldüğü gibi, $(C =>) söz dizimi tek $(C return) deyimi içeren isimsiz işlevlerde büyük kolaylık ve okunaklılık sağlamaktadır. -) - -$(P -Başka bir örnek olarak iki parametre kullanan bir isimsiz işlev tanımlayalım. Aşağıdaki algoritma kendisine verilen iki dilimin birbirlerine karşılık olan elemanlarını iki parametre alan bir işleve göndermektedir. O işlevin döndürdüğü sonuçları da bir dizi olarak döndürüyor: -) - ---- -$(CODE_NAME ikiliHesap)import std.exception; - -int[] ikiliHesap(int function$(HILITE (int, int)) işlem, - const int[] soldakiler, - const int[] sağdakiler) { - enforce(soldakiler.length == sağdakiler.length); - - int[] sonuçlar; - - foreach (i; 0 .. soldakiler.length) { - sonuçlar ~= işlem(soldakiler[i], sağdakiler[i]); - } - - return sonuçlar; -} ---- - -$(P -Oradaki işlev göstergesi iki parametre aldığından, $(C ikiliHesap)'ın çağrıldığı yerde $(C =>) karakterlerinden önce parantez içinde iki parametre belirtilmelidir: -) - ---- -$(CODE_XREF ikiliHesap)import std.stdio; - -void main() { - writeln(ikiliHesap($(HILITE (a, b)) => (a * 10) + b, - [ 1, 2, 3 ], - [ 4, 5, 6 ])); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[14, 25, 36] -) - -$(H5 $(IX delegate) $(IX temsilci) Temsilciler) - -$(P -$(IX kapsam) $(IX kapama) Temsilci, işlev göstergesine ek olarak onun içinde tanımlandığı kapsamın da saklanmasından oluşur. Temsilciler daha çok fonksiyonel programlama dillerinde görülen $(I kapamaları) da gerçekleştirirler. Temsilciler çoğu emirli dilde bulunmasalar da D'nin güçlü olanakları arasındadırlar. -) - -$(P -$(LINK2 /ders/d/yasam_surecleri.html, Yaşam Süreçleri ve Temel İşlemler bölümünde) gördüğümüz gibi, değişkenlerin yaşamları tanımlı oldukları kapsamdan çıkıldığında son bulur: -) - ---- -{ - int artış = 10; - // ... -} // ← artış'ın yaşamı burada son bulur ---- - -$(P -$(C artış) gibi $(I yerel) değişkenlerin adresleri bu yüzden işlevlerden döndürülemezler. -) - -$(P -$(C artış)'ın işlev göstergesi döndüren bir işlev içinde tanımlanmış olan yerel bir değişken olduğunu düşünelim. Bu işlevin sonuç olarak döndürdüğü isimsiz işlev bu yerel değişkeni de kullanıyor olsun: -) - ---- -alias Hesapİşlevi = int function(int); - -Hesapİşlevi hesapçı() { - int artış = 10; - return sayı => $(HILITE artış) + sayı; $(DERLEME_HATASI) -} ---- - -$(P -Döndürülen isimsiz işlev yerel bir değişkeni kullanmaya çalıştığı için o kod hatalıdır. Derlenmesine izin verilmiş olsa, isimsiz işlev daha sonradan işletildiği sırada yaşamı çoktan sona ermiş olan $(C artış) değişkenine erişmeye çalışacaktır. -) - -$(P -O kodun derlenip doğru olarak çalışabilmesi için $(C artış)'ın yaşam sürecinin isimsiz işlev yaşadığı sürece uzatılması gerekir. Temsilciler işte böyle durumlarda yararlıdırlar: Hem işlev göstergesini hem de onun kullandığı kapsamları sakladıkları için o kapsamlardaki değişkenlerin yaşamları, temsilcinin yaşamı kadar uzamış olur. -) - -$(P -Temsilcilerin kullanımı işlev göstergelerine çok benzer: Tek farkları $(C function) yerine $(C delegate) anahtar sözcüğünün kullanılmasıdır. Yukarıdaki kodun derlenip doğru olarak çalışması için o kadarı yeterlidir: -) - ---- -alias Hesapİşlevi = int $(HILITE delegate)(int); - -Hesapİşlevi hesapçı() { - int artış = 10; - return sayı => artış + sayı; -} ---- - -$(P -O temsilcinin kullandığı yerel kapsamdaki $(C artış) gibi değişkenlerin yaşamları temsilci yaşadığı sürece devam edecektir. Bu yüzden temsilciler ilerideki bir zamanda çağrıldıklarında o yerel değişkenleri değiştirebilirler de. Bunun örneklerini daha sonraki bir bölümde öğreneceğimiz yapı ve sınıfların $(C opApply) üye işlevlerinde göreceğiz. -) - -$(P -Yukarıdaki temsilciyi şöyle bir kodla deneyebiliriz: -) - ---- - auto işlev = hesapçı(); - writeln("hesap: ", işlev(3)); ---- - -$(P -$(C hesapçı), isimsiz bir temsilci döndürmektedir. Yukarıdaki kod o temsilciyi $(C işlev) isimli bir değişkenin değeri olarak kullanmakta ve $(C işlev(3)) yazımıyla çağırmaktadır. Temsilcinin işi de kendisine verilen sayı ile $(C artış)'ın toplamını döndürmek olduğu için çıkışa 3 ve 10'un toplamı yazdırılacaktır: -) - -$(SHELL -hesap: 13 -) - -$(H6 Kısa söz dizimi) - -$(P -Yukarıdaki örnekte de kullandığımız gibi, temsilciler de kısa söz dizimiyle ve hatta $(C =>) söz dizimiyle yazılabilirler. $(C function) veya $(C delegate) yazılmadığında hangisinin uygun olduğuna derleyici karar verir. Kapsam saklama kaygısı olmadığından daha etkin olarak çalıştığı için öncelikle $(C function)'ı dener, olamıyorsa $(C delegate)'i seçer. -) - -$(P -Kısa söz dizimini bir kere de parametre almayan bir temsilci ile görelim: -) - ---- -int[] özelSayılarOluştur(int adet, int delegate$(HILITE ()) sayıÜretici) { - int[] sonuç = [ -1 ]; - sonuç.reserve(adet + 2); - - foreach (i; 0 .. adet) { - sonuç ~= sayıÜretici(); - } - - sonuç ~= -1; - - return sonuç; -} ---- - -$(P -O işlev ilk ve son sayıları -1 olan bir dizi sayı oluşturmaktadır. Bu iki özel sayının arasına kaç adet başka sayı geleceğini ve bu sayıların nasıl üretileceklerini ise parametre olarak almaktadır. -) - -$(P -O işlevi, her çağrıldığında aynı sabit değeri döndüren aşağıdaki gibi bir temsilciyle çağırabiliriz. Yukarıda belirtildiği gibi, parametre almayan isimsiz işlevlerin parametre listesinin boş olarak belirtilmesi şarttır: -) - ---- - writeln(özelSayılarOluştur(3, $(HILITE () => 42))); ---- - -$(P -Çıktısı: -) - -$(SHELL --1 42 42 42 -1 -) - -$(P -Aynı işlevi bir de yerel bir değişken kullanan bir temsilci ile çağıralım: -) - ---- - int sonSayı; - writeln(özelSayılarOluştur(15, $(HILITE () => sonSayı += uniform(0, 3)))); - - writeln("son üretilen sayı: ", sonSayı); ---- - -$(P -O temsilci rasgele bir değer üretmekte, ama her zaman için son sayıya eklediği için rasgele sayıların değerleri hep artan yönde gitmektedir. Yerel değişkenin temsilcinin işletilmesi sırasında nasıl değişmiş olduğunu da çıktının son satırında görüyoruz: -) - -$(SHELL --1 0 2 3 4 6 6 8 9 9 9 10 12 14 15 17 -1 -son üretilen sayı: 17 -) - -$(H6 $(IX &, nesne temsilcisi) $(IX temsilci, üye işlev) $(IX nesne temsilcisi) $(IX üye işlev temsilcisi) Temsilci olarak nesne ve üye işlevi) - -$(P -Temsilcinin bir işlev göstergesini ve onun oluşturulduğu kapsamı bir arada sakladığını gördük. Bu ikisinin yerine belirli bir nesne ve onun bir üye işlevi de kullanılabilir. Böyle oluşturulan temsilci, o üye işlevi ve nesnenin kendisini bir araya getirmiş olur. -) - -$(P -Bunun söz dizimi aşağıdaki gibidir: -) - ---- - &$(I nesne).$(I üye_işlev) ---- - -$(P -Önce bu söz diziminin gerçekten de bir $(C delegate) oluşturduğunu yine $(C .stringof)'tan yararlanarak görelim: -) - ---- -import std.stdio; - -struct Konum { - long x; - long y; - - void sağa(size_t adım) { x += adım; } - void sola(size_t adım) { x -= adım; } - void yukarıya(size_t adım) { y += adım; } - void aşağıya(size_t adım) { y -= adım; } -} - -void main() { - auto nokta = Konum(); - writeln(typeof($(HILITE &nokta.sağa)).stringof); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -void delegate(ulong adım) -) - -$(P -O söz dizimi yalnızca bir temsilci oluşturur. Nesnenin üye işlevi temsilci oluşturulduğu zaman çağrılmaz. O işlev, temsilci daha sonradan işlev gibi kullanıldığında çağrılacaktır. Bunun örneğini görmek için bir temsilci değişken tanımlayabiliriz: -) - ---- - auto yönİşlevi = &nokta.sağa; // burada tanımlanır - yönİşlevi(3); // burada çağrılır - writeln(nokta); ---- - -$(P -Çıktısı: -) - -$(SHELL -Konum(3, 0) -) - -$(P -İşlev göstergeleri, isimsiz işlevler, ve temsilciler kendileri değişken olabildiklerinden; değişkenlerin kullanılabildikleri her yerde kullanılabilirler. Örneğin yukarıdaki nesne ve üye işlevlerinden oluşan bir temsilci dizisi şöyle oluşturulabilir ve daha sonra işlemleri işletilebilir: -) - ---- - auto nokta = Konum(); - - void delegate(size_t)[] işlemler = - [ &nokta.sağa, &nokta.yukarıya, &nokta.sağa ]; - - foreach (işlem; işlemler) { - işlem(1); - } - - writeln(nokta); ---- - -$(P -O dizide iki kere sağa bir kere de yukarıya gitme işlemi bulunduğundan bütün temsilciler işletildiklerinde noktanın durumu şöyle değişmiş olur: -) - -$(SHELL -Konum(2, 1) -) - -$(H6 $(IX .ptr, delegate kapsamı) $(IX gösterge, delegate kapsamı) $(IX .funcptr) Temsilci nitelikleri) - -$(P -Bir temsilcinin işlev ve kapsam göstergeleri $(C .funcptr) ve $(C .ptr) nitelikleri ile elde edilebilir: -) - ---- -struct Yapı { - void işlev() { - } -} - -void main() { - auto nesne = Yapı(); - - auto d = &nesne.işlev; - - assert(d$(HILITE .funcptr) == &Yapı.işlev); - assert(d$(HILITE .ptr) == &nesne); -} ---- - -$(P -Bu niteliklere değerler atayarak $(C delegate) oluşturmak mümkündür: -) - ---- -struct Yapı { - int i; - - void işlev() { - import std.stdio; - writeln(i); - } -} - -void main() { - auto nesne = Yapı(42); - - void delegate() d; - assert(d is null); // null temsilci ile başlıyoruz - - d$(HILITE .funcptr) = &Yapı.işlev; - d$(HILITE .ptr) = &nesne; - - $(HILITE d()); -} ---- - -$(P -Yukarıdaki $(C d()) söz dizimi ile temsilcinin çağrılması $(C nesne.işlev()) ifadesinin (yani, $(C Yapı.işlev)'in $(C nesne) üzerinde işletilmesinin) eşdeğeridir: -) - -$(SHELL -42 -) - -$(H6 $(IX lazy parametre, temsilci) $(C lazy) parametre temsilcidir) - -$(P -$(C lazy) anahtar sözcüğünü $(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) görmüştük: -) - ---- -void logla(Önem önem, $(HILITE lazy) string mesaj) { - if (önem >= önemAyarı) { - writefln("%s", mesaj); - } -} - -// ... - - if (!bağlanıldı_mı) { - logla(Önem.orta, - $(HILITE format)("Hata. Bağlantı durumu: '%s'.", - bağlantıDurumunuÖğren())); - } ---- - -$(P -Yukarıdaki $(C mesaj) isimli parametre $(C lazy) olduğundan, işleve o parametreye karşılık gönderilen bütün $(C format) ifadesi (yaptığı $(C bağlantıDurumunuÖğren()) çağrısı dahil), ancak o parametre işlev içinde kullanıldığında işletilir. -) - -$(P -Perde arkasında $(C lazy) parametreler aslında temsilcidirler. O parametrelere karşılık olarak gönderilen ifadeler otomatik olarak temsilci nesnelerine dönüştürülürler. Buna göre, aşağıdaki kod yukarıdakinin eşdeğeridir: -) - ---- -void logla(Önem önem, string $(HILITE delegate)() tembelMesaj) { // (1) - if (önem >= önemAyarı) { - writefln("%s", $(HILITE tembelMesaj())); // (2) - } -} - -// ... - - if (!bağlanıldı_mı) { - logla(Önem.orta, - delegate string() $(HILITE {) // (3) - return - format("Hata. Bağlantı durumu: '%s'.", - bağlantıDurumunuÖğren()); - $(HILITE })); - } ---- - -$(OL - -$(LI $(C lazy) parametre $(C string) değildir; $(C string) döndüren bir temsilcidir.) - -$(LI O temsilci çağrılır ve dönüş değeri kullanılır.) - -$(LI Bütün ifade onu döndüren bir temsilci ile sarmalanır.) - -) - -$(H6 $(IX belirsiz sayıda lazy parametre) Belirsiz sayıda $(C lazy) parametre) - -$(P -Belirsiz sayıda $(C lazy) parametresi olan bir işlevin $(I sayıları belirsiz olan) bu parametreleri $(C lazy) olarak işaretlemesi olanaksızdır. -) - -$(P -Bu durumda kullanılan çözüm, belirsiz sayıda $(C delegate) parametre tanımlamaktır. Böyle parametreler temsilcilerin $(I dönüş türüne) uyan bütün ifadeleri parametre değeri olarak kabul ederler. Bir koşul, bu temsilcilerin kendilerinin parametre almamasıdır: -) - ---- -import std.stdio; - -void foo(double delegate()$(HILITE []) parametreler$(HILITE ...)) { - foreach (parametre; parametreler) { - writeln($(HILITE parametre())); // Temsilcinin çağrılması - } -} - -void main() { - foo($(HILITE 1.5), () => 2.5); /* 'double' ifade, temsilci - * olarak gönderiliyor. */ -} ---- - -$(P -Yukarıdaki hem $(C double) ifade hem de isimsiz işlev belirsiz sayıdaki parametreye uyar. $(C double) ifade otomatik olarak bir temsilci ile sarmalanır ve işlev gereğinde $(I tembel) olarak işletilebilecek olan bu parametrelerinin değerlerini çıkışa yazdırır: -) - -$(SHELL -1.5 -2.5 -) - -$(P -Bu yöntemin bir yetersizliği, bütün parametrelerin aynı türden olmalarının gerekmesidir (bu örnekte $(C double)). Daha sonraki $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar bölümünde) göreceğimiz $(I çokuzlu şablon parametreleri) bu yetersizliği giderir. -) - -$(H5 $(IX toString, delegate) $(C delegate) parametreli $(C toString)) - -$(P -Nesneleri $(C string) türünde ifade etmek için kullanılan $(C toString) işlevini kitabın bu noktasına kadar hep parametre almayan ve $(C string) döndüren işlevler olarak tanımladık. Yapılar ve sınıflar kendi üyelerinin $(C toString) işlevlerini $(C format) aracılığıyla dolaylı olarak çağırıyorlardı ve $(C toString) işlevleri kolaylıkla tanımlanabiliyordu: -) - ---- -import std.stdio; -import std.string; - -struct Nokta { - int x; - int y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} - -struct Renk { - ubyte r; - ubyte g; - ubyte b; - - string toString() const { - return format("RGB:%s,%s,%s", r, g, b); - } -} - -struct RenkliNokta { - Renk renk; - Nokta nokta; - - string toString() const { - // Bu, Renk.toString ve Nokta.toString'den yararlanıyor: - return format("{%s;%s}", renk, nokta); - } -} - -struct Poligon { - RenkliNokta[] noktalar; - - string toString() const { - // Bu, RenkliNokta.toString'den yararlanıyor - return format("%s", noktalar); - } -} - -void main() { - auto poligon = Poligon( - [ RenkliNokta(Renk(10, 10, 10), Nokta(1, 1)), - RenkliNokta(Renk(20, 20, 20), Nokta(2, 2)), - RenkliNokta(Renk(30, 30, 30), Nokta(3, 3)) ]); - - writeln(poligon); -} ---- - -$(P -Yukarıdaki $(C poligon) nesnesinin programın son satırında çıktıya yazdırılabilmesi için $(C Poligon), $(C RenkliNokta), $(C Renk), ve $(C Nokta) yapılarının $(C toString) işlevlerinden dolaylı olarak yararlanıldığında toplam 10 farklı $(C string) nesnesi oluşturulmaktadır. Dikkat ederseniz, alt düzeylerde oluşturulan her $(C string) nesnesi yalnızca kendi üst düzeyindeki $(C string) nesnesini oluşturmak için kullanılmakta ve ondan sonra yaşamı sona ermektedir. -) - -$(P -Sonuçta çıktıya tek mesaj yazdırılmış olmasına rağmen 10 adet $(C string) nesnesi oluşturulmuş, ancak bunlardan yalnızca sonuncusu çıktıya yazdırılmak için kullanılmıştır: -) - -$(SHELL -[{RGB:10,10,10;(1,1)}, {RGB:20,20,20;(2,2)}, {RGB:30,30,30;(3,3)}] -) - -$(P -Bu yöntem kodun gereksizce yavaş işlemesine neden olabilir. -) - -$(P -Bu yavaşlığın önüne geçmek için $(C toString) işlevinin $(C delegate) türünde parametre alan ve genel olarak daha hızlı işleyen çeşidi de kullanılabilir: -) - ---- - void toString(void delegate(const(char)[]) çıkış) const; ---- - -$(P -Dönüş türünün $(C void) olmasından anlaşıldığı gibi, $(C toString)'in bu tanımı $(C string) döndürmez. Onun yerine, çıktıya yazılacak olan karakterleri kendisine verilen temsilciye gönderir. O temsilci de verilen karakterleri sonuçta yazdırılacak olan tek $(C string)'in sonuna ekler. -) - -$(P -$(IX formattedWrite, std.format) Bu $(C toString) işlevinden yararlanmak için yapılması gereken, $(C std.string.format) yerine $(C std.format.formattedWrite)'ı çağırmak ve $(C çıkış) isimli parametreyi onun ilk parametresi olarak vermektir: -) - ---- -import std.stdio; -$(HILITE import std.format;) - -struct Nokta { - int x; - int y; - - void toString(void delegate(const(char)[]) çıkış) const { - $(HILITE formattedWrite)(çıkış, "(%s,%s)", x, y); - } -} - -struct Renk { - ubyte r; - ubyte g; - ubyte b; - - void toString(void delegate(const(char)[]) çıkış) const { - $(HILITE formattedWrite)(çıkış, "RGB:%s,%s,%s", r, g, b); - } -} - -struct RenkliNokta { - Renk renk; - Nokta nokta; - - void toString(void delegate(const(char)[]) çıkış) const { - $(HILITE formattedWrite)(çıkış, "{%s;%s}", renk, nokta); - } -} - -struct Poligon { - RenkliNokta[] noktalar; - - void toString(void delegate(const(char)[]) çıkış) const { - $(HILITE formattedWrite)(çıkış, "%s", noktalar); - } -} - -void main() { - auto poligon = Poligon( - [ RenkliNokta(Renk(10, 10, 10), Nokta(1, 1)), - RenkliNokta(Renk(20, 20, 20), Nokta(2, 2)), - RenkliNokta(Renk(30, 30, 30), Nokta(3, 3)) ]); - - writeln(poligon); -} ---- - -$(P -Bu programın farkı, yine toplam 10 adet $(C toString) işlevi çağrılmış olmasına rağmen, o çağrıların tek $(C string)'in sonuna karakter eklenmesine neden olmalarıdır. -) - -$(H5 Özet) - -$(UL -$(LI $(C function) anahtar sözcüğü ile işlev göstergeleri tanımlanabilir ve bu göstergeler daha sonra işlev gibi kullanılabilir.) - -$(LI $(C delegate) anahtar sözcüğü temsilci tanımlar. Temsilci, işlev göstergesine ek olarak o işlev göstergesinin kullandığı kapsamı da barındırır.) - -$(LI Bir nesne ve onun bir üye işlevi $(C &nesne.üye_işlev) söz dizimi ile $(C delegate) oluşturur.) - -$(LI İşlev göstergesi veya temsilci gereken yerlerde isimsiz işlevler veya isimsiz temsilciler tanımlanabilir.) - -$(LI Temsilciler $(C .funcptr) ve $(C .ptr) niteliklerine değer atanarak açıkça oluşturulabilirler.) - -$(LI İsimsiz işlevlerin çeşitli kısa söz dizimleri vardır. Tek $(C return) deyimi içeren isimsiz işlevler bu söz dizimlerinin en kısası olan $(C parametre => ifade) söz dizimi ile tanımlanabilirler.) - -$(LI $(C toString)'in daha hızlı işleyen çeşidi $(C delegate) parametre alır.) - -) - -Macros: - SUBTITLE=İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler - - DESCRIPTION=İşlev göstergeleri, isimsiz işlevler, ve D'nin fonksiyonel porogramlama (functional programming) olanaklarından olan temsilciler - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial function delegate işlev fonksiyon göstergesi işaretçisi fonksiyonel programlama işlev hazır değeri isimsiz işlev lambda kapama - -SOZLER= -$(adres) -$(belirsiz_sayida_parametre) -$(emirli_programlama) -$(fonksiyonel_programlama) -$(gosterge) -$(hazir_deger) -$(isimsiz_islev) -$(kapama) -$(tanimsiz_davranis) -$(tembel_degerlendirme) -$(temsilci) diff --git a/ddili/src/ders/d/karakterler.d b/ddili/src/ders/d/karakterler.d deleted file mode 100644 index fb0bdf2..0000000 --- a/ddili/src/ders/d/karakterler.d +++ /dev/null @@ -1,598 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX karakter) Karakterler) - -$(P -Karakterler yazıları oluşturan en alt birimlerdir: harfler, rakamlar, noktalama işaretleri, boşluk karakteri, vs. Önceki bölümdeki dizilere ek olarak bu bölümde de karakterleri tanıyınca iki bölüm sonra anlatılacak olan $(I dizgi) kavramını anlamak kolay olacak. -) - -$(P -Bilgisayar veri türleri temelde bitlerden oluştuklarından, karakterler de bitlerin birleşimlerinden oluşan tamsayı değerler olarak ifade edilirler. Örneğin, küçük harf $(C 'a')nın tamsayı değeri 97'dir ve $(C '1') rakamının tamsayı değeri 49'dur. Bu değerler tamamen anlaşmalara bağlı olarak atanmışlardır ve kökleri ASCII kod tablosuna dayanır. -) - -$(P -Karakterler bazı dillerde geleneksel olarak 256 farklı değer tutabilen $(C char) türüyle gösterilirler. Eğer $(C char) türünü başka dillerden tanıyorsanız onun her harfi barındıracak kadar büyük bir tür olmadığını biliyorsunuzdur. D'de üç farklı karakter türü bulunur. Buna açıklık getirmek için bu konunun tarihçesini gözden geçirelim. -) - -$(H5 Tarihçe) - -$(H6 $(IX ASCII) ASCII Tablosu) - -$(P -Donanımın çok kısıtlı olduğu günlerde tasarlanan ilk ASCII tablosu 7 bitlik değerlerden oluşuyordu ve bu yüzden ancak 128 karakter değeri barındırabiliyordu. Bu değerler İngiliz alfabesini oluşturan 26 harfin küçük ve büyük olanlarını, rakamları, sık kullanılan noktalama işaretlerini, programların çıktılarını uç birimlerde gösterirken kullanılan kontrol karakterlerini, vs. ifade etmek için yeterliydi. -) - -$(P -Örnek olarak, $(STRING "merhaba") metnindeki karakterlerin ASCII kodları sırasıyla şöyledir (bu gösterimlerde okumayı kolaylaştırmak için bayt değerleri arasında virgül kullanıyorum): -) - -$(MONO -109,101,114,104,97,98,97 -) - -$(P -Her bir değer bir harfe karşılık gelir. Örneğin, iki $(C 'a') harfi için iki adet 97 değeri kullanılmıştır. -) - -$(P -Donanımdaki gelişmeler doğrultusunda ASCII tablosundaki kodlar daha sonra 8 bite çıkartılarak 256 karakter destekleyen $(I Genişletilmiş $(ASIL Extended) ASCII) tablosu tanımlanmıştır. -) - -$(H6 $(IX kod tablosu) IBM Kod Tabloları) - -$(P -IBM firması ASCII tablosuna dayanan ve 128 ve daha büyük karakter değerlerini dünya dillerine ayıran bir dizi kod tablosu tanımladı. Bu kod tabloları sayesinde İngiliz alfabesinden başka alfabelerin de desteklenmeleri sağlanmış oldu. Örneğin, Türk alfabesine özgü karakterler IBM'in 857 numaralı kod tablosunda yer aldılar. -) - -$(P -Her ne kadar ASCII'den çok daha yararlı olsalar da, kod tablolarının önemli sorunları vardır: Yazının doğru olarak görüntülenebilmesi için yazıldığı zaman hangi kod tablosunun kullanıldığının bilinmesi gerekir çünkü farklı kod tablolarındaki kodlar farklı karakterlere karşılık gelirler. Örneğin, 857 numaralı kod tablosunda $(C 'Ğ') olan karakter 437 numaralı kod tablosu ile görüntülendiğinde $(C 'ª') karakteri olarak belirir. Başka bir sorun, yazı içinde birden fazla dilin karakteri kullanıldığında kod tablolarının yetersiz kalmalarıdır. Ayrıca, 128'den fazla özel karakteri olan diller zaten 8 bitlik bir tabloda ifade edilemezler. -) - -$(H6 ISO/IEC 8859 Kod Tabloları) - -$(P -Uluslararası standartlaştırma çalışmaları sonucunda ISO/IEC 8859 standart karakter kodları tanımlanmış ve örneğin Türk alfabesinin özel harfleri 8859-9 tablosunda yer almışlardır. Yapısal olarak IBM'in tablolarının eşdeğeri olduklarından IBM'in kod tablolarının sorunları bu standartta da bulunur. Hatta, Felemenkçe'nin ij karakteri gibi bazı karakterler bu tablolarda yer bulamamışlardır. -) - -$(H6 $(IX unicode) Unicode) - -$(P -Unicode standardı bu sorunları çözer. Unicode, dünya dillerindeki ve yazı sistemlerindeki harflerin, karakterlerin, ve yazım işaretlerinin yüz binden fazlasını tanımlar ve her birisine farklı bir kod verir. Böylece, Unicode'un tanımladığı kodları kullanan metinler bütün dünya karakterlerini hiçbir karışıklık ve kısıtlama olmadan bir arada bulundurabilirler. -) - -$(H5 $(IX kodlama, unicode) Unicode kodlama çeşitleri) - -$(P -Unicode, her bir karaktere bir kod değeri verir. Örnek olarak, $(C 'Ğ') harfinin Unicode'daki değeri 286'dır. Unicode'un desteklediği karakter sayısı o kadar fazla olunca, karakterleri ifade eden değerler de doğal olarak artık 8 bitle ifade edilemezler. Örneğin, kod değeri 255'ten büyük olduğundan $(C 'Ğ')nin en az 2 baytla gösterilmesi gerekir. -) - -$(P -Karakterlerin elektronik ortamda nasıl ifade edildiklerine $(I karakter kodlaması) denir. Yukarıda $(STRING "merhaba") dizgisinin karakterlerinin ASCII kodlarıyla nasıl ifade edildiklerini görmüştük. Şimdi Unicode karakterlerinin standart kodlamalarından üçünü göreceğiz. -) - -$(P -$(IX UTF-32) $(B UTF-32:) Bu kodlama her Unicode karakteri için 32 bit (4 bayt) kullanır. $(STRING "merhaba")nın UTF-32 kodlaması da ASCII kodlamasıyla aynıdır. Tek fark, her karakter için 4 bayt kullanılmasıdır: -) - -$(MONO -0,0,0,109, 0,0,0,101, 0,0,0,114, 0,0,0,104, 0,0,0,97, -0,0,0,98, 0,0,0,97 -) - -$(P -Başka bir örnek olarak, ifade edilecek metnin örneğin $(STRING "aĞ") olduğunu düşünürsek: -) - -$(MONO -0,0,0,97, 0,0,1,30 -) - -$(P -$(I Not: Baytların sıraları farklı platformlarda farklı olabilir.) -) - -$(P -$(C 'a')da bir ve $(C 'Ğ')de iki adet anlamlı bayt olduğundan toplam beş adet de sıfır bulunmaktadır. Bu sıfırlar her karaktere 4 bayt verebilmek için gereken $(I doldurma baytları) olarak düşünülebilir. -) - -$(P -Dikkat ederseniz, bu kodlama her zaman için ASCII kodlamasının 4 katı yer tutmaktadır. Metin içindeki karakterlerin büyük bir bölümünün İngiliz alfabesindeki karakterlerden oluştuğu durumlarda, çoğu karakter için 3 tane de 0 kullanılacağından bu kodlama duruma göre fazla savurgan olabilir. -) - -$(P -Öte yandan, karakterlerin her birisinin tam olarak 4 bayt yer tutuyor olmasının getirdiği yararlar da vardır. Örneğin, bir sonraki Unicode karakteri hiç hesap gerektirmeden her zaman için tam dört bayt ötededir. -) - -$(P -$(IX UTF-16) $(B UTF-16:) Bu kodlama, Unicode karakterlerinin çoğunu 16 bitle (2 bayt) gösterir. İki bayt yaklaşık olarak 65 bin değer tutabildiğinden, yaklaşık yüz bin Unicode karakterinin geri kalan 35 bin kadarı için daha fazla bayt kullanmak gerekir. -) - -$(P -Örnek olarak $(STRING "aĞ") UTF-16'da aşağıdaki 4 bayt olarak kodlanır: -) - -$(MONO -0,97, 1,30 -) - -$(P -$(I Not: Baytların sıraları farklı ortamlarda farklı olabilir.) -) - -$(P -Bu kodlama çoğu belgede UTF-32'den daha az yer tutar ama nadir kullanılan bazı karakterler için ikiden fazla bayt kullandığından işlenmesi daha karmaşıktır. -) - -$(P -$(IX UTF-8) $(B UTF-8:) Bu kodlama, karakterleri en az 1 ve en fazla 4 baytla ifade eder. Eğer karakter ASCII tablosundaki karakterlerden biriyse, tek baytla ve aynen ASCII tablosundaki değeriyle ifade edilir. Bunların dışındaki karakterlerin bazıları 2, bazıları 3, diğerleri de 4 bayt olarak ifade edilirler. Türk alfabesinin İngiliz alfabesinde bulunmayan özel karakterleri 2 baytlık gruptadırlar. -) - -$(P -Çoğu belge için UTF-8 bütün kodlamalar arasında en az yer tutan kodlamadır. Başka bir yararı, ASCII tablosundaki kodlara aynen karşılık geldiğinden, ASCII kodlanarak yazılmış ve İngiliz alfabesini kullanan belgeler de otomatik olarak UTF-8 düzenine uyarlar. Bu kodlamada hiç savurganlık yoktur; bütün karakterler gerçekten gereken sayıda baytla ifade edilirler. Örneğin, $(STRING "aĞ")ın UTF-8 kodlaması aşağıdaki gibidir: -) - -$(MONO -97, 196,158 -) - -$(H5 D'nin karakter türleri) - -$(P -$(IX char) -$(IX wchar) -$(IX dchar) -D'de karakterleri ifade etmek için 3 farklı tür vardır. Bunlar yukarıda anlatılan Unicode kodlama yöntemlerine karşılık gelirler. Temel türlerin tanıtıldığı sayfada gösterildikleri gibi: -) - - - - - - - - - - - - - - - - - - - - - -
    Tür Açıklama İlk Değeri
    charişaretsiz 8 bit UTF-8 karakter değeri0xFF
    wcharişaretsiz 16 bit UTF-16 karakter değeri0xFFFF
    dcharişaretsiz 32 bit UTF-32 karakter değeri0x0000FFFF
    - -$(P -Başka bazı programlama dillerinden farklı olarak, D'de her karakter aynı uzunlukta olmayabilir. Örneğin, $(C 'Ğ') harfi Unicode'da en az 2 baytla gösterilebildiğinden 8 bitlik $(C char) türüne sığmaz. Öte yandan, $(C dchar) 4 bayttan oluştuğundan her Unicode karakterini tutabilir. -) - -$(H5 $(IX hazır değer, karakter) $(IX karakter sabiti) Karakter sabitleri) - -$(P -Karakterleri program içinde tek olarak belirtmek gerektiğinde etraflarına tek tırnak işaretleri koyulur: -) - ---- - char a_harfi = 'a'; - wchar büyük_yumuşak_g = 'Ğ'; ---- - -$(P -Karakter sabitleri için çift tırnak kullanılamaz çünkü o zaman iki bölüm sonra göreceğimiz $(I dizgi) sabiti anlamına gelir: $(C 'a') karakter değeridir, $(STRING "a") tek karakterli bir dizgidir. -) - -$(P -Türk alfabesindeki bazı harflerin Unicode kodları 2 bayttan oluştuklarından $(C char) türündeki değişkenlere atanamazlar. -) - -$(P -Karakterleri sabit olarak program içine yazmanın bir çok yolu vardır: -) - -$(UL -$(LI En doğal olarak, klavyeden doğrudan karakterin tuşuna basmak -) - -$(LI Çalışma ortamındaki başka bir programdan veya bir metinden kopyalamak. Örneğin, bir internet sitesinden veya çalışma ortamında karakter seçmeye yarayan bir programdan kopyalanabilir (Linux ortamlarında bu programın ismi $(I Character Map)'tir (uç birimlerde $(C charmap)).) -) - -$(LI Karakterlerin bazılarını standart kısa isimleriyle yazmak. Bunun söz dizimi $(C \&$(I karakter_ismi);) biçimindedir. Örneğin, avro karakterinin ismi $(C euro)'dur ve programda değeri şöyle yazılabilir: - ---- - wchar para_sembolü = '\€'; ---- - -$(P -Diğer isimli karakterleri D'nin $(LINK2 http://dlang.org/entity.html, isimli karakterler listesinde) bulabilirsiniz. -) - -) - -$(LI Karakterleri tamsayı Unicode değerleriyle belirtmek: - ---- - char a = 97; - wchar Ğ = 286; ---- - -) - -$(LI ASCII tablosundaki karakterleri değerleriyle $(C \$(I sekizli_düzende_kod)) veya $(C \x$(I on_altılı_düzende_kod)) söz dizimleriyle yazmak: - ---- - char soru_işareti_sekizli = '\77'; - char soru_işareti_on_altılı = '\x3f'; ---- - -) - -$(LI Karakterleri Unicode değerleriyle yazmak. $(C wchar) için $(C \u$(I dört_haneli_kod)) söz dizimini, $(C dchar) için de $(C \U$(I sekiz_haneli_kod)) söz dizimini kullanabilirsiniz ($(C u) ve $(C U) karakterlerinin farklı olduklarına dikkat edin). Bu yazımda karakterin kodunun on altılı sayı sisteminde $(ASIL hexadecimal) yazılması gerekir: - ---- - wchar Ğ_w = '\u011e'; - dchar Ğ_d = '\U0000011e'; ---- - -) - -) - -$(P -Bu yöntemler karakterleri çift tırnak içinde bir arada yazdığınız durumlarda da geçerlidir. Örneğin, aşağıdaki iki satır aynı çıktıyı verirler: -) - ---- - writeln("Ağ fiyatı: 10.25€"); - writeln("\x41\u011f fiyatı: 10.25\€"); ---- - - -$(H5 Kontrol karakterleri) - -$(P -$(IX kontrol karakteri) Bazı karakterler yalnızca metin düzeniyle ilgilidirler; kendilerine özgü görünümleri yoktur. Örneğin, uç birime yeni bir satıra geçileceğini bildiren $(I yeni satır) karakterinin gösterilecek bir şekli yoktur; yalnızca yeni bir satıra geçilmesini sağlar. Böyle karakterlere $(I kontrol karakteri) denir. Kontrol karakterleri $(C \$(I özel_harf)) söz dizimiyle ifade edilirler. -) - - - - - - - - - - - - - - - - - - - - - -
    Yazım İsim Açıklama
    \nyeni satırYeni satıra geçirir
    \rsatır başıSatırın başına götürür
    \tsekmeBir sonraki sekme noktasına kadar boşluk bırakır
    - -$(P -Örneğin, çıktıda otomatik olarak yeni satır açmayan $(C write) bile $(C \n) karakterlerini yeni satır açmak için kullanır. Yazdırılacak metnin içinde istenen noktalara $(C \n) karakterleri yerleştirmek o noktalarda yeni satır açılmasını sağlar: -) - ---- - write("birinci satır\nikinci satır\nüçüncü satır\n"); ---- - -$(P -Çıktısı: -) - -$(SHELL -birinci satır -ikinci satır -üçüncü satır -) - -$(H5 $(IX ') $(IX \) Tek tırnak ve ters bölü) - -$(P -Tek tırnak karakterinin kendisini tek tırnaklar arasında yazamayız çünkü derleyici ikinci tırnağı gördüğünde tırnakları kapattığımızı düşünür: $(C '''). İlk ikisi açma ve kapama tırnakları olarak algılanırlar, üçüncüsü de tek başına algılanır ve yazım hatasına neden olur. -) - -$(P -Ters bölü karakteri de başka özel karakterleri ifade etmek için kullanıldığından, derleyici onu bir özel karakterin başlangıcı olarak algılar: $(C '\'). Derleyici $(C \') yazımını bir özel karakter olarak algılar ve baştaki tek tırnakla eşlemek için bir tane daha tek tırnak arar ve bulamaz. -) - -$(P -Bu iki karakteri sabit olarak yazmak gerektiğinde başlarına bir ters bölü daha yazılır: -) - - - - - - - - - - - - - - - -
    Yazım İsim Açıklama
    \'tek tırnakTek tırnağın karakter olarak tanımlanmasına olanak verir: '\''
    \\ters bölüTers bölü karakterinin yazılmasına olanak verir: '\\' veya "\\"
    - - -$(H5 $(IX std.uni) std.uni modülü) - -$(P -$(C std.uni) modülü Unicode karakterleriyle ilgili yardımcı işlevler içerir. Bu modüldeki işlevleri $(LINK2 http://dlang.org/phobos/std_uni.html, kendi belgesinde) bulabilirsiniz. -) - -$(P -$(C is) ile başlayan işlevler karakterle ilgili sorular cevaplarlar: cevap yanlışsa $(C false), doğruysa $(C true) döndürürler. Bu işlevler mantıksal ifadelerde kullanışlıdırlar: -) - -$(UL -$(LI $(C isLower): Küçük harf mi? -) -$(LI $(C isUpper): Büyük harf mi? -) -$(LI $(C isAlpha): Herhangi bir harf mi? -) -$(LI $(C isWhite): Herhangi bir boşluk karakteri mi? -) -) - -$(P -$(C to) ile başlayan işlevler verilen karakteri kullanarak yeni bir karakter üretirler: -) - -$(UL -$(LI $(C toLower): Küçük harfini üretir -) -$(LI $(C toUpper): Büyük harfini üretir -) -) - -$(P -Aşağıdaki program bütün bu işlevleri kullanmaktadır: -) - ---- -import std.stdio; -import std.uni; - -void main() { - writeln("ğ küçük müdür? ", isLower('ğ')); - writeln("Ş küçük müdür? ", isLower('Ş')); - - writeln("İ büyük müdür? ", isUpper('İ')); - writeln("ç büyük müdür? ", isUpper('ç')); - - writeln("z harf midir? ", isAlpha('z')); - writeln("\€ harf midir? ", isAlpha('\€')); - - writeln("'yeni satır' boşluk mudur? ", isWhite('\n')); - writeln("alt çizgi boşluk mudur? ", isWhite('_')); - - writeln("Ğ'nin küçüğü: ", toLower('Ğ')); - writeln("İ'nin küçüğü: ", toLower('İ')); - - writeln("ş'nin büyüğü: ", toUpper('ş')); - writeln("ı'nın büyüğü: ", toUpper('ı')); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -ğ küçük müdür? true -Ş küçük müdür? false -İ büyük müdür? true -ç büyük müdür? false -z harf midir? true -€ harf midir? false -'yeni satır' boşluk mudur? true -alt çizgi boşluk mudur? false -Ğ'nin küçüğü: ğ -İ'nin küçüğü: i -ş'nin büyüğü: Ş -ı'nın büyüğü: I -) - -$(H5 Türk alfabesinin şanssız harfleri: ı ve i) - -$(P -$(C 'ı') ve $(C 'i') harflerinin küçük ve büyük biçimleri Türk alfabesinde tutarlıdır: noktalıysa noktalı, noktasızsa noktasız. Oysa çoğu yabancı alfabede bu konuda bir tutarsızlık vardır: noktalı $(C 'i')nin büyüğü noktasız $(C 'I')dır. -) - -$(P -Bilgisayar sistemlerinin temelleri İngiliz alfabesiyle başladığından $(C 'i')nin büyüğü $(C 'I), $(C 'I')nın küçüğü ise $(C 'i')dir. Bu yüzden bu iki harf için özel dikkat göstermek gerekir: -) - ---- -import std.stdio; -import std.uni; - -void main() { - writeln("i'nin büyüğü: ", toUpper('i')); - writeln("I'nın küçüğü: ", toLower('I')); -} ---- - -$(P -İstenmeyen çıktısı: -) - -$(SHELL -i'nin büyüğü: I -I'nın küçüğü: i -) - -$(P -Karakter kodları kullanılarak yapılan küçük-büyük dönüşümleri ve harf sıralamaları aslında bütün alfabeler için sorunludur. -) - -$(P -Örneğin, $(C 'I')nın küçüğünün $(C 'i') olarak dönüştürülmesi Azeri ve Kelt alfabeleri için de yanlıştır. -) - -$(P -Benzer sorunlar harflerin sıralanmalarında da bulunur. Örneğin, $(C 'ğ') gibi Türk alfabesine özgü harfler $(C 'z')den sonra sıralandıkları gibi, $(C 'á') gibi aksanlı harfler İngiliz alfabesinde bile $(C 'z')den sonra gelirler. -) - -$(H5 $(IX giriş, karakter okuma) Girişten karakter okumadaki sorunlar) - -$(P -Unicode karakterleri girişten okunurken beklenmedik sonuçlarla karşılaşılabilir. Bunlar genellikle $(I karakter) ile ne kastedildiğinin açık olmamasındandır. Daha ileriye gitmeden önce bu sorunu gösteren bir programa bakalım: -) - ---- -import std.stdio; - -void main() { - char harf; - write("Lütfen bir harf girin: "); - readf(" %s", &harf); - writeln("Okuduğum harf: ", harf); -} ---- - -$(P -Yukarıdaki programı Unicode kodlaması kullanılmayan bir ortamda çalıştırdığınızda programın girişinden aldığı Türkçe harfleri belki de doğru olarak yazdırdığını görebilirsiniz. -) - -$(P -Öte yandan, aynı programı çoğu Linux uç biriminde olduğu gibi bir Unicode ortamında çalıştırdığınızda, yazdırılan harfin sizin yazdığınızla aynı olmadığını görürsünüz. Örneğin, UTF-8 kodlaması kullanan bir uç birimde ASCII tablosunda bulunmayan bir harf girilmiş olsun: -) - -$(SHELL -Lütfen bir harf girin: ğ -Okuduğum harf: $(SHELL_NOTE girilen harf görünmüyor) -) - -$(P -Bunun nedeni, UTF-8 kodlaması kullanan uç birimin ASCII tablosunda bulunmayan $(C 'ğ') gibi harfleri birden fazla kod ile temsil etmesi, ve $(C readf)'in $(C char) okurken bu kodlardan yalnızca birincisini alıyor olmasıdır. O $(C char) da asıl karakteri temsil etmeye yetmediğinden, $(C writeln)'ın yazdırdığı $(I eksik kodlanmış olan harf) uç birimde gösterilememektedir. -) - -$(P -$(C char) olarak okunduğunda harfin kendisinin değil, onu oluşturan kodların okunmakta olduklarını harfi iki farklı $(C char) olarak okuyarak görebiliriz: -) - ---- -import std.stdio; - -void main() { - char birinci_kod; - char ikinci_kod; - - write("Lütfen bir harf girin: "); - readf(" %s", &birinci_kod); - readf(" %s", &ikinci_kod); - - writeln("Okuduğum harf: ", birinci_kod, ikinci_kod); -} ---- - -$(P -Program girişten iki $(C char) okumakta ve onları aynı sırada çıkışa yazdırmaktadır. O $(C char) değerlerinin art arda uç birime gönderilmiş olmaları, bu sefer harfin UTF-8 kodlamasını standart çıkış tarafında tamamlamakta ve karakter doğru olarak gösterilmektedir: -) - -$(SHELL -Lütfen bir harf girin: ğ -Okuduğum harf: ğ -) - -$(P -Bu sonuçlar standart giriş ve çıkışın $(C char) akımları olmalarından kaynaklanır. Karakterlerin iki bölüm sonra göreceğimiz dizgiler aracılığıyla aslında çok daha rahat okunduklarını göreceksiniz. -) - -$(H5 D'nin Unicode desteği) - -$(P -Unicode çok büyük ve karmaşık bir standarttır. D, Unicode'un oldukça kullanışlı bir alt kümesini destekler. -) - -$(P -Unicode ile kodlanmış olan bir metin en aşağıdan en yukarıya doğru şu düzeylerden oluşur: -) - -$(UL - -$(LI $(IX kod birimi) $(B Kod birimi) $(ASIL code unit): UTF kodlamalarını oluşturan kod değerleridir. Unicode karakterleri, kodlamaya ve karakterin kendisine bağlı olarak bir veya daha fazla kod biriminden oluşabilirler. Örneğin, UTF-8 kodlamasında $(C 'a') karakteri tek kod biriminden, $(C 'ğ') karakteri ise iki kod biriminden oluşur. - -$(P -D'nin $(C char), $(C wchar), ve $(C dchar) türleri sırasıyla UTF-8, UTF-16, ve UTF-32 kod birimlerini ifade ederler. -) - -) - -$(LI $(IX kod noktası) $(B Kod noktası) $(ASIL code point): Unicode'un tanımlamış olduğu her harf, im, vs. bir kod noktasıdır. Örneğin, $(C 'a') ve $(C 'ğ') iki farklı kod noktasıdır. - -$(P -Bu kod noktaları kodlamaya bağlı olarak bir veya daha fazla kod birimi ile ifade edilirler. Yukarıda da değindiğim gibi, UTF-8 kodlamasında $(C 'a') tek kod birimi ile, $(C 'ğ') ise iki kod birimi ile ifade edilir. Öte yandan, her ikisi de UTF-16 ve UTF-32 kodlamalarında tek kod birimi ile ifade edilirler.) - -$(P -D'de kod noktalarını tam olarak destekleyen tür $(C dchar)'dır. $(C char) ve $(C wchar) ise yalnızca kod birimi türü olarak kullanılmaya elverişlidirler. -) - -) - -$(LI $(B Karakter) $(ASIL character): yazı sistemlerinde kullanılmak üzere Unicode'un tanımlamış olduğu bütün şekiller, imler, ve konuşma dilinde "karakter" veya "harf" dediğimiz her şey bu tanıma girer. - -$(P -Bu konuda Unicode'un getirdiği bir karışıklık, bazı karakterlerin birden fazla kod noktasından oluşabilmeleridir. Örneğin, $(C 'ğ') harfini ifade etmenin iki yolu vardır:) - -$(UL - -$(LI Tek başına $(C 'ğ') kod noktası olarak) - -$(LI Art arda gelen $(C 'g') ve $(C '˘') kod noktaları olarak ($(C 'g') ve sonrasında gelen $(I birleştirici) $(ASIL combining) breve şapkası)) -) - -$(P -Farklı kod noktalarından oluştuklarından, tek kod noktası olan $(C 'ğ') karakteri ile art arda gelen $(C 'g') ve $(C '˘') karakterlerinin ilgileri yoktur. -) - -) - -) - -$(H5 Özet) - -$(UL - -$(LI Unicode, dünya yazı sistemlerindeki bütün karakterleri destekler.) - -$(LI $(C char) UTF-8 kodlaması içindir; karakterleri ifade etmeye genelde elverişli olmasa da ASCII tablosunu destekler.) - -$(LI $(C wchar) UTF-16 kodlaması içindir; karakterleri ifade etmeye genelde elverişli olmasa da özel durumlarda birden fazla alfabe karakterini destekler.) - -$(LI $(C dchar) UTF-32 kodlaması içindir; 32 bit olması nedeniyle bütün Unicode karakterlerini destekler ve $(I kod noktası) olarak kullanılabilir.) - -) - -Macros: - SUBTITLE=Karakterler - - DESCRIPTION=D dilinde karakterlerin ve Unicode kodlama çeşitlerinin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial karakter char wchar dchar utf-8 utf-16 utf-32 unicode ascii - -SOZLER= -$(bayt) -$(bit) -$(dizgi) -$(karakter) -$(karakter_kodlamasi) -$(kod_tablosu) -$(kontrol_karakteri) -$(uc_birim) diff --git a/ddili/src/ders/d/katmalar.d b/ddili/src/ders/d/katmalar.d deleted file mode 100644 index 1ecee8f..0000000 --- a/ddili/src/ders/d/katmalar.d +++ /dev/null @@ -1,506 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX mixin) $(IX katma) Katmalar) - -$(P -Katmalar, derleme zamanında şablonlar veya dizgiler tarafından üretilen kodların programın istenen noktalarına eklenmelerini sağlarlar. -) - -$(H5 $(IX şablon katması) Şablon katmaları) - -$(P -Şablonların belirli kalıplara göre kod üreten olanaklar olduklarını $(LINK2 /ders/d/sablonlar.html, Şablonlar) ve $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar) bölümlerinde görmüştük. Şablonlardan yararlanarak farklı parametre değerleri için işlev, yapı, birlik, sınıf, arayüz, ve yasal olduğu sürece her tür D kodunu oluşturabiliyorduk. -) - -$(P -Şablon katmaları, bir şablon içinde tanımlanmış olan bütün kodların programın belirli bir noktasına, sanki oraya açıkça elle yazılmış gibi eklenmelerini sağlarlar. Bu açıdan, C ve C++ dillerindeki makrolar gibi işledikleri düşünülebilir. -) - -$(P -$(C mixin) anahtar sözcüğü, şablonun belirli bir kullanımını programın herhangi bir noktasına yerleştirir. "Katmak", "içine karıştırmak" anlamına gelen "mix in"den türemiştir. $(C mixin) anahtar sözcüğünden sonra şablonun belirli parametre değerleri için bir kullanımı yazılır: -) - ---- - mixin $(I bir_şablon)!($(I şablon_parametreleri)) ---- - -$(P -O şablonun o parametrelerle kullanımı için üretilen kodlar, oldukları gibi $(C mixin) satırının bulunduğu noktaya yerleştirilirler. Aşağıdaki örnekte göreceğimiz gibi, $(C mixin) anahtar sözcüğü şablon katmalarının $(I tanımlarında) da kullanılır. -) - -$(P -Örnek olarak bir köşe dizisini ve o köşeler üzerindeki işlemleri kapsayan bir şablon düşünelim: -) - ---- -$(CODE_NAME KöşeDizisiOlanağı)$(HILITE mixin) template KöşeDizisiOlanağı(T, size_t adet) { - T[adet] köşeler; - - void köşeDeğiştir(size_t indeks, T köşe) { - köşeler[indeks] = köşe; - } - - void köşeleriGöster() { - writeln("Bütün köşelerim:"); - - foreach (i, köşe; köşeler) { - writef("%s:%s ", i, köşe); - } - - writeln(); - } -} ---- - -$(P -O şablon, dizi elemanlarının türü ve eleman adedi konusunda esneklik getirmektedir; tür ve eleman adedi ihtiyaca göre serbestçe seçilebilir. -) - -$(P -O şablonun $(C int) ve $(C 2) parametreleri ile kullanımının istenmekte olduğu, bir $(C mixin) ile şöyle belirtilir: -) - ---- - $(HILITE mixin) KöşeDizisiOlanağı!(int, 2); ---- - -$(P -Yukarıdaki $(C mixin), şablonun içindeki kodları kullanarak iki elemanlı $(C int) dizisini ve o diziyi kullanan iki işlevi oluşturur. Böylece, onları örneğin bir yapının üyeleri haline getirebiliriz: -) - ---- -$(CODE_NAME Çizgi)$(CODE_XREF KöşeDizisiOlanağı)struct Çizgi { - mixin KöşeDizisiOlanağı!(int, 2); -} ---- - -$(P -Şablon içindeki kodlar, $(C T)'ye karşılık $(C int), ve $(C adet)'e karşılık $(C 2) olacak şekilde üretilirler ve $(C mixin) anahtar sözcüğünün bulunduğu yere yerleştirilirler. Böylece, $(C Çizgi) yapısı 2 elemanlı bir dizi ve o dizi ile işleyen iki işlev edinmiş olur: -) - ---- -$(CODE_XREF Çizgi)import std.stdio; - -void main() { - auto çizgi = Çizgi(); - çizgi.köşeDeğiştir(0, 100); - çizgi.köşeDeğiştir(1, 200); - çizgi.köşeleriGöster(); -} ---- - -$(P -Program şu çıktıyı üretir: -) - -$(SHELL -Bütün köşelerim: -0:100 1:200 -) - -$(P -Aynı şablonu örneğin bir işlev içinde ve başka parametre değerleri ile de kullanabiliriz: -) - ---- -$(CODE_XREF KöşeDizisiOlanağı)struct Nokta { - int x; - int y; -} - -void main() { - $(HILITE mixin) KöşeDizisiOlanağı!($(HILITE Nokta), 5); - - köşeDeğiştir(3, Nokta(3, 3)); - köşeleriGöster(); -} ---- - -$(P -O $(C mixin), $(C main)'in içine yerel bir dizi ve yerel iki işlev yerleştirir. Çıktısı: -) - -$(SHELL -Bütün köşelerim: -0:Nokta(0,0) 1:Nokta(0,0) 2:Nokta(0,0) 3:Nokta(3,3) 4:Nokta(0,0) -) - -$(H6 $(IX yerel import) $(IX import, yerel) Şablon katmaları yerel $(C import) kullanmalıdır) - -$(P -Şablon katmalarının oldukları gibi kod içine yerleştirilmeleri kendi kullandıkları modüller açısından bir sorun oluşturur: Şablonun kendi yararlandığı modüller şablonun sonradan eklendiği noktalarda mevcut olmayabilirler. -) - -$(P -Örneğin, aşağıdaki şablonun $(C a) isimli bir modülde tanımlı olduğunu düşünelim. Doğal olarak, bu şablon yararlanmakta olduğu $(C format())'ın tanımlandığı $(C std.string) modülünü ekleyecektir: -) - ---- -module a; - -$(HILITE import std.string;) $(CODE_NOTE yanlış yerde) - -mixin template A(T) { - string a() { - T[] dizi; - // ... - return format("%(%s, %)", dizi); - } -} ---- - -$(P -Ancak, $(C std.string) modülü o şablonu kullanan ortamda eklenmiş değilse $(C format())'ın tanımının bilinmediği yönünde bir derleme hatası alınır. Örneğin, $(C a) modülünü kullanan aşağıdaki program derlenemez: -) - ---- -import a; - -void main() { - mixin A!int; $(DERLEME_HATASI) -} ---- - -$(SHELL -Error: $(HILITE undefined identifier format) -Error: mixin deneme.main.A!int error instantiating -) - -$(P -O yüzden, şablon katmalarının kullandıkları modüller yerel kapsamlarda eklenmelidirler: -) - ---- -module a; - -mixin template A(T) { - string a() { - $(HILITE import std.string;) $(CODE_NOTE doğru yerde) - - T[] dizi; - // ... - return format("%(%s, %)", dizi); - } -} ---- - -$(P -Şablon tanımının içinde olduğu sürece, $(C import) bildirimi $(C a()) işlevinin dışında da bulunabilir. -) - -$(H6 $(IX this, şablon parametresi) Sarmalayan türü katmanın içinde edinmek) - -$(P -Bazı durumlarda katmanın kendisi içine katıldığı türü edinmek zorunda kalabilir. Bunun için daha önce $(LINK2 /ders/d/sablonlar_ayrintili.html, Ayrıntılı Şablonlar bölümünde) gördüğümüz $(C this) şablon parametrelerinden yararlanılır: -) - ---- -mixin template ŞablonKatması(T) { - void birİşlev$(HILITE (this AsılTür))() { - import std.stdio; - writefln("İçine katıldığım asıl tür: %s", - $(HILITE AsılTür).stringof); - } -} - -struct BirYapı { - mixin ŞablonKatması!(int); -} - -void main() { - auto a = BirYapı(); - a.birİşlev(); -} ---- - -$(P -Çıktısı, katılan işlevin asıl türü $(C BirYapı) olarak edindiğini gösteriyor: -) - -$(SHELL -İçine katıldığım asıl tür: BirYapı -) - -$(H5 $(IX dizgi katması) $(IX string katması) Dizgi katmaları) - -$(P -D'nin güçlü bir olanağı değerleri derleme sırasında bilinen dizgilerin de kod olarak programın içine yerleştirilebilmeleridir. -) - -$(P -İçinde yasal D kodları bulunan her dizgi $(C mixin) anahtar sözcüğü ile programa eklenebilir. Bu kullanımda dizginin parantez içinde belirtilmesi gerekir: -) - ---- - mixin $(HILITE $(PARANTEZ_AC))$(I derleme_zamanında_oluşturulan_dizgi)$(HILITE $(PARANTEZ_KAPA)) ---- - -$(P -Örneğin, $(I merhaba dünya) programını bir dizgi katması ile şöyle yazabiliriz: -) - ---- -import std.stdio; - -void main() { - mixin (`writeln("merhaba dünya");`); -} ---- - -$(P -Dizgi içindeki kod $(C mixin) satırına eklenir, program derlenir, ve beklediğimiz çıktıyı verir: -) - -$(SHELL -merhaba dünya -) - -$(P -Bunun etkisini göstermek için biraz daha ileri gidebilir ve bütün programı bile bir dizgi katması olarak yazabiliriz: -) - ---- -mixin ( -`import std.stdio; void main() { writeln("merhaba dünya"); }` -); ---- - -$(P -Bu örneklerdeki $(C mixin)'lere gerek olmadığı açıktır. O kodların şimdiye kadar hep yaptığımız gibi programa açıkça yazılmaları daha mantıklı olacaktır. -) - -$(P -Dizgi katmalarının gücü, kodun derleme zamanında otomatik olarak oluşturulabilmesinden gelir. Derleme zamanında oluşturulabildiği sürece, $(C mixin) ifadesi işlevlerin döndürdüğü dizgilerden bile yararlanabilir. Aşağıdaki örnek $(C mixin)'e verilecek olan kod dizgilerini CTFE'den yararlanarak bir işleve oluşturtmaktadır: -) - ---- -import std.stdio; - -string yazdırmaDeyimi(string mesaj) { - return `writeln("` ~ mesaj ~ `");`; -} - -void main() { - mixin (yazdırmaDeyimi("merhaba dünya")); - mixin (yazdırmaDeyimi("selam dünya")); -} ---- - -$(P -Yukarıdaki program, $(C yazdırmaDeyimi)'nin oluşturduğu iki dizgiyi $(C mixin) satırlarının yerlerine yerleştirir ve program o kodlarla derlenir. Burada dikkatinizi çekmek istediğim nokta, $(C writeln) işlevlerinin $(C yazdırmaDeyimi)'nin içinde çağrılmadıklarıdır. $(C yazdırmaDeyimi)'nin yaptığı, yalnızca içinde $(STRING "writeln") geçen dizgiler döndürmektir. -) - -$(P -O dizgiler $(C mixin)'lerin bulundukları satırlara kod olarak yerleştirilirler. Sonuçta derlenen program, şunun eşdeğeridir: -) - ---- -import std.stdio; - -void main() { - writeln("merhaba dünya"); - writeln("selam dünya"); -} ---- - -$(P -$(C mixin)'li program, sanki o iki $(C writeln) satırı varmış gibi derlenir ve çalışır: -) - -$(SHELL -merhaba dünya -selam dünya -) - -$(H5 $(IX isim alanı, katma) Katmaların isim alanları) - -$(P -Şablon katmaları isim çakışmalarını önlemeye yönelik korumalar getirirler. -) - -$(P -Örneğin, aşağıdaki programda $(C main())'in kapsamı içinde iki farklı $(C i) tanımı bulunmaktadır: Biri $(C main()) içinde açıkça tanımlanan, diğeri de $(C Şablon)'un kod içine katılması ile gelen. Şablon katması sonucunda oluşan isim çakışmaları durumunda şablonun getirdiği tanım değil, katmayı kapsayan isim alanındaki tanım kullanılır: -) - ---- -import std.stdio; - -template Şablon() { - $(HILITE int i;) - - void yazdır() { - writeln(i); // Her zaman için Şablon içindeki i'dir - } -} - -void main() { - $(HILITE int i;) - mixin Şablon; - - i = 42; // main içindeki i'yi değiştirir - writeln(i); // main içindeki i'yi yazdırır - yazdır(); // Şablon'un getirdiği i'yi yazdırır -} ---- - -$(P -Yukarıdaki açıklama satırlarından da anlaşılacağı gibi, her şablon katması kendi içeriğini sarmalayan bir isim alanı tanımlar ve şablon içindeki kodlar öncelikle o isim alanındaki isimleri kullanırlar. Bunu $(C yazdır())'ın davranışında görüyoruz: -) - -$(SHELL -42 -0 $(SHELL_NOTE yazdır()'ın yazdırdığı) -) - -$(P -Birden fazla şablonun aynı ismi tanımlaması ise derleyicinin kendi başına karar veremeyeceği bir isim çakışmasıdır. Bunu görmek için aynı şablonu iki kere katmayı deneyelim: -) - ---- -template Şablon() { - int i; -} - -void main() { - mixin Şablon; - mixin Şablon; - - i = 42; $(DERLEME_HATASI) -} ---- - -$(P -Derleme hatası hangi $(C i)'den bahsedildiğinin bilinemediğini bildirir: -) - -$(SHELL -Error: deneme.main.Şablon!().i at ... $(HILITE conflicts with) -deneme.main.Şablon!().i at ... -) - -$(P -Bu gibi isim çakışmalarını gidermenin yolu şablon katmalarına koda eklendikleri noktada isim alanı atamak ve onların içerdikleri isimleri bu isim alanları ile kullanmaktır: -) - ---- - mixin Şablon $(HILITE A); // A.i'yi tanımlar - mixin Şablon $(HILITE B); // B.i'yi tanımlar - - $(HILITE A.)i = 42; // ← hangi i olduğu bellidir ---- - -$(P -Bu olanaklar dizgi katmalarında bulunmaz. Buna rağmen, bütün işi verilen bir dizgiyi şablon katması haline getiren bir şablondan yararlanarak bunun da üstesinden gelinebilir. -) - -$(P -Bunu görmek için önce yukarıdaki isim çakışması sorununu bu sefer de bir dizgi katması ile yaşayalım: -) - ---- -void main() { - mixin ("int i;"); - mixin ("int i;"); $(DERLEME_HATASI) - - i = 42; -} ---- - -$(P -Bu durumdaki derleme hatası $(C i)'nin zaten tanımlanmış olduğunu bildirir: -) - -$(SHELL -Error: declaration deneme.main.i is $(HILITE already defined) -) - -$(P -Bu sorunu gidermenin bir yolu dizgi katmasını şablon katmasına dönüştüren aşağıdaki gibi basit bir şablon kullanmaktır: -) - ---- -template ŞablonKatmasıOlarak(string dizgi) { - mixin (dizgi); -} - -void main() { - mixin ŞablonKatmasıOlarak!("int i;") A; // A.i'yi tanımlar - mixin ŞablonKatmasıOlarak!("int i;") B; // B.i'yi tanımlar - - A.i = 42; // ← hangi i olduğu bellidir -} ---- - -$(H5 $(IX işleç yükleme, mixin) İşleç yüklemedeki kullanımı) - -$(P -Bazı işleçlerin şablon söz dizimi ile tanımlandıklarını $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünde) görmüştük. O söz dizimlerini o bölümde bir kalıp olarak kabul etmenizi rica etmiş ve onların şablonlarla ilgili bölümlerden sonra açıklığa kavuşacaklarını söylemiştim. -) - -$(P -İşleç yüklemeyle ilgili olan üye işlevlerin şablonlar olarak tanımlanmalarının nedeni, işleçleri belirleyen şablon parametrelerinin $(C string) türünde olmaları ve bu yüzden dizgi katmalarından yararlanabilmeleridir. Bunun örneklerini hem o bölümde hem de o bölümün problem çözümlerinde görmüştük. -) - -$(H5 Örnek) - -$(P -($(I Not: Kıstasların bu örnekte olduğu gibi dizgi olarak belirtilmeleri isimsiz işlevlerin $(C =>) söz dizimlerinden daha eski bir olanaktır. Bu örnekteki dizgi kullanımı Phobos'ta hâlâ geçerli olsa da $(C =>) söz dizimi daha kullanışlıdır.)) -) - -$(P -Kendisine verilen sayılardan belirli bir koşula uyanlarını seçen ve bir dizi olarak döndüren bir işlev şablonuna bakalım: -) - ---- -int[] seç($(HILITE string koşul))(in int[] sayılar) { - int[] sonuç; - - foreach (eleman; sayılar) { - if ($(HILITE mixin (koşul))) { - sonuç ~= eleman; - } - } - - return sonuç; -} ---- - -$(P -O işlev şablonu seçme koşulunu şablon parametresi olarak almakta ve $(C if) deyiminin parantezinin içine o koşulu olduğu gibi kod olarak yerleştirmektedir. -) - -$(P -O ifadenin örneğin elemanların 7'den küçük olanlarını seçmesi için $(C if) deyimi içine şöyle bir ifadenin yazılması gerekir: -) - ---- - if (eleman < 7) { ---- - -$(P -Yukarıdaki $(C seç) şablonu bize o koşulu programda bir dizgi olarak bildirme olanağı vermiş olur: -) - ---- - int[] sayılar = [ 1, 8, 6, -2, 10 ]; - int[] seçilenler = seç!$(HILITE "eleman < 7")(sayılar); ---- - -$(P -Önemli bir ayrıntı olarak, $(C seç) şablonuna parametre olarak verilen dizginin içinde kullanılan değişken isminin $(C seç) işlevi içinde tanımlanan değişken ismi ile aynı olması şarttır ve o değişken isminin ne olduğu $(C seç) işlevinin belgelerinde belirtilmek zorundadır. O işlevi kullanan programcılar da o isme uymak zorundadırlar. -) - -$(P -Bu amaçla kullanılan değişken isimleri konusunda Phobos'ta bir standart gelişmeye başlamıştır. Benim seçtiğim "eleman" gibi uzun bir isim değil; a, b, n diye tek harflik isimler kullanılır. -) - -macros: - SUBTITLE=Katmalar - - DESCRIPTION=Şablonlar aracılığıyla üretilen kodların programın belirli noktalarına yerleştirilmesini sağlayan "şablon katmaları", ve derleme zamanında oluşturulan dizgiler kod olarak kullanma olanağı sağlayan "dizgi katmaları". - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial şablon dizgi katma mixin - -SOZLER= -$(katma) -$(sablon) diff --git a/ddili/src/ders/d/kesirli_sayilar.cozum.d b/ddili/src/ders/d/kesirli_sayilar.cozum.d deleted file mode 100644 index 70e25a1..0000000 --- a/ddili/src/ders/d/kesirli_sayilar.cozum.d +++ /dev/null @@ -1,109 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Kesirli Sayılar) - -$(OL - -$(LI -$(C float) yerine $(C double) kullanıldığında farklı biçimde şaşırtıcı olan bir sonuçla karşılaşılır: - ---- -// ... - - $(HILITE double) sonuç = 0; - -// ... - - if ($(HILITE sonuç == 1)) { - writeln("Beklendiği gibi 1"); - - } else { - writeln("FARKLI: ", sonuç); - } ---- - -$(P -$(C sonuç == 1) karşılaştırması doğru çıkmadığı halde sonuç 1 olarak yazdırılmaktadır: -) - -$(SHELL -FARKLI: 1 -) - -$(P -Bu şaşırtıcı durum, kesirli sayılar için normalde kullanılan çıktı düzeniyle ilgilidir. Virgülden sonraki kısım daha fazla haneyle yazdırıldığında değerin aslında tam 1 olmadığı görülür. (Çıktı düzenini $(LINK2 /ders/d/cikti_duzeni.html, ilerideki bir bölümde) göreceğiz:) -) - ---- - write$(HILITE f)ln("FARKLI: %.20f", sonuç); ---- - -$(SHELL -FARKLI: 1.00000000000000066613 -) - -) - -$(LI Önceki bölümdeki hesap makinesi programındaki üç satırdaki $(C int)'leri $(C double) yapmak yeter: - ---- - double birinci; - double ikinci; - - // ... - - double sonuç; ---- - -) - -$(LI -Problemde 5 yerine daha fazla sayı girilmesi istenseydi programın nasıl daha da içinden çıkılmaz bir hale geleceğini görüyor musunuz: - ---- -import std.stdio; - -void main() { - double sayı_1; - double sayı_2; - double sayı_3; - double sayı_4; - double sayı_5; - - write("Sayı 1: "); - readf(" %s", &sayı_1); - write("Sayı 2: "); - readf(" %s", &sayı_2); - write("Sayı 3: "); - readf(" %s", &sayı_3); - write("Sayı 4: "); - readf(" %s", &sayı_4); - write("Sayı 5: "); - readf(" %s", &sayı_5); - - writeln("İki katları:"); - writeln(sayı_1 * 2); - writeln(sayı_2 * 2); - writeln(sayı_3 * 2); - writeln(sayı_4 * 2); - writeln(sayı_5 * 2); - - writeln("Beşte birleri:"); - writeln(sayı_1 / 5); - writeln(sayı_2 / 5); - writeln(sayı_3 / 5); - writeln(sayı_4 / 5); - writeln(sayı_5 / 5); -} ---- - -) - -) - -Macros: - SUBTITLE=Kesirli Sayılar Problem Çözümü - - DESCRIPTION=Kesirli Sayılar Problem Çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial kesirli sayılar problem çözüm diff --git a/ddili/src/ders/d/kesirli_sayilar.d b/ddili/src/ders/d/kesirli_sayilar.d deleted file mode 100644 index 8bda448..0000000 --- a/ddili/src/ders/d/kesirli_sayilar.d +++ /dev/null @@ -1,514 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX kesirli sayı) $(IX kayan noktalı sayı) Kesirli Sayılar) - -$(P -Tamsayıların ve aritmetik işlemlerin oldukça kolay olduklarını, buna rağmen yapılarından kaynaklanan taşma ve kırpılma gibi özellikleri olduğunu gördük. -) - -$(P -Bu bölümde de biraz ayrıntıya girmek zorundayız. Eğer aşağıdaki listedeki herşeyi bildiğinizi düşünüyorsanız, ayrıntılı bilgileri okumayıp doğrudan problemlere geçebilirsiniz: -) - -$(UL - -$(LI Bin kere 0.001 eklemek 1 eklemekle aynı şey değildir) - -$(LI $(C ==) veya $(C !=) mantıksal ifadelerini kesirli sayı türleriyle kullanmak çoğu durumda hatalıdır) - -$(LI Kesirli sayıların ilk değerleri 0 değil, $(C .nan)'dır. $(C .nan) değeriyle işlem yapmak anlamlı değildir; başka bir değerle karşılaştırıldığında $(C .nan) ne küçüktür ne de büyük.) - -$(LI Artı yöndeki taşma değeri $(C .infinity), eksi yöndeki taşma değeri de $(I eksi) $(C .infinity)'dir) -) - -$(P -Kesirli sayı türleri çok daha kullanışlıdırlar ama onların da mutlaka bilinmesi gereken özellikleri vardır. Kırpılma konusunda çok iyidirler, çünkü zaten özellikle virgülden sonrası için tasarlanmışlardır. Belirli sayıda bitle sınırlı oldukları için taşma bu türlerde de vardır ancak alabildikleri değer aralığı tamsayılarla karşılaştırıldığında olağanüstü geniştir. Ek olarak, tamsayı türlerinin taşma durumunda sessiz kalmalarının aksine, kesirli sayılar "sonsuzluk" değerini alırlar. -) - -$(P -$(IX float) -$(IX double) -$(IX real) -Önce kesirli sayı türlerini hatırlayalım: -) - - - - - - - - - - - - - - - - - -
    Tür Bit Uzunluğu İlk Değeri
    float32float.nan
    double64double.nan
    realen az 64, veya donanım sağlıyorsa$(BR)daha fazla (örneğin, 80)real.nan
    - -$(H5 Kesirli tür nitelikleri) - -$(P -Kesirli türlerin nitelikleri tamsayılardan daha fazladır: -) - -$(UL - -$(LI $(C .stringof) türün okunaklı ismidir) - -$(LI $(C .sizeof) türün bayt olarak uzunluğudur; türün kaç bitten oluştuğunu hesaplamak için bu değeri bir bayttaki bit sayısı olan 8 ile çarpmak gerekir) - -$(LI $(IX .min, kesirli sayı) $(C .max) "en çok" anlamına gelen "maximum"un kısaltmasıdır; türün alabileceği en büyük değerdir. Kesirli türlerde $(C .min) bulunmaz; türün alabileceği en düşük değer için $(C .max)'ın eksi işaretlisi kullanılır. Örneğin, $(C double) türünün alabileceği en düşük değer $(C -double.max)'tır.) - -$(LI $(IX .min_normal) $(IX taşma, alttan) $(IX alttan taşma) $(C .min_normal) "türün ifade edebildiği sıfıra en yakın normalize değer" anlamındadır. Tür aslında bundan daha küçük değerler de ifade edebilir ama o değerlerin duyarlığı türün normal duyarlığının altındadır. Bir kesirli sayı değerinin $(C -.min_normal) ile $(C .min_normal) aralığında (0 hariç) olması durumuna $(I alttan taşma) denir.) - -$(LI $(IX .dig) $(C .dig) "basamak sayısı" anlamına gelen "digits"in kısaltmasıdır; türün kaç basamak duyarlığı olduğunu belirtir) - -$(LI $(IX .infinity) $(C .infinity) "sonsuz" anlamına gelir; taşma durumunda kullanılan değerdir.) -) - -$(P -Kesirli sayı türlerinin diğer nitelikleri daha az kullanılır. Bütün nitelikleri dlang.org'da $(LINK2 http://dlang.org/property.html, Properties for Floating Point Types) başlığı altında bulabilirsiniz. -) - -$(P -Yukarıdaki nitelikleri birbirleriyle olan ilişkilerini görmek için bir sayı çizgisine şöyle yerleştirebiliriz: -) - -$(MONO - + +─────────+─────────+ ... + ... +─────────+─────────+ + - │ -max -1 │ 0 │ 1 max │ - │ │ │ │ --infinity -min_normal min_normal infinity -) - -$(P -İki özel sonsuzluk değeri hariç, yukarıdaki çizginin ölçeği doğrudur: $(C min_normal) ile 1 arasında ne kadar değer ifade edilebiliyorsa, 1 ile $(C max) arasında da aynı sayıda değer ifade edilir. Bu da $(C min_normal) ile 1 arasındaki değerlerin son derece yüksek doğrulukta oldukları anlamına gelir. (Aynı durum eksi taraf için de geçerlidir.) -) - -$(H5 $(IX .nan) $(C .nan)) - -$(P -Açıkça ilk değeri verilmeyen kesirli sayıların ilk değerlerinin $(C .nan) olduğunu gördük. $(C .nan) değeri bazı anlamsız işlemler sonucunda da ortaya çıkabilir. Örneğin şu programdaki ifadelerin hepsi $(C .nan) sonucunu verir: -) - ---- -import std.stdio; - -void main() { - double sıfır = 0; - double sonsuz = double.infinity; - - writeln("nan kullanan her işlem: ", double.nan + 1); - writeln("sıfır bölü sıfır : ", sıfır / sıfır); - writeln("sıfır kere sonsuz : ", sıfır * sonsuz); - writeln("sonsuz bölü sonsuz : ", sonsuz / sonsuz); - writeln("sonsuz eksi sonsuz : ", sonsuz - sonsuz); -} ---- - -$(P -$(C .nan)'ın tek yararı bir değişkenin ilklenmemiş olduğunu göstermek değildir. İşlem sonuçlarında oluşan $(C .nan) değerleri sonraki hesaplar sırasında da korunurlar ve böylece hesap hatalarının erkenden ve kolayca yakalanmalarına yardım ederler. -) - -$(H5 Kesirli sayıların yazımları) - -$(P -Bu üç türün niteliklerine bakmadan önce kesirli sayıların nasıl yazıldıklarını görelim. Kesirli sayıları 123 gibi tamsayı şeklinde veya 12.3 gibi noktalı olarak yazabiliriz. -) - -$(P -Ek olarak, $(C 1.23e+4) gibi bir yazımdaki $(C e+), "çarpı 10 üzeri" anlamına gelir. Yani bu örnek 1.23x10$(SUP 4)'tür, bir başka deyişle "1.23 çarpı 10000"dir ve ifadenin değeri 12300'dür. -) - -$(P -Eğer $(C e)'den sonra gelen değer eksi ise, yani örneğin $(C 5.67e-3) gibi yazılmışsa, o zaman "10 üzeri o kadar değere bölünecek" demektir. Yani bu örnek 5.67/10$(SUP 3)'tür, bir başka deyişle "5.67 bölü 1000"dir ve ifadenin değeri 0.00567'dir. -) - -$(P -Kesirli sayıların bu gösterimlerini türlerin niteliklerini yazdıran aşağıdaki programın çıktısında göreceksiniz: -) - ---- -import std.stdio; - -void main() { - writeln("Tür ismi : ", float.stringof); - writeln("Duyarlık : ", float.dig); - writeln("En küçük normalize değeri: ", float.min_normal); - writeln("En küçük değeri : ", -float.max); - writeln("En büyük değeri : ", float.max); - writeln(); - - writeln("Tür ismi : ", double.stringof); - writeln("Duyarlık : ", double.dig); - writeln("En küçük normalize değeri: ", double.min_normal); - writeln("En küçük değeri : ", -double.max); - writeln("En büyük değeri : ", double.max); - writeln(); - - writeln("Tür ismi : ", real.stringof); - writeln("Duyarlık : ", real.dig); - writeln("En küçük normalize değeri: ", real.min_normal); - writeln("En küçük değeri : ", -real.max); - writeln("En büyük değeri : ", real.max); -} ---- - -$(P -Programın çıktısı benim ortamımda aşağıdaki gibi oluyor. $(C real) türü donanıma bağlı olduğu için bu çıktı sizin ortamınızda farklı olabilir: -) - -$(SHELL -Tür ismi : float -Duyarlık : 6 -En küçük normalize değeri: 1.17549e-38 -En küçük değeri : -3.40282e+38 -En büyük değeri : 3.40282e+38 - -Tür ismi : double -Duyarlık : 15 -En küçük normalize değeri: 2.22507e-308 -En küçük değeri : -1.79769e+308 -En büyük değeri : 1.79769e+308 - -Tür ismi : real -Duyarlık : 18 -En küçük normalize değeri: 3.3621e-4932 -En küçük değeri : -1.18973e+4932 -En büyük değeri : 1.18973e+4932 -) - -$(H6 Gözlemler) - -$(P -$(C ulong) türünün tutabileceği en yüksek değerin ne kadar çok basamağı olduğunu hatırlıyor musunuz: 18,446,744,073,709,551,616 sayısı 20 basamaktan oluşur. Buna karşın, en küçük kesirli sayı türü olan $(C float)'un bile tutabileceği en yüksek değer 10$(SUP 38) mertebesindedir. Yani şunun gibi bir değer: 340,282,000,000,000,000,000,000,000,000,000,000,000. $(C real)'in en büyük değeri ise 10$(SUP 4932) mertebesindedir (4900'den fazla basamağı olan bir sayı). -) - -$(P -Başka bir gözlem olarak $(C double)'ın 15 duyarlıkla ifade edebileceği en düşük değere bakalım: -) - -$(MONO - 0.000...$(I (burada 300 tane daha 0 var))...0000222507385850720 -) - -$(H5 Taşma gözardı edilmez) - -$(P -Ne kadar büyük değerler tutuyor olsalar da kesirli sayılarda da taşma olabilir. Kesirli sayı türlerinin iyi tarafı, taşma oluştuğunda tamsayılardaki taşmanın tersine bundan haberimizin olabilmesidir: taşan sayının değeri "artı sonsuz" için $(C .infinity), "eksi sonsuz" için $(C -.infinity) haline gelir. Bunu görmek için şu programda $(C .max)'ın değerini %10 arttırmaya çalışalım. Sayı zaten en büyük değerinde olduğu için, %10 arttırınca taşacak ve yarıya bölünse bile değeri "sonsuz" olacaktır: -) - ---- -import std.stdio; - -void main() { - real sayı = real.max; - - writeln("Önce: ", sayı); - - // 1.1 ile çarpmak, %110 haline getirmektir: - sayı *= 1.1; - writeln("%10 arttırınca: ", sayı); - - // İkiye bölerek küçültmeye çalışalım: - sayı /= 2; - writeln("Yarıya bölünce: ", sayı); -} ---- - -$(P -O programda $(C sayı) bir kere $(C real.infinity) değerini alınca yarıya bölünse bile sonsuz değerinde kalır: -) - -$(SHELL -Önce: 1.18973e+4932 -%10 arttırınca: inf -Yarıya bölünce: inf -) - -$(H5 $(IX duyarlık) $(IX hassasiyet) Duyarlık (Hassasiyet)) - -$(P -Duyarlık, yine günlük hayatta çok karşılaştığımız ama fazla sözünü etmediğimiz bir kavramdır. Duyarlık, bir değeri belirtirken kullandığımız basamak sayısıdır. Örneğin 100 liranın üçte birinin 33 lira olduğunu söylersek, duyarlık 2 basamaktır. Çünkü 33 değeri sadece iki basamaktan ibarettir. Daha hassas değerler gereken bir durumda 33.33 dersek, bu sefer dört basamak kullanmış olduğumuz için duyarlık 4 basamaktır. -) - -$(P -Kesirli sayı türlerinin bit olarak uzunlukları yalnızca alabilecekleri en yüksek değerleri değil; değerlerin duyarlıklarını da etkiler. Bit olarak uzunlukları ne kadar fazlaysa, duyarlıkları da o kadar fazladır. -) - -$(H5 Bölmede kırpılma yoktur) - -$(P -Önceki bölümde gördüğümüz gibi, tamsayı bölme işlemlerinde sonucun virgülden sonrası kaybedilir: -) - ---- - int birinci = 3; - int ikinci = 2; - writeln(birinci / ikinci); ---- - -$(P -Çıktısı: -) - -$(SHELL -1 -) - -$(P -Kesirli sayı türlerinde ise virgülden sonrasını kaybetmek anlamında kırpılma yoktur: -) - ---- - double birinci = 3; - double ikinci = 2; - writeln(birinci / ikinci); ---- - -$(P -Çıktısı: -) - -$(SHELL -1.5 -) - -$(P -Virgülden sonraki bölümün doğruluğu kullanılan türün duyarlığına bağlıdır: $(C real) en yüksek duyarlıklı, $(C float) da en düşük duyarlıklı kesirli sayı türleridir. -) - -$(H5 Hangi durumda hangi tür) - -$(P -Özel bir neden yoksa her zaman için $(C double) türünü kullanabilirsiniz. $(C float)'un duyarlığı düşüktür ama küçük olmasının yarar sağlayacağı nadir programlardan birisini yazıyorsanız düşünerek ve ölçerek karar verebilirsiniz. Öte yandan, $(C real)'in duyarlığı bazı ortamlarda $(C double)'dan daha yüksek olduğundan yüksek duyarlığın önemli olduğu hesaplarda $(C real) türünü kullanmak isteyebilirsiniz. -) - -$(H5 Her değeri ifade etmek olanaksızdır) - -$(P -Her değerin ifade edilememesi kavramını önce günlük hayatımızda göstermek istiyorum. Kullandığımız onlu sayı sisteminde virgülden önceki basamaklar birler, onlar, yüzler, vs. basamaklarıdır; virgülden sonrakiler de onda birler, yüzde birler, binde birler, vs. -) - -$(P -Eğer ifade etmek istediğimiz değer bu basamakların bir karışımı ise, değeri tam olarak ifade edebiliriz. Örneğin 0.23 değeri 2 adet $(I onda bir) değerinden ve 3 adet $(I yüzde bir) değerinden oluştuğu için tam olarak ifade edilebilir. Öte yandan, 1/3 değerini onlu sistemimizde tam olarak ifade edemeyiz çünkü virgülden sonra ne kadar uzatırsak uzatalım yeterli olmaz: 0.33333... -) - -$(P -Benzer durum kesirli sayılarda da vardır. Türlerin bit sayıları sınırlı olduğu için, her değer tam olarak ifade edilemez. -) - -$(P -Bilgisayarlarda kullanılan ikili sayı sistemlerinin bir farkı, virgülden öncesinin birler, ikiler, dörtler, vs. diye; virgülden sonrasının da yarımlar, dörtte birler, sekizde birler, vs. diye gitmesidir. Eğer değer bunların bir karışımı ise tam olarak ifade edilebilir; değilse edilemez. -) - -$(P -Bilgisayarlarda tam olarak ifade edilemeyen bir değer 0.1'dir (10 kuruş gibi). Onlu sistemde tam olarak 0.1 şeklinde ifade edilebilen bu değer, ikili sistemde 0.0001100110011... diye tekrarlar ve kullanılan kesirli sayı türünün duyarlığına bağlı olarak belirli bir yerden sonra hatalıdır. (Tekrarladığını söylediğim o son sayıyı ikili sistemde yazdım, onlu değil...) -) - -$(P -Bunu gösteren aşağıdaki örneği ilginç bulabilirsiniz. Bir değişkenin değerini bir döngü içinde her seferinde 0.001 arttıralım. Döngünün 1000 kere tekrarlanmasının ardından sonucun 1 olmasını bekleriz. Oysa öyle çıkmaz: -) - ---- -import std.stdio; - -void main() { - float sonuç = 0; - - // Bin kere 0.001 değerini ekliyoruz: - int sayaç = 1; - while (sayaç <= 1000) { - sonuç += 0.001; - ++sayaç; - } - - if (sonuç == 1) { - writeln("Beklendiği gibi 1"); - - } else { - writeln("FARKLI: ", sonuç); - } -} ---- - -$(P -0.001 tam olarak ifade edilemeyen bir değer olduğundan, bu değerdeki hata miktarı sonucu döngünün her tekrarında etkilemektedir: -) - -$(SHELL -FARKLI: 0.999991 -) - -$(P -$(I Not: Yukarıdaki $(C sayaç), döngü sayacı olarak kullanılmaktadır. Bu amaçla açıkça değişken tanımlamak aslında önerilmez. Onun yerine, daha ilerideki bir bölümde göreceğimiz $(LINK2 /ders/d/foreach_dongusu.html, $(C foreach) döngüsü) kullanılabilir.) -) - -$(H5 Kesirli sayı karşılaştırmaları) - -$(P -Tamsayılarda şu karşılaştırma işleçlerini kullanıyorduk: eşitlik ($(C ==)), eşit olmama ($(C !=)), küçüklük ($(C <)), büyüklük ($(C >)), küçük veya eşit olma ($(C <=)), büyük veya eşit olma ($(C >=)). Kesirli sayılarda başka karşılaştırma işleçleri de vardır. -) - -$(P -Kesirli sayılarda geçersiz değeri gösteren $(C .nan) da bulunduğu için, onun diğer değerlerle küçük büyük olarak karşılaştırılması anlamsızdır. Örneğin $(C .nan)'ın mı yoksa 1'in mi daha büyük olduğu gibi bir soru yanıtlanamaz. -) - -$(P -$(IX sırasızlık) Bu yüzden kesirli sayılarda başka bir karşılaştırma kavramı daha vardır: sırasızlık. Sırasızlık, değerlerden en az birisinin $(C .nan) olması demektir.) - -$(P -Aşağıdaki tablo kesirli sayı karşılaştırma işleçlerini gösteriyor. İşleçlerin hepsi ikilidir ve örneğin $(C soldaki == sağdaki) şeklinde kullanılır. $(C false) ve $(C true) içeren sütunlar, işleçlerin hangi durumda ne sonuç verdiğini gösterir. -) - -$(P -$(IX <>) -$(IX !<>) -Sonuncu sütun, ifadelerden birisinin $(C .nan) olması durumunda o işlecin kullanımının anlamlı olup olmadığını gösterir. Örneğin $(C 1.2 < real.nan) ifadesinin sonucu $(C false) çıksa bile, ifadelerden birisi $(C real.nan) olduğu için bu sonucun bir anlamı yoktur çünkü bunun tersi olan $(C real.nan < 1.2) ifadesi de $(C false) verir. -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $(BR)İşleç$(BR)AnlamıSoldaki$(BR)BüyükseSoldaki$(BR)Küçükseİkisi$(BR)EşitseEn Az Birisi$(BR).nan ise.nan ile$(BR)Anlamlı
    ==eşittir $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_YES evet)
    !=eşit değildir $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    >büyüktür $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    >=büyüktür veya eşittir $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    <küçüktür $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    <=küçüktür veya eşittir $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    !<>=küçük, büyük, eşit değildir $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    <>küçüktür veya büyüktür $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    <>=küçüktür, büyüktür, veya eşittir $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO hayır)
    !<=küçük değildir ve eşit değildir $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    !<küçük değildir $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    !>=büyük değildir ve eşit değildir $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    !>büyük değildir $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    !<>küçük değildir ve büyük değildir $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES evet)
    - -$(P -Her ne kadar $(C .nan) ile kullanımı anlamlı olsa da, değerlerden birisi $(C .nan) olduğunda $(C ==) işleci her zaman için $(C false) üretir. Her iki değer de $(C .nan) olduğunda bile sonuç $(C false) çıkar: -) - ---- -import std.stdio; - -void main() { - if (double.nan == double.nan) { - writeln("eşitler"); - - } else { - writeln("eşit değiller"); - } -} ---- - -$(P -$(C double.nan)'ın kendisine eşit olacağı beklenebilir, ancak karşılaştırmanın sonucu yine de $(C false)'tur: -) - -$(SHELL -eşit değiller -) - -$(H6 $(IX isNan, std.math) $(C .nan) eşitlik karşılaştırması için $(C isNaN())) - -$(P -Yukarıda gördüğümüz gibi, bir kesirli sayı değişkeninin $(C .nan)'a eşit olup olmadığı $(C ==) işleci ile karşılaştırılamaz: -) - ---- - if (kesirli == double.nan) { $(CODE_NOTE_WRONG YANLIŞ KARŞILAŞTIRMA) - // ... - } ---- - -$(P -O yüzden $(C std.math) modülündeki "nan değerinde mi?" sorusunun yanıtını veren $(C isNaN()) işlevinden yararlanmak gerekir: -) - ---- -import std.math; -// ... - if (isNaN(kesirli)) { $(CODE_NOTE doğru karşılaştırma) - // ... - } ---- - -$(P -Benzer biçimde, $(C .nan)'a eşit olmadığı da $(C !=) ile değil, $(C !isNaN()) ile denetlenmelidir. -) - -$(PROBLEM_COK - -$(PROBLEM -Yukarıda bin kere 0.001 ekleyen programı $(C float) yerine $(C double) (veya $(C real)) kullanacak biçimde değiştirin: - ---- - $(HILITE double) sonuç = 0; ---- - -$(P -Bu problem, kesirli sayı türlerinin eşitlik karşılaştırmalarında kullanılmasının ne kadar yanıltıcı olabildiğini göstermektedir. -) - -) - -$(PROBLEM -Önceki bölümdeki hesap makinesini kesirli bir tür kullanacak şekilde değiştirin. Böylece hesap makineniz çok daha doğru sonuçlar verecektir. Denerken değerleri girmek için 1000, 1.23, veya 1.23e4 şeklinde yazabilirsiniz. -) - -$(PROBLEM -Girişten 5 tane kesirli sayı alan bir program yazın. Bu sayıların önce iki katlarını yazsın, sonra da beşe bölümlerini. Bu problemi bir sonra anlatılacak olan dizilere hazırlık olarak soruyorum. Eğer bu programı şimdiye kadar öğrendiklerinizle yazarsanız, dizileri anlamanız daha kolay olacak. -) - -) - -Macros: - SUBTITLE=Kesirli Sayılar - - DESCRIPTION=D dilinde kesirli sayıların tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial kesirli sayılar kayan noktalı sayılar - -SOZLER= -$(duyarlik) -$(infinity) -$(kirpilma) -$(nitelik) -$(sirasizlik) -$(tasma) diff --git a/ddili/src/ders/d/kosullu_derleme.d b/ddili/src/ders/d/kosullu_derleme.d deleted file mode 100644 index d0d68c0..0000000 --- a/ddili/src/ders/d/kosullu_derleme.d +++ /dev/null @@ -1,741 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX koşullu derleme) Koşullu Derleme) - -$(P -Programın bazı bölümlerinin belirli koşullara bağlı olarak farklı derlenmesi veya hiç derlenmemesi istenebilir. D'nin koşullu derleme olanakları bu konuda kullanılır. -) - -$(P -Bu koşullar yalnızca derleme zamanında değerlendirilirler; programın çalışması sırasında etkileri yoktur. Çalışma zamanında etkili olan $(C if), $(C for), $(C while) gibi D olanakları koşullu derleme olanakları değildir. -) - -$(P -Aslında önceki bölümlerde koşullu derleme olarak kabul edilebilecek olanaklarla karşılaşmıştık: -) - -$(UL - -$(LI Birim testi bloklarındaki kodlar yalnızca $(C -unittest) derleyici seçeneği kullanıldığında derlenir ve çalıştırılır. -) - -$(LI Sözleşmeli programlama olanakları olan $(C in), $(C out), ve $(C invariant) blokları $(C -release) seçeneği kullanılmadığı zaman etkindir. -) - -) - -$(P -Yukarıdakiler programın doğruluğunu arttırma amacına yönelik yardımcı olanaklar olarak görülebilir. Derleyici seçeneklerine bağlı olarak kullanılıp kullanılmamaları, programın asıl davranışını zaten değiştirmemelidir. -) - -$(UL - -$(LI Şablon özellemeleri yalnızca belirtilen türler için etkilidir. O türler programda kullanılmadığında, özelleme kodu derlenmez ve programa dahil edilmez: - ---- -void değişTokuş(T)(ref T birinci, ref T ikinci) { - T geçici = birinci; - birinci = ikinci; - ikinci = geçici; -} - -unittest { - auto bir = 'ğ'; - auto iki = 'ş'; - değişTokuş(bir, iki); - - assert(bir == 'ş'); - assert(iki == 'ğ'); -} - -void değişTokuş(T $(HILITE : uint))(ref T birinci, ref T ikinci) { - birinci ^= ikinci; - ikinci ^= birinci; - birinci ^= ikinc; // DİKKAT: sondaki i harfi unutulmuş! -} - -void main() { -} ---- - -$(P -Yukarıdaki işlev şablonunun $(C uint) özellemesi, daha hızlı olacağı düşünüldüğü için $(C ^) bit işlecinden ($(I ya da) işleci) yararlanıyor. ($(I Not: Tam tersine, çoğu modern işlemcide bu yöntem geçici değişken kullanan yöntemden daha yavaştır.)) -) - -$(P -Sonundaki yazım hatasına rağmen yukarıdaki program derlenir ve çalışır. Bunun nedeni, $(C değişTokuş) işlevinin programda hiç $(C uint) türü için çağrılmamış olması ve bu yüzden $(C uint) özellemesinin hiç derlenmemiş olmasıdır. -) - -$(P -O programdaki hata, işlev $(C uint) türü ile çağrılana kadar ortaya çıkmaz. Bunu, birim testlerinin önemini gösteren bir başka örnek olarak da görebilirsiniz. Birim testi de yazılmış olsa, o özellemedeki hata işlev yazıldığı sırada ortaya çıkar: -) - ---- -unittest { - $(HILITE uint) i = 42; - $(HILITE uint) j = 7; - değişTokuş(i, j); - - assert(i == 7); - assert(j == 42); -} ---- - -$(P -Görüldüğü gibi, şablon özellemelerinin de koşullu olarak derlendiklerini kabul edebiliriz. -) - -) - -) - -$(P -D'nin koşullu derlemeyi destekleyen ve bütünüyle bu amaç için tasarlanmış olan başka olanakları da vardır: -) - -$(UL -$(LI $(C debug)) -$(LI $(C version)) -$(LI $(C static if)) -$(LI $(C is) ifadesi) -$(LI $(C __traits)) -) - -$(P -$(C is) ifadesini bir sonraki bölümde göreceğiz. -) - -$(H5 $(IX debug) debug) - -$(P -$(IX -debug, derleyici seçeneği) Program geliştirme aşamasında yararlı olan bir olanak $(C debug) belirtecidir. Bu belirteçle işaretlenmiş olan ifadeler ve deyimler yalnızca derleyiciye $(C -debug) seçeneği verildiğinde etkilidir: -) - ---- -debug $(I koşullu_derlenen_bir_ifade); - -debug { - // ... koşullu derlenen ifadeler ve deyimler ... - -} else { - // ... diğer durumda derlenen ifadeler ve deyimler ... -} ---- - -$(P -$(C else) bloğu isteğe bağlıdır. -) - -$(P -Yukarıdaki tek ifade de blok içindeki ifadeler de ancak $(C -debug) derleyici seçeneği etkin olduğunda derlenir. -) - -$(P -Şimdiye kadarki programların hemen hemen hepsinde programın nasıl işlediğini gösteren ve çıkışa "ekliyorum", "çıkartıyorum" gibi mesajlar yazdıran satırlar kullandık. Algoritmaların işleyişlerini böylece görsel hale getiriyor ve olası hatalarını bulabiliyorduk. "debug", $(I hata gidermek) anlamına gelir ve bu konuda yararlıdır. -) - -$(P -Bunun bir örneği olarak $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) gördüğümüz $(C ikiliAra) işlevine bakalım. O algoritmanın açıklama satırlarını çıkartıyorum ve bilerek hatalı olarak yazıyorum: -) - ---- -import std.stdio; - -// DİKKAT! Bu algoritma hatalıdır -size_t ikiliAra(const int[] değerler, in int değer) { - if (değerler.length == 0) { - return size_t.max; - } - - immutable ortaNokta = değerler.length / 2; - - if (değer == değerler[ortaNokta]) { - return ortaNokta; - - } else if (değer < değerler[ortaNokta]) { - return ikiliAra(değerler[0 .. ortaNokta], değer); - - } else { - return ikiliAra(değerler[ortaNokta + 1 .. $], değer); - } -} - -void main() { - auto sayılar = [ -100, 0, 1, 2, 7, 10, 42, 365, 1000 ]; - - auto indeks = ikiliAra(sayılar, 42); - writeln("Konum: ", indeks); -} ---- - -$(P -Yukarıdaki program 42'nin aslında 6 olan konumunu yanlış bildirir: -) - -$(SHELL_SMALL -Konum: 1 $(SHELL_NOTE_WRONG yanlış sonuç) -) - -$(P -Bu hatayı bulmanın bir yolu, işlevin önemli noktalarına işlemler hakkında bilgiler veren satırlar eklemektir: -) - ---- -size_t ikiliAra(const int[] değerler, in int değer) { - $(HILITE writeln)(değerler, " içinde ", değer, " arıyorum"); - - if (değerler.length == 0) { - $(HILITE writeln)(değer, " bulunamadı"); - return size_t.max; - } - - immutable ortaNokta = değerler.length / 2; - - $(HILITE writeln)("bakılan konum: ", ortaNokta); - - if (değer == değerler[ortaNokta]) { - $(HILITE writeln)(değer, ", ", ortaNokta, " konumunda bulundu"); - return ortaNokta; - - } else if (değer < değerler[ortaNokta]) { - $(HILITE writeln)("ilk yarıda olması gerek"); - return ikiliAra(değerler[0 .. ortaNokta], değer); - - } else { - $(HILITE writeln)("son yarıda olması gerek"); - return ikiliAra(değerler[ortaNokta + 1 .. $], değer); - } -} ---- - -$(P -Programın şimdiki çıktısı algoritmanın işleyiş adımlarını da gösterir: -) - -$(SHELL_SMALL -[-100,0,1,2,7,10,42,365,1000] içinde 42 arıyorum -bakılan konum: 4 -son yarıda olması gerek -[10,42,365,1000] içinde 42 arıyorum -bakılan konum: 2 -ilk yarıda olması gerek -[10,42] içinde 42 arıyorum -bakılan konum: 1 -42, 1 konumunda bulundu -Konum: 1 -) - -$(P -Hatanın bu çıktıdan yararlanılarak bulunduğunu ve giderildiğini varsayalım. Hata giderildikten sonra artık $(C writefln) satırlarına gerek kalmaz, üstelik silinmeleri gerekir. Buna rağmen, o satırları silmek de bir israf olarak görülebilir çünkü belki de ileride tekrar gerekebilirler. -) - -$(P -Onun yerine, bu satırların başına $(C debug) anahtar sözcüğü yazılabilir: -) - ---- - $(HILITE debug) writeln(değer, " bulunamadı"); ---- - -$(P -O satırlar artık yalnızca $(C -debug) derleyici seçeneği kullanıldığında etkin olacaktır: -) - -$(SHELL_SMALL -dmd deneme.d -ofdeneme -w $(HILITE -debug) -) - -$(P -Böylece programın normal işleyişi sırasında çıktıya hiçbir bilgi yazdırılmayacak, bir hata görüldüğünde ise $(C -debug) kullanılarak algoritmanın işleyişi hakkında bilgi alınabilecektir. -) - -$(H6 $(C debug($(I isim)))) - -$(P -$(C debug) belirtecinin çok yerde kullanılması durumunda programın çıktısı çok kalabalıklaşabilir. Böyle durumlarda $(C debug) belirteçlerine isimler verebilir ve onların yalnızca komut satırında belirtilenlerinin etkinleşmelerini sağlayabiliriz: -) - ---- - $(HILITE debug(ikili_arama)) writeln(değer, " bulunamadı"); ---- - -$(P -İsimli $(C debug) belirteçlerini etkinleştirmek için komut satırında $(C -debug=$(I isim)) yazılır: -) - -$(SHELL_SMALL -dmd deneme.d -ofdeneme -w $(HILITE -debug=ikili_arama) -) - -$(P -İsimli $(C debug) belirteçleri de birden fazla ifade için kullanılabilir: -) - ---- - debug(ikili_arama) { - // ... koşullu derlenen ifadeler ve deyimler ... - } ---- - -$(P -Aynı anda birden çok isimli $(C debug) belirteci de belirtilebilir: -) - -$(SHELL_SMALL -$ dmd deneme.d -ofdeneme -w $(HILITE -debug=ikili_arama) $(HILITE -debug=yigin_yapisi) -) - -$(P -O durumda hem $(C ikili_arama), hem de $(C yigin_yapisi) isimli $(C debug) blokları etkin olur. -) - -$(H6 $(C debug($(I düzey)))) - -$(P -Bazen $(C debug) belirteçlerine isimler vermek yerine, hata ayıklama düzeylerini belirleyen sayısal değerler verilebilir. Örneğin, artan her düzey daha derinlemesine bilgi elde etmek için yararlı olabilir: -) - ---- -$(HILITE debug) import std.stdio; - -void birİşlev(string dosyaİsmi, int[] sayılar) { - $(HILITE debug(1)) writeln("birİşlev işlevine girildi"); - - $(HILITE debug(2)) { - writeln("işlev parametreleri: "); - writeln(" isim: ", dosyaİsmi); - - foreach (i, sayı; sayılar) { - writefln(" %4s: %s", i, sayı); - } - } - - // ... asıl işlemler ... -} ---- - -$(P -Derleyiciye bildirilen $(C debug) düzeyi, o düzey ve daha düşük olanlarının etkinleşmesini sağlar: -) - -$(SHELL_SMALL -$ dmd deneme.d -ofdeneme -w $(HILITE -debug=1) -$ ./deneme -$(DARK_GRAY birİşlev işlevine girildi) -) - -$(P -Daha derinlemesine bilgi almak istendiğinde: -) - -$(SHELL_SMALL -$ dmd deneme.d -ofdeneme -w $(HILITE -debug=2) -$ ./deneme -$(DARK_GRAY birİşlev işlevine girildi -işlev parametreleri: - isim: deneme.txt - 0: 10 - 1: 4 - 2: 100) -) - -$(H5 $(IX version) $(C version($(I isim))), ve $(C version($(I düzey)))) - -$(P -$(C version), $(C debug) olanağına çok benzer ve kod içinde aynı biçimde kullanılır: -) - ---- - version(denemeSürümü) /* ... bir ifade ... */; - - version(okulSürümü) { - // ... okullara satılan sürümle ilgili ifadeler ... - - } else { - // ... başka sürümlerle ilgili ifadeler ... - } - - version(1) birDeğişken = 5; - - version(2) { - // ... sürüm 2 ile ilgili bir olanak ... - } ---- - -$(P -Bütünüyle aynı biçimde çalışıyor olsa da, $(C debug)'dan farkı, programın farklı sürümlerini oluşturma amacıyla kullanılmasıdır. -) - -$(P -$(IX -version, derleyici seçeneği) Yine $(C debug)'da olduğu gibi, aynı anda birden fazla $(C version) bloğu etkinleştirilebilir: -) - -$(SHELL_SMALL -$ dmd deneme.d -ofdeneme -w $(HILITE -version=kayit) $(HILITE -version=hassas_hesap) -) - -$(P -Bazı $(C version) isimleri hazır olarak tanımlıdır. Tam listesini $(LINK2 http://dlang.org/version.html, Conditional Compilation sayfasında) bulacağınız bu isimleri aşağıdaki tabloda özetliyorum: -) - - - - - - - - - - - - - -
    Öntanımlı $(C version) belirteçleri
    Derleyici $(B DigitalMars GNU LDC SDC)
    İşletim sistemi $(B Windows Win32 Win64 linux OSX Posix FreeBSD -OpenBSD -NetBSD -DragonFlyBSD -BSD -Solaris -AIX -Haiku -SkyOS -SysV3 -SysV4 -Hurd)
    Mikro işlemci sonculluğu$(B LittleEndian BigEndian)
    Derleyici seçenekleri $(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 -)
    Mikro işlemci mimarisi $(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 -İki tane de özel $(C version) ismi vardır: -) - -$(UL -$(LI $(IX none, version) $(C none): Bu isim hiçbir zaman tanımlı değildir; kod bloklarını etkisizleştirmek için kullanılabilir.) -$(LI $(IX all, version) $(C all): Bu isim her zaman tanımlıdır; $(C none)'ın tersi olarak kullanılır.) -) - -$(P -O tanımlardan yararlanarak programınızın farklı olanaklarla derlenmesini sağlayabilirsiniz. Kullanım örneği olarak $(C std.ascii) modülünde tanımlı olan $(C newline)'a bakalım. $(I Satır sonu) anlamına gelen kodları belirleyen $(C newline) dizisi, üzerinde derlenmekte olduğu işletim sistemine göre farklı kodlardan oluşmaktadır: -) - ---- -version(Windows) { - immutable newline = "\r\n"; - -} else version(Posix) { - immutable newline = "\n"; - -} else { - static assert(0, "Unsupported OS"); -} ---- - -$(H5 $(C debug)'a ve $(C version)'a isim atamak) - -$(P -$(C debug) ve $(C version)'a sanki değişkenmişler gibi isim atanabilir. Değişkenlerden farklı olarak, atama işlemi değer değiştirmez, değer olarak belirtilen $(C debug) veya $(C version) ismini $(I de) etkinleştirir. -) - ---- -import std.stdio; - -debug(hepsi) { - debug $(HILITE =) ikili_arama; - debug $(HILITE =) yigin_yapisi; - version $(HILITE =) denemeSürümü; - version $(HILITE =) okulSürümü; -} - -void main() { - debug(ikili_arama) writeln("ikili_arama etkin"); - debug(yigin_yapisi) writeln("yigin_yapisi etkin"); - - version(denemeSürümü) writeln("deneme sürümü"); - version(okulSürümü) writeln("okul sürümü"); -} ---- - -$(P -Yukarıdaki koddaki $(C debug(hepsi)) bloğu içindeki atamalar o isimlerin de etkinleşmelerini sağlar. Böylece bu program için derleme satırında dört $(C debug) ve $(C version) seçeneği ayrı ayrı seçilebileceği gibi, $(C -debug=hepsi) kullanıldığında; $(C ikili_arama), $(C yigin_yapisi), $(C denemeSürümü), ve $(C okulSürümü) sanki komut satırında bildirilmişler gibi etkinleşirler: -) - -$(SHELL_SMALL -$ dmd deneme.d -ofdeneme -w -debug=hepsi -$ ./deneme -$(DARK_GRAY ikili_arama etkin -yigin_yapisi etkin -deneme sürümü -okul sürümü) -) - -$(H5 $(IX static if) $(C static if)) - -$(P -Programın çalışması sırasındaki kararlarda çok kullandığımız $(C if) koşulunun derleme zamanındaki eşdeğeri $(C static if)'tir. -) - -$(P -$(C if) koşulunda olduğu gibi, $(C static if) koşulu da bir mantıksal ifade ile kullanılır. $(C static if) bloğundaki kodlar bu mantıksal ifade $(C true) olduğunda derlenir ve programa dahil edilir, $(C false) olduğunda ise o kodlar sanki hiç yazılmamışlar gibi etkisizleşirler. Yine $(C if)'e benzer şekilde, $(C else static if) ve $(C else) blokları da bulunabilir. -) - -$(P -Derleme zamanında işletildiğinden, mantıksal ifadenin sonucunun derleme zamanında biliniyor olması şarttır. -) - -$(P -$(C static if) her kapsamda kullanılabilir: Modül dosyasında en üst düzeyde veya yapı, sınıf, şablon, işlev, vs. kapsamlarında. Koşul sağlandığında blok içindeki kodlar yazıldıkları satırlarda programa dahil edilirler. -) - -$(P -$(C static if) şablon tanımlarında, $(C is) ifadesi ile birlikte, ve $(C __traits) olanağı ile çok kullanılır. -) - -$(P -$(C static if)'in $(C is) ifadesi ile birlikte kullanım örneklerini bir sonraki bölümde göreceğiz. Burada çok basit bir şablon tanımında kullanalım: -) - ---- -import std.stdio; - -struct VeriYapısı(T) { - static if (is (T == $(HILITE float))) { - alias SonuçTürü = $(HILITE double); - - } else static if (is (T == $(HILITE double))) { - alias SonuçTürü = $(HILITE real); - - } else { - static assert(false, T.stringof ~ " desteklenmiyor"); - } - - SonuçTürü işlem() { - writefln("%s için sonuç türü olarak %s kullanıyorum.", - T.stringof, SonuçTürü.stringof); - SonuçTürü sonuç; - // ... - return sonuç; - } -} - -void main() { - auto f = VeriYapısı!float(); - f.işlem(); - - auto d = VeriYapısı!double(); - d.işlem(); -} ---- - -$(P -$(C VeriYapısı) yalnızca $(C float) ve $(C double) türleriyle kullanılabilen bir tür. İşlem sonucunu hep bir adım daha hassas olan türde gerçekleştirmek için $(C float) ile kullanıldığında $(C double), $(C double) ile kullanıldığında ise $(C real) seçiyor: -) - -$(SHELL -float için sonuç türü olarak double kullanıyorum. -double için sonuç türü olarak real kullanıyorum. -) - -$(P -$(C static if) zincirleri oluştururken $(C else static if) yazmak gerektiğine dikkat edin. Yanlışlıkla $(C else if) yazıldığında, $(C static if)'in $(C else) bloğu olarak $(C if) kullanılacak demektir ve $(C if) de doğal olarak çalışma zamanında işletilecektir. -) - -$(H5 $(IX static assert) $(C static assert)) - -$(P -Aslında bir koşullu derleme olanağı olarak kabul edilmese de bu olanağı $(C static if)'e benzerliği nedeniyle burada tanıtmaya karar verdim. -) - -$(P -Çalışma zamanında kullanmaya alıştığımız $(C assert)'le aynı biçimde ama derleme zamanında işletilir. Mantıksal ifadesi $(C false) olduğunda derlemenin bir hata ile sonlandırılmasını sağlar. -) - -$(P -$(C static if) gibi $(C static assert) de programda herhangi bir kapsamda bulunabilir. -) - -$(P -$(C static assert) kullanımının bir örneğini yukarıdaki programda gördük: $(C float) veya $(C double) türlerinden başka bir tür belirtildiğinde derleme $(C static assert(false)) nedeniyle sonlanır: -) - ---- - auto i = VeriYapısı!$(HILITE int)(); ---- - -$(P -Derleme hatası: -) - -$(SHELL -Error: static assert "int desteklenmiyor" -) - -$(P -Başka bir örnek olarak belirli bir algoritmanın yalnızca belirli büyüklükteki türlerle doğru olarak çalışabildiğini varsayalım. Bu koşulu bir $(C static assert) ile denetleyebiliriz: -) - ---- -T birAlgoritma(T)(T değer) { - // Bu algoritma ancak büyüklüğü dördün katı olan türlerle - // çalışabilir - static assert((T.sizeof % 4) == 0); - - // ... -} ---- - -$(P -O işlev şablonu örneğin $(C char) ile çağrıldığında programın derlenmesi bir hata ile sonlanır: -) - -$(SHELL_SMALL -Error: static assert (1LU == 0LU) is false -) - -$(P -Böylece algoritmanın uygunsuz bir türle kullanılmasının ve olasılıkla hatalı çalışmasının önüne geçilmektedir. -) - -$(P -$(C static assert) de $(C is) ifadesi dahil olmak üzere derleme zamanında oluşturulabilen her mantıksal ifade ile kullanılabilir. -) - -$(H5 $(IX tür niteliği) $(IX nitelik, tür) Tür nitelikleri) - -$(P -$(IX __traits) $(IX std.traits) $(C __traits) anahtar sözcüğü ve $(C std.traits) modülü türlerin nitelikleriyle ilgili bilgileri derleme zamanında edinmeye yarar. -) - -$(P -$(C __traits), derleyicinin koddan edinmiş olduğu bilgileri sorgulamaya yarar. Söz dizimi aşağıdaki gibidir: -) - ---- - __traits($(I sözcük), $(I parametreler)) ---- - -$(P -$(I sözcük), $(C __traits)'in hangi amaçla kullanıldığını belirtir. $(I parametreler) ise bir veya daha fazla sayıda olmak üzere tür ismi veya ifadedir. Parametrelerin anlamları kullanılan sözcüğe bağlıdır. -) - -$(P -$(C __traits)'in sunduğu bilgiler dilin başka olanakları tarafından edinilemeyen ve çoğunlukla derleyicinin toplamış olduğu bilgilerdir. Bu bilgiler özellikle şablon kodlarında ve koşullu derleme sırasında yararlıdır. -) - -$(P -Örneğin, "aritmetik mi" anlamına gelen $(C isArithmetic), $(C T) gibi bir şablon parametresinin aritmetik bir tür olup olmamasına göre farklı kod üretmek için kullanılabilir: -) - ---- - static if (__traits($(HILITE isArithmetic), T)) { - // ... aritmetik bir türmüş ... - - } else { - // ... değilmiş ... - } ---- - -$(P -$(C std.traits) modülü de tür nitelikleriyle ilgili bilgileri şablon olanakları olarak yine derleme zamanında sunar. Örneğin, $(C std.traits.isSomeChar), kendisine verilen şablon parametresi bir karakter türü olduğunda $(C true) üretir: -) - ---- -import std.traits; - -// ... - - static if ($(HILITE isSomeChar)!T) { - // ... herhangi bir karakter türüymüş ... - - } else { - // ... bir karakter türü değilmiş ... - } ---- - -$(P -Daha fazla bilgi için $(LINK2 http://dlang.org/traits.html, $(C __traits) belgesine) ve $(LINK2 http://dlang.org/phobos/std_traits.html, $(C std.traits) belgesine) başvurabilirsiniz. -) - -$(H5 Özet) - -$(UL - -$(LI -$(C debug) olarak tanımlanmış olan kodlar yalnızca $(C -debug) derleyici seçeneği etkin olduğunda programa dahil edilirler. -) - -$(LI -$(C version) ile tanımlanmış olan kodlar programın $(C -version) derleme seçeneği ile belirlenen sürümüne dahil olurlar. -) - -$(LI -$(C static if) derleme zamanında işleyen $(C if) deyimi gibidir; kodların derleme zamanındaki koşullara göre programa dahil edilmesini sağlar. -) - -$(LI -$(C static assert) programla ilgili varsayımları derleme zamanında denetler. -) - -$(LI $(C __traits) ve $(C std.traits) türler hakkında derleme sırasında bilgi edinmeye yarar.) - -) - -Macros: - SUBTITLE=Koşullu Derleme - - DESCRIPTION=Kaynak kodun hangi bölümlerinin programa dahil edileceğinin derleme sırasında belirlenmesi - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial koşullu derleme debug version static if static assert - -SOZLER= -$(bayt_sirasi) -$(cokuzlu) -$(gosterge) -$(hata_ayiklama) -$(indeks) -$(statik) -$(surum) diff --git a/ddili/src/ders/d/kosut_islemler.d b/ddili/src/ders/d/kosut_islemler.d deleted file mode 100644 index a0b16fc..0000000 --- a/ddili/src/ders/d/kosut_islemler.d +++ /dev/null @@ -1,1235 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX koşut işlem) $(IX std.parallelism) Koşut İşlemler) - -$(P -$(IX çekirdek) Günümüzdeki mikro işlemciler her birisi bağımsız işlem birimi olarak kullanılabilen birden fazla $(I çekirdekten) oluşurlar. Çekirdekler farklı programların farklı bölümlerini aynı anda işletebilirler. $(C std.parallelism) modülü bu çekirdeklerin aynı anda işletilmelerini ve programın bu sayede daha hızlı çalışmasını sağlayan olanaklar içerir. -) - -$(P -Bu bölümde aşağıdaki olanakların ayrıntılarını göreceğiz. Bu olanakları yalnızca işlemler birbirlerinden bağımsız olduklarında kullanabilirsiniz: -) - -$(UL - -$(LI $(C parallel): Bir aralığın elemanlarına koşut olarak erişir.) - -$(LI $(C task): Başka işlemlerle aynı anda işletilecek olan görevler oluşturur.) - -$(LI $(C asyncBuf): $(C InputRange) aralığındaki elemanları yarı hevesli olarak aynı anda ilerletir.) - -$(LI $(C map): İşlevleri $(C InputRange) aralığındaki elemanlara yarı hevesli olarak aynı anda uygular.) - -$(LI $(C amap): İşlevleri $(C RandomAccessRange) aralığındaki elemanlara tam hevesli olarak aynı anda uygular.) - -$(LI $(C reduce): $(C RandomAccessRange) aralığındaki elemanların hesaplarını aynı anda işletir.) - -) - -$(P -Daha önce kullandığımız bütün örneklerdeki bütün kodlarda işlemlerin yazıldıkları sırada işletildiklerini varsaydık: -) - ---- - ++i; - ++j; ---- - -$(P -Yukarıdaki kodda önce $(C i)'nin değerinin, ondan sonra da $(C j)'nin değerinin arttırılacağını biliyoruz. Aslında bu her zaman doğru değildir: Derleyicinin kodun daha hızlı işlemesi için uyguladığı eniyileştirmeler sonucunda her iki değişken de mikro işlemcinin yazmaçlarında depolanmış olabilirler. Bu yazmaçlar da birbirlerinden bağımsız olduklarından, mikro işlemci o iki işlemi $(I aynı anda) işletebilir. -) - -$(P -Bu tür eniyileştirmeler yararlıdırlar ama çok alt düzeydeki işlemlerden daha üst düzey kapsamlarda uygulanamazlar. Bir grup üst düzey işlemin birbirlerinden bağımsız olduklarına ve bu yüzden de aynı anda işletilebileceklerine çoğu durumda yalnızca programcı karar verebilir. -) - -$(P -Aşağıdaki $(C foreach) döngüsündeki elemanların başından sonuna kadar ve teker teker işletileceklerini biliyoruz: -) - ---- - auto öğrenciler = - [ Öğrenci(1), Öğrenci(2), Öğrenci(3), Öğrenci(4) ]; - - foreach (öğrenci; öğrenciler) { - öğrenci.uzunBirİşlem(); - } ---- - -$(P -Yukarıdaki kod, işletim sisteminin o programı çalıştırmak için seçmiş olduğu tek çekirdek üzerinde işletilir. $(C foreach) döngüsü de öğrencileri başından sonuna kadar işlettiği için $(C uzunBirİşlem()) öğrencilere sırayla ve teker teker uygulanır. Oysa çoğu durumda bir öğrencinin işletilebilmesi için önceki öğrencilerin işlemlerinin tamamlanmış olmaları gerekmez. $(C Öğrenci) işlemlerinin birbirlerinden bağımsız oldukları durumlarda diğer çekirdeklerden yararlanılmıyor olması zaman kaybına yol açacaktır. -) - -$(P -$(IX Thread.sleep) Aşağıdaki örneklerdeki işlemlerin hissedilir derecede uzun süren işlemlere benzemeleri için $(C core.thread) modülündeki $(C Thread.sleep)'ten yararlanacağım. $(C Thread.sleep) işlemleri belirtilen süre kadar durdurur. Ne kadar bekleneceğini bildirmenin bir yolu, "süre" anlamına gelen "duration"ın kısaltması olan $(C dur)'u kullanmaktır. $(C dur)'un şablon parametresi zaman birimini belirler: milisaniye için $(STRING "msecs"), saniye için $(STRING "seconds"). $(C Thread.sleep) işlemciyi hiç meşgul etmeden zaman geçirdiği için buradaki örneklerde fazla yapay kalıyor; buna rağmen, koşut işlemlerin amaçlarını göstermede yeterince etkilidir. -) - ---- -import std.stdio; -import core.thread; - -struct Öğrenci { - int numara; - - void uzunBirİşlem() { - writeln(numara, - " numaralı öğrencinin işlemi başladı"); - - /* Gerçekte yavaş olduklarını varsaydığımız işlemlerin - * yavaşlıklarına benzesin diye 1 saniye bekliyoruz */ - Thread.sleep(1.seconds); - - writeln(numara, " numaralı öğrencinin işlemi bitti"); - } -} - -void main() { - auto öğrenciler = - [ Öğrenci(1), Öğrenci(2), Öğrenci(3), Öğrenci(4) ]; - - foreach (öğrenci; öğrenciler) { - öğrenci.uzunBirİşlem(); - } -} ---- - -$(P -Yukarıdaki programın çalışma süresi uç birimde $(C time) komutu ile ölçülebilir: -) - -$(SHELL -$ $(HILITE time) ./deneme -$(DARK_GRAY 1 numaralı öğrencinin işlemi başladı -1 numaralı öğrencinin işlemi bitti -2 numaralı öğrencinin işlemi başladı -2 numaralı öğrencinin işlemi bitti -3 numaralı öğrencinin işlemi başladı -3 numaralı öğrencinin işlemi bitti -4 numaralı öğrencinin işlemi başladı -4 numaralı öğrencinin işlemi bitti - -real 0m4.003s $(SHELL_NOTE toplam 4 saniye) -user 0m0.000s -sys 0m0.000s) -) - -$(P -Öğrenci işlemleri sırayla işletildiklerinden ve her işlem 1 saniye tuttuğundan toplam süre beklendiği gibi yaklaşık olarak 4 saniye olmaktadır. Oysa 4 öğrencinin işlemleri örneğin 4 çekirdeğin bulunduğu bir ortamda aynı anda ve tek seferde işletilebilseler bütün işlem 1 saniye tutabilir. -) - -$(P -$(IX totalCPUs) Bunun nasıl gerçekleştirildiğine geçmeden önce, programın çalıştırıldığı ortamda kaç çekirdek bulunduğunun $(C std.parallelism.totalCPUs)'un değeri ile belirlenebildiğini göstermek istiyorum: -) - ---- -import std.stdio; -import std.parallelism; - -void main() { - writefln("Bu ortamda toplam %s çekirdek var.", totalCPUs); -} ---- - -$(P -Bu bölümü yazdığım ortamda şu çıktıyı alıyorum: -) - -$(SHELL -Bu ortamda toplam 4 çekirdek var. -) - -$(H5 $(IX parallel) $(C taskPool.parallel())) - -$(P -Bu işlev kısaca $(C parallel()) diye de çağrılabilir. -) - -$(P -$(IX foreach, koşut işlem) $(C parallel()), bir aralığın elemanlarına bütün çekirdeklerden yararlanarak koşut olarak erişmeye yarar. Yukarıdaki programa $(C std.parallelism) modülünü eklemek ve $(C öğrenciler) yerine $(C parallel(öğrenciler)) yazmak bütün çekirdeklerden yararlanmak için yeterlidir: -) - ---- -import std.parallelism; - -// ... - - foreach (öğrenci; $(HILITE parallel)(öğrenciler)) { ---- - -$(P -$(LINK2 /ders/d/foreach_opapply.html, Yapı ve Sınıflarda $(C foreach) bölümünde) gördüğümüz gibi, $(C foreach) döngüsünün kapsamı nesnelerin $(C opApply) işlevlerine bir $(C delegate) olarak gönderilir. $(C parallel())'in döndürdüğü geçici nesne bu $(C delegate)'i her eleman için farklı bir çekirdek üzerinde işleten bir aralık nesnesidir. -) - -$(P -Asıl topluluğu $(C parallel()) işlevine göndererek kullanmak, programın 4 çekirdek bulunan bu ortamda 1 saniyede tamamlanması için yeterli olur: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY 2 numaralı öğrencinin işlemi başladı -1 numaralı öğrencinin işlemi başladı -3 numaralı öğrencinin işlemi başladı -4 numaralı öğrencinin işlemi başladı -2 numaralı öğrencinin işlemi bitti -3 numaralı öğrencinin işlemi bitti -1 numaralı öğrencinin işlemi bitti -4 numaralı öğrencinin işlemi bitti - -real 0m1.004s $(SHELL_NOTE şimdi 1 saniye) -user 0m0.000s -sys 0m0.000s) -) - -$(P -$(I Not: Programın çalışma süresi sizin ortamınızda farklı olabilir; kabaca "4 saniye bölü çekirdek sayısı" hesabının sonucu kadar sürede tamamlanacağını bekleyebiliriz.) -) - -$(P -$(IX iş parçacığı) Programların işletilmeleri sırasında mikro işlemcinin kodların üzerinden belirli geçişlerine $(I iş parçacığı) denir. Programlar aynı anda etkin olarak işletilen birden fazla iş parçacığından oluşuyor olabilirler. İşletim sistemi her iş parçacığını bir çekirdek üzerinde başlatır, işletir, ve diğer iş parçacıkları da işletilebilsinler diye duraklatır. Her iş parçacığının işletilmesi bir çok kere başlatılması ve duraklatılması ile devam eder. -) - -$(P -Mikro işlemcinin bütün çekirdekleri işletim sistemindeki bütün iş parçacıkları tarafından paylaşılır. Bu iş parçacıklarının hangi sırayla başlatıldıklarına ve hangi koşullarda duraksatıldıklarına işletim sistemi karar verir. Bu yüzden $(C uzunBirİşlem()) içinde yazdırdığımız mesajların sıralarının karışık olarak çıktıklarını görüyoruz. Döngü içindeki işlemler her öğrenci için bağımsız oldukları sürece hangisinin daha önce sonlandığının programın işleyişi açısından bir önemi yoktur. -) - -$(P -$(C parallel()) yardımıyla aynı anda işletilen işlemlerin gerçekten birbirlerinden bağımsız oldukları programcının sorumluluğundadır. Örneğin, yukarıdaki mesajların çıkışta belirli bir sırada görünmeleri gerekseydi bunu sağlamak elimizde olmadığından $(C parallel())'in kullanılması bir hata olarak kabul edilirdi. İş parçacıklarının birbirlerine bağımlı oldukları durumlarda $(I eş zamanlı programlamadan) yararlanılır. Onu bir sonraki bölümde göreceğiz. -) - -$(P -$(C foreach) tamamlandığında bütün işlemler de tamamlanmıştır. Program işleyişine bütün öğrenci işlemlerinin tamamlanmış oldukları garantisiyle devam edebilir. -) - -$(H6 $(IX iş birimi büyüklüğü) İş birimi büyüklüğü) - -$(P -$(C parallel())'in ikinci parametresinin anlamı duruma göre farklılık gösterir ve bazen bütünüyle gözardı edilir: -) - ---- - /* ... */ = parallel($(I aralık), $(I iş_birimi_büyüklüğü) = 100); ---- - -$(UL - -$(LI $(C RandomAccessRange) aralıkları üzerinde ilerlerken: - -$(P -İşlemlerin iş parçacıklarına dağıtılmalarının küçük de olsa bir bedeli vardır. Bu bedel özellikle işlemlerin kısa sürdüğü durumlarda farkedilir düzeyde olabilir. Bunun önüne geçmek gereken nadir durumlarda her iş parçacığına birden fazla eleman vermek daha hızlı olabilir: -) - ---- - foreach (öğrenci; parallel(öğrenciler, $(HILITE 2))) { - // ... - } ---- - -$(P -Yukarıdaki kod elemanların iş parçacıklarına ikişer ikişer dağıtılmalarını sağlar. -) - -$(P -Otomatik olarak seçilen iş birimi büyüklüğü çoğu duruma uygundur ve özel olarak belirtilmesi gerekmez.) - -) - -$(LI $(C RandomAccessRange) olmayan aralıklar üzerinde ilerlerken: - -$(P -$(C parallel()), $(C RandomAccessRange) olmayan aralıklar üzerinde ilerlerken ilk elemanları koşut olarak değil, sırayla işletir. Asıl koşutluk baştaki $(I iş birimi büyüklüğü) adet elemanın işlemleri tamamlandıktan sonra başlar. Bu yüzden kısa ve $(C RandomAccessRange) olmayan aralıklar üzerinde ilerlerken $(C parallel())'in etkisiz olduğu gibi yanlış bir izlenim edinilebilir. -) - -) - -$(LI $(C asyncBuf()) ve koşut $(C map())'in sonuçları üzerinde ilerlerken (bu iki işlevi aşağıda göreceğiz): - -$(P -Bu durumda iş birimi büyüklüğü bütünüyle gözardı edilir. $(C parallel()), $(C asyncBuf()) veya $(C map())'in sonuç olarak ürettiği aralığın içindeki ara belleği kullanır. -) - -) - -) - -$(H5 $(IX Task) Görev türü $(C Task)) - -$(P -Programdaki başka işlemlerle aynı anda işletilebilen işlemlere $(I görev) denir. Görevler $(C std.parallelism.Task) türü ile ifade edilirler. -) - -$(P -$(C parallel()) her iş parçacığı için $(C foreach) bloğundaki işlemlerden oluşan farklı bir $(C Task) nesnesi kurar ve o görevi otomatik olarak başlatır. $(C foreach) döngüsünden çıkmadan önce de başlattığı bütün görevlerin tamamlanmalarını bekler. $(I Kurma), $(I başlatma), ve $(I tamamlanmasını bekleme) işlemlerini otomatik olarak yürüttüğü için çok yararlıdır. -) - -$(P -$(IX task) $(IX executeInNewThread) $(IX yieldForce) Aynı anda işletilebilen işlemlerin herhangi bir topluluk ile doğrudan ilgileri olmayan durumlarda kurma, başlatma, ve bekleme işlevlerinin bir $(C Task) nesnesi üzerinden açıkça çağrılmaları gerekir. Görev nesnesi kurmak için $(C task()), görevi başlatmak için $(C executeInNewThread()), görevin tamamlanmasını beklemek için de $(C yieldForce()) kullanılır. Bu işlevleri aşağıdaki programın açıklama satırlarında anlatıyorum. -) - -$(P -Aşağıdaki programdaki $(C birİşlem()) iki farklı iş için iki kere başlatılmaktadır. Hangi iş ile ilgili olarak işlediğini görebilmemiz için $(C kimlik)'in baş harfini çıkışa yazdırıyor. -) - -$(P -$(IX flush, std.stdio) $(I Not: Standart çıkışa yazdırılan bilgiler çoğu durumda çıkışta hemen belirmezler; satır sonu karakteri gelene kadar bir ara bellekte bekletilirler. $(C write) satır sonu karakteri yazdırmadığından, programın işleyişini izleyebilmek için o ara belleğin hemen çıkışa gönderilmesini $(C stdout.flush()) ile sağlıyoruz.) -) - ---- -import std.stdio; -import std.parallelism; -import std.array; -import core.thread; - -/* kimlik'in baş harfini yarım saniyede bir çıkışa yazdırır */ -int birİşlem(string kimlik, int süre) { - writefln("%s %s saniye sürecek", kimlik, süre); - - foreach (i; 0 .. (süre * 2)) { - Thread.sleep(500.msecs); /* yarım saniye */ - write(kimlik.front); - stdout.flush(); - } - - return 1; -} - -void main() { - /* birİşlem()'i işletecek olan bir görev kuruluyor. - * Burada belirtilen işlev parametreleri görev işlevine - * parametre olarak gönderilirler. */ - auto görev = $(HILITE task!birİşlem)("görev", 5); - - /* 'görev' başlatılıyor */ - görev.$(HILITE executeInNewThread()); - - /* 'görev' işine devam ederken başka bir işlem - * başlatılıyor */ - immutable sonuç = birİşlem("main içindeki işlem", 3); - - /* Bu noktada main içinde başlatılan işlemin - * tamamlandığından eminiz; çünkü onu görev olarak değil, - * her zaman yaptığımız gibi bir işlev çağrısı olarak - * başlattık. */ - - /* Öte yandan, bu noktada 'görev'in işini tamamlayıp - * tamamlamadığından emin olamayız. Gerekiyorsa - * tamamlanana kadar beklemek için yieldForce()'u - * çağırıyoruz. yieldForce() ancak görev tamamlanmışsa - * döner. Dönüş değeri görev işlevinin, yani - * birİşlem()'in dönüş değeridir. */ - immutable görevSonucu = görev.$(HILITE yieldForce()); - - writeln(); - writefln("Hepsi tamam; sonuç: %s", sonuç + görevSonucu); -} ---- - -$(P -Programın çıktısı benim denediğim ortamda aşağıdakine benziyor. İşlemlerin aynı anda gerçekleştiklerini $(C m) ve $(C g) harflerinin karışık olarak yazdırılmalarından anlıyoruz: -) - -$(SHELL -main içindeki işlem 3 saniye sürecek -görev 5 saniye sürecek -mgmggmmgmgmggggg -Hepsi tamam; sonuç: 2 -) - -$(P -Yukarıdaki $(C task!birİşlem) kullanımında görev işlevi $(C task)'e şablon parametresi olarak belirtilmektedir. Bu yöntem çoğu duruma uygun olsa da, $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) gördüğümüz gibi, bir şablonun her farklı gerçekleştirmesi farklı bir türdendir. Bu fark, aynı türden olmalarını bekleyeceğimiz görev nesnelerinin aslında farklı türden olmalarına ve bu yüzden birlikte kullanılamamalarına neden olabilir. -) - -$(P -Örneğin, aşağıdaki iki işlevin parametre ve dönüş türleri aynı olduğu halde $(C task()) işlev şablonu yoluyla elde edilen iki $(C Task) şablon gerçekleştirmesi farklı türdendir. Bu yüzden, aynı dizinin elemanı olamazlar: -) - ---- -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) -} ---- - -$(P -Derleyici, "uyumsuz türler" anlamına gelen bir hata mesajı verir: -) - -$(SHELL -Error: $(HILITE incompatible types) for ((task(1)) : (task(2))): -'Task!($(HILITE foo), int)*' and 'Task!($(HILITE bar), int)*' -) - -$(P -$(C task())'in başka bir yüklemesi görev işlevini şablon parametresi olarak değil, işlev parametresi olarak alır: -) - ---- - void işlem(int sayı) { - // ... - } - - auto görev = task($(HILITE &işlem), 42); ---- - -$(P -Bu yöntem farklı şablon gerçekleştirmeleri kullanmadığından, farklı işlev kullanıyor olsalar bile farklı $(C Task) nesneleri aynı dizinin elemanı olabilirler: -) - ---- -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 derlenir) -} ---- - -$(P -Gerektiğinde isimsiz bir işlev veya $(C opCall()) işlecini tanımlamış olan bir türün bir nesnesi de kullanılabilir. Örneğin bir isimsiz işlev ile şöyle çağrılabilir: -) - ---- - auto görev = task((int sayı) $(HILITE {) - /* ... */ - $(HILITE }), 42); ---- - -$(H6 $(IX hata, koşut işlem) Atılan hatalar) - -$(P -Görevler farklı iş parçacıklarında işletildiklerinden, attıkları hatalar onları başlatan iş parçacığı tarafından yakalanamaz. Bu yüzden, atılan hatayı görevin kendisi yakalar ve $(C yieldForce()) çağrılana kadar bekletir. Aynı hata $(C yieldForce()) çağrıldığında tekrar atılır ve böylece görevi başlatmış olan iş parçacığı tarafından yakalanabilir. -) - ---- -import std.stdio; -import std.parallelism; -import core.thread; - -void hataAtanİşlem() { - writeln("hataAtanİşlem() başladı"); - Thread.sleep(1.seconds); - writeln("hataAtanİşlem() hata atıyor"); - throw new Exception("Atılan hata"); -} - -void main() { - auto görev = task!hataAtanİşlem(); - görev.executeInNewThread(); - - writeln("main devam ediyor"); - Thread.sleep(3.seconds); - - writeln("main, görev'in sonucunu alıyor"); - görev.yieldForce(); -} ---- - -$(P -Görev sırasında atılan hatanın programı hemen sonlandırmadığını programın çıktısında görüyoruz: -) - -$(SHELL -main devam ediyor -hataAtanİşlem() başladı -hataAtanİşlem() hata atıyor $(SHELL_NOTE atıldığı zaman) -main, görev'in sonucunu alıyor -object.Exception@deneme.d(10): Atılan hata $(SHELL_NOTE farkedildiği zaman) -) - -$(P -Görevin attığı hata, istendiğinde $(C yieldForce())'u sarmalayan bir $(C try-catch) bloğu ile yakalanabilir. Bunun alışılmışın dışında bir kullanım olduğuna dikkat edin: $(C try-catch) bloğu normalde hatayı atan kodu sarmalar. Görevlerde ise $(C yieldForce())'u sarmalar: -) - ---- - try { - görev.yieldForce(); - - } catch (Exception hata) { - writefln("görev sırasında bir hata olmuş: '%s'", - hata.msg); - } ---- - -$(P -Programın şimdiki çıktısı: -) - -$(SHELL -main devam ediyor -hataAtanİşlem() başladı -hataAtanİşlem() hata atıyor $(SHELL_NOTE atıldığı zaman) -main, görev'in sonucunu alıyor -görev sırasında bir hata olmuş: 'Atılan hata' $(SHELL_NOTE yakalandığı zaman) -) - -$(H6 $(C Task) işlevleri) - -$(UL - -$(LI $(C done): Görevin tamamlanıp tamamlanmadığını bildirir; görev sırasında hata atılmışsa o hatayı atar. - ---- - if (görev.done) { - writeln("Tamamlanmış"); - - } else { - writeln("İşlemeye devam ediyor"); - } ---- - -) - -$(LI $(C executeInNewThread()): Görevi yeni başlattığı bir iş parçacığında işletir.) - -$(LI $(C executeInNewThread(int öncelik)): Görevi yeni başlattığı iş parçacığında ve belirtilen öncelikle $(ASIL priority) işletir. (Öncelik değeri iş parçacıklarının işlem önceliklerini belirleyen bir işletim sistemi kavramıdır.)) - -) - -$(P -Görevin tamamlanmasını beklemek için üç farklı işlev vardır: -) - -$(UL - -$(LI $(C yieldForce()): Henüz başlatılmamışsa görevi bu iş parçacığında başlatır; zaten tamamlanmışsa dönüş değerini döndürür; hâlâ işlemekteyse bitmesini mikro işlemciyi meşgul etmeden bekler; hata atılmışsa tekrar atar.) - -$(LI $(IX spinForce) $(C spinForce()): $(C yieldForce())'tan farkı, gerektiğinde mikro işlemciyi meşgul ederek beklemesidir.) - -$(LI $(IX workForce) $(C workForce()): $(C yieldForce())'tan farkı, beklenen görev tamamlananana kadar yeni bir görevi işletmeye başlamasıdır.) - -) - -$(P -Bunlar arasından çoğu durumda en uygun olan $(C yieldForce())'tur. $(C spinForce()), her ne kadar mikro işlemciyi meşgul etse de görevin çok kısa bir süre sonra tamamlanacağının bilindiği durumlarda yararlıdır. $(C workForce()), görev beklenene kadar başka bir görevin başlatılmasının istendiği durumlara uygundur. -) - -$(P -$(C Task)'in diğer üye işlevleri için internet üzerindeki Phobos belgelerine bakınız. -) - -$(H5 $(IX asyncBuf) $(C taskPool.asyncBuf())) - -$(P -Bu işlev normalde sırayla ilerletilen $(C InputRange) aralıklarının koşut olarak ilerletilmelerini sağlar. $(C asyncBuf()) koşut olarak ilerlettiği aralığın elemanlarını kendisine ait bir ara bellekte bekletir ve gerektikçe buradan sunar. -) - -$(P -Ancak, olasılıkla bütünüyle tembel olan giriş aralığının bütünüyle hevesli hale gelmesini önlemek için elemanları dalgalar halinde ilerletir. Belirli sayıdaki elemanı koşut olarak hazırladıktan sonra onlar $(C popFront()) ile aralıktan çıkartılana kadar başka işlem yapmaz. Daha sonraki elemanları hesaplamaya başlamadan önce hazırdaki o elemanların tamamen kullanılmalarını bekler. -) - -$(P -Parametre olarak bir aralık ve seçime bağlı olarak her dalgada kaç eleman ilerletileceği bilgisini alır. Bu bilgiyi $(I ara bellek uzunluğu) olarak adlandırabiliriz: -) - ---- - auto elemanlar = taskPool.asyncBuf($(I aralık), $(I ara_bellek_uzunluğu)); ---- - -$(P -$(C asyncBuf())'ın etkisini görmek için hem ilerletilmesi hem de $(C foreach) içindeki kullanımı yarım saniye süren bir aralık olduğunu varsayalım. Bu aralık, kurulurken belirtilmiş olan sınır değere kadar elemanlar üretiyor: -) - ---- -import std.stdio; -import core.thread; - -struct BirAralık { - int sınır; - int i; - - bool empty() const @property { - return i >= sınır; - } - - int front() const @property { - return i; - } - - void popFront() { - writefln("%s değerinden sonrası hesaplanıyor", i); - Thread.sleep(500.msecs); - ++i; - } -} - -void main() { - auto aralık = BirAralık(10); - - foreach (eleman; aralık) { - writefln("%s değeri kullanılıyor", eleman); - Thread.sleep(500.msecs); - } -} ---- - -$(P -Aralık tembel olarak kullanıldıkça elemanları teker teker hesaplanır ve döngü içinde kullanılır. Her elemanın hesaplanması ve kullanılması toplam bir saniye sürdüğü için 10 elemanlı aralığın işlemleri 10 saniye sürer: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -0 değeri kullanılıyor -0 değerinden sonrası hesaplanıyor -1 değeri kullanılıyor -1 değerinden sonrası hesaplanıyor -2 değeri kullanılıyor -... -8 değerinden sonrası hesaplanıyor -9 değeri kullanılıyor -9 değerinden sonrası hesaplanıyor - -real 0m10.007s $(SHELL_NOTE toplam 10 saniye) -user 0m0.004s -sys 0m0.000s) -) - -$(P -Elemanların sırayla hesaplandıkları ve kullanıldıkları görülüyor. -) - -$(P -Oysa, bir sonraki elemanın hazırlanmasına başlamak için öndeki elemanların işlemlerinin sonlanmaları gerekmeyebilir. Öndeki elemanın kullanılması ile bir sonraki elemanın hesaplanması aynı anda gerçekleşebilseler, bütün süre kabaca yarıya inebilir. $(C asyncBuf()) bunu sağlar: -) - ---- -import std.parallelism; -//... - foreach (eleman; $(HILITE taskPool.asyncBuf)(aralık, $(HILITE 2))) { ---- - -$(P -Yukarıdaki kullanımda $(C asyncBuf()) her seferinde iki elemanı hazırda bekletecektir. Yeni elemanların hazırlanmaları döngü işlemleri ile koşut olarak gerçekleştirilir ve toplam süre azalır: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -0 değerinden sonrası hesaplanıyor -1 değerinden sonrası hesaplanıyor -0 değeri kullanılıyor -2 değerinden sonrası hesaplanıyor -1 değeri kullanılıyor -3 değerinden sonrası hesaplanıyor -2 değeri kullanılıyor -4 değerinden sonrası hesaplanıyor -3 değeri kullanılıyor -5 değerinden sonrası hesaplanıyor -4 değeri kullanılıyor -6 değerinden sonrası hesaplanıyor -5 değeri kullanılıyor -7 değerinden sonrası hesaplanıyor -6 değeri kullanılıyor -8 değerinden sonrası hesaplanıyor -7 değeri kullanılıyor -9 değerinden sonrası hesaplanıyor -8 değeri kullanılıyor -9 değeri kullanılıyor - -real 0m6.007s $(SHELL_NOTE şimdi 6 saniye) -user 0m0.000s -sys 0m0.004s) -) - -$(P -Hangi ara bellek uzunluğunun daha hızlı sonuç vereceği her programa ve her duruma göre değişebilir. Ara bellek uzunluğunun varsayılan değeri 100'dür. -) - -$(P -$(C asyncBuf()) $(C foreach) döngüleri dışında da yararlıdır. Aşağıdaki kod $(C asyncBuf())'ın dönüş değerini bir $(C InputRange) aralığı olarak kullanıyor: -) - ---- - auto aralık = BirAralık(10); - auto koşutAralık = taskPool.asyncBuf(aralık, 2); - writeln($(HILITE koşutAralık.front)); ---- - -$(H5 $(IX map, koşut işlem) $(C taskPool.map())) - -$(P -$(IX map, std.algorithm) Koşut $(C map())'i anlamadan önce $(C std.algorithm) modülündeki $(C map())'i anlamak gerekir. Çoğu fonksiyonel dilde de bulunan $(C std.algorithm.map), belirli bir işlevi belirli bir aralıktaki bütün elemanlara teker teker uygular. Sonuç olarak o işlevin sonuçlarından oluşan yeni bir aralık döndürür. İşleyişi tembeldir; işlevi elemanlara ancak gerektikçe uygular. $(C std.algorithm.map) tek çekirdek üzerinde işler. -) - -$(P -$(C map())'in tembel işleyişi bir çok programda hız açısından yararlıdır. Ancak, işlevin nasıl olsa bütün elemanlara da uygulanacağı ve o işlemlerin birbirlerinden bağımsız oldukları durumlarda bu tembellik aksine yavaşlığa neden olabilir. $(C std.parallelism) modülündeki $(C taskPool.map()) ve $(C taskPool.amap()) ise bütün işlemci çekirdeklerinden yararlanırlar ve bu gibi durumlarda daha hızlı işleyebilirler. -) - -$(P -Bu üç algoritmayı yine $(C Öğrenci) örneği üzerinde karşılaştıralım. Elemanlara uygulanacak olan işlev örneği olarak $(C Öğrenci) türünün not ortalaması döndüren bir işlevi olduğunu varsayalım. Koşut programlamanın etkisini görebilmek için bu işlevi de $(C Thread.sleep) ile yapay olarak yavaşlatalım. -) - -$(P -$(C std.algorithm.map), uygulanacak olan işlevi şablon parametresi olarak, aralığı da işlev parametresi olarak alır. İşlevin elemanlara uygulanmasından oluşan sonuç değerleri başka bir aralık olarak döndürür: -) - ---- - auto $(I sonuç_aralık) = map!$(I işlev)($(I aralık)); ---- - -$(P -İşlev $(C map())'e önceki bölümlerde de gördüğümüz gibi $(I isimsiz işlev) olarak verilebilir. Aşağıdaki örnekteki $(C ö) parametresi işlevin uygulanmakta olduğu elemanı belirler: -) - ---- -import std.stdio; -import std.algorithm; -import core.thread; - -struct Öğrenci { - int numara; - int[] notlar; - - double ortalamaNot() @property { - writeln(numara, - " numaralı öğrencinin işlemi başladı"); - Thread.sleep(1.seconds); - - const ortalama = notlar.sum / notlar.length; - - writeln(numara, " numaralı öğrencinin işlemi bitti"); - return ortalama; - } -} - -void main() { - Öğrenci[] öğrenciler; - - foreach (i; 0 .. 10) { - /* Her öğrenciye 80'li ve 90'lı iki not */ - öğrenciler ~= Öğrenci(i, [80 + i, 90 + i]); - } - - auto sonuçlar = $(HILITE map)!(ö => ö.ortalamaNot)(öğrenciler); - - foreach (sonuç; sonuçlar) { - writeln(sonuç); - } -} ---- - -$(P -Programın çıktısı $(C map())'in tembel olarak işlediğini gösteriyor; $(C ortalamaNot()) her sonuç için $(C foreach) ilerledikçe çağrılır: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -0 numaralı öğrencinin işlemi başladı -0 numaralı öğrencinin işlemi bitti -85 $(SHELL_NOTE foreach ilerledikçe hesaplanır) -1 numaralı öğrencinin işlemi başladı -1 numaralı öğrencinin işlemi bitti -86 -... -9 numaralı öğrencinin işlemi başladı -9 numaralı öğrencinin işlemi bitti -94 - -real 0m10.006s $(SHELL_NOTE toplam 10 saniye) -user 0m0.000s -sys 0m0.004s) -) - -$(P -$(C std.algorithm.map) hevesli bir algoritma olsaydı, işlemlerin başlangıç ve bitişleriyle ilgili mesajların hepsi en başta yazdırılırlardı. -) - -$(P -$(C std.parallelism) modülündeki $(C taskPool.map()), temelde $(C std.algorithm.map) ile aynı biçimde işler. Tek farkı, işlevleri aynı anda işletmesidir. Ürettiği sonuçları uzunluğu ikinci parametresi ile belirtilen bir ara belleğe yerleştirir ve buradan sunar. Örneğin, aşağıdaki kod işlevleri her adımda üç eleman için aynı anda işletir: -) - ---- -import std.parallelism; -// ... -double ortalamaNot(Öğrenci öğrenci) { - return öğrenci.ortalamaNot; -} -// ... - auto sonuçlar = $(HILITE taskPool.map)!ortalamaNot(öğrenciler, $(HILITE 3)); ---- - -$(P -$(I Not: Yukarıdaki $(C ortalamaNot()) işlevi temsilcilerin şablonlarla kullanımları ile ilgili bir kısıtlama nedeniyle gerekmiştir. Daha kısa olan aşağıdaki satır, $(C TaskPool.map)'in bir "sınıf içi şablon" olması nedeniyle derlenemez:) -) - ---- -auto sonuçlar = - taskPool.map!(ö => ö.ortalamaNot)(öğrenciler, 3); $(DERLEME_HATASI) ---- - -$(P -Bu sefer işlemlerin üçer üçer aynı anda ama belirsiz sırada işletildiklerini görüyoruz: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -0 numaralı öğrencinin işlemi başladı $(SHELL_NOTE aynı anda) -2 numaralı öğrencinin işlemi başladı $(SHELL_NOTE ama belirsiz sırada) -1 numaralı öğrencinin işlemi başladı -0 numaralı öğrencinin işlemi bitti -2 numaralı öğrencinin işlemi bitti -1 numaralı öğrencinin işlemi bitti -85 -86 -87 -5 numaralı öğrencinin işlemi başladı -3 numaralı öğrencinin işlemi başladı -4 numaralı öğrencinin işlemi başladı -5 numaralı öğrencinin işlemi bitti -4 numaralı öğrencinin işlemi bitti -3 numaralı öğrencinin işlemi bitti -88 -89 -90 -8 numaralı öğrencinin işlemi başladı -6 numaralı öğrencinin işlemi başladı -7 numaralı öğrencinin işlemi başladı -8 numaralı öğrencinin işlemi bitti -6 numaralı öğrencinin işlemi bitti -7 numaralı öğrencinin işlemi bitti -91 -92 -93 -9 numaralı öğrencinin işlemi başladı -9 numaralı öğrencinin işlemi bitti -94 - -real 0m4.007s $(SHELL_NOTE toplam 4 saniye) -user 0m0.000s -sys 0m0.004s) -) - -$(P -İşlevin belgesinde $(C bufSize) olarak geçen ikinci parametrenin anlamı $(C asyncBuf())'ın ikinci parametresi ile aynı anlamdadır. Bu parametre, üretilen sonuçların depolandığı ara belleğin uzunluğunu belirtir ve varsayılan değeri 100'dür. Üçüncü parametre ise $(C parallel())'de olduğu gibi $(I iş birimi büyüklüğü) anlamındadır. Farkı, varsayılan değerinin $(C size_t.max) olmasıdır: -) - ---- - /* ... */ = taskPool.map!$(I işlev)($(I aralık), - $(I ara_bellek_uzunluğu) = 100, - $(I iş_birimi_büyüklüğü) = size_t.max); ---- - -$(H5 $(IX amap) $(C taskPool.amap())) - -$(P -İki fark dışında $(C taskPool.map()) ile aynı biçimde işler: -) - -$(UL - -$(LI -Bütünüyle hevesli bir algoritmadır. -) - -$(LI -Yalnızca $(C RandomAccessRange) aralıklarıyla işler. -) - -) - ---- - auto sonuçlar = $(HILITE taskPool.amap)!ortalamaNot(öğrenciler); ---- - -$(P -Hevesli olduğu için $(C amap())'ten dönüldüğünde bütün sonuçlar hesaplanmışlardır: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -0 numaralı öğrencinin işlemi başladı $(SHELL_NOTE hepsi en başta) -2 numaralı öğrencinin işlemi başladı -1 numaralı öğrencinin işlemi başladı -3 numaralı öğrencinin işlemi başladı -0 numaralı öğrencinin işlemi bitti -4 numaralı öğrencinin işlemi başladı -1 numaralı öğrencinin işlemi bitti -5 numaralı öğrencinin işlemi başladı -3 numaralı öğrencinin işlemi bitti -6 numaralı öğrencinin işlemi başladı -2 numaralı öğrencinin işlemi bitti -7 numaralı öğrencinin işlemi başladı -4 numaralı öğrencinin işlemi bitti -8 numaralı öğrencinin işlemi başladı -5 numaralı öğrencinin işlemi bitti -9 numaralı öğrencinin işlemi başladı -6 numaralı öğrencinin işlemi bitti -7 numaralı öğrencinin işlemi bitti -9 numaralı öğrencinin işlemi bitti -8 numaralı öğrencinin işlemi bitti -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 - -real 0m3.005s $(SHELL_NOTE toplam 3 saniye) -user 0m0.000s -sys 0m0.004s) -) - -$(P -$(C amap()) koşut $(C map())'ten daha hızlı işler ama bütün sonuçları alacak kadar büyük bir dizi kullanmak zorundadır. Hız kazancının karşılığı olarak daha fazla bellek kullanır. -) - -$(P -$(C amap())'in isteğe bağlı olan ikinci parametresi de $(C parallel())'de olduğu gibi $(I iş birimi büyüklüğü) anlamındadır: -) - ---- - auto sonuçlar = taskPool.amap!ortalamaNot(öğrenciler, $(HILITE 2)); ---- - -$(P -Sonuçlar dönüş değeri olarak elde edilmek yerine üçüncü parametre olarak verilen bir $(C RandomAccessRange) aralığına da yazılabilirler. O aralığın uzunluğu elemanların uzunluğuna eşit olmalıdır: -) - ---- - double[] sonuçlar; - sonuçlar.length = öğrenciler.length; - taskPool.amap!ortalamaNot(öğrenciler, 2, $(HILITE sonuçlar)); ---- - -$(H5 $(IX reduce, koşut işlem) $(C taskPool.reduce())) - -$(P -$(IX reduce, std.algorithm) Koşut $(C reduce())'u anlamadan önce $(C std.algorithm) modülündeki $(C reduce())'u anlamak gerekir. -) - -$(P -$(IX fold, std.algorithm) $(C std.algorithm.reduce), daha önce $(LINK2 /ders/d/araliklar.html, Aralıklar bölümünde) gördüğümüz $(C fold())'un eşdeğeridir. En belirgin farkı, işlev parametrelerinin sırasının $(C fold)'un tersi olmasıdır. (Bu yüzden, koşut olmayan işlemlerde $(C std.algorithm.reduce) yerine $(LINK2 /ders/d/ufcs.html, UFCS'e) olanak veren $(C std.algorithm.fold)'u yeğlemenizi öneririm.) -) - -$(P -$(C reduce()) başka dillerde de bulunan üst düzey bir algoritmadır. $(C map())'te olduğu gibi, şablon parametresi olarak bir veya birden fazla işlev alır. İşlev parametreleri olarak da bir başlangıç değeri ve bir aralık alır. Belirtilen işlevleri o andaki sonuca ve her elemana uygular. Açıkça başlangıç değeri verilmediği zaman aralığın ilk elemanını başlangıç değeri olarak kullanır. -) - -$(P -Nasıl işlediği, kendi içinde tanımlamış olduğu varsayılan $(C sonuç) isimli bir değişken üzerinden aşağıdaki gibi ifade edilebilir: -) - -$(OL - -$(LI $(C sonuç)'u başlangıç değeri ile ilkler.) - -$(LI Her bir eleman için $(C sonuç = işlev(sonuç, eleman)) ifadesini işletir.) - -$(LI $(C sonuç)'un son değerini döndürür.) - -) - -$(P -Örneğin bir dizinin bütün elemanlarının karelerinin toplamı aşağıdaki gibi hesaplanabilir: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - writeln(reduce!((a, b) => a + b * b)(0, [5, 10])); -} ---- - -$(P -İşlev yukarıdaki gibi dizgi olarak belirtildiğinde $(C a) belirli bir andaki sonuç değerini, $(C b) de eleman değerini temsil eder. İlk işlev parametresi başlangıç değeridir (yukarıdaki $(C 0)). -) - -$(P -Program sonuçta 5 ve 10'un kareleri olan 25 ve 100'ün toplamını yazdırır: -) - -$(SHELL -125 -) - -$(P -Tarifinden de anlaşılacağı gibi $(C reduce()) kendi içinde bir döngü işletir. O döngü tek çekirdek üzerinde işlediğinden, elemanların işlemlerinin birbirlerinden bağımsız oldukları durumlarda yavaş kalabilir. Böyle durumlarda $(C std.parallelism) modülündeki $(C taskPool.reduce()) kullanılarak işlemlerin bütün çekirdekler üzerinde işletilmeleri sağlanabilir. -) - -$(P -Bunun örneğini görmek için $(C reduce())'u yine yapay olarak yavaşlatılmış olan bir işlevle kullanalım: -) - ---- -import std.stdio; -import std.algorithm; -import core.thread; - -int birHesap(int sonuç, int eleman) { - writefln("başladı - eleman: %s, sonuç: %s", - eleman, sonuç); - - Thread.sleep(1.seconds); - sonuç += eleman; - - writefln("tamamlandı - eleman: %s, sonuç: %s", - eleman, sonuç); - - return sonuç; -} - -void main() { - writeln("Sonuç: ", $(HILITE reduce)!birHesap(0, [1, 2, 3, 4])); -} ---- - -$(P -$(C reduce()) elemanları sırayla ve teker teker kullanır ve bu yüzden program 4 saniye sürer: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -başladı - eleman: 1, sonuç: 0 -tamamlandı - eleman: 1, sonuç: 1 -başladı - eleman: 2, sonuç: 1 -tamamlandı - eleman: 2, sonuç: 3 -başladı - eleman: 3, sonuç: 3 -tamamlandı - eleman: 3, sonuç: 6 -başladı - eleman: 4, sonuç: 6 -tamamlandı - eleman: 4, sonuç: 10 -Sonuç: 10 - -real 0m4.003s $(SHELL_NOTE 4 saniye) -user 0m0.000s -sys 0m0.000s) -) - -$(P -$(C parallel()) ve $(C map()) örneklerinde olduğu gibi, bu programa da $(C std.parallelism) modülünü eklemek ve $(C reduce()) yerine $(C taskPool.reduce())'u çağırmak bütün çekirdeklerden yararlanmak için yeterlidir: -) - ---- -import std.parallelism; -// ... - writeln("Sonuç: ", $(HILITE taskPool.reduce)!birHesap(0, [1, 2, 3, 4])); ---- - -$(P -Ancak, $(C taskPool.reduce())'un işleyişinin önemli farklılıkları vardır. -) - -$(P -Yukarıda gördüğümüz koşut algoritmalarda olduğu gibi $(C taskPool.reduce()) da elemanları birden fazla göreve paylaştırarak koşut olarak işletir. Her görev kendisine verilen elemanları kullanarak farklı bir $(C sonuç) hesaplar. Yalnızca tek başlangıç değeri olduğundan, her görevin hesapladığı $(C sonuç) o değerden başlar (yukarıdaki $(C 0)). -) - -$(P -Görevlerin hesapları tamamladıkça, onların ürettikleri sonuçlar son bir kez aynı $(C sonuç) hesabından geçirilirler. Bu son hesap koşut olarak değil, tek çekirdek üzerinde işletilir. O yüzden $(C taskPool.reduce()) bu örnekte olduğu gibi az sayıda elemanla kullanıldığında daha yavaş sonuç verebilir. Bunu aşağıdaki çıktıda göreceğiz. -) - -$(P -Aynı başlangıç değerinin bütün görevler tarafından kullanılıyor olması $(C taskPool.reduce())'un hesapladığı sonucun normal $(C reduce())'dan farklı çıkmasına neden olabilir. Bu sonuç aynı nedenden dolayı yanlış da olabilir. O yüzden başlangıç değeri bu örnekteki toplama işleminin başlangıç değeri olan $(C 0) gibi etkisiz bir değer olmak zorundadır. -) - -$(P -Ek olarak, elemanlara uygulanan işlevin aldığı parametrelerin türü ve işlevin dönüş türü ya aynı olmalıdır ya da birbirlerine otomatik olarak dönüşebilmelidirler. -) - -$(P -$(C taskPool.reduce()) ancak bu özellikleri anlaşılmışsa kullanılmalıdır. -) - ---- -import std.parallelism; -// ... - writeln("Sonuç: ", $(HILITE taskPool.reduce)!birHesap(0, [1, 2, 3, 4])); ---- - -$(P -Çıktısında önce birden fazla görevin aynı anda, onların sonuçlarının ise sırayla işletildiklerini görüyoruz. Sırayla işletilen işlemleri işaretli olarak gösteriyorum: -) - -$(SHELL -$ time ./deneme -$(DARK_GRAY -başladı - eleman: 1, sonuç: 0 $(SHELL_NOTE önce görevler aynı anda) -başladı - eleman: 2, sonuç: 0 -başladı - eleman: 3, sonuç: 0 -başladı - eleman: 4, sonuç: 0 -tamamlandı - eleman: 1, sonuç: 1 -$(HILITE başladı - eleman: 1, sonuç: 0) $(SHELL_NOTE onların sonuçları sırayla) -tamamlandı - eleman: 2, sonuç: 2 -tamamlandı - eleman: 3, sonuç: 3 -tamamlandı - eleman: 4, sonuç: 4 -$(HILITE tamamlandı - eleman: 1, sonuç: 1) -$(HILITE başladı - eleman: 2, sonuç: 1) -$(HILITE tamamlandı - eleman: 2, sonuç: 3) -$(HILITE başladı - eleman: 3, sonuç: 3) -$(HILITE tamamlandı - eleman: 3, sonuç: 6) -$(HILITE başladı - eleman: 4, sonuç: 6) -$(HILITE tamamlandı - eleman: 4, sonuç: 10) -Sonuç: 10 - -real 0m5.006s $(SHELL_NOTE bu örnekte koşut reduce daha yavaş) -user 0m0.004s -sys 0m0.000s) -) - -$(P -Matematik sabiti $(I pi)'nin (π) seri yöntemiyle hesaplanması gibi başka hesaplarda koşut $(C reduce()) daha hızlı işleyecektir. -) - -$(H5 Birden çok işlev ve çokuzlu sonuçlar) - -$(P -Hem $(C std.algorithm) modülündeki $(C map()) hem de $(C std.parallelism) modülündeki $(C map()), $(C amap()), ve $(C reduce()) birden fazla işlev alabilirler. O durumda bütün işlevlerin sonuçları bir arada $(LINK2 /ders/d/cokuzlular.html, Çokuzlular bölümünde) gördüğümüz $(C Tuple) türünde döndürülür. Her işlevin sonucu, o işlevin sırasına karşılık gelen çokuzlu üyesidir. Örneğin, ilk işlevin sonucu çokuzlunun 0 numaralı üyesidir. -) - -$(P -Aşağıdaki program birden fazla işlev kullanımını $(C std.algorithm.map) üzerinde gösteriyor. Dikkat ederseniz $(C çeyreği()) ve $(C onKatı()) işlevlerinin dönüş türleri farklıdır. Öyle bir durumda çokuzlu sonuçların üyelerinin türleri de farklı olur. -) - ---- -import std.stdio; -import std.algorithm; -import std.conv; - -double çeyreği(double değer) { - return değer / 4; -} - -string onKatı(double değer) { - return to!string(değer * 10); -} - -void main() { - auto sayılar = [10, 42, 100]; - auto sonuçlar = map!($(HILITE çeyreği, onKatı))(sayılar); - - writefln(" Çeyreği On Katı"); - - foreach (çeyrekSonucu, onKatSonucu; sonuçlar) { - writefln("%8.2f%8s", çeyrekSonucu, onKatSonucu); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL - Çeyreği On Katı - 2.50 100 - 10.50 420 - 25.00 1000 -) - - -$(P -$(C taskPool.reduce()) kullanımında sonuçların ilk değerlerinin de çokuzlu olarak verilmeleri gerekir: -) - ---- - taskPool.reduce!(foo, bar)($(HILITE tuple(0, 1)), [1, 2, 3, 4]); ---- - -$(H5 $(IX TaskPool) $(C TaskPool)) - -$(P -$(C std.parallelism) modülünün bütün koşut algoritmalarının perde arkasında yararlandıkları görevler bir $(C TaskPool) topluluğunun parçalarıdır. Normalde, bütün algoritmalar aynı $(C taskPool) isimli topluluğu kullanırlar. -) - -$(P -$(C taskPool) programın çalışmakta olduğu ortama uygun sayıda göreve sahip olduğundan çoğu durumda ondan başkaca $(C TaskPool) nesnesine gerek duyulmaz. Buna rağmen bazen özel bir görev topluluğunun açıkça oluşturulması ve bazı koşut işlemler için onun kullanılması istenebilir. -) - -$(P -$(C TaskPool) kaç iş parçacığı kullanacağı bildirilerek kurulur. İş parçacığı adedinin varsayılan değeri ortamdaki çekirdek adedinin bir eksiğidir. Bu bölümde gördüğümüz bütün olanaklar açıkça kurulmuş olan bir $(C TaskPool) nesnesi üzerinden kullanılabilirler. -) - -$(P -Aşağıdaki örnekte $(C parallel()) ile nasıl kullanıldığını görüyoruz: -) - ---- -import std.stdio; -import std.parallelism; - -void $(CODE_DONT_TEST compiler_asm_deprecation_warning)main() { - auto işçiler = new $(HILITE TaskPool(2)); - - foreach (i; $(HILITE işçiler).parallel([1, 2, 3, 4])) { - writefln("%s kullanılıyor", i); - } - - $(HILITE işçiler).finish(); -} ---- - -$(P -Görevler tamamlandığında $(C TaskPool) nesnesinin iş parçacıklarının sonlandırılmaları için $(C TaskPool.finish()) çağrılır. -) - -$(H5 Özet) - -$(UL - -$(LI Birbirlerine bağlı işlemlerin koşut olarak işletilmeleri hatalıdır.) - -$(LI $(C parallel()), Bir aralığın elemanlarına koşut olarak erişilmesini sağlar.) - -$(LI Gerektiğinde görevler $(C task()) ile oluşturulabilirler, $(C executeInNewThread()) ile başlatılabilirler ve $(C yieldForce()) ile beklenebilirler.) - -$(LI Hata atılarak sonlanmış olan görevlerden atılan hatalar $(C yieldForce()) gibi işlevler çağrıldığında yakalanabilirler.) - -$(LI $(C asyncBuf()), $(C InputRange) aralığındaki elemanları yarı hevesli olarak aynı anda ilerletir.) - -$(LI $(C map()), işlevleri $(C InputRange) aralığındaki elemanlara yarı hevesli olarak aynı anda uygular.) - -$(LI $(C amap()), işlevleri $(C RandomAccessRange) aralığındaki elemanlara tam hevesli olarak aynı anda uygular.) - -$(LI $(C reduce()), hesapları $(C RandomAccessRange) aralığındaki elemanlar için aynı anda işletir.) - -$(LI $(C map()), $(C amap()), ve $(C reduce()) birden fazla işlev alabilirler; öyle olduğunda sonuçlar çokuzlu üyeleridirler.) - -$(LI İstendiğinde $(C taskPool)'dan başka $(C TaskPool) nesneleri de kullanılabilir.) - -) - -macros: - SUBTITLE=Koşut İşlemler - - DESCRIPTION=Mikro işlemci çekirdeklerinin hepsinden birden yararlanmayı sağlayan koşut programlama (parallel programming) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial koşut işlemler paralel parallel parallelism parallelization - -SOZLER= -$(eniyilestirme) -$(es_zamanli) -$(fonksiyonel_programlama) -$(gorev) -$(hevesli) -$(is_parcacigi) -$(kapsam) -$(kosut_islemler) -$(mikro_islemci) -$(mikro_islemci_cekirdegi) -$(tembel_degerlendirme) -$(uc_birim) diff --git a/ddili/src/ders/d/main.cozum.d b/ddili/src/ders/d/main.cozum.d deleted file mode 100644 index 8371d21..0000000 --- a/ddili/src/ders/d/main.cozum.d +++ /dev/null @@ -1,76 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Programın Çevresiyle Etkileşimi) - -$(OL - -$(LI - ---- -import std.stdio; -import std.conv; - -int main(string[] parametreler) { - if (parametreler.length != 4) { - stderr.writeln( - "HATA! Doğru kullanım: \n ", parametreler[0], - " bir_sayı işlem başka_sayı"); - return 1; - } - - immutable birinci = to!double(parametreler[1]); - string işlem = parametreler[2]; - immutable ikinci = to!double(parametreler[3]); - - switch (işlem) { - - case "+": - writeln(birinci + ikinci); - break; - - case "-": - writeln(birinci - ikinci); - break; - - case "x": - writeln(birinci * ikinci); - break; - - case "/": - writeln(birinci / ikinci); - break; - - default: - throw new Exception("Geçersiz işlem: " ~ işlem); - } - - return 0; -} ---- - -) - -$(LI - ---- -import std.stdio; -import std.process; - -void main() { - write("Başlatmamı istediğiniz program satırını yazın: "); - string komutSatırı = readln(); - - writeln("Çıktısı: ", executeShell(komutSatırı)); -} ---- - -) - -) - -Macros: - SUBTITLE=Programın Çevresiyle Etkileşimi Problem Çözümleri - - DESCRIPTION=D dilinde programın ana fonksiyonu olan main'in parametreleri ve dönüş türü ile ilgili bölümün problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function parametre main dönüş değeri problem çözüm diff --git a/ddili/src/ders/d/main.d b/ddili/src/ders/d/main.d deleted file mode 100644 index dd03a22..0000000 --- a/ddili/src/ders/d/main.d +++ /dev/null @@ -1,525 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX programın çevresi) $(IX çevre, program) $(IX ortam, program) Programın Çevresiyle Etkileşimi) - -$(P -$(IX main) İşlevleri anlatırken $(C main)'in de bir işlev olduğunu söylemiştim. Programın işleyişi $(C main)'le başlar ve oradan başka işlevlere dallanır. $(C main)'in şimdiye kadar gördüğümüz tanımı şöyleydi: -) - ---- -void main() { - // ... -} ---- - -$(P -O tanıma bakarak $(C main)'in bir değer döndürmediğini ve hiçbir parametre almadığını düşünürüz. Aslında bu mümkün değildir, çünkü programı başlatan ortam bir dönüş değeri bekler; $(C main), $(C void) döndürüyor gibi yazılmış olsa da aslında bir değer döndürür. -) - -$(H5 $(C main)'in dönüş değeri) - -$(P -Programlar her zaman için başka bir ortam tarafından başlatılırlar. Bu ortam, programı ismini yazıp Enter'a basarak başlattığımız uç birim olabilir, menülerindeki "Çalıştır" gibi bir komutla başlattığımız bir geliştirme ortamı olabilir, programı kendisi başlatan başka bir program olabilir, vs. -) - -$(P -Program kendisini başlatan bu ortama işini başarıyla tamamlayıp tamamlamadığı bilgisini $(C main)'in dönüş değeri olarak bildirir. -) - -$(P -Programın dönüş değeri olarak 0 değeri programın başarıyla sonuçlandığını, 0'dan başka bir değer ise programın çalışması sırasında bir hata oluştuğunu bildirmek için kullanılır. İstediğimiz değeri döndürmek bize kalmış olsa da, 0'ın $(I başarı) anlamına gelmesi standartlaşmıştır. -) - -$(P $(I Not: Dönüş değeri olarak ancak [0,127] aralığındaki tamsayılara güvenebilirsiniz. Bunun dışındaki değerler her ortam tarafından desteklenmiyor olabilir. -)) - -$(P -Sıfırdan başka değerler her programa göre değişik anlamlar taşıyabilir. Örneğin Unix türevi ortamlarda dosyaları listelemek için kullanılan $(C ls) programı önemsiz hatalarda 1 değerini, ciddi hatalarda ise 2 değerini döndürür. Komut satırından başlatılan programların dönüş değerleri $(C $?) ortam değişkeninden okunabilir. Örneğin klasörde bulunmayan bir dosyayı görmek istediğimizde, programın dönüş değeri komut satırında $(C $?) değişkeninden aşağıdaki gibi okunabilir. -) - -$(P -$(I Not: Aşağıdaki komut satırı örneklerinde $(C #) karakteriyle başlayan satırlar kullanıcın yazdığı satırları gösteriyor. Aynı adımları denemek istediğinizde o satırları $(C #) karakteri dışında sizin yazarak Enter'a basmanız gerekir. O satırları daha koyu olarak gösterdim.) -) - -$(P $(I Ek olarak, aşağıdaki örnekler bir Linux ortamında denenmiş olsalar da, benzerlerini örneğin Windows DOS pencerelerinde de kullanabilirsiniz.) -) - -$(SHELL -# ls klasorde_bulunmayan_bir_dosya -$(DARK_GRAY ls: klasorde_bulunmayan_bir_dosya: No such file or directory) -# $(HILITE echo $?) -$(DARK_GRAY 2) $(SHELL_NOTE ls'in dönüş değeri) -) - -$(H6 Dönüş değeri $(C void) olan $(C main)'ler de değer üretirler) - -$(P -Şimdiye kadar karşılaştığımız işlevlerin bazılarının işlerini yapamayacakları durumlara düştüklerinde hata attıklarını görmüştük. Şimdiye kadar gördüğümüz kadarıyla, hata atıldığı zaman program bir $(C object.Exception) mesajıyla sonlanıyordu. -) - -$(P -Bu gibi durumlarda, $(C main)'in dönüş türü olarak $(C void) kullanılmış olsa bile, yani $(C main) bir değer döndürmeyecekmiş gibi yazılmış olsa bile, programı çalıştıran ortama otomatik olarak 1 değeri döndürülür. Bunu görmek için hata atarak sonlanan şu basit programı çalıştıralım: -) - ---- -void main() { - throw new Exception("Bir hata oldu"); -} ---- - -$(P -Dönüş türü $(C void) olarak tanımlandığı halde programı çalıştıran ortama 1 değeri döndürülmüştür: -) - -$(SHELL -# ./deneme -$(DARK_GRAY object.Exception: Bir hata oldu) -# echo $? -$(DARK_GRAY $(HILITE 1)) -) - -$(P -Benzer şekilde, dönüş türü $(C void) olarak tanımlanmış olan $(C main) işlevleri başarıyla sonlandıklarında, otomatik olarak dönüş değeri olarak 0 üretirler. Bu sefer $(I başarıyla sonlanan) şu programa bakalım: -) - ---- -import std.stdio; - -void main() { - writeln("Başardım!"); -} ---- - -$(P -Bu program dönüş değeri olarak 0 üretmiştir: -) - -$(SHELL -# ./deneme -$(DARK_GRAY Başardım!) -# echo $? -$(DARK_GRAY $(HILITE 0)) -) - -$(H6 Dönüş değerini belirlemek) - -$(P -Kendi programlarımızdan değer döndürmek, başka işlevlerde de olduğu gibi, $(C main)'i dönüş türünü $(C int) olarak tanımlamak ve bir $(C return) deyimi kullanmak kadar basittir: -) - ---- -import std.stdio; - -$(HILITE int) main() { - int sayı; - write("Lütfen 3-6 arasında bir sayı giriniz: "); - readf(" %s", &sayı); - - if ((sayı < 3) || (sayı > 6)) { - $(HILITE stderr).writeln("HATA: ", sayı, " uygun değil!"); - $(HILITE return 111); - } - - writeln("Teşekkür: ", sayı); - - $(HILITE return 0); -} ---- - -$(P -Programın isminin $(C deneme) olduğunu kabul edersek ve istenen aralıkta bir sayı verildiğinde programın dönüş değeri 0 olur: -) - -$(SHELL -# ./deneme -$(DARK_GRAY Lütfen 3-6 arasında bir sayı giriniz: 5 -Teşekkür: 5) -# echo $? -$(DARK_GRAY $(HILITE 0)) -) - -$(P -Aralığın dışında bir sayı verildiğinde ise programın dönüş değeri 111 olur: -) - -$(SHELL -# ./deneme -$(DARK_GRAY Lütfen 3-6 arasında bir sayı giriniz: 10 -HATA: 10 uygun değil!) -# echo $? -$(DARK_GRAY $(HILITE 111)) -) - -$(P -Normalde hata için 1 değerini kullanmak yeterlidir. Ben yalnızca örnek olması için özel bir nedeni olmadan 111 değerini seçtim. -) - -$(H5 $(IX stderr) Standart hata akımı $(C stderr)) - -$(P -Yukarıdaki programda $(C stderr) akımını kullandım. Bu akım, standart akımların üçüncüsüdür ve programın hata mesajlarını yazmak için kullanılır: -) - -$(UL -$(LI $(C stdin): standart giriş akımı) -$(LI $(C stdout): standart çıkış akımı) -$(LI $(C stderr): standart hata akımı) -) - -$(P -Programlar uç birimde başlatıldıklarında $(C stdout) ve $(C stderr) akımlarına yazılanlar normalde ekranda belirirler. Bu akımlara yazılan mesajları istendiğinde ayrı ayrı elde etmek de mümkündür. -) - -$(H5 $(C main)'in parametreleri) - -$(P -Bazı programlar kendilerini başlatan ortamlardan parametre alabilirler. Örneğin yukarıda gördüğümüz $(C ls) programı parametresiz olarak yalnızca $(C ls) yazarak başlatılabilir: -) - -$(SHELL -# ls -$(DARK_GRAY deneme -deneme.d) -) - -$(P -İsteğe bağlı olarak da bir veya daha çok parametreyle başlatılabilir. Bu parametrelerin anlamları bütünüyle programa bağlıdır ve o programın belgelerinde belirtilmiştir: -) - -$(SHELL -# ls $(HILITE -l deneme) -$(DARK_GRAY -rwxr-xr-x 1 acehreli users 460668 Nov 6 20:38 deneme) -) - -$(P -D programlarını başlatırken kullanılan parametreler $(C main)'e bir $(C string) dizisi olarak gönderilirler. $(C main)'i $(C string[]) parametre alacak şekilde tanımlamak bu parametrelere erişmek için yeterlidir. Aşağıdaki program kendisine verilen parametreleri çıkışına yazdırıyor: -) - ---- -import std.stdio; - -void main($(HILITE string[] parametreler)) { - foreach (i, parametre; parametreler) { - writefln("%3s numaralı parametre: %s", i, parametre); - } -} ---- - -$(P -Rasgele parametrelerle şöyle başlatılabilir: -) - -$(SHELL -# ./deneme komut satirina yazilan parametreler 42 --bir_secenek -$(DARK_GRAY  0 numaralı parametre: ./deneme -  1 numaralı parametre: komut -  2 numaralı parametre: satirina -  3 numaralı parametre: yazilan -  4 numaralı parametre: parametreler -  5 numaralı parametre: 42 -  6 numaralı parametre: --bir_secenek) -) - -$(P -Parametre dizisinin ilk elemanı her zaman için program başlatılırken kullanılan program ismidir. Diğer parametreler bu dizinin geri kalan elemanlarıdır. -) - -$(P -Bu parametrelerle ne yapacağı tamamen programa kalmıştır. Örneğin kendisine verilen iki sözcüğü ters sırada yazdıran bir program: -) - ---- -import std.stdio; - -int main(string[] parametreler) { - if (parametreler.length != 3) { - stderr.writeln("HATA! Doğru kullanım:\n ", - parametreler[0], - " bir_sözcük başka_sözcük"); - return 1; - } - - writeln(parametreler[2], ' ', parametreler[1]); - - return 0; -} ---- - -$(P -Bu program gerektiğinde doğru kullanımını da gösteriyor: -) - -$(SHELL -# ./deneme -$(DARK_GRAY HATA! Doğru kullanım: - ./deneme bir_sözcük başka_sözcük) -# ./deneme dünya merhaba -$(DARK_GRAY merhaba dünya) -) - -$(H5 $(IX program seçenekleri) $(IX komut satırı seçenekleri) $(IX seçenek, program) $(IX getopt, std.getopt) Program seçenekleri ve $(C std.getopt) modülü) - -$(P -$(C main)'in parametreleriyle ve dönüş değeriyle ilgili olarak bilinmesi gerekenler aslında bu kadardır. Ancak parametreleri teker teker listeden ayıklamak zahmetli olabilir. Onun yerine, bu konuda yardım alabileceğimiz $(C std.getopt) modülünün bir kullanımını göstereceğim. -) - -$(P -Bazı parametreler program tarafından bilgi olarak kullanılırlar. Örneğin yukarıdaki programa verilen "dünya" ve "merhaba" parametreleri, o programın ekrana yazdıracağı bilgiyi belirliyordu. -) - -$(P -Bazı parametreler ise programın işini nasıl yapacağını belirlerler; bunlara $(I program seçeneği) denir. Örneğin yukarıda kullandığımız $(C ls) programına komut satırında seçenek olarak $(C -l) vermiştik. -) - -$(P -Program seçenekleri programların kullanışlılıklarını arttırırlar. Böylece, programın istediği parametreler bir insan tarafından teker teker komut satırından yazılmak yerine örneğin bir betik program içinden verilebilirler. -) - -$(P -Komut satırı parametrelerinin ne oldukları ve ne anlama geldikleri her ne kadar tamamen programa bağlı olsalar da onların da bir standardı gelişmiştir. POSIX standardına uygun bir kullanımda, her seçenek $(C --) ile başlar, seçenek ismi bunlara bitişik olarak yazılır, ve seçenek değeri de bir $(C =) karakterinden sonra gelir: -) - -$(SHELL -# ./deneme --bir_secenek=17 -) - -$(P -Phobos'un $(C std.getopt) modülü, programa parametre olarak verilen bu tür seçeneklerin ayıklanmasında yardımcı olur. Ben burada fazla ayrıntısına girmeden $(C getopt) işlevinin kısa bir kullanımını göstereceğim. -) - -$(P -Çıkışına rasgele seçtiği sayıları yazdıran bir program tasarlayalım. Kaç tane sayı yazdıracağı ve sayıların değerlerinin hangi aralıkta olacağı programa komut satırından seçenekler olarak verilsin. Program örneğin şu şekilde başlatılabilsin: -) - -$(SHELL -# ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 -) - -$(P -$(C getopt) işlevi bu değerleri program parametreleri arasında bulmakta yararlıdır. $(C getopt)'un okuduğu değerlerin hangi değişkenlere yazılacakları $(C readf) kullanımından tanıdığımız $(C &) karakteriyle bir $(I gösterge) olarak bildirilir: -) - ---- -import std.stdio; -$(HILITE import std.getopt;) -import std.random; - -void main(string[] parametreler) { - int adet; - int enKüçükDeğer; - int enBüyükDeğer; - - $(HILITE getopt)(parametreler, - "adet", $(HILITE &)adet, - "en-kucuk", $(HILITE &)enKüçükDeğer, - "en-buyuk", $(HILITE &)enBüyükDeğer); - - foreach (i; 0 .. adet) { - write(uniform(enKüçükDeğer, enBüyükDeğer + 1), ' '); - } - - writeln(); -} ---- - -$(SHELL -# ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 -$(DARK_GRAY 11 11 13 11 14 15 10) -) - -$(P -Çoğu zaman parametrelerin kestirmeleri de olur. Örneğin $(C --adet) yazmak yerine kısaca $(C -a) yazılır. Seçeneklerin kestirmeleri $(C getopt)'a $(C |) ayracından sonra bildirilir: -) - ---- - getopt(parametreler, - "adet|a", &adet, - "en-kucuk|k", &enKüçükDeğer, - "en-buyuk|b", &enBüyükDeğer); ---- - -$(P -Çoğu zaman kestirme seçenekler için $(C =) karakteri de kullanılmaz: -) - -$(SHELL -# ./deneme -a7 -k10 -b15 -$(DARK_GRAY 11 13 10 15 14 15 14) -) - -$(P -Parametrelerin programa $(C string) türünde geldiklerini görmüştük. $(C getopt) bu dizgileri değişkenlerin türlerine otomatik olarak dönüştürür. Örneğin yukarıdaki programdaki $(C adet)'in $(C int) olduğunu bildiği için, onu $(C string)'den $(C int)'e çevirir. $(C getopt)'u kullanmadığımız zamanlarda bu dönüşümü daha önce de bir kaç kere kullandığımız $(C to) işleviyle kendimiz de gerçekleştirebiliriz. -) - -$(P -$(C std.conv) modülünde tanımlanmış olan $(C to)'yu daha önce hep tamsayıları $(C string)'e dönüştürmek için $(C to!string) şeklinde kullanmıştık. $(C string) yerine başka türlere de dönüştürebiliriz. Örneğin 0'dan başlayarak kendisine komut satırından bildirilen sayıda ikişer ikişer sayan bir programda $(C to)'yu şöyle kullanabiliriz: -) - ---- -import std.stdio; -$(HILITE import std.conv); - -void main(string[] parametreler) { - // Parametre verilmediğinde 10 varsayıyoruz - size_t adet = 10; - - if (parametreler.length > 1) { - // Bir parametre verilmiş - adet = $(HILITE to!size_t(parametreler[1])); - } - - foreach (i; 0 .. adet) { - write(i * 2, ' '); - } - - writeln(); -} ---- - - -$(P -Program parametre verilmediğinde 10, verildiğinde ise belirtildiği kadar sayı üretir: -) - -$(SHELL -# ./deneme -$(DARK_GRAY 0 2 4 6 8 10 12 14 16 18) -# ./deneme 3 -$(DARK_GRAY 0 2 4) -) - -$(H5 $(IX ortam değişkeni) Ortam değişkenleri) - -$(P -Programları başlatan ortamlar programların yararlanmaları için ortam değişkenleri de sunarlar. Bu değişkenlere $(C std.process) modülündeki $(C environment) topluluğu ile eşleme tablosu arayüzü ile erişilir. Örneğin, çalıştırılacak olan programların hangi klasörlerde arandıklarını belirten $(C PATH) değişkeni aşağıdaki gibi yazdırılabilir: -) - ---- -import std.stdio; -$(HILITE import std.process;) - -void main() { - writeln($(HILITE environment["PATH"])); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -# ./deneme -$(DARK_GRAY /usr/local/bin:/usr/bin) -) - -$(P -$(C std.process.environment) bütün ortam değişkenlerini eşleme tablolarının söz dizimiyle sunar: -) - ---- -import std.process; -// ... - writeln(environment["PATH"]); ---- - -$(P -Buna rağmen kendisi bir eşleme tablosu değildir. Bütün değişkenleri tek eşleme tablosu olarak elde etmek için: -) - ---- - string[string] hepsi = environment.toAA(); ---- - -$(H5 Başka programları başlatmak) - -$(P -Programlar başka programları başlattıklarında onların $(I ortamları) haline gelirler. Program başlatmaya yarayan işlevler $(C std.process) modülünün olanakları rasındadır. -) - -$(P -$(IX executeShell, std.process) Örneğin, $(C executeShell) kendisine parametre olarak verilen dizgiyi sanki komut satırında yazılmış gibi başlatır ve hem programın dönüş değerini hem de çıktısını bir $(I çokuzlu) olarak döndürür. Diziye benzer biçimde kullanılan çokuzluları daha sonra $(LINK2 /ders/d/cokuzlular.html, Çokuzlular bölümünde) göreceğiz: -) - ---- -import std.stdio; -import std.process; - -void main() { - const sonuç = $(HILITE executeShell)("ls -l deneme"); - const dönüşDeğeri = sonuç[0]; - const çıktısı = sonuç[1]; - - writefln("ls programı %s değerini döndürdü.", dönüşDeğeri); - writefln("Çıktısı:\n%s", çıktısı); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -# ./deneme -$(DARK_GRAY -ls programı 0 değerini döndürdü. -Çıktısı: --rwxrwxr-x. 1 acehreli acehreli 1352810 Oct 6 15:00 deneme -) -) - -$(H5 Özet) - -$(UL - -$(LI Dönüş türü $(C void) olarak tanımlansa bile $(C main) başarıyla sonlandığında 0, hata ile sonlandığında 1 döndürür.) - -$(LI $(C stderr) hata mesajlarını yazmaya uygun olan akımdır.) - -$(LI $(C main), $(C string[]) türünde parametre alabilir.) - -$(LI $(C std.getopt) modülü program parametrelerini ayrıştırmaya yarar.) - -$(LI $(C std.process) modülü ortam değişkenlerine eriştirmeye ve başka programları başlatmaya yarar.) - -) - -$(PROBLEM_COK - -$(PROBLEM -Komut satırından parametre olarak iki değer ve bir işlem karakteri alan bir hesap makinesi yazın. Şöyle çalışsın: - -$(SHELL -# ./deneme 3.4 x 7.8 -$(DARK_GRAY 26.52) -) - -$(P $(I Not: $(C *) karakterinin komut satırında özel bir anlamı olduğu için çarpma işlemi için $(C x) karakterini kullanın. Yine de $(C *) karakterini kullanmak isterseniz komut satırında $(C \*) şeklinde yazmanız gerekir. -) -) - -) - -$(PROBLEM -Hangi programı başlatmak istediğinizi soran, bu programı başlatan ve çıktısını yazdıran bir program yazın. -) - -) - -Macros: - SUBTITLE=Programın Çevresiyle Etkileşimi - - DESCRIPTION=D dilinde programın ana fonksiyonu olan main'in parametreleri, dönüş türü, ve ortam değişkenleri. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function parametre main dönüş değeri - -SOZLER= -$(betik) -$(donus_degeri) -$(gosterge) -$(islev) -$(klasor) -$(modul) -$(ortam_degiskeni) -$(parametre) -$(phobos) -$(uc_birim) diff --git a/ddili/src/ders/d/mantiksal_ifadeler.cozum.d b/ddili/src/ders/d/mantiksal_ifadeler.cozum.d deleted file mode 100644 index 03ae861..0000000 --- a/ddili/src/ders/d/mantiksal_ifadeler.cozum.d +++ /dev/null @@ -1,53 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Mantıksal İfadeler) - -$(OL - -$(LI - Derleyici $(C 10 < sayı)'yı bir ifade olarak tanıdığı için, ondan sonra bir virgül bekliyor. Bütün ifadenin etrafına parantezler koyulduğunda da sorun çözülmüyor, çünkü bu sefer de $(C 10 < sayı) ifadesinden sonra bir kapama parantezi bekliyor. -) - -$(LI - $(C (10 < sayı) < 20) şeklinde gruplama kullanıldığında derleme hatası olmaz, çünkü derleyici önce $(C 10 < sayı) ifadesini işletir, ondan sonra onun sonucunu $(C < 20) ile kullanır. $(C 10 < sayı) gibi bir mantıksal ifadenin sonucunun da ya $(C false) ya da $(C true) olduğunu biliyoruz. - -$(P -$(C false) ve $(C true) değerleri tamsayı işlemlerinde kullanıldıklarında otomatik olarak 0 ve 1 değerlerine dönüşürler. (Otomatik tür dönüşümlerini daha sonra göreceğiz.) O yüzden de bütün ifade ya $(C 0 < 20) ya da $(C 1 < 20) haline gelir ve ikisinin sonucu da her zaman için $(C true)'dur. -) - -) - -$(LI -"Alt sınırdan büyüktür ve üst sınırdan küçüktür" mantıksal ifadesini şöyle kurarız: - ---- - writeln("Arasında: ", (sayı > 10) && (sayı < 20)); ---- - -) - -$(LI -"Yeterince bisiklet var" ifadesini $(C kişi_sayısı <= bisiklet_sayısı) veya $(C bisiklet_sayısı >= kişi_sayısı) olarak yazabiliriz. Diğerleri de sorudaki ifade doğrudan D koduna çevrilerek yazılabilir: - ---- - writeln("Plaja gidiyoruz: ", - ((mesafe < 10) && (bisiklet_sayısı >= kişi_sayısı)) - || - ((kişi_sayısı <= 5) && araba_var && ehliyet_var) - ); ---- - -$(P -Okumayı kolaylaştırmak için $(C ||) işlecinin ayrı bir satıra yazıldığına dikkat edin. Böylece sorudaki iki koşulu temiz bir şekilde iki ayrı satırda görebiliyoruz. -) - -) - -) - -Macros: - SUBTITLE=Mantıksal İfadeler Problem Çözümleri - - DESCRIPTION=D.ershane D programlama dili dersi çözümleri: Mantıksal İfadeler - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial mantıksal ifadeler bool true false diff --git a/ddili/src/ders/d/mantiksal_ifadeler.d b/ddili/src/ders/d/mantiksal_ifadeler.d deleted file mode 100644 index fe6e013..0000000 --- a/ddili/src/ders/d/mantiksal_ifadeler.d +++ /dev/null @@ -1,414 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX mantıksal ifade) Mantıksal İfadeler) - -$(P -$(IX ifade) Programda asıl işleri $(I ifadeler) yaparlar. Programda değer veya yan etki üreten her şeye ifade denir. Aslında oldukça geniş bir kavramdır, çünkü $(C 42) gibi bir tamsayı sabiti bile 42 değerini ürettiği için bir ifadedir. $(STRING "merhaba") gibi bir dizgi de bir ifadedir, çünkü $(STRING "merhaba") sabit dizgisini üretir. (Not: Buradaki $(I üretme) kavramını değişken tanımlama ile karıştırmayın. Burada yalnızca $(I değer) üretmekten söz ediliyor; değişken üretmekten değil. Her değerin bir değişkene ait olması gerekmez.) -) - -$(P -$(C writeln) gibi kullanımlar da ifadedirler, çünkü yan etkileri vardır: çıkış akımına karakter yerleştirdikleri için çıkış akımını etkilemiş olurlar. Şimdiye kadar gördükleriniz arasından atama işlecini de bir ifade örneği olarak verebiliriz. -) - -$(P -İfadelerin değer üretiyor olmaları, onların başka ifadelerde değer olarak kullanılmalarını sağlar. Böylece basit ifadeler kullanılarak daha karmaşık ifadeler elde edilebilir. Örneğin hava sıcaklığını veren bir $(C hava_sıcaklığı()) işlevi olduğunu düşünürsek, onu kullanarak şöyle bir çıktı oluşturabiliriz: -) - ---- -writeln("Şu anda hava ", hava_sıcaklığı(), " derece"); ---- - -$(P -O satır toplam dört ifadeden oluşur: -) - -$(OL -$(LI $(C "Şu anda hava ") ifadesi) -$(LI $(C hava_sıcaklığı()) ifadesi) -$(LI $(C " derece") ifadesi) -$(LI ve o üç ifadeyi kullanan $(C writeln)'li ifade) -) - -$(P -Bu bölümde mantıksal ifadeleri göreceğiz ama daha ileri gitmeden önce en temel işlemlerden olan atama işlecini hatırlayalım. -) - -$(P $(B = (atama işleci):) -Sağ tarafındaki ifadenin değerini sol tarafındaki ifadeye (örneğin bir değişkene) atar. -) - ---- -hava_sıcaklığı = 23 // hava_sıcaklığı'nın değeri 23 olur ---- - -$(H5 Mantıksal ifadeler) - -$(P -Mantıksal ifadeler Bool aritmetiğinde geçen ifadelerdir. Karar verme düzeneğinin parçası oldukları için bilgisayarları akıllı gösteren davranışların da temelidirler. Örneğin bir programın "eğer girilen yanıt Evet ise dosyayı kaydedeceğim" gibi bir kararında bir mantıksal ifade kullanılır. -) - -$(P -Mantıksal ifadelerde yalnızca iki değer vardır: "doğru olmama" anlamını taşıyan $(C false) ve "doğruluk" anlamını taşıyan $(C true). -) - -$(P -Aşağıdaki örneklerde bir soru ile kullanılan $(C writeln) ifadelerini şöyle anlamanız gerekiyor: Eğer sorunun karşısına "true" yazılmışsa $(I evet), "false" yazılmışsa $(I hayır)... Örneğin programın çıktısı -) - -$(SHELL -Tatlı var: true -) - -$(P -olduğunda "evet, tatlı var" demektir. Aynı şekilde -) - -$(SHELL -Tatlı var: false -) - -$(P -olduğunda "hayır, tatlı yok" demektir. Yani çıktıda "var" göründüğü için "var olduğunu" düşünmeyin; çıktıdaki "... var: false", "yok" anlamına geliyor. Aşağıdaki program parçalarını hep öyle okumanız gerekiyor. -) - -$(P -Mantıksal ifadeleri daha ileride göreceğimiz $(I koşullarda), $(I döngülerde), $(I parametrelerde), vs. çok kullanacağız. Programlarda bu kadar çok kullanıldıkları için mantıksal ifadeleri çok iyi anlamak gerekir. Tanımları son derece kısa olduğu için çok da kolaydırlar. -) - -$(P -Mantıksal ifadelerde kullanılan mantıksal işleçler şunlardır: -) - -$(UL - -$(LI $(IX ==) $(IX eşittir, mantıksal işleç) $(C ==) - -"Eşit midir" sorusunu yanıtlar. İki tarafındaki ifadelerin değerlerini karşılaştırır ve eşit olduklarında "doğruluk" anlamına gelen $(C true) değerini, eşit olmadıklarında da "doğru olmama" anlamına gelen $(C false) değerini üretir. Ürettiği değerin türü de doğal olarak $(C bool)'dur. Örneğin şöyle iki değişkenimiz olsun: - ---- -int haftadaki_gün_sayısı = 7; -int yıldaki_ay_sayısı = 12; ---- - -$(P -Onları kullanan iki eşitlik işleci ifadesi ve sonuçları şöyle gösterilebilir: -) - ---- -haftadaki_gün_sayısı == 7 // true -yıldaki_ay_sayısı == 11 // false ---- -) - -$(LI $(IX !=) $(IX eşit değildir, mantıksal işleç) $(C !=) - -"Eşit değil midir" sorusunu yanıtlar. İki tarafındaki ifadeleri karşılaştırır ve $(C ==) işlecinin tersi sonuç üretir. - ---- -haftadaki_gün_sayısı != 7 // false -yıldaki_ay_sayısı != 11 // true ---- -) - -$(LI $(IX ||) $(IX veya, mantıksal işleç) $(C ||) - -"Veya" anlamındadır. Sol tarafındaki ifadenin değeri $(C true) ise hiç sağ taraftaki ifadeyi işletmeden $(C true) değerini üretir. Sol taraf $(C false) ise sağ taraftakinin değerini üretir. Bu işlem Türkçe $(I veya) ifadesine benzer: birincisi, ikincisi, veya ikisi birden $(C true) olduğunda $(C true) üretir. - -$(BR)$(BR) - -$(P -Bu işlece verilen iki ifadenin alabileceği olası değerler ve sonuçları şöyledir: -) - - - - - - - -
    Sol ifadeİşleçSağ ifadeSonuç
    false||falsefalse
    false||truetrue
    true||false (bakılmaz)true
    true||true (bakılmaz)true
    - ---- -import std.stdio; - -void main() { - /* false "yok" anlamına gelsin, - * true "var" anlamına gelsin */ - bool baklava_var = false; - bool kadayıf_var = true; - - writeln("Tatlı var: ", baklava_var || kadayıf_var); -} ---- - -$(P -Yukarıdaki programdaki $(C ||) işlecini kullanan ifade, en az bir $(C true) değer olduğu için $(C true) değerini üretir. -) -) - -$(LI $(IX &&) $(IX ve, mantıksal işleç) $(C &&) - -"Ve" anlamındadır. Sol tarafındaki ifadenin değeri $(C false) ise hiç sağ taraftaki ifadeyi işletmeden $(C false) değerini üretir. Sol taraf $(C true) ise sağ taraftakinin değerini üretir. Bu işlem Türkçe $(I ve) ifadesine benzer: birincisi ve ikincisi $(C true) olduğunda $(C true) üretir. - - - - - - - -
    Sol ifadeİşleçSağ ifadeSonuç
    false&&false (bakılmaz)false
    false&&true (bakılmaz)false
    true&&falsefalse
    true&&truetrue
    - ---- -writeln("Baklava yiyeceğim: ", - baklava_yemek_istiyorum && baklava_var); ---- - -$(P $(I -$(B Not:) $(C ||) ve $(C &&) işleçlerinin bu "sağ tarafı ancak gerektiğinde" işletme davranışları işleçler arasında çok nadirdir, ve bir de şimdilik sonraya bırakacağımız $(C ?:) işlecinde vardır. Diğer işleçler bütün ifadelerinin değerlerini her zaman için hesaplarlar ve kullanırlar. -)) - -) - -$(LI $(IX ^, ya da) $(IX ya da, mantıksal işleç) $(C ^) - -"Yalnızca birisi mi" sorusunu yanıtlar. İki ifadeden ya biri ya öbürü $(C true) olduğunda (ama ikisi birden değil) $(C true) değerini üretir. - - - - - - - -
    Sol ifadeİşleçSağ ifadeSonuç
    false^falsefalse
    false^truetrue
    true^falsetrue
    true^truefalse
    - -$(P -Örneğin ancak ve ancak bir arkadaşımın geldiğinde tavla oynayacağımı, aksi taktirde onlarla başka bir şey yapacağımı düşünürsek; onların gelip gelmeme durumlarına göre tavla oynayıp oynamayacağımı şöyle hesaplayabiliriz: -) - ---- -writeln("Tavla oynayacağım: ", ahmet_burada ^ barış_burada); ---- - -) - -$(LI $(IX <, küçüktür) $(IX küçüktür, mantıksal işleç) $(C <) - -"Küçük müdür" sorusunu yanıtlar. Sol taraf sağ taraftan küçükse (veya sıralamada $(I önceyse)) $(C true), değilse $(C false) değerini üretir. - ---- -writeln("Yendik: ", yediğimiz_gol < attığımız_gol); ---- - -) - -$(LI $(IX >, büyüktür) $(IX büyüktür, mantıksal işleç) $(C >) - -"Büyük müdür" sorusunu yanıtlar. Sol taraf sağ taraftan büyükse (veya sıralamada $(I sonraysa)) $(C true), değilse $(C false) değerini üretir. - ---- -writeln("Yenildik: ", yediğimiz_gol > attığımız_gol); ---- -) - -$(LI $(IX <=) $(IX küçüktür veya eşittir, mantıksal işleç) $(C <=) - -"Küçük veya eşit midir" sorusunu yanıtlar. Sol taraf sağ taraftan küçük (veya sıralamada daha önce) veya ona eşit olduğunda $(C true) üretir. $(C >) işlecinin tersidir. - ---- -writeln("Yenilmedik: ", yediğimiz_gol <= attığımız_gol); ---- -) - -$(LI $(IX >=) $(IX büyüktür veya eşittir, mantıksal işleç) $(C >=) - -"Büyük veya eşit midir" sorusunu yanıtlar. Sol taraf sağ taraftan büyük (veya sıralamada daha sonra) veya ona eşit olduğunda $(C true) üretir. $(C <) işlecinin tersidir. - ---- -writeln("Yenmedik: ", yediğimiz_gol >= attığımız_gol); ---- -) - -$(LI $(IX !, değil) $(IX değil, mantıksal işleç) $(C !) - -"Tersi" anlamındadır. Diğer mantıksal işleçlerden farklı olarak tek bir ifade ile çalışır ve sağ tarafındaki ifadenin değerinin tersini üretir: $(C true) ise $(C false), $(C false) ise $(C true). - ---- -writeln("Bakkala gideceğim: ", !ekmek_var); ---- - -) - -) - -$(H5 İfadeleri gruplamak) - -$(P -İfadelerin hangi sırada işletilecekleri gerektiğinde parantezlerle belirtilir. Karmaşık ifadelerde önce parantez içindeki ifadeler işletilir ve onların değeri dıştaki işleçle kullanılır. Örneğin "kahve veya çay varsa ve yanında da baklava veya kadayıf varsa keyfim yerinde" gibi bir ifadeyi şöyle hesaplayabiliriz: -) - ---- -writeln("Keyfim yerinde: ", - (kahve_var || çay_var) && (baklava_var || kadayıf_var)); ---- - -$(P -Kendimiz parantezlerle gruplamazsak, ifadeler D dilinin kuralları ile belirlenmiş olan önceliklere uygun olarak işletilirler. $(C &&) işlecinin önceliği $(C ||) işlecininkinden daha yüksektir. Yukarıdaki mantıksal ifadeyi parantezlerle gruplamadan şöyle yazdığımızı düşünelim: -) - ---- -writeln("Keyfim yerinde: ", - kahve_var || çay_var && baklava_var || kadayıf_var); ---- - -$(P -O ifade, işlem öncelikleri nedeniyle aşağıdakinin eşdeğeri olarak işletilir: -) - ---- -writeln("Keyfim yerinde: ", - kahve_var || (çay_var && baklava_var) || kadayıf_var); ---- - -$(P -Bu da tamamen farklı anlamda bir ifadeye dönüşmüş olur: "kahve varsa, veya çay ve baklava varsa, veya kadayıf varsa; keyfim yerinde". -) - -$(P -Bütün işleçlerin işlem önceliklerini hemen hemen hiçbir programcı ezbere bilmez. O yüzden, gerekmese bile parantezler kullanarak hangi ifadeyi kurmak istediğinizi açıkça belirtmek kodun anlaşılırlığı açısından çok yararlıdır. -) - -$(P -İşleç öncelikleri tablosunu $(LINK2 /ders/d/islec_oncelikleri.html, ilerideki bir bölümde) göreceğiz. -) - -$(H5 $(IX giriş, bool) $(IX bool okumak) Girişten $(C bool) okumak) - -$(P -Yukarıdaki örneklerdeki bütün $(C bool) ifadeler çıkışa $(STRING "false") veya $(STRING "true") dizgileri olarak yazdırılırlar. Bunun tersi de doğrudur: $(C readf()) girişten gelen $(STRING "false") ve $(STRING "true") dizgilerini $(C false) ve $(C true) değerlerine dönüştürür. Bu dizgilerdeki harfler büyük veya küçük olabilir; örneğin, $(STRING "False") ve $(STRING "FALSE") dizgileri $(C false)'a, $(STRING "True") ve $(STRING "TRUE") dizgileri de $(C true)'ya dönüştürülür. -) - -$(PROBLEM_COK - -$(PROBLEM -Sayıların büyüklüğü ve küçüklüğü ile ilgili olan $(C <), $(C >) vs. işleçleri bu bölümde tanıdık. Bu işleçler içinde "arasında mıdır" sorusunu yanıtlayan işleç bulunmaz. Yani verilen bir sayının iki değer arasında olup olmadığını hesaplayan işleç yoktur. Bir arkadaşınız bunun üstesinden gelmek için şöyle bir program yazmış olsun. Bu programı derlemeye çalışın ve derlenemediğini görün: - ---- -import std.stdio; - -void main() { - int sayı = 15; - writeln("Arasında: ", 10 < sayı < 20); $(DERLEME_HATASI) -} ---- - -$(P -Derleme hatasını gidermek için bütün ifadenin etrafında parantez kullanmayı deneyin: -) - ---- -writeln("Arasında: ", (10 < sayı < 20)); $(DERLEME_HATASI) ---- - -$(P -Yine derlenemediğini görün. -) - -) - -$(PROBLEM -Aynı arkadaşınız hatayı gidermek için $(I bir şeyler denerken) derleme hatasının gruplama ile giderildiğini farketsin: - ---- -writeln("Arasında: ", (10 < sayı) < 20); ---- - -$(P -Bu sefer programın beklendiği gibi çalıştığını ve "true" yazdığını gözlemleyin. Ne yazık ki o çıktı yanıltıcıdır çünkü programda gizli bir hata bulunuyor. Hatanın etkisini görmek için 15 yerine bu sefer 20'den büyük bir değer kullanın: -) - ---- - int sayı = 21; ---- - -$(P -O değer 20'den küçük olmadığı halde programın yine de "true" yazdırdığını görün. -) - -$(P -$(B İpucu:) Mantıksal ifadelerin değerlerinin $(C bool) türünde olduklarını hatırlayın. Bildiğiniz $(C bool) değerlerin 20 gibi bir sayıdan küçük olması gibi bir kavram tanımadık. -) - -) - -$(PROBLEM -D'de "arasında mıdır" sorusunu yanıtlayan mantıksal ifadeyi şu şekilde kodlamamız gerekir: alt sınırdan büyüktür ve üst sınırdan küçüktür. Programdaki ifadeyi o şekilde değiştirin ve artık çıktının beklendiği gibi "true" olduğunu görün. Ayrıca, yazdığınız ifadenin $(C sayı)'nın başka değerleri için de doğru çalıştığını denetleyin. Örneğin, $(C sayı) 50 veya 1 olduğunda sonuç "false" çıksın; 12 olduğunda "true" çıksın. -) - -$(PROBLEM -Plaja ancak iki koşuldan birisi gerçekleştiğinde gidiyor olalım: - -$(UL -$(LI Mesafe 10'dan az (kilometre varsayalım) ve yeterince bisiklet var) -$(LI Kişi sayısı 5 veya daha az, arabamız var, ve ehliyetli birisi var) -) - -$(P -Aşağıdaki programdaki mantıksal ifadeyi bu koşullar sağlandığında "true" yazdıracak şekilde kurun. Programı denerken "... var mı?" sorularına "false" veya "true" diye yanıt verin: -) - ---- -import std.stdio; -import std.conv; -import std.string; - -void main() { - write("Kaç kişiyiz? "); - int kişi_sayısı; - readf(" %s", &kişi_sayısı); - - write("Kaç bisiklet var? "); - int bisiklet_sayısı; - readf(" %s", &bisiklet_sayısı); - - write("Mesafe? "); - int mesafe; - readf(" %s", &mesafe); - - write("Araba var mı? "); - bool araba_var; - readf(" %s", &araba_var); - - write("Ehliyet var mı? "); - bool ehliyet_var; - readf(" %s", &ehliyet_var); - - /* Aşağıdaki true'yu silin ve yerine sorudaki koşullardan - * birisi gerçekleştiğinde true üretecek olan bir - * mantıksal ifade yazın: */ - writeln("Plaja gidiyoruz: ", true); -} ---- - -$(P -Programın doğru çalıştığını farklı değerler girerek denetleyin. -) - -) - -) - -Macros: - SUBTITLE=Mantıksal İfadeler - - DESCRIPTION=D dilinin mantıksal ifade işleçleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial mantıksal ifadeler bool true false - -SOZLER= -$(atama) -$(dizgi) -$(ifade) -$(islec) -$(islev) -$(oncelik) -$(yan_etki) diff --git a/ddili/src/ders/d/merhaba_dunya.cozum.d b/ddili/src/ders/d/merhaba_dunya.cozum.d deleted file mode 100644 index b478863..0000000 --- a/ddili/src/ders/d/merhaba_dunya.cozum.d +++ /dev/null @@ -1,52 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Merhaba Dünya) - -$(OL - -$(LI - ---- -import std.stdio; - -void main() { - writeln("Başka bir şey... :p"); -} ---- - -) - -$(LI - ---- -import std.stdio; - -void main() { - writeln("Bir satır..."); - writeln("Başka bir satır..."); -} ---- - -) - -$(LI -Bu program $(C writeln) satırının sonunda noktalı virgül olmadığı için derlenemez: - ---- -import std.stdio; - -void main() { - writeln("Merhaba dünya!") $(DERLEME_HATASI) -} ---- - -) - -) - -Macros: - SUBTITLE=Merhaba Dünya Problem Çözümleri - - DESCRIPTION=İlk D programlama dili dersi çözümleri: Merhaba Dünya! - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial merhaba dünya problem çözüm diff --git a/ddili/src/ders/d/merhaba_dunya.d b/ddili/src/ders/d/merhaba_dunya.d deleted file mode 100644 index 328eb6c..0000000 --- a/ddili/src/ders/d/merhaba_dunya.d +++ /dev/null @@ -1,244 +0,0 @@ -Ddoc - -$(DIV_CLASS page_one, - -$(DERS_BOLUMU $(IX merhaba dünya) "Merhaba Dünya" Programı) - -$(P -Her programlama dilinde gösterilen ilk program $(I merhaba dünya) programıdır. Doğal olarak son derece basit olması gereken bu program o dilde program yazabilmek için mutlaka bilinmesi gereken kavramları da içerdiği için önemlidir. -) - -$(P -Şimdilik bu kavramları bir kenara bırakalım ve önce bu programı görelim: -) - ---- -import std.stdio; - -void main() { - writeln("Merhaba dünya!"); -} ---- - -$(P -Yukarıdaki $(I kaynak koddan) çalışabilir program oluşturulabilmesi için kaynak kodun bir D derleyicisi tarafından derlenmesi gerekir. -) - -$(H5 $(IX derleyici kurulumu) $(IX kurulum, derleyici) Derleyici kurulumu) - -$(P -$(IX gdc) $(IX ldc) Bu bölüm yazıldığı sırada derleyici olarak üç seçenek bulunuyor: Digital Mars derleyicisi $(C dmd), GCC derleyicisi $(C gdc), ve LLVM derleme ortamını hedefleyen $(C ldc). -) - -$(P -$(IX dmd) D programlama dilinin geliştirilmesi sırasındaki asıl derleyici $(C dmd) olmuştur. D'nin yeni olanakları ilk olarak hep bu derleyicide gerçekleştirilmişlerdir. Bu kitaptaki örnekler de hep $(C dmd) üzerinde denenmiş olduklarından sizin de onu kurmanızı öneririm. Başka derleyicileri gerekirse daha sonra kurabilirsiniz. Bu kitaptaki kod örnekleri dmd 2.071 ile derlendi. -) - -$(P -$(C dmd)'nin en yeni sürümünü $(LINK2 http://www.dlang.org/download.html, Digital Mars'ın Download sayfasından) indirebilirsiniz. O sayfadaki sürümler arasından işletim sisteminize uyan ve sisteminizin 32 veya 64 bitlik olmasına bağlı olan en yeni sürümü seçin. D1 sürümlerini seçmeyin. Bu kitap D'nin D2 diye de anılan son sürümünü anlatır. -) - -$(P -Derleyici kurulumu ortama bağlı olarak farklılık gösterse de bir kaç bağlantıya ve düğmeye tıklamak kadar kolaydır. -) - -$(H5 $(IX kaynak dosya) Kaynak dosya) - -$(P -Programcının D dili kurallarına göre yazdığı ve derleyiciye derlemesi için verdiği dosyaya $(I kaynak dosya) denir. D derlemeli bir dil olduğu için, kaynak dosyanın kendisi çalıştırılabilir bir program değildir. Kaynak dosya, ancak derleyici tarafından derlendikten sonra çalıştırılabilen program haline gelir. -) - -$(P -Her tür dosyanın olduğu gibi, kaynak dosyanın da diske kaydedilirken bir isminin olması gerekir. Kaynak dosya isminde sisteminizin izin verdiği her harfi kullanabilirsiniz. Ancak, D kaynak dosyalarının dosya isim $(I uzantısının) $(C .d) olması gelenekleşmiştir. Geliştirme ortamları, araç programlar, ve başka programcılar da bunu beklerler. Örneğin $(C deneme.d), $(C tavla.d), $(C fatura.d), vs. uygun kaynak dosya isimleridir. -) - -$(H5 Merhaba dünya programını derlemek) - -$(P -$(IX metin düzenleyici) $(IX düzenleyici, metin) Kaynak dosya bir $(LINK2 http://wiki.dlang.org/Editors, metin düzenleyicide) (veya aşağıda bahsedildiği gibi bir $(I geliştirme ortamında)) yazılabilir. Yukarıdaki programı bir metin dosyasına yazın veya kopyalayın ve $(C merhaba.d) ismiyle kaydedin. -) - -$(P -Derleyicinin görevi, bu kaynak dosyada yazım hataları bulunmadığını denetlemek ve onu makine koduna dönüştürerek çalıştırılabilir program haline getirmektir. Programı derlemek için şu adımları uygulayın: -) - -$(OL - -$(LI Bir uç birim penceresi (konsol, terminal, komut satırı, vs. diye de bilinir) açın.) - -$(LI $(C merhaba.d) dosyasının kaydedildiği klasöre gidin.) - -$(LI Aşağıdaki komutu yazın ve Enter'a basın. ($(C $) karakterini yazmayın; o karakter komut satırının başını temsil ediyor.)) - -) - -$(SHELL -$(DARK_GRAY $) dmd merhaba.d -) - -$(P -Eğer bir hata yapmadıysanız hiçbir şey olmadığını düşünebilirsiniz. Tersine, $(C dmd)'nin mesaj vermemesi herşeyin yolunda gittiğini gösterir. Bulunduğunuz klasörde $(C merhaba) (veya $(C merhaba.exe)) isminde bir program oluşmuş olması gerekir. -) - -$(P -Eğer derleyici bazı mesajlar yazdıysa programı yazarken bir hata yaptığınız için olabilir. Hatayı bulmaya çalışın, olası yanlışları düzeltin, ve programı tekrar derleyin. Programlama hayatınızda doğal olarak sıklıkla hatalar yapacaksınız. -) - -$(P -Program başarıyla derlenmişse ismini yazarak başlatabilirsiniz. Programın "Merhaba dünya!" yazdığını göreceksiniz: -) - -$(SHELL -$(DARK_GRAY $) ./merhaba $(SHELL_NOTE programın çalıştırılması) -Merhaba dünya! $(SHELL_NOTE programın yazdığı satır) -) - -$(P -Tebrikler! İlk D programınızı çalıştırdınız. -) - -$(H5 $(IX derleyici seçeneği) Derleyici seçenekleri) - -$(P -Derleyicilerin derleme aşamasıyla ilgili komut satırı seçenekleri vardır. Bu seçenekleri görmek için yalnızca derleyicinin ismini yazın ve Enter'a basın: -) - -$(SHELL -$(DARK_GRAY $) dmd $(SHELL_NOTE yalnızca derleyicinin ismi) -DMD64 D Compiler v2.069.0 -Copyright (c) 1999-2015 by Digital Mars written by Walter Bright -Documentation: http://dlang.org/ -Config file: /etc/dmd.conf -Usage: - dmd files.d ... { -switch } - - files.d D source files -... - -de show use of deprecated features as errors (halt compilation) -... - -unittest compile in unit tests -... - -w warnings as errors (compilation will halt) -... -) - -$(P -Özellikle kısaltılmış olarak gösterdiğim yukarıdaki liste her zaman için kullanmanızı önerdiğim seçenekleri içeriyor. Buradaki merhaba dünya programında hiçbir etkisi olmasa da aşağıdaki komut hem birim testlerini etkinleştirir hem de hiçbir uyarıya veya emekliye ayrılmış olanağa izin vermez. Bu ve bazı başka seçeneklerin anlamlarını ilerideki bölümlerde göreceğiz: -) - -$(SHELL -$(DARK_GRAY $) dmd merhaba.d -de -w -unittest -) - -$(P -$(C dmd) komut satırı seçeneklerinin tam listesini $(LINK2 http://dlang.org/dmd-linux.html, DMD Compiler sayfasında) bulabilirsiniz. -) - -$(P -$(C -run) seçeneğini de kullanışlı bulabilirsiniz. $(C -run), kaynak kodun derlenmesini, programın oluşturulmasını, ve çalıştırılmasını tek komuta indirger: -) - -$(SHELL -$(DARK_GRAY $) dmd $(HILITE -run) merhaba.d -w -unittest -Hello world! $(SHELL_NOTE program otomatik olarak çalıştırılır) -) - - -$(H5 $(IX IDE) $(IX geliştirme ortamı) Geliştirme ortamı) - -$(P -Derleyiciye ek olarak bir $(I geliştirme ortamı) (IDE) da kurmayı düşünebilirsiniz. Geliştirme ortamları program yazma, derleme, ve hata ayıklama adımlarını kolaylaştıran programlardır. -) - -$(P -Geliştirme ortamlarında program derlemek ve çalıştırmak, bir tuşa basmak veya bir düğmeye tıklamak kadar kolaydır. Yine de programların uç birimlerde nasıl derlendiklerini bilmeniz de önemlidir. -) - -$(P -Bir geliştirme ortamı kurmaya karar verdiğinizde $(LINK2 http://wiki.dlang.org/IDEs, dlang.org'daki IDEs sayfasındaki) seçeneklere bakabilirsiniz. -) - -$(H5 $(IX türkçe harfler) $(IX windows, türkçe harfler) Türkçe harfler) - -$(P -Kitabın bölümleri bütünüyle Türkçe programlardan oluştuklarından çalıştığınız ortamda Türkçe harflerin doğru olarak görünmeleri önemlidir. Bunun için uç birim penceresinin UTF-8 kodlamasına ayarlanmış olması gerekir. (Linux gibi bazı ortamlarda uç birimler zaten UTF-8'e ayarlıdır.) -) - -$(P -Örneğin, eğer bir Windows ortamında çalışıyorsanız karakter kodlamasını $(LINK2 http://ddili.org/forum/post/8, 65001'e ayarlamanız) ve Lucida Console gibi bir TrueType font seçmeniz gerekir. -) - -$(H5 Merhaba dünya programının içeriği) - -$(P -Bu kadar küçük bir programda bile değinilmesi gereken çok sayıda kavram bulunuyor. Bu kavramları bu aşamada fazla ayrıntılarına girmeden şöyle tanıtabiliriz: -) - -$(P $(B İç olanaklar): Her programlama dili kendi söz dizimini, temel türlerini, anahtar sözcüklerini, kurallarını, vs. tanımlar. Bunlar o dilin $(I iç olanaklarını) oluştururlar. Bu programda görülen parantezler, noktalı virgüller, $(C main) ve $(C void) gibi sözcükler; hep D dilinin kuralları dahilindedirler. Bunları Türkçe gibi bir dilin yazım kurallarına benzetebiliriz: özne, yüklem, noktalama işaretleri, vs... -) - -$(P $(B Kütüphaneler ve işlevler): Dilin iç olanakları yalnızca dilin yapısını belirler. Bu olanaklar kullanılarak oluşturulan işlevlerin bir araya getirilmelerine $(I kütüphane) adı verilir. Kütüphaneler programların yararlanmaları amacıyla bir araya getirilmiş olan program parçacıklarından oluşurlar. -) - -$(P Bu programdaki $(C writeln) işlevi, standart D kütüphanesinde çıkışa satır yazdırmak için kullanılan bir işlevdir. İsmi, "satır yaz"ın karşılığı olan "write line"dan gelir. -) - -$(P $(B Modüller): Kütüphane içerikleri, kullanış amaçlarına göre gruplanmış olan $(I modüllerdir). D'de kütüphaneler programlara bu modüller halinde tanıtılırlar. Bu programda kullanılan tek modül olan $(C std.stdio)'nun ismi, "standart kütüphanenin standart giriş/çıkış modülü" olarak çevirebileceğimiz "standard input/output"tan türemiştir. -) - -$(P $(B Karakterler ve dizgiler): Bu programdaki $(STRING "Merhaba dünya!") gibi bilgilere $(I dizgi), dizgileri oluşturan elemanlara da $(I karakter) adı verilir. Örneğin bu programdaki dizgiyi oluşturan karakterlerden bazıları $(STRING M), $(STRING e), ve $(STRING !) karakterleridir. -) - -$(P $(B İşlem sırası): Program, işini belirli adımları belirli sırada tekrarlayarak yapar. Bu sıranın en başında $(C main) isimli işlevin içindeki işlemler vardır; programın işleyişi, $(C main)'le başlar. Bu küçük programda tek bir işlem bulunuyor: $(C writeln)'li satırdaki işlem. -) - -$(P $(B Büyük/Küçük harf ayrımı): Programda değişiklik yaparken dizgilerin içinde istediğiniz karakterleri kullanabilirsiniz, ama diğer isimleri görüldükleri gibi küçük harfle yazmaya dikkat edin, çünkü D dilinde büyük/küçük harf ayrımı önemlidir. Örneğin $(C writeln) ile $(C Writeln) D dilinde farklı isimlerdir. -) - -$(P $(IX anahtar sözcük) $(B Anahtar sözcük): Dilin iç olanaklarını belirleyen özel sözcüklere $(I anahtar sözcük) denir. Anahtar sözcükler dilin kendisi için ayrılmış olan ve özel anlamlar taşıyan sözcüklerdir; programda başka amaçla kullanılamazlar. Bu programda iki anahtar sözcük bulunuyor: Programa modül eklemeye yarayan $(C import) ve buradaki kullanımında "hiçbir tür" anlamına gelen $(C void). -) - -$(P -D'nin anahtar sözcükleri şunlardır: $(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 __MODULE__), $(C __LINE__), $(C __FUNCTION__), $(C __PRETTY_FUNCTION__), $(C __gshared), $(C __traits), $(C __vector), ve $(C __parameters). -) - -$(P -$(IX asm) $(IX __vector) $(IX delete) $(IX typedef) $(IX volatile) $(IX macro) Bir kaç tanesi hariç, bu sözcükleri ilerideki bölümlerde göreceğiz: $(LINK2 http://dlang.org/statement.html#AsmStatement, $(C asm)) ve $(LINK2 http://dlang.org/phobos/core_simd.html#.Vector, $(C __vector)) bu kitabın kapsamı dışında kalıyor; $(C delete), $(C typedef), ve $(C volatile) emekliye ayrılmışlardır; ve $(C macro) henüz kullanılmamaktadır. -) - -$(PROBLEM_COK - -$(PROBLEM Programa istediğiniz başka bir şey yazdırın.) - -$(PROBLEM Programı birden fazla satır yazacak şekilde değiştirin. Bunun için programa yeni bir $(C writeln) satırı ekleyebilirsiniz.) - -$(PROBLEM Programın başka yerlerinde değişiklikler yapın ve derlemeye çalışın; örneğin $(C writeln) satırının sonundaki noktalı virgül olmadığında derleme hatalarıyla karşılaştığınızı görün. -) - -) - -) - -Macros: - SUBTITLE=Merhaba Dünya - - DESCRIPTION=İlk D programlama dili dersi: Merhaba Dünya! - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial merhaba dünya - -SOZLER= -$(anahtar_sozcuk) -$(derleyici) -$(dizgi) -$(emekli) -$(gelistirme_ortami) -$(ic_olanak) -$(islev) -$(karakter) -$(kaynak_dosya) -$(klasor) -$(metin_duzenleyici) -$(modul) -$(program) -$(standart_cikis) -$(uc_birim) diff --git a/ddili/src/ders/d/moduller.d b/ddili/src/ders/d/moduller.d deleted file mode 100644 index fe09a71..0000000 --- a/ddili/src/ders/d/moduller.d +++ /dev/null @@ -1,630 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX modül) Modüller ve Kütüphaneler) - -$(P -D programlarını ve kütüphanelerini oluşturan en alt yapısal birimler modüllerdir. -) - -$(P -D'nin modül kavramı çok basit bir temel üzerine kuruludur: Her kaynak dosya bir modüldür. Bu tanıma göre, şimdiye kadar deneme programlarımızı yazdığımız tek kaynak dosya bile bir modüldür. -) - -$(P -Her modülün ismi, dosya isminin $(C .d) uzantısından önceki bölümü ile aynıdır ve kaynak dosyanın en başına yazılan $(C module) anahtar sözcüğü ile belirtilir. Örneğin, "kedi.d" isimli bir kaynak dosyanın modül ismi aşağıdaki gibi belirtilir: -) - ---- -module kedi; - -class Kedi { - // ... -} ---- - -$(P -Eğer modül bir pakedin parçası değilse $(C module) satırı isteğe bağlıdır. (Paketleri biraz aşağıda göreceğiz.) Yazılmadığı zaman otomatik olarak dosyanın isminin $(C .d)'den önceki bölümü kullanılır. -) - -$(H6 $(IX static this) $(IX static ~this) $(IX this, static) $(IX ~this, static) $(C static this()) ve $(C static ~this())) - -$(P -Modül düzeyinde tanımlanan $(C static this()) ve $(C static ~this()), yapı ve sınıflardaki eşdeğerleri ile aynı anlamdadır: -) - ---- -module kedi; - -static this() { - // ... modülün ilk işlemleri ... -} - -static ~this() { - // ... modülün son işlemleri ... -} ---- - -$(P -Bu işlevler her iş parçacığında ayrı ayrı işletilir. (Çoğu program yalnızca $(C main())'in işlediği tek iş parçacığından oluşur.) İş parçacıklarının sayısından bağımsız olarak bütün programda tek kere işletilmesi gereken kodlar ise (örneğin, $(C shared) ve $(C immutable) değişkenlerin ilklenmeleri) $(C shared static this()) ve $(C shared static ~this()) işlevlerinde tanımlanırlar. Bunları daha sonraki $(LINK2 /ders/d/es_zamanli_shared.html, Veri Paylaşarak Eş Zamanlı Programlama bölümünde) göreceğiz. -) - -$(H6 Dosya ve modül isimleri) - -$(P -D programlarını Unicode olarak oluşturma konusunda şanslıyız; bu, hangi ortamda olursa olsun geçerlidir. Ancak, dosya sistemleri konusunda aynı serbesti bulunmaz. Örneğin, Windows işletim sistemlerinin standart dosya sistemleri dosya isimlerinde büyük/küçük harf ayrımı gözetmezken, Linux sistemlerinde büyük/küçük harfler farklıdır. Ayrıca, çoğu dosya sistemi dosya isimlerinde kullanılabilecek karakterler konusunda kısıtlamalar getirir. -) - -$(P -O yüzden, programlarınızın taşınabilir olmaları için dosya isimlerinde yalnızca ASCII küçük harfler kullanmanızı öneririm. Örneğin, yukarıdaki $(C Kedi) sınıfı ile birlikte kullanılacak olan bir $(C Köpek) sınıfının modülünün dosya ismini "kopek.d" olarak seçebiliriz. -) - -$(P -Dolayısıyla, modülün ismi de ASCII harflerden oluşur: -) - ---- -module kopek; // ASCII harflerden oluşan modül ismi - -class Köpek { // Unicode harflerden oluşan program kodu - // ... -} ---- - -$(H5 $(IX paket) Paketler) - -$(P -Modüllerin bir araya gelerek oluşturdukları yapıya $(I paket) denir. D'nin paket kavramı da çok basittir: Dosya sisteminde aynı klasörde bulunan bütün modüller aynı pakedin parçası olarak kabul edilirler. Pakedi içeren klasörün ismi de pakedin ismi haline gelir ve modül isimlerinin baş tarafını oluşturur. -) - -$(P -Örneğin, yukarıdaki "kedi.d" ve "kopek.d" dosyalarının "hayvan" isminde bir klasörde bulunduklarını düşünürsek, modül isimlerinin başına klasör ismini yazmak, onları aynı pakedin modülleri yapmaya yeter: -) - ---- -module $(HILITE hayvan.)kedi; - -class Kedi { - // ... -} ---- - -$(P -Aynı şekilde $(C kopek) modülü için de: -) - ---- -module $(HILITE hayvan.)kopek; - -class Köpek { - // ... -} ---- - -$(P -$(C module) satırı bir pakedin parçası olan modüllerde zorunludur. -) - -$(P -Paket isimleri dosya sistemi klasörlerine karşılık geldiğinden, iç içe klasörlerde bulunan modüllerin paket isimleri de o klasör yapısının eşdeğeridir. Örneğin, "hayvan" klasörünün altında bir de "omurgalilar" klasörü olsa, oradaki bir modülün paket ismi bu klasörü de içerir: -) - ---- -module hayvan.omurgalilar.kedi; ---- - -$(P -Kaynak dosyaların ne derece dallanacağı programın büyüklüğüne ve tasarımına bağlıdır. Küçük bir programın bütün dosyalarının tek bir klasörde bulunmasında bir sakınca yoktur. Öte yandan, dosyaları belirli bir düzen altına almak için klasörleri gerektiği kadar dallandırmak da mümkündür. -) - -$(H5 $(IX import) Modüllerin programda kullanılmaları) - -$(P -Şimdiye kadar çok kullandığımız $(C import) anahtar sözcüğü, bir modülün başka bir modüle tanıtılmasını ve o modül içinde kullanılabilmesini sağlar: -) - ---- -import std.stdio; ---- - -$(P -$(C import)'tan sonra yazılan modül ismi, eğer varsa paket bilgisini de içerir. Yukarıdaki koddaki $(C std.), standart kütüphaneyi oluşturan modüllerin $(C std) isimli pakette bulunduklarını gösterir. -) - -$(P -Benzer şekilde, $(C hayvan.kedi) ve $(C hayvan.kopek) modülleri bir "deneme.d" dosyasında şu şekilde bildirilir: -) - ---- -module deneme; // bu modülün ismi - -import hayvan.kedi; // kullandığı bir modül -import hayvan.kopek; // kullandığı başka bir modül - -void main() { - auto kedi = new Kedi(); - auto köpek = new Köpek(); -} ---- - -$(P $(I Not: Aşağıda anlatıldığı gibi, yukarıdaki programın derlenip oluşturulabilmesi için o modül dosyalarının da bağlayıcıya derleme satırında bildirilmeleri gerekir.) -) - -$(P -Birden fazla modül aynı anda eklenebilir: -) - ---- -import hayvan.kedi, hayvan.kopek; ---- - -$(H6 $(IX import, seçerek) Seçerek eklemek) - -$(P -$(IX :, import) Bir modüldeki isimlerin hepsini birden eklemek yerine içindeki isimler tek tek seçilerek eklenebilir: -) - ---- -import std.stdio $(HILITE : writeln;) - -// ... - - write$(HILITE f)ln("Merhaba %s.", isim); $(DERLEME_HATASI) ---- - -$(P -$(C stdio) modülünden yalnızca $(C writeln) eklenmiş olduğundan yukarıdaki kod derlenemez ($(C writefln) eklenmemiştir). -) - -$(P -İsimleri seçerek eklemek hepsini birden eklemekten daha iyidir çünkü $(I isim çakışmalarının) olasılığı daha azdır. Biraz aşağıda bir örneğini göreceğimiz gibi, isim çakışması iki farklı modüldeki aynı ismin eklenmesiyle oluşur. -) - -$(P -Ek olarak, yalnızca belirtilen isimler derleneceğinden derleme sürelerinin kısalacağı da beklenebilir. Öte yandan, her kullanılan ismin ayrıca belirtilmesini gerektirdiğinden seçerek eklemek daha fazla emek gerektirir. -) - -$(P -Kod örneklerini kısa tutmak amacıyla bu kitapta seçerek ekleme olanağından yararlanılmamaktadır. -) - -$(H6 $(IX yerel import) $(IX import, yerel) Yerel $(C import) satırları) - -$(P -Bu kitaptaki bütün $(C import) satırlarını hep programların en başlarına yazdık: -) - ---- -import std.stdio; $(CODE_NOTE en başta) -import std.string; $(CODE_NOTE en başta) - -// ... modülün geri kalanı ... ---- - -$(P -Aslında modüller herhangi başka bir satırda da eklenebilirler. Örneğin, aşağıdaki programdaki iki işlev ihtiyaç duydukları farklı modülleri kendi yerel kapsamlarında eklemekteler: -) - ---- -string mesajOluştur(string isim) { - $(HILITE import std.string;) - - string söz = format("Merhaba %s", isim); - return söz; -} - -void kullanıcıylaEtkileş() { - $(HILITE import std.stdio;) - - write("Lütfen isminizi girin: "); - string isim = readln(); - writeln(mesajOluştur(isim)); -} - -void main() { - kullanıcıylaEtkileş(); -} ---- - -$(P -$(C import) satırlarının yerel kapsamlarda bulunmaları modül kapsamında bulunmalarından daha iyidir çünkü derleyici kullanılmayan kapsamlardaki $(C import) satırlarını derlemek zorunda kalmaz. Ek olarak, yerel olarak eklenmiş olan modüllerdeki isimler ancak eklendikleri kapsamda görünürler ve böylece isim çakışmalarının olasılığı da azalmış olur. -) - -$(P -Daha sonra $(LINK2 /ders/d/katmalar.html, Katmalar bölümünde) göreceğimiz $(I şablon katmaları) olanağında modüllerin yerel olarak eklenmeleri şarttır. -) - -$(P -Bu kitaptaki örnekler yerel $(C import) olanağından hemen hemen hiç yararlanmazlar çünkü bu olanak D'ye bu kitabın yazılmaya başlanmasından sonra eklenmiştir. -) - -$(H6 Modüllerin dosya sistemindeki yerleri) - -$(P -Modül isimleri dosya sistemindeki dosyalara bire bir karşılık geldiğinden, derleyici bir modül dosyasının nerede bulunduğunu modül ismini klasör ve dosya isimlerine dönüştürerek bulur. -) - -$(P -Örneğin, yukarıdaki programın kullandığı iki modül, "hayvan/kedi.d" ve "hayvan/kopek.d" dosyalarıdır. Dolayısıyla, yukarıdaki dosyayı da sayarsak bu programı oluşturmak için üç modül kullanılmaktadır. -) - -$(H6 Kısa ve uzun isimler) - -$(P -Programda kullanılan isimler, paket ve modül bilgilerini de içeren $(I uzun halde) de yazılabilirler. Bunu $(C Kedi) sınıfının tür ismini kısa ve uzun yazarak şöyle gösterebiliriz: -) - ---- - auto kedi0 = new Kedi(); - auto kedi1 = new hayvan.kedi.Kedi(); // üsttekinin aynısı ---- - -$(P -Normalde uzun isimleri kullanmak gerekmez. Onları yalnızca olası karışıklıkları gidermek için kullanırız. Örneğin, iki modülde birden tanımlanmış olan bir ismi kısa olarak yazdığımızda derleyici hangi modüldekinden bahsettiğimizi anlayamaz. -) - -$(P -Hem $(C hayvan) modülünde hem de $(C arabalar) modülünde bulunabilecek $(C Jaguar) isimli iki sınıftan hangisinden bahsettiğimizi uzun ismiyle şöyle belirtmek zorunda kalırız: -) - ---- -import hayvan.jaguar; -import arabalar.jaguar; - -// ... - - auto karışıklık = Jaguar(); $(DERLEME_HATASI) - - auto hayvanım = hayvan.jaguar.Jaguar(); $(CODE_NOTE derlenir) - auto arabam = arabalar.jaguar.Jaguar(); $(CODE_NOTE derlenir) ---- - -$(H6 $(IX takma isimle import) $(IX import, takma isimle) Takma isimle eklemek) - -$(P -Modüller kolaylık veya isim çakışmalarını önleme gibi amaçlarla takma isim vererek eklenebilirler: -) - ---- -import $(HILITE etobur =) hayvan.jaguar; -import $(HILITE araç =) arabalar.jaguar; - -// ... - - auto hayvanım = $(HILITE etobur.)Jaguar(); $(CODE_NOTE derlenir) - auto arabam = $(HILITE araç.)Jaguar(); $(CODE_NOTE derlenir) ---- - -$(P -Bütün modüle takma isim vermek yerine seçilen her isme ayrı ayrı takma isim de verilebilir. -) - -$(P -Bir örnek olarak, aşağıdaki kod $(C -w) derleyici seçeneği ile derlendiğinde derleyici $(C .sort) $(I niteliğinin) değil, $(C sort()) $(I işlevinin) yeğlenmesi yönünde bir uyarı verir: -) - ---- -import std.stdio; -import std.algorithm; - -// ... - - auto dizi = [ 2, 10, 1, 5 ]; - dizi.sort; $(CODE_NOTE_WRONG derleme UYARISI) - writeln(dizi); ---- - -$(SHELL -Warning: use std.algorithm.sort instead of .sort property -) - -$(P -$(I Not: Yukarıdaki $(C dizi.sort) ifadesi $(C sort(dizi)) çağrısının eşdeğiridir. Farkı, $(LINK2 /ders/d/ufcs.html, ilerideki bir bölümde) göreceğimiz UFCS söz dizimi ile yazılmış olmasıdır.) -) - -$(P -Bu durumda bir çözüm, $(C std.algorithm.sort) işlevinin takma isimle eklenmesidir. Aşağıdaki yeni $(C algSort) ismi $(C sort()) $(I işlevi) anlamına geldiğinden derleyici uyarısına gerek kalmamış olur: -) - ---- -import std.stdio; -import std.algorithm : $(HILITE algSort =) sort; - -void main() { - auto arr = [ 2, 10, 1, 5 ]; - arr$(HILITE .algSort); - writeln(arr); -} ---- - -$(H6 $(IX paket, import) Pakedi modül olarak eklemek) - -$(P -Bazen bir paketteki bir modül eklendiğinde o pakedin başka modüllerinin de eklenmeleri gerekiyor olabilir. Örneğin, $(C hayvan.kedi) modülü eklendiğinde $(C hayvan.kopek), $(C hayvan.at), vs. modülleri de ekleniyordur. -) - -$(P -Böyle durumlarda modülleri tek tek eklemek yerine bütün pakedi veya bir bölümünü eklemek mümkündür: -) - ---- -import hayvan; // ← bütün paket modül gibi ekleniyor ---- - -$(P -$(IX package.d) Bu, ismi $(C package.d) olan özel bir ayar dosyası ile sağlanır. Bu dosyada önce bir $(C module) satırıyla pakedin ismi bildirilir, sonra da bir arada eklenmeleri gereken modüller $(C public) olarak eklenirler: -) - ---- -// hayvan/package.d dosyasının içeriği: -module hayvan; - -$(HILITE public) import hayvan.kedi; -$(HILITE public) import hayvan.kopek; -$(HILITE public) import hayvan.at; -// ... diğer modüller için de benzer satırlar ... ---- - -$(P -Bir modülün $(C public) olarak eklenmesi, kullanıcıların eklenen modüldeki isimleri görebilmelerini sağlar. Sonuç olarak, kullanıcılar aslında bir paket olan $(C hayvan) modülünü eklediklerinde $(C hayvan.kedi), $(C hayvan.kopek), vs. modüllere otomatik olarak erişmiş olurlar. -) - -$(H6 $(IX deprecated) Modül olanaklarını emekliye ayırmak) - -$(P -Modüller geliştikçe yeni sürümleri kullanıma sunulur. Modülün yazarları bazı olanakların belirli bir sürümden sonra $(I emekliye ayrılmalarına) karar vermiş olabilirler. Bir olanağın emekliye ayrılması, yeni yazılacak olan programların artık o olanağı kullanmamaları gerektiği anlamına gelir. Emekliye ayrılan bir olanak daha ilerideki bir sürümde modülden çıkartılabilir bile. -) - -$(P -Olanakların emekliye ayrılmalarını gerektiren çeşitli nedenler vardır. Örneğin, modülün yeni sürümü o olanağın yerine kullanılabilecek daha iyi bir olanak getiriyordur, olanak başka bir modüle taşınmıştır, olanağın ismi modülün geri kalanıyla uyumlu olsun diye değiştirilmiştir, vs. -) - -$(P -Bir olanağın emekliye ayrılmış olduğu $(C deprecated) anahtar sözcüğü ile ve gerekiyorsa özel bir mesajla bildirilir. Örneğin, aşağıdaki mesaj, $(C bir_şey_yap()) işlevini kullananlara işlevin isminin değiştiğini belirtmektedir: -) - ---- -deprecated("Lütfen bunun yerine birŞeyYap() işlevini kullanınız.") -void bir_şey_yap() { - // ... -} ---- - -$(P -Emekliye ayrılan olanak kullanıldığında derleyicinin nasıl davranacağı aşağıdaki derleyici seçenekleri ile ayarlanabilir: -) - -$(UL -$(LI $(IX -d, derleyici seçeneği) $(C -d): Emekliye ayrılmış olan olanakların kullanılmasına izin verilir) -$(LI $(IX -dw, derleyici seçeneği) $(C -dw): Emekliye ayrılmış olan olanak kullanıldığında derleme uyarısı verilir) -$(LI $(IX -de, derleyici seçeneği) $(C -de): Emekliye ayrılmış olan olanak kullanıldığında derleme hatası verilir) -) - -$(P -Örneğin, emekliye ayrılmış olan yukarıdaki olanağı kullanan bir program $(C -de) seçeneği ile derlendiğinde derleme hatası oluşur: -) - ---- - bir_şey_yap(); ---- - -$(SHELL_SMALL -$ dmd deneme.d $(HILITE -de) -$(SHELL_OBSERVED deneme.d: $(HILITE Deprecation): function deneme.bir_şey_yap is -deprecated - Lütfen bunun yerine birŞeyYap() işlevini kullanınız.) -) - -$(P -Çoğu durumda, emekliye ayrılan olanak yeni olanağın $(I takma ismi) olarak tanımlanır: -) - ---- -deprecated("Lütfen bunun yerine birŞeyYap() işlevini kullanınız.") -$(HILITE alias bir_şey_yap) = birŞeyYap; - -void birŞeyYap() { - // ... -} ---- - -$(P -$(C alias) anahtar sözcüğünü $(LINK2 /ders/d/alias.html, ilerideki bir bölümde) göreceğiz. -) - -$(H6 Modüllerdeki tanımların programa dahil edilmesi) - -$(P -$(C import) anahtar sözcüğü, belirtilen modülün programın parçası haline gelmesi için yeterli değildir. $(C import), yalnızca o modüldeki olanakların bu kaynak kod içinde kullanılabilmelerini sağlar. O kadarı ancak kaynak kodun $(I derlenebilmesi) için yeterlidir. -) - -$(P -Yukarıdaki programı yalnızca "deneme.d" dosyasını kullanarak oluşturmaya çalışmak yetmez: -) - -$(SHELL_SMALL -$ dmd deneme.d -w -de -$(DARK_GRAY deneme.o: In function `_Dmain': -deneme.d: $(HILITE undefined reference) to `_D6hayvan4kedi4Kedi7__ClassZ' -deneme.d: $(HILITE undefined reference) to `_D6hayvan5kopek6Köpek7__ClassZ' -collect2: ld returned 1 exit status) -) - -$(P -O hata mesajları $(I bağlayıcıdan) gelir. Her ne kadar anlaşılmaz isimler içeriyor olsalar da, yukarıdaki hata mesajları programda kullanılan bazı tanımların bulunamadıklarını bildirir. -) - -$(P -$(IX bağlayıcı) Programın oluşturulması, perde arkasında çağrılan bağlayıcının görevidir. Derleyici, derlediği modülleri bağlayıcıya verir; program, bağlayıcının bir araya getirdiği parçalardan oluşturulur. -) - -$(P -$(IX programın oluşturulması) O yüzden, programı oluşturan bütün parçaların derleme satırında belirtilmeleri gerekir. Yukarıdaki programın oluşturulabilmesi için, kullandığı "hayvan/kedi.d" ve "hayvan/kopek.d" dosyaları da derleme satırında bildirilmelidir: -) - -$(SHELL_SMALL -$ dmd deneme.d hayvan/kedi.d hayvan/kopek.d -w -de -) - -$(P -Modülleri derleme satırında her program için ayrı ayrı belirtmek yerine kütüphaneler içinden de kullanabiliriz. -) - -$(H5 $(IX kütüphane) Kütüphaneler) - -$(P -Modül tanımlarının derlendikten sonra bir araya getirilmelerine kütüphane adı verilir. Kütüphaneler kendileri program olmadıklarından, programların başlangıç işlevi olan $(C main) kütüphanelerde bulunmaz. Kütüphaneler yalnızca işlev, yapı, sınıf, vs. $(I tanımlarını) bir araya getirirler. Daha sonra program oluşturulurken programın diğer modülleriyle bağlanırlar. -) - -$(P -Kütüphane oluşturmak için dmd'nin $(C -lib) seçeneği kullanılır. Oluşturulan kütüphanenin isminin $(C hayvan) olacağını da $(C -of) seçeneği ile bildirirsek, yukarıdaki "kedi.d" ve "kopek.d" modüllerini içeren bir kütüphane şu şekilde oluşturulabilir: -) - -$(SHELL_SMALL -$ dmd hayvan/kedi.d hayvan/kopek.d -lib -ofhayvan -w -de -) - -$(P -Konsoldan çalıştırılan o komut, belirtilen $(C .d) dosyalarını derler ve bir kütüphane dosyası olarak bir araya getirir. Çalıştığınız ortama bağlı olarak kütüphane dosyasının ismi farklı olacaktır. Örneğin, Linux ortamlarında kütüphane dosyalarının uzantıları .a olur: $(C hayvan.a). -) - -$(P -Program oluşturulurken artık "hayvan/kedi.d"nin ve "hayvan/kopek.d"nin ayrı ayrı bildirilmelerine gerek kalmaz. Onları içeren kütüphane dosyası tek başına yeterlidir: -) - -$(SHELL_SMALL -$ dmd deneme.d hayvan.a -w -de -) - -$(P -O komut, daha önce kullandığımız şu komutun eşdeğeridir: -) - -$(SHELL_SMALL -$ dmd deneme.d hayvan/kedi.d hayvan/kopek.d -w -de -) - -$(P -$(IX Phobos, kütüphane) Bir istisna olarak, şimdiye kadar çok yararlandığımız Phobos modüllerini içeren standart kütüphanenin açıkça bildirilmesi gerekmez. O kütüphane, programa otomatik olarak dahil edilir. Yoksa normalde onu da örneğin şu şekilde belirtmemiz gerekirdi: -) - -$(SHELL_SMALL -$ dmd deneme.d hayvan.a /usr/lib64/libphobos2.a -w -de -) - -$(P -$(I Not: Phobos kütüphane dosyasının yeri ve ismi sizin ortamınızda farklı olabilir.) -) - -$(H6 Başka dillerin kütüphanelerini kullanmak) - -$(P -C ve C++ gibi başka bazı derlemeli dillerin kütüphaneleri D programlarında kullanılabilir. Ancak, farklı diller farklı $(I bağlanım) kullandıklarından, böyle bir kütüphanenin D ile kullanılabilmesi için o kütüphanenin bir $(I D ilintisinin) olması gerekir. -) - -$(P -$(IX bağlanım) $(IX özgün isim) $(IX sembol) Bağlanım, bir kütüphanenin olanaklarının dışarıdan erişimini ve o olanakların isimlerinin (sembollerinin) derlenmiş kodda nasıl ifade edildiklerini belirleyen kurallar bütünüdür. Derlenmiş koddaki isimler programcının kaynak kodda yazdığı isimlerden farklıdır: Derlenmiş koddaki isimler belirli bir dilin veya bir derleyicinin kurallarına göre $(I özgünleştirilmişlerdir). -) - -$(P -$(IX mangle, core.demangle) Örneğin, ismi kaynak kodda $(C foo) olan bir işlevin derlenmiş koddaki özgün ismi, C bağlanım kurallarına göre başına alt çizgi karakteri eklenerek oluşturulur: $(C _foo). Özgün isim üretme C++ ve D dillerinde daha karmaşıktır çünkü bu diller aynı ismin farklı modüllerde, yapılarda, sınıflarda, ve bir işlevin farklı yüklemelerinde kullanılmasına izin verir. D kaynak kodundaki $(C foo) gibi bir işlevin özgün ismi onu olası bütün başka $(C foo) isimlerinden ayırt edecek biçimde seçilir. Özgün isimlerin tam olarak ne oldukları genelde programcı için önemli olmasa da, bu konuda $(C core.demangle) modülünden yararlanılabilir: -) - ---- -module deneme; - -import std.stdio; -import core.demangle; - -void foo() { -} - -void main() { - writeln($(HILITE mangle)!(typeof(foo))("deneme.foo")); -} ---- - -$(P -$(I Not: Söz dizimi kitabın bu noktasında yabancı gelen $(C mangle) bir işlev şablonudur. Şablonları daha sonra $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) göreceğiz.) -) - -$(P -Programın çıktısı, yukarıdaki $(C foo) ile aynı türden olan $(C deneme.foo) isimli bir işlevin özgün ismini göstermektedir: -) - -$(SHELL_SMALL -_D6deneme3fooFZv -) - -$(P -Bağlayıcının verdiği hata mesajlarının anlaşılmaz isimler içermelerinin nedeni de özgün isimlerdir. Örneğin, yukarıdaki bir bağlayıcı hata mesajında $(C hayvan.kedi.Kedi) ismi değil, $(C _D6hayvan4kedi4Kedi7__ClassZ) ismi geçmiştir. -) - -$(P -$(IX extern()) $(IX C) $(IX C++) $(IX D) $(IX Objective-C) $(IX Pascal) $(IX System) $(IX Windows) $(C extern()) niteliği olanakların bağlanımlarını belirtmek için kullanılır. $(C extern()) ile kullanılabilen bağlanım türleri şunlardır: $(C C), $(C C++), $(C D), $(C Objective-C), $(C Pascal), $(C System), ve $(C Windows). Örneğin, bir C kütüphanesinde tanımlanmış olan bir işlevi çağırması gereken bir D kodunun o işlevi C bağlanımı ile bildirmesi gerekir: -) - ---- -// 'foo'nun C bağlanımı olduğu bildiriliyor (örneğin, bir C -// kütüphanesinde tanımlanmıştır) -$(HILITE extern(C)) void foo(); - -void main() { - foo(); // bu işlev çağrısı '_foo' özgün ismi ile yapılır -} ---- - -$(P -$(IX namespace, C++) C++'ın $(C namespace) anahtar sözcüğü ile tanımlanan isim alanları $(C extern(C++))'ın ikinci parametresi olarak belirtilir. Örneğin, aşağıdaki $(C bar()) bildirimi, C++ kütüphanesindeki $(C a::b::c::bar()) işlevine karşılık gelir (dikkat ederseniz, D kodu $(C ::) yerine nokta kullanır): -) - ---- -// 'bar'ın a::b::c isim alanında bulunduğu ve C++ bağlanımı -// olduğu bildiriliyor: -extern(C++, $(HILITE a.b.c)) void bar(); - -void main() { - bar(); // a::b::c::bar()'a çağrıdır - a.b.c.bar(); // üsttekinin eşdeğeri -} ---- - -$(P -$(IX ilinti) Bir kütüphanedeki olanakların D bildirimlerini içeren dosyaya o kütüphanenin $(I D ilintisi) denir. D ilintilerini elle kendiniz yazmak yerine, çoğu yaygın kütüphanenin ilintilerini içeren $(LINK2 https://github.com/D-Programming-Deimos/, Deimos projesinden) yararlanmanızı öneririm. -) - -$(P -$(IX extern) Bağlanım türü belirtilmeden kullanılan $(C extern) niteliğinin farklı bir anlamı vardır: Bir değişken için kullanılan yerin başka bir kütüphanenin sorumluluğunda olduğunu bildirir. Farklı anlamlar taşıdıklarından, $(C extern) ve $(C extern()) nitelikleri birlikte kullanılabilir: -) - ---- -// 'g_degisken' için kullanılan yerin bir C kütüphanesi -// tarafından zaten ayrıldığı bildiriliyor: -extern(C) $(HILITE extern) int g_degisken; ---- - -$(P -Yukarıdaki $(C extern) niteliği kullanılmasa, C bağlanımına sahip olmasından bağımsız olarak, $(C g_degisken) bu D modülünün bir değişkeni haline gelirdi. -) - -Macros: - SUBTITLE=Modüller ve Kütüphaneler - - DESCRIPTION=D programlarını oluşturan alt parçalar olan modüllerin, ve onların bir araya gelmelerinden oluşan kütüphanelerin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial modül module kütüphane library - -SOZLER= -$(baglanim) -$(baglayici) -$(derleyici) -$(emekli) -$(ilinti) -$(is_parcacigi) -$(kaynak_dosya) -$(klasor) -$(kutuphane) -$(modul) -$(ozgun_isim_uretme) -$(paket) -$(phobos) -$(takma_isim) -$(tanim) diff --git a/ddili/src/ders/d/nitelikler.d b/ddili/src/ders/d/nitelikler.d deleted file mode 100644 index 5d8fdcc..0000000 --- a/ddili/src/ders/d/nitelikler.d +++ /dev/null @@ -1,354 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX nitelik) Nitelikler) - -$(P -Nitelikler üye işlevlerin üye değişkenlermiş gibi kullanılmalarını sağlayan olanaktır. -) - -$(P -Bu olanağı dinamik dizilerden tanıyorsunuz. Dizilerin $(C length) niteliği dizideki eleman adedini bildirir: -) - ---- - int[] dizi = [ 7, 8, 9 ]; - assert(dizi$(HILITE .length) == 3); ---- - -$(P -Yalnızca bu kullanıma, yani uzunluğu bildirmesine bakarsak, $(C length)'in bir üye değişken olarak tasarlandığını düşünebiliriz: -) - ---- -struct BirDiziGerçekleştirmesi { - int length; - - // ... -} ---- - -$(P -Oysa bu niteliğin diğer kullanımı bunun doğru olamayacağını gösterir. Dinamik dizilerde $(C length) niteliğine yeni bir değer atamak dizi uzunluğunu belki de yeni elemanlar ekleyecek biçimde değiştirir: -) - ---- - dizi$(HILITE .length = 5); // Artık 5 eleman var - assert(dizi.length == 5); ---- - -$(P $(I Not: Sabit uzunluklu dizilerde $(C length) niteliği değiştirilemez.) -) - -$(P -Yukarıdaki atama basit bir değer değişikliği değildir. $(C length)'e yapılan o atamanın arkasında daha karmaşık başka işlemler gizlidir: Dizinin sığasının yeni elemanlar için yeterli olup olmadığına bakılması, gerekiyorsa daha büyük yeni bir yer ayrılması ve dizi elemanlarının o yeni yerin baş tarafına kopyalanmaları. -) - -$(P -Bu açıdan bakınca $(C length)'e yapılan atamanın aslında bir işlev gibi çalışması gerektiği görülür. -) - -$(P -$(IX @property) Nitelikler, üye değişken gibi kullanılmalarına rağmen duruma göre belki de çok karmaşık işlemleri olan işlevlerdir. Bu işlevler $(C @property) belirteciyle işaretlenerek tanımlanırlar. -) - -$(H5 $(IX ()) İşlevlerin parantezsiz çağrılabilmeleri) - -$(P -Bir önceki bölümde de değinildiği gibi, parametre değeri gerekmeyen durumlarda işlev çağırırken parantez yazmak gerekmez: -) - ---- - writeln(); - writeln; // Üsttekinin eşdeğeri ---- - -$(P -Bu olanak niteliklerle çok yakından ilgilidir. Niteliklerin kullanımında hemen hemen hiçbir zaman parantez yazılmaz. -) - -$(H5 Değer üreten nitelik işlevleri) - -$(P -Çok basit bir örnek olarak yalnızca en ve boy üyeleri bulunan bir dikdörtgen yapısına bakalım: -) - ---- -struct Dikdörtgen { - double en; - double boy; -} ---- - -$(P -Dikdörtgenin alanını bildiren bir üye daha olsun: -) - ---- - auto bahçe = Dikdörtgen(10, 20); - writeln(bahçe$(HILITE .alan)); ---- - -$(P -Şimdiye kadarki bölümlerde öğrendiğimiz kadarıyla, bunu yukarıdaki söz dizimiyle gerçekleştirebilmek için bir üçüncü üye eklememiz gerekir: -) - ---- -struct Dikdörtgen { - double en; - double boy; - double alan; -} ---- - -$(P -Bu tasarımın sakıncası, bu yapının nesnelerinin tutarsız durumlara düşebilecek olmalarıdır: Aralarında her zaman için "en * boy == alan" gibi bir ilişkinin bulunması gerektiği halde bu ilişki üyeler serbestçe değiştirildikçe bozulabilir. -) - -$(P -Hatta, nesne tamamen ilgisiz değerlerle bile kurulabilir: -) - ---- - // Tutarsız nesne: alanı 10 * 20 == 200 değil, 1111 - auto bahçe = Dikdörtgen(10, 20, $(HILITE 1111)); ---- - -$(P -İşte böyle durumları önlemenin bir yolu, alan bilgisini D'nin $(I nitelik) olanağından yararlanarak sunmaktır. Bu durumda yapıya yeni üye eklenmez, onun değeri $(C @property) olarak işaretlenmiş olan bir işlevin sonucu olarak hesaplanır. İşlevin ismi üye değişken gibi kullanılacak olan isimdir: $(C alan). Bu işlevin dönüş değeri niteliğin değeri haline gelir: -) - ---- -struct Dikdörtgen { - double en; - double boy; - - double alan() const $(HILITE @property) { - return en * boy; - } -} ---- - -$(P $(I Not: İşlev bildiriminin sonundaki $(C const), $(LINK2 /ders/d/const_uye_islevler.html, const ref Parametreler ve const Üye İşlevler bölümünden) hatırlayacağınız gibi, bu nesnenin bu işlev içinde değiştirilmediğini bildirir.) -) - -$(P -Artık o yapıyı sanki üçüncü bir üyesi varmış gibi kullanabiliriz: -) - ---- - auto bahçe = Dikdörtgen(10, 20); - writeln("Bahçenin alanı: ", bahçe$(HILITE .alan)); ---- - -$(P -Bu olanak sayesinde, $(C alan) niteliğinin değeri işlevde enin ve boyun çarpımı olarak hesaplandığı için her zaman tutarlı olacaktır: -) - -$(SHELL -Bahçenin alanı: 200 -) - -$(H5 Atama işleci ile kullanılan nitelik işlevleri) - -$(P -Dizilerin $(C length) niteliğinde olduğu gibi, kendi tanımladığımız nitelikleri de atama işlemlerinde kullanabiliriz: -) - ---- - bahçe.alan = 50; ---- - -$(P -O atamanın sonucunda alanın gerçekten değişmesi için dikdörtgenin üyelerinin, yani eninin veya boyunun değişmesi gerekir. Bunu sağlamak için dikdörtgenin $(I esnek) olduğunu kabul edebiliriz: "en * boy == alan" ilişkisini koruyabilmek için kenar uzunluklarının değişmeleri gerekir. -) - -$(P -Niteliklerin atama işleminde kullanılmalarını sağlayan işlev de $(C @property) ile işaretlenir. İşlevin ismi bu durumda da niteliğin isminin aynısıdır. Atama işleminin sağ tarafında kullanılan değer bu işlevin tek parametresinin değeri haline gelir. -) - -$(P -$(C alan) niteliğine değer atamayı da sağlayan bir tür şöyle yazılabilir: -) - ---- -import std.stdio; -import std.math; - -struct Dikdörtgen { - double en; - double boy; - - double alan() const @property { - return en * boy; - } - - $(HILITE void alan(double yeniAlan) @property) { - auto büyültme = sqrt(yeniAlan / alan); - - en *= büyültme; - boy *= büyültme; - } -} - -void main() { - auto bahçe = Dikdörtgen(10, 20); - writeln("Bahçenin alanı: ", bahçe.alan); - - $(HILITE bahçe.alan = 50); - writefln("Yeni durum: %s x %s = %s", - bahçe.en, bahçe.boy, bahçe.alan); -} ---- - -$(P -Atama işlemi ile kullanılan işlevde $(C std.math) modülünün karekök almaya yarayan işlevi olan $(C sqrt)'u kullandım. Dikdörtgenin hem eni hem de boyu oranın karekökü kadar değişince alan da yeni değere gelmiş olur. -) - -$(P -$(C alan) niteliğine yukarıda dörtte biri kadar bir değer atandığında (200 yerine 50), kenarların uzunlukları yarıya inmiş olur: -) - -$(SHELL -Bahçenin alanı: 200 -Yeni durum: 5 x 10 = 50 -) - -$(H5 Nitelikler şart değildir) - -$(P -Yukarıdaki örnekteki yapının nasıl sanki üçüncü bir üyesi varmış gibi kullanılabildiğini gördük. Ancak bu hiçbir zaman kesinlikle gerekmez çünkü değişik şekilde yazılıyor olsa da aynı işi üye işlevler yoluyla da gerçekleştirebiliriz: -) - ---- -import std.stdio; -import std.math; - -struct Dikdörtgen { - double en; - double boy; - - double $(HILITE alan()) const { - return en * boy; - } - - void $(HILITE alanDeğiştir(double yeniAlan)) { - auto büyültme = sqrt(yeniAlan / alan); - - en *= büyültme; - boy *= büyültme; - } -} - -void main() { - auto bahçe = Dikdörtgen(10, 20); - writeln("Bahçenin alanı: ", bahçe$(HILITE .alan())); - - bahçe$(HILITE .alanDeğiştir(50)); - writefln("Yeni durum: %s x %s = %s", - bahçe.en, bahçe.boy, bahçe$(HILITE .alan())); -} ---- - -$(P -Hatta, $(LINK2 /ders/d/islev_yukleme.html, İşlev Yükleme bölümünde) de anlatıldığı gibi, bu iki işlevin isimleri aynı da olabilir: -) - ---- - double alan() const { - // ... - } - - void alan(double yeniAlan) { - // ... - } ---- - -$(H5 Ne zaman kullanmalı) - -$(P -Bu bölümde anlatılan nitelik işlevleri ile daha önceki bölümlerde gördüğümüz erişim işlevleri arasında seçim yapmak her zaman kolay olmayabilir. Bazen erişim işlevleri, bazen nitelikler, bazen de ikisi birden doğal gelecektir. Niteliklerin kullanılmamaları da bir kayıp değildir. Örneğin, C++ gibi başka bazı dillerde nitelik olanağı bulunmaz. -) - -$(P -Ancak ne olursa olsun, $(LINK2 /ders/d/sarma.html, Sarma ve Erişim Hakları bölümünde) gördüğümüz gibi, üyelere doğrudan erişimin engellenmesi önemlidir. Yapı ve sınıf tasarımları zamanla geliştikçe üyelerin kullanıcı kodları tarafından doğrudan değiştirilmeleri sorun haline gelebilir. O yüzden, üye erişimlerini mutlaka nitelikler veya erişim işlevleri yoluyla sağlamanızı öneririm. -) - -$(P -Örneğin yukarıdaki $(C Dikdörtgen) yapısının $(C en) ve $(C boy) üyelerinin erişime açık bırakılmaları, yani $(C public) olmaları, ancak çok basit yapılarda kabul edilir bir davranıştır. Normalde bunun yerine ya üye işlevler, ya da nitelikler kullanılmalıdır: -) - ---- -struct Dikdörtgen { -$(HILITE private:) - - double en_; - double boy_; - -public: - - double alan() const @property { - return en * boy; - } - - void alan(double yeniAlan) @property { - auto büyültme = sqrt(yeniAlan / alan); - - en_ *= büyültme; - boy_ *= büyültme; - } - - double $(HILITE en()) const @property { - return en_; - } - - double $(HILITE boy()) const @property { - return boy_; - } -} ---- - -$(P -Üyelerin $(C private) olarak işaretlendiklerine ve o sayede değerlerine yalnızca nitelik işlevleri yoluyla erişilebildiklerine dikkat edin. -) - -$(P -Ayrıca aynı isimdeki nitelik işlevleriyle karışmasınlar diye üyelerin isimlerinin sonlarına $(C _) karakteri eklediğime dikkat edin. Üye isimlerinin bu şekilde farklılaştırılmaları nesne yönelimli programlamada oldukça sık karşılaşılan bir uygulamadır. -) - -$(P -Yukarıda da gördüğümüz gibi, üyelere erişimin nitelik işlevleri yoluyla sağlanması kullanım açısından farklılık getirmez. $(C en) ve $(C boy) yine sanki nesnenin üyeleriymiş gibi kullanılabilir: -) - ---- - auto bahçe = Dikdörtgen(10, 20); - writeln("en: ", bahçe$(HILITE .en), " boy: ", bahçe$(HILITE .boy)); ---- - -$(P -Hatta, atama işleci ile kullanılan nitelik işlevini bu üyeler için bilerek tanımlamadığımız için enin ve boyun dışarıdan değiştirilmeleri de artık olanaksızdır: -) - ---- - bahçe.en = 100; $(DERLEME_HATASI) ---- - -$(P -Bu da, üyelere yapılan değişikliklerin kendi denetimimiz altında olması açısından çok önemlidir. Bu üyeler ancak bu sınıfın kendi işlevleri tarafından değiştirilebilirler. Nesnelerin tutarlılıkları bu sayede bu türün üye işlevleri tarafından sağlanabilir. -) - -$(P -Dışarıdan değiştirilmelerinin yine de uygun olduğu üyeler varsa, atamayı sağlayan nitelik işlevi onlar için özel olarak tanımlanabilir. -) - -Macros: - SUBTITLE=Nitelikler - - DESCRIPTION=İşlev çağrılarını üye değişken yazımıyla sunan sınıf ve yapı niteliklerinin D dilinde tanımlanmaları - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar kullanıcı türleri nitelik property @property - -SOZLER= -$(genel_erisim) -$(nitelik) -$(ozel_erisim) -$(sarma) diff --git a/ddili/src/ders/d/null_ve_is.d b/ddili/src/ders/d/null_ve_is.d deleted file mode 100644 index 5f7dbbd..0000000 --- a/ddili/src/ders/d/null_ve_is.d +++ /dev/null @@ -1,293 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX null) $(IX is, işleç) $(IX !is) $(CH4 null) Değeri ve $(CH4 is) İşleci) - -$(P -Önceki bölümlerde gördüğümüz gibi, referans türünden olan değişkenler hiçbir nesneye erişim sağlamadan da oluşturulabilirler: -) - ---- - BirSınıf erişimSağlayan = new BirSınıf; - - BirSınıf değişken; // erişim sağlamayan ---- - -$(P -Bir referans türü olduğu için yukarıdaki $(C değişken)'in bir kimliği vardır; ama erişim sağladığı bir nesne henüz yoktur. Böyle bir değişkenin bellekte şu şekilde durduğunu düşünebiliriz: -) - -$(MONO - değişken - ───┬──────┬─── - │ null │ - ───┴──────┴─── -) - -$(P -Hiçbir nesneye erişim sağlamayan referansların değerleri $(C null)'dır. Bunu aşağıda anlatıyorum. -) - -$(P -Böyle bir değişken kendisine bir nesne atanana kadar kullanılamaz bir durumdadır. Doğal olarak, erişim sağladığı bir $(C BirSınıf) nesnesi olmadığı için o değişken ile işlemler yapmamız beklenemez: -) - ---- -import std.stdio; - -class BirSınıf { - int üye; -} - -void kullan(BirSınıf değişken) { - writeln(değişken.üye); $(CODE_NOTE_WRONG HATA) -} - -void main() { - BirSınıf değişken; - kullan(değişken); -} ---- - -$(P -$(C kullan) işlevine verilen değişken hiçbir nesneye erişim sağlamadığından, olmayan nesnenin $(C üye)'sine erişilmeye çalışılması programın çökmesine neden olur: -) - -$(SHELL -$ ./deneme -$(DARK_GRAY Segmentation fault) -) - -$(P -$(IX segmentation fault) "Segmentation fault", programın geçerli olmayan bir bellek bölgesine erişmeye çalıştığı için işletim sistemi tarafından acil olarak sonlandırıldığını gösterir. -) - -$(H5 $(C null) değeri) - -$(P -Erişim sağladığı nesne henüz belli olmayan referans türü değişkenleri $(C null) özel değerine sahiptir. Bu değeri de herhangi başka bir değer gibi yazdırabiliriz: -) - ---- - writeln(null); ---- - -$(P -Çıktısı: -) - -$(SHELL -null -) - -$(P -Değeri $(C null) olan bir değişken çok kısıtlı sayıda işlemde kullanılabilir: -) - -$(OL -$(LI Erişim sağlaması için geçerli bir nesne atamak - ---- - değişken = new BirSınıf; // artık nesnesi var ---- - -$(P -O atamadan sonra artık $(C değişken)'in erişim sağladığı bir nesne vardır. $(C değişken) artık $(C BirSınıf) işlemleri için kullanılabilir. -) - -) - -$(LI $(C null) olup olmadığını denetlemek - ---- - if (değişken == null) $(DERLEME_HATASI) ---- - -$(P -Ne yazık ki, $(C ==) işleci asıl nesneleri karşılaştırdığı için; ve $(C null) bir değişkenin eriştirdiği geçerli bir nesne olmadığı için, o ifade derlenemez. -) - -$(P -Bu yüzden, bir değişkenin $(C null) olup olmadığını denetlemek için $(C is) işleci kullanılır. -) - -) - -) - -$(H5 $(C is) işleci) - -$(P -$(C is), İngilizce'de "olmak" fiilinin "öyledir" kullanımındaki anlamına sahiptir. Bu bölümü ilgilendiren kullanımında ikili bir işleçtir, yani sol ve sağ tarafına iki değer alır. Bu iki değer aynıysa $(C true), değilse $(C false) üretir. -) - -$(P $(I Not: $(C is)'in örneğin şablon olanağında tekli işleç olarak kullanıldığı durumlar da vardır.) -) - -$(P -İki değerden birisinin $(C null) olabildiği durumlarda $(C ==) işlecinin kullanılamadığını yukarıda gördük. Onun yerine $(C is)'i kullanmak gerekir. "Bu değişken null ise" koşulunu denetlemeye yarar: -) - ---- - if (değişken $(HILITE is) null) { - // hiçbir nesneye erişim sağlamıyor - } ---- - -$(P -$(C is), başka türlerle de kullanılabilir. Örneğin iki tamsayı değişkenin değerleri şöyle karşılaştırılabilir: -) - ---- - if (hız is yeniHız) { - // ikisi aynı değerde - - } else { - // ikisi farklı değerde - } ---- - -$(P -Dilimlerde de iki dilimin aynı elemanlara erişim sağlayıp sağlamadıklarını denetler: -) - ---- - if (dilim is dilim2) { - // aynı elemanları paylaşıyorlar - } ---- - -$(H5 $(C !is) işleci) - -$(P -$(C ==) ve $(C !=) işleçlerine benzer şekilde, $(C is)'in tersi $(C !is) işlecidir. Değerler eşit olmadığında $(C true) üretir: -) - ---- - if (hız !is yeniHız) { - // farklı değerlere sahipler - } ---- - -$(H5 $(C null) değer atamak) - -$(P -Referans türünden olan bir değişkene $(C null) değerini atamak, o değişkenin artık hiçbir nesneye erişim sağlamamasına neden olur. -) - -$(P -Eğer bu atama sonucunda asıl nesneye erişen başka referans değişkeni kalmamışsa, asıl nesne çöp toplayıcı tarafından sonlandırılacaktır. Hiçbir referans tarafından erişilmiyor olması, o nesnenin artık kullanılmadığını gösterir. -) - -$(P -Örnek olarak, $(LINK2 /ders/d/deger_referans.html, önceki bir bölümde) gördüğümüz iki değişkenin aynı nesneye eriştikleri duruma bakalım: -) - ---- - auto değişken = new BirSınıf; - auto değişken2 = değişken; ---- - -$(MONO - (isimsiz BirSınıf nesnesi) değişken değişken2 - ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── - │ ... │ │ o │ │ o │ - ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── - ▲ │ │ - │ │ │ - └────────────────────┴────────────┘ -) - -$(P -Bu değişkenlerden birisine $(C null) atamak, onun bu değerle ilişkisini keser: -) - ---- - değişken = null; ---- - -$(P -$(C BirSınıf) nesnesine artık yalnızca $(C değişken2) tarafından erişilmektedir: -) - -$(MONO - (isimsiz BirSınıf nesnesi) değişken değişken2 - ───┬───────────────────┬─── ───┬────┬─── ───┬───┬─── - │ ... │ │null│ │ o │ - ───┴───────────────────┴─── ───┴────┴─── ───┴─│─┴─── - ▲ │ - │ │ - └──────────────────────────────────┘ -) - -$(P -İsimsiz $(C BirSınıf) nesnesine erişen son referans olan $(C değişken2)'ye de $(C null) atanması, asıl nesnenin sonlanmasına neden olur: -) - ---- - değişken2 = null; ---- - -$(P -Çöp toplayıcı asıl nesneyi türüne göre ya hemen, ya da ilerideki bir zamanda sonlandıracaktır. Program açısından artık o nesne yoktur çünkü o nesneye erişen referans kalmamıştır: -) - -$(MONO - değişken değişken2 - ───┬───────────────────┬─── ───┬────┬─── ───┬────┬─── - │ │ │null│ │null│ - ───┴───────────────────┴─── ───┴────┴─── ───┴────┴── -) - -$(P -$(LINK2 /ders/d/esleme_tablolari.html, Eşleme tabloları bölümünün) birinci problemi, bir eşleme tablosunu boşaltan üç yöntem gösteriyordu. Şimdi o yöntemlere bir dördüncüsünü ekleyebiliriz; eşleme tablosu değişkenine $(C null) değer atamak, değişkenin erişim sağladığı asıl tablo ile ilişkisini keser: -) - ---- - string[int] isimleSayılar; - // ... - isimleSayılar = null; // artık hiçbir elemana erişim - // sağlamaz ---- - -$(P -Yukarıdaki $(C BirSınıf) örneğine benzer şekilde, eğer $(C isimleSayılar) asıl tabloya erişim sağlayan son referans idiyse, asıl tablonun elemanları çöp toplayıcı tarafından sonlandırılacaklardır. -) - -$(P -Bir dilimin de artık hiçbir elemana erişim sağlaması istenmiyorsa $(C null) atanabilir: -) - ---- - int[] dilim = dizi[ 10 .. 20 ]; - // ... - dilim = null; // artık hiçbir elemana erişim sağlamaz ---- - -$(H5 Özet) - -$(UL -$(LI $(C null), hiçbir değere erişim sağlamayan referans değeridir) -$(LI $(C null) referanslar yalnızca iki işlemde kullanılabilirler: değer atamak, $(C null) olup olmadığını denetlemek) -$(LI $(C ==) işleci asıl nesneye erişmeyi gerektirebileceği için, $(C null) olma olasılığı bulunan referanslar $(C is) ile denetlenmelidir) -$(LI $(C is)'in tersi $(C !is)'dir) -$(LI $(C null) atanan referans artık hiçbir elemana erişim sağlamaz) -$(LI Hiçbir referansın erişim sağlamadığı nesneler çöp toplayıcı tarafından sonlandırılırlar) -) - -Macros: - SUBTITLE=null ve is - - DESCRIPTION=D dilinde hiçbir nesneye erişim sağlamayan değer null, ve bu değeri denetleyen is ve !is işleçleri. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial nesne referans türü null is !is - -SOZLER= -$(cokme) -$(cop_toplayici) -$(deger_turu) -$(dilim) -$(esleme_tablosu) -$(nesne) -$(referans_turu) -$(sonlandirici) -$(sablon) diff --git a/ddili/src/ders/d/object.cozum.d b/ddili/src/ders/d/object.cozum.d deleted file mode 100644 index d1d14cb..0000000 --- a/ddili/src/ders/d/object.cozum.d +++ /dev/null @@ -1,109 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(CH4 Object)) - -$(OL - -$(LI -Eşitlik karşılaştırmasında öncelikle $(C sağdaki)'nin $(C null) olmadığına ve yalnızca $(C x) ve $(C y) üyelerine bakmak yeterli olur: - ---- -enum Renk { mavi, yeşil, kırmızı } - -class Nokta { - int x; - int y; - Renk renk; - -// ... - - override bool opEquals(Object o) const { - const sağdaki = cast(const Nokta)o; - - return (sağdaki && - (x == sağdaki.x) && - (y == sağdaki.y)); - } -} ---- - -) - -$(LI -Sağdaki nesnenin türü de $(C Nokta) olduğunda önce $(C x)'e sonra $(C y)'ye göre karşılaştırılıyor: - ---- -class Nokta { - int x; - int y; - Renk renk; - -// ... - - override int opCmp(Object o) const { - const sağdaki = cast(const Nokta)o; - enforce(sağdaki); - - return (x != sağdaki.x - ? x - sağdaki.x - : y - sağdaki.y); - } -} ---- - -) - -$(LI -Aşağıdaki $(C opCmp) içinde tür dönüştürürken $(C const ÜçgenBölge) yazılamadığına dikkat edin. Bunun nedeni, $(C sağdaki)'nin türü $(C const ÜçgenBölge) olduğunda onun üyesi olan $(C sağdaki.noktalar)'ın da $(C const) olacağı ve $(C const) değişkenin $(C nokta.opCmp)'a parametre olarak gönderilemeyeceğidir. ($(C opCmp)'ın parametresinin $(C const Object) değil, $(C Object) olduğunu hatırlayın.) - ---- -class ÜçgenBölge { - Nokta[3] noktalar; - - this(Nokta bir, Nokta iki, Nokta üç) { - noktalar = [ bir, iki, üç ]; - } - - override bool opEquals(Object o) const { - const sağdaki = cast(const ÜçgenBölge)o; - return sağdaki && (noktalar == sağdaki.noktalar); - } - - override int opCmp(Object o) const { - $(HILITE auto) sağdaki = $(HILITE cast(ÜçgenBölge))o; - enforce(sağdaki); - - foreach (i, nokta; noktalar) { - immutable karşılaştırma = - nokta.opCmp(sağdaki.noktalar[i]); - - if (karşılaştırma != 0) { - /* Sıralamaları bu noktada belli oldu. */ - return karşılaştırma; - } - } - - /* Buraya kadar gelinmişse eşitler demektir. */ - return 0; - } - - override size_t toHash() const { - /* 'noktalar' üyesini bir dizi olarak tanımladığımız - * için dizilerin toHash algoritmasından - * yararlanabiliriz. */ - return typeid(noktalar).getHash(&noktalar); - } -} ---- - -) - -) - - -Macros: - SUBTITLE=Object Problem Çözümleri - - DESCRIPTION=Object Problem Çözümleri - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial object problem çözüm diff --git a/ddili/src/ders/d/object.d b/ddili/src/ders/d/object.d deleted file mode 100644 index 96c8cdb..0000000 --- a/ddili/src/ders/d/object.d +++ /dev/null @@ -1,891 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX Object) $(CH4 Object)) - -$(P -Açıkça başka bir sınıftan türetilmeyen sınıflar otomatik olarak $(C Object) adlı sınıftan türerler. -) - -$(P -Sıradüzenin en üstündeki sınıf $(C Object)'ten otomatik olarak türer: -) - ---- -class Çalgı $(DEL : Object ) { // ": Object" yazılmaz; otomatiktir - // ... -} - -class TelliÇalgı : Çalgı { // dolaylı olarak Object'ten türer - // ... -} ---- - -$(P -En üstteki sınıf $(C Object)'ten türediği için, altındaki bütün sınıflar da dolaylı olarak $(C Object)'ten türerler. Bu anlamda "her sınıf, $(C Object) türündendir". -) - -$(P -Bu türeme sonucunda her sınıf $(C Object)'in bazı üye işlevlerini edinir: -) - -$(UL -$(LI $(C toString): Nesnenin dizgi olarak ifadesi.) -$(LI $(C opEquals): Eşitlik karşılaştırması.) -$(LI $(C opCmp): Sıra karşılaştırması.) -$(LI $(C toHash): Eşleme tablosu indeks değeri.) -) - -$(P -Bu işlevlerden son üçü sınıf nesnelerinin değerlerini ön plana çıkartmak ve onları eşleme tablolarında indeks türü olarak kullanmak için gereklidir. -) - -$(P -Türeme yoluyla edinildikleri için bu işlevlerin türeyen tür için $(C override) anahtar sözcüğü ile tanımlanmaları gerekir. -) - -$(P $(I Not: $(C Object)'ten edinilen başka üyeler de vardır. Bu bölümde yalnızca bu dört işlevini göreceğiz.) -) - -$(H5 $(IX typeid) $(IX TypeInfo) $(C typeid) ve $(C TypeInfo)) - -$(P -$(C Object) sınıfı, $(C std) pakedinin parçası olmayan $(LINK2 http://dlang.org/phobos/object.html, $(C object)) modülünde tanımlanmıştır. Bu modül türler hakkında bilgi taşıyan $(C TypeInfo) sınıfını da tanımlar. Programın çalışması sırasında her farklı tür için farklı bir $(C TypeInfo) nesnesi vardır. $(C typeid) $(I ifadesi) belirli bir türe karşılık gelen $(C TypeInfo) nesnesine erişim sağlar. Biraz aşağıda göreceğimiz gibi, $(C TypeInfo) sınıfı türlerin eşit olup olmadıklarını belirlemek yanında türlerin özel işlevlerine (çoğu bu kitapta gösterilmeyecek olan $(C toHash), $(C postblit), vs.) de erişim sağlar. -) - -$(P -$(C TypeInfo) her zaman için çalışma zamanındaki asıl tür ile ilgilidir. Örneğin, aşağıdaki $(C Kemençe) ve $(C Saz) doğrudan $(C TelliÇalgı) sınıfından ve dolaylı olarak $(C Çalgı) sınıfından türemiş olsalar da ikisinin $(C TypeInfo) nesneleri farklıdır: -) - ---- -class Çalgı { -} - -class TelliÇalgı : Çalgı { -} - -class Kemençe : TelliÇalgı { -} - -class Saz : TelliÇalgı { -} - -void main() { - TypeInfo k = $(HILITE typeid)(Kemençe); - TypeInfo s = $(HILITE typeid)(Saz); - assert(k != s); $(CODE_NOTE türler aynı değil) -} ---- - -$(P -Yukarıdaki kullanımlarda $(C typeid)'ye $(I türün kendisi) verilmektedir ($(C Kemençe) gibi). $(C typeid) diğer kullanımında $(I ifade) alır ve o ifadenin değerinin türü ile ilgili olan $(C TypeInfo) nesnesini döndürür. Örneğin, aşağıdaki işlev birbirleriyle ilgili olsalar da farklı türden olan iki parametre almaktadır: -) - ---- -import std.stdio; - -// ... - -void foo($(HILITE Çalgı) ç, $(HILITE TelliÇalgı) t) { - const aynı_mı = (typeid(ç) == typeid(t)); - - writefln("Türleri %s.", aynı_mı ? "aynı" : "aynı değil"); -} - -// ... - - auto a = new $(HILITE Kemençe)(); - auto b = new $(HILITE Kemençe)(); - foo(a, b); ---- - -$(P -Yukarıdaki $(C foo) çağrısı için gönderilen asıl parametre değerlerinin ikisi de $(C Kemençe) türünden olduklarından $(C foo) onların aynı türden olduklarını belirler: -) - -$(SHELL -Türleri aynı. -) - -$(P -Kendilerine verilen ifadeleri işletmeyen $(C .sizeof) ve $(C typeof)'un aksine, $(C typeid) verilen ifadeyi işletmek zorundadır: -) - ---- -import std.stdio; - -int foo(string bilgi) { - writefln("'%s' sırasında çağrıldı.", bilgi); - return 0; -} - -void main() { - const s = foo("sizeof")$(HILITE .sizeof); // foo() çağrılmaz - alias T = $(HILITE typeof)(foo("typeof")); // foo() çağrılmaz - auto ti = $(HILITE typeid)(foo("typeid")); // foo() çağrılır -} ---- - -$(P -Programın çıktısında görüldüğü gibi, yalnızca $(C typeid)'nin ifadesi işletilmiştir: -) - -$(SHELL -'typeid' sırasında çağrıldı. -) - -$(P -Bu farkın nedeni, bazı ifadelerin asıl türlerinin ancak o ifadeler işletildikten sonra bilinebilmesidir. Örneğin, aşağıdaki işlevin asıl dönüş türü aldığı parametre değerine bağlı olarak her çağrıda ya $(C Kemençe) ya da $(C Saz) olacaktır: -) - ---- -Çalgı foo(int i) { - return ($(HILITE i) % 2) ? new Kemençe() : new Saz(); -} ---- - -$(H5 $(IX toString) $(C toString)) - -$(P -Yapılarda olduğu gibi, $(C toString) sınıf nesnelerinin dizgi olarak kullanılmalarını sağlar: -) - ---- - const saat = new Saat(20, 30, 0); - writeln(saat); // saat.toString()'i çağırır ---- - -$(P -Sınıfın $(C Object)'ten kalıtım yoluyla edindiği $(C toString) işlevi fazla kullanışlı değildir; döndürdüğü $(C string) yalnızca türün ismini içerir: -) - -$(SHELL -deneme.Saat -) - -$(P -Sınıfın isminden önceki bölüm, yani yukarıdaki $(C deneme), o sınıfı içeren modülün ismini belirtir. Ona bakarak, $(C Saat) sınıfının $(C deneme.d) isimli bir kaynak dosya içinde tanımlandığını anlayabiliriz. -) - -$(P -Önceki bölümde olduğu gibi, anlamlı bir $(C string) üretmesi için bu işlev hemen hemen her zaman için özel olarak tanımlanır: -) - ---- -import std.string; - -class Saat { - override string toString() const { - return format("%02s:%02s:%02s", saat, dakika, saniye); - } - - // ... -} - -class ÇalarSaat : Saat { - override string toString() const { - return format("%s ♫%02s:%02s", super.toString(), - alarmSaati, alarmDakikası); - } - - // ... -} - -// ... - - auto başucuSaati = new ÇalarSaat(20, 30, 0, 7, 0); - writeln(başucuSaati); ---- - -$(P -Çıktısı: -) - -$(SHELL -20:30:00 ♫07:00 -) - -$(H5 $(IX opEquals) $(C opEquals)) - -$(P -$(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünde) gördüğümüz gibi, bu üye işlev $(C ==) işlecinin tanımını belirler (ve dolaylı olarak $(C !=) işlecinin tanımını). İşlevin dönüş değeri nesneler eşitlerse $(C true), değillerse $(C false) olmalıdır. -) - -$(P -$(B Uyarı:) Bu işlevin $(C opCmp) ile tutarlı olması gerekir; $(C true) döndürdüğü durumda $(C opCmp) da sıfır döndürmelidir. -) - -$(P -Yapıların aksine, derleyici $(C a == b) gibi bir ifadeyi otomatik olarak $(C a.opEquals(b)) ifadesine dönüştürmez. İki sınıf nesnesi karşılaştırıldıklarında aşağıdaki dört adımlık algoritma uygulanır: -) - ---- -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 İki değişken de aynı nesneye erişim sağlıyorlarsa (veya ikisi de $(C null) iseler) eşittirler.) - -$(LI Yalnızca birisi $(C null) ise eşit değildirler.) - -$(LI Her iki nesne de aynı türden iseler ve o türün $(C opEquals) işlevi tanımlanmışsa $(C a.opEquals(b)) işletilir.) - -$(LI Aksi taktirde, eşit olarak kabul edilebilmeleri için eğer tanımlanmışlarsa hem $(C a.opEquals(b))'nin hem de $(C b.opEquals(a))'nın $(C true) üretmeleri gerekir.) - -) - -$(P -Dolayısıyla, $(C opEquals) programcı tarafından özellikle tanımlanmamışsa o sınıfın nesnelerinin değerlerine bakılmaz; iki sınıf değişkeninin aynı nesneye erişim sağlayıp sağlamadıklarına bakılır: -) - ---- - auto değişken0 = new Saat(6, 7, 8); - auto değişken1 = new Saat(6, 7, 8); - - assert(değişken0 != değişken1); // eşit değiller - // (çünkü farklı nesneler) ---- - -$(P -Yukarıdaki koddaki iki nesne aynı parametre değerleriyle kuruldukları halde, $(C new) ile ayrı ayrı kurulmuş oldukları için iki farklı nesnedir. Bu yüzden, onlara erişim sağlayan $(C değişken0) ve $(C değişken1) değişkenleri $(C Object)'in gözünde $(I eşit değillerdir). -) - -$(P -Öte yandan, aynı nesneye erişim sağladıkları için şu iki değişken $(I eşittir): -) - ---- - auto ortak0 = new Saat(9, 10, 11); - auto ortak1 = ortak0; - - assert(ortak0 == ortak1); // eşitler - // (çünkü aynı nesne) ---- - -$(P -Bazen nesneleri böyle kimliklerine göre değil, değerlerine göre karşılaştırmak isteriz. Örneğin $(C değişken0)'ın ve $(C değişken1)'in erişim sağladıkları nesnelerin değerlerinin eşit olmalarına bakarak, $(C ==) işlecinin $(C true) üretmesini bekleyebiliriz. -) - -$(P -Yapılardan farklı olarak, ve $(C Object)'ten kalıtımla edinildiği için, $(C opEquals) işlevinin parametresi $(C Object)'tir. O yüzden, bu işlevi kendi sınıfımız için tanımlarken parametresini $(C Object) olarak yazmamız gerekir: -) - ---- -class Saat { - override bool opEquals($(HILITE Object o)) const { - // ... - } - - // ... -} ---- - -$(P -Kendimiz $(C Object) olarak doğrudan kullanmayacağımız için bu parametrenin ismini kullanışsız olarak $(C o) diye seçmekte bir sakınca görmüyorum. İlk ve çoğu durumda da tek işimiz, onu bir tür dönüşümünde kullanmak olacak. -) - -$(P -$(C opEquals)'a parametre olarak gelen nesne, kod içinde $(C ==) işlecinin sağ tarafında yazılan nesnedir. Örneğin şu iki satır birbirinin eşdeğeridir: -) - ---- - değişken0 == değişken1; // o, değişken1'i temsil eder ---- - -$(P -Bu işleçteki amaç bu sınıftan iki nesneyi karşılaştırmak olduğu için, işlevi tanımlarken yapılması gereken ilk şey, parametre olarak gelen $(C Object)'in türünü kendi sınıfımızın türüne dönüştürmektir. Sağdaki nesneyi değiştirmek gibi bir niyetimiz de olmadığı için, tür dönüşümünde $(C const) belirtecini kullanmak da uygun olur: -) - ---- - override bool opEquals(Object o) const { - const sağdaki = cast(const Saat)o; - - // ... - } ---- - -$(P -Hatırlayacağınız gibi, tür dönüşümü için $(C std.conv.to) işlevi de kullanılabilir: -) - ---- -import std.conv; -// ... - const sağdaki = to!(const Saat)(o); ---- - -$(P -Yukarıdaki tür dönüşümü işlemi ya $(C sağdaki)'nin türünü bu şekilde $(C const Saat) olarak belirler ya da dönüşüm uyumsuzsa $(C null) üretir. -) - -$(P -Burada karar verilmesi gereken önemli bir konu, sağdaki nesnenin türünün bu nesnenin türü ile aynı olmadığında ne olacağıdır. Sağdaki nesnenin tür dönüşümü sonucunda $(C null) üretmesi, sağdaki nesnenin aslında bu türe dönüştürülemediği anlamına gelir. -) - -$(P -Ben nesnelerin eşit kabul edilebilmeleri için bu dönüşümün başarılı olması gerektiğini varsayacağım. Bu yüzden eşitlik karşılaştırmalarında öncelikle $(C sağdaki)'nin $(C null) olmadığına bakacağım. Zaten $(C null) olduğu durumda $(C sağdaki)'nin üyelerine erişmek hatalıdır: -) - ---- -class Saat { - int saat; - int dakika; - int saniye; - - override bool opEquals(Object o) const { - const sağdaki = cast(const Saat)o; - - return ($(HILITE sağdaki) && - (saat == sağdaki.saat) && - (dakika == sağdaki.dakika) && - (saniye == sağdaki.saniye)); - } - - // ... -} ---- - -$(P -İşlevin bu tanımı sayesinde, $(C ==) işleci $(C Saat) nesnelerini artık değerlerine göre karşılaştırır: -) - ---- - auto değişken0 = new Saat(6, 7, 8); - auto değişken1 = new Saat(6, 7, 8); - - assert(değişken0 == değişken1); // artık eşitler - // (çünkü değerleri aynı) ---- - -$(P -$(C opEquals)'ı tanımlarken, eğer varsa ve nesnelerin eşit kabul edilmeleri için gerekliyse, üst sınıfın üyelerini de unutmamak gerekir. Örneğin alt sınıf olan $(C ÇalarSaat)'in nesnelerini karşılaştırırken, $(C Saat)'ten kalıtımla edindiği parçaları da karşılaştırmak anlamlı olur: -) - ---- -class ÇalarSaat : Saat { - int alarmSaati; - int alarmDakikası; - - override bool opEquals(Object o) const { - const sağdaki = cast(const ÇalarSaat)o; - - return (sağdaki && - (alarmSaati == sağdaki.alarmSaati) && - (alarmDakikası == sağdaki.alarmDakikası) && - $(HILITE super.opEquals(o))); - } - - // ... -} ---- - -$(P -Oradaki ifade $(C super)'in $(C opEquals) işlevini çağırır ve eşitlik kararında onun da sonucunu kullanmış olur. Onun yerine daha kısaca $(C super == o) da yazılabilir. Ancak, öyle yazıldığında yukarıdaki dört adımlı algoritma tekrar işletileceğinden kod biraz daha yavaş olabilir. -) - -$(H5 $(IX opCmp) $(C opCmp)) - -$(P -Sınıf nesnelerini sıralamak için kullanılır. $(C <), $(C <=), $(C >), ve $(C >=) işleçlerinin tanımı için perde arkasında bu işlev kullanılır. -) - -$(P -Bu işlevin dönüş değerini $(C <) işleci üzerinde düşünebilirsiniz: Soldaki nesne önce olduğunda eksi bir değer, sağdaki nesne önce olduğunda artı bir değer, ikisi eşit olduklarında sıfır döndürmelidir. -) - -$(P -$(B Uyarı:) Bu işlevin $(C opEquals) ile tutarlı olması gerekir; sıfır döndürdüğü durumda $(C opEquals) da $(C true) döndürmelidir. -) - -$(P -$(C toString)'in ve $(C opEquals)'un aksine, bu işlevin $(C Object) sınıfından kalıtımla edinilen bir davranışı yoktur. Tanımlanmadan kullanılırsa hata atılır: -) - ---- - auto değişken0 = new Saat(6, 7, 8); - auto değişken1 = new Saat(6, 7, 8); - - assert(değişken0 <= değişken1); $(CODE_NOTE Hata atılır) ---- - -$(SHELL -object.Exception: need opCmp for class deneme.Saat -) - -$(P -Yukarıda $(C opEquals) için söylenenler bu işlev için de geçerlidir: Sağdaki nesnenin türünün bu nesnenin türüne eşit olmadığı durumda hangisinin daha önce sıralanması gerektiği konusuna bir şekilde karar vermek gerekir. -) - -$(P -Bunun en kolayı bu kararı derleyiciye bırakmaktır, çünkü derleyici türler arasında zaten genel bir sıralama belirler. Türler aynı olmadıklarında bu sıralamadan yararlanmanın yolu, $(C typeid)'lerinin $(C opCmp) işlevinden yararlanmaktır: -) - ---- -class Saat { - int saat; - int dakika; - int saniye; - - override int opCmp(Object o) const { - /* Türler aynı olmadıklarında türlerin genel - * sıralamasından yararlanıyoruz. */ - if (typeid(this) != typeid(o)) { - return typeid(this).opCmp(typeid(o)); - } - - const sağdaki = cast(const Saat)o; - /* sağdaki'nin null olup olmadığına bakmaya gerek yok - * çünkü buraya gelinmişse 'o' ile aynı türdendir. */ - - if (saat != sağdaki.saat) { - return saat - sağdaki.saat; - - } else if (dakika != sağdaki.dakika) { - return dakika - sağdaki.dakika; - - } else { - return saniye - sağdaki.saniye; - } - } - - // ... -} ---- - -$(P -Yukarıdaki tanım, nesneleri sıralama amacıyla karşılaştırırken öncelikle türlerinin uyumlu olup olmadıklarına bakıyor. Eğer uyumlu iseler saat bilgisini dikkate alıyor; saatler eşitlerse dakikalara, onlar da eşitlerse saniyelere bakıyor. -) - -$(P -Ne yazık ki, bu işlevin bu gibi karşılaştırmalarda daha güzel veya daha etkin bir yazımı yoktur. Eğer daha uygun bulursanız, if-else-if zinciri yerine onun eşdeğeri olan üçlü işleci de kullanabilirsiniz: -) - ---- - override int opCmp(Object o) const { - if (typeid(this) != typeid(o)) { - return typeid(this).opCmp(typeid(o)); - } - - const sağdaki = cast(const Saat)o; - - return (saat != sağdaki.saat - ? saat - sağdaki.saat - : (dakika != sağdaki.dakika - ? dakika - sağdaki.dakika - : saniye - sağdaki.saniye)); - } ---- - -$(P -Bu işlevi bir alt sınıf için tanımlarken ve karşılaştırmada önemi varsa, üst sınıfını da unutmamak gerekir. Örneğin, aşağıdaki $(C ÇalarSaat.opCmp) sıralama kararında öncelikle üst sınıfından yararlanıyor: -) - ---- -class ÇalarSaat : Saat { - override int opCmp(Object o) const { - const sağdaki = cast(const ÇalarSaat)o; - - const int üstSonuç = $(HILITE super.opCmp(o)); - - if (üstSonuç != 0) { - return üstSonuç; - - } else if (alarmSaati != sağdaki.alarmSaati) { - return alarmSaati - sağdaki.alarmSaati; - - } else { - return alarmDakikası - sağdaki.alarmDakikası; - } - } - - // ... -} ---- - -$(P -Üst sınıfın sıfırdan farklı bir değer döndürmesi durumunda, iki nesnenin sıraları ile ilgili yeterli bilgi edinilmiştir; ve o değer döndürülür. Yukarıdaki kodda, alt sınıfın üyelerine ancak üst sınıf parçaları eşit çıktığında bakılmaktadır. -) - -$(P -Artık bu türün nesneleri sıralama karşılaştırmalarında kullanılabilir: -) - ---- - auto çs0 = new ÇalarSaat(8, 0, 0, 6, 30); - auto çs1 = new ÇalarSaat(8, 0, 0, 6, 31); - - assert(çs0 < çs1); ---- - -$(P -O kodda diğer bütün üyeleri eşit olduğu için, $(C çs0) ve $(C çs1)'in nasıl sıralanacaklarını en son bakılan alarm dakikası belirler. -) - -$(P -Bu işlev yalnızca kendi yazdığımız kodlarda kullanılmak için değildir. Programda kullandığımız kütüphaneler ve dil olanakları da bu işlevi çağırabilir. Örneğin bir dizi içindeki nesnelerin $(C sort) ile sıralanmalarında, veya sınıfın bir eşleme tablosunda indeks türü olarak kullanılmasında da perde arkasında bu işlevden yararlanılır. -) - -$(H6 Dizgi türünden olan üyeler için $(C opCmp)) - -$(P -Dizgi türündeki üyeler için $(C opCmp) işlevini eksi, sıfır, veya artı döndürecek şekilde uzun uzun şöyle yazabilirsiniz: -) - ---- -import std.exception; - -class Öğrenci { - string isim; - - override int opCmp(Object o) const { - const sağdaki = cast(Öğrenci)o; - enforce(sağdaki); - - if (isim < sağdaki.isim) { - return -1; - - } else if (isim > sağdaki.isim) { - return 1; - - } else { - return 0; - } - } - - // ... -} ---- - -$(P -Onun yerine, $(C std.algorithm) modülünde tanımlanmış olan ve aynı karşılaştırmayı daha hızlı olarak gerçekleştiren $(C cmp) işlevini de kullanabilirsiniz: -) - ---- -import std.algorithm; - -class Öğrenci { - string isim; - - override int opCmp(Object o) const { - const sağdaki = cast(Öğrenci)o; - enforce(sağdaki); - - return cmp(isim, sağdaki.isim); - } - - // ... -} ---- - -$(P -Bu türün, kendisiyle uyumsuz olan türlerle sıra karşılaştırılmasında kullanılmasına izin vermediğine dikkat edin. Bu denetimi $(C Object)'ten $(C Öğrenci)'ye tür dönüşümünün başarılı olmasına $(C enforce) ile bakarak sağlıyor. -) - -$(H5 $(IX toHash) $(C toHash)) - -$(P -Bu işlev, sınıfın eşleme tablolarında indeks türü olarak kullanılabilmesini sağlar. Eşleme tablosunun eleman türü olarak kullanıldığı durumda bir etkisi yoktur. -) - -$(P -$(B Uyarı:) Yalnızca bu işlevi tanımlamak yetmez. Bu işlevin eşleme tablolarında doğru olarak kullanılabilmesi için $(C opEquals) ve $(C opCmp) işlevlerinin de birbirleriyle tutarlı olarak tanımlanmış olmaları gerekir. -) - -$(H6 $(IX eşleme tablosu) Eşleme tablosu indeks değerleri) - -$(P -Eşleme tabloları eleman erişimini çok hızlı şekilde gerçekleştiren veri yapılarıdır. Üstelik bunu, tabloda ne kadar eleman bulunduğundan bağımsız olarak yapabilirler. ($(I Not: Her şeyin olduğu gibi bu hızın da bir bedeli vardır: elemanları sırasız olarak tutmak zorundadırlar, ve kesinlikle gereken miktardan daha fazla bellek kullanıyor olabilirler.)) -) - -$(P -Eşleme tablolarının bu hızı, indeks olarak kullanılan türü önce $(I hash) denen bir tamsayı değere çevirmelerinden kaynaklanır. Bu tamsayıyı kendilerine ait bir dizinin indeksi olarak kullanırlar. -) - -$(P -Bu yöntemin hızdan başka bir yararı, tamsayıya dönüştürülebilen her türün eşleme tablosu indeks türü olarak kullanılabilmesidir. -) - -$(P -$(C toHash), sınıf nesnelerinin bu amaç için indeks değerleri döndürmelerini sağlar. -) - -$(P -Bu sayede, pek mantıklı olmasa da, $(C Saat) türünü bile indeks olarak kullanabiliriz: -) - ---- - string[$(HILITE Saat)] zamanİsimleri; - - zamanİsimleri[new Saat(12, 0, 0)] = "öğleni gösteren saat"; ---- - -$(P -$(C Object)'ten kalıtım yoluyla edinilen $(C toHash) işlevi, farklı nesneler için farklı indeks değerleri üretecek şekilde tanımlanmıştır. Bu, $(C opEquals)'un farklı nesnelerin eşit olmadıklarını kabul etmesine benzer. -) - -$(P -Yukarıdaki kod $(C Saat) sınıfı için özel bir $(C toHash) işlevi tanımlanmamış olsa bile derlenir; ama istediğimiz gibi çalışmaz. Yukarıdaki tabloya eklenmiş olan $(C Saat) nesnesi ile aynı değere sahip olan, ama ondan farklı bir $(C Saat) nesnesi ile erişmek istesek; doğal olarak tablodaki "öğleni gösteren saat" değerini bulmayı bekleriz: -) - ---- - if (new Saat(12, 0, 0) in zamanİsimleri) { - writeln("var"); - - } else { - writeln("yok"); - } ---- - -$(P -Ne yazık ki, oradaki $(C in) işleci $(C false) döndürür; yani bu nesnenin tabloda bulunmadığını belirtir: -) - -$(SHELL -yok -) - -$(P -Bunun nedeni, yerleştirilirken kullanılan nesne ile erişirken kullanılan nesnenin $(C new) ile ayrı ayrı oluşturulmuş olmalarıdır; yani ikisi farklı nesnelerdir. -) - -$(P -Dolayısıyla; $(C Object)'ten kalıtımla edinilen $(C toHash), eşleme tablolarında indeks değeri olarak kullanılmaya çoğu durumda elverişli değildir. $(C toHash)'i, bir tamsayı indeks döndürecek şekilde bizim yazmamız gerekir. -) - -$(H6 $(C toHash) için seçilecek üyeler) - -$(P -İndeks değeri, nesnenin üyeleri kullanılarak hesaplanır. Ancak, her üye bu indeks hesabına uygun değildir. -) - -$(P -Bunun için seçilecek üyeler, nesneyi diğer nesnelerden ayırt etmeye yarayan üyeler olmalıdır. Örneğin $(C Öğrenci) gibi bir sınıfın $(C isim) ve $(C soyad) üyelerinin ikisi birden nesneleri ayırt etmek için kullanılabilir; çünkü bu iki üyenin her nesnede farklı olduğunu düşünebiliriz. (İsim benzerliklerini gözardı ediyorum.) -) - -$(P -Öte yandan, $(C Öğrenci) sınıfının $(C notlar) dizisi uygun değildir; çünkü hem birden fazla nesnede aynı not değerleri bulunabilir; hem de aynı öğrencinin notları zamanla değişebilir. -) - -$(H6 İndeks değerinin hesaplanması) - -$(P -İndeks değerinin hesabı eşleme tablosunun hızını doğrudan etkiler. Üstelik, her hesap her çeşit veri üzerinde aynı derece etkili değildir. Uygun hesaplama yöntemleri bu kitabın kapsamı dışında kaldığı için bu konunun ayrıntısına girmeyeceğim ve genel bir ilke vermekle yetineceğim: Genel olarak, değerlerinin farklı oldukları kabul edilen nesnelerin farklı indeks değerlerinin olması etkinlik açısından iyidir. Farklı değerli nesnelerin aynı indeks değerini üretmeleri hata değildir; performans açısından istenmeyen bir durumdur. -) - -$(P -$(C Saat) nesnelerinin farklı kabul edilebilmeleri için bütün üyelerinin değerlerinin önemli olduğunu düşünebiliriz. Bu yüzden, indeks değeri olarak o üç üyeden yararlanılarak elde edilen bir tamsayı değer kullanılabilir. Eğer indeks değeri olarak gece yarısından kaç saniye ötede olunduğu kullanılırsa, herhangi bir üyesi değişik olan iki nesnenin indeks değerlerinin farklı olacağı garanti edilmiş olur: -) - ---- -class Saat { - int saat; - int dakika; - int saniye; - - override size_t toHash() const { - // Saatte 3600 ve dakikada 60 saniye bulunduğu için: - return (3600 * saat) + (60 * dakika) + saniye; - } - - // ... -} ---- - -$(P -Eşleme tablolarında indeks türü olarak $(C Saat) kullanıldığında artık programcı tarafından tanımlanmış olan bu $(C toHash) kullanılır. Bunun sonucunda, yukarıdaki kodda $(C new) ile farklı olarak kurulmuş olan iki nesnenin saat, dakika, ve saniye değerleri aynı olduğundan eşleme tablosunda aynı indeks değeri üretilir. -) - -$(P -Programın çıktısı artık beklenen sonucu verir: -) - -$(SHELL -var -) - -$(P -Önceki işlevlerde olduğu gibi, üst sınıfı unutmamak gerekebilir. Örneğin, $(C ÇalarSaat)'in $(C toHash) işlevi $(C Saat)'inkinden şöyle yararlanabilir: -) - ---- -class ÇalarSaat : Saat { - int alarmSaati; - int alarmDakikası; - - override size_t toHash() const { - return $(HILITE super.toHash()) + alarmSaati + alarmDakikası; - } - - // ... -} ---- - -$(P -$(I Not: Yukarıdaki hesabı bir örnek olarak kabul edin. Tamsayı değerleri toplayarak üretilen indeks değerleri genelde eşleme tablosu performansı açısından iyi değildir.) -) - -$(P -D; kesirli sayılar, diziler, ve yapı türleri için çoğu duruma uygun olan indeks değeri algoritmaları kullanır. Bu algoritmalardan programcı da yararlanabilir. -) - -$(P -$(IX getHash) Kulağa karmaşık geldiği halde aslında çok kısaca yapmamız gereken; önce $(C typeid)'yi üye ile, sonra da $(C typeid)'nin döndürdüğü nesnenin $(C getHash) üye işlevini üyenin adresi ile çağırmaktır. Hepsinin dönüş değeri, o üyeye uygun bir indeks değeridir. Bu; kesirli sayılar, diziler ve yapılar için hep aynı şekilde yazılır. -) - -$(P -Öğrencinin ismini bir $(C string) üyesinde tutan ve eşleme tabloları için indeks değeri olarak bundan yararlanmak isteyen bir sınıfın $(C toHash) işlevi şöyle yazılabilir: -) - ---- -class Öğrenci { - string isim; - - override size_t toHash() const { - return typeid(isim).getHash(&isim); - } - - // ... -} ---- - -$(H6 Yapılar için $(C toHash)) - -$(P -Yapılar değer türleri olduklarından onların indeks değerleri zaten otomatik olarak ve etkin bir algoritmayla hesaplanır. O algoritma nesnenin bütün üyelerini dikkate alır. -) - -$(P -Eğer herhangi bir nedenle, örneğin bir öğrenci yapısının not bilgisini dışarıda bırakacak şekilde kendiniz yazmak isterseniz; $(C toHash)'i yapılar için de tanımlayabilirsiniz. -) - -$(PROBLEM_COK - -$(PROBLEM -Elimizde renkli noktaları ifade eden bir sınıf olsun: - ---- -enum Renk { mavi, yeşil, kırmızı } - -class Nokta { - int x; - int y; - Renk renk; - - this(int x, int y, Renk renk) { - this.x = x; - this.y = y; - this.renk = renk; - } -} ---- - -$(P -Bu sınıfın $(C opEquals) işlevini rengi gözardı edecek şekilde yazın. Şu iki nokta, renkleri farklı olduğu halde eşit çıksınlar. Yani $(C assert) denetimi doğru çıksın: -) - ---- - // Renkleri farklı - auto maviNokta = new Nokta(1, 2, Renk.mavi); - auto yeşilNokta = new Nokta(1, 2, Renk.yeşil); - - // Yine de eşitler - assert(maviNokta == yeşilNokta); ---- - -) - -$(PROBLEM -Aynı sınıf için $(C opCmp) işlevini öncelikle $(C x)'e sonra $(C y)'ye bakacak şekilde yazın. Aşağıdaki $(C assert) denetimleri doğru çıksın: - ---- - auto kırmızıNokta1 = new Nokta(-1, 10, Renk.kırmızı); - auto kırmızıNokta2 = new Nokta(-2, 10, Renk.kırmızı); - auto kırmızıNokta3 = new Nokta(-2, 7, Renk.kırmızı); - - assert(kırmızıNokta1 < maviNokta); - assert(kırmızıNokta3 < kırmızıNokta2); - - /* Mavi renk daha önce olduğu halde, renk gözardı - * edildiğinden maviNokta yeşilNokta'dan daha önce - * olmamalıdır. */ - assert(!(maviNokta < yeşilNokta)); ---- - -$(P -Bu sınıfın $(C opCmp) işlevini de yukarıda $(C Öğrenci) sınıfında olduğu gibi uyumsuz türlerin karşılaştırılmalarını desteklemeyecek biçimde gerçekleştirebilirsiniz. -) - -) - -$(PROBLEM -Üç noktayı bir araya getiren başka bir sınıf olsun: - ---- -class ÜçgenBölge { - Nokta[3] noktalar; - - this(Nokta bir, Nokta iki, Nokta üç) { - noktalar = [ bir, iki, üç ]; - } -} ---- - -$(P -O sınıf için $(C toHash) işlevini bütün noktalarını kullanacak biçimde yazın. Yine aşağıdaki $(C assert)'ler doğru çıksın: -) - ---- - /* bölge1 ve bölge2, değerleri aynı olan farklı noktalarla - * kuruluyorlar. (Hatırlatma: maviNokta ve yeşilNokta - * değer olarak eşit kabul ediliyorlardı.) */ - auto bölge1 = - new ÜçgenBölge(maviNokta, yeşilNokta, kırmızıNokta1); - auto bölge2 = - new ÜçgenBölge(yeşilNokta, maviNokta, kırmızıNokta1); - - // Yine de eşitler - assert(bölge1 == bölge2); - - // Bir eşleme tablosu - double[ÜçgenBölge] bölgeler; - - // bölge1 ile indeksleniyor - bölgeler[bölge1] = 1.25; - - // bölge2 ile de aynı veriye erişiliyor - assert(bölge2 in bölgeler); - assert(bölgeler[bölge2] == 1.25); ---- - -$(P -$(C toHash) tanımlandığında $(C opEquals) ve $(C opCmp) işlevlerinin de tanımlanmaları gerektiğini unutmayın. -) - -) - -) - - -Macros: - SUBTITLE=Object - - DESCRIPTION=D dilinde her sınıfın otomatik olarak türediği üst sınıf, Object - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar kullanıcı türleri Object üst sınıf - -SOZLER= -$(cokme) -$(degisken) -$(esleme_tablosu) -$(ifade) -$(indeks) -$(islec) -$(nesne) -$(siraduzen) -$(ust_sinif) -$(uye_islev) diff --git a/ddili/src/ders/d/onsoz1.d b/ddili/src/ders/d/onsoz1.d deleted file mode 100644 index 9ef65f8..0000000 --- a/ddili/src/ders/d/onsoz1.d +++ /dev/null @@ -1,15 +0,0 @@ -Ddoc - -$(DIV_CLASS foreword, - -$(DERS_BOLUMU_CLASS foreword, Walter Bright'ın Önsözü) - -$(P -) - -) - -Macros: - SUBTITLE = Foreword by Walter Bright - DESCRIPTION= - KEYWORDS= diff --git a/ddili/src/ders/d/onsoz2.d b/ddili/src/ders/d/onsoz2.d deleted file mode 100644 index cbff146..0000000 --- a/ddili/src/ders/d/onsoz2.d +++ /dev/null @@ -1,46 +0,0 @@ -Ddoc - -$(DIV_CLASS foreword, - -$(DERS_BOLUMU_CLASS foreword, Önsöz - Andrei Alexandrescu) - -$(P -Ali'yi tanıyanlarımız açık, sabırlı, ve fazla mütevaziliğe kaçmayan nazik kişiliğinin yazdığı D kitabına da yansıdığını farkedeceklerdir. -) - -$(P -Kitaptaki her cümle belirli bir amaca hizmet ediyor ve her birisi ileriye doğru bir adıma dönüşüyor; ne fazla hızlı ne fazla yavaş. "$(C opApply)'ın tanımında da $(C foreach)'ten yararlanıldığına dikkat edin. $(C main) içinde $(C poligon) nesnesi üzerinde işleyen $(C foreach), poligonun $(C noktalar) üyesi üzerinde işletilen bir $(C foreach)'ten yararlanmış olur." Kitap bu havada ve tam da gereken miktarda sözcük sarfederek devam ediyor. Hem de doğru sırada: Ali, – programcılığa yeni başlayanlara üstesinden gelemeyecekleri kadar "hep birden" geliveren – dil kavramlarını takdire değer bir biçimde ardışık olarak sunmayı başarıyor. -) - -$(P -"D Programlama Dili"nin çok beğendiğim bir tarafı daha var: bu kitap, $(I genel anlamda) programlama öğrenme açısından da iyi bir kitap. Haskell'i tanıtan bir kitap aynı zamanda fonksiyonel programlamayı da öğretecektir; C üzerine olan bir kitap sistem programlama kavramlarını da verecektir; bir Python kitabı betik programlama yöntemlerini de gösterecektir, vs. Peki, D'yi tanıtan bir kitabın satır aralarında neler bulmayı bekleyebiliriz? En iyisi, programcılığın kendisini... -) - -$(P -D, "işe uygun olan alet kullanma" fikrini güder ve kullanıcılarına çok sayıda programlama yöntemi sunar. Bunu yaparken programcının önüne fazla gariplik de sermez. D ile kod yazmanın en zevkli yolu kodlamaya açık fikirle yaklaşmaktır çünkü şekil almaya başlayan her tasarım aslında onu başka bir kalıba sokmaya elveren farklı bir gerçekleştirme, yaklaşım, veya paradigma için taze bir fırsattır. En uygun olanlarını seçebilmesi için programcının olanakların tümünü biliyor olması gerekir – "D Programlama Dili" programcıyı bu bilgilerle donatmanın çok iyi yollarından birisi. Bu bilgileri özümsemiş olmak yalnızca iyi D kodu yazmaya değil, genelde iyi kod yazmaya da yardım eder. -) - -$(P -Kitap, öğretilen programlama ve dil kavramlarını destekleyen yöntemler de gösteriyor. Her programcıya mutlaka öğretilen kod tekrarından kaçınılması, iyi isimler seçilmesi, kodun alt parçalara ayrılması, ve başka bir çok öğüt normal kodlamada da olduğu gibi basit çözümden sağlam çözüme doğru adım adım geçiriliyor. "D Programlama Dili" okuyucularını sonuca hızlıca gitme yanılgısına değil, çok daha kalıcı yararları olan doğru kodlamaya yöneltmeye odaklanıyor. -) - -$(P -D'nin öğretilen ilk programlama dili olabileceğini uzun zamandır tahmin etmekteydim. D, kullanıcılarına çok çeşitli programlama kavramını – sistem, fonksiyonel, nesne yönelimli, türden bağımsız, kod üretmeli – içtenlikle ve yapmacıklığa kaçmadan sunar. Bu fırsatı mükemmelce değerlendirdiğini gördüğüm Ali'nin kitabı da öyle. -$(BR) -$(BR) -Andrei Alexandrescu$(BR) -San Francisco, $(I Mayıs 2015) -) - -) - -Macros: - SUBTITLE = Önsöz - Andrei Alexandrescu - DESCRIPTION= - KEYWORDS= - -SOZLER= -$(betik) -$(fonksiyonel_programlama) -$(kod_uretmeli) -$(turden_bagimsiz) diff --git a/ddili/src/ders/d/onsoz_yazar.d b/ddili/src/ders/d/onsoz_yazar.d deleted file mode 100644 index 388447f..0000000 --- a/ddili/src/ders/d/onsoz_yazar.d +++ /dev/null @@ -1,77 +0,0 @@ -Ddoc - -$(DIV_CLASS preface, - -$(DERS_BOLUMU_CLASS preface, Yazarın Önsözü) - -$(P -D, en alt düzeylerden en üst düzeylere kadar bütün güçlü programlama kavramlarını destekleyen ve özellikle bellek güvenliğini, program doğruluğunu, ve kolay kullanımı ön plana çıkartan $(I çok paradigmalı) bir programlama dilidir. -) - -$(P -Bu kitabın temel amacı yeni başlamış olan okuyuculara programcılığı D dilini kullanarak öğretmektir. Her ne kadar başka dillerde kazanılmış olan deneyimler yararlı olsa da, bu kitap programcılığa en temel kavramlardan başlar. -) - -$(P -Bu kitabı izleyebilmek için D programlarınızı yazacak, derleyecek, ve çalıştıracak bir ortama ihtiyacınız olacak. Bu $(I geliştirme ortamında) en azından bir derleyici ve bir metin düzenleyici bulunması şarttır. Derleyici kurulumunu ve programların nasıl derlendiklerini bir sonraki bölümde göreceğiz. -) - -$(P -Her bölüm olabildiğince az sayıda kavramı hep daha önceki bölümlerde öğrenilen bilgiler üzerine kurulu olarak anlatmaya çalışıyor. Bu yüzden kitabı başından sonuna doğru hiç bölüm atlamadan okumanızı öneririm. Bu kitap her ne kadar yeni başlayanlar için yazılmış olsa da D dilinin hemen hemen tamamını içerir. Deneyimli okuyucular dizin bölümünden yararlanarak kitabı bir D referansı olarak da kullanabilirler. -) - -$(P -Bazı bölümlerin sonunda o zamana kadar öğrendiğiniz bilgileri kullanarak programlayabileceğiniz küçük problemler ve kendi çözümlerinizle karşılaştırabilmeniz için çözümler de bulunuyor. -) - -$(P -Kitabın sonunda (ve HTML sürümünün her sayfasında) kitapta kullanılan Türkçe terimlerin İngilizcelerini içeren bir sözlük bulunuyor. -) - -$(P -Programcılık yeni araçlar, yöntemler, ve kavramlar öğrenmeyi gerektiren çok doyurucu bir uğraştır. D programcılığından en az benim kadar hoşlanacağınızı umuyorum. Programlama dilleri başkalarıyla paylaşıldığında hem daha kolay öğrenilir hem de çok daha zevklidir. Çeşitli $(LINK2 http://ddili.org/forum/, D forumlarını) izlemenizi ve o forumlara katkıda bulunmanızı öneririm. -) - -$(P -Bu kitap $(LINK2 http://ddili.org/ders/d.en/, İngilizce)'ye ve $(LINK2 http://dlang.unix.cat/programmer-en-d/, Fransızca)'ya da çevrilmiştir. -) - -$(H5_FRONTMATTER Teşekkür) - -$(P -Bu kitabın gelişiminde büyük katkıları bulunan aşağıdaki kişilere teşekkür ederim. -) - -$(P -Mert Ataol, Zafer Çelenk, Salih Dinçer, Can Alpay Çiftçi, Faruk Erdem Öncel, Muhammet Aydın (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, ve Chen Lejia. -) - -$(P -Özellikle Luís Marques kitabın İngilizce çevirisinin her bölümü üzerinde ayrıntılı düzenlemeler yaptı ve önerilerde bulundu. Bu kitap bir miktar da olsa yararlı olmayı başarabilmişse bunu Luís'in usta düzenlemelerine borçluyum. -) - -$(P -Bu kitabın yazım sürecinde heyecanımı canlı tutan bütün D topluluğuna teşekkür ederim. D dili, bearophile ve Kenji Hara gibi yorulmak bilmeyen kişilerden oluşan harika bir topluluğa sahiptir. -) - -$(P -Ebru, Damla, ve Derin, sabrınız ve desteğiniz için çok teşekkür ederim. İyi ki varsınız. -$(BR) -$(BR) -Ali Çehreli$(BR) -Mountain View, $(I Nisan 2016) -) - -) - -Macros: - SUBTITLE = Yazarın Önsözü - DESCRIPTION= - KEYWORDS= - -SOZLER= -$(cok_paradigmali) -$(derleyici) -$(gelistirme_ortami) -$(metin_duzenleyici) - diff --git a/ddili/src/ders/d/ozel_islevler.d b/ddili/src/ders/d/ozel_islevler.d deleted file mode 100644 index fe8b614..0000000 --- a/ddili/src/ders/d/ozel_islevler.d +++ /dev/null @@ -1,1239 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Kurucu ve Diğer Özel İşlevler) - -$(P -Her ne kadar bu bölümde yalnızca yapıları kullanıyor olsak da bu temel işlemler daha sonra göreceğimiz sınıflar için de geçerlidir. Sınıflardaki farklılıklarını daha sonraki bölümlerde göstereceğim. -) - -$(P -Yapıların üye işlevleri arasından dört tanesi nesnelerin temel işlemlerini belirledikleri için ayrıca önemlidir: -) - -$(UL -$(LI Kurucu işlev $(C this)) -$(LI Sonlandırıcı işlev $(C ~this)) -$(LI Kopya sonrasını belirleyen $(C this(this))) -$(LI Atama işleci $(C opAssign)) -) - -$(P -Bu temel işlemlerin normalde yapılar için özel olarak tanımlanmaları gerekmez çünkü o işlemler zaten derleyici tarafından otomatik olarak halledilirler. Yine de bu işlevlerin özel olarak kendi isteğimiz doğrultusunda tanımlanmalarının gerektiği durumlar olabilir. -) - -$(H5 $(IX kurucu) $(IX this, kurucu) Kurucu işlev) - -$(P -Kurucu işlevin asıl görevi bir nesnenin üyelerine gerekli değerleri atayarak onu kullanılabilir duruma getirmektir. -) - -$(P -Kurucu işlevleri şimdiye kadar hem bütün yapı örneklerinde hem de $(C File) gibi kütüphane türlerinde gördük. Türün ismi işlev çağrısı gibi kullanıldığında o türün kurucu işlevi çağrılır. Bunu aşağıdaki satırın sağ tarafında görüyoruz: -) - ---- - auto dersBaşı = $(HILITE GününSaati)(8, 30); ---- - -$(P -Benzer biçimde, aşağıdaki satırın sağ tarafında da bir sınıf nesnesi kurulmaktadır: -) - ---- - auto değişken = new $(HILITE BirSınıf()); ---- - -$(P -Tür ismi işlev çağrısı gibi kullanılırken parantez içinde yazılanlar da kurucu işleve gönderilen parametre değerleri haline gelirler. Örneğin, yukarıdaki 8 ve 30 değerleri $(C GününSaati) kurucu işlevine gönderilen parametre değerleridir. -) - -$(P -Şimdiye kadar gördüğümüz nesne kurma söz dizimlerine ek olarak; $(C const), $(C immutable), ve $(C shared) nesneler $(I tür kurucusu) söz dizimiyle de kurulabilirler. ($(C shared) anahtar sözcüğünü $(LINK2 /ders/d/es_zamanli_shared.html, ilerideki bir bölümde) göreceğiz.) -) - -$(P -Örneğin, aşağıdaki üç değişken de $(C immutable) oldukları halde, $(C a) değişkeninin kurulma işlemi $(C b) ve $(C c) değişkenlerininkinden anlamsal olarak farklıdır: -) - ---- - /* Yaygın söz dizimi; değişebilen bir türün immutable bir - * değişkeni: */ - $(HILITE immutable) a = S(1); - - /* Tür kurucusu söz dizimi; immutable bir türün bir - * değişkeni: */ - auto b = $(HILITE immutable(S))(2); - - /* 'b' ile aynı anlamda: */ - immutable c = $(HILITE immutable(S))(3); ---- - -$(H6 Söz dizimi) - -$(P -Diğer işlevlerden farklı olarak, kurucu işlevlerin dönüş değerleri yoktur ve dönüş türü olarak $(C void) bile yazılmaz. Kurucu işlevin ismi $(C this) olmak zorundadır. "Bu" anlamına gelen "this"in "$(I bu) türden nesne kuran işlev" sözünden geldiğini düşünebilirsiniz: -) - ---- -struct BirYapı { - // ... - - this(/* kurucu parametreleri */) { - // ... nesneyi kuran işlemler ... - } -} ---- - -$(P -Kurucu parametreleri nesneyi kullanıma hazırlamak için gereken bilgilerden oluşur. -) - -$(H6 $(IX otomatik kurucu) $(IX varsayılan kurucu) Otomatik kurucu işlev) - -$(P -Şimdiye kadar gördüğümüz bütün yapı örneklerinde derleyici tarafından sağlanan otomatik kurucu işlevden yararlandık. O kurucunun işi, parametre değerlerini sırayla üyelere atamaktır. -) - -$(P -$(LINK2 /ders/d/yapilar.html, Yapılar bölümünden) hatırlayacağınız gibi, parametre listesinde sonda bulunan parametrelerin değerlerinin belirtilmesi gerekmez. Değerleri belirtilmeyen üyeler kendi türlerinin $(C .init) değerlerini alırlar. Yine aynı bölümden hatırlayacağınız gibi, üyelerin $(C .init) değerleri üye tanımı sırasında $(C =) işleciyle belirlenebilir: -) - ---- -struct Deneme { - int üye $(HILITE = 42); -} ---- - -$(P -$(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği bölümünde) gösterilen $(I varsayılan parametre değerleri) olanağını da hatırlarsak, otomatik kurucu işlevin derleyici tarafından aşağıdaki gibi oluşturulduğunu düşünebiliriz: -) - ---- -struct Deneme { - char karakter; - int tamsayı; - double kesirli; - - /* Derleyicinin sağladığı kurucu işlevin eşdeğeri. (Not: - * Bu işlev nesneyi Deneme() yazımıyla kurarken çağrılmaz; - * açıklama amacıyla gösteriyorum.) */ - this(in char karakter_parametre = char.init, - in int tamsayı_parametre = int.init, - in double kesirli_parametre = double.init) { - karakter = karakter_parametre; - tamsayı = tamsayı_parametre; - kesirli = kesirli_parametre; - } -} ---- - -$(P -Eğer çoğu yapıda olduğu gibi o kadarı yeterliyse, bizim ayrıca kurucu işlev tanımlamamız gerekmez. Bütün üyelere geçerli değerler verilmesi nesnenin kurulmuş olması için çoğu durumda yeterlidir. -) - -$(H6 $(IX this, üye erişimi) Üyelere $(C this.) ile erişim) - -$(P -Yukarıdaki kodda parametrelerle üyeler karışmasınlar diye parametrelerin sonlarına $(C _parametre) diye bir belirteç ekledim. Parametrelerin isimlerini de üyelerle aynı yapsaydım kod hatalı olurdu: -) - ---- -struct Deneme { - char karakter; - int tamsayı; - double kesirli; - - this(in char karakter = char.init, - in int tamsayı = int.init, - in double kesirli = double.init) { - // 'in' bir parametreyi kendisine atamaya çalışıyor! - karakter = karakter; $(DERLEME_HATASI) - tamsayı = tamsayı; - kesirli = kesirli; - } -} ---- - -$(P -Bunun nedeni, işlev içinde $(C karakter) yazıldığında üyenin değil, parametrenin anlaşılmasıdır. Yukarıdaki parametreler $(C in) olarak işaretlendiklerinden sabit değerin değiştirilemeyeceğini bildiren derleme hatası alınır: -) - -$(SHELL -Error: variable deneme.Deneme.this.karakter $(HILITE cannot modify const) -) - -$(P -Bu konuda bir çözüm olarak $(C this.)'dan yararlanılır: üye işlevler içinde $(C this.), $(I bu nesnenin) anlamına gelir. Bu olanağı kullanınca, parametrelerin isimlerinin sonlarına artık $(C _parametre) gibi ekler yazmak da gerekmez: -) - ---- - this(in char karakter = char.init, - in int tamsayı = int.init, - in double kesirli = double.init) { - $(HILITE this.)karakter = karakter; - $(HILITE this.)tamsayı = tamsayı; - $(HILITE this.)kesirli = kesirli; - } ---- - -$(P -$(C karakter) yazıldığında parametre, $(C this.karakter) yazıldığında da "bu nesnenin üyesi" anlaşılır ve kod artık istediğimizi yapacak biçimde derlenir ve çalışır. -) - -$(H6 $(IX programcının tanımladığı kurucu) Programcı tarafından tanımlanan kurucu işlev) - -$(P -Yukarıda derleyicinin otomatik olarak yazdığı kurucu işlevin perde arkasında nasıl çalıştığını anlattım. Daha önce de belirttiğim gibi, eğer yapının kurulması için bu kadarı yeterliyse ayrıca kurucu tanımlamak gerekmez. Çoğu duruma uygun olan kurucu perde arkasında zaten derleyici tarafından otomatik olarak yazılır ve çağrılır. -) - -$(P -Bazen nesnenin kurulabilmesi için üyelere sırayla değer atamaktan daha karmaşık işlemler gerekebilir. Örnek olarak daha önce tanımlamış olduğumuz $(C Süre) yapısına bakalım: -) - ---- -struct Süre { - int dakika; -} ---- - -$(P -Tek bir tamsayı üyesi bulunan bu yapı için derleyicinin sağladığı kurucu çoğu durumda yeterlidir: -) - ---- - zaman.azalt(Süre(12)); ---- - -$(P -Ancak, o kurucu yalnızca dakika miktarını aldığından bazı durumlarda programcıların hesaplar yapmaları gerekebilir: -) - ---- - // 23 saat ve 18 dakika öncesi - zaman.azalt(Süre(23 * 60 + 18)); - - // 22 saat ve 20 dakika sonrası - zaman.ekle(Süre(22 * 60 + 20)); ---- - -$(P -Programcıları böyle hesaplardan kurtarmak için saat ve dakika miktarlarını iki ayrı parametre olarak alan bir $(C Süre) kurucusu düşünülebilir. Böylece toplam dakika hesabı kurucu içinde yapılır: -) - ---- -struct Süre { - int dakika; - - this(int saat, int dakika) { - this.dakika = saat * 60 + dakika; - } -} ---- - -$(P -Saat ve dakika farklı iki parametre olduklarından, programcılar da hesabı artık kendileri yapmak zorunda kalmamış olurlar: -) - ---- - // 23 saat ve 18 dakika öncesi - zaman.azalt(Süre($(HILITE 23, 18))); - - // 22 saat ve 20 dakika sonrası - zaman.ekle(Süre($(HILITE 22, 20))); ---- - -$(H6 Programcının kurucusu otomatik kurucunun bazı kullanımlarını geçersizleştirir) - -$(P -Programcı tarafından tek bir kurucu işlevin bile tanımlanmış olması, derleyicinin oluşturduğu kurucu işlevin $(I varsayılan parametre değerleri) ile kullanımını geçersiz hale getirir. Örneğin $(C Süre)'nin tek parametre ile kurulması derleme hatasına neden olur: -) - ---- - zaman.azalt(Süre(12)); $(DERLEME_HATASI) ---- - -$(P -O tek parametreli kullanım, programcının tanımlamış olduğu iki parametreli kurucuya uymamaktadır. Ek olarak, $(C Süre)'nin otomatik kurucusu o kullanımda artık geçersizdir. -) - -$(P -Çözüm olarak kurucuyu $(I yükleyebilir) ve bir tane de tek parametreli kurucu tanımlayabiliriz: -) - ---- -struct Süre { - int dakika; - - this(int saat, int dakika) { - this.dakika = saat * 60 + dakika; - } - - this(int dakika) { - this.dakika = dakika; - } -} ---- - -$(P -Programcı tarafından tanımlanan kurucu, nesnelerin $(C { }) karakterleriyle kurulmaları olanağını da ortadan kaldırır: -) - ---- - Süre süre = { 5 }; $(DERLEME_HATASI) ---- - -$(P -Buna rağmen, hiç parametre yazılmadan kurulum her zaman için geçerlidir: -) - ---- - auto s = Süre(); // derlenir ---- - -$(P -Bunun nedeni, her türün $(C .init) değerinin derleme zamanında bilinmesinin D'de şart olmasıdır. Yukarıdaki $(C s)'nin değeri $(C Süre) türünün ilk değerine eşittir: -) - ---- - assert(s == Süre.init); ---- - -$(H6 $(IX static opCall) $(IX opCall, static) Varsayılan kurucu yerine $(C static opCall)) - -$(P -Her türün ilk değerinin derleme zamanında bilinmesinin gerekli olması varsayılan kurucunun programcı tarafından tanımlanmasını olanaksız hale getirir. -) - -$(P -Her nesne kurulduğunda çıktıya bir satır yazdırmaya çalışan aşağıdaki kurucuya bakalım: -) - ---- -struct Deneme { - this() { $(DERLEME_HATASI) - writeln("Deneme nesnesi kuruluyor"); - } -} ---- - -$(P -Derleyici bunun mümkün olmadığını bildirir: -) - -$(SHELL -Error: constructor deneme.Deneme.this default constructor for -structs only allowed with @disable and no body -) - -$(P $(I Not: Varsayılan kurucunun sınıflar için tanımlanabildiğini ileride göreceğiz. -)) - -$(P -Bu kısıtlamaya rağmen yapı nesnelerinin parametresiz olarak nasıl kurulacakları $(C static opCall) ile belirlenebilir. Bunun yapının $(C .init) değerine bir etkisi yoktur: $(C static opCall) yalnızca nesnelerin parametresiz olarak kurulmalarını sağlar. -) - -$(P -Bunun mümkün olması için $(C static opCall) işlecinin o yapının türünden bir nesne oluşturması ve döndürmesi gerekir: -) - ---- -import std.stdio; - -struct Deneme { - $(HILITE static) Deneme $(HILITE opCall)() { - writeln("Deneme nesnesi kuruluyor"); - Deneme deneme; - return deneme; - } -} - -void main() { - auto deneme = $(HILITE Deneme()); -} ---- - -$(P -$(C main) içindeki $(C Deneme()) çağrısı $(C static opCall)'u işletir: -) - -$(SHELL -Deneme nesnesi kuruluyor -) - -$(P -$(I Not: $(C static opCall)'un içindeyken $(C Deneme()) yazılmaması gerektiğine dikkat edin. O yazım da $(C static opCall)'u çağıracağından $(C static opCall)'dan hiç çıkılamaz: -) -) - ---- - static Deneme opCall() { - writeln("Deneme nesnesi kuruluyor"); - return $(HILITE Deneme()); // ← Yine 'static opCall'u çağırır - } ---- - -$(P -Çıktısı: -) - -$(SHELL -Deneme nesnesi kuruluyor -Deneme nesnesi kuruluyor -Deneme nesnesi kuruluyor -... $(SHELL_NOTE sürekli olarak tekrarlanır) -) - -$(H6 Başka kurucu işlevleri çağırmak) - -$(P -Kurucu işlevler başka kurucu işlevleri çağırabilirler ve böylece olası kod tekrarlarının önüne geçilmiş olur. $(C Süre) gibi basit bir yapı bunun yararını anlatmak için uygun olmasa da bu olanağın kullanımını aşağıdaki gibi iki kurucu ile gösterebiliriz: -) - ---- - this(int saat, int dakika) { - this.dakika = saat * 60 + dakika; - } - - this(int dakika) { - this(0, dakika); // diğer kurucuyu çağırıyor - } ---- - -$(P -Yalnızca dakika alan kurucu diğer kurucuyu saat değeri yerine 0 göndererek çağırmaktadır. -) - -$(P -$(B Uyarı:) Yukarıdaki $(C Süre) kurucularında bir tasarım hatası bulunduğunu söyleyebiliriz. Nesneler tek parametre ile kurulduklarında ne istendiği açık değildir: -) - ---- - auto yolSüresi = Süre(10); // 10 saat mi, 10 dakika mı? ---- - -$(P -$(C Süre)'nin belgelerine veya tanımına bakarak "10 dakika" dendiğini anlayabiliriz. Öte yandan, iki parametre alan kurucuda ilk parametrenin $(I saat) olması bir tutarsızlık oluşturmaktadır. -) - -$(P -Böyle tasarımlar karışıklıklara neden olacaklarından kaçınılmaları gerekir. -) - -$(H6 $(IX kurucu nitelendiricisi) $(IX nitelendirici, kurucu) Kurucu nitelendiricileri) - -$(P -$(I Değişebilen), $(C const), $(C immutable), ve $(C shared) nesneler normalde aynı kurucu ile kurulur: -) - ---- -import std.stdio; - -struct S { - this(int i) { - writeln("Bir nesne kuruluyor"); - } -} - -void main() { - auto d = S(1); - const c = S(2); - immutable i = S(3); - shared s = S(4); -} ---- - -$(P -Yukarıda sağ taraftaki ifadelerde kurulmakta olan nesneler anlamsal olarak $(I değişebilen) türdendir. Aralarındaki fark, değişkenlerin tür nitelendiricileridir. Bu yüzden, bütün nesneler aynı kurucu ile kurulur: -) - -$(SHELL -Bir nesne kuruluyor -Bir nesne kuruluyor -Bir nesne kuruluyor -Bir nesne kuruluyor -) - -$(P -Kurulmakta olan nesnenin nitelendiricisine bağlı olarak bazen bazı üyelerinin farklı kurulmaları veya hiç kurulmamaları istenebilir veya gerekebilir. Örneğin, $(C immutable) bir nesnenin hiçbir üyesinin o nesnenin yaşamı boyunca değişmesi söz konusu olmadığından, nesnenin değişebilen bazı nesnelerinin hiç ilklenmemeleri program hızı açısından yararlı olabilir. -) - -$(P -$(I Nitelendirilmiş kurucular) farklı niteliklere sahip nesnelerin kurulmaları için farklı tanımlanabilirler: -) - ---- -import std.stdio; - -struct S { - this(int i) { - writeln("Bir nesne kuruluyor"); - } - - this(int i) $(HILITE const) { - writeln("const bir nesne kuruluyor"); - } - - this(int i) $(HILITE immutable) { - writeln("immutable bir nesne kuruluyor"); - } - - /* 'shared' anahtar sözcüğünü ilerideki bir bölümde - * göreceğiz. */ - this(int i) $(HILITE shared) { - writeln("shared bir nesne kuruluyor"); - } -} - -void main() { - auto d = S(1); - const c = S(2); - immutable i = S(3); - shared s = S(4); -} ---- - -$(P -Ancak, yukarıda da belirtildiği gibi, sağ taraftaki ifadeler anlamsal olarak $(I değişebilen) olduklarından, yukarıdaki nesneler yine de $(I değişebilen) nesne kurucusu ile kurulurlar: -) - -$(SHELL -Bir nesne kuruluyor -Bir nesne kuruluyor $(SHELL_NOTE_WRONG const kurucu DEĞİL) -Bir nesne kuruluyor $(SHELL_NOTE_WRONG immutable kurucu DEĞİL) -Bir nesne kuruluyor $(SHELL_NOTE_WRONG shared kurucu DEĞİL) -) - -$(P -$(IX tür kurucusu) Nitelendirilmiş kuruculardan yararlanabilmek için $(I tür kurucusu) söz dizimini kullanmak gerekir. ($(I Tür kurucusu) terimi nesne kurucularıyla karıştırılmamalıdır; tür kurucusu türlerle ilgilidir, nesnelerle değil.) Bu söz dizimi, bir nitelendiriciyi ve var olan bir türü birleştirerek farklı bir tür oluşturur. Örneğin, $(C immutable(S)) türü, $(C immutable) ile $(C S)'nin birleşmesinden oluşur: -) - ---- - auto d = S(1); - auto c = $(HILITE const(S))(2); - auto i = $(HILITE immutable(S))(3); - auto s = $(HILITE shared(S))(4); ---- - -$(P -Sağ taraftaki ifadelerdeki nesneler bu sefer farklıdır: $(I değişebilen), $(C const), $(C immutable), ve $(C shared). Dolayısıyla, her nesne kendi türüne uyan kurucu ile kurulur: -) - -$(SHELL -Bir nesne kuruluyor -$(HILITE const) bir nesne kuruluyor -$(HILITE immutable) bir nesne kuruluyor -$(HILITE shared) bir nesne kuruluyor -) - -$(P -Ek olarak, yukarıdaki nesneler $(C auto) ile kurulduklarından; türleri $(I değişebilen), $(C const), $(C immutable), ve $(C shared) olarak çıkarsanır. -) - -$(H6 Kurucu parametresinin değişmezliği) - -$(P -$(LINK2 /ders/d/const_ve_immutable.html, Değişmezlik bölümünde) referans türünden olan işlev parametrelerinin $(C const) olarak mı yoksa $(C immutable) olarak mı işaretlenmelerinin daha uygun olduğunun kararının güç olabildiğini görmüştük. Bu güçlük kurucu parametreleri için de geçerlidir. Ancak, kurucu parametrelerinin $(C immutable) olarak seçilmeleri bazı durumlarda $(C const)'tan daha uygundur. -) - -$(P -Bunun nedeni, kurucu parametrelerinin daha sonradan kullanılmak üzere sıklıkla nesne içerisinde saklanmalarıdır. $(C immutable) olmadığı zaman, parametrenin kurucu çağrıldığındaki değeriyle daha sonradan kullanıldığındaki değeri farklı olabilir. -) - -$(P -Bunun örneği olarak öğrencinin notlarını yazacağı dosyanın ismini parametre olarak alan bir kurucuya bakalım. $(LINK2 /ders/d/const_ve_immutable.html, Değişmezlik bölümündeki) ilkeler doğrultusunda ve daha kullanışlı olabilmek amacıyla parametresi $(C const char[]) olarak tanımlanmış olsun: -) - ---- -import std.stdio; - -struct Öğrenci { - $(HILITE const char[]) kayıtDosyası; - size_t[] notlar; - - this($(HILITE const char[]) kayıtDosyası) { - this.kayıtDosyası = kayıtDosyası; - } - - void notlarıKaydet() { - auto dosya = File(kayıtDosyası.idup, "w"); - dosya.writeln("Öğrencinin notları:"); - dosya.writeln(notlar); - } - - // ... -} - -void main() { - char[] dosyaİsmi; - dosyaİsmi ~= "ogrenci_notlari"; - - auto öğrenci = Öğrenci(dosyaİsmi); - - // ... - - /* dosyaİsmi sonradan değiştiriliyor olsun (bu örnekte - * bütün harfleri 'A' oluyor): */ - $(HILITE dosyaİsmi[] = 'A'); - - // ... - - /* Notlar yanlış dosyaya kaydedilecektir: */ - öğrenci.notlarıKaydet(); -} ---- - -$(P -Yukarıdaki program öğrencinin notlarını $(STRING "ogrenci_notlari") dosyasına değil, ismi bütünüyle A harflerinden oluşan bir dosyaya yazar. O yüzden $(I referans türünden olan) üyelerin ve parametrelerin $(C immutable) olarak tanımlanmalarının daha uygun oldukları düşünülebilir. Bunun dizgilerde $(C string) ile kolayca sağlanabildiğini biliyoruz. Yapının yalnızca değişen satırlarını gösteriyorum: -) - ---- -struct Öğrenci { - $(HILITE string) kayıtDosyası; - // ... - this($(HILITE string) kayıtDosyası) { - // ... - } - // ... -} ---- - -$(P -Kullanıcılar nesneleri artık $(C immutable) dizgilerle kurmak zorundadırlar ve kayıtların yazıldığı dosya konusundaki karışıklık böylece giderilmiş olur. -) - -$(H6 $(IX tür dönüşümü, kurucu) Tek parametreli kurucu yoluyla tür dönüşümü) - -$(P -Tek parametre alan kurucu işlevlerin aslında tür dönüşümü sağladıkları düşünülebilir: Kurucu işlevin parametresinin türünden yola çıkarak yapının türünde bir nesne üretilmektedir. Örneğin, aşağıdaki yapının kurucusu verilen bir $(C string)'e karşılık bir $(C Öğrenci) üretmektedir: -) - ---- -struct Öğrenci { - string isim; - - this(string isim) { - this.isim = isim; - } -} ---- - -$(P -Bu $(I dönüşüm) özellikleri nedeniyle kurucu işlevler $(C to) ve $(C cast) tarafından da dönüşüm amacıyla kullanılırlar. Bunun bir örneğini görmek için aşağıdaki $(C selamVer) işlevine bakalım. O işlev bir $(C Öğrenci) beklediği halde ona $(C string) gönderilmesi doğal olarak derleme hatasına yol açar: -) - ---- -void selamVer(Öğrenci öğrenci) { - writeln("Merhaba ", öğrenci.isim); -} -// ... - selamVer("Eray"); $(DERLEME_HATASI) ---- - -$(P -Öte yandan, aşağıdaki üç satır da derlenir ve $(C selamVer) işlevi üçünde de geçici bir $(C Öğrenci) nesnesi ile çağrılır: -) - ---- -import std.conv; -// ... - selamVer(Öğrenci("Eray")); - selamVer(to!Öğrenci("Ercan")); - selamVer(cast(Öğrenci)"Erdost"); ---- - -$(H6 $(IX @disable, kurucu) Varsayılan kurucunun etkisizleştirilmesi) - -$(P -$(C @disable) olarak işaretlenen işlevler kullanılamaz. -) - -$(P -Bazı durumlarda üyeler için varsayılan mantıklı ilk değerler bulunmayabilir ve nesnelerin kesinlikle özel bir kurucu ile kurulmaları gerekebilir. Örneğin, aşağıdaki türün dosya isminin boş olmaması gerekiyor olabilir: -) - ---- -struct Arşiv { - string dosyaİsmi; -} ---- - -$(P -Ne yazık ki, derleyicinin oluşturduğu kurucu $(C dosyaİsmi)'ni boş olarak ilkleyecektir: -) - ---- - auto arşiv = Arşiv(); $(CODE_NOTE dosyaİsmi üyesi boş) ---- - -$(P -Böyle bir durumu önlemenin yolu, varsayılan kurucuyu tanımını vermeden $(C @disable) olarak bildirmek ve böylece var olan diğer kuruculardan birisinin kullanılmasını şart koşmaktır: -) - ---- -struct Arşiv { - string dosyaİsmi; - - $(HILITE @disable this();) $(CODE_NOTE kullanılamaz) - - this(string dosyaİsmi) { $(CODE_NOTE kullanılabilir) - // ... - } -} - -// ... - - auto arşiv = Arşiv(); $(DERLEME_HATASI) ---- - -$(P -Bu sefer derleyici $(C this())'in kullanılamayacağını bildirir: -) - -$(SHELL -Error: constructor deneme.Arşiv.this is $(HILITE not callable) because -it is annotated with @disable -) - -$(P -$(C Arşiv) nesneleri ya başka bir kurucu ile ya da doğrudan $(C .init) değeriyle kurulmak zorundadır: -) - ---- - auto a = Arşiv("kayitlar"); $(CODE_NOTE derlenir) - auto b = Arşiv.init; $(CODE_NOTE derlenir) ---- - -$(H5 $(IX sonlandırıcı) $(IX ~this) Sonlandırıcı işlev) - -$(P -Nesnenin yaşam süreci sona ererken gereken işlemler sonlandırıcı işlev tarafından işletilir. -) - -$(P -Derleyicinin sunduğu otomatik sonlandırıcı sıra ile bütün üyelerin kendi sonlandırıcılarını çağırır. Kurucu işlevde de olduğu gibi, çoğu yapı türünde bu kadarı zaten yeterlidir. -) - -$(P -Bazı durumlarda ise nesnenin sonlanmasıyla ilgili bazı özel işlemler gerekebilir. Örneğin, nesnenin sahiplenmiş olduğu bir işletim sistemi kaynağının geri verilmesi gerekiyordur; başka bir nesnenin bir üye işlevi çağrılacaktır; başka bir bilgisayar üzerinde çalışmakta olan bir programa onunla olan bağlantının kesilmekte olduğu bildirilecektir; vs. -) - -$(P -Sonlandırıcı işlevin ismi $(C ~this)'tir ve kurucuda olduğu gibi onun da dönüş türü yoktur. -) - -$(H6 Sonlandırıcı işlev yapılarda otomatik olarak işletilir) - -$(P -Sonlandırıcı işlev yapı nesnesinin geçerliliği bittiği an işletilir. (Yapılardan farklı olarak, sonlandırıcı işlev sınıflarda hemen işletilmez.) -) - -$(P -$(LINK2 /ders/d/yasam_surecleri.html, Yaşam Süreçleri bölümünden) hatırlayacağınız gibi, nesnelerin yaşam süreçleri tanımlandıkları kapsamdan çıkılırken sona erer. Bir yapı nesnesinin yaşamının sona erdiği durumlar şunlardır: -) - -$(UL -$(LI Nesnenin tanımlandığı kapsamdan normal olarak veya atılan bir hata ile çıkılırken: - ---- - if (birKoşul) { - auto süre = Süre(7); - // ... - - } // ← Sonlandırıcı işlev 'süre' için burada işletilir ---- - -) - -$(LI İsimsiz bir nesne o nesnenin tanımlandığı ifadenin en sonunda sonlanır: - ---- - zaman.ekle(Süre(5)); // ← Süre(5) hazır değeri bu - // ifadenin sonunda sonlanır ---- - -) - -$(LI Bir nesnenin yapı türündeki bütün üyeleri de asıl nesne ile birlikte sonlanırlar. - -) - -) - -$(H6 Sonlandırıcı örneği) - -$(P -Sonlandırıcı örneği olarak XML düzeni oluşturmaya yarayan bir yapı tasarlayalım. XML elemanları açılı parantezlerlerle belirtilirler, ve verilerden ve başka XML elemanlarından oluşurlar. XML elemanlarının nitelikleri de olabilir; onları bu örnekte dikkate almayacağız. -) - -$(P -Burada amacımız, $(C <isim>) şeklinde açılan bir XML elemanının doğru olarak ve mutlaka $(C </isim>) şeklinde kapatılmasını sağlamak olacak: -) - -$(MONO - <ders1> ← dıştaki XML elemanının açılması - <not> ← içteki XML elemanının açılması - 57 ← veri - </not> ← içtekinin kapatılması - </ders1> ← dıştakinin kapatılması -) - -$(P -Bunu sağlayacak bir yapıyı iki üye ile tasarlayabiliriz. Bu üyeler XML elemanının ismini ve çıkışta ne kadar girintiyle yazdırılacağını temsil edebilirler: -) - ---- -struct XmlElemanı { - string isim; - string girinti; -} ---- - -$(P -Eğer XML elemanını açma işini kurucu işleve ve kapama işini de sonlandırıcı işleve yaptırırsak, nesnelerin yaşam süreçlerini ayarlayarak istediğimiz çıktıyı elde edebiliriz. Örneğin çıktıya nesne kurulduğunda $(C <eleman>), sonlandırıldığında da $(C </eleman>) yazdırabiliriz. -) - -$(P -Kurucuyu bu amaca göre şöyle yazabiliriz: -) - ---- - this(in string isim, in int düzey) { - this.isim = isim; - this.girinti = girintiDizgisi(düzey); - - writeln(girinti, '<', isim, '>'); - } ---- - -$(P -Kurucunun son satırı XML elemanının açılmasını sağlamaktadır. $(C girintiDizgisi), o düzeyin girintisini belirleyen ve boşluklardan oluşan bir $(C string) üretir: -) - ---- -import std.array; -// ... -string girintiDizgisi(in int girintiAdımı) { - return replicate(" ", girintiAdımı * 2); -} ---- - -$(P -Yararlandığı $(C replicate) işlevi, kendisine verilen dizgiyi belirtilen sayıda uç uca ekleyerek yeni bir dizgi üreten bir işlevdir; $(C std.array) modülünde tanımlıdır. Bu durumda yalnızca boşluk karakterlerinden oluşuyor ve satır başlarındaki girintileri oluşturmak için kullanılıyor. -) - -$(P -Sonlandırıcı işlevi de XML elemanını kapatmak için benzer biçimde yazabiliriz: -) - ---- - ~this() { - writeln(girinti, "'); - } ---- - -$(P -O yapıyı kullanan bir deneme programı aşağıdaki gibi yazılabilir: -) - ---- -import std.conv; -import std.random; -import std.array; - -string girintiDizgisi(in int girintiAdımı) { - return replicate(" ", girintiAdımı * 2); -} - -struct XmlElemanı { - string isim; - string girinti; - - this(in string isim, in int düzey) { - this.isim = isim; - this.girinti = girintiDizgisi(düzey); - - writeln(girinti, '<', isim, '>'); - } - - ~this() { - writeln(girinti, "'); - } -} - -void main() { - immutable dersler = XmlElemanı("dersler", 0); - - foreach (dersNumarası; 0 .. 2) { - immutable ders = - XmlElemanı("ders" ~ to!string(dersNumarası), 1); - - foreach (i; 0 .. 3) { - immutable not = XmlElemanı("not", 2); - immutable rasgeleNot = uniform(50, 101); - - writeln(girintiDizgisi(3), rasgeleNot); - } - } -} ---- - -$(P -$(C XmlElemanı) nesnelerinin üç kapsamda oluşturulduklarına dikkat edin. Bu programdaki XML elemanlarının açılıp kapanmaları bütünüyle o nesnelerin kurucu ve sonlandırıcı işlevleri tarafından oluşturulmaktadır. -) - -$(SHELL -<dersler> - <ders0> - <not> - 72 - </not> - <not> - 97 - </not> - <not> - 90 - </not> - </ders0> - <ders1> - <not> - 77 - </not> - <not> - 87 - </not> - <not> - 56 - </not> - </ders1> -</dersler> -) - -$(P -Çıktıda örnek olarak $(C <dersler>) elemanına bakalım: $(C main) içinde ilk olarak $(C dersler) nesnesi kurulduğu için ilk olarak onun kurucusunun çıktısını görüyoruz; ve $(C main)'den çıkılırken sonlandırıldığı için de en son onun sonlandırıcısının çıktısını görüyoruz. -) - -$(H5 $(IX postblit) $(IX this(this)) $(IX kopya sonrası) Kopya sonrası işlevi) - -$(P -Kopyalama, var olan bir nesnenin kopyası olarak yeni bir nesne oluşturmaktır. -) - -$(P -Yapılarda kopyalama işinin ilk aşamasını derleyici gerçekleştirir. Yeni nesnenin bütün üyelerini var olan nesnenin üyelerinden sırayla kopyalar: -) - ---- - auto dönüşSüresi = gidişSüresi; // kopyalama ---- - -$(P -Bu işlemi $(I atama) işlemi ile karıştırmayın. Yukarıda sol taraftaki $(C auto), $(C dönüşSüresi) isminde yeni bir nesne kurulduğunu gösterir. $(C auto), oradaki tür ismi yerine geçmektedir. -) - -$(P -Atama olması için $(C dönüşSüresi)'nin daha önceden tanımlanmış bir nesne olması gerekirdi: -) - ---- - dönüşSüresi = gidişSüresi; // atama (aşağıda anlatılıyor) ---- - -$(P -Kopyalama ile ilgili olarak gereken olası özel işlemler otomatik kopyalama işlemi sonlandıktan sonra gerçekleştirilirler. -) - -$(P -Kurma ile ilgili olduğu için kopya sonrası işlevinin ismi de $(C this)'tir. Diğer kuruculardan ayırt edilebilmesi için parametre listesine özel olarak $(C this) yazılır: -) - ---- - this(this) { - // ... - } ---- - -$(P -$(LINK2 /ders/d/yapilar.html, Yapılar bölümünde) basit bir $(C Öğrenci) yapısı kullanmış ve onun nesnelerinin kopyalanmaları ile ilgili bir sorundan söz etmiştik: -) - ---- -struct Öğrenci { - int numara; - int[] notlar; -} ---- - -$(P -O yapının $(C notlar) üyesi dinamik dizi olduğu için bir referans türüdür. Bu, bir $(C Öğrenci) nesnesinin bir başkasına kopyalanması durumunda ikisinin $(C notlar) üyelerinin aynı $(I asıl) elemanlara erişim sağlamalarına neden olur. Bu yüzden, birinin notlarında yapılan değişiklik diğerinde de görülür: -) - ---- - auto öğrenci1 = Öğrenci(1, [ 70, 90, 85 ]); - - auto öğrenci2 = öğrenci1; // kopyalama - öğrenci2.numara = 2; - - öğrenci1.notlar[0] += 5; // ikincinin notu da değişir: - assert($(HILITE öğrenci2).notlar[0] == $(HILITE 75)); ---- - -$(P -Bunun önüne geçmek için ikinci öğrencinin $(C notlar) üyesinin yalnızca o nesneye ait bir dizi olması sağlanmalıdır. Bunu kopya sonrası işlevinde gerçekleştirebiliriz: -) - ---- -struct Öğrenci { - int numara; - int[] notlar; - - this(this) { - notlar = notlar.dup; - } -} ---- - -$(P -$(C this(this)) işlevine girildiğinde bütün üyelerin çoktan asıl nesnenin kopyaları olarak otomatik olarak kurulduklarını hatırlayın. İşleve girildiğindeki $(C notlar), asıl nesnenin $(C notlar)'ı ile aynı diziye erişim sağlamaktadır. Yukarıdaki tek satırda yapılan ise, $(C notlar)'ın erişim sağladığı asıl dizinin bir kopyasını almak ve onu yine bu nesnenin $(C notlar)'ına atamaktır. Böylece bu nesnenin $(C notlar)'ı yeni bir diziye erişim sağlamaya başlar. -) - -$(P -Birinci öğrencinin notlarında yapılan değişiklik artık ikinci öğrencinin notlarını etkilemez: -) - ---- - öğrenci1.notlar[0] += 5; - assert($(HILITE öğrenci2).notlar[0] == $(HILITE 70)); ---- - -$(H6 $(IX @disable, kopya sonrası) Kopya sonrası işlevinin etkisizleştirilmesi) - -$(P -Kopya sonrası işlevi de $(C @disable) ile etkisizleştirilebilir. Böyle bir türün nesneleri otomatik olarak kopyalanamazlar: -) - ---- -struct Arşiv { -// ... - - $(HILITE @disable this(this);) -} - -// ... - - auto a = Arşiv("kayitlar"); - auto b = a; $(DERLEME_HATASI) ---- - -$(P -Derleyici $(C Arşiv) nesnelerinin kopyalanamayacağını bildirir: -) - -$(SHELL -Error: struct deneme.Arşiv is $(HILITE not copyable) because it is -annotated with @disable -) - -$(H5 $(IX atama işleci) $(IX =) $(IX opAssign) Atama işleci) - -$(P -Atama, zaten var olan bir nesneye yeni bir değer vermek anlamına gelir: -) - ---- - dönüşSüresi = gidişSüresi; // atama ---- - -$(P -Atama temel işlemler arasında diğerlerinden biraz daha karmaşıktır çünkü atama işlemi aslında iki parçadan oluşur: -) - -$(UL -$(LI Soldaki nesnenin sonlandırılması) -$(LI Sağdaki nesnenin soldaki nesneye kopyalanması) -) - -$(P -Ancak, o iki işlemin yukarıdaki sırada işletilmelerinin önemli bir sakıncası vardır: Nesnenin başarıyla kopyalanacağından emin olunmadan önce sonlandırılması hataya açıktır. Yoksa, nesnenin kopyalanması aşamasında bir hata atılsa elimizde sonlandırılmış ama tam kopyalanamamış bir nesne kalır. -) - -$(P -Derleyicinin sunduğu otomatik atama işleci bu yüzden güvenli hareket eder ve perde arkasında aşağıdaki işlemleri gerçekleştirir: -) - -$(OL - -$(LI Sağdaki nesneyi geçici bir nesneye kopyalar. - -$(P -Atama işleminin parçası olan asıl kopyalama işlemi bu adımdır. Henüz soldaki nesnede hiçbir değişiklik yapılmamış olduğundan kopyalama sırasında hata atılsa bile kaybedilen bir şey olmaz. -) - -) - -$(LI Soldaki nesneyi sonlandırır. - -$(P -Atama işleminin diğer parçası bu adımdır. -) - -) - -$(LI Geçici nesneyi soldaki nesneye aktarır. - -$(P -Bu adım ve sonrasında kopya sonrası işlevi veya sonlandırıcı işletilmez. Soldaki nesne ve geçici nesne birbirlerinin yerine kullanılabilir durumda olan iki nesnedir. -) - -) - -) - -$(P -Yalnızca perde arkasındaki bu işlemler süresince geçerli olan geçici nesne yok olduğunda geriye yalnızca sağdaki nesne ve onun kopyası olan soldaki nesne kalır. -) - -$(P -Derleyicinin sunduğu otomatik atama işleci hemen hemen her durumda yeterlidir. Eğer herhangi bir nedenle kendiniz tanımlamak isterseniz, atılabilecek olan hatalara karşı dikkatli olmak gerektiğini unutmayın. -) - -$(P -Söz dizimi şu şekildedir: -) - -$(UL -$(LI İsmi $(C opAssign)'dır) -$(LI Parametre türü yapının kendi türüdür) -$(LI Dönüş türü yapının kendi türüdür) -$(LI İşlevden $(C return this) ile çıkılır) -) - -$(P -Ben burada basit $(C Süre) yapısı üzerinde ve çıktıya bir mesaj yazdıracak şekilde tanımlayacağım: -) - ---- -struct Süre { - int dakika; - - $(HILITE Süre) opAssign($(HILITE Süre) sağdaki) { - writefln( - "dakika, %s değerinden %s değerine değişiyor", - this.dakika, sağdaki.dakika); - - this.dakika = sağdaki.dakika; - - $(HILITE return this;) - } -} -// ... - auto süre = Süre(100); - süre = Süre(200); // atama ---- - -$(P -Çıktısı: -) - -$(SHELL -dakika, 100 değerinden 200 değerine değişiyor -) - -$(H6 Başka türlerden atamak) - -$(P -Bazı durumlarda nesnelere kendi türlerinden farklı türlerin değerlerini de atamak isteyebiliriz. Örneğin atama işlecinin sağ tarafında her zaman için $(C Süre) türü kullanmak yerine, doğrudan bir tamsayı değer kullanmak isteyebiliriz: -) - ---- - süre = 300; ---- - -$(P -Bunu, parametre olarak $(C int) alan bir atama işleci daha tanımlayarak sağlayabiliriz: -) - ---- -struct Süre { - int dakika; - - Süre opAssign(Süre sağdaki) { - writefln( - "dakika, %s değerinden %s değerine değişiyor", - this.dakika, sağdaki.dakika); - - this.dakika = sağdaki.dakika; - - return this; - } - - Süre opAssign($(HILITE int dakika)) { - writeln( - "dakika, bir tamsayı değer ile değiştiriliyor"); - - this.dakika = dakika; - - return this; - } -} -// ... - süre = Süre(200); - süre = $(HILITE 300); ---- - -$(P -Çıktısı: -) - -$(SHELL -dakika, 100 değerinden 200 değerine değişiyor -dakika, bir tamsayı değer ile değiştiriliyor -) - -$(H6 Uyarı) - -$(P -Farklı türleri bu şekilde birbirlerine eşitleyebilmek veya daha genel olarak birbirlerinin yerine kullanabilmek, kolaylık getirdiği kadar karışıklıklara ve hatalara da neden olabilir. -) - -$(P -Atama işlecini farklı türlerden parametre alacak şekilde tanımlamanın yararlı olduğunu düşündüğünüz zamanlarda bunun gerçekten gerekli olup olmadığını iyice tartmanızı öneririm. Kimi zaman yararlıdır, kimi zaman gereksiz ve sorunludur. -) - -$(H5 Özet) - -$(UL - -$(LI Kurucu işlev ($(C this)) nesneleri kullanıma hazırlar. Derleyicinin otomatik olarak tanımladığı kurucu çoğu durumda yeterlidir.) - -$(LI Varsayılan kurucunun davranışı yapılarda değiştirilemez. Gerektiğinde onun yerine $(C static opCall) tanımlanır.) - -$(LI Tek parametreli kurucular $(C to) ve $(C cast) tarafından tür dönüşümü sırasında kullanılırlar.) - -$(LI Sonlandırıcı işlev ($(C ~this)) nesnenin yaşamı sona ererken işletilmesi gereken işlemleri içerir.) - -$(LI Kopya sonrası işlevi ($(C this(this))) derleyicinin otomatik olarak gerçekleştirdiği kopyadan sonra gereken düzeltmeleri içerir.) - -$(LI Atama işlevi ($(C opAssign)) var olan nesnelerin başka nesnelerden atanmaları sırasında işletilir.) - -) - -Macros: - SUBTITLE=Kurucu ve Diğer Özel İşlevler - - DESCRIPTION=D dili yapılarının ve sınıflarının özel işlevleri olan kurucu, sonlandırıcı, kopyalayıcı, ve atama işleci - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial yapı yapılar struct sınıf sınıflar class üye işlev üye fonksiyon kurucu sonlandırıcı bozucu kopyalayıcı atama işleci işleç this this(this) - -SOZLER= -$(atama) -$(cikarsama) -$(islec) -$(kapsam) -$(kopya_sonrasi) -$(kopyalama) -$(kurma) -$(kurucu_islev) -$(sonlandirici_islev) -$(sonlandirma) -$(tur_nitelendirici) -$(varsayilan) diff --git a/ddili/src/ders/d/parametre_serbestligi.cozum.d b/ddili/src/ders/d/parametre_serbestligi.cozum.d deleted file mode 100644 index 4b974a1..0000000 --- a/ddili/src/ders/d/parametre_serbestligi.cozum.d +++ /dev/null @@ -1,106 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Parametre Serbestliği) - -$(P -$(C hesapla) işlevinin belirsiz sayıda $(C Hesap) nesnesi alabilmesi için parametre listesinin bir $(C Hesap) dilimini ve $(C ...) karakterlerini içermesi gerekir: -) - ---- -double[] hesapla(in Hesap[] hesaplar...) { - double[] sonuçlar; - - foreach (hesap; hesaplar) { - final switch (hesap.işlem) { - - case İşlem.toplama: - sonuçlar ~= hesap.birinci + hesap.ikinci; - break; - - case İşlem.çıkarma: - sonuçlar ~= hesap.birinci - hesap.ikinci; - break; - - case İşlem.çarpma: - sonuçlar ~= hesap.birinci * hesap.ikinci; - break; - - case İşlem.bölme: - sonuçlar ~= hesap.birinci / hesap.ikinci; - break; - } - } - - return sonuçlar; -} ---- - -$(P -İşleve gönderilen bütün parametre değerleri $(C hesaplar) dizisinde bulunur. Bütün hesap nesnelerini bir döngüde teker teker kullanarak sonuçları da bir $(C double) dizisine yerleştiriyoruz ve işlevin sonucu olarak döndürüyoruz. -) - -$(P -Bütün program: -) - ---- -import std.stdio; - -enum İşlem { toplama, çıkarma, çarpma, bölme } - -struct Hesap { - İşlem işlem; - double birinci; - double ikinci; -} - -double[] hesapla(Hesap[] hesaplar...) { - double[] sonuçlar; - - foreach (hesap; hesaplar) { - final switch (hesap.işlem) { - - case İşlem.toplama: - sonuçlar ~= hesap.birinci + hesap.ikinci; - break; - - case İşlem.çıkarma: - sonuçlar ~= hesap.birinci - hesap.ikinci; - break; - - case İşlem.çarpma: - sonuçlar ~= hesap.birinci * hesap.ikinci; - break; - - case İşlem.bölme: - sonuçlar ~= hesap.birinci / hesap.ikinci; - break; - } - } - - return sonuçlar; -} - -void main() { - writeln(hesapla(Hesap(İşlem.toplama, 1.1, 2.2), - Hesap(İşlem.çıkarma, 3.3, 4.4), - Hesap(İşlem.çarpma, 5.5, 6.6), - Hesap(İşlem.bölme, 7.7, 8.8))); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[3.3, -1.1, 36.3, 0.875] -) - - -Macros: - SUBTITLE=Parametre Serbestliği - - DESCRIPTION=D'nin işlevlerin kullanışlılığını arttıran olanaklarından olan parametrelere varsayılan değerler verme ve işlevleri belirsiz sayıda parametre [variadic] ile çağrılabilme olanakları ile ilgili dersin problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial belirsiz sayıda parametre variadic varsayılan parametre değeri problem çözüm diff --git a/ddili/src/ders/d/parametre_serbestligi.d b/ddili/src/ders/d/parametre_serbestligi.d deleted file mode 100644 index bb35fcc..0000000 --- a/ddili/src/ders/d/parametre_serbestligi.d +++ /dev/null @@ -1,542 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX parametre serbestliği) $(IX parametre, belirsiz sayıda) Parametre Serbestliği) - -$(P -Bu bölümde $(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) anlatılanlarla doğrudan ilgili olan ve işlev çağırma konusunda bazı serbestlikler sağlayan iki olanağı göstereceğim: -) - -$(UL -$(LI Varsayılan parametre değerleri) -$(LI Belirsiz sayıda parametreler) -) - -$(H5 $(IX varsayılan parametre değeri) $(IX parametre değeri, varsayılan) Varsayılan parametre değerleri) - -$(P -İşlevlerle ilgili bir kolaylık, parametrelere varsayılan değerler atanabilmesidir. Bu, yapı üyelerinin varsayılan değerlerinin belirlenebilmesine benzer. -) - -$(P -Bazı işlevlerin bazı parametreleri çoğu durumda hep aynı değerle çağrılıyor olabilirler. Örnek olarak, bir eşleme tablosunu çıkışa yazdıran bir işlev düşünelim. Yazdırdığı eşleme tablosunun hem indeks türü hem de değer türü $(C string) olsun. Bu işlev, çıktıda kullanacağı ayraç karakterlerini de parametre olarak alacak şekilde esnek tasarlanmış olsun: -) - ---- -import std.algorithm; - -// ... - -void tabloYazdır(in char[] başlık, - in string[string] tablo, - in char[] indeksAyracı, - in char[] elemanAyracı) { - writeln("-- ", başlık, " --"); - - auto indeksler = sort(tablo.keys); - - foreach (sayaç, indeks; indeksler) { - - // İlk elemandan önce ayraç olmamalı - if (sayaç != 0) { - write(elemanAyracı); - } - - write(indeks, indeksAyracı, tablo[indeks]); - } - - writeln(); -} ---- - -$(P -O işlev, indekslerle değerler arasına $(C ":"), elemanlar arasına da $(C ", ") gelecek şekilde şöyle çağrılabilir: -) - ---- - string[string] sözlük = [ - "mavi":"blue", "kırmızı":"red", "gri":"gray" ]; - - tabloYazdır("Renk Sözlüğü", sözlük, ":", ", "); ---- - -$(P -Çıktısı: -) - -$(SHELL --- Renk Sözlüğü -- -gri:gray, kırmızı:red, mavi:blue -) - -$(P -Aynı programda başka tabloların da yazdırıldıklarını, ve çoğu durumda hep aynı ayraçların kullanıldıklarını varsayalım. Yalnızca bazı özel durumlarda farklı ayraçlar kullanılıyor olsun. -) - -$(P -Parametre değerlerinin çoğunlukla aynı değeri aldıkları durumlarda, o değerler $(I varsayılan değer) olarak belirtilebilirler: -) - ---- -void tabloYazdır(in char[] başlık, - in string[string] tablo, - in char[] indeksAyracı $(HILITE = ":"), - in char[] elemanAyracı $(HILITE = ", ")) { - // ... -} ---- - -$(P -Varsayılan değerleri olan parametreler işlev çağrısı sırasında belirtilmeyebilirler: -) - ---- - tabloYazdır("Renk Sözlüğü", - sözlük); /* ← ayraçlar belirtilmemiş; - * varsayılan değerlerini alırlar */ ---- - -$(P -O durumda, belirtilmeyen parametrelerin varsayılan değerleri kullanılır. -) - -$(P -Normalin dışında değer kullanılacağı durumlarda işlev çağrılırken o parametreler için yine de özel değerler verilebilir. Gerekiyorsa yalnızca ilki: -) - ---- - tabloYazdır("Renk Sözlüğü", sözlük$(HILITE , "=")); ---- - -$(P -Çıktısı: -) - -$(SHELL --- Renk Sözlüğü -- -gri=gray, kırmızı=red, mavi=blue -) - -$(P -Veya gerekiyorsa her ikisi birden: -) - ---- - tabloYazdır("Renk Sözlüğü", sözlük$(HILITE , "=", "\n")); ---- - -$(P -Çıktısı: -) - -$(SHELL --- Renk Sözlüğü -- -gri=gray -kırmızı=red -mavi=blue -) - -$(P -Varsayılan değerler yalnızca parametre listesinin son tarafındaki parametreler için belirtilebilir. Baştaki veya aradaki parametrelerin varsayılan değerleri belirtilemez. -) - -$(H6 Özel anahtar sözcüklerin varsayılan değer olarak kullanılmaları) - -$(P -$(IX özel anahtar sözcük) $(IX anahtar sözcük) Aşağıdaki anahtar sözcükler kodda geçtikleri yeri gösteren hazır değerler olarak işlem görürler: -) - -$(UL - -$(LI $(IX __MODULE__) $(C __MODULE__): Modülün ismi) -$(LI $(IX __FILE__) $(C __FILE__): Kaynak dosyanın ismi) -$(LI $(IX __LINE__) $(C __LINE__): Satırın numarası) -$(LI $(IX __FUNCTION__) $(C __FUNCTION__): İşlevin ismi) -$(LI $(IX __PRETTY_FUNCTION__) $(C __PRETTY_FUNCTION__): İşlevin tam bildirimi) - -) - -$(P -Kodun başka noktalarında da kullanışlı olsalar da varsayılan parametre değeri olarak kullanıldıklarında etkileri farklıdır. Normal kod içinde geçtiklerinde değerleri bulundukları yerle ilgilidir: -) - ---- -import std.stdio; - -void işlev(int parametre) { - writefln("%s dosyasının %s numaralı satırında, %s " ~ - "işlevi içindeyiz.", - __FILE__, __LINE__, __FUNCTION__); $(CODE_NOTE $(HILITE satır 6)) -} - -void main() { - işlev(42); -} ---- - -$(P -Bildirilen 6 numaralı satır işlevin kendi kodlarına işaret eder: -) - -$(SHELL -deneme.d dosyasının $(HILITE 6 numaralı satırında), deneme.$(HILITE işlev) -işlevi içindeyiz. -) - -$(P -Ancak, bazı durumlarda işlevin hangi satırda tanımlandığı değil, hangi satırdan çağrıldığı bilgisi daha önemlidir. Bu özel anahtar sözcükler varsayılan parametre değeri olarak kullanıldıklarında kendi bulundukları satırla değil, işlevin çağrıldığı satırla ilgili bilgi verirler: -) - ---- -import std.stdio; - -void işlev(int parametre, - string işlevİsmi = $(HILITE __FUNCTION__), - string dosya = $(HILITE __FILE__), - size_t satır = $(HILITE __LINE__)) { - writefln("%s dosyasının %s numaralı satırındaki %s " ~ - "işlevi tarafından çağrılıyor.", - dosya, satır, işlevİsmi); -} - -void main() { - işlev(42); $(CODE_NOTE $(HILITE satır 13)) -} ---- - -$(P -Bu sefer özel anahtar sözcüklerin değerleri işlevin çağrıldığı $(C main()) işlevine işaret eder: -) - -$(SHELL -deneme.d dosyasının $(HILITE 13 numaralı satırındaki) deneme.$(HILITE main) -işlevi tarafından çağrılıyor. -) - -$(P -$(IX özel değişken) $(IX değişken, özel) Yukarıdakilere ek olarak aşağıdaki özel değişkenler de kullanılan derleyiciye ve derleme saatine göre değerler alırlar: -) - -$(UL - -$(LI $(IX __DATE__) $(C __DATE__): Derleme günü) - -$(LI $(IX __TIME__) $(C __TIME__): Derleme saati) - -$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Derleme günü ve saati) - -$(LI $(IX __VENDOR__) $(C __VENDOR__): Derleyici (örneğin, $(STRING "Digital Mars D"))) - -$(LI $(IX __VERSION__) $(C __VERSION__): Derleyici sürümü bir tamsayı olarak (örneğin, 2.069 sürümü için $(C 2069) değeri)) - -) - -$(H5 $(IX işlev, belirsiz sayıda parametre) Belirsiz sayıda parametreler) - -$(P -Varsayılan parametre değerleri, işlevin aslında kaç tane parametre aldığını değiştirmez. Örneğin yukarıdaki $(C tabloYazdır) işlevi her zaman için dört adet parametre alır; ve işini yaparken o dört parametreyi kullanır. -) - -$(P -D'nin başka bir olanağı, işlevleri belirsiz sayıda parametre ile çağırabilmemizi sağlar. Bu olanağı aslında daha önce de çok kere kullandık. Örneğin $(C writeln)'i hiçbir kısıtlamayla karşılaşmadan sınırsız sayıda parametre ile çağırabiliyorduk: -) - ---- - writeln( - "merhaba", 7, "dünya", 9.8 /*, ve istediğimiz kadar - * daha parametre */); ---- - -$(P -D'de belirsiz sayıda parametre kullanmanın dört yolu vardır: -) - -$(UL - -$(LI $(IX _argptr) $(C extern(C)) olarak işaretlenmiş olan işlevler ve $(C _argptr) gizli parametresini kullanan düzenek. Güvensiz olan bu olanağı bu kitapta anlatmayacağım.) - -$(LI $(IX _arguments) Normal D işlevleri için $(C _argptr) ve $(C TypeInfo[]) türündeki $(C _arguments) gizli parametrelerini kullanan düzenek. $(C writeln) gibi işlevler bu düzeneği kullanır. Hem henüz öğrenmediğimiz $(I göstergeleri) kullandığı için, hem de aynı nedenden ötürü güvensiz olabilen bu olanağı da bu kitapta anlatmayacağım.) - -$(LI Belirsiz sayıdaki parametrelerin hep aynı türden olmalarını gerektiren ama bunun yanında güvenli olan D olanağı. Aşağıda bu düzeneği anlatıyorum.) - -$(LI Belirsiz sayıda şablon parametresi. Bu olanağı daha sonra $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) göreceğiz.) - -) - -$(P -$(IX ..., işlev parametresi) $(IX belirsiz sayıda işlev parametresi) D, belirsiz sayıdaki parametreleri o tür işlevlere bir dizi halinde sunar. Belirsiz sayıda parametre alacak olan işlevin parametresi olarak bir dizi belirtilir ve hemen arkasından $(C ...) karakterleri yazılır: -) - ---- -double topla(in double[] sayılar$(HILITE ...)) { - double sonuç = 0.0; - - foreach (sayı; sayılar) { - sonuç += sayı; - } - - return sonuç; -} ---- - -$(P -O şekilde tanımlanan $(C topla), belirsiz sayıda parametre alan bir işlev haline gelmiş olur. Onu istediğimiz sayıda $(C double) ile çağırabiliriz: -) - ---- - writeln(topla($(HILITE 1.1, 2.2, 3.3))); ---- - -$(P -Bütün parametre değerleri tek dilim olarak da verilebilirler: -) - ---- - writeln(sum($(HILITE [) 1.1, 2.2, 3.3 $(HILITE ]))); // üsttekinin eşdeğeri ---- - -$(P -Parametre listesindeki dizi ve ondan sonra gelen $(C ...) karakterleri işlevin çağrıldığı sırada kullanılan parametreleri temsil ederler. $(C topla) işlevi örneğin beş $(C double) parametre ile çağrıldığında, $(C topla)'nın $(C sayılar) parametresi o beş sayıyı içerir. -) - -$(P -Böyle işlevlerin şart koştukları parametreler de bulunabilir. Örneğin, belirsiz sayıdaki sözcüğü parantezler arasında yazdıran bir işlev düşünelim. Bu işlev, her ne kadar sözcük sayısını serbest bıraksa da, ne tür parantezler kullanılacağının belirtilmesini şart koşsun. -) - -$(P -Kesinlikle belirtilmesi gereken parametreler parametre listesinde baş tarafa yazılırlar. Belirsiz sayıdaki parametreyi temsil eden dizi ise en sona yazılır: -) - ---- -char[] parantezle( - in char[] açma, // ← işlev çağrılırken belirtilmelidir - in char[] kapama, // ← işlev çağrılırken belirtilmelidir - in char[][] sözcükler...) { // ← hiç belirtilmeyebilir - char[] sonuç; - - foreach (sözcük; sözcükler) { - sonuç ~= açma; - sonuç ~= sözcük; - sonuç ~= kapama; - } - - return sonuç; -} ---- - -$(P -O işlevi çağırırken ilk iki parametre mutlaka belirtilmelidir: -) - ---- - parantezle("{"); $(DERLEME_HATASI) ---- - -$(P -Kesinlikle belirtilmeleri gereken baştaki parametreler verildiği sürece, geri kalan parametreler konusunda serbestlik vardır. Buna uygun olarak açma ve kapama parantezlerini kullanan bir örnek: -) - ---- - writeln(parantezle("{", "}", "elma", "armut", "muz")); ---- - -$(P -Çıktısı: -) - -$(SHELL -{elma}{armut}{muz} -) - -$(H6 Parametre dizisinin ömrü kısadır) - -$(P -Belirsiz sayıdaki parametrelerin sunulduğu dilim, ömrü kısa olan geçici bir dizinin elemanlarını gösterir. Bu elemanlar yalnızca işlevin işleyişi sırasında kullanmalıdır. Ömürleri kısa olduğundan, işlevin böyle bir dilimi daha sonradan kullanmak üzere saklaması hatalıdır: -) - ---- -int[] sonraKullanmakÜzereSayılar; - -void foo(int[] sayılar...) { - sonraKullanmakÜzereSayılar = sayılar; $(CODE_NOTE_WRONG HATALI) -} - -struct S { - string[] sonraKullanmakÜzereİsimler; - - void foo(string[] isimler...) { - sonraKullanmakÜzereİsimler = isimler; $(CODE_NOTE_WRONG HATALI) - } -} - -void bar() { - foo(1, 10, 100); /* Geçici [ 1, 10, 100 ] dizisi bu - * noktadan sonra geçerli değildir. */ - - auto s = S(); - s.foo("merhaba", "dünya");/* Geçici [ "merhaba", "dünya" ] - * dizisi bu noktadan sonra - * geçerli değildir. */ - - // ... -} - -void main() { - bar(); -} ---- - -$(P -Program yığıtında yaşayan geçici dizilerin elemanlarına erişim sağlayan dilimler sakladıklarından hem serbest işlev $(C foo()) hem de üye işlev $(C S.foo()) hatalıdır. Belirsiz sayıda parametre alan işlev çağrılırken otomatik olarak oluşturulan diziler yalnızca o işlevin işleyişi sırasında geçerlidirler. -) - -$(P -Bu yüzden, parametreleri gösteren bir dilimi sonradan kullanmak üzere saklamak isteyen bir işlevin dilim elemanlarının kopyalarını alması gerekir: -) - ---- -void foo(int[] sayılar...) { - sonraKullanmakÜzereSayılar = sayılar$(HILITE .dup); $(CODE_NOTE doğru) -} - -// ... - - void foo(string[] isimler...) { - sonraKullanmakÜzereİsimler = isimler$(HILITE .dup); $(CODE_NOTE doğru) - } ---- - -$(P -Ancak, böyle işlevler normal dizi dilimleriyle de çağrılabildiklerinden, normal dilimlerin elemanlarının kopyalanmaları gereksizce masraflı olacaktır. -) - -$(P -Hem doğru hem de hızlı olan bir çözüm, birisi belirsiz sayıda parametre, diğeri ise normal dilim alan aynı isimde iki işlev tanımlamaktır. İşlev belirsiz sayıda parametre ile çağrıldığında birisi, normal dilimle çağrıldığında diğeri işletilir: -) - ---- -int[] sonraKullanmakÜzereSayılar; - -void foo(int[] sayılar$(HILITE ...)) { - /* Bu, belirsiz sayıda parametre alan foo() işlevi - * olduğundan, kendilerini gösteren dilim saklamadan önce - * elemanların kopyalarını almak gerekir. */ - sonraKullanmakÜzereSayılar = sayılar$(HILITE .dup); -} - -void foo(int[] sayılar) { - /* Bu, normal dilim alan foo() işlevi olduğundan, dilimi - * olduğu gibi saklayabiliriz. */ - sonraKullanmakÜzereSayılar = sayılar; -} - -struct S { - string[] sonraKullanmakÜzereİsimler; - - void foo(string[] isimler$(HILITE ...)) { - /* Bu, belirsiz sayıda parametre alan S.foo() işlevi - * olduğundan, kendilerini gösteren dilim saklamadan - * önce elemanların kopyalarını almak gerekir. */ - sonraKullanmakÜzereİsimler = isimler$(HILITE .dup); - } - - void foo(string[] isimler) { - /* Bu, normal dilim alan S.foo() işlevi olduğundan, - * dilimi olduğu gibi saklayabiliriz. */ - sonraKullanmakÜzereİsimler = isimler; - } -} - -void bar() { - /* Bu çağrı, belirsiz sayıda parametre alan işleve - * yönlendirilir. */ - foo(1, 10, 100); - - /* Bu çağrı, normal dilim alan işleve yönlendirilir. */ - foo($(HILITE [) 2, 20, 200 $(HILITE ])); - - auto s = S(); - - /* Bu çağrı, belirsiz sayıda parametre alan işleve - * yönlendirilir. */ - s.foo("merhaba", "dünya"); - - /* Bu çağrı, normal dilim alan işleve yönlendirilir. */ - s.foo($(HILITE [) "selam", "ay" $(HILITE ])); - - // ... -} - -void main() { - bar(); -} ---- - -$(P -Aynı isimde ama farklı parametreli işlevler tanımlamaya $(I işlev yükleme) denir. İşlev yüklemeyi bir sonraki bölümde göreceğiz. -) - -$(PROBLEM_TEK - -$(P -Daha önce gördüğümüz aşağıdaki $(C enum) türünün tanımlı olduğunu varsayın: -) - ---- -enum İşlem { toplama, çıkarma, çarpma, bölme } ---- - -$(P -O işlem çeşidini ve işlemde kullanılacak iki kesirli sayıyı içeren bir de yapı olsun: -) - ---- -struct Hesap { - İşlem işlem; - double birinci; - double ikinci; -} ---- - -$(P -Örneğin $(C Hesap(İşlem.bölme, 7.7, 8.8)) nesnesi, 7.7'nin 8.8'e bölüneceği anlamına gelsin. -) - -$(P -Bu yapı nesnelerinden belirsiz sayıda parametre alan, her birisini teker teker hesaplayan, ve bütün sonuçları bir $(C double) dizisi olarak döndüren $(C hesapla) isminde bir işlev yazın. -) - -$(P -Bu işlev örneğin şöyle çağrılabilsin: -) - ---- -void $(CODE_DONT_TEST)main() { - writeln(hesapla(Hesap(İşlem.toplama, 1.1, 2.2), - Hesap(İşlem.çıkarma, 3.3, 4.4), - Hesap(İşlem.çarpma, 5.5, 6.6), - Hesap(İşlem.bölme, 7.7, 8.8))); -} ---- - -$(P -Yukarıdaki gibi kullanıldığında, $(C hesapla)'nın işlem sonuçlarını yerleştirdiği dizi $(C writeln) tarafından çıktıya şöyle yazdırılacaktır: -) - -$(SHELL -[3.3, -1.1, 36.3, 0.875] -) - -) - - -Macros: - SUBTITLE=Parametre Serbestliği - - DESCRIPTION=D'nin işlevlerin kullanışlılığını arttıran olanaklarından olan parametrelere varsayılan değerler verme ve işlevleri belirsiz sayıda parametre [variadic] ile çağrılabilme olanakları - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial belirsiz sayıda parametre variadic varsayılan parametre değeri - -SOZLER= -$(belirsiz_sayida_parametre) -$(gosterge) -$(varsayilan) diff --git a/ddili/src/ders/d/pdf.derse_ozel.css b/ddili/src/ders/d/pdf.derse_ozel.css deleted file mode 100644 index 536fbd8..0000000 --- a/ddili/src/ders/d/pdf.derse_ozel.css +++ /dev/null @@ -1,35 +0,0 @@ -a.xref:after { - content: " (sayfa " target-counter(attr(href, url), page) ")"; -} - -div.cozum_link_cok a.xref { - content: "Çözümler: Sayfa " target-counter(attr(href, url), page); - font-style:italic; -} - -div.cozum_link_cok a.xref:after { - content: normal; -} - -div.cozum_link_tek a.xref { - content: "Çözüm: Sayfa " target-counter(attr(href, url), page); - font-style:italic; -} - -div.cozum_link_tek a.xref:after { - content: normal; -} - -div.cozum_link_cok, div.cozum_link_tek { - padding-top: .1em; - page-break-before: avoid; -} - -body -{ - counter-reset: h4 -2; -} - -ul.toc a.index { - content: "Dizin"; -} diff --git a/ddili/src/ders/d/pdf_cozum_head.html b/ddili/src/ders/d/pdf_cozum_head.html deleted file mode 100644 index 278be03..0000000 --- a/ddili/src/ders/d/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Problem Çözümleri

    diff --git a/ddili/src/ders/d/pdf_cozum_tail.html b/ddili/src/ders/d/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/d/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/d/pdf_html_head.html b/ddili/src/ders/d/pdf_html_head.html deleted file mode 100644 index a173b5d..0000000 --- a/ddili/src/ders/d/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - D Programlama Dili - - - diff --git a/ddili/src/ders/d/pdf_html_tail.html b/ddili/src/ders/d/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/d/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/d/pdf_sozluk_head.html b/ddili/src/ders/d/pdf_sozluk_head.html deleted file mode 100644 index 0c062c3..0000000 --- a/ddili/src/ders/d/pdf_sozluk_head.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - -
    -

    Sözlük

    diff --git a/ddili/src/ders/d/pdf_sozluk_tail.html b/ddili/src/ders/d/pdf_sozluk_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/d/pdf_sozluk_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/d/pragma.d b/ddili/src/ders/d/pragma.d deleted file mode 100644 index 16f7d85..0000000 --- a/ddili/src/ders/d/pragma.d +++ /dev/null @@ -1,242 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX pragma) Pragmalar) - -$(P -Pragma derleyiciyle etkileşme yöntemlerinden birisidir. Hem derleyiciye bilgi vermeye hem de ondan bilgi almaya yarar. Pragmalar şablonlardan başka kodlarda yararlı olsalar da, özellikle $(C pragma(msg)) şablonların hatalarını ayıklarken kullanışlıdır. -) - -$(P -Her derleyici kendi özel pragmalarını tanımlayabilir ama aşağıdaki pragmalar standarttır: -) - -$(H5 $(C pragma(msg))) - -$(P -Derleme zamanında $(C stderr) çıkış akımına mesaj yazdırmaya yarar; çalışma zamanına bir etkisi yoktur. -) - -$(P -Örneğin, aşağıdaki $(C pragma(msg)) bir işlev şablonunun tam olarak hangi parametrelerle çağrıldığını bildirmektedir: -) - ---- -import std.string; - -void işlev(A, B)(A a, B b) { - pragma($(HILITE msg), format("Şablon parametreleri: '%s' ve '%s'", - A.stringof, B.stringof)); - // ... -} - -void main() { - işlev(42, 1.5); - işlev("merhaba", 'a'); -} ---- - -$(SHELL -Şablon parametreleri: 'int' ve 'double' -Şablon parametreleri: 'string' ve 'char' -) - -$(H5 $(C pragma(lib))) - -$(P -Programın bağlanması gereken kütüphaneleri bildirmek için kullanılır. Programı sistemde kurulu olan bir kütüphaneyle bağlamanın en kolay yolu budur. -) - -$(P -Örneğin, $(C curl) kütüphanesini kullanan aşağıdaki program kütüphaneyi derleme satırında belirtmek gerekmeden oluşturulabilir: -) - ---- -import std.stdio; -import std.net.curl; - -pragma($(HILITE lib), "curl"); - -void main() { - // Kitabın bu bölümünü indirmek: - writeln(get("ddili.org/ders/d/pragma.html")); -} ---- - -$(H5 $(IX inline) $(IX kod içi işlev) $(IX işlev, kod içi) $(IX eniyileştirme, derleyici) $(C pragma(inline))) - -$(P -İşlev içeriğinin kod içine $(I açılıp açılmayacağını) belirler. -) - -$(P -Her işlev çağrısının bir masrafı vardır. Bu masraf, işlevin varsa parametrelerinin kopyalanmaları, varsa dönüş değerinin çağırana döndürülmesi, ve sonlandıktan sonra hangi noktadan devam edileceğinin hesabının tutulması ile ilgilidir. -) - -$(P -Bu masraf çoğu durumda işlevin kendisinin ve çağıran tarafın diğer işlemlerinin masrafları yanında dikkate alınmayacak kadar küçüktür. Ancak, bazı durumlarda salt işlev çağrısı bile programın hızını ölçülebilir derecede yavaşlatabilir. Bu, özellikle işlev içeriğinin göreceli olarak hızlı olduğu ve yine göreceli olarak $(I küçük) bir döngüden çok sayıda çağrıldığı durumlarda görülebilir. -) - -$(P -Aşağıdaki program küçük bir işlevi yine küçük bir döngü içinden çağırmakta ve bir sayacın değerini işlevin dönüş değerine bağlı olarak arttırmaktadır: -) - ---- -import std.stdio; -import std.datetime; - -// Oldukça hızlı bir işlev içeriği: -ubyte hesapla(ubyte i) { - return cast(ubyte)(i * 42); -} - -void main() { - size_t sayaç = 0; - - StopWatch kronometre; - kronometre.start(); - - // Çok sayıda tekrarlanan küçük bir döngü: - foreach (i; 0 .. 100_000_000) { - const parametre = cast(ubyte)i; - - if ($(HILITE hesapla(parametre)) == parametre) { - ++sayaç; - } - } - - kronometre.stop(); - - writefln("%s milisaniye", kronometre.peek.msecs); -} ---- - -$(P -$(IX StopWatch, std.datetime) Bu program döngünün ne kadar sürede işletildiğini $(C std.datetime.StopWatch) ile ölçmektedir: -) - -$(SHELL -$(HILITE 674) milisaniye -) - -$(P -$(IX -inline, derleyici seçeneği) $(C -inline) derleyici seçeneği, işlev içeriklerinin kod içine $(I açılmalarına) dayanan bir eniyileştirmeyi etkinleştirir: -) - -$(SHELL -$ dmd deneme.d -w $(HILITE -inline) -) - -$(P -İşlevin kod içine açılması, içeriğinin çağrıldığı noktaya sanki oraya elle yazılmış gibi yerleştirilmesi anlamına gelir. Yukarıdaki döngü bu eniyileştirme uygulandığında aşağıdaki eşdeğeri gibi derlenecektir: -) - ---- - // Döngünün hesapla()'nın kod içine açıldığındaki eşdeğeri: - foreach (i; 0 .. 100_000_000) { - const parametre = cast(ubyte)i; - - const sonuç = $(HILITE cast(ubyte)(parametre * 42)); - if (sonuç == parametre) { - ++sayaç; - } - } ---- - -$(P -Bu işlev çağrısının böylece ortadan kalkması programı denediğim ortamda %40 kadar bir zaman kazancı sağlamaktadır: -) - -$(SHELL -$(HILITE 407) milisaniye -) - -$(P -İşlevlerin kod içine açılmaları her ne kadar büyük bir kazanç gibi görünse de, bu eniyileştirme her duruma uygun değildir çünkü açılan işlevler kodun fazla büyümesine ve mikro işlemcinin kod ön belleğinden taşmasına neden olabilir. Bunun sonucunda da kod tam tersine $(I daha yavaş) işleyebilir. Bu yüzden, işlevlerin kod içine açılmalarının kararı normalde $(C -inline) seçeneği ile derleyiciye bırakılır. -) - -$(P -Buna rağmen, bazı durumlarda derleyiciye bu konudaki kararında yardım edilmesi yararlı olabilir. $(C inline) pragması bu amaçla kullanılır: -) - -$(UL - -$(LI $(C pragma(inline, false)): $(C -inline) derleyici seçeneği kullanılmış bile olsa belirli işlevlerin kod içine açıl$(I ma)maları gerektiğini bildirir.) - -$(LI $(C pragma(inline, true)): $(C -inline) derleyici seçeneği kullanıldığında belirli işlevlerin kesinlikle kod içine açılmaları gerektiğini bildirir. Bu eniyileştirmenin uygulanamadığı durumlarda derleme hataları oluşur. (Buna rağmen, bu pragmanın tam olarak nasıl işlediği derleyiciden derleyiciye değişebilir.)) - -$(LI $(C pragma(inline)): Kod içine açma kararını $(C -inline) seçeneğinin komut satırında belirtilmiş veya belirtilmemiş olmasına göre tekrar derleyiciye bırakır.) - -) - -$(P -Bu pragmalar, içinde geçtikleri işlevi etkileyebildikleri gibi, birden fazla işlev üzerinde etkili olabilmek için kapsam parantezleriyle veya iki nokta üst üste karakteriyle de kullanılabilirler: -) - ---- -pragma(inline, false) -$(HILITE {) - // Bu kapsamda tanımlanan işlevler kod içine açılmazlar - // ... -$(HILITE }) - -int foo() { - pragma(inline, true); // Bu işlev kod içine açılmalıdır - // ... -} - -pragma(inline, true)$(HILITE :) -// Bu bölümde tanımlanan işlevler kod içine açılmalıdırlar -// ... - -pragma(inline)$(HILITE :) -// Bu bölümde tanımlanan işlevlerin kod içine açılıp -// açılmayacaklarının kararı tekrar derleyici bırakılmıştır -// ... ---- - -$(P -$(IX -O, derleyici seçeneği) Programların daha hızlı işlemelerini sağlayan bir başka derleyici seçeneği $(C -O)'dur. Bu seçenek derleyicinin başka eniyileştirme algoritmaları işletmesini sağlar. Ancak, bunun sonucunda derleme süreleri fazla uzayabilir. -) - -$(H5 $(IX startaddress) $(C pragma(startaddress))) - -$(P -Programın başlangıç adresini belirtmeye yarar. Başlangıç adresi zaten D'nin $(I çalışma ortamı) tarafından belirlendiğinden normalde bu pragmaya gerek olmaz. -) - -$(H5 $(IX mangle, pragma) $(IX isim, özgün) $(IX özgün isim) $(C pragma(mangle))) - -$(P -Özgün isim üretirken normal yöntemle üretilecek olandan farklı bir isim kullanılmasını belirler. Özgün isimler bağlayıcının işlevleri ve o işlevleri çağıranları tanıyabilmesi için önemlidir. Bu pragma özellikle D kodunun tesadüfen bir anahtar sözcüğe karşılık gelen bir kütüphane işlevini çağırması gereken durumlarda yararlıdır. -) - -$(P -Örneğin, $(C body) bir D anahtar sözcüğü olduğundan bir C kütüphanesinin $(C body) ismindeki bir işlevi D kodundan çağrılamaz. İşlevin farklı bir isimle çağrılması ama yine de kütüphanenin $(C body) isimli işlevine bağlanması gerekir: -) - ---- -/* Bir C kütüphanesinin 'body' ismindeki işlevi ancak 'c_body' - * gibi bir isimle çağrılabilir. Ancak, bu isim yine de 'body' - * olarak bağlanmalıdır: */ -pragma($(HILITE mangle), "body") -extern(C) string c_body(string); - -void main() { - /* D kodu işlevi c_body() diye çağırır ama bağlayıcı yine - * de doğru ismi olan 'body'yi kullanacaktır: */ - auto s = $(HILITE c_body)("merhaba"); -} ---- - -Macros: - SUBTITLE=Pragmalar - - DESCRIPTION=Derleyiciyle etkileşme yöntemlerinden birisi olan pragmalar - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial pragma - -SOZLER= -$(eniyilestirme) -$(kod_ici_islev) -$(mikro_islemci) -$(onbellek) diff --git a/ddili/src/ders/d/rss.xml b/ddili/src/ders/d/rss.xml deleted file mode 100644 index 094cf4a..0000000 --- a/ddili/src/ders/d/rss.xml +++ /dev/null @@ -1,1156 +0,0 @@ - - - - - D Programlama Dili Kitabı - http://ddili.org/ders/d/ - Temelden D programlama dili - tr - D Programlama Dili Kitabı - - - 'deprecated', 'extern', ve 'extern()' - http://ddili.org/ders/d/moduller.html - Kütüphane olanaklarının emekliye ayrılmalarına yardım eden 'deprecated', başka kütüphanelerde tanımlı olan olanakları bildiren 'extern', ve başka kütüphanelerle iletişim sağlayan 'extern()' (örneğin, extern(C), extern(C++, std)). - Chapter - 24 Oct 2015 22:00 - - - - Kod Örnekleri .zip Dosyası - http://ddili.org/ders/d/index.html - Kitapta geçen kod örneklerinin çoğunu .zip dosyası halinde indirebilirsiniz. - Kod - 16 Oct 2015 20:00 - - - - Kitap Dizini - http://ddili.org/ders/d/ix.html - Kitabın sözcük dizini. - Chapter - 21 Aug 2015 02:00 - - - - Fiberler - http://ddili.org/ders/d/fiberler.html - Fiberler yoluyla D'de "işbirlikli çoklu görevler" (cooperative multitasking). - Chapter - 17 Aug 2015 15:00 - - - - Pragmalar - http://ddili.org/ders/d/pragma.html - Derleyiciyle etkileşme yöntemlerinden birisi olan pragmalar ve D'ye yeni eklenen pragma(inline). - Chapter - 17 Aug 2015 15:00 - - - - İşleç Öncelikleri - http://ddili.org/ders/d/islec_oncelikleri.html - Birden fazla işleç bulunan durumda o işleçlerin hangi sırada işletilecekleri ve hangi ifadeleri kullanacakları. - Chapter - 17 Aug 2015 15:00 - - - - Andrei Alexandrescu'nun Önsözü - http://ddili.org/ders/d/onsoz2.html - "'D Programlama Dili' okuyucularını sonuca hızlıca gitme yanılgısına değil, çok daha kalıcı yararları olan doğru kodlamaya yöneltmeye odaklanıyor." - Chapter - 17 Aug 2015 15:00 - - - - Ekitap sürümleri - http://ddili.org/ders/d/index.html - PDF sürümüne ek olarak EPUB ve AZW3 ekitap sürümleri. - Ekitap - 15 Dec 2014 02:00 - - - - '.offsetof' niteliği ve 'align' belirteci - http://ddili.org/ders/d/bellek_yonetimi.html - Yapı üyelerinin başlangıç adreslerini veren .offsetof niteliği ve onların hizalanmalarını belirleyen align belirteci. - Ders - 25 Nov 2014 17:00 - - - - Sözleşmeli programlama ve türeme - http://ddili.org/ders/d/invariant.html - Arayüzlerin ve sınıfların 'in' ve 'out' bloklarının türemedeki etkileri. - Ders - 25 Nov 2014 17:00 - - - - Özel anahtar sözcükler - http://ddili.org/ders/d/sablonlar_ayrintili.html - __MODULE__, __FILE__, __LINE__, __FUNCTION__, ve __PRETTY_FUNCTION__ - Ders - 25 Nov 2014 17:00 - - - - pragma - http://ddili.org/ders/d/sablonlar.html - pragma belirteci - Ders - 25 Nov 2014 17:00 - - - - İç İşlevler, Yapılar, ve Sınıflar - http://ddili.org/ders/d/ic_tanimlar.html - İşlev, yapı, ve sınıfların kapsamlar içinde tanımlanmaları. - Ders - 25 Nov 2014 17:00 - - - - Sol Değerler ve Sağ Değerler - http://ddili.org/ders/d/deger_sol_sag.html - Sol değerler (lvalue), sağ değerler (rvalue), farkları, ve her iki çeşidi kabul eden 'auto ref' parametreler. - Ders - 25 Nov 2014 17:00 - - - - 'Dizin' bölümü - http://ddili.org/ders/d/pdf_indir.html - PDF sürümünün otomatik olarak oluşturulan 'Dizin' bölümü. - Ders - 25 Nov 2014 17:00 - - - - 'İçindekiler' bölümü - http://ddili.org/ders/d/pdf_indir.html - PDF sürümünün otomatik olarak oluşturulan 'İçindekiler' bölümü. - Ders - 28 Sep 2014 23:00 - - - - Throwable türünün nitelikleri - http://ddili.org/ders/d/hatalar.html - Hata düzeneğinin Throwable arayüzünün nitelikleri, ikincil hatalar, ve ikincil hatalara erişim örneği. - Ders - 18 Sep 2014 16:00 - - - - Seçerek, yerel, isim değiştirerek, ve paket halinde modül ekleme - http://ddili.org/ders/d/moduller.html - import anahtar sözcüğü ile modül eklerken yararlanılan seçerek ekleme, yerel ekleme, isim değiştirerek ekleme, ve paket halinde ekleme. - Ders - 17 Sep 2014 00:30 - - - - @disable - http://ddili.org/ders/d/ozel_islevler.html - Yapıların bazı özel işlevlerini iptal etmeye yarayan @disable niteliği. - Ders - 12 Sep 2014 00:15 - - - - Çok boyutlu eleman erişimi ve dilimleme işleçlerinin yüklenmeleri - http://ddili.org/ders/d/sablonlar_ayrintili.html - opDollar, opIndex, and opSlice işleçlerinin şablon çeşitleri, ve çok-boyutlu eleman erişimi ve dilimleme örnekleri. - Ders - 11 Sep 2014 00:15 - - - - static this, static ~this, shared static this, ve shared static ~this - http://ddili.org/ders/d/index.html - İş parcaçıklarının ve bütün programın ilk ve son işlemlerinin belirlenmesi. - Ders - 26 Aug 2014 23:30 - - - - Kullanıcı Nitelikleri (UDA) - http://ddili.org/ders/d/uda.html - Tür ve değişken bildirimlerine atanan nitelikler, bu niteliklerin derleme zamanında sorgulanmaları, ve kodun niteliklere bağlı olarak derlenmesi. - Ders - 26 Aug 2014 22:30 - - - - Bellek Yönetimi - http://ddili.org/ders/d/bellek_yonetimi.html - Çeşitli düzeltmeler ve sadeleştirmeler. - Ders - 24 Jul 2014 01:00 - - - - Aralıklar - http://ddili.org/ders/d/araliklar.html - Aralıklar bölümündeki kodlar UFCS'ten yararlanacak biçimde değiştirildi. - Ders - 09 July 2013 19:30 - - - - is İfadesi - http://ddili.org/ders/d/is_ifadesi.html - D'nin içgözlem olanaklarından olan 'is' ifadesi. - Ders - 21 May 2013 23:30 - - - - alias this - http://ddili.org/ders/d/alias_this.html - Bir yapının veya sınıfın otomatik tür dönüşümü yoluyla başka türler yerine geçmesini sağlayan 'alias this' olanağı. - Ders - 31 Jan 2013 20:30 - - - - alias - http://ddili.org/ders/d/alias.html - 'alias this' olanağı kendi bölümüne taşındı ve alias'ın yeni söz dizimi anlatıldı. - Ders - 31 Jan 2013 20:30 - - - - İşlev Çağırma Ortak Söz Dizimi (UFCS) - http://ddili.org/ders/d/ufcs.html - Normal işlevlerin üye işlevler gibi çağrılabilmelerini sağlayan UFCS olanağı. - Ders - 31 Jan 2013 20:30 - - - - İşleç Yükleme - http://ddili.org/ders/d/islec_yukleme.html - Bu bölümde geniş düzeltmeler ve eklemeler. - Ders - 15 Sep 2012 23:30 - - - - Kurucu ve Diğer Özel İşlevler - http://ddili.org/ders/d/ozel_islevler.html - Bu bölümde geniş düzeltmeler ve eklemeler. - Ders - 15 Sep 2012 23:30 - - - - destroy ve scoped - http://ddili.org/ders/d/clear.html - Nesnelerin sonlandırıcı işlevlerinin hemen işletilmelerini sağlayan işlevin ismi clear()'den destroy()'a değiştirilmişti. Bu ve diğer bölümler uygun biçimde düzenlendi. - Ders - 23 Aug 2012 08:00 - - - - Sunumlar - http://ddili.org/sunum/ - D dili sunumları. - Sunum - 10 Aug 2012 00:15 - - - - Mesajlaşarak Eş Zamanlı Programlama - http://ddili.org/ders/d/es_zamanli.html - LinkTerminated ve OwnerTerminated hatalarının mesaj olarak da alınabilecekleri. - Ders - 09 Aug 2012 23:45 - - - - Koşut İşlemler - http://ddili.org/ders/d/kosut_islemler.html - std.parallelism modülündeki işlevlerin parametrelerini açıkla: İş birimi büyüklüğü ve ara bellek uzunluğu. - Ders - 09 Aug 2012 23:30 - - - - Değişmezlik - http://ddili.org/ders/d/const_ve_immutable.html - İşlev parametrelerini const veya immutable olarak tanımlamanın etkileri. - Ders - 09 Aug 2012 22:00 - - - - dmd kurulumu ve program derleme - http://ddili.org/ders/d/merhaba_dunya.html - "Merhaba Dünya" programı bölümüne dmd'nin nasıl kurulduğu ve derleyicinin konsolda nasıl kullanıldığı. - Ders - Sat, 21 Apr 2012 22:15 - - - - Veri Paylaşarak Eş Zamanlı Programlama - http://ddili.org/ders/d/es_zamanli_shared.html - Eş Zamanlı Programlama bölümünün veri paylaşımına dayanan olanakları yeni bir bölüme ayrıldı. - Ders - 12 April 2012 22:30 - - - - Mesajlaşarak Eş Zamanlı Programlama - http://ddili.org/ders/d/es_zamanli.html - Eş Zamanlı Programlama bölümünün ismi değiştirildi, geliştirildi, ve veri paylaşımına dayanan eş zamanlı programlama yeni bir bölüme ayrıldı. - Ders - 12 April 2012 22:30 - - - - assert ve enforce - http://ddili.org/ders/d/assert.html - assert bölümüne hata atmayı kolaylaştıran enforce() işlevi eklendi. - Ders - 12 April 2012 22:30 - - - - scope - http://ddili.org/ders/d/scope.html - D'nin try-catch-finally bloklarını ve RAII sınıflarını bir çok durumda gereksizleştiren scope(success), scope(failure), ve scope(exit) deyimleri. - Ders - 28 March 2012 23:20 - - - - İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler - http://ddili.org/ders/d/kapamalar.html - Bu bölüme isimsiz işlevlerin yeni => söz dizimi eklendi. Bütün kitapta delegate'e karşılık 'kapama' yerine 'temsilci' terimi kullanıldı. Temsilcilerin nesne ve üye işlevlerinden de oluşabildikleri gösterildi. - Ders - 20 March 2012 01:35 - - - - Değişmezlik - http://ddili.org/ders/d/const_ve_immutable.html - 'const ve immutable' adlı bölüm 'Değişmezlik' adı altında neredeyse baştan yazıldı: D dilinin kod güvenliği konusunda yardımcı olanaklarından olan immutable ve const anahtar sözcüklerinin tanıtılmaları ve kullanım ilkeleri. - Ders - 13 Mar 2012 17:30 - - - - Eşleme Tabloları - http://ddili.org/ders/d/esleme_tablolari.html - Eşleme tablolarının .byKey(), .byValue(), ve .get() nitelikleri. - Ders - 26 Feb 2012 15:30 - - - - switch ve case - http://ddili.org/ders/d/switch_case.html - 'goto case', 'goto case ifade', ve 'goto default' deyimlerinin switch deyiminde kullanımları. - Ders - 26 Feb 2012 15:30 - - - - Koşut İşlemler - http://ddili.org/ders/d/kosut_islemler.html - std.parallelism modülünün diğer olanakları eklenerek büyük ölçüde genişletildi - Ders - 14 Feb 2012 20:30 - - - - Çıktı Düzeni bölümüne ek: 'Parametre numaraları' - http://ddili.org/ders/d/cikti_duzeni.html - Düzen belirteçlerinde kullanılan parametre numaralarının tanıtılması. - Ders - 31 Jan 2012 00:00 - - - - Dilimlerde capacity() - http://ddili.org/ders/d/dilimler.html - Dilimlerin paylaşımlarının sona erip ermeyeceğinin capacity() işlevi ile nasıl belirlendiği - Ders - 31 Dec 2011 19:15 - - - - foreach döngüsünün diziler dışında sayaçlı kullanımı - http://ddili.org/ders/d/foreach_opapply.html - foreach döngüsüne sayaç eklemek için opApply'ın nasıl yüklendiği - Ders - 13 Nov 2011 23:00 - - - - İşleç Yükleme - http://ddili.org/ders/d/islec_yukleme.html - İşleç yüklemenin yeni söz dizimi anlatıldı - Ders - 8 Jul 2011 16:30 - - - - Bellek Yönetimi - http://ddili.org/ders/d/bellek_yonetimi.html - realloc() açıklandı ve onu kullanan bir örnek eklendi - Ders - 5 Jul 2011 15:30 - - - - Bellek Yönetimi - http://ddili.org/ders/d/bellek_yonetimi.html - Çöp toplayıcının tanıtılması, bellekten yer ayrılması ve nesnelerin bu yerlerde kurulmaları - Ders - 4 Jul 2011 01:50 - - - - clear ve scoped - http://ddili.org/ders/d/clear.html - Nesnelerin sonlandırıcı işlevlerinin hemen işletilmelerini sağlayan clear() ve bunu otomatik olarak yapan scoped - Ders - 26 Jun 2011 00:45 - - - - Tür Nitelikleri - http://ddili.org/ders/d/tur_nitelikleri.html - Türler hakkında üst düzey bilgiler sunan __traits anahtar sözcüğü ve std.traits modülü - Ders - 18 Jun 2011 12:45 - - - - Eş Zamanlı Programlama - http://ddili.org/ders/d/es_zamanli.html - std.concurrency modülü yardımıyla işlemlerin birden fazla iş parçacığı (thread) olarak işletilmeleri ve bu iş parçacıklarının mesajlaşarak veya veri paylaşarak etkileşmeleri - Ders - 18 Jun 2011 12:00 - - - - Koşut İşlemler - http://ddili.org/ders/d/kosut_islemler.html - std.parallelism modülü yardımıyla işlemlerin aynı anda birden fazla mikro işlemci çekirdeği üzerinde işletilmeleri - Ders - 04 Jun 2011 01:10 - - - - Çokuzlular - http://ddili.org/ders/d/cokuzlular.html - Veri çokuzluları (tuple) ve tür çokuzluları (TypeTuple) - Ders - 19 May 2011 19:00 - - - - Başka Aralık Olanakları - http://ddili.org/ders/d/araliklar_baska.html - D'ye özgü aralık kavramının std.range modülünde tanımlanmış olan yardımcı şablon olanakları - Ders - 18 May 2011 10:30 - - - - Aralıklar - http://ddili.org/ders/d/araliklar.html - D'ye özgü aralık kavramının tanıtılması ve Phobos'un InputRange, ForwardRange, BidirectionalRange, RandomAccessRange, ve OutputRange aralıklarının örneklerle gösterilmesi - Ders - 06 May 2011 23:55 - - - - Güncellemeler: std.cstream yerine std.stdio - http://ddili.org/ders/d/ - std.cstream bölümü çıkartıldı, yerine std.stdio geldi. Bazı bölümler çeşitli başka düzeltmelerle geliştirildi. - Ders - 21 Mar 2011 23:00 - - - - Başka Dizi Olanakları - http://ddili.org/ders/d/dilimler.html - D dizilerinin başka olanakları: dilimler, bütün elemanları etkileyen işlemler, $, .dup niteliği, vs. - Ders - 02 Feb 2011 14:30 - - - - scope(exit), scope(success), ve scope(failure) - http://ddili.org/ders/d/hatalar.html - İfadelerin kesinlikle işletilmeleri ile ilgili olan scope deyimleri 'Hata Atma ve Yakalama' bölümüne eklendi - Ders - 08 Jun 2010 17:10 - - - - Katmalar - http://ddili.org/ders/d/katmalar.html - Şablonlar veya dizgiler tarafından derleme zamanında oluşturulan kodların programın istenen noktalarına mixin anahtar sözcüğü ile eklenmeleri - Ders - 31 May 2010 14:00 - - - - Diğer İşlev Olanakları - http://ddili.org/ders/d/islevler_diger.html - Önceki işlev bölümlerinde sonraya bırakılan diğer işlev olanakları: auto ref, inout, pure, @safe, vs. - Ders - 28 May 2010 11:30 - - - - Ayrıntılı Şablonlar - http://ddili.org/ders/d/sablonlar_ayrintili.html - Şablon çeşitleri, şablon parametre çeşitleri, şablon kısıtlamaları, ve meta programlama örnekleri - Ders - 25 May 2010 22:00 - - - - Etiketler - http://ddili.org/ders/d/etiketler.html - Satırlara, döngülere, ve switch deyimlerine isimler vermeyi sağlayan etiketler, ve goto anahtar sözcüğüne D'de neden hiç gerek olmadığı - Ders - 7 May 2010 21:40 - - - - Birlikler - http://ddili.org/ders/d/birlikler.html - Birden fazla değişkenin aynı bellek alanını paylaşmalarını sağlayan birlik [union] olanağı - Ders - 5 May 2010 20:10 - - - - Yapı ve Sınıflarda foreach - http://ddili.org/ders/d/foreach_opapply.html - Kullanıcı türlerinin foreach döngüsü ile çalışacak şekilde programlanmaları - Ders - 4 May 2010 20:30 - - - - İşlev Göstergeleri ve Kapamalar - http://ddili.org/ders/d/kapamalar.html - İşlevlerin adreslerini saklayarak onları daha sonradan çağırmaya yarayan işlev göstergeleri (function); işlev göstergelerine ek olarak o işlevlerin işletildikleri kapsamı da saklayan kapamalar (closure, delegate); ve bunları ifadeler içinde kullanmaya olanak veren isimsiz işlevler ve isimsiz kapamalar - Ders - 1 May 2010 23:30 - - - - Koşullu Derleme - http://ddili.org/ders/d/kosullu_derleme.html - Programa dahil edilecek kodları derleme zamanındaki kararlarla belirleyen debug, version, static if, ve is ifadesi; ve derleme zamanındaki önemli denetimler için static assert - Ders - 30 Apr 2010 23:00 - - - - Bit İşlemleri - http://ddili.org/ders/d/bit_islemleri.html - D'nin en alt düzey işlemlerinden olan ~ (tersi), | (veya), & (ve), ^ (ya da), << (sola kaydırma), >> (sağa kaydırma), ve >>> (sağa işaretsiz kaydırma) işleçlerinin tanıtılması; ve bu işleçlerin kullanım amaçlarını gösteren bazı örnekler. - Ders - 6 Apr 2010 22:40 - - - - Göstergeler - http://ddili.org/ders/d/gostergeler.html - D'nin en alt düzey olanaklarından olan göstergelerin (pointer) tanıtılması. Göstergelerin kullanım amaçları ve belleğe doğrudan erişme yöntemleri. - Ders - 2 Apr 2010 01:00 - - - - alias ve alias this - http://ddili.org/ders/d/alias.html - Var olan türlere yeni isimler vermek, ve isim gizlemeyi (name hiding) etkisiz kılmak için kullanılan 'alias'; ve nesnelerin otomatik tür dönüşümlerinde kullanılmalarına yarayan 'alias this'. - Ders - 18 Feb 2010 20:45 - - - - Şablonlar - http://ddili.org/ders/d/sablonlar.html - Derleyicinin belirli bir kalıba göre kod üretmesini sağlayan şablon (template) olanağı. Örnek bir algoritma ve veri yapısı ile kullanımlarının gösterilmesi. - Ders - 18 Feb 2010 01:00 - - - - Yapı ve Sınıflarda Sözleşmeli Programlama - http://ddili.org/ders/d/invariant.html - Yapı ve sınıf türlerinde kod güvenliği sağlayan sözleşmeli programlamanın gösterilmesi; in, out, ve invariant bloklarının tanıtılması - Ders - 08 Feb 2010 20:45 - - - - Nitelikler - http://ddili.org/ders/d/nitelikler.html - Yapı ve sınıf üyelerine erişimi denetim altına almaya yarayan, ve işlevleri sanki üye değişkenlere erişiliyormuş gibi sunan nitelikler (@property) - Ders - 06 Feb 2010 17:40 - - - - Sarma ve Erişim Hakları - http://ddili.org/ders/d/sarma.html - Nesne yönelimli programlamanın en yararlı olanaklarından olan sarmanın (encapsulation) tanıtılması ve D'nin bu konuda sunduğu anahtar sözcükleri private, protected, public, ve package - Ders - 02 Feb 2010 23:30 - - - - Modüller ve Kütüphaneler - http://ddili.org/ders/d/moduller.html - Programların alt birimleri olan modüller ve onların bir araya gelmelerinden oluşan kütüphaneler - Ders - 02 Feb 2010 22:40 - - - - scope - http://ddili.org/ders/d/scope.html - Nesnelerin sonlandırıcı işlevlerinin çöp toplayıcı tarafından sonraki bir zamanda çağrılmaları yerine, hemen çağrılmalarını sağlayan scope anahtar sözcüğü - Ders - 27 Jan 2010 20:40 - - - - Arayüzler (interface) - http://ddili.org/ders/d/interface.html - D'nin arayüz tanımlamaya yarayan 'interface' olanağı, ve onun aracılığıyla programın farklı bölümlerinin birbirlerinden soyutlanmaları - Ders - 22 Jan 2010 21:35 - - - - Object - http://ddili.org/ders/d/object.html - D'de sınıf sıradüzenlerinin en üstünde bulunan Object sınıfının tanıtılması; o sınıftan kalıtım yoluyla edinilen toString, opEquals, opCmp, ve toHash üye işlevlerinin tanıtılmaları; ve alt sınıflar için özel olarak tanımlanmaları. - Ders - 16 Jan 2010 01:20 - - - - Türeme - http://ddili.org/ders/d/tureme.html - Nesneye yönelik programlamanın temel olanaklarından olan türemenin D dilinde kullanımı; super, override, ve abstract anahtar sözcüklerinin tanıtılması. - Ders - 14 Jan 2010 20:20 - - - - Sınıflar - http://ddili.org/ders/d/siniflar.html - D'nin nesneye yönelik programlama olanakları olan sınıfların genel olarak tanıtılmaları ve yapılarla aralarındaki farkların gösterilmesi - Ders - 11 Jan 2010 20:45 - - - - Parametre Serbestliği Problem Çözümleri - http://ddili.org/ders/d/parametre_serbestligi.cozum.html - Parametre Serbestliği bölümünün çözümleri tamamlandı. Bölümün sonuna problem çözümleri bağlantısı eklendi - Ders - 07 Jan 2010 20:00 - - - - Yapılar Problem Çözümleri - http://ddili.org/ders/d/yapilar.cozum.html - Yapılar bölümünün çözümleri tamamlandı. Bölümün sonuna problem çözümleri bağlantısı eklendi - Ders - 07 Jan 2010 15:55 - - - - İşleç Yükleme - http://ddili.org/ders/d/islec_yukleme.html - Yapıların ve sınıfların da işleçlerle temel türlerde olduğu kadar rahat kullanılmalarını sağlayan işleç yükleme olanağı [operator overloading] - Ders - 28 Dec 2009 22:15 - - - - Kurucu ve Diğer Özel İşlevler - http://ddili.org/ders/d/ozel_islevler.html - Yapıların özel işlevlerinin tanıtılması: kurucu işlev this, sonlandırıcı işlev ~this, kopya sonrası işlevi this(this), ve atama işleci opAssign - Ders - 27 Dec 2009 00:25 - - - - const ref Parametreler ve const Üye İşlevler - http://ddili.org/ders/d/const_uye_islevler.html - Parametrede değişiklik yapılmayacağı garantisini veren 'const ref' parametreler, ve nesnede değişiklik yapılmayacağı garantisini veren const üye işlevler - Ders - 26 Dec 2009 22:45 - - - - Üye İşlevler - http://ddili.org/ders/d/uye_islevler.html - Yapı ve sınıfların tanımları içinde tanımlanan ve çağrıldıkları nesne üzerinde çalışan üye işlevler - Ders - 24 Dec 2009 00:15 - - - - İşlev Yükleme - http://ddili.org/ders/d/islev_yukleme.html - Aynı isimde birden fazla işlev tanımlamaya yarayan işlev yükleme olanağı [overloading] - Ders - 21 Dec 2009 15:30 - - - - Yapılar bölümüne ek - http://ddili.org/ders/d/yapilar.html - Yapılar bölümüne iki başlık eklendi: "Yapı tanımı, tür tanımıdır; nesne tanımı değildir" ve "static üyeler" - Ders - 21 Dec 2009 13:00 - - - - Parametre Serbestliği - http://ddili.org/ders/d/parametre_serbestligi.html - İşlev parametrelerinde serbestlikler sunan iki olanağın tanıtılmaları: varsayılan parametre değerleri, ve belirsiz sayıda parametreler - Ders - 20 Dec 2009 00:35 - - - - Yapılar - http://ddili.org/ders/d/yapilar.html - Başka türleri bir araya getirerek yeni türler oluşturmaya yarayan yapı [struct] olanağı - Ders - 19 Dec 2009 23:35 - - - - Tür Dönüşümleri - http://ddili.org/ders/d/tur_donusumleri.html - Otomatik ve elle açıkça yapılan tür dönüşümleri - Ders - 14 Dec 2009 23:35 - - - - null ve is - http://ddili.org/ders/d/null_ve_is.html - Hiçbir nesneye erişim sağlamama kavramını ifade eden null değeri, referansları karşılaştırmada kullanılan is işleci, ve onun tersi olan !is işleci - Ders - 06 Dec 2009 18:10 - - - - Değerler ve Referanslar - http://ddili.org/ders/d/deger_referans.html - Değer türleri, referans değişkenleri, referans türleri, ve adres alma işleci & - Ders - 01 Dec 2009 23:00 - - - - Yaşam Süreçleri ve Temel İşlemler - http://ddili.org/ders/d/yasam_surecleri.html - Değişkenlerin, nesnelerin, ve parametrelerin yaşam süreçlerinin anlatılması; ve her değişkenin yaşamı boyunca karşılaştığı kurma, sonlandırma, ve atama işlemlerinin ayrıntıları - Ders - 28 Nov 2009 21:45 - - - - Sözleşmeli Programlama - http://ddili.org/ders/d/sozlesmeli.html - İşlevleri hizmet sunan birimler olarak gören, ve bu birimlerin giriş koşullarını ve çıkış garantilerini denetleyen 'sözleşmeli programlama' [contract programming] olanağı - Ders - 24 Nov 2009 14:10 - - - - Birim Testleri - http://ddili.org/ders/d/birim_testler.html - Programcılıkta hatalar, modern programcılığın en güçlü araçlarından olan birim testleri, ve D'nin unittest blokları - Ders - 23 Nov 2009 18:30 - - - - assert İfadesi - http://ddili.org/ders/d/assert.html - Programcının en etkin yardımcılarından olan assert ifadesi - Ders - 22 Nov 2009 01:25 - - - - Hata Atma ve Yakalama (Aykırı Durumlar) - http://ddili.org/ders/d/hatalar.html - D programlarının hatalarla ilgilenme düzeneği olan try-catch blokları ve throw anahtar sözcüğünün tanıtılması; hata çeşitleri; ve hata atma önerileri - Ders - 13 Nov 2009 22:40 - - - - main'in Parametreleri ve Dönüş Değeri - http://ddili.org/ders/d/main.html - D programlarının giriş noktası olan main işlevinin aldığı parametreler, dönüş değeri, ortam değişkenlerine erişim, program içinden başka programların başlatılması, ve hata akımı 'derr' - Ders - 07 Nov 2009 18:00 - - - - Tembel Değerlendirmeler - http://ddili.org/ders/d/tembel_degerlendirmeler.html - D dilinin işlevsel programlama ile ilgili olanaklarından olan ve işlemlerin gerçekten gerekene kadar geciktirilebilmelerini sağlayan 'lazy' anahtar sözcüğünün örnekleri - Ders - 07 Nov 2009 00:40 - - - - İşlev Parametreleri - http://ddili.org/ders/d/islev_parametreleri.html - D dilinde işlev parametrelerini tanımlarken kullanılan belirteçlerin tanıtılması: in, out, ref, const, immutable, ve lazy - Ders - 04 Nov 2009 20:30 - - - - const ve immutable - http://ddili.org/ders/d/const_ve_immutable.html - D dilinin kod güvenliği konusunda yardımcı olanaklarından const ve immutable anahtar sözcüklerinin tanıtılması - Ders - 31 Oct 2009 21:30 - - - - switch ve case Problem Çözümleri - http://ddili.org/ders/d/switch_case.cozum.html - switch ve case bölümünün çözümleri tamamlandı. Bölümün sonuna problem çözümleri bağlantısı eklendi - Ders - 25 Oct 2009 22:30 - - - - foreach Döngüsü Problem Çözümleri - http://ddili.org/ders/d/foreach_dongusu.cozum.html - foreach Döngüsü bölümünün çözümleri tamamlandı. Bölümün sonuna problem çözümleri bağlantısı eklendi - Ders - 25 Oct 2009 22:15 - - - - Eşleme Tabloları Problem Çözümleri - http://ddili.org/ders/d/esleme_tablolari.html - Eşleme Tabloları bölümünün çözümleri tamamlandı. Bölümün sonuna problem çözümleri bağlantısı eklendi - Ders - 25 Oct 2009 22:00 - - - - İşlevler - http://ddili.org/ders/d/islevler.html - D programlarının yapı taşları olan işlevlerin anlatılması - Ders - 24 Oct 2009 22:20 - - - - enum - http://ddili.org/ders/d/enum.html - Sihirli sabitler yerine isimli sabitler kullanmayı sağlayan enum olanağı - Ders - 11 Oct 2009 01:45 - - - - switch ve case - http://ddili.org/ders/d/switch_case.html - switch deyiminin ve case ile belirtilen durumlarının tanıtılması - Ders - 08 Oct 2009 23:40 - - - - Kaynak Dosya alt başlığı - http://ddili.org/ders/d/merhaba_dunya.html - 'Merhaba Dünya' bölümüne kaynak dosyaları tanıtan bir alt başlık eklendi - Ek - 08 Oct 2009 23:40 - - - - foreach Döngüsü - http://ddili.org/ders/d/foreach_dongusu.html - D'nin en kullanışlı deyimlerinden olan foreach'in tanıtılması ve değişik topluluk ve aralıklarla kullanımının gösterilmesi - Ders - 07 Oct 2009 21:05 - - - - Eşleme Tabloları - http://ddili.org/ders/d/esleme_tablolari.html - 'hash table' veri yapısı olarak gerçekleştirilmiş olan ve D'nin iç olanakları arasında bulunan eşleme tabloları - Ders - 06 Oct 2009 15:30 - - - - do-while Döngüsü - http://ddili.org/ders/d/do_while.html - do-while döngüsünün tanıtılması ve while'dan olan çok küçük farkı - Ders - 04 Oct 2009 11:30 - - - - Giriş Düzeni - http://ddili.org/ders/d/giris_duzeni.html - std.stream.readf işlevinin girişten okunan karakterlerin düzeniyle ilgili C'deki scanf'e benzeyen olanakları - Ders - 04 Oct 2009 23:30 - - - - Çıktı Düzeni - http://ddili.org/ders/d/cikti_duzeni.html - std.format modülünde tanımlanmış olan ve bir çok modül tarafından kullanılan düzen dizgisi (format string) karakterleri - Ders - 03 Oct 2009 22:30 - - - - Hazır Değerler - http://ddili.org/ders/d/hazir_degerler.html - Programın yazıldığı sırada eklenen tamsayı, kesirli sayı, karakter, ve dizgi hazır değerlerinin (literal) ve söz dizimlerinin tanıtılması - Ders - 30 Sep 2009 22:55 - - - - Üçlü İşleç - http://ddili.org/ders/d/uclu_islec.html - Üçlü ?: işlecinin tanıtılması ve bazı durumlara if koşulundan daha uygun olduğunun gösterilmesi - Ders - 28 Sep 2009 01:00 - - - - Dosyalarla Unicode - http://ddili.org/ders/d/dosyalar_unicode.html - BOM içeren Unicode dosyaların EndianStream ile kullanılmaları - Ders - 28 Sep 2009 01:00 - - - - İsim Alanı - http://ddili.org/ders/d/isim_alani.html - Program içinde tanımlanan isimlerin geçerli oldukları alanlar (kapsamlar) ve değişkenlerin ilk kullanıldıkları yere yakın olarak tanımlanmalarının yararları - Ders - 22 Sep 2009 00:10 - - - - for Döngüsü - http://ddili.org/ders/d/for_dongusu.html - for döngüsünün tanıtılması ve while döngüsü ile karşılaştırılması - Ders - 22 Sep 2009 00:10 - - - - auto ve typeof - http://ddili.org/ders/d/auto.html - auto ve typeof anahtar sözcüklerinin tanıtılması - Ders - 21 Sep 2009 01:35 - - - - Dosyalar - http://ddili.org/ders/d/dosyalar.html - D dilinin standart kütüphanesi Phobos'taki iki farklı dosya olanağının tanıtılması - Ders - 20 Sep 2009 23:55 - - - - Standart Akımları Dosyalara Bağlamak - http://ddili.org/ders/d/standart_akim_baglamak.html - Programların standart giriş ve çıkışlarının, programda hiçbir değişiklik yapmaya gerek kalmadan nasıl dosyalara ve başka programlara bağlandığı - Ders - 16 Sep 2009 22:30 - - - - Dizgiler - http://ddili.org/ders/d/dizgiler.html - D'de karakter dizisinden farkları olmayan dizgilerin ve std.string modülünün tanıtılması; değişik dizgi türlerinin şaşırtıcı olabilecek etkileşimleri - Ders - 01 Sep 2009 03:10 - - - - Atama ve İşlem Sıraları - http://ddili.org/ders/d/atama_ve_sira.html - Programcılıktaki atama ve işlem sırası kavramlarının çok kısaca tanıtılması ve = işaretinin nasıl matematikten farklı anlamda kullanıldığı - Ders - 30 Aug 2009 14:00 - - - - Dilimler ve Aralıklar - http://ddili.org/ders/d/dilimler.html - D'de dizi dilimlerinin ve aralıklarının tanıtılması - Ders - 23 Aug 2009 22:00 - - - - Karakterler - http://ddili.org/ders/d/karakterler.html - D'de karakter türlerinin ve Unicode kodlama çeşitlerinin tanıtılması - Ders - 19 Aug 2009 14:45 - - - - Diziler - http://ddili.org/ders/d/diziler.html - D'de statik ve dinamik dizilerin ve temel dizi işlemlerinin tanıtılması - Ders - 12 Aug 2009 23:00 - - - - Kesirli Sayılar - http://ddili.org/ders/d/kesirli_sayilar.html - D'de kesirli sayıların tanıtılması; duyarlık, taşma, ve kırpılma kavramlarının anlatılması - Ders - 6 Aug 2009 22:30 - - - - Tamsayılar ve Aritmetik İşlemler - http://ddili.org/ders/d/aritmetik_islemler.html - D'de tamsayıların ve tamsayıları kullanan aritmetik işlemlerin tanıtılması, ve taşma ve kırpılma kavramlarının anlatılması - Ders - 4 Aug 2009 22:30 - - - - while Döngüsü - http://ddili.org/ders/d/while_dongusu.html - Programın döngü halinde işlemler yapmasını sağlayan while deyiminin tanıtılması - Ders - Fri, 31 Jul 2009 23:30 - - - - Programcılık - http://ddili.org/ders/d/programcilik.html - Programcılığın ne olup ne olmadığı hakkında rastgele fikirler - Ders - Fri, 31 Jul 2009 16:00 - - - - if Koşulu - http://ddili.org/ders/d/if_kosulu.html - Programın duruma göre davranma düzeneği olan if deyiminin tanıtılması - Ders - Fri, 31 Jul 2009 16:00 - - - - Mantıksal İfadeler - http://ddili.org/ders/d/mantiksal_ifadeler.html - Programın karar alma aşamalarında kullanılan mantıksal ifadelerinin tanıtılması - Ders - Thu, 30 Jul 2009 13:45 - - - - Girişten Bilgi Almak - http://ddili.org/ders/d/standart_giris.html - Standart girişten bilgi almak ve bilgiyi değişkenlerde depolamak - Ders - Tue, 28 Jul 2009 03:00 - - - - Standart Giriş ve Çıkış Akımları - http://ddili.org/ders/d/giris_cikis.html - Standart giriş ve çıkış akımları din ve dout - Ders - Tue, 28 Jul 2009 03:00 - - - - Değişkenler - http://ddili.org/ders/d/degiskenler.html - Değişken kavramı - Ders - Tue, 28 Jul 2009 03:00 - - - - Temel Türler - http://ddili.org/ders/d/temel_turler.html - D'nin temel türleri - Ders - Tue, 28 Jul 2009 03:00 - - - - writeln ve write - http://ddili.org/ders/d/writeln.html - writeln ve write çıkış işlevleri - Ders - Tue, 28 Jul 2009 03:00 - - - - "Merhaba Dünya" Programı - http://ddili.org/ders/d/merhaba_dunya.html - İlk program "merhaba dünya" - Ders - Tue, 28 Jul 2009 03:00 - - - - Derleyici - http://ddili.org/ders/d/derleyici.html - Derleyici kavramı - Ders - Tue, 28 Jul 2009 03:00 - - - - Tanıtım - http://ddili.org/ders/d/tanitim.html - Bölümlerle ilgili giriş bilgileri ve öğrenciden beklenenler - Ders - Tue, 28 Jul 2009 03:00 - - - - diff --git a/ddili/src/ders/d/sablonlar.d b/ddili/src/ders/d/sablonlar.d deleted file mode 100644 index e7f546b..0000000 --- a/ddili/src/ders/d/sablonlar.d +++ /dev/null @@ -1,1105 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX şablon) $(IX template) Şablonlar) - -$(P -Şablonlar derleyicinin belirli bir kalıba uygun olarak kod üretmesini sağlayan olanaktır. Herhangi bir kod parçasının bazı bölümleri sonraya bırakılır; derleyici o kod bölümlerini uygun olan türler, değerler, vs. ile kendisi oluşturur. -) - -$(P -Şablonlar algoritmaların ve veri yapılarının türden bağımsız olarak yazılabilmelerini sağlarlar ve bu yüzden özellikle kütüphanelerde çok yararlıdırlar. -) - -$(P -D'nin şablon olanağı bazı başka dillerdekilerle karşılaştırıldığında çok güçlü ve çok kapsamlıdır. Bu yüzden şablonların bütün ayrıntılarına bu bölümde giremeyeceğim. Burada, gündelik kullanımda en çok karşılaşılan işlev, yapı, ve sınıf şablonlarının türlerle nasıl kullanıldıklarını göstereceğim. -) - -$(P -Kendisine verilen değeri parantez içinde yazdıran basit bir işleve bakalım: -) - ---- -void parantezliYazdır(int değer) { - writefln("(%s)", değer); -} ---- - -$(P -Parametresi $(C int) olarak tanımlandığından, o işlev yalnızca $(C int) türüyle veya otomatik olarak $(C int)'e dönüşebilen türlerle kullanılabilir. Derleyici, örneğin kesirli sayı türleriyle çağrılmasına izin vermez. -) - -$(P -O işlevi kullanan programın zamanla geliştiğini ve artık başka türlerden olan değerlerin de parantez içinde yazdırılması gerektiğini düşünelim. Bunun için bir çözüm, D'nin işlev yükleme olanağıdır; aynı işlev başka türler için de tanımlanır: -) - ---- -// Daha önce yazılmış olan işlev -void parantezliYazdır(int değer) { - writefln("(%s)", değer); -} - -// İşlevin double türü için yüklenmesi -void parantezliYazdır($(HILITE double) değer) { - writefln("(%s)", değer); -} ---- - -$(P -Bu da ancak belirli bir noktaya kadar yeterlidir çünkü bu işlevi bu sefer de örneğin $(C real) türüyle veya kendi tanımlamış olabileceğimiz başka türlerle kullanamayız. Tabii işlevi o türler için de yüklemeyi düşünebiliriz ama her tür için ayrı ayrı yazılmasının çok külfetli olacağı açıktır. -) - -$(P -Burada dikkatinizi çekmek istediğim nokta, tür ne olursa olsun işlevin içeriğinin hep aynı olduğudur. Türler için yüklenen bu işlevdeki işlemler, $(I türden bağımsız olarak) hepsinde aynıdır. Benzer durumlar özellikle algoritmalarda ve veri yapılarında karşımıza çıkar. -) - -$(P -Örneğin, ikili arama algoritması türden bağımsızdır: O algoritma yalnızca işlemlerle ilgilidir. Aynı biçimde, örneğin bağlı liste veri yapısı da türden bağımsızdır: Yalnızca topluluktaki elemanların nasıl bir arada tutulduklarını belirler. -) - -$(P -İşte şablonlar bu gibi durumlarda yararlıdır: Kod bir kalıp halinde tarif edilir ve derleyici, programda kullanılan türler için kodu gerektikçe kendisi üretir. -) - -$(H5 $(IX işlev şablonu) İşlev şablonları) - -$(P -İşlevi bir kalıp olarak tarif etmek, içinde kullanılan bir veya daha fazla türün $(I belirsiz) olarak sonraya bırakılması anlamına gelir. -) - -$(P -$(IX parametre, şablon) İşlevdeki hangi türlerin sonraya bırakıldıkları işlev parametrelerinden hemen önce yazılan şablon parametreleriyle belirtilir. Bu yüzden işlev şablonlarında iki adet parametre parantezi bulunur; birincisi şablon parametreleridir, ikincisi de işlev parametreleri: -) - ---- -void parantezliYazdır$(HILITE (T))(T değer) { - writefln("(%s)", değer); -} ---- - -$(P -Yukarıda şablon parametresi olarak kullanılan $(C T), "bu işlevde T yazdığımız yerlerde asıl hangi türün kullanılacağına derleyici gerektikçe kendisi karar versin" anlamına gelir. $(C T) yerine herhangi başka bir isim de yazılabilir. Ancak, "type"ın baş harfi olduğu için $(C T) harfi gelenekleşmiştir. "Tür"ün baş harfine de uyduğu için aksine bir neden olmadığı sürece $(C T) kullanmak yerinde olacaktır. -) - -$(P -O şablonu yukarıdaki gibi türden bağımsız olarak yazmak, kendi türlerimiz de dahil olmak üzere onu çeşitli türlerle çağırma olanağı sağlar: -) - ---- -import std.stdio; - -void parantezliYazdır(T)(T değer) { - writefln("(%s)", değer); -} - -void main() { - parantezliYazdır(42); // int ile - parantezliYazdır(1.2); // double ile - - auto birDeğer = BirYapı(); - parantezliYazdır(birDeğer); // BirYapı nesnesi ile -} - -struct BirYapı { - string toString() const { - return "merhaba"; - } -} ---- - -$(P -Derleyici, programdaki kullanımlarına bakarak yukarıdaki işlev şablonunu gereken her tür için ayrı ayrı üretir. Program, sanki o işlev $(C T)'nin kullanıldığı üç farklı tür için, yani $(C int), $(C double), ve $(C BirYapı) için ayrı ayrı yazılmış gibi derlenir: -) - -$(MONO -/* Not: Bu işlevlerin hiçbirisi programa dahil değildir. - * Derleyicinin kendi ürettiği işlevlerin eşdeğerleri - * olarak gösteriyorum. */ - -void parantezliYazdır($(HILITE int) değer) { - writefln("(%s)", değer); -} - -void parantezliYazdır($(HILITE double) değer) { - writefln("(%s)", değer); -} - -void parantezliYazdır($(HILITE BirYapı) değer) { - writefln("(%s)", değer); -} -) - -$(P -Programın çıktısı da o üç farklı işlevin etkisini gösterecek biçimde her tür için farklıdır: -) - -$(SHELL -(42) -(1.2) -(merhaba) -) - -$(P -Her şablon parametresi birden fazla işlev parametresini belirliyor olabilir. Örneğin, tek parametresi bulunan aşağıdaki şablonun hem iki işlev parametresinin hem de dönüş değerinin türü o şablon parametresi ile belirlenmektedir: -) - ---- -/* 'dilim'in 'değer'e eşit olmayan elemanlarından oluşan yeni - * bir dilim döndürür. */ -$(HILITE T)[] süz(T)(const($(HILITE T))[] dilim, $(HILITE T) değer) { - T[] sonuç; - - foreach (eleman; dilim) { - if (eleman != değer) { - sonuç ~= eleman; - } - } - - return sonuç; -} ---- - -$(H5 Birden fazla şablon parametresi kullanılabilir) - -$(P -Aynı işlevi, açma ve kapama parantezlerini de kullanıcıdan alacak şekilde değiştirdiğimizi düşünelim: -) - ---- -void parantezliYazdır(T)(T değer, char açma, char kapama) { - writeln(açma, değer, kapama); -} ---- - -$(P -Artık o işlevi, istediğimiz parantez karakterleri ile çağırabiliriz: -) - ---- - parantezliYazdır(42, '<', '>'); ---- - -$(P -Parantezleri belirleyebiliyor olmak işlevin kullanışlılığını arttırmış olsa da, parantezlerin türünün $(C char) olarak sabitlenmiş olmaları işlevin kullanışlılığını tür açısından düşürmüştür. İşlevi örneğin ancak $(C wchar) ile ifade edilebilen Unicode karakterleri arasında yazdırmaya çalışsak, $(C wchar)'ın $(C char)'a dönüştürülemeyeceği ile ilgili bir derleme hatası alırız: -) - ---- - parantezliYazdır(42, '→', '←'); $(DERLEME_HATASI) ---- - -$(SHELL_SMALL -Error: template deneme.parantezliYazdır(T) cannot deduce -template function from argument types !()(int,$(HILITE wchar),$(HILITE wchar)) -) - -$(P -Bunun bir çözümü, parantez karakterlerini her karakteri ifade edebilen $(C dchar) olarak tanımlamaktır. Bu da yetersiz olacaktır çünkü işlev bu sefer de örneğin $(C string) ile veya kendi özel türlerimizle kullanılamaz. -) - -$(P -$(IX , (virgül), şablon parametre listesi) Başka bir çözüm, yine şablon olanağından yararlanmak ve parantezin türünü de derleyiciye bırakmaktır. Yapmamız gereken, işlev parametresi olarak $(C char) yerine yeni bir şablon parametresi kullanmak ve onu da şablon parametre listesinde belirtmektir: -) - ---- -void parantezliYazdır(T$(HILITE , ParantezTürü))(T değer, - $(HILITE ParantezTürü) açma, - $(HILITE ParantezTürü) kapama) { - writeln(açma, değer, kapama); -} ---- - -$(P -Yeni şablon parametresinin anlamı da $(C T)'ninki gibidir: "bu işlev tanımında ParantezTürü geçen yerlerde hangi tür gerekiyorsa o kullanılsın". -) - -$(P -Artık parantez olarak herhangi bir tür kullanılabilir. Örneğin $(C wchar) ve $(C string) türleriyle: -) - ---- - parantezliYazdır(42, '→', '←'); - parantezliYazdır(1.2, "-=", "=-"); ---- - -$(SHELL -→42← --=1.2=- -) - -$(P -Bu şablonun yararı, tek işlev tanımlamış olduğumuz halde $(C T) ve $(C ParantezTürü) şablon parametrelerinin otomatik olarak belirlenebilmeleridir. -) - -$(H5 $(IX tür çıkarsama) $(IX çıkarsama, tür) Tür çıkarsama) - -$(P -Derleyici yukarıdaki iki kullanımda şu türleri otomatik olarak seçer: -) - -$(UL -$(LI 42'nin yazdırıldığı satırda $(C int) ve $(C wchar)) -$(LI 1.2'nin yazdırıldığı satırda $(C double) ve $(C string)) -) - -$(P -İşlevin çağrıldığı noktalarda hangi türlerin gerektiği işlevin parametrelerinden kolayca anlaşılabilmektedir. Derleyicinin, türü işlev çağrılırken kullanılan parametrelerden anlamasına $(I tür çıkarsaması) denir. -) - -$(P -Derleyici şablon parametrelerini ancak ve ancak işlev çağrılırken kullanılan türlerden çıkarsayabilir. -) - -$(H5 $(IX tür belirtilmesi, şablon) Türün açıkça belirtilmesi) - -$(P -Bazı durumlarda ise şablon parametreleri çıkarsanamazlar, çünkü örneğin işlevin parametresi olarak geçmiyorlardır. Öyle durumlarda derleyicinin şablonun kullanımına bakarak çıkarsaması olanaksızdır. -) - -$(P -Örnek olarak kullanıcıya bir soru soran ve o soru karşılığında girişten bir değer okuyan bir işlev düşünelim; okuduğu değeri döndürüyor olsun. Ayrıca, bütün türler için kullanılabilmesi için de dönüş türünü sabitlemeyelim ve bir şablon parametresi olarak tanımlayalım: -) - ---- -$(HILITE T) giriştenOku$(HILITE (T))(string soru) { - writef("%s (%s): ", soru, T.stringof); - - $(HILITE T) cevap; - readf(" %s", &cevap); - - return cevap; -} ---- - -$(P -O işlev, girişten okuma işini türden bağımsız olarak gerçekleştirdiği için programda çok yararlı olacaktır. Örneğin, kullanıcı bilgilerini edinmek için şu şekilde çağırmayı düşünebiliriz: -) - ---- - giriştenOku("Yaşınız?"); ---- - -$(P -Ancak, o çağırma sırasında $(C T)'nin hangi türden olacağını belirten hiçbir ipucu yoktur. Soru işleve $(C string) olarak gitmektedir ama derleyici dönüş türü için hangi türü istediğimizi bilemez ve $(C T)'yi çıkarsayamadığını bildiren bir hata verir: -) - -$(SHELL_SMALL -Error: template deneme.giriştenOku(T) $(HILITE cannot deduce) template -function from argument types !()(string) -) - -$(P -$(IX !, şablon parametre değeri) Bu gibi durumlarda şablon parametrelerinin ne oldukları programcı tarafından açıkça belirtilmek zorundadır. Şablonun hangi türlerle üretileceği, yani şablon parametreleri, işlev isminden sonraki ünlem işareti ve hemen ardından gelen şablon parametre listesi ile bildirilir: -) - ---- - giriştenOku$(HILITE !(int))("Yaşınız?"); ---- - -$(P -O kod artık derlenir ve yukarıdaki şablon, $(C T) yerine $(C int) yazılmış gibi derlenir. -) - -$(P -Tek şablon parametresi belirtilen durumlarda bir kolaylık olarak şablon parantezleri yazılmayabilir: -) - ---- - giriştenOku$(HILITE !int)("Yaşınız?"); // üsttekinin eşdeğeri ---- - -$(P -O yazılışı şimdiye kadar çok kullandığımız $(C to!string)'den tanıyorsunuz. $(C to) bir işlev şablonudur. Ona verdiğimiz değerin hangi türe dönüştürüleceğini bir şablon parametresi olarak alır. Tek şablon parametresi gerektiği için de $(C to!(string)) yerine onun kısası olan $(C to!string) yazılır. -) - -$(H5 $(IX özelleme, şablon) Şablon özellemeleri) - -$(P -$(C giriştenOku) işlevini başka türlerle de kullanabiliriz. Ancak, derleyicinin ürettiği kod her tür için geçerli olmayabilir. Örneğin, iki boyutlu düzlemdeki bir noktayı ifade eden bir yapı olsun: -) - ---- -struct Nokta { - int x; - int y; -} ---- - -$(P -Her ne kadar yasal olarak derlenebilse de, $(C giriştenOku) şablonunu bu yapı ile kullanırsak şablon içindeki $(C readf) işlevi doğru çalışmaz. Şablon içinde $(C Nokta) türüne karşılık olarak üretilen kod şöyle olacaktır: -) - ---- - Nokta cevap; - readf(" %s", &cevap); // YANLIŞ ---- - -$(P -Doğrusu, $(C Nokta)'yı oluşturacak olan x ve y değerlerinin girişten ayrı ayrı okunmaları ve nesnenin bu değerlerle $(I kurulmasıdır). -) - -$(P -Böyle durumlarda, şablonun belirli bir tür için özel olarak tanımlanmasına $(I özelleme) denir. Hangi tür için özellendiği, şablon parametre listesinde $(C :) karakterinden sonra yazılarak belirtilir: -) - ---- -// Şablonun genel tanımı (yukarıdakinin aynısı) -T giriştenOku(T)(string soru) { - writef("%s (%s): ", soru, T.stringof); - - T cevap; - readf(" %s", &cevap); - - return cevap; -} - -// Şablonun Nokta türü için özellenmesi -T giriştenOku(T $(HILITE : Nokta))(string soru) { - writefln("%s (Nokta)", soru); - - auto x = giriştenOku!int(" x"); - auto y = giriştenOku!int(" y"); - - return Nokta(x, y); -} ---- - -$(P -$(C giriştenOku) işlevi bir $(C Nokta) için çağrıldığında, derleyici artık o özel tanımı kullanır: -) - ---- - auto merkez = giriştenOku!Nokta("Merkez?"); ---- - -$(P -O işlev de kendi içinde $(C giriştenOku!int)'i iki kere çağırarak x ve y değerlerini ayrı ayrı okur: -) - -$(SHELL_SMALL -Merkez? (Nokta) - x (int): 11 - y (int): 22 -) - -$(P -$(C giriştenOku!int) çağrıları şablonun genel tanımına, $(C giriştenOku!Nokta) çağrıları da şablonun özel tanımına yönlendirilecektir. -) - -$(P -Başka bir örnek olarak, şablonu $(C string) ile kullanmayı da düşünebiliriz. Ne yazık ki şablonun genel tanımı $(I girişin sonuna kadar) okunmasına neden olur: -) - ---- - // bütün girişi okur: - auto isim = giriştenOku!string("İsminiz?"); ---- - -$(P -Eğer $(C string)'lerin tek satır olarak okunmalarının uygun olduğunu kabul edersek, bu durumda da çözüm şablonu $(C string) için $(I özel) olarak tanımlamaktır: -) - ---- -T giriştenOku(T $(HILITE : string))(string soru) { - writef("%s (string): ", soru); - - // Bir önceki kullanıcı girişinin sonunda kalmış - // olabilecek boşluk karakterlerini de oku ve gözardı et - string cevap; - do { - cevap = strip(readln()); - } while (cevap.length == 0); - - return cevap; -} ---- - -$(H5 $(IX yapı şablonu) $(IX sınıf şablonu) Yapı ve sınıf şablonları) - -$(P -Yukarıdaki $(C Nokta) sınıfının iki üyesi $(C int) olarak tanımlandığından, işlev şablonlarında karşılaştığımız yetersizlik onda da vardır. -) - -$(P -$(C Nokta) yapısının daha kapsamlı olduğunu düşünelim. Örneğin, kendisine verilen başka bir noktaya olan uzaklığını hesaplayabilsin: -) - ---- -import std.math; - -// ... - -struct Nokta { - int x; - int y; - - int uzaklık(in Nokta diğerNokta) const { - immutable real xFarkı = x - diğerNokta.x; - immutable real yFarkı = y - diğerNokta.y; - - immutable uzaklık = sqrt((xFarkı * xFarkı) + - (yFarkı * yFarkı)); - - return cast(int)uzaklık; - } -} ---- - -$(P -O yapı, örneğin kilometre duyarlığındaki uygulamalarda yeterlidir: -) - ---- - auto merkez = giriştenOku!Nokta("Merkez?"); - auto şube = giriştenOku!Nokta("Şube?"); - - writeln("Uzaklık: ", merkez.uzaklık(şube)); ---- - -$(P -Ancak, kesirli değerler gerektiren daha hassas uygulamalarda kullanışsızdır. -) - -$(P -Yapı ve sınıf şablonları, onları da belirli bir kalıba uygun olarak tanımlama olanağı sağlarlar. Bu durumda, yapıya $(C (T)) parametresi eklemek ve tanımındaki $(C int)'ler yerine $(C T) kullanmak, bu tanımın bir şablon haline gelmesi ve üyelerin türlerinin derleyici tarafından belirlenmesi için yeterlidir: -) - ---- -struct Nokta$(HILITE (T)) { - $(HILITE T) x; - $(HILITE T) y; - - $(HILITE T) uzaklık(in Nokta diğerNokta) const { - immutable real xFarkı = x - diğerNokta.x; - immutable real yFarkı = y - diğerNokta.y; - - immutable uzaklık = sqrt((xFarkı * xFarkı) + - (yFarkı * yFarkı)); - - return cast($(HILITE T))uzaklık; - } -} ---- - -$(P -Yapı ve sınıflar işlev olmadıklarından, çağrılmaları söz konusu değildir. O yüzden derleyicinin şablon parametrelerini çıkarsaması olanaksızdır; türleri açıkça belirtmemiz gerekir: -) - ---- - auto merkez = Nokta$(HILITE !int)(0, 0); - auto şube = Nokta$(HILITE !int)(100, 100); - - writeln("Uzaklık: ", merkez.uzaklık(şube)); ---- - -$(P -Yukarıdaki kullanım, derleyicinin $(C Nokta) şablonunu $(C T) yerine $(C int) gelecek şekilde üretmesini sağlar. Bir şablon olduğundan başka türlerle de kullanabiliriz. Örneğin, virgülden sonrasının önemli olduğu bir uygulamada: -) - ---- - auto nokta1 = Nokta$(HILITE !double)(1.2, 3.4); - auto nokta2 = Nokta$(HILITE !double)(5.6, 7.8); - - writeln(nokta1.uzaklık(nokta2)); ---- - -$(P -Yapı ve sınıf şablonları, veri yapılarını böyle türden bağımsız olarak tanımlama olanağı sağlar. Dikkat ederseniz, $(C Nokta) şablonundaki üyeler ve işlemler tamamen $(C T)'nin asıl türünden bağımsız olarak yazılmışlardır. -) - -$(P -$(C Nokta)'nın artık bir yapı şablonu olması, $(C giriştenOku) işlev şablonunun daha önce yazmış olduğumuz $(C Nokta) özellemesinde bir sorun oluşturur: -) - ---- -T giriştenOku(T : Nokta)(string soru) { $(DERLEME_HATASI) - writefln("%s (Nokta)", soru); - - auto x = giriştenOku!int(" x"); - auto y = giriştenOku!int(" y"); - - return Nokta(x, y); -} ---- - -$(P -Hatanın nedeni, artık $(C Nokta) diye bir tür bulunmamasıdır: $(C Nokta) artık bir tür değil, bir $(I yapı şablonudur). Bir tür olarak kabul edilebilmesi için, mutlaka şablon parametresinin de belirtilmesi gerekir. $(C giriştenOku) işlev şablonunu $(I bütün Nokta kullanımları için) özellemek için aşağıdaki değişiklikleri yapabiliriz. Açıklamalarını koddan sonra yapacağım: -) - ---- -Nokta!T giriştenOku(T : Nokta!T)(string soru) { // 2, 1 - writefln("%s (Nokta!%s)", soru, T.stringof); // 5 - - auto x = giriştenOku!T(" x"); // 3a - auto y = giriştenOku!T(" y"); // 3b - - return Nokta!T(x, y); // 4 -} ---- - -$(OL - -$(LI Bu işlev şablonu özellemesinin $(C Nokta)'nın bütün kullanımlarını desteklemesi için, şablon parametre listesinde $(C Nokta!T) yazılması gerekir; bir anlamda, $(C T) ne olursa olsun, bu özellemenin $(C Nokta!T) türleri için olduğu belirtilmektedir: $(C Nokta!int), $(C Nokta!double), vs.) - -$(LI Girişten okunan türe uyması için dönüş türünün de $(C Nokta!T) olarak belirtilmesi gerekir.) - -$(LI Bu işlevin önceki tanımında olduğu gibi $(C giriştenOku!int)'i çağıramayız çünkü $(C Nokta)'nın üyeleri herhangi bir türden olabilir. Bu yüzden, $(C T) ne ise, $(C giriştenOku) şablonunu o türden değer okuyacak şekilde, yani $(C giriştenOku!T) şeklinde çağırmamız gerekir.) - -$(LI 1 ve 2 numaralı maddelere benzer şekilde, döndürdüğümüz değer de bir $(C Nokta!T) olmak zorundadır.) - -$(LI Okumakta olduğumuz türün "(Nokta)" yerine örneğin "(Nokta!double)" olarak bildirilmesi için şablon türünün ismini $(C T.stringof)'tan ediniyoruz.) - -) - -$(H5 $(IX varsayılan şablon parametresi) Varsayılan şablon parametreleri) - -$(P -Şablonların getirdiği bu esneklik çok kullanışlı olsa da şablon parametrelerinin her durumda belirtilmeleri bazen gereksiz olabilir. Örneğin, $(C giriştenOku) işlev şablonu programda hemen hemen her yerde $(C int) ile kullanılıyordur ve belki de yalnızca bir kaç noktada örneğin $(C double) ile de kullanılıyordur. -) - -$(P -Böyle durumlarda şablon parametrelerine varsayılan türler verilebilir ve açıkça belirtilmediğinde o türler kullanılır. Varsayılan şablon parametre türleri $(C =) karakterinden sonra belirtilir: -) - ---- -T giriştenOku(T $(HILITE = int))(string soru) { - // ... -} - -// ... - - auto yaş = giriştenOku("Yaşınız?"); ---- - -$(P -Yukarıdaki işlev çağrısında şablon parametresi belirtilmediği halde $(C int) varsayılır; yukarıdaki çağrı $(C giriştenOku!int) ile aynıdır. -) - -$(P -Yapı ve sınıf şablonları için de varsayılan parametre türleri bildirilebilir. Ancak, şablon parametre listesinin boş olsa bile yazılması şarttır: -) - ---- -struct Nokta(T = int) { - // ... -} - -// ... - - Nokta!$(HILITE ()) merkez; ---- - -$(P -$(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği bölümünde) işlev parametreleri için anlatılana benzer şekilde, varsayılan şablon parametreleri ya bütün parametreler için ya da yalnızca sondaki parametreler için belirtilebilir: -) - ---- -void birŞablon(T0, T1 $(HILITE = int), T2 $(HILITE = char))() { - // ... -} ---- - -$(P -O şablonun son iki parametresinin belirtilmesi gerekmez ama birincisi şarttır: -) - ---- - birŞablon!string(); ---- - -$(P -O kullanımda ikinci parametre $(C int), üçüncü parametre de $(C char) olur. -) - -$(H5 Her şablon gerçekleştirmesi farklı bir türdür) - -$(P -Bir şablonun belirli bir tür veya türler için üretilmesi yepyeni bir tür oluşturur. Örneğin $(C Nokta!int) başlıbaşına bir türdür. Aynı şekilde, $(C Nokta!double) da başlıbaşına bir türdür. -) - -$(P -Bu türler birbirlerinden farklıdırlar: -) - ---- -Nokta!int nokta3 = Nokta!double(0.25, 0.75); $(DERLEME_HATASI) ---- - -$(P -Türlerin uyumsuz olduklarını gösteren bir derleme hatası alınır: -) - -$(SHELL_SMALL -Error: cannot implicitly convert expression (Nokta(0.25,0.75)) -of type $(HILITE Nokta!(double)) to $(HILITE Nokta!(int)) -) - -$(H5 Derleme zamanı olanağıdır) - -$(P -Şablon olanağı bütünüyle derleme zamanında işleyen ve derleyici tarafından işletilen bir olanaktır. Derleyicinin kod üretmesiyle ilgili olduğundan, program çalışmaya başladığında şablonların koda çevrilmeleri ve derlenmeleri çoktan tamamlanmıştır. -) - -$(H5 Sınıf şablonu örneği: yığın veri yapısı) - -$(P -Yapı ve sınıf şablonları $(I veri yapılarında) çok kullanılırlar. Bunun bir örneğini görmek için bir $(I yığın topluluğu) (stack container) tanımlayalım. -) - -$(P -Yığın topluluğu veri yapılarının en basit olanlarındandır: Elemanların üst üste durdukları düşünülür. Eklenen her eleman en üste yerleştirilir ve yalnızca bu üstteki elemana erişilebilir. Topluluktan eleman çıkartılmak istendiğinde de yalnızca en üstteki eleman çıkartılabilir. -) - -$(P -Kullanışlı olsun diye topluluktaki eleman sayısını veren bir nitelik de tasarlarsak, bu basit veri yapısının işlemlerini şöyle sıralayabiliriz: -) - -$(UL -$(LI Eleman eklemek) -$(LI Eleman çıkartmak) -$(LI Üsttekine eriştirmek) -$(LI Eleman adedini bildirmek) -) - -$(P -Bu veri yapısını gerçekleştirmek için D'nin iç olanaklarından olan dizilerden yararlanabiliriz. Dizinin sonuncu elemanı, yığın topluluğunun $(I üstteki) elemanı olarak kabul edilebilir. -) - -$(P -Dizi elemanı türünü de sabit bir tür olarak yazmak yerine şablon parametresi olarak belirlersek, bu veri yapısını her türle kullanabilecek şekilde şöyle tanımlayabiliriz: -) - ---- -$(CODE_NAME Yığın)class Yığın$(HILITE (T)) { -private: - - $(HILITE T)[] elemanlar; - -public: - - void ekle($(HILITE T) eleman) { - elemanlar ~= eleman; - } - - void çıkart() { - --elemanlar.length; - } - - $(HILITE T) üstteki() const @property { - return elemanlar[$ - 1]; - } - - size_t uzunluk() const @property { - return elemanlar.length; - } -} ---- - -$(P -Ekleme ve çıkartma işlemlerinin üye işlevler olmaları doğaldır. $(C üstteki) ve $(C uzunluk) işlevlerini ise nitelik olarak tanımlamayı daha uygun buldum. Çünkü ikisi de bu veri yapısıyla ilgili basit bir bilgi sunuyorlar. -) - -$(P -Bu sınıf için bir $(C unittest) bloğu tanımlayarak beklediğimiz şekilde çalıştığından emin olabiliriz. Aşağıdaki blok bu türü $(C int) türündeki elemanlarla kullanıyor: -) - ---- -unittest { - auto yığın = new Yığın$(HILITE !int); - - // Eklenen eleman üstte görünmeli - yığın.ekle(42); - assert(yığın.üstteki == 42); - assert(yığın.uzunluk == 1); - - // .üstteki ve .uzunluk elemanları etkilememeli - assert(yığın.üstteki == 42); - assert(yığın.uzunluk == 1); - - // Yeni eklenen eleman üstte görünmeli - yığın.ekle(100); - assert(yığın.üstteki == 100); - assert(yığın.uzunluk == 2); - - // Eleman çıkartılınca önceki görünmeli - yığın.çıkart(); - assert(yığın.üstteki == 42); - assert(yığın.uzunluk == 1); - - // Son eleman çıkartılınca boş kalmalı - yığın.çıkart(); - assert(yığın.uzunluk == 0); -} ---- - -$(P -Bu veri yapısını bir şablon olarak tanımlamış olmanın yararını görmek için onu kendi tanımladığımız bir türle kullanalım: -) - ---- -struct Nokta(T) { - T x; - T y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} ---- - -$(P -$(C double) türünde üyeleri bulunan $(C Nokta)'ları içeren bir $(C Yığın) şablonu şöyle oluşturulabilir: -) - ---- - auto noktalar = new Yığın!(Nokta!double); ---- - -$(P -Bu veri yapısına on tane rasgele değerli nokta ekleyen ve sonra onları teker teker çıkartan bir deneme programı şöyle yazılabilir: -) - ---- -$(CODE_XREF Yığın)import std.string; -import std.stdio; -import std.random; - -struct Nokta(T) { - T x; - T y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} - -// -0.50 ile 0.50 arasında rasgele bir değer döndürür -double rasgele_double() -out (sonuç) { - assert((sonuç >= -0.50) && (sonuç < 0.50)); - -} body { - return (double(uniform(0, 100)) - 50) / 100; -} - -// Belirtilen sayıda rasgele Nokta!double içeren bir Yığın -// döndürür -Yığın!(Nokta!double) rasgeleNoktalar(size_t adet) -out (sonuç) { - assert(sonuç.uzunluk == adet); - -} body { - auto noktalar = new Yığın!(Nokta!double); - - foreach (i; 0 .. adet) { - immutable nokta = Nokta!double(rasgele_double(), - rasgele_double()); - writeln("ekliyorum : ", nokta); - noktalar.ekle(nokta); - } - - return noktalar; -} - -void main() { - auto üstÜsteNoktalar = rasgeleNoktalar(10); - - while (üstÜsteNoktalar.uzunluk) { - writeln("çıkartıyorum: ", üstÜsteNoktalar.üstteki); - üstÜsteNoktalar.çıkart(); - } -} ---- - -$(P -Programın çıktısından anlaşılacağı gibi, eklenenlerle çıkartılanlar ters sırada olmaktadır: -) - -$(SHELL_SMALL -ekliyorum : (0.02,0.1) -ekliyorum : (0.23,-0.34) -ekliyorum : (0.47,0.39) -ekliyorum : (0.03,-0.05) -ekliyorum : (0.01,-0.47) -ekliyorum : (-0.25,0.02) -ekliyorum : (0.39,0.35) -ekliyorum : (0.32,0.31) -ekliyorum : (0.02,-0.27) -ekliyorum : (0.25,0.24) -çıkartıyorum: (0.25,0.24) -çıkartıyorum: (0.02,-0.27) -çıkartıyorum: (0.32,0.31) -çıkartıyorum: (0.39,0.35) -çıkartıyorum: (-0.25,0.02) -çıkartıyorum: (0.01,-0.47) -çıkartıyorum: (0.03,-0.05) -çıkartıyorum: (0.47,0.39) -çıkartıyorum: (0.23,-0.34) -çıkartıyorum: (0.02,0.1) -) - -$(H5 İşlev şablonu örneği: ikili arama algoritması) - -$(P -İkili arama algoritması, bir dizi halinde yan yana ve sıralı olarak duran değerler arasında arama yapan en hızlı algoritmadır. Bu algoritmanın bir diğer adı "ikiye bölerek arama", İngilizcesi de "binary search"tür. -) - -$(P -Çok basit bir algoritmadır: Sıralı olarak duran değerlerin ortadakine bakılır. Eğer aranan değere eşitse, değer bulunmuş demektir. Eğer değilse, o orta değerin aranan değerden daha küçük veya büyük olmasına göre ya sol yarıda ya da sağ yarıda aynı algoritma tekrarlanır. -) - -$(P -Böyle kendisini tekrarlayarak tarif edilen algoritmalar $(I özyinelemeli) olarak $(I da) programlanabilirler. Ben de bu işlevi yukarıdaki tanımına da çok uyduğu için kendisini çağıran bir işlev olarak yazacağım. -) - -$(P -İşlevi şablon olarak yazmak yerine, önce $(C int) için gerçekleştireceğim. Ondan sonra algoritmada kullanılan $(C int)'leri $(C T) yaparak onu bir şablona dönüştüreceğim. -) - ---- -/* Aranan değer dizide varsa değerin indeksini, yoksa - * size_t.max döndürür. */ -size_t ikiliAra(const int[] değerler, in int değer) { - // Dizi boşsa bulamadık demektir. - if (değerler.length == 0) { - return size_t.max; - } - - immutable ortaNokta = değerler.length / 2; - - if (değer == değerler[ortaNokta]) { - // Bulduk. - return ortaNokta; - - } else if (değer < değerler[ortaNokta]) { - // Sol tarafta aramaya devam etmeliyiz - return ikiliAra(değerler[0 .. ortaNokta], değer); - - } else { - // Sağ tarafta aramaya devam etmeliyiz - auto indeks = - ikiliAra(değerler[ortaNokta + 1 .. $], değer); - - if (indeks != size_t.max) { - // İndeksi düzeltmemiz gerekiyor çünkü bu noktada - // indeks, sağ taraftaki dilim ile ilgili olan - // ve sıfırdan başlayan bir değerdedir. - indeks += ortaNokta + 1; - } - - return indeks; - } - - assert(false, "Bu satıra hiç gelinmemeliydi"); -} ---- - -$(P -Yukarıdaki işlev bu basit algoritmayı şu dört adım halinde gerçekleştiriyor: -) - -$(UL -$(LI Dizi boşsa bulamadığımızı bildirmek için $(C size_t.max) döndür.) -$(LI Ortadaki değer aranan değere eşitse ortadaki değerin indeksini döndür.) -$(LI Aranan değer ortadaki değerden önceyse aynı işlevi sol tarafta devam ettir.) -$(LI Değilse aynı işlevi sağ tarafta devam ettir.) -) - -$(P -O işlevi deneyen bir kod da şöyle yazılabilir: -) - ---- -unittest { - auto dizi = [ 1, 2, 3, 5 ]; - assert(ikiliAra(dizi, 0) == size_t.max); - assert(ikiliAra(dizi, 1) == 0); - assert(ikiliAra(dizi, 4) == size_t.max); - assert(ikiliAra(dizi, 5) == 3); - assert(ikiliAra(dizi, 6) == size_t.max); -} ---- - -$(P -O işlevi bir kere $(C int) dizileri için yazıp doğru çalıştığından emin olduktan sonra, şimdi artık bir şablon haline getirebiliriz. Dikkat ederseniz, işlevin tanımında yalnızca iki yerde $(C int) geçiyor: -) - ---- -size_t ikiliAra(const int[] değerler, in int değer) { - // ... burada hiç int bulunmuyor ... -} ---- - -$(P -Parametrelerde geçen $(C int)'ler bu işlevin kullanılabildiği değerlerin türünü belirlemekteler. Onları şablon parametreleri olarak tanımlamak bu işlevin bir şablon haline gelmesi ve dolayısıyla başka türlerle de kullanılabilmesi için yeterlidir: -) - ---- -size_t ikiliAra$(HILITE (T))(const $(HILITE T)[] değerler, in $(HILITE T) değer) { - // ... -} ---- - -$(P -Artık o işlevi içindeki işlemlere uyan her türle kullanabiliriz. Dikkat ederseniz, elemanlar işlev içinde yalnızca $(C ==) ve $(C <) işleçleriyle kullanılıyorlar: -) - ---- - if (değer $(HILITE ==) değerler[ortaNokta]) { - // ... - - } else if (değer $(HILITE <) değerler[ortaNokta]) { - // ... ---- - -$(P -O yüzden, yukarıda tanımladığımız $(C Nokta) şablonu henüz bu türle kullanılmaya hazır değildir: -) - ---- -import std.string; - -struct Nokta(T) { - T x; - T y; - - string toString() const { - return format("(%s,%s)", x, y); - } -} - -void $(CODE_DONT_TEST)main() { - Nokta!int[] noktalar; - - foreach (i; 0 .. 15) { - noktalar ~= Nokta!int(i, i); - } - - assert(ikiliAra(noktalar, Nokta!int(10, 10)) == 10); -} ---- - -$(P -Bir derleme hatası alırız: -) - -$(SHELL_SMALL -Error: need member function $(HILITE opCmp()) for struct -const(Nokta!(int)) to compare -) - -$(P -O hata, $(C Nokta!int)'in bir karşılaştırma işleminde kullanılabilmesi için $(C opCmp) işlevinin tanımlanmış olması gerektiğini bildirir. Bu eksikliği gidermek için $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünde) gösterildiği biçimde bir $(C opCmp) tanımladığımızda program artık derlenir ve ikili arama işlevi $(C Nokta) şablonu ile de kullanılabilir: -) - ---- -struct Nokta(T) { -// ... - - int opCmp(const ref Nokta sağdaki) const { - return (x == sağdaki.x - ? y - sağdaki.y - : x - sağdaki.x); - } -} ---- - -$(H5 Özet) - -$(P -Şablonlar bu bölümde gösterdiklerimden çok daha kapsamlıdır. Devamını sonraya bırakarak bu bölümü şöyle özetleyebiliriz: -) - -$(UL - -$(LI Şablonlar kodun kalıp halinde tarif edilmesini ve derleyici tarafından gereken her tür için otomatik olarak üretilmesini sağlayan olanaktır.) - -$(LI Şablonlar bütünüyle derleme zamanında işleyen bir olanaktır.) - -$(LI Tanımlarken isimlerinden sonra şablon parametresi de belirtmek; işlevlerin, yapıların, ve sınıfların şablon haline gelmeleri için yeterlidir. - ---- -void işlevŞablonu$(HILITE (T))(T işlevParametresi) { - // ... -} - -class SınıfŞablonu$(HILITE (T)) { - // ... -} ---- - -) - -$(LI Şablon parametreleri ünlem işaretinden sonra açıkça belirtilebilirler. Tek parametre için parantez kullanmaya gerek yoktur. - ---- - auto nesne1 = new SınıfŞablonu!(double); - auto nesne2 = new SınıfŞablonu!double; // aynı şey ---- - -) - -$(LI Şablonun farklı türlerle her kullanımı farklı bir türdür. - ---- - assert(typeid(SınıfŞablonu!$(HILITE int)) != - typeid(SınıfŞablonu!$(HILITE uint))); ---- - -) - -$(LI Şablon parametreleri yalnızca işlev şablonlarında çıkarsanabilirler. - ---- - işlevŞablonu(42); // işlevŞablonu!int(42) çağrılır ---- - -) - -$(LI Şablonlar $(C :) karakterinden sonra belirtilen tür için özellenebilirler. - ---- -class SınıfŞablonu(T $(HILITE : dchar)) { - // ... -} ---- - -) - -$(LI Varsayılan şablon parametre türleri $(C =) karakterinden sonra belirtilebilir. - ---- -void işlevŞablonu(T $(HILITE = long))(T işlevParametresi) { - // ... -} ---- - -) - -$(LI $(C pragma(msg)) şablon yazarken yararlı olabilir.) - -) - -Macros: - SUBTITLE=Şablonlar - - DESCRIPTION=Derleyicinin belirli bir kalıba uygun olarak kod üretmesini sağlayan şablon (template) olanağı; D'nin 'türden bağımsız programlama'ya getirdiği çözüm - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial şablon şablonlar template türden bağımsız programlama generic programming işlev şablonu yapı şablonu sınıf şablonu - -SOZLER= -$(algoritma) -$(anahtar_sozcuk) -$(baglayici) -$(bagli_liste) -$(calisma_ortami) -$(cikarsama) -$(indeks) -$(kisitlama) -$(nitelik) -$(ozelleme) -$(ozyineleme) -$(sablon) -$(tasma) -$(topluluk) -$(ozgun_isim_uretme) -$(veri_yapilari) -$(yukleme) diff --git a/ddili/src/ders/d/sablonlar_ayrintili.d b/ddili/src/ders/d/sablonlar_ayrintili.d deleted file mode 100644 index 28abc60..0000000 --- a/ddili/src/ders/d/sablonlar_ayrintili.d +++ /dev/null @@ -1,2156 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX şablon) $(IX template) Ayrıntılı Şablonlar) - -$(P -Şablonların ne kadar kullanışlı olduklarını $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) görmüştük. Algoritmaların veya veri yapılarının tek tanımını yazarak birden çok türle çalışmalarını sağlayabiliyorduk. -) - -$(P -O bölümde şablonların en çok karşılaşılan kullanımlarını göstermiştim. Bu bölümde şablon olanağını daha ayrıntılı olarak göreceğiz. Devam etmeden önce en azından o bölümün sonundaki özeti bir kere daha gözden geçirmenizi öneririm; o bölümde anlatılanları burada tekrarlamamaya çalışacağım. -) - -$(P -Daha önce işlev, yapı, ve sınıf şablonlarını tanımıştık ve şablon parametrelerinin türler konusunda serbestlik getirdiklerini görmüştük. Bu bölümde; hem birlik ve arayüz şablonlarını da tanıyacağız; hem de şablon parametrelerinin değer, $(C this), $(C alias), ve çokuzlu çeşitleri olduğunu da göreceğiz. -) - -$(H5 $(IX kestirme söz dizimi, şablon) Kestirme ve uzun söz dizimi) - -$(P -C++ gibi başka dillerde de olduğu gibi D'nin şablonları çok güçlü olanaklardır. Buna rağmen, en çok yararlanılan kullanımlarının olabildiğince rahat ve anlaşılır olmasına çalışılmıştır. İşlev, yapı, veya sınıf şablonu tanımlamak; isminden sonra şablon parametre listesi eklemek kadar kolaydır: -) - ---- -T ikiKatı$(HILITE (T))(T değer) { - return 2 * değer; -} - -class Kesirli$(HILITE (T)) { - T pay; - T payda; - - // ... -} ---- - -$(P -Daha önce de görmüş olduğunuz yukarıdaki tanımlar, D'nin kestirme şablon tanımlarıdır. -) - -$(P -Aslında şablonlar daha uzun olarak $(C template) anahtar sözcüğü ile tanımlanırlar. Yukarıdaki söz dizimleri, aşağıdaki tanımların kısa eşdeğerleridir: -) - ---- -template ikiKatı$(HILITE (T)) { - T ikiKatı(T değer) { - return 2 * değer; - } -} - -template Kesirli$(HILITE (T)) { - class Kesirli { - T pay; - T payda; - - // ... - } -} ---- - -$(P -Derleyicinin her zaman için uzun tanımı kullandığını, ve kestirme söz dizimini arka planda şu şekilde uzun tanıma dönüştürdüğünü düşünebiliriz: -) - -$(OL -$(LI Tanımladığımız şablonu bir $(C template) kapsamı içine alır.) -$(LI O kapsama da aynı ismi verir.) -$(LI Şablon parametre listesini bizim tanımladığımız şablondan alır ve o kapsama verir.) -) - -$(P -Kestirme tanım; biraz aşağıda göreceğimiz $(I tek tanım içeren şablon) olanağı ile ilgilidir. -) - -$(H6 $(IX isim alanı, şablon) Şablon isim alanı) - -$(P -$(C template) bloğu, aslında bir seferde birden çok şablon tanımlanmasına da olanak verir: -) - ---- -template ŞablonBloğu(T) { - T birİşlev(T değer) { - return değer / 3; - } - - struct BirYapı { - T üye; - } -} ---- - -$(P -Yukarıdaki blokta bir işlev bir de yapı şablonu tanımlamaktadır. O şablonları örneğin $(C int) ve $(C double) türleri için, ve uzun isimleriyle şöyle kullanabiliriz: -) - ---- - auto sonuç = $(HILITE ŞablonBloğu!int).birİşlev(42); - writeln(sonuç); - - auto nesne = $(HILITE ŞablonBloğu!double).BirYapı(5.6); - writeln(nesne.üye); ---- - -$(P -Şablonun belirli bir türle kullanımı bir $(I isim alanı) tanımlar. Bloğun içindeki isimler o isim alanı açıkça belirtilerek kullanılabilirler. Bu isimler fazla uzun olabileceklerinden onlara $(LINK2 /ders/d/alias.html, $(C alias) bölümünde) gördüğümüz $(C alias) anahtar sözcüğü ile kısa takma isimler verilebilir: -) - ---- - alias KarakterYapısı = ŞablonBloğu!dchar.BirYapı; - -// ... - - auto nesne = $(HILITE KarakterYapısı)('ğ'); - writeln(nesne.üye); ---- - -$(H6 $(IX aynı isimde tanım içeren şablon) $(IX eponymous) Aynı isimde tanım içeren $(C template) blokları) - -$(P -Şablon bloğunun ismi ile aynı isimde tanım içeren şablon blokları içerdikleri o tanımın yerine geçerler. Bu, şimdiye kadarki şablonlarda kullandığımız kestirme söz dizimini sağlayan olanaktır. ($(I Not: Bu olanağa İngilizce'de) eponymous templates $(I denir.)) -) - -$(P -Örnek olarak, büyüklüğü 20 bayttan fazla olan türlerin $(I büyük) olarak kabul edildiği bir program olsun. Bir türün büyük olup olmadığının kararı şöyle bir şablonun içindeki bir $(C bool) değişken ile belirlenebilir: -) - ---- -template büyük_mü(T) { - enum büyük_mü = T.sizeof > 20; -} ---- - -$(P -Dikkat ederseniz, hem şablonun hem de içindeki tanımın isimleri aynıdır. Öyle olduğunda bu uzun şablon tanımının isim alanı ve içindeki tanım açıkça $(C büyük_mü!int.büyük_mü) diye yazılmaz, kısaca yalnızca şablonun isim alanı yazılır: -) - ---- - writeln($(HILITE büyük_mü!int)); ---- - -$(P -Yukarıdaki işaretli bölüm, şablon içindeki aynı isimli $(C bool) yerine geçer. Yukarıdaki kod çıktıya $(C false) yazar çünkü $(C büyük_mü!int), şablon içindeki $(C bool) türündeki değişkendir ve $(C int)'in uzunluğu 4 bayt olduğundan o $(C bool) değişkenin değeri $(C false)'tur. -) - -$(P -Yukarıdaki aynı isimde tanım içeren şablon, kısa söz dizimiyle de tanımlanabilir: -) - ---- -enum büyük_mü$(HILITE (T)) = T.sizeof > 20; ---- - -$(P -Aynı isimde tanım içeren şablonların yaygın bir kullanımı, türlere takma isimler vermektir. Örneğin, aşağıdaki şablon verilen türlerden büyük olanına eşdeğer olan bir $(C alias) tanımlamaktadır: -) - ---- -$(CODE_NAME Büyüğü)template Büyüğü(A, B) { - static if (A.sizeof < B.sizeof) { - alias Büyüğü = B; - - } else { - alias Büyüğü = A; - } -} ---- - -$(P -Sekiz bayttan oluşan $(C long) türü dört bayttan oluşan $(C int) türünden daha büyük olduğundan $(C Büyüğü!(int, long)), $(C long)'un eşdeğeri olur. Bu çeşit şablonlar $(C A) ve $(C B) gibi türlerin kendilerinin şablon parametreleri oldukları durumlarda özellikle yararlıdırlar: -) - ---- -$(CODE_XREF Büyüğü)// ... - -/* Bu işlevin dönüş türü, şablon parametrelerinden büyük - * olanıdır: Ya A ya da B. */ -auto hesapla(A, B)(A a, B b) { - $(HILITE Büyüğü!(A, B)) sonuç; - // ... - return sonuç; -} - -void main() { - auto h = hesapla(1, 2$(HILITE L)); - static assert(is (typeof(h) == $(HILITE long))); -} ---- - -$(H5 Şablon çeşitleri) - -$(H6 İşlev, sınıf, ve yapı şablonları) - -$(P -Bu alt başlığı bütünlük amacıyla yazdım. -) - -$(P -Yukarıda da görüldüğü gibi, bu tür şablonlarla hem $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) hem de daha sonraki örneklerde çok karşılaştık. -) - -$(H6 $(IX üye işlev şablonu) Üye işlev şablonları) - -$(P -Yapı ve sınıf üye işlevleri de şablon olabilir. Örneğin, aşağıdaki $(C ekle()) üye işlev şablonu, içindeki işlemlerle uyumlu olduğu sürece her türden değişkeni kabul eder (bu örnekteki tek şart, o değişkenin $(C to!string) ile kullanılabilmesidir): -) - ---- -class Toplayıcı { - string içerik; - - void ekle$(HILITE (T))(auto ref const T değer) { - import std.conv; - içerik ~= değer.to!string; - } -} ---- - -$(P -Ancak, şablonların teoride sonsuz farklı kullanımı olabileceğinden, $(LINK2 /ders/d/tureme.html, sanal işlev) olamazlar çünkü derleyici şablonun hangi kullanımlarının sınıfın arayüzüne dahil edileceğine karar veremez. (Sanal işlev olamadıklarından $(C abstract) anahtar sözcüğü ile de tanımlanamazlar.) -) - -$(P -Örneğin, aşağıdaki alt sınıfın $(C ekle()) şablonu üst sınıftaki aynı isimli işlevin yeni tanımını veriyormuş gibi görünse de aslında isim gizlemeye neden olur (isim gizlemeyi $(LINK2 /ders/d/alias.html, alias bölümünde) görmüştük): -) - ---- -class Toplayıcı { - string içerik; - - void ekle(T)(auto ref const T değer) { - import std.conv; - içerik ~= değer.to!string; - } -} - -class KümeParantezliToplayıcı : Toplayıcı { - /* Bu şablon üst sınıftakinin yeni tanımı değildir; üst - * sınıftaki 'ekle' ismini gizlemektedir. */ - void ekle(T)(auto ref const T değer) { - import std.string; - super.ekle(format("{%s}", değer)); - } -} - -void toplayıcıyıDoldur($(HILITE Toplayıcı) toplayıcı) { - /* Aşağıdaki işlev çağrıları sanal değildir. Buradaki - * 'toplayıcı' parametresinin türü 'Toplayıcı' olduğundan - * her iki çağrı da Toplayıcı.ekle şablonuna - * devredilirler. */ - - toplayıcı.ekle(42); - toplayıcı.ekle("merhaba"); -} - -void main() { - auto toplayıcı = new $(HILITE KümeParantezliToplayıcı)(); - toplayıcıyıDoldur(toplayıcı); - - import std.stdio; - writeln(toplayıcı.içerik); -} ---- - -$(P -Sonuçta, asıl nesnenin türü $(C KümeParantezliToplayıcı) olduğu halde, $(C toplayıcıyıDoldur()) işlevinin içindeki bütün çağrılar parametresinin türü olan $(C Toplayıcı)'ya sevk edilir. İçerik $(C KümeParantezliToplayıcı.ekle()) işlevinin yerleştirdiği küme parantezlerini içermemektedir: -) - -$(SHELL -42merhaba $(SHELL_NOTE KümeParantezliToplayıcı'nın işi değil) -) - -$(H6 $(IX birlik şablonu) $(IX union şablonu) Birlik şablonları) - -$(P -Birlik şablonları, yapı şablonları ile aynı şekilde tanımlanırlar. Birlik şablonları için de kestirme şablon söz dizimi kullanılabilir. -) - -$(P -Bir örnek olarak, $(LINK2 /ders/d/birlikler.html, Birlikler bölümünde) tanımladığımız $(C IpAdresi) birliğinin daha genel ve daha kullanışlı olanını tasarlamaya çalışalım. O bölümdeki birlik; değer olarak $(C uint) türünü kullanıyordu. O değerin parçalarına erişmek için kullanılan dizinin elemanlarının türü de $(C ubyte) idi: -) - ---- -union IpAdresi { - uint değer; - ubyte[4] baytlar; -} ---- - -$(P -O birlik, hem IPv4 adresi değeri tutuyordu, hem de o değerin parçalarına ayrı ayrı erişme olanağı veriyordu. -) - -$(P -Aynı kavramı daha genel isimler de kullanarak bir şablon halinde şöyle tanımlayabiliriz: -) - ---- -union ParçalıDeğer$(HILITE (AsılTür, ParçaTürü)) { - AsılTür değer; - ParçaTürü[/* gereken eleman adedi */] parçalar; -} ---- - -$(P -Bu birlik şablonu, asıl değerin ve alt parçalarının türünü serbestçe tanımlama olanağı verir. Asıl tür ve parça türü, birbirlerinden bağımsız olarak seçilebilirler. -) - -$(P -Burada gereken bir işlem, parça dizisinin uzunluğunun kullanılan türlere bağlı olarak hesaplanmasıdır. $(C IpAdresi) birliğinde, $(C uint)'in dört adet $(C ubyte) parçası olduğunu bildiğimiz için sabit olarak 4 yazabilmiştik. Bu şablonda ise dizinin uzunluğu, kullanılan türlere göre otomatik olarak hesaplanmalıdır. -) - -$(P -Türlerin bayt olarak uzunluklarının $(C .sizeof) niteliğinden öğrenilebildiğini biliyoruz. Kaç parça gerektiği bilgisini $(C .sizeof) niteliğinden yararlanan ve kısa söz dizimine olanak veren bir şablon içinde hesaplayabiliriz: -) - ---- -$(CODE_NAME elemanAdedi)template elemanAdedi(AsılTür, ParçaTürü) { - enum elemanAdedi = (AsılTür.sizeof + (ParçaTürü.sizeof - 1)) - / ParçaTürü.sizeof; -} ---- - -$(P $(I Not: O hesaptaki $(C (ParçaTürü.sizeof - 1)) ifadesi, türlerin uzunluklarının birbirlerine tam olarak bölünemediği durumlarda gerekir. Asıl türün 5 bayt, parça türünün 2 bayt olduğunu düşünün. Aslında 3 parça gerektiği halde o ifade eklenmediğinde 5/2 hesabının sonucu tamsayı kırpılması nedeniyle 2 çıkar.) -) - -$(P -Artık parça dizisinin eleman adedi olarak o şablonun değerini kullanabiliriz ve böylece birliğin tanımı tamamlanmış olur: -) - ---- -$(CODE_NAME ParçalıDeğer)$(CODE_XREF elemanAdedi)union ParçalıDeğer(AsılTür, ParçaTürü) { - AsılTür değer; - ParçaTürü[elemanAdedi!(AsılTür, ParçaTürü)] parçalar; -} ---- - -$(P -Daha önce tanımladığımız $(C IpAdresi) birliğinin eşdeğeri olarak bu şablonu kullanmak istesek, türleri $(C IpAdresi)'nde olduğu gibi sırasıyla $(C uint) ve $(C ubyte) olarak belirtmemiz gerekir: -) - ---- -$(CODE_XREF ParçalıDeğer)import std.stdio; - -void main() { - auto adres = ParçalıDeğer!$(HILITE (uint, ubyte))(0xc0a80102); - - foreach (eleman; adres.parçalar) { - write(eleman, ' '); - } -} ---- - -$(P -$(LINK2 /ders/d/birlikler.html, Birlikler bölümünde) gördüğümüz çıktının aynısını elde ederiz: -) - -$(SHELL_SMALL -2 1 168 192 -) - -$(P -Bu şablonun getirdiği esnekliği görmek için IPv4 adresinin parçalarını iki adet $(C ushort) olarak edinmek istediğimizi düşünelim. Bu sefer, $(C ParçalıDeğer) şablonunun $(C ParçaTürü) parametresi olarak $(C ushort) yazmak yeterlidir: -) - ---- - auto adres = ParçalıDeğer!(uint, $(HILITE ushort))(0xc0a80102); ---- - -$(P -Alışık olmadığımız bir düzende olsa da, bu seferki çıktı iki $(C ushort)'tan oluşmaktadır: -) - -$(SHELL_SMALL -258 49320 -) - -$(H6 $(IX arayüz şablonu) $(IX interface şablonu) Arayüz şablonları) - -$(P -Arayüz şablonları arayüzde kullanılan türler, değerler, vs. konusunda serbestlik getirirler. Arayüz şablonlarında da kestirme tanım kullanılabilir. -) - -$(P -Örnek olarak, renkli nesnelerin arayüzünü tanımlayan ama renk olarak hangi türün kullanılacağını serbest bırakan bir arayüz tasarlayalım: -) - ---- -interface RenkliNesne(RenkTürü) { - void renklendir(RenkTürü renk); -} ---- - -$(P -O arayüz, kendisinden türeyen sınıfların $(C renklendir) işlevini tanımlamalarını gerektirir; ama renk olarak ne tür kullanılacağı konusunu serbest bırakır. -) - -$(P -Bir sitedeki bir çerçeveyi temsil eden bir sınıf; renk olarak kırmızı, yeşil, ve maviden oluşan üçlü bir yapı kullanabilir: -) - ---- -struct KırmızıYeşilMavi { - ubyte kırmızı; - ubyte yeşil; - ubyte mavi; -} - -class SiteÇerçevesi : RenkliNesne$(HILITE !KırmızıYeşilMavi) { - void renklendir(KırmızıYeşilMavi renk) { - // ... - } -} ---- - -$(P -Öte yandan, renk olarak ışığın frekansını kullanmak isteyen bir sınıf, renk için frekans değerine uygun olan başka bir türden yararlanabilir: -) - ---- -alias Frekans = double; - -class Lamba : RenkliNesne$(HILITE !Frekans) { - void renklendir(Frekans renk) { - // ... - } -} ---- - -$(P -Yine $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünden) hatırlayacağınız gibi, "her şablon gerçekleştirmesi farklı bir türdür". Buna göre, $(C RenkliNesne!KırmızıYeşilMavi) ve $(C RenkliNesne!Frekans) arayüzleri, farklı arayüzlerdir. Bu yüzden, onlardan türeyen sınıflar da birbirlerinden bağımsız sıradüzenlerin parçalarıdırlar; $(C SiteÇerçevesi) ve $(C Lamba), birbirlerinden bağımsızdır. -) - -$(H5 $(IX parametre, şablon) Şablon parametre çeşitleri) - -$(P -Şimdiye kadar gördüğümüz şablonlar, hep türler konusunda serbestlik getiriyorlardı. -) - -$(P -Yukarıdaki örneklerde de kullandığımız $(C T) ve $(C RenkTürü) gibi şablon parametreleri, hep türleri temsil ediyorlardı. Örneğin $(C T)'nin anlamı, şablonun kod içindeki kullanımına bağlı olarak $(C int), $(C double), $(C Öğrenci), vs. gibi bir tür olabiliyordu. -) - -$(P -Şablon parametreleri; değer, $(C this), $(C alias), ve çokuzlu da olabilirler. -) - -$(H6 $(IX tür şablon parametresi) Tür parametreleri) - -$(P -Bu alt başlığı bütünlük amacıyla yazdım. -) - -$(P -Şimdiye kadar gördüğümüz bütün şablon parametreleri zaten hep tür parametreleriydi. -) - -$(H6 $(IX değer şablon parametresi) Değer parametreleri) - -$(P -Şablon parametresi olarak değerler de kullanılabilir. Bu, şablonun tanımı ile ilgili bir değerin serbest bırakılmasını sağlar. -) - -$(P -Şablonlar derleme zamanı olanakları olduklarından, değer olarak kullanılan şablon parametresinin derleme zamanında hesaplanabilmesi şarttır. Bu yüzden, programın çalışması sırasında hesaplanan, örneğin girişten okunan bir değer kullanılamaz. -) - -$(P -Bir örnek olarak, belirli sayıda köşeden oluşan şekilleri temsil eden yapılar tanımlayalım: -) - ---- -struct Üçgen { - Nokta[3] köşeler; -// ... -} - -struct Dörtgen { - Nokta[4] köşeler; -// ... -} - -struct Beşgen { - Nokta[5] köşeler; -// ... -} ---- - -$(P -Örnek kısa olsun diye başka üyelerini göstermedim. Normalde, o türlerin başka üyelerinin ve işlevlerinin de bulunduğunu ve hepsinde tamamen aynı şekilde tanımlandıklarını varsayalım. Sonuçta, dizi uzunluğunu belirleyen $(I değer) dışında, o yapıların tanımları aynı olsun. -) - -$(P -Değer şablon parametreleri böyle durumlarda yararlıdır. Yukarıdaki tanımlar yerine tek yapı şablonu tanımlanabilir. Yeni tanım genel amaçlı olduğu için, ismini de o şekillerin genel ismi olan $(I poligon) koyarak şöyle tanımlayabiliriz: -) - ---- -struct Poligon$(HILITE (size_t köşeAdedi)) { - Nokta[köşeAdedi] köşeler; -// ... -} ---- - -$(P -O yapı şablonu parametre olarak $(C size_t) türünde ve $(C köşeAdedi) isminde bir şablon parametresi almaktadır. O parametre değeri yapının tanımında herhangi bir yerde kullanılabilir. -) - -$(P -Artık o şablonu istediğimiz sayıda köşesi olan poligonları ifade etmek için kullanabiliriz: -) - ---- - auto yüzKöşeli = Poligon!100(); ---- - -$(P -Yine $(C alias)'tan yararlanarak kullanışlı isimler verebiliriz: -) - ---- -alias Üçgen = Poligon!3; -alias Dörtgen = Poligon!4; -alias Beşgen = Poligon!5; - -// ... - - auto üçgen = Üçgen(); - auto dörtgen = Dörtgen(); - auto beşgen = Beşgen(); ---- - -$(P -Yukarıdaki $(I değer) şablon parametresinin türü $(C size_t) idi. Değer derleme zamanında bilindiği sürece değer türü olarak bütün temel türler, yapılar, diziler, dizgiler, vs. kullanılabilir. -) - ---- -struct S { - int i; -} - -// Türü S yapısı olan değer şablon parametresi -void foo($(HILITE S s))() { - // ... -} - -void main() { - // İşlev şablonunun S(42) hazır değeriyle kullanılması - foo!(S(42))(); -} ---- - -$(P -Başka bir örnek olarak, basit XML elemanları oluşturmakta kullanılan bir sınıf şablonu tasarlayalım. Bu basit XML tanımı, çok basitçe şu çıktıyı üretmek için kullanılsın: -) - -$(UL -$(LI Önce $(C <) $(C >) karakterleri arasında elemanın ismi: $(C <isim>)) -$(LI Sonra elemanın değeri) -$(LI En sonunda da $(C </) $(C >) karakterleri arasında yine elemanın ismi: $(C </isim>)) -) - -$(P -Örneğin değeri 42 olan bir elemanın $(C <isim>42</isim>) şeklinde görünmesini isteyelim. -) - -$(P -Eleman isimlerini bir sınıf şablonunun $(C string) türündeki bir değer parametresi olarak belirleyebiliriz: -) - ---- -$(CODE_NAME XmlElemanı)import std.string; - -class XmlElemanı$(HILITE (string isim)) { - double değer; - - this(double değer) { - this.değer = değer; - } - - override string toString() const { - return format("<%s>%s", isim, değer, isim); - } -} ---- - -$(P -Bu örnekteki şablon parametresi, şablonda kullanılan bir türle değil, bir $(C string) $(I değeriyle) ilgilidir. O $(C string)'in değeri de şablon içinde gereken her yerde kullanılabilir. -) - -$(P -$(C alias)'tan yararlanarak kullanışlı tür isimleri de tanımlayarak: -) - ---- -$(CODE_XREF XmlElemanı)alias Konum = XmlElemanı!"konum"; -alias Sıcaklık = XmlElemanı!"sıcaklık"; -alias Ağırlık = XmlElemanı!"ağırlık"; - -void main() { - Object[] elemanlar; - - elemanlar ~= new Konum(1); - elemanlar ~= new Sıcaklık(23); - elemanlar ~= new Ağırlık(78); - - writeln(elemanlar); -} ---- - -$(P -$(I Not: Ben bu örnekte kısa olsun diye ve nasıl olsa bütün sınıf sıradüzenlerinin en üstünde bulunduğu için bir $(C Object) dizisi kullandım. O sınıf şablonu aslında daha uygun bir arayüz sınıfından da türetilebilirdi.) -) - -$(P -Yukarıdaki kodun çıktısı: -) - -$(SHELL_SMALL -[<konum>1</konum>, <sıcaklık>23</sıcaklık>, <ağırlık>78</ağırlık>] -) - -$(P -Değer parametrelerinin de varsayılan değerleri olabilir. Örneğin, herhangi boyutlu bir uzaydaki noktaları temsil eden bir yapı tasarlayalım. Noktaların koordinat değerleri için kullanılan tür ve uzayın kaç boyutlu olduğu, şablon parametreleri ile belirlensin: -) - ---- -struct Konum(T, size_t boyut $(HILITE = 3)) { - T[boyut] koordinatlar; -} ---- - -$(P -$(C boyut) parametresinin varsayılan bir değerinin bulunması, bu şablonun o parametre belirtilmeden de kullanılabilmesini sağlar: -) - ---- - Konum!double merkez; // üç boyutlu uzayda bir nokta ---- - -$(P -Gerektiğinde farklı bir değer de belirtilebilir: -) - ---- - Konum!(int, 2) nokta; // iki boyutlu düzlemde bir nokta ---- - -$(P -$(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği bölümünde) $(I özel anahtar sözcüklerin) varsayılan parametre değeri olarak kullanıldıklarında farklı etkileri olduğunu görmüştük. -) - -$(P -Benzer biçimde, varsayılan şablon parametre değeri olarak kullanıldıklarında şablonun tanımlandığı yerle değil, şablonun kullanıldığı yerle ilgili bilgi verirler: -) - ---- -import std.stdio; - -void işlev(T, - string işlevİsmi = $(HILITE __FUNCTION__), - string dosya = $(HILITE __FILE__), - size_t satır = $(HILITE __LINE__))(T parametre) { - writefln("%s dosyasının %s numaralı satırındaki %s " ~ - "işlevi tarafından kullanılıyor.", - dosya, satır, işlevİsmi); -} - -void main() { - işlev(42); $(CODE_NOTE $(HILITE satır 15)) -} ---- - -$(P -Yukarıdaki özel anahtar sözcükler şablonun tanımında geçtikleri halde şablonu kullanmakta olan $(C main()) işlevine işaret ederler: -) - -$(SHELL -deneme.d dosyasının $(HILITE 15 numaralı satırındaki) deneme.$(HILITE main) -işlevi tarafından kullanılıyor. -) - -$(P -$(C __FUNCTION__) anahtar sözcüğünü aşağıdaki işleç yükleme örneğinde de kullanacağız. -) - -$(H6 $(IX this, şablon parametresi) Üye işlevler için $(C this) şablon parametreleri) - -$(P -Üye işlevler de şablon olarak tanımlanabilirler. Üye işlev şablonlarının da tür ve değer parametreleri bulunabilir, ve normal işlev şablonlarından beklendiği gibi çalışırlar. -) - -$(P -Ek olarak, üye işlev şablonlarının parametreleri $(C this) anahtar sözcüğü ile de tanımlanabilir. Bu durumda, o anahtar sözcükten sonra yazılan isim, o nesnenin $(C this) referansının türü haline gelir. ($(I Not: Burada, çoğunlukla kurucu işlevler içinde gördüğümüz $(C this.üye = değer) kullanımındaki $(C this) referansından, yani) nesnenin kendisini ifade eden $(I referanstan bahsediyoruz.)) -) - ---- -struct BirYapı(T) { - void birİşlev$(HILITE (this KendiTürüm))() const { - writeln("Bu nesnenin türü: ", KendiTürüm.stringof); - } -} ---- - -$(P -$(C KendiTürüm) şablon parametresi o üye işlevin işlemekte olduğu nesnenin asıl türüdür: -) - ---- - auto m = BirYapı!int(); - auto c = const(BirYapı!int)(); - auto i = immutable(BirYapı!int)(); - - m.birİşlev(); - c.birİşlev(); - i.birİşlev(); ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -Bu nesnenin türü: BirYapı!int -Bu nesnenin türü: const(BirYapı!int) -Bu nesnenin türü: immutable(BirYapı!int) -) - -$(P -Görüldüğü gibi, $(C KendiTürüm) hem $(C T)'nin bu kullanımda $(C int) olan karşılığını hem de $(C const) ve $(C immutable) gibi tür belirteçlerini içerir. -) - -$(P -$(C this) şablon parametresi, şablon olmayan yapıların veya sınıfların üye işlevlerinde de kullanılabilir. -) - -$(P -$(C this) şablon parametreleri özellikle iki bölüm sonra göreceğimiz $(I şablon katmalarında) yararlıdır. O bölümde bir örneğini göreceğiz. -) - -$(H6 $(IX alias şablon parametresi) $(C alias) parametreleri) - -$(P -$(C alias) şablon parametrelerine karşılık olarak D programlarında geçebilen bütün yasal isimler veya ifadeler kullanılabilir. Bu isimler yerel isimler, modül isimleri, başka şablon isimleri, vs. olabilirler. Tek koşul, o parametrenin şablon içindeki kullanımının o parametreye uygun olmasıdır. -) - -$(P -Bu olanak, $(C filter) ve $(C map) gibi şablonların da işlemleri dışarıdan almalarını sağlayan olanaktır. -) - -$(P -Örnek olarak, hangi yerel değişkeni değiştireceği kendisine bir $(C alias) parametre olarak bildirilen bir yapıya bakalım: -) - ---- -struct BirYapı($(HILITE alias değişken)) { - void birİşlev(int değer) { - değişken = değer; - } -} ---- - -$(P -O yapının üye işlevi, $(C değişken) isminde bir değişkene bir atama yapmaktadır. O değişkenin programdaki hangi değişken olduğu; bu şablon tanımlandığı zaman değil, şablon kullanıldığı zaman belirlenir: -) - ---- - int x = 1; - int y = 2; - - auto nesne = BirYapı!$(HILITE x)(); - nesne.birİşlev(10); - writeln("x: ", x, ", y: ", y); ---- - -$(P -Yapı şablonunun yukarıdaki kullanımında yerel $(C x) değişkeni belirtildiği için, $(C birİşlev) içindeki atama onu etkiler: -) - -$(SHELL_SMALL -x: $(HILITE 10), y: 2 -) - -$(P -Öte yandan, $(C BirYapı!y) biçimindeki kullanımda $(C değişken) değişkeni $(C y) yerine geçerdi. -) - -$(P -Başka bir örnek olarak, $(C filter) ve $(C map) gibi $(C alias) parametresini işlev olarak kullanan bir işlev şablonuna bakalım: -) - ---- -void çağıran(alias işlev)() { - write("çağırıyorum: "); - $(HILITE işlev()); -} ---- - -$(P -$(C ()) parantezlerinden anlaşıldığı gibi, $(C çağıran) ismindeki işlev şablonu, kendisine verilen parametreyi bir işlev olarak kullanmaktadır. Ayrıca, parantezlerin içinin boş olmasından anlaşıldığı gibi, o işlev parametre göndermeden çağrılmaktadır. -) - -$(P -Parametre almadıkları için o kullanıma uyan iki de işlev bulunduğunu varsayalım: -) - ---- -void birinci() { - writeln("birinci"); -} - -void ikinci() { - writeln("ikinci"); -} ---- - -$(P -O işlevler, $(C çağıran) şablonu içindeki kullanıma uydukları için o şablonun $(C alias) parametresinin değeri olabilirler: -) - ---- - çağıran!birinci(); - çağıran!ikinci(); ---- - -$(P -Belirtilen işlevin çağrıldığını görürüz: -) - -$(SHELL_SMALL -çağırıyorum: birinci -çağırıyorum: ikinci -) - -$(P -$(C alias) şablon parametrelerini her çeşit şablonla kullanabilirsiniz. Önemli olan, o parametrenin şablon içindeki kullanıma uymasıdır. Örneğin, yukarıdaki $(C alias) parametresi yerine bir değişken kullanılması derleme hatasına neden olacaktır: -) - ---- - int değişken; - çağıran!değişken(); $(DERLEME_HATASI) ---- - -$(P -Aldığımız hata, $(C ()) karakterlerinden önce bir işlev beklendiğini, $(C int) türündeki $(C değişken)'in uygun olmadığını belirtir: -) - -$(SHELL_SMALL -Error: $(HILITE function expected before ()), not değişken of type int -) - -$(P -Her ne kadar işaretlediğim satır nedeniyle olsa da, derleme hatası aslında $(C çağıran) işlevinin içindeki $(C işlev()) satırı için verilir. Derleyicinin gözünde hatalı olan; şablona gönderilen parametre değil, o parametrenin şablondaki kullanılışıdır. Uygunsuz şablon parametrelerini önlemenin bir yolu, $(I şablon kısıtlamaları) tanımlamaktır. Bunu aşağıda göreceğiz. -) - -$(P -Öte yandan, bir işlev gibi çağrılabilen her olanak bu örnekteki $(C alias) parametresi yerine kullanılabilir. Aşağıda hem $(C opCall()) işlecini yüklemiş olan bir sınıf ile hem de bir isimsiz işlev ile kullanımını görüyoruz: -) - ---- -class Sınıf { - void opCall() { - writeln("Sınıf.opCall çağrıldı."); - } -} - -// ... - - auto nesne = new Sınıf(); - çağıran!$(HILITE nesne)(); - - çağıran!($(HILITE {) writeln("İsimsiz işlev çağrıldı."); $(HILITE }))(); ---- - -$(P -Çıktısı: -) - -$(SHELL -çağırıyorum: Sınıf.opCall çağrıldı. -çağırıyorum: İsimsiz işlev çağrıldı. -) - -$(P -$(C alias) parametreleri de özellenebilirler. Ancak, özelleme söz dizimi diğer parametre çeşitlerinden farklıdır; özellenen tür $(C alias) ile parametre ismi arasına yazılır: -) - ---- -import std.stdio; - -void foo(alias değişken)() { - writefln("Genel tanım %s türündeki '%s' değişkeni için işliyor.", - typeof(değişken).stringof, değişken.stringof); -} - -void foo(alias $(HILITE int) i)() { - writefln("int özellemesi '%s' değişkeni için işliyor.", - i.stringof); -} - -void foo(alias $(HILITE double) d)() { - writefln("double özellemesi '%s' değişkeni için işliyor.", - d.stringof); -} - -void main() { - string isim; - foo!isim(); - - int adet; - foo!adet(); - - double uzunluk; - foo!uzunluk(); -} ---- - -$(P -Asıl değişkenlerin isimlerinin de şablon içinde görülebildiklerine ayrıca dikkat edin: -) - -$(SHELL -Genel tanım string türündeki 'isim' değişkeni için işliyor. -int özellemesi 'adet' değişkeni için işliyor. -double özellemesi 'uzunluk' değişkeni için işliyor. -) - -$(H6 $(IX çokuzlu şablon parametresi) Çokuzlu parametreleri) - -$(P -İşlevlerin belirsiz sayıda parametre alabildiklerini biliyoruz. Örneğin, $(C writeln) işlevini istediğimiz sayıda parametre ile çağırabiliriz. Bu tür işlevlerin nasıl tanımlandıklarını $(LINK2 /ders/d/parametre_serbestligi.html, Parametre Serbestliği bölümünde) görmüştük. -) - -$(P -$(IX ..., şablon parametresi) $(IX belirsiz sayıda şablon parametresi) Aynı serbestlik şablon parametrelerinde de bulunur. Şablon parametrelerinin sayısını ve çeşitlerini serbest bırakmak şablon parametre listesinin en sonuna bir çokuzlu ismi ve $(C ...) karakterleri yazmak kadar basittir. İsmi belirtilen çokuzlu, şablon parametre değerlerini ifade eden bir $(C AliasSeq) gibi kullanılabilir. -) - -$(P -Bunu parametreleri hakkında bilgi veren basit bir işlev şablonunda görelim: -) - ---- -$(CODE_NAME bilgiVer)void bilgiVer($(HILITE T...))(T parametreler) { - // ... -} ---- - -$(P -Şablon parametresi olan $(C T...), $(C bilgiVer) işlev şablonunun belirsiz sayıda parametre ile çağrılabilmesini sağlar. Hem $(C T) hem de $(C parametreler) çokuzludur: -) - -$(UL -$(LI $(C T), işlev parametre değerlerinin türlerinden oluşan çokuzludur.) -$(LI $(C parametreler), işlev parametre değerlerinden oluşan çokuzludur.) -) - -$(P -İşlevin üç farklı türden parametre ile çağrıldığı duruma bakalım: -) - ---- -$(CODE_XREF bilgiVer)import std.stdio; - -// ... - -void main() { - bilgiVer($(HILITE 1, "abc", 2.3)); -} ---- - -$(P -Aşağıda $(C parametreler)'in $(C foreach) ile kullanımını görüyoruz: -) - ---- -void bilgiVer(T...)(T parametreler) { - // 'parametreler' bir AliasSeq gibi kullanılır: - foreach (i, parametre; $(HILITE parametreler)) { - writefln("%s: %s türünde %s", - i, typeof(parametre).stringof, parametre); - } -} ---- - -$(P -Çıktısı: -) - -$(SHELL_SMALL -0: int türünde 1 -1: string türünde abc -2: double türünde 2.3 -) - -$(P -Parametrelerin türleri $(C typeof(parametre)) yerine $(C T[i]) ile de edinilebilir. -) - -$(P -İşlev şablonu parametre türlerinin derleyici tarafından çıkarsanabildiklerini biliyorsunuz. Yukarıdaki $(C bilgiVer()) çağrısı sırasında parametre değerlerine bakılarak onların türlerinin sırasıyla $(C int), $(C string), ve $(C double) oldukları derleyici tarafından çıkarsanmıştır. -) - -$(P -Bazı durumlarda ise şablon parametrelerinin açıkça belirtilmeleri gerekebilir. Örneğin, $(C std.conv.to) şablonu hedef türünü açıkça belirtilmesi gereken bir şablon parametresi olarak alır: -) - ---- - to!$(HILITE string)(42); ---- - -$(P -Şablon parametreleri açıkça belirtildiğinde o parametreler değer, tür, veya başka çeşitlerden karışık olabilirler. Öyle durumlarda her şablon parametresinin tür veya başka çeşitten olup olmadığının belirlenmesi ve şablon kodlarının buna uygun olarak yazılması gerekebilir. Şablon çeşitlerini ayırt etmenin yolu, parametreleri yine $(C AliasSeq) gibi kullanmaktır. -) - -$(P -Bunun örneğini görmek için yapı tanımı üreten bir işlev tasarlayalım. Bu işlev belirtilen türlerde ve isimlerde üyeleri olan yapı tanımı içeren kaynak kod üretsin ve $(C string) olarak döndürsün. İlk olarak yapının ismi verildikten sonra üyelerin tür ve isimleri çiftler halinde belirtilsinler: -) - ---- -import std.stdio; - -void $(CODE_DONT_TEST)main() { - writeln(yapıTanımla!("Öğrenci", - string, "isim", - int, "numara", - int[], "notlar")()); -} ---- - -$(P -Yukarıdaki programın çıktısı aşağıdaki kaynak kod olmalıdır: -) - -$(SHELL -struct Öğrenci { - string isim; - int numara; - int[] notlar; -} -) - -$(P -$(I Not: $(C yapıTanımla) gibi kod üreten işlevlerin yararlarını $(LINK2 /ders/d/katmalar.html, daha sonraki bir bölümdeki) $(C mixin) anahtar sözcüğünü öğrenirken göreceğiz.) -) - -$(P -O sonucu üreten şablon aşağıdaki gibi tanımlanabilir. Koddaki denetimlerde $(C is) ifadesinden de yararlanıldığına dikkat edin. Hatırlarsanız, $(C is (parametre)) ifadesi $(C parametre) geçerli bir tür olduğunda $(C true) üretiyordu: -) - ---- -import std.string; - -string yapıTanımla(string isim, $(HILITE Üyeler)...)() { - /* Üyeler tür ve isim olarak çiftler halinde - * belirtilmelidir. Önce bunu denetleyelim. */ - static assert(($(HILITE Üyeler).length % 2) == 0, - "Üyeler çiftler halinde belirtilmedilir."); - - /* Önce yapı tanımının ilk satırını oluşturuyoruz. */ - string sonuç = "struct " ~ isim ~ "\n{\n"; - - foreach (i, parametre; $(HILITE Üyeler)) { - static if (i % 2) { - /* Tek numaralı parametreler üye isimlerini - * belirliyorlar. Onları hemen burada kullanmak - * yerine aşağıdaki 'else' bölümünde Üyeler[i+1] - * söz dizimiyle kullanacağız. - * - * Yine de üye isimlerinin string olarak - * belirtildiklerini burada denetleyelim. */ - static assert(is (typeof(parametre) == string), - "Üye ismi " ~ parametre.stringof ~ - " string değil."); - - } else { - /* Bu durumda 'parametre' üyenin türünü - * belirtiyor. Öncelikle bu parametrenin gerçekten - * bir tür olduğunu denetleyelim. */ - static assert(is (parametre), - parametre.stringof ~ " tür değil."); - - /* Tür ve üye isimlerini kullanarak satırı - * oluşturuyoruz. - * - * Not: Burada Üyeler[i] yerine 'parametre' de - * yazabilirdik. */ - sonuç ~= format(" %s %s;\n", - $(HILITE Üyeler[i]).stringof, $(HILITE Üyeler[i+1])); - } - } - - /* Yapı tanımının kapama parantezi. */ - sonuç ~= "}"; - - return sonuç; -} - -import std.stdio; - -void main() { - writeln(yapıTanımla!("Öğrenci", - string, "isim", - int, "numara", - int[], "notlar")()); -} ---- - -$(H5 $(IX typeof(this)) $(IX typeof(super)) $(IX typeof(return))$(C typeof(this)), $(C typeof(super)), ve $(C typeof(return))) - -$(P -Şablonların genel tanımlar ve genel algoritmalar olmalarının bir etkisi, bazı tür isimlerinin yazımlarının güç veya olanaksız olmasıdır. Aşağıdaki üç özel $(C typeof) çeşidi böyle durumlarda yararlıdır. Her ne kadar bu bölümde tanıtılıyor olsalar da, bu olanaklar şablon olmayan kodlarda da geçerlidirler. -) - -$(UL - -$(LI $(C typeof(this)), yapının veya sınıfın $(C this) referansının türünü (yani, kendi tam türünü) verir. Bu olanak yapı veya sınıfın tanımı içinde olmak koşuluyla üye işlevler dışında da kullanılabilir. - ---- -struct Liste(T) { - // T int olduğunda 'sonraki'nin türü Liste!int'tir - typeof(this) *sonraki; - // ... -} ---- - -) - -$(LI $(C typeof(super)) bir sınıfın üst sınıfının türünü (yani, $(C super) referansının türünü) verir. - ---- -class ListeOrtak(T) { - // ... -} - -class Liste(T) : ListeOrtak!T { - // T int olduğunda 'sonraki'nin türü ListeOrtak!int'tir - typeof(super) *sonraki; - // ... -} ---- - -) - -$(LI $(C typeof(return)) bir işlevin dönüş türünü o işlev içerisindeyken verir. - -$(P -Örneğin, yukarıdaki $(C hesapla()) işlevinin dönüş türünü $(C auto) yerine daha açıklayıcı olmak için $(C Büyüğü!(A, B)) olarak tanımlayabiliriz. (Daha açık olmanın bir yararı, işlev açıklamalarının en azından bir bölümünün gereksiz hale gelmesidir.) -) - ---- -$(HILITE Büyüğü!(A, B)) hesapla(A, B)(A a, B b) { - // ... -} ---- - -$(P -$(C typeof(return)), dönüş türünün işlevin içinde tekrarlanmasını önler: -) - ---- -Büyüğü!(A, B) hesapla(A, B)(A a, B b) { - $(HILITE typeof(return)) sonuç; // Ya A ya da B - // ... - return sonuç; -} ---- - -) - -) - -$(H5 Şablon özellemeleri) - -$(P -Özellemeleri de $(LINK2 /ders/d/sablonlar.html, Şablonlar bölümünde) görmüştük. Aşağıdaki $(I meta programlama) başlığında da özelleme örnekleri göreceksiniz. -) - -$(P -Tür parametrelerinde olduğu gibi, başka çeşit şablon parametreleri de özellenebilir. Aşağıdaki şablonun hem genel hem de 0 değeri için özel tanımı görülüyor: -) - ---- -void birİşlev(int birDeğer)() { - // ... genel tanımı ... -} - -void birİşlev(int birDeğer $(HILITE : 0))() { - // ... sıfıra özel tanımı ... -} ---- - -$(H5 $(IX meta programlama) Meta programlama) - -$(P -Kod üretme ile ilgili olduklarından şablonlar diğer D olanaklarından daha üst düzey programlama araçları olarak kabul edilirler. Şablonlar bir anlamda kod oluşturan kodlardır. Kodların daha üst düzey kodlarla oluşturulmaları kavramına $(I meta programlama) denir. -) - -$(P -Şablonların derleme zamanı olanakları olmaları normalde çalışma zamanında yapılan işlemlerin derleme zamanına taşınmalarına olanak verir. ($(I Not: Aynı amaçla) Derleme Zamanında İşlev İşletme (CTFE) $(I olanağı da kullanılabilir. Bu konuyu daha sonraki bir bölümde göreceğiz.)) -) - -$(P -$(IX özyineleme) Şablonların bu amaçla derleme zamanında $(I işletilmeleri), çoğunlukla özyineleme üzerine kuruludur. -) - -$(P -Bunun bir örneğini görmek için 0'dan başlayarak belirli bir sayıya kadar olan bütün sayıların toplamını hesaplayan normal bir işlev düşünelim. Bu işlev, parametre olarak örneğin 4 aldığında 0+1+2+3+4'ün toplamını döndürsün: -) - ---- -int topla(int sonDeğer) { - int sonuç = 0; - - foreach (değer; 0 .. sonDeğer + 1) { - sonuç += değer; - } - - return sonuç; -} ---- - -$(P -Aynı işlevi özyinelemeli olarak da yazabiliriz: -) - ---- -int topla(int sonDeğer) { - return (sonDeğer == 0 - ? sonDeğer - : sonDeğer + $(HILITE topla)(sonDeğer - 1)); -} ---- - -$(P -Özyinelemeli işlev kendi düzeyindeki değeri bir önceki hesaba eklemektedir. İşlevde 0 değerinin özel olarak kullanıldığını görüyorsunuz; özyineleme onun sayesinde sonlanmaktadır. -) - -$(P -İşlevlerin normalde çalışma zamanı olanakları olduklarını biliyoruz. $(C topla)'yı çalışma zamanında gerektikçe çağırabilir ve sonucunu kullanabiliriz: -) - ---- - writeln(topla(4)); ---- - -$(P -Aynı sonucun yalnızca derleme zamanında gerektiği durumlarda ise, o hesap bir işlev şablonuyla da gerçekleştirilebilir. Yapılması gereken; değerin işlev parametresi olarak değil, şablon parametresi olarak kullanılmasıdır: -) - ---- -// Uyarı: Bu kod yanlıştır -int topla($(HILITE int sonDeğer))() { - return (sonDeğer == 0 - ? sonDeğer - : sonDeğer + topla$(HILITE !(sonDeğer - 1))()); -} ---- - -$(P -Bu şablon da hesap sırasında kendisinden yararlanmaktadır. Kendisini, $(C sonDeğer)'in bir eksiği ile kullanmakta ve hesabı yine özyinelemeli olarak elde etmeye çalışmaktadır. Ne yazık ki o kod yazıldığı şekilde çalışamaz. -) - -$(P -Derleyici, $(C ?:) işlecini çalışma zamanında işleteceği için, yukarıdaki özyineleme derleme zamanında sonlanamaz: -) - ---- - writeln(topla!4()); $(DERLEME_HATASI) ---- - -$(P -Derleyici, aynı şablonun sonsuz kere dallandığını anlar ve bir hata ile sonlanır: -) - -$(SHELL_SMALL -Error: template instance deneme.topla!($(HILITE -296)) recursive expansion -) - -$(P -Şablon parametresi olarak verdiğimiz 4'ten geriye doğru -296'ya kadar saydığına bakılırsa, derleyici şablonların özyineleme sayısını 300 ile sınırlamaktadır. -) - -$(P -Meta programlamada özyinelemeyi kırmanın yolu, şablon özellemeleri kullanmaktır. Bu durumda, aynı şablonu 0 değeri için özelleyebilir ve özyinelemenin kırılmasını bu sayede sağlayabiliriz: -) - ---- -$(CODE_NAME topla)// Genel tanım -int topla(int sonDeğer)() { - return sonDeğer + topla!(sonDeğer - 1)(); -} - -// Sıfır değeri için özellemesi -int topla(int sonDeğer $(HILITE : 0))() { - return 0; -} ---- - -$(P -Derleyici, $(C sonDeğer)'in sıfırdan farklı değerleri için hep genel tanımı kullanır ve en sonunda 0 değeri için özel tanıma geçer. O tanım da basitçe 0 değerini döndürdüğü için özyineleme sonlanmış olur. -) - -$(P -O işlev şablonunu şöyle bir programla deneyebiliriz: -) - ---- -$(CODE_XREF topla)import std.stdio; - -void main() { - writeln(topla!4()); -} ---- - -$(P -Şimdi hatasız olarak derlenecek ve 4+3+2+1+0'ın toplamını üretecektir: -) - -$(SHELL_SMALL -10 -) - -$(P -Burada dikkatinizi çekmek istediğim önemli nokta, $(C topla!4()) işlevinin bütünüyle derleme zamanında işletiliyor olmasıdır. Sonuçta derleyicinin ürettiği kod, $(C writeln)'e doğrudan 10 hazır değerini göndermenin eşdeğeridir: -) - ---- - writeln(10); // topla!4()'lü kodun eşdeğeri ---- - -$(P -Derleyicinin ürettiği kod, 10 hazır değerini doğrudan programa yazmak kadar hızlı ve basittir. O 10 hazır değeri, yine de 4+3+2+1+0 hesabının sonucu olarak bulunmaktadır; ancak o hesap, şablonların özyinelemeli olarak kullanılmalarının sonucunda derleme zamanında işletilmektedir. -) - -$(P -Burada görüldüğü gibi, meta programlamanın yararlarından birisi, şablonların derleme zamanında işletilmelerinden yararlanarak normalde çalışma zamanında yapılmasına alıştığımız hesapların derleme zamanına taşınabilmesidir. -) - -$(P -Yukarıda da söylediğim gibi, daha sonraki bir bölümde göstereceğim CTFE olanağı bazı meta programlama yöntemlerini D'de gereksiz hale getirir. -) - - -$(H5 $(IX çok şekillilik, derleme zamanı) $(IX derleme zamanı çok şekilliliği) Derleme zamanı çok şekilliliği) - -$(P -Bu kavram, İngilizce'de "compile time polymorphism" olarak geçer. -) - -$(P -Nesne yönelimli programlamada çok şekilliliğin sınıf türetme ile sağlandığını biliyorsunuz. Örneğin bir işlev parametresinin bir arayüz olması, o parametre yerine o arayüzden türemiş her sınıfın kullanılabileceği anlamına gelir. -) - -$(P -Daha önce gördüğümüz bir örneği hatırlayalım: -) - ---- -import std.stdio; - -interface SesliAlet { - string ses(); -} - -class Keman : SesliAlet { - string ses() { - return "♩♪♪"; - } -} - -class Çan : SesliAlet { - string ses() { - return "çın"; - } -} - -void sesliAletKullan($(HILITE SesliAlet alet)) { - // ... bazı işlemler ... - writeln(alet.ses()); - // ... başka işlemler ... -} - -void main() { - sesliAletKullan(new Keman); - sesliAletKullan(new Çan); -} ---- - -$(P -Yukarıdaki $(C sesliAletKullan) işlevi çok şekillilikten yararlanmaktadır. Parametresi $(C SesliAlet) olduğu için, ondan türemiş olan bütün türlerle kullanılabilir. -) - -$(P -Yukarıdaki son cümlede geçen $(I bütün türlerle kullanılabilme) kavramını şablonlardan da tanıyoruz. Böyle düşününce, şablonların da bir çeşit çok şekillilik sunduklarını görürüz. Şablonlar bütünüyle derleyicinin derleme zamanındaki kod üretmesiyle ilgili olduklarından, şablonların sundukları çok şekilliliğe $(I derleme zamanı çok şekilliliği) denir. -) - -$(P -Doğrusu, her iki çok şekillilik de $(I bütün) türlerle kullanılamaz. Her ikisinde de türlerin uymaları gereken bazı koşullar vardır. -) - -$(P -Çalışma zamanı çok şekilliliği, belirli bir arayüzden türeme ile kısıtlıdır. -) - -$(P -Derleme zamanı çok şekilliliği ise şablon içindeki kullanıma uyma ile kısıtlıdır. Şablon parametresi, şablon içindeki kullanımda derleme hatasına neden olmuyorsa, o şablonla kullanılabilir. ($(I Not: Eğer tanımlanmışsa, şablon kısıtlamalarına da uyması gerekir. Şablon kısıtlamalarını biraz aşağıda anlatacağım.)) -) - -$(P -Örneğin, yukarıdaki $(C sesliAletKullan) işlevi bir şablon olarak yazıldığında, $(C ses) üye işlevi bulunan bütün türlerle kullanılabilir: -) - ---- -void sesliAletKullan$(HILITE (T))(T alet) { - // ... bazı işlemler ... - writeln(alet.ses()); - // ... başka işlemler ... -} - -class Araba { - string ses() { - return "düt düt"; - } -} - -// ... - - sesliAletKullan(new Keman); - sesliAletKullan(new Çan); - sesliAletKullan(new Araba); ---- - -$(P -Yukarıdaki şablon, diğerleriyle kalıtım ilişkisi bulunmayan $(C Araba) türü ile de kullanılabilmiştir. -) - -$(P -$(IX ördek tipleme) Derleme zamanı çok şekilliliği $(I ördek tipleme) olarak da bilinir. Ördek tipleme, asıl türü değil, davranışı ön plana çıkartan mizahi bir terimdir. -) - -$(H5 $(IX kod şişmesi) Kod şişmesi) - -$(P -Şablonlar kod üretme ile ilgilidirler. Derleyici, şablonun farklı parametrelerle her kullanımı için farklı kod üretir. -) - -$(P -Örneğin yukarıda en son yazdığımız $(C sesliAletKullan) işlev şablonu, programda kullanıldığı her tür için ayrı ayrı üretilir ve derlenir. Programda yüz farklı tür ile çağrıldığını düşünürsek; derleyici o işlev şablonunun tanımını, her tür için ayrı ayrı, toplam yüz kere oluşturacaktır. -) - -$(P -Programın boyutunun büyümesine neden olduğu için bu etkiye $(I kod şişmesi) (code bloat) denir. Çoğu programda sorun oluşturmasa da, şablonların bu özelliğinin akılda tutulması gerekir. -) - -$(P -Öte yandan, $(C sesliAletKullan) işlevinin ilk yazdığımız $(C SesliAlet) alan tanımında, yani şablon olmayan tanımında, böyle bir kod tekrarı yoktur. Derleyici, o işlevi bir kere derler ve her $(C SesliAlet) türü için aynı işlevi çağırır. İşlev tek olduğu halde her hayvanın kendisine özel olarak davranabilmesi, derleyici tarafından işlev göstergeleriyle sağlanır. Derleyici her tür için farklı bir işlev göstergesi kullanır ve böylece her tür için farklı üye işlev çağrılır. Çalışma zamanında çok küçük bir hız kaybına yol açsa da, işlev göstergeleri kullanmanın çoğu programda önemi yoktur ve zaten bu çözümü sunan en hızlı gerçekleştirmedir. -) - -$(P -Burada sözünü ettiğim hız etkilerini tasarımlarınızda fazla ön planda tutmayın. Program boyutunun artması da, çalışma zamanında fazladan işlemler yapılması da hızı düşürecektir. Belirli bir programda hangisinin etkisinin daha fazla olduğuna ancak o program denenerek karar verilebilir. -) - -$(H5 $(IX kısıtlama, şablon) $(IX şablon kısıtlaması) Şablon kısıtlamaları) - -$(P -Şablonların her tür ve değerdeki şablon parametresi ile çağrılabiliyor olmalarının getirdiği bir sorun vardır. Uyumsuz bir parametre kullanıldığında, bu uyumsuzluk ancak şablonun kendi kodları derlenirken farkedilebilir. Bu yüzden, derleme hatasında belirtilen satır numarası, şablon bloğuna işaret eder. -) - -$(P -Yukarıdaki $(C sesliAletKullan) şablonunu $(C ses) isminde üye işlevi bulunmayan bir türle çağıralım: -) - ---- -class Fincan { - // ... ses() işlevi yok ... -} - -// ... - - sesliAletKullan(new Fincan); // ← uyumsuz bir tür ---- - -$(P -Oradaki hata, şablonun uyumsuz bir türle çağrılıyor olmasıdır. Oysa derleme hatası, şablon içindeki kullanıma işaret eder: -) - ---- -void sesliAletKullan(T)(T alet) { - // ... bazı işlemler ... - writeln(alet.ses()); $(DERLEME_HATASI) - // ... başka işlemler ... -} ---- - -$(P -Bunun bir sakıncası, belki de bir kütüphane modülünde tanımlı olan bir şablona işaret edilmesinin, hatanın o kütüphanede olduğu yanılgısını uyandırabileceğidir. Daha önemlisi, asıl hatanın hangi satırda olduğunun hiç bildirilmiyor olmasıdır. -) - -$(P -Böyle bir sorunun arayüzlerde bulunmadığına dikkat edin. Parametre olarak arayüz alacak şekilde yazılmış olan bir işlev, ancak o arayüzden türemiş olan türlerle çağrılabilir. Türeyen her tür arayüzün işlevlerini gerçekleştirmek zorunda olduğu için, işlevin uyumsuz bir türle çağrılması olanaksızdır. O durumda derleme hatası, işlevi uygunsuz türle çağıran satıra işaret eder. -) - -$(P -Şablonların yalnızca belirli koşulları sağlayan türlerle kullanılmaları şablon kısıtlamaları ile sağlanır. Şablon kısıtlamaları, şablon bloğunun hemen öncesine yazılan $(C if) deyiminin içindeki mantıksal ifadelerdir: -) - ---- -void birŞablon(T)() - if (/* ... kısıtlama koşulu ... */) { - // ... şablonun tanımı ... -} ---- - -$(P -Derleyici bu şablon tanımını ancak kısıtlama koşulu $(C true) olduğunda göze alır. Koşulun $(C false) olduğu durumda ise bu şablon tanımını gözardı eder. -) - -$(P -Şablonlar derleme zamanı olanakları olduklarından şablon kısıtlamaları da derleme zamanında işletilirler. Bu yüzden, $(LINK2 /ders/d/is_ifadesi.html, $(C is) İfadesi bölümünde) gördüğümüz ve derleme zamanında işletildiğini öğrendiğimiz $(C is) ifadesi ile de çok kullanılırlar. Bunun örneklerini aşağıda göstereceğim. -) - -$(H6 $(IX tek üyeli çokuzlu parametre yöntemi) $(IX çokuzlu şablon parametresi, tek üye) Tek üyeli çokuzlu parametre yöntemi) - -$(P -Bazen tek şablon parametresi gerekebilir ama o parametrenin tür, değer, veya $(C alias) çeşidinden olabilmesi istenir. Bunu sağlamanın bir yolu, çokuzlu çeşidinde parametre kullanmak ama çokuzlunun uzunluğunu bir şablon kısıtlaması ile tek olarak belirlemektir: -) - ---- -template birŞablon(T...) - $(HILITE if (T.length == 1)) { - static if (is ($(HILITE T[0]))) { - // Şablonun tek parametresi bir türmüş - enum bool birŞablon = /* ... */; - - } else { - // Şablonun tek parametresi tür değilmiş - enum bool birŞablon = /* ... */; - } -} ---- - -$(P -Daha ileride göreceğimiz $(C std.traits) modülündeki bazı şablonlar bu yöntemden yararlanır. -) - -$(H6 $(IX isimli şablon kısıtlaması) İsimli kısıtlama yöntemi) - -$(P -Şablon kısıtlamaları bazı durumlarda yukarıdakinden çok daha karmaşık olabilirler. Bunun üstesinden gelmenin bir yolu, benim $(I isimli kısıtlama) olarak adlandırdığım bir yöntemdir. Bu yöntem D'nin dört olanağından yararlanarak kısıtlamaya anlaşılır bir isim verir. Bu dört olanak; isimsiz işlev, $(C typeof), $(C is) ifadesi, ve tek tanım içeren şablonlardır. -) - -$(P -Bu yöntemi burada daha çok bir kalıp olarak göstereceğim ve her ayrıntısına girmemeye çalışacağım. -) - -$(P -Parametresini belirli şekilde kullanan bir işlev şablonu olsun: -) - ---- -void kullan(T)(T nesne) { - // ... - nesne.hazırlan(); - // ... - nesne.uç(42); - // ... - nesne.kon(); - // ... -} ---- - -$(P -Şablon içindeki kullanımından anlaşıldığı gibi, bu şablonun kullanıldığı türlerin $(C hazırlan), $(C uç), ve $(C kon) isminde üç üye işlevinin bulunması gerekir (UFCS olanağı sayesinde normal işlevler de olabilirler.) O işlevlerden $(C uç)'un ayrıca $(C int) türünde bir de parametresi olmalıdır. -) - -$(P -Bu kısıtlamayı $(C is) ve $(C typeof) ifadelerinden yararlanarak şöyle yazabiliriz: -) - ---- -void kullan(T)(T nesne) - if (is (typeof(nesne.hazırlan())) && - is (typeof(nesne.uç(1))) && - is (typeof(nesne.kon()))) { - // ... -} ---- - -$(P -O koşulun anlamını aşağıda daha ayrıntılı olarak göreceğiz. Şimdilik $(C is (typeof(nesne.hazırlan()))) kullanımını bir kalıp olarak $(I eğer o tür $(C nesne.hazırlan()) çağrısını destekliyorsa) anlamında kabul edebilirsiniz. İşleve $(C is (typeof(nesne.uç(1)))) biçiminde parametre verildiğinde ise, $(I o işlev $(C int) türünde parametre de alıyorsa) diye kabul edebilirsiniz. -) - -$(P -Yukarıdaki gibi bir kısıtlama istendiği gibi çalışıyor olsa da, her zaman için tam açık olmayabilir. Onun yerine, o şablon kısıtlamasının ne anlama geldiğini daha iyi açıklayan bir isim verilebilir: -) - ---- -void kullan(T)(T nesne) - if (uçabilir_mi!T) { // ← isimli kısıtlama - // ... -} ---- - -$(P -Bu kısıtlama bir öncekinden daha açıktır. Bu şablonun $(I uçabilen) türlerle çalıştığını okunaklı bir şekilde belgeler. -) - -$(P -Yukarıdaki gibi isimli kısıtlamalar şu kalıba uygun olarak tanımlanırlar: -) - ---- -template uçabilir_mi(T) { - enum uçabilir_mi = is (typeof( - { - T uçan; - uçan.hazırlan(); // uçmaya hazırlanabilmeli - uçan.uç(1); // belirli mesafe uçabilmeli - uçan.kon(); // istendiğinde konabilmeli - }())); -} ---- - -$(P -O yöntemde kullanılan D olanaklarını ve birbirleriyle nasıl etkileştiklerini çok kısaca göstermek istiyorum: -) - ---- -template uçabilir_mi(T) { - // (6) (5) (4) - enum uçabilir_mi = is (typeof( - $(HILITE {) // (1) - T uçan; // (2) - uçan.hazırlan(); - uçan.uç(1); - uçan.kon(); - // (3) - $(HILITE })())); -} ---- - -$(OL - -$(LI $(B İsimsiz işlev:) İsimsiz işlevleri $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler bölümünde) görmüştük. Yukarıda sarıyla işaretlenmiş olan blok parantezleri isimsiz bir işlev tanımlar. -) - -$(LI $(B İşlev bloğu:) İşlev bloğu, kısıtlaması tanımlanmakta olan türü asıl şablonda kullanıldığı gibi kullanır. Yukarıdaki blokta önce bu türden bir nesne oluşturulmakta ve o türün sahip olması gereken üç üye işlevi çağrılmaktadır. ($(I Not: Bu kodlar $(C typeof) tarafından kullanılırlar ama hiçbir zaman işletilmezler.)) -) - -$(LI $(B İşlevin işletilmesi:) Bir işlevin sonuna yazılan $(C ()) parantezleri normalde o işlevi işletir. Ancak, yukarıdaki işletme bir $(C typeof) içinde olduğundan bu işlev hiçbir zaman işletilmez. (Bu, bir sonraki maddede açıklanıyor.)) - -$(LI $(IX typeof) $(B $(C typeof) ifadesi:) $(C typeof), şimdiye kadarki örneklerde çok kullandığımız gibi, kendisine verilen ifadenin türünü üretir. - -$(P -$(C typeof)'un önemli bir özelliği, türünü ürettiği ifadeyi işletmemesidir. $(C typeof), bir ifadenin $(I eğer işletilse) ne türden bir değer üreteceğini bildirir: -) - ---- - int i = 42; - typeof(++i) j; // 'int j;' yazmakla aynı anlamdadır - - assert(i == 42); // ++i işletilmemiştir ---- - -$(P -Yukarıdaki $(C assert)'ten de anlaşıldığı gibi, $(C ++i) ifadesi işletilmemiştir. $(C typeof), yalnızca o ifadenin türünü üretmiş ve böylece $(C j) de $(C int) olarak tanımlanmıştır. -) - -$(P -Eğer $(C typeof)'a verilen ifadenin geçerli bir türü yoksa, $(C typeof) $(C void) bile olmayan geçersiz bir tür döndürür. -) - -$(P -Eğer $(C uçabilir_mi) şablonuna gönderilen tür, o isimsiz işlev içindeki kodlarda gösterildiği gibi derlenebiliyorsa, $(C typeof) geçerli bir tür üretir. Eğer o tür işlev içindeki kodlardaki gibi derlenemiyorsa, $(C typeof) geçersiz bir tür döndürür. -) - -) - -$(LI $(B $(C is) ifadesi:) $(LINK2 /ders/d/is_ifadesi.html, $(C is) İfadesi bölümünde) $(C is) ifadesinin birden fazla kullanımını görmüştük. Buradaki $(C is ($(I Tür))) şeklindeki kullanımı, kendisine verilen türün anlamlı olduğu durumda $(C true) değerini üretir: - ---- - int i; - writeln(is (typeof(i))); // true - writeln(is (typeof(varOlmayanBirİsim))); // false ---- - -$(P -Yukarıdaki ikinci ifadede bilinmeyen bir isim kullanıldığı halde derleyici hata vermez. Programın çıktısı ikinci satır için $(C false) değerini içerir: -) - -$(SHELL_SMALL -true -false -) - -$(P -Bunun nedeni, $(C typeof)'un ikinci kullanım için geçersiz bir tür üretmiş olmasıdır. -) - -) - -$(LI $(B Tek tanım içeren şablon:) Daha yukarıda anlatıldığı gibi, $(C uçabilir_mi) şablonunun içinde tek tanım bulunduğundan ve o tanımın ismi şablonun ismiyle aynı olduğundan; $(C uçabilir_mi) şablonu, içerdiği $(C uçabilir_mi) $(C enum) sabit değeri yerine geçer. -) - -) - -$(P -Sonuçta, yukarıdaki $(C kullan) işlev şablonu bütün bu olanaklar sayesinde isimli bir kısıtlama edinmiş olur: -) - ---- -void kullan(T)(T nesne) - if (uçabilir_mi!T) { - // ... -} ---- - -$(P -O şablonu birisi uyumlu, diğeri uyumsuz iki türle çağırmayı deneyelim: -) - ---- -// Şablondaki kullanıma uyan bir tür -class ModelUçak { - void hazırlan() { - } - - void uç(int mesafe) { - } - - void kon() { - } -} - -// Şablondaki kullanıma uymayan bir tür -class Güvercin { - void uç(int mesafe) { - } -} - -// ... - - kullan(new ModelUçak); // ← derlenir - kullan(new Güvercin); $(DERLEME_HATASI) ---- - -$(P -İsimli veya isimsiz, bir şablon kısıtlaması tanımlanmış olduğundan, bu derleme hatası artık şablonun içine değil şablonun uyumsuz türle kullanıldığı satıra işaret eder. -) - -$(H5 $(IX yükleme, işleç) $(IX çok boyutlu işleç yükleme) $(IX işleç yükleme, çok boyutlu) Şablonların çok boyutlu işleç yüklemedeki kullanımı) - -$(P -$(C opDollar), $(C opIndex), ve $(C opSlice) işlevlerinin eleman erişimi ve dilimleme amacıyla kullanıldıklarını $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme bölümünde) görmüştük. Bu işlevler o bölümdeki gibi $(I tek boyutlu) kullanımlarında aşağıdaki görevleri üstlenirler: -) - -$(UL - -$(LI $(C opDollar): Topluluktaki eleman adedini döndürür.) - -$(LI $(C opSlice): Topluluğun ya bütün elemanlarını ya da bir bölümünü ifade eden aralık nesnesi döndürür.) - -$(LI $(C opIndex): Belirtilen elemana erişim sağlar.) - -) - -$(P -O işlevlerin şablon olarak yüklenebilen çeşitleri de vardır. Bu işlev şablonlarının anlamları yukarıdakilerden farklıdır. Özellikle $(C opSlice)'ın görevinin $(C opIndex) tarafından üstlenilmiş olduğuna dikkat edin: -) - -$(UL - -$(LI $(IX opDollar şablonu) $(C opDollar) şablonu: Topluluğun belirli bir boyutunun uzunluğunu döndürür. Hangi boyutun uzunluğunun döndürüleceği şablon parametresinden anlaşılır: - ---- - size_t opDollar$(HILITE (size_t boyut))() const { - // ... - } ---- - -) - -$(LI $(IX opSlice şablonu) $(C opSlice) şablonu: Dilimi belirleyen sayı aralığı bilgisini döndürür. (Örneğin, $(C dizi[baş..son]) yazımındaki $(C baş) ve $(C son) değerleri.) Bu bilgi $(C Tuple!(size_t, size_t)) veya eşdeğeri bir tür olarak döndürülebilir. Aralığın hangi boyutla ilgili olduğu şablon parametresinden anlaşılır: - ---- - Tuple!(size_t, size_t) opSlice$(HILITE (size_t boyut))(size_t baş, - size_t son) { - return tuple(baş, son); - } ---- - -) - -$(LI $(IX opIndex şablonu) $(C opIndex) şablonu: Belirtilen alt topluluğu ifade eden bir aralık döndürür. Aralığın sınırları şablonun çokuzlu parametrelerinden anlaşılır: - ---- - Aralık opIndex$(HILITE (A...))(A parametreler) { - // ... - } ---- - -) - -) - -$(P -$(IX opIndexAssign şablonu) $(IX opIndexOpAssign şablonu) $(C opIndexAssign) ve $(C opIndexOpAssign)'ın da şablon çeşitleri vardır. Bunlar da belirli bir alt topluluktaki elemanlar üzerinde işlerler. -) - -$(P -Çok boyutlu işleçleri tanımlayan türler aşağıdaki gibi çok boyutlu erişim ve dilimleme söz dizimlerinde kullanılabilirler: -) - ---- - // İndekslerle belirlenen alt topluluktaki - // elemanların değerlerini 42 yapar: - m[a, b..c, $-1, d..e] = 42; -// ↑ ↑ ↑ ↑ -// boyutlar: 0 1 2 3 ---- - -$(P -Öyle bir ifade görüldüğünde önce $(C $) karakterleri için $(C opDollar) ve konum aralıkları için $(C opSlice) perde arkasında otomatik olarak çağrılır. Elde edilen uzunluk ve aralık bilgileri yine otomatik olarak $(C opIndexAssign)'a parametre olarak geçirilir. Sonuçta, yukarıdaki ifade yerine aşağıdaki ifade işletilmiş olur (boyut değerleri sarı ile işaretli): -) - ---- - // Üsttekinin eşdeğeri: - m.opIndexAssign( - 42, $(CODE_NOTE atanan değer) - a, $(CODE_NOTE sıfırıncı boyutun parametresi) - m.opSlice!$(HILITE 1)(b, c), $(CODE_NOTE birinci boyutun parametresi) - m.opDollar!$(HILITE 2)() - 1, $(CODE_NOTE ikinci boyutun parametresi) - m.opSlice!$(HILITE 3)(d, e)); $(CODE_NOTE üçüncü boyutun parametresi) ---- - -$(P -Sonuçta, $(C opIndexAssign) işlemde kullanacağı alt aralığı çokuzlu şablon parametrelerinin türlerine ve değerlerine bakarak belirler. -) - -$(H6 İşleç yükleme örneği) - -$(P -Aşağıdaki $(C Matris) türü bu işleçlerin nasıl tanımlandıklarının bir örneğini içeriyor. -) - -$(P -Bu örnek çok daha hızlı işleyecek biçimde de gerçekleştirilebilir. Örneğin, aşağıdaki kodun tek elemana $(C m[i, j]) biçiminde erişirken bile $(I tek elemanlı bir alt matris) oluşturması gereksiz kabul edilebilir. -) - -$(P -Ek olarak, işlev başlarındaki $(C writeln(__FUNCTION__)) ifadelerinin kodun işlevselliğiyle bir ilgisi bulunmuyor. Onlar yalnızca perde arkasında hangi işlevlerin hangi sırada çağrıldıklarını göstermek amacıyla eklenmişlerdir. -) - -$(P -Boyut değerlerini denetlemek için şablon kısıtlamalarından yararlanıldığına da dikkat edin. -) - ---- -import std.stdio; -import std.format; -import std.string; - -/* İki boyutlu bir int dizisi gibi işler. */ -struct Matris { -private: - - int[][] satırlar; - - /* İndekslerle belirlenen satır ve sütun aralığı bilgisini - * bir araya getirir. */ - struct Aralık { - size_t baş; - size_t son; - } - - /* Satır ve sütun aralıklarıyla belirlenen alt matrisi - * döndürür. */ - Matris altMatris(Aralık satırAralığı, Aralık sütunAralığı) { - writeln(__FUNCTION__); - - int[][] dilimler; - - foreach (satır; satırlar[satırAralığı.baş .. - satırAralığı.son]) { - - dilimler ~= satır[sütunAralığı.baş .. - sütunAralığı.son]; - } - - return Matris(dilimler); - } - -public: - - this(size_t yükseklik, size_t genişlik) { - writeln(__FUNCTION__); - - satırlar = new int[][](yükseklik, genişlik); - } - - this(int[][] satırlar) { - writeln(__FUNCTION__); - - this.satırlar = satırlar; - } - - void toString(void delegate(const(char)[]) hedef) const { - formattedWrite(hedef, "%(%(%5s %)\n%)", satırlar); - } - - /* Belirtilen değeri matrisin bütün elemanlarına atar. */ - Matris opAssign(int değer) { - writeln(__FUNCTION__); - - foreach (satır; satırlar) { - satır[] = değer; - } - - return this; - } - - /* Belirtilen işleci ve değeri her elemana uygular ve - * sonucu o elemana atar. */ - Matris opOpAssign(string işleç)(int değer) { - writeln(__FUNCTION__); - - foreach (satır; satırlar) { - mixin ("satır[] " ~ işleç ~ "= değer;"); - } - - return this; - } - - /* Belirtilen boyutun uzunluğunu döndürür. */ - size_t opDollar(size_t boyut)() const - if (boyut <= 1) { - writeln(__FUNCTION__); - - static if (boyut == 0) { - /* Sıfırıncı boyutun uzunluğu isteniyor; - * 'satırlar' dizisinin uzunluğudur. */ - return satırlar.length; - - } else { - /* Birinci boyutun uzunluğu isteniyor; 'satırlar' - * dizisinin elemanlarının uzunluğudur. */ - return satırlar.length ? satırlar[0].length : 0; - } - } - - /* 'baş' ve 'son' ile belirlenen aralığı ifade eden bir - * nesne döndürür. - * - * Not: Bu gerçekleştirmede 'boyut' parametresi - * kullanılmıyor olsa da, bu bilgi başka bir tür için - * yararlı olabilir. */ - Aralık opSlice(size_t boyut)(size_t baş, size_t son) - if (boyut <= 1) { - writeln(__FUNCTION__); - - return Aralık(baş, son); - } - - /* Parametrelerle belirlenen alt matrisi döndürür. */ - Matris opIndex(A...)(A parametreler) - if (A.length <= 2) { - writeln(__FUNCTION__); - - /* Bütün elemanları temsil eden aralıklarla - * başlıyoruz. Böylece opIndex'in parametresiz - * kullanımında bütün elemanlar kapsanırlar. */ - Aralık[2] aralıklar = [ Aralık(0, opDollar!0), - Aralık(0, opDollar!1) ]; - - foreach (boyut, p; parametreler) { - static if (is (typeof(p) == Aralık)) { - /* Bu boyut için 'matris[baş..son]' gibi - * aralık belirtilmiş; parametreyi olduğu gibi - * aralık olarak kullanabiliriz. */ - aralıklar[boyut] = p; - - } else static if (is (typeof(p) : size_t)) { - /* Bu boyut için 'matris[i]' gibi tek konum - * değeri belirtilmiş; kullanmadan önce tek - * uzunluklu aralık oluşturmak gerekiyor. */ - aralıklar[boyut] = Aralık(p, p + 1); - - } else { - /* Bu işlevin başka bir türle çağrılmasını - * beklemiyoruz. */ - static assert( - false, format("Geçersiz indeks türü: %s", - typeof(p).stringof)); - } - } - - /* 'parametreler'in karşılık geldiği alt matrisi - * döndürüyoruz. */ - return altMatris(aralıklar[0], aralıklar[1]); - } - - /* Belirtilen değeri belirtilen elemanlara atar. */ - Matris opIndexAssign(A...)(int değer, A parametreler) - if (A.length <= 2) { - writeln(__FUNCTION__); - - Matris altMatris = opIndex(parametreler); - return altMatris = değer; - } - - /* Belirtilen işleci ve değeri belirtilen elemanlara - * uygular ve sonuçları yine aynı elemanlara atar. */ - Matris opIndexOpAssign(string işleç, A...)(int değer, - A parametreler) - if (A.length <= 2) { - writeln(__FUNCTION__); - - Matris altMatris = opIndex(parametreler); - mixin ("return altMatris " ~ işleç ~ "= değer;"); - } -} - -/* Dizgi halinde belirtilen ifadeyi işletir ve hem işlemin - * sonucunu hem de matrisin yeni durumunu yazdırır. */ -void işlet(string ifade)(Matris m) { - writefln("\n--- %s ---", ifade); - mixin ("auto sonuç = " ~ ifade ~ ";"); - writefln("sonuç:\n%s", sonuç); - writefln("m:\n%s", m); -} - -void main() { - enum yükseklik = 10; - enum genişlik = 8; - - auto m = Matris(yükseklik, genişlik); - - int sayaç = 0; - foreach (satır; 0 .. yükseklik) { - foreach (sütun; 0 .. genişlik) { - writefln("%s / %s ilkleniyor", - sayaç + 1, yükseklik * genişlik); - - m[satır, sütun] = sayaç; - ++sayaç; - } - } - - writeln(m); - - işlet!("m[1, 1] = 42")(m); - işlet!("m[0, 1 .. $] = 43")(m); - işlet!("m[0 .. $, 3] = 44")(m); - işlet!("m[$-4 .. $-1, $-4 .. $-1] = 7")(m); - - işlet!("m[1, 1] *= 2")(m); - işlet!("m[0, 1 .. $] *= 4")(m); - işlet!("m[0 .. $, 0] *= 10")(m); - işlet!("m[$-4 .. $-2, $-4 .. $-2] -= 666")(m); - - işlet!("m[1, 1]")(m); - işlet!("m[2, 0 .. $]")(m); - işlet!("m[0 .. $, 2]")(m); - işlet!("m[0 .. $ / 2, 0 .. $ / 2]")(m); - - işlet!("++m[1..3, 1..3]")(m); - işlet!("--m[2..5, 2..5]")(m); - - işlet!("m[]")(m); - işlet!("m[] = 20")(m); - işlet!("m[] /= 4")(m); - işlet!("(m[] += 5) /= 10")(m); -} ---- - -$(H5 Özet) - -$(P -Önceki şablonlar bölümünün sonunda şunları hatırlatmıştım: -) - -$(UL - -$(LI Şablonlar kodun kalıp halinde tarif edilmesini ve derleyici tarafından gereken her tür için otomatik olarak üretilmesini sağlayan olanaktır.) - -$(LI Şablonlar bütünüyle derleme zamanında işleyen bir olanaktır.) - -$(LI Tanımlarken isimlerinden sonra şablon parametresi de belirtmek; işlevlerin, yapıların, ve sınıfların şablon haline gelmeleri için yeterlidir.) - -$(LI Şablon parametreleri ünlem işaretinden sonra açıkça belirtilebilirler. Tek parametre için parantez kullanmaya gerek yoktur.) - -$(LI Şablonun farklı türlerle her kullanımı farklı bir türdür.) - -$(LI Şablon parametreleri yalnızca işlev şablonlarında çıkarsanabilirler.) - -$(LI Şablonlar $(C :) karakterinden sonra belirtilen tür için özellenebilirler.) - -$(LI Varsayılan şablon parametre türleri $(C =) karakterinden sonra belirtilebilir.) - -) - -$(P -Bu bölümde de şu kavramları gördük: -) - -$(UL - -$(LI Şablonlar kestirme veya uzun söz dizimleriyle tanımlanabilirler.) - -$(LI Şablon kapsamı bir isim alanı belirler.) - -$(LI İçinde bir tanımla aynı isime sahip olan şablon o tanım yerine geçer.) - -$(LI İşlev, sınıf, yapı, birlik, ve arayüz şablonları tanımlanabildiği gibi, bu tanımlar şablon kapsamı içinde karışık olarak bulunabilirler.) - -$(LI Şablon parametrelerinin tür, değer, $(C this), $(C alias), ve çokuzlu çeşitleri vardır.) - -$(LI Şablonlar parametrelerinin herhangi bir kullanımı için özellenebilirler.) - -$(LI $(C typeof(this)), $(C typeof(super)), ve $(C typeof(return)) tür yazımlarında kolaylık sağlarlar.) - -$(LI Meta programlama, işlemlerin derleme zamanında yapılmalarını sağlar.) - -$(LI Şablonlar $(I derleme zamanı çok şekilliliği) olanaklarıdır.) - -$(LI Şablonun her farklı parametreli kullanımı için ayrı kod üretilmesi $(I kod şişmesine) neden olabilir.) - -$(LI Olası derleme hatalarının şablonun yanlış kullanıldığı satıra işaret edebilmesi için şablon kısıtlamaları tanımlanabilir.) - -$(LI İsimli kısıtlama yöntemi kısıtlamalara okunaklı isimler vermeye yarar.) - -$(LI $(C opDollar), $(C opSlice), $(C opIndex), $(C opIndexAssign), ve $(C opIndexOpAssign) işlevlerinin şablon çeşitleri çok boyutlu eleman erişimine ve dilimlemeye olanak sağlar.) - -) - -macros: - SUBTITLE=Ayrıntılı Şablonlar - - DESCRIPTION=Derleyicinin belirli bir kalıba uygun olarak kod üretmesini sağlayan şablon (template) olanağı; D'nin 'türden bağımsız programlama'ya getirdiği çözüm - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial şablon şablonlar template türden bağımsız programlama generic programming işlev şablonu yapı şablonu sınıf şablonu alias - -SOZLER= -$(arayuz) -$(birlik) -$(blok) -$(ctfe) -$(cokuzlu) -$(hazir_deger) -$(isim_alani) -$(kapsam) -$(kisitlama) -$(ordek_tipleme) -$(ozelleme) -$(ozyineleme) -$(siraduzen) -$(sablon) diff --git a/ddili/src/ders/d/sarma.d b/ddili/src/ders/d/sarma.d deleted file mode 100644 index 03cee8a..0000000 --- a/ddili/src/ders/d/sarma.d +++ /dev/null @@ -1,525 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Sarma ve Erişim Hakları) - -$(P -Şimdiye kadar tasarladığımız bütün yapı ve sınıf türlerinin bütün üyeleri dışarıdan erişime açıktı. -) - -$(P -Hatırlamak için şöyle bir öğrenci yapısı düşünelim. -) - ---- -enum Cinsiyet { kız, erkek } - -struct Öğrenci { - string isim; - Cinsiyet cinsiyet; -} ---- - -$(P -O yapının nesnelerinin üyelerine istediğimiz gibi erişebiliyorduk: -) - ---- - auto öğrenci = Öğrenci("Tolga", Cinsiyet.erkek); - writefln("%s bir %s öğrencidir.", öğrenci$(HILITE .isim), öğrenci$(HILITE .cinsiyet)); ---- - -$(P -Üyelere böyle serbestçe erişebilmek, o üyeleri programda istediğimiz gibi kullanma olanağı sağladığı için yararlıdır. O kod, öğrenci hakkındaki bilgiyi çıkışa şöyle yazdırır: -) - -$(SHELL_SMALL -Tolga bir erkek öğrencidir. -) - -$(P -Ancak, üye erişiminin bu kadar serbest olması sakıncalar da doğurabilir. Örneğin belki de yanlışlıkla, öğrencinin yalnızca ismini değiştirdiğimizi düşünelim: -) - ---- - öğrenci.isim = "Ayşe"; ---- - -$(P -O atama sonucunda artık nesnenin geçerliliği bozulmuş olabilir: -) - -$(SHELL_SMALL -$(HILITE Ayşe) bir $(HILITE erkek) öğrencidir. -) - -$(P -Başka bir örnek olarak, bir grup öğrenciyi barındıran $(C Okul) isminde bir sınıfa bakalım. Bu sınıf, okuldaki kız ve erkek öğrencilerin sayılarını ayrı olarak tutuyor olsun: -) - ---- -class Okul { - Öğrenci[] öğrenciler; - size_t $(HILITE kızToplamı); - size_t $(HILITE erkekToplamı); - - void ekle(in Öğrenci öğrenci) { - öğrenciler ~= öğrenci; - - final switch (öğrenci.cinsiyet) { - - case Cinsiyet.kız: - ++kızToplamı; - break; - - case Cinsiyet.erkek: - ++erkekToplamı; - break; - } - } - - override string toString() const { - return format( - "%s kız, %s erkek; toplam %s öğrenci", - kızToplamı, erkekToplamı, öğrenciler.length); - } -} ---- - -$(P -$(C ekle) işlevini kullanarak o sınıfın nesnelerine yeni öğrenciler ekleyebiliriz: -) - ---- - auto okul = new Okul; - okul.ekle(Öğrenci("Leyla", Cinsiyet.kız)); - okul.ekle(Öğrenci("Metin", Cinsiyet.erkek)); - writeln(okul); ---- - -$(P -ve tutarlı bir çıktı elde ederiz: -) - -$(SHELL_SMALL -1 kız, 1 erkek; toplam 2 öğrenci -) - -$(P -Oysa bu sınıfın üyelerine serbestçe erişebiliyor olmak, onun nesnelerini de tutarsız hale getirebilir. Örneğin $(C öğrenciler) üyesine doğrudan yeni bir öğrenci eklediğimizi düşünelim: -) - ---- - okul$(HILITE .öğrenciler) ~= Öğrenci("Nimet", Cinsiyet.kız); ---- - -$(P -Yeni öğrenci, toplamları sayan $(C ekle) işlevi çağrılmadan eklendiği için bu $(C Okul) nesnesi artık tutarsızdır: -) - -$(SHELL_SMALL -$(HILITE 1) kız, $(HILITE 1) erkek; toplam $(HILITE 3) öğrenci -) - -$(H5 $(IX sarma) Sarma) - -$(P -Sarma, bu tür durumları önlemek için üyelere erişimi kısıtlayan bir olanaktır. -) - -$(P -Başka bir yararı, kullanıcıların sınıfın iç yapısını bilmek zorunda kalmamalarıdır. Sınıf, sarma yoluyla bir anlamda bir kara kutu haline gelir ve ancak arayüzünü belirleyen işlevler aracılığıyla kullanılabilir. -) - -$(P -Kullanıcıların sınıfın üyelerine doğrudan erişememeleri, ayrıca sınıfın iç tasarımının ileride rahatça değiştirilebilmesini de sağlar. Sınıfın arayüzündeki işlevlerin tanımına dokunulmadığı sürece, içinin yapısı istendiği gibi değiştirilebilir. -) - -$(P -Sarma, kredi kartı numarası veya şifre gibi değerli veya gizli verilere erişimi kısıtlamak için değildir ve bu amaçla kullanılamaz. Sarma, program geliştirme konusunda yararlı bir olanaktır: Programdaki tanımların kolay ve doğru kullanılmalarını ve kolayca değiştirilebilmelerini sağlar. -) - -$(H5 $(IX erişim hakkı) $(IX hak, erişim) Erişim hakları) - -$(P -D'de erişim hakları iki bağlamda belirtilebilir: -) - -$(UL - -$(LI -$(B Yapı veya sınıf düzeyinde): Her yapı veya sınıf üyesinin erişim hakkı ayrıca belirtilebilir. -) - -$(LI -$(B Modül düzeyinde): Modül içinde tanımlanmış olan her tür olanağın erişim hakkı ayrıca belirtilebilir: sınıf, yapı, işlev, enum, vs. -) - -) - -$(P -Bir üyenin veya modül tanımının erişim hakkı aşağıdaki özelliklerden birisi olarak belirtilebilir. Varsayılan erişim $(C public)'tir. -) - -$(UL - -$(LI $(IX public) $(C public), $(I genel): Programın her tarafından erişilebilmeyi ifade eder; hiçbir erişim kısıtlaması yoktur. - -$(P -Bunun bir örneği olarak $(C stdout) standart akımını düşünebilirsiniz. Onu bildiren $(C std.stdio) modülünün $(C import) ile eklenmesi, $(C stdout)'un serbestçe kullanılabilmesi için yeterlidir. -) - -) - -$(LI $(IX private) $(C private), $(I özel): özel erişimi ifade eder - -$(P -Bu şekilde tanımlanan üyelere içinde tanımlı oldukları sınıfın kodları tarafından, veya o sınıfı barındıran modüldeki kodlar tarafından erişilebilir. -) - -$(P -Ek olarak, $(C private) olarak işaretlenmiş olan üye işlevler alt sınıflar tarafından tekrar tanımlanamazlar. -) - -) - -$(LI $(IX package) $(C package), $(I pakede açık): paketteki modüller tarafından erişilebilmeyi ifade eder - -$(P -Bu şekilde işaretlenmiş olan bir tanım, onu barındıran paketteki bütün kodlara açıktır. Bu erişim hakkı yalnızca modülü içeren en içteki pakede verilir. -) - -$(P -Örneğin $(C hayvan.omurgalilar.kedi) isimli bir modül içinde $(C package) olarak işaretlenmiş olan bir tanım; $(C kedi) modülünün kendisinden başka, $(C omurgalilar) pakedindeki bütün modüllere de açıktır. -) - -$(P -$(C private) belirtecinde olduğu gibi, $(C package) olarak işaretlenmiş olan üye işlevler alt sınıflar tarafından tekrar tanımlanamazlar. -) - -) - -$(LI $(IX protected) $(C protected), $(I korumalı): türetilen sınıf tarafından da erişilebilmeyi ifade eder - -$(P -$(C private) erişimi genişleten bir erişimdir: Bu şekilde işaretlenmiş olan üyeye sınıf tarafından erişilmek yanında, o sınıftan türetilen sınıflardan da erişilebilir. -) - -) - -) - -$(P -$(IX export) Ek olarak, $(C export) belirteci program içinde tanımlanmış olanaklara programın dışından da erişilebilmesini sağlar. -) - -$(H5 Belirtilmesi) - -$(P -Erişim hakları üç şekilde belirtilebilir. -) - -$(P -Tek bir tanımın önüne yazıldığında yalnızca o tanımın erişim haklarını belirler. Bu, Java ve bazı başka dillerdeki gibidir: -) - ---- -private int birSayı; - -private void birİşlev() { - // ... -} ---- - -$(P -İki nokta üst üste karakteriyle yazıldığında, aynı şekilde yeni bir erişim hakkı yazılana kadarki bütün tanımları etkiler. Bu, C++'daki gibidir: -) - ---- -private: - // ... - // ... buradaki bütün tanımlar özel ... - // ... - -protected: - // ... - // ... buradakiler korumalı ... - // ... ---- - -$(P -Blok söz dizimiyle yazıldığında bütün bloğun içini etkiler: -) - ---- -private { - // ... - // ... buradakilerin hepsi özel ... - // ... -} ---- - -$(P -Bu üç yöntemin etkisi aynıdır. Hangisini kullanacağınıza tasarımınıza uygun olduğunu düşündüğünüz şekilde serbestçe karar verebilirsiniz. -) - -$(H5 $(C import)'lar normalde modüle özeldir) - -$(P -$(C import) ile eklenen modüller, o modülü dolaylı olarak ekleyen başka modüller tarafından görülemezler. Örneğin $(C okul) modülü $(C std.stdio) modülünü eklese, $(C okul) modülünü ekleyen başka modüller $(C std.stdio)'dan otomatik olarak yararlanamazlar. -) - -$(P -Örneğin $(C okul) modülü şöyle başlıyor olsun: -) - ---- -module okul.okul; - -import std.stdio; // kendi işi için eklenmiş... - -// ... ---- - -$(P -Onu kullanan şu program derlenemez: -) - ---- -import okul.okul; - -void main() { - writeln("merhaba"); $(DERLEME_HATASI) -} ---- - -$(P -O yüzden, $(C std.stdio)'yu asıl modülün ayrıca eklemesi gerekir: -) - ---- -import okul.okul; -$(HILITE import std.stdio;) - -void main() { - writeln("merhaba"); // şimdi derlenir -} ---- - -$(P -$(IX public import) $(IX import, public) Bazen, eklenen bir modülün başka modülleri de otomatik ve dolaylı olarak sunması istenebilir. Örneğin $(C okul) isimli bir modülün eklenmesinin, $(C ogrenci) modülünü de otomatik olarak eklemesi istenebilir. Bu, $(C import) işlemi $(C public) olarak işaretlenerek sağlanır: -) - ---- -module okul.okul; - -$(HILITE public import) okul.ogrenci; - -// ... ---- - -$(P -Artık $(C okul) modülünü ekleyen modüller $(C ogrenci) modülünü açıkça eklemek zorunda kalmadan, onun içindeki $(C Öğrenci) yapısını kullanabilirler: -) - ---- -import okul.okul; - -void main() { - auto öğrenci = Öğrenci("Tolga", Cinsiyet.erkek); - - // ... -} ---- - -$(P -O program yalnızca $(C okul) modülünü eklediği halde $(C Öğrenci) yapısını da kullanabilmektedir. -) - -$(H5 Sarmayı ne zaman kullanmalı) - -$(P -Sarma, giriş bölümünde gösterdiğim sorunları önlemek ve sınıf tasarımlarını serbest bırakmak için çok etkili bir olanaktır. -) - -$(P -Üyelerin ve başka değişkenlerin ilgisiz kodlar tarafından serbestçe değiştirilmeleri önlenmiş olur. Böylece, yukarıdaki $(C ekle) işlevinde olduğu gibi, nesnelerin tutarlılıkları denetim altına alınmış olur. -) - -$(P -Ayrıca, başka kodlar yapı ve sınıf gerçekleştirmelerine bağımlı kalmamış olurlar. Örneğin $(C Okul.öğrenciler) üyesine erişilebiliyor olması, sizin o diziyi daha sonradan örneğin bir eşleme tablosu olarak değiştirmenizi güçleştirir. Çünkü bu değişiklik kullanıcı kodlarını da etkileyecektir. -) - -$(P -Sarma, nesne yönelimli programlamanın en yararlı olanakları arasındadır. -) - -$(H5 Örnek) - -$(P -Yukarıdaki $(C Öğrenci) yapısını ve $(C Okul) sınıfını sarmaya uygun olacak şekilde tanımlayalım ve küçük bir deneme programında kullanalım. -) - -$(P -Bu örnekte toplam üç dosya tanımlayacağız. Önceki bölümden de hatırlayacağınız gibi; "okul" isminde bir klasör içinde tanımlandıkları için ilk ikisi $(C okul) pakedinin parçaları olacaklar: -) - -$(UL -$(LI "okul/ogrenci.d": $(C Öğrenci) yapısını içeren $(C ogrenci) modülü) -$(LI "okul/okul.d": $(C Okul) sınıfını tanımlayan $(C okul) modülü) -$(LI "deneme.d": küçük bir deneme programı) -) - -$(P -Bu programın oluşturulabilmesi için bütün dosyaların belirtilmesi gerektiğini unutmayın: -) - -$(SHELL -$ dmd deneme.d okul/ogrenci.d okul/okul.d -w -) - -$(P -İlk önce "okul/ogrenci.d" dosyasını görelim: -) - ---- -module okul.ogrenci; - -import std.string; -import std.conv; - -enum Cinsiyet { kız, erkek } - -struct Öğrenci { - $(HILITE package) string isim; - $(HILITE package) Cinsiyet cinsiyet; - - string toString() const { - return format("%s bir %s öğrencidir.", - isim, to!string(cinsiyet)); - } -} ---- - -$(P -Bu yapının üyelerini yalnızca kendi pakedindeki kodlara açmak için $(C package) olarak belirledim; çünkü biraz sonra göreceğimiz $(C Okul) sınıfının bu yapının üyelerine doğrudan erişmesini istedim. -) - -$(P -Aynı pakedin parçası olsa bile başka bir modül tarafından yapının üyelerine erişilmesi, aslında temelde sarmaya karşıdır. Yine de; $(C Öğrenci) ve $(C Okul)'un birbirlerinin üyelerine doğrudan erişebilecek kadar yakın tanımlar oldukları düşünülebilir. -) - -$(P -Bu sefer de, o modülden yararlanan "okul/okul.d" dosyasına bakalım: -) - ---- -module okul.okul; - -$(HILITE public import) okul.ogrenci; // 1 - -import std.string; - -class Okul { -$(HILITE private:) // 2 - - Öğrenci[] öğrenciler; - size_t kızToplamı; - size_t erkekToplamı; - -$(HILITE public:) // 3 - - void ekle(in Öğrenci öğrenci) { - öğrenciler ~= öğrenci; - - final switch (öğrenci$(HILITE .cinsiyet)) { // 4a - - case Cinsiyet.kız: - ++kızToplamı; - break; - - case Cinsiyet.erkek: - ++erkekToplamı; - break; - } - } - - override string toString() const { - string sonuç = format( - "%s kız, %s erkek; toplam %s öğrenci", - kızToplamı, erkekToplamı, öğrenciler.length); - - foreach (i, öğrenci; öğrenciler) { - sonuç ~= (i == 0) ? ": " : ", "; - sonuç ~= öğrenci$(HILITE .isim); // 4b - } - - return sonuç; - } -} ---- - -$(OL -$(LI Bu modülü ekleyen programlar $(C ogrenci) modülünü de ayrıca eklemek zorunda kalmasınlar diye; $(C public) olarak ekleniyor. Bir anlamda, bu "ekleme", genele açılıyor. -) - -$(LI $(C Okul) sınıfının bütün üyeleri özel olarak işaretleniyor. Bu sayede sınıf nesnelerinin tutarlılığı güvence altına alınmış oluyor.) - -$(LI Bu sınıfın herhangi bir şekilde kullanışlı olabilmesi için üye işlevler sunması gerekir; $(C ekle) ve $(C toString) işlevleri, kullanılabilmeleri için $(C public) olarak işaretleniyorlar. -) - -$(LI Önceki dosyada $(C package) olarak işaretlendikleri için, $(C Öğrenci) yapısının her iki üyesine de bu modüldeki kodlar tarafından erişilebiliyor. -) - -) - -$(P -Son olarak bu iki modülü kullanan program dosyasına da bakalım: -) - ---- -import std.stdio; -import okul.okul; - -void main() { - auto öğrenci = Öğrenci("Tolga", Cinsiyet.erkek); - writeln(öğrenci); - - auto okul = new Okul; - - okul.ekle(Öğrenci("Leyla", Cinsiyet.kız)); - okul.ekle(Öğrenci("Metin", Cinsiyet.erkek)); - okul.ekle(Öğrenci("Nimet", Cinsiyet.kız)); - - writeln(okul); -} ---- - -$(P -Bu program, $(C Öğrenci) ve $(C Okul)'u ancak genel erişime açık olan arayüzleri aracılığıyla kullanabilir. Ne $(C Öğrenci)'nin, ne de $(C Okul)'un üyelerine erişemez ve bu yüzden nesneler her zaman için tutarlıdır: -) - -$(SHELL_SMALL -Tolga bir erkek öğrencidir. -2 kız, 1 erkek; toplam 3 öğrenci: Leyla, Metin, Nimet -) - -$(P -Dikkat ederseniz, o program bu tanımları yalnızca $(C Okul.ekle) ve $(C Öğrenci.toString) işlevleri aracılığıyla kullanıyor. O işlevlerin kullanımları değiştirilmediği sürece, $(C Öğrenci)'nin ve $(C Okul)'un tanımlarında yapılan hiçbir değişiklik bu programı etkilemez. -) - -Macros: - SUBTITLE=Sarma ve Erişim Hakları - - DESCRIPTION=Program parçalarının birbirlerinden bağımsızlaştırılmalarına olanak veren sarma kavramı; ve onu sağlayan private, protected, ve public anahtar sözcükleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar yapı struct yapılar sarma encapsulation private protected public - -SOZLER= -$(blok) -$(genel_erisim) -$(korumali_erisim) -$(modul) -$(ozel_erisim) -$(paket) -$(sarma) -$(varsayilan) diff --git a/ddili/src/ders/d/scope.d b/ddili/src/ders/d/scope.d deleted file mode 100644 index 9498790..0000000 --- a/ddili/src/ders/d/scope.d +++ /dev/null @@ -1,145 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX scope(success)) $(IX scope(exit)) $(IX scope(failure)) $(CH4 scope)) - -$(P -Kesinlikle işletilmeleri gereken ifadelerin $(C finally) bloklarına, hatalı durumlarda işletilmeleri gereken ifadelerin de $(C catch) bloklarına yazıldıklarını bir önceki bölümde gördük. Bu blokların kullanımlarıyla ilgili bir kaç gözlemde bulunabiliriz: -) - -$(UL - -$(LI $(C catch) ve $(C finally) blokları $(C try) bloğu olmadan kullanılamaz.) - -$(LI Bu bloklarda kullanılmak istenen bazı değişkenler o noktalarda geçerli olmayabilirler: - ---- -void birİşlev(ref int çıkış) { - try { - $(HILITE int birDeğer) = 42; - - çıkış += birDeğer; - hataAtabilecekBirİşlev(); - - } catch (Exception hata) { - çıkış -= birDeğer; $(DERLEME_HATASI) - } -} ---- - -$(P -Yukarıdaki işlev, referans türündeki parametresinde değişiklik yapmakta ve hata atılması durumunda onu eski haline getirmeye çalışmaktadır. Ne yazık ki, $(C birDeğer) yalnızca $(C try) bloğu içinde tanımlı olduğu için bir derleme hatası alınır. $(I (Not: Yaşam süreçleriyle ilgili olan bu konuyu ilerideki bir bölümde tekrar değineceğim.)) -) - -) - -$(LI Bir kapsamdan çıkılırken kesinlikle işletilmesi gereken ifadelerin hepsinin bir arada en aşağıdaki $(C finally) bloğuna yazılmaları, ilgili oldukları kodlardan uzakta kalacakları için istenmeyebilir.) - -) - -$(P -$(C catch) ve $(C finally) bloklarına benzer şekilde işleyen ve bazı durumlarda daha uygun olan olanak $(C scope) deyimidir. Üç farklı $(C scope) kullanımı, yine ifadelerin kapsamlardan çıkılırken kesinlikle işletilmeleri ile ilgilidir: -) - -$(UL - -$(LI $(C scope(success)): Kapsamdan başarıyla çıkılırken işletilecek olan ifadeleri belirler.) - -$(LI $(C scope(failure)): Kapsamdan hatayla çıkılırken işletilecek olan ifadeleri belirler.) - -$(LI $(C scope(exit)): Kapsamdan başarıyla veya hatayla çıkılırken işletilecek olan ifadeleri belirler.) -) - -$(P -Bu deyimler yine atılan hatalarla ilgili olsalar da $(C try-catch) bloklarının parçası değillerdir. -) - -$(P -Örneğin, hata atıldığında $(C çıkış)'ın değerini düzeltmeye çalışan yukarıdaki işlevi bir $(C scope(failure)) deyimiyle daha kısa olarak şöyle yazabiliriz: -) - ---- -void birİşlev(ref int çıkış) { - int birDeğer = 42; - - çıkış += birDeğer; - $(HILITE scope(failure)) çıkış -= birDeğer; - - hataAtabilecekBirİşlev(); -} ---- - -$(P -Yukarıdaki $(C scope) deyimi, kendisinden sonra yazılan ifadenin işlevden hata ile çıkıldığı durumda işletileceğini bildirir. Bunun bir yararı, yapılan bir değişikliğin hatalı durumda geri çevrilecek olduğunun tam da değişikliğin yapıldığı yerde görülebilmesidir. -) - -$(P -$(C scope) deyimleri bloklar halinde de bildirilebilirler: -) - ---- - scope(exit) { - // ... çıkarken işletilecek olan ifadeler ... - } ---- - -$(P -Bu kavramları deneyen bir işlevi şöyle yazabiliriz: -) - ---- -void deneme() { - scope(exit) writeln("çıkarken 1"); - - scope(success) { - writeln("başarılıysa 1"); - writeln("başarılıysa 2"); - } - - scope(failure) writeln("hata atılırsa 1"); - scope(exit) writeln("çıkarken 2"); - scope(failure) writeln("hata atılırsa 2"); - - yüzdeElliHataAtanİşlev(); -} ---- - -$(P -İşlevin çıktısı, hata atılmayan durumda yalnızca $(C scope(exit)) ve $(C scope(success)) ifadelerini içerir: -) - -$(SHELL -çıkarken 2 -başarılıysa 1 -başarılıysa 2 -çıkarken 1 -) - -$(P -Hata atılan durumda ise $(C scope(exit)) ve $(C scope(failure)) ifadelerini içerir: -) - -$(SHELL -hata atılırsa 2 -çıkarken 2 -hata atılırsa 1 -çıkarken 1 -object.Exception: hata mesajı -) - -$(P -Çıktılardan anlaşıldığı gibi, $(C scope) deyimlerinin ifadeleri ters sırada işletilmektedir. Bunun nedeni, daha sonra gelen kodların daha önceki değişkenlerin durumlarına bağlı olabilecekleridir. $(C scope) deyimlerindeki ifadelerinin ters sırada işletilmeleri programın durumunda yapılan değişikliklerin geri adımlar atılarak ters sırada işletilmelerini sağlar. -) - -Macros: - SUBTITLE=scope - - DESCRIPTION=Kapsamlardan çıkılırken işletilmesi gereken ifadeler için kullanılan scope(success), scope(failure), ve scope(exit) deyimleri. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial scope - -SOZLER= -$(blok) -$(deyim) -$(hata_atma) -$(ifade) -$(kapsam) diff --git a/ddili/src/ders/d/siniflar.d b/ddili/src/ders/d/siniflar.d deleted file mode 100644 index 08e46d2..0000000 --- a/ddili/src/ders/d/siniflar.d +++ /dev/null @@ -1,468 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX class) $(IX sınıf) Sınıflar) - -$(P -$(IX OOP) $(IX NYP) $(IX nesne yönelimli programlama) Sınıflar kullanıcı türü tanımlamaya yarayan başka bir olanaktır. D'nin nesne yönelimli programlama olanakları sınıflar yoluyla gerçekleştirilir. Nesne yönelimli programlamayı üç temel kavram üzerinde düşünebiliriz: -) - -$(UL - -$(LI $(B Sarma:) Üyelere erişimin kısıtlanması ($(I Not: Aslında yapılarda da bulunan bu olanağı genelde yapıların kullanım amaçlarının dışında kaldığı için göstermedim.)) -) - -$(LI $(B Kalıtım:) Başka bir türün üyelerini ve üye işlevlerini kendisininmiş gibi edinmek -) - -$(LI $(B Çok şekillilik:) Birbirlerine yakın türlerin daha genel ortak bir tür gibi kullanılabilmeleri -) - -) - -$(P -Sarma, daha sonra göreceğimiz $(I erişim hakları) ile sağlanır. Kalıtım, $(I gerçekleştirme) türemesidir. $(I Çok şekillilik) ise $(I arayüz) türemesi yoluyla gerçekleştirilir. -) - -$(P -Bu bölümde sınıfları genel olarak tanıtacağım ve özellikle $(I referans türü) olduklarına dikkat çekeceğim. Sınıfların diğer olanaklarını daha sonraki bölümlere bırakacağım. -) - -$(H5 Yapılarla karşılaştırılması) - -$(P -Sınıflar yapılara temelde çok benzerler. Bu yüzden daha önce şu bölümlerde yapılar üzerinde gördüğümüz hemen hemen her konu sınıflar için de geçerlidir: -) - -$(UL -$(LI $(LINK2 /ders/d/yapilar.html, Yapılar)) -$(LI $(LINK2 /ders/d/uye_islevler.html, Üye İşlevler)) -$(LI $(LINK2 /ders/d/const_uye_islevler.html, $(C const ref) Parametreler ve $(C const) Üye İşlevler)) -$(LI $(LINK2 /ders/d/ozel_islevler.html, Kurucu ve Diğer Özel İşlevler)) -$(LI $(LINK2 /ders/d/islec_yukleme.html, İşleç Yükleme)) -) - -$(P -Sınıfları yapılardan ayıran önemli farklar da vardır. Bu farkları aşağıdaki bölümlerde anlatıyorum. -) - -$(H6 Referans türleridir) - -$(P -Sınıfların yapılardan farklı olmalarının en büyük nedeni, yapıların $(I değer türü) olmalarına karşın sınıfların $(I referans türü) olmalarıdır. Aşağıdaki farklılıkların büyük bir çoğunluğu, sınıfların bu özelliğinden kaynaklanır. -) - -$(H6 $(IX null, sınıf) $(IX new, sınıf) Sınıf değişkenleri $(C null) olabilirler) - -$(P -Sınıf değişkenlerinin kendileri değer taşımadıklarından, asıl nesne $(C new) anahtar sözcüğü ile oluşturulur. Aynı nedenden, $(LINK2 /ders/d/null_ve_is.html, $(C null) ve $(C is) bölümünde) de gösterildiği gibi, sınıf değişkenleri $(C null) da olabilirler. Yani, "hiçbir nesneye erişim sağlamıyor" olabilirler. -) - -$(P -Hatırlayacağınız gibi, bir değişkenin $(C null) olup olmadığı $(C ==) ve $(C !=) işleçleriyle değil, duruma göre $(C is) ve $(C !is) işleçleriyle denetlenir: -) - ---- - BirSınıf erişimSağlayan = new BirSınıf; - assert(erişimSağlayan $(HILITE !is) null); - - BirSınıf değişken; // erişim sağlamayan - assert(değişken $(HILITE is) null); ---- - -$(P -Bunun nedeni $(C ==) işlecinin nesnenin üyelerini de kullanmasının gerekebileceğidir. O üye erişimi, değişkenin $(C null) olduğu durumda programın bir bellek hatası ile sonlanmasına neden olur. O yüzden sınıf değişkenlerinin $(C is) veya $(C !is) ile karşılaştırılmaları gerekir. -) - -$(H6 $(IX değişken, sınıf) $(IX nesne, sınıf) Sınıf nesneleri ve değişkenleri) - -$(P -Sınıf $(I nesnesi) ile sınıf $(I değişkeni) farklı kavramlardır. -) - -$(P -Sınıf nesnesi, $(C new) anahtar sözcüğü ile oluşturulan ve kendi ismi olmayan bir program yapısıdır. Temsil ettiği kavramı gerçekleştiren, onun işlemlerini yapan, ve o türün davranışını belirleyen hep bu sınıf nesnesidir. Sınıf nesnelerine doğrudan erişemeyiz. -) - -$(P -Sınıf değişkeni ise sınıf nesnesine erişim sağlayan bir program yapısıdır. Kendisi iş yapmasa da eriştirdiği nesnenin aracısı gibi işlem görür. -) - -$(P -Daha önce $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar bölümünde) gördüğümüz şu koda bakalım: -) - ---- - auto değişken1 = new BirSınıf; - auto değişken2 = değişken1; ---- - -$(P -İlk satırda sağ taraftaki $(C new), isimsiz bir $(C BirSınıf) nesnesi oluşturur. $(C değişken1) ve $(C değişken2) ise yalnızca bu isimsiz nesneye erişim sağlayan değişkenlerdir: -) - -$(MONO - (isimsiz BirSınıf nesnesi) değişken1 değişken2 - ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── - │ ... │ │ o │ │ o │ - ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── - ▲ │ │ - │ │ │ - └────────────────────┴────────────┘ -) - -$(H6 $(IX kopyalama, sınıf) Kopyalama) - -$(P -Değişkenleri etkiler. -) - -$(P -Referans türü oldukları için; sınıf değişkenlerinin kopyalanarak oluşturulmaları, onların hangi nesneye erişim sağlayacaklarını belirler. Bu işlem sırasında asıl nesne kopyalanmaz. -) - -$(P -Bir nesne kopyalanmadığı için de, yapılarda $(I kopya sonrası işlevi) olarak öğrendiğimiz $(C this(this)) üye işlevi sınıflarda bulunmaz. -) - ---- - auto değişken2 = değişken1; ---- - -$(P -Yukarıdaki kodda $(C değişken2), $(C değişken1)'in kopyası olarak oluşturulmaktadır. O işlem her ikisinin de aynı nesneye erişim sağlamalarına neden olur. -) - -$(P -Sınıf nesnelerinin kopyalanmaları gerektiğinde bunu sağlayan bir üye işlev tanımlanmalıdır. Bu işleve $(C kopyala()) gibi bir isim verebileceğiniz gibi, dizilere benzemesi açısından $(C dup()) isminin daha uygun olduğunu düşünebilirsiniz. Bu işlev yeni bir nesne oluşturmalı ve ona erişim sağlayan bir değişken döndürmelidir: -) - ---- -class Sınıf { - Yapı yapıNesnesi; - char[] dizgi; - int tamsayı; - -// ... - - this(Yapı yapıNesnesi, const char[] dizgi, int tamsayı) { - this.yapıNesnesi = yapıNesnesi; - this.dizgi = dizgi.dup; - this.tamsayı = tamsayı; - } - - Sınıf dup() const { - return new Sınıf(yapıNesnesi, dizgi, tamsayı); - } -} ---- - -$(P -$(C dup()) içinde oluşturulan yeni nesne için yalnızca $(C Sınıf)'ın kurucusundan yararlanıldığına dikkat edin. Kurucu $(C dizgi) üyesini $(C dup()) ile açıkça kopyalıyor. $(C yapıNesnesi) ve $(C tamsayı) üyeleri ise değer türleri olduklarından onlar zaten otomatik olarak kopyalanırlar. -) - -$(P -O işlevden örneğin şöyle yararlanılabilir: -) - ---- - auto nesne1 = new Sınıf(Yapı(1.5), "merhaba", 42); - auto nesne2 = nesne1.dup(); ---- - -$(P -Sonuçta, $(C nesne2) $(C nesne1)'in hiçbir üyesini paylaşmayan ayrı bir nesnedir. -) - -$(P -Benzer biçimde, nesnenin $(C immutable) bir kopyası da ismi $(C idup) olan bir işlev tarafından sağlanabilir: -) - ---- -class Sınıf { -// ... - - immutable(Sınıf) idup() const { - return new immutable(Sınıf)(yapıNesnesi, dizgi, tamsayı); - } -} - -// ... - - immutable(Sınıf) imm = nesne1.idup(); ---- - -$(H6 $(IX atama, sınıf) Atama) - -$(P -Değişkenleri etkiler. -) - -$(P -Referans türü oldukları için; sınıf değişkenlerinin atanmaları, daha önce erişim sağladıkları nesneyi bırakmalarına ve yeni bir nesneye erişim sağlamalarına neden olur. -) - -$(P -Eğer $(I bırakılan) nesneye erişim sağlayan başka değişken yoksa, asıl nesne ilerideki belirsiz bir zamanda çöp toplayıcı tarafından sonlandırılacak demektir. -) - ---- - auto değişken1 = new BirSınıf; - auto değişken2 = new BirSınıf; - değişken1 $(HILITE =) değişken2; ---- - -$(P -Yukarıdaki atama işlemi, $(C değişken1)'in kendi nesnesini bırakmasına ve $(C değişken2)'nin nesnesine erişim sağlamaya başlamasına neden olur. Kendisine erişim sağlayan başka bir değişken olmadığı için bırakılan nesne daha sonra çöp toplayıcı tarafından sonlandırılacaktır. -) - -$(P -Atama işleminin davranışı sınıflar için değiştirilemez; yani $(C opAssign) sınıflarda yüklenemez. -) - -$(H6 Tanımlama) - -$(P -$(C struct) yerine $(C class) anahtar sözcüğü kullanılır: -) - ---- -$(HILITE class) SatrançTaşı { - // ... -} ---- - -$(H6 Kurma) - -$(P -Kurucu işlevin ismi, yapılarda olduğu gibi $(C this)'tir. Yapılardan farklı olarak sınıf nesneleri $(C {}) karakterleri ile kurulamaz. -) - ---- -class SatrançTaşı { - dchar şekil; - - $(HILITE this)(dchar şekil) { - this.şekil = şekil; - } -} ---- - -$(P -Yapıların aksine, sınıf üyeleri kurucu parametre değerlerinden sırayla otomatik olarak kurulamazlar: -) - ---- -class SatrançTaşı { - dchar şekil; - size_t değer; -} - -void main() { - auto şah = new SatrançTaşı('♔', 100); $(DERLEME_HATASI) -} ---- - -$(SHELL -Error: no constructor for SatrançTaşı -) - -$(P -Nesnelerin o yazımla kurulabilmeleri için programcının açıkça bir kurucu tanımlamış olması şarttır. -) - -$(H6 Sonlandırma) - -$(P -Sonlandırıcı işlevin ismi yapılarda olduğu gibi $(C ~this)'tir: -) - ---- - ~this() { - // ... - } ---- - -$(P -Ancak, yapılardan farklı olarak, sınıfların sonlandırıcıları nesnenin yaşamı sona erdiği an işletilmez. Yukarıda da değinildiği gibi, sonlandırıcı ilerideki belirsiz bir zamandaki bir çöp toplama işlemi sırasında işletilir. -) - -$(P -Daha sonra $(LINK2 /ders/d/bellek_yonetimi.html, Bellek Yönetimi bölümünde) de göreceğimiz gibi, sınıf sonlandırıcılarının aşağıdaki kurallara uymaları şarttır: -) - -$(UL - -$(LI Sınıf sonlandırıcısındaki kodlar, yaşamı çöp toplayıcı tarafından yönetilen hiçbir üyeye erişmemelidir. Bunun nedeni, çöp toplayıcının nesneyi veya üyelerini hangi sırada sonlandıracağı garantisini vermek zorunda olmamasıdır. Sonlandırıcı işletilmeye başladığında bütün üyeler zaten sonlandırılmış olabilirler.) - -$(LI Sınıf sonlandırıcısı çöp toplayıcıdan yeni bellek ayırmamalıdır. Bunun nedeni, çöp toplayıcının temizlik işlemleri sırasında yeni bellek ayırabilme garantisini vermek zorunda olmamasıdır.) - -) - -$(P -Bu kurallara uymamak tanımsız davranıştır. Tanımsız davranışın bir etkisini sonlandırıcı içinde yeni bir sınıf nesnesi kurmaya çalışarak görebiliriz: -) - ---- -class C { - ~this() { - auto c = new C(); // ← YANLIŞ: Sınıf sonlandırıcısında - // yeni nesne kuruluyor - } -} - -void main() { - auto c = new C(); -} ---- - -$(P -Program bir hata ile sonlanır: -) - -$(SHELL -core.exception.$(HILITE InvalidMemoryOperationError)@(0) -) - -$(P -Sonlandırıcı içinde çöp toplayıcıdan $(I dolaylı olarak) bellek ayırmak da aynı derecede yanlıştır. Örneğin, dinamik dizi elemanları için kullanılan bellek bölgesi de çöp toplayıcı tarafından yönetilir. Bu yüzden, bir dinamik dizinin yeni bellek ayrılmasını gerektirecek herhangi biçimde kullanılması da tanımsız davranıştır: -) - ---- - ~this() { - auto dizi = [ 1 ]; // ← YANLIŞ: Sınıf sonlandırıcısında - // çöp toplayıcıdan - // dolaylı olarak bellek - // ayrılıyor - } ---- - -$(SHELL -core.exception.$(HILITE InvalidMemoryOperationError)@(0) -) - -$(H6 Üye erişimi) - -$(P -Yapılarda olduğu gibi, üyelere nokta karakteri ile erişilir. -) - ---- - auto şah = new SatrançTaşı('♔'); - writeln(şah$(HILITE .şekil)); ---- - -$(P -Her ne kadar değişkenin bir üyesine erişiliyor gibi yazılsa da, erişilen asıl nesnenin üyesidir. Sınıf değişkenlerinin üyeleri yoktur, sınıf nesnelerinin üyeleri vardır. Bir başka deyişle, $(C şah)'ın $(C şekil) diye bir üyesi yoktur, isimsiz sınıf nesnesinin $(C şekil) diye bir üyesi vardır. -) - -$(P $(I Not: Üye değişkenlere böyle doğrudan erişilmesi çoğu durumda doğru kabul edilmez. Onun yerine daha sonra $(LINK2 /ders/d/nitelikler.html, Nitelikler bölümünde) göreceğimiz sınıf niteliklerinden yararlanmak daha uygundur.) -) - -$(H6 İşleç yükleme) - -$(P -Yapılardaki gibidir. -) - -$(P -Bir fark, $(C opAssign)'ın sınıflar için özel olarak tanımlanamamasıdır. Yukarıda atama başlığında anlatıldığı gibi, sınıflarda atama işleminin anlamı $(I yeni nesneye erişim sağlamaktır); bu anlam değiştirilemez. -) - -$(H6 Üye işlevler) - -$(P -Sınıf üye işlevleri yapılarda olduğu gibi tanımlanırlar ve kullanılırlar. Buna rağmen, aralarında önemli bir fark vardır: Sınıf üye işlevleri $(I yeniden tanımlanabilirler) ve bu, onlar için varsayılan durumdur. Yeniden tanımlama kavramını daha sonra $(LINK2 /ders/d/tureme.html, Türeme bölümünde) göreceğiz. -) - -$(P -$(IX final) Yeniden tanımlama düzeneğinin program hızına kötü bir etkisi olduğundan, burada daha fazla ayrıntısına girmeden bütün sınıf üye işlevlerini $(C final) olarak tanımlamanızı öneririm. Bu ilkeyi derleyici hatası almadığınız sürece bütün sınıf üyeleri için uygulayabilirsiniz: -) - ---- -class Sınıf { - $(HILITE final) int işlev() { $(CODE_NOTE Önerilir) - // ... - } -} ---- - -$(P -Yapılardan başka bir fark, bazı işlevlerin $(C Object) sınıfından kalıtım yoluyla hazır olarak edinilmiş olmalarıdır. Bunlar arasından $(C toString) işlevinin $(C override) anahtar sözcüğü ile nasıl tanımlandığını $(LINK2 /ders/d/tureme.html, bir sonraki bölümde) göreceğiz. -) - -$(H6 $(IX is, işleç) $(IX !is) $(C is) ve $(C !is) işleçleri) - -$(P -Sınıf değişkenleri üzerinde işler. -) - -$(P -$(C is) işleci, sınıf değişkenlerinin aynı nesneye erişim sağlayıp sağlamadıklarını bildirir. İki değişken de aynı nesneye erişim sağlıyorlarsa $(C true), değilse $(C false) değerini üretir. $(C !is) işleci de bunun tersi olarak işler: Aynı nesneye erişim sağlıyorlarsa $(C false), değilse $(C true) değerini üretir. -) - ---- - auto benimŞah = new SatrançTaşı('♔'); - auto seninŞah = new SatrançTaşı('♔'); - assert(benimŞah !is seninŞah); ---- - -$(P -Yukarıdaki koddaki $(C benimŞah) ve $(C seninŞah) değişkenleri $(C new) ile oluşturulmuş olan iki farklı nesneye erişim sağladıkları için $(C !is)'in sonucu $(C true)'dur. Bu iki nesnenin aynı şekilde kurulmuş olmaları, yani ikisinin $(C şekil) üyelerinin de $(C'♔') olması bunu değiştirmez; nesneler birbirlerinden ayrı iki nesnedir. -) - -$(P -İki değişkenin aynı nesneye erişim sağladıkları durumda ise $(C is) işleci $(C true) üretir: -) - ---- - auto benimŞah2 = benimŞah; - assert(benimŞah2 is benimŞah); ---- - -$(P -Yukarıdaki iki değişken de aynı nesneye erişim sağlamaya başlarlar. $(C is) işleci bu durumda $(C true) üretir. -) - -$(H5 Özet) - -$(UL - -$(LI Sınıfların yapılarla çok sayıda ortak yanları olduğu kadar büyük farkları da vardır. -) - -$(LI Sınıflar referans türleridir; $(C new) ile isimsiz bir $(I sınıf nesnesi) kurulur; döndürülen, o nesneye erişim sağlayan bir $(I sınıf değişkenidir). -) - -$(LI Hiçbir nesneye erişim sağlamayan sınıf değişkenlerinin değeri $(C null)'dır; bu durum $(C is) veya $(C !is) ile denetlenir ($(C ==) veya $(C !=) ile değil). -) - -$(LI Kopyalama normalde değişkeni kopyalar; nesnenin kopyalanabilmesi için $(C dup()) gibi bir üye işlev yazılması gerekir. -) - -$(LI Atama, değişkenin başka bir nesneyi göstermesini sağlar; bu davranış değiştirilemez. -) - -) - -Macros: - SUBTITLE=Sınıflar - - DESCRIPTION=D dilinin nesne yönelimli programlama olanaklarının temeli olan sınıflar (class) - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar kullanıcı türleri - -SOZLER= -$(arayuz) -$(cok_sekillilik) -$(deger_turu) -$(degisken) -$(gerceklestirme) -$(kalitim) -$(nesne) -$(referans_turu) -$(sarma) -$(sinif) -$(tanimsiz_davranis) -$(turetmek) -$(yapi) -$(yeniden_tanimlama) diff --git a/ddili/src/ders/d/sozlesmeli.cozum.d b/ddili/src/ders/d/sozlesmeli.cozum.d deleted file mode 100644 index b7f356d..0000000 --- a/ddili/src/ders/d/sozlesmeli.cozum.d +++ /dev/null @@ -1,81 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Sözleşmeli Programlama) - -$(P -Birim testlerinin yazımına $(C main)'deki kodlar kopyalanarak başlanabilir. Aşağıdaki programa yalnızca ikinci takımın kazandığı durumun testi eklenmiş: -) - ---- -int puanEkle(in int goller1, - in int goller2, - ref int puan1, - ref int puan2) -in { - assert(goller1 >= 0); - assert(goller2 >= 0); - assert(puan1 >= 0); - assert(puan2 >= 0); - -} out (sonuç) { - assert((sonuç >= 0) && (sonuç <= 2)); - -} body { - int kazanan; - - if (goller1 > goller2) { - puan1 += 3; - kazanan = 1; - - } else if (goller1 < goller2) { - puan2 += 3; - kazanan = 2; - - } else { - ++puan1; - ++puan2; - kazanan = 0; - } - - return kazanan; -} - -unittest { - int birincininPuanı = 10; - int ikincininPuanı = 7; - int kazananTaraf; - - // Birinci takım kazanır - kazananTaraf = - puanEkle(3, 1, birincininPuanı, ikincininPuanı); - assert(birincininPuanı == 13); - assert(ikincininPuanı == 7); - assert(kazananTaraf == 1); - - // Berabere - kazananTaraf = - puanEkle(2, 2, birincininPuanı, ikincininPuanı); - assert(birincininPuanı == 14); - assert(ikincininPuanı == 8); - assert(kazananTaraf == 0); - - // İkinci takım kazanır - kazananTaraf = - puanEkle(0, 1, birincininPuanı, ikincininPuanı); - assert(birincininPuanı == 14); - assert(ikincininPuanı == 11); - assert(kazananTaraf == 2); -} - -void main() { - // ... -} ---- - - -Macros: - SUBTITLE=Sözleşmeli Programlama - - DESCRIPTION=D dilinin kod güvenilirliğini arttıran olanağı 'sözleşmeli programlama' [contract programming] problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sözleşmeli programlama contract programming design by contract problem çözüm diff --git a/ddili/src/ders/d/sozlesmeli.d b/ddili/src/ders/d/sozlesmeli.d deleted file mode 100644 index eba5b87..0000000 --- a/ddili/src/ders/d/sozlesmeli.d +++ /dev/null @@ -1,406 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX sözleşmeli programlama) Sözleşmeli Programlama) - -$(P -Sözleşmeli programlama, işlevlerin hizmet sunan birimler olarak kabul edilmeleri düşüncesi üzerine kurulu bir programlama yöntemidir. Bu düşünceye göre, işlevler ve onları çağıran kodlar arasında yazısız bazı anlaşmalar vardır. Sözleşmeli programlama, bu anlaşmaları dil düzeyinde belirlemeye yarayan olanaktır. -) - -$(P -Sözleşmeli programlama, ticari bir dil olan Eiffel tarafından "design by contract (DBC)" adıyla yayılmıştır. Bu yöntem D dilinde "contract programming" olarak geçer. Birim testlerinde olduğu gibi, $(C assert) denetimlerine dayanır ve D'nin kod sağlamlığı sağlayan bir başka olanağıdır. -) - -$(P -D'de sözleşmeli programlama üç temelden oluşur: -) - -$(UL -$(LI İşlevlerin $(C in) blokları) -$(LI İşlevlerin $(C out) blokları) -$(LI Yapı ve sınıfların $(C invariant) blokları) -) - -$(P -$(C invariant) bloklarını ve $(I sözleşme kalıtımını) $(LINK2 /ders/d/invariant.html, ilerideki bir bölümde) ve yapı ve sınıflardan daha sonra göreceğiz. -) - -$(H5 $(IX in, giriş koşulu) $(IX giriş koşulu) Giriş koşulları için $(C in) blokları) - -$(P -İşlevlerin doğru çalışabilmeleri, aldıkları parametre değerlerine bağlı olabilir. Örneğin karekök alan bir işlev kendisine verilen parametrenin sıfırdan küçük olmamasını şart koşar; veya parametre olarak tarih bilgisi alan bir işlev ayın 1 ile 12 arasında olmasını şart koşar. -) - -$(P -Bu tür koşulları daha önce $(LINK2 /ders/d/assert.html, $(C assert) ve $(C enforce) bölümünde) görmüştük. İşlevlerin parametreleriyle ilgili olan $(C assert) denetimleri işlevin tanımlandığı blok içinde yapılıyordu: -) - ---- -string zamanDizgisi(in int saat, in int dakika) { - assert((saat >= 0) && (saat <= 23)); - assert((dakika >= 0) && (dakika <= 59)); - - return format("%02s:%02s", saat, dakika); -} ---- - -$(P -$(IX body) D'nin sözleşmeli programlama anlayışında işlevlerin giriş koşulları "giriş" anlamına gelen $(C in) bloklarında denetlenir. Sözleşmeli programlama blokları kullanıldığı zaman, işlevin asıl bloğu da "gövde" anlamına gelen $(C body) ile belirlenir: -) - ---- -import std.stdio; -import std.string; - -string zamanDizgisi(in int saat, in int dakika) -$(HILITE in) { - assert((saat >= 0) && (saat <= 23)); - assert((dakika >= 0) && (dakika <= 59)); - -} $(HILITE body) { - return format("%02s:%02s", saat, dakika); -} - -void main() { - writeln(zamanDizgisi(12, 34)); -} ---- - -$(P -İşlevin $(C in) bloğunun yararı, işlevin başlatılmasıyla ilgili olan denetimlerin bir arada ve ayrı bir blok içinde yapılmasıdır. Böylece $(C assert) denetimleri işlevin asıl işlemlerinin arasına karışmamış olurlar. İşlevin içinde yine de gerektikçe $(C assert) denetimleri kullanılabilir, ama giriş koşulları sözleşmeli programlama anlayışına uygun olarak $(C in) bloğuna yazılırlar. -) - -$(P -$(C in) bloklarındaki kodlar programın çalışması sırasında işlevin her çağrılışında otomatik olarak işletilirler. İşlevin asıl işleyişi, ancak bu koşullar sağlandığında devam eder. Böylece işlevin geçersiz başlangıç koşulları ile çalışması ve programın yanlış sonuçlarla devam etmesi önlenmiş olur. -) - -$(P -$(C in) bloğundaki bir $(C assert) denetiminin başarısız olması sözleşmeyi işlevi çağıran tarafın bozduğunu gösterir. İşlev sözleşmenin gerektirdiği şekilde çağrılmamış demektir. -) - -$(H5 $(IX out, sözleşme) $(IX çıkış garantisi) Çıkış garantileri için $(C out) blokları) - -$(P -İşlevin yaptığı kabul edilen sözleşmenin karşı tarafı da işlevin sağladığı garantilerdir. Örneğin belirli bir senedeki Şubat ayının kaç gün çektiği bilgisini döndüren bir işlevin çıkış garantisi, döndürdüğü değerin 28 veya 29 olmasıdır. -) - -$(P -Çıkış garantileri, işlevlerin "çıkış" anlamına gelen $(C out) bloklarında denetlenirler. -) - -$(P -İşlevin dönüş değerinin özel bir ismi yoktur; bu değer $(C return) ile isimsiz olarak döndürülür. Bu durum, dönüş değeriyle ilgili garantileri yazarken bir sorun doğurur: ismi olmayınca, dönüş değeriyle ilgili $(C assert) denetimleri de yazılamaz. -) - -$(P -Bu sorun $(C out) anahtar sözcüğünden sonra verilen isimle halledilmiştir. Bu isim dönüş değerini temsil eder ve denetlenecek olan garantilerde bu isim kullanılır: -) - ---- -int şubattaKaçGün(in int yıl) -$(HILITE out (sonuç)) { - assert((sonuç == 28) || (sonuç == 29)); - -} body { - return artıkYıl_mı(yıl) ? 29 : 28; -} ---- - -$(P -Ben $(C out) bloğunun parametresinin ismi olarak $(C sonuç) yazmayı uygun buldum; siz $(C dönüşDeğeri) gibi başka bir isim de verebilirsiniz. Hangi ismi kullanırsanız kullanın, o isim işlevin dönüş değerini temsil eder. -) - -$(P -Bazen işlevin dönüş değeri yoktur, veya dönüş değerinin denetlenmesi gerekmiyordur. O zaman $(C out) bloğu parametresiz olarak yazılır: -) - ---- -out { - // ... -} ---- - -$(P -İşleve girerken $(C in) bloklarının otomatik olarak işletilmeleri gibi, $(C out) blokları da işlevden çıkarken otomatik olarak işletilirler. -) - -$(P -$(C out) bloğundaki bir $(C assert) denetiminin başarısız olması sözleşmenin işlev tarafından bozulduğunu gösterir. İşlev sözleşmenin gerektirdiği değeri veya yan etkiyi üretememiş demektir. -) - -$(P -Daha önceki bölümlerde hiç kullanmamış olduğumuzdan da anlaşılabileceği gibi, $(C in) ve $(C out) bloklarının kullanımı seçime bağlıdır. Bunlara yine seçime bağlı olan $(C unittest) bloklarını da eklersek, D'de işlevler dört blok halinde yazılabilirler: -) - -$(UL -$(LI Giriş koşulları için $(C in) bloğu: seçime bağlıdır ve giriş koşullarını denetler) - -$(LI Çıkış garantileri için $(C out) bloğu: seçime bağlıdır ve çıkış garantilerini denetler) - -$(LI İşlevin asıl işlemlerini içeren $(C body) bloğu: bu bloğun yazılması şarttır, ama eğer $(C in) ve $(C out) blokları kullanılmamışsa $(C body) anahtar sözcüğü yazılmayabilir) - -$(LI İşlevin birim testlerini içeren $(C unittest) bloğu: bu aslında işlevin parçası değildir ve kendi başına işlev gibi yazılır; ama denetlediği işlevin hemen altına yazılması, aralarındaki bağı gösterme bakımından uygun olur) -) - -$(P -Bütün bu blokları içeren bir işlev tanımı şöyle yazılabilir: -) - ---- -import std.stdio; - -/* Toplamı iki parça olarak bölüştürür. - * - * Toplamdan öncelikle birinciye verir, ama birinciye hiçbir - * zaman 7'den fazla vermez. Gerisini ikinciye verir. */ -void bölüştür(in int toplam, out int birinci, out int ikinci) -in { - assert(toplam >= 0); - -} out { - assert(toplam == (birinci + ikinci)); - -} body { - birinci = (toplam >= 7) ? 7 : toplam; - ikinci = toplam - birinci; -} - -unittest { - int birinci; - int ikinci; - - // Toplam 0 ise ikisi de 0 olmalı - bölüştür(0, birinci, ikinci); - assert(birinci == 0); - assert(ikinci == 0); - - // Toplam 7'den az ise birincisi toplam'a, ikincisi 0'a - // eşit olmalı - bölüştür(3, birinci, ikinci); - assert(birinci == 3); - assert(ikinci == 0); - - // Sınır koşulunu deneyelim - bölüştür(7, birinci, ikinci); - assert(birinci == 7); - assert(ikinci == 0); - - // 7'den fazla olduğunda birinci 7 olmalı, gerisi ikinciye - // gitmeli - bölüştür(8, birinci, ikinci); - assert(birinci == 7); - assert(ikinci == 1); - - // Bir tane de büyük bir değerle deneyelim - bölüştür(1_000_007, birinci, ikinci); - assert(birinci == 7); - assert(ikinci == 1_000_000); -} - -void main() { - int birinci; - int ikinci; - - bölüştür(123, birinci, ikinci); - writeln("birinci: ", birinci, " ikinci: ", ikinci); -} ---- - -$(P -Program aşağıdaki gibi derlenebilir ve çalıştırılabilir: -) - -$(SHELL -$ dmd deneme.d -w -unittest -$ ./deneme -$(DARK_GRAY birinci: 7 ikinci: 116) -) - -$(P -Bu işlevin asıl işi yalnızca 2 satırdan oluşuyor; denetleyen kodlar ise tam 19 satır! Bu kadar küçük bir işlev için bu kadar emeğin gereksiz olduğu düşünülebilir. Ama dikkat ederseniz, programcı hiçbir zaman bilerek hatalı kod yazmaz. Programcının yazdığı kod her zaman için $(I doğru çalışacak şekilde) yazılmıştır. Buna rağmen, hatalar da hep böyle doğru çalışacağı düşünülen kodlar arasından çıkar. -) - -$(P -İşlevlerden beklenenlerin birim testleri ve sözleşmeli programlama ile böyle açıkça ortaya koyulmaları, doğru olarak yazdığımız işlevlerin her zaman için doğru kalmalarına yardım eder. Program hatalarını azaltan hiçbir olanağı küçümsememenizi öneririm. Birim testleri ve sözleşmeli programlama olanakları bizi zorlu hatalardan koruyan çok etkili araçlardır. Böylece zamanımızı hata ayıklamak yerine, ondan çok daha zevkli ve verimli olan kod yazmaya ayırabiliriz. -) - -$(H5 Sözleşmeli programlamayı etkisizleştirmek) - -$(P -$(IX -release, derleyici seçeneği) Birim testlerinin tersine, sözleşmeli programlama normalde etkilidir; etkisizleştirmek için özel bir derleyici veya geliştirme ortamı ayarı gerekir. Bunun için $(C dmd) derleyicisinde $(C -release) seçeneği kullanılır: -) - -$(SHELL -dmd deneme.d -w -release -) - -$(P -Program o seçenekle derlendiğinde $(C in), $(C out), ve $(C invariant) blokları programa dahil edilmezler. -) - -$(H5 $(IX in ve enforce) $(IX enforce ve in) $(IX assert ve enforce) $(IX enforce ve assert) $(C in) bloğu mu $(C enforce) mu) - -$(P -$(LINK2 /ders/d/assert.html, $(C assert) ve $(C enforce) bölümünde) karşılaştığımız $(C assert) ile $(C enforce) arasındaki karar güçlüğü $(C in) blokları ile $(C enforce()) arasında da vardır. $(C in) bloğundaki $(C assert) denetimlerinin mi yoksa işlev tanımı içindeki $(C enforce) denetimlerinin mi daha uygun olduğuna karar vermek bazen güç olabilir. -) - -$(P -Yukarıda gördüğümüz gibi, sözleşmeli programlama bütünüyle etkisizleştirilebilir. Bundan da anlaşılabileceği gibi, sözleşmeli programlama da $(C assert) ve $(C unittest) gibi $(I programcı hatalarına) karşı koruma getiren bir olanaktır. -) - -$(P -Bu yüzden işlevlerin giriş koşulu denetimlerinin hangi yöntemle sağlanacağının kararı da yine $(LINK2 /ders/d/assert.html, $(C assert) ve $(C enforce) bölümünde) gördüğümüz maddelerle verilebilir: -) - -$(UL - -$(LI -Eğer denetim programın kendisi ile ilgili ise, yani programcının olası hatalarına karşı koruma getiriyorsa $(C in) bloklarındaki $(C assert) denetimleri kullanılmalıdır. Örneğin, işlev yalnızca programın kendi işlemleri için çağırdığı bir yardımcı işlevse, o işlevin giriş koşullarını sağlamak bütünüyle programı yazan programcının sorumluluğunda demektir. O yüzden böyle bir işlevin giriş koşullarının denetimi $(C in) bloklarında yapılmalıdır.) - -$(LI -Herhangi bir işlem başka bazı koşullar sağlanmadığı için gerçekleştirilemiyorsa $(C enforce) ile hata atılmalıdır. - -$(P -Bunun bir örneğini görmek için bir dilimin en ortasını yine bir dilim olarak döndüren bir işleve bakalım. Bu işlev bir kütüphaneye ait olsun; yani, belirli bir modülün özel bir yardımcı işlevi değil, bir kütüphanenin arayüzünün bir parçası olsun. Kullanıcılar böyle bir işlevi doğru veya yanlış her türlü parametre değeriyle çağırabilecekleri için bu işlevin giriş koşullarının her zaman için denetlenmesi gerekecektir. -) - -$(P -O yüzden aşağıdaki işlevde $(C in) bloğundaki $(C assert) denetimlerinden değil, işlevin tanımındaki bir $(C enforce)'tan yararlanılmaktadır. Yoksa $(C in) bloğu kullanılmış olsa, sözleşmeli programlama etkisizleştirildiğinde böyle bir denetimin ortadan kalkması güvensiz olurdu. -) - ---- -import std.exception; - -inout(int)[] ortadakiler(inout(int)[] asılDilim, size_t uzunluk) -out (sonuç) { - assert(sonuç.length == uzunluk); - -} body { - $(HILITE enforce)(asılDilim.length >= uzunluk); - - immutable baş = (asılDilim.length - uzunluk) / 2; - immutable son = baş + uzunluk; - - return asılDilim[baş .. son]; -} - -unittest { - auto dilim = [1, 2, 3, 4, 5]; - - assert(ortadakiler(dilim, 3) == [2, 3, 4]); - assert(ortadakiler(dilim, 2) == [2, 3]); - assert(ortadakiler(dilim, 5) == dilim); -} - -void main() { -} ---- - -$(P -$(C out) blokları ile ilgili buna benzer bir karar güçlüğü yoktur. Her işlev döndürdüğü değerden kendisi sorumlu olduğundan ve bir anlamda dönüş değeri programcının sorumluluğunda olduğundan çıkış denetimleri her zaman için $(C out) bloklarına yazılmalıdır. Yukarıdaki işlev buna uygun olarak $(C out) bloğundan yararlanıyor. -) - -) - -$(LI -$(C in) blokları ve $(C enforce) arasında karar verirken başka bir kıstas, karşılaşılan durumun giderilebilen bir hata çeşidi olup olmadığıdır. Eğer giderilebilen bir durumsa hata atmak uygun olabilir. Böylece daha üst düzeydeki bir işlev atılan bu hatayı yakalayabilir ve hatanın türüne göre farklı davranabilir. -) - -) - -$(PROBLEM_TEK - -$(P -İki futbol takımının puanlarını bir maçın sonucuna göre arttıran bir işlev yazın. -) - -$(P -Bu işlevin ilk iki parametresi birinci ve ikinci takımın attıkları goller olsun. Son iki parametresi de bu takımların maçtan önceki puanları olsun. Bu işlev golleri dikkate alarak birinci ve ikinci takımın puanlarını düzenlesin: fazla gol atan taraf üç puan kazansız, goller eşitse iki takım da birer puan kazansınlar. -) - -$(P -Ek olarak, işlevin dönüş değeri de kazanan tarafı belirtsin: birinci kazanmışsa 1, ikinci kazanmışsa 2, berabere kalmışlarsa 0. -) - -$(P -Aşağıdaki programla başlayın ve işlevin dört bloğunu uygun şekilde doldurun. Benim $(C main) içine yazdığım $(C assert) denetimlerini silmeyin. Onlar benim bu işlevin çalışması konusundaki beklentilerimi belgeliyorlar. -) - ---- -int puanEkle(in int goller1, - in int goller2, - ref int puan1, - ref int puan2) -in { - // ... - -} out (sonuç) { - // ... - -} body { - int kazanan; - - // ... - - return kazanan; -} - -unittest { - // ... -} - -void main() { - int birincininPuanı = 10; - int ikincininPuanı = 7; - int kazananTaraf; - - kazananTaraf = - puanEkle(3, 1, birincininPuanı, ikincininPuanı); - assert(birincininPuanı == 13); - assert(ikincininPuanı == 7); - assert(kazananTaraf == 1); - - kazananTaraf = - puanEkle(2, 2, birincininPuanı, ikincininPuanı); - assert(birincininPuanı == 14); - assert(ikincininPuanı == 8); - assert(kazananTaraf == 0); -} ---- - -$(P -$(I Not: Burada üç değerli bir $(C enum) türü döndürmek daha uygun olabilir:) -) - ---- -enum MaçSonucu { - birinciKazandı, ikinciKazandı, berabere -} - -$(HILITE MaçSonucu) puanEkle(in int goller1, - in int goller2, - ref int puan1, - ref int puan2) -// ... ---- - -$(P -Dönüş değeri 0, 1, ve 2 değerleriyle $(C out) bloğunda karşılaştırılabilsin diye ben bu problemde $(C int) türünü seçtim. -) - -) - -Macros: - SUBTITLE=Sözleşmeli Programlama - - DESCRIPTION=D dilinin kod güvenilirliğini arttıran olanağı 'sözleşmeli programlama' [contract programming] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sözleşmeli programlama contract programming design by contract - -SOZLER= -$(birim_testi) -$(cikis_kosulu) -$(donus_degeri) -$(giris_kosulu) -$(islev) -$(parametre) -$(sozlesmeli_programlama) diff --git a/ddili/src/ders/d/standart_akim_baglamak.cozum.d b/ddili/src/ders/d/standart_akim_baglamak.cozum.d deleted file mode 100644 index ca91428..0000000 --- a/ddili/src/ders/d/standart_akim_baglamak.cozum.d +++ /dev/null @@ -1,27 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Standart Akımları Dosyalara Bağlamak) - -$(P -Programların giriş ve çıkışlarının birbirlerine bağlanabilmeleri özellikle Unix türevi işletim sistemlerinde çok kullanılır. Buna olanak vermek için, programların olabildiğince standart giriş ve çıkış akımlarıyla etkileşecek şekilde yazılmalarına çalışılır. -) - -$(P -Örneğin ismi $(C deneme.d) olan bir dosyanın hangi klasörde olduğu $(C find) ve $(C grep) programları ile şu şekilde bulunabilir: -) - -$(SHELL -find | grep deneme.d -) - -$(P -$(C find), içinde bulunulan klasörden itibaren bütün klasörlerin içindeki bütün dosyaların isimlerini çıkışına gönderir. Onun çıktısı $(C |) ile $(C grep)'e verilir ve o da içinde $(C deneme.d) bulunan satırları kendi çıkışına yazdırır. -) - - -Macros: - SUBTITLE=Standart Akımları Dosyalara Bağlamak Problem Çözümü - - DESCRIPTION=Standart Akımları Dosyalara Bağlamak Bölümü Problem Çözümü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial standart giriş çıkış dosya bağlamak yönlendirmek problem çözüm diff --git a/ddili/src/ders/d/standart_akim_baglamak.d b/ddili/src/ders/d/standart_akim_baglamak.d deleted file mode 100644 index 95f007a..0000000 --- a/ddili/src/ders/d/standart_akim_baglamak.d +++ /dev/null @@ -1,146 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX bağlamak, standart akım) Standart Akımları Dosyalara Bağlamak) - -$(P -Önceki bölümlerdeki programlar hep standart giriş ve çıkış akımları ile etkileşiyorlardı. D'nin standart akımlarının $(C stdin) ve $(C stdout) olduklarını görmüştük, ve açıkça akım bildirmeden çağrılan $(C writeln) gibi işlevlerin de arka planda bu akımları kullandıklarını öğrenmiştik. Ek olarak, standart girişin hep klavye olduğu, ve standart çıkışın da hep ekran olduğu durumlarda çalışmıştık. -) - -$(P -Bundan sonraki bölümde programları dosyalarla etkileşecek şekilde yazmayı öğreneceğiz. Dosyaların da karakter akımı olduklarını, ve bu yüzden standart giriş ve çıkışla etkileşmekten bir farkları olmadıklarını göreceksiniz. -) - -$(P -Dosya akımlarına geçmeden önce, programcılık hayatınızda çok işinize yarayacak başka bir bilgiyi bu bölümde vermek istiyorum: programınızın standart giriş ve çıkışını, sizin kodunuzda hiçbir değişiklik gerekmeden dosyalara $(I bağlayabilirsiniz). Programınız ekran yerine bir dosyaya yazabilir, ve klavye yerine bir dosyadan veya bir programdan okuyabilir. Bu, bütün modern uç birimlerin hepsinde bulunan ve programlama dilinden bağımsız bir olanaktır. -) - -$(H5 $(IX >, çıkış bağlamak) Standart çıkışı $(C >) ile bir dosyaya bağlamak) - -$(P -Programınızı bir uç birimden başlatıyorsanız, programı çalıştırmak için yazdığınız komutun sonuna $(C >) karakterinden sonra bir dosya ismi yazmanız, programın standart çıkış akımının o dosyaya bağlanması için yeterlidir. Bu durumda, programın standart çıkışına yazdığı herşey o dosyaya yazılır. -) - -$(P -Standart girişinden bir sayı alan, o sayıyı 2 ile çarpan, ve sonucu standart çıkışına yazdıran bir program düşünelim: -) - ---- -import std.stdio; - -void main() { - double sayı; - readf(" %s", &sayı); - - writeln(sayı * 2); -} ---- - -$(P -O programın isminin $(C iki_kat) olduğunu varsayarsak, programı komut satırından -) - -$(SHELL -./iki_kat > iki_kat_sonucu.txt -) - -$(P -şeklinde başlatır ve girişine örneğin 1.2 yazarsanız, programın çıktısı olan 2.4'ün ekrana $(I değil), $(C iki_kat_sonucu.txt) ismindeki bir dosyaya yazıldığını görürsünüz. $(I Not: Bu program baştan "Lütfen bir sayı giriniz: " gibi bir mesaj yazmadığı halde, siz yine de sayıyı klavyeden yazıp Enter'a basmalısınız.) -) - -$(H5 $(IX <, giriş bağlamak) Standart girişi $(C <) ile bir dosyaya bağlamak) - -$(P -Çıkışın $(C >) karakteriyle bir dosyaya bağlanmasına benzer şekilde, giriş de $(C <) karakteriyle bir dosyaya bağlanabilir. Bu durumda da girişinden bilgi bekleyen bir program klavyeden okumak yerine, belirtilen dosyadan okur. -) - -$(P -Bu sefer de elimizde girişinden aldığı sayının onda birini hesaplayan bir program olsun: -) - ---- -import std.stdio; - -void main() { - double sayı; - readf(" %s", &sayı); - - writeln(sayı / 10); -} ---- - -$(P -Eğer iki kat alan programın oluşturduğu dosya hâlâ klasörde duruyorsa, ve bu programın isminin de $(C onda_bir) olduğunu varsayarsak, programı komut satırından -) - -$(SHELL -./onda_bir < iki_kat_sonucu.txt -) - -$(P -şeklinde başlatırsanız, girişini daha önce oluşturulan $(C iki_kat_sonucu.txt) dosyasından aldığını ve çıkışa $(C 0.24) yazdırdığını görürsünüz. $(I Not: $(C iki_kat_sonucu.txt) dosyasında 2.4 olduğunu varsayıyorum.) -) - -$(P -$(C onda_bir) programı; ihtiyacı olan sayıyı artık klavyeden değil, bir dosyadan okumaktadır. -) - -$(H5 Giriş ve çıkış akımlarının ikisini birden dosyalara bağlamak) - -$(P -$(C >) ve $(C <) karakterlerini aynı anda kullanabilirsiniz: -) - -$(SHELL -./onda_bir < iki_kat_sonucu.txt > butun_sonuc.txt -) - -$(P -Bu sefer giriş $(C iki_kat_sonucu.txt) dosyasından okunur, ve çıkış da $(C butun_sonuc.txt) dosyasına yazılır. -) - -$(H5 $(IX |, program bağlamak) Programları $(C |) ile birbirlerine bağlamak) - -$(P -Yukarıda kullanılan $(C iki_kat_sonucu.txt) dosyasının iki program arasında aracılık yaptığına dikkat edin: $(C iki_kat) programı, hesapladığı sonucu $(C iki_kat_sonucu.txt) dosyasına yazmaktadır, ve $(C onda_bir) programı da ihtiyaç duyduğu sayıyı $(C iki_kat_sonucu.txt) dosyasından okumaktadır. -) - -$(P -$(C |) karakteri, programları böyle bir aracı dosyaya gerek olmadan birbirlerine bağlar. $(C |) karakteri, solundaki programın standart çıkışını sağındaki programın standart girişine bağlar. Örneğin komut satırında birbirine şu şekilde bağlanan iki program, toplu olarak "beşte bir" hesaplayan bir komut haline dönüşür: -) - -$(SHELL -./iki_kat | ./onda_bir -) - -$(P -Önce $(C iki_kat) programı çalışır ve girişinden bir sayı alır. $(I Not: O programın "Lütfen bir sayı giriniz: " gibi bir mesaj yazmadığını hatırlayın; siz yine de sayıyı klavyeden yazıp Enter'a basmalısınız.) -) - -$(P -Sonra, $(C iki_kat) programının çıkışı $(C onda_bir) programının girişine verilir ve iki katı alınmış olan sayının onda biri, yani baştaki sayının "beşte biri" çıkışa yazılır. -) - -$(PROBLEM_TEK - -$(P -İkiden fazla programı art arda bağlamayı deneyin: -) - -$(SHELL -./birinci | ./ikinci | ./ucuncu -) - -) - -Macros: - SUBTITLE=Standart Akımları Dosyalara Bağlamak - - DESCRIPTION=Standart akımları dosyalara ve başka programların standart giriş ve çıkışlarına bağlanmaları - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial standart giriş çıkış dosya bağlamak yönlendirmek - -SOZLER= -$(akim) -$(standart_cikis) -$(standart_giris) -$(uc_birim) diff --git a/ddili/src/ders/d/standart_giris.cozum.d b/ddili/src/ders/d/standart_giris.cozum.d deleted file mode 100644 index cc6dabe..0000000 --- a/ddili/src/ders/d/standart_giris.cozum.d +++ /dev/null @@ -1,15 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Girişten Bilgi Almak) - -$(P -$(C stdin), gelen karakterleri istenen türe dönüştüremeyince kullanılamaz duruma girer. Örneğin, $(C int) türünde bilgi beklenen durumda "abc" harflerinin girilmesi $(C stdin)'in kullanılamaz duruma girmesine neden olur. -) - - -Macros: - SUBTITLE=Girişten Bilgi Almak - - DESCRIPTION=D programlama dili dersi çözümleri: Standart girişten bilgi almak - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial değişkenler standart giriş diff --git a/ddili/src/ders/d/standart_giris.d b/ddili/src/ders/d/standart_giris.d deleted file mode 100644 index d4583d1..0000000 --- a/ddili/src/ders/d/standart_giris.d +++ /dev/null @@ -1,231 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX giriş) Girişten Bilgi Almak) - -$(P -Girilen bilginin daha sonradan kullanılabilmesi için bir değişkende saklanması gerekir. Örneğin okulda kaç tane öğrenci bulunduğu bilgisini alan bir program, bu bilgiyi $(C int) türünde bir değişkende tutabilir. -) - -$(P -Yazdırma işlemi sırasında dolaylı olarak $(C stdout) akımının kullanıldığını bir önceki bölümde gördük. Bu, bilginin nereye gideceğini açıklamaya yetiyordu; çünkü $(C stdout), $(I standart çıkış) demektir. Çıkışa ne yazdırılacağını da parametre olarak veriyorduk. Örneğin $(C write(öğrenci_sayısı);) yazmak, çıkışa $(C öğrenci_sayısı) değişkeninin $(I değerinin) yazdırılacağını söylemeye yetiyordu. Özetlersek: -) - -$(MONO -akım: stdout -işlem: write -yazdırılan: öğrenci_sayısı değişkeninin değeri -hedef: normalde ekran -) - -$(P -$(IX readf) $(C write)'ın karşılığı $(C readf)'tir. İsmindeki "f", "belirli bir düzende"nin İngilizcesi olan "formatted"dan gelir. -) - -$(P -Standart girişin de $(C stdin) olduğunu görmüştük. -) - -$(P -Okuma durumunda bundan başkaca önemli bir ayrıntı vardır: okunan bilginin nerede depolanacağının da belirtilmesi gerekir. Okuma işlemini de özetlersek: -) - -$(MONO -akım: stdin -işlem: readf -okunan: bir bilgi -hedef: ? -) - -$(P -Bilginin depolanacağı hedef belirtilirken bir değişkenin adresi kullanılır. Bir değişkenin adresi, o değişkenin değerinin bellekte yazılı olduğu yerdir. -) - -$(P -$(IX &, adres) D'de isimlerden önce kullanılan $(C &) karakteri, o isimle belirtilen şeyin $(I adresi) anlamına gelir. $(C readf)'e okuduğu bilgiyi yerleştireceği yer bu şekilde bildirilir: $(C &öğrenci_sayısı). Burada $(C &öğrenci_sayısı), "öğrenci_sayısı değişkenine" diye okunabilir. Bu kullanım, yukarıdaki soru işaretini ortadan kaldırır: -) - -$(MONO -akım: stdin -işlem: readf -okunan: bir bilgi -hedef: öğrenci_sayısı değişkeninin bellekteki yeri -) - -$(P -İsimlerin başına $(C &) karakteri koymak o ismin belirttiği şeyin $(I gösterilmesi) anlamına gelir. Bu gösterme kavramı, sonraki bölümlerde karşılaşacağımız referansların ve göstergelerin de özünü oluşturur. -) - -$(P -$(C readf) konusunda bir noktayı ilerideki bölümlere bırakacağız ve şimdilik ilk parametresi olarak $(STRING "%s") kullanmak gerektiğini kabul edeceğiz: -) - ---- - readf("%s", &öğrenci_sayısı); ---- - -$(P -($(I Not: Çoğu durumda aslında boşluk karakteri ile) $(STRING " %s") $(I yazmak gerekeceğini aşağıda gösteriyorum.)) -) - -$(P -$(STRING "%s"), verinin değişkene uygun olan düzende dönüştürüleceğini belirtir. Örneğin girişten gelen '4' ve '2' karakterleri, bir $(C int)'e okunduklarında 42 tamsayı değerini oluşturacak şekilde okunurlar. -) - -$(P -Bu anlatılanları gösteren programda önce sizden öğrenci sayısını bildirmeniz isteniyor. İstediğiniz değeri yazdıktan sonra Enter'a basmanız gerekir. -) - ---- -import std.stdio; - -void main() { - write("Okulda kaç öğrenci var? "); - - // Öğrenci sayısının tutulacağı değişkenin tanımlanması - int öğrenci_sayısı; - - /* Girişten gelen bilginin öğrenci_sayısı değişkenine - * atanması */ - readf("%s", &öğrenci_sayısı); - - writeln( - "Anladım: okulda ", öğrenci_sayısı, " öğrenci varmış."); -} ---- - -$(H5 $(IX %s, boşluklu) $(IX boşluk) Boşlukların gözardı edilmelerinin gerekmesi) - -$(P -Yukarıdaki gibi programlarda değerleri yazdıktan sonra Enter tuşuna basılması gerektiğini biliyorsunuz. Kullanıcının Enter tuşuna basmış olması da özel bir kod olarak ifade edilir ve o bile programın standart girişinde belirir. Programlar böylece bilgilerin tek satır olarak mı yoksa farklı satırlarda mı girildiklerini algılayabilirler. -) - -$(P -Bazı durumlarda ise girişte bekleyen o özel kodların hiçbir önemi yoktur; süzülüp gözardı edilmeleri gerekir. Yoksa standart girişi $(I tıkarlar) ve başka bilgilerin girilmesini engellerler.) - -$(P -Bunun bir örneğini görmek için yukarıdaki programda ayrıca öğretmen sayısının da girilmesini isteyelim. Program düzenini biraz değiştirerek ve açıklamaları kaldırarak: -) - ---- -import std.stdio; - -void main() { - write("Okulda kaç öğrenci var? "); - int öğrenci_sayısı; - readf("%s", &öğrenci_sayısı); - - write("Kaç öğretmen var? "); - int öğretmen_sayısı; - readf("%s", &öğretmen_sayısı); - - writeln("Anladım: okulda ", öğrenci_sayısı, " öğrenci", - " ve ", öğretmen_sayısı, " öğretmen varmış."); -} ---- - -$(P -Ne yazık ki program ikinci $(C int)'i okuyamaz: -) - -$(SHELL -Okulda kaç öğrenci var? 100 -Kaç öğretmen var? 20 - $(SHELL_NOTE_WRONG Burada bir hata atılır) -) - -$(P -Öğretmen sayısı olarak 20 yazılmış olsa da $(I bir önceki 100'ün) sonunda basılmış olan Enter'ın kodları girişi tıkamıştır ve o yüzden $(C öğretmen_sayısı) değişkeninin değeri olan 20 okunamamaktadır. Programın girişine gelen kodları şu şekilde ifade edebiliriz: -) - -$(MONO -100$(HILITE [EnterKodu])20[EnterKodu] -) - -$(P -Girişin tıkandığı noktayı işaretli olarak belirttim. -) - -$(P -Bu durumda çözüm, öğretmen sayısından önce gelen Enter kodunun önemli olmadığını belirtmek için $(STRING %s) belirtecinden önce bir boşluk karakteri kullanmaktır: $(STRING " %s"). Düzen dizgisi içinde geçen boşluk karakterleri $(I sıfır veya daha fazla sayıdaki görünmez kodu) okuyup gözardı etmeye yararlar. O tek boşluk karakteri bütün görünmez karakter kodlarını okuyup gözardı eder: normal boşluk karakteri, Enter'la girilen satır sonu karakteri, Tab karakteri vs. -) - -$(P -Genel bir kural olarak, okunan her değer için $(STRING " %s") kullanabilirsiniz. Yukarıdaki program o değişiklikle artık istendiği gibi çalışır. Yalnızca değişen satırlarını göstererek: -) - ---- -// ... - readf(" %s", &öğrenci_sayısı); -// ... - readf(" %s", &öğretmen_sayısı); -// ... ---- - -$(P -Çıktısı: -) - -$(SHELL -Okulda kaç öğrenci var? 100 -Kaç öğretmen var? 20 -Anladım: okulda 100 öğrenci ve 20 öğretmen varmış. -) - -$(H5 Ek bilgiler) - -$(UL -$(LI -$(IX açıklama) $(IX /*) $(IX */) Daha önce gördüğümüz $(COMMENT //) karakterleri tek bir satır açıklama yazmaya elverişlidir. Birden fazla satırda blok halinde açıklama yazmak için açıklamayı $(COMMENT /*) ve $(COMMENT */) belirteçleri arasına alabilirsiniz. - -$(P -$(IX /+) $(IX +/) Başka açıklama belirteçlerini de içerebilmek için $(COMMENT /+) ve $(COMMENT +/) belirteçleri kullanılır: -) - ---- - /+ - // Tek satırlık açıklama - - /* - Birden fazla - satırlık açıklama - */ - - Yukarıdaki belirteçleri bile içerebilen açıklama bloğu - +/ ---- - -) - -$(LI -Kaynak kodlardaki boşluk karakterlerinin çoğu önemsizdir. O yüzden fazla uzayan satırları bölebiliriz veya daha anlaşılır olacağını düşündüğümüz boşluklar ekleyebiliriz. Hatta yazım hatasına yol açmadığı sürece hiç boşluk kullanmayabiliriz bile: - ---- -import std.stdio;void main(){writeln("Okuması zor!");} ---- - -$(P -Fazla sıkışık kodu okumak güçtür. -) - -) - -) - -$(PROBLEM_TEK - -$(P -Girişten sayı beklenen durumda harfler girin ve programın yanlış çalıştığını gözlemleyin. -) - -) - -Macros: - SUBTITLE=Girişten Bilgi Almak - - DESCRIPTION=D dilinde girişten bilgi almak - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial girişten bilgi almak okumak - -SOZLER= -$(gosterge) -$(standart_cikis) diff --git a/ddili/src/ders/d/switch_case.cozum.d b/ddili/src/ders/d/switch_case.cozum.d deleted file mode 100644 index 207c6c7..0000000 --- a/ddili/src/ders/d/switch_case.cozum.d +++ /dev/null @@ -1,101 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU $(C switch) ve $(C case)) - -$(OL - -$(LI - ---- -import std.stdio; -import std.string; - -void main() { - string işlem; - double birinci; - double ikinci; - - write("İşlem? "); - işlem = strip(readln()); - - write("İki sayıyı aralarında boşlukla yazın: "); - readf(" %s %s", &birinci, &ikinci); - - double sonuç; - - final switch (işlem) { - - case "topla": - sonuç = birinci + ikinci; - break; - - case "çıkart": - sonuç = birinci - ikinci; - break; - - case "çarp": - sonuç = birinci * ikinci; - break; - - case "böl": - sonuç = birinci / ikinci; - break; - } - - writeln(sonuç); -} ---- - -) - -$(LI $(C case) değerlerinin virgüllerle belirlenebilmesi olanağını kullanarak: - ---- - final switch (işlem) { - - case "topla"$(HILITE, "+"): - sonuç = birinci + ikinci; - break; - - case "çıkart"$(HILITE, "-"): - sonuç = birinci - ikinci; - break; - - case "çarp"$(HILITE, "*"): - sonuç = birinci * ikinci; - break; - - case "böl"$(HILITE, "/"): - sonuç = birinci / ikinci; - break; - } ---- - -) - -$(LI Bu durumda $(C default) bölümünü eklemek gerekeceği için $(C final switch) kullanamayız. Programın değişen yerleri: - ---- -// ... - - switch (işlem) { - - // ... - - default: - throw new Exception("Geçersiz işlem"); - } - -// ... ---- - -) - -) - -Macros: - SUBTITLE=switch ve case Problem Çözümleri - - DESCRIPTION=D dilinin çoklu koşul olanağını gerçekleştiren switch ve case deyimi bölümünün problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial switch case koşul problem çözüm diff --git a/ddili/src/ders/d/switch_case.d b/ddili/src/ders/d/switch_case.d deleted file mode 100644 index 2859197..0000000 --- a/ddili/src/ders/d/switch_case.d +++ /dev/null @@ -1,376 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX switch) $(IX case) $(CH4 switch) ve $(CH4 case)) - -$(P -$(C switch), $(I çoklu koşul) gibi çalışan bir deyimdir ve bu açıdan bir "if else if" zincirine benzer. Buradaki kullanımında "durum" anlamına gelen $(C case), $(C switch)'in denetlediği değerin karşılaştırıldığı durumları belirlemek için kullanılır; kendisi bir deyim değildir. -) - -$(P -$(C switch), parantez içinde bir ifade alır; o ifadenin değerini kendi kapsamı içindeki $(C case)'lerle karşılaştırır ve o değere eşit olan $(C case)'in işlemlerini işletir. Söz dizimini şöyle gösterebiliriz: -) - ---- - switch ($(I ifade)) { - - case $(I değer_1): - // ifade'nin değer_1'e eşit olduğu durumdaki işlemler - // ... - break; - - case $(I değer_2): - // ifade'nin değer_2'ye eşit olduğu durumdaki işlemler - // ... - break; - - // ... başka case'ler ... - - default: - // hiçbir değere uymayan durumdaki işlemler - // ... - break; - } ---- - -$(P -Her ne kadar bir koşul gibi çalışsa da, $(C switch)'in aldığı ifade bir mantıksal ifade olarak kullanılmaz. Yani bir $(C if)'te olduğu gibi "eğer böyleyse" anlamında değildir. $(C switch)'teki ifadenin $(I değerinin), $(C case)'lerdeki değerlere eşit olup olmadığına bakılır. Yani, buradaki koşullar hep eşitlik karşılaştırmalarıdır. Bu açıdan bakıldığında bir "if else if" zinciri gibi düşünülebilir: -) - ---- - auto değer = $(I ifade); - - if (değer == $(I değer_1)) { - // değer_1 durumundaki işlemler - // ... - - } else if (değer == $(I değer_2)) { - // değer_2 durumundaki işlemler - // ... - } - - // ... başka 'else if'ler ... - - } else { - // hiçbir değere uymayan durumdaki işlemler - // ... - } ---- - -$(P -Ancak, bu "if else if" $(C switch)'in tam eşdeğeri değildir. Nedenlerini aşağıdaki başlıklarda açıklıyorum. -) - -$(P -İfadenin değerine eşit olan bir $(C case) değeri varsa, o $(C case)'in altındaki işlemler işletilir. Eğer yoksa, "varsayılan" anlamına gelen $(C default)'un altındaki işlemler işletilir. -) - -$(H5 $(IX goto case) $(IX goto default) $(C goto)) - -$(P -$(C goto) programcılıkta kaçınılması öğütlenen bir deyimdir. Buna rağmen nadir durumlarda $(C switch) deyimi ile kullanılması gerekebilir. $(C goto) deyimini ayrıntılı olarak $(LINK2 /ders/d/etiketler.html, daha ilerideki bir bölümde) göreceğiz. -) - -$(P -$(C if) koşulunun kapsamı olduğu için, kapsamdaki işlemler sonlanınca bütün $(C if) deyiminin işi bitmiş olur. $(C switch)'te ise ifadenin değerine eşit bir $(C case) bulunduğu zaman programın işleyişi o $(C case)'e atlar ve ya bir $(C break) ile ya da bir $(C goto case) ile $(I karşılaşılana kadar) devam eder. $(C goto case) hemen alttaki $(C case)'e devam edilmesine neden olur: -) - ---- - switch (değer) { - - case 5: - writeln("beş"); - $(HILITE goto case); // bir sonraki case'e devam eder - - case 4: - writeln("dört"); - break; - - default: - writeln("bilmiyorum"); - break; - } ---- - -$(P -$(C goto case)'in bu kullanımı isteğe bağlıdır çünkü $(C break) deyimi bulunmadığında program zaten bir sonraki $(C case) veya $(C default) bölümüne devam eder: -) - ---- - case 5: - writeln("beş"); - // 'break' deyimi yok; bir sonraki case'e devam eder - - case 4: - writeln("dört"); - break; ---- - -$(P -$(C değer) 5 olduğunda $(C case 5) satırının altına gidilir ve orada "beş" yazdırılır. Onun sonundaki $(C goto case) bir sonraki $(C case)'e devam edilmesini sağladığı için "dört" de yazdırılır ve çıktıda ikisi de yer alırlar: -) - -$(SHELL -beş -dört -) - -$(P -$(C goto) deyimi $(C case) bölümlerinde üç farklı biçimde kullanılabilir: -) - -$(UL - -$(LI $(C goto case), bir sonraki $(C case)'e atlanmasını sağlar.) - -$(LI $(C goto default), $(C default) bölümüne atlanmasını sağlar.) - -$(LI $(C goto case $(I ifade)), ifadeye uyan $(C case)'e atlanmasını sağlar.) - -) - -$(P -Bu üç kullanımı bir önceki bölümde gördüğümüz $(C foreach)'ten de yararlanan aşağıdaki programla deneyebiliriz: -) - ---- -import std.stdio; - -void main() { - foreach (değer; [ 1, 2, 3, 10, 20 ]) { - writefln("--- değer: %s ---", değer); - - switch (değer) { - - 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 -Çıktısı: -) - -$(SHELL ---- değer: 1 --- -case 1 -case 2 -case 10 ---- değer: 2 --- -case 2 -case 10 ---- değer: 3 --- -case 3 -default ---- değer: 10 --- -case 10 ---- değer: 20 --- -default -) - -$(H5 İfadenin değeri ancak tamsayı, $(C bool), veya dizgi olabilir) - -$(P -$(C if)'te eşitlik karşılaştırmasında herhangi bir tür kullanılabilir. $(C switch)'te ise ifade değeri olarak ancak tamsayılar, bool, veya dizgiler kullanılabilir. -) - ---- - string işlem = /* ... */; - // ... - switch (işlem) { - - case "toplama": - sonuç = birinci + ikinci; - break; - - case "çıkarma": - sonuç = birinci - ikinci; - break; - - case "çarpma": - sonuç = birinci * ikinci; - break; - - case "bölme": - sonuç = birinci / ikinci; - break; - - default: - throw new Exception(format("Geçersiz işlem: %s", işlem)); - } ---- - -$(P $(I Not: Yukarıdaki kod hiçbir $(C case)'e uymayan durumda bir hata atmaktadır. Hataları $(LINK2 /ders/d/hatalar.html, ilerideki bir bölümde) göreceğiz.) -) - -$(P -Her ne kadar ifade türü olarak $(C bool) da kullanılabiliyor olsa da, $(C false) ve $(C true) diye iki değeri olan bu tür için çoğu durumda $(C if)'in veya $(C ?:) üçlü işlecinin daha uygun olduğunu düşünebilirsiniz. -) - -$(H5 $(IX .., case değer aralığı) $(IX aralık, case) Değer aralıkları) - -$(P -Belirli bir değer aralığındaki durumlar $(C case)'ler arasına $(C ..) karakterleri yerleştirilerek belirtilir: -) - ---- - switch (zarDeğeri) { - - case 1: - writeln("Sen kazandın"); - break; - - case 2: $(HILITE ..) case 5: - writeln("Berabere"); - break; - - case 6: - writeln("Ben kazandım"); - break; - - default: - /* Aslında bu durumun hiç gerçekleşmemesi gerekir çünkü - * yukarıdaki durumlar bütün olası değerleri - * kapsamaktadır. (Aşağıdaki 'final switch'e bakınız.) */ - break; - } ---- - -$(P -Yukarıdaki zarla oynanan oyunda zarın 2, 3, 4, veya 5 değerinde berabere kalınmaktadır. -) - -$(H5 $(IX , (virgül), case değer listesi) Ayrık değerler) - -$(P -Yukarıdaki oyunda [2,5] aralığında değil de 2 ve 4 değerleri geldiğinde berabere kalındığını varsayalım. Öyle durumlarda $(C case)'in değerlerinin aralarına virgül yazılır: -) - ---- - case 2$(HILITE ,) 4: - writeln("Berabere"); - break; ---- - -$(H5 $(IX final switch) $(C final switch) deyimi) - -$(P -Bu deyim de $(C switch) gibidir ama bazı kısıtlamaları vardır: -) - -$(UL -$(LI $(C default) bölümü bulunamaz; zaten bu durum bazı koşullarda anlamsızdır: Örneğin, zarın değerlerinin altısının da işlemlerinin belirli olduğu bir durumda $(C default) bölümüne gerek yoktur. -) - -$(LI $(C case)'lerde aralıklı değerler kullanılamaz (virgülle gösterilen ayrık değerler ise kullanılabilir). -) - -$(LI Eğer ifade bir $(C enum) türüyse türün bütün değerlerinin $(C case)'ler tarafından kapsanmış olmaları gerekir ($(C enum)'ları bir sonraki bölümde göreceğiz). -) - -) - ---- - final switch (zarDeğeri) { - - case 1: - writeln("Sen kazandın"); - break; - - case 2, 3, 4, 5: - writeln("Berabere"); - break; - - case 6: - writeln("Ben kazandım"); - break; - } ---- - -$(H5 Ne zaman kullanmalı) - -$(P -Yukarıda anlatılanlardan anlaşıldığı gibi; $(C switch), bir ifadenin derleme zamanında bilinen değerlerle karşılaştırıldığı durumlarda kullanışlıdır. -) - -$(P -Eğer karşılaştırılacak değer yalnızca iki taneyse, $(C switch) yerine bir "if else" daha uygun olabilir. Örneğin yazı/tura gibi bir sonuçta $(C if) deyimi yeterlidir: -) - ---- - if (yazıTuraSonucu == yazı) { - // ... - - } else { - // ... - } ---- - -$(P -Genel bir kural olarak, $(C switch)'i üç veya daha fazla değer olduğunda düşünebilirsiniz. -) - -$(P -Mevcut değerlerin her birisinin $(C case) değeri olarak yer alması gereken durumlarda $(C final switch)'i yeğleyin. Bu, özellikle $(C enum) türlerine uygundur. -) - -$(PROBLEM_COK -$(PROBLEM -Yukarıdaki örneklerden birisindeki gibi bir hesap makinesi yapın. Kullanıcıdan önce işlemi $(C string) olarak, sonra da sayıları $(C double) olarak alsın ve işleme göre hesap yapsın. Örneğin işlem "topla" olarak ve sayılar "5 7" olarak girildiğinde ekrana 12 yazsın. - -$(P -Girişi şu şekilde okuyabilirsiniz: -) - ---- - string işlem; - double birinci; - double ikinci; - - // ... - - işlem = strip(readln()); - readf(" %s %s", &birinci, &ikinci); ---- - -) - -$(PROBLEM -Hesap makinesini geliştirin ve "topla" gibi sözlü işlemler yanında "+" gibi simgeleri de desteklemesini sağlayın: işlem dizgisi olarak "+" girildiğinde de aynı şekilde çalışsın. -) - -$(PROBLEM -Program bilinmeyen bir işlem girildiğinde hata atsın. Hata atma düzeneğini $(LINK2 /ders/d/hatalar.html, ilerideki bir bölümde) göreceğiz. Şimdilik yukarıdaki $(C throw) deyimini kendi programınıza uygulayın. -) - -) - -Macros: - SUBTITLE=switch ve case - - DESCRIPTION=D dilinin çoklu koşul olanağını gerçekleştiren switch ve case deyiminin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial switch case koşul - -SOZLER= -$(deyim) -$(kapsam) -$(uclu_islec) diff --git a/ddili/src/ders/d/tembel_degerlendirmeler.d b/ddili/src/ders/d/tembel_degerlendirmeler.d deleted file mode 100644 index c321ee5..0000000 --- a/ddili/src/ders/d/tembel_degerlendirmeler.d +++ /dev/null @@ -1,103 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX tembel işleç) Tembel İşleçler) - -$(P -Tembel değerlendirmeler işlemlerin gerçekten gerekli oldukları zamana kadar geciktirilmeleri anlamına gelir. İngilizcesi "lazy evaluation" olan tembel değerlendirmeler Haskell gibi bazı programlama dillerinin de temel olanakları arasındadır. -) - -$(P -İşlemlerin gerekene kadar geciktirilmeleri doğal olarak hız kazancı sağlayabilir çünkü belki de gerekmeyecek olan bir işlem için baştan zaman harcanmamış olur. Öte yandan, bir önceki bölümde de gördüğümüz gibi, $(C lazy) parametrelerin her erişildiklerinde tekrar hesaplanıyor olmaları zaman kaybına da neden olabilir. Bu olanak, dikkatli kullanıldığında ilginç programlama yöntemlerine olanak verir. -) - -$(P -Tembel değerlendirmelere yakın olan bir kavram, işleçlere verilen ifadelerin duruma göre hiç işletilmiyor olmalarıdır. Bu kavramı daha önce gördüğümüz aşağıdaki işleçlerden tanıyorsunuz: -) - -$(UL - -$(LI $(IX ||, tembel değerlendirme) $(IX veya, mantıksal işleç) $(C ||) ($(I veya)) işleci: İkinci ifade ancak birincisi $(C false) olduğunda işletilir. - ---- - if (birİfade() || belkiDeİşletilmeyecekOlanİfade()) { - // ... - } ---- - -$(P -Eğer $(C birİfade())'nin sonucu $(C true) ise, sonucun da $(C true) olacağı daha ikinci ifade işletilmeden bellidir. O durumda ikinci ifade işletilmez. -) - -) - -$(LI $(IX &&, tembel değerlendirme) $(IX ve, mantıksal işleç) $(C &&) ($(I ve)) işleci: İkinci ifade ancak birincisi $(C true) olduğunda işletilir. - ---- - if (birİfade() && belkiDeİşletilmeyecekOlanİfade()) { - // ... - } ---- - -$(P -Eğer $(C birİfade())'nin sonucu $(C false) ise, sonucun da $(C false) olacağı daha ikinci ifade işletilmeden bellidir. O durumda ikinci ifade işletilmez. -) - -) - -$(LI $(IX ?:, tembel değerlendirme) $(IX üçlü işleç) $(C ?:) işleci (üçlü işleç): Koşul $(C true) olduğunda birinci ifade, $(C false) olduğunda ikinci ifade işletilir. - ---- - int i = birKoşul() ? yaBuİfade() : yaDaBuİfade(); ---- - -$(P -$(C birKoşul())'un sonucuna göre ifadelerden yalnızca birisi işletilir. -) - -) - -) - -$(P -Bu işleçlerdeki tembellik yalnızca hız kazancıyla ilgili değildir. İfadelerden birisinin işletilmesi duruma göre hatalı olabilir. -) - -$(P -Örneğin, aşağıdaki $(I baş harfi A ise) koşulu dizginin boş olma olasılığı varsa hatalıdır: -) - ---- - dstring s; - // ... - if (s[0] == 'A') { - // ... - } ---- - -$(P -$(C s)'nin sıfır indeksli elemanına erişmeden önce öyle bir elemanın varlığından emin olmak gerekir. Bu yüzden aşağıdaki koşul yukarıdaki denetimi $(C &&) işlecinin sağ tarafına almakta ve böylece o denetimi ancak dizgi dolu olduğunda işletmektedir: -) - ---- - if ((s.length >= 1) && (s[0] == 'A')) { - // ... - } ---- - -$(P -Tembel değerlendirmeler ilerideki bölümlerde göreceğimiz $(LINK2 /ders/d/kapamalar.html, işlev göstergeleri, temsilciler), ve $(LINK2 /ders/d/araliklar.html, aralıklarla) da sağlanabilir. -) - -Macros: - SUBTITLE=Tembel Değerlendirmeler - - DESCRIPTION=D dilinde tembel değerlendirmeler [lazy evaluation] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial işlev fonksiyon function parametre tembel değerlendirme değerlendirmeler lazy evaluation - -SOZLER= -$(deger) -$(fonksiyonel_programlama) -$(islev) -$(parametre) -$(tembel_degerlendirme) diff --git a/ddili/src/ders/d/temel_turler.cozum.d b/ddili/src/ders/d/temel_turler.cozum.d deleted file mode 100644 index 5c30c64..0000000 --- a/ddili/src/ders/d/temel_turler.cozum.d +++ /dev/null @@ -1,34 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Temel Türler) - -$(P -$(C int) yerine başka bir tür ismi kullanmak yeter. İki tanesi: -) - ---- -import std.stdio; - -void main() { - writeln("Tür : ", short.stringof); - writeln("Bayt olarak uzunluğu: ", short.sizeof); - writeln("En küçük değeri : ", short.min); - writeln("En büyük değeri : ", short.max); - writeln("İlk değeri : ", short.init); - - writeln(); - - writeln("Tür : ", ulong.stringof); - writeln("Bayt olarak uzunluğu: ", ulong.sizeof); - writeln("En küçük değeri : ", ulong.min); - writeln("En büyük değeri : ", ulong.max); - writeln("İlk değeri : ", ulong.init); -} ---- - -Macros: - SUBTITLE=Temel Türler Problem Çözümü - - DESCRIPTION=İlk D programlama dili dersi çözümleri: Temel Türler - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial temel tür problem çözüm diff --git a/ddili/src/ders/d/temel_turler.d b/ddili/src/ders/d/temel_turler.d deleted file mode 100644 index a52311a..0000000 --- a/ddili/src/ders/d/temel_turler.d +++ /dev/null @@ -1,308 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX tür) $(IX tip) $(IX temel tür) Temel Türler) - -$(P -Bir bilgisayarın beyninin $(I mikro işlemci) olduğunu gördük. Bir programdaki işlemlerin çoğunu mikro işlemci yapar. Kendi yapmadığı işleri de bilgisayarın yan birimlerine devreder. -) - -$(P -Bilgisayarlarda en küçük bilgi miktarı, 0 veya 1 değerini tutabilen ve $(I bit) adı verilen yapıdır. -) - -$(P -Yalnızca 0 ve 1 değerini tutabilen bir veri türünün kullanımı çok kısıtlı olduğu için, mikro işlemciler birden fazla bitin yan yana getirilmesinden oluşan daha kullanışlı veri türleri tanımlamışlardır: örneğin 8 bitten oluşan $(I bayt) veya 32, 64, vs. bitten oluşan daha büyük veri türleri... Eğer türlerden N bitlik olanı bir mikro işlemcinin en etkin olarak kullandığı tür ise, o mikro işlemcinin $(I N bitlik) olduğu söylenir: "32 bitlik işlemci", "64 bitlik işlemci", gibi... -) - -$(P -Mikro işlemcinin tanımladığı veri türleri de kendi başlarına yeterli değillerdir; çünkü örneğin $(I öğrenci ismi) gibi veya $(I oyun kağıdı) gibi özel bilgileri tutamazlar. Mikro işlemcinin sunduğu bu genel amaçlı veri türlerini daha kullanışlı türlere çevirmek programlama dillerinin görevidir. D'nin temel türleri bile tek başlarına kullanıldıklarında $(I oyun kağıdı) gibi bir kavramı destekleyemezler. O tür kavramlar ileride anlatılacak olan $(I yapılarla) ve $(I sınıflarla) ifade edilirler. -) - -$(P -D'nin temel türleri çoğunlukla diğer dillerdeki temel türlere benzerler. Ek olarak, D'de belki de ancak bilimsel hesaplarda işe yarayan bazı ek türler de bulunur. -) - -$(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) -Tabloda kullanılan terimlerin açıklamalarını aşağıda bulacaksınız. -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    D'nin Temel Veri Türleri
    Tür Açıklama İlk Değeri
    boolBool değerifalse
    byteişaretli 8 bit0
    ubyteişaretsiz 8 bit0
    shortişaretli 16 bit0
    ushortişaretsiz 16 bit0
    intişaretli 32 bit0
    uintişaretsiz 32 bit0
    longişaretli 64 bit0L
    ulongişaretsiz 64 bit0L
    float32 bit kayan noktalı sayıfloat.nan
    double64 bit kayan noktalı sayıdouble.nan
    realya donanımın (mikro işlemcinin) tanımladığı en büyük kayan noktalı sayı türüdür (örneğin, x86 mikro işlemcilerinde 80 bit), ya da double'dır; hangisi daha büyükse...real.nan
    ifloatsanal float değerfloat.nan * 1.0i
    idoublesanal double değerdouble.nan * 1.0i
    irealsanal real değerreal.nan * 1.0i
    cfloatiki float'tan oluşan karmaşık sayıfloat.nan + float.nan * 1.0i
    cdoubleiki double'dan oluşan karmaşık sayıdouble.nan + double.nan * 1.0i
    crealiki real'den oluşan karmaşık sayıreal.nan + real.nan * 1.0i
    charUTF-8 kod birimi0xFF
    wcharUTF-16 kod birimi0xFFFF
    dcharUTF-32 kod birimi ve Unicode kod noktası0x0000FFFF
    - -$(P -Bunlara ek olarak $(I hiçbir türden olmama) kavramını ifade eden $(C void) anahtar sözcüğü de vardır. $(C cent) ve $(C ucent) anahtar sözcükleri, işaretli ve işaretsiz 128 bitlik veri türlerini temsil etmek üzere ilerisi için ayrılmışlardır. -) - -$(P -Aksine bir neden bulunmadığı sürece genel bir kural olarak tam değerler için $(C int) kullanabilirsiniz. Kesirli değerleri olan kavramlar için de öncelikle $(C double) türü uygundur. -) - -$(P Tablodaki terimlerin açıklamaları aşağıdaki gibidir:) - -$(UL - -$(LI -$(B Bool değer:) Mantıksal ifadelerde kullanılan ve "doğruluk" durumunda $(C true), "doğru olmama" durumunda $(C false) değerini alan türdür -) - -$(LI -$(B İşaretli tür:) Hem eksi hem artı değerler alabilen türdür; Örneğin -128'den 127'ye kadar değer alabilen $(C byte). İsimleri eksi $(I işaretinden) gelir. -) - -$(LI -$(B İşaretsiz tür:) Yalnızca artı değerler alabilen türdür; Örneğin 0'dan 255'e kadar değer alabilen $(C ubyte). Bu türlerin başındaki $(C u) harfi, "işaretsiz" anlamına gelen "unsigned"ın baş harfidir. -) - -$(LI -$(B Kayan noktalı sayı:) Kabaca, 1.25 gibi kesirli değerleri tutabilen türdür; hesapların hassasiyeti türlerin bit sayısıyla doğru orantılıdır (yüksek bit sayısı yüksek hassasiyet sağlar); bunların dışındaki türler kesirli değerler alamazlar; örneğin $(C int), yalnızca tamsayı değerler alabilir -) - -$(LI -$(B Karmaşık sayı:) Matematikte geçen karmaşık sayı değerlerini alabilen türdür -) - -$(LI -$(B Sanal değer:) Karmaşık sayıların salt sanal değerlerini taşıyabilen türdür; tabloda İlk Değer sütununda geçen $(C i), matematikte -1'in kare kökü olan sayıdır -) - -$(LI -$(IX .nan) $(B nan:) "Not a number"ın kısaltmasıdır ve $(I geçersiz kesirli sayı değeri) anlamına gelir -) - -) - -$(H5 Tür nitelikleri) - -$(P D'de türlerin $(I nitelikleri) vardır. Niteliklere türün isminden sonra bir nokta ve nitelik ismiyle erişilir. Örneğin $(C int)'in $(C .sizeof) niteliğine $(C int.sizeof) diye erişilir. Tür niteliklerinin yalnızca bazılarını burada göreceğiz; gerisini sonraki bölümlere bırakacağız: -) - -$(UL - -$(LI $(IX .stringof) $(C .stringof) türün okunaklı ismidir) - -$(LI $(IX .sizeof) $(C .sizeof) türün bayt olarak uzunluğudur; türün kaç bitten oluştuğunu hesaplamak için bu değeri bir bayttaki bit sayısı olan 8 ile çarpmak gerekir) - -$(LI $(IX .min) $(C .min) "en az" anlamına gelen "minimum"un kısaltmasıdır; türün alabileceği en küçük değerdir) - -$(LI $(IX .max) $(C .max) "en çok" anlamına gelen "maximum"un kısaltmasıdır; türün alabileceği en büyük değerdir) - -$(LI $(IX .init) $(IX ilk değer) $(IX varsayılan değer, tür) $(C .init) "ilk değer" anlamına gelen "initial value"nun kısasıdır"; belirli bir tür için özel bir değer belirtilmediğinde kullanılan değer budur) - -) - -$(P -Bu nitelikleri $(C int) türü üzerinde gösteren bir program şöyle yazılabilir: -) - ---- -import std.stdio; - -void main() { - writeln("Tür : ", int.stringof); - writeln("Bayt olarak uzunluğu: ", int.sizeof); - writeln("En küçük değeri : ", int.min); - writeln("En büyük değeri : ", int.max); - writeln("İlk değeri : ", int.init); -} ---- - -$(P -Programın çıktısı: -) - -$(SHELL -Tür : int -Bayt olarak uzunluğu: 4 -En küçük değeri : -2147483648 -En büyük değeri : 2147483647 -İlk değeri : 0 -) - -$(H5 $(IX size_t) $(C size_t)) - -$(P -Programlarda $(C size_t) türü ile de karşılaşacaksınız. $(C size_t) bütünüyle farklı bir tür değildir; ortama bağlı olarak $(C ulong) veya başka bir işaretsiz temel türün takma ismidir. İsmi "size type"tan gelir ve "büyüklük türü" anlamındadır. $(I Adet) gibi saymayla ilgili olan kavramları temsil ederken kullanılır. -) - -$(P -Asıl türünün sisteme göre farklı olmasının nedeni, $(C size_t)'nin programın kullanabileceği en büyük bellek miktarını tutabilecek kadar büyük bir tür olmasının gerekmesidir: 32 bitlik sistemlerde $(C uint) ve 64 bitlik sistemlerde $(C ulong). Bu yüzden, 32 bitlik sistemlerdeki en büyük tamsayı türü $(C size_t) değil, $(C ulong)'dur. -) - -$(P -Bu türün sizin ortamınızda hangi temel türün takma ismi olduğunu yine $(C .stringof) niteliği ile öğrenebilirsiniz: -) - ---- -import std.stdio; - -void main() { - writeln(size_t.stringof); -} ---- - -$(P -Yukarıdaki programı denediğim ortamda şu çıktıyı alıyorum: -) - -$(SHELL -ulong -) - -$(PROBLEM_TEK - -$(P -Diğer türlerin de niteliklerini yazdırın. -) - -$(P -$(I Not: İlerisi için düşünüldükleri için geçersiz olan $(C cent) ve $(C ucent) türlerini hiçbir durumda kullanamazsınız. Bir istisna olarak, $(I hiçbir türden olmamayı) temsil eden $(C void) türünün ise $(C .min), $(C .max), ve $(C .init) nitelikleri yoktur.) -) - -$(P -$(I Ek olarak, $(C .min) niteliği kesirli sayı türleriyle kullanılamaz. Eğer bu problemde bir kesirli sayı türünü $(C .min) niteliği ile kullanırsanız derleyici bir hata verecektir. Daha sonra $(LINK2 /ders/d/kesirli_sayilar.html, Kesirli Sayılar bölümünde) göreceğimiz gibi, kesirli sayı türlerinin en küçük değeri için $(C .max) niteliğinin eksi işaretlisini kullanmak gerekir (örneğin, $(C -double.max)).) -) - -) - - -Macros: - SUBTITLE=Temel Türler - - DESCRIPTION=D dilinin temel türleri - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial temel türler - -SOZLER= -$(bayt) -$(bit) -$(emekliye_ayrılmıştır) -$(isaretli_tur) -$(isaretsiz_tur) -$(kayan_noktali) -$(mikro_islemci) -$(nitelik) -$(sanal_sayi) -$(sinif) -$(yapi) diff --git a/ddili/src/ders/d/title.html b/ddili/src/ders/d/title.html deleted file mode 100644 index c92c099..0000000 --- a/ddili/src/ders/d/title.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - -

    -D Programlama Dili -

    - -
    - -

    -Birinci Yayım -

    - -
    - -

    -Ali Çehreli -

    - -
    - -
    -

    -Teknik düzenleme: Luís Marques -

    -
    - -
    diff --git a/ddili/src/ders/d/toc_head.html b/ddili/src/ders/d/toc_head.html deleted file mode 100644 index e6853b1..0000000 --- a/ddili/src/ders/d/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    İçindekiler

    diff --git a/ddili/src/ders/d/toc_tail.html b/ddili/src/ders/d/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/d/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/d/tur_donusumleri.d b/ddili/src/ders/d/tur_donusumleri.d deleted file mode 100644 index 7b9c18f..0000000 --- a/ddili/src/ders/d/tur_donusumleri.d +++ /dev/null @@ -1,740 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX tür dönüşümü) $(IX dönüşüm, tür) Tür Dönüşümleri) - -$(P -İşlemlerde kullanılan değişken ve nesne türlerinin hem o işlemlerle hem de birbirleriyle uyumlu olmaları gerekir. Yoksa anlamsız veya yanlış sonuçlar doğabilir. D'nin de aralarında bulunduğu bazı diller türlerin uyumluluklarını derleme zamanında denetlerler. Böyle dillere "türleri derleme zamanında belli olan" anlamında "statically typed" dil denir. -) - -$(P -Anlamsız işlem örneği olarak, bir toplama işleminde sanki bir sayıymış gibi dizgi kullanmaya çalışan şu koda bakalım: -) - ---- - char[] dizgi; - writeln(dizgi + 5); $(DERLEME_HATASI) ---- - -$(P -Derleyici o kodu tür uyuşmazlığı nedeniyle reddeder. Bu yazıyı yazdığım sırada kullandığım derleyici, türlerin uyumsuz olduğunu bildiren şu hatayı veriyor: -) - -$(SHELL -Error: $(HILITE incompatible types) for ((dizgi) + (5)): 'char[]' and 'int' -) - -$(P -O hata mesajı, $(C ((dizgi) + (5))) ifadesinde uyumsuz türler olduğunu belirtir: $(C char[]) ve $(C int). -) - -$(P -Tür uyumsuzluğu, $(I farklı tür) demek değildir. Çünkü farklı türlerin güvenle kullanılabildiği işlemler de vardır. Örneğin $(C double) türündeki bir değişkene $(C int) türünde bir değer eklenmesinde bir sakınca yoktur: -) - ---- - double toplam = 1.25; - int artış = 3; - toplam += artış; ---- - -$(P -$(C toplam) ve $(C artış) farklı türlerden oldukları halde o işlemde bir yanlışlık yoktur; çünkü bir kesirli sayı değişkeninin bir $(C int) değer kadar arttırılmasında bir uyumsuzluk yoktur. -) - -$(H5 $(IX otomatik tür dönüşümü) Otomatik tür dönüşümleri) - -$(P -Her ne kadar bir $(C double) değerin bir $(C int) değer kadar arttırılmasında bir sakınca olmasa da, o işlemin mikro işlemcide yine de belirli bir türde yapılması gerekir. $(LINK2 /ders/d/kesirli_sayilar.html, Kesirli Sayılar bölümünden) hatırlayacağınız gibi; 64 bitlik olan $(C double), 32 bitlik olan $(C int)'ten daha $(I büyük) (veya $(I geniş)) bir türdür. Bir $(C int)'e sığabilen her değer bir $(C double)'a da sığabilir. -) - -$(P -Birbirinden farklı türler kullanılan işlemlerle karşılaştığında, derleyici önce değerlerden birisini diğer türe dönüştürür, ve işlemi ondan sonra gerçekleştirir. Bu dönüşümde kullanılan tür, değer kaybına neden olmayacak şekilde seçilir. Örneğin $(C double) türü $(C int) türünün bütün değerlerini tutabilir, ama bunun tersi doğru değildir. O yüzden yukarıdaki $(C toplam += artış) işlemi $(C double) türünde güvenle gerçekleştirilebilir. -) - -$(P -Dönüştürülen değer, her zaman için isimsiz ve geçici bir değişken veya nesnedir. Asıl değerin kendisi değişmez. Örneğin yukarıdaki $(C +=) işlemi sırasında $(C artış)'ın kendi türü değiştirilmez, ama $(C artış)'ın değerine eşit olan geçici bir değer kullanılır. Yukarıdaki işlemde perde arkasında neler olduğunu şöyle gösterebiliriz: -) - ---- - { - double $(I aslında_isimsiz_olan_double_bir_deger) = artış; - toplam += $(I aslında_isimsiz_olan_double_bir_deger); - } ---- - -$(P -Derleyici, $(C int) değeri önce $(C double) türündeki geçici bir ara değere dönüştürür ve işlemde o dönüştürdüğü değeri kullanır. Bu örnekteki geçici değer yalnızca $(C +=) işlemi süresince yaşar. -) - -$(P -Böyle otomatik dönüşümler aritmetik işlemlerle sınırlı değildir. Birbirinin aynısı olmayan türlerin kullanıldığı başka durumlarda da otomatik tür dönüşümleri uygulanır. Eğer kullanılan türler bir dönüşüm sonucunda birlikte kullanılabiliyorlarsa, derleyici gerektikçe değerleri otomatik olarak dönüştürür. Örneğin $(C int) türünde parametre alan bir işleve $(C byte) türünde bir değer gönderilebilir: -) - ---- -void birİşlem(int sayı) { - // ... -} - -void main() { - byte küçükDeğer = 7; - birİşlem(küçükDeğer); // otomatik tür dönüşümü -} ---- - -$(P -Orada da önce $(C küçükDeğer)'e eşit geçici bir $(C int) oluşturulur, ve $(C birİşlem) o geçici $(C int) değeri ile çağrılır. -) - -$(H6 $(IX tamsayı terfisi) $(IX terfi, tamsayı) $(IX int terfisi) $(C int) terfileri) - -$(P -Aşağıdaki tabloda sol taraftaki türler çoğu aritmetik işlemde doğrudan kullanılmazlar, önce otomatik olarak sağ taraftaki türlere dönüştürülürler: -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Hangi Türden Hangi Türe
    boolint
    byteint
    ubyteint
    shortint
    ushortint
    charint
    wcharint
    dcharuint
    - -$(P -$(C int) terfileri $(C enum) türlerine de uygulanır. -) - -$(P -Bu terfilerin nedeni mikro işlemcinin doğal türünün $(C int) olmasıdır. Örneğin, aşağıdaki her iki değişken de $(C ubyte) oldukları halde toplama işlemi o değişkenlerin değerleri $(C int) türüne terfi edildikten sonra gerçekleştirilir: -) - ---- - ubyte a = 1; - ubyte b = 2; - writeln(typeof(a + b).stringof); // işlem ubyte değildir ---- - -$(P -Çıktısı: -) - -$(SHELL -int -) - -$(P -Terfi edilen $(C a) ve $(C b) değişkenleri değildir. Toplama işleminde kullanılabilsinler diye yalnızca onların değerleri geçici değerler olarak terfi edilirler. -) - -$(H6 $(IX aritmetik dönüşüm) Aritmetik dönüşümler) - -$(P -Aritmetik işlemlerde kullanılan değerler güvenli yönde, yani küçük türden büyük türe doğru gerçekleştirilirler. Bu kadarını akılda tutmak çoğu durumda yeterli olsa da aslında bu kurallar oldukça karışıktır, ve işaretli türlerden işaretsiz türlere yapılan dönüşümlerde de hataya yol açabilirler. -) - -$(P -Dönüşüm kuralları şöyledir: -) - -$(OL - -$(LI Değerlerden birisi $(C real) ise diğeri $(C real)'e dönüştürülür) - -$(LI Değilse ama birisi $(C double) ise diğeri $(C double)'a dönüştürülür) - -$(LI Değilse ama birisi $(C float) ise diğeri $(C float)'a dönüştürülür) - -$(LI Değilse yukarıdaki $(C int) terfisi dönüşümleri uygulanır ve sonra şu işlemlere geçilir: - -$(OL -$(LI Eğer iki tür de aynı ise durulur) -$(LI Eğer her ikisi de işaretli ise, veya her ikisi de işaretsiz ise; küçük tür büyük türe dönüştürülür) -$(LI Eğer işaretli tür işaretsiz türden büyükse, işaretsiz olan işaretliye dönüştürülür) -$(LI Hiçbirisi değilse işaretli tür işaretsiz türe dönüştürülür) -) -) -) - -$(P -Yukarıdaki son kural ne yazık ki hatalara yol açabilir: -) - ---- - int a = 0; - int b = 1; - size_t c = 0; - writeln(a - b + c); // Şaşırtıcı sonuç! ---- - -$(P -Çıktısı şaşırtıcı biçimde $(C size_t.max) olur: -) - -$(SHELL -18446744073709551615 -) - -$(P -Yukarıdaki son kural nedeniyle ifade $(C int) türünde değil, $(C size_t) türünde gerçekleştirilir. $(C size_t) de işaretsiz bir tür olduğundan -1 değerini taşıyamaz ve sonuç alttan taşarak $(C size_t.max) olur. -) - - -$(H6 $(IX sabit uzunluklu dizi, dilime dönüşüm) $(IX statik dizi, dilime dönüşüm) Dilim dönüşümleri) - -$(P -Bir kolaylık olarak, sabit uzunluklu diziler işlev çağrılarında otomatik olarak dilimlere dönüşebilirler: -) - ---- -import std.stdio; - -void foo() { - $(HILITE int[2]) dizi = [ 1, 2 ]; - - // Sabit uzunluklu dizi dilim olarak geçiriliyor: - bar(dizi); -} - -void bar($(HILITE int[]) dilim) { - writeln(dilim); -} - -void main() { - foo(); -} ---- - -$(P -$(C bar())'ın parametresi bütün elemanlara erişim sağlayan bir dilimdir: -) - -$(SHELL -[1, 2] -) - -$(P -$(B Uyarı:) Eğer işlev, dilimi sonradan kullanmak üzere saklıyorsa $(I yerel) bir sabit uzunluklu dizinin o işleve geçirilmesi yanlıştır. Örneğin, aşağıdaki programda $(C bar())'ın sonradan kullanılmak üzere sakladığı dilim $(C foo())'dan çıkıldığında geçerli değildir: -) - ---- -import std.stdio; - -void foo() { - int[2] dizi = [ 1, 2 ]; - - // Sabit uzunluklu dizi dilim olarak geçiriliyor: - bar(dizi); - -} // ← NOT: 'dizi' bu noktadan sonra geçerli değildir - -int[] saklananDilim; - -void bar(int[] dilim) { - // Yakında geçersiz olacak bir dilim saklamaktadır: - saklananDilim = dilim; - writefln("bar içinde : %s", saklananDilim); -} - -void main() { - foo(); - - /* HATA: Artık dizi elemanı olmayan belleğe erişir */ - writefln("main içinde: %s", saklananDilim); -} ---- - -$(P -Böyle bir hatanın sonucunda programın davranışı tanımsızdır. Örneğin, $(C dizi)'nin elemanlarının bulunduğu belleğin çoktan başka amaçlarla kullanıldığı gözlemlenebilir: -) - -$(SHELL -bar içinde : [1, 2] $(SHELL_NOTE asıl elemanlar) -main içinde: [4396640, 0] $(SHELL_NOTE_WRONG tanımsız davranışın gözlemlenmesi) -) - -$(H6 $(C const) dönüşümleri) - -$(P -Her referans türü kendisinin $(C const) olanına otomatik olarak dönüşür. Bu güvenli bir dönüşümdür çünkü hem zaten türün büyüklüğünde bir değişiklik olmaz hem de $(C const) değerler değiştirilemezler: -) - ---- -char[] parantezİçinde($(HILITE const char[]) metin) { - return "{" ~ metin ~ "}"; -} - -void main() { - $(HILITE char[]) birSöz; - birSöz ~= "merhaba dünya"; - parantezİçinde(birSöz); -} ---- - -$(P -O kodda sabit olmayan $(C birSöz), sabit parametre alan işleve güvenle gönderilebilir, çünkü değerler sabit referanslar aracılığıyla değiştirilemezler. -) - -$(P -Bunun tersi doğru değildir. $(C const) bir referans türü, $(C const) olmayan bir türe dönüşmez: -) - ---- -char[] parantezİçinde(const char[] metin) { - char[] parametreDeğeri = metin; $(DERLEME_HATASI) -// ... -} ---- - -$(P -Bu konu yalnızca referans değişkenleri ve referans türleri ile ilgilidir. Çünkü değer türlerinde zaten değer kopyalandığı için, kopyanın $(C const) olan asıl nesneyi değiştirmesi söz konusu olamaz: -) - ---- - const int köşeAdedi = 4; - int kopyası = köşeAdedi; // derlenir (değer türü) ---- - -$(P -Yukarıdaki durumda $(C const) türden $(C const) olmayan türe dönüşüm yasaldır çünkü dönüştürülen değer asıl değerin bir kopyası haline gelir. -) - -$(H6 $(C immutable) dönüşümleri) - -$(P -$(C immutable) belirteci kesinlikle değişmezlik gerektirdiğinden ne $(C immutable) türlere dönüşümler ne de $(C immutable) türlerden dönüşümler otomatiktir: -) - ---- - string a = "merhaba"; - char[] b = a; $(DERLEME_HATASI) - string c = b; $(DERLEME_HATASI) ---- - -$(P -$(C const) dönüşümlerde olduğu gibi bu konu da yalnızca referans türleriyle ilgilidir. Değer türlerinin değerleri kopyalandıklarından, değer türlerinde her iki yöne doğru dönüşümler de otomatiktir: -) - ---- - immutable a = 10; - int b = a; // derlenir (değer türü) ---- - -$(H6 $(C enum) dönüşümleri) - -$(P -$(LINK2 /ders/d/enum.html, $(C enum) bölümünden) hatırlayacağınız gibi, $(C enum) türleri $(I isimli değerler) kullanma olanağı sunarlar: -) - ---- - enum OyunKağıdıRengi { maça, kupa, karo, sinek } ---- - -$(P -Değerleri özellikle belirtilmediği için o tanımda değerler sıfırdan başlayarak ve birer birer arttırılarak atanır. Buna göre örneğin $(C OyunKağıdıRengi.sinek)'in değeri 3 olur. -) - -$(P -Böyle isimli $(C enum) değerleri, otomatik olarak tamsayı türlere dönüşürler. Örneğin aşağıdaki koddaki toplama işlemi sırasında $(C OyunKağıdıRengi.kupa) 1 değerini alır ve sonuç 11 olur: -) - ---- - int sonuç = 10 + OyunKağıdıRengi.kupa; - assert(sonuç == 11); ---- - -$(P -Bunun tersi doğru değildir: tamsayı değerler $(C enum) türlerine otomatik olarak dönüşmezler. Örneğin aşağıdaki kodda $(C renk) değişkeninin 2 değerinin karşılığı olan $(C OyunKağıdıRengi.karo) değerini almasını bekleyebiliriz; ama derlenemez: -) - ---- - OyunKağıdıRengi renk = 2; $(DERLEME_HATASI) ---- - -$(P -Tamsayıdan $(C enum) değerlere dönüşümün açıkça yapılması gerekir. Bunu aşağıda göreceğiz. -) - -$(H6 $(IX bool, otomatik dönüşüm) $(C bool) dönüşümleri) - -$(P -$(C false) 0'a, $(C true) da 1'e otomatik olarak dönüşür: -) - ---- - int birKoşul = false; - assert(birKoşul == 0); - - int başkaKoşul = true; - assert(başkaKoşul == 1); ---- - -$(P -$(I Hazır değer) kullanıldığında bunun tersi ancak iki özel değer için doğrudur: 0 hazır değeri $(C false)'a, 1 hazır değeri de $(C true)'ya otomatik olarak dönüşür: -) - ---- - bool birDurum = 0; - assert(!birDurum); // false - - bool başkaDurum = 1; - assert(başkaDurum); // true ---- - -$(P -Sıfır ve bir dışındaki hazır değerler otomatik olarak dönüşmezler: -) - ---- - bool b = 2; $(DERLEME_HATASI) ---- - -$(P -Bazı deyimlerin mantıksal ifadelerden yararlandıklarını biliyorsunuz: $(C if), $(C while), vs. Aslında böyle deyimlerde yalnızca $(C bool) değil, başka türler de kullanılabilir. Başka türler kullanıldığında sıfır değeri $(C false)'a, sıfırdan başka değerler de $(C true)'ya otomatik olarak dönüşürler: -) - ---- - int i; - // ... - - if (i) { // ← int, mantıksal ifade yerine kullanılıyor - // ... 'i' sıfır değilmiş - - } else { - // ... 'i' sıfırmış - } ---- - -$(P -Benzer biçimde, $(C null) değerler otomatik olarak $(C false)'a, $(C null) olmayan değerler de $(C true)'ya dönüşürler. Bu, referansların $(C null) olup olmadıklarının denetlenmesinde kolaylık sağlar: -) - ---- - int[] a; - // ... - - if (a) { // ← otomatik bool dönüşümü - // ... null değil; 'a' kullanılabilir ... - - } else { - // ... null; 'a' kullanılamaz ... - } ---- - -$(H5 $(IX açıkça yapılan tür dönüşümü) Açıkça yapılan tür dönüşümleri) - -$(P -Bazı durumlarda bazı tür dönüşümlerinin elle açıkça yapılması gerekebilir çünkü bazı dönüşümler veri kaybı tehlikesi ve güvensizlik nedeniyle otomatik değillerdir: -) - -$(UL -$(LI Büyük türden küçük türe dönüşümler) -$(LI $(C const) türden değişebilen türe dönüşümler) -$(LI $(C immutable) dönüşümleri) -$(LI Tamsayılardan $(C enum) değerlere dönüşümler) -$(LI vs.) -) - -$(P -Programcının isteği ile açıkça yapılan tür dönüşümleri için aşağıdaki yöntemler kullanılabilir: -) - -$(UL -$(LI Kurma söz dizimi) -$(LI $(C std.conv.to) işlevi) -$(LI $(C std.exception.assumeUnique) işlevi) -$(LI $(C cast) işleci) -) - -$(H6 $(IX kurucu, tür dönüşümü) Kurma söz dizimi) - -$(P -Yapı ve sınıf nesnelerinin kurma söz dizimi başka türlerle de kullanılabilir: -) - ---- - $(I HedefTür)(değer) ---- - -$(P -Örneğin, aşağıdaki $(I dönüşüm) bir $(C int) değerinden bir $(C double) değeri elde etmektedir (örneğin, sonucun virgülden sonrasını kaybetmemek için): -) - ---- - int i; - // ... - const sonuç = $(HILITE double(i)) / 2; ---- - -$(H6 $(IX to, std.conv) Çoğu dönüşüm için $(C to())) - -$(P -Daha önce hep değerleri $(C string) türüne dönüştürmek için $(C to!string) olarak kullandığımız $(C to) aslında mümkün olan her dönüşümü sağlayabilir. Söz dizimi şöyledir: -) - ---- - to!($(I HedefTür))(değer) ---- - -$(P -Aslında bir şablon olan $(C to), şablonların daha ileride göreceğimiz kısa söz diziminden de yararlanabildiği için hedef türün tek sözcükle belirtilebildiği durumlarda hedef tür parantezsiz olarak da yazılabilir: -) - ---- - to!$(I HedefTür)(değer) ---- - -$(P -$(C to)'nun kullanımını görmek için bir $(C double) değerini $(C short) türüne ve bir $(C string) değerini de $(C int) türüne dönüştürmeye çalışan aşağıdaki koda bakalım: -) - ---- -void main() { - double d = -1.75; - - short s = d; $(DERLEME_HATASI) - int i = "42"; $(DERLEME_HATASI) -} ---- - -$(P -Her $(C double) değer $(C short) olarak ifade edilemeyeceğinden ve her dizgi $(C int) olarak kabul edilebilecek karakterler içermediğinden o dönüşümler otomatik değildir. Programcı, uygun olan durumlarda bu dönüşümleri açıkça $(C to) ile gerçekleştirebilir: -) - ---- -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 -Dikkat ederseniz $(C short) türü kesirli değer alamadığı için $(C s)'nin değeri -1 olarak dönüştürülebilmiştir. -) - -$(P -$(C to()) güvenlidir: Mümkün olmayan dönüşümlerde hata atar. -) - -$(H6 $(IX assumeUnique, std.exception) Hızlı $(C immutable) dönüşümleri için $(C assumeUnique())) - -$(P -$(C to()), $(C immutable) dönüşümlerini de gerçekleştirebilir: -) - ---- - int[] dilim = [ 10, 20, 30 ]; - auto değişmez = to!($(HILITE immutable int[]))(dilim); ---- - -$(P -Yukarıdaki koddaki değiştirilebilen elemanlardan oluşan $(C dilim)'e ek olarak $(C immutable) bir dilim daha oluşturulmaktadır. $(C değişmez)'in elemanlarının gerçekten değişmemelerinin sağlanabilmesi için $(C dilim) ile aynı elemanları paylaşmaması gerekir. Aksi taktirde, $(C dilim) yoluyla yapılan değişiklikler $(C değişmez)'in elemanlarının da değişmesine ve böylece $(C immutable) belirtecine aykırı duruma düşmesine neden olurdu. -) - -$(P -Bu yüzden $(C to()), $(C immutable) dönüşümlerini asıl değerin kopyasını alarak gerçekleştirir. Aynı durum dizilerin $(C .idup) niteliği için de geçerlidir; hatırlarsanız $(C .idup)'un ismi "kopyala" anlamına gelen "duplicate"ten türemiştir. $(C değişmez)'in elemanlarının $(C dilim)'inkilerden farklı olduklarını ilk elemanlarının adreslerinin farklı olmasına bakarak gösterebiliriz: -) - ---- - assert(&(dilim[0]) $(HILITE !=) &(değişmez[0])); ---- - -$(P -Bazen bu kopya gereksiz olabilir ve nadiren de olsa program hızını etkileyebilir. Bunun bir örneğini görmek için değişmez bir tamsayı dilimi bekleyen bir işleve bakalım: -) - ---- -void işlev(immutable int[] koordinatlar) { - // ... -} - -void main() { - int[] sayılar; - sayılar ~= 10; - // ... çeşitli değişiklikler ... - sayılar[0] = 42; - - işlev(sayılar); $(DERLEME_HATASI) -} ---- - -$(P -Yukarıdaki kod, $(C sayılar) parametresi işlevin gerekçesini yerine getirmediği için derlenemez çünkü programın derlenebilmesi için $(C işlev())'e $(C immutable) bir dilim verilmesi şarttır. Bunun bir yolunun $(C to()) olduğunu gördük: -) - ---- -import std.conv; -// ... - auto değişmezSayılar = to!($(HILITE immutable int[]))(sayılar); - işlev(değişmezSayılar); ---- - -$(P -Ancak, eğer $(C sayılar) dilimi yalnızca bu parametreyi oluşturmak için gerekmişse ve $(C işlev()) çağrıldıktan sonra bir daha hiç kullanılmayacaksa, elemanların $(C değişmezSayılar) dilimine kopyalanmaları gereksiz olacaktır. $(C assumeUnique()), bir dilimin elemanlarının belirli bir noktadan sonra değişmez olarak işaretlenmelerini sağlar: -) - ---- -import std.exception; -// ... - auto değişmezSayılar = assumeUnique(sayılar); - işlev(değişmezSayılar); - assert(sayılar is null); // asıl dilim null olur ---- - -$(P -"Tek kopya olduğunu varsay" anlamına gelen $(C assumeUnique()) eleman kopyalamaz; aynı elemanlara $(C immutable) olarak erişim sağlayan yeni bir dilim döndürür. Elemanların asıl dilim aracılığıyla yanlışlıkla değiştirilmelerini önlemek için de asıl dilimi $(C null)'a eşitler. -) - -$(H6 $(IX cast) $(C cast) işleci) - -$(P -$(C to())'nun ve $(C assumeUnique())'in kendi gerçekleştirmelerinde de yararlandıkları alt düzey dönüşüm işleci $(C cast) işlecidir. -) - -$(P -Hedef tür $(C cast) parantezinin içine yazılır: -) - ---- - cast($(I HedefTür))değer ---- - -$(P -$(C cast), $(C to())'nun güvenle gerçekleştiremediği dönüşümleri de yapacak kadar güçlüdür. Örneğin, aşağıdaki dönüşümler $(C to())'nun çalışma zamanında hata atmasına neden olur: -) - ---- - OyunKağıdıRengi renk = to!OyunKağıdıRengi(7); $(CODE_NOTE_WRONG hata atar) - bool b = to!bool(2); $(CODE_NOTE_WRONG hata atar) ---- - -$(P -Örneğin, atılan hata dönüştürülmek istenen 7 değerinin $(C OyunKağıdıRengi) türünde bir karşılığı olmadığını bildirir: -) - -$(SHELL -std.conv.ConvException@phobos/std/conv.d(1778): Value (7) -$(HILITE does not match any member) value of enum 'OyunKağıdıRengi' -) - -$(P -Bir tamsayının $(C OyunKağıdıRengi) değeri olarak kullanılabileceğinden veya bir tamsayı değerin $(C bool) anlamında kullanılabileceğinden ancak programcı emin olabilir. Bu gibi durumlarda $(C cast) işlecinden yararlanılmalıdır: -) - ---- - // Olasılıkla hatalı ama mümkün: - OyunKağıdıRengi renk = cast(OyunKağıdıRengi)7; - - bool b = cast(bool)2; - assert(b); ---- - -$(P -Gösterge türleri arasındaki dönüşümler $(C cast) ile yapılmak zorundadır: -) - ---- - $(HILITE void *) v; - // ... - int * p = cast($(HILITE int*))v; ---- - -$(P -Yaygın olmasa da, bazı C kütüphane arayüzleri gösterge değerlerinin gösterge olmayan değişkenlerde tutulmalarını gerektirebilir. Asıl gösterge değeri sonuçta tekrar elde edilebildiği sürece böyle dönüşümler de $(C cast) ile gerçekleştirilir: -) - ---- - size_t saklananGöstergeDeğeri = cast($(HILITE size_t))p; - // ... - int * p2 = cast($(HILITE int*))saklananGöstergeDeğeri; ---- - -$(H5 Özet) - -$(UL -$(LI Otomatik tür dönüşümleri güvenli yönde yapılır: Küçük türden büyük türe doğru ve değişebilen türden değişmez türe doğru.) - -$(LI Ancak, işaretsiz türlere doğru yapılan dönüşümler o türler eksi değerler tutamadıkları için şaşırtıcı sonuçlar doğurabilirler.) - -$(LI $(C enum) türler tamsayı türlere otomatik olarak dönüşürler ama tamsayılar $(C enum) türlere otomatik olarak dönüşmezler.) - -$(LI $(C false) 0'a, $(C true) da 1'e otomatik olarak dönüşür. Benzer biçimde, sıfır değerler $(C false)'a, sıfır olmayan değerler de $(C true)'ya otomatik olarak dönüşür.) - -$(LI $(C null) referanslar otomatik olarak $(C false) değerine, $(C null) olmayan referanslar da $(C true) değerine dönüşürler.) - -$(LI Bazı tür dönüşümleri için kurma söz dizimi kullanılabilir.) - -$(LI Açıkça yapılan çoğu dönüşüm için $(C to()) kullanılır.) - -$(LI Kopyalamadan $(C immutable)'a dönüştürmek için $(C assumeUnique()) kullanılır.) - -$(LI $(C cast) en alt düzey ve en güçlü dönüşüm işlecidir.) - -) - -Macros: - SUBTITLE=Tür Dönüşümleri - - DESCRIPTION=D dilinde otomatik ve elle açıkça yapılan tür dönüşümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial tür dönüşümleri - -SOZLER= -$(acikca_elle_yapilan) -$(degismez) -$(derleyici) -$(dinamik) -$(gecici) -$(hazir_deger) -$(isaretli_tur) -$(isaretsiz_tur) -$(mikro_islemci) -$(otomatik) -$(referans_turu) -$(sabit) -$(soz_dizimi) -$(statik) -$(tanimsiz_davranis) -$(tur_donusumu) diff --git a/ddili/src/ders/d/tureme.cozum.d b/ddili/src/ders/d/tureme.cozum.d deleted file mode 100644 index 4d332fb..0000000 --- a/ddili/src/ders/d/tureme.cozum.d +++ /dev/null @@ -1,197 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Türeme) - -$(OL - -$(LI Üst sınıfın $(C abstract) olarak belirttiği $(C sesÇıkart) işlevi alt sınıflar tarafından $(C override) anahtar sözcüğü ile tanımlanır. - -$(P -Bu problemde $(C Tren) sınıfını gözardı edersek yalnızca $(C Vagon.sesÇıkart) ve $(C Lokomotif.sesÇıkart) işlevleri yeterlidir: -) - ---- -import std.stdio; - -class DemirYoluAracı { - void ilerle(in size_t kilometre) { - writefln("Araç %s kilometre ilerliyor:", kilometre); - - foreach (i; 0 .. kilometre / 100) { - writefln(" %s", sesÇıkart()); - } - } - - abstract string sesÇıkart(); -} - -class Vagon : DemirYoluAracı { - $(HILITE override) string sesÇıkart() const { - return "takıtak tukutak"; - } - - // ... -} - -class YolcuVagonu : Vagon { - // ... -} - -class YükVagonu : Vagon { - // ... -} - -class Lokomotif : DemirYoluAracı { - $(HILITE override) string sesÇıkart() { - return "çuf çuf"; - } -} - -void main() { - auto vagon1 = new YolcuVagonu; - vagon1.ilerle(100); - - auto vagon2 = new YükVagonu; - vagon2.ilerle(200); - - auto lokomotif = new Lokomotif; - lokomotif.ilerle(300); -} ---- - -) - -$(LI -Aşağıdaki program $(C Tren)'in sesini onu oluşturan parçaların bir birleşimi olarak üretmektedir: - ---- -import std.stdio; - -class DemirYoluAracı { - void ilerle(in size_t kilometre) { - writefln("Araç %s kilometre ilerliyor:", kilometre); - - foreach (i; 0 .. kilometre / 100) { - writefln(" %s", sesÇıkart()); - } - } - - abstract string sesÇıkart(); -} - -class Vagon : DemirYoluAracı { - override string sesÇıkart() const { - return "takıtak tukutak"; - } - - abstract void bindir(); - abstract void indir(); -} - -class YolcuVagonu : Vagon { - override void bindir() { - writeln("Yolcular biniyor"); - } - - override void indir() { - writeln("Yolcular iniyor"); - } -} - -class YükVagonu : Vagon { - override void bindir() { - writeln("Mal yükleniyor"); - } - - override void indir() { - writeln("Mal boşalıyor"); - } -} - -class Lokomotif : DemirYoluAracı { - override string sesÇıkart() { - return "çuf çuf"; - } -} - -class Tren : DemirYoluAracı { - Lokomotif lokomotif; - Vagon[] vagonlar; - - this(Lokomotif lokomotif) { - this.lokomotif = lokomotif; - } - - void vagonEkle(Vagon[] vagonlar...) { - this.vagonlar ~= vagonlar; - } - - $(HILITE override) string sesÇıkart() { - string sonuç = lokomotif.sesÇıkart(); - - foreach (vagon; vagonlar) { - sonuç ~= ", " ~ vagon.sesÇıkart(); - } - - return sonuç; - } - - void istasyondanAyrıl(string istasyon) { - foreach (vagon; vagonlar) { - vagon.bindir(); - } - - writefln("%s garından ayrılıyoruz", istasyon); - } - - void istasyonaGel(string istasyon) { - writefln("%s garına geldik", istasyon); - - foreach (vagon; vagonlar) { - vagon.indir(); - } - } -} - -void main() { - auto lokomotif = new Lokomotif; - auto tren = new Tren(lokomotif); - - tren.vagonEkle(new YolcuVagonu, new YükVagonu); - - tren.istasyondanAyrıl("Ankara"); - tren.ilerle(500); - tren.istasyonaGel("Haydarpaşa"); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Yolcular biniyor -Mal yükleniyor -Ankara garından ayrılıyoruz -Araç 500 kilometre ilerliyor: - çuf çuf, takıtak tukutak, takıtak tukutak $(SHELL_NOTE Tren.sesÇıkart'ın sonucu) - çuf çuf, takıtak tukutak, takıtak tukutak - çuf çuf, takıtak tukutak, takıtak tukutak - çuf çuf, takıtak tukutak, takıtak tukutak - çuf çuf, takıtak tukutak, takıtak tukutak -Haydarpaşa garına geldik -Yolcular iniyor -Mal boşalıyor -) - -) - -) - - -Macros: - SUBTITLE=Türeme Problem Çözümleri - - DESCRIPTION=Türeme Problem Çözümleri - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial türeme problem çözüm diff --git a/ddili/src/ders/d/tureme.d b/ddili/src/ders/d/tureme.d deleted file mode 100644 index 9aa23de..0000000 --- a/ddili/src/ders/d/tureme.d +++ /dev/null @@ -1,1097 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX türeme) $(IX kalıtım) Türeme) - -$(P -Daha genel bir türün daha özel bir alt türünü tanımlamaya türetme denir. Türetilen alt tür; genel türün üyelerini edinir, onun gibi davranır, ve onun yerine geçebilir. -) - -$(P -$(IX üst sınıf) $(IX alt sınıf) D'de türeme yalnızca sınıflar arasında geçerlidir. Yeni bir sınıf, mevcut başka bir sınıftan türetilerek tanımlanabilir. Bir sınıfın türetildiği türe $(I üst sınıf), ondan türetilen yeni sınıfa da $(I alt sınıf) adı verilir. Üst sınıfın özelliklerinin alt sınıf tarafından edinilmesine $(I kalıtım) denir. -) - -$(P -D'de iki tür türeme vardır. Bu bölümde $(I gerçekleştirme türemesi) olan $(C class)'tan türemeyi göstereceğim; $(I arayüz türemesi) olan $(C interface)'ten türemeyi ise daha sonraki bir bölüme bırakacağım. -) - -$(P -$(IX :, türeme) Sınıfın hangi sınıftan türetildiği, tanımlanırken isminden sonra yazılan $(C :) karakterinden sonra belirtilir: -) - ---- -class $(I AltSınıf) : $(I ÜstSınıf) { - // ... -} ---- - -$(P -Masa saati kavramını temsil eden bir sınıf olduğunu varsayalım: -) - ---- -$(CODE_NAME Saat)$(CODE_COMMENT_OUT)class Saat { - int saat; - int dakika; - int saniye; - - void ayarla(int saat, int dakika, int saniye = 0) { - this.saat = saat; - this.dakika = dakika; - this.saniye = saniye; - } -$(CODE_COMMENT_OUT)} ---- - -$(P -Bu sınıfın üyelerinin, nesne oluşturulduğu an özel değerler almalarının şart olmadığını varsayalım. O yüzden bu sınıfın kurucu işlevine gerek yok. Saat, daha sonraki bir zamanda $(C ayarla) üye işlevi ile ayarlanabiliyor; ve varsayılan değeri belirtilmiş olduğu için de saniye değerini vermek isteğe bağlı: -) - ---- - auto masaSaati = new Saat; - masaSaati.ayarla(20, 30); - writefln( - "%02s:%02s:%02s", - masaSaati.saat, masaSaati.dakika, masaSaati.saniye); ---- - -$(P $(I Not: Zaman bilgisini $(C toString) üye işlevi ile yazdırmak çok daha uygun olurdu. O işlevi biraz aşağıda $(C override) anahtar sözcüğünü tanırken ekleyeceğiz.) -) - -$(P -Yukarıdaki kodun çıktısı: -) - -$(SHELL -20:30:00 -) - -$(P -Bu kadarına bakarak $(C Saat) sınıfının bir yapı olarak da tanımlanabileceğini düşünebiliriz. Bu üç üyeyi bir yapı olarak da bir araya getirebilirdik, ve o yapı için de üye işlevler tanımlayabilirdik. Programa bağlı olarak, bu kadarı yeterli de olabilirdi. -) - -$(P -Oysa $(C Saat)'in sınıf olması, bize ondan yararlanarak yeni türler tanımlama olanağı sunar. -) - -$(P -Örneğin, temelde bu $(C Saat) sınıfının olanaklarını olduğu gibi içeren, ve ek olarak alarm bilgisi de taşıyan bir $(C ÇalarSaat) sınıfı düşünebiliriz. Bu sınıfı tek başına tanımlamak istesek; $(C Saat)'in mevcut üç üyesinin aynılarına ek olarak iki tane de alarm üyesi, ve saati ayarlamak için kullanılan $(C ayarla) işlevinin yanında da bir $(C alarmıKur) işlevi gerekirdi. -) - -$(P -Bu sınıf, bu anlatıma uygun olarak şöyle gerçekleştirilebilir: -) - ---- -class ÇalarSaat { - $(HILITE int saat;) - $(HILITE int dakika;) - $(HILITE int saniye;) - int alarmSaati; - int alarmDakikası; - - $(HILITE void ayarla(int saat, int dakika, int saniye = 0) {) - $(HILITE this.saat = saat;) - $(HILITE this.dakika = dakika;) - $(HILITE this.saniye = saniye;) - $(HILITE }) - - void alarmıKur(int saat, int dakika) { - alarmSaati = saat; - alarmDakikası = dakika; - } -} ---- - -$(P -$(C Saat) sınıfında da bulunan üyelerini sarı ile gösterdim. Görüldüğü gibi; $(C Saat) ve $(C ÇalarSaat) sınıflarını aynı program içinde bu şekilde ayrı ayrı tanımlamak oldukça fazla kod tekrarına neden olur. -) - -$(P -$(C class)'tan türetmek, bir sınıfın üyelerinin başka bir sınıf tarafından oldukları gibi edinilmelerini sağlar. $(C ÇalarSaat)'i $(C Saat)'ten türeterek tanımlamak, yeni sınıfı büyük ölçüde kolaylaştırır ve kod tekrarını ortadan kaldırır: -) - ---- -$(CODE_NAME ÇalarSaat)$(CODE_COMMENT_OUT)class ÇalarSaat $(HILITE : Saat) { - int alarmSaati; - int alarmDakikası; - - void alarmıKur(int saat, int dakika) { - alarmSaati = saat; - alarmDakikası = dakika; - } -$(CODE_COMMENT_OUT)} ---- - -$(P -$(C ÇalarSaat)'in $(C Saat)'ten türetildiği bu tanım öncekinin eşdeğeridir. Bu tanımdaki sarı ile işaretli bölüm, bir önceki tanımdaki sarı ile işaretli bölüm yerine geçer. -) - -$(P -$(C ÇalarSaat), $(C Saat)'in bütün üye değişkenlerini ve işlevlerini kalıtım yoluyla edindiği için bir $(C Saat) gibi de kullanılabilir: -) - ---- - auto başucuSaati = new ÇalarSaat; - başucuSaati$(HILITE .ayarla(20, 30)); - başucuSaati.alarmıKur(7, 0); ---- - -$(P -Yeni türün $(C Saat)'ten kalıtım yoluyla edindiği üyeleri de kendi üyeleri haline gelir, ve istendiğinde dışarıdan erişilebilir: -) - ---- - writefln("%02s:%02s:%02s ♫%02s:%02s", - başucuSaati$(HILITE .saat), - başucuSaati$(HILITE .dakika), - başucuSaati$(HILITE .saniye), - başucuSaati.alarmSaati, - başucuSaati.alarmDakikası); ---- - -$(P -Yukarıdaki kodun çıktısı: -) - -$(SHELL -20:30:00 ♫07:00 -) - -$(P $(I Not: Onun yerine biraz aşağıda gösterilecek olan $(C ÇalarSaat.toString) işlevini kullanmak çok daha doğru olur.) -) - -$(P -Bu örnekte görüldüğü gibi, üye veya üye işlev edinmek amacıyla yapılan türemeye $(I gerçekleştirme türemesi) denir. -) - -$(P -$(C Saat)'ten kalıtım yoluyla edinilen üyeler de $(C ÇalarSaat)'in parçaları haline gelirler. Her $(C ÇalarSaat) nesnesinin artık hem kendi tanımladığı alarmla ilgili üyeleri, hem de kalıtımla edindiği saatle ilgili üyeleri vardır. -) - -$(P -Belleği bu sefer aşağıya doğru ilerleyen bir şerit olarak hayal edersek, $(C ÇalarSaat) nesnelerinin bellekte aşağıdakine benzer biçimde durduklarını düşünebiliriz: -) - -$(MONO - │ . │ - │ . │ - nesnenin adresi → ├───────────────┤ - │$(GRAY $(I (başka veriler)))│ - │$(HILITE  saat )│ - │$(HILITE  dakika )│ - │$(HILITE  saniye )│ - │ alarmSaati │ - │ alarmDakikası │ - ├───────────────┤ - │ . │ - │ . │ -) - -$(P -$(IX vtbl) Yukarıdaki şekli yalnızca bir fikir vermesi için gösteriyorum. Üyelerin bellekte tam olarak nasıl durdukları derleyicinin kodu derlerken aldığı kararlara bağlıdır. Örneğin, $(I başka veriler) diye işaretlenmiş olan bölümde o türün sanal işlev tablosunu gösteren bir gösterge bulunur. (Nesnelerin belleğe tam olarak nasıl yerleştirildikleri bu kitabın kapsamı dışındadır.) -) - -$(H5 $(IX is-a) $(IX o-türdendir ilişkisi) Uyarı: "o türden" ise türetin) - -$(P -Gerçekleştirme türemesinin $(I üye edinme) ile ilgili olduğunu gördük. Bu amaçla türetmeyi ancak türler arasında "bu özel tür, o genel türdendir" gibi bir ilişki $(ASIL is-a) kurabiliyorsanız düşünün. Yukarıdaki örnek için böyle bir ilişkinin var olduğunu söyleyebiliriz, çünkü "çalar saat bir saattir." -) - -$(P -$(IX has-a) $(IX içerme ilişkisi) Bazı türler arasında ise böyle bir ilişki yoktur. Çoğu durumda türler arasında bir $(I içerme) ilişkisi $(ASIL has-a) vardır. Örneğin $(C Saat) sınıfına $(C Pil) de eklemek istediğimizi düşünelim. $(C Pil) üyesini türeme yoluyla edinmek uygun olmaz, çünkü "saat bir pildir" ifadesi doğru değildir: -) - ---- -class Saat : Pil { $(CODE_NOTE_WRONG YANLIŞ TASARIM) - // ... -} ---- - -$(P -Bunun nedeni saatin bir pil $(I olmaması) ama bir pil $(I içermesidir). Türler arasında böyle bir içerme ilişkisi bulunduğunda doğru olan içeren türün diğerini üye olarak tanımlamasıdır: -) - ---- -class Saat { - Pil pil; $(CODE_NOTE Doğru tasarım) - // ... -} ---- - -$(H5 $(IX tekli türeme) $(IX tekli kalıtım) $(IX sıradüzen) En fazla bir $(C class)'tan türetilebilir) - -$(P -Sınıflar birden çok $(C class)'tan türetilemezler. -) - -$(P -Örneğin "çalar saat sesli bir alettir" ilişkisini gerçekleştirmek için $(C ÇalarSaat)'i bir de $(C SesliAlet) sınıfından türetmek istesek, derleme hatası ile karşılaşırız: -) - ---- -class SesliAlet { - // ... -} - -class ÇalarSaat : Saat$(HILITE, SesliAlet) { $(DERLEME_HATASI) - // ... -} ---- - -$(P -$(C interface)'lerden ise istenildiği kadar sayıda türetilebilir. Bunu da daha sonra göreceğiz. -) - -$(P -Öte yandan, sınıfların ne kadar derinlemesine türetildiklerinin bir sınırı yoktur: -) - ---- -class Çalgı { - // ... -} - -class TelliÇalgı : Çalgı { - // ... -} - -class Kemençe : TelliÇalgı { - // ... -} ---- - -$(P -Yukarıdaki kodda $(C Kemençe) $(C TelliÇalgı)'dan, $(C TelliÇalgı) da $(C Çalgı)'dan türetilmiştir. Bu tanımda $(C Kemençe), $(C TelliÇalgı) ve $(C Çalgı) özelden genele doğru bir $(I sıradüzen) oluştururlar. -) - -$(H5 Sıradüzenin gösterimi) - -$(P -Aralarında türeme ilişkisi bulunan türlerin hepsine birden $(I sıradüzen) ismi verilir. -) - -$(P -Nesne yönelimli programlamada sıradüzenin geleneksel bir gösterimi vardır: üst sınıflar yukarıda ve alt sınıflar aşağıda olacak şekilde gösterilirler. Sınıflar arasındaki türeme ilişkisi de alt sınıftan üst sınıfa doğru bir okla belirtilir. -) - -$(P -Örneğin yukarıdaki sınıf ilişkisini de içeren bir sıradüzen şöyle gösterilir: -) - -$(MONO - Çalgı - ↗ ↖ - TelliÇalgı NefesliÇalgı - ↗ ↖ ↗ ↖ - Kemençe Saz Kaval Ney -) - -$(H5 $(IX super, üye erişimi) Üst sınıf üyelerine erişmek için $(C super) anahtar sözcüğü) - -$(P -Alt sınıf içinden üst sınıfın üyelerine erişilmek istendiğinde, üst sınıfı temsil etmek için $(C super) anahtar sözcüğü kullanılır. -) - -$(P -Örneğin $(C ÇalarSaat) sınıfının üye işlevlerinin içindeyken, $(C Saat)'ten edindiği bir üyeye $(C super.dakika) diye erişilebilir: -) - ---- -class ÇalarSaat : Saat { - // ... - - void birÜyeİşlev() { - $(HILITE super.)dakika = 10; // Saat'ten edindiği dakika değişir - dakika = 10; // ... aynı şey - } -} ---- - -$(P -Yukarıdaki koddan da anlaşıldığı gibi, $(C super) anahtar sözcüğü her zaman gerekli değildir çünkü bu durumda yalnızca $(C dakika) yazıldığında da üst sınıftaki $(C dakika) anlaşılır. $(C super)'in bu kullanımı, hem üst sınıfta hem de alt sınıfta aynı isimde üyeler bulunduğu durumlardaki karışıklıkları gidermek için yararlıdır. Bunu biraz aşağıdaki $(C super.sıfırla()) ve $(C super.toString()) kullanımlarında göreceğiz. -) - -$(P -Sıradüzendeki iki sınıfın aynı isimde üyeleri varsa isim karışıklıkları üyelerin tam isimleri belirtilerek giderilir: -) - ---- -class Alet { - string $(HILITE üretici); -} - -class Saat : Alet { - string $(HILITE üretici); -} - -class ÇalarSaat : Saat { - // ... - - void foo() { - $(HILITE Alet.)üretici = "Öz Saatçilik"; - $(HILITE Saat.)üretici = "En Öz Saatçilik"; - } -} ---- - -$(H5 $(IX super, kurucu) Üst sınıf üyelerini kurmak için $(C super) anahtar sözcüğü) - -$(P -$(C super) anahtar sözcüğü, $(I üst sınıfın kurucusu) anlamına da gelir. Alt sınıfın kurucusundan üst sınıfın kurucusunu çağırmak için kullanılır. Bu kullanımda; $(C this) nasıl bu sınıfın kurucusu ise, $(C super) de üst sınıfın kurucusudur. -) - -$(P -Üst sınıfın kurucusunun açıkça çağrılması gerekmez. Eğer alt sınıfın kurucusu üst sınıfın herhangi bir kurucusunu açıkça çağırıyorsa, üst sınıfın kurucusu çağrıldığı noktada işletilir. Öte yandan (ve eğer üst sınıfın varsayılan kurucusu varsa), üst sınıfın varsayılan kurucusu henüz alt sınıf kurucusuna girilmeden otomatik olarak işletilir. -) - -$(P -Yukarıdaki $(C Saat) ve $(C ÇalarSaat) sınıflarının kurucularını tanımlamamıştık. Bu yüzden her ikisinin üyeleri de kendi $(C .init) değerleri ile ilklenirler. Hatırlarsanız, o değer $(C int) için sıfırdır. -) - -$(P -$(C Saat)'in aşağıdaki gibi bir kurucusu olduğunu varsayalım: -) - ---- -$(CODE_NAME Saat_ctor)$(CODE_COMMENT_OUT)class Saat { - this(int saat, int dakika, int saniye) { - this.saat = saat; - this.dakika = dakika; - this.saniye = saniye; - } - - // ... -$(CODE_COMMENT_OUT)} ---- - -$(P -Kullanıcıların $(C Saat) nesnelerini bu kurucu ile kurmaları gerektiğini biliyoruz: -) - ---- - auto saat = new Saat(17, 15, 0); ---- - -$(P -Bir $(C Saat) nesnesinin öyle tek başına kurulması doğaldır. -) - -$(P -Ancak, kullanıcıların bir $(C ÇalarSaat) kurdukları durumda, onun türemeyle edindiği $(C Saat) parçasını açıkça kurmaları olanaksızdır. Hatta kullanıcılar bazı durumlarda $(C ÇalarSaat)'in bir $(C Saat)'ten türediğini bile bilmek zorunda değillerdir.) - -$(P -Kullanıcının tek amacı, yalnızca alt sınıftan bir nesne kurmak ve onu kullanmak olabilir: -) - ---- - auto başucuSaati = new ÇalarSaat(/* ... */); - // ... bir ÇalarSaat olarak kullan ... ---- - -$(P -Bu yüzden, kalıtımla edindiği üst sınıf parçasını kurmak alt sınıfın görevidir. Üst sınıfın kurucusu $(C super) ismiyle çağrılır: -) - ---- -$(CODE_NAME ÇalarSaat_ctor)$(CODE_COMMENT_OUT)class ÇalarSaat : Saat { - this(int saat, int dakika, int saniye, // Saat için - int alarmSaati, int alarmDakikası) { // ÇalarSaat için - $(HILITE super)(saat, dakika, saniye); - this.alarmSaati = alarmSaati; - this.alarmDakikası = alarmDakikası; - } - - // ... -$(CODE_COMMENT_OUT)} ---- - -$(P -$(C ÇalarSaat)'in kurucusu, hem kendisi için gereken alarmla ilgili bilgileri hem de üst sınıf için gereken saat bilgilerini parametre olarak almakta ve $(C Saat)'in üyelerini $(C super)'i çağırarak kurmaktadır. -) - -$(H5 $(IX override) Üye işlevleri $(C override) ile özel olarak tanımlamak) - -$(P -Türemenin önemli bir yararı, üst sınıfta bulunan işlevlerin alt sınıf tarafından özel olarak yeniden tanımlanabilmesidir. $(C override), bu kullanımda "hükümsüz kılmak, bastırmak" anlamına gelir. Alt sınıf, üst sınıfın işlevini kendisine uygun olacak şekilde yeniden tanımlayabilir. -) - -$(P -$(IX sanal işlev) Alt sınıfta yeniden tanımlanabilen işlevlere $(I sanal işlev) denir. Derleyiciler sanal işlevleri $(I sanal işlev gösterge tabloları) $(ASIL virtual function pointer table (vtbl)) ve $(I vtbl göstergeleri) ile gerçekleştirirler. Bu konu bu kitabın kapsamı dışında olsa da, sanal işlev çağrılarının normal işlev çağrılarından biraz daha yavaş olduklarını bilmeniz gerekir. D'de bütün sınıf işlevleri sanal varsayılırlar. O yüzden, üst sınıfın işlevinin yeniden tanımlanmasının gerekmediği bir durumda o işlevin sanal olmaması için $(C final) olarak işaretlenmesi uygun olur. $(C final) anahtar sözcüğünü daha sonra $(LINK2 /ders/d/interface.html, Arayüzler bölümünde) göreceğiz. -) - -$(P -$(C Saat)'in $(C sıfırla) isminde bir üye işlevi olduğunu düşünelim. Bu işlev bütün üyelerin değerlerini sıfırlıyor olsun: -) - ---- -class Saat { - void sıfırla() { - saat = 0; - dakika = 0; - saniye = 0; - } - - // ... -} ---- - -$(P -Hatırlayacağınız gibi, aynı işlev kalıtım yoluyla $(C ÇalarSaat) tarafından da edinilir ve onun nesneleri ile de kullanılabilir: -) - ---- - auto başucuSaati = new ÇalarSaat(20, 30, 0, 7, 0); - // ... - başucuSaati.sıfırla(); ---- - -$(P -Ancak, $(C Saat)'in bu $(C sıfırla) işlevinin alarmla ilgili üyelerden haberi yoktur; o, yalnızca kendi sınıfının üyeleri ile ilgili olabilir. Bu yüzden, alt sınıfın üyelerinin de sıfırlanabilmeleri için; üst sınıftaki $(C sıfırla) işlevinin $(I bastırılması), ve alt sınıfta yeniden tanımlanması gerekir: -) - ---- -class ÇalarSaat : Saat { - $(HILITE override) void sıfırla() { - super.sıfırla(); - alarmSaati = 0; - alarmDakikası = 0; - } - - // ... -} ---- - -$(P -Alt sınıfın yalnızca kendi üyelerini sıfırladığına, ve üst sınıfın işini $(C super.sıfırla()) çağrısı yoluyla üst sınıfa havale ettiğine dikkat edin. -) - -$(P -Yukarıdaki kodda $(C super.sıfırla()) yerine yalnızca $(C sıfırla()) yazamadığımıza da ayrıca dikkat edin. Eğer yazsaydık, $(C ÇalarSaat) sınıfı içinde bulunduğumuz için öncelikle onun $(sıfırla) işlevi anlaşılırdı, ve içinde bulunduğumuz bu işlev tekrar tekrar kendisini çağırırdı. Sonuçta da program sonsuz döngüye girer, bir bellek sorunu yaşar, ve çökerek sonlanırdı. -) - -$(P -$(C toString)'in tanımını bu noktaya kadar geciktirmemin nedeni, her sınıfın bir sonraki bölümde anlatacağım $(C Object) isminde bir sınıftan otomatik olarak türemiş olması ve $(C Object)'in zaten bir $(C toString) işlevi tanımlamış olmasıdır. -) - -$(P -Bu yüzden, bir sınıfın $(C toString) işlevinin tanımlanabilmesi için $(C override) anahtar sözcüğünün de kullanılması gerekir: -) - ---- -$(CODE_NAME Saat_ÇalarSaat)import std.string; - -class Saat { - $(HILITE override) string toString() const { - return format("%02s:%02s:%02s", saat, dakika, saniye); - } - - // ... -$(CODE_XREF Saat)$(CODE_XREF Saat_ctor)} - -class ÇalarSaat : Saat { - $(HILITE override) string toString() const { - return format("%s ♫%02s:%02s", super.toString(), - alarmSaati, alarmDakikası); - } - - // ... -$(CODE_XREF ÇalarSaat)$(CODE_XREF ÇalarSaat_ctor)} ---- - -$(P -$(C ÇalarSaat)'in işlevinin $(C Saat)'in işlevini $(C super.toString()) diye çağırdığına dikkat edin. -) - -$(P -Artık $(C ÇalarSaat) nesnelerini de dizgi olarak ifade edebiliriz: -) - ---- -$(CODE_XREF Saat_ÇalarSaat)void main() { - auto masaSaatim = new ÇalarSaat(10, 15, 0, 6, 45); - writeln($(HILITE masaSaatim)); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -10:15:00 ♫06:45 -) - -$(H5 $(IX çok şekillilik, çalışma zamanı) $(IX çalışma zamanı çok şekilliliği) Alt sınıf nesnesi, üst sınıf nesnesi yerine geçebilir) - -$(P -Üst sınıf daha $(I genel), ve alt sınıf daha $(I özel) olduğu için; alt sınıf nesneleri üst sınıf nesneleri yerine geçebilirler. Buna $(I çok şekillilik) denir. -) - -$(P -Bu genellik ve özellik ilişkisini "bu tür o türdendir" gibi ifadelerde görebiliriz: "çalar saat bir saattir", "öğrenci bir insandır", "kedi bir omurgalı hayvandır", vs. Bu ifadelere uygun olarak; saatin gerektiği yerde çalar saat, insanın gerektiği yerde öğrenci, omurgalı hayvanın gerektiği yerde de kedi kullanılabilir. -) - -$(P -Üst sınıfın yerine kullanılan alt sınıf nesneleri kendi türlerini kaybetmezler. Nasıl normal hayatta bir çalar saatin bir saat olarak kullanılması onun aslında bir çalar saat olduğu gerçeğini değiştirmiyorsa, türemede de değiştirmez. Alt sınıf kendisi gibi davranmaya devam eder. -) - -$(P -Elimizde $(C Saat) nesneleri ile çalışabilen bir işlev olsun. Bu işlev kendi işlemleri sırasında bu verilen saati de sıfırlıyor olsun: -) - ---- -void kullan(Saat saat) { - // ... - saat.sıfırla(); - // ... -} ---- - -$(P -Çok şekilliliğin yararı böyle durumlarda ortaya çıkar. Yukarıdaki işlev $(C Saat) türünden bir parametre beklediği halde, onu bir $(C ÇalarSaat) nesnesi ile de çağırabiliriz: -) - ---- - auto masaSaatim = new ÇalarSaat(10, 15, 0, 6, 45); - writeln("önce : ", masaSaatim); - $(HILITE kullan(masaSaatim)); - writeln("sonra: ", masaSaatim); ---- - -$(P -$(C kullan) işlevi, $(C masaSaatim) nesnesini bir $(C ÇalarSaat) olmasına rağmen kabul eder, ve bir $(C Saat) gibi kullanır. Bu, aralarındaki türemenin "çalar saat bir saattir" ilişkisini oluşturmuş olmasındandır. Sonuçta, $(C masaSaatim) nesnesi sıfırlanmıştır: -) - -$(SHELL -önce : 10:15:00 ♫06:45 -sonra: 00:00:00 ♫$(HILITE 00:00) -) - -$(P -Burada dikkatinizi çekmek istediğim önemli nokta, yalnızca saat bilgilerinin değil, alarm bilgilerinin de sıfırlanmış olmasıdır. -) - -$(P -$(C kullan) işlevinde bir $(C Saat)'in $(C sıfırla) işlevinin çağrılıyor olmasına karşın; asıl nesne bir $(C ÇalarSaat) olduğu için, o türün özel $(C sıfırla) işlevi çağrılır ve onun tanımı gereği hem saatle ilgili üyeleri, hem de alarmla ilgili üyeleri sıfırlanır. -) - -$(P -$(C masaSaatim)'in $(C kullan) işlevine bir $(C Saat)'miş gibi gönderilebilmesi, onun asıl türünde bir değişiklik yapmaz. Görüldüğü gibi, $(C kullan) işlevi bir $(C Saat) nesnesi kullandığını düşündüğü halde, elindeki nesnenin asıl türüne uygun olan $(C sıfırla) işlevi çağrılmıştır. -) - -$(P -$(C Saat) sıradüzenine bir sınıf daha ekleyelim. Sıfırlanmaya çalışıldığında üyelerinin rasgele değerler aldığı $(C BozukSaat) sınıfı: -) - ---- -import std.random; - -class BozukSaat : Saat { - this() { - super(0, 0, 0); - } - - override void sıfırla() { - saat = uniform(0, 24); - dakika = uniform(0, 60); - saniye = uniform(0, 60); - } -} ---- - -$(P -O türün parametre kullanmadan kurulabilmesi için parametresiz bir kurucu işlev tanımladığına da dikkat edin. O kurucunun tek yaptığı, kendi sorumluğunda bulunan üst sınıfını kurmaktır. -) - -$(P -$(C kullan) işlevine bu türden bir nesne gönderdiğimiz durumda da bu türün özel $(C sıfırla) işlevi çağrılır. Çünkü bu durumda da $(C kullan) içindeki $(C saat) parametresinin asıl türü $(C BozukSaat)'tir: -) - ---- - auto raftakiSaat = new BozukSaat; - kullan(raftakiSaat); - writeln(raftakiSaat); ---- - -$(P -$(C BozukSaat)'in $(C kullan) içinde sıfırlanması sonucunda oluşan rasgele saat değerleri: -) - -$(SHELL -22:46:37 -) - -$(H5 Türeme geçişlidir) - -$(P -Sınıfların birbirlerinin yerine geçmeleri yalnızca türeyen iki sınıfla sınırlı değildir. Alt sınıflar, üst sınıflarının türedikleri sınıfların da yerine geçerler. -) - -$(P -Yukarıdaki $(C Çalgı) sıradüzenini hatırlayalım: -) - ---- -class Çalgı { - // ... -} - -class TelliÇalgı : Çalgı { - // ... -} - -class Kemençe : TelliÇalgı { - // ... -} ---- - -$(P -Oradaki türemeler şu iki ifadeyi gerçekleştirirler: "telli çalgı bir çalgıdır" ve "kemençe bir telli çalgıdır". Dolayısıyla, "kemençe bir çalgıdır" da doğru bir ifadedir. Bu yüzden, $(C Çalgı) beklenen yerde $(C Kemençe) de kullanılabilir. -) - -$(P -Gerekli türlerin ve üye işlevlerin tanımlanmış olduklarını varsayarsak: -) - ---- -void güzelÇal(Çalgı çalgı, Parça parça) { - çalgı.akortEt(); - çalgı.çal(parça); -} - -// ... - - auto kemençem = new Kemençe; - güzelÇal(kemençem, doğaçlama); ---- - -$(P -$(C güzelÇal) işlevi bir $(C Çalgı) beklediği halde, onu bir $(C Kemençe) ile çağırabiliyoruz; çünkü geçişli olarak "kemençe bir çalgıdır". -) - -$(P -Türeme yalnızca iki sınıfla sınırlı değildir. Eldeki probleme bağlı olarak, ve her sınıfın tek bir $(C class)'tan türeyebileceği kuralına uyulduğu sürece, sıradüzen gerektiği kadar kapsamlı olabilir. -) - -$(H5 $(IX soyut üye işlev) Soyut üye işlevler ve soyut sınıflar) - -$(P -Bazen bir sınıfta bulunmasının doğal olduğu, ama o sınıfın kendisinin tanımlayamadığı işlevlerle karşılaşılabilir. Somut bir gerçekleştirmesi bulunmayan bu işleve bu sınıfın bir $(I soyut işlevi) denir. En az bir soyut işlevi bulunan sınıflara da $(I soyut sınıf) ismi verilir. -) - -$(P -Örneğin satranç taşlarını ifade eden bir sıradüzende $(C SatrançTaşı) sınıfının taşın hamlesinin yasal olup olmadığını sorgulamaya yarayan $(C yasal_mı) isminde bir işlevi olduğunu varsayalım. Böyle bir sıradüzende bu üst sınıf, taşın hangi karelere ilerletilebileceğini bilemiyor olabilir; her taşın hareketi, onunla ilgili olan alt sınıf tarafından biliniyordur: piyonun hareketini $(C Piyon) sınıfı biliyordur, şahın hareketini $(C Şah) sınıfı, vs. -) - -$(P -$(IX abstract) $(C abstract) anahtar sözcüğü, o üye işlevin bu sınıfta gerçekleştirilmediğini, ve alt sınıflardan birisinde gerçekleştirilmesinin $(I şart olduğunu) bildirir: -) - ---- -class SatrançTaşı { - $(HILITE abstract) bool yasal_mı(in Kare nereden, in Kare nereye); -} ---- - -$(P -Görüldüğü gibi; o işlev o sınıfta tanımlanmamış, yalnızca $(C abstract) olarak bildirilmiştir. -) - -$(P -Soyut sınıf türlerinin nesneleri oluşturulamaz: -) - ---- - auto taş = new SatrançTaşı; $(DERLEME_HATASI) ---- - -$(P -Bunun nedeni, eksik işlevi yüzünden bu sınıfın kullanılamaz durumda bulunmasıdır. Çünkü; eğer oluşturulabilse, $(C taş.yasal_mı(buKare, şuKare)) gibi bir çağrının sonucunda ne yapılacağı bu sınıf tarafından bilinemez. -) - -$(P -Öte yandan, bu işlevin tanımını veren alt sınıfların nesneleri oluşturulabilir; çünkü alt sınıf bu işlevi kendisine göre tanımlamıştır ve işlev çağrısı sonucunda ne yapılacağı böylece bilinir: -) - ---- -class Piyon : SatrançTaşı { - override bool yasal_mı(in Kare nereden, in Kare nereye) { - // ... işlevin piyon tarafından gerçekleştirilmesi ... - return karar; - } -} ---- - -$(P -Bu işlevin tanımını da sunduğu için bu alt sınıftan nesneler oluşturulabilir: -) - ---- - auto taş = new Piyon; // derlenir ---- - -$(P -Soyut işlevlerin de tanımları bulunabilir. (Alt sınıf yine de kendi tanımını vermek zorundadır.) Örneğin, $(C SatrançTaşı) türünün $(C yasal_mı) işlevi genel denetimler içerebilir: -) - ---- -class SatrançTaşı { - $(HILITE abstract) bool yasal_mı(in Kare nereden, in Kare nereye) { - // 'nereden' karesinin 'nereye' karesinden farklı - // olduğunu denetliyoruz - return nereden != nereye; - } -} - -class Piyon : SatrançTaşı{ - override bool yasal_mı(in Kare nereden, in Kare nereye) { - // Öncelikle hamlenin herhangi bir SatrançTaşı için - // yasal olduğundan emin oluyoruz - if (!$(HILITE super.yasal_mı)(nereden, nereye)) { - return false; - } - - // ... sonra Piyon için özel karar veriyoruz ... - - return karar; - } -} ---- - -$(P -$(C SatrançTaşı) sınıfı $(C yasal_mı) işlevi tanımlanmış olduğu halde yine de sanaldır. $(C Piyon) sınıfının ise nesneleri oluşturulabilir. -) - -$(H5 Örnek) - -$(P -Bir örnek olarak demir yolunda ilerleyen araçlarla ilgili bir sıradüzene bakalım. Bu örnekte sonuçta şu sıradüzeni gerçekleştirmeye çalışacağız: -) - -$(MONO - DemirYoluAracı - / | \ - Lokomotif Tren Vagon { bindir()?, indir()? } - / \ - YolcuVagonu YükVagonu -) - -$(P -$(C Vagon) sınıfının soyut olarak bıraktığı işlevleri aynı satırda soru işaretleriyle belirttim. -) - -$(P -Burada amacım yalnızca sınıf ve sıradüzen tasarımlarını göstermek olduğu için fazla ayrıntıya girmeyeceğim ve yalnızca gerektiği kadar kod yazacağım. O yüzden aşağıdaki işlevlerde gerçek işler yapmak yerine yalnızca çıkışa mesaj yazdırmakla yetineceğim. -) - -$(P -Yukarıdaki tasarımdaki en genel araç olan $(C DemirYoluAracı) yalnızca ilerleme işiyle ilgilenecek şekilde tasarlanmış olsun. Genel olarak bir $(I demir yolu aracı) olarak kabul edilebilmek için bu tasarımda bundan daha fazlası da gerekmiyor. O sınıfı şöyle tanımlayabiliriz: -) - ---- -$(CODE_NAME DemirYoluAracı)class DemirYoluAracı { - void ilerle(in size_t kilometre) { - writefln("Araç %s kilometre ilerliyor", kilometre); - } -} ---- - -$(P -$(C DemirYoluAracı)'ndan türemiş olan bir tür $(C Lokomotif). Bu sınıfın henüz bir özelliği bulunmuyor: -) - ---- -$(CODE_NAME Lokomotif)$(CODE_XREF DemirYoluAracı)class Lokomotif : DemirYoluAracı { -} ---- - -$(P -Problemler bölümünde $(C DemirYoluAracı)'na soyut $(C sesÇıkart) işlevini eklediğimiz zaman $(C Lokomotif) türü de $(C sesÇıkart) işlevinin tanımını vermek zorunda kalacak. -) - -$(P -Benzer biçimde, $(C Vagon) da bir $(C DemirYoluAracı)'dır. Ancak, eğer vagonların programda yük ve yolcu vagonu olarak ikiye ayrılmaları gerekiyorsa $(I indirme) ve $(I bindirme) işlemlerinin farklı olarak tanımlanmaları gerekebilir. Kullanım amacına uygun olarak her vagon mal veya yolcu taşır. Bu genel tanıma uygun olarak bu sınıfa iki işlev ekleyelim: -) - ---- -$(CODE_NAME Vagon)class Vagon : DemirYoluAracı { - $(HILITE abstract) void bindir(); - $(HILITE abstract) void indir(); -} ---- - -$(P -Görüldüğü gibi, bu işlevlerin $(C Vagon) arayüzünde $(I soyut) olarak tanımlanmaları gerekiyor çünkü vagonun indirme ve bindirme işlemleri sırasında tam olarak ne olacağı o vagonun türüne bağlıdır. Bu işlemler $(C Vagon) düzeyinde bilinemez. Yolcu vagonlarının indirme işlemi vagon kapılarının açılması ve yolcuların trenden çıkmalarını beklemek kadar basit olabilir. Yük vagonlarında ise yük taşıyan görevlilere ve belki de vinç gibi bazı araçlara gerek duyulabilir. Bu yüzden $(C indir) ve $(C bindir) işlevlerinin sıradüzenin bu aşamasında soyut olmaları gerekir. -) - -$(P -Soyut $(C Vagon) sınıfının soyut işlevlerini gerçekleştirmek, ondan türeyen somut iki sınıfın görevidir: -) - ---- -$(CODE_NAME YolcuVagonu_YükVagonu)class YolcuVagonu : Vagon { - override void bindir() { - writeln("Yolcular biniyor"); - } - - override void indir() { - writeln("Yolcular iniyor"); - } -} - -class YükVagonu : Vagon { - override void bindir() { - writeln("Mal yükleniyor"); - } - - override void indir() { - writeln("Mal boşalıyor"); - } -} ---- - -$(P -Soyut bir sınıf olması $(C Vagon)'un kullanılamayacağı anlamına gelmez. $(C Vagon) sınıfının kendisinden nesne oluşturulamasa da $(C Vagon) sınıfı bir arayüz olarak kullanılabilir. Yukarıdaki türetmeler "yük vagonu bir vagondur" ve "yolcu vagonu bir vagondur" ilişkilerini gerçekleştirdikleri için bu iki sınıfı $(C Vagon) yerine kullanabiliriz. Bunu biraz aşağıda $(C Tren) sınıfı içinde göreceğiz. -) - -$(P -Treni temsil eden sınıfı bir lokomotif ve bir vagon dizisi içerecek biçimde tanımlayabiliriz: -) - ---- -$(CODE_NAME Tren_members)$(CODE_COMMENT_OUT)class Tren : DemirYoluAracı { - Lokomotif lokomotif; - Vagon[] vagonlar; - - // ... -$(CODE_COMMENT_OUT)} ---- - -$(P -Burada çok önemli bir konuya tekrar dikkatinizi çekmek istiyorum. Her ne kadar $(C Lokomotif) ve $(C Vagon) demir yolu araçları olsalar da, trenin onlardan türetilmesi doğru olmaz. Yukarıda değindiğimiz kuralı hatırlayalım: sınıfların türemeleri için, "bu özel tür, o genel türdendir" gibi bir ilişki bulunması gerekir. Oysa tren ne bir lokomotiftir, ne de vagondur. Tren onları $(I içerir). Bu yüzden lokomotif ve vagon kavramlarını trenin üyeleri olarak tanımladık. -) - -$(P -Bir trenin her zaman için lokomotifinin olması gerektiğini kabul edersek geçerli bir $(C Lokomotif) nesnesini şart koşan bir kurucu tanımlamamız gerekir. Vagonlar seçime bağlı iseler onlar da vagon eklemeye yarayan bir işlevle eklenebilirler: -) - ---- -$(CODE_NAME Tren_ctor)import std.exception; -// ... - -$(CODE_COMMENT_OUT)class Tren : DemirYoluAracı { - // ... - - this(Lokomotif lokomotif) { - enforce(lokomotif !is null, "Lokomotif null olamaz"); - this.lokomotif = lokomotif; - } - - void vagonEkle(Vagon[] vagonlar...) { - this.vagonlar ~= vagonlar; - } - - // ... -$(CODE_COMMENT_OUT)} ---- - -$(P -Kurucuya benzer biçimde, $(C vagonEkle) işlevi de vagon nesnelerinin $(C null) olup olmadıklarına bakabilirdi. Bu konuyu gözardı ediyorum. -) - -$(P -Trenle ilgili bir durum daha düşünebiliriz. Trenin istasyondan ayrılma ve istasyona gelme işlemlerinin de desteklenmesinin gerektiğini varsayalım: -) - ---- -$(CODE_NAME Tren)$(CODE_XREF Vagon)class Tren : DemirYoluAracı { - // ... - - void istasyondanAyrıl(string istasyon) { - foreach (vagon; vagonlar) { - vagon.bindir(); - } - - writefln("%s garından ayrılıyoruz", istasyon); - } - - void istasyonaGel(string istasyon) { - writefln("%s garına geldik", istasyon); - - foreach (vagon; vagonlar) { - vagon.indir(); - } - } -$(CODE_XREF Tren_members)$(CODE_XREF Tren_ctor)} ---- - -$(P -Bu programda geriye kalan, $(C std.stdio) modülünün eklenmesi ve bu sınıfları kullanan bir $(C main) işlevinin yazılmasıdır: -) - ---- -$(CODE_XREF Lokomotif)$(CODE_XREF Tren)$(CODE_XREF YolcuVagonu_YükVagonu)import std.stdio; -// ... -void main() { - auto lokomotif = new Lokomotif; - auto tren = new Tren(lokomotif); - - tren.vagonEkle(new YolcuVagonu, new YükVagonu); - - tren.istasyondanAyrıl("Ankara"); - tren.ilerle(500); - tren.istasyonaGel("Haydarpaşa"); -} ---- - -$(P -Programda trene farklı türden iki vagon eklenmektedir: $(C YolcuVagonu) ve $(C YükVagonu). Ek olarak, $(C Tren) sınıfı programda iki farklı arayüzün sunduğu işlevlerle kullanılmaktadır: -) - -$(OL - -$(LI $(C ilerle) işlevi çağrıldığında $(C tren) nesnesi bir $(C DemirYoluAracı) olarak kullanılmaktadır çünkü o işlev $(C DemirYoluAracı) düzeyinde bildirilmiştir ve $(C Tren) tarafından türeme yoluyla edinilmiştir.) - -$(LI $(C istasyondanAyrıl) ve $(C istasyonaGel) işlevleri çağrıldığında ise $(C tren) nesnesi bir $(C Tren) olarak kullanılmaktadır çünkü o işlevler $(C Tren) düzeyinde bildirilmişlerdir.) - -) - -$(P -Programın çıktısı $(C indir) ve $(C bindir) işlevlerinin vagonların türüne bağlı olarak uygulandığını gösteriyor: -) - -$(SHELL -Yolcular biniyor $(SHELL_NOTE) -Mal yükleniyor $(SHELL_NOTE) -Ankara garından ayrılıyoruz -Araç 500 kilometre ilerliyor -Haydarpaşa garına geldik -Yolcular iniyor $(SHELL_NOTE) -Mal boşalıyor $(SHELL_NOTE) -) - -$(H5 Özet) - -$(UL -$(LI Türeme, "bu tür o türdendir" ilişkisi içindir.) -$(LI Her sınıf en fazla bir $(C class)'tan türetilebilir.) -$(LI $(C super)'in iki kullanımı vardır: üst sınıfın kurucusunu çağırmak ve üst sınıfın üyelerine erişmek.) -$(LI $(C override), üst sınıfın bir işlevini bu sınıf için özel olarak tanımlar.) -$(LI $(C abstract), soyut işlevin alt sınıflardan birisinde tanımlanmasını şart koşar.) -) - -$(PROBLEM_COK - -$(PROBLEM -Yukarıdaki sıradüzenin en üst sınıfı olan $(C DemirYoluAracı)'nı değiştirelim. Kaç kilometre ilerlediğini bildirmenin yanında her yüz kilometre için bir de ses çıkartsın: - ---- -class DemirYoluAracı { - void ilerle(in size_t kilometre) { - writefln("Araç %s kilometre ilerliyor:", kilometre); - - foreach (i; 0 .. kilometre / 100) { - writefln(" %s", $(HILITE sesÇıkart())); - } - } -} ---- - -$(P -Ancak, $(C sesÇıkart) işlevi $(C DemirYoluAracı) sınıfında tanımlanamasın çünkü her aracın kendi özel sesi olsun: -) - -$(UL -$(LI $(C Lokomotif) için "çuf çuf") -$(LI $(C Vagon) için "takıtak tukutak") -) - -$(P $(I Not: $(C Tren.sesÇıkart) işlevini şimdilik bir sonraki soruya bırakın.) -) - -$(P -Her aracın farklı sesi olduğu için $(C sesÇıkart)'ın genel bir tanımı verilemez. O yüzden üst sınıfta soyut olarak bildirilmesi gerekir: -) - ---- -class DemirYoluAracı { - // ... - - abstract string sesÇıkart(); -} ---- - -$(P -$(C sesÇıkart) işlevini alt sınıflar için gerçekleştirin ve şu $(C main) ile deneyin: -) - ---- -$(CODE_XREF Lokomotif)$(CODE_XREF Tren)$(CODE_XREF YolcuVagonu_YükVagonu)void main() { - auto vagon1 = new YolcuVagonu; - vagon1.ilerle(100); - - auto vagon2 = new YükVagonu; - vagon2.ilerle(200); - - auto lokomotif = new Lokomotif; - lokomotif.ilerle(300); -} ---- - -$(P -Bu programın aşağıdaki çıktıyı vermesini sağlayın: -) - -$(SHELL -Araç 100 kilometre ilerliyor: - takıtak tukutak -Araç 200 kilometre ilerliyor: - takıtak tukutak - takıtak tukutak -Araç 300 kilometre ilerliyor: - çuf çuf - çuf çuf - çuf çuf -) - -$(P -Dikkat ederseniz, $(C YolcuVagonu) ile $(C YükVagonu) aynı sesi çıkartıyorlar. O yüzden onların sesi, ortak üst sınıfları olan $(C Vagon) tarafından sağlanabilir. -) - -) - -$(PROBLEM -$(C sesÇıkart) işlevini $(C Tren) sınıfı için nasıl tanımlayabileceğinizi düşünün. - -$(P -Bir fikir: $(C Tren)'in sesini, içerdiği lokomotifin ve vagonların seslerinin birleşimi olarak oluşturabilirsiniz. -) - -) - -) - -Macros: - SUBTITLE=Türeme - - DESCRIPTION=D dilinde class'tan türeme [implementation inheritance] - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial sınıf class sınıflar kullanıcı türleri türeme gerçekleştirme türemesi - -SOZLER= -$(alt_sinif) -$(arayuz) -$(cok_sekillilik) -$(cokme) -$(gerceklestirme) -$(kalitim) -$(kurucu_islev) -$(phobos) -$(sanal_islev) -$(sanal_islev_tablosu) -$(siraduzen) -$(soyut) -$(turetmek) -$(ust_sinif) -$(varsayilan) -$(yeniden_tanimlama) diff --git a/ddili/src/ders/d/uclu_islec.cozum.d b/ddili/src/ders/d/uclu_islec.cozum.d deleted file mode 100644 index 81c2075..0000000 --- a/ddili/src/ders/d/uclu_islec.cozum.d +++ /dev/null @@ -1,33 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Üçlü İşleç $(C ?:)) - -$(P -Soruda istendiği için $(C ?:) işlecini kullanıyoruz; siz burada $(C if) deyiminin daha kullanışlı olduğunu düşünebilirsiniz. Dikkat ederseniz, bu çözümde iki tane $(C ?:) işleci kullanılmaktadır: -) - ---- -import std.stdio; - -void main() { - write("Lütfen net miktarı girin: "); - - int net; - readf(" %s", &net); - - writeln(net < 0 ? -net : net, " lira ", - net < 0 ? "zarardasınız" : "kazançlısınız"); -} ---- - -$(P -Program sıfır değeri için bile "kazançlısınız" yazmaktadır. Programı değiştirerek daha uygun bir mesaj yazmasını sağlayın. -) - - -Macros: - SUBTITLE=Üçlü İşleç ?: Çözümü - - DESCRIPTION=D dilinin üçlü ?: işlecinin problem çözümü - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial ?: üçlü işleç problem çözüm diff --git a/ddili/src/ders/d/uclu_islec.d b/ddili/src/ders/d/uclu_islec.d deleted file mode 100644 index 2bc8f59..0000000 --- a/ddili/src/ders/d/uclu_islec.d +++ /dev/null @@ -1,251 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX üçlü işleç) $(IX ?:) Üçlü İşleç $(CH4 ?:)) - -$(P -$(C ?:) işleci, temelde bir $(C if-else) deyimi gibi çalışır: -) - ---- -if (/* koşul */) { - /* doğruluk işlemleri */ - -} else { - /* doğru olmama işlemleri */ -} ---- - -$(P -$(C if) deyimi, koşul doğru olduğunda doğruluk işlemlerini, aksi durumda diğer işlemleri işletir. Hatırlarsanız, $(C if) bir deyimdir ve bu yüzden kendi değeri yoktur; tek etkisi, programın işleyişini etkilemesidir. -) - -$(P -$(C ?:) işleci ise bir $(I ifadedir) ve $(C if-else) ile aynı işi, ama bir değer üretecek şekilde gerçekleştirir. Yukarıdaki kodu $(C ?:) kullanarak şöyle yazabiliriz (bölüm açıklamalarını kısaltarak gösteriyorum): -) - ---- -/* koşul */ ? /* doğruluk işlemi */ : /* doğru olmama işlemi */ ---- - -$(P -$(C ?:) işleci üç bölümündeki üç ifade yüzünden $(I üçlü işleç) olarak adlandırılır. -) - -$(P -Bu işlecin değeri; koşula bağlı olarak ya doğruluk işleminin, ya da doğru olmama işleminin değeridir. İfade olduğu için, ifadelerin kullanılabildiği her yerde kullanılabilir. -) - -$(P -Aşağıdaki örneklerde aynı işi hem $(C ?:) işleci ile, hem de $(C if) deyimi ile gerçekleştireceğim. $(C ?:) işlecinin bu örneklerdeki gibi durumlarda çok daha kısa olduğunu göreceksiniz. -) - -$(UL - -$(LI $(B İlkleme) - -$(P -Artık yıl olduğunda 366, olmadığında 365 değeri ile ilklemek için: -) ---- - int günAdedi = artıkYıl ? 366 : 365; ---- - -$(P -Aynı işi $(C if) ile yapmak istesek; bir yol, baştan hiç ilklememek ve değeri sonra vermektir: -) - ---- - int günAdedi; - - if (artıkYıl) { - günAdedi = 366; - - } else { - günAdedi = 365; - } ---- - -$(P -$(C if) ile başka bir yol; baştan $(I artık yıl) değilmiş gibi ilklemek ve adedi sonra bir arttırmak olabilir: -) - ---- - int günAdedi = 365; - - if (artıkYıl) { - ++günAdedi; - } ---- - -) - -$(LI $(B Yazdırma) - -$(P -Yazdırılan bir mesajın bir parçasını $(C ?:) ile duruma göre farklı yazdırmak: -) ---- - writeln("Bardağın yarısı ", iyimser ? "dolu" : "boş"); ---- - -$(P -Aynı işi yapmak için mesajın baş tarafını önce yazdırabilir ve gerisini sonra $(C if) ile seçebiliriz: -) - ---- - write("Bardağın yarısı "); - - if (iyimser) { - writeln("dolu"); - - } else { - writeln("boş"); - } ---- - -$(P -$(C if) ile başka bir yol, bütün mesajı farklı olarak yazdırmaktır: -) - ---- - if (iyimser) { - writeln("Bardağın yarısı dolu"); - - } else { - writeln("Bardağın yarısı boş"); - } ---- - -) - -$(LI $(B Hesap) - -$(P -Tavla puanını mars olup olmama durumuna göre $(C ?:) ile 2 veya 1 arttırmak: -) ---- - tavlaPuanı += marsOldu ? 2 : 1; ---- - -$(P -$(C if) ile puanı duruma göre 2 veya 1 arttırmak: -) ---- - if (marsOldu) { - tavlaPuanı += 2; - - } else { - tavlaPuanı += 1; - } ---- - -$(P -$(C if) ile başka bir yol; baştan bir arttırmak ve mars ise bir kere daha arttırmak olabilir: -) ---- - ++tavlaPuanı; - - if (marsOldu) { - ++tavlaPuanı; - } ---- - -) - -) - -$(P -Bu örneklerden görüldüğü gibi kod $(C ?:) işleci ile çok daha kısa olmaktadır. -) - -$(H5 Üçlü işlecin türü) - -$(P -$(C ?:) işlecinin değeri denetlenen koşula bağlı olarak ya doğruluk ifadesinin ya da doğru olmama ifadesinin değeridir. Bu iki ifadenin $(I ortak) bir türlerinin bulunması şarttır. -) - -$(P -$(IX ortak tür) Ortak tür, oldukça karmaşık bir yöntemle ve iki tür arasındaki $(LINK2 /ders/d/tur_donusumleri.html, tür dönüşümü) ve $(LINK2 /ders/d/tureme.html, türeme) ilişkilerine de bağlı olarak seçilir. Ek olarak, ortak türün $(I çeşidi) $(LINK2 /ders/d/deger_sol_sag.html, ya sol değerdir ya da sağ değer). Bu kavramları ilerideki bölümlerde göreceğiz. -) - -$(P -Şimdilik, ortak türü $(I açıkça tür dönüşümü gerektirmeden her iki değeri de tutabilen bir tür) olarak kabul edin. Örneğin, $(C int) ve $(C long) türleri için ortak bir tür vardır çünkü her ikisi de $(C long) türü ile ifade edilebilir. Öte yandan, $(C int) ve $(C string) türlerinin ortak bir türü yoktur çünkü ikisi de diğerine otomatik olarak dönüşemez. -) - -$(P -Hatırlarsanız, bir ifadenin türü $(C typeof) ve $(C .stringof) ile öğrenilebilir. Bu yöntem üçlü ifade için seçilen ortak türü öğrenmek için de kullanılabilir: -) - ---- - int i; - double d; - - auto sonuç = birKoşul ? i : d; - writeln(typeof(sonuç)$(HILITE .stringof)); ---- - -$(P -$(C double) türü $(C int) değerlerini ifade edebildiğinden (ve bunun tersi doğru olmadığından), yukarıdaki üçlü ifadenin türü $(C double) olarak seçilmiştir: -) - -$(SHELL -double -) - -$(P -Geçerli olmayan bir örnek olarak bir forum sitesinin oluşturduğu bir mesaja bakalım. Bağlı olan kullanıcı sayısı 1 olduğunda mesaj "Tek" kelimesi ile yazılsın: "$(B Tek) kullanıcı bağlı". Kullanıcı sayısı 1'den farklı olduğunda ise rakamla gösterilsin: "$(B 3) kullanıcı bağlı". -) - -$(P -"Tek" ve 3 arasındaki seçim $(C ?:) işleciyle doğrudan yapılamaz: -) - ---- - writeln((adet == 1) ? "Tek" : adet, $(DERLEME_HATASI) - " kullanıcı bağlı"); ---- - -$(P -Ne yazık ki o kod yasal değildir çünkü koşul sonucunda seçilecek iki ifadenin ortak türü yoktur: $(STRING "Tek") bir $(C string) olduğu halde, $(C adet) bir $(C int)'tir. -) - -$(P -Çözüm olarak $(C adet)'i de $(C string)'e dönüştürebiliriz. $(C std.conv) modülünde bulunan $(C to!string) işlevi kendisine verilen ifadenin $(C string) karşılığını üretir: -) - ---- -import std.conv; -// ... - writeln((adet == 1) ? "Tek" : to!string(adet), - " kullanıcı bağlı"); ---- - -$(P -Bu durumda $(C ?:) işlecinin her iki ifadesi de $(C string) olduğundan kod hatasız olarak derlenir ve çalışır. -) - -$(PROBLEM_TEK - -$(P -Program kullanıcıdan bir tamsayı değer alsın; bu değerin sıfırdan küçük olması $(I zararda olmak), sıfırdan büyük olması da $(I kazançlı olmak) anlamına gelsin. -) - -$(P -Program verilen değere göre sonu "zarardasınız" veya "kazançlısınız" ile biten bir mesaj yazdırsın. Örneğin "100 lira zarardasınız" veya "70 lira kazançlısınız". Daha uygun görseniz bile bu bölümle ilgili olabilmesi için $(C if) koşulunu kullanmayın. -) - -) - - -Macros: - SUBTITLE=Üçlü İşleç ?: - - DESCRIPTION=D dilinin üçlü ?: işlecinin tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial ?: üçlü işleç - -SOZLER= -$(deyim) -$(ifade) -$(ilklemek) -$(islec) diff --git a/ddili/src/ders/d/uda.d b/ddili/src/ders/d/uda.d deleted file mode 100644 index 79279bd..0000000 --- a/ddili/src/ders/d/uda.d +++ /dev/null @@ -1,422 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX kullanıcı niteliği) $(IX UDA) Kullanıcı Nitelikleri (UDA)) - -$(P -Programdaki her tanıma (yapı türü, sınıf türü, değişken, vs.) nitelikler atanabilir ve bu nitelikler derleme zamanında sorgulanarak programın farklı derlenmesi sağlanabilir. Kullanıcı nitelikleri bütünüyle derleme zamanında etkili olan bir olanaktır. -) - -$(P -$(IX @) Nitelikler $(C @) işareti ile belirtilirler ve o niteliğin atanmakta olduğu tanımdan önce yazılırlar. Örneğin, aşağıdaki kod $(C isim) değişkenine $(C ŞifreliKayıt) niteliğini atar: -) - ---- - $(HILITE @ŞifreliKayıt) string isim; ---- - -$(P -Birden fazla nitelik ayrı ayrı belirtilebilecekleri gibi, hepsi birden parantez içinde de belirtilebilirler. Örneğin, aşağıdaki iki satırdaki nitelikler aynı anlamdadır: -) - ---- - @ŞifreliKayıt @RenkliÇıktı string soyad; $(CODE_NOTE ayrı ayrı) - @$(HILITE $(PARANTEZ_AC))ŞifreliKayıt, RenkliÇıktı$(HILITE $(PARANTEZ_KAPA)) string adres; $(CODE_NOTE ikisi birden) ---- - -$(P -Nitelikler yalnızca tür isminden oluşabildikleri gibi, nesne veya temel tür değeri de olabilirler. Ancak, anlamları genelde açık olmadığından $(C 42) gibi hazır değerlerin nitelik olarak kullanılması önerilmez: -) - ---- -$(CODE_NAME ŞifreliKayıt)struct ŞifreliKayıt { -} - -enum Renk { siyah, mavi, kırmızı } - -struct RenkliÇıktı { - Renk renk; -} - -void main() { - @ŞifreliKayıt int a; $(CODE_NOTE tür ismi) - @ŞifreliKayıt() int b; $(CODE_NOTE nesne) - @RenkliÇıktı(Renk.mavi) int c; $(CODE_NOTE nesne) - @(42) int d; $(CODE_NOTE hazır değer (önerilmez)) -} ---- - -$(P -Yukarıdaki $(C a) ve $(C b) değişkenlerinin nitelikleri farklı çeşittendir: $(C a) değişkeni $(C ŞifreliKayıt) türünün kendisi ile, $(C b) değişkeni ise bir $(C ŞifreliKayıt) $(I nesnesi) ile nitelendirilmiştir. Bu, niteliklerin derleme zamanında sorgulanmaları açısından önemli bir farktır. Bu farkı aşağıdaki örnek programda göreceğiz. -) - -$(P -$(IX __traits) $(IX getAttributes) Niteliklerin ne anlama geldikleri bütünüyle programın ihtiyaçlarına bağlıdır. Nitelikler $(C __traits(getAttributes)) ile derleme zamanında elde edilirler, çeşitli derleme zamanı olanağı ile sorgulanırlar, ve programın uygun biçimde derlenmesi için kullanılırlar. -) - -$(P -Aşağıdaki kod belirli bir yapı üyesinin (örneğin, $(C Kişi.isim)) niteliklerinin $(C __traits(getAttributes)) ile nasıl elde edildiğini gösteriyor: -) - ---- -$(CODE_NAME Kişi)import std.stdio; - -// ... - -struct Kişi { - @ŞifreliKayıt @RenkliÇıktı(Renk.mavi) string isim; - string soyad; - @RenkliÇıktı(Renk.kırmızı) string adres; -} - -void $(CODE_DONT_TEST)main() { - foreach (nitelik; __traits($(HILITE getAttributes), Kişi.isim)) { - writeln(nitelik.stringof); - } -} ---- - -$(P -Program, $(C Kişi.isim) üyesinin niteliklerini yazdırır: -) - -$(SHELL -ŞifreliKayıt -RenkliÇıktı(cast(Renk)1) -) - -$(P -Kullanıcı niteliklerinden yararlanırken aşağıdaki $(C __traits) ifadeleri de kullanışlıdır: -) - -$(UL - -$(LI $(IX allMembers) $(C __traits(allMembers)) bir türün (veya modülün) bütün üyelerini $(C string) türünde döndürür. -) - -$(LI $(IX getMember) $(C __traits(getMember)) üyelere erişirken kullanılabilen bir $(I isim) $(ASIL symbol) üretir. İlk parametresi bir tür veya değişken ismi, ikinci parametresi ise bir dizgidir. Birinci parametresi ile ikinci parametresini bir nokta ile birleştirir ve yeni bir isim üretir. Örneğin, $(C __traits(getMember, Kişi, $(STRING "isim"))), $(C Kişi.isim)'i oluşturur. -) - -) - ---- -$(CODE_XREF ŞifreliKayıt)$(CODE_XREF Kişi)import std.string; - -// ... - -void main() { - foreach (üyeİsmi; __traits($(HILITE allMembers), Kişi)) { - writef("%5s üyesinin nitelikleri:", üyeİsmi); - - foreach (nitelik; - __traits(getAttributes, - __traits($(HILITE getMember), Kişi, üyeİsmi))) { - writef(" %s", nitelik.stringof); - } - - writeln(); - } -} ---- - -$(P -Program, bütün üyelerin niteliklerini yazdırır: -) - -$(SHELL - isim üyesinin nitelikleri: ŞifreliKayıt RenkliÇıktı(cast(Renk)1) -soyad üyesinin nitelikleri: -adres üyesinin nitelikleri: RenkliÇıktı(cast(Renk)2) -) - -$(P -$(IX hasUDA, std.traits) Kullanıcı nitelikleri konusunda yararlı olan bir başka araç belirli bir $(I ismin) belirli bir niteliğe sahip olup olmadığını bildiren $(C std.traits.hasUDA) şablonudur; değeri, nitelik bulunduğunda $(C true), bulunmadığında ise $(C false) olur. $(C Kişi.isim)'in $(C ŞifreliKayıt) niteliği bulunduğandan aşağıdaki $(C static assert) denetimi başarılı olur: -) - ---- -import std.traits; - -// ... - -static assert(hasUDA!(Kişi.isim, ŞifreliKayıt)); ---- - -$(P -$(C hasUDA) bir nitelik türü ile kullanılabileceği gibi, o türün belirli bir değeri ile de kullanılabilir. Aşağıdaki $(C static assert) denetimlerinin ikisi de başarılı olur: -) - ---- -static assert(hasUDA!(Kişi.isim, $(HILITE RenkliÇıktı))); -static assert(hasUDA!(Kişi.isim, $(HILITE RenkliÇıktı(Renk.mavi)))); ---- - -$(H5 Örnek) - -$(P -Niteliklerin derleme zamanında nasıl sorgulanabildiklerini görmek için bir işlev şablonu tasarlayalım. Bu şablon kendisine verilen yapı nesnesinin bütün üyelerini niteliklerine uygun olarak XML düzeninde yazdırsın -) - ---- -void xmlOlarakYazdır(T)(T nesne) { -// ... - - foreach (üyeİsmi; __traits($(HILITE allMembers), T)) { // (1) - string değer = - __traits($(HILITE getMember), nesne, üyeİsmi).to!string; // (2) - - static if ($(HILITE hasUDA)!(__traits(getMember, T, üyeİsmi),// (3) - ŞifreliKayıt)) { - değer = değer.şifrelisi.to!string; - } - - writefln(` <%1$s renk="%2$s">%3$s`, üyeİsmi, - $(HILITE renkNiteliği)!(T, üyeİsmi), değer); // (4) - } -} ---- - -$(P -Bu şablonun işaretli bölümleri şöyle açıklanabilir: -) - -$(OL - -$(LI Türün bütün üyeleri $(C __traits(allMembers)) ile elde ediliyor.) - -$(LI Her üyenin değeri biraz aşağıda kullanılmak üzere $(C string) türünde elde ediliyor. Örneğin, $(C üyeİsmi) $(STRING "isim") olduğunda atama işlecinin sağ tarafı $(C nesne.isim.to!string) ifadesidir.) - -$(LI Her üyenin $(C ŞifreliKayıt) niteliğinin olup olmadığı $(C hasUDA) ile sorgulanıyor ve bu niteliğe sahip olan üyelerin değerleri şifreleniyor.) - -$(LI Biraz aşağıda göreceğimiz $(C renkNiteliği()) şablonu ile her üyenin renk niteliği elde ediliyor.) - -) - -$(P -$(C renkNiteliği()) işlev şablonu aşağıdaki gibi gerçekleştirilebilir: -) - ---- -Renk renkNiteliği(T, string üye)() { - foreach (nitelik; __traits(getAttributes, - __traits(getMember, T, üye))) { - static if (is ($(HILITE typeof(nitelik)) == RenkliÇıktı)) { - return nitelik.renk; - } - } - - return Renk.siyah; -} ---- - -$(P -Bütün bu olanaklar derleme zamanında işlediğinde $(C xmlOlarakYazdır()) şablonu $(C Kişi) türü için aşağıdaki işlevin eşdeğeri olarak oluşturulur ve derlenir: -) - ---- -/* xmlOlarakYazdır!Kişi işlevinin eşdeğeri */ -void xmlOlarakYazdır_Kişi(Kişi nesne) { -// ... - - { - string değer = nesne.$(HILITE isim).to!string; - $(HILITE değer = değer.şifrelisi.to!string;) - writefln(` <%1$s renk="%2$s">%3$s`, - "isim", Renk.mavi, değer); - } - { - string değer = nesne.$(HILITE soyad).to!string; - writefln(` <%1$s renk="%2$s">%3$s`, - "soyad", Renk.siyah, değer); - } - { - string değer = nesne.$(HILITE adres).to!string; - writefln(` <%1$s renk="%2$s">%3$s`, - "adres", Renk.kırmızı, değer); - } -} ---- - -$(P -Programda başka açıklamalar da bulunuyor: -) - ---- -import std.stdio; -import std.string; -import std.algorithm; -import std.conv; -import std.traits; - -/* Nitelediği tanımın şifreleneceğini belirler. */ -struct ŞifreliKayıt { -} - -enum Renk { siyah, mavi, kırmızı } - -/* Nitelediği tanımın rengini belirler. Belirtilmediği zaman - * siyah renk varsayılır. */ -struct RenkliÇıktı { - Renk renk; -} - -struct Kişi { - /* Bu üyenin şifreleneceği ve mavi renkle yazdırılacağı - * belirtiliyor. */ - @ŞifreliKayıt @RenkliÇıktı(Renk.mavi) string isim; - - /* Bu üye için herhangi bir nitelik belirtilmiyor. */ - string soyad; - - /* Bu üyenin kırmızı renkle yazdırılacağı belirtiliyor. */ - @RenkliÇıktı(Renk.kırmızı) string adres; -} - -/* Belirtilen üyenin varsa renk niteliğinin değerini, yoksa - * Renk.siyah değerini döndürür. */ -Renk renkNiteliği(T, string üye)() { - auto sonuç = Renk.siyah; - - foreach (nitelik; __traits(getAttributes, - __traits(getMember, T, üye))) { - static if (is (typeof(nitelik) == RenkliÇıktı)) { - sonuç = nitelik.renk; - } - } - - return sonuç; -} - -/* Verilen dizginin Sezar şifresi ile şifrelenmişini - * döndürür. (Uyarı: Sezar şifresi çok güçsüz bir şifreleme - * algoritmasıdır.) */ -auto şifrelisi(string değer) { - return değer.map!(a => dchar(a + 1)); -} - -unittest { - assert("abcdefghij".şifrelisi.equal("bcdefghijk")); -} - -/* Belirtilen nesneyi niteliklerine uygun olarak XML düzeninde - * yazdırır. */ -void xmlOlarakYazdır(T)(T nesne) { - writefln("<%s>", T.stringof); - scope(exit) writefln("", T.stringof); - - foreach (üyeİsmi; __traits(allMembers, T)) { - string değer = - __traits(getMember, nesne, üyeİsmi).to!string; - - static if (hasUDA!(__traits(getMember, T, üyeİsmi), - ŞifreliKayıt)) { - değer = değer.şifrelisi.to!string; - } - - writefln(` <%1$s renk="%2$s">%3$s`, - üyeİsmi, renkNiteliği!(T, üyeİsmi), değer); - } -} - -void main() { - auto kişiler = [ Kişi("Doğu", "Doğan", "Diyarbakır"), - Kişi("Batı", "Batan", "Balıkesir") ]; - - foreach (kişi; kişiler) { - xmlOlarakYazdır(kişi); - } -} ---- - -$(P -Programın çıktısı bütün üyelerin kendi renk niteliklerine sahip olduklarını ve $(C isim) üyesinin de şifrelendiğini gösteriyor: -) - -$(SHELL -<Kişi> - <isim renk="mavi">EpĠv</isim> $(SHELL_NOTE mavi ve şifreli) - <soyad renk="siyah">Doğan</soyad> - <adres renk="kırmızı">Diyarbakır</adres> $(SHELL_NOTE kırmızı) -</Kişi> -<Kişi> - <isim renk="mavi">CbuIJ</isim> $(SHELL_NOTE mavi ve şifreli) - <soyad renk="siyah">Batan</soyad> - <adres renk="kırmızı">Balıkesir</adres> $(SHELL_NOTE kırmızı) -</Kişi> -) - -$(H5 Kullanıcı niteliklerinin yararı) - -$(P -Kullanıcı niteliklerinin yararı, niteliklerin programın başka bir yerinde değişiklik gerekmeden değiştirilebilmesidir. Örneğin, bütün üyelerin şifreli olarak yazdırılması için $(C Kişi) yapısının aşağıdaki gibi değiştirilmesi yeterlidir: -) - ---- -struct Kişi { - $(HILITE @ŞifreliKayıt) { - string isim; - string soyad; - string adres; - } -} - -// ... - - xmlOlarakYazdır(Kişi("Güney", "Gün", "Giresun")); ---- - -$(P -Çıktısı: -) - -$(SHELL -<Kişi> - <isim renk="siyah">Hýofz</isim> $(SHELL_NOTE şifreli) - <soyad renk="siyah">Hýo</soyad> $(SHELL_NOTE şifreli) - <adres renk="siyah">Hjsftvo</adres> $(SHELL_NOTE şifreli) -</Kişi> -) - -$(P -Hatta, $(C xmlOlarakYazdır()) ve yararlandığı nitelikler başka türlerle de kullanılabilir: -) - ---- -struct Veri { - $(HILITE @RenkliÇıktı(Renk.mavi)) string mesaj; -} - -// ... - - xmlOlarakYazdır(Veri("merhaba dünya")); ---- - -$(P -Çıktısı: -) - -$(SHELL -<Veri> - <mesaj renk="mavi">merhaba dünya</mesaj> $(SHELL_NOTE mavi) -</Veri> -) - -$(H5 Özet) - -$(UL - -$(LI Programda kullanılan bütün tanımlara nitelikler atanabilir.) - -$(LI Nitelikler tür isimlerinden veya değerlerden oluşabilir.) - -$(LI Nitelikler derleme zamanında $(C hasUDA) ve $(C __traits(getAttributes)) ile sorgulanarak programın farklı derlenmesi sağlanabilir.) - -) - -macros: - SUBTITLE=Kullanıcı Nitelikleri (UDA) - - DESCRIPTION=Türlere programcı tarafından eklenen nitelikler. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial nitelikler uda - -SOZLER= -$(nitelik) diff --git a/ddili/src/ders/d/ufcs.d b/ddili/src/ders/d/ufcs.d deleted file mode 100644 index ea6e552..0000000 --- a/ddili/src/ders/d/ufcs.d +++ /dev/null @@ -1,227 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX UFCS) $(IX işlev çağırma ortak söz dizimi) $(IX ortak söz dizimi, işlev çağırma) İşlev Çağırma Ortak Söz Dizimi (UFCS)) - -$(P -UFCS "universal function call syntax"in kısaltmasıdır. Normal işlevlerin üye işlevler gibi çağrılabilmelerini sağlar. Derleyicinin otomatik olarak sağladığı bu olanak çok kısa olarak iki ifade ile anlatılabilir: -) - ---- - değişken.işlev($(I parametre_değerleri)) ---- - -$(P -Yukarıdaki gibi bir ifade ile karşılaşıldığında eğer $(C değişken)'in o parametrelere uyan $(C işlev) isminde bir üye işlevi yoksa, derleyici hata vermeden önce bir de aşağıdaki normal işlev çağrısını dener: -) - ---- - işlev(değişken, $(I parametre_değerleri)) ---- - -$(P -Eğer derlenebiliyorsa o ifade kabul edilir ve aslında normal bir işlev olan $(C işlev) sanki bir üye işlevmiş gibi çağrılmış olur. -) - -$(P -Belirli bir yapı veya sınıf türünü yakından ilgilendiren işlevlerin o türün üye işlevleri olarak tanımlandıklarını biliyoruz. Her normal işlev özel üyelere erişemediğinden üye işlevler sarma kavramı için gereklidir. Örneğin, $(C private) olarak işaretlenmiş olan üyelere ancak o türün kendi üye işlevleri ve o türü içeren modül tarafından erişilebilir. -) - -$(P -Deposundaki benzin miktarını da bildiren bir $(C Araba) türü olsun: -) - ---- -$(CODE_NAME Araba)class Araba { - enum ekonomi = 12.5; // litre başına km (ortalama) - private double kalanBenzin; // litre - - this(double kalanBenzin) { - this.kalanBenzin = kalanBenzin; - } - - double benzin() const { - return kalanBenzin; - } - - // ... -} ---- - -$(P -Üye işlevler ne kadar yararlı ve gerekli olsalar da, belirli bir tür üzerindeki olası bütün işlemlerin üye işlevler olarak tanımlanmaları beklenmemelidir çünkü bazı işlemler ancak belirli programlarda anlamlıdırlar veya yararlıdırlar. Örneğin, arabanın belirli bir mesafeyi gidip gidemeyeceğini bildiren işlevin üye işlev olarak değil, normal işlev olarak tanımlanması daha uygun olabilir: -) - ---- -$(CODE_NAME gidebilir_mi)bool gidebilir_mi(Araba araba, double mesafe) { - return (araba.benzin() * araba.ekonomi) >= mesafe; -} ---- - -$(P -Doğal olarak, bu işlemin serbest işlev olarak tanımlanmış olması işlev çağrısı söz diziminde farklılık doğurur. Değişkenin ismi aşağıdaki iki kullanımda farklı yerlerde geçmektedir: -) - ---- -$(CODE_XREF Araba)$(CODE_XREF gidebilir_mi)void main() { - auto araba = new Araba(5); - - auto kalanBenzin = $(HILITE araba).benzin(); // Üye işlev söz dizimi - - if (gidebilir_mi($(HILITE araba), 100)) { // Normal işlev söz dizimi - // ... - } -} ---- - -$(P -UFCS, söz dizimindeki bu farklılığı ortadan kaldırır; normal işlevlerin de üye işlevler gibi çağrılabilmelerini sağlar: -) - ---- - if ($(HILITE araba).gidebilir_mi(100)) { // Normal işlev, üye işlev söz dizimi ile - // ... - } ---- - -$(P -Bu olanak hazır değerler dahil olmak üzere temel türlerle de kullanılabilir: -) - ---- -int yarısı(int değer) { - return değer / 2; -} - -void main() { - assert(42.yarısı() == 21); -} ---- - -$(P -Bir sonraki bölümde göreceğimiz gibi, işlev çağrısı sırasında parametre değeri kullanılmadığında o işlev parantezsiz olarak da çağrılabilir. Bu olanaktan da yararlanıldığında yukarıdaki ifade daha da kısalır. Sonuçta, aşağıdaki satırların üçü de aynı anlamdadır: -) - ---- - sonuç = yarısı(değer); - sonuç = değer.yarısı(); - sonuç = değer.yarısı; ---- - -$(P -$(IX zincirleme, işlev çağrısı) $(IX işlev çağrısı zincirleme) UFCS özellikle işlevlerin $(I zincirleme olarak) çağrıldığı durumlarda yararlıdır. Bunu $(C int) dizileri ile işleyen üç işlev üzerinde görelim: -) - ---- -$(CODE_NAME islevler)// Bütün elemanların 'bölen' ile bölünmüşlerini döndürür -int[] bölümleri(int[] dilim, int bölen) { - int[] sonuç; - sonuç.reserve(dilim.length); - - foreach (değer; dilim) { - sonuç ~= değer / bölen; - } - - return sonuç; -} - -// Bütün elemanların 'çarpan' ile çarpılmışlarını döndürür -int[] çarpımları(int[] dilim, int çarpan) { - int[] sonuç; - sonuç.reserve(dilim.length); - - foreach (değer; dilim) { - sonuç ~= değer * çarpan; - } - - return sonuç; -} - -// Elemanların çift olanlarını döndürür -int[] çiftleri(int[] dilim) { - int[] sonuç; - sonuç.reserve(dilim.length); - - foreach (değer; dilim) { - if (!(değer % 2)) { - sonuç ~= değer; - } - } - - return sonuç; -} ---- - -$(P -UFCS'ten yararlanılmadığı zaman bu üç işlevi zincirleme olarak çağırmanın bir yolu aşağıdaki gibidir: -) - ---- -$(CODE_XREF islevler)import std.stdio; - -// ... - -void main() { - auto sayılar = [ 1, 2, 3, 4, 5 ]; - writeln(çiftleri(bölümleri(çarpımları(sayılar, 10), 3))); -} ---- - -$(P -Sayılar önce 10 ile çarpılmakta, sonra 3 ile bölünmekte, ve sonucun çift olanları kullanılmaktadır: -) - -$(SHELL -[6, 10, 16] -) - -$(P -Yukarıdaki ifadenin bir sorunu, $(C çarpımları) ile $(C 10)'un ve $(C bölümleri) ile $(C 3)'ün birbirleriyle ilgili olmalarına rağmen ifadede birbirlerinden uzakta yazılmak zorunda olmalarıdır. UFCS bu sorunu ortadan kaldırır ve işlem sıralarına uyan daha doğal bir söz dizimi getirir: -) - ---- - writeln(sayılar.çarpımları(10).bölümleri(3).çiftleri); ---- - -$(P -Bazı programcılar $(C writeln) gibi çağrılarda da UFCS'ten yararlanırlar: -) - ---- - sayılar.çarpımları(10).bölümleri(3).çiftleri.writeln; ---- - -$(P -Ek bir bilgi olarak, yukarıdaki bütün program $(C map) ve $(C filter)'dan yararlanılarak da yazılabilir: -) - ---- -import std.stdio; -import std.algorithm; - -void main() { - auto sayılar = [ 1, 2, 3, 4, 5 ]; - - writeln(sayılar - .map!(a => a * 10) - .map!(a => a / 3) - .filter!(a => !(a % 2))); -} ---- - -$(P -Bunu sağlayan $(LINK2 /ders/d/sablonlar.html, şablon), $(LINK2 /ders/d/araliklar.html, aralık), ve $(LINK2 /ders/d/katmalar.html, isimsiz işlev) olanaklarını daha sonraki bölümlerde göreceğiz. -) - -Macros: - SUBTITLE=İşlev Çağırma Ortak Söz Dizimi (UFCS) - - DESCRIPTION=Normal işlevleri üye işlev söz dizimi ile çağırma olanağı. - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial - -SOZLER= -$(aralik) -$(islev) -$(sarma) -$(sablon) -$(uye_islev) diff --git a/ddili/src/ders/d/uye_islevler.cozum.d b/ddili/src/ders/d/uye_islevler.cozum.d deleted file mode 100644 index d767c6f..0000000 --- a/ddili/src/ders/d/uye_islevler.cozum.d +++ /dev/null @@ -1,170 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Üye İşlevler) - -$(OL - -$(LI -Azaltan işlev, eksi değerler nedeniyle daha karmaşık oluyor: - ---- -struct GününSaati { - // ... - - void azalt(in Süre süre) { - int azalanDakika = süre.dakika % 60; - int azalanSaat = süre.dakika / 60; - - dakika -= azalanDakika; - - if (dakika < 0) { - dakika += 60; - ++azalanSaat; - } - - saat -= azalanSaat; - - if (saat < 0) { - saat = 24 - (-saat % 24); - } - } - - // ... -} ---- - -) - -$(LI -$(C toString)'in programı çok daha kısa ve kullanışlı hale getirdiğini göreceksiniz. Karşılaştırma amacıyla, $(LINK2 /ders/d/islev_yukleme.cozum.html, programın önceki halinde) $(C Toplantı) nesnesini yazdıran işlevi tekrar göstermek istiyorum: - ---- -void bilgiVer(in Toplantı toplantı) { - bilgiVer(toplantı.başlangıç); - write('-'); - bilgiVer(toplantı.bitiş); - - writef(" \"%s\" toplantısı (%s katılımcı)", - toplantı.konu, - toplantı.katılımcıSayısı); -} ---- - -$(P -Aşağıdaki programdaki $(C Toplantı.toString) ise kısaca şöyle yazılabiliyor: -) - ---- - string toString() { - return format("%s-%s \"%s\" toplantısı (%s katılımcı)", - başlangıç, bitiş, konu, katılımcıSayısı); - } ---- - -$(P -Programın tamamı: -) - ---- -import std.stdio; -import std.string; - -struct Süre { - int dakika; -} - -struct GününSaati { - int saat; - int dakika; - - string toString() { - return format("%02s:%02s", saat, dakika); - } - - void ekle(in Süre süre) { - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - } -} - -struct Toplantı { - string konu; - int katılımcıSayısı; - GününSaati başlangıç; - GününSaati bitiş; - - string toString() { - return format("%s-%s \"%s\" toplantısı (%s katılımcı)", - başlangıç, bitiş, konu, katılımcıSayısı); - } -} - -struct Yemek { - GününSaati zaman; - string adres; - - string toString() { - GününSaati bitiş = zaman; - bitiş.ekle(Süre(90)); - - return format("%s-%s Yemek, Yer: %s", - zaman, bitiş, adres); - } -} - -struct GünlükPlan { - Toplantı sabahToplantısı; - Yemek öğleYemeği; - Toplantı akşamToplantısı; - - string toString() { - return format("%s\n%s\n%s", - sabahToplantısı, - öğleYemeği, - akşamToplantısı); - } -} - -void main() { - auto geziToplantısı = Toplantı("Bisiklet gezisi", 4, - GününSaati(10, 30), - GününSaati(11, 45)); - - auto öğleYemeği = Yemek(GününSaati(12, 30), "Taksim"); - - auto bütçeToplantısı = Toplantı("Bütçe", 8, - GününSaati(15, 30), - GününSaati(17, 30)); - - auto bugününPlanı = GünlükPlan(geziToplantısı, - öğleYemeği, - bütçeToplantısı); - - writeln(bugününPlanı); - writeln(); -} ---- - -$(P -Programın çıktısı da eski halinin aynısı: -) - -$(SHELL -10:30-11:45 "Bisiklet gezisi" toplantısı (4 katılımcı) -12:30-14:00 Yemek, Yer: Taksim -15:30-17:30 "Bütçe" toplantısı (8 katılımcı) -) - -) - -) - -Macros: - SUBTITLE=Üye İşlevler - - DESCRIPTION=D dili yapılarının ve sınıflarının üye işlevleri, ve toString problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial yapı yapılar struct üye işlev üye fonksiyon toString problem çözüm diff --git a/ddili/src/ders/d/uye_islevler.d b/ddili/src/ders/d/uye_islevler.d deleted file mode 100644 index 2de7ecf..0000000 --- a/ddili/src/ders/d/uye_islevler.d +++ /dev/null @@ -1,435 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX üye işlev) $(IX işlev, üye) Üye İşlevler) - -$(P -Bu bölümde her ne kadar yapıları kullanıyor olsak da buradaki bilgilerin çoğu daha sonra göreceğimiz sınıflar için de geçerlidir. -) - -$(P -Bu bölümde yapıların ve sınıfların üye işlevlerini tanıyacağız, ve bunların içerisinden özel olarak, nesneleri $(C string) türüne dönüştürmede kullanılan $(C toString) üye işlevini göreceğiz. -) - -$(P -Bir yapının tanımlandığı çoğu durumda, o yapıyı kullanan bir grup işlev de onunla birlikte tanımlanır. Bunun örneklerini önceki bölümlerde $(C zamanEkle) ve $(C bilgiVer) işlevlerinde gördük. O işlevler bir anlamda $(C GününSaati) yapısı ile birlikte $(I sunulan) ve o yapının $(I arayüzünü) oluşturan işlevlerdir.) - -$(P -Hatırlarsanız; $(C zamanEkle) ve $(C bilgiVer) işlevlerinin ilk parametresi, $(I üzerinde işlem yaptıkları) nesneyi belirliyordu. Şimdiye kadar tanımladığımız bütün diğer işlevler gibi, onlar da bağımsız olarak, tek başlarına, ve modül kapsamında tanımlanmışlardı. -) - -$(P -Bir yapının arayüzünü oluşturan işlevler çok karşılaşılan bir kavram olduğu için; o işlevler yapının içinde, yapının üye işlevleri olarak da tanımlanabilirler. -) - -$(H5 Üye işlev) - -$(P -Bir yapının veya sınıfın küme parantezlerinin içinde tanımlanan işlevlere $(I üye işlev) denir: -) - ---- -struct BirYapı { - void $(I üye_işlev)(/* parametreleri */) { - // ... işlevin tanımı ... - } - - // ... yapının üyeleri ve diğer işlevleri ... -} ---- - -$(P -Üye işlevlere yapının diğer üyelerinde olduğu gibi nesne isminden sonraki nokta karakteri ve ardından yazılan işlev ismi ile erişilir: -) - ---- - $(I nesne.üye_işlev(parametreleri)); ---- - -$(P -Üye işlevleri aslında daha önce de kullandık; örneğin standart giriş ve çıkış işlemlerinde $(C stdin) ve $(C stdout) nesnelerini açıkça yazabiliyorduk: -) - ---- - stdin.readf(" %s", &numara); - stdout.writeln(numara); ---- - -$(P -O satırlardaki $(C readf) ve $(C writeln) üye işlevlerdir. -) - -$(P -İlk örneğimiz olarak $(C GününSaati) yapısını yazdıran $(C bilgiVer) işlevini bir üye işlev olarak tanımlayalım. O işlevi daha önce serbest olarak şöyle tanımlamıştık: -) - ---- -void bilgiVer(in GününSaati zaman) { - writef("%02s:%02s", zaman.saat, zaman.dakika); -} ---- - -$(P -Üye işlev olarak yapının içinde tanımlanırken bazı değişiklikler gerekir: -) - ---- -struct GününSaati { - int saat; - int dakika; - - void bilgiVer() { - writef("%02s:%02s", saat, dakika); - } -} ---- - -$(P -Daha önce yapı dışında serbest olarak tanımlanmış olan $(C bilgiVer) işlevi ile bu üye işlev arasında iki fark vardır: -) - -$(UL -$(LI Üye işlev yazdırdığı nesneyi parametre olarak almaz) -$(LI O yüzden üyelere $(C zaman.saat) ve $(C zaman.dakika) diye değil, $(C saat) ve $(C dakika) diye erişir) -) - -$(P -Bunun nedeni, üye işlevlerin zaten her zaman için bir nesne üzerinde çağrılıyor olmalarıdır: -) - ---- - auto zaman = GününSaati(10, 30); - $(HILITE zaman.)bilgiVer(); ---- - -$(P -Orada, $(C bilgiVer) işlevi $(C zaman) nesnesini yazdıracak şekilde çağrılmaktadır. Üye işlevin tanımı içinde noktasız olarak yazılan $(C saat) ve $(C dakika), $(C zaman) nesnesinin üyeleridir; ve sırasıyla $(C zaman.saat) ve $(C zaman.dakika) üyelerini temsil ederler. -) - -$(P -O üye işlev çağrısı, daha önceden serbest olarak yazılmış olan $(C bilgiVer)'in şu şekilde çağrıldığı durumla eşdeğerdir: -) - ---- - zaman.bilgiVer(); // üye işlev - bilgiVer(zaman); // serbest işlev (önceki tanım) ---- - -$(P -Üye işlev her çağrıldığında, üzerinde çağrıldığı nesnenin üyelerine erişir: -) - ---- - auto sabah = GününSaati(10, 0); - auto akşam = GününSaati(22, 0); - - $(HILITE sabah).bilgiVer(); - write('-'); - $(HILITE akşam).bilgiVer(); - writeln(); ---- - -$(P -$(C bilgiVer), $(C sabah) üzerinde çağrıldığında $(C sabah)'ın değerini, $(C akşam) üzerinde çağrıldığında da $(C akşam)'ın değerini yazdırır: -) - -$(SHELL -10:00-22:00 -) - -$(H6 $(IX toString) Nesneyi $(C string) olarak ifade eden $(C toString)) - -$(P -Bir önceki bölümde $(C bilgiVer) işlevinin eksikliklerinden söz etmiştim. Rahatsız edici bir diğer eksikliğini burada göstermek istiyorum: Her ne kadar zamanı okunaklı bir düzende çıktıya gönderiyor olsa da, genel çıktı düzeni açısından $(C '-') karakterini yazdırmayı ve satırın sonlandırılmasını kendimiz ayrıca halletmek zorunda kalıyoruz. -) - -$(P -Oysa, nesnelerin diğer türler gibi kullanışlı olabilmeleri için örneğin şu şekilde yazabilmemiz çok yararlı olurdu: -) - ---- - writefln("%s-%s", sabah, akşam); ---- - -$(P -Öyle yazabilseydik; daha önceki 4 satırı böyle tek satıra indirgemiş olmanın yanında, nesneleri $(C stdout)'tan başka akımlara da, örneğin bir dosyaya da aynı şekilde yazdırabilirdik: -) - ---- - auto dosya = File("zaman_bilgisi", "w"); - dosya.writefln("%s-%s", sabah, akşam); ---- - -$(P -Yapıların $(C toString) ismindeki üye işlevleri özeldir ve nesneleri $(C string) türüne dönüştürmek için kullanılır. Bunun doğru olarak çalışabilmesi için, ismi "string'e dönüştür"den gelen bu işlev o nesneyi ifade eden bir $(C string) döndürmelidir. -) - -$(P -Bu işlevin içeriğini sonraya bırakalım, ve önce yapı içinde nasıl tanımlandığına bakalım: -) - ---- -import std.stdio; - -struct GününSaati { - int saat; - int dakika; - - string toString() { - return "deneme"; - } -} - -void main() { - auto sabah = GününSaati(10, 0); - auto akşam = GününSaati(22, 0); - - writefln("%s-%s", sabah, akşam); -} ---- - -$(P -Nesneleri dizgi olarak kullanabilen kütüphane işlevleri onların $(C toString) işlevlerini çağırırlar ve döndürülen dizgiyi kendi amaçlarına uygun biçimde kullanırlar. -) - -$(P -Bu örnekte henüz anlamlı bir dizgi üretmediğimiz için çıktı da şimdilik anlamsız oluyor: -) - -$(SHELL -deneme-deneme -) - -$(P -Ayrıca $(C bilgiVer)'i artık emekliye ayırmakta olduğumuza da dikkat edin; $(C toString)'in tanımını tamamlayınca ona ihtiyacımız kalmayacak. -) - -$(P -$(C toString) işlevini yazmanın en kolay yolu, $(C std.string) modülünde tanımlanmış olan $(C format) işlevini kullanmaktır. Bu işlev, çıktı düzeni için kullandığımız bütün olanaklara sahiptir ve örneğin $(C writef) ile aynı şekilde çalışır. Tek farkı, ürettiği sonucu bir akıma göndermek yerine, bir $(C string) olarak döndürmesidir. -) - -$(P -$(C toString)'in de zaten bir $(C string) döndürmesi gerektiği için, $(C format)'ın döndürdüğü değeri olduğu gibi döndürebilir: -) - ---- -import std.string; -// ... -struct GününSaati { -// ... - string toString() { - return $(HILITE format)("%02s:%02s", saat, dakika); - } -} ---- - -$(P -$(C toString)'in yalnızca bu nesneyi $(C string)'e dönüştürdüğüne dikkat edin. Çıktının geri kalanı, $(C writefln) çağrısı tarafından halledilmektedir. $(C writefln), $(C "%s") düzen bilgilerine karşılık olarak $(C toString)'i otomatik olarak iki nesne için ayrı ayrı çağırır, aralarına $(C '-') karakterini yerleştirir, ve en sonunda da satırı sonlandırır: -) - -$(SHELL -10:00-22:00 -) - -$(P -Görüldüğü gibi, burada anlatılan $(C toString) işlevi parametre almamaktadır. $(C toString)'in parametre olarak $(C delegate) alan bir tanımı daha vardır. O tanımını daha ilerideki $(LINK2 /ders/d/kapamalar.html, İşlev Göstergeleri, İsimsiz İşlevler, ve Temsilciler bölümünde) göreceğiz. -) - -$(H6 Örnek: $(C ekle) üye işlevi) - -$(P -Bu sefer de $(C GününSaati) nesnelerine zaman ekleyen bir üye işlev tanımlayalım. -) - -$(P -Ama ona geçmeden önce, önceki bölümlerde yaptığımız bir yanlışı gidermek istiyorum. $(LINK2 /ders/d/yapilar.html, Yapılar bölümünde) tanımladığımız $(C zamanEkle) işlevinin, $(C GününSaati) nesnelerini toplamasının normal bir işlem olmadığını görmüş, ama yine de o şekilde kullanmıştık: -) - ---- -GününSaati zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek) { // anlamsız - // ... -} ---- - -$(P -Gün içindeki iki zamanı birbirine eklemek doğal bir işlem değildir. Örneğin yola çıkma zamanına sinemaya varma zamanını ekleyemeyiz. Gün içindeki bir zamana eklenmesi normal olan, bir $(I süredir.) Örneğin yola çıkma zamanına $(I yol süresini) ekleyerek sinemaya varış zamanını buluruz. -) - -$(P -Öte yandan, gün içindeki iki zamanın birbirlerinden çıkartılmaları normal bir işlem olarak görülebilir. O işlemin sonucu da örneğin $(C Süre) türünden olmalıdır. -) - -$(P -Bu bakış açısı ile, dakika duyarlığıyla çalışan bir $(C Süre) yapısını ve onu kullanan $(C zamanEkle) işlevini şöyle yazabiliriz: -) - ---- -struct Süre { - int dakika; -} - -GününSaati zamanEkle(in GününSaati başlangıç, in Süre süre) { - // başlangıç'ın kopyasıyla başlıyoruz - GününSaati sonuç = başlangıç; - - // Süreyi ekliyoruz - sonuç.dakika += süre.dakika; - - // Taşmaları ayarlıyoruz - sonuç.saat += sonuç.dakika / 60; - sonuç.dakika %= 60; - sonuç.saat %= 24; - - return sonuç; -} - -unittest { - // Basit bir test - assert(zamanEkle(GününSaati(10, 30), Süre(10)) - == GününSaati(10, 40)); - - // Gece yarısı testi - assert(zamanEkle(GününSaati(23, 9), Süre(51)) - == GününSaati(0, 0)); - - // Sonraki güne taşma testi - assert(zamanEkle(GününSaati(17, 45), Süre(8 * 60)) - == GününSaati(1, 45)); -} ---- - -$(P -Şimdi aynı işlevi bir üye işlev olarak tanımlayalım. Üye işlev zaten bir nesne üzerinde çalışacağı için $(C GününSaati) parametresine gerek kalmaz ve parametre olarak yalnızca süreyi geçirmek yeter: -) - ---- -struct Süre { - int dakika; -} - -struct GününSaati { - int saat; - int dakika; - - string toString() { - return format("%02s:%02s", saat, dakika); - } - - void $(HILITE ekle)(in Süre süre) { - dakika += süre.dakika; - - saat += dakika / 60; - dakika %= 60; - saat %= 24; - } - - unittest { - auto zaman = GününSaati(10, 30); - - // Basit bir test - zaman$(HILITE .ekle)(Süre(10)); - assert(zaman == GününSaati(10, 40)); - - // 15 saat sonra bir sonraki güne taşmalı - zaman$(HILITE .ekle)(Süre(15 * 60)); - assert(zaman == GününSaati(1, 40)); - - // 22 saat ve 20 dakika sonra gece yarısı olmalı - zaman$(HILITE .ekle)(Süre(22 * 60 + 20)); - assert(zaman == GününSaati(0, 0)); - } -} ---- - -$(P -$(C ekle), nesnenin zamanını belirtilen süre kadar ilerletir. Daha sonraki bölümlerde göreceğimiz $(I işleç yükleme) olanağı sayesinde bu konuda biraz daha kolaylık kazanacağız. Örneğin $(C +=) işlecini yükleyerek yapı nesnelerini de temel türler gibi kullanabileceğiz: -) - ---- - zaman += Süre(10); // bunu daha sonra öğreneceğiz ---- - -$(P -Ayrıca gördüğünüz gibi, üye işlevler için de $(C unittest) blokları yazılabilir. O blokların yapı tanımını kalabalıklaştırdığını düşünüyorsanız, bloğu bütünüyle yapının dışında da tanımlayabilirsiniz: -) - ---- -struct GününSaati { - // ... yapı tanımı ... -} - -unittest { - // ... yapı testleri ... -} ---- - -$(P -Bunun nedeni, $(C unittest) bloklarının aslında belirli bir noktada tanımlanmalarının gerekmemesidir. Denetledikleri kodlarla bir arada bulunmaları daha doğal olsa da, onları uygun bulduğunuz başka yerlerde de tanımlayabilirsiniz. -) - -$(PROBLEM_COK - -$(PROBLEM -$(C GününSaati) yapısına nesnelerin değerini $(C Süre) kadar azaltan bir üye işlev ekleyin. $(C ekle) işlevinde olduğu gibi, süre azaltıldığında bir önceki güne taşsın. Örneğin 00:05'ten 10 dakika azaltınca 23:55 olsun. - -$(P -Başka bir deyişle, $(C azalt) işlevini şu birim testlerini geçecek biçimde gerçekleştirin: -) - ---- -struct GününSaati { - // ... - - void azalt(in Süre süre) { - // ... burasını siz yazın ... - } - - unittest { - auto zaman = GününSaati(10, 30); - - // Basit bir test - zaman.azalt(Süre(12)); - assert(zaman == GününSaati(10, 18)); - - // 3 gün ve 11 saat önce - zaman.azalt(Süre(3 * 24 * 60 + 11 * 60)); - assert(zaman == GününSaati(23, 18)); - - // 23 saat ve 18 dakika önce gece yarısı olmalı - zaman.azalt(Süre(23 * 60 + 18)); - assert(zaman == GününSaati(0, 0)); - - // 1 dakika öncesi - zaman.azalt(Süre(1)); - assert(zaman == GününSaati(23, 59)); - } -} ---- - -) - -$(PROBLEM -Daha önce $(LINK2 /ders/d/islev_yukleme.cozum.html, İşlev Yükleme bölümünün çözümünde) kullanılan diğer bütün $(C bilgiVer) işlevlerinin yerine $(C Toplantı), $(C Yemek), ve $(C GünlükPlan) yapıları için $(C toString) üye işlevlerini tanımlayın. - -$(P -Çok daha kullanışlı olmalarının yanında, her birisinin tek satırda yazılabildiğini göreceksiniz. -) - -) - -) - -Macros: - SUBTITLE=Üye İşlevler - - DESCRIPTION=D dili yapılarının ve sınıflarının üye işlevleri, ve toString - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial yapı yapılar struct üye işlev üye fonksiyon toString - -SOZLER= -$(arayuz) -$(islec) -$(phobos) -$(uye_islev) -$(yapi) -$(yukleme) diff --git a/ddili/src/ders/d/while_dongusu.cozum.d b/ddili/src/ders/d/while_dongusu.cozum.d deleted file mode 100644 index 66bff33..0000000 --- a/ddili/src/ders/d/while_dongusu.cozum.d +++ /dev/null @@ -1,51 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU while Döngüsü) - -$(OL - -$(LI -$(C sayı)'nın ilk değeri 0 olduğu için $(C while) döngüsünün mantıksal ifadesi en baştan $(C false) oluyor ve döngüye bir kere bile girilmiyor. Bunun için programcılıkta çok kullanılan bir yöntem, döngüye girmeyi sağlayacak bir ilk değer kullanmaktır: - ---- - int sayı = 3; ---- - -) - -$(LI - -Bu programda açıkça ilk değerler verilmiyor çünkü sayıların 0 olan ilk değerleri her iki döngüye de mutlaka girileceğini garanti ediyorlar: - ---- -import std.stdio; - -void main() { - int gizli_sayı; - - while ((gizli_sayı < 1) || (gizli_sayı > 10)) { - write("1-10 aralığındaki gizli sayıyı bildirin: "); - readf(" %s", &gizli_sayı); - } - - int tahmin; - - while (tahmin != gizli_sayı) { - write("Tahmin? "); - readf(" %s", &tahmin); - } - - writeln("Doğru!"); -} ---- - -) - -) - -Macros: - SUBTITLE=while Döngüsü Problem Çözümleri - - DESCRIPTION=D.ershane D programlama dili dersi çözümleri: while Döngüsü - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial while döngüsü problem çözüm diff --git a/ddili/src/ders/d/while_dongusu.d b/ddili/src/ders/d/while_dongusu.d deleted file mode 100644 index c22e88a..0000000 --- a/ddili/src/ders/d/while_dongusu.d +++ /dev/null @@ -1,229 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX while) $(IX döngü, while) $(CH4 while) Döngüsü) - -$(P -$(C while) döngüsü $(C if) koşuluna çok benzer ve onun tekrar tekrar işletilmesidir. Mantıksal bir ifade alır, bu ifade doğru ise kapsamdaki ifadeleri işletir. Kapsamdaki işlemler tamamlanınca mantıksal ifadeye tekrar bakar ve doğru olduğu sürece bu döngüde devam eder. "while", "olduğu sürece" demektir. Söz dizimi şöyledir: -) - ---- - while (bir_mantıksal_ifade) { - // işletilecek bir ifade - // işletilecek başka bir ifade - // vs... - } ---- - -$(P -Örneğin "baklava olduğu sürece baklava ye" gibi bir ifade şöyle programlanabilir: -) - ---- -import std.stdio; - -void main() { - bool hâlâ_baklava_var = true; - - while (hâlâ_baklava_var) { - writeln("Tabağa baklava koyuyorum"); - writeln("Baklava yiyorum"); - } -} ---- - -$(P -O program sonsuza kadar o döngü içinde kalacaktır, çünkü $(C hâlâ_baklava_var) değişkeninin değeri hiç değişmemekte ve hep $(C true) olarak kalmaktadır. -) - -$(P -$(C while)'ın gücü, mantıksal ifadenin programın çalışması sırasında değiştiği durumlarda daha iyi anlaşılır. Bunu görmek için kullanıcıdan bir sayı alan ve bu sayı "0 veya daha büyük" olduğu sürece döngüde kalan bir program düşünelim. Hatırlarsanız, $(C int) türündeki değişkenlerin ilk değeri 0 olduğu için bu programda da $(C sayı)'nın ilk değeri 0'dır: -) - ---- -import std.stdio; - -void main() { - int sayı; - - while (sayı >= 0) { - write("Bir sayı girin: "); - readf(" %s", &sayı); - - writeln(sayı, " için teşekkürler!"); - } - - writeln("Döngüden çıktım"); -} ---- - -$(P -O program girilen sayı için teşekkür eder ve eksi bir sayı girildiğinde döngüden çıkar. -) - -$(H5 $(IX continue) $(C continue) deyimi) - -$(P -"continue", "devam et" demektir. Bu deyim, döngünün geri kalanındaki ifadelerin işletilmeleri yerine, hemen döngünün başına dönülmesini sağlar. -) - -$(P -Yukarıdaki programda girilen her sayı için teşekkür etmek yerine, biraz seçici olalım ve 13 değeri girildiğinde beğenmeyip tekrar döngünün başına dönelim. Bu programda 13'e teşekkür edilmez, çünkü sayı 13 olduğunda $(C continue) ile hemen döngünün başına gidilir: -) - ---- -import std.stdio; - -void main() { - int sayı; - - while (sayı >= 0) { - write("Bir sayı girin: "); - readf(" %s", &sayı); - - if (sayı == 13) { - writeln("Uğursuz sayı kabul etmiyorum..."); - continue; - } - - writeln(sayı, " için teşekkürler!"); - } - - writeln("Döngüden çıktım"); -} ---- - -$(P -O programın davranışını şöyle özetleyebiliriz: girilen sayı 0 veya daha büyük olduğu sürece sayı al, ama 13 değerini kullanma. -) - -$(P -$(C continue) deyimi $(C do-while), $(C for), ve $(C foreach) deyimleriyle birlikte de kullanılabilir. Bu olanakları sonraki bölümlerde göreceğiz. -) - -$(H5 $(IX break) $(C break) deyimi) - -$(P -Bir çok sözlük anlamı olan "break" D'de "döngüyü kır" anlamındadır. Bazen artık döngüyle işimiz kalmadığını anladığımızda döngüden hemen çıkmak isteriz; $(C break) bunu sağlar. Bu programın aradığı özel sayının 42 olduğunu varsayalım ve o sayıyı bulduğu an döngüyle işi bitsin: -) - ---- -import std.stdio; - -void main() { - int sayı; - - while (sayı >= 0) { - write("Bir sayı girin: "); - readf(" %s", &sayı); - - if (sayı == 42) { - writeln("ARADIĞIMI BULDUM!"); - break; - } - - writeln(sayı, " için teşekkürler!"); - } - - writeln("Döngüden çıktım"); -} ---- - -$(P -Şimdiki davranışını da şöyle özetleyebiliriz: girilen sayı 0 veya daha büyük olduğu sürece sayı al, 42 gelirse hemen çık. -) - -$(P -$(C break) deyimi $(C do-while), $(C for), $(C foreach), ve $(C switch) deyimleriyle birlikte de kullanılabilir. Bu olanakları sonraki bölümlerde göreceğiz. -) - -$(H5 $(IX döngü, sonsuz) Sonsuz döngü) - -$(P -$(C break) deyiminin kullanıldığı bazı durumlarda bilerek sonsuz döngü oluşturulur ve $(C break) deyimi o döngünün tek çıkışı olur. Sonsuz döngü oluşturmak için $(C while)'a sabit $(C true) değeri verilir. Örneğin kullanıcıya bir menü göstererek ondan bir komut bekleyen aşağıdaki program, ancak kullanıcı özellikle istediğinde bu döngüden çıkmaktadır: -) - ---- -import std.stdio; - -void main() { - // Sonsuz döngü, çünkü mantıksal ifade hep true: - while (true) { - write("0:Çık, 1:Türkçe, 2:İngilizce - Seçiminiz? "); - - int seçim; - readf(" %s", &seçim); - - if (seçim == 0) { - writeln("Tamam, sonra görüşürüz..."); - $(HILITE break); // Bu döngünün tek çıkışı - - } else if (seçim == 1) { - writeln("Merhaba!"); - - } else if (seçim == 2) { - writeln("Hello!"); - - } else { - writeln("O dili bilmiyorum. :/"); - } - } -} ---- - -$(P -($(I Not: Sonsuz döngülerden hata atılınca da çıkılabilir. Hata atma düzeneğini daha sonraki bir bölümde göreceğiz.)) -) - -$(PROBLEM_COK - -$(PROBLEM -Şu program girişten 3 geldiği sürece döngüde kalmak için programlanmış ama bir hata var: kullanıcıdan bir kere bile sayı istemiyor: - ---- -import std.stdio; - -void main() { - int sayı; - - while (sayı == 3) { - write("Sayı? "); - readf(" %s", &sayı); - } -} ---- - -$(P -Neden? O programdaki hatayı giderin ve beklendiği gibi çalışmasını sağlayın: kullanıcıdan sayı alsın ve sayı 3 olduğu sürece döngüde kalsın. -) - -) - -$(PROBLEM -Bilgisayar iki kişiye (Ayşe ve Barış) şöyle bir oyun oynatsın: en başta Ayşe'den 1-10 aralığında bir sayı alsın. Ayşe'nin bu aralık dışında sayı girmesini kabul etmesin ve doğru sayı girene kadar Ayşe'den sayı almaya devam etsin. - -$(P -Ondan sonra Barış'tan teker teker sayı almaya başlasın ve Barış'ın girdiği sayı Ayşe'nin baştan girdiği sayıya eşit olunca oyunu sonlandırsın. -) - -$(P -$(B Not:) Ayşe'nin girdiği sayı ekranda göründüğünden tabii ki Barış tarafından hemen bilinir. Bu aşamada bunun bir önemi yok; burada amacımız döngüleri öğrenmek. -) - -) - -) - - -Macros: - SUBTITLE=while Döngüsü - - DESCRIPTION=D dilinin döngü deyimlerinden while'ın tanıtılması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial while döngü deyim - -SOZLER= -$(deyim) -$(dongu) -$(ifade) -$(kapsam) -$(mantiksal_ifade) diff --git a/ddili/src/ders/d/writeln.cozum.d b/ddili/src/ders/d/writeln.cozum.d deleted file mode 100644 index ea0cead..0000000 --- a/ddili/src/ders/d/writeln.cozum.d +++ /dev/null @@ -1,31 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU writeln ve write) - -$(OL - -$(LI -Bir yöntem, arada bir parametre daha kullanmaktır: - ---- - writeln("Merhaba dünya!", " ", "Merhaba balıklar!"); ---- -) - -$(LI -$(C write) da birden fazla parametre alabilir: - ---- - write("bir", " iki", " üç"); ---- - -) - -) - -Macros: - SUBTITLE=writeln ve write Problem Çözümleri - - DESCRIPTION=D programlama dili dersi çözümleri: writeln ve write - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial writeln write problem çözüm diff --git a/ddili/src/ders/d/writeln.d b/ddili/src/ders/d/writeln.d deleted file mode 100644 index e085c81..0000000 --- a/ddili/src/ders/d/writeln.d +++ /dev/null @@ -1,80 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX writeln) $(IX write) $(CH4 writeln) ve $(CH4 write)) - -$(P -Bundan önceki bölümde, yazdırılmak istenen dizginin $(C writeln)'e parantez içinde verildiğini gördük. -) - -$(P -Programda $(C writeln) gibi iş yapan birimlere $(I işlev), o işlevlerin işlerini yaparken kullanacakları bilgilere de $(I parametre) adı verilir. Parametreler işlevlere parantez içinde verilirler. -) - -$(P -$(C writeln) satıra yazdırmak için bir seferde birden fazla parametre alabilir. Parametrelerin birbirleriyle karışmalarını önlemek için aralarında virgül kullanılır. -) - ---- -import std.stdio; - -void main() { - writeln("Merhaba dünya!", "Merhaba balıklar!"); -} ---- - -$(P -Bazen, aynı satıra yazdırılacak olan bütün bilgi $(C writeln)'e hep birden parametre olarak geçirilebilecek şekilde hazır bulunmayabilir. Böyle durumlarda satırın baş tarafları $(C write) ile parça parça oluşturulabilir ve satırdaki en sonuncu bilgi $(C writeln) ile yazdırılabilir.) - -$(P -$(C writeln) yazdıklarının sonunda yeni bir satıra geçer, $(C write) aynı satırda kalır: -) - ---- -import std.stdio; - -void main() { - // Önce elimizde hazır bulunan bilgiyi yazdırıyor olalım: - write("Merhaba"); - - // ... arada başka işlemlerin yapıldığını varsayalım ... - - write("dünya!"); - - // ve en sonunda: - writeln(); -} ---- - -$(P -$(C writeln)'i parametresiz kullanmak, satırın sonlanmasını sağlar. -) - -$(P -$(IX //) $(IX açıklama) Başlarında $(COMMENT //) karakterleri bulunan satırlara $(I açıklama satırı) adı verilir. Bu satırlar programa dahil değildirler; programın bilgisayara yaptıracağı işleri etkilemezler. Tek amaçları, belirli noktalarda ne yapılmak istendiğini programı daha sonra okuyacak olan kişilere açıklamaktır. -) - -$(PROBLEM_COK - -$(PROBLEM - -Buradaki programların ikisi de dizgileri aralarında boşluk olmadan birbirlerine yapışık olarak yazdırıyorlar; bu sorunu giderin - -) - -$(PROBLEM -$(C write)'ı da birden fazla parametreyle çağırmayı deneyin -) - -) - -Macros: - SUBTITLE=writeln ve write - - DESCRIPTION=D dilindeki yazdırma işlevlerinden writeln ve write - - KEYWORDS=d programlama dili dersleri öğrenmek tutorial writeln write - -SOZLER= -$(aciklama) -$(islev) -$(parametre) diff --git a/ddili/src/ders/d/yapilar.cozum.d b/ddili/src/ders/d/yapilar.cozum.d deleted file mode 100644 index 7e6d17f..0000000 --- a/ddili/src/ders/d/yapilar.cozum.d +++ /dev/null @@ -1,222 +0,0 @@ -Ddoc - -$(COZUM_BOLUMU Yapılar) - -$(OL - -$(LI -Aksine bir neden olmadığı için, en basit olarak iki tane karakter ile: - ---- -struct OyunKağıdı { - dchar renk; - dchar değer; -} ---- - -) - -$(LI -Yine çok basit olarak, yapı nesnesinin üyelerini yan yana çıkışa göndermek yeterli olur: - ---- -void oyunKağıdıYazdır(in OyunKağıdı kağıt) { - write(kağıt.renk, kağıt.değer); -} ---- - -) - -$(LI -Eğer $(C yeniSeri) isminde başka bir işlevin yazılmış olduğunu kabul edersek, $(C yeniDeste) işlevini de onu her renk için dört kere çağırarak kolayca yazabiliriz: - ---- -OyunKağıdı[] yeniDeste() -out (sonuç) { - assert(sonuç.length == 52); - -} body { - OyunKağıdı[] deste; - - deste ~= yeniSeri('♠'); - deste ~= yeniSeri('♡'); - deste ~= yeniSeri('♢'); - deste ~= yeniSeri('♣'); - - return deste; -} ---- - -$(P -İşin diğer bölümü yararlandığımız $(C yeniSeri) tarafından halledilir. Bu işlev verilen renk bilgisini bir dizginin bütün elemanlarıyla sırayla birleştirerek bir seri oluşturuyor: -) - ---- -OyunKağıdı[] yeniSeri(in dchar renk) -in { - assert((renk == '♠') || - (renk == '♡') || - (renk == '♢') || - (renk == '♣')); - -} out (sonuç) { - assert(sonuç.length == 13); - -} body { - OyunKağıdı[] seri; - - foreach (değer; "234567890JQKA") { - seri ~= OyunKağıdı(renk, değer); - } - - return seri; -} ---- - -$(P -Program hatalarını önlemek için işlevlerin giriş koşullarını ve çıkış garantilerini de yazdığıma dikkat edin. -) - -) - -$(LI -Rasgele seçilen iki elemanı değiş tokuş etmek, sonuçta destenin karışmasını da sağlar. Rastgele seçim sırasında, küçük de olsa aynı elemanı seçme olasılığı da vardır. Ama bu önemli bir sorun oluşturmaz, çünkü elemanı kendisiyle değiştirmenin etkisi yoktur. - ---- -void karıştır(OyunKağıdı[] deste, in int değişTokuşAdedi) { - /* Not: Daha etkin bir yöntem, desteyi başından sonuna - * kadar ilerlemek ve her elemanı destenin sonuna - * doğru rasgele bir elemanla değiştirmektir. - * - * En doğrusu, zaten aynı algoritmayı uygulayan - * std.algorithm.randomShuffle işlevini çağırmaktır. Bu - * karıştır() işlevini bütünüyle kaldırıp main() içinde - * açıklandığı gibi randomShuffle()'ı çağırmak daha doğru - * olur. */ - foreach (i; 0 .. değişTokuşAdedi) { - - // Rasgele iki tanesini seç - immutable birinci = uniform(0, deste.length); - immutable ikinci = uniform(0, deste.length); - - // Değiş tokuş et - swap(deste[birinci], deste[ikinci]); - } -} ---- - -$(P -O işlevde $(C std.algorithm) modülündeki $(C swap) işlevinden yararlandım. $(C swap), kendisine verilen iki değeri değiş tokuş eder. Temelde şu işlev gibi çalışır: -) - ---- -void değişTokuş(ref OyunKağıdı soldaki, - ref OyunKağıdı sağdaki) { - immutable geçici = soldaki; - soldaki = sağdaki; - sağdaki = geçici; -} ---- - -) - -) - -$(P -Programın tamamı şöyle: -) - ---- -import std.stdio; -import std.random; -import std.algorithm; - -struct OyunKağıdı { - dchar renk; - dchar değer; -} - -void oyunKağıdıYazdır(in OyunKağıdı kağıt) { - write(kağıt.renk, kağıt.değer); -} - -OyunKağıdı[] yeniSeri(in dchar renk) -in { - assert((renk == '♠') || - (renk == '♡') || - (renk == '♢') || - (renk == '♣')); - -} out (sonuç) { - assert(sonuç.length == 13); - -} body { - OyunKağıdı[] seri; - - foreach (değer; "234567890JQKA") { - seri ~= OyunKağıdı(renk, değer); - } - - return seri; -} - -OyunKağıdı[] yeniDeste() -out (sonuç) { - assert(sonuç.length == 52); - -} body { - OyunKağıdı[] deste; - - deste ~= yeniSeri('♠'); - deste ~= yeniSeri('♡'); - deste ~= yeniSeri('♢'); - deste ~= yeniSeri('♣'); - - return deste; -} - -void karıştır(OyunKağıdı[] deste, in int değişTokuşAdedi) { - /* Not: Daha etkin bir yöntem, desteyi başından sonuna - * kadar ilerlemek ve her elemanı destenin sonuna - * doğru rasgele bir elemanla değiştirmektir. - * - * En doğrusu, zaten aynı algoritmayı uygulayan - * std.algorithm.randomShuffle işlevini çağırmaktır. Bu - * karıştır() işlevini bütünüyle kaldırıp main() içinde - * açıklandığı gibi randomShuffle()'ı çağırmak daha doğru - * olur. */ - foreach (i; 0 .. değişTokuşAdedi) { - - // Rasgele iki tanesini seç - immutable birinci = uniform(0, deste.length); - immutable ikinci = uniform(0, deste.length); - - // Değiş tokuş et - swap(deste[birinci], deste[ikinci]); - } -} - -void main() { - OyunKağıdı[] deste = yeniDeste(); - - karıştır(deste, 100); - /* Not: Yukarıdaki karıştır() çağrısı yerine aşağıdaki - * randomShuffle() daha doğru olur: - * - * randomShuffle(deste); - */ - foreach (kağıt; deste) { - oyunKağıdıYazdır(kağıt); - write(' '); - } - - writeln(); -} ---- - -Macros: - SUBTITLE=Yapılar - - DESCRIPTION=D dilinin kullanıcı türleri tanımlaya yarayan olanağı 'struct' problem çözümleri - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial yapı yapılar struct kullanıcı türleri problem çözüm diff --git a/ddili/src/ders/d/yapilar.d b/ddili/src/ders/d/yapilar.d deleted file mode 100644 index 777476f..0000000 --- a/ddili/src/ders/d/yapilar.d +++ /dev/null @@ -1,944 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX struct) $(IX yapı) Yapılar) - -$(P -Kitabın başında temel türlerin üst düzey kavramları ifade etmede yetersiz kalacaklarını söylemiştim. $(C int) türünden bir tamsayı örneğin iki olay arasında geçen süreyi dakika türünden ifade etmek için kullanılabilir; ama böyle bir değer her zaman tek başına kullanışlı olamaz. Değişkenler bazen başka değişkenlerle bir araya geldiklerinde anlam kazanırlar. -) - -$(P -Yapılar temel türleri, başka yapıları, veya sınıfları bir araya getirerek yeni türler oluşturmaya yarayan olanaklardır. Yeni tür $(C struct) anahtar sözcüğü ile oluşturulur. $(C struct), "yapı" anlamına gelen "structure"ın kısaltmasıdır. -) - -$(P -Bu bölümde yapılarla ilgili olarak anlatacağım çoğu bilgi, daha sonra göreceğimiz sınıfları anlamada da yardımcı olacak. Özellikle $(I bir araya getirerek yeni tür tanımlama) kavramı, yapılarda ve sınıflarda aynıdır. -) - -$(P -Yapı kavramını anlamak için daha önce $(LINK2 /ders/d/assert.html, $(C assert) ve $(C enforce) bölümünde) gördüğümüz $(C zamanEkle) işlevine bakalım. Aşağıdaki tanım o bölümün problem çözümlerinde geçiyordu: -) - ---- -void zamanEkle( - in int başlangıçSaati, in int başlangıçDakikası, - in int eklenecekSaat, in int eklenecekDakika, - out int sonuçSaati, out int sonuçDakikası) { - sonuçDakikası = başlangıçDakikası + eklenecekDakika; - sonuçSaati = başlangıçSaati + eklenecekSaat; - sonuçSaati += sonuçDakikası / 60; - - sonuçDakikası %= 60; - sonuçSaati %= 24; -} ---- - -$(P $(I Not: İşlevin $(C in), $(C out), ve $(C unittest) bloklarını fazla yer tutmamak için bu bölümde göstermiyorum.) -) - -$(P -Her ne kadar o işlev altı tane parametre alıyor gibi görünse de, birbirleriyle ilgili olan parametreleri çifter çifter düşünürsek, aslında üç çift bilgi aldığını görürüz. O çiftlerden ilk ikisi giriş olarak, sonuncusu da çıkış olarak kullanılmaktadır. -) - -$(H5 Tanımlanması) - -$(P -$(C struct) birbirleriyle ilişkili değişkenleri bir araya getirerek yeni bir tür olarak kullanma olanağı verir: -) - ---- -$(CODE_NAME GününSaati)struct GününSaati { - int saat; - int dakika; -} ---- - -$(P -Yukarıdaki tanım, $(C saat) ve $(C dakika) isimli iki $(C int) değişkeni bir araya getiren ve ismi $(C GününSaati) olan yeni bir tür tanımlar. Yukarıdaki tanımdan sonra artık başka türler gibi kullanabileceğimiz $(C GününSaati) isminde yeni bir türümüz olur. Örnek olarak $(C int) türüne benzer kullanımını şöyle gösterebiliriz: -) - ---- - int sayı; // bir değişken - sayı = başkaSayı; // başkaSayı'nın değerini alması - - GününSaati zaman; // bir nesne - zaman = başkaZaman; // başkaZaman'ın değerini alması ---- - -$(P -Yapı türleri şöyle tanımlanır: -) - ---- -struct $(I Türİsmi) { - // ... türü oluşturan üyeler ve varsa özel işlevleri ... -} ---- - -$(P -Yapılar için özel işlevler de tanımlanabilir. Bunları daha sonraki bir bölümde anlatacağım. Bu bölümde yalnızca yapı üyelerini gösteriyorum. -) - -$(P -Yapının bir araya getirdiği parçalara $(I üye) adı verilir. Bu tanıma göre, yukarıdaki $(C GününSaati) yapısının iki üyesi vardır: $(C saat) ve $(C dakika). -) - -$(H6 $(C struct) tür tanımıdır, değişken tanımı değildir) - -$(P -Burada bir uyarıda bulunmam gerekiyor: $(LINK2 /ders/d/isim_alani.html, İsim Alanı bölümünde) ve $(LINK2 /ders/d/yasam_surecleri.html, Yaşam Süreçleri bölümünde) anlatılanlar doğrultusunda; yapı tanımında kullanılan küme parantezlerine bakarak, o kapsam içindeki üyelerin yapının tanımlandığı an yaşamaya başladıklarını düşünebilirsiniz. Bu doğru değildir. -) - -$(P -Yapı tanımı, değişken tanımlamaz: -) - ---- -struct GününSaati { - int saat; // ← Değişken tanımı değildir; daha sonra - // bir yapı nesnesinin parçası olacaktır. - - int dakika; // ← Değişken tanımı değildir; daha sonra - // bir yapı nesnesinin parçası olacaktır. -} ---- - -$(P -Yapı tanımı, daha sonradan yapı nesneleri oluşturulduğunda ne tür üye değişkenlerinin olacağını belirler. O üye değişkenler bu yapı türünden bir nesne oluşturulduğu zaman o nesnenin parçası olarak oluşturulurlar: -) - ---- - GününSaati yatmaZamanı; // içinde kendi saat ve dakika - // değişkenlerini barındırır - - GününSaati kalkmaZamanı; // bu da kendi saat ve dakika - // değişkenlerini barındırır; - // bunun saat ve dakika - // değişkenleri öncekinden - // bağımsızdır ---- - -$(P -$(IX nesne, yapı) Yapı ve sınıf değişkenlerine $(I nesne) denir. -) - -$(H6 Kodlama kolaylığı) - -$(P -Saat ve dakika gibi iki bilgiyi böyle bir araya getirerek tek bir tür gibi kullanabilmek büyük kolaylık sağlar. Örneğin yukarıdaki işlev altı tane $(C int) yerine, asıl amacına çok daha uygun olacak şekilde üç tane $(C GününSaati) türünde parametre alabilir: -) - ---- -void zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek, - out GününSaati sonuç) { - // ... -} ---- - -$(P $(I Not: Günün saatini belirten böyle iki değerin eklenmesi aslında normal bir işlem olarak kabul edilmemelidir. Örneğin kahvaltı zamanı olan 7:30'a öğle yemeği zamanı olan 12:00'yi eklemek doğal değildir. Burada aslında $(C Süre) diye yeni bir tür tanımlamak ve $(C GününSaati) nesnelerine $(C Süre) nesnelerini eklemek çok daha doğru olurdu. Ben bu bölümde yine de yalnızca $(C GününSaati) türünü kullanacağım.) -) - -$(P -Hatırlayacağınız gibi, işlevler $(C return) deyimiyle tek bir değer döndürebilirler. $(C zamanEkle) ürettiği saat ve dakika değerlerini zaten bu yüzden iki tane $(C out) parametre olarak tanımlamak zorunda kalıyordu. Ürettiği iki tane sonucu tek bir değer olarak döndüremiyordu. -) - -$(P -Yapılar bu kısıtlamayı da ortadan kaldırırlar: Birden fazla bilgiyi bir araya getirerek tek bir tür oluşturdukları için, işlevlerin dönüş türü olarak kullanılabilirler. Artık işlevden tek bir $(C GününSaati) nesnesi döndürebiliriz: -) - ---- -GününSaati zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek) { - // ... -} ---- - -$(P -Böylece $(C zamanEkle) artık yan etki oluşturan değil, değer üreten bir işlev haline de gelmiş olur. $(LINK2 /ders/d/islevler.html, İşlevler bölümünden) hatırlayacağınız gibi; işlevlerin yan etki oluşturmak yerine değer üretmeleri tercih edilir. -) - -$(P -Yapılar da yapı üyesi olabilirler. Örneğin $(C GününSaati) yapısından iki üyesi bulunan başka bir yapı şöyle tasarlanabilir: -) - ---- -struct Toplantı { - string konu; - size_t katılımcıSayısı; - GününSaati başlangıç; - GününSaati bitiş; -} ---- - -$(P -$(C Toplantı) yapısı da başka bir yapının üyesi olabilir. $(C Yemek) diye bir yapı olduğunu da varsayarsak: -) - ---- -struct GünlükPlan { - Toplantı projeToplantısı; - Yemek öğleYemeği; - Toplantı bütçeToplantısı; -} ---- - -$(H5 $(IX ., üye) Üye erişimi) - -$(P -Yapı üyelerini de herhangi bir değişken gibi kullanabiliriz. Tek fark, üyelere erişmek için nesnenin isminden sonra önce erişim işleci olan $(I nokta), ondan sonra da üyenin isminin yazılmasıdır: -) - ---- - başlangıç.saat = 10; ---- - -$(P -O satır, $(C başlangıç) nesnesinin $(C saat) üyesine $(C 10) değerini atar. -) - -$(P -Yapılarla ilgili bu kadarlık bilgiyi kullanarak $(C zamanEkle) işlevini artık şu şekilde değiştirebiliriz: -) - ---- -$(CODE_NAME zamanEkle)GününSaati zamanEkle(in GününSaati başlangıç, - in GününSaati eklenecek) { - GününSaati sonuç; - - sonuç.dakika = başlangıç.dakika + eklenecek.dakika; - sonuç.saat = başlangıç.saat + eklenecek.saat; - sonuç.saat += sonuç.dakika / 60; - - sonuç.dakika %= 60; - sonuç.saat %= 24; - - return sonuç; -} ---- - -$(P -Bu işlevde kullanılan değişken isimlerinin artık çok daha kısa seçilebildiğine dikkat edin: nesnelere $(C başlangıç), $(C eklenecek), ve $(C sonuç) gibi kısa isimler verebiliyoruz. $(C başlangıçSaati) gibi bileşik isimler kullanmak yerine de nesnelerin üyelerine nokta ile $(C başlangıç.saat) şeklinde erişiyoruz. -) - -$(P -O işlevi kullanan bir kod aşağıdaki şekilde yazılabilir. Bu program, 1 saat 15 dakika süren ve 8:30'da başlayan dersin bitiş zamanını 9:45 olarak hesaplar: -) - ---- -$(CODE_XREF GününSaati)$(CODE_XREF zamanEkle)void main() { - GününSaati dersBaşı; - dersBaşı.saat = 8; - dersBaşı.dakika = 30; - - GününSaati dersSüresi; - dersSüresi.saat = 1; - dersSüresi.dakika = 15; - - immutable dersSonu = zamanEkle(dersBaşı, dersSüresi); - - writefln("Ders sonu: %s:%s", - dersSonu.saat, dersSonu.dakika); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -Ders sonu: 9:45 -) - -$(P -Yukarıdaki $(C main)'i şimdiye kadar bildiklerimizi kullanarak yazdım. Biraz aşağıda bu işlemlerin çok daha kolay ve kısa olanlarını göstereceğim. -) - -$(H5 $(IX kurma) Kurma) - -$(P -Yukarıdaki $(C main)'in ilk üç satırı, $(C dersBaşı) nesnesinin kurulması ile ilgilidir; ondan sonraki üç satır da $(C dersSüresi) nesnesinin kurulmasıdır. O satırlarda önce nesne tanımlanmakta, sonra saat ve dakika üyelerinin değerleri atanmaktadır. -) - -$(P -Herhangi bir değişkenin veya nesnenin tutarlı bir şekilde kullanılabilmesi için mutlaka kurulması gerekir. Bu çok önemli ve yaygın bir işlem olduğu için, yapı nesneleri için kısa bir kurma söz dizimi vardır: -) - ---- - GününSaati dersBaşı = GününSaati(8, 30); - GününSaati dersSüresi = GününSaati(1, 15); ---- - -$(P -Nesneler bu şekilde kurulurken belirtilen değerler, yapının üyelerine yapı içinde tanımlandıkları sıra ile atanırlar: yapı içinde $(C saat) önce tanımlandığı için 8 değeri $(C dersBaşı.saat)'e, 30 değeri de $(C dersBaşı.dakika)'ya atanır. -) - -$(P -$(LINK2 /ders/d/tur_donusumleri.html, Tür Dönüşümleri bölümünde) gördüğümüz gibi, kurma söz dizimi başka türlerle de kullanılabilir: -) - ---- - auto u = ubyte(42); // u'nun türü ubyte olur - auto i = int(u); // i'nin türü int olur ---- - -$(H6 $(C immutable) olarak kurabilme olanağı) - -$(P -Nesneleri aynı anda hem tanımlamak hem de değerlerini verebilmek, onları $(C immutable) olarak işaretleme olanağı da sağlar: -) - ---- - immutable dersBaşı = GününSaati(8, 30); - immutable dersSüresi = GününSaati(1, 15); ---- - -$(P -Kurulduktan sonra artık hiç değişmeyecek oldukları durumlarda, bu nesnelerin sonraki satırlarda yanlışlıkla değiştirilmeleri böylece önlenmiş olur. Yukarıdaki programda ise nesneleri $(C immutable) olarak işaretleyemezdik, çünkü ondan sonra üyelerinin değerlerini atamamız mümkün olmazdı: -) - ---- - $(HILITE immutable) GününSaati dersBaşı; - dersBaşı.saat = 8; $(DERLEME_HATASI) - dersBaşı.dakika = 30; $(DERLEME_HATASI) ---- - -$(P -Doğal olarak, $(C immutable) olarak işaretlendiği için değişemeyen $(C dersBaşı) nesnesinin üyelerini değiştirmek olanaksızdır. -) - -$(H6 Sondaki üyelerin değerleri boş bırakılabilir) - -$(P -Yapı nesneleri kurulurken $(I sondaki) üyelerin değerleri belirtilmeyebilir. Bu durumda sondaki üyeler yine de otomatik olarak kendi türlerinin $(C .init) değeri ile ilklenirler. -) - -$(P -Bunu gösteren aşağıdaki programda $(C Deneme) türü gittikçe azalan sayıda parametre ile kuruluyor ve geri kalan parametrelerin de otomatik olarak ilklendikleri $(C assert) denetimleri ile gösteriliyor (programda kullanmak zorunda kaldığım $(C isNaN) işlevini programdan sonra açıklıyorum): -) - ---- -import std.math; - -struct Deneme { - char karakter; - int tamsayı; - double kesirli; -} - -void main() { - // Bütün değerlerle - auto d1 = Deneme('a', 1, 2.3); - assert(d1.karakter == 'a'); - assert(d1.tamsayı == 1); - assert(d1.kesirli == 2.3); - - // Sonuncusu eksik - auto d2 = Deneme('a', 1); - assert(d2.karakter == 'a'); - assert(d2.tamsayı == 1); - assert($(HILITE isNaN(d2.kesirli))); - - // Son ikisi eksik - auto d3 = Deneme('a'); - assert(d3.karakter == 'a'); - assert($(HILITE d3.tamsayı == int.init)); - assert($(HILITE isNaN(d3.kesirli))); - - // Hiçbirisi yok - auto d4 = Deneme(); - assert($(HILITE d4.karakter == char.init)); - assert($(HILITE d4.tamsayı == int.init)); - assert($(HILITE isNaN(d4.kesirli))); - - // Yukarıdakiyle aynı şey - Deneme d5; - assert(d5.karakter == char.init); - assert(d5.tamsayı == int.init); - assert(isNaN(d5.kesirli)); -} ---- - -$(P -$(LINK2 /ders/d/kesirli_sayilar.html, Kesirli Sayılar bölümünden) hatırlayacağınız gibi $(C double)'ın ilk değeri $(C double.nan)'dır ve bir değerin $(C .nan)'a eşit olup olmadığı $(C ==) işleci ile denetlenemez. O yüzden yukarıdaki programda $(C std.math.isNaN)'dan yararlanılmıştır. -) - -$(H6 $(IX varsayılan değer, üye) Varsayılan üye değerlerinin belirlenmesi) - -$(P -Üyelerin otomatik olarak ilkleniyor olmaları çok yararlı bir olanaktır. Üyelerin rasgele değerlerle kullanılmaları önlenmiş olur. Ancak, her üyenin kendi türünün $(C .init) değerini alması her duruma uygun değildir. Örneğin $(C char.init) değeri geçerli bir karakter bile değildir. -) - -$(P -Bu yüzden üyelerin $(I otomatik olarak) alacakları değerler programcı tarafından belirlenebilir. Bu sayede örneğin yukarıda gördüğümüz ve hiçbir kullanışlılığı olmayan $(C double.nan) değeri yerine, çoğu zaman çok daha uygun olan 0.0 değerini kullanabiliriz. -) - -$(P -Üyelerin aldıkları bu özel ilk değerlere $(I varsayılan değer) denir ve üye tanımından sonraki atama söz dizimiyle belirlenir: -) - ---- -struct Deneme { - char karakter $(HILITE = 'A'); - int tamsayı $(HILITE = 11); - double kesirli $(HILITE = 0.25); -} ---- - -$(P -Üye tanımı sırasında kullanılan bu yazım şeklinin bir atama işlemi olmadığına dikkat edin. Yukarıdaki kodun tek amacı, üyeler için hangi değerlerin varsayılacağını belirlemektir. Bu değerler, daha sonra nesne oluşturulurken gerekirse kullanılacaktır. -) - -$(P -Nesne kurulurken değerleri özellikle belirtilmeyen üyeler o varsayılan değerleri alırlar. Örneğin aşağıdaki kullanımda nesnenin hiçbir üyesinin değeri verilmemektedir: -) - ---- - Deneme d; // hiçbir üye değeri belirtilmiyor - writefln("%s,%s,%s", d.karakter, d.tamsayı, d.kesirli); ---- - -$(P -Bütün üyeler türün tanımında belirtilmiş olan ilk değerlere sahip olurlar: -) - -$(SHELL -A,11,0.25 -) - -$(H6 $(IX {}, kurma) $(IX C söz dizimi, yapı kurucusu) $(C {}) karakterleriyle kurma) - -$(P -Yukarıdaki kurma söz dizimi varken kullanmaya gerek olmasa da, bunu da bilmeniz gerekir. Yapı nesnelerini başka bir söz dizimiyle de kurabilirsiniz: -) - ---- - GününSaati dersBaşı = { 8, 30 }; ---- - -$(P -Belirlenen değerler bu kullanımda da üyelere sıra ile atanırlar; ve bu kullanımda da üye sayısından daha az değer verilebilir. -) - -$(P -Bu söz dizimi D'ye C programlama dilinden geçmiştir: -) - ---- - auto dersBaşı = GününSaati(8, 30); // ← normal - GününSaati dersSonu = { 9, 30 }; // ← C söz dizimi ---- - -$(P -$(IX isimli ilklendirici) Bu söz diziminin bir yararı, $(I isimli ilklendirici) olanağıdır. Verilen bir değerin hangi üye ile ilgili olduğu üyenin ismi ile belirtilebilir. Bu olanak, üyelerin yapı içindeki tanımlarından farklı sırada ilklenmelerine de izin verir: -) - ---- - GününSaati g = { $(HILITE dakika:) 42, $(HILITE saat:) 7 }; ---- - -$(H5 $(IX kopyalama, yapı) $(IX atama, yapı) Kopyalama ve Atama) - -$(P -Yapılar değer türleridir. Bundan; $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar bölümünde) açıklandığı gibi, her yapı nesnesinin kendisine ait değeri olduğunu anlarız. Kurulduklarında kendi değerlerini edinirler; atandıklarında da yalnızca kendi değerleri değişir. -) - ---- - auto seninYemekSaatin = GününSaati(12, 0); - auto benimYemekSaatim = seninYemekSaatin; - - // Yalnızca benim yemek saatim 12:05 olur: - benimYemekSaatim.dakika += 5; - - // ... senin yemek saatin değişmez: - assert(seninYemekSaatin.dakika == 0); ---- - -$(P -Kopyalama sırasında bir nesnenin bütün üyeleri sırayla diğer üyeye kopyalanır. Benzer şekilde, atama işlemi de bütün üyelerin sırayla atanmaları anlamına gelir. -) - -$(P -Bu konuda referans türünden olan üyelere özellikle dikkat etmek gerekir. -) - -$(H6 $(IX referans türü, üye) Referans türünden olan üyelere dikkat!) - -$(P -Burada çok önemli bir konuyu hatırlatmak gerekiyor: Referans türünden olan değişkenler kopyalandıklarında veya atandıklarında asıl nesne değişmez, ona erişim sağlayan referans değişir, ve sonuçta asıl nesneye birden fazla referans tarafından erişim sağlanmış olur. -) - -$(P -Bunun yapı üyeleri açısından önemi, iki farklı yapı nesnesinin üyelerinin aynı asıl nesneye erişim sağlıyor olacaklarıdır. Bunu görmek için referans türünden bir üyesi olan bir yapıya bakalım. Bir öğrencinin numarasını ve notlarını içeren şöyle bir yapı tanımlanmış olsun: -) - ---- -struct Öğrenci { - int numara; - int[] notlar; -} ---- - -$(P -O türden bir nesnenin başka bir nesnenin değeriyle kurulduğu şu koda bakalım: -) - ---- - // Birinci öğrenciyi kuralım: - auto öğrenci1 = Öğrenci(1, [ 70, 90, 85 ]); - - // İkinci öğrenciyi birincinin kopyası olarak kuralım ... - auto öğrenci2 = öğrenci1; - - // ... ve sonra numarasını değiştirelim: - öğrenci2.numara = 2; - - // DİKKAT: İki öğrenci bu noktada aynı notları paylaşmaktadırlar! - - // İlk öğrencinin notunda bir değişiklik yaptığımızda ... - öğrenci1.notlar[0] += 5; - - // ... ikinci öğrencinin notunun da değiştiğini görürüz: - writeln(öğrenci2.notlar[0]); ---- - -$(P -$(C öğrenci2) nesnesi kurulduğu zaman, üyeleri sırayla $(C öğrenci1)'in üyelerinin değerlerini alır. $(C int) bir değer türü olduğu için, her iki $(C Öğrenci) nesnesinin ayrı $(C numara) değeri olur. -) - -$(P -Her iki $(C Öğrenci) nesnesinin birbirlerinden bağımsız olan $(C notlar) üyeleri de vardır. Ancak, dizi dilimleri referans türleri olduklarından, her ne kadar $(C notlar) üyeleri bağımsız olsalar da, aslında aynı asıl dizinin elemanlarına erişim sağlarlar. Sonuçta, bir nesnenin $(C notlar) üyesinde yapılan değişiklik diğerini de etkiler. -) - -$(P -Yukarıdaki kodun çıktısı, iki öğrenci nesnesinin aynı asıl notları paylaştıklarını gösterir: -) - -$(SHELL -75 -) - -$(P -Belki de burada hiç kopyalama işlemini kullanmadan, ikinci nesneyi kendi numarasıyla ve birincinin notlarının $(I kopyasıyla) kurmak daha doğru olur: -) - ---- - // İkinci öğrenciyi birincinin notlarının kopyası ile - // kuruyoruz - auto öğrenci2 = Öğrenci(2, öğrenci1.notlar$(HILITE .dup)); - - // İlk öğrencinin notunda bir değişiklik yaptığımızda ... - öğrenci1.notlar[0] += 5; - - // ... ikinci öğrencinin notu bu sefer değişmez: - writeln(öğrenci2.notlar[0]); ---- - -$(P -Dizilerin $(C .dup) niteliği ile kopyalandığı için bu sefer her nesnenin ayrı notları olur. Şimdiki çıktı, ikinci öğrencinin notunun etkilenmediğini gösterir: -) - -$(SHELL -70 -) - -$(P -$(I Not: İstenen durumlarda referans türünden üyelerin otomatik olarak kopyalanmaları da sağlanabilir. Bunu daha sonra yapı işlevlerini anlatırken göstereceğim.) -) - -$(H5 $(IX hazır değer, yapı) Yapı hazır değerleri) - -$(P -Nasıl 10 gibi hazır değerleri hiç değişken tanımlamak zorunda kalmadan işlemlerde kullanabiliyorsak, yapı nesnelerini de isimleri olmayan $(I hazır değerler) olarak kullanabiliriz. -) - -$(P -Yapı hazır değerlerini oluşturmak için yine kurma söz dizimi kullanılır ve yapı nesnesi gereken her yerde kullanılabilir. -) - ---- - GününSaati(8, 30) // ← hazır değer ---- - -$(P -Yukarıdaki $(C main) işlevini şimdiye kadar öğrendiklerimizi kullanarak şöyle yazabiliriz: -) - ---- -$(CODE_XREF GününSaati)$(CODE_XREF zamanEkle)void main() { - immutable dersBaşı = GününSaati(8, 30); - immutable dersSüresi = GününSaati(1, 15); - - immutable dersSonu = zamanEkle(dersBaşı, dersSüresi); - - writefln("Ders sonu: %s:%s", - dersSonu.saat, dersSonu.dakika); -} ---- - -$(P -Dikkat ederseniz, o programda $(C dersBaşı) ve $(C dersSüresi) nesnelerinin açıkça belirtilmelerine gerek yoktur. Onlar yalnızca $(C dersSonu) nesnesini hesaplamak için kullanılan aslında geçici nesnelerdir. O nesneleri açıkça tanımlamak yerine, $(C zamanEkle) işlevine şu şekilde $(I hazır değer) olarak da gönderebiliriz: -) - ---- -$(CODE_XREF GününSaati)$(CODE_XREF zamanEkle)void main() { - immutable dersSonu = zamanEkle(GününSaati(8, 30), - GününSaati(1, 15)); - - writefln("Ders sonu: %s:%s", - dersSonu.saat, dersSonu.dakika); -} ---- - -$(H5 $(IX static, üye) $(C static) üyeler) - -$(P -Çoğu durumda her yapı nesnesinin kendi üyelerine sahip olmasını isteriz. Bazı durumlarda ise belirli bir yapı türünden olan bütün nesnelerin tek bir değişkeni paylaşmaları uygun olabilir. Bu, o yapı türü için akılda tutulması gereken genel bir bilgi bulunduğunda gerekebilir. -) - -$(P -Bütün nesnelerin tek bir üyeyi paylaşmalarının bir örneği olarak, her bir nesne için farklı bir tanıtıcı numara olduğu bir durum düşünelim: -) - ---- -struct Nokta { - // Her nesnenin kendi tanıtıcı numarası - size_t numara; - - int satır; - int sütun; -} ---- - -$(P -Her nesneye farklı bir numara verebilmek için $(C sonrakiNumara) gibi bir değişken barındırmak, ve her nesne için o sayıyı bir arttırmak gerekir: -) - ---- -Nokta NoktaOluştur(int satır, int sütun) { - size_t numara = sonrakiNumara; - ++sonrakiNumara; - - return Nokta(numara, satır, sütun); -} ---- - -$(P -Burada karar verilmesi gereken şey, her nesnenin oluşturulması sırasında ortak olarak kullanılan $(C sonrakiNumara) bilgisinin nerede tanımlanacağıdır. $(C static) üyeler işte bu gibi durumlarda kullanışlıdırlar. -) - -$(P -O bilgi bir yapı üyesi olarak tanımlanır ve $(C static) olarak işaretlenir. Diğer üyelerin aksine, böyle üyelerden her iş parçacığında yalnızca bir adet oluşturulur. (Çoğu program yalnızca $(C main())'in işlediği tek iş parçacığından oluşur.): -) - ---- -import std.stdio; - -struct Nokta { - // Her nesnenin kendi tanıtıcı numarası - size_t numara; - - int satır; - int sütun; - - // Bundan sonra oluşturulacak olan nesnenin numarası - $(HILITE static size_t sonrakiNumara;) -} - -Nokta NoktaOluştur(int satır, int sütun) { - size_t numara = $(HILITE Nokta.)sonrakiNumara; - ++$(HILITE Nokta.)sonrakiNumara; - - return Nokta(numara, satır, sütun); -} - -void main() { - auto üstteki = NoktaOluştur(7, 0); - auto ortadaki = NoktaOluştur(8, 0); - auto alttaki = NoktaOluştur(9, 0); - - writeln(üstteki.numara); - writeln(ortadaki.numara); - writeln(alttaki.numara); -} ---- - -$(P -$(C sonrakiNumara) her seferinde bir arttırıldığı için her nesnenin farklı numarası olur: -) - -$(SHELL -0 -1 -2 -) - -$(P -$(C static) üyeler bütün türe ait olduklarından onlara erişmek için bir nesne olması gerekmez. O üyeye türün ismi kullanılarak erişilebileceği gibi, o türün bir nesnesi üzerinden de erişilebilir: -) - ---- - ++Nokta.sonrakiNumara; - ++$(HILITE alttaki).sonrakiNumara; // üst satırın eşdeğeri ---- - -$(P -İş parçacığı başına tek değişken yerine bütün programda tek değişken gerektiğinde o değişkenin $(C shared static) olarak tanımlanması gerekir. $(C shared) anahtar sözcüğünü daha sonraki bir bölümde göreceğiz. -) - -$(H6 $(IX static this) $(IX static ~this) $(IX this, static) $(IX ~this, static) İlk işlemler için $(C static this()), son işlemler için $(C static ~this())) - -$(P -Yukarıda $(C sonrakiNumara) üyesini özel bir değerle ilklemedik ve otomatik ilk değeri olan sıfırdan yararlandık. Gerektiğinde özel bir değerle de ilkleyebilirdik: -) - ---- - static size_t sonrakiNumara $(HILITE = 1000); ---- - -$(P -O yöntem ancak ilk değer derleme zamanında bilindiğinde kullanılabilir. Ek olarak, bazı durumlarda yapının kullanımına geçmeden önce bazı ilkleme kodlarının işletilmesi de gerekebilir. Bu gibi ilkleme kodları yapının $(C static this()) kapsamına yazılırlar. -) - -$(P -Örneğin, aşağıdaki kod nesne numaralarını hep sıfırdan başlatmak yerine eğer mevcutsa özel bir ayar dosyasından okuyor: -) - ---- -import std.file; - -struct Nokta { -// ... - - enum sonNumaraDosyası = "Nokta_son_numara_dosyasi"; - - $(HILITE static this()) { - if (exists(sonNumaraDosyası)) { - auto dosya = File(sonNumaraDosyası, "r"); - dosya.readf(" %s", &sonrakiNumara); - } - } -} ---- - -$(P -Bir yapının özel $(C static this()) kapsamındaki kodlar her iş parçacığında ayrı ayrı işletilir. Bu kodlar o yapı o iş parçacığında kullanılmaya başlanmadan önce otomatik olarak işletilir. İş parçacıklarının sayısından bağımsız olarak bütün programda tek kere işletilmesi gereken kodlar ise (örneğin, $(C immutable) değişkenlerin ilklenmeleri) $(C shared static this()) işlevlerinde tanımlanmalıdırlar. Bunları daha sonraki $(LINK2 /ders/d/es_zamanli_shared.html, Veri Paylaşarak Eş Zamanlı Programlama bölümünde) göreceğiz. -) - -$(P -Benzer biçimde, $(C static ~this()) yapı türünün belirli bir iş parçacığındaki son işlemleri için, $(C shared static ~this()) de bütün programdaki son işlemleri içindir. -) - -$(P -Örneğin, aşağıdaki $(C static ~this()) yukarıdaki $(C static this()) tarafından okunabilsin diye son numarayı ayar dosyasına kaydetmektedir: -) - ---- -struct Nokta { -// ... - - $(HILITE static ~this()) { - auto dosya = File(sonNumaraDosyası, "w"); - dosya.writeln(sonrakiNumara); - } -} ---- - -$(P -Böylece, program nesne numaralarını artık hep kaldığı yerden başlatacaktır. Örneğin, ikinci kere çalıştırıldığında programın çıktısı aşağıdaki gibidir: -) - -$(SHELL -3 -4 -5 -) - -$(PROBLEM_COK - -$(PROBLEM -Tek bir oyun kağıdını temsil eden ve ismi $(C OyunKağıdı) olan bir yapı tasarlayın. Bu yapının kağıt rengi ve kağıt değeri için iki üyesi olduğu düşünülebilir. - -$(P -Renk için bir $(C enum) değer kullanabileceğiniz gibi; doğrudan ♠, ♡, ♢, ve ♣ karakterlerini de kullanabilirsiniz. -) - -$(P -Kağıt değeri olarak da bir $(C int) veya bir $(C dchar) üye kullanabilirsiniz. $(C int) seçerseniz 1'den 13'e kadar değerlerden belki de 1, 11, 12, ve 13 değerlerini sırasıyla as, vale, kız ve papaz için düşünebilirsiniz. -) - -$(P -Daha başka çözümler de bulunabilir. Örneğin kağıt değerini de bir $(C enum) olarak tanımlayabilirsiniz. -) - -$(P -Bu yapının nesnelerinin nasıl kurulacakları, üyeler için seçtiğiniz türlere bağlı olacak. Örneğin eğer her iki üyeyi de $(C dchar) türünde tasarladıysanız, şöyle kurulabilirler: -) - ---- - auto kağıt = OyunKağıdı('♣', '2'); ---- - -) - -$(PROBLEM -Bir $(C OyunKağıdı) nesnesi alan ve o nesneyi çıkışa yazdıran $(C oyunKağıdıYazdır) isminde bir işlev tanımlayın: - ---- -struct OyunKağıdı { - // ... burasını siz yazın ... -} - -void oyunKağıdıYazdır(in OyunKağıdı kağıt) { - // ... burasını siz yazın ... -} - -void main() { - auto kağıt = OyunKağıdı(/* ... */); - oyunKağıdıYazdır(kağıt); -} ---- - -$(P -Örneğin sinek ikiliyi çıktıya şu şekilde yazdırsın: -) - -$(SHELL -♣2 -) - -$(P -Kupa asını da şu şekilde: -) - -$(SHELL -♡A -) - -$(P -O işlevin içeriği, doğal olarak yapıyı nasıl tasarladığınıza bağlı olacaktır. -) - -) - -$(PROBLEM - -İsmi $(C yeniDeste) olan bir işlev yazın. Elli iki farklı oyun kağıdını temsil eden $(C OyunKağıdı)[] türünde bir dilim döndürsün: - ---- -OyunKağıdı[] yeniDeste() -out (sonuç) { - assert(sonuç.length == 52); - -} body { - // ... burasını siz yazın ... -} ---- - -$(P -Bu işlev örneğin şöyle kullanılabilsin: -) - ---- - OyunKağıdı[] deste = yeniDeste(); - - foreach (kağıt; deste) { - oyunKağıdıYazdır(kağıt); - write(' '); - } - - writeln(); ---- - -$(P -Eğer destedeki her kağıt gerçekten farklı olmuşsa, şuna benzer bir çıktı olmalıdır: -) - -$(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 -Desteyi karıştıran bir işlev yazın. $(LINK2 http://dlang.org/phobos/std_random.html, $(C std.random) modülünde) tanımlı olan $(C uniform) işlevini kullanarak rasgele seçtiği iki kağıdın yerini değiştirsin. Bu işlemi kaç kere tekrarlayacağını da parametre olarak alsın: - ---- -void karıştır(OyunKağıdı[] deste, in int değişTokuşAdedi) { - // ... burasını siz yazın -} ---- - -$(P -Şu şekilde çağrılabilsin: -) - ---- - OyunKağıdı[] deste = yeniDeste(); - karıştır(deste, 1); - - foreach (kağıt; deste) { - oyunKağıdıYazdır(kağıt); - write(' '); - } - - writeln(); ---- - -$(P -$(C değişTokuşAdedi) ile belirtilen değer kadar değiş tokuş işlemi gerçekleştirsin. Örneğin 1 ile çağrıldığında şuna benzer bir çıktı versin: -) - -$(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 -$(C değişTokuşAdedi) olarak daha yüksek bir değer verdiğinizde deste iyice karışmış olmalıdır: -) - ---- - karıştır(deste, $(HILITE 100)); ---- - -$(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 Not: Deste karıştırmak için daha etkin bir yöntemi çözüm programında açıklıyorum.) -) - -) - -) - -Macros: - SUBTITLE=Yapılar - - DESCRIPTION=D dilinin kullanıcı türleri tanımlaya yarayan olanağı 'struct' - - KEYWORDS=d programlama dili ders bölümler öğrenmek tutorial yapı yapılar struct kullanıcı türleri - -SOZLER= -$(donus_degeri) -$(ilklemek) -$(isimli_ilklendirici) -$(is_parcacigi) -$(kapsam) -$(kurma) -$(nesne) -$(referans) -$(sabit) -$(uye) -$(varsayilan) -$(yan_etki) -$(yapi) diff --git a/ddili/src/ders/d/yasam_surecleri.d b/ddili/src/ders/d/yasam_surecleri.d deleted file mode 100644 index 150ce2b..0000000 --- a/ddili/src/ders/d/yasam_surecleri.d +++ /dev/null @@ -1,369 +0,0 @@ -Ddoc - -$(DERS_BOLUMU $(IX yaşam süreci) Yaşam Süreçleri ve Temel İşlemler) - -$(P -Çok yakında yapı ve sınıfları anlatmaya başlayacağım. Yapıların kullanıcı türlerinin temeli olduklarını göreceğiz. Onlar sayesinde temel türleri ve başka yapıları bir araya getirerek yeni türler oluşturabileceğiz. -) - -$(P -Daha sonra D'nin nesneye dayalı programlama olanaklarının temelini oluşturan sınıfları tanıyacağız. Sınıflar başka türleri bir araya getirmenin yanında o türlerle ilgili özel işlemleri de belirlememizi sağlayacaklar. -) - -$(P -O konulara geçmeden önce şimdiye kadar hiç üzerinde durmadan kullandığımız bazı temel kavramları ve temel işlemleri açıklamam gerekiyor. Bu kavramlar ileride yapı ve sınıf tasarımları sırasında yararlı olacak. -) - -$(P -Şimdiye kadar kavramları temsil eden veri yapılarına $(I değişken) adını verdik. Bir kaç noktada da yapı ve sınıf türünden olan değişkenlere özel olarak $(I nesne) dedik. Ben bu bölümde bunların hepsine birden genel olarak $(I değişken) diyeceğim. Herhangi bir türden olan herhangi bir veri yapısı en azından bu bölümde $(I değişken) adını alacak. -) - -$(P -Bu bölümde yalnızca şimdiye kadar gördüğümüz temel türleri, dizileri, ve eşleme tablolarını kullanacağım; siz bu kavramların bütün türler için geçerli olduklarını aklınızda tutun. -) - -$(H5 $(IX değişken, yaşam süreci) Değişkenlerin yaşam süreçleri) - -$(P -Bir değişkenin tanımlanması ile başlayan ve $(I geçerliliğinin bitmesine) kadar geçen süreye o değişkenin $(I yaşam süreci) denir. -) - -$(P -Geçerliliğin bitmesi kavramını $(LINK2 /ders/d/isim_alani.html, İsim Alanı bölümünde) $(I değişkenin tanımlandığı kapsamdan çıkılması) olarak tanımlamıştım. -) - -$(P -O konuyu hatırlamak için şu örneğe bakalım: -) - ---- -void hızDenemesi() { - int hız; // tek değişken ... - - foreach (i; 0 .. 10) { - hız = 100 + i; // ... 10 farklı değer alır - // ... - } -} // ← yaşamı burada sonlanır ---- - -$(P -O koddaki $(C hız) değişkeninin yaşam süreci $(C hızDenemesi) işlevinden çıkıldığında sona erer. Orada 100 ile 109 arasında 10 değişik değer alan tek değişken vardır. -) - -$(P -Aşağıdaki kodda ise durum yaşam süreçleri açısından çok farklıdır: -) - ---- -void hızDenemesi() { - foreach (i; 0 .. 10) { - int hız = 100 + i; // 10 farklı değişken vardır - // ... - } // ← yaşamları burada sonlanır -} ---- - -$(P -O kodda her birisi tek değer alan 10 farklı değişken vardır: döngünün her tekrarında $(C hız) isminde yeni bir değişken yaşamaya başlar; yaşamı, döngünün kapama parantezinde sona erer. -) - -$(H5 $(IX parametre, yaşam süreci) Parametrelerin yaşam süreçleri) - -$(P -$(LINK2 /ders/d/islev_parametreleri.html, İşlev Parametreleri bölümünde) gördüğümüz parametre türlerine bir de yaşam süreçleri açısından bakalım: -) - -$(P -$(IX ref, parametre yaşam süreci) $(C ref): Parametre aslında işlev çağrıldığında kullanılan değişkenin takma ismidir. Parametrenin asıl değişkenin yaşam süreci üzerinde etkisi yoktur. -) - -$(P -$(IX in, parametre yaşam süreci) $(C in): $(I Değer türündeki) bir parametrenin yaşamı işleve girildiği an başlar ve işlevden çıkıldığı an sona erer. $(I Referans türündeki) bir parametrenin yaşamı ise $(C ref)'te olduğu gibidir. -) - -$(P -$(IX out, parametre yaşam süreci) $(C out): Parametre aslında işlev çağrıldığında kullanılan değişkenin takma ismidir. $(C ref)'ten farklı olarak, işleve girildiğinde asıl değişkene önce otomatik olarak türünün $(C .init) değeri atanır. Bu değer daha sonra işlev içinde değiştirilebilir. -) - -$(P -$(IX lazy, parametre yaşam süreci) $(C lazy): Parametre tembel olarak işletildiğinden yaşamı kullanıldığı an başlar ve o an sona erer. -) - -$(P -Bu dört parametre türünü kullanan ve yaşam süreçlerini açıklayan bir örnek şöyle yazılabilir: -) - ---- -void main() { - int main_in; // değeri işleve kopyalanır - - int main_ref; // işleve kendisi olarak ve kendi - // değeriyle gönderilir - - int main_out; // işleve kendisi olarak gönderilir; - // işleve girildiği an değeri sıfırlanır - - işlev(main_in, main_ref, main_out, birHesap()); -} - -void işlev( - in int p_in, // yaşamı main_in'in kopyası olarak - // işleve girilirken başlar ve işlevden - // çıkılırken sonlanır - - ref int p_ref, // main_ref'in takma ismidir - - out int p_out, // main_out'un takma ismidir; ref'ten - // farklı olarak, işleve girildiğinde - // değeri önce int.init olarak atanır - - lazy int p_lazy) { // yaşamı işlev içinde kullanıldığı an - // başlar ve eğer kullanımı bitmişse - // hemen o an sonlanır; değeri için, - // her kullanıldığı an 'birHesap' - // işlevi çağrılır - // ... -} - -int birHesap() { - int sonuç; - // ... - return sonuç; -} ---- - -$(H5 Temel işlemler) - -$(P -Hangi türden olursa olsun, bir değişkenin yaşamı boyunca etkili olan üç temel işlem vardır: -) - -$(UL -$(LI $(B Kurma): Yaşamın başlangıcı.) -$(LI $(B Sonlandırma): Yaşamın sonu.) -$(LI $(B Atama): Değerin değişmesi.) -) - -$(P -Değişkenlerin yaşam süreçleri kurma işlemiyle başlar ve sonlandırma işlemiyle sona erer. Bu süreç boyunca değişkene yeni değerler atanabilir. -) - -$(H6 $(IX ilkleme) $(IX kurma) Kurma) - -$(P -Her değişken, kullanılmadan önce kurulmak zorundadır. Burada "kurma" sözcüğünü "hazırlamak, inşa etmek" anlamlarında kullanıyorum. Kurma iki alt adımdan oluşur: -) - -$(OL -$(LI $(B Yer ayrılması): Değişkenin yaşayacağı yer belirlenir.) -$(LI $(B İlk değerinin verilmesi): O adrese ilk değeri yerleştirilir.) -) - -$(P -Her değişken bilgisayarın belleğinde kendisine ayrılan bir yerde yaşar. Derleyicinin istediğimiz işleri yaptırmak için mikro işlemcinin anladığı dilde kodlar ürettiğini biliyorsunuz. Derleyicinin ürettiği kodların bir bölümünün görevi, tanımlanan değişkenler için bellekten yer ayırmaktır. -) - -$(P -Örneğin, hızı temsil eden şöyle bir değişken olsun: -) - ---- - int hız = 123; ---- - -$(P -Daha önce $(LINK2 /ders/d/deger_referans.html, Değerler ve Referanslar bölümünde) gördüğümüz gibi, o değişkenin belleğin bir noktasında yaşadığını düşünebiliriz: -) - -$(MONO - ──┬─────┬─────┬─────┬── - │ │ 123 │ │ - ──┴─────┴─────┴─────┴── -) - -$(P -Her değişkenin bellekte bulunduğu yere o değişkenin $(I adresi) denir. Bir anlamda o değişken o adreste yaşamaktadır. Programda bir değişkenin değerini değiştirdiğimizde, değişkenin yeni değeri aynı yere yerleştirilir: -) - ---- - ++hız; ---- - -$(P -Aynı adresteki değer bir artar: -) - -$(MONO - ──┬─────┬─────┬─────┬── - │ │ 124 │ │ - ──┴─────┴─────┴─────┴── -) - -$(P -Kurma, değişkenin yaşamı başladığı anda gerçekleştirilir çünkü değişkeni kullanıma hazırlayan işlemleri içerir. Değişkenin herhangi bir biçimde kullanılabilmesi için kurulmuş olması önemlidir. -) - -$(P -Değişkenler üç farklı şekilde kurulabilirler: -) - -$(UL -$(LI $(B Varsayılan şekilde): Programcı değer belirtmemişse) -$(LI $(B Kopyalanarak): Başka bir değişkenin değeriyle) -$(LI $(B Belirli bir değerle): Programcının belirlediği değerle) -) - -$(P -Hiçbir değer kullanılmadan kurulduğunda değişkenin değeri o türün $(I varsayılan) değeridir. Varsayılan değer, her türün $(C .init) niteliğidir: -) - ---- - int hız; ---- - -$(P -O durumda $(C hız)'ın değeri $(C int.init)'tir (yani 0). Varsayılan değerle kurulmuş olan bir değişkenin programda sonradan başka değerler alacağını düşünebiliriz. -) - ---- - File dosya; ---- - -$(P -$(LINK2 /ders/d/dosyalar.html, Dosyalar bölümünde) gördüğümüz $(C std.stdio.File) türünden olan yukarıdaki $(C dosya) nesnesi dosya sisteminin hiçbir dosyasına bağlı olmayan bir $(C File) yapısı nesnesidir. Onun dosya sisteminin hangi dosyasına erişmek için kullanılacağının daha sonradan belirleneceğini düşünebiliriz; varsayılan şekilde kurulmuş olduğu için henüz kullanılamaz. -) - -$(P -Değişken bazen başka bir değişkenin değeri $(I kopyalanarak) kurulur: -) - ---- - int hız = başkaHız; ---- - -$(P -O durumda $(C hız)'ın değeri $(C başkaHız)'ın değerinden kopyalanır ve $(C hız) yaşamına o değerle başlar. Sınıf değişkenlerinde ise durum farklıdır: -) - ---- - auto sınıfDeğişkeni = başkaSınıfDeğişkeni; ---- - -$(P -$(C sınıfDeğişkeni) de yaşamına $(C başkaSınıfDeğişkeni)'nin kopyası olarak başlar. -) - -$(P -Aralarındaki önemli ayrım, $(C hız) ile $(C başkaHız)'ın birbirlerinden farklı iki değer olmalarına karşın $(C sınıfDeğişkeni) ile $(C başkaSınıfDeğişkeni)'nin aynı nesneye erişim sağlamalarıdır. Bu çok önemli ayrım $(I değer türleri) ile $(I referans türleri) arasındaki farktan ileri gelir. -) - -$(P -Son olarak, değişkenler belirli değerlerle veya özel şekillerde kurulabilirler: -) - ---- - int hız = birHesabınSonucu(); ---- - -$(P -Yukarıdaki $(C hız)'ın ilk değeri programın çalışması sırasındaki bir hesabın değeri olarak belirlenmektedir. -) - ---- - auto sınıfDeğişkeni = new BirSınıf; ---- - -$(P -Yukarıdaki $(C sınıfDeğişkeni), yaşamına $(C new) ile kurulan nesneye erişim sağlayacak şekilde başlamaktadır. -) - -$(H6 $(IX sonlandırma) Sonlandırma) - -$(P -Değişkenin yaşamının sona ermesi sırasında yapılan işlemlere sonlandırma denir. Kurma gibi sonlandırma da iki adımdan oluşur: -) - -$(OL -$(LI $(B Son işlemler): Değişkenin yapması gereken son işlemler işletilir) -$(LI $(B Belleğin geri verilmesi): Değişkenin yaşadığı yer geri verilir) -) - -$(P -Temel türlerin çoğunda sonlandırma sırasında özel işlemler gerekmez. Örneğin $(C int) türünden bir değişkenin bellekte yaşamakta olduğu yere sıfır gibi özel bir değer atanmaz. Program o adresin artık boş olduğunun hesabını tutar ve orayı daha sonra başka değişkenler için kullanır. -) - -$(P -Öte yandan, bazı türlerden olan değişkenlerin yaşamlarının sonlanması sırasında özel işlemler gerekebilir. Örneğin bir $(C File) nesnesi, eğer varsa, ara belleğinde tutmakta olduğu karakterleri diske yazmak zorundadır. Ek olarak, dosyayla işinin bittiğini dosya sistemine bildirmek için de dosyayı kapatmak zorundadır. Bu işlemler $(C File)'ın sonlandırma işlemleridir. -) - -$(P -Dizilerde durum biraz daha üst düzeydedir: o dizinin erişim sağlamakta olduğu bütün elemanlar da sonlanırlar. Eğer dizinin elemanları temel türlerdense özel bir sonlanma işlemi gerekmez. Ama eğer dizinin elemanları sonlanma gerektiren bir yapı veya sınıf türündense, o türün sonlandırma işlemleri her eleman için uygulanır. -) - -$(P -Sonlandırma eşleme tablolarında da dizilerdeki gibidir. Ek olarak, eşleme tablosunun sahip olduğu indeks değişkenleri de sonlandırılırlar. Eğer indeks türü olarak bir yapı veya sınıf türü kullanılmışsa, her indeks nesnesi için o türün gerektirdiği sonlandırma işlemleri uygulanır. -) - -$(P $(B Çöp toplayıcı): D $(I çöp toplayıcılı) bir dildir. Bu tür dillerde sonlandırma işlemleri programcı tarafından açıkça yapılmak zorunda değildir. Yaşamı sona eren bir değişkenin sonlandırılması otomatik olarak çöp toplayıcı denen düzenek tarafından halledilir. Çöp toplayıcının ayrıntılarını ilerideki bir bölümde göreceğiz. -) - -$(P -Değişkenler iki şekilde sonlandırılabilirler: -) - -$(UL -$(LI $(B Hemen): Sonlandırma işlemleri hemen işletilir) -$(LI $(B Sonra): Çöp toplayıcı tarafından ilerideki bir zamanda) -) - -$(P -Bir değişkenin bunlardan hangi şekilde sonlandırılacağı öncelikle kendi türüne bağlıdır. Temel türlerin hemen sonlandırıldıklarını düşünebilirsiniz çünkü zaten sonlandırma için özel işlemleri yoktur. Bazı türlerin değişkenlerinin son işlemleri ise çöp toplayıcı tarafından daha sonraki bir zamanda işletilebilir. -) - -$(H6 $(IX atama) Atama) - -$(P -Bir değişkenin yaşamı boyunca karşılaştığı diğer önemli işlem atamadır. -) - -$(P -Temel türlerde atama işlemi yalnızca değişkenin değerinin değiştirilmesi olarak görülebilir. Yukarıdaki bellek gösteriminde olduğu gibi, değişken örneğin 123 olan bir değer yerine artık 124 değerine sahip olabilir. -) - -$(P -Daha genel olarak aslında atama işlemi de iki adımdan oluşur: -) - -$(OL -$(LI $(B Eski değerin sonlandırılması): Eğer varsa, sonlandırma işlemleri ya hemen ya da çöp toplayıcı tarafından daha sonra işletilir) -$(LI $(B Yeni değerin verilmesi): Eski değerin yerine yeni değer atanır) -) - -$(P -Bu iki adım sonlandırma işlemleri bulunmadığı için temel türlerde önemli değildir. Ama sonlandırma işlemleri bulunan türlerde atamanın böyle iki adımdan oluştuğunu akılda tutmakta yarar vardır: atama aslında bir sonlandırma ve bir yeni değer verme işlemidir. -) - - -Macros: - SUBTITLE=Yaşam Süreçleri ve Temel İşlemler - - DESCRIPTION=D dilinde türlerin temel işlemleri olan kurma, sonlandırma, ve atama işlemlerinin tanıtılması; ve değişkenlerin yaşam süreçlerinin tanımlanması - - KEYWORDS=d programlama dili ders dersler öğrenmek tutorial nesne temel işlemler kurma kurucu sonlandırma bozucu kopyalama kopyalayıcı atama işleç - -SOZLER= -$(adres) -$(atama) -$(cop_toplayici) -$(deger_turu) -$(degisken) -$(kopyalama) -$(kurma) -$(nesne) -$(referans_turu) -$(sinif) -$(sonlandirma) -$(varsayilan) -$(yapi) -$(yasam_sureci) diff --git a/ddili/src/ders/gtkd/Makefile.in b/ddili/src/ders/gtkd/Makefile.in deleted file mode 100644 index 63ea07b..0000000 --- a/ddili/src/ders/gtkd/Makefile.in +++ /dev/null @@ -1,16 +0,0 @@ -DERS_SON_D=devami_gelecek.d - -DERS_D_BOLUMLER= \ - pdf_indir.d \ - tanitim.d \ - merhaba_gtkd.d \ - pencere_kurma_islemleri.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - -include Makefile.ders.in -$(eval $(call derse_ozel,gtkd,Gtkd_ile_Programlama,turkish)) diff --git a/ddili/src/ders/gtkd/breadcrumbs.ddoc b/ddili/src/ders/gtkd/breadcrumbs.ddoc deleted file mode 100644 index b24c340..0000000 --- a/ddili/src/ders/gtkd/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > GtkD - -BREADCRUMBS_FULL=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > $(LINK2 /ders/gtkd/index.html, GtkD) diff --git a/ddili/src/ders/gtkd/code/BENIOKU b/ddili/src/ders/gtkd/code/BENIOKU deleted file mode 100644 index 1d6dd5c..0000000 --- a/ddili/src/ders/gtkd/code/BENIOKU +++ /dev/null @@ -1 +0,0 @@ -Bu klasör bu kitaptaki örnek programların bazılarını içerir. diff --git a/ddili/src/ders/gtkd/copyright.d b/ddili/src/ders/gtkd/copyright.d deleted file mode 100644 index fa5b06a..0000000 --- a/ddili/src/ders/gtkd/copyright.d +++ /dev/null @@ -1,20 +0,0 @@ -Ddoc - -$(NOT___ Hack: pdf çıktısında kapağa taşma şansı azalsın diye) -$(BR) -$(BR) - -$(H5 Copyright © 2009-2015 Mengü Kağan ve Can Alpay Çiftçi) - -$(HR) - -$(P -Creative Commons License
    Ddili.org icerigi by Ali Cehreli is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
    Based on a work at digitalmars.com.
    Permissions beyond the scope of this license may be available at http://ddili.org/copyright.html. -) - -Macros: - SUBTITLE=Kopyalama Hakları - - DESCRIPTION=Site içeriği kopyalama hakları - - KEYWORDS=copyright hak lisans diff --git a/ddili/src/ders/gtkd/derse_ozel.ddoc b/ddili/src/ders/gtkd/derse_ozel.ddoc deleted file mode 100644 index 06438fc..0000000 --- a/ddili/src/ders/gtkd/derse_ozel.ddoc +++ /dev/null @@ -1,12 +0,0 @@ -MAIN_TITLE=GtkD ile Görsel Programlama -SUB_MAIN_TITLE_DERSE_OZEL= -SUB_AUTHOR=Mengü Kağan ve Can Alpay Çiftçi -LANG=tr -LANGUAGE=turkish - -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/gtkd/rss.xml, GtkD RSS GtkD Dersleri RSS Beslemesi) -$(BR) -$(LINK2 /ders/gtkd/Gtkd_ile_Programlama.pdf, -PDF Olarak $(IMG pdficon_small.gif)) - -MINI_SOZLUK= diff --git a/ddili/src/ders/gtkd/devami_gelecek.d b/ddili/src/ders/gtkd/devami_gelecek.d deleted file mode 100644 index 3b26089..0000000 --- a/ddili/src/ders/gtkd/devami_gelecek.d +++ /dev/null @@ -1,24 +0,0 @@ -Ddoc - -$(B) -$(H3 Şimdilik bu kadar!) - -$(P -Ama devamı gelecek... Eklenen yeni derslerden haberdar olmak için: -) - -$(UL -$(LI $(LINK2 /ders/gtkd/rss.xml, GtkD RSS) beslemesini kullanabilirsiniz -) -$(LI $(LINK2 http://ddili.org/forum/forum/4, Ddili Forum'un Duyurular Bölümüne) arada bir göz atabilirsiniz -) -) - -Macros: - SUBTITLE=Devamı Gelecek - - DESCRIPTION= - - KEYWORDS=d programlama dili gtkd görsel programlama dersleri öğrenmek tutorial - -MINI_SOZLUK= diff --git a/ddili/src/ders/gtkd/frontispiece.d b/ddili/src/ders/gtkd/frontispiece.d deleted file mode 100644 index 075eb86..0000000 --- a/ddili/src/ders/gtkd/frontispiece.d +++ /dev/null @@ -1 +0,0 @@ -Ddoc diff --git a/ddili/src/ders/gtkd/halftitle.html b/ddili/src/ders/gtkd/halftitle.html deleted file mode 100644 index 18b4885..0000000 --- a/ddili/src/ders/gtkd/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -GtkD -

    - -

    -Mengü Kağan ve Can Alpay Çiftçi -

    - -
    diff --git a/ddili/src/ders/gtkd/index.d b/ddili/src/ders/gtkd/index.d deleted file mode 100644 index 51ed0c6..0000000 --- a/ddili/src/ders/gtkd/index.d +++ /dev/null @@ -1,25 +0,0 @@ -Ddoc - -$(H5 GtkD ile Görsel Programlama) - -$(UL -$(LI $(LINK2 /ders/gtkd/ix.html, Kitap Dizini)) - -$(LI $(LINK2 /ders/gtkd/tanitim.html, Tanıtım)) - -$(LI $(LINK2 /ders/gtkd/merhaba_gtkd.html, Merhaba GtkD)) - -$(LI $(LINK2 /ders/gtkd/pencere_kurma_islemleri.html, Pencere Kurma İşlemleri)) -) -Macros: - SUBTITLE=GtkD ile Görsel Programlama - - DESCRIPTION=Gtk kütüphanesini kullanarak D programları yazmayı sağlayan GtkD ile görsel programlama - - KEYWORDS=d programlama dili ile görsel programlama gtk gtkd öğrenmek tutorial - - BREADCRUMBS=$(BREADCRUMBS_INDEX) - -SOZLER= - -MINI_SOZLUK= diff --git a/ddili/src/ders/gtkd/index_section_head.html b/ddili/src/ders/gtkd/index_section_head.html deleted file mode 100644 index 1a79845..0000000 --- a/ddili/src/ders/gtkd/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Dizin

    diff --git a/ddili/src/ders/gtkd/index_section_tail.html b/ddili/src/ders/gtkd/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/gtkd/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/gtkd/ix.d b/ddili/src/ders/gtkd/ix.d deleted file mode 100644 index 3e8bbc3..0000000 --- a/ddili/src/ders/gtkd/ix.d +++ /dev/null @@ -1,14 +0,0 @@ -Ddoc - -$(H4 Dizin) - -$(DIV_CLASS web_index_section, -$(INDEX_ENTRIES) -) - -Macros: - SUBTITLE=Dizin Bölümü - - DESCRIPTION=GtkD ile Görsel Programlama kitabının dizin bölümü - - KEYWORDS=dizin diff --git a/ddili/src/ders/gtkd/merhaba_gtkd.d b/ddili/src/ders/gtkd/merhaba_gtkd.d deleted file mode 100644 index 8876e30..0000000 --- a/ddili/src/ders/gtkd/merhaba_gtkd.d +++ /dev/null @@ -1,117 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Merhaba GtkD) - -$(P -Bu dersimizde gtkD ile pencere oluşturabilmek için gerekli modülleri öğreneceğiz. -) - -$(P -En basit pencere, içi boş penceredir. İlk önce içi boş bir pencere nasıl oluşturulur onu öğreneceğiz. -Sizce bir pencere oluşturmak için neler yapmak gerekir? Hızlıca düşünürsek: -) - -$(OL - -$(LI -Gtk işlevlerini kullanmadan önce bir işlev çağırmamız gerekiyorsa o işlevi çağırmalıyız. (Sizce gerek var mıdır $(GULEN ) ) -) - -$(LI -Pencere oluştur demeliyiz. -) -$(LI -Pencereyi göster demeliyiz. -) -$(LI -Bu yaptıklarımızı çalıştır demeliyiz. -) -) - -$(P -Bunları sağlayan kod parçacığı aşağıda : -) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("deneme"); - pencere.show(); - Main.run; - - return 0; -} ---- -$(P -Örneğimizde neler yaptık bakalım: -) - -$(P -$(IX gtk.Window) -$(IX gtk.Main) -İlk olarak iki tane modülü programımıza dahil ettik. Bunlar $(C gtk.Window) ve $(C gtk.Main). -Sonra D'nin ana işlevi olan main işlevini tanımladık. Ama burada main işlevini tanımlarken $(BLUE string[]) türünde bir argüman tanımladık. Burada argümanın hangi türden tanımlandığı önemlidir.) -$(P Şimdi sıra tanımlanan argümanı kullanmaya geldi. Dersimizin ilk bölümlerinde pencereleri oluşturmadan önce; daha doğrusu gtk'nin işlevlerini kullanmadan önce başka bir işlev çağırmamız gerektiğinden söz etmiştik. Bu işlevi her gtk uygulamasında çağırmamız gerekir. Şimdi bu işlev hangi modülde bulunur, adı nedir, parametre alır mı, alırsa hangi türden parametre alır gibi soruların yanıtlarını alacağız.) -$(P -$(IX init) -Tanıyacağımız ilk işlev, $(HILITE Main.init). Bu işlev $(C gtk.Main) modülünde bulunuyor. Parametre olarak da D uygulamasının ana işlevini tanımlarken kullandığımız argümanı kullanıyor. Ve parametre türü olarak da sadece $(BLUE string[]) türü kabul ediyor. Kısaca: ) - ---- -void Main.init (string[] args); ---- - -$(P Gtk işlevlerini kullanmak için gtk.Main modülünde bulunan $(HILITE Main.init) işlevini kullandıktan sonra sıra penceremizi oluşturmaya geldi. Pencereyi oluşturabilmek için ilk önce $(C gtk.Window) modülündeki $(I Window) sınıfını kuruyoruz. Bunun için şu satırı yazmıştık : ) - ---- - auto pencere = new Window("deneme"); ---- - -$(P Koddan anlaşıldığı üzere bu sınıfın kurucusu tanımlı ve parametre türü olarak $(BLUE string) değer alıyor. Kurucudaki parametre, oluşturacağımız pencerenin başlığını tanımlar.) -$(P -$(IX show) -Artık uygulamamızın sonuna doğru geldik. Bundan sonraki işlev yine $(C gtk.Window) modülünde tanımlı. Daha önce pencere adı ile kurduğumuz sınıfın $(HILITE show) işlevine erişiyoruz. Show sözcüğünün Türkçe anlamı görüntüle, göster demektir. Biz de oluşturduğumuz pencereyi göstermek için $(B pencere.show;) kodunu yazıyoruz.) -$(P -$(IX run) -Ve sonunda bu gtk uygulamasını çalıştırmak için gtkD ile ilişkili olan son kod satırımızı yazıyoruz. Bu kod satırında gtk uygulamasını çalıştır anlamına gelen $(HILITE Main.run;) kodunu yazıyoruz. Bu kod parçası yine $(C gtk.Main) modülünde bulunuyor. -) - - - -$(H5 $(IX derleme) $(IX dmd) Peki Bu GtkD Kodlarını Nasıl Çalıştıracağız? ) - - -$(P -Bu örneği merhaba.d adında çalışma alanınıza kaydettikten sonra şu şekilde -derleyebilirsiniz: -) - -$(SHELL_SMALL -dmd merhaba.d -I/gtkD/yolu/src -L-ldl -L-L/gtkD/yolu/src -L-lgtkd -) - -$(P -Yukarıda dikkat etmeniz gereken kısım “/gtkD/yolu” kısmıdır ki onu gtkD'yi nereye -kurduysanız, o yol (path) şeklinde değiştirmeniz gerekmektedir. Örneğin ben -~/projects/gtkD klasörü altında kurduğum için şu şekilde derledim: -) - -$(SHELL_SMALL -dmd merhaba.d -I~/projects/gtkD/src -L-ldl -L-L~/projects/gtkD/src -L-lgtkd -) - -$(P -merhaba.d dosyasını derledikten sonra da terminalde ./merhaba yazarak pencerenizi -görebilirsiniz. -) - - -Macros: - SUBTITLE=Merhaba gtkD - - DESCRIPTION=İlk GtkD programınız - - KEYWORDS=d programlama dili ile görsel programlama gtk gtkd öğrenmek tutorial merhaba dünya - diff --git a/ddili/src/ders/gtkd/pdf.derse_ozel.css b/ddili/src/ders/gtkd/pdf.derse_ozel.css deleted file mode 100644 index aeeb933..0000000 --- a/ddili/src/ders/gtkd/pdf.derse_ozel.css +++ /dev/null @@ -1,12 +0,0 @@ -a.xref:after { - content: " (sayfa " target-counter(attr(href, url), page) ")"; -} - -body -{ - counter-reset: h4 -1; -} - -ul.toc a.index { - content: "Dizin"; -} diff --git a/ddili/src/ders/gtkd/pdf_cozum_head.html b/ddili/src/ders/gtkd/pdf_cozum_head.html deleted file mode 100644 index 278be03..0000000 --- a/ddili/src/ders/gtkd/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Problem Çözümleri

    diff --git a/ddili/src/ders/gtkd/pdf_cozum_tail.html b/ddili/src/ders/gtkd/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/gtkd/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/gtkd/pdf_html_head.html b/ddili/src/ders/gtkd/pdf_html_head.html deleted file mode 100644 index 5e4eaf8..0000000 --- a/ddili/src/ders/gtkd/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - GtkD - - - diff --git a/ddili/src/ders/gtkd/pdf_html_tail.html b/ddili/src/ders/gtkd/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/gtkd/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/gtkd/pdf_indir.d b/ddili/src/ders/gtkd/pdf_indir.d deleted file mode 100644 index bb4e954..0000000 --- a/ddili/src/ders/gtkd/pdf_indir.d +++ /dev/null @@ -1,20 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Kitabın PDF Dosyası) - -$(P -Bu kitabın en son sürümünü bir PDF dosyası olarak şu adresten indirebilirsiniz: -) - -$(P -$(IMG pdficon_small.gif) $(LINK http://ddili.org/ders/gtkd/Gtkd_ile_Programlama.pdf) -) - -Macros: - SUBTITLE=GtkD ile Görsel Programlama PDF Dosyası - - DESCRIPTION=GtkD ile Görsel Programlama PDF Dosyası - - KEYWORDS= - - MINI_SOZLUK= diff --git a/ddili/src/ders/gtkd/pdf_sozluk_head.html b/ddili/src/ders/gtkd/pdf_sozluk_head.html deleted file mode 100644 index f5edc52..0000000 --- a/ddili/src/ders/gtkd/pdf_sozluk_head.html +++ /dev/null @@ -1,3 +0,0 @@ - -

    Sözlük

    - diff --git a/ddili/src/ders/gtkd/pencere_kurma_islemleri.d b/ddili/src/ders/gtkd/pencere_kurma_islemleri.d deleted file mode 100644 index 3119f71..0000000 --- a/ddili/src/ders/gtkd/pencere_kurma_islemleri.d +++ /dev/null @@ -1,252 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Pencere Kurma İşlemleri) - -$(P Daha önceki dersimizde başlığı olan boş bir pencere yapmasını öğrenmiştik. Bu dersimizde pencere kurma ile ilgili işlemler yapacağız. ) -$(P Anlatılacak bütün işlevler $(C gtk.Window) modülünde tanımlı olan $(I Window) sınıfının üye işlevleridir. ) - -$(H5 $(IX resize) Pencere Boyutunu Ayarlamak $(C resize(int en, int boy))) - -$(P gtkD ile oluşturulan pencerenin öntanımlı boyutu 200x200 piksel boyutundadır. Eğer öntanımlı piksel boyutunu değiştirmek isterseniz, $(HILITE resize) işlevini kullanabilirsiniz. $(HILITE resize) iki tane $(BLUE int) türünde parametre alır. Bunlar sırasıyla en ve boydur. ) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = - new Window("450 eninde ve 570 boyunda resize."); - pencere.resize(450,570) ; // Pencere 450 piksel eninde - // ve 570 boyundadır. - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX maximize) Pencereyi Tam Ekran Yapmak $(C maximize()) ) -$(P -Eğer pencerenin tam ekran olmasını istiyorsanız $(HILITE maximize()) işlevini kullanabilirsiniz. -) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("maximize"); - pencere.maximize(); // Pencereyi tam ekran yapar. - pencere.show(); - Main.run; - - return 0; -} ---- - -$(H5 $(IX unmaximize) Pencereyi Tam Ekrandan Çıkartmak $(C unmaximize()) ) -$(P -Eğer pencerenin tam ekran kipinden çıkmasını istiyorsanız $(HILITE unmaximize()) işlevini kullanabilirsiniz. -) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("unmaximize"); - pencere.maximize(); // Pencereyi tam ekran yapar. - pencere.unmaximize();/*Pencereyi tam ekran kipinden çıkarır - ve öntanımlı boyutuna getirir. */ - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX move) Pencereyi Ekranda Konumlandırmak $(C move(int sağa, int aşağıya))) -$(P -Eğer pencerenin ekranda konumunu ayarlamak isterseniz, $(HILITE move) işlevini kullanabilirsiniz. -$(HILITE move) işlevi 2 parametre alır, parametre türü olarak $(BLUE int) değeri alır ve birimi pikseldir. Ekranın nereye yerleşeceğini de sol üst köşeden başlayarak girilen değer kadar sağa(en), ve aşağıya(boy) taşır. -) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("move"); - pencere.move(450,570) ; // 450 piksel sağa ve 570 piksel - // aşağıya taşır. - pencere.show(); - Main.run; - return 0; -} ---- - - - -$(H5 $(IX setPosition) Pencereyi Piksel Değeri Girmeden Konumlandırmak $(C setPosition(GtkWindowPosition nereye))) -$(P -Eğer pencerenin konumunu piksel olarak değer vermeden örneğin ortala diyerek belirtmek istersek setPosition() işlevini kullanabiliriz. -) -$(P -$(C setPosition()) işlevi parametre olarak şu değerleri alır: -) - -$(P $(B GtkWindowPosition.POS_NONE:) Pencere konumu üzerinde herhangi bir değişiklik yapılmayacağını belirtir.) - -$(P $(B GtkWindowPosition.POS_CENTER: ) Pencereyi ekranın ortasına yerleştirir.) - -$(P $(B GtkWindowPosition.POS_MOUSE: ) Pencere, farenin o anda bulunduğu konumda açılır.) - -$(P $(B GtkWindowPosition.POS_CENTER_ALWAYS:) Pencerenin boyutu değişse de, pencereyi hep ekranın ortasında tutar.) - -$(P $(B GtkWindowPosition.POS_CENTER_ON_PARENT:) Eğer ikinci bir pencere varsa bu pencerenin ana pencereyi ortalamasını sağlar.) - -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("maximize"); - pencere.setPosition(GtkWindowPosition.POS_CENTER_ALWAYS); - // ekranın ortasında açılmasını sağlar - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX setIconFromFile) Pencereye Simge Eklemek $(C setIconFromFile)) -$(P -Eğer pencereye simge(icon) eklemek isterseniz $(HILITE setIconFromFile) işlevini kullanabilirsiniz. $(BLUE int) türünde değer döndürür ve $(BLUE string) türünde parametre alır. Parametre olarak ekleyeceğiniz resmin yolunu yazarsınız. -) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("deneme"); - pencere.setIconFromFile("/home/canalpay/den.png"); - // Pencereye simge koymaya yarar. - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX setOpacity) Pencereyi Saydamlaştırmak $(C setOpacity(double değer))) -$(P Pencereyi saydamlaştırmak için $(HILITE setOpacity) işlevi kullanılır. 0 ile 1 arasındaki $(BLUE double) değerler alır. Değer 1'e yaklaştıkça saydamlığı azalır.) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("Saydam Pencere Yapımı"); - pencere.setOpacity(0.9); //Pencerenin saydamlığı oldukça azdır. - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX setSizeRequest) Pencere Boyutlandırma $(C setSizeRequest(int en, int boy))) -$(P -Kullanımı resize() ile aynıdır. Tek farkı fare imleci ile pencerenin boyutu büyütülebilir ama öntanımlı boyuttan daha küçük yapılamaz. -) -$(P Ayrıca bir sonraki işlev için resize yerine bunun kullanılması gerekecektir.) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("setSizeRequest örneği"); - pencere.setSizeRequest(450, 450); - pencere.show(); - Main.run; - return 0; -} ---- -$(H5 $(IX setResizable) Boyutlandırılamayan Pencereler $(C setResizable(bool değer))) -$(P -Pencere boyutlandırılabilsin mi yoksa boyutlandırılamasın mı diye ayarlamak için $(HILITE setResizable()) işlevi kullanılır. "Resizable" sözcük anlamı "tekrar boyutlandırılabilir"dir. Parametre olarak $(BLUE bool) değer alır. Eğer parametre olarak false değeri girilirse boyutlandırılamaz. Eğer true değeri girilirse boyutlandırılabilir. -) -$(UYARI setResizable işlevini doğru kullanabilmek için boyutlandırmayı $(B setSizeRequest) işlevi ile yapmak zorundasınız.) -$(P Örnek :) ---- -import gtk.Window; -import gtk.Main; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window("Boyutlandırılamayan Pencere"); - pencere.setSizeRequest(450, 450); - pencere.setResizable(false); - //false değeri girerek boyutlandırılamayacağını belirtmiş olduk - pencere.show(); - Main.run; - return 0; -} ---- - -$(H5 $(IX X düğmesi) X Düğmesine Kapama Görevi Verme) -$(P -Eğer daha önceki örnekleri konsol üzerinde çalıştırdıysanız $(B X) düğmesine bastığınızda hala programın çalışmaya devam ettiğini görmüşsünüzdür. Programdan çıkmak için de $(I Ctrl + C) tuşuna basmış olmalısınız. Ama biz $(B X) düğmesine bastığımızda programın çıkmasını istiyoruz.) -$(P $(B X) düğmesine bastığımızda çıkmamız için şu kod parçacığını eklememiz gerekiyor.) - -$(IX addOnHide) - ---- - pencere.addOnHide( delegate void(Widget aux){ exit(0); }); ---- - -$(P Bu kod parçacığının çalışması için de $(C std.c.process) ve $(C gtk.Widget) modüllerini eklememiz gerekiyor. -) - -$(P Örnek :) - ---- -import gtk.Window; -import gtk.Main; -import gtk.Widget; -import std.c.process; - -int main(string[] args) -{ - Main.init(args); - auto pencere = new Window - ("[X] Düğmesine Bas ve Program Sonlandırılsın!"); - pencere.setSizeRequest(450, 450); - pencere.setResizable(false); - pencere.addOnHide( delegate void(Widget aux){ exit(0); }); - - pencere.show(); - Main.run; - return 0; -} ---- -Macros: - SUBTITLE=Pencere İşlemleri - - DESCRIPTION=Yapabileceğimiz Pencere İşlemlerini Tanıyalım. - KEYWORDS=d programlama dili ile görsel programlama gtk gtkd öğrenmek tutorial pencere işlemleri diff --git a/ddili/src/ders/gtkd/rss.xml b/ddili/src/ders/gtkd/rss.xml deleted file mode 100644 index 533be04..0000000 --- a/ddili/src/ders/gtkd/rss.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - GtkD ile Görsel Programlama - http://ddili.org/ders/gtkd/ - GTK kütüphanisinin D ile kullanımı sağlayan GtkD ile görsel programlamanın anlatılması - tr - D Programlama Dili Dersleri - - - 'İçindekiler' bölümü - http://ddili.org/ders/gtkd/index.html - Kitabın PDF sürümüne otomatik olarak oluşturulan bir 'İçindekiler' bölümü eklendi. - Haber - 28 Sep 2014 23:00 - - - - GtkD Kitabı artık pdf - http://ddili.org/ders/gtkd/index.html - Bütün kitabı PDF olarak bölüm başlıklarındaki bağlantısından indirebilirsiniz. - Haber - 21 Jan 2012 18:00 - - - - Pencere Kurma İşlemleri - http://ddili.org/ders/gtkd/pencere_kurma_islemleri.html - GtkD ile pencere işlemleri - 08 Apr 2010 10:00 - - - - Merhaba GtkD - http://ddili.org/ders/gtkd/merhaba_gtkd.html - GtkD ile ilk program - 08 Apr 2010 10:00 - - - - Tanıtım - http://ddili.org/ders/gtkd/tanitim.html - Tanıtım bölümü - 22 Mar 2010 00:00 - - - - diff --git a/ddili/src/ders/gtkd/tanitim.d b/ddili/src/ders/gtkd/tanitim.d deleted file mode 100644 index 618c1bc..0000000 --- a/ddili/src/ders/gtkd/tanitim.d +++ /dev/null @@ -1,42 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Tanıtım) - -$(P -Sıra GtkD'ye geldiğine göre D hakkında ciddi bir bilgiye sahipsiniz demektir. Eğer -eksikleriniz varsa mutlaka $(LINK2 /ders/d/, D derslerini) okuyun ve ardından bu derslere başlayın. -) - -$(P -İsterseniz önce GTK'nın ne olduğundan bahsedelim. GTK, C ile yazılmış, GNU LGPL -2.1 ile lisanslanmış platformdan bağımsız bir grafik kullanıcı arayüzüdür. Örneğin -GNOME masaüstü GTK ile yazılmıştır. GTK C ile yazılmış olmasına rağmen, C++ başta -olmak üzere, C#, Java, Python ve hatta PHP bağlayıcıları (binding) bulunmaktadır. -GtkD de GTK'nın D bağlayıcısıdır ve GTK gibi GNU LGPL 2.1 lisansına sahiptir. -) - -$(P -GtkD ile çalışabilmeniz için aşağıdakilere ihtiyacımız var: -) - -$(UL -$(LI DMD 2) -$(LI gtkD 1.3.2) -$(LI Bir programlama veya metin editörü (örneğin: geany, gedit)) -) - -$(P -GtkD'yi kurabilmek için ayrıntılı bilgiyi D Dili Forum'da bulabilirsiniz. -) - -$(P -Tanıtım için zannediyorum bu kadarı yeter, bir sonraki derste gtkD ile merhaba -dünya diyeceğiz. Hazırlanmayı ihmâl etmeyin. -) - -Macros: - SUBTITLE=GtkD Dersleri Tanıtımı - - DESCRIPTION=GtkD dersleri tanıtımı - - KEYWORDS=d programlama dili ile görsel programlama gtk gtkd öğrenmek tutorial tanıtım diff --git a/ddili/src/ders/gtkd/title.html b/ddili/src/ders/gtkd/title.html deleted file mode 100644 index 0797a42..0000000 --- a/ddili/src/ders/gtkd/title.html +++ /dev/null @@ -1,15 +0,0 @@ -
    - -

    -GtkD -

    - -

    -GtkD ile D programlama dilinde görsel programlama -

    - -

    -Mengü Kağan ve Can Alpay Çiftçi -

    - -
    diff --git a/ddili/src/ders/gtkd/toc_head.html b/ddili/src/ders/gtkd/toc_head.html deleted file mode 100644 index e6853b1..0000000 --- a/ddili/src/ders/gtkd/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    İçindekiler

    diff --git a/ddili/src/ders/gtkd/toc_tail.html b/ddili/src/ders/gtkd/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/gtkd/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/index.d b/ddili/src/ders/index.d deleted file mode 100644 index 3c0aa18..0000000 --- a/ddili/src/ders/index.d +++ /dev/null @@ -1,19 +0,0 @@ -Ddoc - -$(H5 Dersler) - -$(UL -$(LI $(LINK2 /ders/d/index.html, D Programlama Dili), $(I Ali Çehreli)) -$(LI $(LINK2 /ders/gtkd/index.html, GtkD ile Görsel Programlama), $(I Mengü Kağan ve Can Alpay Çiftçi)) -$(LI $(LINK2 /ders/sdl/index.html, SDL ile Oyun Programlama), $(I Faruk Erdem Öncel)) -$(LI $(LINK2 /ders/d.en/index.html, Programming in D), $(I Ali Çehreli)) -) - -Macros: - SUBTITLE=D Dersleri - - DESCRIPTION=D programlama dili dersleri - - KEYWORDS=d programlama dili d2 dersleri öğrenmek tutorial - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/ders/sdl/Makefile.in b/ddili/src/ders/sdl/Makefile.in deleted file mode 100644 index 531ab40..0000000 --- a/ddili/src/ders/sdl/Makefile.in +++ /dev/null @@ -1,15 +0,0 @@ -DERS_SON_D=devami_gelecek.d - -DERS_D_BOLUMLER= \ - pdf_indir.d \ - hosgeldiniz.d \ - hareket.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - -include Makefile.ders.in -$(eval $(call derse_ozel,sdl,SDL_ile_Oyun_Programlama,turkish)) diff --git a/ddili/src/ders/sdl/breadcrumbs.ddoc b/ddili/src/ders/sdl/breadcrumbs.ddoc deleted file mode 100644 index 84dd77c..0000000 --- a/ddili/src/ders/sdl/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > SDL - -BREADCRUMBS_FULL=$(LINK2 /index.html, Ana Sayfa) > $(LINK2 /ders/index.html, Dersler) > $(LINK2 /ders/sdl/index.html, SDL) diff --git a/ddili/src/ders/sdl/code/BENIOKU b/ddili/src/ders/sdl/code/BENIOKU deleted file mode 100644 index 1d6dd5c..0000000 --- a/ddili/src/ders/sdl/code/BENIOKU +++ /dev/null @@ -1 +0,0 @@ -Bu klasör bu kitaptaki örnek programların bazılarını içerir. diff --git a/ddili/src/ders/sdl/copyright.d b/ddili/src/ders/sdl/copyright.d deleted file mode 100644 index 85173cf..0000000 --- a/ddili/src/ders/sdl/copyright.d +++ /dev/null @@ -1,20 +0,0 @@ -Ddoc - -$(NOT___ Hack: pdf çıktısında kapağa taşma şansı azalsın diye) -$(BR) -$(BR) - -$(H5 Copyright © 2009-2015 Faruk Erdem Öncel) - -$(HR) - -$(P -Creative Commons License
    Ddili.org icerigi by Ali Cehreli is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
    Based on a work at digitalmars.com.
    Permissions beyond the scope of this license may be available at http://ddili.org/copyright.html. -) - -Macros: - SUBTITLE=Kopyalama Hakları - - DESCRIPTION=Site içeriği kopyalama hakları - - KEYWORDS=copyright hak lisans diff --git a/ddili/src/ders/sdl/derse_ozel.ddoc b/ddili/src/ders/sdl/derse_ozel.ddoc deleted file mode 100644 index 8b0af8d..0000000 --- a/ddili/src/ders/sdl/derse_ozel.ddoc +++ /dev/null @@ -1,23 +0,0 @@ -MAIN_TITLE=SDL ile Oyun Programlama -SUB_MAIN_TITLE_DERSE_OZEL= -SUB_AUTHOR=Faruk Erdem Öncel -LANG=tr -LANGUAGE=turkish - -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/sdl/rss.xml, SDL RSS SDL Dersleri RSS Beslemesi) -$(BR) -$(LINK2 /ders/sdl/SDL_ile_Oyun_Programlama.pdf, -PDF Olarak $(IMG pdficon_small.gif)) - - -MINIBASLIK = $0 - - -BASLIK =

    $0

    - -ALTBASLIK =
    $0
    - - -MINI_SOZLUK= - -KODLAR=$(ROOT_DIR)/ders/sdl/src \ No newline at end of file diff --git a/ddili/src/ders/sdl/devami_gelecek.d b/ddili/src/ders/sdl/devami_gelecek.d deleted file mode 100644 index f0df793..0000000 --- a/ddili/src/ders/sdl/devami_gelecek.d +++ /dev/null @@ -1,24 +0,0 @@ -Ddoc - -$(B) -$(H3 Şimdilik bu kadar!) - -$(P -Ama devamı gelecek... Eklenen yeni derslerden haberdar olmak için: -) - -$(UL -$(LI $(LINK2 /ders/sdl/rss.xml, SDL RSS) beslemesini kullanabilirsiniz -) -$(LI $(LINK2 http://ddili.org/forum/forum/4, Ddili Forum'un Duyurular Bölümüne) arada bir göz atabilirsiniz -) -) - -Macros: - SUBTITLE=Devamı Gelecek - - DESCRIPTION= - - KEYWORDS=d programlama dili sdl oyun programlama dersleri öğrenmek tutorial - -MINI_SOZLUK= diff --git a/ddili/src/ders/sdl/frontispiece.d b/ddili/src/ders/sdl/frontispiece.d deleted file mode 100644 index 075eb86..0000000 --- a/ddili/src/ders/sdl/frontispiece.d +++ /dev/null @@ -1 +0,0 @@ -Ddoc diff --git a/ddili/src/ders/sdl/halftitle.html b/ddili/src/ders/sdl/halftitle.html deleted file mode 100644 index cb3dbc2..0000000 --- a/ddili/src/ders/sdl/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -D ile SDL -

    - -

    -Faruk Erdem Öncel -

    - -
    diff --git a/ddili/src/ders/sdl/hareket.d b/ddili/src/ders/sdl/hareket.d deleted file mode 100644 index d81f18c..0000000 --- a/ddili/src/ders/sdl/hareket.d +++ /dev/null @@ -1,286 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Oyunlarda hareket) - -$(P -Bu derste oyunlarda hareket konusu inceleyeceğiz. Daha önceki dersimizde vektörler -için bir oyun nesnesinin konum bilgilerini tutan basit bir yapı olduğunu bilmeniz -yeterli demiştik. Bu derste vektörler konusunda temel bazı bilgiler öğreneceğiz. -) - -$(ALTBASLIK $(IX lineer cebir) Lineer cebir nedir) - -$(P -Lineer cebir vektörleri inceleyen matematik koludur. Eğer oyunumuzda bir yarış -arabasının hızı, kameranın yönü gibi bilgilere ihtiyacımız varsa vektörleri -kullanmak durumundayız. -) - -$(P -Lineer cebiri daha iyi anladığınız takdirde vektörlerin davranışları üzerinde -daha fazla kontrol sahibi olabilirsiniz. -) - -$(ALTBASLIK $(IX vektör) Vektör nedir?) - -$(P Oyunlarda vektörler konum, yön ve hız bilgisi tutarlar. İşte size iki -boyutlu örnekler) - -$(P - -) - -$(P Konum vektörü Sonic oyun karakterinin merkezden 3 metre doğuda -ve 4 metre güneyde durduğunu gösteriyor. Hız vektörü dakikada uçağın 2 -kilometre yukarı ve 3 kilometre sağa hareket ettiğini gösteriyor. Yön vektörü -geminin sola doğru işaret ettiğini gösteriyor) - -$(P Sizin de görebileceğiniz üzere vektörün kendisi aslında bir dizi sayıdan -oluşuyor, kullanıldığı yere göre bir anlam kazanıyor.Örneğin $(HILITE vektör (-1, 0)) -şekildeki gibi bir geminin yönü olabileceği gibi, bulunduğunuz konuma 1 kilometre -uzaktaki bir binanın konumu ya da saatte 1km hızla hareket eden bir salyangozun hızı -da olabilir.) - -$(P Bu yüzden vektörleri kullanırken birimlerini de belirlemek önemli. Şimdi temel -olarak vektörlerin ne olduğunu öğrendiğimize göre onları nasıl kullanacağımızı -öğrenmenin zamanı geldi) - -$(ALTBASLIK Vektörlerde toplama) - -$(P Vektörleri toplamak için vektörün her bileşenini ayrı ayrı toplamak gerekiyor. -Örneğin:) - -$(SHELL - (0,1,4) + (3,-2,5) = (0+3, 1-2, 4+5) = (3,-1,9) -) - -$(P Peki vektörleri neden toplamak isteriz? Vektörleri toplamanın nedenlerinden -bir tanesi fizik entegrasyonunu yapabilmek içindir. Oyun içinde her fiziksel nesne -konum, hız, ivme gibi bilgileri tutabilmek için vektörleri kullanacaktır. Oyunun -her karesinde (genellikle saniyenin 1/60'ı kadar bir sürede) hızı konuma ve ivmeyi -hıza eklemeliyiz.) - - - -$(P Burada gene SDL'in koordinat sistemine göre düşünüyoruz. Yani y ekseni aşağı -doğru bakıyor. Yukarıya hareket etmek demek y değerinin azalması demek) - -$(P Aslında Maryo ekranın altında durduğuna göre, konumu (0, 480) gibi bir değer -olmalı (Ekran çözünürlüğümüzün 640x480 olduğunu varsayarsak) Ama biz hesaplamalarda -kolaylık olsun diye bulunduğu konumu (0, 0) noktası kabul edeceğiz) - -$(P İlk karede hızını (1, -3) konumuna (0, 0) ekleyerek yeni konumunu (1, -3) -buluyoruz. Daha sonra ivmesini (0, 1) hıza (1, -3) ekleyerek yeni hızını (1, -2) -buluyoruz) - -$(P -İkinci karede de aynısını yapıyoruz. Hızını (1, -2) konuma (1, -3) ekleyerek -yeni konumu (2, -5) buluyoruz. Daha sonra ivmeyi (0, 1) hıza (1, -2) ekleyerek -yeni hızı (1, -1) olarak buluyoruz. -) - -$(P -Genellikle oyunlarda oyuncu karakterin ivmesini klavye veya oyun çubuğu kullanarak -kontrol eder, oyun da yeni hız ve konum bilgilerini vektörleri toplayarak -hesaplar) - -$(ALTBASLIK Vektörlerde çıkartma) -$(P -Vektörlerde çıkartmayı da toplamaya benzer şekilde yapıyoruz. Vektörlerin çıkartarak -bir konumdan bir diğer konumu işaret eden bir vektör buluyoruz. Örneğin oyuncumuz -(1, 2) konumunda elinde bir lazer silahı ile duruyor ve düşman robotu (4, 3) -noktasında olsun. Lazerin oyuncuyu vurabilmek için katetmesi gereken mesafeyi -gösteren vektörü oyuncunun konumunu robotun konumundan çıkararak bulabilirsiniz. -$(SHELL -(4,3)-(1,2) = (4-1, 3-2) = (3,1) -) -) - - -$(ALTBASLIK Skaler vektör çarpımı) -$(P -Vektörler söz konusu olduğunda skaler derken vektörü oluşturan her sayıya skaler -diyoruz. Örneğin (3,4) bir vektörken, 5 skaler. Oyunlarda bir vektörü bir skalerle -çarpabilmek oldukça işimize yarayacak. Örneğin oyuncu hareket ederken oyuna daha -gerçeklik katmak için oyuncunun hızına karşı gösterilen hava direncini -hesaplayabiliriz. Bunu da oyuncunun hızını her karede 0.9 ile çarparak buluruz. -Bunu yapmak için vektörün her bileşenini skaler ile çarpıyoruz. Eğer oyuncunun -hızı (10,20) ise yeni hızı şu şekilde bulabiliriz: -$(SHELL -0.9*(10,20) = (0.9*10, 0.9*20) = (9,18) -) -) - -$(ALTBASLIK Kodlamaya başlayalım) -$(P -Bu dersimize önceki derste kodladığımız kod üzerinde çok az değişiklik yaparak -devam edeceğiz. Eğer daha önceki dersi kodladıysanız o dersin üzerinde değişiklikler -yaparak devam edebilirsiniz. Eğer kodlamadıysanız buradan sıkıştırılmış dosyayı indirerek projenin ana dizininde açmanız yeterli. Bir önceki dersin kaynak kodlarını içeren $(HILITE test) isminde bir dizin -oluşacak. -) - -$(ALTBASLIK oyun.d dosyasının içinde) -$(P $(C class TemelOyun)'un üzerinde bunları girin) - ---- -class Top : Oyuncu -{ - this(Grafik2D grafik, Vector2 konum) - { - // Oyuncu sınıfının kurucu işlevini çağırıyoruz - super(grafik, konum); - } -} ---- -$(P Burada yaptığımız hiç bir şey yok aslında. $(B Top) sınıfını -$(B Oyuncu) sınıfından türetiyoruz. $(B Top) sınıfının kurucu işlevinde de -devraldığımız $(Oyuncu) sınıfın kurucu işlevini çağırıyoruz.) - -$(P Bilgilerinizi tazelemek için türeme ile ilgili dershanedeki -bu dersi -tekrar gözden geçirmek isteyebilirsiniz) - -$(P class Oyun: TemelOyun'un altındaki ilk { den sonra Oyuncu oyuncu kısmını -aşağıdaki gibi değiştirin) - ---- - Top top; - Vector2 topHızı; ---- - -$(P Oyun sınıfının içerikYükle() işlevini aşağıdaki gibi değiştirin) - ---- - super.içerikYükle(); - - // oyuncuya ait içeriği yükle - auto topkonum = Vector2(0, 0); - top = new Top(içerik.yükle("top.png", true), topkonum); - topHızı = Vector2(2, 2); ---- - -$(P Burada da ilk derste öğrendiklerimiz dışında yaptığımız tek şey -içerik.yükle() işlevine geçtiğimiz ikinci parametre true saydam bir -grafik yüklemek istediğimizi belirtiyor) - -$(P Oyun sınıfının çiz işlevini de artık olmayan oyuncu nesnemiz yerine -topu çizdirecek şekilde değiştirelim) - ---- - /// Nesneleri göster - // topu çiz - top.çiz(çizici); ---- - -$(P Bu noktaya kadar yaptığımız basitçe penguen resmi yerine saydam bir -top grafiği yüklemekti. Herhangi bir hata yapıp yapmadığınızı test etmek için -Ubuntu altında şu komutları vermeniz yeterli) - -$(SHELL -$ make ornek -$ cd bin -$ ./ornek -) - -$(ALTBASLIK Topu hareket ettirmek) - -$(P Oyun sınıfının güncelle() işlevinin en sonuna $(COMMENT // Oyun mantığı) -yazan kısmın hemen altına topun güncellenme işlevini ekleyelim.) ---- - // Oyun mantığı - topuGüncelle(); ---- -$(P Bu işlevin hemen altındaki } parantezinden sonra topun güncellenme işlevini -ekleyelim) - -$(IX yansıtma) - ---- - private void topuGüncelle() - { - top.konum += topHızı; - - // alta mı geldik - if (top.konum.y + top.yükseklik >= ekranyükseklik) - // alta geldiysek hızı y ekseninde yansıtmamız yeterli - topHızı.yansıt(Vector2(0, 1)); - - // sağa mı geldik - if (top.konum.x + top.genişlik >= ekrangenişlik) - // sağa geldiysek hızı x ekseninde yansıtmamız yeterli - topHızı.yansıt(Vector2(1, 0)); - - // üste mi geldik - if (top.konum.y <= 0) - // üste geldiysek hızı y ekseninde yansıtıyoruz - topHızı.yansıt(Vector2(0, 1)); - - // sola mı geldik - if (top.konum.x <= 0) - // sola geldiysek hızı x ekseninde yansıtıyoruz - topHızı.yansıt(Vector2(1, 0)); - } ---- - -$(P Daha önce oyunun her karesinde hızı konuma ekliyoruz demiştik. İşte topun -güncelle metodunda bunu yapıyoruz. Topun ilk konumu (0,0) noktasıydı. Oyun -başladığında top sağa ve aşağı doğru hareket edecek) - -$(P Alta gelip gelmediğimizi topun y konumunu, topun yüksekliğine ekleyerek -hesaplayabiliriz. Eğer bu değer ekranyüksekliğine büyük ya da eşitse topu y -ekseninde yansıtıyoruz. Top alt tarafa hem sağdan hem de soldan yaklaşabilir) - -$(P Şimdi işin en püf noktasına geldik. Bir vektörü yansıtmak ne demek) - - - -$(P Şekilde yeşil renkli vektörü y ekseni üzerinde yansıttığımız zaman elde -ettiğimiz vektör turuncu renkte olan vektör. yansıt işlevine geçtiğimiz - Vector2(0, 1) parametresi vektörü y ekseninde yansıtmak istediğimizi belirtiyor.) -$(P Bu vektörün uzunluğunun bir birim olduğunu kolayca görebiliriz. Uzunluğu bir birim olan vektörlere birim vektör deniliyor. Dikkat ederseniz topun sağdan ya da soldan -gelse de y ekseninde yansıttığımızda istediğimiz hareketi elde edebiliyoruz) - -$(ALTBASLIK Oyunu çalıştırmak) - -$(P -Programın kaynak kodunu derlemek için şu komutları girin: -) -$(P -Ubuntu altında konsoldan: -) - -$(SHELL - -$ make ornek -$ cd bin -$ ./ornek -) - -$(P -Windows altında $(I Başlat->Çalıştır->) yolunu izleyerek buraya $(COMMENT cmd) yazın. Açılan konsolda: -) - -$(SHELL -make ornek -f win32.mak -cd bin -ornek.exe -) - - -$(P -Eğer her şey yolunda gittiyse ekranınınızda sağa sola hareket eden bir top görmeniz -lazım. Mutlu kodlamalar! -) - - -
    -
    - -Macros: -SUBTITLE=Oyunlarda Hareket - -DESCRIPTION=Oyunlarda hareket - -KEYWORDS=d programlama dili ile görsel programlama sdl öğrenmek tutorial - - diff --git a/ddili/src/ders/sdl/hosgeldiniz.d b/ddili/src/ders/sdl/hosgeldiniz.d deleted file mode 100644 index ed06255..0000000 --- a/ddili/src/ders/sdl/hosgeldiniz.d +++ /dev/null @@ -1,587 +0,0 @@ -Ddoc -$(DERS_BOLUMU Hoşgeldiniz) - -$(P -D ile 2D oyun programlama dersine hoşgeldiniz. Bu derste D dili kullanarak 2D -oyun programlamaya bir başlangıç yapacağız. -) - - -$(P -Bir oyunun geliştirme aşamaları şu bölümlerden oluşuyor -) - -$(UL -$(LI Oyun tasarımı ve senaryo) -$(LI Programlama) -$(LI Görsel Sanatlar) - (Çizim, resim ve diğer sanatsal içerik) -) - -$(P -Biz bunlardan programlama ile ilgileneceğiz. Bunu yaparken programlama dili -olarak D kullanacağız. -) - -$(P -Kullanacağımız grafik kütüphanesi ise SDL (Simple Direct Media Layer) -) - -$(P -$(LINK http://www.libsdl.org) -) - -$(P -Konuları takip edebilmek için sadece D programlama dilini bilmeniz -yeterli. Her ne kadar SDL grafik kütüphanesi kullanıyor olsak da -asıl amacım sadece 2D oyun programlama, oyun değişkenleri ile ilgili -temel kavramları anlatmak olacak. -) -$(P -SDL ile ilgili konuları sadece gerektiğinde ve $(I uzmanlar için) -bölümünde ek bilgi olarak anlatmayı düşünüyorum. -) - -$(ALTBASLIK Haydi başlayalım) - -$(P -Başlamak için şu basit adımları izlemeniz yeterli: -) - -$(OL - $(LI D derleyicisini kurun) - $(LI SDL için geliştirme kütüphanelerini kurun) - $(LI Örnek projeyi bilgisayarınıza indirin) -) - -$(P gerekli araçları kuralım) - -$(P - 1- D derleyicisi: -) - -$(P -Ubuntu altında Digital Mars derleyicisinin son sürümünü -) - -$(P -$(LINK http://dlang.org/download.html) -) - -$(P -adresinden indirerek otomatik olarak yazılım merkezi ile kurabilirsiniz. -) - -$(P -Windows altında Digital Mars derleyicisinin son sürümünü -) - -$(P -$(LINK http://dlang.org/download.html) -) - -$(P -adresinden otomatik olarak kurabilirsiniz. -) - - -$(P -2- SDL geliştirme kütüphaneleri: -) - -$(P -SDL geliştirme kütüphanelerini kurmak için Ubuntu altında konsolda -) - -$(SHELL -$ sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev -) - -$(P -komutunu vermeniz yeterli. -) - -$(P - Windows altında, SDL geliştirme kütüphaneleri örnek proje - ile beraber geliyor. Bu yüzden SDL ile ilgili kütüphaneleri - indirmenize gerek yok. -) - - -$(P - Ancak kolaylık olması açısından $(HILITE bin) - dizininde bulunan $(COMMENT *.dll) uzantılı dosyaları - $(C C:\WINDOWS\system32) dizinine kopyalamak isteyebilirsiniz. -) - -$(P -3- Örnek projeyi indirelim: -) - -$(P -Örnek projeyi indirmek için tarayıcınızla -) - -$(P -$(LINK https://github.com/erdemoncel/oyun) -) - -$(P - adresini açıp sağ üst tarafta bulunan $(SUP Downloads) düğmesine tıklayıp - $(SUP Download .tar.gz) ya da $(SUP Download .zip) seçerek projeyi - indirmeniz yeterli -) - -$(P - Ya da eğer $(I github) kullanımını biliyorsanız basitçe: -) - -$(SHELL -$ git clone git@github.com:erdemoncel/oyun.git -) - -$(P -komutuyla projenin bir kopyasını bilgisayarınıza indirebilirsiniz. -) - -$(ALTBASLIK Oyun döngüsü) - - -$(P -Oyun Döngüsü - -Bir oyunun temel bileşeni bir oyun döngüsüdür. İster çok gelişmiş ya da -basit bir oyun olsun her oyun bir oyun döngüsü kullanır. Şimdi bir oyun -döngüsünün yapısını inceleyelim. -) - -$(MINIBASLIK İlklendir : -) -$(P -Oyunda kullandığımız oyun değişkenlerinin ilk değerlerini burada veriyoruz. -) - -$(MINIBASLIK İçeriği Yükle :) -$(P -Oyunumuzun ihtiyaç duyduğu 2D hareketli grafikler texture, oyuncu modelleri, -ses efektleri ve müzik gibi içeriği bu bölümde yüklüyoruz, -) - -$(MINIBASLIK Güncelle : -) -$(P -Burada oyuncunun klavye, oyun çubuğu ya da başka bir donanımla girdiği -girdileri kontrol ediyor ve oyunun her karesinde oyun değişkenlerini değiştiriyoruz. -) - -$(MINIBASLIK Çiz : -) -$(P -Burada oyunumuza grafik kartına ne gönderileceğini ve ekrana nasıl -çizileceğini söylüyoruz -) - -$(ALTBASLIK Bir oyuncu oluşturmak) - -$(P -Ekranda bir görüntü oluşabilmesi için çeşitli bilgileri saklamalıyız. Bir -oyuncunun konumu buna iyi bir örnek olabilir. -) - -$(P Şimdi kağıt üzerinde bir oyuncu oluşturmak için neler yapmamız gerektiğini düşünelim. Bunları kağıda yazın -) - -$(P İlklendir bölümüne oyuncumuzun ekran üzerinde başlangıç konumunu belirleyen bir işlev ekleyelim. -) - -$(MINIBASLIK -İlklendir -) - - ---- - oyuncuKonumunuBelirle() ---- - -$(P -Şimdi teknik olarak oyuncumuz mevcut. Oyunun $(C ilklendir()) metodunda -sağladığımız bilgilerle artık oyun karakterimizi istediğimiz yere hareket -ettirebiliriz. -) - -$(P Ama ekranda bir şey göremezsek bu iyi bir oyun sayılmaz -) - -$(P Ekrana bir şeyler çizebilmek için ilkönce çizeceğimiz -animasyonu ya da grafiği yüklemeliyiz. -) - - -$(MINIBASLIK -İçerik Yükle -) - ---- - içerik.yükle(dosya) ---- -$(P -Daha sonra oyunumuzu ekran kartının belleğinde çiziyoruz. -) - -$(MINIBASLIK -Çiz -) - ---- - oyuncuyu.çiz() ---- - -$(P -Oldukça basit. Peki oyunun içinde sürekli değişiklikler olmasını istersek -ne yapmamız lazım. -) - -$(P Burada işte $(C güncelle()) metodu karşımıza çıkıyor. Güncelle -metodunu kullanarak ilk değerlerini verdiğimiz oyun değişkenlerini istediğimiz gibi -değiştirebiliyoruz. Güncelle metoduna bazı kodlar ekleyerek oyun içinde -tutulan istediğimiz bilgiyi değiştirebiliriz. -) - -$(MINIBASLIK -Güncelle -) - ---- - if (sağTuşBasılıMı) - konumuGüncelle & animasyonuGüncelle ---- - -$(P -Örneğin klavyenin sağ tuşuna basınca oyun karakterimizin sağa doğru hareket -etmesini istiyorsak $(C güncelle()) metodunda sağ tuşun basılı olup -olmadığını denetliyoruz ve animasyonu güncelliyoruz. -) - -$(P Burada $(C animasyonuGüncelle()) kısmına sağa doğru -yürüyen bir oyuncu animasyonu koyabiliriz. -) - - -$(ALTBASLIK oyuncu.d dosyasının içinde) -$(P -Artık oyunumuzu kodlamaya başlayabiliriz. -) - -$(P -İlkönce daha önce indirmiş olduğunuz proje dosyalarını bir sıkıştırma programı ile -açıp $(HILITE test) klasörünün içine gelin. Burada herhangi bir -editörle $(COMMENT oyuncu.d) isminde yeni bir dosya oluşturun. Ve içine şunları girin. -) - ---- -import sdl, vector2, cizici; - -class Oyuncu -{ - // ilklendir - this(Grafik2D grafik, Vector2 konum) - { - } - - void güncelle() - { - } - - void çiz(Çizici çizici) - { - - } -} ---- -$(P -Dikkat ederseniz biraz önce oyun döngüsünde bahsettiğimiz - işlevlerin bazılarını $(B Oyuncu) sınıfımız için de yazmış olduk. - -) - -$(P D derslerinden hatırlayacağınız gibi $(C this()) -sınıfın kurucu işlevi ve sınıf üyelerine ilk değer ataması -burada gerçekleşiyor. D'nin böyle bir özelliği olduğu için -ekstradan bir $(C ilklendir()) işlevi yazmamıza gerek yok. -$(B Oyuncu) sınıfının değişkenlerine ilk değer atamasını -burada yapabiliriz. -) - -$(P -Şimdi düşünelim. Bir oyun karakterinin ne gibi özellikleri -olmalı? -) - - -$(P -Herşeyden önce ekrana çizebileceğimiz bir 2D grafiğe ya da bir -animasyona sahip olmalı. Ayrıca konumunu belirten koordinatlara sahip -olmalı. -) - -$(P -İşte $(B Oyuncu) sınıfımız bu yüzden parametre olarak bir 2 boyutlu -grafik ve konum bilgisi alıyor. Burada şimdilik $(B Vector2) nin -ekran üzerinde bir oyun nesnesinin x ve y koordinatlarını tutan basit -bir yapı olduğunu bilmeniz yeterli. -) - -$(P -Örneğin: -) ---- - auto oyuncuKonum = Vector2(6, 6); ---- -$(P - diyerek aşağıdaki $(I Calvin and Hobbes) çizgi romanının sevimli karakteri -$(I Hobbes)'un konumunu bir $(B Vector2) yapısında tutabiliriz. -) -$(P - -) - -$(P - Burada hemen farkedebileceğiniz bir şey $(HILITE SDL'in y ekseni alıştığımız koordinat -sisteminin tersine aşağı doğru bakıyor.) -) -$(P -Ayrıca koordinat merkezi olarak oyuncu grafiğinin sol üst köşesini alıyoruz. (resimdeki kırmızı nokta ile işaretlenmiş kısım) -) - -$(P -$(C class Oyuncu)'nun altındaki ilk { den sonra bunları girin. -) ---- - // her oyuncunun bir 2D grafiği olmalı - Grafik2D oyuncuGrafik; - - // oyuncunun ekranın sol üst köşesine göre göreceli konumu - Vector2 konum; - - // oyuncunun durum - bool aktif; - - // oyuncunun sağlık puanı - int sağlık; - - // oyuncunun genişliği ve yüksekliği - int genişlik; - int yükseklik; ---- - -$(P - $(B Vector2) yapısıyla bir nesnenin konumunun x ve y koordinatlarını tutabileceğimizi söylemiştik. -$(B Grafik2D) ise grafik bilgisini -tutan ve ekrana çizilebilen özel bir veri türü. -) - -$(P -Oyuncumuzu çizdireceğimiz zaman oyuncuya ait $(B Grafik2D) iki boyutlu grafiğini -$(B Vector2) ile belirtilen konuma çizdireceğiz. -) - -$(P -Şimdi yapmamız gereken oyuncunun ilk konumunu belirtmek ve oyuncuya ait -grafiğe bir ilk değer vermek. -) - -$(P -$(B Oyuncu) sınıfının kurucu işlevini aşağıdaki gibi değiştirin. -) - ---- - this(Grafik2D grafik, Vector2 konum) - { - oyuncuGrafik = grafik; - - // oyuncunun başlangıç konumu belirle - this.konum = konum; - - // oyuncuyu aktif yapıyoruz - aktif = true; - - // oyuncunun sağlık puanını belirle - sağlık = 100; - - // her grafik nesnesinin bir w (genişlik) ve h (yükseklik) değeri - // olduğu için oyuncumuzun genişlik ve yükseklik değerlerine bu - // değerleri atayabiliriz - genişlik = grafik.w; - yükseklik = grafik.h; - } ---- - -$(P -Oyuncumuzun ihtiyaç duyduğu ilk değerleri verdikten sonra artık oyuncuyu -çizdirebiliriz. Bunu da oyuncunun çiz yöntemine bir $(B Çizici) - nesnesi geçerek yapıyoruz.) -$(P $(B Çizici) nesnesinin yaptığı kendine geçilen bir -iki boyutlu grafiği ekranın belirli bir konumuna çizdirmek. $(B Çizici) -sınıfının ayrıntıların merak ediyorsanız $(HILITE src) klasöründe bulan $(COMMENT cizici.d) -modülünü inceleyebilirsiniz. -) - -$(P -Oyuncu sınıfının $(C çiz) işlevini aşağıdaki gibi değiştirin. -) - ---- - void çiz(Çizici çizici) - { - çizici.çiz(oyuncuGrafik, konum); - } ---- - -$(P - $(B Oyuncu) sınıfıyla şimdilik işimiz bitti. -) - -$(ALTBASLIK oyun.d dosyasının içinde) - -$(P - $(HILITE test) klasöründeki $(COMMENT oyun.d) dosyasını açın. -) -$(P -Kod içinde $(B Oyun) sınıfını bulduktan sonra $(C class Oyun : TemelOyun) un hemen altına { -dan sonra oyuncuyu ekleyin. -) ---- - Oyuncu oyuncu; ---- -$(P -Oyuncuyu ekrana çizebilmek için önce oyuncuya ait grafiği yüklemeli -ve oyuncunun ilk konumunu belirlemeliyiz.) - -$(P Sabitdisk üzerinde bulunan grafikleri okuyup oyuna yükleyeceğimiz için -bunları $(C içerikYükle()) metodunun hemen altında yapabiliriz.) - -$(P $(C super.içerikYükle()) nin altına bu kodu ekleyin. -) ---- - // oyuncuya ait içeriği yükle - auto oyuncuKonum = Vector2(288, 203); - oyuncu = new Oyuncu(içerik.yükle("penguen.bmp"), oyuncuKonum); ---- -$(P -Burada $(C içerik.yükle) kısmı dikkatinizi çekmiş olabilir. Oyunumuzun içerik -isimli bir yapısı var. Bir grafiği yüklemek için bu yapıya yüklemek istediğiniz -2D grafiğin ismini vermeniz yeterli. -) -$(P -$(C içerik.yükle()) işlevi bir $(B Grafik2D) döndürüyor. Ayrıca $(B Oyuncu) sınıfının -kurucu işlevi de parametre olarak bir 2D grafik ve oyuncunun konumunu belirten bir vektör alıyordu. - -) -$(P -Oyuncuyu ekrana çizmeye hazırız. Oyun sınıfının $(C çiz) işlevine bu -kodu ekleyin. -) ---- - /// Nesneleri göster - // oyuncuyu çiz - oyuncu.çiz(çizici); ---- -$(P -İşte bu kadar. Artık çizme bölümü de tamamlanmış oldu. -) - -$(ALTBASLIK Oyunu çalıştırmak) - -$(P -Programın kaynak kodunu derlemek için şu komutları girin: -) -$(P -Ubuntu altında konsoldan: -) - -$(SHELL - -$ make ornek -$ cd bin -$ ./ornek -) - -$(P -Windows altında $(I Başlat->Çalıştır->) yolunu izleyerek buraya $(COMMENT cmd) yazın. Açılan konsolda: -) - -$(SHELL -make ornek -f win32.mak -cd bin -ornek.exe -) - - -$(P -Eğer her şey yolunda gittiyse ekranınınızda sevimli bir penguen görmeniz -lazım -) - - -
    -
    - -$(ALTBASLIK Dersin kaynak kodu) - -$(P -Programı derlerken herhangi bir sorun çıktıysa merak etmeyin. Dersin kaynak -kodunu buradan indirebilirsiniz. -) - -$(ALTBASLIK Uzman ipucu) -$(P -Yazının başında da belirttiğim gibi SDL ile ilgili konuları bu bölümde -anlatacağım. Bu yüzden bu bölümü okuyup okumamak sizin -zevkinize kalmış -) - -$(P -Amacımız ekranın arkaplan rengini değiştirmek. -$(B TemelOyun) sınıfı içinde $(COMMENT //Ekranı temizle) - yazan kısmın hemen altına gelin. -) -$(IX SDL_FillRect) -$(IX SDL_MapRGB) ---- - SDL_FillRect(ekran, &ekran.clip_rect, - SDL_MapRGB(ekran.format, 0x00, 0x00, 0xFF)); ---- - -$(P -Burada $(C SDL_MapRGB) işlevinin aldığı ilk parametre $(I piksellerin biçimi), -ikincisi de rengi oluşturan $(HILITE r g b) değerleri. -Yani $(I kırmızı yeşil mavi) değerleri. İşlevin kendisi de bir $(c uint) tamsayı -döndürüyor.Yani bu kırmızı yeşil mavi değerlerinin birleşimi olan renk değerini -SDL'de bir $(c uint) işaretsiz tamsayı ile ifade edebiliyoruz. -) - -$(P -Eğer Gimp, Photoshop gibi çizim programları kullandıysanız renklerin -karşılıklarının 0 ile 255 arasında değişen $(I RGB) değerler ile ifade -edildiğini hatırlayacaksınız. Örneğin $(HILITE RGB(255, 0, 0)) kırmızı gibi. -) - -$(P -Biz de bu renk değeri kullanarak ekranı temizle işlevini, ekranı maviye -boyayacak şekilde değiştireceğiz. Burada sizin de farketmiş olabileceğiniz -gibi bu değerler onaltılık sayı sisteminde $(HILITE 0x) şeklinde yazılmış. -Bu yüzden ekranı maviye boyamak için ikinci renk değerini -$(HILITE 0xFF) olarak değiştirin. -) - -$(P -Programı tekrar derlediğinizde artık ekranın maviye boyandığını göreceksiniz. -) - -Macros: - SUBTITLE=SDL'e Hoşgeldiniz - - DESCRIPTION=İlk SDL Programınız - - KEYWORDS=d programlama dili ile görsel programlama sdl öğrenmek tutorial - diff --git a/ddili/src/ders/sdl/index.d b/ddili/src/ders/sdl/index.d deleted file mode 100644 index 3bd6221..0000000 --- a/ddili/src/ders/sdl/index.d +++ /dev/null @@ -1,24 +0,0 @@ -Ddoc - -$(H5 SDL ile Oyun Programlama) - -$(UL - -$(LI $(LINK2 /ders/sdl/ix.html, Kitap Dizini)) -$(LI $(LINK2 /ders/sdl/hosgeldiniz.html, Hoşgeldiniz)) -$(LI $(LINK2 /ders/sdl/hareket.html, Oyunlarda hareket)) - -) - -Macros: - SUBTITLE=SDL ile Oyun Programlama - - DESCRIPTION=SDL kütüphanesini kullanarak D oyun programları - - KEYWORDS=d programlama dili ile görsel programlama sdl öğrenmek tutorial - - BREADCRUMBS=$(BREADCRUMBS_INDEX) - -SOZLER= - -MINI_SOZLUK= diff --git a/ddili/src/ders/sdl/index_section_head.html b/ddili/src/ders/sdl/index_section_head.html deleted file mode 100644 index 1a79845..0000000 --- a/ddili/src/ders/sdl/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Dizin

    diff --git a/ddili/src/ders/sdl/index_section_tail.html b/ddili/src/ders/sdl/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/sdl/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/sdl/ix.d b/ddili/src/ders/sdl/ix.d deleted file mode 100644 index c739aed..0000000 --- a/ddili/src/ders/sdl/ix.d +++ /dev/null @@ -1,14 +0,0 @@ -Ddoc - -$(H4 Dizin) - -$(DIV_CLASS web_index_section, -$(INDEX_ENTRIES) -) - -Macros: - SUBTITLE=Dizin Bölümü - - DESCRIPTION=SDL ile Oyun Programlama kitabının dizin bölümü - - KEYWORDS=dizin diff --git a/ddili/src/ders/sdl/pdf.derse_ozel.css b/ddili/src/ders/sdl/pdf.derse_ozel.css deleted file mode 100644 index aeeb933..0000000 --- a/ddili/src/ders/sdl/pdf.derse_ozel.css +++ /dev/null @@ -1,12 +0,0 @@ -a.xref:after { - content: " (sayfa " target-counter(attr(href, url), page) ")"; -} - -body -{ - counter-reset: h4 -1; -} - -ul.toc a.index { - content: "Dizin"; -} diff --git a/ddili/src/ders/sdl/pdf_cozum_head.html b/ddili/src/ders/sdl/pdf_cozum_head.html deleted file mode 100644 index 278be03..0000000 --- a/ddili/src/ders/sdl/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Problem Çözümleri

    diff --git a/ddili/src/ders/sdl/pdf_cozum_tail.html b/ddili/src/ders/sdl/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/sdl/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/sdl/pdf_html_head.html b/ddili/src/ders/sdl/pdf_html_head.html deleted file mode 100644 index cbed4e5..0000000 --- a/ddili/src/ders/sdl/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - D ile SDL - - - diff --git a/ddili/src/ders/sdl/pdf_html_tail.html b/ddili/src/ders/sdl/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/sdl/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/sdl/pdf_indir.d b/ddili/src/ders/sdl/pdf_indir.d deleted file mode 100644 index e71e0ee..0000000 --- a/ddili/src/ders/sdl/pdf_indir.d +++ /dev/null @@ -1,20 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Kitabın PDF Dosyası) - -$(P -Bu kitabın en son sürümünü bir PDF dosyası olarak şu adresten indirebilirsiniz: -) - -$(P -$(IMG pdficon_small.gif) $(LINK http://ddili.org/ders/sdl/SDL_ile_Oyun_Programlama.pdf) -) - -Macros: - SUBTITLE=SDL ile Oyun Programlama PDF Dosyası - - DESCRIPTION=SDL ile Oyun Programlama PDF Dosyası - - KEYWORDS= - - MINI_SOZLUK= diff --git a/ddili/src/ders/sdl/pdf_sozluk_head.html b/ddili/src/ders/sdl/pdf_sozluk_head.html deleted file mode 100644 index f5edc52..0000000 --- a/ddili/src/ders/sdl/pdf_sozluk_head.html +++ /dev/null @@ -1,3 +0,0 @@ - -

    Sözlük

    - diff --git a/ddili/src/ders/sdl/rss.xml b/ddili/src/ders/sdl/rss.xml deleted file mode 100644 index 6eb2589..0000000 --- a/ddili/src/ders/sdl/rss.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - D Programlama Dili ile SDL Oyun Programları - http://ddili.org/ders/sdl/ - D programlama dili ile SDL oyun programcılığı - tr - D Programlama Dili Dersleri - - - 'İçindekiler' bölümü - http://ddili.org/ders/sdl/index.html - Kitabın PDF sürümüne otomatik olarak oluşturulan bir 'İçindekiler' bölümü eklendi. - Haber - 28 Sep 2014 23:00 - - - - SDL Kitabı artık pdf - http://ddili.org/ders/sdl/index.html - Bütün kitabı PDF olarak bölüm başlıklarındaki bağlantısından indirebilirsiniz. - Haber - 21 Jan 2012 18:00 - - - - Oyunlarda hareket - http://ddili.org/ders/sdl/hareket.html - SDL programcılığında oyun karakterlerinin hareketleri - Ders - 15 Jun 2011 14:30 - - - - Hoşgeldiniz - http://ddili.org/ders/sdl/hosgeldiniz.html - SDL programcılığı - Ders - 21 May 2011 21:00 - - - - diff --git a/ddili/src/ders/sdl/src/ders1.zip b/ddili/src/ders/sdl/src/ders1.zip deleted file mode 100644 index 90c1909..0000000 Binary files a/ddili/src/ders/sdl/src/ders1.zip and /dev/null differ diff --git a/ddili/src/ders/sdl/title.html b/ddili/src/ders/sdl/title.html deleted file mode 100644 index f4e0379..0000000 --- a/ddili/src/ders/sdl/title.html +++ /dev/null @@ -1,15 +0,0 @@ -
    - -

    -D ile SDL -

    - -

    -D programlama dilinde SDL ile oyun programlama -

    - -

    -Faruk Erdem Öncel -

    - -
    diff --git a/ddili/src/ders/sdl/toc_head.html b/ddili/src/ders/sdl/toc_head.html deleted file mode 100644 index 3f4be19..0000000 --- a/ddili/src/ders/sdl/toc_head.html +++ /dev/null @@ -1,2 +0,0 @@ -

    İçindekiler

    - diff --git a/ddili/src/ders/sdl/toc_tail.html b/ddili/src/ders/sdl/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/sdl/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/ders/template/Makefile.in b/ddili/src/ders/template/Makefile.in deleted file mode 100644 index 90f541c..0000000 --- a/ddili/src/ders/template/Makefile.in +++ /dev/null @@ -1,19 +0,0 @@ -DERS_SON_D=to_be_continued.d - -# Taken out for the draft release -# foreword1.d \ - -DERS_D_BOLUMLER= \ - foreword2.d \ - preface.d \ - hello_world.d \ - -DERS_D_KAYNAK= \ - index.d \ - $(DERS_D_BOLUMLER) \ - -COZUM_D_KAYNAK= \ - hello_world.cozum.d \ - -include Makefile.ders.in -$(eval $(call derse_ozel,d.de,Programmierung_in_D,german)) diff --git a/ddili/src/ders/template/README b/ddili/src/ders/template/README deleted file mode 100644 index f253c1f..0000000 --- a/ddili/src/ders/template/README +++ /dev/null @@ -1,25 +0,0 @@ -This is the template to use for a new book (or a new translation for an existing one). - -1) Copy this directory as a new one that you will work on: - - cp -pr template d.de - -2) Add the equivalent of the following line to src/Makefile - - include ders/d.de/Makefile.in - -3) If missing for your language, implement the proper alphabet class and add it to alphabet.d: - - case "german": - result = new GermanAlphabet(); - break; - -4) This template comes with three chapters and one solution solution chapter (*.cozum.d (cozum is the transliteration of çözüm)) Make the entire project to see that everything builds properly: - - make - -5) Edit relevant files in your directory - -6) Add new chapters. - -If this is a translation, you can copy and paste from e.g. the English version (d.en directory) one by one. Also, keep an eye on changes made to the original so that you can keep the book up to date. diff --git a/ddili/src/ders/template/breadcrumbs.ddoc b/ddili/src/ders/template/breadcrumbs.ddoc deleted file mode 100644 index 37a58e3..0000000 --- a/ddili/src/ders/template/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > Prg in D - -BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > $(LINK2 /ders/d.de/index.html, Prg in D) diff --git a/ddili/src/ders/template/code/README b/ddili/src/ders/template/code/README deleted file mode 100644 index 028b9e5..0000000 --- a/ddili/src/ders/template/code/README +++ /dev/null @@ -1,22 +0,0 @@ -This directory contains most of the code samples that appear in the book -"Programming in D": - - http://ddili.org/ders/d.en/ - -The names of the sample programs are in chapterName.N.d format. The programs -are extracted from the chapter files and named automatically. - - chapterName - The name of the chapter file without the .html extension, from - which the program was extracted (e.g. hello_world for - hello_world.html) - - N - The order in which the program was extracted from that chapter - file (e.g. 1) - -For example, hello_world.1.d is the first program that was extracted from -hello_world.html. - -The word 'cozum' (transliteration of Turkish "çözüm") that appears in some of -the chapter file names has been replaced with its English translation -(solution). Accordingly, unit_testing.solution.2.d is the second program that -was extracted from unit_testing.cozum.html. diff --git a/ddili/src/ders/template/derse_ozel.ddoc b/ddili/src/ders/template/derse_ozel.ddoc deleted file mode 100644 index 9b60d0a..0000000 --- a/ddili/src/ders/template/derse_ozel.ddoc +++ /dev/null @@ -1,47 +0,0 @@ -MAIN_TITLE=Programming in D -SUB_MAIN_TITLE_DERSE_OZEL=– Tutorial and Reference -SUB_AUTHOR=Ali Çehreli -LANG=en -LANGUAGE=english - -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.de/rss.xml, RSS Programming in D RSS Feed) -$(BR) -$(LINK2 /ders/d.de/index.html, -Download or buy $(IMG book.png)) - -DUSEY_NAVIGASYON= -
    Other D Resources
    -$(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)) -) - -$(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)) -) - -MINIBASLIK = $0 - - -BASLIK =

    $0

    - -ALTBASLIK =
    $0
    - - -MINI_SOZLUK= - -GERI_METIN=Prev -ILERI_METIN=Next -PROBLEM_METIN=Exercise -PROBLEM_COK_METIN=Exercises -PROBLEM_TEK_COZUMSUZ_METIN=the solution will be posted later... -PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... -COZUM_METIN=the solution -COZUMLER_METIN=the solutions - -DERLEME_HATASI_METIN=compilation ERROR diff --git a/ddili/src/ders/template/foreword2.d b/ddili/src/ders/template/foreword2.d deleted file mode 100644 index 1444030..0000000 --- a/ddili/src/ders/template/foreword2.d +++ /dev/null @@ -1,40 +0,0 @@ -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/ddili/src/ders/template/frontispiece.d b/ddili/src/ders/template/frontispiece.d deleted file mode 100644 index 27f5200..0000000 --- a/ddili/src/ders/template/frontispiece.d +++ /dev/null @@ -1,9 +0,0 @@ -Ddoc - -
    - -
    - -
    - -
    diff --git a/ddili/src/ders/template/halftitle.html b/ddili/src/ders/template/halftitle.html deleted file mode 100644 index bbfcfd9..0000000 --- a/ddili/src/ders/template/halftitle.html +++ /dev/null @@ -1,11 +0,0 @@ -
    - -

    -Programming in D -

    - -

    -Ali Çehreli -

    - -
    diff --git a/ddili/src/ders/template/hello_world.cozum.d b/ddili/src/ders/template/hello_world.cozum.d deleted file mode 100644 index 2adc68b..0000000 --- a/ddili/src/ders/template/hello_world.cozum.d +++ /dev/null @@ -1,52 +0,0 @@ -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/ddili/src/ders/template/hello_world.d b/ddili/src/ders/template/hello_world.d deleted file mode 100644 index 2b20edd..0000000 --- a/ddili/src/ders/template/hello_world.d +++ /dev/null @@ -1,222 +0,0 @@ -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.069. -) - -$(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 only the name of the compiler: -) - -$(SHELL -$(SHELL_OBSERVED $) dmd $(SHELL_NOTE enter only the name) -DMD64 D Compiler v2.069.0 -Copyright (c) 1999-2015 by Digital Mars written by Walter Bright -Documentation: http://dlang.org/ -Config file: /etc/dmd.conf -Usage: - dmd files.d ... { -switch } - - files.d D source files -... - -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: -) - -$(SHELL -$(SHELL_OBSERVED $) dmd $(HILITE -run) hello.d -w -unittest -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 __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 in the upcoming chapters with the exception of these keywords: $(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/ddili/src/ders/template/index.d b/ddili/src/ders/template/index.d deleted file mode 100644 index 4a7dba5..0000000 --- a/ddili/src/ders/template/index.d +++ /dev/null @@ -1,148 +0,0 @@ -Ddoc - -$(DERS_BOLUMU Programming in D) - -
    - - - -$(P -The paper version of this book is available through two publishing platforms: -) - -$(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.de/Programming_in_D_code_samples.zip, Click here to download code samples as a $(C .zip) file.) -) - -$(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.de/Programming_in_D.pdf, PDF), $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.epub, EPUB) (for most ebook readers), $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.azw3, AZW3) (for newer Kindles), or $(LINK_DOWNLOAD http://ddili.org/ders/d.de/Programming_in_D.mobi, MOBI) (for older Kindles).) - -) - -$(H5 Web version) - -$(UL -$(WORK_IN_PROCESS -$(LI $(LINK2 /ders/d.de/foreword1.html, Foreword by Walter Bright)) -) -$(LI $(LINK2 /ders/d.de/ix.html, Book Index)) -$(LI $(LINK2 /ders/d.de/foreword2.html, Foreword by Andrei Alexandrescu)) -$(LI $(LINK2 /ders/d.de/preface.html, Preface)) -$(LI $(LINK2 /ders/d.de/hello_world.html, The Hello World Program) $(INDEX_KEYWORDS main)) -$(LI $(LINK2 /ders/d.de/writeln.html, writeln and write)) -$(LI $(LINK2 /ders/d.de/compiler.html, Compilation)) -$(LI $(LINK2 /ders/d.de/types.html, Fundamental Types) $(INDEX_KEYWORDS char int double (and more))) -$(LI $(LINK2 /ders/d.de/assignment.html, Assignment and Order of Evaluation) $(INDEX_KEYWORDS =)) -$(LI $(LINK2 /ders/d.de/variables.html, Variables)) -$(LI $(LINK2 /ders/d.de/io.html, Standard Input and Output Streams) $(INDEX_KEYWORDS stdin stdout)) -$(LI $(LINK2 /ders/d.de/input.html, Reading from the Standard Input)) -$(LI $(LINK2 /ders/d.de/logical_expressions.html, Logical Expressions) $(INDEX_KEYWORDS bool true false ! == != < <= > >= || &&)) -$(LI $(LINK2 /ders/d.de/if.html, if Statement) $(INDEX_KEYWORDS if else)) -$(LI $(LINK2 /ders/d.de/while.html, while Loop) $(INDEX_KEYWORDS while continue break)) -$(LI $(LINK2 /ders/d.de/arithmetic.html, Integers and Arithmetic Operations) $(INDEX_KEYWORDS ++ -- + - * / % ^^ += -= *= /= %= ^^=)) -$(LI $(LINK2 /ders/d.de/floating_point.html, Floating Point Types) $(INDEX_KEYWORDS .nan .infinity isNaN <> !<>= (and more))) -$(LI $(LINK2 /ders/d.de/arrays.html, Arrays) $(INDEX_KEYWORDS [] .length ~ ~=)) -$(LI $(LINK2 /ders/d.de/characters.html, Characters) $(INDEX_KEYWORDS char wchar dchar)) -$(LI $(LINK2 /ders/d.de/slices.html, Slices and Other Array Features) $(INDEX_KEYWORDS .. $ .dup capacity)) -$(LI $(LINK2 /ders/d.de/strings.html, Strings) $(INDEX_KEYWORDS char[] wchar[] dchar[] string wstring dstring)) -$(LI $(LINK2 /ders/d.de/stream_redirect.html, Redirecting Standard Input and Output Streams)) -$(LI $(LINK2 /ders/d.de/files.html, Files) $(INDEX_KEYWORDS File)) -$(LI $(LINK2 /ders/d.de/auto_and_typeof.html, auto and typeof) $(INDEX_KEYWORDS auto typeof)) -$(LI $(LINK2 /ders/d.de/name_space.html, Name Scope)) -$(LI $(LINK2 /ders/d.de/for.html, for Loop) $(INDEX_KEYWORDS for)) -$(LI $(LINK2 /ders/d.de/ternary.html, Ternary Operator ?:) $(INDEX_KEYWORDS ?:)) -$(LI $(LINK2 /ders/d.de/literals.html, Literals)) -$(LI $(LINK2 /ders/d.de/formatted_output.html, Formatted Output) $(INDEX_KEYWORDS writef writefln)) -$(LI $(LINK2 /ders/d.de/formatted_input.html, Formatted Input)) -$(LI $(LINK2 /ders/d.de/do_while.html, do-while Loop) $(INDEX_KEYWORDS do while)) -$(LI $(LINK2 /ders/d.de/aa.html, Associative Arrays) $(INDEX_KEYWORDS .keys .values .byKey .byValue .byKeyValue .get .remove in)) -$(LI $(LINK2 /ders/d.de/foreach.html, foreach Loop) $(INDEX_KEYWORDS foreach .byKey .byValue .byKeyValue)) -$(LI $(LINK2 /ders/d.de/switch_case.html, switch and case) $(INDEX_KEYWORDS switch, case, default, final switch)) -$(LI $(LINK2 /ders/d.de/enum.html, enum) $(INDEX_KEYWORDS enum .min .max)) -$(LI $(LINK2 /ders/d.de/functions.html, Functions) $(INDEX_KEYWORDS return void)) -$(LI $(LINK2 /ders/d.de/const_and_immutable.html, Immutability) $(INDEX_KEYWORDS enum const immutable .dup .idup)) -$(LI $(LINK2 /ders/d.de/value_vs_reference.html, Value Types and Reference Types) $(INDEX_KEYWORDS &)) -$(LI $(LINK2 /ders/d.de/function_parameters.html, Function Parameters) $(INDEX_KEYWORDS in out ref inout lazy scope shared)) -$(LI $(LINK2 /ders/d.de/lvalue_rvalue.html, Lvalues and Rvalues) $(INDEX_KEYWORDS auto ref)) -$(LI $(LINK2 /ders/d.de/lazy_operators.html, Lazy Operators)) -$(LI $(LINK2 /ders/d.de/main.html, Program Environment) $(INDEX_KEYWORDS main stderr)) -$(LI $(LINK2 /ders/d.de/exceptions.html, Exceptions) $(INDEX_KEYWORDS throw try catch finally)) -$(LI $(LINK2 /ders/d.de/scope.html, scope) $(INDEX_KEYWORDS scope(exit) scope(success) scope(failure))) -$(LI $(LINK2 /ders/d.de/assert.html, assert and enforce) $(INDEX_KEYWORDS assert enforce)) -$(LI $(LINK2 /ders/d.de/unit_testing.html, Unit Testing) $(INDEX_KEYWORDS unittest)) -$(LI $(LINK2 /ders/d.de/contracts.html, Contract Programming) $(INDEX_KEYWORDS in out body)) -$(LI $(LINK2 /ders/d.de/lifetimes.html, Lifetimes and Fundamental Operations)) -$(LI $(LINK2 /ders/d.de/null_is.html, The null Value and the is Operator) $(INDEX_KEYWORDS null is !is)) -$(LI $(LINK2 /ders/d.de/cast.html, Type Conversions) $(INDEX_KEYWORDS to assumeUnique cast)) -$(LI $(LINK2 /ders/d.de/struct.html, Structs) $(INDEX_KEYWORDS struct . {} static, static this, static ~this)) -$(LI $(LINK2 /ders/d.de/parameter_flexibility.html, Variable Number of Parameters) $(INDEX_KEYWORDS T[]... __MODULE__ __FILE__ __LINE__ __FUNCTION__ __PRETTY_FUNCTION__)) -$(LI $(LINK2 /ders/d.de/function_overloading.html, Function Overloading)) -$(LI $(LINK2 /ders/d.de/member_functions.html, Member Functions) $(INDEX_KEYWORDS toString)) -$(LI $(LINK2 /ders/d.de/const_member_functions.html, const ref Parameters and const Member Functions) $(INDEX_KEYWORDS const ref, in ref, inout)) -$(LI $(LINK2 /ders/d.de/special_functions.html, Constructor and Other Special Functions) $(INDEX_KEYWORDS this ~this this(this) opAssign @disable)) -$(LI $(LINK2 /ders/d.de/operator_overloading.html, Operator Overloading) $(INDEX_KEYWORDS opUnary opBinary opEquals opCmp opIndex (and more))) -$(LI $(LINK2 /ders/d.de/class.html, Classes) $(INDEX_KEYWORDS class new)) -$(LI $(LINK2 /ders/d.de/inheritance.html, Inheritance) $(INDEX_KEYWORDS : super override abstract)) -$(LI $(LINK2 /ders/d.de/object.html, Object) $(INDEX_KEYWORDS toString opEquals opCmp toHash typeid TypeInfo)) -$(LI $(LINK2 /ders/d.de/interface.html, Interfaces) $(INDEX_KEYWORDS interface static final)) -$(LI $(LINK2 /ders/d.de/destroy.html, destroy and scoped) $(INDEX_KEYWORDS destroy scoped)) -$(LI $(LINK2 /ders/d.de/modules.html, Modules and Libraries) $(INDEX_KEYWORDS import, module, static this, static ~this)) -$(LI $(LINK2 /ders/d.de/encapsulation.html, Encapsulation and Protection Attributes) $(INDEX_KEYWORDS private protected public package)) -$(LI $(LINK2 /ders/d.de/ufcs.html, Universal Function Call Syntax (UFCS))) -$(LI $(LINK2 /ders/d.de/property.html, Properties) $(INDEX_KEYWORDS @property)) -$(LI $(LINK2 /ders/d.de/invariant.html, Contract Programming for Structs and Classes) $(INDEX_KEYWORDS invariant)) -$(LI $(LINK2 /ders/d.de/templates.html, Templates)) -$(LI $(LINK2 /ders/d.de/pragma.html, Pragmas)) -$(LI $(LINK2 /ders/d.de/alias.html, alias and with) $(INDEX_KEYWORDS alias with)) -$(LI $(LINK2 /ders/d.de/alias_this.html, alias this) $(INDEX_KEYWORDS alias this)) -$(LI $(LINK2 /ders/d.de/pointers.html, Pointers) $(INDEX_KEYWORDS * &)) -$(LI $(LINK2 /ders/d.de/bit_operations.html, Bit Operations) $(INDEX_KEYWORDS ~ & | ^ >> >>> <<)) -$(LI $(LINK2 /ders/d.de/cond_comp.html, Conditional Compilation) $(INDEX_KEYWORDS debug, version, static if, static assert, __traits)) -$(LI $(LINK2 /ders/d.de/is_expr.html, is Expression) $(INDEX_KEYWORDS is())) -$(LI $(LINK2 /ders/d.de/lambda.html, Function Pointers, Delegates, and Lambdas) $(INDEX_KEYWORDS function delegate => toString)) -$(LI $(LINK2 /ders/d.de/foreach_opapply.html, foreach with Structs and Classes) $(INDEX_KEYWORDS opApply empty popFront front (and more))) -$(LI $(LINK2 /ders/d.de/nested.html, Nested Functions, Structs, and Classes) $(INDEX_KEYWORDS static)) -$(LI $(LINK2 /ders/d.de/union.html, Unions) $(INDEX_KEYWORDS union)) -$(LI $(LINK2 /ders/d.de/goto.html, Labels and goto) $(INDEX_KEYWORDS goto)) -$(LI $(LINK2 /ders/d.de/tuples.html, Tuples) $(INDEX_KEYWORDS tuple Tuple AliasSeq .tupleof foreach)) -$(LI $(LINK2 /ders/d.de/templates_more.html, More Templates) $(INDEX_KEYWORDS template opDollar opIndex opSlice)) -$(LI $(LINK2 /ders/d.de/functions_more.html, More Functions) $(INDEX_KEYWORDS inout pure nothrow @nogc @safe @trusted @system CTFE __ctfe)) -$(LI $(LINK2 /ders/d.de/mixin.html, Mixins) $(INDEX_KEYWORDS mixin)) -$(LI $(LINK2 /ders/d.de/ranges.html, Ranges) $(INDEX_KEYWORDS InputRange ForwardRange BidirectionalRange RandomAccessRange OutputRange)) -$(LI $(LINK2 /ders/d.de/ranges_more.html, More Ranges) $(INDEX_KEYWORDS isInputRange ElementType hasLength inputRangeObject (and more))) -$(LI $(LINK2 /ders/d.de/parallelism.html, Parallelism) $(INDEX_KEYWORDS parallel task asyncBuf map amap reduce)) -$(LI $(LINK2 /ders/d.de/concurrency.html, Message Passing Concurrency) $(INDEX_KEYWORDS spawn thisTid ownerTid send receive (and more))) -$(LI $(LINK2 /ders/d.de/concurrency_shared.html, Data Sharing Concurrency) $(INDEX_KEYWORDS synchronized, shared, shared static this, shared static ~this)) -$(LI $(LINK2 /ders/d.de/fibers.html, Fibers) $(INDEX_KEYWORDS call yield)) -$(LI $(LINK2 /ders/d.de/memory.html, Memory Management) $(INDEX_KEYWORDS calloc realloc emplace destroy .alignof)) -$(LI $(LINK2 /ders/d.de/uda.html, User Defined Attributes (UDA)) $(INDEX_KEYWORDS @)) -$(LI $(LINK2 /ders/d.de/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/ddili/src/ders/template/index_section_head.html b/ddili/src/ders/template/index_section_head.html deleted file mode 100644 index 862063a..0000000 --- a/ddili/src/ders/template/index_section_head.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -

    Index

    diff --git a/ddili/src/ders/template/index_section_tail.html b/ddili/src/ders/template/index_section_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/template/index_section_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/template/ix.d b/ddili/src/ders/template/ix.d deleted file mode 100644 index b85c3ae..0000000 --- a/ddili/src/ders/template/ix.d +++ /dev/null @@ -1,14 +0,0 @@ -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/ddili/src/ders/template/pdf.derse_ozel.css b/ddili/src/ders/template/pdf.derse_ozel.css deleted file mode 100644 index fe51491..0000000 --- a/ddili/src/ders/template/pdf.derse_ozel.css +++ /dev/null @@ -1,31 +0,0 @@ -a.xref:after { - content: " (page " target-counter(attr(href, url), page) ")"; -} - -div.cozum_link_cok a.xref { - content: "The solutions are on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_cok a.xref:after { - content: normal; -} - -div.cozum_link_tek a.xref { - content: "The solution is on page " target-counter(attr(href, url), page) "."; - font-style:italic; -} - -div.cozum_link_tek a.xref:after { - content: normal; -} - -div.cozum_link_cok, div.cozum_link_tek { - padding-top: .1em; - page-break-before: avoid; -} - -body -{ - counter-reset: h4 -2; -} diff --git a/ddili/src/ders/template/pdf_cozum_head.html b/ddili/src/ders/template/pdf_cozum_head.html deleted file mode 100644 index d7ebaae..0000000 --- a/ddili/src/ders/template/pdf_cozum_head.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -
    -
    -

    Exercise Solutions

    diff --git a/ddili/src/ders/template/pdf_cozum_tail.html b/ddili/src/ders/template/pdf_cozum_tail.html deleted file mode 100644 index bdd6bc9..0000000 --- a/ddili/src/ders/template/pdf_cozum_tail.html +++ /dev/null @@ -1,2 +0,0 @@ -
    -
    diff --git a/ddili/src/ders/template/pdf_html_head.html b/ddili/src/ders/template/pdf_html_head.html deleted file mode 100644 index d75ecee..0000000 --- a/ddili/src/ders/template/pdf_html_head.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - Programming in D - - - diff --git a/ddili/src/ders/template/pdf_html_tail.html b/ddili/src/ders/template/pdf_html_tail.html deleted file mode 100644 index 308b1d0..0000000 --- a/ddili/src/ders/template/pdf_html_tail.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ddili/src/ders/template/rss.xml b/ddili/src/ders/template/rss.xml deleted file mode 100644 index 1e35711..0000000 --- a/ddili/src/ders/template/rss.xml +++ /dev/null @@ -1,812 +0,0 @@ - - - - - Programming in D - http://ddili.org/ders/d.en/ - Programming with the D programming language - en - D Programming Language Book - - - 'deprecated', 'extern', and 'extern()' - http://ddili.org/ders/d.en/modules.html - Added 'deprecated' that helps with deprecation process of library features, 'extern' that is for declaring external symbols, and 'extern()' that allows interacting with libraries of other languages (extern(C), extern(C++, std), etc.). - Chapter - 24 Oct 2015 22:00 - - - - Code Samples .zip File - http://ddili.org/ders/d.en/index.html - You can download most of the code samples that appear in the book as a .zip file. - Code - 16 Oct 2015 20:00 - - - - Book Index - http://ddili.org/ders/d.en/ix.html - The index section of the book. - Chapter - 21 Aug 2015 02:00 - - - - Fibers - http://ddili.org/ders/d.en/fibers.html - Cooperative multitasking in D with fibers. - Chapter - 17 Aug 2015 15:00 - - - - Pragmas - http://ddili.org/ders/d.en/pragma.html - Pragmas that allow interactions with the compiler, including the most recent pragma(inline). - Chapter - 17 Aug 2015 15:00 - - - - Operator Precedence - http://ddili.org/ders/d.en/operator_precedence.html - The rules that specify the execution order of chained operators and the expressions that they use. - Chapter - 17 Aug 2015 15:00 - - - - Foreword by Andrei Alexandrescu - http://ddili.org/ders/d.en/foreword2.html - "Instead of falling for getting things done quickly, 'Programming in D' focuses on getting things done properly, to the lasting benefit of its reader." - Chapter - 17 Aug 2015 15:00 - - - - Ebook versions - http://ddili.org/ders/d.en/index.html - In addition to the PDF version, now there are EPUB and AZW3 versions as well. - Ebook - 15 Dec 2014 02:00 - - - - '.offsetof' property and 'align' attribute - http://ddili.org/ders/d.en/memory.html - The .offsetof property to get the offsets of and the align attribute to specify the alignments of struct members. - Chapter - 25 Nov 2014 17:00 - - - - Contract inheritance - http://ddili.org/ders/d.en/invariant.html - Inheriting 'in' and 'out' blocks of interface and class functions. - Chapter - 25 Nov 2014 17:00 - - - - The special keywords - http://ddili.org/ders/d.en/templates_more.html - __MODULE__, __FILE__, __LINE__, __FUNCTION__, and __PRETTY_FUNCTION__ - Chapter - 25 Nov 2014 17:00 - - - - pragma - http://ddili.org/ders/d.en/templates.html - The pragma directive - Chapter - 25 Nov 2014 17:00 - - - - Nested Functions, Structs, and Classes - http://ddili.org/ders/d.en/nested.html - Defining functions, structs, and classes inside existing scopes. - Chapter - 25 Nov 2014 17:00 - - - - Lvalues and Rvalues - http://ddili.org/ders/d.en/lvalue_rvalue.html - Lvalues, rvalues, their differences, and 'auto ref' parameter type that can accept either kind. - Chapter - 25 Nov 2014 17:00 - - - - The 'Index' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated index section for the PDF version of the book. - Chapter - 25 Nov 2014 17:00 - - - - The 'Table of Contents' section - http://ddili.org/ders/d.en/pdf_indir.html - Automatically generated TOC section for the PDF version of the book. - Chapter - 28 Sep 2014 23:00 - - - - The attributes of Throwable - http://ddili.org/ders/d.en/exceptions.html - The attributes of the Throwable interface, collateral exceptions, and an example of accessing collateral exceptions through the .next property. - Chapter - 18 Sep 2014 16:00 - - - - Selective, local, renamed, and package imports - http://ddili.org/ders/d.en/modules.html - Importing modules selectively, locally, under a different name, and as a package. - Chapter - 17 Sep 2014 00:30 - - - - @disable - http://ddili.org/ders/d.en/special_functions.html - The @disable attribute to disable special functions of structs. - Chapter - 12 Sep 2014 00:15 - - - - Operator overloading for multi-dimensional indexing and slicing - http://ddili.org/ders/d.en/templates_more.html - The templated versions of opDollar, opIndex, and opSlice, and examples of using them to support multiple indexes inside square brackets. - Chapter - 11 Sep 2014 00:15 - - - - static this, static ~this, shared static this, and shared static ~this - http://ddili.org/ders/d.en/index.html - Specifying the initial and final operations of threads and programs. - Chapter - 26 Aug 2014 23:30 - - - - User Defined Attributes (UDA) - http://ddili.org/ders/d.en/uda.html - Assigning user defined attributes to type and variable declarations, testing the attributes at compile time, and compiling the program according to those attributes. - Chapter - 26 Aug 2014 22:30 - - - - Memory Management - http://ddili.org/ders/d.en/memory.html - The garbage collector, allocating memory, and placing objects at specific locations in memory. - Chapter - 24 Jul 2014 01:00 - - - - Type traits - http://ddili.org/ders/d.en/cond_comp.html - Using type traits in conditional compilation. - Chapter - 29 May 2014 20:30 - - - - Data Sharing Concurrency - http://ddili.org/ders/d.en/concurrency_shared.html - Multi-threaded programming in D by data sharing. - Chapter - 20 May 2014 12:00 - - - - More Ranges - http://ddili.org/ders/d.en/ranges_more.html - Useful range templates of the std.range module. Providing range capabilities depending on the capabilities on dependent ranges. - Chapter - 01 November 2013 17:00 - - - - Mixins - http://ddili.org/ders/d.en/mixin.html - Template and string mixins that allow inserting compile-time generated code into the source code. - Chapter - 01 November 2013 17:00 - - - - More Functions - http://ddili.org/ders/d.en/functions_more.html - More features of D functions: auto, ref, auto ref, and inout return attributes; pure and nothrow behavioral attributes; and @safe, @trusted, and @system memory safety attributes. - Chapter - 01 November 2013 17:00 - - - - More Templates - http://ddili.org/ders/d.en/templates_more.html - More information about templates: Templates can define any kind of code; template parameters can be of type, value, alias, this, and tuple; template constraints enable template definitions only for template arguments that satisfy that template's requirements. - Chapter - 19 September 2013 00:30 - - - - Tuples - http://ddili.org/ders/d.en/tuples.html - Tuples, which combine values of different types and make them available similar to struct objects, and TypeTuple, which represents the concept of 'list of values' as seen in parameter lists, template argument lists, and array literal initialization lists. - Chapter - 19 September 2013 00:30 - - - - Ranges - http://ddili.org/ders/d.en/ranges.html - The code examples in the Ranges chapter are made const-correct and they take advantage of UFCS. - Chapter - 09 July 2013 19:30 - - - - Labels and goto - http://ddili.org/ders/d.en/goto.html - Labels that give names to code lines and the goto statement that make program execution go to a label. - Chapter - 28 June 2013 19:00 - - - - Unions - http://ddili.org/ders/d.en/union.html - Sharing the same memory area for multiple members. - Chapter - 26 June 2013 10:00 - - - - foreach with Structs and Classes - http://ddili.org/ders/d.en/foreach_opapply.html - Providing foreach support for structs and classes. - Chapter - 14 June 2013 18:30 - - - - Function Pointers, Delegates, and Lambdas - http://ddili.org/ders/d.en/lambda.html - Function pointers and delegates allow storing how the program should behave at a later time. Lambdas (anonymous function or function literals) make code more readable and reduce boilerplate code. - Chapter - 9 June 2013 18:00 - - - - is Expression - http://ddili.org/ders/d.en/is_expr.html - is expression, one of the most powerful compile-time features of the D programming language. - Chapter - 3 June 2013 22:00 - - - - Conditional Compilation - http://ddili.org/ders/d.en/cond_comp.html - Compiling parts of programs in special ways depending on conditions that are checked at compile time. - Chapter - 3 June 2013 21:30 - - - - Bit Operations - http://ddili.org/ders/d.en/bit_operations.html - The D features that enable manipulating data bit-by-bit. - Chapter - 21 May 2013 23:30 - - - - Pointers - http://ddili.org/ders/d.en/pointers.html - Pointers are variables that provide access to other variables. They are low-level capabilities of the microprocessor. - Chapter - 31 Jan 2013 20:30 - - - - alias this - http://ddili.org/ders/d.en/alias_this.html - 'alias this' enables automatic type conversions of user-defined types. - Chapter - 31 Jan 2013 20:30 - - - - alias - http://ddili.org/ders/d.en/alias.html - 'alias' assigns aliases to existing names - Chapter - 31 Jan 2013 20:30 - - - - Contract Programming for Structs and Classes - http://ddili.org/ders/d.en/invariant.html - The 'invariant' keyword and the use of the 'in' and 'out' blocks with structs and classes. - Chapter - 31 Jan 2013 20:30 - - - - Properties - http://ddili.org/ders/d.en/property.html - Properties allow using member functions like member variables. - Chapter - 31 Jan 2013 20:30 - - - - Universal Function Call Syntax (UFCS) - http://ddili.org/ders/d.en/ufcs.html - UFCS enables the member function syntax even for regular functions. - Chapter - 31 Jan 2013 20:30 - - - - Encapsulation and Protection Attributes - http://ddili.org/ders/d.en/encapsulation.html - Preserving class invariants by limiting access to class members. - Chapter - 2 Nov 2012 21:00 - - - - Modules and Libraries - http://ddili.org/ders/d.en/modules.html - Organizing D programs and libraries as modules and packages. - Chapter - 2 Nov 2012 21:00 - - - - destroy and scoped - http://ddili.org/ders/d.en/destroy.html - 'destroy()' to call destructors explicitly and 'scoped()' to destroy objects automatically. - Chapter - 2 Nov 2012 21:00 - - - - Interfaces - http://ddili.org/ders/d.en/interface.html - The 'interface' keyword to define class interfaces. - Chapter - 2 Nov 2012 21:00 - - - - Object - http://ddili.org/ders/d.en/object.html - The Object class that is at the top of class hierarchies and its member functions toString(), opEquals(), opCmp(), and toHash(). - Chapter - 2 Nov 2012 21:00 - - - - Inheritance - http://ddili.org/ders/d.en/inheritance.html - Inheriting the members of existing classes. - Chapter - 2 Nov 2012 21:00 - - - - Classes - http://ddili.org/ders/d.en/class.html - The 'class' feature of the D programming language, which supports the object oriented programming (OOP) paradigm. - Chapter - 2 Nov 2012 21:00 - - - - Operator Overloading - http://ddili.org/ders/d.en/operator_overloading.html - Defining the behaviors of operators for structs to allow their uses as convenient as the fundamental types. - Chapter - 15 Sep 2012 23:30 - - - - Constructor and Other Special Functions - http://ddili.org/ders/d.en/special_functions.html - The four special functions of structs: constructor, destructor, postblit, and assignment. - Chapter - 15 Sep 2012 23:30 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Receiving LinkTerminated and OwnerTerminated exceptions as messages. - Chapter - 09 Aug 2012 23:45 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - Explain the parameters of the functions of the std.parallelism module: Work unit size and buffer size. - Chapter - 09 Aug 2012 23:30 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The consequences of marking function parameters const or immutable. - Chapter - 09 Aug 2012 22:00 - - - - const ref Parameters and const Member Functions - http://ddili.org/ders/d.en/const_member_functions.html - Marking parameters as 'const ref' and member functions as 'const' in order to be able to use them with immutable variables as well. - Chapter - Sun, 10 Jun 2012 17:40 - - - - Member Functions - http://ddili.org/ders/d.en/member_functions.html - Defining functions that are closely related to a struct inside the curly brackets of that struct definition. - Chapter - Sun, 10 Jun 2012 16:15 - - - - Function Overloading - http://ddili.org/ders/d.en/function_overloading.html - The function overloading feature that enables defining multiple functions having the same name. - Chapter - Sun, 10 Jun 2012 14:50 - - - - Variable Number of Parameters - http://ddili.org/ders/d.en/parameter_flexibility.html - Default parameter values and variadic functions. - Chapter - Sun, 10 Jun 2012 13:20 - - - - Structs - http://ddili.org/ders/d.en/struct.html - The 'struct' feature for defining higher-level concepts as user-defined types. - Chapter - Sun, 10 Jun 2012 12:20 - - - - Type Conversions - http://ddili.org/ders/d.en/cast.html - Automatic and explicit type conversions of D: integer promotions, arithmetic conversions, the to() and assumeUnique() functions, and the cast operator. - Chapter - Sun, 10 Jun 2012 00:05 - - - - The null Value and the is Operator - http://ddili.org/ders/d.en/null_is.html - The null value and the is and !is operators. - Chapter - Sat, 09 Jun 2012 18:30 - - - - Value Types and Reference Types - http://ddili.org/ders/d.en/value_vs_reference.html - Introducing value types, reference variables, and reference types; and their differences. - Chapter - Sat, 09 Jun 2012 17:45 - - - - Lifetimes and Fundamental Operations - http://ddili.org/ders/d.en/lifetimes.html - Object lifetimes: Initialization and finalization of variables. - Chapter - Sat, 09 Jun 2012 15:15 - - - - Contract Programming - http://ddili.org/ders/d.en/contracts.html - Contract programming in D: the 'in' and 'out' blocks of functions. - Chapter - Tue, 27 Apr 2012 23:15 - - - - Unit Testing - http://ddili.org/ders/d.en/unit_testing.html - Unit tests for reducing the risk of bugs and test driven development (TDD). - Chapter - Tue, 24 Apr 2012 22:15 - - - - Installing dmd and compiling programs - http://ddili.org/ders/d.en/hello_world.html - Added how to install dmd and how to compile programs on the command line. - Chapter - Sat, 21 Apr 2012 22:15 - - - - Message Passing Concurrency - http://ddili.org/ders/d.en/concurrency.html - Multi-threaded programming in D by message passing, provided by the std.concurrency module. - Chapter - 15 Apr 2012 23:15 - - - - assert and enforce - http://ddili.org/ders/d.en/assert.html - The assert checks and the enforce() function that help with program correctness. - Chapter - 12 April 2012 23:30 - - - - scope - http://ddili.org/ders/d.en/scope.html - The scope(success), scope(failure), and scope(exit) statements of D, which in many cases obviate the need for try-catch-finally blocks and RAII classes. - Chapter - 28 Mar 2012 23:20 - - - - Program Environment - http://ddili.org/ders/d.en/main.html - The environment that starts a D program and the ways the program can interact with its environment: return value, parameters, environment variables. - Chapter - 28 Mar 2012 23:10 - - - - Lazy Operators - http://ddili.org/ders/d.en/lazy_operators.html - The shortcut evaluations of three operators: logical or, logical and, and the ternary operator. - Chapter - 28 Mar 2012 01:00 - - - - Function Parameters - http://ddili.org/ders/d.en/function_parameters.html - Different kinds of function parameters and how they affect the functions and the arguments. - Chapter - 28 Mar 2012 01:00 - - - - Immutability - http://ddili.org/ders/d.en/const_and_immutable.html - The concept of immutability in the D programming language, the const and immutable keywords, and recommendations on how to take advantage of immutability when defining variables and function parameters. - Chapter - 22 Mar 2012 22:50 - - - - Functions - http://ddili.org/ders/d.en/functions.html - The functions that define the building blocks of program behavior. - Chapter - 14 Mar 2012 00:30 - - - - enum - http://ddili.org/ders/d.en/enum.html - The enum feature that enables defining named constant values. - Chapter - 13 Mar 2012 18:30 - - - - switch and case - http://ddili.org/ders/d.en/switch_case.html - The switch and final switch statements, their case sections, and the use of the goto statement under the case sections. - Chapter - 27 Feb 2012 18:10 - - - - The foreach Loop - http://ddili.org/ders/d.en/foreach.html - The foreach loop, one of the most common statements of D. Its use with arrays, strings, and associative arrays. - Chapter - 27 Feb 2012 18:00 - - - - Associative Arrays - http://ddili.org/ders/d.en/aa.html - Associative arrays, the hash table implementation of the D programming language. - Chapter - 26 Feb 2012 22:40 - - - - Parallelism - http://ddili.org/ders/d.en/parallelism.html - The std.parallelism module to make programs run faster by taking advantage of multiple cores of the system. - Chapter - 19 Feb 2012 23:10 - - - - The do-while Loop - http://ddili.org/ders/d.en/do_while.html - The do-while loop and its comparison to the while loop. - Chapter - 11 Feb 2012 20:10 - - - - Formatted Input - http://ddili.org/ders/d.en/formatted_input.html - Reading data that match specific formats. - Chapter - 11 Feb 2012 20:00 - - - - Formatted Output - http://ddili.org/ders/d.en/formatted_output.html - Determining the format of printed values. - Chapter - 31 Jan 2012 00:00 - - - - Literals - http://ddili.org/ders/d.en/literals.html - The syntax of literals of different D types. - Chapter - 31 Jan 2012 00:00 - - - - The Ternary Operator ?: - http://ddili.org/ders/d.en/ternary.html - The ternary operator and comparing it to the if-else statement. - Chapter - 31 Jan 2012 00:00 - - - - The for Loop - http://ddili.org/ders/d.en/for.html - The for loop and its comparison to the while loop. - Chapter - 31 Jan 2012 00:00 - - - - PDF version of the book - http://ddili.org/ders/d.en/index.html - The PDF version of the book is now available through a link in chapter headers - News - 21 Jan 2012 18:00 - - - - Name Space - http://ddili.org/ders/d.en/name_space.html - The lifetime and accessibility of names of variables and other program constructs - Chapter - 21 Jan 2012 18:00 - - - - auto and typeof - http://ddili.org/ders/d.en/auto_and_typeof.html - The 'auto' keyword and its use during type inference, and the typeof keyword to get the type of expressions - Chapter - 21 Jan 2012 18:00 - - - - Files - http://ddili.org/ders/d.en/files.html - Reading from and writing to files using the std.stdio.File struct - Chapter - 21 Jan 2012 18:00 - - - - Redirecting Standard Input and Output Streams - http://ddili.org/ders/d.en/stream_redirect.html - How to redirect standard input and output streams of program to files and other programs - Chapter - 21 Jan 2012 18:00 - - - - Templates - http://ddili.org/ders/d.en/templates.html - The 'Templates' chapter - Chapter - 12 Jan 2012 00:40 - - - - Strings - http://ddili.org/ders/d.en/strings.html - The 'Strings' chapter - Chapter - 03 Jan 2012 18:00 - - - - Slices and Other Array Features - http://ddili.org/ders/d.en/slices.html - The 'Slices and Other Array Features' chapter - Chapter - 31 Dec 2011 19:15 - - - - Characters - http://ddili.org/ders/d.en/characters.html - The 'Characters' chapter - Chapter - 18 Dec 2011 19:15 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter has been proofread. - Proofreading - 18 Dec 2011 19:00 - - - - Arrays - http://ddili.org/ders/d.en/arrays.html - The 'Arrays' chapter - Chapter - 11 Dec 2011 14:00 - - - - Floating Point Types - http://ddili.org/ders/d.en/floating_point.html - The 'Floating Point Types' chapter - Chapter - 09 Dec 2011 22:10 - - - - Index - http://ddili.org/ders/d.en/index.html - The index of the book - Book - 13 Nov 2011 23:00 - - - - diff --git a/ddili/src/ders/template/title.html b/ddili/src/ders/template/title.html deleted file mode 100644 index f189df7..0000000 --- a/ddili/src/ders/template/title.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - -

    -Programming in D -

    - -
    - -

    -First Edition -

    - -
    - -

    -Ali Çehreli -

    - -
    - -
    -

    -Edited by Luís Marques -

    -
    - -
    diff --git a/ddili/src/ders/template/toc_head.html b/ddili/src/ders/template/toc_head.html deleted file mode 100644 index 345f048..0000000 --- a/ddili/src/ders/template/toc_head.html +++ /dev/null @@ -1 +0,0 @@ -

    Contents

    diff --git a/ddili/src/ders/template/toc_tail.html b/ddili/src/ders/template/toc_tail.html deleted file mode 100644 index 04f5b84..0000000 --- a/ddili/src/ders/template/toc_tail.html +++ /dev/null @@ -1 +0,0 @@ -
    diff --git a/ddili/src/dusey_navigasyon.ddoc b/ddili/src/dusey_navigasyon.ddoc deleted file mode 100644 index d317b4a..0000000 --- a/ddili/src/dusey_navigasyon.ddoc +++ /dev/null @@ -1,39 +0,0 @@ - -GOOGLE_TRANSLATE= - - -DUSEY_NAVIGASYON = -$(UL -$(LI $(LINK2 $(ROOT_DIR)/ders/, $(IMG book.png) Kitaplar)) -$(LI $(LINK2 /forum/, $(IMG forum.png) Forum)) -$(XXX $(LI $(LINK2 /wiki/, $(IMG pencil.png) Wiki))) -$(XXX $(LI $(LINK2 /wiki/index.php?title=Ddili_Projeleri, $(IMG d_source.png) Projeler))) -$(BR) -$(LI $(LINK2 $(ROOT_DIR)/tanitim/, $(IMG bullet_black.png) Tanıtım)) -$(LI $(LINK2 $(ROOT_DIR)/makale/, $(IMG bullet_black.png) Makaleler)) -$(LI $(LINK2 $(ROOT_DIR)/sunum/, $(IMG bullet_black.png) Sunumlar)) -$(LI $(LINK2 $(ROOT_DIR)/kurulum/, $(IMG bullet_black.png) Kurulum)) -) -$(BR) -$(BR) -
    İngilizce Kaynaklar
    -$(UL -$(LI $(LINK2 http://www.dlang.org/, $(IMG bullet_black.png) Digital Mars)) -$(LI $(LINK2 http://forum.dlang.org/, $(IMG bullet_black.png) Newsgroups)) -$(LI $(LINK2 $(ROOT_DIR)/ders/d.en/, $(IMG bullet_black.png) Programming in D)) -$(LI $(LINK2 http://wiki.dlang.org/Books, $(IMG bullet_black.png) Books)) -) -$(BR) -$(BR) - -
    Diğer
    -$(UL -$(LI $(LINK2 http://ddili.org/rss.xml, $(IMG rss-icon.png) RSS Beslemesi)) -$(LI $(LINK2 $(ROOT_DIR)/iletisim.html, $(IMG email.png) İletişim)) -$(LI $(LINK2 $(ROOT_DIR)/copyright.html, $(IMG cc_80x15.png) Hakları)) -) -$(BR) -$(GOOGLE_TRANSLATE) -$(BR) -$(BR) -$(FOOTER_DIV) diff --git a/ddili/src/ebook_font_manifest b/ddili/src/ebook_font_manifest deleted file mode 100644 index 850b19f..0000000 --- a/ddili/src/ebook_font_manifest +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/ddili/src/ebook_override.css b/ddili/src/ebook_override.css deleted file mode 100644 index d4da722..0000000 --- a/ddili/src/ebook_override.css +++ /dev/null @@ -1,142 +0,0 @@ -h4::before { - content: normal; -} - -h5::before { - content: "◼ "; -} - -pre.c_code -{ - background-color: #ffffff; -} - -pre.shell, pre.shell_small -{ - font-weight: normal; - border-top: dotted 1px #808080; - border-bottom: dotted 1px #808080; - background-color: #ffffff; - font-size: 1em; - line-height: 1.2; -} - -pre.d_code { - background-color: #ffffff; -} - -h4 { - color: black; - background-color: #e0e0e0; -} - -h4 > code { - color: black; - background-color: #e0e0e0; -} - -a.index_word { - text-decoration: none; -} - -pre.d_code, pre.c_code, div.quote, pre.mono, pre.mono_nobold -{ - border-top: dotted 1px #808080; - border-bottom: dotted 1px #808080; - line-height: 1.2; -} - -pre::before, pre::after { - border: initial; - position: initial; - width: initial; - content: initial; - z-index: initial; - margin: initial; -} - -div.frontispiece { - display: none; -} - -div.halftitle { - display: none; -} - -.foreword, .preface { - page-break-before: initial; -} - -div.toc_header, div.foreword, div.preface, div.frontmatter, div.first_chapter, div.index { - page-break-before: initial; -} - -.title { - page-break-before: initial; -} - -div.toc { - page-break-before: initial; -} - -div.index_section { - margin: initial; -} - -.d_inline, .d_inline_h4 { - font-size: 1em; -} - -h4 .d_inline { color:black; } - -pre.d_code, pre.c_code, div.quote, pre.mono, pre.mono_nobold { - font-size: 1em; -} - -table, tbody, tfoot, thead, tr, th, td, caption { - font-size: 1em; -} - -.d_keyword { - font-style: normal; - font-weight: normal; - font-family: monospace; -} - -.d_string { - font-style: normal; - font-weight: normal; - font-family: monospace; -} - -.d_comment { - font-style: normal; - font-weight: normal; - font-family: monospace; -} - -.hilite { - border: 1px solid #808080; - font-family: monospace; -} - -.d_hata { - border: 1px solid #808080; - font-style: normal; - font-weight: normal; - font-family: monospace; -} - -.shell_note { - border: 1px solid #808080; - font-style: normal; - font-weight: normal; - font-family: monospace; -} - -.shell_note_wrong, .code_note_wrong { - border: 1px solid #808080; - font-style: normal; - font-weight: normal; - font-family: monospace; -} diff --git a/ddili/src/ebooksanitizer.d b/ddili/src/ebooksanitizer.d deleted file mode 100644 index 6677fd9..0000000 --- a/ddili/src/ebooksanitizer.d +++ /dev/null @@ -1,35 +0,0 @@ -/** - * This program sanitizes the source HTML for ebook generation. - * - * 1) Replace Unicode box characters with their ASCII relatives. - */ - -import std.stdio; -import std.algorithm; -import std.string; -import std.regex; - -void main() { - const dchar[dchar] transTable = [ - '│' : '|', - '─' : '-', - '┬' : '+', - '┴' : '+', - '┌' : '+', - '└' : '+', - '┐' : '+', - '┘' : '+', - '├' : '+', - '┤' : '+', - ]; - - enum indexArrow = ctRegex!`⬁`; - enum imageDir = ctRegex!`src=".*/image/`; - - stdin - .byLine(KeepTerminator.yes) - .map!(l => l.translate(transTable)) - .map!(l => replaceAll(l, indexArrow, `==>`)) - .map!(l => replaceAll(l, imageDir, `src="image/`)) - .copy(stdout.lockingTextWriter); -} diff --git a/ddili/src/fonts.css b/ddili/src/fonts.css deleted file mode 100644 index f011a0f..0000000 --- a/ddili/src/fonts.css +++ /dev/null @@ -1,83 +0,0 @@ -@font-face { - font-family: ddili-sans-serif; - font-style: normal; - font-weight: normal; - src: url("fonts/opensans/OpenSans-Regular-webfont.woff") -} - -@font-face { - font-family: ddili-sans-serif; - font-style: normal; - font-weight: bold; - src: url("fonts/opensans/OpenSans-Bold-webfont.woff") -} - -@font-face { - font-family: ddili-sans-serif; - font-style: italic; - font-weight: normal; - src: url("fonts/opensans/OpenSans-Italic-webfont.woff") -} - -@font-face { - font-family: ddili-sans-serif; - font-style: italic; - font-weight: bold; - src: url("fonts/opensans/OpenSans-BoldItalic-webfont.woff") -} - -@font-face { - font-family: ddili-serif; - font-style: normal; - font-weight: normal; - src: url("fonts/andada/Andada-Regular.ttf") -} - -@font-face { - font-family: ddili-serif; - font-style: normal; - font-weight: bold; - src: url("fonts/andada/Andada-Bold.ttf") -} - -@font-face { - font-family: ddili-serif; - font-style: italic; - font-weight: normal; - src: url("fonts/andada/Andada-Italic.ttf") -} - -@font-face { - font-family: ddili-serif; - font-style: italic; - font-weight: bold; - src: url("fonts/andada/Andada-BoldItalic.ttf") -} - -@font-face { - font-family: ddili-monospace; - font-style: normal; - font-weight: normal; - src: url("fonts/dejavu/DejaVuSansMono-webfont.ttf") -} - -@font-face { - font-family: ddili-monospace; - font-style: normal; - font-weight: bold; - src: url("fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf") -} - -@font-face { - font-family: ddili-monospace; - font-style: italic; - font-weight: normal; - src: url("fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf") -} - -@font-face { - font-family: ddili-monospace; - font-style: italic; - font-weight: bold; - src: url("fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf") -} diff --git a/ddili/src/fonts/andada/Andada-Bold.ttf b/ddili/src/fonts/andada/Andada-Bold.ttf deleted file mode 100644 index bb72899..0000000 Binary files a/ddili/src/fonts/andada/Andada-Bold.ttf and /dev/null differ diff --git a/ddili/src/fonts/andada/Andada-BoldItalic.ttf b/ddili/src/fonts/andada/Andada-BoldItalic.ttf deleted file mode 100644 index ba5e82e..0000000 Binary files a/ddili/src/fonts/andada/Andada-BoldItalic.ttf and /dev/null differ diff --git a/ddili/src/fonts/andada/Andada-Italic.ttf b/ddili/src/fonts/andada/Andada-Italic.ttf deleted file mode 100644 index c32af0e..0000000 Binary files a/ddili/src/fonts/andada/Andada-Italic.ttf and /dev/null differ diff --git a/ddili/src/fonts/andada/Andada-Regular.ttf b/ddili/src/fonts/andada/Andada-Regular.ttf deleted file mode 100644 index 99c3560..0000000 Binary files a/ddili/src/fonts/andada/Andada-Regular.ttf and /dev/null differ diff --git a/ddili/src/fonts/andada/OFL.txt b/ddili/src/fonts/andada/OFL.txt deleted file mode 100644 index 23ef2e7..0000000 --- a/ddili/src/fonts/andada/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2011-2012, Carolina Giovagnoli (huertatipografica.com.ar), with Reserved Font Name 'Andada' - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/ddili/src/fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf b/ddili/src/fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf deleted file mode 100644 index b70b411..0000000 Binary files a/ddili/src/fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf and /dev/null differ diff --git a/ddili/src/fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf b/ddili/src/fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf deleted file mode 100644 index 14a08c9..0000000 Binary files a/ddili/src/fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf and /dev/null differ diff --git a/ddili/src/fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf b/ddili/src/fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf deleted file mode 100644 index e8f6cba..0000000 Binary files a/ddili/src/fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf and /dev/null differ diff --git a/ddili/src/fonts/dejavu/DejaVuSansMono-webfont.ttf b/ddili/src/fonts/dejavu/DejaVuSansMono-webfont.ttf deleted file mode 100644 index aaee9d1..0000000 Binary files a/ddili/src/fonts/dejavu/DejaVuSansMono-webfont.ttf and /dev/null differ diff --git a/ddili/src/fonts/opensans/OpenSans-Bold.ttf b/ddili/src/fonts/opensans/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43..0000000 Binary files a/ddili/src/fonts/opensans/OpenSans-Bold.ttf and /dev/null differ diff --git a/ddili/src/fonts/opensans/OpenSans-BoldItalic.ttf b/ddili/src/fonts/opensans/OpenSans-BoldItalic.ttf deleted file mode 100644 index 9bc8009..0000000 Binary files a/ddili/src/fonts/opensans/OpenSans-BoldItalic.ttf and /dev/null differ diff --git a/ddili/src/fonts/opensans/OpenSans-Italic.ttf b/ddili/src/fonts/opensans/OpenSans-Italic.ttf deleted file mode 100644 index c90da48..0000000 Binary files a/ddili/src/fonts/opensans/OpenSans-Italic.ttf and /dev/null differ diff --git a/ddili/src/fonts/opensans/OpenSans-Regular.ttf b/ddili/src/fonts/opensans/OpenSans-Regular.ttf deleted file mode 100644 index db43334..0000000 Binary files a/ddili/src/fonts/opensans/OpenSans-Regular.ttf and /dev/null differ diff --git a/ddili/src/iletisim.d b/ddili/src/iletisim.d deleted file mode 100644 index 0b10732..0000000 --- a/ddili/src/iletisim.d +++ /dev/null @@ -1,29 +0,0 @@ -Ddoc - -$(H4 İletişim) - -$(P -D diliyle ilgili her türlü soruyu ve konuyu $(LINK2 /forum/, Ddili forumunda) açabilirsiniz. -) - -$(P -Siteyle ilgili her türlü yorumunuzu da Ali Çehreli'ye yazabilirsiniz: $(LINK2 mailto:acehreli@yahoo.com, acehreli@yahoo.com) -) - - -$(BR) - -$(P -Bu site sürekli olarak yenilenmekte olduğu için, değişikliklerden haberdar olmak için RSS beslemesinden yararlanabilirsiniz. Doğrudan turuncu ikona tıklayarak RSS beslemesini tarayıcınıza tanıtabilirsiniz. Firefox buna LiveBookmarks olanağı adını veriyor. -) - -$(P -Teşekkürler! -) - -Macros: - SUBTITLE=İletişim - - DESCRIPTION=ddili.org iletişim bilgileri - - KEYWORDS=iletişim e-mail e-posta irtibat kim kimdir diff --git a/ddili/src/image/Androgynous_D_coders.png b/ddili/src/image/Androgynous_D_coders.png deleted file mode 100644 index 037a178..0000000 Binary files a/ddili/src/image/Androgynous_D_coders.png and /dev/null differ diff --git a/ddili/src/image/Androgynous_D_coders_5.5x7.png b/ddili/src/image/Androgynous_D_coders_5.5x7.png deleted file mode 100644 index 5b28004..0000000 Binary files a/ddili/src/image/Androgynous_D_coders_5.5x7.png and /dev/null differ diff --git a/ddili/src/image/book.png b/ddili/src/image/book.png deleted file mode 100644 index 3b0df24..0000000 Binary files a/ddili/src/image/book.png and /dev/null differ diff --git a/ddili/src/image/bullet_black.png b/ddili/src/image/bullet_black.png deleted file mode 100644 index 1751155..0000000 Binary files a/ddili/src/image/bullet_black.png and /dev/null differ diff --git a/ddili/src/image/by-nc-sa.png b/ddili/src/image/by-nc-sa.png deleted file mode 100644 index 01422cf..0000000 Binary files a/ddili/src/image/by-nc-sa.png and /dev/null differ diff --git a/ddili/src/image/cc_80x15.png b/ddili/src/image/cc_80x15.png deleted file mode 100644 index ed028fe..0000000 Binary files a/ddili/src/image/cc_80x15.png and /dev/null differ diff --git a/ddili/src/image/cc_88x31.png b/ddili/src/image/cc_88x31.png deleted file mode 100644 index 0f2a0f1..0000000 Binary files a/ddili/src/image/cc_88x31.png and /dev/null differ diff --git a/ddili/src/image/d_harfi.jpg b/ddili/src/image/d_harfi.jpg deleted file mode 100644 index e7e3f44..0000000 Binary files a/ddili/src/image/d_harfi.jpg and /dev/null differ diff --git a/ddili/src/image/d_harfi_renamed.jpg b/ddili/src/image/d_harfi_renamed.jpg deleted file mode 100644 index 51eec41..0000000 Binary files a/ddili/src/image/d_harfi_renamed.jpg and /dev/null differ diff --git a/ddili/src/image/d_source.png b/ddili/src/image/d_source.png deleted file mode 100644 index f5da5e3..0000000 Binary files a/ddili/src/image/d_source.png and /dev/null differ diff --git a/ddili/src/image/email.png b/ddili/src/image/email.png deleted file mode 100644 index f4390b1..0000000 Binary files a/ddili/src/image/email.png and /dev/null differ diff --git a/ddili/src/image/favicon.png b/ddili/src/image/favicon.png deleted file mode 100644 index cc6d7b1..0000000 Binary files a/ddili/src/image/favicon.png and /dev/null differ diff --git a/ddili/src/image/forum.png b/ddili/src/image/forum.png deleted file mode 100644 index 79c1ba9..0000000 Binary files a/ddili/src/image/forum.png and /dev/null differ diff --git a/ddili/src/image/gulen.png b/ddili/src/image/gulen.png deleted file mode 100644 index 74408e2..0000000 Binary files a/ddili/src/image/gulen.png and /dev/null differ diff --git a/ddili/src/image/oyundongusu.jpg b/ddili/src/image/oyundongusu.jpg deleted file mode 100644 index 3dad8bc..0000000 Binary files a/ddili/src/image/oyundongusu.jpg and /dev/null differ diff --git a/ddili/src/image/oyunlardahareket.png b/ddili/src/image/oyunlardahareket.png deleted file mode 100644 index b4e1f6c..0000000 Binary files a/ddili/src/image/oyunlardahareket.png and /dev/null differ diff --git a/ddili/src/image/pdficon_small.gif b/ddili/src/image/pdficon_small.gif deleted file mode 100644 index bb5edca..0000000 Binary files a/ddili/src/image/pdficon_small.gif and /dev/null differ diff --git a/ddili/src/image/pencil.png b/ddili/src/image/pencil.png deleted file mode 100644 index e2d67db..0000000 Binary files a/ddili/src/image/pencil.png and /dev/null differ diff --git a/ddili/src/image/penguen.png b/ddili/src/image/penguen.png deleted file mode 100644 index 95301db..0000000 Binary files a/ddili/src/image/penguen.png and /dev/null differ diff --git a/ddili/src/image/rss-icon.png b/ddili/src/image/rss-icon.png deleted file mode 100644 index 1679ab0..0000000 Binary files a/ddili/src/image/rss-icon.png and /dev/null differ diff --git a/ddili/src/image/sdlkoordinat.png b/ddili/src/image/sdlkoordinat.png deleted file mode 100644 index 333789a..0000000 Binary files a/ddili/src/image/sdlkoordinat.png and /dev/null differ diff --git a/ddili/src/image/vektorler1.jpg b/ddili/src/image/vektorler1.jpg deleted file mode 100644 index e5be9ae..0000000 Binary files a/ddili/src/image/vektorler1.jpg and /dev/null differ diff --git a/ddili/src/image/vektorler1.png b/ddili/src/image/vektorler1.png deleted file mode 100644 index a064f59..0000000 Binary files a/ddili/src/image/vektorler1.png and /dev/null differ diff --git a/ddili/src/image/vektorler2.png b/ddili/src/image/vektorler2.png deleted file mode 100644 index fe06d28..0000000 Binary files a/ddili/src/image/vektorler2.png and /dev/null differ diff --git a/ddili/src/image/vektorler3.png b/ddili/src/image/vektorler3.png deleted file mode 100644 index 84d4e45..0000000 Binary files a/ddili/src/image/vektorler3.png and /dev/null differ diff --git a/ddili/src/image/vektorler4.png b/ddili/src/image/vektorler4.png deleted file mode 100644 index 66ce569..0000000 Binary files a/ddili/src/image/vektorler4.png and /dev/null differ diff --git a/ddili/src/index.d b/ddili/src/index.d deleted file mode 100644 index bfa8f4c..0000000 --- a/ddili/src/index.d +++ /dev/null @@ -1,54 +0,0 @@ -Ddoc - -
    - ---- - -import std.stdio; - -void main() -{ - writeln("Merhaba dünya!"); -} - ---- - -
    - -
    - - - -
    -$(LINK2 /ders/d/index.html, -$(BR)Türkçe) -
    - -
    -$(LINK2 /ders/d.en/index.html, $(BR)English) -
    - -
    -$(LINK2 /ders/d.cn/index.html, $(BR)中文) -
    -
    - -$(H4 $(LINK2 /ders/index.html, Bütün kitaplar)) - -$(H4 $(LINK2 /forum/, Forum)) - -Macros: - SUBTITLE=Ana Sayfa - - DESCRIPTION=Türkçe D programlama dili kitabı, forum, dil ile ilgili belgeler, makaleler, kod örnekleri, forum, haberler, ve başka çeşitli bilgiler - - KEYWORDS=d programlama dili kitap ders forum - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/kurulum/breadcrumbs.ddoc b/ddili/src/kurulum/breadcrumbs.ddoc deleted file mode 100644 index bbb574e..0000000 --- a/ddili/src/kurulum/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /, Ana Sayfa) > Kurulum - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) > $(LINK2 /kurulum/, Kurulum) diff --git a/ddili/src/kurulum/dmd.d b/ddili/src/kurulum/dmd.d deleted file mode 100644 index e452e8f..0000000 --- a/ddili/src/kurulum/dmd.d +++ /dev/null @@ -1,102 +0,0 @@ -Ddoc - -$(H4 $(CH4 dmd'nin) otomatik olarak kurulması) - -$(P -$(C dmd)'yi kurmanın en kolay yolu, indirme sayfasındaki seçeneklerden $(I 1-click install) yazanlardan birisine tıklamaktır: -) - -$(P -$(LINK http://www.dlang.org/download.html) -) - -$(P -Orada hem Windows'un hem de Linux'un kurulum dosyaları bulunuyor. -) - -$(H4 $(CH4 dmd)'nin $(C .zip) dosyasından kurulması) - -$(P -Herhangi bir nedenle $(C .zip) dosyasını indirmeniz gerekiyorsa şu adımları izleyebilirsiniz: -) - -$(STEPS - -$(LI Derleyiciyi dlang.org'dan indirin: - -$(P - $(LINK http://www.dlang.org/download.html) -) -) - -$(LI -İndirdiğiniz zip dosyasını herhangi bir klasöre açın [unzip]. Örneğin $(CODE ~/dmd) -) - -$(LI - -Derleyici programlarının rahatça bulunabilmeleri için $(CODE ~/dmd/linux/bin32) klasörünü $(CODE PATH)'e ekleyin. $(CODE dmd)'yi $(CODE ~/dmd) klasörüne açtığınızı varsayarsak, bunu geçici olarak etkinleştirmek için şu komutu kullanabilirsiniz: - -$(SHELL -export PATH=$PATH:~/dmd/linux/bin32 -) - -$(P -Eğer dmd'yi her zaman için bir konsoldan başlatmak yeterli olacaksa ve konsol olarak bash kullanıyorsanız, o satırı $(CODE ~/.bashrc) dosyanıza ekleyin. Artık açtığınız konsollarda yalnızca $(CODE dmd) yazmak derleyicinin başlatılması için yeterli olacaktır. Bir sonraki adıma geçebilirsiniz. -) - -$(P -Eğer PATH'i menülerden başlatılan programlar için bile etkin hale getirmek istiyorsanız, $(CODE ~/.profile) dosyasına PATH'e klasörün tam yolunu ekleyen satırı yazın. Örneğin dmd'nin $(CODE /home/kullanici_ismi/dmd) gibi bir klasöre kurulduğunu varsayarsak, $(CODE ~/.profile) dosyanızın sonuna şöyle bir satır ekleyin: -) - -$(SHELL -PATH=$PATH:/home/kullanici_ismi/dmd/linux/bin32 -) - -$(P -Bu ayarın etkinleşmesi için oturumu kapatıp tekrar açmanız gerekebilir. -) -) - -$(P -$(CODE dmd)'nin bu noktada D programlarını derleyebilir duruma gelmiş olması gerekiyor. -) - -$(P -$(I Not: $(C dmd)'nin 64 bitlik olanını kullanmak için klasör yolunda $(C bin32) yerine $(C bin64) yazmanız gerekir. Bunun üretilen programların 32 bit veya 64 bit olması ile ilgisi yoktur; yalnızca $(C dmd)'nin kendisini etkiler.) -) - -$(LI -Derleyiciyle gelen örnek programların bulunduğu $(CODE ~/dmd/samples/d) klasörüne geçin ve örnekleri derlemeyi deneyin: - -$(SHELL -dmd hello.d -) - -Eğer başarıyla derlenirse aynı klasör içinde $(CODE hello) isminde yeni bir program oluşacaktır. Çalıştırmak için: - -$(SHELL -./hello -) - -Herşey yolunda gittiyse ekranda şöyle bir çıktı olması gerekir: - -$(SHELL -hello world -args.length = 1 -args[0] = './hello' -) - -Programı değişik sayıda argümanla çalıştırmayı da deneyebilirsiniz: - -$(SHELL -./hello 42 "bir arada" -) -) -) -Macros: - SUBTITLE=dmd Kurulumu - - DESCRIPTION=Digital Mars firmasının D derleyicisi dmd'nin Linux'ta ve Windows'da kurulması - - KEYWORDS=d programlama dili derleyici dmd indir kur diff --git a/ddili/src/kurulum/emacs_d-mode.d b/ddili/src/kurulum/emacs_d-mode.d deleted file mode 100644 index 42ffc73..0000000 --- a/ddili/src/kurulum/emacs_d-mode.d +++ /dev/null @@ -1,86 +0,0 @@ -Ddoc - -$(H5 Emacs'e d-mode düzenleme modunu eklemek) - -$(P - Kurmak için: -) - -$(STEPS - -$(LI $(P $(CODE d-mode.el) dosyasını şu adresten indirin: -) - -$(P $(LINK https://github.com/Emacs-D-Mode-Maintainers/Emacs-D-Mode/blob/master/d-mode.el)) - -) - -$(LI $(P -İndirdiğiniz dosyayı Emacs'in lisp dosyaları için baktığı bir klasöre kopyalayın (root yetkisi gerekebilir!). Uygun klasörlerin listesini şu komutla görebilirsiniz:) - -$(P $(CODE -C-h v load-path -)) - -$(P -Benim sistemimdeki kopyalama komutu şöyleydi: -) - -$(SHELL -cp d-mode.el /usr/share/emacs/21.4/site-lisp -) -) - -$(LI -$(P -Aslında $(CODE d-mode.el)'yi derlemek hız kazancı sağlayacaktır ama benim ortamımda da şu bilinen hatayı verdi: -) - -$(P -$(CODE error: "`c-lang-defconst' must be used in a file") -) - -$(P -Eğer sizin ortamınızda çalışırsa Emacs'in içindeyken $(CODE d-mode.el)'yi derleyin: -) - -$(P -$(CODE M-x byte-compile-file) -) - -$(P -ve oluşan $(CODE d-mode.elc) dosyasını da site-lisp klasörüne kopyalayın. -) - -$(SHELL -cp d-mode.elc /usr/share/emacs/21.4/site-lisp -) - -) - -$(LI $(P - $(CODE .emacs) dosyanıza şu satırları ekleyin:) - -$(P -$(SHELL -(add-to-list 'auto-mode-alist '("\\.d\\'" . d-mode)) -(autoload 'd-mode "d-mode" "Major mode for D programs" t) -) -) -) - -$(P -Artık uzantısı $(CODE .d) olan dosyalar d-mode'da açılacaklardır. Emacs içindeyken d-mode'u kendiniz de şöyle başlatabilirsiniz: -) - -$(P -$(CODE M-x d-mode) -) -) - -Macros: - SUBTITLE=d-mode Kurulumu - - DESCRIPTION=Emacs'e d-mode modunun eklenmesi - - KEYWORDS=d programlama dili emacs d-mode indir kur diff --git a/ddili/src/kurulum/gdc.d b/ddili/src/kurulum/gdc.d deleted file mode 100644 index aba9ce9..0000000 --- a/ddili/src/kurulum/gdc.d +++ /dev/null @@ -1,107 +0,0 @@ -Ddoc - -$(H5 Linux'ta GNU'nun Derleyicisi $(CODE gdc)'nin Kurulması) - -$(P $(RED $(B Not:) Bu yazı yazıldığı sırada gdc yalnızca D'nin birinci sürümünü (D1) desteklemektedir. D2 hem çok daha modern olanaklar içerdiği için, hem de D.ershane'de de D2 anlatıldığı için, ben gdc yerine dmd'yi kurmanızı öneririm.) -) - -$(P Eğer kullandığınız Linux dağıtımı $(CODE gdc)'yi paket olarak veriyorsa, en kolayı paket yöneticisini kullanmaktır. $(CODE gdc)'nin paket isminin $(CODE gcc-gdc) olduğunu ve paket yöneticisi olarak $(CODE yum)'un kullanıldığını varsayarsak, şu komutu $(CODE root) olarak işletmeniz yeterli olabilir: -) - -$(SHELL -yum install gcc-gdc -) - -$(P -Örneğin eğer Ubuntu kullanıyorsanız, $(CODE gdc) pakedini şu adreste arayabilirsiniz: -) - -$(P -$(LINK http://packages.ubuntu.com/) -) - -$(P -Şimdiki CentOS 5 ortamımda öyle bir paket bulunmadığı için ben programı projenin sitesinden indirerek kurmaya karar verdim... -) - -$(STEPS - -$(LI Derleyiciyi SourceForge'dan indirin: -$(P -$(LINK http://sourceforge.net/project/showfiles.php?group_id=154306) -) - -$(P -Bu yazıyı yazdığım sırada 0.24 sürümündeler. Seçenekler içinden kendi ortamınıza uyan $(CODE tar) dosyasını seçin. Örneğin $(CODE gdc-0.24-i686-linux-gnu-gcc-4.1.2.tar.bz2)... -) -) - -$(LI - -İndirdiğiniz $(CODE tar) dosyasını açın: - -$(SHELL -tar jxvf gdc-0.24-i686-linux-gnu-gcc-4.1.2.tar.bz2 -) - -$(P -Derleyici dosyaları $(CODE gdc) isimli bir klasöre açılacaklardır. -) -) - -$(LI -Program dosyalarının bulunduğu $(CODE gdc/bin) klasörünü $(CODE PATH)'e ekleyin. Örneğin dosyaların $(CODE ~/gdc) klasörüne açıldıklarını varsayarsak: - -$(SHELL -export PATH=$PATH:~/gdc/bin -) - -$(P -$(CODE gdc)'nin bu noktada D programlarını derleyebilir duruma gelmiş olması gerekir. Derleyicini bulunup çalıştırılabildiğini denemek için -) - -$(SHELL -gdc --version -) - -yazabilirsiniz. Çıkışa derleyici sürümü ile ilgili bilgilerin yazılmış olması gerekir. -) - -$(LI - Derleyiciyi denemek için bir "merhaba dünya" programı yazın ve $(CODE merhaba.d) ismiyle kaydedin: - ---- -import std.stdio; -void main() -{ - printf("merhaba dünya\n"); -} ---- -) - -$(LI -Programı derleyin -$(SHELL gdc merhaba.d -o merhaba -) - -$(P Eğer çalışırsa aynı klasör içinde $(CODE merhaba) isminde bir program oluşacaktır. Çalıştırmayı deneyin: -) - -$(SHELL ./merhaba) - -$(P Ekrana şu çıktıyı vermesi gerekir: - -$(SHELL -merhaba dünya -) -) -) - -) - -Macros: - SUBTITLE=gdc Kurulumu - - DESCRIPTION=GNU'nun D derleyicisi gdc'nin kurulması - - KEYWORDS=d programlama dili derleyici gdc indir kur diff --git a/ddili/src/kurulum/index.d b/ddili/src/kurulum/index.d deleted file mode 100644 index efd27df..0000000 --- a/ddili/src/kurulum/index.d +++ /dev/null @@ -1,69 +0,0 @@ -Ddoc - -$(H4 D Programlama Dili ile İlgili Programların Kurulması) - -$(P -Burada D programları yazarken kullanılan programlardan bazılarının nasıl kurulduklarını bulacaksınız. -) - -$(H5 Derleyiciler) - -$(P -Bu yazının yazıldığı günlerde D derleyicisi olarak üç seçenek var. D'nin yeni olanakları hep öncelikle Digital Mars derleyicisi olan $(C dmd) üzerinde geliştirilmişlerdir. Bu yüzden $(C dmd)'nin daha yetenekli olduğunu düşünebiliriz. -) - -$(P -Derleyicilerin hepsi ücretsiz, kullanımları tamamen serbest ve hepsi de yaygın ortamları destekliyor: Linux, Windows, ve Mac. -) - -$(UL - -$(LI $(B $(LINK2 /kurulum/dmd.html, Linux'ta $(CODE dmd)'nin kurulması)): $(CODE dmd), D dilinin yaratıcısı ve ana destekçisi olan Digital Mars firmasının derleyicisidir. -) - -$(LI $(B $(LINK2 /kurulum/gdc.html, Linux'ta $(CODE gdc)'nin kurulması)): $(CODE gdc), çoğumuzun $(CODE gcc) ile yakından tanıdığı GNU'nun derleyicisidir. $(B Uyarı:) Ana $(C gcc) sürümü bu yazıyı yazdığım sırada D2 desteğini henüz içermiyor ama geliştirici sürümlerinde dilin 2.053 sürümü destekleniyor. -) - -$(LI Halen geliştirilmekte olan $(LINK2 http://www.dsource.org/projects/ldc, $(CODE ldc)). $(B Uyarı:) Henüz D2'yi desteklemiyor. -) - -) - -$(H5 Geliştirme Ortamları) - -$(P -Geliştirme ortamı seçenekleri için dlang.org sitesine bakabilirsiniz: -) - -$(P -$(LINK http://wiki.dlang.org/IDEs) -) - -$(P -$(LINK http://wiki.dlang.org/Editors) -) - -$(H5 $(C dmd) derleyicisinin Windows'da Code::Blocks altında kurulması) - -$(P -Kurulum adımlarını Ddili Forum'da $(LINK2 http://ddili.org/forum/thread/2, esatarslan52'nin açtığı bir konuda) bulabilirsiniz. -) - -$(P -Code::Blocks projelerinde Türkçe harfler kullanabilmek için de forumdaki şu yazıdan yararlanabilirsiniz: $(LINK http://ddili.org/forum/post/8) -) - -$(H5 Emacs $(CODE d-mode) Metin Düzenleme Modu) - -$(P -Eğer hâlâ Emacs'ta ısrar eden bir programcıysanız D kaynak kodlarını yazmanızda yardımcı olması için $(LINK2 /kurulum/emacs_d-mode.html, d-mode'u kurmanızı) öneririm. Emacs'in iyi taraflarından birisi, dosyaları doğal olarak UTF-8 düzeninde kaydetmesidir. -) - -Macros: - SUBTITLE=Kurulum - - DESCRIPTION=D programlama dili derleyicilerinin ve araçlarının kurulmaları - - KEYWORDS=d programlama dili derleyici araç gereç program kur indir - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/kurulum/locale.properties b/ddili/src/kurulum/locale.properties deleted file mode 100644 index d750231..0000000 --- a/ddili/src/kurulum/locale.properties +++ /dev/null @@ -1,346 +0,0 @@ -# locale.properties by Neil Hodgson neilh@scintilla.org -# Placed in the public domain 2001 - -# locale.properties defines the localised text for the user interface -# Some definitions are commented out because they duplicate another string -# The format of each line is original=localised, such as File=&Fichier -# Even though the original text may have ellipses "..." and access key -# indicators "&" in the user interface, these do not appear in this file -# for the original texts. Translated texts should have an access key indicator -# if needed as the translated text may not include the original access key. -# Ellipses are automatically added when needed. -# The "/" character should not be used in menu entries as on GTK+ the "/" is -# used to specifiy the menu hierarchy and so will produce extra menu items. -# Each original text may have only one translation, even if it appears in -# different parts of the user interface. - -# Please state any further license conditions and copyright notices you desire. -# If there are no further notices then contributed translations will be assumed -# to be made freely available under the same conditions as SciTE. -# Email addresses included in this file may attract spam if the file is published. - -# Define the encoding of this file so that on GTK+ 2, the file can be -# reencoded as UTF-8 as that is the GTK+ 2 user interface encoding. -# A common choice for European users is LATIN1. For other locales look at -# the set of encodings supported by iconv. - -# 'Help' bölümü 'Hakkında' kısmında (1.74'te, muhtemelen önceki sürümlerde de) Türkçe karakterler hatalı -# gözüküyor,1.75'te sorun yok - -translation.encoding=UTF-8 - -# Menus - -# File menu -File=&Dosya -New=&Yeni -Open=&Aç -Open Selected Filename=&Seçili Dosyayı Aç -Revert=&Eskiye Dön -Close=&Kapat -Save=&Kaydet -Save As=&Farklı Kaydet -Export=&Aktar -As HTML=&HTML Formatında -As RTF=&RTF Formatında -Page Setup=&Sayfa Yapısı -Print=&Yazdır -Load Session=&Oturum Yükle -Save Session=Oturumu Kaydet -Exit=&Çıkış - -# Edit menu -Edit=&Düzen -Undo=&Geri Al -Redo=&Yinele -Cut=&Kes -Copy=&Kopyala -Paste=&Yapıştır -Delete=&Sil -Select All=&Tümünü Şeç -Copy as RTF=&RTF Formatında Kopyala -Match Brace=Parantezi Karşılaştır -Select to Brace=Uygun Parantezi Göster -Show Calltip=Prototipi Göster -Complete Symbol=&Sembolü Tamamla -Complete Word=&Kelimeyi Tamamla -Expand Abbreviation=Kısaltmayı Aç -Block Comment or Uncomment=Seçili Alanı Yoruma Çevir/Geri Al -Box Comment=Yorum Alanı Olarak Düzenle -Stream Comment=Yorum Aralığı Olarak Düzenle -Make Selection Uppercase=Seçili Alanı Büyük Harfe Çevir -Make Selection Lowercase=Seçili Alanı Küçük Harfe Çevir - -# Search menu -Search=&Ara -Find=&Bul -Find Next=&Sonrakini Bul -Find Previous=&Öncekini Bul -Find in Files=&Dosya İçerisinde Bul -Replace=&Değiştir -Go to=&Git -Next Bookmark=&Sonraki Yer İmi -Previous Bookmark=&Önceki Yer İmi -Toggle Bookmark=&Diğer Yer İmine Geç -Clear All Bookmarks=&Tüm Yer İmlerini Sil - -# View menu -View=&Görünüm -Toggle current fold=&Aktif Bloğu Küçült/Genişlet -Toggle all folds=&Tüm Blokları Küçült/Genişlet -Full Screen=&Tam Ekran -Tool Bar=&Araç Çubuğu -Tab Bar=&Sekme Çubuğu -Status Bar=&Durum Çubuğu -Whitespace=&Boşluk -End of Line=&Satır Sonu -Indentation Guides=&Girinti İşaretleri -Line Numbers=&Satır Numaraları -Margin=&Kenar -Fold Margin=&Blok Kenarlığı -Output=&Çıktı Penceresi -Parameters=&Parametreler - -# Tools menu -Tools=&Araçlar -Compile=&Derle -Build=&Oluştur -Go=&Çalıştır -Stop Executing=&Çalıştırmayı Durdur -Next Message=&Sonraki Mesaj -Previous Message=&Önceki Mesaj -Clear Output=&Çıktı Penceresini Boşalt -Switch Pane=&Diğer Pencereye Geç - -# Options menu -Options=&Seçenekler -Always On Top=&Her Zaman Üstte -Vertical Split=&Pencereler Yan Yana -Line End Characters=&Satır Sonu Karakterleri -CR + LF=CR &+ LF -CR=&CR -LF=&LF -Convert Line End Characters=&Satır Sonu Karakterlerini Dönüştür -Change Indentation Settings=&Girinti Ayarlarını Değiştir -Use Monospaced Font=&Eş Aralıklı Yazı Tipi -Open Local Options File=&Yerel Ayarlar Dosyasını Aç -Open User Options File=&Kullanıcı Ayarları Dosyasını Aç -Open Global Options File=&Global Ayarlar Dosyasını Aç -Open Abbreviations File=&Kısaltmalar Dosyasını Aç - -# Language menu -Language=&Dil - -# Buffers menu -Buffers=&Sekmeler -Previous=&Önceki -Next=&Sonraki -Close All=&Tümünü kapat - -# Help menu -Help=&Yardım -About Sc1=&Sc1 Hakkında -About SciTE=&SciTE Hakkında - -# Dialogs - -# Generic dialog -OK=&Tamam -Cancel=&İptal -Yes=&Evet -No=&Hayır - -# About dialog -#About SciTE= -# This is to add something like: Swahili translation 1.41.1 by Neil Hodgson -TranslationCredit=Türkçe çevirisini yapan (1.73): Adsız Kahraman ;)\nDüzeltme (1.76): Ali Çehreli -Contributors:=Katkıda bulunanlar: - -# Open, Save dialogs -Open File=Dosya Aç -Save File=Dosyayı Kaydet -Save File As=Dosyayı Farklı Kaydet -Export File As HTML=HTML Formatında Aktar -Export File As RTF=RTF Formatında Aktar -Save Current Session=Oturumu Kaydet -Custom Filter=Özel Filtre - -# Find in Files dialog -#Find in Files= -Find what:=&Aranan: -Files:=&Dosyalar: -#Find= - -# Go To dialog -Go To=&Git -Destination Line Number:=&Hedef satır: -Current line:=Aktif satır: -Last line:=Son satır: - -# Indentation Settings dialog -Indentation Settings=Girinti Ayarları -Tab Size:=&Tab Adımı: -Indent Size:=&Girinti Adımı: -Use tabs:=Tab &Kullan: - -# Replace and Find dialogs -#Replace= -#Find= -#Find what:= -Replace with:=Yeni &değer: -Match whole word only=&Yalnızca tam sözcük -Match case=&Büyük/küçük harfe duyarlı -Regular expression=&Düzenli ifade -Wrap around=Başa &sarmalı -Transform backslash expressions=&Ters eğik çizgi (\) ibarelerini dönüştür -#Find Next= -Replace All=&Tümünü Değiştir -Replace in Selection=Seçili &Alanda Değiştir -#Close= -Direction=&Yön -Reverse direction=&Ters yönde -Up=&Yukarı -Down=&Aşağı - -# Parameters dialog -Execute=&Çalıştır -Set=&Ayarla - -# Other UI strings -Untitled=Adsız - -# Properties used in global options -Text=Text -All Source=Kaynak dosyalar -All Files (*.*)=Bütün dosyalar (*.*) - -# Messages -# Messages may contain variables such as file names or search strings indicated -# by ^0 which are replaced by values before display. ^1, ^2, ... may be used in the future. -Can not find the string '^0'.=Dizgi bulunamıyor '^0'. -Find string must not be empty for 'Replace All' command.='Tümünü değiştir' komutu için 'bul' dizgisi boş olamaz. -Selection must not be empty for 'Replace in Selection' command.='Seçili alanda değiştir' komutu için seçili alan boş olamaz. -No replacements because string '^0' was not present.=Değiştirilmedi; '^0' dizgisi mevcut değil. -Could not open file '^0'.='^0' dosyası açılamadı. -Could not save file '^0'.='^0' dosyası kaydedilemedi. -Save changes to '^0'?= '^0' kaydedilsin mi? -Save changes to (Untitled)?=(Adsız) kaydedilsin mi? -The file '^0' has been modified. Should it be reloaded?='^0' dosyası değiştirilmiş. Yeniden yüklensin mi? -Bad file.=Hatalı dosya. -Failed to create dialog box: ^0.=Diyalog penceresi oluşturulamadı: ^0. -Can not start printer document.=Yazıcı dokümanı başlatılamadı. -URI '^0' not understood.=URI '^0' anlaşılamadı. -Invalid directory '^0'.=Geçersiz klasör '^0'. - -# 1.42 -Directory:=Klasör: -Wrap=&Satırları katla -Hide=&Gizle -Check if already open=Açık &mı kontrol et - -# 1.43 -Find string must not be empty for 'Replace in Selection' command.='Seçili alanda değiştir' komutu için 'Bul' dizgisi boş olamaz. -List Macros=Makroları Listele -Run Current Macro=Aktif Makroyu Çalıştır -Record Macro=Makroyu Kaydet -Stop Recording Macro=Makro Kaydını Durdur -SciTE Help=&SciTE Yardım -Sc1 Help=&Sc1 Yardım -Edit Properties=&Özellikleri Düzenle -Wrap Output=Çıktı Satırlarını Katla - -# 1.44 -Read-Only=&Salt Okunur -READ=OKU - -# 1.46 -As TeX=&TeX Formatında -Export File As TeX=TEX Formatında Aktar -Save a Copy=&Kopyasını Farklı Kaydet - -# 1.47 -As LaTeX=Latex Formatında -Export File As LaTeX=Latex Formatında Aktar -Encoding=Karakter Kodlaması -8 Bit=&8 Bit -UCS-2 Big Endian=UCS-2 &Big Endian -UCS-2 Little Endian=UCS-2 &Little Endian -UTF-8=&UTF-8 - -# 1.49 -Save All=Tümünü &Kaydet -Browse=&Göz At -Select a folder to search from=Arama yapmak için klasör seçin -UTF-8 Cookie=UTF-8 &Çerezler - -# 1.50 -Insert Abbreviation=Kısaltma &Ekle -Abbreviation:=Kısaltma: -Insert=&Ekle -Mark All=Tümünü &Seç - -# 1.51 -In Selection=Seçili Alanda -Paragraph=Para&graf -Join=&Birleştir -Split=&Ayır - -# 1.52 -Block comment variable '^0' is not defined in SciTE *.properties!=Yorum alanı değişkeni'^0' SciTE *.properties içinde tanımlanmamış! -Box comment variables '^0', '^1' and '^2' are not defined in SciTE *.properties!=Yorum alanı değişkenleri '^0', '^1' ve '^2' SciTE *.properties içinde tanımlanmamış! -Stream comment variables '^0' and '^1' are not defined in SciTE *.properties!=Yorum aralığı değişkenleri '^0' and '^1' SciTE *.properties içinde tanımlanmamış! -The file '^0' has been modified outside SciTE. Should it be reloaded?='^0' dosyası SciTE dışında değiştirilmiş. Yeniden yüklensin mi? -As PDF=&PDF Formatında -Export File As PDF=PDF Formatında Aktar - -# 1.53 -Version=Sürüm -by=Yazar: - -#1.54 -Incremental Search=Kademeli &Arama -Search for:=Aranan: - -#1.55 -Could not save file '^0'. Save under a different name?='^0' dosyası kaydedilemedi. Farklı bir isimle kaydedilsin mi? - -#1.56 -As XML=XML Formatında -Export File As XML=XML Formatında Aktar - -#1.57 -Destination Line:=Hedef &Satır: -Column:=&Sütun: - -#1.58 -Replacements:=Değiştirme: -Open Files Here=Dosyaları Burada Aç - -#1.59 - -#1.60 - -#1.61 -File '^0' is ^1 bytes long,\nlarger than the ^2 bytes limit set in the properties.\nDo you still want to open it?='^0' dosyası ^1 bayt uzunluğunda ,\nözelliklerde belirtilen ^2 bayt sınırını aşıyor.\nYine de açmak istiyor musunuz? -Open Lua Startup Script=Lua Başlangıç Betiğini Aç -All Files (*)=Tüm Dosyalar (*) -Hidden Files (.*)=Gizli Dosyalar (.*) - -#1.62 -Show hidden files=Gizli dosyaları göster - -#1.63 -Replace in Buffers=Sekmelerde Değiştir -Find string must not be empty for 'Replace in Buffers' command.='Sekmelerde değiştir' komutu için bul dizgisi boş olamaz. -Search only in this style:=Sadece bu biçemde ara: - -#1.67 -Duplicate=Ç&oğalt - -#1.72 -Convert=Dönüştür - -#1.73 -Code Page Property=Kod Sayfası Özellikleri -UTF-8 with BOM=BOM ile UTF-8 -Open Directory Options File=Klasör Seçenekleri Dosyasını Aç diff --git a/ddili/src/makale/bellek.d b/ddili/src/makale/bellek.d deleted file mode 100644 index 47cf816..0000000 --- a/ddili/src/makale/bellek.d +++ /dev/null @@ -1,463 +0,0 @@ -Ddoc - -$(H4 Bellek Yönetimi) - -$(P - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) 14 Temmuz 2009 -$(BR) - $(B İngilizcesi:) $(LINK2 http://wiki.dlang.org/Memory_Management, Memory Management) -) - -$(P -Her ciddi program bellek ayırma ve geri verme işlemleriyle ilgilenmek zorundadır. Programların karmaşıklıkları, boyutları, ve hızları arttıkça bu işlemlerin önemi de artar. D'de bellek yönetimi için birçok seçenek bulunur. -) - -$(P -D'de bellek ayırma konusunda 3 temel nesne türü vardır: -) - -$(OL -$(LI Programın statik veri bölgesinde [static data segment] yer alan $(I statik veriler)) -$(LI CPU'nun program yığıtında [program stack] yer alan $(I yığıt verileri)) -$(LI Çöp toplamalı bellekte yer alan ve dinamik olarak ayrılan $(I çöp toplamalı veriler)) -) - -$(P -Bunların programda kullanılmaları ve bazı ileri bellek yönetimi teknikleri şunlardır: -) - -$(UL_FARK -$(HEADER_INDEX yazinca_kopyalama, Dizgilerde (ve Dizilerde) $(I Yazınca Kopyalama) [copy-on-write]) -$(HEADER_INDEX gercek_zaman, Gerçek Zamanlı [real time] Programlama) -$(HEADER_INDEX kesintisiz, Kesintisiz Çalışma) -$(HEADER_INDEX serbest, Serbest Bellek Listeleri [free list]) -$(HEADER_INDEX referans, Referans Sayma) -$(HEADER_INDEX nesne_ayirma, Nesne Ayırmanın Özel Olarak Tanımlanması) -$(HEADER_INDEX isaretle, İşaretle ve Geri Ver [mark/release]) -$(HEADER_INDEX raii, Kaynakları Bozucu Fonksiyonlarda Geri Verme: RAII (Resource Acquisition is Initialization)) -$(HEADER_INDEX yigit, Sınıf Nesnelerini Yığıtta Oluşturmak) -$(HEADER_INDEX dizi, Yığıtta İlklenmemiş Diziler Oluşturmak) -$(HEADER_INDEX kesme, Kesme Servisleri [Interrupt Service Routines] (ISR)) -) - -$(HEADER yazinca_kopyalama, Dizgilerde (ve Dizilerde) $(I Yazınca Kopyalama) [copy-on-write]) - -$(P -Bir fonksiyona bir dizinin gönderildiği bir örneği ele alalım; fonksiyon dizide duruma göre değişiklik yapıyor olsun ve sonuçta değişiklik yaptığı bu diziyi fonksiyondan döndürsün. Diziler fonksiyonlara değer olarak değil, referans olarak gönderildikleri için; dizi içeriğinin kime ait olduğu cevaplanması gereken önemli bir sorudur. Örneğin bir dizideki ASCII karakterleri büyük harfe çeviren bir fonksiyon şöyle yazılabilir: -) - ---- -char[] büyük_harfe_çevir(char[] s) -{ - int i; - - for (i = 0; i < s.length; i++) - { - char c = s[i]; - if ('a' <= c && c <= 'z') - s[i] = c - (cast(char)'a' - 'A'); - } - return s; -} ---- - -$(P -Dikkat edilirse, fonksiyonu çağıran taraftaki dizideki karakterlerin de değiştirildikleri görülür. Bunun istenen bir sonuç olmayabileceğinin yanı sıra, $(CODE s[]) yazılamayan bir bellek bölgesine bağlı bir dizi dilimi bile olabilir. -) - -$(P -Bunun önüne geçmek için $(CODE büyük_harfe_çevir()) içinde $(CODE s[])'nin bir kopyası alınabilir. Ancak bu da dizinin zaten bütünüyle büyük harflerden oluştuğu durumlarda tamamen gereksiz bir işlemdir. -) - -$(P -Burada çözüm, $(I yazınca kopyalama) yöntemini kullanmaktır; dizi ancak gerçekten gerekiyorsa kopyalanır. Bazı dizgi işleme dillerinde bu işlem otomatiktir ama otomatik olmasının büyük bir bedeli de vardır: "abcdeF" gibi bir dizgi 5 kere kopyalanmak zorunda kalacaktır. Dolayısıyla, bu yöntemden en yüksek verimin alınabilmesi için bunun kod içinde açıkça yapılması gerekir. $(CODE büyük_harfe_çevir()) $(I yazınca kopyalama) yönteminden yararlanacak şekilde etkin olarak şöyle yazılabilir: -) - ---- -char[] büyük_harfe_çevir(char[] s) -{ - int değişti; - int i; - - değişti = 0; - for (i = 0; i < s.length; i++) - { - char c = s[i]; - if ('a' <= c && c <= 'z') - { - if (!değişti) - { char[] r = new char[s.length]; - r[] = s; - s = r; - değişti = 1; - } - s[i] = c - (cast(char)'a' - 'A'); - } - } - return s; -} ---- - -$(P -D'nin Phobos kütüphenesindeki dizi fonksiyonları $(I yazınca kopyalama) yönteminden yararlanırlar. -) - -$(HEADER gercek_zaman, Gerçek Zamanlı [real time] Programlama) - -$(P -Gerçek zamanlı programlama, işlemlerin en fazla ne kadar gecikmeyle tamamlanacaklarının garanti edilmesidir. Bu gecikme, malloc/free'nin ve çöp toplamanın da aralarında bulunduğu çoğu bellek yönetimi işlemlerinde teorik olarak sınırsızdır. Bu durumda en güvenilir yöntem; gerekli belleğin önceden ayrılmasıdır. Böylece gecikmeye tahammülü olmayan işlem sırasında bellek ayrılmaz ve çöp toplayıcı çalışmayacağı için gecikme miktarı belirli bir sınırın altında kalır. -) - -$(HEADER kesintisiz, Kesintisiz Çalışma) - -$(P -Gerçek zamanlı programlamaya bağlı olarak, çöp toplayıcının belirsiz anlarda çalışmaya başlayarak programda duraksamalara neden olmasının önlenmesi gerekir. Örnek olarak bir savaş oyunu programını düşünebiliriz. Her ne kadar programda bir bozukluk olarak kabul edilmese de, oyunun rastgele duraksaması kullanıcıyı son derece rahatsız edecektir. -) - -$(P -Duraksamaları ortadan kaldırmanın veya hiç olmazsa azaltmanın bazı yolları vardır: -) - -$(UL -$(LI Gereken bütün belleği programın kesintisiz olarak çalışması gereken yerinden önce ayırmak) -$(LI Çöp toplayıcıyı programın zaten durmuş olduğu yerlerde açıkça çağırmak. Örneğin kullanıcıdan giriş beklenen bir yerde... Böylece çöp toplayıcının kendi başına çalışma olasılığı azalmış olur.) -$(LI Kesintisiz çalışması gereken yerden önce $(CODE std.gc.disable())'ı, sonra $(CODE std.gc.enable())'ı çağırmak. Çöp toplayıcı çalışmaya karar vermek yerine öncelikle yeni bellek ayırmayı tercih edecektir.) -) - -$(HEADER serbest, Serbest Bellek Listeleri [free list]) - -$(P -Serbest bellek listeleri, sıklıkla ayrılıp tekrar geri verilen türlerde büyük hız kazancı sağlar. Aslında çok basit bir fikirdir: işi biten nesne geri verilmek yerine, bir serbest bellek listesine yerleştirilir. Bellek gerektiği zaman da öncelikle bu listeye bakılır. -) - ---- -class Foo -{ - static Foo serbestler_listesi; // listenin başı - - static Foo ayır() - { Foo f; - - if (serbestler_listesi) - { f = serbestler_listesi; - serbestler_listesi = f.sonraki; - } - else - f = new Foo(); - return f; - } - - static void geri_ver(Foo f) - { - f.sonraki = serbestler_listesi; - serbestler_listesi = f; - } - - Foo sonraki; // bağlı liste için - ... -} - -void deneme() -{ - Foo f = Foo.ayır(); - ... - Foo.geri_ver(f); -} ---- - -$(P -Bu tür listeler son derece hızlı olabilirler. -) - -$(UL -$(LI Birden fazla iş parçacığıyla kullanıldığında $(CODE ayır()) ve $(CODE geri_ver()) fonksiyonlarının erişim sıralarının yönetilmesi gerekir. [synchronization]) -$(LI Foo kurucusu $(CODE ayır()) içinde tekrar tekrar çağrılmadığı için döndürülen nesnenin en azından bazı üyelerinin değerlerinin tekrardan atanması gerekebilir.) -$(LI Bu yönteme RAII'nin eklenmesine gerek yoktur, çünkü atılan bir hata yüzünden $(CODE geri_ver())'in çağrılamadığı durumlarda kaybedilen bellek, çöp toplayıcı tarafından geri alınacaktır.) -) - -$(HEADER referans, Referans Sayma) - -$(P -Burada fikir, nesne içinde bir sayaç barındırmaktır. Sayaç, nesneye yapılan her referansta arttırılır, sonlandırılan her referansta da azaltılır. Sayacın değeri sıfıra indiğinde artık nesne geri verilebilir demektir. -) - -$(P -D'de referans saymaya yönelik olanaklar bulunmadığı için istendiğinde elle yapılması gerekir. -) - -$(P -$(LINK2 http://www.dlang.org/COM.html, Win32 COM programlama), referans sayıları için $(CODE AddRef()) ve $(CODE Release()) fonksiyonlarını kullanır. -) - -$(HEADER nesne_ayirma, Nesne Ayırmanın Özel Olarak Tanımlanması) - -$(P -D'de her tür nesne için özel ayırma ve geri verme fonksiyonları tanımlanabilir. Nesneler normalde çöp toplamalı bellek bölgesinde yer alırlar; çöp toplayıcı çalışmaya karar verdiğinde de sonlanmış olan nesnelerin bellekleri otomatik olarak geri alınır. Ayırma ve geri verme işlemlerini bir tür için özel olarak tanımlamak için $(CODE new) ve $(CODE delete) bildirimleri kullanılır. Örneğin bu işi C kütüphanesindeki $(CODE malloc) ve $(CODE free) ile yapmak için: -) - ---- -import std.c.stdlib; -import std.outofmemory; -import std.gc; - -class Foo -{ - new(size_t uzunluk) - { - void* p; - - p = std.c.stdlib.malloc(uzunluk); - if (!p) - throw new OutOfMemoryException(); - std.gc.addRange(p, p + uzunluk); - return p; - } - - delete(void* p) - { - if (p) - { std.gc.removeRange(p); - std.c.stdlib.free(p); - } - } -} ---- - -$(P -$(CODE new())'ün özel olarak tanımlanmasıyla ilgili önemli bazı bilgiler: -) - -$(UL -$(LI $(CODE new())'ün dönüş türü belirtilmiyor olsa da $(CODE void*)'dir ve $(CODE new())'ün $(CODE void*) döndürmesi gerekir.) - -$(LI Bellek ayıramadığı zaman null döndürmek yerine bir hata atması gerekir. Atılan hatanın türü programcıya kalmıştır. Bu örnekte bir $(CODE OutOfMemoryException) atılmaktadır.) - -$(LI Döndürülen bellek sistemin normal yerleştirme aralığında [alignment] olmalıdır. Örneğin win32 sistemlerinde bu adım 8'dir.) - -$(LI $(CODE uzunluk) parametresinin nedeni, ayırma fonksiyonunun $(CODE Foo)'nun bir alt türü tarafından çağrılmış olabileceği, ve o alt türün $(CODE Foo)'dan daha büyük yer tutuyor olabileceğidir.) - -$(LI Çöp toplamalı belleği gösteren kök işaretçiler taranırken statik veri bölgesine ve program yığıtına da bakılır. C'nin çalışma ortamı tarafından kullanılan bellek buna dahil değildir. Bu yüzden, $(CODE Foo)'nun veya ondan türemiş olan bir türün üyelerinin çöp toplamalı belleği gösterdiklerinde, bu durumdan çöp toplayıcının haberdar edilmesi gerekir. Bu işlem $(CODE std.gc.addRange()) ile yapılır.) - -$(LI Belleğin ilklenmesine gerek yoktur; $(CODE new()) çağrıldıktan hemen sonra sınıf üyelerine zaten ilk değerleri verilir, ve varsa kurucu fonksiyon çalıştırılır.) -) - - -$(P -$(CODE delete())'in özel olarak tanımlanmasıyla ilgili önemli bazı bilgiler: -) - -$(UL -$(LI Bozucu fonksiyon, $(CODE p)'nin gösterdiği bölgede zaten çalıştırılmıştır; dolayısıyla gösterdiği yerde artık çöp değerler bulunmaktadır.) - -$(LI $(CODE p) işaretçisi null olabilir.) - -$(LI Eğer çöp toplayıcıya $(CODE std.gc.addRange()) ile haber verilmişse, geri veren fonksiyonda da ona karşılık bir $(CODE std.gc.removeRange()) çağrısı bulunması gerekir.) - -$(LI Eğer bir $(CODE delete()) tanımlanmışsa, ona karşılık gelen bir $(CODE new())'ün de tanımlanmış olması gerekir.) - -) - -$(P -Bir sınıf için özel bellek ayırma ve geri verme fonksiyonları yazıldığında, bellek sızıntısı ve geçersiz işaretçiler gibi sorunları önlemek için çok dikkatli olmak gerekir. Özellikle hata atma durumlarında bellek sızıntılarını önlemek için RAII yöntemini uygulamak çok önemlidir. -) - -$(P -Bu özel fonksiyonlar $(CODE struct) ve $(CODE union) türleri için de tanımlanabilirler. -) - -$(HEADER isaretle, İşaretle ve Geri Ver [mark/release]) - -$(P -Bu yöntem program yığıtına benzer. Bellekte program yığıtı gibi kullanılacak bir yer ayrılır ve nesneler için gereken yer, bu bellekte ilerleyen bir işaretçinin değeri değiştirilerek $(I ayrılır). Bazı noktalara $(I işaretler koyulur) ve yığıt işaretçisine tekrar o noktalardan birisi göstertilerek belleğin bir bölümü tümden $(I geri verilmiş) olur. -) - ---- -import std.c.stdlib; -import std.outofmemory; - -class Foo -{ - static void[] bellek; - static int indeks; - static const int uzunluk = 100; - - static this() - { void *p; - - p = malloc(uzunluk); - if (!p) - throw new OutOfMemoryException; - std.gc.addRange(p, p + uzunluk); - bellek = p[0 .. uzunluk]; - } - - static ~this() - { - if (bellek.length) - { - std.gc.removeRange(bellek); - free(bellek); - bellek = null; - } - } - - new(size_t gereken_uzunluk) - { void *p; - - p = &bellek[indeks]; - indeks += gereken_uzunluk; - if (indeks > bellek.length) - throw new OutOfMemory; - return p; - } - - delete(void* p) - { - // [Çevirenin notu: Bu yöntemde Foo nesneleri teker - // teker 'delete' ile geri verilmeyecekleri için, - // 'delete'in yanlışlıkla çağrıldığı durumları - // yakalamak için burada program sonlandırılıyor.] - assert(0); - } - - static int yerini_işaretle() - { - return indeks; - } - - static void geri_ver(int i) - { - indeks = i; - } -} - -void test() -{ - int m = Foo.yerini_işaretle(); - Foo f1 = new Foo; // yer ayır - Foo f2 = new Foo; // yer ayır - ... - Foo.geri_ver(m); // f1 ve f2'yi birlikte geri ver -} ---- - -$(P -$(CODE bellek[]) için ayrılan yer $(CODE std.gc.addRange(p, p + uzunluk);) ile çöp toplayıcıya bildirildiği için, açıkça geri verilmesi gerekmez. -) - -$(HEADER raii, Kaynakları Bozucu Fonksiyonlarda Geri Verme: RAII (Resource Acquisition is Initialization)) - -$(P -RAII yöntemi, ayırma ve geri verme fonksiyonlarının açıkça kod içinde yapıldığı durumlarda bellek sızıntılarının önlenmesinde çok yararlıdır. Bu tür sınıflara $(LINK2 http://www.dlang.org/attribute.html#scope, $(CODE scope) niteliği) eklemek bu konuda yarar sağlar. -) - -$(HEADER yigit, Sınıf Nesnelerini Yığıtta Oluşturmak) - -$(P -Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar: -) - -$(UL -$(LI fonksiyonlarda yerel tanımlandıklarında) -$(LI $(CODE new) ile oluşturulduklarında) -$(LI $(CODE new) argümansız kullanıldığında (yine de kurucu için argüman kullanılabilir)) -$(LI $(CODE scope) depolama türü ile tanımlandıklarında) -) - -$(P -Çöp toplayıcının bu nesne için zaman geçirmesi gerekmeyeceği için bu etkin bir yöntemdir. Ama böyle nesneleri gösteren referansların fonksiyondan çıkıldığı anda geçersiz hale geldiklerine dikkat etmek gerekir. -) - ---- -class C { ... } - -scope c = new C(); // c yığıttadır -scope c2 = new C(5); // yığıttadır -scope c3 = new(5) C(); // özel ayırıcısı ile ayrılmıştır ---- - -$(P -Eğer sınıfın bir bozucusu varsa, kapsamdan (örneğin fonksiyondan) hangi nedenle olursa olsun çıkılırken, o bozucu fonksiyon çağrılır (hata atıldığında bile). -) - -$(HEADER dizi, Yığıtta İlklenmemiş Diziler Oluşturmak) - -$(P -D'de diziler normalde otomatik olarak ilklenirler. Dolayısıyla şu kodda $(CODE dizi[]) elemanlarının ilk değerleri hiç kullanılmadıklarından ($(CODE diziyi_doldur()) tarafından üzerlerine yazıldığı varsayılırsa) bu kod daha hızlandırılabilir. -) - ---- -void foo() -{ byte[1024] dizi; - - diziyi_doldur(dizi); - ... -} ---- - -$(P -Eğer programın bu kod yüzünden gerçekten hız kaybettiği kanıtlanmışsa, elemanların ilklenmelerinin istenmediği $(CODE void) ile belirtilir: -) - ---- -void foo() -{ byte[1024] dizi = void; - - diziyi_doldur(dizi); - ... -} ---- - -$(P -Ancak, yığıttaki ilklenmemiş verilerle ilgili bazı uyarıları akılda tutmak gerekir: -) - -$(UL -$(LI -Yığıttaki ilklenmemiş veriler de çöp toplayıcı tarafından taranırlar ve başka bellek bölgelerine referanslar barındırıp barındırmadıklarına bakılır. Yığıtta bulunan ilklenmemiş değerler daha önce program tarafından kullanılan nesnelerin kalıntıları oldukları için, bu eski değerler çöp toplayıcı belleğindeki nesneleri gösteriyor gibi algılanabilirler ve çöp toplayıcı belleğindeki bu şanssız nesneler hiçbir zaman geri verilmeyebilirler. Bu gerçekten karşılaşılan bir hatadır ve farkedilip giderilmesi çok belalı olabilir. -) - -$(LI -Bir fonksiyon, kendisini çağırana bir yığıt nesnesi referansı döndürme hatasına düşmüş olabilir. Bu referans yine yığıtın ilklenmemiş bir bölgesindeki geçerli bir nesneyi gösteriyor gibi algılanabilir ve programın davranışını garip ve tutarsız hale getirebilir. Oysa yığıttaki verilerin her zaman için ilklenmeleri, böyle olası hataların hiç olmazsa tutarlı ve tekrarlanabilir olmalarını sağlar. -) - -$(LI -İlklenmemiş veri doğru kullanıldığında bile hatalara yol açabilir. D'nin tasarım hedeflerinden birisi, programların güvenilirliklerini ve taşınabilirliklerini arttırmaktır. İlklenmemiş veriler; tanımsız, taşınamaz, hatalı, ve tutarsız davranışların kaynaklarıdırlar. Bu yüzden verileri ilklemeden kullanmak, ancak ve ancak hız kazancı için diğer yollar tükendiğinde ve gerçekten bir hız kazancı sağladığı kanıtlandığında denenmelidir. -) - -) - -$(HEADER kesme, Kesme Servisleri [Interrupt Service Routines] (ISR)) - -$(P -Çöp toplayıcı her çalıştığında yığıtlarını ve yazmaçlarını [register] taramak için bütün iş parçacıklarını [thread] durdurmak zorundadır. Eğer bir kesme servisi iş parçacığı durdurulursa, bütün program göçebilir. -) - -$(P -Bu yüzden kesme servislerinin durdurulmamaları gereklidir. $(CODE std.thread) modülündeki fonksiyonlar tarafından başlatılan iş parçacıkları durdurulurlar. Ama C'nin $(CODE _beginthread()) ve benzeri fonksiyonları tarafından başlatılan iş parçacıkları durdurulmazlar, çünkü çöp toplayıcının bunların varlığından haberi yoktur. -) - -$(P -Bunun başarıyla çalışabilmesi için: -) - -$(UL -$(LI -ISR iş parçacığı çöp toplamalı kaynaklar kullanmamalıdır. Yani global $(CODE new) kullanılamaz, dinamik dizilerin boyları değiştirilemez, ve çağrışımlı dizilere yeni elemanlar eklenemez. D kütüphanesinin ISR tarafından her kullanımı dikkatle incelenmeli ve çöp toplayıcı belleğinin kullanılmadığından emin olunmalıdır. Daha da iyisi, kesme servisi D çalışma zamanı kütüphanesindeki hiçbir fonksiyonu çağırmamalıdır. -) - -$(LI -ISR, çöp toplamalı bellekteki bir bölgenin tek referansı olamaz; çünkü bundan haberi olmayan çöp toplayıcı belleği geri verebilir. Bir çözüm, durdurulan bir iş parçacığında veya bir globalde de o bellek bölgesini gösteren bir referans tutmaktır. -) - -) - - -Macros: - SUBTITLE="Bellek Yönetimi", Walter Bright - - DESCRIPTION=D'de etkin bellek yönetimi yöntemleri. - - KEYWORDS=d programlama dili makale d tanıtım bellek yönetimi çöp toplayıcı diff --git a/ddili/src/makale/breadcrumbs.ddoc b/ddili/src/makale/breadcrumbs.ddoc deleted file mode 100644 index b48da97..0000000 --- a/ddili/src/makale/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /, Ana Sayfa) > Makaleler - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) > $(LINK2 /makale/, Makaleler) diff --git a/ddili/src/makale/d_dilimleri.d b/ddili/src/makale/d_dilimleri.d deleted file mode 100644 index 0b84f8b..0000000 --- a/ddili/src/makale/d_dilimleri.d +++ /dev/null @@ -1,349 +0,0 @@ -Ddoc - -$(H4 D Dilimleri) - -$(P - $(B Yazar:) Steven Schveighoffer -$(BR) - $(B Çevirmen:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Çeviri Tarihi:) Mayıs 2011 -$(BR) - $(B İngilizcesi:) $(LINK2 http://dlang.org/d-array-article.html, D Slices) -) - -$(P -D dilinin en hoş olanaklarından birisi dilimleridir $(ASIL slice). D'den başka bir dil kullandığım zamanlarda hep D dilimlerini arıyorum. Yazımlarının kısalığı ve hızlı olmalarının yanında dilimler çok da kullanışlıdırlar. -) - -$(P -Burada D dilimlerinin ve dizilerinin iç yapılarını anlatacağım. Bunları okuduğunuzda D dilimlerinin doğru kullanımlarını ve onların normal dizilerden temelde ne kadar farklı olduklarını göreceğinizi umuyorum. -) - -$(H5 Taşmalı Bir Sorun) - -$(P -Diziler çoğu dilin bir iç olanağıdır; kendi elemanlarına sahiptirler ve referans türü olarak kullanılırlar. Bütün bu dil yapısına "dizi" adı verilir ve bunlar üzerindeki işlemlerin (değer atama, dinamik dizilere veri ekleme, uzunluk alma vs.) dizi türünün özel işlemleri oldukları söylenir. -) - -$(P -D'nin geçmişi C'ye dayanır; diziler C'de yan yana duran veriler olarak tanımlanırlar. C'de dizilere ve dizilerin bölümlerine referans olarak gösterge $(ASIL pointer) kullanılır. C dizilerine erişim sağlayan göstergeler dizinin elemanlarına sahip değillerdir. Desteklenen bütün işlemler, göstergenin belirli miktar ilerisindeki veya gerisindeki verinin değerinin okunması veya o değerin değiştirilmesidir. -) - -$(P -C dizilerinin söz dizimini bildiğinizi düşünüyorum; yine de dizi yazımları başka dillerde farklı olduğundan önce C dizilerinin kullanımlarını göstermek istiyorum: -) - ---- -dizi[0] = 4; /* 'dizi' dizisinin ilk elemanını 4 yapar */ -x = dizi[1]; /* 'dizi' dizisinin ikinci elemanının değerini - x'e kopyalar */ ---- - -$(P -Bunun dışındaki bütün işlemler (uzunluk alma, ekleme, yer ayırma, sonlandırma) kütüphane işlevlerine, tahminlere ve belgelere dayanır. Bunun yanlışı nerededir? C dizilerinin en büyük sorunu, diziye ait olmayan verilere bile gösterge ile erişilebilmesidir. Sıfırdan küçük olan sıra numaraları bile kullanılabilir! Üstelik dizi için kullanılan gösterge türü ile tek değişken için kullanılan gösterge türü aynıdır. Örneğin gösterge türündeki bir işlev parametresi bir dizi olabileceği gibi tek bir değişken de olabilir. Bu, ara bellek taşırma saldırılarının $(ASIL buffer overflow attacks) dayandığı sorundur. Bu konuda daha ayrıntılı bilgi için Walter Bright'ın $(LINK2 http://drdobbs.com/blogs/cpp/228701625, C's Biggest Mistake) (C'nin En Büyük Hatası) makalesini okuyabilirsiniz. -) - -$(H5 Dilimler) - -$(P -Peki D bu durumu nasıl düzeltir? D dizileri bir çok açıdan C dizilerine benzerler. Hatta D, C'nin göstergeli dizi söz dizimini de destekler. Ancak D, C dizi söz dizimi üzerine kurulu olan $(I dilim) adında yeni bir tür getirir. Dilim, bir dizinin (dinamik olsun olmasın) belirli bir bölümüdür; hem o bölümü gösteren göstergeyi hem de o bölümün uzunluğunu bir arada tutar. Veri uzunluğunun biliniyor olmasının ve verinin depolandığı belleğin çöp toplayıcı tarafından sahipleniliyor olmasının etkisiyle, dilimler çeşitli bellek hatalarına karşı korumalı olan son derece güçlü ve dinamik bir kavramdır. Ek olarak, D dilimlerinin işlevsellikleri, ilk parametreleri dilim olan işlevler yazılarak arttırılabilir. Bu olanak, dilin iç türlerine nitelik ve üye işlev gibi çağrılabilen yeni işlevler eklemeye yarar. Başka dillerdeki eşdeğerlerinin gariplikleri veya yavaşlıkları ile karşılaştırıldıklarında, D dilimlerinin hoş ve kısa söz dizimleri ile çok hızlı işleyen kodlar üretilebilir. -) - -$(P -D dilimlerinin nasıl kullanıldıklarına bakalım: -) - ---- -import std.stdio; - -void main() -{ - int[] a; // a bir dilimdir - - a = new int[5]; // en az 5 elemanı bulunan dinamik - // bir tamsayı dizisi için yer ayır - // ve bana ilk 5 tanesine eriştiren - // bir dilim ver. D'de bütün veriler - // ilklenirler; int'lerin ilk - // değerleri 0'dır; o yüzden bu - // dizide beş tane 0 vardır - - int[] b = a[0..2]; // Bu bir 'dilimleme' işlemidir. b, - // a'nın ilk iki elemanına erişim - // sağlamaktadır. D'de sınırın sağ - // tarafı "açıktır"; bu yüzden a[2] - // dilime dahil değildir. - - int[] c = a[$-2..$]; // c, a'nın son iki elemanına erişim - // sağlamaktadır; $, dilimleme ve - // indeksleme işleminde uzunluk - // anlamına gelir - - c[0] = 4; // bu a[3]'ü de değiştirir - c[1] = 5; // bu a[4]'ü de değiştirir - - b[] = c[]; // a[]'nın ilk iki elemanına son iki - // elemanının değerlerini (4, 5) atar - - writeln(a); // "[4, 5, 0, 4, 5]" yazar - - int[5] d; // d, program yığıtında duran sabit - // uzunluklu bir dizidir - b = d[0..2]; // dilimler sabit uzunluklu dizilere - // de erişim sağlayabilirler! -} ---- - -$(P -Dizinin oluşturulmasıyla ilgili açıklamayı şaşırtıcı bulabilirsiniz: "en az 5 elemanı bulunan dinamik bir tamsayı dizisi için yer ayır ve bana ilk 5 tanesine eriştiren bir dilim ver". Neden yalnızca "5 elemanı bulunan dizi için yer ayır" değil? Deneyimli programcılar bile bazen D'nin dizi kavramlarıyla ilgili karışıklıklar yaşarlar; haksız oldukları söylenemez. Göründüklerinin aksine, D dilimleri normal dinamik dizi türleri $(I değillerdir) (en azından perde arkasında). Dilimler, her türlü diziye (dinamik olsun olmasın) güvenli ve basit bir $(I arayüz) sunarlar. D dilimleri hakkındaki belki de en yaygın olan yanılgıya geçelim. -) - -$(H5 Sorumlusu Kim?) - -$(P -D dilimleri hemen hemen bütün açılardan dinamik dizilere benzerler—normalde işlevlere referans olarak gönderilirler ve dinamik dizilerin sahip olmaları beklenen bütün işlevlere ve niteliklere sahiptirler. Ancak bir açıdan çok farklıdırlar. Dilim diziye $(I sahip değildir), ona $(I erişim sağlar). Bunun anlamı, dilimin verilerin oluşturulmalarından ve sonlandırılmalarından sorumlu olmadığıdır. Dinamik dizilerin belleklerinden sorumlu olan, D $(I çalışma ortamıdır) $(ASIL D runtime). -) - -$(P -Peki D'nin gerçek dinamik dizi türü nerededir? Dinamik dizi kavramı çalışma ortamı içinde gizlidir ve aslında resmi bir türü yoktur. Dilimler yeterlidir; hatta veri ile ne yapılmak istendiğini çalışma ortamı anlayabildiği için, dinamik dizilerin başlıca tür olmadıklarının farkına bile varılmaz. Dahası, çoğu D programcısı D dilimlerinin dinamik dizilerle $(I aynı) tür olduklarını düşünür—dilimler D'nin resmi tanım belgelerinde bile dinamik dizi türü olarak geçerler! Elemanlara sahip olunmadığı gerçeği çok ince bir noktadır ve kolayca gözden kaçabilir. -) - -$(P -Bunun doğurduğu bir sonuç; $(C length)'in dizilerin değil, dilimlerin niteliği olduğudur; yani $(C length) dizinin değil, dilimin uzunluğunu bildirir. Bu, dile yeni başlayanlara karışık gelen bir konudur. Örneğin aşağıdaki kodda bununla ilgili olan büyük bir yanlışlık vardır: -) - ---- -import std.stdio; - -void kısalt(int[] dizi) -{ - if(dizi.length > 2) - dizi.length = 2; -} - -void main() -{ - int[] dizi = new int[5]; - kısalt(dizi); - dizi.kısalt(); // üye işlev gibi de çağrılabilmektedir - writeln(dizi.length); // 5 yazar -} ---- - -$(P -Gönderilen $(C dizi)'nin uzunluğunu 2 olarak değiştiriyor gibi görünse de o işlevin aslında hiçbir etkisi yoktur (bunu $(C writeln)'ın çıktısında görüyoruz). Bunun nedeni, verinin referans olarak gönderilmiş olmasına karşın dilimin parçası olan göstergenin ve uzunluğun değer olarak gönderilmiş olmalarıdır. Bir çok dilde bütünüyle referans olarak gönderilen dizi türleri vardır. Örneğin C# ve Java dizileri, referans olarak gönderilen $(C Object) nesneleridir. C++'ın $(C vector) türü; hem veriyi hem de nitelikleri ya hep birden referans olarak, ya da hep birden değer olarak geçirir. -) - -$(P -Bu sorunu gidermek için iki çözüm vardır. Ya dilim $(C ref) anahtar sözcüğü ile açıkça referans olarak gönderilir ya da sonuç dilim asıl dilime atanmak üzere işlevden döndürülür. Örneğin dilimin referans olarak gönderilmesi için işlev şöyle değiştirilir: -) - ---- -void kısalt(ref int[] dizi) ---- - -$(P -Bu değişikliği yaptığımızı düşünelim; kısaltma işleminden sonra ikinci elemandan ötedeki verilere ne olur? D'de dilimler verilere sahip olmadıklarından o elemanlar dinamik diziye ait olmaya devam ederler ve geçerliliklerini korurlar. Bunun temel nedeni, aynı veriye başka bir dilim tarafından da erişim sağlanıyor olabilmesidir! $(I Tek) dilim veriye sahip olmadığından, hiçbir dilim veriye başka hangi dilimler tarafından erişim sağlandığı yolunda tahminde bulunamaz. -) - -$(P -Peki o veriye artık hiçbir dilim erişim sağlamadığında ne olur? Bu, D'nin çöp toplayıcısı ile ilgili bir konudur. Artık hiçbir dilim tarafından erişim sağlanmayan dinamik dizilerin sonlandırılmaları çöp toplayıcının görevidir. Hatta, D'nin dilimlerinin var olabilmeleri büyük ölçüde çöp toplayıcı sayesindedir; bellek sızıntısı, dilimlerin birbirlerinin üzerlerine yazmaları, veya dizi yaşam süreçleri gibi kaygılara hiç düşmeden dinamik dizileri istediğiniz gibi dilimleyebilirsiniz. -) - -$(H5 Dilimlere Eleman Eklemek) - -$(P -Gerçek dinamik dizilerde olduğu gibi D dilimlerine de eleman eklenebilir. Ekleme ve birleştirme için $(C ~) işleci kullanılır. Dizilerin ekleme ve birleştirme işlemleri aşağıdaki gibidir: -) - ---- -int[] a; // boş dilim hiçbir veriye erişim sağlamaz ama - // kendisine yine de eleman eklenebilir - -a ~= 1; // eleman eklenir; elemanları tutmak için -a ~= 2; // otomatik olarak yeni bir dizi oluşturulur - -a ~= [3, 4]; // başka bir dizi eklenir; burada 'dizi hazır - // değeri' (array literal) ekleniyor -a = a ~ a; // a'yı kendisi ile birleştirir; şimdi a'nın - // durumu şudur: [1, 2, 3, 4, 1, 2, 3, 4] - -int[5] b; // program yığıtındaki sabit uzunluklu bir dizi -a = b[1..$]; // şimdi a, b'nin bir dilimidir -a ~= 5; // bu işlemden önce a yığıttaki verilere erişim - // sağlamakta olduğundan, sonuna eklemek yeni - // yer ayrılmasına neden olur ve herşey - // beklendiği gibi çalışır ---- - -$(P -Performansa önem veren okuyucular o dört eleman eklendiğinde neler olup bittiğini merak edeceklerdir. Dilim kendi verilerine sahip olmadığına göre her eleman ekleme işleminde yeni bellek ayrılması nasıl önlenebilir? D dilimlerinin temel bir şartı etkin olmalarıdır; aksi taktirde programcılar tarafından benimsenmezlerdi. D'nin bu konuda getirdiği çözüm programcılar tarafından hemen hemen hiç farkedilmez; ve dilimler işte bu yüzden gerçek dinamik dizilere çok benzerler. -) - -$(H5 Nasıl Çalışır) - -$(P -"En az 5 elemanı bulunan dinamik bir dizi için yer ayır" dediğimi hatırlıyor musunuz? İşte çalışma ortamı bu noktada devreye girer. Gereken bellek bir $(I bellek sayfasına) $(ASIL page) (32 bitlik x86 sistemlerinde 4096 baytlık bölge) sığdığında, bellek ayıran birim hep 2'nin katları uzunlukta bellek bölgeleri ayırır; daha fazla bellek gereken durumlarda ise yeteri kadar sayfa ayırır. Yani bir dizi ayrıldığında çoğu durumda asıl gerekenden daha fazla yer ayrılmış olabilir. Örneğin toplam 20 bayt yer tutan beş adet int için yer gerektiğinde aslında 32 baytlık yer ayrılır. Bunun sonucunda da 3 int'lik daha yer var demektir. -) - -$(P -Kolayca görülebileceği gibi, dizinin yerini değiştirmeye gerek kalmadan eleman eklemek mümkündür. Ancak, mevcut başka verilerin üzerine yazılmasını $(ASIL stomping) önlemek de şarttır. Hatırlarsanız, dilimin o verinin başka dilimler tarafından da eriştirilip eriştirilmediğini bilemiyor olmasının yanında, kendisinin bile dizinin hangi bölümüne erişim sağladığından haberi yoktur (baştaki elemanlara da erişim sağlıyor olabilir ortadakilere de). Bu düzeneği doğru olarak işletebilmek için, çalışma ortamı bellek bölgesinin ne kadarının $(I kullanımda) olduğunu ayırdığı bellek bloğunun içine yazar. (Bunun sonucunda da boş olan yer aslında daha azdır. Örneğin yukarıdaki örnekte yeni bellek ayırmak gerekmeden kullanılabilecek tamsayı adedi aslında 7'dir.) -) - -$(P -Bir dilime eleman eklemek istediğimizde, çalışma ortamı önce bloğun eklenebilir olduğuna (yani $(I kullanımda) alanının geçerli olduğuna) ve dilim ile geçerli verilerin aynı noktada $(I sonlandıklarına) bakar (dilimin nerede başladığının önemi yoktur). Çalışma ortamı yeni verinin bellek bölgesinin son tarafına sığıp sığmayacağına da bakar. Eğer bu iki durum da geçerliyse, yeni veri dizinin sonundaki boş yere yazılır ve bloğun ne kadarının $(I kullanımda) olduğunu gösteren değer buna uygun olarak arttırılır. İki durumdan birisi bile geçersizse hem dizideki eski verileri hem de yeni verileri alabilecek kadar büyük yeni bir bellek bölgesi ayrılır ve bütün veriler buraya kopyalanırlar. Peki eski bloğa ne olur? Eğer onu kullanan başka dilimler varsa hiçbir şey olmaz. Eğer ona erişim sağlayan başka hiçbir referans yoksa o bölge artık çöp olarak kabul edilir ve çöp toplayıcının bir sonraki temizlik işlemi sırasında, daha sonradan başka bir amaçla kullanılmak üzere hazır hale getirilir. Dilimler diğer dilimleri geçersiz hale getirmeden böylece başka yere taşınabilirler. Bu durum, dizi taşıma ve vector'e eleman ekleme işlemlerinin aynı elemanlara erişim sağlamakta olan başka referansları (göstergeleri $(ASIL pointer) ve erişicileri $(ASIL iterator)) geçersiz hale getirdikleri C ve C++ dillerindeki durumlardan çok farklıdır. -) - -$(P -Sonuçta hem hızlı hem de bir çok açıdan kullanışlı olan bir ekleme işlemi sağlanmış olur. Hız veya veri bozulması gibi kaygılardan arınmış olarak dilimlere istediğiniz biçimde ekleme yapabilirsiniz. Dilimin erişim sağladığı verinin ne yığında $(ASIL heap), program yığıtında $(ASIL stack), veya ROM'da bulunup bulunmadığının ne de $(C null) olup olmadığının bilinmesi gerekir. Ekleme işlemi programcı açısından her zaman için başarıyla sonuçlanır (yeterli bellek olduğunu kabul edersek); duruma göre farklı olabilen işlemler perde arkasında çalışma ortamı tarafından halledilir. -) - -$(H5 Belirsizlik) - -$(P -Ekleme işleminin deneyimli D programcılarını bile yanıltan bir özelliği, bu işlemin sonucunun belirsizmiş gibi algılanabilmesidir. -) - -$(P -Verilen diziye belirtilen sayıda A yazan (gerekiyorsa ekleyen) ve doldurduğu diziyi döndüren şöyle bir işlev olsun: -) - ---- -import std.stdio; - -char[] A_doldur(char[] dizi, size_t adet) -{ - if(dizi.length < adet) - dizi.length = adet; // uzunluğu, bütün A'ları tutacak - // kadar arttır - dizi[0..adet] = 'A'; // bütün elemanları A yap - return dizi[0..adet]; // sonucu döndür -} - -void main() -{ - char[] dizgi = new char[10]; // bunun için ayrılan bloğun - // kapasitesi 15 elemandır - dizgi[] = 'B'; - A_doldur(dizgi, 20); // başka yere taşınması gerekir (20 > 15) - writeln(dizgi); // "BBBBBBBBBB" - A_doldur(dizgi, 12); // taşınmadan uzayabilir (12 <= 15)! - writeln(dizgi); // "AAAAAAAAAA" -} ---- - -$(P -$(C A_doldur) işlevinin hatası nedir? Hatası yoktur; peki uzunluğun arttırılması dizinin taşınmasını gerektiriyorsa ne olur? O durumda işleve gönderilen dizi değil, taşınmış olan dizi A'larla doldurulur. Bu da aynı dizinin tekrar kullanılacağı durumlarda veya gönderilen dizinin A'larla doldurulmasının istendiği durumlarda şaşırtıcı olmaktadır. Sonuçta, $(C dizi[])'nin bulunduğu bellek bloğunun taşıma gerekmeden eklenebilmesine bağlı olarak gönderilen dizi A'larla doldurulmuş olabilir de olmayabilir de. -) - -$(P -Biraz düşününce, bu belirsizliği önlemenin tek yolunun dizinin her ekleme işleminde kopyalanması olduğunu görürüz—yeni verinin bir yere yazılmasının gerektiği doğrudur ama sistem, veriye erişim sağlayan her dilimin hesabını tutamaz. Neyse ki bu durumun etkisini azaltacak iki seçenek vardır: -) - -$(OL - -$(LI İşlevin dönüş türünü tekrar dilime atamak. Dikkat ederseniz bu işlevin en önemli sonucu dönüş türüdür; dilimin kullanılıp kullanılmadığının işlev açısından önemi yoktur.) - -$(LI İşleve gönderilen dilimi artık kullanmamak. Gönderilen dilim bir daha kullanılmazsa onunla ilgili bir sorun da kalmaz.) - -) - -$(P -İşlevin yazarı da bu sorunları önleyebilir. Dikkat edilirse, bu sorun yalnızca gönderilen dilime ek yapıldıktan veya uzunluğu değiştirildikten $(B sonra) dilimin başlangıçtaki bölümüne yazıldığında ortaya çıkmaktadır. Bu durum önlenebildiğinde dilimlerle ilgili bu belirsizlik de ortadan kalkmış olur. Çalışma ortamının dilimi nasıl kullanacağıyla ilgili bilgi edinmeye yarayan nitelikleri biraz sonra göreceğiz. Gönderilen dilimin değiştirilip değiştirilmeyeceğinin belirsiz olduğu da işlevin belgesinde belirtilebilir. -) - -$(P -Son bir seçenek, gönderilen dilimin değişeceğini göstermiş olmak için işlev parametresini $(C ref) olarak tanımlamaktır. Dilimin sağ değer $(ASIL rvalue) olabildiği gibi durumlarda bu her zaman mümkün olmayabilir. Ayrıca bu, aynı veriye erişim sağlamakta olan başka referanslar için de çözüm olamaz. -) - -$(H5 Meta Bilgiyi Kaydetmek $(ASIL Caching)) - -$(P -Dilimlerle ilgili başka bir konu, ekleme işleminin hızlı olması ama yeterince hızlı olamamasıdır. Blokla ilgili meta bilginin (başlangıç adresinin, uzunluğunun ve kullanımda olan miktarın) her ekleme işleminde tekrardan okunması gerekir. Bunun anlamı, her ekleme işlemi sırasında çöp toplayıcının bellek bölgeleri içinde O(lg(n)) karmaşıklığında bir arama yapılacak olduğudur (çöp toplayıcının evrensel kilidinin sahiplenilmesi de gerekir). Oysa istenen, her eklemenin amortize sabit zamanda $(ASIL amortized constant time) gerçekleşmesidir. Bu hedefe, bildiğim kadarıyla yalnızca D'de bulunan ve meta bilgiyi kaydetmeye dayanan bir yöntemle ulaşılır. -) - -$(P -D2'de varsayılan depolama biçimi iş parçacığına özeldir $(ASIL thread local). Bunun sonucunda, yığındaki verinin tek iş parçacığına mı ait olduğunu (çoğu böyledir) veya bütün iş parçacıkları tarafından mı paylaşıldığını artık tür sisteminden öğrenebiliyoruz. Bundan yararlanarak, blokla ilgili meta bilginin kilit gerektirmeyen ve her iş parçacığına özel olan bir kayıt $(ASIL cache) olarak saklanması mümkün olabilmektedir. Bu kayıt, blok meta bilgisinin son sorgulamalarının belirli sayıdaki cevabını saklar ve böylece bir dilimin sonuna eklenip eklenemeyeceği hızlıca öğrenilebilir. -) - -$(P -Bu kilitsiz sorgulama yöntemi, ekleme işlemini elemanların üstüne yazma tehlikesini taşıyan D1'den bile daha hızlı hale getirmiştir. -) - -$(H5 Dilim Üyeleri ve $(C Appender)) - -$(P -D dilimlerinin bu ilginç davranışları, ekleme sonucunda ne olacağının baştan bilinebilmesi ihtiyacını da doğurur. Bunu karşılamak için dilimlere çok sayıda nitelik ve işlev eklenmiştir. -) - -$(P -$(C size_t reserve(size_t n)): Dilime n adet eleman barındırabilecek kadar yer ayırır. Eğer dilim taşınmadan eklenebiliyorsa ve n adet elemana yeri varsa (burada n, mevcut ve yeni eklenecek olan elemanların toplamıdır) hiçbir değişiklik olmaz. Dönüş değeri, dilimin kapasitesidir. -) - ---- -int[] dilim; -dilim.reserve(50); -foreach(int i; 0..50) - dilim ~= i; // taşıma gerekmez ---- - -$(P -$(C size_t capacity): Dilimin ekleme sonucunda kaç eleman tutabileceğini belirtir. Eğer dilime taşıma gerekmeden eklenemiyorsa bu değer 0'dır. Eğer 0 değilse, döndürülen değer mevcut elemanların adedini de içerir. -) - ---- -int[] dilim = new int[5]; -assert(dilim.capacity == 7); // mevcut 5 eleman dahil -int[] dilim2 = dilim; -dilim.length = 6; -assert(dilim.capacity == 7); // taşımadan eklemek kapasiteyi - // değiştirmez -assert(dilim2.capacity == 0); // dilim2'ye taşımadan eklenemez - // yoksa dilim'in altıncı - // elemanının üstüne yazar ---- - -$(P -$(C assumeSafeAppend():) Dilimin sonuna eklenmesinin güvenli olduğunu çalışma ortamına bildirir. Bunun etkisi, dizinin ne kadarının kullanımda olduğunu belirten meta bilginin bu dilimin sonuna göre ayarlanmasıdır. -) - ---- -int[] dilim = new int[5]; -dilim = dilim[0..2]; -assert(dilim.capacity == 0); // güvenli değildir çünkü bu - // blokta geçerli olan başka - // veriler bulunmaktadır -dilim.assumeSafeAppend(); -assert(dilim.capacity == 7); // kullanımda olan eleman bilgisi - // 2 olarak değiştirilmiştir ---- - -$(P -D dilimlerine ekleme işlemlerini yine de yeterince hızlı bulmuyorsanız bir seçeneğiniz daha vardır. $(C std.array.Appender), elemanları çalışma ortamından blokla ilgili bilgi edinmeyi gerektirmeden ve en hızlı biçimde ekler. $(C Appender), $(I çıkış aralığı) kavramını ekleme işlemi olarak sunar (normalde çıkış aralığı olarak kullanıldıklarında dilimler kendi elemanlarının üzerine yazarlar). Aralıklar ve $(C Appender) hakkında daha fazla bilgi için $(LINK2 http://d-programming-language.org/, D belgelerine) bakabilirsiniz. ($(I Çevirmenin notu: Aralıklarla ve $(C Appender) ile ilgili Türkçe bilgiler için de $(LINK2 /ders/d/araliklar.html, Aralıklar dersine) bakabilirsiniz.)) -) - -$(H5 Sonuç) - -$(P -D'nin dizileri ve dilimleri, ustaların ve yeni başlayanların ihtiyaç duyacakları dizi türleriyle ilgili bütün kullanımları son derece zengin olanaklar halinde sunarlar. D dilimleri, değerlerinin ancak başka diller kullanılmak zorunda kalındığında farkedildiği, çok hızlı ve kullanışlı olanaklardır. D'yi henüz iyi tanımayan programcıların hiç olmazsa D'nin dilimlerine biraz zaman ayırmalarını ve dizi programcılığının ne kadar kolay olabileceğini görmelerini öneririm. -) - -$(HR) - -$(P -$(I David Gileadi, Andrej Mitrovic, Jesse Phillips, Alex Dovhal, Johann MacDonagh, ve Jonathan Davis'e bu makale üzerindeki görüşleri ve önerileri için teşekkür ederim.) -) - -$(P -© 2011 by Steven Schveighoffer -Creative Commons License -This work is licensed under a $(LINK2 http://creativecommons.org/licenses/by-nd/3.0/, Creative Commons Attribution-NoDerivs 3.0 Unported License). -) - -Macros: - SUBTITLE="D Dilimleri", Steven Schveighoffer - - DESCRIPTION=Steven Schveighoffer'in D en kullanışlı olanaklarından olan dilimlerin ayrıntıların anlattığı "D Slices" makalesinin Türkçe çevirisi 'D Dilimleri' - - KEYWORDS=d programlama dili makale d steven schveighoffer d slices dilim diff --git a/ddili/src/makale/d_tipleri.d b/ddili/src/makale/d_tipleri.d deleted file mode 100644 index ff4715c..0000000 --- a/ddili/src/makale/d_tipleri.d +++ /dev/null @@ -1,244 +0,0 @@ -Ddoc - -$(H4 D Dilindeki Tipleri Anlamak) - -$(P - $(B Yazar:) $(LINK2 http://www.zafercelenk.net/, Zafer Çelenk) -$(BR) - $(B Tarih:) 6 Mart 2012 -) - -$(P -Usta bir programcı olma yolunda tipler büyük önem taşır. Tipler hakkında bilgi vermeye başlamadan önce tiplere neden ihtiyacımız olduğu konusunu incelemek gerekir diye düşünüyorum. -) - -$(P -Bildiğiniz gibi bilgisayarlar ikili sistemde çalışır yani bir bilgisayara bir şeyler anlatmak isterseniz bunu önce, bir (1) ve sıfırlardan (0) oluşan bir dizi haline getirip bilgisayara vermelisiniz. Ancak bu işlem biz insanlar için çok zor olduğundan dolayı biz anlatmak istediklerimizi kendi tanımladığımız dilde yazıp derleyiciler aracılığı ile bilgisayarların anlayacağı dile çeviriyoruz. Dolayısıyla eğer bir bilgisayarın belleğini çalışırken görebilseydik bir ve sıfırlardan oluşan diziler görürdük. Her şeyin bir ve sıfırlardan oluştuğu böyle bir dünyada neyin rakam neyin harf olduğunu anlamanın tek yolu bu bir sıfır dizilerini uygun bir şekilde işaretlemek olacaktır. İşte programcı olarak bizim yaptığımız da bilgisayara işlemek için verdiğimiz veriyi tipleri kullanarak işaretlemek ve bilgisayarın doğru bir şekilde işlemesini sağlamaktır. -) - -$(P -D dilinde işaretli ve işaretsiz olarak ayrılan tipler bulunmaktadır. Bu işaret kavramını anlamak için bit düzeyine bir yolculuk yapmamız gerekir. -) - -$(P -Gelin şimdi hep beraber belleğin derinliklerinde ne oyunlar dönüyor bakalım. Bildiğiniz gibi bilgisayarlar ikili sistemde çalışır ve ikili sistemde kullanabileceğiniz sadece iki sayı vardır 1 ve 0. Eğer 1 bit (Binary digiT) ile çalışıyorsanız ifade edebileceğiniz 2 sayı vardır 1 ve 0, eğer 2 bitle çalışıyorsanız aşağıdaki dört sayıyı ifade edebilirsiniz. -) - -$(MONO -00 = 0 -01 = 1 -10 = 2 -11 = 3 -) - -$(P -Eğer üç bit ile çalışıyorsanız sekiz sayıyı gösterebilirsiniz. Gördüğünüz gibi bir bitlik bir artış ifade edebileceğimiz değer sayısını iki katına çıkarmıştır. -) - -$(MONO -000 = 0 -001 = 1 -010 = 2 -011 = 3 -100 = 4 -101 = 5 -110 = 6 -111 = 7 -) - -$(P -Bu örneklerden de anlaşıldığı gibi eğer sekiz bite sahipseniz gösterebileceğiniz en büyük sayı 255 dir. -) - -$(MONO -11111111 = 255 -) - -$(P -Burada gördüğünüz gibi sekiz bit bir araya gelerek 1 byte oluşturuyor. Bilgisayarların temelde kullandığı bilgi dizisi byte olarak kabul edilmiştir. -) - -$(P -Bu durumda şöyle bir düşünelim, birler ve sıfırlardan oluşan bir dünyada negatif (-) bir değeri nasıl gösterebiliriz. Çözüm sekiz bitten oluşan dizinin ilk bitini sayının negatif olup olmadığını gösteren işaret biti olarak kullanmaktan geçer. Bu durumda verimizi göstermek için bize 7 bit kalır ve 7 bit ile göstereceğimiz en büyük sayı 127 olur. Buraya kadar yazdıklarımızı toparlayacak olursak eğer sayınız sıfırdan küçükse negatif olduğunu göstermek için ilk biti kullanmak zorundadır dolayısıyla size kalan 7 bit ile göstereceğiz en büyük sayı 127 olmaktadır. Aksi halde sayınız negatif değilse 8 bitide kullanabilirsiniz böylece gösterebileceğiniz en büyük sayı 255 olacaktır. İşte işaretli ve işaretsiz tip arasındaki temel fark budur. -) - -$(H5 TAMSAYI TİPLERİ) - -$(P -D dilinde işaretli ve işaretsiz olmak üzere toplam sekiz tane tamsayı tipi bulunur. D dilinde işaretsiz olan tiplerin başına "u" harfi getirilir. "u" harfi ingilizce "unsigned" yani işaretsiz kelimesinin baş harfinden gelmektedir. Ayrıca D dilinde 128 bitten oluşan cent ve ucent adında iki tip daha vardır ancak bunlar henüz kullanıma açılmamış ve ilerisi için hazırlanmıştır. Şimdi bunları sıra ile tanıyalım; -) - -$(H6 $(C byte) ve $(C ubyte)) - -$(P -$(C byte) tipi -128 ile 127 arasında değer alabilen işaretli 8 bitten oluşan bir tipdir. $(C ubyte) ise $(C byte) tipinin işaretsiz halidir ve negatif değer alamaz. sınırı ise 255 dir. -) - -$(H6 $(C short) ve $(C ushort)) - -$(P -$(C short) tipi -32_768 ile 32_767 arasında değer alabilen işaretli 16 bitten oluşan bir tiptir. $(C ushort) ise $(C short) tipinin işaretsiz halidir ve negatif değer alamaz. sınırı ise 65_535 dir. -) - -$(H6 $(C int) ve $(C uint)) - -$(P -$(C int) tipi -2_147_483_648 ile 2_147_483_647 arasında değer alabilen işaretli 32 bitten oluşan bir tiptir. $(C uint) ise $(C int) tipinin işaretsiz halidir ve negatif değer alamaz. sınırı ise 4_294_ 967_295 dir. -) - -$(H6 $(C long) ve $(C ulong)) - -$(P -$(C long) tipi -9_223_372_036_854_775_808 ile 9_223_372_036_854_775_807 arasında değer alabilen işaretli 64 bitten oluşan bir tiptir. $(C ulong) ise $(C long) tipinin işaretsiz halidir ve negatif değer alamaz. sınırı ise 18_446_744_073_709_551_615 dir. -) - -$(H5 KESİR SAYI TİPLERİ) - -$(P -Kayan noktalı sayılar olarakta bilinen kesirli sayıları barındıran tiplerdir. Bu tipler 1.25 gibi sayıları tutan tiplerdir. Bu tiplerde hassasiyeti tiplerin bit sayısı belirler. Bit sayısı ne kadar yüksek ise hassasiyet o kadar çoktur. Kesirli değerleri alabilen tek tipler bunlardır. Şimdi bunları sıra ile tanıyalım; -) - -$(H6 $(C float)) - -$(P -$(C float) tipi 32 bitten oluşan kayan noktalı bir sayı tipidir. -) - -$(H6 $(C double)) - -$(P -$(C double) tipi 64 bitten oluşan kayan noktalı bir sayı tipidir. -) - -$(H6 $(C real)) - -$(P -Donanımın tanımladığı en büyük kayan noktalı sayı tipidir. (Örneğin : x86 işlemcilerde 80 bit) veya $(C double) 'dir hangisi büyükse. -) - -$(H5 SANAL TİPLER) - -$(P -Karmaşık sayıların sadece sanal değerlerini taşıyabilen bir tipdir. Şimdi bunları sıra ile tanıyalım; -) - -$(H6 $(C ifloat)) - -$(P -$(C ifloat) tipi sanal float tipidir. -) - -$(H6 $(C idouble)) - -$(P -$(C idouble) tipi sanal double tipidir. -) - -$(H6 $(C ireal)) - -$(P -$(C ireal) tipi sanal real tipidir. -) - -$(H5 KARMAŞIK SAYI TİPLERİ) - -$(P -Matematikte geçen karmaşık sayıları taşıyabilen bir tiptir. Şimdi bunları sıra ile tanıyalım; -) - -$(H6 $(C cfloat)) - -$(P -$(C cfloat) tipi iki float tipden oluşan bir tipdir. -) - -$(H6 $(C cdouble)) - -$(P -$(C cdouble) tipi iki double tipden oluşan bir tipdir. -) - -$(H6 $(C creal)) - -$(P -$(C creal) tipi iki real tipden oluşan tipdir. -) - -$(H5 KARAKTER TİPLERİ) - -$(P -Karakter tipleri bellekte karakterleri tutabilen tiplerdir. Temel karakter kümesi ASCII olmakla birlikte daha farklı bir çok karakter kümesine ait karakterler bu tiplerle kullanılabilir. Şimdi bunları sıra ile tanıyalım; -) - -$(H6 $(C char)) - -$(P -$(C char) tipi UTF-8 kod birimine uygun karakterleri tutabilen bir tipdir. -) - -$(H6 $(C wchar)) - -$(P -$(C wchar) tipi UTF-16 kod birimine uygun karakterleri tutabilen bir tipdir. -) - -$(H6 $(C dchar)) - -$(P -$(C dchar) tipi UTF-32 kod birimine uygun karakterleri tutabilen bir tipdir. -) - -$(H5 TİP NİTELİKLERİ) - -$(P -D dilindeki bu zengin tip çeşitlerini kullanarak programlarınızda işlemlerinizi rahat bir şekilde gerçekleştirebilirsiniz. Bunun yanında herhangi bir tip için bilgi almak istediğinizde D yine sizin yardımınıza yetişecektir. D 'nin tipler için tanımladığı nitelikler sayesinde istediğiniz tipin bilgilerini öğrenebilirsiniz. Bu niteliklere ulaşabilmek için tip isminden sonra bir nokta ve nitelik ismini yazmanız yeterli. Kısaca bu olanaklar; -) - -$(H6 $(C stringof)) - -$(P -İlgili tipin okunaklı ismidir. Örnegin: $(C int.stringof)) - -$(H6 $(C sizeof)) - -$(P -İlgili tipin bayt olarak uzunluğudur; tipin kaç bitten oluştuğunu hesaplamak için bu değeri bir bayttaki bit sayısı olan 8 ile çarpmak gerekir. Örneğin: $(C int.sizeof)) - -$(H6 $(C min)) - -$(P -İlgili tipin alabileceği en küçük değeri gösterir. Örneğin: $(C int.min) -) - -$(H6 $(C max)) - -$(P -İlgili tipin alabileceği en büyük değeri gösterir. Örneğin: $(C int.max) -) - -$(P -Bu nitelikleri kullanan örnek bir kod aşağıdaki gibi yazılabilir. -) - ---- -import std.stdio; - -void main() -{ - writeln("Tip : ", int.stringof); - writeln("Bayt olarak uzunluğu: ", int.sizeof); - writeln("En küçük değeri : ", int.min); - writeln("En büyük değeri : ", int.max); -} ---- - -$(P -D dilinde tipleri tanıtmaya çalıştım. Umarım D dilinin zenginliği sizi de etkilemiştir. D dili tipler yanında diğer pek çok özelliği ile ayrıca ilgi çekici ve güzel bir dildir. Umarım bu dile ilginiz artarak büyür. -) - -Macros: - SUBTITLE="D Dilindeki Tipleri Anlamak", Zafer Çelenk - - DESCRIPTION=Zafer Çelenk'in D'nin temel tiplerini tanıttığı yazısı. - - KEYWORDS=d programlama dili makale d zafer çelenk tip tür diff --git a/ddili/src/makale/degismez.d b/ddili/src/makale/degismez.d deleted file mode 100644 index 7268189..0000000 --- a/ddili/src/makale/degismez.d +++ /dev/null @@ -1,481 +0,0 @@ -Ddoc - -$(H4 $(CODE const) ve $(CODE immutable) Kavramları) - -$(P - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) 13 Temmuz 2009 -$(BR) - $(B İngilizcesi:) $(LINK2 http://www.dlang.org/const3.html, Const and Immutable) -) - -$(P -$(I [Çevirenin notu: "constant" ve "immutable" sözcüklerinin hem İngilizce hem Türkçe karşılıkları temelde aynıdır: sabit, değişmez. Türkçe'de $(CODE const) başka programlama dillerinde zaten "sabit" olarak geçtiği için ben burada da aynı karşılığı kullanıyorum. Böylece $(CODE immutable)'a "değişmez" kalıyor.]) -) - -$(P -Bir veri yapısını veya bir arayüz fonksiyonunu incelerken hangi verilerin değişmeyeceklerini, hangilerinin değişebileceklerini, ve değişimlerin kimin tarafından yapılabileceğini bilmek çok önemlidir. Bunu sağlayan düzeneklerden birisi, dilin tür sistemidir. D'de her veri $(CODE const) veya $(CODE immutable) olarak belirtilebilir. Özellikle belirtilmemişse veri $(I değişebilir) demektir. -) - -$(P -$(CODE immutable), verinin $(I değişemez) olduğunu bildirir. Bu tür veriler, ilk değerlerini aldıktan programın sonuna kadar aynı değerde kalırlar. Bunlar ROM'a veya donanımın $(I değiştirilemez) olarak işaretlediği bellek bölgelerine yerleştirilmiş olabilirler. Verinin değişemez olduğunun baştan biliniyor olması; derleyici için eniyileme olanaklarının artmasının yanında, fonksiyonel programlama açısından da yararlıdır. -) - -$(P -$(CODE const), verinin $(CODE const) olarak belirlenen referans yoluyla değiştirilemeyeceğini bildirir. Aynı veri, başka bir referans aracılığıyla değiştirilebilir durumda olabilir. $(CODE const), fonksiyonların verileri değiştirmeyecekleri sözü vermeleri için kullanılır. -) - -$(P -$(CODE const) ve $(CODE immutable) $(I geçişlidirler); onlar aracılığıyla erişilen başka veriler de yine $(CODE const) veya $(CODE immutable)'dırlar. -) - -$(H6 $(CODE immutable) depolama türü) - -$(P -$(CODE immutable)'ın en basit kullanımı sabit değerler oluşturmaktır. -) - ---- -immutable int x = 3; // x 3'tür -x = 4; // hata: x değiştirilemez -char[x] s; // s, uzunluğu 3 olan bir char dizisidir ---- - -$(P -Verinin türü, ilkleme için kullanılan değerden çıkarsanabilir: -) - ---- -immutable y = 4; // y'nin türü int'tir -y = 5; // hata: y değiştirilemez ---- - -$(P -Eğer ilkleme değeri henüz mevcut değilse, değişmez veri daha sonraki bir noktada kendisiyle ilgili kurucuda ilklenebilir. -) - ---- -immutable int z; -void deneme() -{ - z = 3; // hata: z değiştirilemez -} -static this() -{ - z = 3; // olur: ilkleme değeri z'nin tanımlandığı - // noktada bilinmiyorsa burada ilklenir -} ---- - -$(P -Yerel olmayan bir $(CODE immutable) verinin ilk değerinin derleme zamanında hesaplanabilen bir türden olması gerekir. -) - ---- -int foo(int f) { return f * 3; } -int i = 5; -immutable x = 3 * 4; // olur: 12 -immutable y = i + 1; // hata: derleme zamanında işletilmez -immutable z = foo(2) + 1; // olur: foo(2) derleme zamanında 7 - // olarak hesaplanır ---- - -$(P -$(CODE static) olmayan yerel $(CODE immutable) veriler çalışma zamanında ilklenirler. -) - ---- -int foo(int f) -{ - immutable x = f + 1; // çalışma zamanında işletilir - x = 3; // hata: x değiştirilemez -} ---- - -$(P -Değişmezlik geçişli olduğu için $(CODE immutable) yoluyla erişilen veri de değişmezdir. -) - ---- -immutable char[] s = "foo"; -s[0] = 'a'; // hata: s bir immutable veri referansıdır -s = "bar"; // hata: s değiştirilemez ---- - -$(P -$(CODE immutable) veriler $(I soldeğer)lerdir [lvalue]. Yani adresleri alınabilir ve bellekte yer tutarlar. -) - -$(H6 $(CODE const) depolama türü) - -$(P -$(CODE const) bildirimi ile $(CODE immutable) bildirimi arasında şunlardan başka fark yoktur: -) - -$(UL -$(LI Veri, $(CODE const) olarak bildirilmiş referansı yoluyla değiştirilemez, ama aynı veri $(CODE const) olmayan başka bir referans yoluyla değiştirilebilir. -) - -$(LI $(CODE const) bildiriminin kendi türü de $(CODE const)'tır. -) -) - -$(H6 $(CODE immutable) türler) - -$(P -Değeri değişmeyecek olan verilerin türleri $(CODE immutable) olarak bildirilebilir. $(CODE immutable) anahtar sözcüğü bir $(I tür kurucusu) olarak düşünülebilir: -) - ---- -immutable(char)[] s = "merhaba"; ---- - -$(P -$(CODE immutable) anahtar sözcüğü, o kullanımda parantez içindeki türü etkiler. Orada $(CODE s)'ye yeni değerler atanabiliyor olsa da, $(CODE s)'nin içindeki değerler değiştirilemez: -) - ---- -s[0] = 'b'; // hata: s[] değiştirilemez -s = null; // olur: s'nin kendisi immutable değil ---- - -$(P -$(CODE immutable) geçişlidir; kendisi yoluyla erişilen veri de değiştirilemez: -) - ---- -immutable(char*)** p = ...; -p = ...; // olur: p immutable değil -*p = ...; // olur: *p immutable değil -**p = ...; // hata: **p değiştirilemez -***p = ...; // hata: ***p değiştirilemez ---- - -$(P -Depolama türü olarak kullanılan $(CODE immutable), bütün bildirimin türü için kullanılan tür kurucusu $(CODE immutable)'ın eşdeğeridir: -) - ---- -immutable int x = 3; // x'in türü immutable(int) olur -immutable(int) y = 3; // y değiştirilemez ---- - -$(H6 Değiştirilemeyen veri oluşturmak) - -$(P -Bunun bir yolu, değiştirilemeyen değerler kullanmaktır. Örneğin dizgi sabitleri değiştirilemezler: -) - ---- -auto s = "merhaba"; // s'nin türü immutable(char)[] olur -char[] p = "dünya"; // hata: immutable, değiştirilebilen bir - // türe otomatik olarak dönüşemez ---- - -$(P -Diğer yol, tür değişiminde $(CODE immutable) kullanmaktır. Ama böyle yapıldığında, veriyi değiştirebilecek başka referansların bulunmadığını sağlamak programcının sorumluluğundadır. -) - ---- -char[] s = ...; -immutable(char)[] p = cast(immutable)s; // tanımsız davranış - -immutable(char)[] p = cast(immutable)s.dup; - // olur: p, .dup ile kopyalanan yeni kopyanın tek referansıdır ---- - -$(P -Dizilerin $(CODE immutable) kopyalarını almanın kolay yolu, $(CODE .idup) dizi niteliğini kullanmaktır: -) - ---- -auto p = s.idup; -p[0] = ...; // hata: p[] değiştirilemez ---- - -$(H6 $(CODE immutable)'ın tür değişimi ile kaldırılması) - -$(P -Değişmezlik tür dönüşümü ile kaldırılabilir: -) - ---- -immutable int* p = ...; -int* q = cast(int*)p; ---- - -$(P -Ama bu, verinin artık değiştirilebileceği anlamına gelmez: -) - ---- -*q = 3; // derleyici izin verir ama tanımsız davranıştır ---- - -$(P -Bazı durumlarda $(CODE immutable)'ın kaldırılabilmesi gerekebilir. Örneğin kaynak kodlarına sahip olmadığımız bir kütüphane, $(CODE immutable) olması gereken fonksiyon parametrelerini $(CODE immutable) olarak bildirmemiş olabilir. O kütüphane fonksiyonlarını çağırabilmek için $(CODE immutable)'ı tür dönüşümü yoluyla kaldırmak zorunda kalırız; ancak bu durumda sorumluluk tamamen programcıya aittir; derleyici o veri için artık denetim sağlayamaz. -) - -$(H6 $(CODE immutable) üye fonksiyonlar) - -$(P -Üye fonksiyonları $(CODE immutable) olarak bildirmek, o nesnenin o üye fonksiyon içindeyken değiştirilemeyeceği anlamına gelir. Bu, nesne içinde tanımlı bütün verileri ve onların $(CODE this) yoluyla erişildiği durumları kapsar: -) - ---- -struct S -{ int x; - - immutable void foo() - { - x = 4; // hata: x değiştirilemez - this.x = 4; // hata: x değiştirilemez - } -} ---- - -$(P -$(CODE const) ve $(CODE immutable) belirteçleri üye fonksiyonların parametre listesinden sonra da yazılabilirler: -) - ---- -struct S -{ - void bar() immutable - { - } -} ---- - -$(H6 $(CODE const) türler) - -$(P -$(CODE const) türler de $(CODE immutable) türler gibidirler; ama bu türden olan veriler $(CODE const) olmayan referanslar yoluyla değiştirilebilirler. -) - -$(H6 $(CODE const) üye fonksiyonlar) - -$(P -$(CODE const) üye fonksiyonlar içindeyken nesnenin $(CODE this) yoluyla erişilen üyeleri değiştirilemez. -) - -$(H6 Otomatik dönüşümler) - -$(P -Değişken (normal) türler ve $(CODE immutable) türler otomatik olarak $(CODE const)'a dönüşürler. Değişken türler $(CODE immutable) türlere, $(CODE immutable) türler de değişken türlere otomatik olarak dönüşmezler. -) - -$(H6 D'deki $(CODE const) ve $(CODE immutable)'ın C++'daki $(CODE const) ile karşılaştırılması) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    const ve immutable Karşılaştırması
    OlanakDC++98
    const anahtar sözcüğüvarvar
    immutable anahtar sözcüğüvaryok
    const yazımıFonksiyon bildirimi gibi: ---- -// const int -// const işaretçisi -// işaretçisi: -const(int*)* p; ---- - Sonda: - -$(C_CODE -// const int -// const işaretçisi -// işaretçisi: -const int *const *p; -) -
    const geçişliliğivar: ---- -// const int -// const işaretçisi -// const işaretçisi: -const int** p; -**p = 3; // hata ---- - yok: - -$(C_CODE -// int -// işaretçisi -// const işaretçisi: -int** const p; -**p = 3; // olur -) -
    const'ı kaldırmakvar: ---- -// const int işaretçisi: -const(int)* p; - -int* q = cast(int*)p; //olur ---- - var: -$(C_CODE -// const int işaretçisi: -const int* p; -int* q = const_cast<int*>p; - // olur -) -
    const'ı kaldırdıktan sonra değişiklikyok: ---- -// const int işaretçisi: -const(int)* p; -int* q = cast(int*)p; -*q = 3; // tanımsız davranış ---- - var: -$(C_CODE -// const int işaretçisi: -const int* p; -int* q = const_cast<int*>p; -*q = 3; // olur -) -
    fonksiyon yüklemede üst düzey const'ın etkisivar: ---- -void foo(int x); -void foo(const int x);//olur ---- - yok: -$(C_CODE -void foo(int x); -void foo(const int x);//hata -) -
    aynı const'a değişken olarak da erişebilmekvar: ---- -void foo(const int* x, - int* y) -{ - bar(*x); // bar(3) - *y = 4; - bar(*x); // bar(4) -} -... -int i = 3; -foo(&i, &i); ---- - var: -$(C_CODE -void foo(const int* x, - int* y) -{ - bar(*x); // bar(3) - *y = 4; - bar(*x); // bar(4) -} -... -int i = 3; -foo(&i, &i); -) -
    aynı immutable'a değişken olarak da erişebilmekyok: ---- -void foo(immutable int* x, - int* y) -{ - bar(*x);// bar(3) - - *y = 4; // tanımsız - // davranış - - bar(*x);// bar(??) -} -... -int i = 3; -foo(cast(immutable)&i, &i); ---- - immutable desteklenmez
    dizgi sabitlerinin türü ---- -immutable(char)[] ---- - -$(C_CODE -const char[N] -) -
    dizgi sabitinin değişken dizgiye otomatik dönüşümüizin verilmezizin verilir ama emekliye ayrılmış [deprecated] bir olanaktır
    - - - - - - -Macros: - SUBTITLE="const ve immutable Kavramları", Walter Bright - - DESCRIPTION=D'nin birbirlerini bütünleyen const ve immutable kavramları. - - KEYWORDS=d programlama dili makale d const immutable sabit değişmez diff --git a/ddili/src/makale/dub_tanisma.d b/ddili/src/makale/dub_tanisma.d deleted file mode 100644 index ef1796b..0000000 --- a/ddili/src/makale/dub_tanisma.d +++ /dev/null @@ -1,135 +0,0 @@ -Ddoc - -$(H4 DUB ile Tanışalım) - -$(P - $(B Yazar:) $(LINK2 mailto:zafercelenk@gmail.com, Zafer Çelenk) -$(BR) - $(B Tarih:) 28 Mart 2016 -) - -$(P -Program geliştirme sürecinde artarak çoğalan kodların, gerekli işlemlerden sonra çalıştırılıp çıktılarının gözlemlenmesi temel bir ihtiyaçtır. Bu süreç bir programcının programını geliştirmesi esnasında en çok tekrarladığı döngüdür. Bu döngü PHP, Python gibi dillerde sadece yaz-çalıştır şeklinde olmakla birlikte C++ ve D gibi derleyici ile kullanılan dillerde önce yaz sonra derle ve sonra çalıştır şeklinde üç adımdan oluşmaktadır. Zaman içinde geliştiriciler bu süreci daha kolay hale getirmek için yollar aramış ve programlamanın ilk zamanlarında $(C makefile)$(DIPNOT 1) komut dosyalarını kullanarak bu soruna bir çözüm getirmeye çalışmışlardır. Bu çözümde geliştirme sürecinde yazdığınız komutların çalışması için gerekli olan işlemler belli bir düzene göre oluşturulan $(C makefile) isimli dosyalara yazılır. Ardından $(C make)$(DIPNOT 1) isimli bir program ile bu dosya çalıştırılır ve programın sonucunu gözlemleyebilirsiniz. -) - -$(P -Zamanla bilgisayarlar komut satırından görsel arayüzlere doğru geçip, tümleşik geliştirme sistemleri (IDE) ortaya çıkmaya başlayınca $(C makefile) yerine geliştirme ortamları bu işleri tek bir fonksiyon tuşu veya klavye kısayolu ile yapmaya başladı. İlk zamanlarda bu yeterli olsa da zamanla giderek artan hazır kod kütüphaneleri kullanılmaya başlandıkça programların çevreye bağımlılığı daha da artmaya başladı. Özellikle açık kaynak dünyasında ortaya çıkan ve herkesin özgürce kullanabilmesini sağlamak için genel kamu lisansı (GPL)$(DIPNOT 2) altında dağıtılan kütüphaneleri programlarımıza otomatik olarak eklemek ve onların da başka bağımlılıklarını yönetmek görsel arayüze sahip IDE’ler ile çok kolay olmuyordu. Bu durum geliştiricilerin yeniden komut satırının otomasyon gücüne yönelmelerine neden oldu. -) - -$(P -Bunun üzerine yıllar önce geliştirilen $(C make) programının çok daha becerikli ve gelişmiş özelliklerle donatılmış yeni arkadaşları birer birer ortaya çıkmaya başladı. Örneğin Java dilinde çalışanlar için $(C ant) ve $(C maven) gibi araçlar öne çıkarken, C++ için $(C cpm), Python için $(C pip), hatta çok iyi bir IDE'ye sahip olan C# için $(C nuget) gibi araçlar bir anda oldukça popüler oldu. Bu araçlar yardımıyla sadece bir kaç komut kullanarak paket deposunda bulunan herhangi bir paketi bilgisayarınıza indirip projenize entegre edebiliyor ve çoğu zaman fazladan bir takım ayarlamalar yapmanıza gerek kalmadan kullanmaya başlayabiliyorsunuz. -) - -$(P -Bu kısa tarihçeden sonra D dili için geliştirilmiş proje ve paket yönetim programı olan DUB$(DIPNOT 3) hakkında konuşmaya başlayabiliriz. DUB özellikle komut satırından projelerini yönetmeyi seven yazılımcılar için oldukça faydalı bir programdır ancak IDE üzerinde çalışan geliştiriciler tarafından da oldukça kullanışlı bulunacak yönleri vardır. Halen geliştirilme aşamasında olmakla beraber, bir çok özelliği ile kullanıma uygun ve gayet iyi çalışan bir programdır. DUB programının kurulumuna geçmeden önce ulaşabileceğiniz kaynakları sizinle paylaşmak istiyorum. -) - -$(P -DUB projesi de diğer D projeleri (Phobos vs.) gibi açık kaynak kodlu ve kodlara herkesin ulaşabileceği bir şekilde $(LINK2 https://github.com/D-Programming-Language/dub, Github üzerinde geliştiriliyor). DUB programına resmi D dili sayfasından ulaşmanız da mümkün. Bunun dışında DUB kullanarak projelerinize ekleyebileceğiniz bir çok pakete $(LINK2 http://code.dlang.org, D paket deposu) üzerinden ulaşabilirsiniz. Bu paket deposu DUB ile projenize dahil edebileceğiniz paketleri gayet güzel bir şekilde kategori bazlı olarak ayırıp sizin kullanımınıza sunuyor. -) - -$(P -DUB programının kurulumu oldukça kolay; $(LINK2 http://code.dlang.org/download, indirme bölümünden) kullandığınız sisteme göre uygun olan kurulum dosyasını indirip program kurulumunu otomatik olarak yapabilirsiniz. Dikkat etmeniz gereken nokta, DUB sadece bir yönetim programı. Eğer D programları derlemek istiyorsanız sisteminizde bir de $(C dmd) gibi bir D derleyicisi kurulu olmalı. -) - -$(P -Kurulumdan sonra DUB artık kullanıma hazırdır. Temel DUB kullanımı için üç komutu bilmeniz yeterli, bu komutlardan birincisi yeni bir proje şablonu hazırlamanızı sağlayan $(C init) komutudur. Bu komutu kullanarak yeni bir proje oluşturabilirsiniz. Bunun için projenizi oluşturacağınız klasöre geçip komut satırından şu komutu vermelisiniz. -) - -$(SHELL -$ dub init projem -) - -$(P -Bu komuttan sonra DUB sizin için $(C projem) isimli bir klasör ve bu klasörün içine temel dosyalardan oluşan bir proje şablonu oluşturacaktır. Hiçbir değişiklik yapmadan oluşan bu projeyi çalıştırıp çıktısını ekranda görebilirsiniz. Bunun için aşağıdaki komutu vermelisiniz. -) - -$(SHELL -$ dub run -) - -$(P -Bu komutu bulunduğunuz proje klasörü içinde çalıştırdığınızda önce projeniz derlenir ve herhangi bir hata yoksa proje çalıştırılır. Projeniz eğer bir konsol projesi ise çıktısını ekranda görebilirsiniz. Bizim bu projemiz bir konsol projesi olduğu için bu komuttan sonra ekranda çıktısını görebiliriz. Bendeki çıktı şu şekilde: -) - -$(SHELL -$ dub run -Performing "debug" build using dmd for x64. -projem ~master: building configuration "application"... -Linking... -Running ./projem -Edit source/app.d to start your project. -) - -$(P -Ayrıca komut satırına doğrudan $(C dub) yazarsanız ön tanımlı olarak yine $(C dub run) komutu işletilir. $(C run) komutunun çıktısına baktığımızda ilk satırda DUB'un projemizi derlemek için kullandığı derleyici bilgisini görmekteyiz, ikinci satırda ise derleme ayarları için $(C application) isimli bir ayar setini kullandığı görünüyor. Derleme işleminden herhangi bir hata alınmadığı için üçüncü satırda bağlama işlemine geçtiğini görüyoruz ve son satırda DUB oluşan programı çalıştırarak görevini tamamlıyor. -) - -$(P -Bunun dışında bazen programı doğrudan çalıştırmak yerine bir derleme işlemi yaparak her şeyin yolunda olup olmadığını görmek isteyebilirsiniz. Bunun için DUB'a vermeniz gereken komut şöyle olmalıdır. -) - -$(SHELL -$ dub build -) - -$(P -Bu komutu verdiğinizde DUB projenizi sadece derler ve eğer herhangi bir hata yoksa bağlama (Linking) işlemini yapmadan süreci sonlandırır. -) - -$(P -Bu yazımda kısaca proje ve paket yönetim sistemlerinin tarihçesini ve D için geliştirilen DUB yönetim programını anlatmaya çalıştım. Bununla beraber DUB programının kurulumu ve temel kullanımı hakkında bazı bilgiler verdim. DUB hızlı, pratik ve kolay bir şekilde D projelerinizi yapılandırmayı ve ihtiyaç duyduğunuz kütüphaneleri kolay bir şekilde projenize eklemeyi vaat ediyor ve bana kalırsa bunu da gayet iyi yapıyor. Paket kurulumu ile ilgili örnekleri bir sonraki yazımda göstereceğim. -) - -$(P -Bununla beraber projenizde DUB altyapısının kullanımı, hem ileride projenize yeni katılacak kişilerin kurulum ve ilk başlama maliyetlerini en aza indiriyor hem de projenizin farklı geliştiricilere dağıtılabilmesini ve yeniden yapılandırma sürecini çok kolaylaştırıyor. Kısaca deneyin, seveceğinizi düşünüyorum. ;$(PARANTEZ_KAPA). Yazı hakkındaki sorularınızı $(LINK2 http://ddili.org/forum, forum bölümünde) sorabilirsiniz. -) - -$(P -Zafer ($(LINK2 mailto:zafercelenk@gmail.com, zafercelenk@gmail.com)). -) - - - -$(H5 Referanslar) - -$(OL - - - -$(LI -$(LINK2 http://e-bergi.com/y/Make-makefile, E-Bergi - Dosya İşlemleri Otomasyonu) -) - - - -$(LI -$(LINK2 https://tr.wikipedia.org/wiki/GNU_Genel_Kamu_Lisansı, GNU Genel Kamu Lisansı) -) - - - -$(LI -$(LINK2 http://code.dlang.org/download, DUB - The D package registry) -) - -$(LI -$(LINK2 https://github.com/D-Programming-Language/dub, Github üzerinde DUB projesi) -) - -$(LI -$(LINK2 http://code.dlang.org/download, DUB programının indirme sayfası) -) - -) - - - -Macros: - SUBTITLE="D Dilindeki Tipleri Anlamak", Zafer Çelenk - - DESCRIPTION=Zafer Çelenk'in D'nin temel tiplerini tanıttığı yazısı. - - KEYWORDS=d programlama dili makale d zafer çelenk tip tür - -DIPNOT=$(LINK2 #dipnot$1, $(SUP $1)) diff --git a/ddili/src/makale/duzenli_ifadeler.d b/ddili/src/makale/duzenli_ifadeler.d deleted file mode 100644 index be7cf4a..0000000 --- a/ddili/src/makale/duzenli_ifadeler.d +++ /dev/null @@ -1,194 +0,0 @@ -Ddoc - -$(H4 Düzenli İfadeler [Regular Expressions]) - -$(P - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) 14 Temmuz 2009 -) - -$(P -Düzenli ifadeler, metinlerin belirli bir $(I desene) uyanlarını bulmaya ve eşleştirmeye yarayan çok güçlü araçlardır. Düzenli ifadeler Perl, Ruby, Javascript gibi dillerin iç olanaklarıdırlar ve özellikle Perl ve Ruby'de son derece beceriklidirler. Peki düzenli ifadeler D'de neden dilin iç olanakları arasında değildir? Aşağıda düzenli ifadelerin D'de Ruby'den ne farklılıklar gösterdiğini bulacaksınız. -) - -$(P -Bu makale düzenli ifadelerin D'de nasıl kullanıldıklarını gösterir ama düzenli ifadelerin ne olduklarını anlatmaz. Ne de olsa salt düzenli ifadeleri kapsayan kitaplar yazılmıştır. D'nin düzenli ifadeler olanağı bütünüyle Phobos kütüphanesinin $(LINK2 http://www.dlang.org/phobos/std_regex.html, std.regex) modülünde tanımlanmıştır. Düzenli ifadelerin şablonlarla bağıntılı olarak ileri düzey kullanımlarını görmek için $(LINK2 http://www.dlang.org/templates-revisited.html, Templates Revisited) yazısını da okuyabilirsiniz. -) - -$(P -Düzenli ifadeler Ruby'de özel dizgi sabitleri olarak oluşturulurlar: -) - -$(C_CODE -r = /desen/ -s = /p[1-5]\s*/ -) - -$(P -D'de özel sabitler olarak değil, nesne olarak oluşturulurlar: -) - ---- -r = RegExp("desen"); -s = RegExp(r"p[1-5]\s*"); ---- - -$(P -Desen içindeki $(CODE \) karakterlerinin C'de olduğu gibi çift olarak yazılmaları gerekmez; başlarındaki $(CODE r) karakteriyle belirtilen $(I wysiwyg) dizgiler ("sonuç gördüğünle aynıdır" anlamında) kullanılabilir. Bu kodda $(CODE r) ve $(CODE s) nesnelerinin türü $(CODE RegExp)'tir ama türleri otomatik olarak da belirlenebilir: -) - ---- -auto r = RegExp("desen"); -auto s = RegExp(r"p[1-5]\s*"); ---- - -$(P -Bir düzenli ifadenin bir $(CODE s) dizgisine uygunluğunu Ruby'de bulmak için $(CODE =~) işleci kullanılır. Bu işleç, aranan desenin dizgi içinde uyduğu ilk yerin indeksini döndürür: -) - -$(C_CODE -s = "abcabcabab" -s =~ /b/ /* uyar, 1 döndürür */ -s =~ /f/ /* uymaz, nil döndürür */ -) - -$(P -Bunun D'deki eşdeğeri şöyledir: -) - ---- -auto s = "abcabcabab"; -std.regexp.find(s, "b"); /* uyar, 1 döndürür */ -std.regexp.find(s, "f"); /* uymaz, -1 döndürür */ ---- - -$(P -Bunun $(CODE std.string.find)'a benzerliğine dikkat edin: $(CODE std.string.find)'ın farkı, düzenli ifadeye uyan kısmı değil, bir alt dizgiyi bulmasıdır. -) - -$(P -Ruby'nin $(CODE =~) işleci, aramanın sonuçlarını gösteren bazı özel değişkenleri otomatik olarak tanımlar: -) - -$(C_CODE -s = "abcdef" -if s =~ /c/ - "#{$`}[#{$&}]#{$'}" /* "ab[c]def" dizgisinin esdegeridir -) - -$(P -$(CODE std.regexp.search()) ise uyanların bilgisini taşıyan bir $(CODE RegExp) nesnesi döndürür: -) - ---- -auto m = std.regexp.search("abcdef", "c"); -if (m) - writefln("%s[%s]%s", m.pre, m.match(0), m.post); ---- - -$(P -veya daha kısaca: -) - ---- -if (auto m = std.regexp.search("abcdef", "c")) - writefln("%s[%s]%s", m.pre, m.match(0), m.post); - // "ab[c]def" yazar ---- - -$(H6 Metin Arama ve Değiştirme) - -$(P -Bu işlem biraz daha ilginçtir. $(CODE "a")ların önce ilkini, sonra da hepsini $(CODE "ZZ") ile değiştirmenin yolu Ruby'de şudur: -) - -$(C_CODE -s = "Tavuğa roket tak." -s.sub(/a/, "ZZ") // sonuç: "TZZvuğa roket tak." -s.gsub(/a/, "ZZ") // sonuç: "TZZvuğZZ roket tZZk." -) - -$(P -D'de ise şöyle: -) - ---- -s = "Tavuğa roket tak."; -sub(s, "a", "ZZ"); // sonuç: "TZZvuğa roket tak." -sub(s, "a", "ZZ", "g"); // sonuç: "TZZvuğZZ roket tZZk." ---- - -$(P -Eskisinin yerine gelecek olan metin parçasının içinde, uyan metin parçalarını belirten $(CODE $&, $$, $', $`, .. 9) belirteçleri kullanılabilir: -) - ---- -sub(s, "[at]", "[$&]", "g"); - // sonuç: "T[a]vuğ[a] roke[t] [t][a]k." ---- - -$(P -veya yerleştirilecek dizgi bir $(CODE delegate) tarafından verilebilir: -) - ---- -sub(s, "[at]", - (RegExp m) { return toupper(m.match(0)); }, - "g"); // sonuç: "TAvuğA rokeT TAk." ---- - -$(P -($(CODE toupper()) $(LINK2 http://www.dlang.org/phobos/std_string.html, std.string) modülünde tanımlanmıştır.) -) - -$(H6 Döngüler) - -$(P -Bir dizgi içindeki uyan bütün yerler bir döngüyle şöyle görülebilir: -) - ---- -import std.stdio; -import std.regexp; - -void main() -{ - foreach(m; RegExp("ab").search("abcabcabab")) - { - writefln("%s[%s]%s", m.pre, m.match(0), m.post); - } -} -// Çıktısı: -// [ab]cabcabab -// abc[ab]cabab -// abcabc[ab]ab -// abcabcab[ab] ---- - -$(H6 Sonuç) - -$(P -Düzenli ifadeler D'de de Ruby kadar güçlüdürler ama yazımları onunki kadar kısa değildir: -) - -$(UL -$(LI -Düzenli ifadeleri sabitler olarak yazabilmek - bunu başarabilmek için yalnızca sözcükleme [lexing] yetmezdi; ayrıca söz dizimi [syntax] ve anlam [semantic] çözümlemeleri de gerekirdi -) -$(LI -Uyanları otomatik olarak değişkenlere atamak - bunun isim çakışmaları riski vardır ve zaten D'nin geri kalan olanaklarına uymaz -) -) - -$(P -Yine de aynı derecede güçlüdür. -) - - -Macros: - SUBTITLE=Düzenli İfadeler [regular expressions] - - DESCRIPTION=Düzenli ifadelerin [regular expressions] D'de Ruby'den ne farklılıklar gösterdiği. - - KEYWORDS=d programlama dili makale d tanıtım düzenli ifadeler regexp diff --git a/ddili/src/makale/eleman_erisimi_uzerine.d b/ddili/src/makale/eleman_erisimi_uzerine.d deleted file mode 100644 index dd59236..0000000 --- a/ddili/src/makale/eleman_erisimi_uzerine.d +++ /dev/null @@ -1,885 +0,0 @@ -Ddoc - -$(H4 Eleman Erişimi Üzerine) - -$(P - $(B Yazar:) $(LINK2 http://erdani.org, Andrei Alexandrescu) -$(BR) - $(B Çevirmen:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Çeviri Tarihi:) Ocak 2011 -) - -$(H5 Çevirmenin Notu) - -$(P -Bu yazının konusu İngilizce kaynaklarda "iteration" olarak geçer ve aslında iki işlemi kapsar: -) - -$(UL -$(LI topluluk elemanlarına erişmek) -$(LI topluluğun başka elemanlarına geçmek) -) -$(P -İngilizce kaynaklar bu iki işlem arasından "ilerleme"yi öne çıkartan "iterate" sözcüğünü kullandıkları halde ben daha önceki yazılarımla ve çevirilerimle uyumlu olabilmek için "iteration"ın karşılığı olarak "erişme", "iterator"ın karşılığı olarak da "erişici" sözcüklerini kullandım. -) - -$(P -Türkçeleştirdiğim bazı terimlerin İngilizce asıllarını parantezler içinde gri olarak ekledim. -) - -$(P -Anlaşılmalarını kolaylaştırmak için kod örneklerini de Türkçeleştirdim, ama arayüzleri belirleyen işlevlerin isimlerini İngilizce olarak bırakmaya karar verdim; o işlevlerin Türkçelerini ise kod açıklamaları olarak belirttim. -) - -$(HR) - - -$(H5 Özet) - -$(P -İleriye doğru erişme kavramının öncülüğünü Lisp'in tekli bağlı listeleri yapmıştır. Ondan sonra gelen nesne yönelimli topluluk tasarımları, elemanlara sıra ile art arda erişilmesini $(ASIL sequential access) Iterator tasarım örüntüsüyle $(ASIL design pattern) ve erişiciler aracılığıyla sunmuşlardır. Erişiciler her ne kadar güvenli ve mantıklı olsalar da; arayüzleri, topluluklardan bağımsız algoritmaların esnek, genel, ve etkin olarak yazılabilmelerine engel olur. Örneğin bir topluluğu sıralama, ikili yığın $(ASIL binary heap) olarak düzenleme, hatta tersine çevirme gibi işlemlerin yalnızca o topluluğun Iterator türü ile sağlanması beklenemez. Hemen hemen aynı dönemlerde C++'nın $(I Standard Template Library)'si (STL) kendi erişici sıradüzenini tanımlamış ve topluluklardan bağımsız algoritmaların o sıradüzen aracılığıyla gerçekleştirilebileceklerini göstermiştir. Ancak; STL erişicilerinin güvenlikten yoksunluk, kullanım güçlüğü, tanım güçlüğü, ve C++ diline fazla bağlı olma gibi sakıncaları vardır. Bu sakıncalar, onların başka diller tarafından benimsenmelerini kısıtlamıştır. Ben; Iterator örüntüsünün ve STL'nin üstünlüklerini bir araya getiren bir arayüz öneriyorum ve STL'deki algoritmaları da kapsayan bu soyutlamaları D dilinde gerçekleştirerek bu arayüzün mantıklı olduğunun kanıtlarını sunuyorum. -) - -$(P -Standard Template Library'ye, Iterator tasarım örüntüsüne, veya fonksiyonel dillere yabancı olanları dışlamış olmamak için bu makale biraz uzunca oldu. -) - -$(H5 Giriş) - -$(P -Bu makale, $(I ilerleme) işlemine taze bir bakış açısı getirir. İlerlemenin günümüz dillerindeki durumunu tartışacağım, ve salt ileri yönde ilerlemenin genel algoritmalar için yeterli olmadığı fikrini tekrar ortaya koyacağım (bu fikir Alexander Stepanov'a aittir [13]). Daha sonra, ilerleme kavramına, başka dillerde ve kütüphanelerde bulunan soyutlamalar üzerine kurulu olan yeni bir yaklaşım önereceğim. Önerdiğim düzenek hem mantıklı ve ifade gücüne sahip, hem de basit ve şimdiye kadar farkedilmediği halde barizdir. -) - -$(P -D'nin standart kütüphanesi için algoritmalar yazmaya başladığımda, önce C++'nın STL'sini örnek almıştım. STL, temel bazı sorulara çarpıcı yanıtlar getirir; bu yüzden benim STL'yi seçme kararım çok kolay olmuştu. Ama ilk gerçekleştirmem umduğum gibi çıkmamıştı. Önceki bir örneğin üstüne kurulu olmasına rağmen, STL'nin D'ye taşınmış hali de neredeyse C++'daki kadar uzun ve zor kullanılır bir hale gelmişti. -) - -$(P -O ilk denemeyi bir kenara bıraktım. İkinci tasarımıma STL'de olduğu gibi göstergelerden $(ASIL pointer), akımlardan, veya başka dillerde olduğu gibi tekli bağlı listelerden değil; doğrudan üst düzey kavramlardan başladım. Sonuçta ortaya çıkan taze, basit, ve güzel çözümü sizlerle paylaşma gereği duydum. -) - -$(H5 İlerlemek Yetmez) - -$(P -Neyi sevmem bilir misiniz? İç çamaşırlı imparatorları. Bakalım: -) - -$(C_CODE -qsort [] = [] -qsort (x:xs) = qsort (filter (< x) xs) ++ [x] - ++ qsort (filter (>= x) xs) -) - -$(P -Yukarıdakine benzer kodlar fonksiyonel dillerin üstünlüğünü gösterme amacıyla her durumda kullanılmışlardır. O kod Haskell dilinde fonksiyonel biçimde yazılmış olan, ve bazılarının inanışına göre de mevcut Haskell bilgisi gerektirmeden anlaşılabilecek olan bir quicksort gerçekleştirmesidir. İlk satır: boş listenin $(C qsort)'u boş listedir. İkinci satır (dar sütuna sığsın diye bölünmüş olarak): $(C x) ile başlayan ve ardından gelen başka $(C x)'lerin ($(C xs)) $(C qsort)'u üç listenin bileşimidir ($(C ++)): $(C xs) içinde $(C x)'ten küçük olanların sıralanmışı, $(C x)'in kendisi, ve $(C xs) içinde $(C x)'ten büyük veya ona eşit olanların sıralanmışı. Kolaysa quicksort'u kendi Üzünçlü dilinizle iki satırda yazmayı deneyin! -) - -$(P -Haskell'i ne kadar beğensem de, o örnek karşısında hayranlık ve öfkeyle karışık hisler duyuyorum. C'nin değerli taraflarının, dizi dışına taşma hataları üzerine kurulu örneklerle gösterilmesi karşısında duyduğum hislerin aynısı... -) - -$(P -Yukarıdaki $(C qsort) ile ilgili bir kaç yetersizlik vardır. Birincisi, $(C qsort) aslında qsort değildir. İlk defa tanıtıldığı Hoare'un makalesine [8] göre qsort, bir $(I yerel) algoritmadır. Hoare'un $(I yerel bölümleme) $(ASIL in-place partition) işlemi, makalesinin getirdiği en büyük katkı olma yanında, qsort'un da özünü oluşturur. $(C qsort) ise yerel olmadığı için olsa olsa quicksort'tan esinlenmiş bir uygulama olarak kabul edilebilir. (İyi bir uygulama olduğu da söylenemez. O kadar yan işlem kullanmaktadır ki komik olarak bile kabul edilemez—listenin iki kere ilerlenmesinin ve o birleştirmelerin bedava olduğunu mu sanıyorsunuz?) İkincisi, dayanak $(ASIL pivot) olarak ilk elemanı kullanan $(C qsort) açık olarak kötü bir seçim yapmakta ve böylece çoğunlukla sıralanmış olan verilerle karşılaştığında karesel $(ASIL quadratic) performans garanti etmektedir. Doğrusu, giriş verileri nadiren rasgeledir. Veriler örneğin daha önceden sıralanmışlardır, sonradan yeni elemanlar eklenmiştir, ve tekrar sıralanmaları gerekmiştir. Doğru çözüm, dayanak elemanını rasgele seçmektir; ama tekli bağlı listeden rasgele bir eleman seçmek hiç de küçümsenecek bir işlem olmadığı için, rasgele eleman seçmek $(C qsort)'u üç satırdan çok daha uzun hale getirir. -) - -$(P -Geldik $(C qsort)'la ilgili ve bu makalenin konusunu da oluşturan üçüncü noktaya: $(C qsort) ince bir mesaj veriyor: tekli bağlı listeler ve ileri yönde ilerleme bütün ihtiyaçlarınızı karşılar. Bu doğru değildir ve böyleymiş gibi gösterilmeye çalışılmamalıdır. -) - -$(P -Her durum karşısında tekli bağlı liste (S-list) kullanımını Lisp başlatmıştır. S-list'ler asıl veriden ve bir-sonraki-elemana-referans'tan oluşan çiftleri değişik biçimlerde bir araya getirirler. Üstelik S-list'ler, $(I kapalılık) $(ASIL closure) özelliğine sahip oldukları için [1] (buradaki $(I kapalılık), işlev kapamaları $(ASIL function closures) ile karıştırılmamalıdır), ne kadar karmaşık olursa olsun her veri yapısını bir $(I yönlü graf) olarak kodlayarak ifade etme yeteneğine sahiptirler. Talihsiz bir ek bilgi olmasa aslında çok da güçlü bir olanaktır—algoritma karmaşıklığı çokterimli $(ASIL polynomial) hale gelebilir. -) - -$(P -Algoritmaların çokterimli yavaşlıkları gibi konular S-list'lerin gücüne gölge düşürmek için fazla sıkıcı bulunmuş olmalı ki; bazı fonksiyonel programcıların kafalarına, aslında çoğu algoritmanın özünde bulunan dizi ve eşleme tablosu $(ASIL associative array) gibi veri yapılarını hor görecek fikirler yerleştirilmiştir. Lisp ve başka fonksiyonel dillerde de diziler bulunur ama hemen hemen her durumda S-list'lere sağlanan olanaklardan mahrum ikinci sınıf vatandaş konumundadırlar. Fonksiyonel diller üzerine çok yazı okudum ve hatırı sayılır derecede fonksiyonel kod da yazdım; sonunda bir şey farkettim—bir çok tasarımcı, neden yaptıklarını hiç düşünmeden algoritmaların özünde $(I sırayla arama) $(ASIL linear search) yöntemini uyguluyorlardı. Hatta, Scheme dilini kullanan saygın programlama kitabı (benim de en sevdiklerimden olan) "Structure and Interpretation of Computer Programs" [1] arama yapılan her noktada sırayla aramayı kullanır, ve dizilerden yalnızca bir noktada ve o da kapalılığı desteklemedikleri için dikkate alınmamaları gerektiğini belirtmek için sözeder. -) - -$(P -Alan Perlis'in "Lisp programcıları her şeyin değerini bilirler, hiçbir şeyin bedelini bilmezler" derken neyi düşündüğünü bilmiyorum ama benim naçizane görüşüme göre, fonksiyonel dillerin tekli bağlı listelere fazlaca bağlı olmaları ve dizileri ve eşleme tablolarını göreceli olarak göz ardı etmeleri güçlülük değil, zayıflıktır. -) - -$(P -Ne yazık ki yalnızca ileri yönde ilerleyerek erişme $(ASIL forward access) kavramı nesneye yönelik programlamaya da yayılmıştır. -) - -$(H5 The Gang of Four (GoF) Iterator Örüntüsü) - -$(P -Iterator örüntüsü, topluluk elemanlarına erişmekte kullanılan bir arayüz tanımlar. Gang of Four'un (GoF) $(LINK2 http://www.informit.com/store/product.aspx?isbn=0201633612, Design Patterns: Elements of Reusable Object-Oriented Software) [7] kitabında tanıtılan Iterator örüntüsünün temelinde "sırayla erişme" vardır: -) - -$(UL -$(LI -Bir topluluğun elemanlarına, topluluğun nasıl gerçekleştirildiğinden bağımsız olarak sırayla erişme yöntemi. -) -) - -$(P -Yazarlar konunun ayrıntılarına girdikçe bu basit tanımı sırasız erişime de izin verme gibi önemli biçimlerde değiştirirler, ama bunu bir sisteme oturtmazlar: -) - ---- -interface Iterator -{ - void First(); // Başlat(): Erişimi tekrar başlat - void Next(); // İlerlet(): Bir sonraki elemana geç - bool IsDone(); // Bitti_mi(): Sonuna geldik mi? - T CurrentItem(); // ŞimdikiEleman(): Şimdiki elemana eriş -} ---- - -$(P -Günümüzün nesne yönelimli kütüphanelerinin çoğu bu kalıba uyar. Bazıları $(C First) metodunun yerine başka bir $(C Iterator) döndüren örneğin $(C Clone) isminde daha genel bir metot kullanır: Böylece ilerleme sırasında bazı noktalar hatırlanmak istendiğinde bağımsız $(C Iterator) nesneleri oluşturulabilir ve farklı değişkenler olarak saklanabilir. Bu, ilerlemeyi $(C First) ile başından başlatmaktan daha genel bir yöntemdir. -) - -$(P -Özetle, fonksiyonel ve nesne yönelimli diller belirgin ilerleme biçimleri kullanırlar. Aralarında bazı farklar bulunsa da ikisi de ileri yönde ilerlemeye odaklanmıştır. Bu şekilde odaklanmak, geriye doğru veya rasgele erişimli ilerlemenin gerekliliğini gözden kaçırmaya neden olabilir. Bir toplulukta geriye doğru GoF yöntemi ile ilerleyebilmek, bütün topluluk kopyalanmadığı sürece olanaksızdır. Eğer STL gelmese, bununla da yetinilmek zorunda kalınırdı. -) - -$(H5 STL Biçiminde İlerleme) - -$(P -Dick Fosbury, atletizm dünyasını 1968 olimpiyatlarındaki yüksek atlama tekniği ile şaşırtmıştı. O güne kadar bilinen atlama tekniklerinden farklı olarak sırt üstü ve sırtını geriye doğru eğerek atlıyordu. Fosbury'nin tekniğinin çok farklı olması nedeniyle acil olarak bir komite kurulmuş ve sonunda tekniğin geçerli olduğu kabul edilmişti. Sonuçta Fosbury hem altın madalyayı kazanmış hem de tarih yazmış oldu. Günümüzde hemen hemen bütün yüksek atlamacılar Fosbury'nin tekniğini uygular. -) - -$(P -STL bunun benzerini erişici örüntüsü ile gerçekleştirmiştir. Yaklaşımının tazeliğine bakıldığında STL erişicilerinin Iterator örüntüsünün aynısı olmadıkları düşünülebilir. STL, erişicilere çok daha verimli bir açıdan yaklaşmıştır. Klasik Iterator örüntüsü, topluluklara sırayla erişim ile ilgilenir ve algoritmaları kendi başlarının çarelerine bakmak zorunda bırakır. Oysa STL, algoritmaları en genel ve evrensel biçimleri ile tanımlamaya odaklanır. Erişiciler veri yapılarını algoritmalara bağlarlar. -) - -$(P -STL'den önceki topluluklar GoF biçiminde ilerleme sunuyorlardı; bazıları eleman numarası ile erişim de sağlıyordu. Alexander Stepanov, algoritmaların veri yapılarından önde geldiklerini ve o yüzden öncelikle algoritmalara odaklanılması gerektiğini farketti. Kullandıkları verilerin yapılarını algoritmalar belirlerlerdi. Stepanov, yapıların topluluklardan bağımsız olarak tanımlanmaları gerektiğini de farketti. Böyle bir bağımsızlık, her algoritmanın her topluluk türü için ayrı ayrı tanımlanmasının sonucunda oluşan algoritma adedindeki aşırı artışı önler. -) - -$(P -$(I m) algoritmanın $(I n) topluluğa bağlı olduğu durumda $(I m x n) algoritma gerçekleştirilmesi gerekir. $(I m) algoritmanın $(I n) topluluktan bağımsız olduğu durumda ise $(I m + n) gerçekleştirme vardır; bu da kod bakımı açısından çok daha az iş anlamına gelir. STL'nin erişici tasarımının temelinde algoritmalarla toplulukların birbirlerinden bağımsız hale getirilmeleri yatar. Farklı algoritmaların farklı erişim biçimleri gerektirdikleri farkedilmiştir. Bazıları ileri yönde ilerlemeyi, bazıları çift yönde ilerlemeyi, bazıları rasgele erişimi, vs. gerektirmektedirler. Buna bağlı olarak STL, erişici çeşitlerini içeren bir sıradüzen tanımlama yolunu seçmiştir. -) - -$(P -"Erişici çeşitleri" kavramı yenidir ama sıkıcıdır da: gökyüzü mavidir, su ıslaktır, ve farklı algoritmalar farklı erişim yöntemleri ile iyi işlerler. İlginç olan, farklı erişim çeşitlerinin varlığı değildir; ilginç olan, bu çeşitlerin sayısının çok $(I az) olmasıdır. Eğer 50 algoritma 30 çeşit erişim gerektirseydi, o tasarım üstesinden gelinemez bir durumda olurdu. Erişim çeşitleri işlev parametrelerine benzer: sayıları çok fazla ise bir yerde bir yanlışlık var demektir. STL yalnızca beş tane erişim çeşidi kullanarak çok sayıda algoritma yazılabildiğini göstermiştir: -) - -$(UL - -$(LI $(I Giriş erişicileri), dosya ve ağ akımlarında olduğu gibi tek geçişli giriş durumunu modellerler) - -$(LI $(I Çıkış erişicileri), sırayla erişimli dosyalarda ve ağ akımlarında olduğu gibi tek geçişli çıkış durumunu modellerler) - -$(LI $(I İleri yönde erişiciler), tekli bağlı liste erişimini modellerler) - -$(LI $(I Çift yönlü erişiciler), çift bağlı liste (ve beklenmedik şekilde) UTF kodlamalı dizgi erişimini modellerler) - -$(LI $(I Rasgele erişiciler), dizi erişimini modellerler) - -) - -$(P -Bu beş erişici çeşidinin kavramsal sıradüzeni oldukça basittir: Şekil 1'de görüldüğü gibi, en temelde giriş ve çıkış erişicileri bulunur; daha yetenekli olan başka çeşit erişiciler bir öncekinin yetenekleri üzerine kuruludur. -) - -$(MONO - InputIterator OutputIterator -$(ASIL GirişErişicisi) $(ASIL ÇıkışErişicisi) - ↖ ↗ - ForwardIterator - $(ASIL İleriYöndeErişici) - ↑ - BidirectionalIterator - $(ASIL ÇiftYönlüErişici) - ↑ - RandomAccessIterator - $(ASIL RasgeleErişimliErişici) -) - -$(P $(B Şekil 1) STL'deki erişici çeşidi sıradüzeni) - -$(H6 Göstergeleri Temel Almak) - -$(P -Erişici çeşitlerini belirlemek ve onları bir sıradüzene yerleştirmek, STL'nin klasik erişici kalıplarını geride bırakmasını sağlamıştır. STL'nin belirleyici bir başka özelliği, isimli işlevlerden oluşan bir arayüz kullanmak yerine, C++'nın göstergeleri üzerine kurulmuş olmasıdır. -) - -$(P -$(C erisici) ismindeki bir STL erişicisinin (veya C++ göstergesinin) erişim sağladığı değere $(C *erisici) söz dizimi ile erişilir. $(C erisici)'yi bir sonraki değere ilerletmek için $(C ++erisici) veya $(C erisici++) yazılır. Bir erişiciyi kopyalamak, onu başka bir erişiciye atayarak sağlanır. Son olarak, değer aralığının tüketilip tüketilmediği, erişicinin aralığın sonunu gösteren başka bir erişici ile karşılaştırılması ile sağlanır. GoF erişicilerinin tersine, ve göstergelerin bir dizinin sonuna geldiklerinden haberlerinin olmamasına benzer şekilde, C++ erişicileri de aralığın sonuna geldiklerini kendileri bilemezler. -) - -$(P -Çift yönlü erişiciler, bir soldaki elemana ilerlemeyi sağlayan $(C --erisici) yazımını desteklerler; rasgele erişiciler buna ek olarak tamsayı eklemeyi ($(C erisici = erisici + 5) gibi), eleman numarası ile rasgele erişimi ($(C erisici[5]) gibi), ve erişiciler arasındaki uzaklık farkını ($(C int kalan = son - erisici) gibi) da desteklerler. Bu düzenek, dillerin iç olanaklarından olan göstergelerin, en güçlü erişici çeşidi olan rasgele erişiciler olarak kullanılabilmelerini sağlamıştır. Eğer bir göstergeyseniz, $(C RandomAccessIterator) soyluluğuna sahipsiniz demektir. -) - -$(P -Yukarıdaki temel işlemler hep sabit zamanda işlerler. $(C erisici += n) işlemi, aslında rasgele erişim sağlamayan erişicilerle bile $(C ++iter) işlemini $(C n) kere tekrarlayarak sağlanabilirdi. Ancak bu yöntem $(I O(n)) zaman alacağı için, $(C erisici += n) işleminin sabit zamanda olduğunu varsayan algoritmalar bundan çok kötü bir şekilde etkilenirlerdi. -) - -$(P -İsimli işlev kullanan geleneksel bir arayüz yerine gösterge yazımını kabul etmiş olması, STL'nin kabul edilmesini hızlandıran dahiyane bir karar olmuştur. STL algoritmalarının hiçbir özel işlem gerektirmeden göstergelerle doğrudan işleyebilmeleri, onların dizilerle de kullanılabilmelerini sağlamıştır. Daha da iyisi, erişici kullanan kodların gösterge kullanımına benziyor olması, programcıların kodları kolayca anlamalarına ve yazmalarına yardım etmiştir. Bu erişicilerin bu kadar akla yatkın olmaları, ANSI/ISO standart komitesinin STL'nin C++98 standardına eklenmesi teklifini alışılmış uzun komite sürecinden geçirmek yerine hemen kabul etmesine neden olmuş; ve tarihi bir olay haline gelmiştir. Çoğu kişiye göre STL mevcut topluluk ve algoritma kütüphanelerinin en iyisidir. STL'nin bu başarıyı hem de isimsiz işlev $(ASIL lambda function) olanağı bulunmayan bir dil ile gerçekleştirmesini, bir kolu arkasına bağlanmış bir boksörün bir üst kilonun şampiyonunu yenmesine benzetebiliriz. -) - -$(H5 STL Erişicilerinin Sorunları) - -$(P -Zamanla STL deneyimi arttı ve doğal olarak sorunların farkına varılmaya ve bunların üstesinden gelme yolları aranmaya başlandı. STL'nin, algoritmaları en genel ve en geniş alanda uygulanabilir olarak tanımlama hedefi, en başındaki kadar önemliydi; ancak o hedefe ulaşmanın başka yollarının da bulunabileceği gittikçe açıklık kazanmaya başladı. -) - -$(H6 Çiftleme Gereği) - -$(P -STL'nin soyutlamasının temelinde göstergelerin bulunuyor olmasının bir sonucu, kullanışlı bir iş yapabilmek için tek değil, iki erişici gerekmesidir. Tek erişici yeterli değildir çünkü topluluk dahilinde kalınacağından emin olunamayacağı için o erişici hiçbir yöne ilerletilemez. (Bu, dizilerin $(I gösterge ve dizi uzunluğu çifti) olarak geçirilmeleri gereken C biçimi kodlamaya benzer.) -) - -$(P -Çoğu erişicinin çiftler halinde geçirilmelerinin gerekiyor olması, onları bir araya getiren bir soyutlamanın eksikliğine işaret etmektedir. Böyle bir soyutlama olmadıkça erişici kullanan kodlar bazı güçlükleri göğüslemek zorundadırlar. Örneğin erişiciler üzerine kurulu olan kodlar, birbirlerine bağlanmaya elverişli değillerdir. STL bir aralığı belirleyen iki erişicinin farklı parametreler olmalarını beklediği için, işlevlerin sonuçları başka STL işlevlerine doğrudan geçirilemezler; parametre olarak geçirmek için her adımda isimli değişkenler tanımlamak gerekir. -) - -$(H6 İlerleme ve Erişme) - -$(P -STL'de $(C *erisici) ifadesinin $(C T)'nin $(C const) olup olmadığına bağlı olarak ya değişikliğe izin veren ya da vermeyen bir $(C T) referansı olması gerekir. Bazen erişilmekte olan verinin değiştirilmesi olanaksızdır; örneğin topluluk salt okunur bir topluluktur veya veri bir giriş akımından okunmaktadır. -) - -$(P -Abrahams ve başka yazarlar [3] STL deneyimlerine dayanan ve ilerleme ile erişme kavramlarını birbirlerinden bağımsız hale getiren bir öneri getirirler. Önerileri, erişicileri ikinci bir çeşit sıradüzende tanımlama üzerine kuruludur. Bu yeni çeşit sıradüzen, Şekil 1'deki klasik erişici sıradüzeninden bağımsızdır. Kısaca: -) - -$(UL - -$(LI $(B Okuma erişicileri): $(C degisken = *erisici) yazılabilir) - -$(LI $(B Yazma erişicileri): $(C *erisici = degisken) yazılabilir) - -$(LI $(B Değiş tokuş etme erişicileri): $(C iter_swap(erisici1, erisici2)) yazılabilir) - -$(LI $(B $(C lvalue) erişicileri): $(C adres = &(*erisici)) yazılabilir; yani, erişilmekte olan elemanın bellekteki adresi alınabilir) - -) - -$(P -Şekil 2 bu sıradüzeni göstermektedir. Her birisi Şekil 1'deki klasik sıradüzenden bir erişici çeşidi ile birleştirilebilir. Örneğin, çift yönlü ve değiş tokuş etmeye izin veren bir erişici tanımlanabilir ve kullanılabilir. -) - -$(MONO - Readable Writable - $(ASIL Okunabilen) $(ASIL Yazılabilen) - ↗ ↖ ↗ - Readable Lvalue Swappable -$(ASIL Okunabilen Lvalue) $(ASIL Değiş Tokuş Edilebilen) - ↖ ↗ - Writable Lvalue - $(ASIL Yazılabilen Lvalue) -) - -$(P -$(B Şekil 2) Abrahams ve başka yazarların [3] STL için önerdikleri erişim sıradüzeni -) - -$(H6 Güvenlik Eksikliği) - -$(P -STL erişicileri bir kaç yönden güvensizdirler. Bir aralığı belirleyen iki erişicinin doğru olarak bir araya getirilmeleri bütünüyle kullanıcının sorumluluğundadır. Erişici çiftleri oluştururken yanlışlık yapmak çok kolaydır ve bunun denetlenmesi pahalı bir işlemdir. Bir erişicinin aralığın dışına taşacak şekilde ilerletildiğinin denetlenmesi de pahalıdır. Hatta böyle denetimler erişicilerin temel işlemlerinden hiçbirisi için doğal da değillerdir. Neden? Çünkü erişiciler göstergeler üzerine kurulmuşlardır ve göstergeler $(I de) doğal olarak denetlenemezler. (Erişicilerin geçersiz hale gelme sorunları da vardır—bir topluluk değiştiğinde, o topluluğun elemanlarına erişim sağlamakta olan erişicilerin işi bozulur; ama bu erişicilerle ilgili bir konu olmaktan çok bir bellek modeli konusudur.) -) - -$(P -"Güvenli" STL gerçekleştirmeleri, böyle güvenlik sorunlarına "iri erişiciler"—erişici çiftleri— üzerine kurulu tasarımlar yoluyla özenle eğilirler. Böyle gerçekleştirmeler, göstergelere dayalı soyutlamaların göstergelerin sorunlarını da beraberlerinde getirdiklerinin sessiz tanıklarıdırlar. Az da olsa güvenliğin pahalı yöntemler ve veriler gerekmeden sağlanabilmesi arzulanırdı. -) - -$(H6 Gariplikler) - -$(P -C++'nın çeşitli gariplikleri erişici tanımlamayı ve kullanmayı gereksizce güçleştirir. Erişicileri doğru olarak tanımlamak çok güç olduğu için Boost salt bu amaca yönelik bir kütüphane tanımlar [2]. Üstelik erişiciler yalnızca üç temel işlem gerçekleştirmek zorundadırlar (karşılaştırma, ilerletme, ve eriştirme). Erişici kullanan kodlar da çok karmaşık olabilirler. Örneğin erişicilere karşı olan kişiler aşağıdaki gibi kodları örnek gösterirler: -) - -$(C_CODE -vector<int> v = ...; -for (vector<int>::iterator i = v.begin(); i != v.end(); ++i) -{ - ... burada *i kullanilir ... -} -) - -$(P -O kod, eleman numarası ile erişen eski tür döngülerle veya GoF türü isimli işlev kullanan erişicilerle karşılaştırıldığında söz dizimi açısından daha kalabalıktır. ($(C for_each)'in bu konuya çözüm getirdiği yanılgısında değilim, ve lütfen bana bu konuda kızgın mesajlar göndermeyin.) -) - -$(P -Erişicilere dayalı başka sıkıntılar da vardır. Giriş erişicilerinin ve ileri yönde erişicilerin yazımları aynı olsa da aralarında ince anlamsal farklar vardır—ileri yönde erişicilerini kopyalamak erişim durumunun bir kopyasını saklar, ama bir giriş erişicisini kopyalamak aynı erişim durumunun başka bir görünümünü oluşturur. Bu da şaşırtıcı olabilen çalışma zamanı hatalarına neden olur. -) - -$(P -Erişici kullanımını geliştirmek amacıyla Adobe [4] ve Boost [12] birbirlerinden bağımsız olarak $(I aralık) $(ASIL range) denen ve iki erişiciyi bir araya getiren soyutlamalar tanımladılar. Doğal olarak, bu erişicilerin aynı topluluğa (veya akıma) ait olmaları gerekir. Bir çift bekleyen algoritmalar artık bir aralık kullanabilirler, ve böylece çiftleme hataları hem aza indirgenmiş hem de böyle hataların yakalanmaları kolaylaşmış olur. Aralık kullanan bir algoritma olduğunda örneğin -) - -$(C_CODE -sort(v.begin(), v.end()); -) - -$(P -yazmak yerine artık şöyle yazılır: -) - -$(C_CODE -vector<int> v; -... -// v'nin "tamami" uzerinde calisir -sort(v); -) - -$(P -Algoritmaların çok kullanıldığı durumlarda aralıklar kodu çok kolaylaştırırlar. Aralıklar, işlevlerin birleştirebilmelerini de sağlarlar. Bunlar önemli gelişmeler olsalar da, Adobe/Boost aralıkları STL'nin tasarımındaki bütün eksiklikleri gideremezler. -) - -$(P -Eğer dayanaksız bir kuram geliştirmeme izin verilirse, STL'nin C++ göstergelerinin bir genellemesi olmasının ince bir sorunu vardır: ayrıntılı tasarımı, C++'ya ondan kopartılamayacak kadar bağlıdır. Bu da C++'yı iyi derece bilmeden STL'nin anlaşılmasını çok zor hale getirir; C++'yı derinlemesine anlamadan STL'yi tanımak isteyen yeni başlayanların karşılarına bazıları önemli bazıları ise tesadüfi ayrıntılardan oluşmuş bir duvar örülür; bu duvar da kişiyi daha ileri gitmekten soğutur. STL günümüzde hâlâ yaygın değildir: C++ programcıları arasında çok saygındır, ama genelde programcılık alanında neredeyse hiç tanınmaz. STL'den sonra ortaya çıkan diller bile hâlâ "çıtayı göğüsleyen" atlama tekniğini kullanmaktadırlar. Neden? Bence başka dil veya arayüz yazarlarının STL örneklerini incelerken "Garip... Bunu zaten yapabiliyoruz... Bunun yazımı çok uzun... Şuna bak, çok acayip... Aman boşver. Gel $(C Find)'ı $(C Array)'in bir üye işlevi yapalım" diye düşünmüş olduklarını hayal edebiliriz. -) - -$(H5 CDJ#++) - -$(P -C++, C#, Java, ve D'den esinlenmiş olan ve bu yazıdaki örneklerde kullanılan sözde dile CDJ#++ diyeceğim. Amacım bu yazıyı dilden bağımsız hale getirmek ve yazım kuralları ve küçük ayrıntılar yerine tasarıma odaklanmak. Diller benzer işleri farklı biçimlerde hallederler; o yüzden CDJ#++'nın kurucu ve sonlandırıcı işlevleri yoktur, bağımsız tür üretim $(ASIL generic type creation) ve kullanım ayrıntılarına girmez, arayüzleri öylesine tanımlar, ve parametreleri işlevlere referans olarak geçirme $(ASIL pass-by-reference) seçime bağlıdır. -) - -$(P -Olanakları hiçbirisine doğrudan denk olmasa da, yukarıdaki dillerden herhangi birisini biliyorsanız CDJ#++'nın kullanımını kolaylıkla anlayacaksınız. -) - -$(H5 İlerleme İşlemine Yeni Bir Bakış Açısı) - -$(P -STL erişicilerinin güvenlik sorunları GoF erişicilerinde bulunmaz. Bir topluluğun elemanlarına erişmek için bir çift GoF erişicisi gerekmediği için, o tasarımda çiftleme hataları olamaz. Ayrıca, $(C IsDone), $(C Next), ve $(C CurrentItem) işlevlerinin içlerine aşağıdaki hayalî erişicide görüldüğü gibi küçük denetimler eklemek oldukça kolaydır. -) - ---- -class DiziErişicisi -{ -public: - - bool IsDone() { // Bitti_mi() - assert(baş <= son); - return baş == son; - } - - void Next() { // İlerlet() - assert(!IsDone()); - ++baş; - } - - T CurrentItem() { // ŞimdikiEleman() - assert(!IsDone()); - return *baş; - } - -private: - - T* baş; - T* son; -} ---- - -$(P -GoF arayüzü doğal olarak daha sağlamdır ve bunun için hızdan ödün verilmemektedir. Buradaki kazanç, aralığı belirleyen sınırların bir birim halinde birleştirilmiş olmaları ve bu sayede daha üst düzey ve daha güvenli bir arayüz sağlamalarıdır. -) - -$(P -GoF türü arayüzler bazı durumlarda erişiciler kadar hızlı olabilirler ama bazen daha yavaştırlar. Örneğin $(C IsDone) içindeki karşılaştırma erişicilerde olduğu kadar hızlıdır. Ancak, $(C DiziErişicisi) bir STL erişicisinin iki katı kadar yer tutar; bu da erişicinin tek bir elemanı gösterdiği durumlarda belirgin bir etkidir. (Örnek olarak büyük bir diziyi ve onun elemanlarını gösteren erişicileri düşünebilirsiniz.) Yine de çoğu durumda STL erişicileri de çiftler halinde kullanıldıkları için öyle durumlarda yer kaybından söz edilemez. -) - -$(P -STL, erişmenin $(C IsDone), $(C Next), ve $(C CurrentItem) ile sınırlı olmadığını da etkin bir şekilde ortaya koymuştur. STL ve GoF erişicilerinin getirdikleri fikirler üzerine kurulu olan etkin, esnek, basit, ve kullanışlı bir erişici oluşturmaya çalışalım. STL'nin yaptığı gibi gösterge soyutlamasına bağlı kalmak yerine, burada GoF'un yaklaşımını örnek almak daha iyidir. Böyle bir erişici türü, etkinlikten çoğu durumda ödün vermeden hem daha akıllı hem de daha güvenli olabilir. Ama STL'nin çok yararlı olan erişici gruplarını da kullanalım. Önceki örnekte görüldüğü gibi, bu yeni erişicinin sınırlardan, yani aralığın başından ve sonundan haberinin olması gerekecektir. Buna bağlı olarak, bu aday soyutlamaya "aralık", özelleşmiş hallerine (giriş, çıkış, ilerleme, çift uçlu, ve rasgele erişimli) de "grup" $(ASIL category) diyeceğiz. -) - -$(H6 Erişimi ve İlerlemeyi Birbirlerinden Ayırmak) - -$(P -Abrahams ve başka yazarların yukarıdaki "İlerleme ve Erişme" başlığındaki önerileri ne kadar akla yatkın olsa da; bu yazı erişimden çok ilerleme ile ilgilidir. Şimdi böyle bir ayrımın yararını erişimin ayrıntılarına fazlaca girmeyi gerektirmeden gösterecek olan küçük bir soyutlama adımı atalım: Aralıkların erişime göre gruplanmalarını, aralık elemanlarının türünü $(C T) ile gösterdiğimiz $(C Ref<T>) isminde bir tür arkasına gizleyeceğiz. $(C Ref<T>) çok basit olarak $(C T)'nin takma ismi veya bir $(C T) referansı olabileceği gibi; okuma, yazma, değiş tokuş, veya adres alma işlemlerinin bazılarını veya tümünü birden sağlayan bir aracı tür de olabilir. Temelde, $(C Ref<T>) Şekil 2'deki gruplamaları oluşturmaya yarayan bir türdür. Aşağıda gösterilen aralık grupları $(C T)'ye olduğu kadar $(C Ref)'e de bağlı olabilirler. -) - -$(H6 Tek Geçişli Aralıklar) - -$(P -İşimize klavyeden tuş girişi, ağ pakedi okuma, veya C'nin $(C fgetc) arayüzü gibi durumlarda karşılaşılan ardışık akımlarla başlayalım. $(C IsDone)/$(C CurrentItem)/$(C Next) üçlüsünü bunlar için gerçekleştirmek kolaydır: $(C IsDone), girişin sonlanıp sonlanmadığını denetler ve tek elemanlık bir ara belleği doldurur; $(C CurrentItem), bu belleğin içeriğini döndürür; ve $(C Next), $(C IsDone)'ın bir sonraki çağrılışında yeni bir eleman okuması gerektiğini bildiren bir bayrak kullanır. Bu tek geçişli aralık arayüzünü temel işlemlerinin isimlerini de farklı seçerek tanımlayalım. (Bu yeni isimlerin daha uygun olduklarını arayüz geliştikçe anlayacağız.) -) - ---- -interface OnePassRange // TekGeçişliAralık -{ - bool empty(); // boş_mu() - Ref front(); // baştaki() - void popFront(); // baştakiniÇıkart() -} ---- - -$(P -Yukarıdaki "interface" (arayüz) anahtar sözcüğünü biraz serbestçe kullanıyorum. Seçilen dile bağlı olarak açıkça arayüz olabileceği gibi, otomatik arayüz veya ördek türü [10] de olabilir (örneğin "$(C empty), $(C front), ve $(C popFront) işlevleri varsa bir giriş aralığıdır" gibi bir kabulde olduğu gibi). -) - -$(P -$(C OnePassRange) aşağıdaki döngüdeki gibi kullanılabilir: -) - ---- -OnePassRange r = ...; -for (; !r.empty(); r.popFront()) { - ... burada r.front() kullanılır ... -} ---- - -$(P -Böyle giriş aralıkları $(C map) ve $(C reduce) gibi güçlü algoritmalarda kullanılmaya elverişlidirler bile. Ama daha basit bir örnek olarak $(C find) algoritmasına bakalım: -) - ---- -OnePassRange find(OnePassRange r, T değer) -{ - for (; !r.empty(); r.popFront()) { - if (r.front() == değer) break; - } - return r; -} ---- - -$(P -$(C find)'ın tanımı çok basittir—belirtilen aralığı değer bulunana veya aralık tükenene kadar ilerletir. Aralığın bulunan değerden başlayan bölümünü de döndürür. -) - -$(P -Dikkat ederseniz, tek geçişli aralıklar giriş için de çıkış için de kullanılabilirler—bu; kullanılan $(C Ref<T>)'nin okuma, yazma, veya her ikisine birden izin verip vermemesine bağlıdır. Yazmaya izin veren tek geçişli aralığa $(C WOnePassRange) dersek, $(C copy) algoritmasını şöyle tanımlayabiliriz: -) - ---- -WOnePassRange copy(OnePassRange kaynak, WOnePassRange hedef) -{ - for (; !kaynak.empty(); kaynak.popFront(), hedef.popFront()) { - assert(!hedef.empty()); - hedef.front() = kaynak.front(); - } - return hedef; -} ---- - -$(P -$(C copy), gerekiyorsa kopyalamaya devam edilebilsin diye hedef aralığın geri kalanını döndürmektedir. -) - -$(H6 İlerleme Aralıkları) - -$(P -İlerleme aralıkları, en çok fonksiyonel dillerin ve GoF erişicilerinin gerçekleştirmelerine benzerler: bellekte bulunan veriler üzerinde baştan sona doğru ilerlemek. -) - ---- -interface ForwardRange : OnePassRange // İlerlemeAralığı -{ - ForwardRange save(); // kaydet() -} ---- - -$(P -$(C ForwardRange), $(C OnePassRange)'in bütün temel işlemlerine sahiptir; ek olarak ilerleme işleminin belirli bir andaki durumunu kaydetmeye yarayan $(C save) işlevi de vardır. -) - -$(P -Aralığın sıradan bir kopyası neden kullanılamaz? -) - ---- -void işlev(ForwardRange r) -{ - ForwardRange bakKopyaladım = r; - ... -} ---- - -$(P -$(C save) işlevinin iki amacı vardır. Birincisi, Java gibi referans türleri kullanan dillerde $(C bakKopyaladım) gerçek bir kopya değildir—bir takma isimdir; yani asıl $(C Range) nesnesine erişim sağlayan bir başka referanstır. Öyle olduğu için asıl nesne ancak bir işlev çağrısı ile kopyalanabilir. İkincisi, C++ gibi değer türleri kullanan dillerde parametrelerin işlevlere geçirilmeleri sırasındaki kopyalama ile aralığın durumunu kaydeden kopyalama arasında fark bulunmaz. O yüzden bunu $(C save) işlevi ile açıkça yapmak kodun anlaşılırlığı açısından yararlıdır. (Bu; yazım açısından aynı, ama anlam açısından farklı olan STL'deki ilerleme ve giriş erişicileri arasındaki sorunu da çözer.) -) - -$(P -Çok sayıdaki ilginç algoritmayı artık bu ilerleme aralığı arayüzünü kullanarak tanımlayabiliriz. Aralıklar kullanan algoritmaların neye benzediklerini görmek için yan yana aynı olan elemanları bulan $(C tekrarlananıBul) gibi bir işlevin nasıl yazıldığına bakalım: -) - ---- -ForwardRange tekrarlananıBul(ForwardRange r) -{ - if (!r.empty()) { - auto s = r.save(); - s.popFront(); - for (; !s.empty(); r.popFront(), s.popFront()) { - if (r.front() == s.front()) break; - } - } - return r; -} ---- - -$(P -$(C auto s = r.save();) deyimi işletildikten sonra $(C s) ve $(C r) artık birbirlerinden bağımsızdırlar. $(C ForwardRange) yerine $(C OnePassRange) kullanılmaya çalışılsaydı $(C OnePassRange)'in $(C save) işlevi bulunmadığı için kod derlenemezdi. Eğer $(C ForwardRange) $(C save)'i çağırmak yerine kopyalamayı seçseydi kod bir $(C OnePassRange) ile de derlenirdi ama çalışma zamanında yanlış sonuçlar üretirdi. (Nedeni: Döngü daha ilk adımda dururdu, çünkü $(C r) ve $(C s) birbirlerine bağlı olacaklarından $(C r.front()) ve $(C s.front()) eşit olurlardı.) -) - -$(H6 Çift Uçlu Aralıklar) - -$(P -Aralık özellemelerinin bir sonraki düzeyinde $(C front) ve $(C popFront) işlevlerinin benzerleri olarak çalışan $(C back) ve $(C popBack) işlevlerini sunan çift uçlu aralıklar var. -) - ---- -interface DoubleEndedRange : ForwardRange // ÇiftUçluAralık -{ - Ref back(); // sondaki() - void popBack(); // sondakiniÇıkart() -} ---- - -$(P -Değiş tokuş edilebilen elemanlardan oluşan çift uçlu bir aralığın elemanlarını ters sıraya dizen $(C reverse) algoritmasına bakalım: -) - ---- -void reverse(DoubleEndedRange r) // tersineÇevir() -{ - while (!r.empty()) { - swap(r.front(), r.back()); - r.popFront(); - if (r.empty()) break; - r.popBack(); - } -} ---- - -$(P -Çok kolay. Aralıklar, algoritma yazmanın yanında yeni başka aralıklar tanımlamaya da yararlar. Örneğin çift uçlu bir aralığı ters sırada ilerlemeye yarayan $(C Retro) ismindeki yeni bir aralık, $(C front)'u $(C back)'e, $(C popFront)'u da $(C popBack)'e bağlamak kadar kolaydır: -) - ---- -struct Retro // GeriyeDoğru -{ - private DoubleEndedRange r; - bool empty() { return r.empty(); } // boş_mu() - Ref front() { return r.back(); } // baştaki() - void popFront() { r.popBack(); } // baştakiniÇıkart() - Ref back() { return r.front(); } // sondaki() - void popBack() { r.popFront(); } // sondakiniÇıkart() -} ---- - -$(P -$(B NOT:) "retro" ("geçmişe doğru) ismi aslında tam uymuyor ama daha doğru olan "reverser" ("tersine çevirici") da zorlama gelmişti. -) - -$(H6 Rasgele Erişimli Aralıklar) - -$(P -Aralıkların en güçlüsü olan rasgele erişimli aralıklar, tek uçlu aralık işlemlerine ek olarak eleman numarası ile erişme ve sabit zamanda erişme olanaklarını da sunarlar. Bu tür aralıklar hem dizilerdeki gibi ardışık hem de STL'nin $(C deque)'indeki gibi ardışık olmayan yapıları kapsarlar. Rasgele erişimli aralıklar $(C ForwardRange)'in temel işlevleri üzerine $(C at) ve $(C slice) işlevlerini de getirirler. $(C at), belirtilen numaralı elemana erişim sağlar; $(C slice), belirtilen iki numara arasındaki alt aralığı verir. -) - ---- -interface RandomAccessRange : ForwardRange // RasgeleErişimliAralık -{ - Ref at(int i); // numaralıEleman() - RandomAccessRange slice(int i, int j); // altAralık() -} ---- - -$(P -Buradaki çarpıcı ayrıntı; $(C RandomAccessRange)'in $(C DoubleEndedRange)'in değil, $(C ForwardRange)'in üzerine kurulu olmasıdır. Neden? Nedeni sonsuzluktur. On ile bölünmekten kalan değerleri üreten bir aralık düşünelim: 0, 1, 2, ..., 9, 0, 1 ... Belirli bir eleman numarasına karşılık gelen seri elemanını hesaplamak kolaydır ve bu açıdan $(C RandomAccessRange) tanımına uyar. Ancak, "son" elemanı bulunmadığı için bu aralık bir $(C DoubleEndedRange) olamaz. Dolayısıyla, rasgele erişimli aralıklar sonsuz olup olmamalarına bağlı olarak farklı işlemlerin üzerine kuruludurlar. -) - -$(P -Çoğu algoritma alt aralıkların sabit zamanda oluşturulabilmelerini bekler. Örneğin $(C quicksort) sabit zamanda rasgele erişim bulunmadığında iyi bir dayanak $(ASIL pivot) seçemez, ve girişi rastgele bir yerden ikiye ayırabilmek için de alt aralık işlemini sabit zamanda yapması gerekir. -) - -$(P -Aralıklar için önerilmekte olan sıradüzen Şekil 3'te görüldüğü gibidir. -) - -$(MONO - OnePassRange - $(ASIL TekGeçişliAralık) - ↑ - ForwardRange - $(ASIL İlerlemeAralığı) - ↗ ↖ - DoubleEndedRange RandomAccessRange (sonsuz) - $(ASIL ÇiftUçluAralık) $(ASIL RasgeleErişimliAralık) - ↑ - RandomAccessRange (sonlu) - $(ASIL RasgeleErişimliAralık) -) - -$(P $(B Şekil 3) Önerilmekte olan aralık grupları sıradüzeni -) - -$(H5 Aralık Deneyimleri) - -$(P -Güzel bir soru: Yukarıda tanımlanan aralıklar örneğin STL'yi gerçekleştirebilecek ifade gücüne sahip midirler? Hatta daha fazlasını verebilirler mi? Daha alt düzey soyutlamalar oldukları için erişicilerin aralıklardan daha becerikli oldukları açıktır. Buna rağmen, benim deneyimlerime göre aralıkların ifade yeteneklerinin az derecede düşük olması, getirdikleri üst düzey soyutlamaların ve güvenliliklerin yararları yanında önemsiz kalmaktadır. -) - -$(H5 Aralıkların Ek Nitelikleri) - -$(P -Şimdiye kadar tanımladığımız aralık arayüzleri çok sayıda algoritmayı topluluklardan bağımsız olarak gerçekleştirebilecek kadar ve bu yüzden de şaşırtıcı derecede yeteneklidirler. Yine de bazı yararlı temel işlemler garipsenecek kadar eksiktir. Örneğin çoğu rasgele erişimli aralık ve diğer aralıkların bazıları aslında $(C length) (uzunluk) bilgisini de sunabilirler. Hatta, uzunluk kavramı başından sonuna kadar ilerleyerek bir giriş aralığı tarafından da sağlanabilir, ama bazı topluluklar uzunluk işlemini doğal olarak sabit zamanda gerçekleştirirler. -) - -$(P -İlginç olarak, uzunluk işlemi belirli bir aralık çeşidine bağlı değildir. Uzunluğun rasgele erişimli aralıklarda şart, diğer aralıklarda şart olmadığı düşünülebilir. Ancak, uzunluğu bulunmayan rasgele erişimli aralıklar da vardır. Yukarıdaki "Rasgele Erişimli Aralıklar" başlığı altında sözü geçen 10 ile bölünme aralığının uzunluğunun bulunmadığı açıktır, ama o kadar açık olmayan durumlarla da karşılaşılabilir. Örneğin bir dizi üzerinde gerçekleştirilmiş olan döngüsel ara belleği $(ASIL circular buffer) düşünün. Bu aralığın $(C i)'inci elemanına sabit zamanda erişilebilir—bu, dizinin $(C (i % n)) ile hesaplanan elemanıdır. Ama bu ara belleğin uzunluğunun $(C n) olduğunu söylemek şaşırtıcı olabilir: kullanıcılar $(C n) adım ilerleyerek dizinin sonuna erişeceklerini düşünebilirler, ama öyle olmaz. Öte yandan, bazı giriş akımlarının bile uzunlukları bulunabilir—örneğin 100 adet rasgele sayı üreten bir aralık. -) - -$(P -Bu yüzden $(C length) aralıklar için ek bir niteliktir. Eğer olabiliyorsa aralık tarafından sunulmalıdır ama şart koşulmamalıdır. Aralık algoritmaları da aralıkların $(C length)'i sunmalarının şart olup olmadığına kendileri karar verebilirler. -) - -$(P -Deneyimler doğrultusunda kullanışlılığı görülen bir başka nitelik, sonsuzluktur. Sonsuz bir aralığın $(C empty()) işlevi her zaman için $(C false) döndürür. Çoğu dilde bunu algılamak zordur; o yüzden sonsuzluğu belirten Bool türünde ayrı bir $(C isInfinite) (sonsuz_mu) niteliği sunulabilir. Sonsuzluk kavramının aralık arayüzlerinin önemli bir parçası olduğunu düşünmesem de D'de bunun çok kolay sağlanabildiğini ve bazen çok yararlı olabildiğini söyleyebilirim. "Rasgele Erişimli Aralıklar" bölümünde değinildiği gibi; rasgele erişimli aralıklar, çift uçlu aralıklar ve sonsuzluk kavramı arasında bir ilişki vardır: rastgele erişimli bir aralık sonsuzsa, ilerleme aralıklarının üzerine kuruludur; değilse, çift uçlu aralıkların üzerine kuruludur. -) - -$(P -Daha az karşılaşılan aralık işlemleri, $(C lookahead) (ileriye_bak) ve $(C putback) (geri_koy) gibi işlemlerdir. Bir giriş aralığı, belirli sayıya kadar ilerideki elemanlara bakmaya, veya elemanları aralığa geri koymaya izin verebilir. C'nin ardışık dosya arayüzünde en azından tek karakter destekleyen $(C ungetc) işlevi vardır. $(C lookahead) ve $(C putback), ayrıştırma $(ASIL parsing) işiyle ilgilenen akımlar gibi uygulamalarda kullanışlıdırlar. -) - -$(H5 Üst Düzey Aralıklar) - -$(P -Parametre olarak başka işlevler alan veya döndüren üst düzey işlevlere benzer şekilde; üst düzey aralıklar da başka aralıkları bir araya getiren veya kullanan, ve kendileri de aralık arayüzü sunan yapılardır. Başka aralıkları farklı şekillerde sunan aralıklar oluşturmak kolaydır, kullanışlıdır, ve eğlencelidir. Hatta üst düzey aralıkların ilk çıktığı zaman STL'den beklenenleri sonunda karşıladıklarını da söyleyebiliriz. STL ilk çıktığı zamanlarda, ihtiyaca uygun olarak yazılmış olan erişicilerin bir çok sorunu çözeceğine inanılmış ve böyle erişiciler yazılmıştı. Ne yazık ki o tür erişicilerin başarısı yarım kalmıştır. Bunun nedeninden emin değilim ama erişicilerin tanım güçlüğünün ve yazımlarının uzunluğunun payı olduğunu sanıyorum. -) - -$(P -Aralık tanımlamak ve kullanmak ise çok kolaydır. Aslında çoğu algoritmanın ürettiği sonuç, belirli bir ihtiyaca uygun olan bir aralıktır. Örnek olarak üst düzey bir işlev olan klasik $(C filter)'a (süz) bakalım: parametre olarak bir aralık ve o aralıktaki elemanların hangilerinin seçileceklerini belirleyen bir kıstas işlevi alır. $(C filter)'ın kendi yaptığı iş çok azdır—o, yalnızca bir aralık kurar ve döndürür; süzme işi ise adına $(C Filter) diyebileceğimiz döndürülen aralığın işlemleri ile gerçekleştirilir. -) - -$(UL - -$(LI -$(B Tembellik.) Üst düzey aralıklar işlemlerini hevesli olarak değil, fonksiyonel dillerin de yeğledikleri gibi tembel olarak yapabilirler. Örneğin bir STL algoritması olan $(C set_union)'a bakalım; sıralanmış olan iki aralık alır ve her iki aralıktaki elemanların bileşiminden oluşan ve yine sıralanmış olan bir aralık üretir; bu işi doğrusal $(ASIL linear) zamanda gerçekleştirir. $(C set_union) heveslidir—döndüğü zaman bütün işini bitirmiştir. Bu yöntemin iki sakıncası vardır. Birincisi, hedef aralığın oluşturulması ve belki de bellek ayrılması gerekmektedir. Bu da $(C set_union)'ın ürettiği elemanlara sırayla bakılacağı ama belki de hepsinin gerekmeyeceği gibi durumlarda hem bellek hem de zaman açısından savurganlıktır. İkincisi, işini tamamlayabilmek için bütün giriş elemanlarını okuması gerektiği için $(C set_union) sonsuz aralıklar üzerinde kullanılamaz. -) - -$(P -Tembel değerlendirmelerin bir yararı olarak modüler birleşimlere $(ASIL modular composition) olanak vermesi gösterilir. Bunun nedeni, tembel değerlendirmelerin güçlü üreticilerin ürettikleri büyük veri alanlarına ait olan değerlerin seçiciler tarafından seçilebilmelerine olanak tanımasıdır. Hughes'ün güzelce açıkladığı [9] bu yarar, $(C MapReduce) algoritmasının [6] Google'ın ünlü gerçekleştirmesinde de görülmektedir. Tembel değerlendirmeler D'nin standart kütüphanesinde de olabilen her yerde ve oldukça etkili bir biçimde kullanılmaktadır. -) - -$(LI -$(B Aralık Yeteneklerinin Korunması.) Bir $(C r) aralığını ters sırada ilerleyen $(C Retro)'yu hatırlayalım. Asıl aralığın çift uçlu bir aralık olması gerektiği açıktır. Soru: Asıl aralık rasgele erişim de sağlıyorsa $(C Retro) da rasgele erişim sağlamalı mıdır? Bu sorunun yanıtı kesinlikle evettir. Genel bir kural olarak, üst düzey aralıklar asıl aralığa da bağlı olmak üzere sunabildikleri en üst aralık çeşidini sunmalıdırlar. Bu kurala göre $(C Retro) aşağıdaki gibi işlemelidir: -) - ---- -struct Retro { - - ... burası önceki ile aynı ... - - static if (isRandomAccess(DoubleEndedRange)// rasgeleErişimli_mi() - && hasLength(DoubleEndedRange)) { // uzunluğuVar_mı() - - Ref at(unsigned i) { - return r.at(r.length() - 1 - i); - } - - DoubleEndedRange slice(int i, int j) { - return r.slice(r.length() - j, r.length() - i); - } - } - - static if (hasLength(DoubleEndedRange)) { // uzunluğuVar_mı() - unsigned length() { - return r.length(); - } - } -} ---- - -$(P -Yukarıda bir CDJ#++ olanağı olan $(C static if)'ten yararlanıyorum: Koşul doğru olduğunda blok içindeki kod derlenir, değilse programa dahil edilmez. $(C hasLength) ve $(C isRandomAccess) kıstasları, asıl aralığın $(C length) sunup sunmadığını ve rasgele erişimli bir aralık olup olmadığını derleme zamanında iç gözlemden $(ASIL introspection) yararlanarak bildirmektedirler. $(C DoubleEndedRange)'in $(C length)'i sunup sunmayacağının da $(C r)'ye bağlı olduğuna dikkat edin. -) - -$(P -Arayüzlerin duruma göre böyle zenginleştirilmeleri dilin statik iç gözlem düzeneklerini oldukça zorlar. Bunun Java veya C#'ta nasıl yapıldığını bilmiyorum ama C++'da çok güç olsa da yapılabiliyor. Öte yandan $(C static if) D'de vardır ve $(C isRandomAccess) ve $(C hasLength) gibi kıstasları gerçekleştirmeyi çok kolaylaştırır. Dinamik dillerde ise dinamik iç gözlemle ilgili böyle sorunlar yoktur; kullanıcılar aralık nesnelerinin yetenekleri hakkında kolayca bilgi edinebilirler. -) - -$(P -Statik iç gözlem çok güzel olanaklar sağlar. Örneğin $(C Retro) asıl aralık olarak yine kendisini $(C Retro<Retro<BirAralık>>) biçiminde kullanıyor olsa, onun yerine hiç hesap yapılmadan doğrudan $(C BirAralık) kullanılabilir. -) - -$(LI -$(B Chain (Zincir)). Üst düzey aralık örneklerinden olan $(C Chain), birden fazla ve farklı çeşitlerden olabilen aralığı tek bir aralıkmış gibi sunar. $(C Chain) sanki tek bir aralıkmış gibi, asıl aralıklar arasında geçiş yapıldığının farkında bile olunmadan kullanılır. $(C Chain)'in yetenekleri doğal olarak asıl aralıkların yeteneklerinin bir kesişimidir. Örneğin $(C Chain)'in $(C length) işlevinin olabilmesi için asıl aralıkların hepsinin de $(C length) işlevlerinin olması gerekir. Eğer bütün aralıklar rasgele erişim sağlıyorlarsa $(I ve) uzunlukları varsa $(C Chain) de rasgele erişim sağlayabilir. Öyle bir durumda $(C Chain)'in $(I n)'inci elemanına erişmek, $(C Chain)'in asıl aralıklarının sayısına bağlı kalmak zorundadır. (Bu da bir algoritma karmaşıklığı sorunu oluşturabilir.) $(C Chain), oldukça ilginç işlemlerin önünü açar. Örneğin $(C sort(Chain(a, b, c))) ifadesi, üç tane fiziksel dizi üzerine kurulmuş olan mantıksal bir diziyi sıralayabilir. $(C Chain) üzerinde ilerlemek tembel bir işlem olsa da, $(C sort)'un kendisi tembel olmadığı için döndüğünde üç dizi içindeki elemanlar sıralanmış olurlar. -) - -) - -$(H5 Üç Noktalı Algoritmalar) - -$(P -STL'deki bazı algoritmalar üç erişici kullanırlar; birisi aralığın başını, diğeri bir orta noktasını, sonuncusu da sonunu belirler. Örneğin STL'nin $(C nth_element) ve $(C rotate) algoritmalarının arayüzleri şöyledir: -) - ---- -void nth_element(RIt baş, RIt orta, RIt son); -void rotate(FIt baş, FIt orta, FIt son); ---- - -$(P -Yukarıdaki $(C RIt) bir rasgele erişim erişicisi ve $(C FIt) bir ilerleme erişicisidir. $(C orta), $(C baş) ile $(C son) arasında olmak zorundadır. $(C nth_element), aralığın en küçük $(C orta - baş) adet elemanını aralığın baş tarafına taşır, ve $(C orta)'nın $(C (orta - baş))'ıncı en küçük elemanı göstermesini sağlar. $(C nth_element)'ın yararı bunu aralığı hiç sıralamadan yapmasıdır; bütün işi, yalnızca $(I n)'inci en küçük elemanı bulmaktır. Aralığı sıralamak ve ondan sonra $(C orta) elemana bakmak da işe yarar, ama $(C nth_element) $(C sort)'tan çok daha az sayıda işlem yaptığı için büyük verilerle uğraşırken önemlidir. ($(C nth_element) indeksli arama $(ASIL index searching) ve en yakın komşu $(ASIL nearest neighbors) gibi algoritmalarda da kullanılır.) -) - -$(P -İsmi garip olsa da $(C rotate) benim en sevdiğim algoritmalardandır. Aralıktaki elemanların [$(C orta), $(C son)) arasındakilerini [$(C baş), $(C orta)) arasındakilerden daha önce olacak şekilde yer değiştirir. Başka bir deyişle, $(C rotate) bir $(I öne getirme) işlemidir: [$(C orta), $(C son)) bölümü aralığın başına getirilir. Dikkatsizce yazıldığında çok uzun zaman alabilse de $(C rotate) aslında çok az sayıda veri aktaran akıllı bir algoritmadır. -) - -$(P -$(B NOT:) $(C bring_to_front)'un (başa_getir) $(C rotate)'ten çok daha uygun bir isim olduğunu düşünüyorum. -) - -$(P -Bu tür işlevleri aralıklarla nasıl kullanabiliriz? Bu konu benim için uzunca bir süre içinden çıkılmaz bir sorun oluşturmuştu. Sonunda basit bir gerçeği farkettim: üç noktalı algoritmalar aslında kavramsal olarak üç erişici değil, $(C sol) ve $(C sağ) diyebileceğimiz iki aralık almaktadırlar. Soldaki aralık [$(C baş), $(C orta))'dan, sağdaki aralık da [$(C orta), $(C son))'dan oluşur. $(C nth_element) ve $(C rotate)'i bu gözlemin ışığında sonunda aşağıdaki gibi tanımlamaya karar verdim ve gerçekleştirdim: -) - ---- -void nth_element(RR sol, RR sağ); -void rotate(FR sol, FR sağ); ---- - -$(P -Yukarıdaki $(C RR) bir rasgele erişim aralığı ve $(C FR) bir ilerleme aralığıdır. O işlevler belirli bir aralıkla örneğin şöyle kullanılabilirler: -) - ---- -Aralık r = ...; -nth_element(r.slice(0, 5), r.slice(5, r.length)); -rotate(r.slice(0, 5), r.slice(5, r.length)); ---- - -$(P -Aynı mantık $(C partial_sort) gibi diğer STL algoritmalarına da uygulanabilir. Tam bu çözümü kabul edecekken bunun başka olanaklar da sunduğunu farkettim. Yukarıdaki işlevleri aşağıdaki gibi tanımladığımızı düşünelim: -) - ---- -void nth_element(R1 sol, R2 sağ); -void rotate(R1 sol, R2 sağ); ---- - -$(P -Şimdi $(C R1) ve $(C R2), yan yana olmaları veya aynı çeşitten olmaları bile gerekmeyen farklı herhangi iki aralıktır. (Yan yana olmaları algoritmalar için yararlı bir bilgi oluşturmaz.) Artık elimizde çok daha güçlü algoritmalar vardır. Örneğin $(C nth_element) bir aralığın en küçük $(I n) elemanını değil, bellekte yan yana bile bulunmayan iki aralığın en küçük $(I n) elemanını bulabilir! Daha da iyisi, $(C nth_element)'ın aldığı $(C R2)'nin artık rasgele erişimli bir aralık olması da gerekmemektedir—bir giriş aralığı olması yeterlidir. $(C nth_element)'ın gerçekleştirmesi bu durumun farkına varabilir ve $(C R2)'nin yeteneklerine uygun olarak farklı algoritmalar kullanabilir. -) - -$(P -Üç erişici yerine iki aralık kullanıldığında hem aynı sorun çözülmekte, hem de daha büyük işlevsellik kazanılmaktadır. -) - -$(H5 Zayıflıkları) - -$(P -Bildiğimiz bir dildeki yöntemleri yeni öğrenmekte olduğumuz bir dile uygulayışımıza benzer şekilde, ben ve başka bir çok kişi STL erişicilerinden aralıklara geçerken erişiciler üzerine kurulu olan tasarımları doğal olarak aralıklara da uygulamaya çalıştık. Bunun sonucunda da aralıklara kolayca geçirilemediklerini farkettiğimiz bazı erişici algoritmalarıyla karşılaştık. Bunun bir örneği, birden fazla topluluk erişicisini indeksler aracılığıyla erişecek şekilde saklayan Boost'un MultiIndex'idir [11]. Onu tek elemanlı aralıklarla gerçekleştirmek, her bir indeks için harcanan alanı iki katına çıkartır. -) - -$(P -Farkettiğim başka bir zayıflık, $(C find) gibi tek erişici döndüren STL algoritmalarında görülür: -) - ---- -It find(It baş, It son, E değer) ---- - -$(P -Yukarıdaki $(C It) tek geçişli bir erişicidir ve $(C E) de onun eriştirdiği elemanın türüdür. STL'deki $(C find), $(C baş) ile $(C son) arasında $(C *erisici == değer) koşulunu sağlayan ilk $(C erisici)'yi döndürür. Döndürülen o erişici de aralığın baş tarafını oluşturmak için $(C baş) ile, son tarafını oluşturmak için de $(C son) ile bir arada kullanılabilir. -) - -$(P -Böyle bir yetenek aralıklarda bulunmaz. Aralık kullanan $(C find)'ın bildirimi şöyledir: -) - ---- -Aralık find(Aralık r, E değer) ---- - -$(P -Bu $(C find), aralığı $(C değer)'i bulana kadar başından tüketir ve geri kalanını döndürür. Bunun sonucunda da bulunan değerden ancak sonrasına erişilebilir, öncesine değil. -) - -$(P -Neyse ki bu sorun da $(C Until) (BulanaKadar) isminde yeni bir aralık tanımlayarak çözülebilir. $(C Until) başka bir $(C aralık) ve bir $(C değer) alır, ve aralığın baş tarafından $(C aralık.front() == değer) koşulunun sağlandığı noktaya kadarki aralığı döndürür. Tembel değerlendirmelerin sağladıkları kazanç ile! -) - -$(P -Doğal olarak, erişicilerin yapabildikleri ama aralıkların yapamadıkları başka şeyler de olacaktır. Neyse ki bunların sayısı çok değil gibi görünüyor ve aralık tasarımları, erişicilerle uygulanabilen kurnazlıklardan fazla yoksun kalmıyorlar. -) - -$(P -Aralıkların kullanılan dilin bellek modeline bağlı bir zayıflığı, bir topluluğa erişim sağlamakta olan mevcut aralıkların o topluluk değiştikçe geçersiz hale gelmeleridir. STL erişicilerinin geçersiz hale gelme kuralları kesin olarak belirlidir, ama bu duruma düşüp düşmedikleri denetlenemez. Bir topluluk erişicisinin topluluk değişti diye geçersiz hale gelmesine rağmen kullanılması tanımsız davranıştır. Aynı sorun aralıklarda da bulunur. ("İlerleme İşlemine Yeni Bir Bakış Açısı" bölümünde anlatıldığı gibi, aralıklar geçersiz erişici çiftlerine izin vermedikleri için yine de erişicilerden daha güvenlidirler.) Bu konuda daha fazla araştırma yapmamış olsam da güvenlik denetimlerinin aralıklara erişicilerden daha kolay ekleneceklerine inanıyorum. -) - -$(H5 Sonuç) - -$(P -Bu yazı; hem GoF erişicilerinin güvenliliklerine ve tanım ve kullanım kolaylıklarına, hem de STL erişicilerinin ifade gücüne sahip olan aralıkları anlatır. Aralıklar tanım ve kullanım kolaylığı sunarlar, tembel değerlendirmelerden yararlandırırlar, ve ilginç yeni olanaklar sunarlar. -) - -$(H5 Teşekkür) - -$(P -Bu yazı, şimdiye kadar edindiğim en değerli eleştirilerden geçti. Yazıyı eleştirenlerin bazılarının yazıyı teknik ve edebi açılardan benden daha iyi yazabilecek olduklarını biliyorum. Eğer bu yazıyı beğenmediyseniz eleştiriden geçmemiş halinin çok daha kötü olduğunu bilmek isteyebilirsiniz. -) - -$(P -Tabii ki okuduğunuz yazıyı beğendiğinizi umuyorum; bunun için çok çalıştım. Aynı amaç için aynı derecede çok çalışan şu kişilere büyük teşekkür borcum var: Adam Badura, Walter Bright, Ali Çehreli, Emil Dotchevski, Tony Van Eerd, Neil Groves, Craig Henderson, Daniel Hulme, Scott McMurray, Scott Meyers, Bartosz Milewski, Rob Stewart, ve Andrew Sutton. -) - -$(P -Normalde eleştirmenler arasında ayrım yapmak istemem. Ama bu sefer Rob Stewart'ı ayrıca anmam gerekiyor. Rob yazının ilk taslağının neredeyse her paragrafı üzerinde fikir belirtti ve bütün yazının yapısı ile ilgili üst düzey yorumlar getirdi. Eğer ilk yazı bir bina olmuş olsaydı; Rob'ın yorumları her bir tuğlasını kapsamış, mimari tasalarına değinmiş, ve en sonunda da şehir ve bölge planlamacılığı konularını çözmüş olurdu. -) - -$(H5 Referanslar) - -$(P -[1] Harold Abelson ve Gerald J. Sussman. Structure and Interpretation of Computer Programs. MIT Press, Cambridge, MA, USA, 1996. -) - -$(P -[2] David Abrahams, Jeremy Siek, ve Thomas Witt. $(LINK2 http://boost.org/doc/libs/1_40_0/libs/iterator/doc/, Boost.Iterator Kütüphanesi). 2003. -) - -$(P -[3] David Abrahams, Jeremy Siek, ve Thomas Witt. $(LINK2 http://www.boost.org/doc/libs/1_40_0/libs/iterator/doc/new-iter-concepts.html, New Iterator Concepts). 2006. -) - -$(P -[4] Adobe. $(LINK2 http://stlab.adobe.com/, Adobe Source Library). -) - -$(P -[5] Andrei Alexandrescu. $(LINK2 http://dlang.org/phobos/std_algorithm.html, Phobos Kütüphanesi'nin std.algorithm Modülü). 2009. -) - -$(P -[6] Jeffrey Dean ve Sanjay Ghemawat. "Mapreduce: Simplified Data Processing on Large Clusters." Commun. ACM, 51(1):107–113, 2008. -) - -$(P -[7] Erich Gamma, Richard Helm, Ralph Johnson, ve John Vlissides. $(LINK2 http://www.informit.com/store/product.aspx?isbn=0201633612, Design Patterns: Elements of Reusable Object-Oriented Software). Addison-Wesley, Boston, MA, 1995. -) - -$(P -[8] Sir Charles Antony Richard Hoare. "Quicksort." The Computer Journal, 5(1):10–16, 1962. -) - -$(P -[9] John Hughes. "$(LINK2 http://www.cs.chalmers.se/~rjmh/Papers/whyfp.html, Why Functional Programming Matters)." Comput. J., 32(2):98–107, 1989. -) - -$(P -[10] Andrew Koenig. "$(LINK2 http://www.ddj.com/cpp/184401971, Templates and Duck Typing)." Dr. Dobb's Journal, June 2005. . -) - -$(P -[11] Joaquín M. López Muñoz. $(LINK2 http://www.boost.org/doc/libs/1_40_0/libs/multi_index/doc/index.html, Boost.MultiIndex Kütüphanesi). 2003. -) - -$(P -[12] Thorsten Ottosen. $(LINK2 http://boost.org/doc/libs/1_39_0/libs/range/, Boost Range Kütüphanesi). 2003. -) - -$(P -[13] Alexander Stepanov ve Meng Lee. "$(LINK2 http://www.stepanovpapers.com/STL/DOC.PDF, The Standard Template Library)." Technical report, WG21 X3J16/94-0095, 1994. -) - -Macros: - SUBTITLE="Eleman Erişimi Üzerine", Andrei Alexandrescu - - DESCRIPTION=Andrei Alexandrescu'nun 'On Iteration' makalesinin Türkçe çevirisi 'Eleman Erişimi Üzerine' - - KEYWORDS=d programlama dili makale d andrei alexandrescu iteration iterators erişiciler ranges aralıklar diff --git a/ddili/src/makale/index.d b/ddili/src/makale/index.d deleted file mode 100644 index 02905c1..0000000 --- a/ddili/src/makale/index.d +++ /dev/null @@ -1,141 +0,0 @@ -Ddoc - -$(H4 D Diliyle İlgili Makaleler) - -$(UL - -$(LI $(LINK2 /makale/dub_tanisma.html, DUB ile Tanışalım - Zafer Çelenk) - -$(P -Bu yazı proje ve paket yönetim programı olan DUB'ı tanıtır. -) -) - -$(LI $(LINK2 /makale/saflik.html, D'nin Saflık Kavramı - David Nadlinger) - -$(P -Bu yazı D'deki saflık $(ASIL purity) kavramını ve $(C pure) anahtar sözcüğünün diğer dil olanaklarıyla etkileşimini anlatır. -) - -) - -$(LI $(LINK2 /makale/d_tipleri.html, D Dilindeki Tipleri Anlamak - Zafer Çelenk) - -$(P -Bu yazı D'nin temel tiplerini ve onların bitlerle nasıl gerçekleştirildiklerini anlatır. -) -) - -$(LI $(LINK2 /makale/d_dilimleri.html, D Dilimleri - Steven Schveighoffer) - -$(P -Bu yazı D'nin en kullanışlı olanaklarından olan dilimleri tanıtır ve performanslarının ve güvenliliklerinin perde arkasında nasıl sağlandığını anlatır. -) - -$(P -$(LINK2 http://www.dsource.org/projects/dcollections/wiki/ArrayArticle, Yazının aslı), Digital Mars firmasının Haziran 2011 tarihli makale yarışmasının adayları arasındadır. -) -) - -$(LI -$(LINK2 /makale/eleman_erisimi_uzerine.html, Eleman Erişimi Üzerine - Andrei Alexandrescu) - -$(P D dilinin tasarlayıcılarından olan Andrei Alexandrescu bu yazısında toplulukların algoritmalardan soyutlanmalarını sağlayan erişicileri (iterator) tanıtıyor, eksikliklerine değiniyor, ve erişici kavramından daha güçlü olduğunu gösterdiği aralık (range) kavramını öneriyor. -) - -$(P -$(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357, Yazının aslı) informIT'te 9 Kasım 2009'da yayınlanmıştır. -) - -$(P -Bu yazıda ortaya atılan fikirler daha sonradan D'nin standart kütüphanesi olan Phobos'ta da bazı isim değişiklikleriyle uygulanmıştır: -) - -$(UL -$(LI OnePassRange $(C InputRange) ismiyle) -$(LI $(C ForwardRange) aynı isimle) -$(LI DoubleEndedRange $(C BidirectionalRange) ismiyle) -$(LI $(C RandomAccessRange) aynı isimle) -$(LI ve yazıda çok az değinilen $(C OutputRange).) -) - -) - -$(LI $(LINK2 /makale/neden_d.html, Neden D - Andrei Alexandrescu) - -$(P C++'nın en büyük ustalarından birisi olan Andrei Alexandrescu, şimdilerde enerjisini Walter Bright tarafından tasarlanmış olan D programlama dilini geliştirme üzerine yönlendirmiş durumda... Alexandrescu, çeşitli nedenlerle C++'ya eklenemeyen çoğu dil olanağının D'ye eklenmesine yardım ederek, bir anlamda D'yi $(I C++'nın olmayı başaramadığı dil) haline getiriyor. -) - -$(P -Kendisine özgü heyecanlı tarzını içeren bu yazısında Alexandrescu, D dilinin neden önemli olduğunu ve belki de sizin için de uygun bir dil olabileceğini göstermeye çalışıyor. -) - -$(P -Bu yazının İngilizce aslı ilk olarak $(LINK2 http://www.accu.org, ACCU)'nun yayın organlarından CVu'nun Mayıs 2009 sayısında yayınlanmıştır. Bütün hakları yazarı Andrei Alexandrescu'ya aittir. -) - -$(P Haziran 2009'da Ali Çehreli tarafından çevrilen yazının İngilizce aslı, Türkçe çevirisinden kısa bir süre sonra $(LINK2 http://www.ddj.com/, Doctor Dobbs Journal)'ın sitesinde de yayınlanmıştır: $(LINK2 http://www.ddj.com/hpc-high-performance-computing/217801225, İngilizce aslı) -) - -) - -$(LI -$(LINK2 /makale/tembel_hesap.html, Fonksiyon Argümanlarında Tembel Değerlendirmeler - Walter Bright) - -$(P -D'nin yaratıcısı olan Walter Bright bu yazısında tembel değerlendirmelerin gücünü gösteriyor ve bu olanağın açtığı kod kolaylıklarının örneklerini veriyor. -) -) - -$(LI -$(LINK2 /makale/katma.html, Dizgi Katmaları [mixin]) - -$(P -Derleme zamanında oluşturulan dizgilerin nasıl koda dönüştürüldüklerini gösteren kısa bir yazı. -) -) - -$(LI -$(LINK2 /makale/degismez.html, $(CODE const) ve $(CODE immutable) Kavramları) - -$(P -D'deki değişmezlik kavramlarının tanıtılmaları ve $(CODE const) ve $(CODE immutable) anahtar sözcüklerinin kullanımları. -) -) - -$(LI -$(LINK2 /makale/bellek.html, Bellek Yönetimi) - -$(P -Her ciddi program bellek ayırma ve geri verme işlemleriyle ilgilenmek zorundadır. Programların karmaşıklıkları, boyutları, ve hızları arttıkça bu işlemlerin önemi de artar. D'de bellek yönetimi için kullanılan yöntemler... -) -) - -$(LI -$(LINK2 /makale/duzenli_ifadeler.html, Düzenli İfadeler [Regular Expressions]) - -$(P -Düzenli ifadeler, metinlerin belirli bir $(I desene) uyanlarını bulmaya ve eşleştirmeye yarayan çok güçlü araçlardır. Düzenli ifadelerin D'de Ruby'deki kadar güçlü olduklarını ve ondan ne farklılıklar gösterdiğini bulacaksınız. -) - -) - -$(LI -$(LINK2 /makale/shared.html, $(C shared)'e Geçiş) - -$(P -D2'de evrensel değişkenler artık normalde iş parçacıklarına özel alana (thread local storage (TLS)) yerleştiriliyorlar. Bu yazıda bununla ilgili bilgiler; ve özellikle $(C immutable), $(C shared), ve $(C __gshared) anahtar sözcüklerinin bu konudaki etkilerini göreceksiniz. -) - -) - -) - -Macros: - SUBTITLE=Makaleler - - DESCRIPTION=D programlama diliyle ilgili ilginç ve önemli makaleler - - KEYWORDS=d programlama dili makale belge tanıtım - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/makale/katma.d b/ddili/src/makale/katma.d deleted file mode 100644 index fb361c3..0000000 --- a/ddili/src/makale/katma.d +++ /dev/null @@ -1,100 +0,0 @@ -Ddoc - -$(H4 Dizgi Katmaları [mixin]) - - -$(P -$(LINK2 http://www.dlang.org/template-mixin.html, $(I Şablon) katmalarına) hem isim olarak hem de işlev olarak benziyor olsalar da, $(I dizgi) katmaları farklı olanaklardır. -) - -$(P -Dizgi katmaları, derleme zamanı dizgilerinin D kodu olarak derlenmelerini ve programa dahil edilmelerini sağlayan olanaktır. D'de dizgi işlemlerine derleme zamanında da izin verildiği için, bu iki olanak birleştirilince özel bir alana yönelik [domain-specific] diller oluşturulabilmektedir. -) - -$(P -Örneğin belirtilen isim ve üyelerle yapı oluşturan bir şablon şöyle tanımlanabilir: -) - ---- -template YapıOluştur(char[] Yapı, char[] Üyeler) -{ - const char[] YapıOluştur = - "struct " ~ Yapı ~ "{ int " ~ Üyeler ~ "; }"; -} - -mixin(YapıOluştur!("Koordinat", "x, y")); ---- - -$(P -Bu $(CODE mixin)'in ürettiği kod şöyledir: -) - ---- -struct Koordinat { int x, y; } ---- - -$(P -ve programda sıradan bir D kodu gibi kullanılabilir: -) - ---- -void main() -{ - Koordinat k; - k.x = 42; - k.y = 7; - - writefln("%d,%d", k.x, k.y); -} ---- - -$(P -Yaptığı iş metin dönüştürmek ve o metni kod olarak derletmek olduğu için dizgi katmaları C önişlemcisi olanaklarına benzerler. Ama aralarında önemli farklar vardır: -) - -$(UL -$(LI -C önişlemcisi sözcük çözümleme [lexical analysis] aşamasından $(B önce) çalışır. O yüzden C'nin sözcüklenmesi [lexing] ve ayrıştırılması [parsing] ancak bütün başlıkların eklenmesinden sonra olabilir; bütün klasörlerin içerikleri ve derleyici ayarları bilinmelidir. Katmalar ise anlamsal çözümleme [semantic analysis] aşamasında oluşurlar ve sözcükleme ve ayrıştırma aşamalarını etkilemezler. Sözcükleme ve ayrıştırma, çözümleme olmadan da yapılabilir. -) - -$(LI -C önişlemcisi farklı gibi görünen söz dizimleri oluşturabilir: - -$(C_CODE -#define BEGIN { -#define END } - -BEGIN - int x = 3; - foo(x); -END -) - -$(P -Böyle bir şey katmalarda mümkün değildir. Katılan kodun yasal bildirimlerden, deyimlerden, ve ifadelerden oluşması gerekir. -) - -) - -$(LI -C makroları tanımlandıkları noktadan başlayarak kendi isimlerine uyan bütün kodu etkileyebilirler; o isimler iç kapsamlarda olsalar bile... C makroları kapsamlardan habersizdirler. Bu, $(I kod sağlığına) aykırı olarak kabul edilir. D katmaları ise normal dil kurallarına uyarlar ve bu açıdan sağlıklıdırlar. -) - -$(LI -C önişlemcisi ifadelerinin söz dizimleri ve anlamları C dilinden farklıdır. C önişlemcisi ayrı bir dil olarak görülebilir. D'nin katmaları ise aynı dilin parçalarıdırlar. -) - -$(LI -C önişlemcisinin C'nin $(CODE const) belirteçlerinden ve C++'nın şablonlarından haberi yoktur. D'nin katmalarında ise şablonlar ve $(CODE const) belirteçleri kullanılabilir. -) - -) - - - -Macros: - SUBTITLE="Katmalar", Walter Bright - - DESCRIPTION=Dizgi sabitlerinin derleme zamanında D kodu olarak derlenmelerini sağlayan $(I katma) olanağı - - KEYWORDS=d programlama dili makale d tanıtım mixin dizgi katmaları \ No newline at end of file diff --git a/ddili/src/makale/neden_d.d b/ddili/src/makale/neden_d.d deleted file mode 100644 index ae9e1e8..0000000 --- a/ddili/src/makale/neden_d.d +++ /dev/null @@ -1,522 +0,0 @@ -Ddoc - -$(H4 Neden D) - -$(P - $(B Yazar:) $(LINK2 http://erdani.org, Andrei Alexandrescu) -$(BR) - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) Haziran 2009 -) - -$(P -Bakalım D, üzerinde durmaya değecek bir programlama dili mi... -) - -$(P -Sizi kolayca ikna edebileceğim yanılgısına düşmeyeceğim. Biz programcılar dil tercihi konusunda oldukça garibizdir. Bir programcının kitapçıda rastladığı "Falanca Programlama Dili" isimli bir kitaba karşı tepkisi herhalde şunun gibi bir şeydir: "Kendime bu dilde hoşlanmadığım bir şey bulana kadar 30 saniye süre tanıyorum." Bir programlama dilini edinmek oldukça uzun ve güç bir iştir. Getireceği tatmin de hem çok uzun vadede gelir, hem de kesin bile değildir. Böyle zorlu bir maceraya atılmayı daha başından engelleyecek nedenler aramak aslında bir savunma mekanizması olarak görülmelidir. Şüpheli ve riskli bir yatırım olduğu için, bu konuda çabucak olumsuz bir karar vermek aslında programcı için büyük bir rahatlıktır. -) - -$(P -Öte yandan, bir programlama dili öğrenmek ve kullanabilmek çok zevkli bir uğraştır da. Bir dilin zevkli kabul edilebilmesi için genellikle programcının değer verdiği ilkeleri tatmin edici derecede karşılıyor olması gerekir. Bu konudaki bir uyumsuzluk; programcının o dilin baştan savma, güvensiz, kurumlu, ve usandırıcı olduğunu düşünmesine neden olacaktır. Bir dil her ihtiyacı ve zevki eşit ölçüde karşılayamayacağı için, programcılık alanındaki bir kaç sağlam temel üzerine kurulmuş olması önemlidir. -) - -$(P -Nedir D'nin hikayesi? D'yi zaten duymuş olabilirsiniz: garip dil isimlerinden birisinin sahibi, konu dışı olduğu şeklinde uyarılana kadar başka dillerin forumlarında adı duyulan, bir arkadaş tarafından belki hararetle bahsedilmiş olan, veya "Kesin D adında bir dil de vardır" diye düşünerek aratınca karşılaşılan bir dil... -) - -$(P -Bu yazıda bu dile çok geniş bir açıdan bakacağım ve bu yüzden dilin bazı kavramlarının ve özelliklerinin üzerinde fazla duramayacağım. Bu yazıda kaynakça da göstermiyorum ama nasıl olsa sözü geçen terimler hakkında daha fazla bilgi edinmek için $(LINK2 http://www.google.com, google.com)'dan yararlanabilirsiniz. $(I [Çevirenin notu: Yardımı olacağını düşündüğüm yerlerde terimlerin İngilizce asıllarını da köşeli parantezler içinde vereceğim.]) -) - -$(P -İşe D'nin temel olanaklarını gözden geçirmekle başlayalım. Olanaklarının ve kısıtlamalarının bazıları doğal olarak biraz muallak kalacaklar. Hoşunuza gitmeyen bir şey okuduğunuzda buna fazla takılmayın çünkü anlam bir sonraki cümlede tamamlanıyor olabilir. Örneğin diyelim "D çöp toplamalı [garbage collection] bir dildir" ifadesini okuduğunuzda iliklerinize kadar dondunuz ve oradan hemen uzaklaşma ihtiyacı hissettiniz... Biraz sabır gösterirseniz, D'de kurucu [constructor] ve bozucu [destructor] işlevlerin de bulunduğunu, ve isterseniz nesne yaşam süreçlerini sizin de belirleyebileceğinizi göreceksiniz. -) - -$(H5 Konuya girmeden önce) - -$(P -Konuya girmeden önce bilmeniz gereken bir kaç şey var. Öncelikle, eğer daha önceden de D'ye şöyle bir bakmaya karar vermiş ve vazgeçmişseniz, bu sefer de öyle olacağını düşünmeyin. Çünkü içinde bulunduğumuz bu dönem, erken başlamanın getirdiği avantajlar açısından öncekilerden çok farklı bir dönem. D gelişmesini çok hızlı ama biraz sessiz olarak sürdürmekte ve ondaki müthiş gelişmeler tam da şu sıralarda duyulmaya başlanmakta... Hatta bazı gelişmeler ilk olarak bu yazı aracılığıyla duyuruluyor. Bu yazıyı hazırladığım sırada üçte biri bitmiş olan "The D Programming Language" isimli kitabımı tamamlamaya ve bir kaç ay içinde çıkartmaya çalışıyorum. -) - -$(P -Bu hızlı gelişme süreci doğal olarak bendenizi sürekli hareket halindeki bir hedefin peşinde koşmak durumunda bırakıyor. Uzun süre güncel kalacak olan bir yazı çıkartmaya karar vermiş olsam da, ne yazık ki biraz da moral bozucu olarak bu yazıda ya hiç gerçekleştirilmemiş ya da henüz yarım olarak gerçekleştirilmiş olanaklardan da söz etmek zorunda kalacağım. -) - -$(P -Bu dilin D1 ve D2 olarak başlıca iki sürümü var. Ben bu yazıda yalnızca D2 üzerinde duruyorum. D1 şu anda zaten çok kararlı bir durumda: artık üzerinde değişiklik yapılmıyor ve yalnızca hataları gideriliyor. D2 ise önceki sürümlerle uyumluluk konusunda özveride bulunma kararı almış başlıca bir sürüm... Bu özverinin nedeni, kararlı bir şekilde daha iyiye doğru gitmek, ve çok çekirdekli işlemcilerle [manycores] ve türden bağımsız programlamayla [generic programming] ilgili önemli bazı olanaklar getirmektir. Tabii bunun sonucunda dilin karmaşıklığı da artmış oluyor; ama gerçek hayatta kullanılan hiçbir dil zaten hiç küçülmemiş, hep büyümüştür. $(I Küçük ve hoş) olma kaygısıyla başlayan diller bile kullanıldıkça büyümek zorunda kalmışlardır. (Burada ayrıntıya girmeyelim ama evet, Lisp bile.) Programcılar hep küçük ve sade dillerin düşünü kurarlar ama kendilerine geldiklerinde asıl aradıklarının hep daha fazla modelleme yeteneği olduğunu farkederler. -) - -$(P -Resmî olarak kabul edilen D derleyicisi dmd, bütün yaygın platformlar (Windows, Mac ve Linux) için $(LINK2 http://www.dlang.org/download.html, dlang.org)'dan edinilebilir. Başka ortamlara uygun sürümlerinin çalışmaları da devam etmekte; özellikle .NET'e taşınmakta olduğunu belirtmekte yarar var. Ayrıca iki tane de temel D kütüphanesi mevcut: resmî olarak kabul edilen Phobos, ve çok sağlam bir kütüphane olarak tanınan Tango. Başlangıçta D1 için tasarlanmış olan Tango, şu sırada D2'ye taşınıyor. D1 sürümü sinir bozucu derecede küçük ve garip olarak tanınan Phobos ise D2'nin tüm yeteneklerinden yararlanabilmek için köklü değişikliklerden geçiyor. (Tahmin edileceği gibi kütüphanelerden hangisinin daha iyi olduğu konusunda politik görüşler ve atışmalar da var; ama bu rekabet sonuçta her ikisinin de daha iyiye gitmesine yardım ediyor.) -) - -$(P -Son olarak, gayet yaygın olan Qt pencereleme kütüphanesi de kısa bir süre önce D desteği vermeye başladı (bunu yazdığım sırada henüz alfa sürümündeler). Bu çok önemli bir haber, çünkü Qt taşınabilir görsel programlar yazabilmek için çok önemli bir ortamdır (kimisine göre de en iyisidir); ve yaygın kullanımdaki bütün işletim sistemlerini destekler. Qt'nin bu D desteği, D'yi bir anlamda $(I görselinci boyut)a taşımakta ve sunduğu olanakları mükemmel bir şekilde tamamlamaktadır. Daha da iyisi, bu gelişme tam da Qt'nin LGPL lisansını desteklemeye başlamasının hemen ardından geldi. Ticari programlar Qt'yi artık herhangi bir kısıtlama altında kalmadan ve lisans ücreti ödemeden kullanabiliyorlar. -) - -$(H5 D'nin temelleri) - -$(P -D'nin en doğru tanımı, $(I üst düzey sistem programlama dili) olarak yapılabilir. Normalde üst düzey dillerde ve hatta betik dillerde [script language] görmeye alıştığımız bazı olanaklara sahiptir: çok hızlı kodlama-derleme-çalıştırma süreci, çöp toplama, dile yerleşik $(I hızlı eşleme tabloları) [hash tables], tür bildirimlerini yazmak zorunda olmamak, vs. Ama aynı zamanda alt düzey olanaklar da sunar: işaretçiler, elle ($(CODE malloc)/$(CODE free)) veya yarı-otomatik (kurucular ve bozucular) olarak yapılabilen kaynak yönetimi, ve bellek ile C ve C++ programcılarının çok sevdikleri gibi doğrudan etkileşebilme olanağı. Hatta D, C fonksiyonlarını hiç bir dönüşüm gerektirmeden çağırabilir. Yani C standart kütüphanesinin tamamı D programcılarının kullanımına hazır durumdadır. Ama genelde o kadar alt düzeye inme ihtiyacı hissedilmez; çünkü hem D'nin kolaylıkları çoğu zaman daha güçlü ve daha güvenlidir, hem de zaten D alt düzey programlama kadar etkin kod üretir. Genelde D'de kolaylık ve verimlilik arasında seçim yapmak gerekmez. -) - -$(P -D'nin $(I çok paradigmalı) [multi-paradigm] olduğu söylenebilir: kodlama olarak nesne yönelimli [object-oriented], fonksiyonel [functional], türden bağımsız [generic], ve yordamsal [procedural] programlama tarzlarını destekler. D'deki bazı genel kavramları aşağıdaki küçük bölümlerde bulacaksınız. -) - -$(H5 Biraz haksızca sataşarak merhaba) - -$(P -Daha fazla uzatmadan şu söz dizimi konusunu aradan çıkartalım: -) - ---- -import std.stdio; -void main() -{ - writeln("merhaba dünya"); -} ---- - -$(P -Söz dizimi biraz giysi konusuna benzer; mantıklı düşününce giysilerin önemli olmaması gerektiğini biliriz. Hatta bu konuya fazla önem vermenin de biraz sığlık olarak kabul edilebileceğini de görebiliriz; ama giysi konusu hemen dikkatimizi çeker. (The Matrix filmindeki kırmızı elbiseli kızı bugün bile hatırlarım.) C'nin söz dizimine benzerliği nedeniyle D çoğumuza tanıdık gelecektir. Bu benzerliği C++, Java ve C#'ta da görmekteyiz. (Bu dillerden en az birisini bildiğinizi varsayıyorum ve tam sayıların, kayan noktalı sayıların, dizilerin, erişicilerin [iterator] ve özyinelemenin D'de de bulunduğunu söyleme gereği duymuyorum.) -) - -$(P -Hazır başka dillerden söz etmişken, C ve C++'nın "merhaba dünya" programlarına biraz haksızca da olsa sataşalım. C programının klasik hali K&R'ın ikinci basımında şöyledir: -) - ---- -#include -main() -{ - printf("hello, world\n"); -} ---- - -$(P -ve aynı derecede klasik olan C++ programı da şöyledir (heyecandaki fazlalığa dikkat edin): -) - ---- -#include -int main() -{ - std::cout << "Hello, world!\n"; -} ---- - -$(P -Bu tanıdık programın değişik dillerdeki halleri karşılaştırılırken çoğunlukla kod uzunluğuna ve programı anlamak için gereken bilgi miktarına bakılır. Bu sefer değişik bir yol çizelim ve doğruluktan söz edelim: mesaj standart çıkışa gönderilemezse ne olur? C programı hatayı gözardı eder, çünkü $(CODE printf)'in dönüş değerine bakmamaktadır. Gerçeği söylemek gerekirse, aslında durum daha da kötüdür: C programı benim ortamımda hatasız ve uyarısız olarak derleniyor olsa bile işletim sistemine belirsiz bir değer döndürür, çünkü program akışı $(CODE main)'den bir $(CODE return) deyimi olmaksızın çıkmaktadır. (Ubuntu'da hep 13 değerini gördüğüm için biraz ürktüğümü söyleyebilirim.) Hatta program C89 ve C99 standartlarına uymaz bile. Biraz araştırma sonucunda İnternet'ten de öğrenilebileceği gibi, bu selamlamanın doğrusu C'de aslında şöyle olmalıdır: -) - ---- -#include -int main() -{ - printf("hello, world\n"); - return 0; -} ---- - -$(P -Ama onun da doğruluğu hâlâ şüphelidir; çünkü belirsiz bir dönüş değeri yerine, bu sefer de mesajın yazdırılıp yazdırılmadığından bağımsız olarak hep başarılı sonlanma anlamına gelen $(CODE 0) değerini döndürmektedir. -) - -$(P -$(CODE return) deyimi unutulduğunda C++ programının $(CODE 0) döndüreceği standart tarafından garanti edilmiştir ama o da hatayı gözardı etmektedir; çünkü program başladığında $(CODE std::cout.exceptions())'ın değeri sıfırdır ve kimse $(CODE std::cout.bad())'e bakmamaktadır. Sonuçta mesaj doğru olarak yazdırılmamış bile olsa, her iki program da başarılı sonlandığını iddia etmektedirler. Yani aslında C ve C++ programlarının düzeltilmiş halleri alışık olduğumuzdan daha gösterişsizdirler: -) - ---- -#include -int main() -{ - return printf("hello, world\n") < 0; -} ---- - -$(P -ve -) - ---- -#include -int main() -{ - std::cout << "Hello, world!\n"; - return std::cout.bad(); -} ---- - -$(P -Biraz araştırınca, "merhaba dünya" programının Java (yer darlığı nedeniyle kodunu göstermiyorum), J# (Java ile en ufak bir ilgisi olmayan bir dil), ve Perl gibi başka dillerde de her durumda başarı iddiasında bulunulduğunu görmekteyiz. Tam bir komployla karşı karşıya olduğumuzu düşünmek üzereyken, neyse ki Python ve C#'ta öyle olmadığını görüyoruz ve rahatlıyoruz: mesaj yazdırılamadığında ikisinde de hata [exception] atılır. -) - -$(P -Peki bu programın D halinde durum nasıl? Hiçbir değişiklik gerekmez, çünkü bir sorun çıktığında $(CODE writeln) hata atar; $(CODE main) içinde oluşan hatalarla ilgili mesajlar eğer mümkünse standart hata akımına yazdırılırlar; ve program da bir hata koduyla sonlanır. Yani program yazıldığı haliyle zaten doğru çalışmaktadır. Diğer dillere böyle haksızca sataşmamın iki nedeni var. Birincisi, "merhaba dünya" programları hep böyle hileliler diye milyonlarca programcının sokaklara dökülerek ayaklandığını hayal etmek oldukça komik. (Gözünüzde canlandırın: "Merhaba dünya! Çıkış kodunun 13 olması bir tesadüf mü?" veya "Merhaba koyunlar! Uyanın artık!" vs.) İkincisi, maalesef bu programlar yaygın bazı programcı davranışlarını sergilemektedirler. Öte yandan D, doğru olanı yapmanıza izin vermek yanında, en kolay yöntemin aynı zamanda en doğru yöntem olduğunu da sağlamaya çalışmaktadır. Kolaylık ve doğruluk arasındaki bu özdeşlik, programcılıkta aslında sandığımızdan çok daha sık olarak karşımıza çıkar. (Bu arada benim kodumun yanlış olduğunu da düşünmeyin; $(CODE void main()) D'de yasaldır ve tam da düşündüğünüz şekilde çalışır. Sonuçta, yeni başlayanları $(CODE int main()) yerine $(CODE void main()) yazdılar diye C++ forumlarında haşlayan titizler, D'ye geçtiklerinde kendilerine yeni uğraşlar bulmak zorunda kalacaklar.) -) - -$(P -Söz diziminden bahsedecekken anlam konularına kaydık. Söz dizimine dönersek; D'de C++, C# ve Java'da olmayan dikkate değer bir fark var: D'de parametreli türler $(CODE T<X, Y, Z>) yerine $(CODE T!(X, Y, Z)) olarak gösterilirler ($(CODE T<X>) yerine de $(CODE T!(X)) veya daha kısaca $(CODE T!X) kullanılır). C++'da bu konuda açılı parantezlerin seçilmiş olması; $(CODE <), $(CODE >), ve $(CODE >>) işleçleriyle çakıştığı için büyük gramer sorunlarına yol açmıştır. Böyle belirsiz durumları gidermek için de rasgele kabul edilebilecek kurallar gerekmiştir. Hatta dünyanın en az bilinen söz dizimi kuralı da bu şekilde ortaya çıkmıştır: $(CODE nesne.template fonksiyon<arguman>()). Süpermen düzeyinde iyi C++'cı olan bir arkadaşınıza o söz diziminin ne anlama geldiğini sorun; arkadaşınızın büyük olasılıkla Kriptonit kullanmak zorunda kaldığını göreceksinizdir. Açılı parantezler Java ve C#'ta da kullanılmaktadır fakat o diller aritmetik ifadelerin parametre olarak kullanılmasına izin vermezler; ama sonuçta böyle bir olanağın ileride eklenme şansı da ortadan kalkmıştır. D bu konuda geleneksel birli $(CODE !) işlecini ikili olarak kullanır ve parametreleri geleneksel parantezlerle gösterir (parantezleri hep doğru sayıda kapatıyorsunuzdur (değil mi?)). -) - -$(H5 Derleme modeli) - -$(P -D'de derleme, erişim denetimi, ve modül kavramlarının temeli dosyadır. Paketleme kavramının temeli de klasördür; bu konuda daha fazla karmaşıklık getirmez. Program kodunun daha gelişmiş bir veri tabanında bulunmasına gerek görülmez. D'nin yaklaşımı olan dosya ve klasör kavramı da zaten bir tür $(I veri tabanı)dır. En iyi programcılar tarafından uzun emekler sonucunda geliştirilmiş olan dosya ve klasör modeli sayesinde şu araçları da hazır olarak kullanma şansımız doğar: sürüm denetimi [version control], yedekleme, işletim sistemi düzeyinde koruma, günlükleme [journaling], vs. Böylece program geliştirmeye başlamak için gereken araçlar da ikiye inmiş olur: metin düzenleyici ve derleyici. Aslında D'de araçlar konusunda henüz fazla gelişme kaydedildiğini söyleyemeyiz ama yine de şunlar mevcut: Emacs'te d-mode modu, Eclipse'in Descent eklentisi, Linux'ta hata ayıklayıcı ZeroBugs, ve Poseidon geliştirme ortamı... -) - -$(P -D'de kod üretimi bildiğimiz derleme ve bağlama adımlarından oluşur ama benzerlerinden çok daha hızlıdır; bunun iki, hayır üç nedeni var. Birincisi; dilin grameri sözcükleme [lexing], ayrıştırma [parsing], ve çözümleme [analysis] adımlarını birbirlerinden ayrı ve çok hızlı olarak yapmaya olanak verir. İkincisi; başka derleyicilerin çoğunda olduğu gibi program parçalarını [object file] ayrı ayrı oluşturmak yerine, D derleyicisine herşeyi önce bellekte oluşturmasını ve en sonunda diske yazmasını söyleyebilirsiniz. Üçüncüsü; D'nin yaratıcısı ve ilk gerçekleştiricisi olan Walter Bright, en iyileme [optimization] konusunda son derece deneyimli ve uzman birisidir. Geliştirme aşamalarındaki beklemelerin az olması D'yi çok güçlü bir yorumlayıcı [interpreter] haline de getirir ($(CODE #!/bin/sh) yazımındaki gibi $(CODE #!) bile desteklenir). -) - -$(P -D ayrı ayrı derlemeyi destekleyen gerçek bir modül sistemine sahiptir. Modül özetlerini (başlık dosyalarının entel ismi) kaynak dosyalarından otomatik olarak öğrenebilir. Böylece başlık dosyalarıyla kendimiz ilgilenmek zorunda kalmayız; ama istersek bizim yazmamıza da izin verilir; ve bu konunun şikayet edilecek tarafı da kalmamış olur. -) - -$(H5 Bellek modeli ve çok çekirdekliler [manycores]) - -$(P -C fonksiyonlarını doğrudan çağırıyor olması D'nin de C bellek modeli üzerine kurulu olduğunu düşündürmüş olabilir. Öyle olabilse gerçekten iyi olurdu ama ne yazık ki bu, paralel mimarileri ile yüksek işlem gücü sağlayan çok çekirdekli işlemciler yüzünden olanaksızdır. Çok çekirdekliler artık günlük hayatımızdalar; C'nin bu konudaki yaklaşımı ise ne yazık ki çok sıradan ve hataya açık olarak kalmıştır. Yordamsal ve nesne yönelimli bazı başka diller ise bu konuda ancak pek az gelişme gösterebilmişlerdir ve bu durum, paralel işlemlerle ilgili sorunları değişmezlik [immutability] kavramının yardımıyla aşan fonksiyonel dillerin tekrardan gündeme gelmesine neden olmuştur. -) - -$(P -Yeni bir dil olduğu için iş parçacıkları [threads] konusunda D çok şanslı bir durumdadır; D'nin bellek modeli diğer modellerden köklü farklılıklar içerir. İş parçacıklarıyla şu şekilde çalışmaya alışmışızdır: iş parçacığı başlatmak için bir fonksiyon çağrılır ve bu yeni iş parçacığı bütün programın belleğini görebilen ve değiştirebilen bir duruma geliverir. Veya seçime bağlı olarak, işletim sistemine bağlı bazı yöntemler kullanılarak iş parçacığına özel [thread-private] bellek de kullanılabilir. Şimdiye kadar yalnızca bir sorun olarak görülen bu durum, günümüzde bir kabus halini almıştır. Dünün sorunları, verinin eş zamanlı olarak değişmesinin getirdiği doğal bir sonuçtu: verinin hep geçerli bir durumda olmasını sağlamak amacıyla bütün değişikleri takip etmek, ve değişikliklerin doğru sırada yapılmalarını hedeflemek son derece güç bir iştir. Ama insanlar yine de bu durumu kabul etmek zorundaydılar; çünkü paylaşımlı bellek, donanımı çok sadık olarak modellemekteydi ve doğru çalıştığında çok verimli olmaktaydı. Şimdi $(I kabus) konusuna geliyoruz: günümüzde bellek eskiden olduğundan daha az paylaşımlıdır. Bugünkü donanımlarda işlemciler belleği daha katmanlı bir şekilde kullanmaktadırlar; her bir çekirdeğin kendine özel bir belleği vardır! Sonuçta paylaşımlı bellek yalnızca zor yöntem olmakla kalmamış, yavaş olan yöntem haline de gelmiştir; çünkü paylaşımlı bellek, artık donanımı sadık bir şekilde modellememektedir. -) - -$(P -Geleneksel diller bu tür sorunlarla uğraşırken, fonksiyonel diller bu konuya matematiksel saflıkla yaklaşıyorlardı: donanımı modellemekle değil, gerçek matematiği modellemekle ilgileniyorlardı. Matematik çoğunlukla değişim [mutation] içermediğinden ve zamandan bağımsız olduğundan, paralel işlemler için çok uygundur. (Matematikçilikten dönme o ilk programcıların paralel işlemeyi ilk duyduklarında nasıl sevinmiş olabileceklerini hayal edebiliyorum.) Böyle bir modelin sırasız ve paralel işlemlere ne kadar uygun olduğu fonksiyonel programcılıkta başından beri biliniyordu. Ama bu uygunluk daha çok $(I saklı bir enerji) olarak duruyordu; günümüzde olduğu gibi bir amaç olarak görülmüyordu. Paralel işlemler kullanan ciddi programların en azından bazı bölümlerinin fonksiyonel ve değişimden bağımsız olarak yazılmalarının önemi günümüzde daha iyi anlaşılmaktadır. -) - -$(P -Bu konuda D'nin aldığı tutum nedir? D'nin paralelliğe yaklaşımı temel bir kavram üzerine kuruludur: Belleğin öncelikle iş parçacığına özel olduğu varsayılır, ve ancak istendiğinde paylaşımlı olarak kullanılır. -) - -$(P -D'de bütün bellek, hatta globaller bile, öncelikle onu kullanan iş parçacığına özeldir. Paylaşılmak istenirse nesneler $(CODE shared) anahtar sözcüğü ile bildirilirler ve ancak ondan sonra başka iş parçacıkları tarafından da görülebilirler. Burada önemli olan, tüm sistemin $(CODE shared) olan nesnelerden haberli olması, ve o nesnelere erişimin doğru sırada yapılmasını sağlamak için bazı kısıtlamalar getirmesidir. Bu model, nesnelerin paylaşımlı olduklarını varsayan dillerdeki bir sürü belalı sorunu böylece ortadan kaldırır. O dillerde nesnelerin hangilerinin paylaşıldıkları bilinmediği için programcıya güvenilmekte ve programcının nesneleri doğru olarak bildirmiş ve kullanmış olması beklenmektedir. Ondan sonra da çeşitli durumları açıklamak için karmaşık kurallar gerekmiştir: paylaşılmayan veriler, paylaşımlı olarak bildirilmiş olan veriler, paylaşımlı oldukları bildirilmediği halde paylaşımlı olan veriler, ve bunların her türlü kombinasyonu... Bu kurallar da onları anlayabilen topu topu beş kişi için gayet açıkça yazılmıştır ve geri kalanlarımıza da pes etmekten başka çare kalmamıştır. -) - -$(P -Çok çekirdekliler konusunda araştırma ve geliştirme çabaları yoğun olarak devam ettiği halde hâlâ iyi bir model bulunamamıştır. Ama özel bellek modeli üzerine kurulu olduğu için D'nin bu konuda önü açıktır: saf fonksiyonlar, kilitsiz nesneler [lock-free primitives], alışık olduğumuz kilitlere dayalı programlama, mesaj kuyrukları (henüz plan aşamasında), vs. Sahiplenme türleri gibi daha ileri konular da halen tartışılmaktadır. -) - -$(H5 Değişmezlik [Immutability]) - -$(P -Buraya kadar tamam... Pekiyi matematiğin saflığı, değişmezlik, ve fonksiyonel kodlama gibi konulara ne oldu? D, fonksiyonel programlamanın ve değişmezlik kavramının paralel programlamadaki (ve başka konulardaki) önemini kabul eder ve hiçbir şekilde değişmeyecek olan nesneleri bildirmek için $(CODE immutable) anahtar sözcüğünü getirir. Aynı zamanda, D çoğumuzun yakından tanıdığı değişkenlik kavramını ve bu kavramın en iyi çözüm olduğu durumların varlığını da kabul eder. (Eğer siz çoğumuza dahil değilseniz, $(I bazılarımız) demiş olayım.) D'nin bu soruna yanıtı ilginçtir, çünkü böylece değişken ve değişmez nesneler kusursuzca bir araya getirilmiş olurlar. -) - -$(P -Değişmez veriler neden bu kadar yararlıdır? Değişmez verilerin iş parçacıkları tarafından paylaşılmalarında erişim sırasının [synchronization] önemi ortadan kalkar. Doğal olarak da, hiç yapılmayan sıralama en hızlı sıralamadır... Buradaki ustalık, salt okunan [read-only] verilerin gerçekten yalnızca okunduklarını sağlamaktadır; yoksa zaten bu konuda hiçbir güvenceden söz edemeyiz. D, paralel programlamanın bu önemli özelliğini sağlayabilmek için fonksiyonel programlamaya eşi görülmemiş derecede destek verir. $(CODE immutable) olarak bildirilen verilerin değişmezlikleri derleme zamanında belirlenir; ve doğru yazılmış olan bir program $(CODE immutable) veriyi kesinlikle değiştiremez. Dahası, değişmezlik derinlemesinedir: değişmez bir alan içerisinden referansla geçtiğiniz başka bir alan da değişmezdir. (Neden? Çünkü öyle olmadığı zaman bütün sistem zayıflar: değişken verileri farkında olmadan paylaşmaya başlarız ve önlemeye çalıştığımız karmaşık kurallarla tekrar karşı karşıya geliriz.) Birbirlerine bağlı nesnelerin oluşturdukları alt grafların tamamı kolaylıkla $(CODE immutable) olarak bildirilebilirler. D'nin tür sistemi bunları tanıdığı için iş parçacıkları tarafından paylaşılmaları sağlanır ve tek işli [single-threaded] programlar için de eniyileme olanakları doğmuş olur. -) - -$(P -Pekiyi özel bellek modelini benimseyen ilk dil D midir? Hiç de değil. D'yi diğerlerinden ayrı kılan, değişken ve değişmez verileri öncelikle özel olarak kabul ettiği tek bir bellek sistemi altında barındırmasıdır. Aslında bu konuda anlatacak çok ayrıntı var; biz yine tanıtıma devam edelim. -) - -$(H5 Güvenlik ön planda) - -$(P -Bir sistem dili olması nedeniyle D, son derece verimli ama bir o kadar da tehlikeli olanaklar sunar: denetimsiz işaretçiler, programcının elle yapabildiği bellek denetimi, ve en dikkatli tasarımları bile mahvedebilecek tür dönüşümleri. Bununla beraber, modüllerin güvenli olduklarını bildirebilme olanağı ve o olanağı desteklemek için bellek güvenliği sağlayan ve SafeD olarak adlandırılan bir derleme şekli de sunar. Yine de bu şekilde başarılı olarak derlenmiş olması; ne kodun taşınabilir olduğunu, ne güvenli olarak programlandığını, ne de birim testler [unit testing] gerektirmediğini gösterir. SafeD yalnızca bellek hatalarının [memory corruption] olasılığını azaltır. -) - -$(P -Böyle güvenli modüller (veya güvenli derleme); işaretçileri olduklarından farklı kullanma ve yığıt nesnelerinin adreslerini fonksiyonlardan döndürme gibi bütün tehlikeli dil olanaklarını daha derleme zamanında önler. SafeD'de bellek hatalarıyla karşılaşamazsınız. Aslında bütün büyük programların kodlarının çoğunluğu böyle güvenli modüllerden oluşmalıdır; bu programların $(I sistem) modüllerinin az sayıda olmasına çalışılmalı, ve sistem modüllerinin kod incelemeleri [code review] çok dikkatlice yapılmalıdır. Çoğu program tamamen SafeD ile yazılabilir, ama tabii bellek ayırma gibi işleri halleden bazı modülleri yazarken biraz el emeği de gerekecektir. Böylelikle, programın bazı bölümlerini başka bir dilde yazmak gerekmemiş olur. SafeD henüz hazır değil; bu yazı yazıldığı sırada SafeD'nin gelişimi halen devam etmekteydi. -) - -$(H5 Kabul, başka yollar da olabilir) - -$(P -D'nin çok paradigmalı olduğunu söylerken, bazı seçimler sırasında çatışmaların gerekmeyebileceğini kastediyoruz. ["It doesn't have an axe to grind."] D bu konuyu kavramıştır... Herşeyin nesne, fonksiyon, liste, eşleme tablosu, veya Noel Baba olması gerekmez. Ne oldukları size kalmış olmalıdır. Bu yüzden D programcılığı özgürlükler getirir, çünkü bir sorunu çözmeye çalışırken eldeki olanakları o çözüme uydurmak için çabalamak gerekmez. Buradaki bir gerçeği de dile getirmek gerekiyor: özgürlüklerle birlikte sorumluluklar da gelir; bu sefer de çözüm için hangi tasarımın daha uygun olacağına karar vermek için zaman harcamak gerekecektir. -) - -$(P -D bu konuda C++'nın izinden yürür; $(I doğru olan tek yol) gibi bir ısrarı yoktur. Ek olarak, D her paradigmaya daha fazla destek verir, paradigmalar arasında daha fazla uyum sağlar, ve seçilen paradigmaların izlenmesinde daha az engel çıkartır. Burada aslında iyi bir öğrenciyle karşı karşıyayız; D C++'tan çok şey öğrenmiştir. Daha az seçici diller olan Java, Haskell, Eiffel, Javascript, Python, ve Lisp'ten de etkilenmiştir. (Aslında çoğu dil Lisp'ten bir şeyler almıştır ama bazıları bunu itiraf edemez.) -) - -$(H5 Nesne yönelimli olanaklar) - -$(P -D'de hem $(CODE struct) hem de $(CODE class) var. Bunların birçok özellikleri aynı olsa da amaçları farklıdır: $(CODE struct)'lar değer türleridirler [value types], $(CODE class)'lar ise dinamik çokşekillilik [dynamic polymorphism] amacıyla ve yalnızca referans ile kullanılırlar. Böylelikle karışıklıkların, dilimleme [slicing] hatalarının, ve $(CODE $(GREEN // Bu siniftan turetmeyiniz!)) gibi açıklama satırlarının önüne geçilmiş olur. Türler tasarlanırken onların tekşekilli değer mi oldukları yoksa çokşekilli referans mı oldukları başından belirlenir. C++ bu kavramları karışık olarak kullanmaya izin vermesiyle tanınmaktadır; ama gerçekte bunların kullanımları hem nadirdir, hem hataya açıktırlar, hem de zaten tartışmaya açık olduklarından bütünüyle engellemek en doğrusudur. -) - -$(P -D'nin nesne yönelimliliği Java ve C#'ınkine benzer: gerçekleştirme olarak tekli, arayüz olarak çoklu kalıtım... Ama D bunu çoklu kalıtımın zararlı olduğu savını sürdürmek için yapmaz; verilerin çoklu kalıtımda yer almalarının ve etkin olarak gerçekleştirilmelerinin ne kadar zor olduğunu kabul ettiği için yapar. Çoklu kalıtımın yararlarını olabildiğince, ama denetimli olarak sunabilmek için birden fazla türden türetmeye şu şekilde izin verir: -) - ---- -class GereçÜstSınıfı { ... } -class Alet { ... } -class Gereç : GereçÜstSınıfı, Arayüz1, Arayüz2 -{ - Alet Alet_döndür() { ... } - alias Alet_döndür this; // Gereç Alet'ten türemiş olur -} ---- - -$(P -$(I [Çevirenin notu: Örnekteki Türkçe harfler dikkatsizlik yüzünden değil: D, kaynak kodlarda Unicode'u destekler!]) -) - -$(P -Burada $(CODE alias)'ın anlamı şudur: $(CODE Alet)'in gerektiği bir durumda elimizde bir $(CODE Gereç) varsa, derleyici $(CODE Alet_döndür)'ü çağırarak $(CODE Alet) edinir. Burada fonksiyon çağrısı tamamen şeffaf olarak gerçekleşir; zaten öyle olmasaydı $(I alt tür) kavramı değil, olsa olsa ona yakın bir şey olabilirdi. (Bu size imalı geldiyse, imalı olduğu içindir.) Dahası, $(CODE Alet_döndür) döndürdüğü tür konusunda da tam yetki sahibidir: örneğin isterse $(CODE this)'in bir alt türünü döndürür, isterse de yepyeni bir nesneyi. Gereken durumlarda yine de bazı fonksiyon çağrılarını yakalamak için yönlendirmeler yapmak da gerekebilir. Ama ilk bakışta bir sürü kod tekrarı gerektirecek gibi görünse de, burada da D'nin yansıma [reflection] ve kod üretme olanaklarından yararlanılır. Burada temel fikir, D'nin $(CODE alias this) ile yeni alt türler oluşturmaya izin vermesidir. Bu sayede $(CODE int)'ten bile yeni türler türetilebilir. -) - -$(P -D nesne yönelimli programcılık deneyimlerinden edinilen yararlı başka yöntemler de getirir: aşırı yüklemenin yanlışlıkla yapılmasını engellemek için açıkça yazılması gereken $(CODE override) sözcüğü; sinyallere [signal] ve yuvalara [slot] doğrudan destek; ve tescilli olduğu için tam adını veremeyeceğim için $(I sözleşmeli programlama) [contract programming] diyeceğim bir yöntem. $(I [Çevirenin notu: Yazar burada Eiffel programlama dilinin tescilli ifadesi "Design by Contract" özelliğine gönderme yapıyor.]) -) - -$(H5 Fonksiyonel programlama) - -$(P -Çabuk cevap verin: Fibonacci serisi fonksiyonel programlamaya uygun olarak nasıl yazılır? -) - ---- -uint fib(uint n) -{ - return n < 2 ? n : fib(n -1) + fib(n -2); -} ---- - -$(P -Fantaziler kurmaktan hoşlandığımı itiraf ediyorum. Fantazilerimden birisi zamanda geriye gitmek, Fibonacci'nin bu yazımını tamamen ortadan kaldırmak, ve böylelikle bilgisayar öğretmenlerinin onu öğretmelerine başından engel olmaktır. (Listemde kabarcık sıralama [bubble sort] ve O(n log n) yer kullanan $(CODE quicksort) algoritmaları da var. Ama bu $(CODE fib) o ikisini gölgede bırakır. Ayrıca Hitler veya Stalin'i ortadan kaldırmanın bile yararları tartışılır olsa da, $(CODE fib)'i ortadan kaldırmak gayet iyi olur.) $(CODE fib) fonksiyonunun çalışma süresi $(CODE n) ile üstel olarak değiştiği için; bu şekilde yazılmış olması, karmaşıklık ve işlemlerin bedeli gibi kavramlardan tamamen habersizlik anlamına gelir; sevimliliğin baştansavmacılığa yeğlenmesinin bir örneğidir, ve savurganlıktır. Üstel çalışma süresinin ne kadar kötü olduğu herkesçe biliniyor mu? Benim bilgisayarımda $(CODE fib(10)) ve $(CODE fib(20)) farkedilmeyecek kadar çabuk çalışıyor, ama $(CODE fib(50)) on dokuz buçuk dakika tutuyor. $(CODE fib(1000)) ise herhalde bütün insanlık tarihinin sonuna kadar sürecektir. Belki de insanlık tarihinin sonunu görmek bu fonksiyonu öğretmeye devam ettiğimiz için hiç olmazsa bir teselli olur. -) - -$(P -O zaman fonksiyonel Fibonacci'yi $(I çevreci) olarak nasıl yazabiliriz? -) - ---- -uint fib(uint n) -{ - uint ilerlet(uint i, uint fib_1, uint fib_2) - { - return i == n - ? fib_2 - : ilerlet(i + 1, fib_1 + fib_2, fib_1); - } - return ilerlet(0, 1, 0); -} ---- - -$(P -Bu fonksiyon $(CODE fib(50))'yi gözardı edilebilecek kadar hızlı hesaplar, çünkü bu gerçekleştirme O(n) zaman alır. Bellek karmaşıklığını da kuyruk eniyileştirmesi [tail call optimization] halleder. Bu yeni $(CODE fib)'in sorunu ise eskisi kadar gösterişli olmamasıdır. Yeni $(CODE fib), fonksiyon parametresi kılığında iki durum değişkeni kullanmaktadır. Döngüyü ve bu değişkenleri daha açık olarak şöyle yazabiliriz: -) - ---- -uint fib(uint n) -{ - uint fib_1 = 1, fib_2 = 0; - foreach (i; 0 .. n) - { - auto t = fib_1; - fib_1 += fib_2; - fib_2 = t; - } - return fib_2; -} ---- - -$(P -Eyvah! Ama bu artık fonksiyonel değil! Döngü içindeki o iğrenç dönüşümlere bakın hele! Maalesef yanlış bir adım attık ve matematiksel saflık zirvesinden düşerek basit günahkarların düzeyine inmiş olduk. -) - -$(P -Ama şöyle bir düşünecek olursak, döngülü $(CODE fib)'in o kadar da ayıplanır bir tarafı olmadığını görürüz. Kapalı bir kutu olarak görecek olursak, $(CODE fib) verilen aynı giriş değerlerine karşılık hep aynı çıkışı üretmektedir. Yani bu anlamda saftır. Özel durum değişkenleri kullandığı için teorik olarak daha az fonksiyonel oduğunu söyleyebiliriz tabii, ama özünde fonksiyoneldir. Bu düşünceye devam edersek çok ilginç bir sonuca varırız: bir fonksiyondaki durum değişkenleri tamamen geçici iseler (yani program yığıtındaysalar) ve özelseler (yani onları değiştirebilecek başka fonksiyonlara referans olarak geçirilmemişlerse), fonksiyon saf olarak kabul edilebilir. -) - -$(P -İşte D'deki fonksiyonel saflığın tanımı da budur: saf bir fonksiyon içerisinde geçici ve özel değişiklikler yapılabilir. Bu tür fonksiyonların bildirimlerine $(CODE pure) koyulur: -) - ---- -pure uint fib(uint n) -{ - ... döngülü kod ... -} ---- - -$(P -Saflık kavramını böylece gevşek bırakan D, iki açıdan yarar sağlamış olur: bir tarafta çok sağlam bir saflık garantisi, öte yanda döngülü gerçekleştirmeler tercih edildiği durumlardaki kodlama rahatlığı. Bundan daha güzel bir şey olabilir mi... -) - -$(P -Fibonacci serisinin fonksiyonel dillerde aslında bir yazımı daha vardır: $(I sonsuz liste) denen yöntem... Fonksiyon yazmak yerine, sonsuz ve tembel bir liste oluşturulur ve bu listeden Fibonacci sayıları istendikçe çekilirler. Böyle tembel listeler D'de çok güzel bir şekilde tanımlanırlar. Örneğin aşağıdaki kod, ilk 50 Fibonacci sayısını oluşturmaktadır. (Bu örneği çalıştırmak için $(CODE std.range)'i eklemeniz gerekir.) -) - ---- -foreach (f; take(50, recurrence!("a[n-1] + a[n-2]")(0, 1))) -{ - writeln(f); -} ---- - -$(P -Aslında tek satır bile değil, yarım satır! Orada $(CODE recurrence)'ın anlamı, $(CODE 0) ve $(CODE 1) ile başlayan ve $(CODE "a[n-1] + a[n-2]") formülünü kullanan sonsuz bir liste oluşturmaktır. Bu ifadenin hiçbir noktasında ne dinamik bellek ayrılır, ne bir fonksiyon çağrılır, ne de geridönüşümsüz bir kaynak kullanılır. Sonuçta bu kod, daha önce gördüğümüz döngülü fonksiyonun eşdeğeridir. Bunun nasıl işlediği bir sonraki bölümde anlatılıyor. -) - -$(H5 Şablonlarla türden bağımsız programlama [Generic programming]) - -$(P -(Hani çok beğendiğiniz bir filmi, kitabı veya bir müziği birisine anlatırken fazla abartmamaya ve karşınızdakinin beklentilerini fazla yükseltmemeye çalışırsınız ya... D'nin türden bağımsız programlama olanağını anlatırken aynı hisler içerisindeyim.) Aslında türden bağımsız programlamanın birden fazla tanımı vardır; hatta $(LINK2 http://www.wikipedia.com, Wikipedia)'daki tarafsız tanımı bile hâlâ tartışmalara neden olmaktadır. Bazı insanlar onu $(I parametreli türlerle programlama) olarak kabul ederler (templates, generics); başkaları ise $(I algoritmaları olabildiğince genel olarak, ama karmaşıklık güvencelerini de koruyacak şekilde ifade etmek) olarak görürler. Bunlardan ilkine bu bölümde, ikincisine de bir sonraki bölümde değineceğim. -) - -$(P -Türleri parametreli olabilen $(CODE struct)'lar, $(CODE class)'lar ve fonksiyonlar D'de çok basit bir söz dizimi ile tanımlanırlar. Örneğin bir $(CODE min) fonksiyonu şöyle yazılabilir: -) - ---- -auto min(T)(T a, T b) { return b < a ? b : a; } -... -auto x = min(4, 5); ---- - -$(P -Burada $(CODE T) tür parametresidir, $(CODE a) ile $(CODE b) de fonksiyon parametreleridir. Dönüş türünün $(CODE auto) olması, $(CODE min)'in döndürdüğü türün derleyici tarafından belirlendiği anlamına gelir. Bu da bir liste sınıfının özü: -) - ---- -class Liste(T) -{ - T değer; - Liste sonraki; - ... -} -... -Liste!int sayılar; ---- - -$(P -Aslında eğlence daha yeni başlıyor. Bu konunun hakkını vermek bu kadar kısa bir yazı içinde mümkün olmadığı için, sonraki paragraflarda başka dillerden olan farklılıklarını anlatacağım. -) - -$(H5 Parametre çeşitleri) - -$(P -Şablonlarda parametre olarak türler yanında sayılar (tam veya kesirli), dizgiler, derleme zamanında bilinen $(CODE struct) değerleri, ve $(CODE alias)'lar da kullanılabilir. Takma isim anlamına gelen $(CODE alias)'lar programdaki herhangi bir ismin yerini alabilirler: değer, tür, fonksiyon, ve hatta başka bir şablon. (D, sonsuz şablon şablon şablon ... parametre sorunundan da böylece sıyrılmış olur; çünkü o durumlarda parametre $(CODE alias) olarak geçirilir.) $(CODE alias) parametreler lambda fonksiyonlarında da işe yararlar. Belirsiz sayıda [variadic] parametreler de desteklenmektedir. -) - -$(H5 Dizgi [string] işlemleri) - -$(P -Derleme zamanında değiştirilemeseler, dizgileri şablon parametresi olarak kullanmak anlamsız olurdu. D, bütün dizgi işlemlerine derleme zamanında bile izin verir: ekleme, indeksleme, alt dizgi seçme, üzerinde ilerleme, karşılaştırma, vs. -) - -$(H5 Kod üretme: Bir anlamda $(I türden bağımsız programlama çeviricisi [assembler])) - -$(P -Derleme zamanındaki dizgi işlemleri her ne kadar ilginç olsalar da verilerle kısıtlı kalmak zorundadırlar. Bunun bir ileri aşaması, dizgileri $(CODE mixin) ifadesi yoluyla koda çevirme olanağıdır. $(CODE recurrence) örneğini hatırlıyor musunuz? Fibonacci serisinin formülünü $(CODE recurrence)'e bir dizgi olarak veriyorduk. İşte o dizgi daha sonra koda dönüşür ve parametreler de o koda geçirilirler. Başka bir örnek olarak, bir aralıktaki nesnelerin D'de nasıl sıraya dizildiklerine bakabilirsiniz: -) - ---- -// bir tamsayılar dizisi -auto dizi = [ 1, 3, 5, 2 ]; - -// küçükten büyüğe doğru sıralama -sort(dizi); - -// bir lambda fonksiyonu yardımıyla ters sırada -sort!((x, y) { return x > y; })(dizi); - -// yine ters sırada ama bu sefer kod üretme olanağı ile -// ve parametreleri geleneksel isimleri a ve b olarak -// tanımlayan bir karşılaştırma -sort!("a > b")(dizi); ---- - -$(P -Kod üretme aslında çok önemli bir özelliktir, çünkü dilde bulunmayan yeni olanaklar sağlar. Örneğin D'de bit alanları [bit fields] bulunmadığı halde standart modül $(CODE std.bitmanip) bütün bit işlemlerini çok da etkin bir biçimde gerçekleştirir. -) - -$(H5 İçgözlem [Introspection]) - -$(P -İçgözlem, yani bir kod birimini inceleyebilme olanağı, bir anlamda kod üretmenin tümleyenidir; çünkü kodu üretmek yerine, üretilen koda bakmakla ilgilidir. Aynı zamanda kod üretmeye de yardımı olur; örneğin numaralama [enumeration] ile ilgili bir ayrıştırma fonksiyonu için gereken bilgiyi sağlar. Aslında içgözlem henüz bütünüyle desteklenmiyor ama eskisinden daha iyi bir tasarımı bitirilmiş durumda ve gerçekleştirilmeyi bekliyor. -) - -$(H5 $(CODE is) ve $(CODE static if)) - -$(P -C++'da en basitinden olmayan şablon yazan hemen hemen herkes bir noktada şu iki şeye ihtiyaç duymuş ve büyük engellerle karşılaşmıştır: a) belirli bir kodun derlenebilip derlenemeyeceğini kodun yazıldığı sırada saptayabilmek ve ona göre karar verebilmek, b) Bool ifadelerini derleme zamanında işletebilmek ve sonuca göre kod üretebilmek. D'de derleme zamanında işletilen $(CODE is(typeof(ifade))) kullanımı, ifade doğru olduğunda $(CODE true), yanlış olduğunda da $(CODE false) sonucunu verir (derleme sonlanmadan). $(CODE static if) de $(CODE if) gibidir ama derleme zamanında işletilir ve derleme zamanında geçerli olan bütün Bool ifadeleriyle kullanılabilir (yani $(CODE #if)'in sonunda gerçekten işe yarayan halidir.) Salt bu iki özelliğin türden bağımsız programlamadaki güçlükleri yarıya indirdiğini söyleyebilirim. C++0x'in bunlardan birisini bile içermeyecek olması ise beni bütünüyle hayal kırıklığına uğratmıştır. -) - -$(H5 Dahası var...) - -$(P -Türden bağımsız programlama çok geniş bir konu... Her ne kadar D bu konuyu şaşırtıcı derecede az sayıda kavramla hallediyor olsa da, daha fazla bilgiyi iyice ayrıntılarına girmeden vermek mümkün değil. D'de başka olanaklar da var: uyarlanabilen hata mesajları, C++0x'teki kavramlara [concepts] benzeyen kısıtlamalı şablonlar, eşlemeler [tuples], D'ye has $(I yerel somutlama) denen bir özellik [local instantiation] (esnek ve etkin lambda'lar için gerekir), vs. -) - -$(H5 Standart kütüphane hakkında bir kaç söz) - -$(P -Daha önce de bahsettiğim gibi, bu konu biraz hassas ve politik. Phobos ve Tango adında oldukça kapsamlı iki değişik kütüphane var. Ben bunlardan yalnızca birincisini kullandığım için yazacaklarım yalnızca onunla ilgili olacak. -) - -$(P -STL çıktığından bu yana topluluk ve algoritma kavramları yepyeni bir boyuta erişti. Bu o kadar önemli bir olgudur ki, STL'den sonra çıkıp da onu dikkate almayan kütüphaneler beceriksiz ve saf olarak kalma riski altındadırlar. (Dolayısıyla hangi dili kullanıyor olurlarsa olsunlar, bütün programcılara STL'yi mutlaka anlamalarını öneririm.) Bunun nedeni STL'nin mükemmel bir kütüphane olması değildir; çünkü STL zaten mükemmel değildir. STL mecburen C++'nın hem güçlü hem de zayıf taraflarına bağlı kalmak zorundaydı. Örneğin çok etkindir ama üst düzey programlama desteği iyi değildir. C++'ya olan bu yakınlığı, başka dil programcılarının STL'nin kavramlarını anlamalarını güçleştirebilir; çünkü STL'nin çok önemli olan özü, kütüphane olanaklarının arkasında farkedilmeden saklı kalmış olabilir. Dahası, STL'nin kendi hataları da vardır; örneğin kavramsal yapısı, bir sürü başka veri yapısına ve onların elemanları üzerinde ilerlemeye elverişli değildir. -) - -$(P -STL'nin en yararlı tarafı, temel topluluklar ve algoritmalar içeren bir kütüphanenin nasıl tasarlanabileceği sorusunu yeniden ortaya atmış olması ve bu sorunun güzel bir yanıtını oluşturmasıdır. STL'nin sorduğu soru şudur: $(I Bir algoritma, üzerinde çalışacağı veriden en az ne istemelidir?) Bu konuya çoğu kütüphanecinin ve çoğu algoritma uzmanının bu kadar süre kayıtsız kalmış olması aslında şaşırtıcıdır. STL'nin yaklaşımı, birleştirici arayüzler [unifying interfaces] arayışından farklıdır. Birleştirici arayüzler örneğin indeksli erişimin hem dizilere hem de listelere uygun olduğunu savunurlar ve toplulukların yapısal özelliklerinin getirebileceği bazı doğal uygunsuzlukları gerçekleştirmelerle ilgili bir sorun olarak görürler. STL, bu tür görüşlerdeki kusurları açıkça ortaya serer; örneğin sıralı arama [linear search] gibi temel bir algoritmanın bile birleştirici bir arayüzde bulunmaması gerektiği açıktır (kimsenin karesel [quadratic] bir algoritmanın sonuçlanmasını beklemek istemeyeceğini varsayıyorum). Bu gerçekleri algoritmalarla ciddi olarak ilgilenen herkesin bilmesi gerekirken; algoritmaları anlamış olmakla, onların bir programlama dilinde nasıl gerçekleştirilebileceklerini görmek arasında her nasılsa bazı kopukluklar oluşmuştur. Örneğin algoritma temelleri konusunda deneyimli birisi olduğum halde, ben sıralı arama algoritmasındaki saflığı ve mükemmelliği on beş yıl önce STL'deki gerçekleştirmesini görene kadar kendim farkedememiştim. -) - -$(P -Bu kadar sözün özü, Phobos'un da STL'ye benzer bir kütüphane olduğudur (belgelerinde $(CODE std.algorithm) ve $(CODE std.range)'e bakabilirsiniz). Bence aslında Phobos'un algoritmaları STL'ninkilerden iki nedenden ötürü daha iyidir. Birincisi, Phobos tabii ki kendisinden önceki kütüphanelerden yararlanma avantajına sahiptir. İkincisi, daha üstün bir dilden ve hem de tam anlamıyla yararlanmıştır. -) - -$(H5 Aralıklar [ranges] iyi, erişiciler [iterators] değil) - -$(P -STL'den en farklı tarafı, Phobos'ta erişicilerin bulunmamasıdır. Onda erişici soyutlamasının yerini aralık soyutlaması almıştır. Erişiciler kadar etkin olmalarının yanında aralıklar; sarma [encapsulation], doğruluk denetimi, ve soyutlama konularında çok daha güçlüdürler. (Dikkat ederseniz, erişicilerde temel işlemlerin hiçbirisi denetlenemez bile; garip...) Aralık kullanan kodlar en az erişici kullanan kodlar kadar hızlıdırlar, onlardan daha güvenlidirler, ve yazımları da daha kısadır. Böylece sonunda $(CODE for) döngüleri de tek satıra sığacak kadar kısa yazılabilmektedir. Aralıklar kullanan kod o kadar kısa ve özdür ki, erişicilerle yazıldığında çok külfetli olabilecek bir çok kodlama tarzının önü açılmış olur. Örneğin iki aralıkta art arda zincirleme olarak ilerlemeyi sağlayan $(CODE chain) diye bir fonksiyon düşünülebilir ve son derece de kullanışlıdır [chain: zincir]. $(CODE chain)'i erişici kullanacak şekilde yazmaya kalksak, giriş olarak dört erişici, çıkış olarak da iki erişici gerekeceği için; bu, kullanışsız olacak düzeyde hantaldır. Öte yandan, $(CODE chain)'i aralıklarla yazmak yalnızca iki giriş aralığı ve tek bir çıkış aralığı gerektirir. Dahası, belirsiz sayıda parametre olanağı [variadic] kullanıldığında $(CODE chain)'e belirsiz sayıda aralık verilebilir ve birdenbire kullanışlılığı iyice artmış olur. $(CODE chain), $(CODE std.range) modülünde tanımlanmıştır. Örneğin üç farklı dizinin elemanları üzerinde ilerlemek şu kadar kolaydır: -) - ---- -int[] a, b, c; -... -foreach (e; chain(a, b, c)) -{ - // ... e'yi kullan ... -} ---- - -$(P -O kodda dizilerin birbirlerine eklenmediklerine dikkat edin! $(CODE chain) onları oldukları yerde bırakır ve dizileri sırayla ayrı ayrı gezer. Bunun sonucunda da $(CODE chain) yoluyla erişilerek yapılan değişikler dizilerdeki elemanların kendilerini değiştirmiş olurlar. Örneğin şu kodun ne yaptığını tahmin edin: -) - ---- -sort(chain(a, b, c)); ---- - -$(P -Doğru bildiniz: dizilerin elemanları ortaklaşa olarak sıralanırlar; yani dizilerin boyları hiç değişmeden, en küçük elemanlar $(CODE a)'da olacak şekilde... Tabii bu sadece aralıkların ve aralık birleştiricilerinin algoritmalarla birlikte kullanıldıklarında ne kadar güçlü olduklarını gösteren küçücük bir örnek. -) - -$(H5 Sonsuza kadar tembellik ve ötesi) - -$(P -STL algoritmalarının (tabii başkalarının da) $(I hevesli) olduklarını söyleyebiliriz: fonksiyondan dönüldüğünde işlerini çoktan bitirmişlerdir. Phobos ise uygun olan yerlerde $(I tembel) davranır. Böylelikle Phobos'un birleştirme [composition] ve sonsuz aralıklarla çalışabilme gibi yetenekleri ortaya çıkmıştır. Örnek olarak üst düzey bir $(CODE map) fonksiyonunu ele alalım (fonksiyonel programlamada çok bilinen $(CODE map) fonksiyonundan bahsediyorum, STL'deki $(CODE map) topluluğundan değil); bu fonksiyon, kendisine verilen bir fonksiyonu yine kendisine verilen bir aralıktaki nesnelerle teker teker çağırır. Bu $(CODE map) hevesli olsa, iki sorun ortaya çıkardı. Birincisi, sonucu yerleştirmek için yer ayırması gerekirdi (örneğin bir liste veya bir dizi). İkincisi, fonksiyondan dönmeden önce aralıktaki bütün nesneleri kullanmış olması gerekirdi. Bunlardan birincisi etkinlikle ilgilidir: çoğu durumda bellek ayırmak zaten gerekmemelidir ve mümkünse kaçınılmalıdır (örneğin kullanıcının tek istediğinin $(CODE map)'in ürettiği sonuçlara sırayla bakmak olduğunu düşünün). İkincisi ise temel bir sorundur: hevesli $(CODE map) sonsuz aralıklarla çalışamaz, çünkü o zaman sonsuz iş yapması gerekir. -) - -$(P -Phobos'taki map, bu nedenle tembel bir aralık döndürecek şekilde tasarlanmıştır; işini adım adım ve ancak kullanıcı elemanları sonuçtan okudukça halleder. Öte yandan $(CODE reduce) fonksiyonu (bir anlamda $(CODE map)'in tersidir) işini hevesli olarak yapar. Bazı işlemlerin de hem hevesli olanları, hem de tembel olanları gerekir. Örneğin $(CODE retro(r)) verilen $(CODE r) aralığını ters sırada bir aralık olarak döndürür, $(CODE reverse(r)) ise $(CODE r) aralığını olduğu yerde tersine çevirir. -) - -$(H5 Sonuç) - -$(P -Aslında böyle kısa bir tanıtım yazısında bile anlatacak başka şeyler olmalıydı: birim testler, UTF dizgileri, derleme zamanı fonksiyon çalıştırabilmek (bir anlamda derleme zamanındaki D yorumlayıcısı), dinamik kapamalar [dynamic closures], ve başka bir sürü olanak. Umarım ilginizi uyandırmayı başarabilmişimdir. Eğer kendinize acısız bir sistem programlama dili, usandırmayan bir uygulama dili, ilke sahibi olduğu halde burnu havada olmayan bir dil, veya en önemlisi, bütün bunların ağırlıklı bir bileşimi olan bir dil arıyorsanız, belki de sizin için de uygun olan D'dir. -) - -$(P -Sorularınızı doğrudan yazara gönderebilirsiniz, ama daha da iyisi, news.digitalmars.com Usenet sunucusuna bağlanarak çok hareketli bir ortam olan digitalmars.d haber grubunda sorabilirsiniz. -) - -Macros: - SUBTITLE="Neden D", Andrei Alexandrescu - - DESCRIPTION=Andrei Alexandrescu'nun 'The Case for D' makalesinin Türkçe çevirisi 'Neden D' - - KEYWORDS=d programlama dili makale d tanıtım özellik olanak karşılaştırma andrei alexandrescu diff --git a/ddili/src/makale/saflik.d b/ddili/src/makale/saflik.d deleted file mode 100644 index 1d24ecf..0000000 --- a/ddili/src/makale/saflik.d +++ /dev/null @@ -1,390 +0,0 @@ -Ddoc - -$(H4 D'nin Saflık Kavramı) - -$(P - $(B Yazar:) $(LINK2 http://klickverbot.at/, David Nadlinger) -$(BR) - $(B Çevirmen:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Çeviri Tarihi:) Ekim 2013 -$(BR) - $(B İngilizcesi:) $(LINK2 http://klickverbot.at/blog/2012/05/purity-in-d/, Purity in D) -) - - - -$(P -Programlama dillerinin tasarımları tartışmaya açık konulardır. Buna rağmen, hem donanımda yaşanan gelişmelerin hem de belirli bir dilde yazılmış olan programların bakımlarının önemli konular olmaları nedeniyle daha önceden $(LINK2 http://en.wikipedia.org/wiki/Functional_programming, fonksiyonel programlamada) ortaya çıkmış olan kavramlar yeni dillerde tekrardan keşfedilmektedirler. $(LINK2 http://dlang.org/, D programlama dili) de $(I fonksiyonel saflık) konusuna kendi yaklaşımını getirir. Bu yazı D'nin $(C pure) $(ASIL saf) anahtar sözcüğünü tanıtmakta ve onun diğer olanaklarla nasıl etkileştiğini göstermektedir. -) - - - -$(P -Saflık kavramı hem programcının hem de derleyicinin kaynak kodu anlama konusunda yararlandıkları güçlü bir araçtır. Bu olanağın yararlarına ve asıl kullanımlarına geçmeden önce $(C pure) anahtar sözcüğünün D'de tam olarak ne anlama geldiğine bakalım. Eğer bu kavramı başka dillerden tanıyorsanız lütfen bir süreliğine hiç duymamış gibi davranın. Bu kavramın D'deki gerçekleştirmesindeki küçük farkların büyük etkileri olduğunu göreceksiniz. -) - -$(P -$(C pure), bir işlevi çağıran ile o işlev arasında bir sözleşme belirleyen bir niteliktir. Saf bir işlev değişebilen $(ASIL mutable) $(I evrensel) değişkenleri $(I kullanamaz). Burada "evrensel" ile kastedilen, işlevin parametreleri dışındaki tüm değerlerdir (işlevin parametreleri de iş parçacıkları arasında paylaşılabilen $(ASIL shared) veriler olamazlar). Ek olarak, burada "kullanamaz" sözü ile kastedilen, o değişkenlere okumak için de yazmak için de erişilemediğidir. Bunun tersi olarak, $(C pure) olarak işaretlenmemiş olan bir işlev de $(I saf değil) $(ASIL impure) olarak adlandırılır. -) - -$(P -Yukarıdaki tanımın anlamı, saf bir işlevin aynı parametre değerlerine $(ASIL argument) karşılık olarak hep aynı yan etkiyi üreteceği ve aynı değeri döndüreceğidir. Dolayısıyla, saf bir işlev saf olmayan işlevleri çağıramaz ve klasik anlamda giriş ve çıkış işlemleri uygulayamaz. -) - -$(P -Saflık kavramının fazla kısıtlayıcı olmasını engellemek adına, aslında izin verilmemesi gereken bazı yan etkiler yine de mümkün bırakılmıştır (isterseniz bu ayrıntıları atlayabilirsiniz): -) - -$(UL - -$(LI -$(I Programın sonlanması): D gibi bir sistem dilinde programın hemen sonlanabilmesi her zaman için olasıdır. Bunun önüne geçmek olanaksız olduğundan D'nin saflık kavramı buna açıkça izin verir. -) - -$(LI -$(I Kesirli sayı işlemleri): x86 işlemcilerinde (ve belki başka işlemcilerde de) kesirli sayı hesapları evrensel bayrakların değerlerine bağlıdır. Bir işlev $(C x + y) veya $(C cast(int)x) kadar masum bile olsa tek x87/SSE kesirli sayı ifadesi içerse, o işlevin etkisi veya hata atıp atmayacağı programın evrensel durumuna bağlı demektir.$(DIPNOT 1) Dolayısıyla, saflık kavramının en katı tanımında tek kesirli sayı işlemine bile izin verilmemesi gerekir. Böyle bir karar fazla kısıtlayıcı olacağından, D'de saf işlevlerin kesirli işlem bayraklarını okumalarına ve onlara yazmalarına izin verilir. (Buna rağmen, bu gibi işlevlerin işlemlerinin sonunda o bayrakları tekrar eski değerlerine getirecekleri beklenir.) -) - -$(LI -$(I Çöp toplayıcıdan bellek ayırmak): İlk bakışta gözden kaçabilse de, bellek ayırmak bile (örneğin $(C malloc) ile) programın evrensel durumu ile ilgili bir işlemdir çünkü sistemde ne kadar boş bellek bulunduğu evrensel bilgisinden yararlanmak zorundadır. Buna rağmen, bellek ayıramıyor olmak çok sayıda işlem için ciddi derecede kısıtlayıcı bir durum olurdu. Ancak, D'de çöp toplayıcıdan $(ASIL GC) $(C new) ile bellek ayrılamadığı zaman zaten $(C Error)'dan türemiş olan bir hata atılır. Dolayısıyla, saf işlevler de $(I tür sisteminin) $(ASIL type system) verdiği garantileri ihlal etmeden $(C new) işlecini çağırabilirler. ($(I Not: Aslında program yığıtının $(ASIL stack) kullanılması bile saflığı bozabilir çünkü yığıtın belirli bir andaki durumuna göre her işlev yığıt taşmasına $(ASIL stack overflow) neden olabilir.)) -) - -$(P -$(I Not: D'de üstesinden gelinemeyen hata türleri $(C Error) sınıfından türerler. Bu tür hatalar $(C @safe)$(DIPNOT 2) olmayan kodlar tarafından yakalanabilseler de $(C Error) hatalarının yakalanmalarından sonra tür sistemi artık programın işleyişi ve mutlak değişmezleri konusunda hiçbir garanti veremez. -) -) - -) - -$(H5 Referans saydamlığı $(ASIL referential transparency)) - -$(P -Fonksiyonel programlama dili dünyasında çok yaygın olarak geçen parametrelerin değişmezliği $(ASIL immutability) kavramının yukarıdaki tanımda geçmiyor olması size şaşırtıcı gelmiş olabilir. D'de saf işlevler parametrelerinde değişiklik yapabilirler. Örneğin, aşağıdaki kod D'de yasaldır: -) - ---- -int okuVeArttır(ref int x) pure { - return x++; -} ---- - -$(P -Programlama dilleri açısından bakıldığında saflık kavramı normalde referans saydamlığını da içerir. Bunun anlamı, saf bir işlev çağrısı yerine o çağrının dönüş değerinin yerleştirilmesi sonucunda programın davranışında hiçbir değişiklik olmayacağıdır. Bu, D'nin varsayılan davranışı değildir. Örneğin, aşağıdaki kod -) - ---- -int değer = 1; -auto sonuç = okuVeArttır(değer) * okuVeArttır(değer); -// assert(değer == 3 && sonuç == 2); ---- - -$(P -$(C okuVeArttır)'ın tek kere çağrılması ile aynı sonucu doğurmaz: -) - ---- -int değer = 1; -auto geçici = okuVeArttır(değer); -auto sonuç = geçici * geçici; -// assert(değer == 2 && sonuç == 1); ---- - -$(P -Bir sonraki başlıkta da göreceğimiz gibi, bu aslında emirli dillerde çok aranan bir özelliktir. Buna rağmen, saflık kavramının klasik tanımının verdiği garantiler ve bunun getirdiği yararlar da göz ardı edilmemelidir. Bu konuda D'nin tür sisteminin başka bir özelliğinden yararlanılır: Veriye erişimin geçişli olarak $(C const) olarak işaretlenebilmesi veya verinin en başından $(C immutable) olarak tanımlanabilmesi$(DIPNOT 3). Bu konuyu biraz daha yakından incelemek için aşağıdaki üç işlev bildirimine bakalım: -) - ---- -int a(int[] val) pure; -int b(const int[] val) pure; -int c(immutable int[] val) pure; ---- - -$(P -Parametresi değişebilen türden olan $(C a)'nın durumu $(C okuVeArttır)'ınkinin aynısıdır ($(C int[]) türü bir gösterge ve bir uzunluktan oluşan ve belleğin bir bölgesine erişim sağlamaya yarayan bir dinamik dizidir). $(C b) ve $(C c) ise çok daha iyi durumdadırlar: Saf olduklarından bu işlevlerin programın evrensel durumunu okuyamadıklarını ve evrensel durumunda değişiklik yapamadıklarını biliyoruz. Dolayısıyla, $(C b) ve $(C c) işlevlerinin kelimenin yaygın anlamı ile $(I yan etkileri olmadığını) ve bu yüzden bu işlevlere yapılan çağrıların referans saydamlığına sahip olduklarını söyleyebiliriz. -) - -$(P -Peki, $(C b) ile $(C c) arasındaki bir farklılıktan söz edilebilir mi? Saflık kavramı açısından hayır, çünkü $(C const) ve $(C immutable) işlevin parametre üzerindeki hakları konusunda aynı anlama gelir. ($(C immutable) ayrıca verinin kesinlikle değişmeyeceği garantisini de verir ama işlev saf olduğundan bu parametreye erişim sağlayan referanslar zaten işlevden dışarıya sızdırılamazlar (dönüş değeri dışında).) -) - -$(P -Yine de, işlevi çağıranı ilgilendiren ince ama önemli bir farktan söz edilebilir: Bu fark, parametre değerinin gerçekte yalnızca $(C const) mı yoksa $(C immutable) mı olduğuna bağlıdır. Bunun nedeni, $(C const)'ın hem değişebilen hem $(C immutable) değerlere erişim sağlayabilmesine karşın $(C immutable)'ın yalnızca $(C immutable) olan veya $(C immutable)'a dönüşebilen değerlere erişim sağlayabilmesidir. Dolayısıyla, aşağıda yazılanlar $(C immutable) bir dizi ile çağrılan durumda hem $(C b) için hem de $(C c) için aynen geçerlidir. -) - -$(P -Örnek olarak, $(I ifade değerlerini hatırlamaya) $(ASIL memoization) veya alt ifadelerin azaltılmalarına dayanan eniyileştirme yöntemlerini düşünelim. $(C pure) bir işleve aynı $(C immutable) değerlerle yapılan birden fazla çağrının teke indirilebilmesi için o değerlerin yalnızca kimliklerine bakılması yeterlidir. (Örneğin, yalnızca o değerlerin adreslerine bakmak gibi bir kaç basit karşılaştırma yaparak.) Öte yandan, $(C const) olan bir parametre değerinin değişebilen referanslar içerdiği durumda o referans iki çağrı arasında başka kodlar tarafından değiştirilebileceğinden, o referansların çalışma zamanında derinlemesine incelenmeleri veya derleyicinin program akışını derleme zamanında anlayabilmesi gerekir. -) - -$(P -Aynı durum koşut işlemleri de etkiler: $(C pure) bir işlevin parametre değerlerinin referans içermediği veya yalnızca $(C immutable) referans içerdiği durumlarda işlemlerin koşut olarak işletilmelerinde bir sakınca yoktur çünkü o işlev programın davranışında belirsizliklere neden olabilecek yan etkiler oluşturamaz; ek olarak, parametre değerlerine erişim konusunda $(I yarış) $(ASIL data race) halinde de olunamaz. Ne var ki, $(C const) parametreleri inceleyerek aynı tür sonuçlara varılamayabilir çünkü aynı değerlere değişebilen çeşitten erişimi olan kodlar onları değiştirebilirler. -) - -$(H5 Dönüş türünün referans içermesi) - -$(P -Parametrelerinin değişebilme açısından farklılıklar göstermelerine karşın, yukarıdaki örneklerdeki $(C a), $(C b), ve $(C c) işlevlerinin üçünün de dönüş türü bu gibi örneklerde sık kullanılan $(C int) idi. Peki, saf bir işlevin referans içeren bir tür döndürdüğü durumlarda neler önemli olabilir? -) - -$(P -İlk önemli konu, referans saydamlığı göze alınırken eşitlik karşılaştırmalarında yararlanılan adreslerdir. Genelde fonksiyonel dillerde bir verinin belleğin hangi noktasında bulunduğunun önemi ya hiç yoktur ya da çok azdır. Bir sistem dili olan D bu kavramı da kullanıma sunar. Örneğin, bir dizi oluşturan ve o diziyi ilk $(C adet) adet asal sayı ile dolduran $(C ulong[] asallar(uint adet) pure) işlevine bakalım. $(C asallar) aynı $(C adet) değeri ile her çağrıldığında aynı sonucu üretiyor olsa da, o sonuçları içeren diziler her seferinde farklı adreslerde bulunacaklardır. Dolayısıyla, dönüş değerlerinde referans içeren işlevlerin referans saydamlığı göze alınırken bit düzeyindeki eşitlik kavramı ($(C is)) değil, mantıksal anlamdaki eşitlik kavramı ($(C ==)) önemlidir. -) - -$(P -Referans saydamlığındaki ikinci önemli konu ise dönüş türünün değişebilen referanslar içerip içermediğidir. $(C asallar) işlevini kullanan aşağıdaki koda bakalım: -) - ---- -auto p = asallar(42); -auto q = asallar(42); -p[] *= 2; ---- - -$(P -$(C asallar)'a yapılan yukarıdaki ikinci çağrı yerine $(C q = p) işleminin gelemeyeceği açıktır çünkü o durumda $(C q) da $(C p) ile aynı bellek bölgesine erişim sağlayacağından çarpma işleminden sonra o da sonuçların iki katlarına sahip olmaya başlayacaktır. Genel açıdan bakıldığında, dönüş türünde değişebilen referanslar içeren saf işlev çağrılarının referans saydamlığı taşıdığı söylenemez; buna rağmen, bazı çağrılar çağıran taraftaki kullanımlara bağlı olarak yine de eniyileştirilebilirler. -) - -$(H5 $(I Yarı saflık) ve getirdiği garantiler) - -$(P -D'nin $(C pure) anahtar sözcüğünün ilk tasarımı çok daha sıkı kurallara bağlıydı. Dil, o zamanlarda bu yazının başında verilen tanıma uyan tek saflık kavramı içeriyor idiyse de, sonuçta kabul edilen ve daha gevşek kurallar getiren bugünkü saflık kavramının tartışmaları sırasında iki terim ortaya çıkmıştı: Yukarıdaki $(C okuVeArttır) ve $(C a) işlevlerinde de görüldüğü gibi, değişebilen parametrelere sahip işlevleri tanımlayan $(I yarı saf) $(ASIL weakly pure), ve $(C b) ve $(C c) işlevlerinde görüldüğü gibi, yan etkileri bulunmayan işlevleri tanımlayan $(I tam saf) $(ASIL strongly pure). Ancak, bu terimler konusunda tam bir anlaşma bulunmamakta ve bunların forumlardaki kullanımları sürekli olarak karışıklıklara neden olmaktadır; bu terimleri saflık kavramı tasarımında ilk ortaya atan Don Clugston bile artık kullanılmamalarını istemektedir. -) - -$(P -Buna rağmen, belki de parametre ve dönüş türlerine bağlı olarak verilen garantilerin farklılıkları nedeniyle bu terimler hâlâ kullanılmaktadır. Çok basit olmalarına rağmen belki de kurallarına yabancı kalındığından D'deki saflık kavramı tam olarak anlaşılamamaktadır. Peki, saf işlevlerin parametrelerini değiştirmelerine neden izin verilmektedir? -) - -$(P -D'deki saflık kavramının arkasındaki asıl güç, kuralları böyle yumuşatmanın aslında daha fazla sayıda işlevin $(I tam) saf olabilmelerini sağlamasıdır. Bunu göstermek için $(I id Software)'den tanınan John Carmack'ın $(I Functional Programming in C++) başlıklı $(LINK2 http://altdevblogaday.com/, #AltDevBlogADay) yazısından bir bölüm aktarayım. Yazı, fonksiyonel programlama ilkelerinin C++'a uygulanmasının yararlarını gösterir: -) - -$(QUOTE -Programcılıkta saf işlevler verinin daha fazla kopyalanmasına neden olur; program hızı açısından bakıldığında bunun yanlış bir gerçekleştirme yöntemi olduğu açıktır. Bir uç örnek olarak, $(C ÜçgenÇiz) gibi bir işlev parametre olarak bir çerçeve ara belleği $(ASIL framebuffer) alır ve sonuç olarak içine üçgen çizilmiş olan yepyeni bir çerçeve ara belleği döndürür. Bunu yapmayın. — $(LINK http://www.altdevblogaday.com/2012/04/26/functional-programming-in-c/) -) - -$(P -Yukarıdaki görüş doğrudur; çerçeve ara belleğinin her üçgen çizimi için tekrar kopyalanmasının iyi bir fikir olmadığı açıktır. Buna rağmen, üçgen çizen saf bir işlev D'de hızdan ödün vermeden de gerçekleştirilebilir! Böyle bir işlevin bildirimi aşağıdaki gibi olabilir:$(DIPNOT 4) -) - ---- -alias Renk = ubyte[4]; -struct Köşe { float[3] konum; /* ... */ } -alias Üçgen = Köşe[3]; -void üçgenÇiz(Renk[] çerçeve, const ref Üçgen üçgen) pure; ---- - -$(P -Bu kadarıyla iyi: Yukarıdaki alıntıda da belirtildiği gibi, $(C üçgenÇiz)'in referans saydamlığı taşıdığını söyleyemiyoruz çünkü çerçeve ara belleğine yazmak zorundadır. Yine de, $(C pure) belirteci sayesinde bu işlevin programın gizli veya evrensel durumunu kullanamadığından eminiz. Dahası, saf olması nedeniyle bu işlev başka saf işlevler tarafından da çağrılabilmektedir. Küçük örneğimize devam edersek, eğer her çerçeve için yeni bir ara bellek ayrılsaydı, üçgenlerden oluşan bir görüntüyü oluşturan bir işlev aşağıdaki gibi olurdu: -) - ---- -Renk[] sahneÇiz( - const Üçgen[] üçgenler, - ushort en = 640, - ushort boy = 480 -) pure { - auto sahne = new Renk[en * boy]; - foreach (ref üçgen; üçgenler) { - üçgenÇiz(sahne, üçgen); - } - return sahne; -} ---- - -$(P -$(C sahneÇiz)'in parametrelerinin değişebilen referanslar içermediklerine dikkat edin – parametrelerinde değişiklik yapan $(C üçgenÇiz)'i çağırdığı halde kendisi parametrelerinde değişiklik yapmamaktadır! -) - -$(P -Bu her ne kadar zorlama bir örnek olsa da, emirli kod performansından ödün vermeyen D'de benzer durumlarla sık karşılaşılır (örneğin, değişebilen toplulukların saf işlevler içinde geçtikleri her durum). Bu konu saflık kavramının yukarıda anlatılan ilk tasarımından edinilen deneyimlere de uymaktadır – saflık kuralları yumuşatıldığında, aynı güçlü garantiler çelişkili bir biçimde daha çok sayıda kod için sağlanabilmektedir. -) - -$(P -Çoğu modern kodlama standardı zaten evrensel değişkenlerden kaçınılmasını önerdiğinden, giriş çıkış işlemleri ile ilgilenmeyen çoğu D işlevi kolaylıkla $(C pure) olarak işaretlenebilir. Bu doğru olduğuna göre neden varsayılan belirteç $(C pure) olmamıştır ve diğer işlevlerin örneğin $(C impure) diye işaretlenmeleri gerekmemiştir? D'nin ikinci sürümü açısından bakıldığında bunun nedeni saflık kavramının son tasarımının dilin gelişmesinin sonlarına rastlaması ve böyle bir değişikliğin halihazırda yazılmış olan kodları etkileme riskinin fazla yüksek olmasıdır. Yine de bu, gelecekteki başka diller ve belki de D'nin bir sonraki ana sürümü açısından dikkate alınacak bir fikirdir. -) - -$(H5 Şablonlar ve saflık) - -$(P -Bu noktaya kadar saflık tasarımını başka olanaklardan bağımsız olarak ele aldık. Bundan sonraki başlıklarda öncelikle işlev şablonları olmak üzere saflığın diğer dil olanaklarıyla etkileşimlerini göreceğiz. -) - -$(P -Bir işlev şablonunun tür parametrelerinin belirli türler için her kullanımı, sıradan bir işlevin eşdeğeridir. Bu yüzden saflık kavramı onlar için de yukarıda anlatıldığı gibidir. Buna rağmen, belirli bir şablonun saf olup olamadığı o şablonun kullanıldığı türlere bağlı olabildiğinden şablonların durumu biraz daha karışıktır. -) - -$(P -Bunun bir örneği olarak bir aralık$(DIPNOT 5) $(ASIL range) alan ve o aralıktaki elemanlardan oluşan bir dizi döndüren bir işleve bakalım (bu işlev $(C std.array) modülünde zaten bulunur ve oradaki gerçekleştirmesi çok daha kullanışlıdır). Böyle bir işlev aşağıdaki gibi yazılabilir: -) - ---- -auto array(R)(R r) if (isInputRange!R) { - ElementType!R[] sonuç; - while (!r.empty) { - sonuç ~= r.front; - r.popFront(); - } - return sonuç; -} ---- - -$(P -$(I Yukarıdaki işlev, bir aralıktaki elemanları bir dizi olarak döndüren $(C std.array.array)'in fazla hızlı olmayan bir gerçekleştirmesidir. Bu işlev $(C pure) olabilir mi?) -) - -$(P -Bu işlevin nasıl işlediğini tahmin etmek güç olmasa gerek – aralığın başındaki eleman aralıktan çıkartılıyor ve dizinin sonuna ekleniyor, ve bu işlem aralıkta eleman kalmayana kadar sürdürülüyor. Soru: Bu işlev $(C pure) yapılabilir mi? $(C R) türü $(C map) veya $(C filter) gibi işlevlerin dönüş türü olduğunda bu kodun saf kodlar tarafından çağrılamaması için bir neden yoktur. Öte yandan, eğer $(C R) örneğin girişten satır okuma gibi bir işlemi de sarmalıyorsa $(C r.empty), $(C r.front), ve $(C r.popFront)'un hepsinin birden $(C pure) olmaları olanaksızdır. Dolayısıyla, $(C array) $(C pure) olarak işaretlense, böyle aralıklarla çalışamaz. Peki, ne yapılabilir? -) - -$(P -Buna bir çözüm olarak $(C pure) belirtecini $(C R)'ye bağlı olarak koymayı sağlayan bir D söz dizimi değişikliği önerilmişti. Bu çözüm önerisi dile getireceği karmaşıklık ve neden olacağı aşırı kod tekrarı nedeniyle benimsenmedi. Sonuçta kabul edilen çözüm oldukça basittir: D'de şablon kodları nasıl olsa derleyici tarafından görülmek zorunda olduklarından şablonların saflığı derleyici tarafından otomatik olarak belirlenebilir (aynısı $(C nothrow) ve başka belirteçler için de geçerlidir). -) - -$(P -Bunun anlamı, yukarıdaki örnekteki $(C array)'in buna izin veren aralık türleri için saf işlevler tarafından çağrılabilmesi, başka türler için ise çağrılamamasıdır. Saflığın şablon parametrelerine bağlı olmadığı durumda işlev şablonları en azından belgeleme açısından yine de açıkça $(C pure) olarak işaretlenebilir. -) - -$(H5 Saf üye işlevler) - -$(P -Doğal olarak, yapı ve sınıf üye işlevleri de saf olabilir. Normal işlevlerin saflık kuralları bir farkla onlar için de geçerlidir: Saflık kuralları üye işlevlerin gizli $(C this) parametresini de kapsar. Bunun anlamı, saf işlevlerin üye değişkenlere de erişebilecekleri ve onları da değiştirebilecekleridir. -) - ---- -class Foo { - int getBar() const pure { - return bar; - } - - void setBar(int bar) pure { - this.bar = bar; - } - - private int bar; -} ---- - -$(P -$(I Saf işlevler üye değişkenlere erişebilirler. (Not: Normalde getter/setter işlevleri yerine D'de nitelik işlevlerinden $(ASIL property) yararlanılır. -)) -) - -$(P -Bir üye işlev $(C const) veya $(C immutable) olarak işaretlendiğinde de o belirteç aslında $(C this) parametresine uygulanır. Dolayısıyla, yukarıda değişebilme konusunda anlatılanlar burada da geçerlidir. -) - -$(P -Saflık, türeme konusunda da beklenebileceği gibidir: Genel olarak, bir alt sınıfın beklentileri üst sınıfınkilerden daha azdır; verdiği garantiler ise daha fazladır (dönüş türlerinin $(I ortakdeğişkeliğinde) $(ASIL covariance) görüldüğü gibi). Dolayısıyla, saf olmayan bir işlevin alt sınıftaki tanımı saf olabilir ama bunun tersi doğru değildir. Bir kolaylık olarak da, saf olan bir üst sınıf işlevinin alt sınıftaki tanımının açıkça $(C pure) olarak işaretlenmesi gerekmez; böyle bir işlev otomatik olarak saftır (C++'taki $(C virtual) gibi). Walter Bright'ın bu konuda bir $(LINK2 http://www.drdobbs.com/blogs/cpp/232601305, blog yazısı) vardır. -) - -$(H5 $(C pure) ve $(C immutable) – bir kere daha) - -$(P -$(C const) ve $(C immutable) belirteçlerinin referans saydamlığına etkilerini yukarıda gördük. $(C pure)'un getirdiği garantiler bazı durumlarda bazı ek çıkarımlar da sağlar. Bunun tür sisteminin bir parçası da olan belirgin bir örneği, $(C pure) işlevlerin dönüş türlerinin bazı durumlarda güvenle $(C immutable) olarak kullanılabilmeleridir. Örnek olarak yine yukarıdaki $(C ulong[] asallar(uint adet) pure) işlevine bakalım. Aşağıdaki kodun nasıl olup da derlenebildiği ilk bakışta açık olmayabilir: -) - ---- -immutable ulong[] a = asallar(5); ---- - -$(P -$(C immutable) bir verinin başka hiçbir değişebilen referansı olamadığını biliyoruz. Buna rağmen, $(C asallar)'ın döndürdüğü ve değişebilen değerlerden oluşan dizi $(C immutable) olarak tanımlanabilmektedir. Peki, yukarıdaki kodun derlenebilmesinin nedeni nedir? Döndürdüğü değerleri değiştirebilecek olan başka referansların bulunamayacağı, bir işlevin $(C pure) olmasının sonucudur: Böyle bir işlev değişebilen referanslar içeren parametrelere sahip değildir ve değişebilen evrensel değişkenlere erişemez. Dolayısıyla, her ne kadar değişebilen değerlerden oluşan bir referans döndürüyor olsa da o işlevi çağıranlar o değerlerin değişemeyeceklerinden emindirler. -) - -$(P -Her ne kadar fazla önemsiz bir ayrıntıymış gibi görünse de bu aslında şaşırtıcı derecede kullanışlı bir olanaktır çünkü hem işlevlerin $(I fonksiyonel programlamada) olduğu gibi değişmez veriler kullanabilmelerini, hem de verinin hız nedeniyle olduğu yerde değiştirildiği $(I geleneksel kodlarda) gereksiz veri kopyalarının önüne geçilmesini sağlamaktadır. -) - -$(H5 Peki, kaçış kapısı nerede?) - -$(P -Saf bir işlevin kullandığı bütün kodların da saf olmaları gerektiği gerçeğinden yola çıkıldığında saflığın bulaşıcı $(ASIL viral) olduğu görülür. D'nin saflık kuralları her ne kadar bu konuda kolaylıklar getiriyor olsalar da bazen saf olmayan kodların saf kodlar tarafından çağrılmaları gerekebilir. -) - -$(P -Bunun bir örneği, halihazırda yazılmış olan $(ASIL legacy) kodlardır. Örneğin, saflığın gereklerini yerine getirdiği halde $(C pure) olarak işaretlenmemiş olan bir C işlevi bulunabilir. Tür sisteminin kendi başına çıkarsayamadığı başka durumlarda da olduğu gibi, bu durumun üstesinden gelmenin çaresi de $(C cast) kullanmaktır. İşlevin adresi alınır, tür dönüşümü ile $(C pure) belirteci eklenir, ve işlev o adres yoluyla çağrılır (tür sistemini yanıltabildiğinden $(C @safe) kodlar içerisinde bu mümkün değildir). Belirli bir kod içinde böyle $(I numaralara) ihtiyacın yeterince fazla olduğu durumlarda $(C safOlduğunuVarsay) $(ASIL $(C assumePure)) gibi bir şablondan yararlanılabilir. -) - -$(P -Saflık bazen kısa süreliğine de olsa ayak bağı olabilir: Hata ayıklama amacıyla ekrana mesaj yazdırmak için veya istatistiksel amaçla evrensel bir sayacı arttırmak için bir işleve saf olmayan kodlar eklenmiş olabilir. Saf olmayan böyle bir kodun zincirleme çağrılmış olan $(C pure) işlevlerin en içtekine eklenmesinin gerekmesi ama bunun tür sistemi tarafından engellenmesi büyük bir sıkıntı kaynağı olurdu. Her ne kadar bazılarınca küçümsenen bir davranış olsa da bu tür kodlar gerçekte oldukça kullanışlıdırlar. -) - -$(P -Saflığın hata ayıklama amacıyla $(I geçici olarak) etkisizleştirilmesi ihtiyacına rağmen başlangıçta D'de bu kullanıma yönelik hiçbir olanak bulunmuyordu. Sonunda özel bir kural olarak $(C debug) blokları içindeki saf olmayan kodların saf kodlar arasında bulunmalarına izin verildi. Bu kullanışlı olanağın etkinleştirilmesi için derleyiciye $(C -debug) komut satırı seçeneğinin verilmesi gerekir. -) - -$(H5 Sonuç) - -$(P -Başlangıçtaki görüşü tekrarlarsak, saflık kavramının önemi, işlevlerin gizli durum kullanmadıklarının tür sistemi tarafından garanti edilmesidir. D'nin $(C pure) anahtar sözcüğünün başka dillerdekilerden daha az kısıtlama getirdiğini gördük. Buna rağmen, $(C const) ve $(C immutable) belirteçleri yardımıyla aynı garantilerin sağlanabildiklerini ve $(C pure)'un D'nin başka olanakları ile etkileşimlerinin doğal olabildiğini ve belki de en önemlisi, emirli programlamaya izin verilebildiğini gördük. -) - -$(P -Peki, bu konuda daha fazla bilgi için başka kaynaklar nelerdir? Aslında $(C pure) anahtar sözcüğü ile ilgili kurallar oldukça kısadır. Bu kurallar için dil referansının $(LINK2 http://dlang.org/, dlang.org'daki) $(LINK2 http://dlang.org/function.html, Functions bölümüne) bakabilirsiniz. Don Clugston'ın başlatmış olduğu ve $(LINK2 http://forum.dlang.org/thread/i7bp8o$(DOLAR)6po$(DOLAR)1@digitalmars.com, son değişikleri de getirmiş olan tartışma) da $(C pure)'un gelişimini görmek açısından oldukça ilginçtir. Burada anlatılan kavramların tasarımları ve gerçekleştirmeleri ile ilgili soruları da $(LINK2 http://forum.dlang.org/group/digitalmars.D, D dili forumlarında) sormak isteyebilirsiniz. -) - -$(P -Bu yazıyı beğendiyseniz $(LINK2 http://twitter.com/?status=@klickverbot:, bana fikrinizi bildirmenizi), yazıyı $(LINK2 http://twitter.com/?status=Just%20read%20%C2%BBPurity%20in%20D%C2%AB%20by%20@klickverbot:%20http://klickverbot.at/blog/2012/05/purity-in-d, Twitter'da paylaşmanızı), ve yazının $(LINK2 http://news.ycombinator.com/item?id=4032248, Hacker News'deki) ve $(LINK2 http://www.reddit.com/r/programming/comments/u84fc/purity_in_d/, Reddit'teki) tartışmalarına katılmanızı isterim. $(LINK2 http://klickverbot.at/blog/tags/D/, D) hakkında başka yazılarım da var. -) - - - -$(H5 Notlar) - -$(OL - - - -$(LI -Bunun etkileri ilk akla geldiğinden çok daha ciddi ve şaşırtıcı olabilir: Bir çok Windows yazıcı sürücüsünün yazdırma işlemi sırasında değiştirdikleri FPU bayraklarını tekrar eski değerlerine çevirmedikleri olurdu. Bir belge yazdırıldıktan sonra bir çok programın çökmesi bunun yüzündendi – bu, yalnızca müşteri bilgisayarında oluşan çökme hatalarının güzel bir örneğidir. -) - - - -$(LI -D kodları dilin bazen SafeD olarak adlandırılan bir alt kümesine kısıtlanabilir. Bu olanak üç anahtar sözcük sayesinde her işlev için teker teker ayarlanabilir: $(C @safe) olarak işaretlenen kod bellek hataları içeremez; dolayısıyla, bu gibi işlevlerde örneğin gösterge aritmetiği ve C'deki gibi bellek yönetimi kullanılamaz. $(C @system) olarak işaretlenmiş olan kod bunun tersidir – bu gibi işlevlerde $(I inline assembly) de dahil olmak üzere dilin bütün olanakları kullanılabilir. Son olarak, $(C @trusted) belirteci bu iki kavram arasında bir köprü kurar; programcının araştırarak güvenli olduğuna karar verdiği halde aslında güvenli olarak işaretlenmemiş olan kodlara arayüz oluşturur. $(C void*) türünden bir C arayüz işlevini sarmalayan bir D işlevi bunun bir örneği olarak görülebilir. -) - - - -$(LI -C++'ın tersine, D'de $(C const) ve $(C immutable) geçişlidir. Bir $(C const) referans yoluyla erişilen her değer otomatik olarak $(C const)'tır. Örneğin, aşağıdaki kodu ele alalım: -) - ---- -struct Foo { - int bar; - int* baz; -}; - -void fun(const Foo* foo); ---- - -$(P -C++'ta $(C fun) $(C foo)'nun kendisini değiştiremez; örneğin, $(C foo->bar)'a yeni bir değer atayamaz. Buna rağmen, $(C foo->baz)'ın gösterdiği değeri değiştirmesi yasaldır – buna $(I sığ const) $(ASIL shallow const) da denir. Öte yandan, D'de $(C const) $(I derindir); $(C fun) içinde $(C foo.baz), $(C const int) gösteren $(C const) bir göstergedir ve bu yüzden $(C *foo.baz)'da da değişiklik yapılamaz. Aynı kural $(C immutable) için de geçerlidir; ek olarak, $(C immutable) olan veriyi değiştirebilecek başka referans bulunmadığı da garanti edilmiştir. Yani, veriyi $(C fun) değiştiremediği gibi, başka hiçbir kod da değiştiremez ($(C immutable) değerlerin ROM gibi bir bellek bölgesinde duruyor olduklarını düşünebiliriz). $(C immutable) otomatik olarak $(C const)'tır da. -) - - - -$(LI -Bu örnek yalnızca açıklayıcı olduğu için seçilmiştir; yoksa, bu işlev herhalde ancak çok basit bir görüntüleyicide kullanılabilir. Saflığın bu örneğe bir yarar katıp katmadığı bir yana, bu işlev gerçek bir grafik arayüzü üzerinde gerçekleştirilmiş olsa, GPU'nun durumunun saflığının nasıl sağlanacağı konusu da düşünülmelidir. -) - - - -$(LI -C++'ın erişicilerinin $(ASIL iterator) gösterge genellemeleri olmalarına benzer biçimde, D aralıkları da $(I topluluk) kavramının genellemeleridir. En basit aralık üç temel işlev sunar: $(C empty), $(C front), ve $(C popFront). Bu arayüzün kullanıcıları verinin perde arkasında nasıl saklandığından habersizdirler – örneğin, veri bir bellek bölgesinden, ağ üzerinden, standart girişten, vs. geliyor olabilir. Aralıklar algoritmaların kullanabilecekleri basit ama güçlü bir soyutlama sunarlar. -) - -) - - - -Macros: - SUBTITLE="D'de Saflık Kavramı", David Nadlinger - - DESCRIPTION=David Nadlinger'ın D'deki saflık kavramını anlatan makalesinin Türkçe çevirisi "D'de Saflık Kavramı". - - KEYWORDS=d programlama dili makale d david nadlinger purity pure saflık saf - -DIPNOT=$(LINK2 #dipnot$1, $(SUP $1)) diff --git a/ddili/src/makale/shared.d b/ddili/src/makale/shared.d deleted file mode 100644 index 87f76ff..0000000 --- a/ddili/src/makale/shared.d +++ /dev/null @@ -1,229 +0,0 @@ -Ddoc - -$(H4 $(C shared)'e Geçiş) - -$(P - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) 13 Aralık 2009 -$(BR) - $(B İngilizcesi:) $(LINK2 http://www.dlang.org/migrate-to-shared.html, Migrating to Shared) -) - -$(P -$(C dmd)'nin 2.030 sürümünden başlayarak, C ve C++'da olduğunun aksine, statik ve evrensel (global) değişkenler artık evrensel alanda değil, her iş parçacığına özel alanda (thread local storage - TLS) bulunuyorlar. Çoğu D programının bu değişimden hiç etkilenmeden derlenmesi ve çalışması beklense de, bilinmesi gereken bazı konular vardır: -) - -$(OL -$(LI TLS değişkenlerinin hızları) -$(LI TLS değişkenlerinin hangileri olduklarını bilebilmek) -$(LI $(C immutable) olarak işaretlemek) -$(LI $(C shared) olarak işaretlemek) -$(LI $(C __gshared) ile klasik alana yerleştirmek) -$(LI Derleyici hataları) -$(LI Bağlayıcı hataları) -) - -$(H5 TLS değişkenlerinin hızları) - -$(P -Evet, iş parçacığına özel değişkenleri okumak ve yazmak klasik evrensel değişkenlerden daha yavaştır. Tek bir mikro işlemci işlemi yerine üç işlem gerekeceği düşünülebilir; ancak, en azından Linux üzerinde, derleyici seçeneği olarak PIC (position independent code) seçildiğinde, TLS değişkenlerinin klasik evrensel değişkenlerden az da olsa daha hızlı olmaları beklenir. Bu yüzden hız konusunda bir sorun olacağını düşünmek gerçekçi olmaz. Yine de hız sorunu olacağını bir an için kabul ederek neler yapılabileceğine bakalım: -) - -$(OL -$(LI Evrensel değişkenleri azaltın. Evrensel değişken kullanımın azaltılması; kodun bağımsızlığını ve geliştirilebilirliğini arttıracağı için, bu zaten hedeflenen bir amaçtır. -) - -$(LI Evrensel değişkenleri $(C immutable) yapın. $(C immutable) verilerin iş parçacıkları tarafından ortak kullanılmaları ve eş zamanlı çalışma sorunları olmadığı için derleyici onları TLS'ye yerleştirmez. -) - -$(LI Evrensel değişkenin yerel bir kopyasını kullanın. Asıl değişken yerine yerel kopyasının kullanılması erişimin daha hızlı olmasını sağlar. -) - -$(LI Değişkeni $(C __gshared) ile normal evrensel değişken haline getirin. -) - -) - -$(H5 TLS değişkenlerinin hangileri olduklarını bilebilmek) - -$(P -Kendileriyle ilgili bu konuda herhangi bir işlem yapılabilmesi için evrensel değişkenlerin öncelikle tanınmaları gerekir. -) - -$(P -Programların ne kadar karmaşık olabilecekleri göz önüne alınırsa, evrensel değişkenleri bulmak her zaman kolay olmayabilir. Eskiden otomatik olarak evrensel olduklarından, onları $(C grep) programıyla da ayırt edemeyiz. Bulduğumuz kadarının da evrensel değişkenlerin tümü olduğundan emin olamayız. -) - -$(P -Bu konuda yardımcı olması için yeni bir $(C dmd) seçeneği eklenmiştir: $(C -vtls). Program o seçeneği kullanarak derlendiğinde, TLS alanına yerleştirilen bütün değişkenler listelenir: -) - ---- -int x; - -void main() -{ - static int y; -} ---- - -$(SHELL -dmd test $(HILITE -vtls) -test.d(2): x is thread local -test.d(6): y is thread local -) - -$(H5 $(C immutable) olarak işaretlemek) - -$(P -$(C immutable) veriler bir kere ilk değerlerini aldıktan sonra değiştirilemezler. Bu, çoklu iş parçacıkları kullanıldığında eş zamanlı çalışmayla ilgili erişim sorunlarının ortadan kalkması anlamına gelir. Bu yüzden, $(C immutable) verilerin TLS'e yerleştirilmelerine de gerek yoktur. Derleyici bu tür değişkenleri TLS'e değil, güvenle klasik evrensel alana yerleştirir. -) - -$(P -Programlarda kullanılan evrensel değişkenlerin büyük bir bölümü bu türdendir. $(C immutable) olarak işaretlenmeleri yeter: -) - ---- -int[3] tablo = [6, 123, 0x87]; ---- - -$(P -yerine -) - ---- -immutable int[3] tablo = [6, 123, 0x87]; ---- - -$(P -$(C immutable)'ın getirdiği $(I değişmezlik) kavramının başka bir yararı, derleyiciye daha fazla eniyiliştirme [optimization] olanağı sunmasıdır. -) - -$(H5 $(C shared) olarak işaretlemek) - -$(P -Gerçekten birden fazla iş parçacığı tarafından erişilen veriler $(C shared) olarak işaretlenmelidir: -) - ---- -shared int birDurum; ---- - -$(P -Bu, $(C birDurum)'un klasik evrensel alana yerleştirilmesi yanında, türünün de $(C shared) olmasına neden olur: -) - ---- -int* p = &birDurum; // hata: birDurum shared'dir -shared(int)* q = &birDurum; // çalışır ---- - -$(P -$(C const) ve $(C immutable)'da olduğu gibi, $(C shared) de $(I geçişlidir). Böylece paylaşılan verilere erişimlerin doğruluğu da derleme zamanında denetlenebilir. -) - -$(H5 $(C __gshared) ile klasik alana yerleştirmek) - -$(P -Yukarıdaki çözümlerin kullanılamadığı zamanlar olabilir: -) - -$(OL - -$(LI klasik evrensel değişkenler kullanan C kodu kullanıldığında) - -$(LI programı çabucak derlenebilir hale getirerek, daha uygun çözümün daha sonraya bırakıldığında) - -$(LI program tek iş parçacığı kullandığında [single-threaded]; bu durumda zaten eş zamanlı erişim sorunları yoktur) - -$(LI en ufak hız kaybı bile önemli olduğunda) - -$(LI eş zamanlı erişim konusunu kendiniz halletmek istediğinizde) - -) - -$(P -D bir sistem dili olduğu için, size bu serbestiyi $(C __gshared) anahtar sözcüğü ile sağlar: -) - ---- -__gshared int x; - -void main() -{ - __gshared int y; -} ---- - -$(P -$(C __gshared), değişkeni klasik evrensel alana yerleştirir. -) - -$(P -Doğal olarak, $(I safe) modda $(C __gshared) kullanılamaz. -) - -$(P -Kodun içindeki $(C __gshared) değişkenler daha uygun çözümlerin uygulanması için kolayca bulunabilirler. -) - -$(H5 Derleyici hataları) - -$(P -TLS ile ilgili en çok karşılaşılacak hata herhalde şudur: -) - ---- -int x; -int* p = &x; ---- - -$(SHELL -test.d(2): Error: non-constant expression & x -) - -$(P (Türkçesi: "sabit olmayan ifade")) - -$(P -Klasik evrensel değişkenlerle normalde derlenebilen o ifade, TLS değişkenleriyle derlenemez. Bunun nedeni, TLS değişkenlerinin adreslerinin ne bağlayıcı [linker] ne de yükleyici [loader] tarafından bilinebilmesidir. Bu değişkenlerin adresleri çalışma zamanında hesaplanır. -) - -$(P -Çözüm, bu tür değişkenleri statik kurucu işlevlerde ilklemektir. -) - -$(H5 Bağlayıcı hataları) - -$(P -Evrensel değişkenlerle ilgili bağlayıcı hataları da alabilirsiniz. Çoğu durumda bunların nedeni; değişkenin bir modül tarafından TLS'e yerleştirmesine karşın, başka bir modül tarafından klasik evrensel alana yerleştirilmesidir. Böyle bir durum; program, dmd'nin önceki bir sürümü ile derlenmiş olan kütüphanelerle bağlanırken oluşabilir. Kullanılan $(C libphobos2.a) kütüphanesinin yeni sürümle gelen yeni kütüphane olduğundan emin olun. -) - -$(P -Bu durumla C ile etkileşirken de karşılaşılabilir. C, TLS değişkenlerini de destekliyor olsa da; C'de evrensel değişkenler için varsayılan yerleşim, klasik evrensel yerleşimdir. Bu gibi durumlarda D iliştirici [binding] dosyalarındaki bildirimlerin TLS ve klasik yerleşim konularında C'dekilere uyduklarından emin olmak gerekir: -) - -$(FARK_C -int x; -extern int y; -__thread int z; -) - -$(FARK_D ---- -extern (C) -{ - extern shared int x; - shared int y; - extern int z; -} ---- -) - - -Macros: - SUBTITLE=shared'e Geçiş - - DESCRIPTION=D'de statik ve evrensel değişkenlin normalde iş parçacığına özel olmaları - - KEYWORDS=d programlama dili makale d tanıtım evrensel global erişim TLS thread is parçacığı diff --git a/ddili/src/makale/tembel_hesap.d b/ddili/src/makale/tembel_hesap.d deleted file mode 100644 index af862e9..0000000 --- a/ddili/src/makale/tembel_hesap.d +++ /dev/null @@ -1,295 +0,0 @@ -Ddoc - -$(H4 Fonksiyon Argümanlarında Tembel Değerlendirmeler) - -$(P - $(B Yazar:) $(LINK2 http://www.dlang.org, Walter Bright) -$(BR) - $(B Çeviren:) $(LINK2 http://acehreli.org, Ali Çehreli) -$(BR) - $(B Tarih:) 10 Temmuz 2009 -$(BR) - $(B İngilizcesi:) $(LINK2 http://www.dlang.org/lazy-evaluation.html, Lazy Evaluation of Function Arguments) -) - -$(P -Tembel değerlendirme [lazy evaluation], bir ifadenin işletilmesinin ancak gerçekten gerek duyulana kadar geciktirilmesidir. Tembel değerlendirme geleneksel olarak $(CODE &&), $(CODE ||), ve $(CODE ?:) işleçlerinde karşımıza çıkar. -) - ---- -void deneme(int* p) -{ - if (p && p[0]) - ... -} ---- - -$(P -İkinci ifade olan $(CODE p[0]), ancak $(CODE p)'nin $(CODE NULL)'dan farklı olduğu durumda işletilecektir. Eğer ikinci ifade tembel olarak işletilmemiş olsa, $(CODE p)'nin $(CODE NULL) olduğu durumda bir çalışma zamanı hatası oluşurdu. -) - -$(P -Ne kadar değerli olsalar da tembel işleçlerin ciddi kısıtlamaları vardır. Örneğin çalışması global bir değişkenle yönetilen bir loglama fonksiyonunu ele alalım. Loglamayı bu global değişkenin değerine göre açıp kapatıyor olalım: -) - ---- -void log(char[] mesaj) -{ - if (loglama_açık) - fwritefln(log_dosyası, mesaj); -} ---- - -$(P -Yazdırılacak olan mesaj çoğu durumda çalışma zamanında oluşturulur: -) - ---- -void foo(int i) -{ - log("foo(), i'nin " ~ toString(i) ~ " değeriyle çağrıldı"); -} ---- - -$(P -Bu kod istediğimiz gibi çalışıyor olsa da; burada mesaj loglamanın kapalı olduğu zamanlarda bile oluşturulmaktadır. Çok miktarda loglama yapan programlar bu yüzden gereksizce zaman kaybederler. -) - -$(P -Bir çözüm, tembel değerlendirmenin programcı tarafından açıkça yapılmasıdır: -) - ---- -void foo(int i) -{ - if (loglama_açık) log("foo(), i'nin " ~ toString(i) - ~ " değeriyle çağrıldı"); -} ---- - -$(P -Ancak bu, loglama yönetimiyle ilgili bir değişkeni kullanıcıya gösterdiği için sarma ilkesine aykırı bir kullanımdır. Bunun önüne geçmenin C'deki bir yolu makro tanımlamaktır: -) - -$(C_CODE -#define LOG(mesaj) (loglama_acik && log(mesaj)) -) - -$(P -Ama bu çözüm de makroların bilinen eksiklikleri nedeniyle yeni sorunlar doğurur: -) - -$(UL -$(LI $(CODE loglama_acik) değişkeni kullanıcının isim alanına dahil olmuştur) -$(LI Makrolar hata ayıklayıcılar tarafından görülemezler) -$(LI Makrolar global olarak çalışırlar; bir kapsam içine alınamazlar) -$(LI Makrolar sınıf üyeleri olamazlar) -$(LI Adresleri alınamadığı için başka fonksiyonlara fonksiyon olarak geçirilemezler) -) - -$(P -Bunun yerine fonksiyon parametrelerinin tembel olarak işletilmeleri daha sağlam bir çözümdür. D'de bunu yapmanın bir yolu, $(CODE delegate()) parametreler kullanmaktır: -) - ---- -void log(char[] delegate() dg) -{ - if (loglama_açık) - fwritefln(log_dosyası, dg()); -} - -void foo(int i) -{ - log( { return "foo(), i'nin " ~ toString(i) - ~ " değeriyle çağrıldı"; } ); -} ---- - -$(P -Bu kodda mesaj yalnızca loglamanın açık olduğu durumda oluşturulacaktır ve bu sefer sarma ilkesi de korunmaktadır. Bu seferki sorun ise, ifadelerin $(CODE { return $(I ifade); }) olarak yazılmalarını gerektirmesidir. -) - -$(P -D bu noktada Andrei Alexandrescu tarafından önerilmiş olan küçük ama önemli bir adım atar: D'de her ifade, dönüş türü $(CODE void) veya ifadenin kendi türü olan bir $(CODE delegate)'e otomatik olarak dönüşür. $(CODE delegate) bildiriminin yerini de Tomasz Stachowiak'ın önerisi olan $(CODE lazy) türler alır. Fonksiyonların yeni yazımı şöyledir: -) - ---- -void log(lazy char[] dg) -{ - if (loglama_açık) - fwritefln(log_dosyası, dg()); -} - -void foo(int i) -{ - log("foo(), i'nin " ~ toString(i) ~ " değeriyle çağrıldı"); -} ---- - -$(P -Tekrar en baştaki kullanıma dönmüş olduk, ama burada ifade ancak loglama açık olduğunda işletilmektedir. -) - -$(P -Kodda karşılaşılan yazım veya tasarım tekrarlarını soyutlamak ve bir şekilde sarmalamak, hem kodun karmaşıklığını hem de hata risklerini azaltır. Bunun en güzel örneklerinden birisi fonksiyon kavramının ta kendisidir. Tembel değerlendirme kavramı ise bir sürü başka yöntemin soyutlanabilmesini sağlar. -) - -$(P -Basit bir örnek olarak $(CODE tekrar) kere tekrarlanması gereken bir ifadeyi ele alalım. Bunun en klasik yazımı şöyledir: -) - ---- -for (int i = 0; i < tekrar; i++) - ifade; ---- - -$(P -O yöntemi tembel değerlendirme kullanarak şu şekilde sarmalayabiliriz: -) - ---- -void tekrarla(int tekrar, lazy void ifade) -{ - for (int i = 0; i < tekrar; i++) - ifade(); -} ---- - -$(P -ve şöyle kullanabiliriz: -) - ---- -void foo() -{ - int x = 0; - tekrarla(10, writef(x++)); -} ---- - -$(P -Çıktısı bekleneceği gibi şu şekildedir: -) - -$(SHELL -0123456789 -) - -$(P -Daha karmaşık kontrol yapıları da kurulabilir. Örneğin şöyle $(CODE switch) benzeri bir yapı oluşturulabilir: -) - ---- -bool eğer(bool b, lazy void dg) -{ - if (b) - dg(); - return b; -} - -/* Buradaki belirsiz argümanlar bu özel durumda bir - delegate dizisine dönüşürler. - */ -void seçenekler(bool delegate()[] kontroller...) -{ - foreach (c; kontroller) - { if (c()) - break; - } -} ---- - -$(P -ve şöyle kullanılabilir: -) - ---- -void foo() -{ - int v = 2; - seçenekler - ( - eğer(v == 1, writefln("değeri 1")), - eğer(v == 2, writefln("değeri 2")), - eğer(v == 3, writefln("değeri 3")), - eğer(true, writefln("varsayılan değer")) - ); -} ---- - -$(P -Bunun çıktısı şöyle olur: -) - -$(SHELL -değeri 2 -) - -$(P -Burada Lisp programlama dilinin makrolarıyla olan benzerlikleri farketmiş olabilirsiniz. -) - -$(P -Son bir örnek olarak şu çok karşılaşılan kod örüntüsüne [pattern] bakalım: -) - ---- -Abc p; -p = foo(); -if (!p) - throw new Hata("foo() başarısız"); -p.bar(); // p'yi şimdi kullanabiliriz ---- - -$(P -$(CODE throw) ifade değil, bir deyim olduğu için; bunu kullanan ifadelerin birden fazla satırda yazılmaları ve değişken tanımlanması gerekmektedir. (Bu konuda daha ayrıntılı bilgi için Andrei Alexandrescu ve Petru Marginean'ın $(LINK2 http://erdani.org/publications/cuj-06-2003.html, Enforcements) makalesini okuyabilirsiniz.) Oysa bütün bu kod tembel değerlendirme kullanılarak bir fonksiyon içine alınabilir: -) - ---- -Abc Güvenli(Abc p, lazy char[] mesaj) -{ - if (!p) - throw new Hata(mesaj()); - return p; -} ---- - -$(P -ve biraz önceki kod şöyle basit bir hale gelir: -) - ---- -Güvenli(foo(), "foo() başarısız").bar(); ---- - -$(P -ve 5 satırlık kod tek satıra indirgenmiş olur. Hattâ bir şablona dönüştürülürse $(CODE Güvenli) çok daha kullanışlı bir hale getirilebilir: -) - ---- -T Güvenli(T)(T p, lazy char[] mesaj) -{ - if (!p) - throw new Hata(mesaj()); - return p; -} ---- - -$(H6 Sonuç) - -$(P -Fonksiyon argümanlarının tembel olarak işletilebilmeleri fonksiyonların ifade yeteneklerini olağanüstü bir biçimde arttırmaktadır. Çok kullanılan kod örüntülerinin daha önceden hantal veya olanaksız olan sarmalanmaları da bu sayede olanaklı hale gelmektedir. -) - -$(H6 Teşekkür) - -$(P -Andrei Alexandrescu, Bartosz Milewski, ve David Held bana bu konuda hem ilham kaynağı oldular hem de büyük yardımda bulundular. Kendilerine içten teşekkür ederim. D camiasının da $(LINK2 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=41633, Tomasz Stachowiak'ın açtığı bir konuda) da görüldüğü gibi yapıcı eleştirileri olmuştur. -) - -Macros: - SUBTITLE="Fonksiyon Argümanlarında Tembel Değerlendirme", Walter Bright - - DESCRIPTION=Walter Bright'ın 'Lazy Evaluation of Function Arguments' makalesinin Türkçe çevirisi 'Fonksiyon Argümanlarında Tembel Değerlendirmeler' - - KEYWORDS=d programlama dili makale d tanıtım lazy evaluation tembel değerlendirme walter bright diff --git a/ddili/src/make_ders_macros.sh b/ddili/src/make_ders_macros.sh deleted file mode 100755 index 7d16b3b..0000000 --- a/ddili/src/make_ders_macros.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -butun_hedefler=$1 -butun_ders_links=$2 - -# Once toplam ders adedini bulalim -total_ders=0 -for hedef in $butun_ders_links; do - let ++total_ders -done; - -for hedef in $butun_hedefler; do - ders_html=`echo /$hedef | sed 's/.d.macros.ddoc/.html/'` - count=1 - - echo > $hedef - - for link in $butun_ders_links; do - if [ $link = $ders_html ]; then - let prev=$count-1 - let next=$count+1 - - echo -n 'DERS_NAV_GERI=$(LINK2 $(ROOT_DIR)' >> $hedef - echo -n $butun_ders_links | awk "{print \$$prev}" >> $hedef - echo ', [ ↢ $(GERI_METIN) ])' >> $hedef - echo >> $hedef - - # Sonuncusunda [Ileri->] baglantisi secili olmasin - if [ $count == $total_ders ]; then - echo -n 'DERS_NAV_ILERI=' >> $hedef - echo -n '[ $(ILERI_METIN) ↣ ]' >> $hedef - echo >> $hedef - else - echo -n 'DERS_NAV_ILERI=$(LINK2 $(ROOT_DIR)' >> $hedef - echo -n $butun_ders_links | awk "{print \$$next}" >> $hedef - echo ', [ $(ILERI_METIN) ↣ ])' >> $hedef - echo >> $hedef - fi; - - cozum_html=`echo $ders_html | sed 's/.html/.cozum.html/'` - - echo 'COZUM_HTML=$(ROOT_DIR)'$cozum_html >> $hedef - echo >> $hedef - - fi; - let ++count ; - done; -done; diff --git a/ddili/src/ornek_kod/breadcrumbs.ddoc b/ddili/src/ornek_kod/breadcrumbs.ddoc deleted file mode 100644 index 021356e..0000000 --- a/ddili/src/ornek_kod/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /, Ana Sayfa) > Örnek Kodlar - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) > $(LINK2 /ornek_kod/, Örnek Kodlar) diff --git a/ddili/src/ornek_kod/index.d b/ddili/src/ornek_kod/index.d deleted file mode 100644 index 13dd958..0000000 --- a/ddili/src/ornek_kod/index.d +++ /dev/null @@ -1,210 +0,0 @@ -Ddoc - -$(H3 Örnek Kodlar) - -$(P -Buradaki küçük D programları, dilin özellikleri ve söz dizimi hakkında hızlıca bir fikir verme amacı taşıyorlar. -) - -$(H5 Merhaba dünya) - -$(P -Klasik "merhaba dünya" programı. -) - ---- -import std.stdio; - -void main() -{ - writeln("Merhaba dünya!"); -} ---- - -$(UL - -$(LI -D'de başlık dosyası yerine $(I modül) kavramı var. $(CODE import) anahtar sözcüğü ile $(CODE std.stdio) modülünün kullanıldığı belirtiliyor. -) - -$(LI -$(CODE writeln) standart çıkışa bir satır yazdırmak için kullanılıyor. -) - -$(LI -D'de hem $(CODE void main), hem de $(CODE int main) yasal. C ve C++'daki bu konudaki tarihsel karışıklık D'de yok... -) - -) - -$(H5 Türkçe harfler) - -$(P -D'nin en güzel taraflarından birisi, kaynak kodlarda Unicode karakterlerin de kullanılabilmesi. -) - ---- -import std.stdio; - -void main() -{ - Türkçe_harf_dene(); -} - -void Türkçe_harf_dene() -{ - writeln("ğüşiöçıĞÜŞİÖÇI"); -} ---- - -$(UL - -$(LI -Dosyayı UTF-8 olarak kaydetmek yeterli. -) - -$(LI -C'nin ve C++'nın tersine, isimlerin önceden bildirilmesi gerekmiyor. Programda $(CODE Türkçe_harf_dene) işlevini çağırabilmek için onu önceden bildirmek gerekmedi. -) - -) - -$(H5 Aralıklar ve UFCS) - -$(P -D'de eleman erişimi aralık soyutlaması ile sağlanır. Aşağıdaki program sonsuz bir FibonacciSerisi aralığı tanımlamakta ve onu Phobos algoritması $(C take()) ile kullanarak yalnızca ilk 10 değeri yazdırmaktadır: -) - ---- -import std.stdio; -import std.range; - -struct FibonacciSerisi -{ - int baştaki = 0; - int sonraki = 1; - - enum empty = false; - - int front() const @property - { - return baştaki; - } - - void popFront() - { - int ikiSonraki = baştaki + sonraki; - baştaki = sonraki; - sonraki = ikiSonraki; - } -} - -void main() -{ - FibonacciSerisi().take(10).writeln(); - - /* - * Yukarıdaki satırda UFCS (uniform function call syntax) - * olanağının yararını görüyoruz. Aynı satır daha - * alışılmış işlev çağrısı söz dizimi ile de - * işletilebilirdi: - * - * writeln(take(FibonacciSerisi(), 10)); - */ -} ---- - -$(P -Çıktısı: -) - -$(SHELL -[0, 1, 1, 2, 3, 5, 8, 13, 21, 34] -) - -$(H5 İç işlevler ve isimsiz işlevler) - -$(P -İşlevler başka işlevlerin içerisinde tanımlanabilirler. Hatta, isimsiz olarak ifadeler içerisinde bile tanımlanabilirler. $(ASIL lambda functions, function literals, veya delegates) -) - ---- -import std.stdio; - -void main() -{ - /* - * Örnek olsun diye bir iç işlev kullanılıyor; onun yerine - * serbest işlev da olurdu. - * - * Bu işlevin üç parametresi: - * - * baş: hesaplanacak ilk değer - * son: hesaplanacak son değerden bir sonrası - * hesap: parametre olarak tek bir int alan ve int - * döndüren bir temsilci - */ - void içİşlev(int baş, int son, int delegate(int) hesap) - { - foreach (i; baş..son) { - /* Parametre olarak verilen fonksiyonu kullanarak - * bir hesap yapıyoruz*/ - immutable sonuç = hesap(i); - writefln("%s: %s", i, sonuç); - } - } - - writeln("kareler:"); - - /* - * Yukarıdaki iç işlevin burada isimsiz bir işlev ile - * çağrılmakta olduğunu görüyoruz. - * - * Burada üçüncü parametre bir 'işlev hazır değeridir' - * (lambda function). Parametre olarak gönderilen işlevi - * hemen ifadenin içinde tanımlıyoruz. - * - * Parametre almayan bir fonksiyon sabiti olsa daha da - * kısa olarak yazılabilirdi: - * - * içİşlev(3, 10, { return 5; } ); - */ - içİşlev(3, 10, (int sayı){ return sayı * sayı; } ); - - writeln("iki katlar:"); - - /* Bu çağrı ise D'nin => söz diziminden yararlanıyor. */ - içİşlev(1, 7, sayı => sayı * 2); -} ---- - -$(P -Çıktısı: -) - -$(SHELL -kareler: -3: 9 -4: 16 -5: 25 -6: 36 -7: 49 -8: 64 -9: 81 -iki katlar: -1: 2 -2: 4 -3: 6 -4: 8 -5: 10 -6: 12 -) - -Macros: - SUBTITLE=Örnek Kodlar - - DESCRIPTION=D programlama diliyle yazılmış küçük örnek programlar - - KEYWORDS=örnek program kod merhaba dünya - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/pdf.css b/ddili/src/pdf.css deleted file mode 100644 index 072cf48..0000000 --- a/ddili/src/pdf.css +++ /dev/null @@ -1,1220 +0,0 @@ -/* reset */ -html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td -{ -margin: 0; -padding: 0; -border: 0; -outline: 0; -font-size: 100%; -vertical-align: baseline; -background: transparent; -} - -table, tbody, tfoot, thead, tr, th, td, caption { -/* margin: 1em 1em 1em 1em; */ - padding: 0px 2px 0px 2px; - border: 1px solid black; - border-collapse: collapse; - outline: 0; - font-family: monospace; - font-size: .9em; -} - -th, caption { - font-family: sans-serif; - padding: 0 .1em 0 .1em; - margin-top: 0; -} - -td.serif { - font-family: serif; -} - -table.full { - width: 100%; -} - -table.wide { - width: 80%; -} - -table.medium { - width: 40%; -} - -table.narrow { - width: 25%; -} - -table.centered { - text-align:center; -} - -table { - margin: .75em 0 .75em 0; -} - -caption { - border: none; - font-weight:bold; -} - -sup { -font-size:.65em; -vertical-align:top; -} - -sub { -font-size:.65em; -vertical-align:bottom; -} - -hr.thin { - color: #000000; - background-color: #000000; - height: 1px; - border: 0; - width: 100%; -} - -body -{ - line-height: 1.2; -} - -ol, ul -{ -margin: .75em 0 .75em 1.5em; -} - -/* Default bullets were 2022 25e6 25aa */ -ul > li::marker { content: "\2219" } -li ul > li::marker { content: "\2218" } -li li ul > li::marker { content: "\2022" } - -ul.font_list li::marker { content: none } -ul.no_bullet li::marker { content: none } - -li { - margin-top: .25em; -} - -/* standart baglanti renkleri */ -a:link {color: blue;} -a:visited {color: purple;} -a:hover {color: red;} -a:active {color: purple;} - -a:link .d_inline{color: blue;} -a:visited .d_inline {color: purple;} -a:hover .d_inline {color: red;} -a:active .d_inline {color: purple;} - -a { - color:#000000; - text-decoration:none; - } - -@page { margin: .75in 1in .75in 1in } -@page { size: 7in 10in } - -@page cover { - margin: 0; - padding: 0; -} -div.frontcover { page: cover; } - -h4 { string-set: bolumismi content() } - -body { - display: block; - page: main; -} - -@page :right { - margin: .75in .75in .688in 1.25in; - @top-right { - content: string(bolumismi); - font-size:9pt; - font-family: serif; - font-style:italic; - } - @bottom-right { - content: counter(page); - font-size:9pt; - font-family: serif; - width:40px; - } -} - -@page :left { - margin: .75in 1.25in .688in .75in; - @top-left { - content: string(bolumismi); - font-size:9pt; - font-family: serif; - font-style:italic; - } - @bottom-left { - content: counter(page); - font-size:9pt; - font-family: serif; - width:40px; - } -} - -@page chapter:first:left { - @top-right { - content: ""; - font-family: serif; - } - @top-left { - content: ""; - font-family: serif; - } - @bottom-left { - content: counter(page); - font-size:9pt; - font-family: serif; - width:40px; - } -} - -@page chapter:first:right { - @top-right { - content: ""; - font-family: serif; - } - @top-left { - content: ""; - font-family: serif; - } - @bottom-right { - content: counter(page); - font-size:9pt; - font-family: serif; - width:40px; - } -} - -.chapter { - page: chapter; - prince-page-group: start; -} - -@page:first { - @bottom-left { content: normal } - @bottom-center { content: normal } - @bottom-right { content: normal } -} - -@page { orphans:2; widows:2; } - -h1 { counter-increment: h1 } -h2 { counter-increment: h2 } -h3 { counter-increment: h3 } -h4 { counter-reset: h5 } -h5 { counter-increment: h5 } -h5 { counter-reset: h6 } -h6 { counter-increment: h6 } - -div.chapter { - counter-increment: chapter_counter; -} - -h4.chapter, h4.solution { - counter-reset: h5; -} - -h4::before { - content: counter(chapter_counter); - padding: 0 .5em 0 0; - float: left; -} - -div.imprint h4::before { - content: normal; -} - -h5::before { - content: counter(chapter_counter) "." counter(h5); - color: black; - float: left; - padding: 0 .5em 0 0; -} - -h5.chapter { - counter-increment: h5; -} - -h5.solution_subsection:before { - content: normal; -} - -h4 { page-break-before: always } -h5 { page-break-after: avoid } -h6 { page-break-after: avoid } - -body { - background-color:#ffffff; - font-family: serif; - font-size: 10pt; - } - -#kitap_baslik, #kitap_baslik_d, #kitap_baslik_gtkd, #kitap_baslik_sdl, #kitap_alt_baslik, #kitap_yazar, #kitap_seri { - color:#000040; - font-family: sans-serif; - text-align: center; - font-weight:bold; -} - -#kitap_baslik_d { - font-size:160pt; -} - -#kitap_baslik { - padding-top: 1em; - font-size:100pt; -} - -#kitap_baslik_gtkd { - font-size:100pt; -} - -#kitap_baslik_sdl { - font-size:80pt; -} - -#kitap_baslik_2 { - margin-right: .25em; - font-size:32pt; -} - -#kitap_surum { - font-size:8pt; - text-align:left; -} - -#kitap_alt_baslik { - font-family: serif; - font-size:14pt; - font-style:italic; -} - -#kitap_yazar { - margin: 2em 2em 0 0; - font-size:20pt; - text-align:right; - float:right; -} - -#kitap_seri { - margin:4em 0 0 2em; - font-size:12pt; - text-align: left; - text-align: bottom; - float:left; - -} - -div.frontcover { - height: 10in; - width: 7in; - background-color:#ffffff; - page-break-before: right; -} - -div.halftitle, div.title, div.imprint, div.dedication { - page: blank -} - -div.toc_header, div.foreword, div.preface, div.frontmatter { - page: frontmatter; -} - -div.solution { - counter-increment: chapter_counter; -} - -h4.frontmatter:before, h4.index:before, h4.sozluk:before, h4.solution:before { - content: normal; -} - -div.toc_header h4:before { - content: normal; -} - -@page frontmatter :left { - @top-left { - content: ""; - font-family: serif; - } - @top-right { - content: ""; - font-family: serif; - } - @bottom-left { - content: counter(page, lower-roman); - font-family: serif; - } -} - -@page frontmatter :right { - @top-left { - content: ""; - font-family: serif; - } - @top-right { - content: ""; - font-family: serif; - } - @bottom-right { - content: counter(page, lower-roman); - font-family: serif; - } -} - -@page blank :left { - @top-left { - content: normal; - font-family: serif; - } - @bottom-left { - content: normal; - font-family: serif; - } -} - -@page blank :right { - @top-right { - content: normal; - font-family: serif; - } - @bottom-right { - content: normal; - font-family: serif; - } -} - -div.cozum_link { - page-break-before: avoid -} - -#container { - width:713px; - height:auto; - margin:auto; - } - -#container_cozum { - width:600px; - height:auto; - margin:auto; - } - -#header { - width:713px; - height:76px; - background-color:#003366; -} - -#header_ders { - width:713px; - height:76px; - background-color:#663366; -} - -#header_cozum { - width:600px; - height:60px; - background-color:#336633; - float:right; -} - -#header1 { - color:#e0e0e0; - font-size:2.75em; - font-weight:bold; - width:550px; - margin: 12px 0 0 24px; - float:left; - } - -#header1_cozum { - color:#e0e0e0; - font-size:2em; - font-weight:bold; - width:550px; - margin: 12px 0 0 24px; - float:left; - } - -#headersecondary { - color:#999999; - font-weight:bold; - margin: 12px 12px 8px 8px; - float:right; - width:113px; - text-align:right; - line-height:160%; -} - - -#headersecondary a{ - text-decoration:none; - color:#e0e0e0; - margin: 0 0 0 .5em; - } - -#headersecondary a:hover { - border-bottom: 3px solid #999999; - } - -#headersecondary a:active { - color:#999999; - border-bottom: 3px solid #CCCCCC; - } - -#vertinav { - background-color:#e0e0e0; - width:113px; - float:left; - } - -#vertinav ul { - list-style: none; - display:inline; - } -#vertinav li a{ - margin:0 0 0 12px; - color:#333333; - text-decoration:none; - } - -#vertinav li a:link { - color: blue; - } - -#vertinav li a:visited { - color: purple; - } - -#vertinav li a:hover { - color: red; - } - -#vertinav li a:active { - color: purple; - } - -.vertinavheader { - color:#000000; - margin-left:8px; - } - -#vertinavcontent { - margin-left:16px; - color:#666666; -} - -#content { - background-color:#ffffff; - float:right; - width:713px; - line-height:1.1; - } - -#content li { - margin: 0 0 4px 0; - } - -#breadcrumbs { - float:right; - width:600px; - background-color:#e0e0e0; - font-size:.9em; - } - -#breadcrumbs_ders { - float:left; - width:420px; - background-color:#e0e0e0; - font-size:.9em; - } - -#ders_nav_bas { - float:right; - width:180px; - font-size:.9em; - text-align:center; - } - -#ders_nav_son { - float:right; - width:180px; - font-size:.9em; - text-align:center; - } - -#footer { - background-color:#e0e0e0; - float:right; - width:100px; - font-size:.5em; - } - -h1,h2,h3,h4,h5,h6 { - font-family: sans-serif; - font-weight:bold; - } - -h1 { - font-size: 3em; - prince-bookmark-level: 1 - } - -h2 { - font-size:2.5em; - prince-bookmark-level: 2 - } - -h3 { - font-size:2em; - prince-bookmark-level: 3 - } - -h4 { - background-color: black; - color: #ffffff; - margin: 0 0 .75em 0; - padding: .1em 0 .1em .5em; - /* .d_inline_h4 unutulmasin! */ - font-size:1.25em; - prince-bookmark-level: 4; - } - -h5 { - margin: .75em 0 0 0; - font-size:1.1em; - prince-bookmark-level: 5 - } - -h5.solution_subsection { - margin-top: 1em; -} - -h5.index_section:before { - content: normal; -} - -h5.index_section { - margin-bottom: -.5em; - padding-bottom: 0; -} - -h5.fark { - margin-top: .5em; -} - -h6 { - padding: .5em 0 0 0; - font-size:1em; - prince-bookmark-level: 6 - } - -p { - color:#000000; -/* margin-top: .75em; */ -/* margin-bottom: .75em; */ - text-indent: 1em; -} - -h4+p, h5+p, h6+p, pre+p, table+p, ul+p, ol+p { - text-indent: 0em; -} - -/* Style applied to preformatted text used to show examples. */ -pre.d_code, pre.c_code, div.quote, pre.mono, pre.mono_nobold -{ - border-left: dotted 1px #808080; - border-right: dotted 1px #808080; - background-color: #ffffff; - padding: .5em .5em .5em .5em; - margin: .75em 0 .75em 0; - font-family: monospace; - font-size: .75em; - line-height: 1.1; -} - -pre::before, pre::after { - border-top: dotted 1px #808080; - content: ''; - position:absolute; - z-index:100; - width: 47.8em; -} - -li > pre::before, li > pre::after { - width: 45.8em; -} - - -li li > pre::before, li li > pre::after { - width: 43.8em; -} - -li li li > pre::before, li li li > pre::after { - width: 41.8em; -} - -li li li li > pre::before, li li li li > pre::after { - width: 39.8em; -} - -pre::before { - margin: -.5em; -} - -pre:after { - margin: .5em .5em 0 -.5em; -} - -pre.mono_nobold -{ - background-color: #e0e0e0; -} - -pre.c_code -{ - background-color: #f0f0e0; -} - -/* Style applied to preformated text for shell commands. */ -pre.shell, pre.shell_small -{ - margin: .5em 0 .5em 0; - padding: .5em .5em .5em .5em; - font-family: monospace; - font-weight:bold; - border-left: dotted 1px #808080; - border-right: dotted 1px #808080; - background-color: #e0e0e0; - font-size: .75em; - line-height: 1.1; -} - -pre.shell_small -{ -/* font-size: .9em; */ -} - -div.quote { - background-color:#e0e0e0; -} - -div.foreword h4:before { - content: normal; -} - -h4 .foreword_by { - font-size: .75em; - font-weight:normal; -} - -div.preface h4::before { - content: normal; -} - -div.preface h5::before { - content: normal; -} - -.shell_observed { - color: #404040; -} - -div.halftitle, div.title { - padding-top: 4in; - font-family: sans-serif; - text-align:right; -} - -@page frontispiece { - margin: .5in; - padding: 0; -} - -@page frontispiece:left { - margin: .5in 1in .5in .5in; - @top-left { - content: normal; - } - @bottom-left { - content: normal; - } -} - -div.frontispiece { - page: frontispiece; -} - -.title .editors { - font-size: 14pt; -} - -.halftitle { - font-size: 30pt; -} - -.halftitle_sub { - font-size: 20pt; -} - -.title { - font-size: 16pt; -} - -div.imprint { -/* padding-top: 1in; */ -} - -.copyright_title { - font-size: 12pt; - font-family: sans-serif; -} - -.page_one { - counter-reset: page 1; -} - -ul.toc { page-break-before: avoid } - -.foreword, .preface { - page-break-before: right; -} - -.d_comment, .d_string, .d_keyword, .d_inline, .d_inline_h4, .d_hata, .shell_note, .shell_note_wrong, .code_note_wrong { - font-family: monospace; -} - -@media screen { - -/* Style applied to preformated text used to show examples. */ -pre.d_code, pre.c_code, div.quote, pre.mono -{ - background-color: #ffffff; -} - -pre.d_code { - background-color: #f8f8f8; -} - -/* Elements of D source code text */ -.d_comment { color: green; } -.d_string { color: red; } -.d_keyword { color: blue; } - -.d_inline, .d_inline_h4 { - color:black; - font-size: .9em; -} - -h1 .d_inline { color:#000000; } -h2 .d_inline { color:#000000; } -h3 .d_inline { color:#000000; } -h4 .d_inline { color:#ffffff; } -h5 .d_inline { color:#000000; } -h6 .d_inline { color:#000000; } - -h4 .d_inline, h5 .d_inline, h6 .d_inline { - font-size: 1em; -} - -.d_inline_index { - font-size:.9em; - color:#888888; -} - -.d_hata { - background-color:#ff4040; - color:#000000; -} - -.hilite { - background-color:#ffff88; -} - -.shell_note { - color: blue; -} - -.shell_note_wrong, .code_note_wrong { - color: red; -} - -.unordered_false { - color: darkred; -} - -.unordered_true { - color: green; -} - -.unordered_no { - color: red; -} - -.unordered_yes { - color: gray; -} - -p.kapak_author { - font-size: 16pt; -} - -} /* media screen */ - - -.steps li{ - margin:0 0 1.5em .5em; - text-decoration:none; - } - -ul.fark { - margin-left:4em; -} - -.on_ust { - height:180px; - width:600px; -} - -img.d_harfi { - height:180px; - width:180px; - float:right; - margin:16px 16px 8px 16px; -} - -.d_harfi_yan { - float:left; - width:380px; - height:150px; - margin:8px 0 0 0; -} - -div.mini_sozluk_sozcuk { - margin-bottom: 4px; -} - -div.sozluk { - margin:0; - padding: 0; - column-count: 2; - column-gap: 10px; -} - -div.sozcuk { - margin: 0; -} - -.mini_sozluk { - background-color:#ffe8ff; - border: dotted 1px #c0c0c0; - font-size: .8em; - margin: 0 8px 0 8px; - padding: 2px; -} - -.butun_sozluk { - margin: 0 4px 0 8px; - text-align:right; - } - -div.problem { - border: dotted 1px #c0c0c0; - background-color:#ffeeee; - margin: 0 1em 0.75em 16px; - padding: .5em; -} - -div.cozum, div.odev { -/* margin: 0 1em 0.75em 16px;*/ -} - -/* TOC */ - -ul.toc li { - margin: 0; -} - -ul.toc li::marker, ul.toc ul li::marker { - content: none; -} - -ul.toc, ul.toc ul { - list-style-type: none; - margin: 0; padding: 0; -} -ul.toc ul { - margin-left: 3em; - font-weight: normal; -} -ul.toc .d_inline { - font-weight: normal; -} -ul.toc a.toc:after { - content: leader('.') target-counter(attr(href), page); - font-style: normal; -} - -ul.toc a.subsection:after, a.solution_subsection:after { - content: leader('.') target-counter(attr(href), page); - font-style: normal; -} - -ul.toc a.chapter:before { - content: target-counter(attr(href), chapter_counter) ". "; - font-weight: bold; -} - -ul.toc a.chapter, a.solution, a.index, a.sozluk, a.foreword, a.preface, a.frontmatter { - line-height: 1.1; - font-weight: bold; - font-style: normal; - vertical-align: text-bottom; -} - -ul.toc a.chapter:after, a.solution:after, a.index:after, a.sozluk:after { - content: leader(' ') target-counter(attr(href), page); - font-style: normal; -} - -ul.toc a.foreword:after, a.preface:after { - content: leader(' ') target-counter(attr(href), page, lower-roman); - font-style: normal; -} - -ul.toc a.frontmatter { - font-weight: normal; -} - -ul.toc a.frontmatter:after { - content: leader('.') target-counter(attr(href), page, lower-roman); - font-style: normal; - font-weight: normal; -} - -ul.index_section { - list-style-type: none; - padding: 0 0 0 1em; - text-indent: -1em; - line-height: 1; -} - -ul.index_section > li::marker, ul.index_section > ul > li::marker { - content: none; -} - -h5.index_section { - margin-top: 1em; -} - -ul.index_section a { - content: target-counter(attr(href), page); -} - -div.index_section { - margin: 0 0 0 -1em; - column-count: 2; - column-gap: 1em; - page-break-before: avoid; -} - -html { - counter-reset: chapter_counter -4; -} - -@page { - @footnotes { - border-top: solid black thin; - padding-top: .5em; - padding-left: 1em; - margin-top: .5em; - } -} - -a[href]:after::footnote-marker { - margin-right: .5em; -} - -a[href^="http"]:after { - float: footnote; - content: " " attr(href); - text-indent: 0em; - font-size: .8em; - font-weight:none; - font-style:normal; -} - -a[href^="mailto"]:after { - float: footnote; - content: " " attr(href); - text-indent: 0em; - font-size: .8em; - font-weight:none; - font-style:normal; -} - -@page { - counter-reset: footnote -} - -@media print { - -div.toc_header, div.foreword, div.preface, div.frontmatter, div.first_chapter, div.index { - page-break-before: right; -} - -.d_comment { - color: #222222; -} -.d_string { color: black; } -.d_keyword { - color: black; - font-weight:bold; -} - -.d_inline, .d_inline_h4 { - color:black; - font-size: .9em; -} - -h1 .d_inline { color:black; } -h2 .d_inline { color:black; } -h3 .d_inline { color:black; } -h4 .d_inline { color:white; } -h5 .d_inline { color:black; } -h6 .d_inline { color:black; } - -h4 .d_inline, h5 .d_inline, h6 .d_inline { - font-size: 1em; -} - -.d_inline_index { - font-size:.9em; - color:#888888; -} - -.d_hata { - color: white; - background-color: #202020; - padding: 0 .25em 0 .25em; - font-weight:normal; -} - -.hilite { - background-color:#c0c0c0; -} - -.shell_note { - background-color: #c0c0c0; - font-style:italic; - padding: 0 .25em .1em .25em; - font-weight:normal; -} - -.shell_note_wrong, .code_note_wrong { - color: white; - background-color: #202020; - font-style:italic; - padding: 0 .25em .1em .25em; - font-weight:normal; -} - -pre.c_code -{ - color: black; - background-color: #f0f0f0; -} - -a:link {color: black;} -a:visited {color: black;} -a:hover {color: black;} -a:active {color: black;} - -a:link .d_inline{color: black;} -a:visited .d_inline {color: black;} -a:hover .d_inline {color: black;} -a:active .d_inline {color: black;} - -h5::before { - color: black; -} - -#kitap_baslik, #kitap_baslik_d, #kitap_baslik_gtkd, #kitap_baslik_sdl, #kitap_alt_baslik, #kitap_surum, #kitap_yazar, #kitap_seri { - color: black; -} - -h4 { - color: white; - } - -pre.d_code, pre.c_code, div.quote, pre.mono -{ - background-color: #ffffff; - color: black; -} - -pre.d_code { - background-color: #f0f0f0; -} - -pre.mono_nobold -{ - background-color: #e0e0e0; -} - -pre.c_code -{ - background-color: #e0e0e0; -} - -pre.shell, pre.shell_small -{ - background-color: #e0e0e0; -} - -.unordered_false { - color: normal; -} - -.unordered_true { - color: normal; -} - -.unordered_no { - color: normal; -} - -.unordered_yes { - color: normal; -} - -p.kapak_author { - content: none; -} - -.title { - page-break-before: right; -} - -div.toc { - page-break-before: always; -} - -.mono { - page-break-inside: avoid -} - -} /* media print */ diff --git a/ddili/src/pdf.ddoc b/ddili/src/pdf.ddoc deleted file mode 100644 index 2a1be6c..0000000 --- a/ddili/src/pdf.ddoc +++ /dev/null @@ -1,8 +0,0 @@ - -DDOC = $(DIV_CLASS $(CLASS), - - -$(BODY) -) - -COZUM_BOLUMU =
    $0
    diff --git a/ddili/src/pdf_fonts.css b/ddili/src/pdf_fonts.css deleted file mode 100644 index 3306688..0000000 --- a/ddili/src/pdf_fonts.css +++ /dev/null @@ -1,85 +0,0 @@ -/* Prince does not support WOFF fonts. */ - -@font-face { - font-family: sans-serif; - font-style: normal; - font-weight: normal; - src: url("fonts/opensans/OpenSans-Regular.ttf") -} - -@font-face { - font-family: sans-serif; - font-style: normal; - font-weight: bold; - src: url("fonts/opensans/OpenSans-Bold.ttf") -} - -@font-face { - font-family: sans-serif; - font-style: italic; - font-weight: normal; - src: url("fonts/opensans/OpenSans-Italic.ttf") -} - -@font-face { - font-family: sans-serif; - font-style: italic; - font-weight: bold; - src: url("fonts/opensans/OpenSans-BoldItalic.ttf") -} - -@font-face { - font-family: serif; - font-style: normal; - font-weight: normal; - src: url("fonts/andada/Andada-Regular.ttf") -} - -@font-face { - font-family: serif; - font-style: normal; - font-weight: bold; - src: url("fonts/andada/Andada-Bold.ttf") -} - -@font-face { - font-family: serif; - font-style: italic; - font-weight: normal; - src: url("fonts/andada/Andada-Italic.ttf") -} - -@font-face { - font-family: serif; - font-style: italic; - font-weight: bold; - src: url("fonts/andada/Andada-BoldItalic.ttf") -} - -@font-face { - font-family: monospace; - font-style: normal; - font-weight: normal; - src: url("fonts/dejavu/DejaVuSansMono-webfont.ttf") -} - -@font-face { - font-family: monospace; - font-style: normal; - font-weight: bold; - src: url("fonts/dejavu/DejaVuSansMono-Bold-webfont.ttf") -} - -@font-face { - font-family: monospace; - font-style: italic; - font-weight: normal; - src: url("fonts/dejavu/DejaVuSansMono-Oblique-webfont.ttf") -} - -@font-face { - font-family: monospace; - font-style: italic; - font-weight: bold; - src: url("fonts/dejavu/DejaVuSansMono-BoldOblique-webfont.ttf") -} diff --git a/ddili/src/regular.ddoc b/ddili/src/regular.ddoc deleted file mode 100644 index 7e84ee0..0000000 --- a/ddili/src/regular.ddoc +++ /dev/null @@ -1,15 +0,0 @@ - -MAIN_TITLE=D Programlama Dili - -HEADER_ID=header - -BREADCRUMBS_ID=breadcrumbs - -CONTAINER_ID=container - -VERTINAV_CONTENT=$(DUSEY_NAVIGASYON) - -HORIZNAV_CONTENT=$(LINK2 /rss.xml, RSS RSS Beslemesi) - $(BR) -$(LINK2 /forum/, Ddili Forum ) - diff --git a/ddili/src/reveal_ix.css b/ddili/src/reveal_ix.css deleted file mode 100644 index 88d4846..0000000 --- a/ddili/src/reveal_ix.css +++ /dev/null @@ -1,34 +0,0 @@ -/* These formats make index anchors visible in a hacky way. */ - -a[name^="ix_"]:after { - position: absolute; - float: footnote; - content: attr(content); - color: red; - line-height: 0; - z-index:2; -} - -a[name^="ix_"]:after::footnote-call { - color: red; - position: relative; - left: -5em; - width: 60px; - padding: 0; - margin: 0; - border: 0; -/* margin-left: -3em; */ - content: attr(content); - font-size: 60%; - vertical-align: super; - line-height: 0; -} - -a[name^="ix_"]:after::footnote-marker { - color: red; - float: left; - content: none;/*counter(footnote);*/ - font-size: 1em; - vertical-align: super; - line-height: 0.1; -} diff --git a/ddili/src/robots.txt b/ddili/src/robots.txt deleted file mode 100644 index daeb38b..0000000 --- a/ddili/src/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: /image/ diff --git a/ddili/src/rss.xml b/ddili/src/rss.xml deleted file mode 100644 index 2b309f2..0000000 --- a/ddili/src/rss.xml +++ /dev/null @@ -1,316 +0,0 @@ - - - - - D Programlama Dili - http://ddili.org - D programlama dili ile ilgili belgeler, makaleler, kod örnekleri, yardımcı bilgiler, forum, vs. - tr - D Programlama Dili - - - Zafer Çelenk'in "DUB ile Tanışalım" makalesi - http://ddili.org/makale/dub_tanisma.html - Proje ve paket yönetim programı olan DUB'ın kullanımı. - Makale - Sat, 02 Apr 2016 23:30 - - - - David Nadlinger'ın "D'nin Saflık Kavramı" makalesi - http://ddili.org/makale/saflik.html - Fonksiyonel programlamanın önemli kavramlarından olan saflık kavramının D'deki gerçekleştirmesi ve pure anahtar sözcüğünün diğer dil olanakları ile nasıl etkileştiği. - Makale - Thu, 17 Oct 2013 22:15 - - - - Sunumlar Bölümü - http://ddili.org/sunum/index.html - D diliyle ilgili sunumlar. - Tanıtım - Fri, 13 Jul 2012 16:30 - - - - Zafer Çelenk'in "D Dilindeki Tipleri Anlamak" makalesi - http://ddili.org/makale/d_tipleri.html - D'nin temel tipleri ve bitlerle nasıl gerçekleştirildikleri. - Makale - Wed, 7 Mar 2012 10:45 - - - - SDL ile Oyun Programlama dersleri - http://ddili.org/ders/sdl/ - SDL kütüphanesini kullanarak D ile oyun programları yazma - Eğitsel - Wed, 15 Jun 2011 14:30 - - - - Steven Schveighoffer'ın "D Dilimleri" makalesi - http://ddili.org/makale/d_dilimleri.html - D'nin çok kullanışlı, çok hızlı ve güvenli olanaklarından olan dilimlerin tanıtılmaları ve bazı işlemlerinin perde arkasında nasıl gerçekleştirildiklerinin gösterilmesi - Makale - Wed, 31 May 2011 23:50 - - - - D Programlama Dili güncellendi - http://ddili.org/ders/d/ - D Programlama Dili dersleri ve program örnekleri, D'deki gelişmeler doğrultusunda güncellendi - Eğitsel - 21 Mar 2011 23:00 - - - - Andrei Alexandrescu'nun "Eleman Erişimi Üzerine" makalesi - http://ddili.org/makale/eleman_erisimi_uzerine.html - Erişici kavramından daha güçlü olduğu gösterilen aralık (range) kavramı - Makale - Mon, 17 Jan 2011 01:30 - - - - Programming in D kitabı artık pdf - http://ddili.org/ders/d.en/index.html - İngilizce D kitabının hepsini birden PDF olarak bölüm başlıklarındaki bağlantısından indirebilirsiniz. - News - 21 Jan 2012 18:00 - - - - GtkD kitabı artık pdf - http://ddili.org/ders/gtkd/index.html - GtkD kitabının hepsini birden PDF olarak bölüm başlıklarındaki bağlantısından indirebilirsiniz. - Haber - 21 Jan 2012 18:00 - - - - SDL kitabı artık pdf - http://ddili.org/ders/sdl/index.html - SDL kitabının tamamını PDF olarak bölüm başlıklarındaki bağlantısından indirebilirsiniz. - Haber - 21 Jan 2012 18:00 - - - - D Programlama Dili dersleri artık pdf - http://ddili.org/ders/d/pdf_indir.html - D Programlama Dili derslerinin hepsini birden tek bir pdf olarak indirebilirsiniz - Eğitsel - 02 Apr 2010 01:40 - - - - D.ershane'ye şablonlar (template) da eklendi - http://ddili.org/ders/d/ - D dersleri artık şablonları (template) da kapsıyor - Eğitsel - 18 Feb 2010 01:00 - - - - "shared'e Geçiş" isimli makale - http://ddili.org/makale/shared.html - D'nin bellek erişimini güvenli hale getirmek için evrensel [global] değişkenleri normalde iş parçacığına özel alana [thread local storage, (TLS)] yerleştirmesi, ve bununla ilgili olanakları - Makale - Mon, 14 Dec 2009 10:30 - - - - Projeler - http://ddili.org/wiki/index.php?title=Ddili_Projeleri - Ddili topluluğunun ortaklaşa geliştirmekte olduğu projelere başlandı - Haber - 17 Sep 2009 12:00 - - - - D.ershane - http://ddili.org/ders/d/ - Temelden başlayarak adım adım D programcılığı öğrenebileceğiniz bir bölüm - Eğitsel - 01 Sep 2009 18:15 - - - - Ddili Wiki - http://ddili.org/wiki/ - Türkçe belgeler ve program örnekleri bulabileceğiniz, ve katkılarınızla geliştireceğiniz bir paylaşım alanı - Eğitsel - 01 Sep 2009 18:15 - - - - Geliştirme ortamı SciTE'ın Linux'ta kurulması - http://ddili.org/kurulum/scite.html - Son derece basit bir geliştirme ortamı olan SciTE'ın Linux ortamında kurulması. - Eğitsel - Mon, 20 Jul 2009 00:05 - - - - "Düzenli İfadeler [Regular Expressions]" isimli makale - http://ddili.org/makale/duzenli_ifadeler.html - Düzenli ifadelerin D'de Ruby'deki kadar güçlü oldukları ve ondan ne farklılıklar gösterdikleri. - Makale - Tue, 14 Jul 2009 17:45 - - - - "Bellek Yönetimi" isimli makale - http://ddili.org/makale/bellek.html - D'de bellek yönetimi için kullanılan yöntemler. - Makale - Tue, 14 Jul 2009 14:50 - - - - "const ve immutable Kavramları" isimli makale - http://ddili.org/makale/degismez.html - D'deki değişmezlik kavramlarının tanıtılmaları ve const ve immutable anahtar sözcüklerinin kullanımları. - Makale - Mon, 13 Jul 2009 13:10 - - - - "Dizgi Katmaları [mixin]" isimli makale - http://ddili.org/makale/katma.html - Derleme zamanında oluşturulan dizgilerin nasıl koda dönüştürüldüklerini gösteren kısa bir yazı. - Makale - Sat, 11 Jul 2009 22:20 - - - - Walter Bright'ın "Fonksiyon Argümanlarında Tembel Değerlendirmeler" isimli makalesi - http://ddili.org/makale/tembel_hesap.html - Tembel değerlendirme [lazy evaluation], bir ifadenin işletilmesinin ancak gerçekten gerek duyulana kadar geciktirilmesidir. Bu makalede D'de fonksiyon argümanlarının tembel olarak işletilebilme olanağının fonksiyonların ifade yeteneklerini ne kadar arttırdığı anlatılmaktadır. - Makale - Fri, 10 Jul 2009 22:00 - - - - Karmaşık sayı türleri ve C++'nın std::complex'i - http://ddili.org/tanitim/fark_karmasik.html - D'nin karmaşık sayı türlerinin tanıtılması ve C++'ın std::complex türüyle karşılaştırılması - Tanıtım - Thu, 09 Jul 2009 17:20 - - - - Olanakların dil içinde veya kütüphanelerde gerçekleştirilmeleri - http://ddili.org/tanitim/dil_kutuphane.html - Bazı veri yapılarının neden kütüphane türleri yerine D dilinin iç olanakları olarak sunulmuş oldukları - Tanıtım - Thu, 09 Jul 2009 15:15 - - - - D olanaklarının C önişlemcisiyle [preprocessor] karşılaştırılması - http://ddili.org/tanitim/fark_onislemci.html - C'nin önişlemcisi ile D'nin sunduğu olanakların farkları - Tanıtım - Tue, 07 Jul 2009 23:30 - - - - Dizgilerin [string] C++ dizgileri ile karşılaştırılması - http://ddili.org/tanitim/fark_dizgi.html - C++ dilindeki dizgilerle D dilindeki dizgilerin farkları - Tanıtım - Tue, 07 Jul 2009 00:30 - - - - D dilinin C++ dili ile karşılaştırılması - http://ddili.org/tanitim/fark_cpp.html - C++ dilindeki nesne yönelimli programlama ve şablonlar gibi bazı işlemlerin D dilinde nasıl yapıldıkları - Tanıtım - Mon, 06 Jul 2009 00:10 - - - - D diline genel bakış - http://ddili.org/tanitim/genel.html - D'nin ana özellikleri - Tanıtım - Sat, 04 Jul 2009 01:00 - - - - D dilinin C dili ile karşılaştırılması - http://ddili.org/tanitim/fark_c.html - C dilindeki bazı işlemlerin D dilinde nasıl yapıldıkları - Tanıtım - Sat, 04 Jul 2009 01:00 - - - - D Derleyicisi dmd'nin Code::Blocks'la kullanımı - http://ddili.org/kurulum/ - Digital Mars derleyicisi dmd'nin Code::Blocks geliştirme ortamına eklenmesi - Eğitsel - Tue, 30 Jun 2009 15:00 - - - - D Derleyicisi dmd'nin Windows'da kurulması - http://ddili.org/kurulum/ - Digital Mars derleyicisi dmd'nin Windows'da kurulması - Eğitsel - Tue, 30 Jun 2009 15:00 - - - - Kod örnekleri - http://ddili.org/ornek_kod/ - D'nin bazı özelliklerini gösteren küçük D programları. - Eğitsel - Sat, 27 Jun 2009 19:40 - - - - D Derleyicisi kurulumu - http://ddili.org/kurulum/ - Digital Mars derleyicisi dmd'nin ve GNU derleyicisi gdc'nin Linux ortamında kurulması. - Eğitsel - Mon, 22 Jun 2009 01:40 - - - - Ddili Forumu Açıldı - http://ddili.org/forum/ - D diliyle ilgili her türlü konuyu tartışabileceğimiz Ddili Forumu açıldı. - Haber - Sat, 20 Jun 2009 - - - - Andrei Alexandrescu'nun yeni makalesi: Neden D - http://ddili.org/makale/neden_d.html - C++'nın en büyük ustalarından olan Andrei Alexandrescu, şimdilerde enerjisini Walter Bright tarafından tasarlanmış olan D programlama dilini geliştirmeye harcıyor. Alexandrescu, çeşitli nedenlerle C++'ya eklenemeyen çoğu dil olanağının D'ye eklenmesine yardım ederek, bir anlamda D'yi C++'nın olmayı başaramadığı dil haline getiriyor. - Makale - Tue, 16 Jun 2009 - - - - Emacs'e d-mode eklemek - http://ddili.org/kurulum/emacs_d-mode.html - Emacs altında D programı yazarken işe yarayan d-mode modunun Emacs'e eklenmesi. - Eğitsel - Tue, 16 Jun 2009 - - - - D Programlama Dili sitesi ddili.org açıldı - http://ddili.org/ - Bütünüyle D programlama diline odaklı site ddili.org - Haber - Mon, 15 Jun 2009 - - - - diff --git a/ddili/src/sozluk.d b/ddili/src/sozluk.d deleted file mode 100644 index 1bb1962..0000000 --- a/ddili/src/sozluk.d +++ /dev/null @@ -1,36 +0,0 @@ -Ddoc - -$(SOZLUK_BODY) - -Macros: - SUBTITLE=D Programlama Dili Sözlüğü - - DESCRIPTION=ddili.org'da ve D.ershane'de kullanılan D terimleri sözlüğü - - KEYWORDS=d programlama dili sözlük - -CLASS=sozluk - -MAIN_TITLE=D.ershane Sözlüğü - -SUB_MAIN_TITLE=Ddili.org - -CONTAINER_ID=container_cozum - -HEADER_ID=header_cozum - -HEADER1_ID=header1_cozum - -HORIZNAV_CONTENT= - -VERTINAV_DIV= - -BREADCRUMBS_DIV= - -DERS_NAV_BAS= - -DERS_NAV_SON= - -FOOTER_DIV= - -HEADER_SECONDARY_DIV= diff --git a/ddili/src/sozluk.txt b/ddili/src/sozluk.txt deleted file mode 100644 index a64d1b0..0000000 --- a/ddili/src/sozluk.txt +++ /dev/null @@ -1,451 +0,0 @@ -acikca_elle_yapilan | açıkça elle yapılan | explicit | programcı tarafından açık olarak yapılan - -aciklama | açıklama satırı | comment | programı açıklamak için programın içine yazılan satır - -adres | adres | address | değişkenin (veya nesnenin) bellekte bulunduğu yer - -akim | akım | stream | nesnelerin art arda erişildiği giriş çıkış birimi - -algoritma | algoritma | algorithm | verilerin işlenme adımları, işlev - -alt_duzey | alt düzey | low level | donanıma yakın olanak - -alt_sinif | alt sınıf | subclass | başka sınıftan türetilen sınıf - -anahtar_sozcuk | anahtar sözcük | keyword | dilin kendisi için ayırmış olduğu ve iç olanakları için kullandığı sözcük - -aralik | aralık | range | belirli biçimde erişilen bir grup eleman - -arayuz | arayüz | interface | yapının, sınıfın, veya modülün sunduğu işlevler - -arttirma | arttırma | increment | değerini bir arttırmak - -atama | atama | assign | değişkene yeni bir değer vermek - -ayni_isimde_tanim_iceren | aynı isimde tanım içeren | eponymous | kendisiyle aynı isimde tanım içeren şablon - -azaltma | azaltma | decrement | değerini bir azaltmak - -baglam_degistirme | bağlam değiştirme | context switching | başka iş parçacığına geçilmesi - -baglanim | bağlanım | linkage | derlenen koddaki özgün ismi belirleyen kurallar vs. - -baglayici | bağlayıcı | linker | derleyicinin oluşturduğu program parçalarını bir araya getiren program - -bagli_liste | bağlı liste | linked list | her elemanı bir sonraki elemanı gösteren veri yapısı - -belirsiz_sayida_parametre | belirsiz sayıda parametre | variadic | aynı işlevi (veya şablonu) farklı sayıda parametre ile çağırabilme olanağı - -bellek_sizintisi | bellek sızıntısı | memory leak | artık kullanılmasa bile bellek bölgesinin geri verilmemesi - -betik | betik | script | terminal komutlarından oluşan program - -bayrak | bayrak | flag | bir işlemin veya sonucun geçerli olup olmadığını bildiren bit - -bayt | bayt | byte | 8 bitlik tür - -bayt_sirasi | bayt sırası | endianness | veriyi oluşturan baytların bellekte sıralanma düzeni - -bildirim | bildirim | declare | tanımını vermeden belirtmek - -birim_testi | birim testi | unit test | programın alt birimlerinin bağımsız olarak denetlenmeleri - -birlik | birlik | union | birden fazla değişkeni aynı bellek bölgesinde depolayan veri yapısı - -bit | bit | bit | 0 ve 1 değerlerini alabilen en temel bilgi birimi - -blok | blok | block | küme parantezleriyle gruplanmış ifadelerin tümü - -bom | BOM | BOM, byte order mark | dosyanın en başına yazılan Unicode kodlama belirteci - -buyuk_soncul | büyük soncul | big endian | değerin üst bitlerini oluşturan baytın bellekte önceki adreste bulunduğu işlemci mimarisi - -ctfe | CTFE | Compile Time Function Execution | derleme zamanında işlev işletme - -cagri_yigiti | çağrı yığıtı | call stack | belleğin kısa ömürlü değişkenler ve işlev çağrıları için kullanılan bölgesi - -calisma_ortami | çalışma ortamı | runtime | çalışma zamanında dil desteği veren ve her programa otomatik olarak eklenmiş olan program parçası - -cerceve | çerçeve | frame | işlev çağrısının yerel durumunu barındıran alan - -cikarsama | çıkarsama | deduction, inference | derleyicinin kendiliğinden anlaması - -cikis_kosulu | çıkış koşulu | postcondition | işlevin garanti ettiği sonuç - -cikti | çıktı | output | programın bilgi olarak ürettiği herşey - -cok_paradigmali | çok paradigmalı | multi-paradigm | çeşitli programlama yöntemlerini destekleyen - -cok_sekillilik | çok şekillilik | polymorphism | başka bir tür gibi davranabilmek - -coklu_gorev | çoklu görev | multi-tasking | birden fazla görevin etkin olması - -cokuzlu | çokuzlu | tuple | bir kaç parçanın diziye benzer biçimde bir araya gelmesinden oluşan yapı - -cokme | çökme | crash | programın hata ile sonlanması - -cop_toplayici | çöp toplayıcı | garbage collector | işi biten nesneleri sonlandıran düzenek - -deger | değer | value | ay adedi 12 gibi isimsiz bir büyüklük - -deger_turu | değer türü | value type | değer taşıyan tür - -degisken | değişken | variable | kavramları temsil eden veya sınıf nesnesine erişim sağlayan program yapısı - -degismez | değişmez | immutable | programın çalışması süresince kesinlikle değişmeyen - -derleyici | derleyici | compiler | programlama dili kodunu bilgisayarın anladığı makine koduna çeviren program - -deyim | deyim | statement | ifadelerin işletilmelerini ve sıralarını etkileyen program yapısı - -dilim | dilim | slice | başka bir dizinin bir bölümüne erişim sağlayan yapı - -dinamik | dinamik | dynamic | çalışma zamanında değişebilen - -dizgi | dizgi | string | "merhaba" gibi bir karakter dizisi - -dizi | dizi | array | elemanları yan yana duran ve indeksle erişilen topluluk - -doldurma_bayti | doldurma baytı | padding byte | değişkenleri hizalamak için aralarına gelen baytlar - -dongu | döngü | loop | tekrarlanan program yapısı - -dongu_acilimi | döngü açılımı | loop unrolling | döngü içeriğinin her eleman için art arda tekrarlanması - -donus_degeri | dönüş değeri | return value | işlevin üreterek döndürdüğü değer - -duyarlik | duyarlık | precision | sayının belirgin hane sayısı - -duzen | düzen | format | bilginin giriş ve çıkışta nasıl düzenlendiği - -eleman | eleman | element | topluluktaki verilerin her biri - -emekli | emekliye ayrılan | deprecated | hâlâ kullanılan ama yakında geçersiz olacak olanak - -emirli_programlama | emirli programlama | imperative programming | işlemlerin deyimler halinde adım adım belirlendikleri programlama yöntemi - -eniyilestirme | eniyileştirme | optimization | kodun daha hızlı çalışacak biçimde davranışı bozulmadan değiştirilmesi - -erisici | erişici | iterator | elemanlara erişim sağlayan yapı - -esleme_tablosu | eşleme tablosu | associative array | elemanlarına tamsayı olmayan indekslerle de erişilebilen veri yapısı (bir 'hash table' gerçekleştirmesi) - -es_zamanli | eş zamanlı programlama | concurrency | iş parçacıklarının birbirlerine bağımlı olarak işlemeleri - -etiket | etiket | label | kod satırlarına isimler vermeye yarayan olanak - -evrensel | evrensel | global | modül düzeyinde tanımlanmış - -fonksiyonel_programlama | fonksiyonel programlama | functional programming | yan etki üretmeme ilkesine dayalı programlama yöntemi - -gecici | geçici | temporary | bir işlem için geçici olarak oluşturulan ve yaşamı kısa süren değişken veya nesne - -gecisli_coklu_gorev | geçişli çoklu görev | preemptive multi-tasking | görevlerin bilinmeyen zamanlarda duraksatıldığı çoklu görev yöntemi - -gelistirme_ortami | geliştirme ortamı | IDE | program yazmayı kolaylaştıran program - -genel_erisim | genel erişim | public | herkese açık erişim - -gerceklestirme | gerçekleştirme | implementation | kodun oluşturulması - -giris_cikisa_bagli | giriş/çıkış'a bağlı | I/O bound | zamanının çoğunu giriş/çıkış işlemlerini bekleyerek geçiren - -giris_kosulu | giriş koşulu | precondition | işlevin gerektirdiği koşul - -gorev | görev | task | programın geri kalanıyla koşut işletilebilen işlem birimi - -gosterge | gösterge | pointer | bir değişkeni gösteren değişken - -hata | hata | exception | devam edilemeyen işlemden atılan nesne - -hata_atma | hata atma | throw exception | işlemin devam edilemeyeceği için sonlandırılması - -hata_ayiklama | hata ayıklama | debug | programın hatalarını bulma ve giderme - -hazir_deger | hazır değer | literal | kod içinde hazır olarak yazılan değerler - -hevesli | hevesli | eager | işlemlerin, ürettikleri sonuçların kullanılacaklarından emin olunmadan gerçekleştirilmeleri - -hizalama | hizalama birimi | alignment | bir türün değişkenlerinin bulunabileceği adres adımı - -ic_olanak | iç olanak | core feature | dilin kütüphane gerektirmeyen bir olanağı - -ic_tanim | iç tanım | nested definition | iç kapsamda tanımlanmış olan - -ifade | ifade | expression | programın değer oluşturan veya yan etki üreten bir bölümü - -ikili_sistem | ikili sayı sistemi | binary system | iki rakamdan oluşan sayı sistemi - -ikincil_hata | ikincil hata | collateral exception | hata atılması sırasında atılan başka hata - -ilinti | ilinti | binding | yabancı kütüphanenin olanaklarını D söz dizimiyle bildiren dosya - -ilklemek | ilklemek | initialize | ilk değerini vermek - -indeks | indeks | index | topluluk elemanlarına erişmek için kullanılan bilgi - -infinity | infinity | infinity | sonsuzluk - -isim_alani | isim alanı | name scope | ismin geçerli olduğu kapsam - -isim_gizleme | isim gizleme | name hiding | üst sınıfın aynı isimli üyelerinin alt sınıftakiler tarafından gizlenmeleri - -isimli_ilklendirici | isimli ilklendirici | designated initializer | yapı üyelerinin isimle ilklendirilmeleri - -isimsiz_islev | isimsiz işlev | lambda | çoğunlukla işlevlere parametre değeri olarak gönderilen kısa ve isimsiz işlev - -istemci | istemci | client | sunucu programın hizmetlerinden yararlanan program - -isaretli_tur | işaretli tür | signed type | eksi ve artı değer alabilen tür - -isaretsiz_tur | işaretsiz tür | unsigned type | yalnızca artı değer alabilen tür - -isbirlikli_coklu_gorev | işbirlikli çoklu görev | cooperative multi-tasking | görevlerin kendilerini duraksattıkları çoklu görev yöntemi - -islec | işleç | operator | bir veya daha fazla ifadeyle iş yapan özel işaret (+, -, =, [], vs.) - -islec_birlesimi | işleç birleşimi | operator associativity | aynı önceliğe sahip işleçlerden soldakinin mi sağdakinin mi önce işletileceği - -islev | işlev | function | programdaki bir kaç adımı bir araya getiren program parçası - -is_parcacigi | iş parçacığı | thread | işletim sisteminin program işletme birimi - -kalan | kalan | modulus | bölme işleminin kalan değeri - -kalitim | kalıtım | inheritance | başka bir türün üyelerini türeme yoluyla edinmek - -kapama | kapama | closure | işlemi ve işlediği kapsamı bir arada saklayan program yapısı - -kapsam | kapsam | scope | küme parantezleriyle belirlenen bir alan - -karakter | karakter | character | 'a', '€', '\n', gibi en alt düzey metin parçası - -karakter_kodlamasi | karakter kodlaması | character encoding | karakter kodlarının ifade edilme yöntemi - -katma | katma | mixin | program içine otomatik olarak kod yerleştirme - -kayan_noktali | kayan noktalı sayı | floating point | kesirli sayı - -kaynak_dosya | kaynak dosya | source file | programcının yazdığı kodu içeren dosya - -kesintisiz_islem | kesintisiz işlem | atomic operation | bir iş parçacığı tarafından kesintiye uğramadan işletilen işlem - -kirpilma | kırpılma | truncate | sayının virgülden sonrasının kaybedilmesi - -kisitlama | kısıtlama | constraint | şablon parametrelerinin uyması gereken koşulların belirlenmesi - -kilitsiz_veri_yapisi | kilitsiz veri yapısı | lock-free data structure | kilit nesnesi gerektirmeden eş zamanlı programlamada doğru işleyen veri yapısı - -klasor | klasör | directory | dosyaları barındıran dosya sistemi yapısı, "dizin" - -kod_birimi | kod birimi | code unit | UTF kodlaması kod değeri - -kod_ici_islev | kod içi işlev | inline function | işlevin çağrılması yerine içeriğinin çağrıldığı noktaya yerleştirilmesi - -kod_noktasi | kod noktası | code point | Unicode'un tanımlamış olduğu harf, im, vs. - -kod_sismesi | kod şişmesi | code bloat | şablon için çok sayıda kod üretilmesi - -kod_tablosu | kod tablosu | code page | 127'den büyük karakter değerlerinin bir dünya dili için tanımlanmaları - -kod_uretmeli | kod üretmeli | generative | kod üreten kodlar kullanan - -kontrol_karakteri | kontrol karakteri | control character | yeni satır açan $(C '\n'), yatay sekme karakteri $(C '\t'), gibi özel karakterler - -kopya_sonrasi | kopya sonrası | post blit | üyelerin kopyalanmalarından sonraki işlemler - -kopyalama | kopyalama | copy construct | nesneyi başka bir nesnenin kopyası olarak kurmak - -korumali_birlik | korumalı birlik | discriminated union | yalnızca geçerli üyesine eriştiren birlik - -korumali_erisim | korumalı erişim | protected | belirli ölçüde korumalı erişim - -kosut_islemler | koşut işlemler | parallelization | bağımsız işlemlerin aynı anda işletilmeleri - -kurma | kurma | construct | yapı veya sınıf nesnesini kullanılabilir duruma getirmek - -kurucu_islev | kurucu işlev | constructor | nesneyi kuran işlev - -kucuk_soncul | küçük soncul | little endian | değerin alt bitlerini oluşturan baytın bellekte önceki adreste bulunduğu işlemci mimarisi - -kutuphane | kütüphane | library | belirli bir konuda çözüm getiren tür tanımlarının ve işlevlerin bir araya gelmesi - -makine_kodu | makine kodu | machine code | mikro işlemcinin dili - -mantiksal_ifade | mantıksal ifade | logical expression | değeri false veya true olan ifade - -mesajlasma | mesajlaşma | message passing | iş parçacıklarının birbirlerine mesaj göndermeleri - -metin_duzenleyici | metin düzenleyici | text editor | metin yazmaya yarayan program - -mikro_islemci | mikro işlemci | CPU | bilgisayarın beyni - -mikro_islemciye_bagli | mikro işlemciye bağlı | CPU bound | zamanının çoğunu mikro işlemciyi işleterek geçiren - -mikro_islemci_cekirdegi | mikro işlemci çekirdeği | CPU core | başlı başına mikro işlemci olarak kullanılabilen işlemci birimi - -modul | modül | module | programın veya kütüphanenin işlev ve tür tanımlarından oluşan bir alt birimi - -mutlak_degismez | mutlak değişmez | invariant | nesnenin tutarlılığı açısından her zaman için doğru olan - -nesne | nesne | object | belirli bir sınıf veya yapı türünden olan değişken - -nesne_yonelimli | nesne yönelimli | object oriented | işlemlerin ilgili oldukları tür üzerinde tanımlanmaları - -nitelik | nitelik | property, attribute | bir türün veya nesnenin bir özelliği - -on_altili_sistem | on altılı sayı sistemi | hexadecimal system | on altı rakamdan oluşan sayı sistemi - -ortak_islev | ortak işlev | coroutine | aynı zamanda işletilen görevlerden birisi - -ortam_degiskeni | ortam değişkeni | environment variable | programı başlatan ortamın sunduğu PATH gibi değişken - -otomatik | otomatik | implicit | derleyici tarafından otomatik olarak yapılan - -onbellek | ön bellek | cache | hızlı veri veya kod erişimi için kullanılan mikro işlemci iç belleği - -onceki_degerli_arttirma | önceki değerli arttırma | post-increment | sayıyı arttıran ama önceki değerini üreten işleç - -onceki_degerli_azaltma | önceki değerli azaltma | post-decrement | sayıyı azaltan ama önceki değerini üreten işleç - -oncelik | öncelik | precedence | işleçlerin hangi sırada işletilecekleri - -ordek_tipleme | ördek tipleme | duck typing | türün değil, davranışın önemli olması - -ozel_erisim | özel erişim | private | başkalarına kapalı erişim - -ozelleme | özelleme | specialization | şablonun bir özel tanımı - -ozgun_isim_uretme | özgün isim üretme | name mangling | bağlayıcı tanıyabilsin diye programdaki isimlerin özgünleştirilmeleri - -ozyineleme | özyineleme | recursion | bir işlevin doğrudan veya dolaylı olarak kendisini çağırması - -paket | paket | package | aynı klasörde bulunan modüller - -parametre | parametre | parameter | işleve işini yapması için verilen bilgi - -parametre_degeri | parametre değeri | argument | işleve parametre olarak verilen bir değer - -phobos | Phobos | Phobos | D dilinin standart kütüphanesi - -program | program | program | bilgisayara yapacağı işleri bildiren bir dizi ifade - -referans | referans | reference | asıl nesneye, onun takma ismi gibi erişim sağlayan program yapısı - -referans_turu | referans türü | reference type | başka bir nesneye erişim sağlayan tür - -sabit | sabit | const | bir bağlamda değiştirilmeyen - -sag_deger | sağ değer | rvalue | adresi alınamayan değer - -sanal_islev | sanal işlev | virtual function | tanımı alt sınıfta değiştirilebilen işlev - -sanal_islev_tablosu | sanal işlev tablosu | virtual function table, vtbl | sınıfın sanal işlev göstergelerinden oluşan tablo - -sanal_sayi | sanal sayı | imaginary number | salt sanal değerden oluşan karmaşık sayı - -sarma | sarma | encapsulation | üyelere dışarıdan erişimi kısıtlamak - -sekme | sekme | tab | çıktı düzeni için kullanılan hayali sütunlar - -siga | sığa | capacity | yeni elemanlar için önceden ayrılmış olan yer - -siraduzen | sıradüzen | hierarchy | sınıfların türeyerek oluşturdukları aile ağacı - -sirasizlik | sırasızlık | unordered | sıra ilişkisi olmama durumu - -sinif | sınıf | class | kendi üzerinde kullanılan işlevleri de tanımlayan veri yapısı - -sihirli_sabit | sihirli sabit | magic constant | ne anlama geldiği anlaşılmayan sabit değer - -sol_deger | sol değer | lvalue | adresi alınabilen değer - -soyut | soyut | abstract | somut gerçekleştirmesi verilmemiş olan - -soz_dizimi | söz dizimi | syntax | dilin yazım ile ilgili olan kuralları - -standart_cikis | standart çıkış | standard output | program çıktısının normalde gönderildiği akım - -standart_giris | standart giriş | standard input | program girişinin normalde okunduğu akım - -statik | statik | static | derleme zamanında belirli olan - -sonek_islec | sonek işleç | postfix operator | $(C i++)'da olduğu gibi ifadenin sonuna eklenen işleç - -sonlandirma | sonlandırma | destruct | nesneyi kullanımdan kaldırırken gereken işlemleri yapmak - -sonlandirici_islev | sonlandırıcı işlev | destructor | nesneyi sonlandıran işlev - -sozlesmeli_programlama | sözleşmeli programlama | contract programming | işlevlerin giriş çıkış koşullarını ve nesnelerin tutarlılığını denetleyen dil olanağı - -sunucu | sunucu | server | başka programlara hizmet eden program - -surum | sürüm | version | programın, olanaklarına göre farklar içeren hali - -sablon | şablon | template | derleyicinin örneğin 'türden bağımsız programlama' için kod üretme düzeneği - -takma_isim | takma isim | alias | var olan bir olanağın başka bir ismi - -tanim | tanım | definition | bir ismin neyi ifade ettiğinin belirtilmesi - -tanimsiz_davranis | tanımsız davranış | undefined behavior | programın ne yapacağının dil tarafından tanımlanmamış olması - -tasma | taşma; üstten veya alttan | overflow veya underflow | değerin bir türe sığamayacak kadar büyük veya küçük olması - -tasima | taşıma | move | bir yerden bir yere kopyalamadan aktarma - -tembel_degerlendirme | tembel değerlendirme | lazy evaluation | işlemlerin gerçekten gerekene kadar geciktirilmesi - -temsilci | temsilci | delegate | oluşturulduğu ortamdaki değişkenlere erişebilen isimsiz işlev - -topluluk | topluluk | container | aynı türden birden fazla veriyi bir araya getiren veri yapısı - -tur_donusumu | tür dönüşümü | type conversion | bir değeri kullanarak başka bir türden değer elde etmek - -tur_nitelendirici | tür nitelendirici | type qualifier | const, immutable, shared, ve inout - -turden_bagimsiz | türden bağımsız | generic | belirli türlere bağlı olmayan veri yapıları ve algoritmalar - -turetmek | türetmek | inherit | bir sınıfı başka sınıfın alt türü olarak tanımlamak - -uc_birim | uç birim | terminal | bilgisayar sistemlerinin kullanıcıyla etkileşen giriş/çıkış birimi; "konsol", "komut satırı", "cmd penceresi", "DOS ekranı", vs. - -uclu_islec | üçlü işleç | ternary operator | ifadenin değerine göre ya birinci, ya da ikinci değeri üreten işleç - -ust_duzey | üst düzey | high level | donanımdan bağımsız kavramları temsil etmeye elverişli - -ust_sinif | üst sınıf | super class | kendisinden sınıf türetilen sınıf - -uye | üye | member | yapı veya sınıfın özel değişkenleri ve nesneleri - -uye_islev | üye işlev | member function | yapı veya sınıfın kendi tanımladığı işlemleri - -varsayilan | varsayılan | default | özellikle belirtilmediğinde kullanılan - -vekil | vekil | proxy | başka nesne yerine kullanılan nesne - -veri_yapilari | veri yapıları | data structures | verilerin bilgisayar biliminin tanımladığı biçimde saklanmaları ve işlenmeleri - -ve | ve (mantıksal) | and (logical) | iki ifadenin her ikisinin doğru olduğunu denetleyen işleç - -veya | veya (mantıksal) | or (logical) | iki ifadenin en az birisinin doğru olduğunu denetleyen işleç - -ya_da | ya da (mantıksal) | xor (exclusive or) (logical) | iki ifadenin tekinin doğru olduğunu denetleyen işleç - -yan_etki | yan etki | side effect | bir ifadenin, ürettiği değer dışındaki etkisi - -yapi | yapı | struct | başka verileri bir araya getiren veri yapısı - -yaris_hali | yarış hali | race condition | verinin yazılma ve okunma sırasının kesin olmaması - -yasam_sureci | yaşam süreci | object lifetime | bir değişkenin veya nesnenin tanımlanmasından işinin bitmesine kadar geçen süre - -yazmac | yazmaç | register | mikro işlemcinin en temel iç depolama ve işlem birimi - -yeniden_tanimlama | yeniden tanımlama | override | üye işlevin alt sınıf tarafından yeniden tanımlanması - -yigit_cozulmesi | yığıt çözülmesi | stack unwinding | atılan hata nedeniyle çerçevelerin çağrı yığıtından çıkartılmaları - -yukleme | yükleme | overloading | aynı isimde birden çok işlev tanımlama - -zaman_uyumsuz | zaman uyumsuz | asynchronous | önceden bilinmeyen zaman aralıklarında gerçekleşen diff --git a/ddili/src/sozlukmaker.d b/ddili/src/sozlukmaker.d deleted file mode 100644 index f3d1642..0000000 --- a/ddili/src/sozlukmaker.d +++ /dev/null @@ -1,135 +0,0 @@ -/** - * This program processes the sozluk.txt file to generate: - * - * 1) sozluk.ddoc - * - * 2) sozluk_body.ddoc - * - */ - -import std.stdio; -import std.format; -import std.string; -import std.exception; -import std.algorithm; -import std.array; -import std.regex; - -import alphabet; - -struct Entry -{ - string macroName; - string word; - string otherLanguage; - string definition; -} - -string parseRequiredPart(R)(ref R parts) -{ - enforce(!parts.empty); - - const part = parts.front.idup; - parts.popFront(); - - return part; -} - -Entry parseEntry(const(char)[] line) -{ - auto parts = line - .splitter('|') - .map!strip; - - Entry entry; - - try { - entry.macroName = parseRequiredPart(parts); - entry.word = parseRequiredPart(parts); - entry.otherLanguage = parseRequiredPart(parts); - entry.definition = parseRequiredPart(parts); - - enforce (parts.empty); - - } catch (Exception exc) { - throw new Exception( - format("Invalid sozluk line: %s", line)); - } - - return entry; -} - -Entry languageSwapped(Entry entry) -{ - return Entry( - entry.macroName, entry.otherLanguage, entry.word, entry.definition); -} - -void main(string[] args) -{ - const alphabetName = args[1]; /* "english", "turkish", etc. */ - Alphabet alphabet = makeAlphabet(alphabetName); - - auto macroEntries = File("sozluk.txt", "r") - .byLine - .filter!(l => !l.empty) - .map!strip - .map!parseEntry - .array; - - auto sozlukEntries = macroEntries - .map!(e => (e.word == e.otherLanguage - ? [ e ] - : [ e, e.languageSwapped ])) - .joiner - .array - .sort!((l, r) => - indexSectionOrder(l.word, r.word, alphabet)); - - writeln("Generating sozluk.ddoc"); - auto sozluk = File("sozluk.ddoc", "w"); - - foreach (entry; macroEntries) { - if (entry.word.front == 'ı') { - throw new Exception( - format("Limitation: Current framework will sort this entry " ~ - " among the 'i's: %s", entry)); - } - - sozluk.writefln( - `%s =
    $(B %s:) [%s], %s
    `, - entry.macroName, entry.word, entry.otherLanguage, entry.definition); - } - - writeln("Generating sozluk_body.ddoc"); - auto sozluk_body = File("sozluk_body.ddoc", "w"); - sozluk_body.write("SOZLUK_BODY="); - - dchar lastInitial = ' '; - - foreach (entry; sozlukEntries) { - dchar initial = alphabet.toUpper(initialLetter(entry.word)); - if (initial == 'I') { - /* HACK: 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.) */ - initial = 'İ'; - } - - if (lastInitial != initial) { - if (lastInitial != ' ') { - sozluk_body.writeln(`
`); - } - - sozluk_body.writefln( - `
%s
`, initial); - sozluk_body.writeln(`
    `); - lastInitial = initial; - } - - sozluk_body.writefln(`
  • $(B %s:) [%s], %s
  • `, - entry.word, entry.otherLanguage, entry.definition); - } - - sozluk_body.writeln(`
`); -} diff --git a/ddili/src/style.css b/ddili/src/style.css deleted file mode 100644 index 2e5d380..0000000 --- a/ddili/src/style.css +++ /dev/null @@ -1,534 +0,0 @@ -/* reset */ -html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td -{ -margin: 0; -padding: 0; -border: 0; -outline: 0; -font-size: 100%; -vertical-align: baseline; -background: transparent; -} - -table, tbody, tfoot, thead, tr, th, td, caption, pre { - margin: 1em 1em 1em 1em; - border: dotted 1px #c0c0c0; - outline: 1; - font-family: monospace; -} - -th, caption { - font-family: sans-serif; - padding: 0 .1em 0 .1em; - margin-top: 0; -} - -tr, td { - font-size: .9em; -} - -td.serif { - font-family: serif; -} - -table.full { - width: auto; -} - -table.wide { - width: 95%; -} - -table.medium { - width: 50%; -} - -table.narrow { - width: 30%; -} - -table.centered { - text-align:center; -} - -caption { - border: none; - font-weight:bold; -} - -sup { - font-size:.65em; - vertical-align:top; -} - -sub { - font-size:.65em; - vertical-align:bottom; -} - -hr.thin { - color: #000000; - background-color: #000000; - height: 1px; - border: 0; - width: 100%; -} - -body -{ - line-height: 1.1; -} - -ol, ul -{ - font-family: serif; - margin: 1em 0 .75em 1.5em; -} - -/* standart baglanti renkleri */ -a:link {color: blue;} -a:visited {color: purple;} -a:hover {color: red;} -a:active {color: purple;} - -a:link .d_inline{color: blue;} -a:visited .d_inline {color: purple;} -a:hover .d_inline {color: red;} -a:active .d_inline {color: purple;} - -a { - color:#000000; - text-decoration:none; - } - -body { - background-color:#cccccc; - font-size: 1em; - } - -#container { - max-width:55em; - height:auto; - margin:auto; - } - -#container_cozum { - max-width:55em; - height:auto; - margin:auto; - } - -#header { - font-family: sans-serif; - height:5em; - background-color:#222266; -} - -#header_ders { - font-family: sans-serif; - height:5em; - background-color:#222266; -} - -#header_cozum { - font-family: sans-serif; - width:74%; - height:4em; - background-color:#226622; - float:right; -} - -#header1 { - color:#ffffff; - font-family: sans-serif; - font-size:1.75em; - width:70%; - margin: .5em 0 0 1em; - float:left; - } - -#header1_cozum { - color:#ffffff; - font-size:2em; - width:550px; - margin: .5em 0 0 1em; - float:left; - } - -#headersecondary { - color:#999999; - margin: .5em .5em 0 0; - float:right; - width:25%; - text-align:right; - line-height:160%; -} - - -#headersecondary a{ - text-decoration:none; - color:#e0e0e0; - margin: 0 0 0 .5em; - } - -#headersecondary a:hover { - border-bottom: 3px solid #999999; - } - -#headersecondary a:active { - color:#e0e0e0; - border-bottom: 3px solid #CCCCCC; - } - -#vertinav { - font-family: sans-serif; - background-color:#f0f0f0; - width:25%; - float:left; - } - -#vertinav ul { - font-family: sans-serif; - list-style: none; - display:inline; - } -#vertinav li a{ - font-family: sans-serif; - margin:0 0 0 12px; - color:#333333; - text-decoration:none; - } - -#vertinav li a:link { - font-family: sans-serif; - color: blue; - } - -#vertinav li a:visited { - font-family: sans-serif; - color: purple; - } - -#vertinav li a:hover { - font-family: sans-serif; - color: red; - } - -#vertinav li a:active { - font-family: sans-serif; - color: purple; - } - -.vertinavheader { - color:#000000; - margin:0 0 -.5em .5em; - } - -#vertinavcontent { - font-family: sans-serif; - margin-left:16px; - color:#666666; -} - -#content { - background-color:#fafafa; - float:right; - width:74%; - line-height:1.2; - } - -#resume_content { - font-family: serif; - background-color:#fcfcfc; - float:right; - width:90%; - line-height:1.2; - } - -#resume_content li { - margin: .2em 0 0 1em; - } - -#resume_content ol, ul { - margin: .5em 1em 1em 1em; - } - - -#content li { - margin: 0.5em 0 0 1em; - } - -#breadcrumbs { - background-color:#e0e0e0; - font-size:.9em; - } - -#resume_breadcrumbs { - float:right; - width:90%; - background-color:#e0e0e0; - font-size:.9em; - } - -#breadcrumbs_ders { - font-family: sans-serif; - float:left; - font-size:.9em; - } - -#ders_nav_bas { - font-family: sans-serif; - float:right; - font-size:.9em; - text-align:right; - margin-right:1em; - } - -#ders_nav_son { - font-family: sans-serif; - float:right; - font-size:.9em; - text-align:right; - margin-right:1em; - } - -#footer { - float:right; - width:100px; - font-size:.5em; - } - -h1,h2,h3,h4,h5,h6 { - font-family: sans-serif; - font-weight:bold; - color:#000033; - margin: 1.5em .5em .5em .5em; - } - -h1 { - font-size: 3em; - } - -h2 { - font-size:2.5em; - } - -h3 { - font-size:2em; - } - -h4 { - font-size:1.5em; - } - -h5 { - font-size:1.1em; - } - -h5.fark { - margin-top: .5em; -} - -h6 { - font-size:1em; - } - -p { - color:#000000; - font-family: serif; - margin: 0.75em 1em 0.75em 1em; - } - -li p { - margin-left: 0em; -} - -/* Style applied to preformated text used to show examples. */ -pre.d_code, pre.c_code, div.quote, pre.mono, pre.mono_nobold, pre.shell, pre.shell_small -{ - background-color: #ffffff; - border: dotted 1px #c0c0c0; - padding: .5em .5em .5em .5em; - margin: .5em 1em 0.75em 1em; - overflow: auto; - max-height: 40em; -} - -li pre.d_code, li pre.c_code, li div.quote, li pre.mono, li pre.mono_nobold, li pre.shell, li pre.shell_small { - margin-left: 0em; - } - -li li pre.d_code, li li pre.c_code, li li div.quote, li li pre.mono, li li pre.mono_nobold, li li pre.shell, li li pre.shell_small { - margin-right: 0em; -} - -pre.mono_nobold -{ - font-weight:bold; - background-color: #e0e0e0; -} - -pre.c_code -{ - background-color: #f0f0e0; -} - -/* Style applied to preformated text for shell commands. */ -pre.shell, pre.shell_small -{ - font-weight:bold; - background-color: #e0e0e0; -} - -pre.shell_small -{ - font-size: 1em; -} - -.hilite { - background-color:#ffff88; -} - -.shell_note { - color: blue; -} - -.shell_note_wrong, .code_note_wrong { - color: red; -} - -.shell_observed { - color: #404040; -} - -.unordered_false { - color: darkred; -} - -.unordered_true { - color: green; -} - -.unordered_no { - color: red; -} - -.unordered_yes { - color: gray; -} - -div.quote { - background-color:#e0e0e0; -} - -div.haber { - background-color:#ffffff; -} - -/* Elements of D source code text */ -.d_comment { color: green; } -.d_string { color: red; } -p .d_string { - font-family: monospace; -} -ol .d_string { - font-family: monospace; -} -ul .d_string { - font-family: monospace; -} - -.d_keyword { color: blue; } -.d_psymbol { text-decoration: underline; } -.d_param { font-style: italic; } - -.d_inline { - font-family: monospace; - font-weight:bold; - color:#002222; -} - -h4 .d_inline { font-size: 1em; } -h5 .d_inline { font-size: 1em; } -h6 .d_inline { font-size: 1em; } - -.d_inline_index { - font-size: .7em; - color:#888888; -} - -.d_hata { - background-color:#ff4040; - color:#000000; -} - -.steps li{ - margin:0 0 1.5em .5em; - text-decoration:none; - } - -ul.fark { - margin-left:4em; -} - -.on_ust { - width:100%; -} - -img.d_harfi { - float:right; - margin:1em 1em .5em .5em ; -} - -.d_harfi_yan { - float:left; - margin:1em 1em .5em .5em ; -} - -.main_page_content { - float:left; - width:100%; - margin:auto; -} - -div.mini_sozluk_sozcuk { - margin-bottom: 4px; -} - -div.sozcuk { - margin: 10px 10px 10px 10px; -} - -.mini_sozluk { - background-color:#fcf4f4; - border: dotted 1px #c0c0c0; - font-size: .8em; - margin: 0 8px 0 8px; - padding: 2px; -} - -.butun_sozluk { - font-size:.8em; - margin: 0 4px 0 8px; - text-align:right; - } - -.foreword_by { - font-size: .75em; - font-weight:normal; -} - -ul.index_section { - list-style-type: none; - padding: 0 0 0 2em; - text-indent: -2em; - line-height: 1; -} - -div.web_index_section { - margin-left: 2em; -} - -h5.web_index_section { - margin-left: -1em; -} diff --git a/ddili/src/sunum/2012_AliCehreli_Cpp11_D.pdf b/ddili/src/sunum/2012_AliCehreli_Cpp11_D.pdf deleted file mode 100644 index a3671ad..0000000 Binary files a/ddili/src/sunum/2012_AliCehreli_Cpp11_D.pdf and /dev/null differ diff --git a/ddili/src/sunum/2012_AliCehreli_D_Tanitim.pdf b/ddili/src/sunum/2012_AliCehreli_D_Tanitim.pdf deleted file mode 100644 index 7938b79..0000000 Binary files a/ddili/src/sunum/2012_AliCehreli_D_Tanitim.pdf and /dev/null differ diff --git a/ddili/src/sunum/2012_AliCehreli_Kosut_Islemler.pdf b/ddili/src/sunum/2012_AliCehreli_Kosut_Islemler.pdf deleted file mode 100644 index 4e60606..0000000 Binary files a/ddili/src/sunum/2012_AliCehreli_Kosut_Islemler.pdf and /dev/null differ diff --git a/ddili/src/sunum/2012_SalihDincer_Diziler_Dilimler.pdf b/ddili/src/sunum/2012_SalihDincer_Diziler_Dilimler.pdf deleted file mode 100644 index 5e8bf21..0000000 Binary files a/ddili/src/sunum/2012_SalihDincer_Diziler_Dilimler.pdf and /dev/null differ diff --git a/ddili/src/sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf b/ddili/src/sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf deleted file mode 100644 index 834aaca..0000000 Binary files a/ddili/src/sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf and /dev/null differ diff --git a/ddili/src/sunum/breadcrumbs.ddoc b/ddili/src/sunum/breadcrumbs.ddoc deleted file mode 100644 index 7feebc6..0000000 --- a/ddili/src/sunum/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /, Ana Sayfa) > Sunum - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) > $(LINK2 /sunum/, Sunum) diff --git a/ddili/src/sunum/index.d b/ddili/src/sunum/index.d deleted file mode 100644 index b146101..0000000 --- a/ddili/src/sunum/index.d +++ /dev/null @@ -1,66 +0,0 @@ -Ddoc - -$(H4 D Diliyle İlgili Sunumlar) - -$(P $(I Can Alpay Çiftçi)) - -$(P -D programlama dili ile ilgilenen insanların sayısının artması, bu insanları "nasıl D'yi daha iyi anlatırız" konusunda düşünmeye ve fikir alış-verişinde bulunmaya sevk etmiştir. -) - -$(P -Bu fikirler arasında mutlak olarak en çok beğenilen; insanlar ile konuşarak insanlara daha hazırlıklı bir biçimde D anlatılabilecek bir imkan tanıyan $(B sunum)dur. -) - -$(P -Sunum sayesinde herkes iyi bildiklerini başkalarıyla paylaşabilecek ve başkalarından bir şeyler öğrenebilecektir. Böylece gönüllülük esasına dayanan bir topluluk projesi meydana gelmiştir. -) - - -$(P -İlk filizini incelemek için: -) - -$(UL - -$(LI $(LINK2 /sunum/merhaba_2012.html, D ve C++, 2012, Ankara) -) - -$(P -Bu sunum D'nin temel özelliklerini, C++11'in C++'a getirdiklerini ve bunun D karşılıklarını anlatır. Genel olarak anlatılanlar: -) - -$(UL -$(LI $(LINK2 /sunum/2012_AliCehreli_D_Tanitim.pdf, $(IMG pdficon_small.gif) D Programlama Dili)) -$(LI $(LINK2 /sunum/2012_SalihDincer_Diziler_Dilimler.pdf, $(IMG pdficon_small.gif) Dizi, Dilim ve Şablonlar)) -$(LI $(LINK2 /sunum/2012_ZaferCelenk_D_ile_Guvenli_Programlar.pdf, $(IMG pdficon_small.gif) Güvenli Programlar)) -$(LI $(LINK2 /sunum/2012_AliCehreli_Kosut_Islemler.pdf, $(IMG pdficon_small.gif) Koşut İşlemler ve Eş Zamanlı Programlama (taslak))) -$(LI Diziler, filtreler, rasgelelik, yazın dilinin matematik ve programlama dili ile ilişkisi) -$(LI D'nin ve dmd'nin Gelişimi) -$(LI $(LINK2 /sunum/2012_AliCehreli_Cpp11_D.pdf, $(IMG pdficon_small.gif) C++11'in Bazı Yenilikleri ve D'deki Karşılıkları)) -) - -$(LI $(LINK2 /sunum/sonrasi.html, D Sunumundan Sonrası) -) - -$(P -Bir sonraki sunuma nasıl katkı verilebileceğinin veya nasıl katılabileceğinin ön planlarını buradan öğrenebilirsiniz. -) - -$(LI $(LINK2 /sunum/ingilizce_sunumlar.html, D ile İlgili İngilizce Sunumlar) -) - -$(P -Tabii ki sunum fikrini ilk biz bulmadığımız gibi ilk gerçekleştirenler de biz değiliz. Türkçe olmayan sunumlar için buraya bakabilirsiniz. -) - -) - -Macros: - SUBTITLE=Sunumlar - - DESCRIPTION=D programlama diliyle ilgili ilginç ve önemli sunumlar - - KEYWORDS=d programlama dili makale belge tanıtım sunum - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/sunum/ingilizce_sunumlar.d b/ddili/src/sunum/ingilizce_sunumlar.d deleted file mode 100644 index be91196..0000000 --- a/ddili/src/sunum/ingilizce_sunumlar.d +++ /dev/null @@ -1,68 +0,0 @@ -Ddoc - -$(H4 İngilizce Sunumlar) - -$(P $(I Can Alpay Çiftçi)) - -$(P -D dilini ve Türkiye'de bu dile Türkçe destek veren ddili.org'u tanıtmak için atılan fikir olan sunum fikri tabii ki ilk bizim tarafımızdan bulunmamıştı. -) - - -$(P -Daha çok Amerika'da olmak üzere Microsoft ve Google gibi şirketlerin ve ACCU derneğinin seminer merkezlerinde gerçekleşen D programlama dili ile ilgili seminerler var. Biz de buradan o seminerleri duyurmaya ve varsa video kayıtlarını göstermeye çalışıyoruz. -) - -$(UL - -$(LI -Walter Bright'ın "The D Programming Language" sunumu) - -$(UL - -$(LI -$(LINK2 http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/The-D-Programming-Language,Video izle) -) - -$(LI -$(LINK2 /forum/thread/782, Forumda Tartış) -) - -) - -$(LI Andrei Alexandrescu'nun "Generic Programming Galore Using D" sunumu) - -$(UL - -$(LI -$(LINK2 http://www.infoq.com/presentations/Generic-Programming-Galore-Using-D,Video izle) -) - -$(LI -$(LINK2 /forum/thread/781, Forumda Tartış) -) - -) - -$(LI Andrei Alexandrescu'nun "Three Unlikely Successful Features of D" sunumu) - -$(UL - -$(LI -$(LINK2 http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2012/Three-Unlikely-Successful-Features-of-D,Video izle) -) - -$(LI -$(LINK2 /forum/thread/778, Forumda Tartış) -) -) -) - -Macros: - SUBTITLE=D ile İlgili İngilizce Sunumlar ) - - DESCRIPTION=D programlama diliyle ilgili ilginç ve önemli ingilizce sunumlar - - KEYWORDS=d programlama dili makale belge tanıtım sunum ingilizce english - - BREADCRUMBS=$(BREADCRUMBS_FULL) diff --git a/ddili/src/sunum/merhaba_2012.d b/ddili/src/sunum/merhaba_2012.d deleted file mode 100644 index a1bb7be..0000000 --- a/ddili/src/sunum/merhaba_2012.d +++ /dev/null @@ -1,120 +0,0 @@ -Ddoc - -$(CENTER -$(H4 D ve C++11 Konferansı) - -$(H5 30 Haziran 2012, 12:30, Tütev, Ankara) - -$(P -$(I Katılım herkese açık ve ücretsizdir.) -) - -$(H6 Konuşmacılar) - -$(P -Mert Ataol $(BR) -Ali Çehreli $(BR) -Zafer Çelenk $(BR) -Can Alpay Çiftçi $(BR) -Salih Dinçer $(BR) -) -) - -$(H5 İki Modern Dil: D ve C++) - -$(P -D, köklü dillerin tecrübelerini ve modern dillerin yeniliklerini harmanlayarak bir araya getirmiş bir dil. Sistem dillerinin gücünü kullanmaya izin verirken, modern dillerin faydalı olanaklarını da kullanımınıza sunar. Tamamen özgür ruhludur ve tüm kodlarına rahatlıkla ulaşıp inceleyebilirsiniz. Öğrenmek istediğinizde hemen indirip okumaya başlayabileceğiniz ücretsiz ve tamamen Türkçe bir e-kitaba sahiptir. Yardıma her an hazır ve sorduğunuz tüm sorulara cevap verecek düzeyde kişilerden oluşan topluluğa sahiptir. Sizleri tüm bu özellikleri bünyesinde barındıran D dili ile tanışmaya çağırıyoruz. -) - -$(P -Sistem dili denilince ilk akla gelen isim tabi ki C ve C++ dilleridir. Windows ve Linux gibi sistemlerin temel taşı, günümüzde hala geçerliliğini koruyan bir dil. C++ hakkında bilgilerinizi tazelemek ya da son eklenen yenilikler konusunda bilgi sahibi olmak, C++ hakkında bilgiye sahip insanlarla tanışmak ve fikir alışverişinde bulunmak istemez misiniz? O zaman sizi de seminerimize bekliyoruz. -) - -$(P -Amacımız sadece bir şeyler anlatmak değil. Biz hep beraber konuşup bilgi alışverişinde bulunmak istiyoruz. Herkesin tecrübe ve fikirlerinden diğer katılımcılarla beraber biz de faydalanmak istiyoruz. Kısaca hep birlikte sohbet edeceğimiz bir ortama meraklı, istekli ya da bu ortamda bulunmak ve bu bilgi ve tecrübe dolu sohbetten faydalanmak isteyen herkesi bekliyoruz. -) - -$(H5 Program) - -$(UL - -$(LI 12:30 - Açılış) - -$(LI 12:35 - "D Programlama Dili", Ali Çehreli) - -$(P D programlama dilinin genel tanıtımı, kullanım alanları, ve başka dillerle karşılaştırılması.) - -$(LI 13:15 - "Akan Veriyi Dizginleme: Dizi, Dilim ve Şablonlar", Salih Dinçer) - -$(P D'nin veriyi taşıyan/değerlendiren yapılarının büyüleyici esnekliğinden bahsedeceğiz. Verilerinizi işlenmek üzere D'ye güvenle emanet edebileceğinizi ve GC sayesinde dinamik bellek yönetimin kolaylığını göreceksiniz. -) - -$(LI 14:00 - "D ile Güvenli Programlar", Zafer Çelenk) - -$(P D'nin temel hedeflerinden birisi doğru işleyen programlar üretmek olmuştur. D'nin program doğruluğuna yönelik olanaklarından yalnızca bir kaç tanesi: assert, enforce, unittest, ve Sözleşmeli Programlama.) - -$(LI 14:45 - "Koşut İşlemler ve Eş Zamanlı Programlama", Ali Çehreli) - -$(P Mikro işlemci hızları artık fiziğin sınırına erişmiş durumda. Programcılar hızlı programlar üretebilmek için koşut işlemlerden ve eş zamanlı programlamadan yararlanmak zorundalar. C++11'in ve D'nin getirdikleri çözümler.) - -$(LI 15:30 - "ş.o.m. içinde D", Mert Ataol) - -$(P Diziler, filtreler, rasgelelik, yazın dilinin matematik ve programlama dili ile ilişkisi... Şiir kıvamında programcılık...) - -$(LI D'nin ve dmd'nin Gelişimi, Can Alpay Çiftçi) - -$(P D'nin ve dmd'nin geçtikleri aşamalar. Araçların ve kütüphanelerin kullanışsız eski dönemlerden çok yararlı oldukları bugünkü durumlarına gelişleri.) - -$(LI 16:00 - "C++11'in Bazı Yenilikleri ve D'deki Karşılıkları", Ali Çehreli) - -$(P - C++11 modern programcılığın gerektirdiği yeni olanaklar getiriyor. Bu olanakların bazıları, çözüm getirdikleri sorunlar, ve D dilindeki karşılıkları.) - -$(UL -$(LI Rvalue referansları ve aktarma kurucuları ) -$(LI Sabit ifadeler ) -$(LI İlkleme listeleri ) -$(LI Aralık elemanları üzerinde for döngüsü ) -$(LI İsimsiz işlevler ) -$(LI Null sabiti ) -$(LI Serbest parametreli şablonlar ) -$(LI Dizgi hazır değerleri ) -$(LI Kullanıcı tanımlı hazır değerler) -) - -$(LI 17:00 - Kapanış) -) - -$(P -$(LINK2 http://ddili.org/forum/post/6169, Ayrıntılı konferans programı için tıklayınız). -) - -$(H5 Resimler) - -$(UL - -$(LI $(LINK2 http://www.flickr.com/photos/81819962@N02, Salih Dinçer'in Çektikleri)) - -$(LI $(LINK2 http://www.flickr.com/photos/elektronikhobi/sets/72157630998207822/, Erdem'in Çektikleri)) - -$(LI $(LINK2 https://picasaweb.google.com/105063462623798775561/DSemineri, Zafer Çelenk'in Çektikleri)) - -) - -$(H5 Yorumlar) - -$(P $(LINK2 /forum/post/6383, ddili.org/forum üyelerinin yorumları.) -) - - -$(BR) - - -Macros: - SUBTITLE=D ve C++ Sunumu (30 Haziran 2012) - - DESCRIPTION=D programlama diliyle ilgili ilginç ve önemli sunumlar - - KEYWORDS=d programlama dili makale belge tanıtım sunum - - BREADCRUMBS=$(BREADCRUMBS_FULL) diff --git a/ddili/src/sunum/sonrasi.d b/ddili/src/sunum/sonrasi.d deleted file mode 100644 index 0cb259e..0000000 --- a/ddili/src/sunum/sonrasi.d +++ /dev/null @@ -1,30 +0,0 @@ -Ddoc - -$(H4 Sunumlardan Sonrası) - -$(P $(I Can Alpay Çiftçi)) - -$(P -Sonrası en zorudur. Çünkü önceki gerçekleştirildiğinde çıkıp kaybolan yüksek düzeyde enerji, sonrası için $(I acaba) sorusunu akıllara getirir. Önceki sorunların kaynağı deneyimsizlik denerek geçilebildiği halde sonrası için bu deneyimsizlik savunması geçerli değildir. Yetersiz enerji yüzünden bu karamsarlık çöker üzerimize. O, gerçekleşen sorunları hatta daha büyük sorunları yaşar mıyız karamsarlığı ve bundan dolayı çoğu kez devam etmeme kararı alınır sonrasında. -) -$(P -Ancak bazen sonrası en kolayıdır. Çünkü ortaya çıkan bu enerji kaybolup gitmemiştir. Tersine önceki çalışmaları çok daha zevkli hale getiren bu enerji çoğalarak başka insanlara da aktarılmıştır. Bu insanlara zevk veren enerji ise bir günün ardından, sonrası için kişilerin içindeki düşlere, düşler düşüncelere ve en sonunda düşünceler planlara dönüşür. Sonraları için öncelerinde gerçekleşen sorunları yok etme isteği karşı önünüzde daha büyük bir enerji açığa çıkar artık. Bizimki de böyle oldu. -) -$(P -Ortaya çıkan zevk ve ardındaki enerji o kadar doyumsuzdu ki en büyük tartışma bu etkinliği nasıl bir günden daha fazla devam ettiririz sorusunda gerçekleşti. -) -$(P -Siz de bu etkinlikte bir şeylerin ucundan tutmak istiyorsanız ya da bir fikir vermek istiyorsanız, en kötü sadece bu etkinliğe katılmak istiyorsanız, $(LINK2 /forum/, forumlarımızı) ve forumlardan muhtemelen daha geç güncellenecek ancak daha kesin bilgiler bulunacak olan bu sayfalarımızı okuyabilirsiniz. -) -$(P -Teşekkürler! -) - -Macros: - SUBTITLE=Sonraki Sunumlar - - DESCRIPTION=D programlama diliyle ilgili ilginç ve önemli sonraki sunumlar - - KEYWORDS=d programlama dili makale belge tanıtım sonraki sunum - - BREADCRUMBS=$(BREADCRUMBS_FULL) diff --git a/ddili/src/tanitim/breadcrumbs.ddoc b/ddili/src/tanitim/breadcrumbs.ddoc deleted file mode 100644 index 2c63e12..0000000 --- a/ddili/src/tanitim/breadcrumbs.ddoc +++ /dev/null @@ -1,4 +0,0 @@ - -BREADCRUMBS_INDEX=$(LINK2 /, Ana Sayfa) > Tanıtım - -BREADCRUMBS_FULL=$(LINK2 /, Ana Sayfa) > $(LINK2 /tanitim/, Tanıtım) diff --git a/ddili/src/tanitim/dil_kutuphane.d b/ddili/src/tanitim/dil_kutuphane.d deleted file mode 100644 index 47c9bea..0000000 --- a/ddili/src/tanitim/dil_kutuphane.d +++ /dev/null @@ -1,155 +0,0 @@ -Ddoc - -$(H4 Olanakların Dil İçinde veya Kütüphanelerde Gerçekleştirilmeleri) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu sayfadaki bilgiler $(LINK2 http://www.dlang.org/builtin.html, Digital Mars'ın sitesindeki aslından) alınmıştır. -) - -$(P -C++ gibi bazı başka dillerde kütüphanelerle gerçekleştirilmiş olan bazı olanaklar D'de dilin iç olanaklarıdır: -) - -$(OL -$(LI Dinamik diziler) -$(LI Dizgiler [string]) -$(LI Eşleme tabloları [associative arrays]) -$(LI Karmaşık sayılar) -) - -$(P -Bazılarına göre bu, yarar sağlamak yerine dilin gereksizce büyümesine neden olur. Onlara göre bu olanaklar standart kütüphanelerde gerçekleştirilmelidirler. -) - -$(P -Önce bazı genel gözlemler: -) - -$(OL -$(LI -Bunların hepsi de çok yaygın olarak kullanılan olanaklar oldukları için, kullanışlılıklarını biraz olsun arttırmak çok kişiye yarar sağlar. -) -$(LI -Dil içi türlerin yanlış kullanılmaları durumunda derleyiciler daha anlaşılır ve daha yardımcı hata mesajları verebilirler. Kütüphane olanakları ile ilgili hata mesajları bazen son derece anlaşılmaz olurlar. -) -$(LI -Kütüphaneler yeni söz dizimleri, yeni işleçler, veya yeni anahtar sözcükler oluşturamazlar. -) -$(LI -Her seferinde kütüphane olanaklarının da tekrar tekrar derlenmeleri gerektiği için derleme yavaştır. -) -$(LI -Kütüphanelerin kullanıcıya esneklik sağlamaları beklenir. Eğer derleyicinin bile tanıyabileceği derecede standartlaştırılırlarsa (C++ standardı buna izin verir), zaten dil içi olanaklar kadar sabitleşmişler ve esnekliklerini kaybetmişler demektir. -) -$(LI -Kütüphanelerde yeni türler tanımlayabilme olanağı günümüzde çok gelişmiş olsa da, hâlâ dil olanakları kadar rahat değillerdir: uyumsuz kullanımlar, doğal olmayan söz dizimleri, içine düşülebilecek bazı garip durumlar. -) -) - -$(P -Biraz daha ayrıntılı açıklamalar: -) - -$(H6 Dinamik diziler) - -$(P -Diziler C++'da dilin iç olanaklarındandırlar ama pek kullanışlı oldukları söylenemez. C++'da dizilerin sorunlarını gidermek yerine, standart şablon kütüphanesi [STL] içinde tanımlı bazı yeni diziler tanımlanmıştır. Bunların her birisi dizilerin değişik bir açığını kapatır: -) - -$(UL -$(LI basic_string) -$(LI vector) -$(LI valarray) -$(LI deque) -$(LI slice_array) -$(LI gslice_array) -$(LI mask_array) -$(LI indirect_array) -) - -$(P -Dil içindeki dizinin sorunları giderilse, bunların hiçbirisine gerek kalmaz ve öğrenilecek tek bir tür olur. Değişik dizi türlerinin birbirleriyle kullanılabilmeleri için de çaba gerekmemiş olur. -) - -$(P -Dil içi diziler bize yazım kolaylığı sağlarlar. Dizi sabitleri [literal] belirleyebiliriz ve dizilere ait yeni işleçler tanımlayabiliriz. Oysa kütüphane gerçekleştirmeleri mevcut işleçleri yüklemek zorundadırlar. D dizileri C++'daki $(CODE dizi[i]) indeks işlecine ek olarak şu işleçleri de sunar: birleştirme işleci $(CODE ~), sonuna ekleme işleci $(CODE ~=), dilimleme işleci $(CODE dizi[i..j]), vektör işleci $(CODE dizi[]). -) - -$(P -$(CODE ~) ve $(CODE ~=) işleçleri, yalnızca mevcut işleçlerin yüklenebildiği durumda ortaya çıkan bir sorunun üstesinden gelirler. Normalde kütüphane dizilerini birleştirmek için $(CODE +) işleci kullanılır. Ne yazık ki bu seçim $(CODE +) işlecinin $(I vektör toplamı) anlamında kullanılmasını engellemiş olur. Ayrıca, $(I birleştirme işlemi) ile $(I toplama işlemi) aynı şey olmadıklarından, ikisi için aynı işlecin kullanılıyor olması karışıklık doğurur. -) - -$(H6 Dizgiler) - -$(P -Bu konuyla ilgili daha ayrıntılı bilgiyi $(LINK2 /tanitim/fark_dizgi.html, Dizgilerin [string] C++ Dizgileri ile Karşılaştırılması) sayfasında okuyabilirsiniz... -) - -$(P -C++'da dil içindeki dizgiler $(I sabit dizgiler [string literal]) ve $(I char dizileri) olarak sunulurlar. Ama onların da sorunu, C++ dizilerinin bütün zayıflıklarını aynen taşımalarıdır. -) - -$(P -Sonuçta dizgi bir char dizisi değil midir? Dolayısıyla dizilerin sorunları çözüldüğünde, dizgilerin sorunları da çözülmüş olur. D'de ayrı bir dizgi sınıfının bulunmuyor olması baştan garip gelebilir. Ama sonuçta char dizilerinden farklı olmadıklarına göre, özel bir sınıf olarak sunmaya gerek yoktur. -) - -$(P -Dahası, kaynak kodda sabit olarak yazılan dizgilerin kütüphanedeki dizgilerle farklı türden olmaları sorunu da çözülmüş olur. $(I [Çevirenin notu: $(CODE "merhaba") gibi bir dizgi sabiti $(CODE std::string) türünden değildir.]) -) - -$(H6 Eşleme tabloları) - -$(P -Bunun temel amacı yazım kolaylığı sağlamaktır. $(CODE T) türünde bir anahtarla indekslenen ve $(CODE int) türünde değerler tutan bir dizi çok doğal olarak şöyle yazılabilir: -) - ---- -int[T] foo; ---- - -$(P -ve şöyle bir kullanımdan çok daha kısadır: -) - ---- -import std.associativeArray; -... -std.associativeArray.AA!(T, int) foo; ---- - -$(P -Eşleme tablolarının dilin iç olanağı olmaları onların kaynak kodda sabit olarak tanımlanabilmelerini de sağlar. Bu da çok arzulanan bir olanaktır. -) - -$(H6 Karmaşık sayılar) - -$(P -Bu konuyla ilgili daha ayrıntılı bilgiyi $(LINK2 /tanitim/fark_karmasik.html, Karmaşık Sayı Türleri ve C++'nın std::complex'i) sayfasında okuyabilirsiniz... -) - -$(P -Bu olanağın dilin bir iç olanağı olmasının baş nedeni, C'deki sanal ve karmaşık kayan noktalı sayılarla [complex floating point] uyumlu olmaktır. Diğer bir nedeni, sanal kayan noktalı sayıları sabit olarak yazabilmektir: -) - ---- -c = (6 + 2i - 1 + 3i) / 3i; ---- - -$(P -Herhalde şöyle bir kullanımdan üstünlüğü açıktır: -) - ---- -c = (complex!(double)(6,2) + complex!(double)(-1,3)) - / complex!(double)(0,3); ---- - - -Macros: - SUBTITLE=İç Olanak veya Kütüphane - - DESCRIPTION=D programlama dili olanaklarının dil içinde mi yoksa kütüphanelerde mi gerçekleştirildikleri - - KEYWORDS=d programlama dili tanıtım bilgi iç olanak kütüphane gerçekleştirme diff --git a/ddili/src/tanitim/fark_c.d b/ddili/src/tanitim/fark_c.d deleted file mode 100644 index a89d1d3..0000000 --- a/ddili/src/tanitim/fark_c.d +++ /dev/null @@ -1,1403 +0,0 @@ -Ddoc - -$(H4 D'nin C ile Karşılaştırılması) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu bölüm, C programcılarına yönelik olarak D dilinin C'den olan bazı farklılıklarını gösteriyor. Nesne yönelimli programlama konusundaki farklarını ise $(LINK2 /tanitim/fark_cpp.html, C++ karşılaştırmasında) okuyabilirsiniz. -) - -$(P -Bu sayfadaki bilgiler $(LINK2 http://www.dlang.org/ctod.html, Digital Mars'ın sitesindeki aslından) alınmıştır. -) - -$(UL_FARK -$(FARK_INDEX sizeof, Tür büyüklükleri) -$(FARK_INDEX minmax, Türlerin en küçük ve en büyük değerleri) -$(FARK_INDEX temel, Temel türler) -$(FARK_INDEX ozel, Özel kesirli sayı değerleri) -$(FARK_INDEX kalan, Kesirli sayı bölümünden kalan) -$(FARK_INDEX nan, Kesirli sayı karşılaştırmalarında NAN) -$(FARK_INDEX assert, Güvenli kodlamanın vazgeçilmez parçası $(CODE assert)) -$(FARK_INDEX eleman_ilk, Dizi elemanlarını ilklemek) -$(FARK_INDEX dizi_ziyaret, Dizi elemanlarını sırayla ziyaret etmek) -$(FARK_INDEX dinamik_dizi, Değişken uzunlukta diziler) -$(FARK_INDEX dizgi_birlestir, Dizgileri birleştirmek) -$(FARK_INDEX format, Formatlı çıktı) -$(FARK_INDEX bildirim, Fonksiyonları önceden bildirmek) -$(FARK_INDEX void, Parametre almayan fonksiyonlar) -$(FARK_INDEX etiket, Etiket kullanan $(CODE break) ve $(CODE continue) deyimleri) -$(FARK_INDEX goto, $(CODE goto) deyimi) -$(FARK_INDEX typedef, $(CODE typedef struct)) -$(FARK_INDEX dizgi_arama, Dizgi aramak) -$(FARK_INDEX alignment, Yapı üyelerinin bellek yerleşimleri [alignment]) -$(FARK_INDEX isimsiz, İsimsiz $(CODE struct)'lar ve $(CODE union)'lar) -$(FARK_INDEX yapi_degisken, Yapıyı ve değişkeni bir arada tanımlamak) -$(FARK_INDEX offset, Yapı üyesinin başlangıçtan uzaklığı [offset]) -$(FARK_INDEX birlik_ilk, Birlik ilkleme) -$(FARK_INDEX yapi_ilk, Yapı ilkleme) -$(FARK_INDEX dizi_ilk, Dizi ilkleme) -$(FARK_INDEX ozel_karakter, Dizgi sabitlerinde özel karakterler) -$(FARK_INDEX ascii, ASCII ve evrensel karakterler) -$(FARK_INDEX enum_dizgi, $(CODE enum)'ların dizgi karşılıkları) -$(FARK_INDEX typedef_yeni, $(CODE typedef)'le yeni tür oluşturmak) -$(FARK_INDEX yapi_esitlik, Yapılarda eşitlik karşılaştırması) -$(FARK_INDEX dizgi_esitlik, Dizgilerde eşitlik karşılaştırması) -$(FARK_INDEX dizi_sirala, Dizi sıralamak) -$(FARK_INDEX volatile, $(CODE volatile) bellek erişimi) -$(FARK_INDEX sabit_dizgi, Sabit dizgiler) -$(FARK_INDEX veri_yapisi_ziyaret, Veri yapılarını ziyaret etmek) -$(FARK_INDEX saga_kaydirma, İşaretsiz sayılarda sağa kaydırma) -$(FARK_INDEX kapama, Dinamik kapamalar [closures]) -$(FARK_INDEX belirsiz_parametre, Belirsiz sayıda fonksiyon parametreleri) -) - -$(FARK sizeof, Tür büyüklükleri) - -$(FARK_C -sizeof(int) -sizeof(char *) -sizeof(double) -sizeof(struct Foo) -) - -$(FARK_D ---- -int.sizeof -(char *).sizeof -double.sizeof -Foo.sizeof ---- -) - - -$(FARK minmax, Türlerin en küçük ve en büyük değerleri) - -$(FARK_C -#include <limits.h> -#include <math.h> - -CHAR_MAX -CHAR_MIN -ULONG_MAX -DBL_MIN -) - -$(FARK_D ---- -char.max -char.min -ulong.max -double.min ---- -) - - -$(FARK temel, Temel türler) - -$(H6 C'den D'ye) - -$(C_CODE - - bool => bit - char => char - signed char => byte - unsigned char => ubyte - short => short - unsigned short => ushort - wchar_t => wchar - int => int - unsigned => uint - long => int - unsigned long => uint - long long => long - unsigned long long => ulong - float => float - double => double - long double => real -_Imaginary long double => ireal - _Complex long double => creal -) - -$(P -$(CODE char) işaretsiz [unsigned] 8 bittir, $(CODE wchar) ise işaretsiz 16 bittir. İşaretli ve işaretsiz türler C'de değişik boyutta olabilirler, D'de aynıdırlar.) - - -$(FARK ozel, Özel kesirli sayı değerleri) - -$(FARK_C -#include <fp.h> - -NAN -INFINITY - -#include <float.h> - -DBL_DIG -DBL_EPSILON -DBL_MANT_DIG -DBL_MAX_10_EXP -DBL_MAX_EXP -DBL_MIN_10_EXP -DBL_MIN_EXP -) - -$(FARK_D ---- -double.nan -double.infinity -double.dig -double.epsilon -double.mant_dig -double.max_10_exp -double.max_exp -double.min_10_exp -double.min_exp ---- -) - - -$(FARK kalan, Kesirli sayı bölümünden kalan) - -$(FARK_C -#include <math.h> - -float f = fmodf(x,y); -double d = fmod(x,y); -long double r = fmodl(x,y); -) - -$(FARK_D ---- -float f = x % y; -double d = x % y; -real r = x % y; ---- -) - - -$(FARK nan, Kesirli sayı karşılaştırmalarında NAN) - -$(P -$(I Not A Number)'ın kısaltması olan $(CODE NAN), bir kesirli sayının bit gösterimi düzeyinde geçersiz olduğu anlamına gelir. C, $(CODE NAN) geçersiz değerinin sayı karşılaştırmalarında yer almasının nasıl bir sonuç vereceğini belirlemez. -) - -$(FARK_C -#include <math.h> - -if (isnan(x) || isnan(y)) - sonuc = FALSE; -else - sonuc = (x < y); -) - -$(FARK_D ---- -sonuç = (x < y); // x veya y 'nan' olduğunda 'false'tur ---- -) - - -$(FARK assert, Güvenli kodlamanın vazgeçilmez parçası $(CODE assert)) - -$(P -C'de $(CODE assert) dilin parçası değildir ama $(CODE __FILE__) ve $(CODE __LINE__) makroları aracılığıyla kullanışlı bilgi verir. D'de ise $(CODE assert) dilin parçasıdır. -) - -$(FARK_C -#include <assert.h> - -assert(e == 0); -) - -$(FARK_D ---- -assert(e == 0); ---- -) - - -$(FARK eleman_ilk, Dizi elemanlarını ilklemek) - -$(FARK_C -#define DIZI_UZUNLUGU 17 -int dizi[DIZI_UZUNLUGU]; -for (i = 0; i < DIZI_UZUNLUGU; i++) - dizi[i] = deger; -) - -$(FARK_D ---- -int dizi[17]; -dizi[] = değer; ---- -) - - -$(FARK dizi_ziyaret, Dizi elemanlarını sırayla ziyaret etmek) - -$(P -C'de diziler kendi uzunluklarını bilmedikleri için uzunluk ya ayrı olarak tanımlanır, ya da $(CODE sizeof) işlecinden yararlanılır. D'de ise dizilerin uzunlukları dizinin bir niteliğidir [property]. -) - -$(FARK_C -#define DIZI_UZUNLUGU 17 -int dizi[DIZI_UZUNLUGU]; -for (i = 0; i < DIZI_UZUNLUGU; i++) - fonksiyon(dizi[i]); -) - -$(P -veya -) - -$(C_CODE -int dizi[17]; -for (i = 0; i < sizeof(dizi) / sizeof(dizi[0]); i++) - fonksiyon(dizi[i]); -) - -$(FARK_D ---- -int dizi[17]; -for (i = 0; i < dizi.length; i++) - fonksiyon(dizi[i]); ---- -) - -$(P -veya daha da iyisi: -) - ---- -int dizi[17]; -foreach (int değer; dizi) - fonksiyon(değer); ---- - - -$(FARK dinamik_dizi, Değişken uzunlukta diziler) - -$(P -C'de uzunluğu tutmak için ayrı bir değişken gerekir, ve dizinin uzunluğu değiştiğinde onun da değeri değiştirilir. D ise dinamik dizileri desteklediği için gereken bellek yönetimi de otomatik olarak halledilir. -) - -$(FARK_C -#include <stdlib.h> - -int dizi_uzunlugu; -int *dizi; -int *yeni_dizi; - -yeni_dizi = - (int *)realloc(dizi, (dizi_uzunlugu + 1) * sizeof(int)); -if (!yeni_dizi) - error("bellek yetersiz"); -dizi = yeni_dizi; -dizi[dizi_uzunlugu++] = x; -) - -$(FARK_D ---- -int[] dizi; - -dizi.length = dizi.length + 1; -dizi[dizi.length - 1] = x; ---- -) - - -$(FARK dizgi_birlestir, Dizgileri birleştirmek) - -$(P -C'de bu konuda çok sorun vardır: belleğin ne zaman geri verileceği, NULL işaretçiler, dizgilerin uzunluklarını bulmak, ve genel olarak bellek yönetimi. D'de ise $(CODE char) ve $(CODE wchar) türleri için yüklenmiş olan $(CODE ~) işleci $(I birleştirmek), $(CODE ~=) işleci ise $(I sonuna eklemek) anlamına gelir. -) - -$(FARK_C -#include <string.h> - -char *s1; -char *s2; -char *s; - -// s1 ve s2'yi ekle ve sonucu s'ye yerleştir -free(s); -s = (char *)malloc((s1 ? strlen(s1) : 0) + - (s2 ? strlen(s2) : 0) + 1); -if (!s) - error("bellek yetersiz"); -if (s1) - strcpy(s, s1); -else - *s = 0; -if (s2) - strcpy(s + strlen(s), s2); - -// s'ye "merhaba" dizgisini ekle -char merhaba[] = "merhaba"; -char *yeni_s; -size_t s_uzunluk = s ? strlen(s) : 0; -yeni_s = (char *)realloc(s, - (s_uzunluk + sizeof(merhaba) + 1) - * sizeof(char)); -if (!yeni_s) - error("bellek yetersiz"); -s = yeni_s; -memcpy(s + s_uzunluk, merhaba, sizeof(merhaba)); -) - -$(FARK_D ---- -char[] s1; -char[] s2; -char[] s; - -s = s1 ~ s2; -s ~= "merhaba"; ---- -) - - -$(FARK format, Formatlı çıktı) - -$(P -D'de $(CODE printf)'e ek olarak tür güvenliği getiren $(CODE writefln) vardır. -) - -$(FARK_C -#include <stdio.h> - -printf("Arabaları %d kere çağırıyoruz!\n", kac_kere); -) - -$(FARK_D ---- -printf("Arabaları %d kere çağırıyoruz!\n", kaç_kere); ---- - -$(P -veya daha güvenli olarak -) ---- -import std.stdio; - -writefln("Arabaları %s kere çağırıyoruz!", kaç_kere); ---- -) - - -$(FARK bildirim, Fonksiyonları önceden bildirmek) - -$(P -C'de her fonksiyonun kullanılmadan önce bildirilmiş olması gerekir. D'de ise programa bir bütün olarak bakıldığı için, fonksiyon bildirimleri gereksiz olmalarının yanında yasal bile değillerdir. Hatalara elverişli bu külfetten kurtulmuş olarak fonksiyonları istediğimiz sırada yazabiliriz. -) - -$(FARK_C -// bildirim -void ileride_tanimli_fonksiyon(); - -void fonksiyon() -{ - ileride_tanimli_fonksiyon(); -} - -// tanim -void ileride_tanimli_fonksiyon() -{ - ... -} -) - -$(FARK_D ---- -void fonksiyon() -{ - ileride_tanımlı_fonksiyon(); -} - -// tanım -void ileride_tanımlı_fonksiyon() -{ - ... -} ---- -) - - -$(FARK void, Parametre almayan fonksiyonlar) - -$(P -D'de bir fonksiyonun parametre almadığının açıkça belirtilmesine gerek yoktur; boş bırakılan parametre listesi zaten o anlama gelir. -) - -$(FARK_C -void fonksiyon(void); -) - -$(FARK_D ---- -void fonksiyon() -{ - ... -} ---- -) - - -$(FARK etiket, Etiket kullanan $(CODE break) ve $(CODE continue) deyimleri) - -$(P -C'de $(CODE break) ve $(CODE continue) deyimleri en içteki döngüyü veya $(CODE switch)'i etkiler. En dıştaki döngüden de çıkmak istendiğinde $(CODE goto) kullanmak gerekir. D'de ise döngülere ve $(CODE switch)'lere de etiket verilebilir ve $(CODE break) ve $(CODE continue) deyimlerinde etiket belirtilerek istenen düzeydeki döngü veya $(CODE switch) belirtilebilir. -) - -$(FARK_C - for (i = 0; i < 10; i++) - { - for (j = 0; j < 10; j++) - { - if (j == 3) - goto Dis_Etiket; - if (j == 4) - goto Ic_Etiket; - } - Ic_Etiket: - ; - } -Dis_Etiket: - ; -) - -$(FARK_D ---- -Dış_Etiket: - for (i = 0; i < 10; i++) - { - for (j = 0; j < 10; j++) - { - if (j == 3) - break Dış_Etiket; - if (j == 4) - continue Dış_Etiket; - } - } - // 'break Dış_Etiket;' buraya getirir ---- -) - - -$(FARK goto, $(CODE goto) deyimi) - -$(P -$(CODE goto) deyimi C'de de önerilmez ama bazen mecbur kalınır. Her ne kadar D'de gerekmese de, istendiğinde kullanılabilsin diye $(CODE goto) D'de de vardır. -) - - - -$(FARK typedef, $(CODE typedef struct)) - -$(P -Yapı tanımlarında $(CODE typedef) gerekmez. -) - -$(FARK_C -typedef struct ABC { ... } ABC; -) - -$(FARK_D ---- -struct ABC { ... } ---- -) - - -$(FARK dizgi_arama, Dizgi aramak) - -$(P -Verilen bir dizgiyi bir dizgi listesinde aramak, oldukça yaygın bir işlemdir. Örneğin programın hangi komut satırı parametreleri ile çalıştırıldığını anlamak ve programın davranışını ona göre ayarlamak... -) - -$(P -Bu işi C'de yapmak için birbirine denk üç veri yapısı kullanmak gerekir: $(CODE enum), dizgi tablosu, ve $(CODE switch) içindeki $(CODE case)'ler. Bu üçünü denk tutmak listedeki dizgi sayısı arttığında oldukça güçleşebilir ve hatalara açık durumlar doğurur. Dahası, bu bir sıralı arama algoritması olduğu için, büyük listelerde yavaş da kalabilir. Öyle olduğunda zaman da bir hızlı eşleme tablosu kurmak gerekebilir ve iş iyice karmaşıklaşır. -) - -$(P -D ise $(CODE switch) deyimlerinde dizgilere izin vererek bu işlemi büyük ölçüde kolaylaştırır. Listeye yeni değerler eklemek son derece basit hale gelir ve gerektiği durumlarda daha hızlı algoritmalar kullanmak da derleyiciye bırakılmış olur. -) - -$(FARK_C -#include <string.h> -void islem_yap(char *s) -{ - enum Degerler { Merhaba, GuleGule, Belki, deger_adet }; - static char *tablo[] = { "merhaba", "güle güle", "belki" }; - int i; - - for (i = 0; i < deger_adet; i++) - { - if (strcmp(s, tablo[i]) == 0) - break; - } - switch (i) - { - case Merhaba: ... - case GuleGule: ... - case Belki: ... - default: ... - } -} -) - -$(FARK_D ---- -void işlem_yap(char[] s) -{ - switch (s) - { - case "merhaba": ... - case "güle güle": ... - case "belki": ... - default: ... - } -} ---- -) - - -$(FARK alignment, Yapı üyelerinin bellek yerleşimleri [alignment]) - -$(P -C'de bunun bir yolu, istenen yerleşimi derleyici ayarlarıyla belirtmektir. Öyle yapınca, bütün yapılar etkilenmiş olurlar ve bütün kaynak dosyaların baştan derlenmeleri gerekir. Bu işi kolaylaştırmak için $(CODE #pragma)'lar kullanılır; ancak $(CODE #pragma)'lar maalesef derleyiciye bağlı ve taşınmaz olanaklardır. D'de bu konuda taşınabilir bir çözüm sunulmuştur. -) - -$(FARK_C -#pragma pack(1) -struct ABC -{ - ... -}; -#pragma pack() -) - -$(FARK_D ---- -struct ABC -{ - int z; // z varsayılan ayara göre yerleştirilecek - - align (1) int x; // x byte sınırına yerleştirilecek - align (4) - { - ... // {} içinde tanımlanan üyeler dword - // sınırına yerleştirilecekler - } - align (2): // bu noktadan sonra word sınırına geçilir - - int y; // y word sınırındadır -} ---- -) - - -$(FARK isimsiz, İsimsiz $(CODE struct)'lar ve $(CODE union)'lar) - -$(P -Bazı durumlarda üyelerin yerleşimleri için yapılardan ve birliklerden [union] yararlanılır. C bunların isimsiz olarak tanımlanmalarına izin vermediği için kullanılmayacak isimler uydurmak gerekir. O isimlerden kurtulmak için de makrolardan yararlanılır. D ise bu yapıların isimsiz olarak tanımlanmalarına izin verir. -) - -$(FARK_C -struct Foo -{ - int i; - union Bar - { - struct Abc { int x; long y; } _abc; - char *p; - } _bar; -}; - -#define x _bar._abc.x -#define y _bar._abc.y -#define p _bar.p - -struct Foo f; - -f.i; -f.x; -f.y; -f.p; -) - -$(FARK_D ---- -struct Foo -{ - int i; - union - { - struct { int x; long y; } - char* p; - } -} - -Foo f; - -f.i; -f.x; -f.y; -f.p; ---- -) - - -$(FARK yapi_degisken, Yapıyı ve değişkeni bir arada tanımlamak) - -$(P -D'de tür ve değişken aynı satırda tanımlanamaz. -) - -$(FARK_C -struct Foo { int x; int y; } foo; -) - -$(P -veya iki satırda -) - -$(C_CODE -struct Foo { int x; int y; }; // sonda ';' vardir -struct Foo foo; -) - -$(FARK_D ---- -struct Foo { int x; int y; } // sonda ';' yoktur -Foo foo; ---- -) - - -$(FARK offset, Yapı üyesinin başlangıçtan uzaklığı [offset]) - -$(FARK_C -#include <stddef.h> -struct Foo { int x; int y; }; - -uzaklik = offsetof(Foo, y); -) - -$(FARK_D ---- -struct Foo { int x; int y; } - -uzaklık = Foo.y.offsetof; ---- -) - - -$(FARK birlik_ilk, Birlik ilkleme) - -$(P -C'de birliğin $(I ilk üyesi) ilklenir. D'de ise hangi üyenin ilklendiğinin açıkça belirtilmesi gerekir. -) - -$(FARK_C -union U { int a; long b; }; -union U x = { 5 }; // 'a' 5'e esitlenir -) - -$(FARK_D ---- -union U { int a; long b; } -U x = { a:5 }; ---- -) - - -$(FARK yapi_ilk, Yapı ilkleme) - -$(P -C'de üyeler sırayla ilklenirler. Bu kural, küçük yapılarda fazla sorun çıkartmasa da yapı büyüdüğünde sorun haline gelebilir. Özellikle yeni üyelerin araya eklendiği durumlarda kod içinde daha önce yazılmış olan bütün ilkleme ifadelerinin bulunması ve değişiklikten sonra da doğru olarak ilklendiklerine dikkat edilmesi gerekir. D'de ise üyeler açıkça ilklenirler. -) - -$(FARK_C -struct S { int a; int b; }; -struct S x = { 5, 3 }; -) - -$(FARK_D ---- -struct S { int a; int b; } -S x = { b:3, a:5 }; ---- -) - - -$(FARK dizi_ilk, Dizi ilkleme) - -$(P -C'de diziler değerlerin sırasına göre ilklenirler. -) - -$(FARK_C -int a[3] = { 3,2,2 }; -) - -$(P - İç diziler için $(CODE { }) parantezlerinin kullanımı isteğe bağlıdır. -) - -$(C_CODE -int b[3][2] = { 2,3, {6,5}, 3,4 }; -) - -$(FARK_D - -$(P -D'de ise hem sırayla ilklenebilirler, hem de hangi elemanın ilklendiği açıkça belirtilebilir. Şunların hepsi aynı sonucu verir: -) - ---- -int[3] a = [ 3, 2, 0 ]; -int[3] a = [ 3, 2 ]; // C'de olduğu gibi, belirtilmeyen - // elemanların değeri 0'dır -int[3] a = [ 2:0, 0:3, 1:2 ]; -int[3] a = [ 2:0, 0:3, 2 ]; // indeks belirtilmediğinde, bir - // önceki indeks değerinden devam - // edilir ---- -) - -$(P -Bu, özellikle dizi indekslerken $(CODE enum)'lar kullanıldığı durumlarda önemlidir. -) - ---- -enum renk { siyah, kırmızı, yeşil } -int[3] c = [ siyah:3, yeşil:2, kırmızı:5 ]; ---- - -$(P -İç dizilerin ilklenmeleri de açıkça belirtilmek zorundadır: -) - ---- -int[2][3] b = [ [2,3], [6,5], [3,4] ]; - -int[2][3] b = [[2,6,3],[3,5,4]]; // hata ---- - - -$(FARK ozel_karakter, Dizgi sabitlerinde özel karakterler) - -$(H6 C) - -$(P -C'de $(CODE \) karakteri özel karakterleri belirlemek için kullanıldığı için, $(BR) $(CODE c:\klasor\dosya.c) gibi bir dosya ismi şöyle belirtilir: -) - -$(C_CODE -char dosya[] = "c:\\klasor\\dosya.c"; -) - -$(P -Bu durum düzenli ifadeleri [regexp] daha da karmaşıklaştırır. Örneğin $(BR)$(CODE /"[^\\]*(\\.[^\\]*)*"/) ifadesi için: -) - -$(C_CODE -char tirnak_icinde[] = "\"[^\\\\]*(\\\\.[^\\\\]*)*\""; -) - -$(FARK_D - -$(P -D'de ise dizgi içindeki karakterler olmaları gerektiği gibi yazılırlar; özel karakterler ayrı olarak... -) - ---- -char[] dosya = `c:\klasor\dosya.c`; -char[] tırnak_içinde = r"[^\\]*(\\.[^\\]*)*"; ---- -) - - -$(FARK ascii, ASCII ve evrensel karakterler) - -$(P -C'de $(CODE wchar_t) türü vardır ve sabit dizgilerin başında $(CODE L) karakteri kullanılır. -) - -$(FARK_C -#include <wchar.h> -char foo_ascii[] = "merhaba"; -wchar_t foo_wchar[] = L"merhaba"; -) - -$(P -Ancak kodun hem ASCII hem de uluslararası karakterlere uyumlu olması istendiğinde makrolardan yararlanmak zorunda kalınır: -) - -$(C_CODE -#include <tchar.h> -tchar merhaba[] = TEXT("merhaba"); -) - -$(FARK_D - -$(P -D'de ise ne tür dizgi kullanıldığı koddan anlaşılır. -) - ---- -char[] foo_ascii = "merhaba"; // ascii -wchar[] foo_wchar = "merhaba"; // wchar ---- -) - - -$(FARK enum_dizgi, $(CODE enum)'ların dizgi karşılıkları) - -$(FARK_C -enum RENKLER { kirmizi, mavi, yesil, deger_adet }; -char *dizgiler[deger_adet] = {"kırmızı", "mavi", "yeşil" }; -) - -$(FARK_D ---- -enum RENKLER { kırmızı, mavi, yeşil } - -char[][RENKLER.max + 1] dizgiler = -[ - RENKLER.kırmızı : "kırmızı", - RENKLER.mavi : "mavi", - RENKLER.yeşil : "yeşil", -]; ---- -) - - -$(FARK typedef_yeni, $(CODE typedef)'le yeni tür oluşturmak) - -$(H6 C) -$(P -C'de $(CODE typedef) yeni tür oluşturmaz; türe yeni bir isim verir. -) - -$(C_CODE -typedef void *Cerez; -void foo(void *); -void bar(Cerez); - -Cerez h; -foo(h); // farkedilmeyen kodlama hatasi -bar(h); // dogru kullanim -) - -$(P -Böyle hatalardan kurtulmak için tür bir yapı içine alınır: -) - -$(C_CODE -struct Cerez__ { void *deger; } -typedef struct Cerez__ *Cerez; -void foo(void *); -void bar(Cerez); - -Cerez h; -foo(h); // simdi derleme hatasi -bar(h); // dogru kullanim -) - -$(P -Tür için ilk değer atamak için makro tanımlamak, adlandırma yöntemleri geliştirmek, ve kodlamaya dikkat etmek gerekir: -) - -$(C_CODE -#define CEREZ_ILK ((Cerez)-1) - -Cerez h = CEREZ_ILK; -h = fonksiyon(); -if (h != CEREZ_ILK) - ... -) - -$(P -Yapı kullanan çözüm daha da karmaşık hale gelir: -) - -$(C_CODE -struct Cerez__ CEREZ_ILK; - -void Cerez_ilk_degeri() // bu fonksiyon en basta cagrilir -{ - CEREZ_ILK.value = (void *)-1; -} - -Cerez h = CEREZ_ILK; -h = fonksiyon(); -if (memcmp(&h,&CEREZ_ILK,sizeof(Cerez)) != 0) - ... -) - -$(FARK_D - -$(P -$(CODE typedef) yeni tür oluşturduğu için C'deki gibi çözümler gerekmez. -) - ---- -typedef void* Cerez; -void foo(void*); -void bar(Cerez); - -Cerez h; -foo(h); -bar(h); ---- -) - -$(P -İlklemek de çok daha basittir: -) - ---- -typedef void* Cerez = cast(void*)(-1); -Cerez h; -h = fonksiyon(); -if (h != Cerez.init) - ... ---- - - -$(FARK yapi_esitlik, Yapılarda eşitlik karşılaştırması) - -$(P -C'de yapı ataması çok basit olarak derleyici tarafından halledilir, ama eşitlik karşılaştırması için bir yardım gelmez. Bazen kullanılan $(CODE memcmp) ise her durumda çalışmayabilir, çünkü yapı üyeleri arasındaki olası doldurma bitlerinin [padding] hangi değerlerde olacakları standart tarafından tanımlanmamıştır. D'de ise karşılaştırma da atama kadar basittir. -) - -$(FARK_C -#include <string.h> - -struct A x, y; -... -x = y; -... -if (memcmp(&x, &y, sizeof(struct A)) == 0) - ... -) - -$(FARK_D ---- -A x, y; -... -x = y; -... -if (x == y) - ... ---- -) - - -$(FARK dizgi_esitlik, Dizgilerde eşitlik karşılaştırması) - -$(P -C'de dizgiler sonlarındaki $(CODE '\0') karakteri ile tanımlandıkları için, sonlandırma karakterini bulmayı gerektiren dizi işlemleri doğal olarak yavaştır. D'de ise dizilerin (ve dolayısıyla dizgilerin) uzunlukları da bilindiği için eşitlik karşılaştırmaları çok daha hızlı bir şekilde gerçekleştirilebilir. D ayrıca sıralama karşılaştırmalarını da destekler. -) - -$(FARK_C -char dizgi[] = "merhaba"; - -if (strcmp(dizgi, "selam") == 0) // dizgiler esit mi? - ... -) - -$(FARK_D ---- -char[] dizgi = "merhaba"; - -if (dizgi == "selam") - ... - -if (dizgi < "selam") - ... ---- -) - - -$(FARK dizi_sirala, Dizi sıralamak) - -$(FARK_C -int karsilastir(const void *p1, const void *p2) -{ - tur *t1 = (tur *)p1; - tur *t2 = (tur *)p2; - - return *t1 - *t2; -} - -type dizi[10]; -... -qsort(dizi, sizeof(dizi)/sizeof(dizi[0]), - sizeof(dizi[0]), karsilastir); -) - -$(FARK_D ---- -type[] dizi; -... -dizi.sort; // dizi olduğu yerde sıralanır ---- -) - - -$(FARK volatile, $(CODE volatile) bellek erişimi) - -$(P -Paylaşımlı bellek veya belleğe bağlı giriş/çıkış gibi işlemlerde karşılaşılan ve değeri derleyici farkında olmadan değişebilen bellek için $(CODE volatile) işaretçiler kulanılabilir. $(CODE volatile) D'de bir deyim türüdür ve deyimi etkiler. -) - -$(FARK_C -volatile int *p = adres; - -i = *p; -) - -$(FARK_D ---- -int* p = adres; - -volatile { i = *p; } ---- -) - - -$(FARK sabit_dizgi, Sabit dizgiler) - -$(P -C'de sabit dizgiler birden fazla satıra taştığında satır sonlarında $(CODE \) karakteri gerekir. D'de gerekmez ve böylece başka yerlerden kopyalanan metin, olduğu gibi kullanılabilir. -) - -$(FARK_C -"Bu sabit dizgi\n\ -birden fazla\n\ -satırda bulunuyor\n" -) - -$(FARK_D ---- -"Bu sabit dizgi -birden fazla -satırda bulunuyor -" ---- -) - - -$(FARK veri_yapisi_ziyaret, Veri yapılarını ziyaret etmek) - -$(P -Özyinelemeli bir veri yapısındaki elemanları ziyaret eden bir fonksiyon düşünün. Basit bir sembol tablosu olsun. Veri yapısı olarak bir ikili agaç dizisi olsun. Kodun bütün sembollere bakması ve tek olan sembolleri bulması gereksin... -) - -$(P -Bu işlem için $(CODE eleman_ara) gibi bir yardımcı fonksiyon gerekir. Bu fonksiyonun ağacın dışında bulunması gereken bir yapıdan bilgi okuması, ve o yapıya bilgi yazması gerekir. Bu bilgi de $(CODE ElemanBilgisi) isimli bir yapıda tutuluyor olsun, ve etkin olması için fonksiyona işaretçi olarak geçirilsin: -) - -$(FARK_C -struct Sembol -{ - char *numara; - struct Sembol *sol; - struct Sembol *sag; -}; - -struct ElemanBilgisi -{ - char *numara; - struct Sembol *sm; -}; - -static void eleman_ara(struct ElemanBilgisi *p, - struct Sembol *s) -{ - while (s) - { - if (strcmp(p->numara,s->numara) == 0) - { - if (p->sm) - error("birden fazla anlamı olan eleman: %s\n", - p->numara); - p->sm = s; - } - - if (s->sol) - eleman_ara(p,s->sol); - s = s->sag; - } -} - -struct Sembol *sembol_ara(Sembol *tablo[], - int sembol_adet, - char *numara) -{ - struct ElemanBilgisi pb; - int i; - - pb.numara = numara; - pb.sm = NULL; - for (i = 0; i < sembol_adet; i++) - { - eleman_ara(pb, tablo[i]); - } - return pb.sm; -} -) - -$(FARK_D - -$(P -Aynı algoritma D'de aşağıdaki gibi yazılabilir. Kapsam fonksiyonları, dıştaki fonksiyondaki nesnelere erişebildikleri için $(CODE ElemanBilgisi) gibi bir türe gerek kalmaz. Yardımcı fonksiyon bütünüyle onu kullanan fonksiyonun içinde tanımlandığı için kod yerelliği açısından önemlidir. C ve D kodları arasında hız bakımından da ölçülebilir bir fark yoktur. -) - ---- -class Sembol -{ char[] numara; - Sembol sol; - Sembol sağ; -} - -Sembol sembol_ara(Sembol[] tablo, char[] numara) -{ Sembol sm; - - void eleman_ara(Sembol s) - { - while (s) - { - if (numara == s.numara) - { - if (sm) - error( - "birden fazla anlamı olan eleman: %s\n", - numara ); - sm = s; - } - - if (s.sol) - eleman_ara(s.sol); - s = s.sağ; - } - } - - for (int i = 0; i < tablo.length; i++) - { - eleman_ara(tablo[i]); - } - return sm; -} ---- -) - - -$(FARK saga_kaydirma, İşaretsiz sayılarda sağa kaydırma) - -$(H6 C) - -$(P -Sağa kaydırma işleçleri $(CODE >>) ve $(CODE >>=), işlemlerini sol tarafın türüne göre ya işaretli olarak ya da işaretsiz olarak yaparlar. Türün $(CODE int) olduğu durumda da işaretsiz bir sonuç elde etmek için tür değişimi kullanmak gerekir: -) - -$(C_CODE -int i, j; -... -j = (unsigned)i >> 3; -) - -$(P -Sağa kaydırılan bir $(CODE int) ise bu doğru çalışır, ama örneğin $(CODE typedef) yoluyla gizlenmiş bir $(CODE long) ise, üst bitlerin farkında olunmadan kaybedilme riski doğar: -) - -$(C_CODE -benim_int i, j; -... -j = (unsigned)i >> 3; -) - -$(FARK_D - -$(P -$(CODE >>) ve $(CODE >>=) işleçleri D'de de aynı şekilde çalışırlar, ama D'de kaydırmayı tür ne olursa olsun işaretsiz olarak yapan $(CODE >>>) ve $(CODE >>>=) işleçleri de vardır: -) - ---- -benim_int i, j; -... -j = i >>> 3; ---- -) - - -$(FARK kapama, Dinamik kapamalar [closures]) - -$(H6 C) - -$(P -Esnek bir topluluk veri yapısı düşünün. Esnek kabul edilebilmesi için, dışarıdan verilen bir fonksiyonu elemanların hepsine teker teker uygulasın. Bunu, ismi $(I uygula) olan ve bir fonksiyon işaretçisi yanında bir de topluluktaki elemanlardan birisini alan bir fonksiyon olarak düşünebiliriz. -) - -$(P -Ek olarak, yapılan işle ilgili bilgi tutmak için ve o bilginin türü esnek olabilsin diye $(CODE void*) bir de $(I kapsam) yapısı alması gerekir. Bu örnekte $(CODE int)'lerden oluşan bir topluluk, ve o topluluktaki en büyük elemanı bulmaya çalışan bir kullanıcı kodu var: -) - -$(C_CODE -void uygula( - void *p, int *dizi, int uzunluk, void (*fp)(void *, int)) -{ - for (int i = 0; i < uzunluk; i++) - fp(p, dizi[i]); -} - -struct Topluluk -{ - int dizi[10]; -}; - -void en_buyugunu_bul(void *p, int i) -{ - int *p_en_buyuk = (int *)p; - - if (i > *p_en_buyuk) - *p_en_buyuk = i; -} - -void fonksiyon(struct Topluluk *t) -{ - int en_buyuk = INT_MIN; - - uygula(&en_buyuk, - t->dizi, sizeof(t->dizi)/sizeof(t->dizi[0]), - en_buyugunu_bul); -} -) - -$(FARK_D - -$(P -D'de ise işle ilgili bilgiyi aktarmak için $(I temsilcilerden) [delegate], kapsam içindeki bilgiye erişebilmek ve yerelliği arttırmak için de kapsam fonksiyonlarından yararlanılır. Tür dönüşümlerine gerek kalmadığı için, onların getireceği olası hatalar da ortadan kalkmış olur: -) - ---- -class Topluluk -{ - int[10] dizi; - - void uygula(void delegate(int) fp) - { - for (int i = 0; i < dizi.length; i++) - fp(dizi[i]); - } -} - -void fonksiyon(Topluluk c) -{ - int en_buyuk = int.min; - - void en_buyugunu_bul(int i) - { - if (i > en_buyuk) - en_buyuk = i; - } - - c.uygula(en_buyugunu_bul); -} ---- -) - -$(P -Başka bir yöntem ise $(I fonksiyon sabiti) kullanmaktır; böylece gereksiz fonksiyon isimleri bulmak da gerekmemiş olur: -) - ---- -void fonksiyon(Topluluk t) -{ - int en_buyuk = int.min; - - t.uygula( - delegate(int i) { if (i > en_buyuk) en_buyuk = i; } ); -} ---- - -$(FARK belirsiz_parametre, Belirsiz sayıda fonksiyon parametreleri) - -$(P -Kaç tane oldukları baştan bilinmeyen parametrelerinin hepsinin toplamını bulan bir fonksiyon yazmaya çalışalım. -) - -$(FARK_C -#include <stdio.h> -#include <stdarg.h> - -int toplam(int uzunluk, ...) -{ int i; - int t = 0; - va_list ap; - - va_start(ap, uzunluk); - for (i = 0; i < uzunluk; i++) - t += va_arg(ap, int); - va_end(ap); - return t; -} - -int main() -{ - int i; - - i = toplam(3, 8,7,6); - printf("toplam = %d\n", i); - - return 0; -} -) - -$(P -Bunda iki sorun vardır: Birincisi, $(CODE toplam) fonksiyonuna kaç tane sayı olduğunun bildirilmesi gerekir; ikincisi, gönderilen parametrelerin gerçekten $(CODE int) olup olmadıklarını anlamanın hiçbir yolu yoktur. -) - -$(FARK_D - -$(P -Dizi türündeki bir parametreyi izleyen $(CODE ...) karakterleri, o diziden sonra gelen bütün parametrelerin o dizinin elemanları olarak sunulacaklarını belirler. Parametrelerin gerçekten dizinin türüne uyup uymadıkları denetlenir, ve kaç tane parametre bulunduğu dizinin $(CODE length) niteliğinden anlaşılabilir: -) - ---- -import std.stdio; - -int toplam(int[] değerler ...) -{ - int t = 0; - - foreach (int x; değerler) - t += x; - return t; -} - -int main() -{ - int i; - - i = toplam(8,7,6); - writefln("toplam = %d", i); - - return 0; -} ---- -) - - - -Macros: - SUBTITLE=C ile Farkları - - DESCRIPTION=D programlama dilinin C dilinden farkları - - KEYWORDS=d programlama dili tanıtım bilgi karşılaştırma c dili fark diff --git a/ddili/src/tanitim/fark_cpp.d b/ddili/src/tanitim/fark_cpp.d deleted file mode 100644 index 56f2c94..0000000 --- a/ddili/src/tanitim/fark_cpp.d +++ /dev/null @@ -1,777 +0,0 @@ -Ddoc - -$(H4 D'nin C++ ile Karşılaştırılması) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu bölüm, C++ programcılarına yönelik olarak D dilinin nesne yönelimli olanaklarını C++'takilerle karşılaştırır. Daha alt düzey ve daha genel olanaklarını $(LINK2 /tanitim/fark_c.html, C karşılaştırmasında) okuyabilirsiniz. -) - -$(P -Bu sayfadaki bilgiler $(LINK2 http://www.dlang.org/cpptod.html, Digital Mars'ın sitesindeki aslından) alınmıştır. -) - -$(UL_FARK -$(FARK_INDEX kurucular, Kurucular) -$(FARK_INDEX ust_sinif_ilk, Üst sınıf ilkleme) -$(FARK_INDEX yapi_karsilastir, Yapılarda eşitlik karşılaştırması) -$(FARK_INDEX typedef_yeni, $(CODE typedef)'le yeni tür oluşturmak) -$(FARK_INDEX friend, $(CODE friend) sınıflar) -$(FARK_INDEX islec_yukleme, İşleç yükleme [operator overloading]) -$(FARK_INDEX namespace_using, İsim alanı kısaltan $(CODE using) bildirimleri) -$(FARK_INDEX raii, RAII (Resource Acquisition Is Initialization)) -$(FARK_INDEX nitelik, Nitelikler [properties]) -$(FARK_INDEX sablon_ozyineleme, Özyinelemeli şablonlar) -$(FARK_INDEX sablon_meta, Meta şablonlar) -$(FARK_INDEX tur_ozellik, Tür özellikleri [type traits]) -) - -$(FARK kurucular, Kurucular) - -$(P -C++'ta kurucu fonksiyonlar sınıfla aynı isimdedirler; D'de ise $(CODE this) anahtar sözcüğü ile belirtilirler. -) - -$(FARK_CPP -class Foo -{ - Foo(int x); -}; -) - -$(FARK_D ---- -class Foo -{ - this(int x) { } -} ---- -) - - -$(FARK ust_sinif_ilk, Üst sınıf ilkleme) - -$(H6 C++) - -$(P -Üst sınıflar ilk değer listesinde ilklenirler. -) - -$(C_CODE -class A { A() {... } }; -class B : A -{ - B(int x) - : A() // ust sinif kurucusu cagrilir - { ... - } -}; -) - -$(FARK_D - -$(P -Üst sınıfın kurucusu $(CODE super()) şeklinde çağrılır. -) - ---- -class A { this() { ... } } -class B : A -{ - this(int x) - { ... - super(); // üst sınıf kurucusu çağrılır - ... - } -} ---- -) - -$(P -Böylece üst sınıf, alt sınıf kurucusunun herhangi bir noktasında kurulabilir. Ayrıca D'de bir kurucu fonksiyon içerisinden başka bir kurucu fonksiyon çağrılabilir: -) - ---- -class A -{ int a; - int b; - this() { a = 7; b = foo(); } - this(int x) - { - this(); - a = x; - } -} ---- - -$(P -Hatta üyeler daha kurucu çağrılmadan bile sabit değerlerle ilklenebilirler. Aşağıdaki kod yukarıdakinin eşdeğeridir: -) - ---- -class A -{ int a = 7; - int b; - this() { b = foo(); } - this(int x) - { - this(); - a = x; - } -} ---- - - -$(FARK yapi_karsilastir, Yapılarda eşitlik karşılaştırması) - -$(H6 C++) - -$(P -C++'da yapı ataması çok basit olarak derleyici tarafından halledilir, ama eşitlik karşılaştırması için bir yardım gelmez. -) - -$(C_CODE -#include <string.h> - -struct A x, y; - -inline bool operator==(const A& x, const A& y) -{ - return (memcmp(&x, &y, sizeof(struct A)) == 0); -} -... -if (x == y) - ... -) - -$(P -$(CODE operator==) işlecinin her tür için ayrı ayrı tanımlanması gerekir. Ayrıca kod içinde $(CODE ==) kullanımını görmek işleç fonksiyonu hakkında hiçbir fikir vermez, herhangi bir şekilde tanımlanmış olabilir. -) - -$(P -$(CODE memcmp) ise her durumda çalışmayabilir, çünkü yapı üyeleri arasındaki olası doldurma bitlerinin [padding] hangi değerlerde olacakları standart tarafından tanımlanmamıştır. -) - -$(P -O yüzden üyelerin teker teker karşılaştırılmaları gerekir ama bu da güvensizdir, çünkü sınıfa eklenen yeni bir üyenin bu fonksiyona da eklenmesinin unutulmaması gerekir. -) - -$(FARK_D - -$(P -D'de ise karşılaştırma da atama kadar basittir: -) - ---- -A x, y; -... -if (x == y) - ... ---- -) - - -$(FARK typedef_yeni, $(CODE typedef)'le yeni tür oluşturmak) - -$(H6 C++) -$(P -C++'da $(CODE typedef) yeni tür oluşturmaz; türe yeni bir isim verir. -) - -$(C_CODE -#define CEREZ_ILK ((Cerez)(-1)) -typedef void *Cerez; -void foo(void *); -void bar(Cerez); - -Cerez h = CEREZ_ILK; -foo(h); // farkedilmeyen kodlama hatasi -bar(h); // dogru kullanim -) - -$(P -Böyle hatalardan kurtulmak için tür bir yapı içine alınır: -) - -$(C_CODE -#define CEREZ_ILK ((void *)(-1)) -struct Cerez -{ void *isaretci; - - // ilk deger - Cerez() { isaretci = CEREZ_ILK; } - - Cerez(int i) { isaretci = (void *)i; } - - // sarmalanan tUre dOnUsUm - operator void*() { return isaretci; } -}; -void bar(Cerez); - -Cerez h; -bar(h); -h = fonksiyon(); -if (h != CEREZ_ILK) - ... -) - -$(FARK_D - -$(P -$(CODE typedef) yeni tür oluşturduğu için C++'daki gibi çözümler gerekmez. -) - ---- -typedef void* Cerez = cast(void*)-1; -void bar(Cerez); - -Cerez h; -bar(h); -h = fonksiyon(); -if (h != Cerez.init) - ... ---- -) - - -$(FARK friend, $(CODE friend) sınıflar) - -$(H6 C++) - -$(P -Bazı durumlarda başka sınıfın özel üyelerine erişmesi gereken sınıflar gerekebilir. -) - -$(C_CODE -class A -{ - private: - int a; - - public: - int foo(B *j); - friend class B; - friend int abc(A *); -}; - -class B -{ - private: - int b; - - public: - int bar(A *j); - friend class A; -}; - -int A::foo(B *j) { return j->b; } -int B::bar(A *j) { return j->a; } - -int abc(A *p) { return p->a; } -) - -$(FARK_D - -$(P -D'de ise aynı modülün içinde tanımlanmış olmak, birbirlerinin üyelerine erişebilmek için yeterlidir. Birbirleriyle yakından ilgili olan sınıfların aynı modülde bulunmaları nasıl doğalsa, birbirlerinin üyelerine erişmeleri de doğal kabul edilir. -) - ---- -module X; - -class A -{ - private: - static int a; - - public: - int foo(B j) { return j.b; } -} - -class B -{ - private: - static int b; - - public: - int bar(A j) { return j.a; } -} - -int abc(A p) { return p.a; } ---- -) - - -$(FARK islec_yukleme, İşleç yükleme [operator overloading]) - -$(H6 C++) - -$(P -Aritmetik işlemlerde kullanılabilen bir sınıf için karşılaştırma işleçleri tanımlamak isteyelim. Toplam sekiz fonksiyon gerekir: -) - -$(C_CODE -struct A -{ - int operator < (int i); - int operator <= (int i); - int operator > (int i); - int operator >= (int i); -}; - -int operator < (int i, A &a) { return a > i; } -int operator <= (int i, A &a) { return a >= i; } -int operator > (int i, A &a) { return a < i; } -int operator >= (int i, A &a) { return a <= i; } -) - -$(FARK_D - -$(P -D, karşılaştırma işleçlerinin önemini ve birbirleriyle olan doğal ilişkilerini kabul eder, ve tek bir fonksiyon gerektirir: -) - ---- -struct A -{ - int opCmp(int i); -} ---- - -$(P -Derleyici bütün $(CODE <), $(CODE <=), $(CODE >), ve $(CODE >=) işleçlerinin ve sol tarafın nesne olmadığı serbest fonksiyonların hepsini; programcının tanımladığı bu tek karşılaştırma fonksiyonundan çıkarsar. -) - -$(P -Derleyici diğer işleç yüklemelerinde de benzer yardımlar sunar ve programcının aynı sonucu elde etmesi için çok daha az kod yazması gerekir. -) - -) - - -$(FARK namespace_using, İsim alanı kısaltan $(CODE using) bildirimleri) - -$(P -D'de isim alanlarının ve başlık dosyalarının yerine modüller kullanılır. $(CODE using) bildirimleri yerine $(CODE alias)'lar vardır. -) - -$(FARK_CPP -namespace foo -{ - int x; -} -using foo::x; -) - -$(FARK_D ---- -/** foo.d modülü **/ -module foo; -int x; - -/** Başka bir modül **/ -import foo; -alias foo.x x; ---- -) - -$(P -Aslında $(CODE alias)'ın başka kullanımları da vardır: isimlere yeni adlar verir, şablon üyelerinden ve iç içe sınıflardan bahsederken kolaylık sağlar. -) - -$(FARK raii, RAII (Resource Acquisition Is Initialization)) - -$(H6 C++) - -$(P -Bozucu fonksiyonlar, kapsamlardan hata atıldığı durumlarda çıkılırken bile çağrıldıkları için; C++'da kaynaklar bozucu fonksiyonlarda geri verilmek zorundadırlar. -) - -$(C_CODE -class Dosya -{ Cerez *h; - - ~Dosya() - { - h->geri_ver(); - } -}; -) - -$(FARK_D - -$(P -Kaynak sorunlarının başında gelen bellek yönetimi D'de normalde çöp toplayıcı tarafından halledilir. Diğer bir tür kaynak, iş parçacıklarında [thread] yararlanılan bayraklar ve kilitlerdir; bunlar da normalde D dilinin iş parçacıklarına verdiği destekle halledilir. -) - -$(P -Bunların dışında gereken RAII durumlarında da $(I kapsam) [scope] sınıflarından yararlanılır. Kapsam sınıflarının bozucu fonksiyonları C++'da olduğu gibi, kapsamlardan çıkılırken çağrılırlar. -) - ---- -scope class Dosya -{ Çerez h; - - ~this() - { - h.geri_ver(); - } -} - -void deneme() -{ - if (...) - { scope f = new Dosya(); - ... - } // Kapsamdan bir hata atılarak bile çıkılmış olsa - // f.~this() bu kapama parantezinde çağrılır -} ---- -) - - -$(FARK nitelik, Nitelikler [properties]) - -$(H6 C++) - -$(P -Sınıfların $(I nitelik) olarak adlandırabileceğimiz üyeleri geleneksel olarak değerlerini okumak için $(CODE get), değerlerini değiştirmek için de $(CODE set) fonksiyonları yoluyla kullanılırlar. -) - -$(C_CODE -class Abc -{ - public: - void set_bir_nitelik(int yeni_deger) - { - bir_nitelik = yeni_deger; - } - - int get_bir_nitelik() { return bir_nitelik; } - - private: - int bir_nitelik; -}; - -Abc a; -a.set_bir_nitelik(3); -int x = a.get_bir_nitelik(); -) - -$(FARK_D - -$(P -Nitelikler üye nesne yazımıyla kullanılırlar ama atama veya okuma arka planda fonksiyonlar tarafından hallediliyor olabilir: -) - ---- -class Abc -{ - // değeri değiştir - void bir_nitelik(int yeni_değer) - { - benim_değerim = yeni_değer; - } - - // değeri döndür - int bir_nitelik() { return benim_değerim; } - - private: - int benim_değerim; -} ---- - -$(P -Sanki bir üye nesneymiş gibi kullanılır: -) - ---- -Abc a; -a.bir_nitelik = 3; // a.bir_nitelik(3) ile aynı şey -int x = a.bir_nitelik; // int x = a.bir_nitelik() ile aynı şey ---- -) - -$(P -Bu sayede, başlangıçta gerçekten üye nesne olarak tasarlanmış olan bir üyenin daha sonradan fonksiyonlar tarafından halledilmesi gerekse; kullanıcı kodunda hiçbir değişiklik yapmadan, salt sınıf tanımını değiştirmek yeterli olur. -) - - -$(FARK sablon_ozyineleme, Özyinelemeli şablonlar) - -$(H6 C++) - -$(P -Şablonların ileri düzey kullanımlarından birisi, onları özyinelemeli olarak kullanmak ve özyinelemeyi özel bir şablon tanımıyla [specialization] sonlandırmaktır. Örneğin faktöriyel hesaplayan bir şablon şöyle yazılabilir: -) - -$(C_CODE -template<int n> class faktoriyel -{ - public: - enum { sonuc = n * faktoriyel<n - 1>::sonuc }; -}; - -template<> class faktoriyel<1> -{ - public: - enum { sonuc = 1 }; -}; - -void deneme() -{ - printf("%d\n", faktoriyel<4>::sonuc); // 24 yazar -} -) - -$(FARK_D - -$(P -D'de de temelde aynıdır ama tek olan şablon üyelerinin üst kapsamda görünebilmeleri olanağı yoluyla daha basittir: -) - ---- -template faktöriyel(int n) -{ - enum { faktöriyel = n * .faktöriyel!(n-1) } -} - -template faktöriyel(int n : 1) -{ - enum { faktöriyel = 1 } -} - -void deneme() -{ - writefln("%d", faktöriyel!(4)); // 24 yazar -} ---- -) - - -$(FARK sablon_meta, Meta şablonlar) - -$(P -Problem: En az $(CODE bit_adedi) kadar bitten oluşan işaretli bir türe $(CODE typedef) yoluyla yeni bir isim verin. -) - -$(H6 C++) - -$(P -Bu kod Dr. Carlo Pescio'nun $(LINK2 http://www.eptacom.net/pubblicazioni/pub_eng/paramint.html, Template Metaprogramming: Make parameterized integers portable with this novel technique) adlı yazısından basitleştirilerek uyarlanmıştır. -) - -$(P -Derlemeyi şablon parametrelerine bağlı bir ifadenin sonucuna göre yönlendirmek C++'da mümkün değildir. Bu yüzden, şablonu ifadenin çeşitli değerleri için özel olarak tanımlamak gerekir. Daha kötüsü, özel tanımları "küçüktür veya eşittir" gibi koşullara göre yapmak da mümkün olmadığı için, ifade değerini bir arttırmak gibi özyinelemeli yöntemlerden yararlanmak gerekir. Derleyicinin uygun bir özel tanım bulamaması durumunda da ya derleyici göçer, ya da anlaşılması hemen hemen imkansız bir hata mesajı verilir. -) - -$(P -C++'da şablon $(CODE typedef)'leri olmadığı için de bazen makrolardan da yararlanmak gerekebilir. -) - -$(C_CODE -#include <limits.h> - -template< int bit_adedi > struct Sayi -{ - typedef Sayi< bit_adedi + 1 > :: int_tUrU int_tUrU ; -} ; - -struct Sayi< 8 > -{ - typedef signed char int_tUrU ; -} ; - -struct Sayi< 16 > -{ - typedef short int_tUrU ; -} ; - -struct Sayi< 32 > -{ - typedef int int_tUrU ; -} ; - -struct Sayi< 64 > -{ - typedef long long int_tUrU ; -} ; - -// Temel turlerin istenen bit adedini tam karsilamadigi -// durumlarda, bu metaprogram bit_adedi'ni bir derleyici -// hatasi olusana veya INT_MAX'e varilana kadar oteler. -// Sablonun INT_MAX ozel tanimi int_tUrU'nu tanimlamadigi -// icin de sonunda mutlaka bir derleme hata mesaji alinir. -struct Sayi< INT_MAX > -{ -} ; - -// Yazim kolayligi acisindan -#define Sayi( bit_adedi ) Sayi< bit_adedi > :: int_tUrU - -#include <stdio.h> - -int main() -{ - Sayi( 8 ) i ; - Sayi( 16 ) j ; - Sayi( 29 ) k ; - Sayi( 64 ) l ; - printf("%d %d %d %d\n", - sizeof(i), sizeof(j), sizeof(k), sizeof(l)); - return 0 ; -} -) - -$(H6 Boost'un C++ çözümü) - -$(P -Bu da David Abrahams'ın $(LINK2 http://boost.org, Boost kütüphanesini) kullanan çözümü: -) - -$(C_CODE -#include <boost/mpl/if.hpp> -#include <boost/mpl/assert.hpp> - -template <int bit_adedi> struct Sayi - : mpl::if_c<(bit_adedi <= 8), signed char - , mpl::if_c<(bit_adedi <= 16), short - , mpl::if_c<(bit_adedi <= 32), long - , long long>::type >::type > -{ - BOOST_MPL_ASSERT_RELATION(bit_adedi, <=, 64); -} - -#include <stdio.h> - -int main() -{ - Sayi< 8 > i ; - Sayi< 16 > j ; - Sayi< 29 > k ; - Sayi< 64 > l ; - printf("%d %d %d %d\n", - sizeof(i), sizeof(j), sizeof(k), sizeof(l)); - return 0 ; -} -) - -$(FARK_D - -$(P -Özyinelemeli şablonlar kullanılabilse de, D'de çok daha iyi bir yol vardır ve C++'daki çözümlere kıyasla burada neler olduğunu anlamak da çok kolaydır. Derlenmesi çok hızlıdır, ve hata mesajları da çok daha anlaşılırdır: -) - ---- -import std.stdio; - -template Sayi(int bit_adedi) -{ - static if (bit_adedi <= 8) - alias byte Sayi; - else static if (bit_adedi <= 16) - alias short Sayi; - else static if (bit_adedi <= 32) - alias int Sayi; - else static if (bit_adedi <= 64) - alias long Sayi; - else - static assert(0); -} - -int main() -{ - Sayi!(8) i ; - Sayi!(16) j ; - Sayi!(29) k ; - Sayi!(64) l ; - writefln("%d %d %d %d", - i.sizeof, j.sizeof, k.sizeof, l.sizeof); - return 0; -} ---- -) - - -$(FARK tur_ozellik, Tür özellikleri [type traits]) - -$(P -Tür özellikleri derleme zamanında kullanışlıdırlar. -) - -$(H6 C++) - -$(P -Parametresinin bir fonksiyon olup olmadığını anlayan bu şablon, David Vandevoorde ve Nicolai M. Josuttis'in $(LINK2 http://www.amazon.com/exec/obidos/ASIN/0201734842/ref=ase_classicempire/102-2957199-2585768, C++ Templates: The Complete Guide) kitaplarının 353'üncü sayfasından uyarlanmıştır: -) - -$(C_CODE -template<typename T> class Fonksiyondur -{ - private: - typedef char Bir; - typedef struct { char a[2]; } Iki; - template<typename U> static Bir oyle_mi(...); - template<typename U> static Iki oyle_mi(U (*)[1]); - public: - enum { - Evet = sizeof(Fonksiyondur<T>::oyle_mi<T>(0)) == 1 - }; -}; - -void deneme() -{ - typedef int (fp)(int); - - assert(Fonksiyondur<fp>::Evet == 1); -} -) - -$(P -Bu çözüm $(LINK2 http://www.dlang.org/glossary.html#sfinae, SFINAE (Substitution Failure Is Not An Error)) kuralından yararlanır ve oldukça ileri düzey şablon olanakları kullanır. $(I [Çevirenin notu: C++ dilinin bir özelliği olan SFINAE, şablon parametresi çıkarsarken yasal olmayan türlerle karşılaşıldığında derleyicinin hata vermek yerine, o özel tanımı gözardı edeceğini söyleyen kuraldır.]) -) - -$(FARK_D - -$(P -SFINAE D'de çok basit bir yolla yapılabilir: -) - ---- -template Fonksiyondur(T) -{ - static if ( is(T[]) ) - const int Fonksiyondur = 0; - else - const int Fonksiyondur = 1; -} - -void deneme() -{ - typedef int fp(int); - - assert(Fonksiyondur!(fp) == 1); -} ---- - -$(P -Daha da iyisi, bir türün bir fonksiyon olup olmadığını anlamak için şablon yöntemlerine başvurmaya gerek de yoktur. D'nin $(CODE is) ifadesi bu soruyu doğrudan yanıtlar: -) - ---- -void deneme() -{ - alias int fp(int); - - assert( is(fp == function) ); -} ---- -) - -Macros: - SUBTITLE=C++ ile Farkları - - DESCRIPTION=D programlama dilinin C++ dilinden nesne yönelimli ve başka olanakları açısından farklılıkları - - KEYWORDS=d programlama dili tanıtım bilgi karşılaştırma c++ cpp dili fark diff --git a/ddili/src/tanitim/fark_dizgi.d b/ddili/src/tanitim/fark_dizgi.d deleted file mode 100644 index fcc636a..0000000 --- a/ddili/src/tanitim/fark_dizgi.d +++ /dev/null @@ -1,535 +0,0 @@ -Ddoc - -$(H4 Dizgilerin [string] C++ Dizgileri ile Karşılaştırılması) - -$(ESKI_KARSILASTIRMA) - -$(P -Dizgiler C++'da olduğu gibi kütüphanelerle de halledilebilirler. Bu bölümde D'de dizgilerin neden dilin bir iç olanağı olduklarını ve bunun getirdiği yararlar gösteriliyor. -) - -$(P -Bu sayfadaki bilgiler $(LINK2 http://www.dlang.org/cppstrings.html, Digital Mars'ın sitesindeki aslından) alınmıştır. -) - -$(H6 Birleştirme işleci) - -$(P -C++'da dizgiler mevcut işleçleri kullanmak zorundadırlar. Bu konuda en uygun işleçler $(CODE +) ve $(CODE +=) işleçleridir. Bunun zararlarından birisi, $(CODE +) işlecinin doğal anlamının "toplama işlemi" olmasıdır. Kod okunurken işlemin "toplama" mı yoksa "birleştirme" mi olduğunu anlamak için nesnelerin türlerinin ne olduklarının araştırılması gerekebilir. -) - -$(P -Ek olarak, $(CODE double) dizileri için yüklenmiş olan bir $(CODE +) işlecinin dizilerle vektör toplamı mı yapacağı, yoksa dizileri mi birleştireceği açık değildir. -) - -$(P -Bu sorunlar D'de dizilerle çalışan yeni $(CODE ~) "birleştirme" işleciyle giderilir. "Sonuna ekleme" işleci de $(CODE ~=) işlecidir. Böylece artık $(CODE double) dizilerinde de karışıklık kalmamış olur: $(CODE ~) işleci dizilerin birleştirilecekleri, $(CODE +) işleci de vektör toplamlarının alınacağı anlamına gelir. Eklenen bu yeni işleç dizilerde tutarlılık da sağlamış olur. (D'de $(I dizgi) karakter dizisidir; özel bir tür değildir.) -) - -$(H6 Söz dizimi uyumluluğu) - -$(P -C++'da işleç yüklemenin çalışabilmesi için işlenenlerden birisinin yüklenebilen bir tür olması gerekir. O yüzden, C++'daki $(CODE string) her ifadeyle kullanılamaz: -) - -$(C_CODE -const char abc[5] = "dünya"; -string str = "merhaba" + abc; -) - -$(P -Onun çalışabilmesi için dizgi kavramının dilin bir iç olanağı olması gerekir: -) - ---- -const char abc[5] = "dünya"; -string str = "merhaba" ~ abc; ---- - -$(H6 Söz dizimi tutarlılığı) - -$(P -Bir C++ dizgisinin uzunluğunu bulmanın üç yolu vardır: -) - -$(C_CODE -const char abc[] = "dünya"; : sizeof(abc)/sizeof(abc[0])-1 - : strlen(abc) -string str; : str.length() -) - -$(P -O tutarsızlık da şablonlarda sorunlar doğurur. D'nin çözümü tutarlıdır: -) - ---- -char[5] abc = "dünya"; : abc.length -char[] str : str.length ---- - -$(H6 Boş dizgiler) - -$(P -C++'da $(CODE string)'lerin boş olup olmadıkları bir fonksiyonla belirlenir: -) - -$(C_CODE -string str; -if (str.empty()) - // bos dizgi -) - -$(P -D'de boş dizgilerin uzunlukları sıfırdır: -) - ---- -char[] str; -if (!str.length) - // boş dizgi ---- - - -$(H6 Dizgi uzunluğunu değiştirmek) - -$(P -C++'da $(CODE resize()) fonksiyonu kullanılır: -) - -$(C_CODE -string str; -str.resize(yeni_uzunluk); -) - -$(P -D'de dizgiler dizi oldukları için $(CODE length) niteliklerini değiştirmek yeterlidir: -) - ---- -char[] str; -str.length = yeni_uzunluk; ---- - -$(H6 Dizgi dilimlemek [slicing]) - -$(P -C++'da bir kurucu fonksiyon kullanılır: -) - -$(C_CODE -string s1 = "merhaba dünya"; -string s2(s1, 8, 5); // s2 "dünya" olur -) - -$(P -D'de dizi dilimleme kullanılır: -) - ---- -string s1 = "merhaba dünya"; -string s2 = s1[8 .. 13]; // s2 "dünya" olur ---- - -$(H6 Dizgiler arası kopyalama) - -$(P -C++'ta dizgiler $(CODE replace) fonksiyonu ile kopyalanırlar: -) - -$(C_CODE -string s1 = "merhaba dünya"; -string s2 = "güle güle "; -s2.replace(10, 5, s1, 8, 5); // s2 "güle güle dünya" olur -) - -$(P -D'de ise sol tarafta dilimleme kullanılır: -) - ---- -char[] s1 = "merhaba dünya".dup; -char[] s2 = "güle güle ".dup; -s2[10..15] = s1[8..13]; // s2 "güle güle dünya" olur ---- - -$(P -D'de dizgi sabitleri değiştirilemedikleri için önce $(CODE .dup) ile kopyalarını almak gerekti. -) - - -$(H6 C dizgilerine dönüştürmek) - -$(P -C kütüphaneleriyle etkileşmek için gereken dönüşümler C++'da $(CODE c_str()) ile yapılır. -) - -$(C_CODE -void foo(const char *); -string s1; -foo(s1.c_str()); -) - -$(P -D'de $(CODE .ptr) niteliği ile: -) - ---- -void foo(char*); -char[] s1; -foo(s1.ptr); ---- - -$(P -Ama bunun çalışabilmesi için $(CODE s1)'in sonunda bir sonlandırma karakteri bulunması gerekir. Sonlandırma karakterinin bulunduğunu garanti eden $(CODE std.string.toStringz) fonksiyonu da kullanılabilir: -) - ---- -void foo(char*); -char[] s1; -foo(std.string.toStringz(s1)); ---- - -$(H6 Dizgi dışına taşmaya karşı denetim) - -$(P -C++'da $(CODE []) işleci dizginin sınırları dışına çıkılıp çıkılmadığını denetlemez. Bu denetim D'de varsayılan davranış olarak bulunur, ve programın hataları giderildikten sonra bir derleyici ayarıyla kapatılabilir. -) - - -$(H6 $(CODE switch) deyimlerinde dizgiler) - -$(P -Bu C++'da olanaksızdır ve kütüphaneyi daha büyütmek dışında bir yolu da yoktur. D'de ise doğaldır: -) - ---- -switch (str) -{ - case "merhaba": - case "dünya": - ... -} ---- - -$(P -$(CODE case)'lerle sabit dizgiler yanında $(CODE char[10]) gibi sabit karakter dizileri veya $(CODE char[]) gibi dinamik diziler de kullanılabilir. -) - -$(H6 Birden fazla dizgi karakterini bir karakterle değiştirmek) - -$(P -C++'ta $(CODE replace()) fonksiyonu ile yapılır: -) - -$(C_CODE -string str = "merhaba"; -str.replace(1,2,2,'?'); // str "m??haba" olur -) - -$(P -D'de dizi dilimleme kullanılır: -) - ---- -char[5] str = "merhaba"; -str[1..3] = '?'; // str "m??haba" olur ---- - - -$(H6 Değerler ve referanslar) - -$(P -C++ dizgileri, STLport'ta gerçekleştirildikleri gibi, $(I değer türleridirler) [value type] ve $(CODE '\0') karakteri ile sonlandırılmışlardır. [Aslında sonlandırma karakteri STLport'un bir seçimidir; öyle olmak zorunda değildir.] Çöp toplamanın eksikliği nedeniyle bunun bazı bedelleri vardır. Öncelikle, oluşturulan her $(CODE string)'in karakterlerin kendisine ait bir kopyasını tutması gerekir. Karakterlerin hangi $(CODE string) tarafından sahiplenildiğinin hesabının tutulması gerekir, çünkü sahip ortadan kalktığında bütün referanslar da geçersiz hale gelirler. Bu $(I referansların geçersizliği) [dangling reference] sorunundan kurtulmak amacıyla $(CODE string)'lerin $(I değer türleri) olarak kullanılmaları gerektiği için; çok sayıdaki bellek ayırma, karakter kopyalama, ve bellek geri verme işlemleri nedeniyle bu tür yavaş hale gelir. Dahası, sonlandırma karakteri dizgilerin diğer dizgileri referans olarak göstermelerine engeldir. $(I Data segment)'teki, program yığıtındaki, vs. karakterlere referans kullanılamaz. -) - -$(P -D'de dizgiler $(I referans türleridirler) ve bellek çöp toplamalıdır. Bu yüzden yalnızca referansların kopyalanmaları gerekir, karakterlerin değil... Karakterler nerede olurlarsa olsunlar D dizgileri onları referans olarak gösterebilir: $(I static data segment), program yığıtı, başka dizgilerin bir bölümü, nesneler, dosya ara bellekleri, vs. Dizgideki karakterlerin sahibinin hesabını tutmaya gerek yoktur. -) - -$(P -Bu noktadaki doğal soru; birden fazla dizginin ortaklaşa karakter kullandıkları durumda dizgilerden birisinde değişiklik yapıldığında ne olacağıdır. Karakterleri gösteren bütün dizgiler değişmiş olurlar. Bunun istenmediği durumda $(I yazınca kopyalama) [copy-on-write] yöntemi kullanılabilir. Bu yöntemde, dizgide değişiklik yapılmadan önce karakterlerin o dizgiye ait bir kopyası alınır ve değişiklik o kopyada yapılır. -) - -$(P -D dizgilerinin referans türleri ve çöp toplamalı olmaları, dizgilerle yoğun işlemler yapan lzw sıkıştırma algoritması gibi işlemlerde hem hızda hem de bellek kullanımında kazanç sağlar. -) - -$(H6 Hız karşılaştırması) - -$(P -Bir dosyadaki sözcüklerin histogramını alan küçük bir programa bakalım. Bu program D'de şöyle yazılabilir: -) - ---- -import std.file; -import std.stdio; - -int main (char[][] argümanlar) -{ - int kelime_toplamı; - int satır_toplamı; - int karakter_toplamı; - int[char[]] sözlük; - - writefln(" satır kelime bayt dosya"); - for (int i = 1; i < argümanlar.length; ++i) - { - char[] giriş; - int kelime_top, satır_top, karakter_top; - int kelime_içindeyiz; - int kelime_başı; - - giriş = cast(char[])std.file.read(argümanlar[i]); - - for (int j = 0; j < giriş.length; j++) - { char karakter; - - karakter = giriş[j]; - if (karakter == '\n') - ++satır_top; - if (karakter >= '0' && karakter <= '9') - { - } - else if (karakter >= 'a' && karakter <= 'z' || - karakter >= 'A' && karakter <= 'Z') - { - if (!kelime_içindeyiz) - { - kelime_başı = j; - kelime_içindeyiz = 1; - ++kelime_top; - } - } - else if (kelime_içindeyiz) - { char[] kelime = giriş[kelime_başı .. j]; - - sözlük[kelime]++; - kelime_içindeyiz = 0; - } - ++karakter_top; - } - if (kelime_içindeyiz) - { char[] kelime = giriş[kelime_başı .. giriş.length]; - sözlük[kelime]++; - } - writefln("%8s%8s%8s %s", - satır_top, kelime_top, karakter_top, - argümanlar[i]); - satır_toplamı += satır_top; - kelime_toplamı += kelime_top; - karakter_toplamı += karakter_top; - } - - if (argümanlar.length > 2) - { - writefln("--------------------------------------\n" - "%8s%8s%8s toplam", - satır_toplamı, - kelime_toplamı, - karakter_toplamı); - } - - writefln("--------------------------------------"); - - foreach (const char[] kelime; sözlük.keys.sort) - { - writefln("%3d %s", sözlük[kelime], kelime); - } - return 0; -} ---- - -$(P -(Ara bellekli giriş/çıkış kullanan $(LINK2 http://www.dlang.org/wc.html, başka bir gerçekleştirmesi) de var.) -) - -$(P -İki programcı bu programın C++ gerçekleştirmelerini de yazdılar: $(LINK2 http://groups.google.com/groups?q=g:thl953709878d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=bjacrl%244un%2401%241%40news.t-online.com, wccpp1) ve $(LINK2 #wccpp2, wccpp2). Programa giriş olarak "Alice Harikalar Diyarında"nın metnini oluşturan $(LINK2 http://www.gutenberg.org/dirs/etext91/alice30.txt, alice30.txt) verildi. D derleyicisi olarak $(LINK2 http://ftp.digitalmars.com/dmd.zip, dmd) ve C++ derleyicisi olarak da $(LINK2 http://ftp.digitalmars.com/dmc.zip, dmc) kullanıldı. Bu iki derleyici aynı eniyileştiriciyi ve aynı kod üreticiyi kullandıkları için, bu karşılaştırma iki dildeki dizgilerin daha gerçekçi bir karşılaştırmasını verir. Programlar bir Windows XP sisteminde denendiler ve dmc için şablon gerçekleştirmesi olarak STLport kullanıldı. -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgramDerleme KomutuDerleme SüresiÇalıştırma KomutuÇalışma Süresi
D wcdmd wc -O -release0.0719wc alice30.txt >log0.0326
C++ wccpp1dmc wccpp1 -o -I\dm\stlport\stlport2.1917wccpp1 alice30.txt >log0.0944
C++ wccpp2dmc wccpp2 -o -I\dm\stlport\stlport2.0463wccpp2 alice30.txt >log0.1012
- - -$(P -Aşağıdaki sonuçlar ise Linux üzerinde ve yine aynı eniyileştiriciyi ve kod üreticiyi paylaşan iki derleyici ile alındılar: D için $(LINK2 http://home.earthlink.net/~dvdfrdmn/d, gdc) ve C++ için g++. Bu seferki sistem gcc 3.4.2'li bir RedHat Linux 8.0 çalıştıran 800MHz'lik bir Pentium III'tü. Ek bilgi açısından Digital Mars'ın derleyicisi dmd'nin sonuçları da eklenmiştir. -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgramDerleme KomutuDerleme SüresiÇalıştırma KomutuÇalışma Süresi
D wcgdc -O2 -frelease -o wc wc.d0.326wc alice30.txt > /dev/null0.041
D wcdmd wc -O -release0.235wc alice30.txt > /dev/null0.041
C++ wccpp1g++ -O2 -o wccpp1 wccpp1.cc2.874wccpp1 alice30.txt > /dev/null0.086
C++ wccpp2g++ -O2 -o wccpp2 wccpp2.cc2.886wccpp2 alice30.txt > /dev/null0.095
- -$(P -Aşağıdaki sonuçlar da gcc 3.4.2'li bir MacOS X 10.3.5 çalıştıran bir PowerMac G5 2x2.0GHz ile alınmışlardır. (Burada ölçümlerin doğruluğu biraz daha düşüktü.) -) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProgramDerleme KomutuDerleme SüresiÇalışma KomutuÇalışma Süresi
D wcgdc -O2 -frelease -o wc wc.d0.28wc alice30.txt > /dev/null0.03
C++ wccpp1g++ -O2 -o wccpp1 wccpp1.cc1.90wccpp1 alice30.txt > /dev/null0.07
C++ wccpp2g++ -O2 -o wccpp2 wccpp2.cc1.88wccpp2 alice30.txt > /dev/null0.08
- -$(HR) -
Allan Odgaard'ın wccpp2 programı:
- -$(C_CODE -#include <algorithm> -#include <cstdio> -#include <fstream> -#include <iterator> -#include <map> -#include <vector> - -bool isWordStartChar (char c) { return isalpha(c); } -bool isWordEndChar (char c) { return !isalnum(c); } - -int main (int argc, char const* argv[]) -{ - using namespace std; - printf("Lines Words Bytes File:\n"); - - map<string, int> dict; - int tLines = 0, tWords = 0, tBytes = 0; - for(int i = 1; i < argc; i++) - { - ifstream file(argv[i]); - istreambuf_iterator<char> from(file.rdbuf()), to; - vector<char> v(from, to); - vector<char>::iterator first = v.begin(), - last = v.end(), - bow, eow; - - int numLines = count(first, last, '\n'); - int numWords = 0; - int numBytes = last - first; - - for(eow = first; eow != last; ) - { - bow = find_if(eow, last, isWordStartChar); - eow = find_if(bow, last, isWordEndChar); - if(bow != eow) - ++dict[string(bow, eow)], ++numWords; - } - - printf("%5d %5d %5d %s\n", - numLines, numWords, numBytes, argv[i]); - - tLines += numLines; - tWords += numWords; - tBytes += numBytes; - } - - if(argc > 2) - printf("-----------------------\n%5d %5d %5d\n", - tLines, tWords, tBytes); - printf("-----------------------\n\n"); - - for(map<string, int>::const_iterator it = dict.begin(); - it != dict.end(); ++it) - printf("%5d %s\n", it->second, it->first.c_str()); - - return 0; -} -) - - - - - -Macros: - SUBTITLE=D Dizgilerinin [string] C++ ile Farkları - - DESCRIPTION=D programlama dilindeki dizgilerin C++ dilinden farklılıkları - - KEYWORDS=d programlama dili tanıtım bilgi karşılaştırma c++ cpp dili dizgi string diff --git a/ddili/src/tanitim/fark_karmasik.d b/ddili/src/tanitim/fark_karmasik.d deleted file mode 100644 index 1995441..0000000 --- a/ddili/src/tanitim/fark_karmasik.d +++ /dev/null @@ -1,227 +0,0 @@ -Ddoc - -$(H4 Karmaşık Sayı Türleri ve C++'nın std::complex'i) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu sayfadaki bilgiler digitalmars.com sitesindeki eski bir yazıdan alınmıştır -) - -$(H6 Yazım güzelliği) - -$(P -C++'ta karmaşık sayı türleri şunlardır: -) - -$(C_CODE -complex<float> -complex<double> -complex<long double> -) - -$(P -C++'da sanal [imaginary] türler yoktur. D'de 3 karmaşık türe ek olarak 3 tane de sanal tür vardır: -) - ---- -cfloat -cdouble -creal -ifloat -idouble -ireal ---- - -$(P -C++'nın karmaşık sayıları aritmetik sabitlerle etkileşebilirler, ama sanal değer kavramı olmadığı için sanal değerler eklemek için kurucu fonksiyon kullanmak gerekir: -) - -$(C_CODE -complex<long double> a = 5; // a = 5 + 0i -complex<long double> b(0,7); // b = 0 + 7i -c = a + b + complex<long double>(0,7); // c = 5 + 14i -) - -$(P -D'de sanal değer sabitlerinin sonuna $(CODE i) karakteri eklenir. Yukarıdaki kodun eşdeğeri şudur: -) - ---- -creal a = 5; // a = 5 + 0i -ireal b = 7i; // b = 7i -c = a + b + 7i; // c = 5 + 14i ---- - -$(P -İçinde sabitlerin de olduğu daha uzun bir ifade: -) ---- -c = (6 + 2i - 1 + 3i) / 3i; ---- - -$(P -C++'da şöyle yazılır: -) - -$(C_CODE -c = (complex<double>(6,2) + complex<double>(-1,3)) - / complex<double>(0,3); -) - -$(P -veya C++'ya da bir sanal tür eklenmiş olsaydı şöyle: -) - -$(C_CODE -c = (6 + imaginary<double>(2) - 1 + imaginary<double>(3)) - / imaginary<double>(3); -) - -$(P -Kısacası, D'de nn gibi bir $(I sanal değer) $(CODE nni) olarak yazılabilir; $(CODE complex<long double>(0,nn)) şeklinde nesne oluşturmaya gerek yoktur. -) - -$(H6 İşlemlerin etkinliği) - -$(P -C++'da sanal sayı türünün olmaması, 0 gerçel sayısının da işlemlerde gereksizce kullanılmasına neden olur. Örneğin D'de iki sanal değerin toplamı tek bir toplama işleminden oluşur: -) - ---- -ireal a, b, c; -c = a + b; ---- - -$(P -C++'da ise gerçel kısımlarının da toplandığı iki toplama işlemidir: -) - -$(C_CODE -c.re = a.re + b.re; -c.im = a.im + b.im; -) - -$(P -Durum çarpma işleminde daha da kötüdür: tek bir çarpma işlemi yerine 4 çarpma ve 2 toplama işlemi: -) - -$(C_CODE -c.re = a.re * b.re - a.im * b.im; -c.im = a.im * b.re + a.re * b.im; -) - -$(P -En kötüsü de bölme işlemidir: D'deki tek bir bölme işlemi yerine, C++'da normal bir gerçekleştirmede 1 karşılaştırma, 3 bölme, 3 çarpma, ve 3 toplama işlemi vardır: -) - -$(C_CODE -if (fabs(b.re) < fabs(b.im)) -{ - r = b.re / b.im; - den = b.im + r * b.re; - c.re = (a.re * r + a.im) / den; - c.im = (a.im * r - a.re) / den; -} -else -{ - r = b.im / b.re; - den = b.re + r * b.im; - c.re = (a.re + r * a.im) / den; - c.im = (a.im - r * a.re) / den; -} -) - -$(P -Bu yavaşlıkları önlemek için sanal değer yerine C++'da $(CODE double) kullanılabilir. Örneğin D'deki şu kod: -) - ---- -cdouble c; -idouble im; -c *= im; ---- - -$(P -sanal kısım için $(CODE double) kullanılarak C++'da şöyle yazılabilir: -) - -$(C_CODE -complex<double> c; -double im; -c = complex<double>(-c.imag() * im, c.real() * im); -) - -$(P -Ama onun sonucunda da aritmetik işlemlerle kullanılabilen bir kütüphane türü kolaylığını artık geride bırakmış oluruz. -) - -$(H6 Anlam) - -$(P -Bütün bunların en kötüsü, sanal değer türünün eksikliği nedeniyle istemeden yanlış sonuçlar elde edilebilmesidir. $(LINK2 http://www.cs.berkeley.edu/~wkahan/, Prof. Kahan)'dan bir alıntı: -) - -$(QUOTE -$(P -Fortran'da ve C/C++ derleyicileriyle gelen kütüphanelerde gereken karmaşık sayı fonksiyonları $(CODE SQRT) ve $(CODE LOG), eğer IEEE 754 aritmetik işlemlerinde $(CODE 0.0)'ın işaretini gözardı edecek şekilde gerçekleştirilirlerse, karmaşık sayı $(CODE z)'nin negatif gerçel değer aldığı durumlarda artık -) - -$(C_CODE -SQRT( CONJ( Z ) ) = CONJ( SQRT( Z ) ) -) - -$(P -ve -) - -$(C_CODE -LOG( CONJ( Z ) ) = CONJ( LOG( Z ) ) -) - -$(P -eşitlikleri sağlanamaz. Karmaşık sayı aritmetiğinde işlemlerin gerçel ve sanal sayıların $(CODE x + i*y) şeklindeki soyutlamaları yerine (x, y) gibi çiftler üzerinde yapıldıkları durumlarda bu tür bozuklukların önüne geçmek imkansızdır. Karmaşık sayı işlemlerinde çiftlerin kullanılması $(I yanlıştır); bir sanal türe ihtiyaç vardır. -) -) - -$(P -Buradaki anlamsal sorunlar şunlardır: -) - -$(UL -$(LI -$(CODE (sonsuz + i)) sonucunu veren $(CODE (1 - sonsuz*i) * i) formülünü ele alalım. Burada ikinci çarpan tek başına $(CODE i) olmak yerine $(CODE (0 + i)) olursa, sonuç $(CODE (sonsuz + NaN*i)) haline gelir; yani yanlış bir NaN oluşmuş olur. $(I [Çevirenin notu: "not a number"'ın kısaltması olan NaN, geçersiz sayı değerlerini ifade eder.]) -) - -$(LI -Ayrı bir sanal tür, 0'ın işaretini de taşıyabilir; bunlar $(I branch cut)'larla ilgili hesaplarda gerekirler. -) - -) - -$(P -C99 standardının Appendix G bölümü bu sorunla ilgili olarak bazı tavsiyelerde bulunur. Ancak o tavsiyeler C++98 standardına dahil olmadıklarından taşınabilirliklerine güvenilemez. -) - -$(H6 Kaynaklar) - -$(UL -$(LI -$(LINK2 http://www.cs.berkeley.edu/~wkahan/JAVAhurt.pdf, How Java's Floating-Point Hurts Everyone Everywhere), Prof. W. Kahan and Joseph D. Darcy -) - -$(LI -$(LINK2 http://www.cs.berkeley.edu/~wkahan/Curmudge.pdf, The Numerical Analyst as Computer Science Curmudgeon), Prof. W. Kahan -) - -$(LI -"Branch Cuts for Complex Elementary Functions, or Much Ado About Nothing's Sign Bit" by W. Kahan, ch. 7 in The State of the Art in Numerical Analysis (1987) ed. by M. Powell and A. Iserles for Oxford U.P. -) -) - -Macros: - SUBTITLE=İç Olanak veya Kütüphane - - DESCRIPTION=D programlama dili olanaklarının dil içinde mi yoksa kütüphanelerde mi gerçekleştirildikleri - - KEYWORDS=d programlama dili tanıtım bilgi iç olanak kütüphane gerçekleştirme diff --git a/ddili/src/tanitim/fark_onislemci.d b/ddili/src/tanitim/fark_onislemci.d deleted file mode 100644 index a05e29e..0000000 --- a/ddili/src/tanitim/fark_onislemci.d +++ /dev/null @@ -1,686 +0,0 @@ -Ddoc - -$(H4 D Olanaklarının C Önişlemcisiyle [preprocessor] Karşılaştırılması) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu sayfadaki bilgiler $(LINK2 http://www.dlang.org/pretod.html, Digital Mars'ın sitesindeki aslından) alınmıştır. -) - -$(P -C'nin yaratıldığı günlerde derleyici teknolojisi henüz çok geriydi. Derleme işleminin öncesine metin makroları işleyen bir adım eklemek, çok kolay bir ek olması yanında programcıya büyük kolaylıklar da sunmuştu. Fakat programların boyutları ve karmaşıklıkları arttıkça önişlemci olanaklarının üstesinden gelinemeyecek sorunları da ortaya çıkmaya başlamıştır. D'de önişlemci bulunmaz, ama başka olanakları sayesinde aynı sorunları daha etkin olarak çözer. -) - -$(UL_FARK -$(FARK_INDEX baslik, Başlık dosyaları) -$(FARK_INDEX pragma_once, #pragma once) -$(FARK_INDEX pragma_pack, #pragma pack) -$(FARK_INDEX makro, Makrolar) -$(FARK_INDEX duruma_gore, Derlemeyi duruma göre yönlendirmek [conditional compilation]) -$(FARK_INDEX kod_tekrari, Kod tekrarını azaltmak) -$(FARK_INDEX error_assert, $(CODE #error) ve derleme zamanı denetimleri) -$(FARK_INDEX katma, Şablon katmaları [mixin]) -) - -$(FARK baslik, Başlık dosyaları) - -$(H6 C) - -$(P -Başlık dosyalarını oldukları gibi metin halinde $(I $(CODE #include) etmek), C ve C++'da çok gündelik işlemlerdir. Bu yüzden derleyici başlık dosyalarında bulunan on binlerce satırı her seferinde baştan derlemek zorunda kalır. Başlık dosyalarının asıl yapmaya çalıştıkları; metin olarak eklenmek yerine, derleyiciye bazı isimleri bildirmektir. Bu D'de $(CODE import) deyimiyle yapılır. Bu durumda daha önceden zaten derlenmiş olan isimler öğrenilmiş olurlar. Böylelikle başlıkların birden fazla sayıda eklenmelerini önlemek için $(CODE #ifdef)'ler, $(CODE #pragma once)'lar, veya kullanımları çok kırılgan olan $(I precompiled headers) gibi çözümler gerekmemiş olur. -) - -$(C_CODE -#include <stdio.h> -) - -$(FARK_D ---- -import std.c.stdio; ---- -) - -$(FARK pragma_once, #pragma once) - -$(H6 C) - -$(P -Başlık dosyalarının birden fazla $(CODE #include) edilmelerini önlemek gerekir. Bunun için başlık dosyalarına ya şu satır eklenir: -) - -$(C_CODE -#pragma once -) - -$(P -ya da daha taşınabilir olarak: -) - -$(C_CODE -#ifndef __STDIO_INCLUDE -#define __STDIO_INCLUDE -... dosya içeriği -#endif -) - -$(FARK_D -$(P -Tamamen gereksizdir; çünkü D, isimleri $(I sembol) olarak ekler. $(CODE import) bildirimi ne kadar çok yapılmış olursa olsun, isimler yalnızca bir kere eklenirler. -) -) - -$(FARK pragma_pack, #pragma pack) - -$(H6 C) - -$(P -Yapı üyelerinin bellekte nasıl yerleştirileceklerini belirler. -) - -$(FARK_D - -$(P -D sınıflarında üyelerin bellek yerleşimlerine karışmak gerekmez; derleyici üyelerin ve hatta yerel değişkenlerin sıralarını istediği gibi değiştirme hakkına sahiptir; ama dış veri yapılarına denk olmaları gerekebileceği için, D'de yapı üyelerinin yerleşimleri de ayarlanabilir: -) - ---- -struct Foo -{ - align (4): // 4 baytlık yerleşim kullanılır - ... -} ---- -) - -$(FARK makro, Makrolar) - -$(P -C'de önişlemci makroları esneklik de sağlayan çok güçlü olanaklardır; ama zayıflıkları da vardır: -) - -$(UL - -$(LI -Makroların kapsamları yoktur; tanımlandıkları noktadan kaynak dosyanın sonuna kadar etkilidirler. Etkileri .h dosyalarının aşar, ve içteki kapsamlara da geçer. $(CODE #include) edilmiş olan on binlerce satırda içinde tanımlanmış olan makroların etkileri bazen istenmeyen sonuçlar doğurur. -) - -$(LI -Hata ayıklayıcının makrolardan haberi yoktur. Hata ayıklayıcının gördüğü kod, makroların açılmış halleri olduğu için; programcının gördüğü ile aynı değildir. -) - -$(LI -Makrolar simgelerin ayrıştırılmalarını olanaksızlaştırabilirler; çünkü daha önce tanımlanmış bir makro, kodu bütünüyle değiştirmiş olabilir. -) - -$(LI -Makroların bütünüyle metin temelli olmaları kullanımlarının tutarsız olmalarına ve dolayısıyla hataya açık olmalarına yol açar. (C++ şablonları bir ölçüde bunları önler.) -) - -$(LI -Makroların görevlerinden birisi dildeki eksiklikleri gidermektir. Bunu başlık dosyalarını sarmalayan $(CODE #ifdef/#endif) satırlarında görüyoruz. -) - -) - -$(P -Makroların temel kullanımları ve bunların D'deki karşılıkları şöyledir: -) - -$(OL - -$(LI Sabit değerler - -$(FARK_C -#define DEGER 5 -) - -$(FARK_D ---- -const int DEĞER = 5; ---- -) - -) - -$(LI Bir dizi sabit değer veya bayrak değerleri tanımlamak - -$(FARK_C -int bayraklar: -#define BAYRAK_X 0x1 -#define BAYRAK_Y 0x2 -#define BAYRAK_Z 0x4 -... -bayraklar |= BAYRAK_X; -) - -$(FARK_D ---- -enum BAYRAK { X = 0x1, Y = 0x2, Z = 0x4 }; -BAYRAK bayraklar; -... -bayraklar |= BAYRAK.X; ---- -) - -) - -$(LI ASCII (char) veya evrensel (wchar) karakterlere göre derlemek - -$(FARK_C -#if UNICODE - #define dchar wchar_t - #define TEXT(s) L##s -#else - #define dchar char - #define TEXT(s) s -#endif - -... -dchar m[] = TEXT("merhaba"); -) - -$(FARK_D ---- -dchar[] m = "merhaba"; ---- -) - -) - -$(LI Eski derleyicilere uygun olarak derleme - -$(FARK_C -#if PROTOTYPES -#define P(p) p -#else -#define P(p) () -#endif -int func P((int x, int y)); -) - -$(FARK_D -$(P -D derleyicisinin açık kodlu olması farklı derleyici söz dizimleri sorunlarını da önler. -) -) - -) - -$(LI Türlere yeni isimler - -$(FARK_C -#define INT int -) - -$(FARK_D ---- -alias int INT; ---- -) -) - -$(LI Bildirimler ve tanımlar için tek başlık dosyası kullanmak - -$(FARK_C -#define EXTERN extern -#include "bildirimler.h" -#undef EXTERN -#define EXTERN -#include "bildirimler.h" -) - -$(P -$(CODE bildirimler.h) dosyasında: -) - -$(C_CODE -EXTERN int foo; -) - -$(FARK_D - -$(P -D'de bildirim ve tanım aynıdır; aynı kaynak koddan hem bildirim hem de tanım elde etmek için $(CODE extern) anahtar sözcüğünü kullanmak gerekmez. -) - -) - -) - -$(LI Hızlı iç [inline] fonksiyonlar - -$(FARK_C -#define X(i) ((i) = (i) / 3) -) - -$(FARK_D ---- -int X(ref int i) { return i = i / 3; } ---- - -$(P -Derleyici eniyileştiricisi fonksiyonu zaten $(I iç fonksiyon) olarak derler. -) -) - -) - -$(LI $(CODE assert) fonksiyonu için dosya ve satır bilgisi - -$(FARK_C -#define assert(e) ((e) || _assert(__LINE__, __FILE__)) -) - -$(FARK_D -$(P -$(CODE assert) dilin bir iç olanağıdır. Bu, derleyici eniyileştiricisinin $(CODE _assert())'in dönüş türünün olmadığı bilgisinden yararlanmasını da sağlar. -) -) - -) - -$(LI Fonksiyon çağırma çeşitleri [calling conventions] - -$(FARK_C -#ifndef _CRTAPI1 -#define _CRTAPI1 __cdecl -#endif -#ifndef _CRTAPI2 -#define _CRTAPI2 __cdecl -#endif - -int _CRTAPI2 fonksiyon(); -) - -$(FARK_D - -$(P -Fonksiyonların nasıl çağrıldıkları gruplar halinde belirlenebilir: -) ---- -extern (Windows) -{ - int bir_fonksiyon(); - int baska_bir_fonksiyon(); -} ---- -) - -) - -$(LI $(CODE __near) ve $(CODE __far) işaretçi yazımını gizlemek - -$(FARK_C -#define LPSTR char FAR * -) - -$(FARK_D -$(P -D'de 16 bitlik kod, işaretçi boyutlarının karışık kullanımı, değişik işaretçi türleri desteklenmez. -) -) - -) - -$(LI Türden bağımsız temel kodlama - -$(H6 C) - -$(P -Hangi fonksiyonun kullanılacağını metni dönüştürerek belirlemek: -) - -$(C_CODE -#ifdef UNICODE -int getValueW(wchar_t *p); -#define getValue getValueW -#else -int getValueA(char *p); -#define getValue getValueA -#endif -) - -$(FARK_D - -$(P -D isimlere yeni takma isimler vermeyi destekler: - ---- -version (UNICODE) -{ - int getValueW(wchar[] p); - alias getValueW getValue; -} -else -{ - int getValueA(char[] p); - alias getValueA getValue; -} ---- -) - -) - -) - -) - - -$(FARK duruma_gore, Derlemeyi duruma göre yönlendirmek [conditional compilation]) - -$(H6 C) - -$(P -Derlemeyi yönlendirebilmek önişlemcinin güçlü yönlerindendir ama zayıflıkları da vardır: -) - -$(UL -$(LI -Önişlemcinin kapsamlardan haberi olmaması, $(CODE #if/#endif) bloklarının kapsamlardan bağımsız ve düzensiz olarak yerleştirilmelerine olanak sağlar; kodu izlemek güçleşir. -) - -$(LI -Derlemeyi yönlendirmek yine makrolar aracılığıyla yapıldığı için, kullanılan makro isimlerinin programdaki başka isimlerle çakışma olasılıkları yine de geçerlidir. -) - -$(LI -$(CODE #if) ifadeleri diğer C ifadelerinden farklı olarak işletilirler. -) - -$(LI -$(I Önişlemci dili) C'den temelde farklılıklar gösterir; örneğin boşluk karakterleri ve satır başları önişlemci için önemli oldukları halde derleyici için önemli olmayabilirler. -) - -) - -$(FARK_D -$(P -Derlemeyi yönlendirmek D'de dil tarafından sağlanır: -) - -$(OL -$(LI -Farklı sürüm kodlarını farklı modüllere yerleştirmek -) - -$(LI -Hata ayıklamaya yardımcı olan $(CODE debug) deyimi -) - -$(LI -Tek kaynak koddan programın farklı sürümlerini üretmeye yarayan $(CODE version) deyimi -) - -$(LI -$(CODE if(0)) deyimi -) - -$(LI -Bir grup kod satırını bir grup açıklama satırına dönüştüren $(CODE /+ +/) açıklamaları -) -) -) - -$(FARK kod_tekrari, Kod tekrarını azaltmak) - -$(H6 C) - -$(P -Bazen fonksiyonlar içinde tekrarlanan kodlarla karşılaşılabilir. Tekrarlanan kodların ayrı bir fonksiyona taşınması daha doğal olsa da, fonksiyon çağrısı bedelini önlemek için bazen makrolar kullanılır. Örneğin şu $(I bayt kod yorumlayıcısı) koduna bakalım: -) - -$(C_CODE -unsigned char *ip; // byte code instruction pointer -int *stack; -int spi; // stack pointer -... -#define pop() (stack[--spi]) -#define push(i) (stack[spi++] = (i)) -while (1) -{ - switch (*ip++) - { - case ADD: - op1 = pop(); - op2 = pop(); - result = op1 + op2; - push(result); - break; - - case SUB: - ... - } -} -) - -$(P -Bu kodda çeşitli sorunlar vardır: -) - -$(OL - -$(LI -Makrolar ifade olarak açıldıkları için değişken tanımlayamazlar. Örneğin program yığıtından taşmaya karşı denetim eklemek gibi bir iş oldukça güçtür. -) - -$(LI -Sembol tablosunda bulunmadıklarından, etkileri tanımlandıkları fonksiyonun dışına da taşar -) - -$(LI -Makro parametreleri metin olarak geçirildiklerinden makronun parametreyi gereğinden fazla işletmemeye dikkat etmesi ve kodun anlamını korumak için parametreyi parantezler içine alması gerekir -) - -$(LI -Hata ayıklayıcı makrolardan habersizdir -) - -) - -$(FARK_D - -$(P -D'de kapsam fonksiyonları kullanılır: -) - ---- -ubyte* ip; // byte code instruction pointer -int[] stack; // operand stack -int spi; // stack pointer -... - -int pop() { return stack[--spi]; } -void push(int i) { stack[spi++] = i; } - -while (1) -{ - switch (*ip++) - { - case ADD: - op1 = pop(); - op2 = pop(); - push(op1 + op2); - break; - - case SUB: - ... - } -} ---- - -$(P -Bunun çözdüğü sorunlar şunlardır: -) - -$(OL -$(LI -Kapsam fonksiyonları D fonksiyonları kadar güçlü olanaklara sahiptirler -) - -$(LI -Kapsam fonksiyonunun ismi, tanımlandığı kapsamla sınırlıdır -) - -$(LI -Parametreler değer olarak geçirildikleri için birden fazla işletilmeleri gibi sorunlar yoktur -) - -$(LI -Kapsam fonksiyonları hata ayıklayıcı tarafından görülebilirler -) -) - -$(P -Ek olarak, kapsam fonksiyonu çağrıları eniyileştirici tarafından yok edilince çağrı bedeli de önlenmiş olur. -) -) - -$(FARK error_assert, $(CODE #error) ve derleme zamanı denetimleri) - -$(P -Derleme zamanında yapılan denetimler sırasında derlemenin bir hata mesajıyla sonlandırılması sağlanabilir. -) - -$(H6 C) - -$(P -Bir yöntem, $(CODE #error) önişlemci komutunu kullanmaktır: -) - -$(C_CODE -#if FOO || BAR - ... derlenecek kod ... -#else -#error "FOO veya BAR'dan en az birisi gerekmektedir" -#endif -) - -$(P -Önişlemci yetersizlikleri burada da geçerlidir: yalnızca tamsayı ifadeler geçerlidir, tür değişimlerine izin verilmez, $(CODE sizeof) kullanılamaz, sabitler kullanılamaz, vs. -) - -$(P -Bunların bazılarını gidermek için bir $(CODE static_assert) (derleme zamanı denetimi) makrosu kullanılabilir (M.Wilson tarafından): -) - -$(C_CODE -#define static_assert(_x) \ -do { \ - typedef int ai[(_x) ? 1 : 0]; \ -} while(0) -) - -$(P -Kullanımı şöyledir: -) - -$(C_CODE -void foo(T t) -{ - static_assert(sizeof(T) < 4); - ... -} -) - -$(P -Bu, koşul yanlış ($(CODE false)) olduğunda bir yazım hatasına dönüşür ve derleme bir hata mesajıyla sonlanır. $(I [Çevirenin notu: $(CODE false) olduğunda $(CODE ai) dizisinin boyu $(CODE 0) olur; bu da C kurallarına göre yasal değildir.]) -) - -$(FARK_D -$(P -$(CODE static assert) D'de bir dil olanağıdır ve bildirimlerin ve deyimlerin kullanılabildikleri her yerde kullanılabilir. Örnek: -) - ---- -version (FOO) -{ - class Bar - { - const int x = 5; - static assert(Bar.x == 5 || Bar.x == 6); - - void foo(T t) - { - static assert(T.sizeof < 4); - ... - } - } -} -else version (BAR) -{ - ... -} -else -{ - static assert(0); // yasal olmayan bir sürüm -} ---- -) - -$(FARK katma, Şablon katmaları [mixin]) - -$(P -D'nin şablon katma olanağı, dışarıdan bakıldığında tıpkı C'nin önişlemcisinde olduğu gibi bir metin yerleştirme olanağına benzer. Yerleştirilen metin yine açıldığı yerde ayrıştırılır [parse], ama katmaların makrolardan üstün tarafları vardır: -) - -$(OL - -$(LI -Katmalar söz dizimine uyarlar ve ayrıştırma ağaçlarında [parse tree] yer alırlar. Makrolar ise hiçbir düzene bağlı olmadan metin yerleştirirler. -) - -$(LI -Katmalar aynı dilin parçalarıdırlar. Makrolar ise C++'nın üstünde ayrı bir dildir; kendine ait ifade kuralları, türleri, sembol tabloları, ve kapsam kuralları vardır. -) - -$(LI -Katmalar kısmi özelleme [partial specialization] kurallarına göre seçilirler; makrolarda aşırı yükleme yoktur. -) - -$(LI -Katmalar kapsam oluştururlar; makrolar oluşturmazlar. -) - -$(LI -Katmalar söz diziminden anlayan araç programlarla uyumludurlar; makrolar değildirler. -) - -$(LI -Katmalarla ilgili bilgiler ve sembol tabloları hata ayıklayıcılara geçirilirler; makrolar metin yerleştirildikten sonra yok olurlar. -) - -$(LI -Katmalarda birden fazla tanımın uygun olduğu durumlarda hangisinin kullanılacağının kuralları bellidir; makro tanımları çakışmak zorundadırlar. -) - -$(LI -Katma isimlerinin başka katma isimleriyle çakışmaları standart bir algoritma yoluyla önlenmiştir; makrolarda kelime birleştirme yoluyla elle yapılır. -) - -$(LI -Katma argümanları tek bir kere işletilirler; makro açılımlarında ise argümanlar kullanıldıkları her sefer işletilirler ve bu yüzden bulunması güç hatalara yol açabilirler. -) - -$(LI -Katma argümanlarının etrafına koruyucu parantezler koymak gerekmez. -) - -$(LI -Katmalar istendiği kadar uzunlukta normal D kodu olarak yazılırlar; makrolarda olduğu gibi satırları $(CODE \) karakteriyle sonlandırmak gerekmez, $(CODE //) açıklamaları koyamamak gibi sorunlar yoktur, vs. -) - -$(LI -Katmalar başka katmalar tanımlayabilirler; makrolar makro tanımlayamazlar. -) - -) - - - -Macros: - SUBTITLE=C Önişlemcisi ile Farkları - - DESCRIPTION=D programlama dilinin C önişlemcisiyle farkları - - KEYWORDS=d programlama dili tanıtım bilgi karşılaştırma c dili önişlemci preprocessor diff --git a/ddili/src/tanitim/genel.d b/ddili/src/tanitim/genel.d deleted file mode 100644 index b32d75c..0000000 --- a/ddili/src/tanitim/genel.d +++ /dev/null @@ -1,417 +0,0 @@ -Ddoc - -$(H4 D Diline Genel Bakış) - -$(ESKI_KARSILASTIRMA) - -$(P -Bu bölümde D'nin ilginç ve önemli olanaklarından bazılarını bulabilirsiniz. Bu listenin kaynağı Digital Mars'ın sitesindeki D tanıtım sayfasıdır. -) - -$(H6 Sınıflar) - -$(P -D'de nesne yönelimli programlamanın temelini sınıflar oluşturuyor. Bu konuda C++'dan oldukça köklü farkları var: Arayüz [interface] türetirken çoklu kalıtım, ama gerçekleştirme [implementation] türetirken tekli kalıtım... Bütün sıradüzenin en tepesinde $(CODE Object) temel arayüzü bulunuyor. Nesneler C ve C++'nın tersine $(I referans olarak) oluşturuluyorlar. -) - -$(H6 İşleç yükleme [operator overloading]) - -$(P -İşleç yükleme konusunda aslında bir fark yok: Başka nesne yönelimli dillerde olduğu gibi D'de de işleçler her tür için özel olarak tanımlanabiliyor. -) - -$(H6 Modüller) - -$(P -Her kaynak dosya, içerisinde bulunduğu klasöre göre bir modülün parçası olarak kabul ediliyor. Bildirimleri derleyiciye tanıtmak için $(CODE #include) etmek yerine, modüller $(CODE import) ediliyorlar. Böylelikle ne başlık dosyalarına gerek kalıyor ne de programcılığın özüyle ilgisi olmayan $(CODE #ifndef) ve $(CODE #pragma once) gibi çözümlere... -) - -$(H6 Bildirimler gereksiz) - -$(P -C'de ve C++'da anahtar kelimeler dışındaki bütün isimlerin önceden bildirilmiş olmaları gerekir. $(CODE printf) bile $(CODE <stdio.h>) başlığı eklenmeden kullanılamaz. (Aslında bazı derleyiciler buna izin verirler ama standart değildir.) Bu yüzden programcılar sürekli olarak $(CODE .h) başlık dosyaları ile $(CODE .c) kod dosyalarını uyumlu tutmaya çabalarlar. Bu hem hataya açık hem de gereksiz bir külfettir. D'de ise bu iş derleyicinin görevidir: -) - ---- -import std.stdio; - -void main() -{ - ismi_henüz_görülmemiş_bir_fonksiyon(); -} - -void ismi_henüz_görülmemiş_bir_fonksiyon() -{ - auto nesne = new İsmiHenüzGörülmemişBirSınıf; - nesne.selam_ver(); -} - -class İsmiHenüzGörülmemişBirSınıf -{ - void selam_ver() - { - writeln("Merhaba!"); - } -} ---- - -$(H6 Şablonlar [templates]) - -$(P -Ne kadar güçlü olsalar da; C++'nın şablonları hem büyük uzmanlık gerektirir, hem de kullanımları bazı durumlarda çok sorunludur. C++ şablonlarının en büyük uzmanlarından birisi Andrei Alexandrescu'dur. Kendisi şablonlarla tasarımlarının ötesinde işler becermiş ve bunları $(I Modern C++ Design) kitabında tanıttığı Loki kütüphanesinde göstermiştir. C++'da gördüğü eksiklikleri D'ye taşımış ve şablonları D'de çok daha kullanışlı bir hale getirmiştir. -) - -$(H6 Çağrışımlı diziler [associative arrays]) - -$(P -Başka dillerde de bulunan ve C++'da $(CODE std::map) ile sunulan çağrışımlı diziler D'de dilin iç olanaklarına dahil ve hızlı eşleme tablosu [hash table] olarak gerçekleştirilmişler. -) - -$(H6 Gerçek anlamda $(CODE typedef)'ler) - -$(P -C'de ve C++'da $(CODE typedef) yeni tür oluşturmaz, olan bir türe daha kullanışlı bir isim verir. D'de ise yeni bir tür oluşur. Sonuçta, -) - ---- -typedef int ÖğrenciNumarası; ---- - -$(P -ile oluşturulan $(CODE ÖğrenciNumarası) türü ile onun temel aldığı $(CODE int) farklıdırlar. Örneğin fonksiyonlar bu türler için ayrı ayrı yüklenebilirler: -) - ---- -int yazdır(int sayı); -int yazdır(ÖğrenciNumarası öğrenci); ---- - -$(H6 Belgeleme) - -$(P -Kaynak kodlar geleneksel olarak; JavaDoc, Doxygen, vs. gibi araçlarla açıklama satırlarında belgelenir. Örneğin fonksiyon parametrelerinin ve dönüş değerlerinin ne anlama geldikleri ve nasıl kullanıldıkları, fonksiyonların başlarındaki $(CODE /**) ve $(CODE */) ile belirlenen açıklama bölümlerine yazılırlar. D'de belgeleme işi de derleyicinin görevidir; ayrıca program kullanmaya gerek yoktur. Dahası, belgeleme salt kod ile sınırlı değildir; okumakta olduğunuz bu sayfalar bile $(LINK2 http://dlang.org/ddoc.html, Ddoc) ile oluşturulmuşlardır. -) - -$(H6 Kapsam fonksiyonları) - -$(P -Fonksiyonlar başka fonksiyonların içinde tanımlanabilirler. -) - -$(H6 Fonksiyon sabitleri [function literals]) - -$(P -Fonksiyonlar kullanıldıkları ifadeler içinde isimsiz olarak tanımlanabilirler. [Not: Fonksiyonlar zaten sabit oldukları halde burada $(I literal)'ı $(I string literal)'a benzeterek $(I sabit) olarak çeviriyorum.] -) - ---- -// Burada üçüncü parametre bir fonksiyon sabiti -foo(3, 10, (int sayı){ return sayı * sayı; } ); - -// Parametre almayan bir fonksiyon sabiti -bar(3, 10, { return 5; } ); ---- - -$(H6 Kapamalar [closures]) - -$(P -Kapsam fonksiyonlarına ve sınıf üye fonksiyonlarına $(I kapama) ([delegate] de denir) olarak referans verebiliriz. Bu özellik türden bağımsız programlamayı kolaylaştırır ve daha güvenli hale getirir. -) - -$(H6 Parametre türleri) - -$(P -Parametreler kullanımlarına göre açıkça giriş, çıkış, veya giriş-çıkış olarak belirtilebilirler. C++'da parametrelerin kullanımları biraz anlaşmaya bağlıdır: giriş parametreleri $(I değer olarak) veya $(I $(CODE const) referans olarak) geçirilirler; çıkış ve giriş-çıkış parametreleri ise $(I işaretçi) veya $(I referans) olarak geçirilirler. Bu konu D'de açıktır. -) - -$(H6 Diziler) - -$(P -C dizilerinin C++'ya da taşınmış olan bir çok sorunu vardır: -) - -$(UL -$(LI -C dizileri kendi uzunluklarından habersizdirler, çünkü dizi kavramı bir işaretçi ve onun kullanımıyla ilgili anlaşmalarla belirlenmiştir. -) - -$(LI -C dizileri ikinci sınıf vatandaştırlar. Örneğin bir fonksiyona geçirilirken $(I ilk elemanlarını gösteren işaretçiye) dönüşürler. Bu dönüşüm sonucunda da, olası boyut bilgisi kaybedilmiş olur. -) - -$(LI -C dizilerinin boyutları değiştirilemez. -) - -$(LI -Uzunluk bilgisi taşımadıkları için dizi dışına taşma gibi yasal olan kullanımlar denetlenemez. -) - -$(LI -Söz dizimi gereği dizi uzunluğu en sonda belirlendiği için, örneğin dizi işaretçilerinin tanımı çok gariptir: - ---- -int (*dizi_işaretçisi)[10]; ---- - -D'de ise söz dizimi doğaldır: - ---- -int[10]* dizi_işaretçisi; // 10 uzunlukta int dizisi - // işaretçisi - -long[] fonksiyon(int x); // long dizisi döndüren - // fonksiyon ---- -) - -) - -$(P -D, dizilerin bu sorunlarını çözer. -) - -$(H6 Dizgiler [strings]) - -$(P -Dizgiler programlama dilinden tam destek almayı gerektirecek kadar yaygın ve yararlı veri yapılarıdır. Modern dillerde olduğu gibi D'de de dizgiler C ve C++'dan farklı olarak doğrudan destek görürler. -) - -$(H6 Otomatik bellek yönetimi) - -$(P -D'de bellek yönetimi çöp toplamalıdır [garbage collected]. Bellek yönetimi konusundaki gözlemler, çöp toplama olanağının program geliştirmeyi çok çabuklaştırdığını ve çok daha kolaylaştırdığını gösteriyor. Ortaya çıkan program da çoğu durumda daha hızlı çalışıyor. -) - -$(H6 Elle bellek yönetimi) - -$(P -D çöp toplamalı olsa da, $(CODE new) ve $(CODE delete) işleçleri her sınıf için ayrı olarak yüklenebilir. -) - -$(H6 RAII) - -$(P -$(I Resource Acquisition Is Initialization)'ın kısaltması olan RAII, kaynakların bozucu fonksiyonlarda geri verilmesi yöntemidir. Hata atılan durumlarda bile kaynak sızıntılarını önler. D RAII'yi çöp toplamadan ayrı olarak ve ona ek olarak destekler. -) - -$(H6 Etkin yapılar) - -$(P -C'deki $(CODE struct) gibi alt düzey yapılar D'de de desteklenir. Bunlar hem C fonksiyonlarıyla etkileşmek için, hem de etkin veri yapıları oluşturmak için yararlıdırlar. -) - -$(H6 Makine dili [assembly]) - -$(P -C'de ve C++'da olduğu gibi, D'de de gereken özel durumlarda doğrudan makine dilinde kod yazılabilir. -) - -$(H6 Sözleşmeli programlama [contract programming]) - -$(P -Eiffel dilinin yaratıcısı Bertrand Meyer tarafından icat edilen sözleşmeli programlama, kodun doğruluğunu sağlama konusunda çok güçlü bir yöntemdir. Sözleşmeli programlama D'de fonksiyon giriş ve çıkış koşulları [precondition ve postcondition], sınıf değişmezleri, ve $(CODE assert)'lerden oluşur. -) - -$(P -Örneğin $(CODE alan_) üyesinin değerinin her zaman için sıfırdan büyük olması beklenen bir $(CODE Üçgen) sınıfında bu $(I değişmezlik) kavramı $(CODE invariant) sözcüğü ile şöyle ifade edilebilir: -) - ---- -class Üçgen -{ - double alan_; - /* ... */ - - invariant() - { - /* Üçgen'in her üye fonksiyonunun her işletilişinden - * sonra bu koşul denetlenecektir. */ - assert(alan_ > 0.0); - } -} ---- - -$(H6 Birim testleri [unit tests]) - -$(P -Birim testleri başka dillerde dilin dışında düzeneklerle halledilirler. Örneğin C++ için CppUnit, unittest++, vs. gibi bir birim test düzeneği genellikle $(CODE make) veya benzeri oluşturma sisteminin parçası olacak şekilde ayarlanırlar ve asıl programın dışında olarak çalıştırılırlar. -) - -$(P -D birim testleri biraz daha ileri götürmüş ve dil olanağı haline getirmiştir. Testler örneğin sınıflara eklenirler, ve istenirse program başlamadan önce otomatik olarak çalıştırılırlar. Böylece sınıfların $(I değişmezlik) koşullarındaki tutarsızlıklar birim testleri tarafından en kısa sürede farkedilmiş olurlar. -) - -$(H6 Hata ayıklama [debug] olanakları) - -$(P -D'de hata ayıklama olanakları dilin parçasıdır. Hata ayıklayıcı kodlar makrolara gerek bırakmadan, derleyici düzeyinde etkin hale getirilebilirler. Böylece programın hem hata ayıklama sürümü [debug], hem de asıl sürümü [release] aynı kod tarafından oluşturulabilir. -) - -$(H6 Hata atma [exceptions]) - -$(P -D, $(I try-catch)'den daha yararlı olduğu görülen $(I try-catch-finally) modelini kullanır. Böylece küçük kaynak temizlikleri için bile C++'da olduğu gibi ayrıca sınıf yazmak gerekmemiş olur. -) - -$(H6 Çoklu iş parçacıkları [multi-threads]) - -$(P -D bu konuya dil düzeyinde çözüm getirir. Sınıf ve fonksiyon düzeyinde koruma sağlar. Örneğin $(CODE synchronized) olarak belirtilen fonksiyonların tek bir anda yalnızca tek bir iş parçacığı tarafından işletilmeleri dil tarafından sağlanır. -) - ---- -synchronized int fonksiyon() -{ - /* Bu kod belirli bir anda tek bir iş parçacığı - * tarafından işletilir */ -} ---- - - -$(H6 Sağlam kodlama yöntemleri) - -$(P -D, kod doğruluğunu ve güvenliğini arttıran bazı olanaklar getirir: -) - -$(UL - -$(LI -İşaretçilere duyulan ihtiyaç büyük ölçüde giderilmiştir: dinamik diziler, referans değişkenler, referans nesneler, vs. -) - -$(LI -Çöp toplamalı bellek yönetimi -) - -$(LI -Çoklu iş parçacıklarına dil desteği -) - -$(LI -Kodda beklenmeyen değişiklikler yapan makrolar yok -) - -$(LI -Makroların bazı görevlerinin yerine $(I iç fonksiyonlar) [inline functions]. -) - -$(LI -C ve C++'dakinin tersine, temel türlerin büyüklükleri açıkça belirtilebilir (o dillerde örneğin $(CODE int)'in kaç bit olduğu bile belli değildir) -) - -$(LI -C ve C++'dakinin tersine, $(CODE char) türünün işaretiyle ilgili belirsizlik yoktur (o dillerde $(CODE char) üçüncü bir tür olarak ya $(CODE signed char)'ın ya da $(CODE unsigned char)'ın eşdeğeridir ama hangisinin eşdeğeri olduğu belirsizdir) -) - -$(LI -Bildirimlerin başlıklarda ve kod dosyalarında ayrı ayrı ve tutarlı olarak yazılmaları gerekmez -) - -$(LI -Hata ayıklayıcı kodu ekleyebilmek için ayrıştırma [parsing] desteği -) - -) - -$(H6 Derleme zamanı denetimleri) - -$(UL - -$(LI -Kuvvetli tür denetimi -) - -$(LI -Tek bir $(CODE ;) karakteri içeren boş $(CODE for) döngülerine izin verilmez -) - -$(LI -Atamalar Bool değere dönüşmezler -) - -$(LI -Eski ve geçersiz arayüz fonksiyonlarına izin verilmez -) - -) - -$(H6 Çalışma zamanı denetimleri) - -$(UL - -$(LI -$(CODE assert()) ifadeleri -) - -$(LI -Dizi dışına taşmaya karşı denetim -) - -$(LI -$(CODE switch) ifadelerinde yanlış $(CODE case) denetimi -) - -$(LI -Bellek yetersizliği durumlarında atılan hata -) - -$(LI -Fonksiyon giriş ve çıkış koşulları ve sınıf değişmezleri yoluyla sözleşmeli programlama desteği -) - -) - - -$(H6 İşleç öncelikleri) - -$(P -Bu konudaki sürprizleri ve hataları engellemek için D C'deki işleç önceliklerini kullanır. -) - -$(H6 C fonksiyonlarına doğrudan destek) - -$(P -D'ni C'ninkilere bire bir karşılık gelen veri yapıları da destekleridiği için, C kütüphane fonksiyonları hiçbir dönüşüm gerekmeden doğrudan çağrılabilirler. -) - -$(H6 Bütün C türlerine destek) - -$(P -C'deki $(CODE struct)'lar, $(CODE union)'lar, $(CODE enum)'lar, işaretçiler, C99'un getirdiği bütün türler desteklenir. Hatta $(CODE struct) üyelerinin bellek sıralanma düzenleri [alignment] bile belirlenebilir. -) - -$(H6 İşletim sistemi hataları) - -$(P -D'nin hata yakalama düzeneği işletim sistemi tarafından atılan hatalarla da uyumludur. -) - -$(H6 Araç programlarla uyumluluk) - -$(P -D standart program parçası [object file] formatında kod üretir. Bu sayede; hata ayıklayıcı, bağlayıcı, vs. mevcut araç programların D programları ile kullanılmaları da sağlanmış olur. -) - -$(H6 Kod sürümleri) - -$(P -D, bir program kodundan birden fazla sürüm üretilmesine destek verir. Böylelikle $(CODE #if)/$(CODE #endif) gibi yöntemlere gerek kalmamış olur. -) - -$(H6 Emekliye ayrılan kod) - -$(P -Çeşitli nedenlerle artık kullanılmaması gereken kodlar $(I eski) oldukları şeklinde işaretlenebilirler ve böylece yeni kodlarda kullanılmaları önlenebilir ama gerekirse eski programlarda kullanılmalarına izin verilebilir. -) - - - - -Macros: - SUBTITLE=Genel Bakış - - DESCRIPTION=D programlama dilinin temel olanakları - - KEYWORDS=d programlama dili tanıtım genel bilgi diff --git a/ddili/src/tanitim/index.d b/ddili/src/tanitim/index.d deleted file mode 100644 index 6c59877..0000000 --- a/ddili/src/tanitim/index.d +++ /dev/null @@ -1,44 +0,0 @@ -Ddoc - -$(H4 D Dili Tanıtımı) - -$(UL - -$(LI -$(LINK2 /tanitim/genel.html, Genel bakış) -) - -$(LI -$(LINK2 /tanitim/fark_c.html, C dili ile karşılaştırılması) -) - -$(LI -$(LINK2 /tanitim/fark_cpp.html, C++ dili ile karşılaştırılması) -) - -$(LI -$(LINK2 /tanitim/fark_dizgi.html, Dizgilerin [string] C++ dizgileri ile karşılaştırılması) -) - -$(LI -$(LINK2 /tanitim/fark_onislemci.html, D olanaklarının C önişlemcisiyle [preprocessor] karşılaştırılması) -) - -$(LI -$(LINK2 /tanitim/dil_kutuphane.html, Olanakların dil içinde veya kütüphanelerde gerçekleştirilmeleri) -) - -$(LI -$(LINK2 /tanitim/fark_karmasik.html, Karmaşık sayı türleri ve C++'nın std::complex'i) -) - -) - -Macros: - SUBTITLE=Tanıtım - - DESCRIPTION=D programlama diline genel bakış ve başka dillerle karşılaştırılması - - KEYWORDS=d programlama dili tanıtım bilgi karşılaştırma - - BREADCRUMBS=$(BREADCRUMBS_INDEX) diff --git a/ddili/src/todo b/ddili/src/todo deleted file mode 100644 index 94713b3..0000000 --- a/ddili/src/todo +++ /dev/null @@ -1,23 +0,0 @@ -I would say other than library writers, nobody should ever write a class dtor. - --Steve" - - -http://www.newshelves.com/2014/07/23/why-you-need-lightning-source-and-createspace/ - - - - -C and C++ interoperability - - - -http://andreareider.com/2011/01/23/the-basics-of-book-design/ - - - - - -"I recommend using both Ingram Spark and CreateSpace to maximize your profits and ensure that no one is discouraged from ordering or stocking the print edition of your book." - -http://janefriedman.com/2015/02/16/self-publish-your-book/ diff --git a/omegat/project_save.tmx b/omegat/project_save.tmx new file mode 100644 index 0000000..c630058 --- /dev/null +++ b/omegat/project_save.tmx @@ -0,0 +1,16778 @@ + + + +
+ + + + + $ .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 . + + + + + $ .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)) +$(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)) +$(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). + + + $(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 OnCrowding.block) causes the sender to wait until there is room in the mailbox. + + + $(C OnCrowding.block) 会使得发送者线程等待,一直到邮箱中有足够空间为止。 + + + + + $(C Thread.sleep()) suspends the operations for the specified amount of time. + + + $(C Thread.sleep()) 会将操作挂起一段时间,其时间长短可在代码中指定。 + + + + + $(C Thread.sleep) is admittedly an artifical method to use in the following examples because it takes time without ever busying any core. + + + 当然在下面的例子中 $(C Thread.sleep) 只是用来模拟长耗时的任务,因为它并不需要处理器核心处理实际的工作,它只是占用时间。 + + + + + $(C __gshared) is necessary when interacting with libraries of languages like C and C++ where data sharing is automatic by default. + + + 在与 C 和 C++ 这样的语言编写的库(它们默认自动共享数据)交互时,$(C __gshared) 必不可少。 + + + + + $(C parallel()) is very convenient as it $(I constructs), $(I starts), and $(I waits for) the tasks automatically. + + + $(C parallel()) 用起来非常方便,因为无论是$(I 构建)、$(I 启动)还是$(I 等待任务执行完成)都是自动进行的。 + + + + + $(C parallel()) returns a range object that knows how to distribute the execution of the $(C delegate) to a separate core for each element. + + + $(C parallel()) 返回一个范围对象,它将决定如何将处理元素的 $(C delegate) 分发给独立的处理器核心执行。 + + + + + $(C parallel()) then waits for all of the tasks to be completed before finally exiting the loop. + + + 之后 $(C parallel()) 会等待所有任务都完成后再退出循环。 + + + + + $(C readf()) continues to wait for more characters to add to the string: +) + + + $(C readf()) 继续等待新输入的字符以添加到字符串: +) + + + + + $(C receive()) on the other hand can wait for more than one type of message. + + + 而 $(C receive()) 可以接收多种类型的消息。 + + + + + $(C receiveTimeout()) prevents blocking the receiving thread indefinitely. + + + $(C receiveTimeout()) 可以防止出现无限等待消息这样的情况。 + + + + + $(C reduce()) calls the functions with the current value of the result and each element of the range. + + + $(C reduce()) 将传入的函数作为运算方法对每个元素进行计算并按照合并到结果中。 + + + + + $(C std.algorithm.map) is an algorithm commonly found in many functional languages. + + + 在许多函数式编程语言里都能找到与 $(C std.algorithm.map) 类似的算法。 + + + + + $(C task()) constructs, $(C executeInNewThread()) starts, and $(C yieldForce()) waits for a task object. + + + 使用 $(C task()) 来创建任务对象;使用 $(C executeInNewThread()) 来启动任务对象;使用 $(C yieldForce()) 来等待任务对象。 + + + + + $(C taskPool.map()) and $(C taskPool.amap()) from the $(C std.parallelism) module take advantage of multiple cores and run faster in many cases. + + + $(C std.parallelism) 模块里的 $(C taskPool.map()) 和 $(C taskPool.amap()) 会充分利用多核,并在大部分情况下加快程序的运行速度。 + + + + + $(C variable1) and $(C variable2) above merely provide access to that anonymous object: +) + + + 上面的 $(C variable1) 和 $(C variable2) 只提供对那个匿名对象的访问: +) + + + + + $(C workForce()) can be called when starting other tasks is preferred over suspending the current thread. + + + $(C workForce()) 则适合用需要优先启动其他任务,而不是先考虑将当前线程挂起的情况。 + + + + + $(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 array) Arrays) + + + $(DERS_BOLUMU $(IX array) 数组) + + + + + $(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 $(IX compilation) Compilation) + + + $(DERS_BOLUMU $(IX compilation) 编译) + + + + + $(DERS_BOLUMU $(IX concurrency, message passing) $(IX message passing concurrency) Message Passing Concurrency) + + + $(DERS_BOLUMU $(IX concurrency, message passing) $(IX message passing concurrency) 基于消息传递的并发) + + + + + $(DERS_BOLUMU $(IX data sharing concurrency) $(IX concurrency, data sharing) Data Sharing Concurrency) + + + $(DERS_BOLUMU $(IX data sharing concurrency) $(IX concurrency, data sharing) 基于数据共享的并发) + + + + + $(DERS_BOLUMU $(IX fiber) Fibers) + + + $(DERS_BOLUMU $(IX fiber) 纤程) + + + + + $(DERS_BOLUMU $(IX parallelism) Parallelism) + + + $(DERS_BOLUMU $(IX parallelism) 并行) + + + + + $(DERS_BOLUMU $(IX property) Properties) + + + $(DERS_BOLUMU $(IX property) 特性) + + + + + $(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) + + + $(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 Tid) $(IX thisTid) $(IX ownerTid) Thread identifiers) + + + $(H5 $(IX Tid) $(IX thisTid) $(IX ownerTid) 线程 ID) + + + + + $(H5 $(IX []) Accessing the elements) + + + $(H5 $(IX []) 元素的访问) + + + + + $(H5 $(IX atomic operation) Atomic operations) + + + $(H5 $(IX atomic operation) 原子操作) + + + + + $(H5 $(IX call stack) $(IX program stack) Call stack) + + + $(H5 $(IX call stack) $(IX program stack) 调用栈) + + + + + $(H5 $(IX compiler) Compiler) + + + $(H5 $(IX compiler) 编译器) + + + + + $(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 delegate, message passing) Expecting different types of messages) + + + $(H5 $(IX delegate, message passing) 接收不同类型的消息) + + + + + $(H5 $(IX exception, concurrency) Exceptions during the execution of the worker) + + + $(H5 $(IX exception, concurrency) 工作线程中的异常) + + + + + $(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 interpreter) Interpreter) + + + $(H5 $(IX interpreter) 解释器) + + + + + $(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 machine code) Machine code) + + + $(H5 $(IX machine code) 机器码) + + + + + $(H5 $(IX prioritySend) $(IX PriorityMessageException) Priority messages) + + + $(H5 $(IX prioritySend) $(IX PriorityMessageException) 消息优先级) + + + + + $(H5 $(IX programming language) Programming language) + + + $(H5 $(IX programming language) 编程语言) + + + + + $(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 receiveTimeout) Waiting for messages up to a certain time) + + + $(H5 $(IX receiveTimeout) 在指定的时间内等待消息) + + + + + $(H5 $(IX remove) Removing key-value pairs) + + + $(H5 $(IX remove) 移除键值对) + + + + + $(H5 $(IX send) $(IX receiveOnly) Message Passing) + + + $(H5 $(IX send) $(IX receiveOnly) 消息传递) + + + + + $(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) + + + $(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()) 用于单次初始化;$(C shared static ~this()) 用于单次析构) + + + + + $(H5 $(IX shared) $(C shared) to share mutable data between threads) + + + $(H5 $(IX shared) 用 $(C shared) 在线程间共享数据)) + + + + + $(H5 $(IX spawn) Starting threads) + + + $(H5 $(IX spawn) 启动线程) + + + + + $(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 $(IX synchronized) $(C synchronized) to avoid race conditions) + + + $(H5 $(IX synchronized) $(C synchronized) 避免竞态条件) + + + + + $(H5 A race condition example) + + + $(H5 竞态条件示例) + + + + + $(H5 Adding key-value pairs) + + + $(H5 添加键值对) + + + + + $(H5 An array example) + + + $(H5 一个数组例子) + + + + + $(H5 Basic array operations) + + + $(H5 数组的基本操作) + + + + + $(H5 Calling functions without parentheses) + + + $(H5 不使用圆括号的函数调用) + + + + + $(H5 Comparing strings) + + + $(H5 比较字符串) + + + + + $(H5 Comparing with structs) + + + $(H5 与结构对比) + + + + + $(H5 Concepts) + + + $(H5 相关概念) + + + + + $(H5 Definition) + + + $(H5 定义) + + + + + $(H5 Detecting thread termination) + + + $(H5 检测线程终止) + + + + + $(H5 Example) + + + $(H5 样例) + + + + + $(H5 Fibers in range implementations) + + + $(H5 范围实现里的纤程) + + + + + $(H5 Initialization) + + + $(H5 初始化) + + + + + $(H5 Lowercase and uppercase are different) + + + $(H5 小写与大写不同) + + + + + $(H5 Mailbox management) + + + $(H5 邮箱管理) + + + + + $(H5 Multiple functions and tuple results) + + + $(H5 多个函数和元组结果) + + + + + $(H5 Online version) + + + $(H5 在线版本) + + + + + $(H5 Properties are not absolutely necessary) + + + $(H5 特性不是唯一的解决方案) + + + + + $(H5 Properties) + + + $(H5 特性) + + + + + $(H5 Property functions that are used in assignment) + + + $(H5 用于赋值的特性函数) + + + + + $(H5 Property functions that return values) + + + $(H5 返回值的特性函数) + + + + + $(H5 Removing key-value pairs) + + + $(H5 删除键-值对) + + + + + $(H5 Sharing is not automatic) + + + $(H5 共享不是自动的) + + + + + $(H5 Summary) + + + $(H5 小结) + + + + + $(H5 The benefit of user defined attributes) + + + $(H5 自定义属性的好处) + + + + + $(H5 Thread names) + + + $(H5 线程名) + + + + + $(H5 Usage) + + + $(H5 用法) + + + + + $(H5 When to use) + + + $(H5 何时使用特性) + + + + + $(H6 $(IX Generator, std.concurrency) $(C std.concurrency.Generator) for presenting fibers as ranges) + + + $(H6 $(IX Generator, std.concurrency) $(C std.concurrency.Generator) 用于将纤程变成范围) + + + + + $(H6 $(IX LinkTerminated) $(IX spawnLinked) $(C LinkTerminated) exception) + + + $(H6 $(IX LinkTerminated) $(IX spawnLinked) $(C LinkTerminated) 异常) + + + + + $(H6 $(IX OwnerTerminated) $(C OwnerTerminated) exception) + + + $(H6 $(IX OwnerTerminated) $(C OwnerTerminated) 异常) + + + + + $(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 error, compilation) $(IX compilation error) Compilation error) + + + $(H6 $(IX error, compilation) $(IX compilation error) 编译错误) + + + + + $(H6 $(IX exception, parallelism) Exceptions) + + + $(H6 $(IX exception, parallelism) 异常处理) + + + + + $(H6 $(IX is, operator) $(IX !is) The $(C is) and $(C !is) operators) + + + $(H6 $(IX is, 运算符) $(IX !is) $(C is) 和 $(C !is) 运算符) + + + + + $(H6 $(IX null, class) $(new, class) Class variables may be $(C null)) + + + $(H6 $(IX null, class) $(new, class) 类变量可以为 $(C null)) + + + + + $(H6 $(IX recursion) Recursion) + + + $(H6 $(IX recursion) 递归) + + + + + $(H6 $(IX remove, array) Removing elements from dynamic arrays) + + + $(H6 $(IX remove, array) 从动态数组里删除元素) + + + + + $(H6 $(IX reverse) Reversing the elements) + + + $(H6 $(IX reverse(反转)) 反转元素) + + + + + $(H6 $(IX sort) Sorting the elements) + + + $(H6 $(IX sort(排序)) 排序元素) + + + + + $(H6 $(IX variable, class) $(IX object, class) Class variables versus class objects) + + + $(H6 $(IX variable, class) $(IX object, class) 类变量与类对象) + + + + + $(H6 $(IX work unit size) Work unit size) + + + $(H6 $(IX work unit size) 工作单元大小) + + + + + $(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 Classes are reference types) + + + $(H6 类是引用类型) + + + + + $(H6 Construction) + + + $(H6 构造函数) + + + + + $(H6 Definition) + + + $(H6 定义) + + + + + $(H6 Destruction) + + + $(H6 析构函数) + + + + + $(H6 Example) + + + $(H6 示例) + + + + + $(H6 Member access) + + + $(H6 成员访问) + + + + + $(H6 Member functions of $(C Task)) + + + $(H6 $(C Task) 的成员函数) + + + + + $(H6 Member functions) + + + $(H6 成员函数) + + + + + $(H6 Operator overloading) + + + $(H6 运算符重载) + + + + + $(H6 Receiving any type of message) + + + $(H6 接收任意类型的消息) + + + + + $(H6 Receiving exceptions as messages) + + + $(H6 接收异常消息) + + + + + $(HILITE array =) array.remove!(a => $(HILITE a == 42)); // Assigned back to array + + + $(HILITE array =) array.remove!(a => $(HILITE a == 42)); // 回赋给 array + + + + + $(HILITE array =) array.remove($(HILITE 1)); // Assigned back to array + + + $(HILITE array =) array.remove($(HILITE 1)); // 回赋给 array + + + + + $(HILITE final) int func() { $(CODE_NOTE Recommended) + + + $(HILITE final) int func() { $(CODE_NOTE 推荐) + + + + + $(HILITE synchronized) { // ← This lock is different from the one above. + + + $(HILITE synchronized) { // ← 此处的锁与上方的锁不同。 + + + + + $(HILITE synchronized) { // ← This lock is different from the one below. + + + $(HILITE synchronized) { // ← 此处的锁与下方的锁不同。 + + + + + $(HILITE yield(node)); // Then, this element + + + $(HILITE yield(node)); // 接着,是当前元素 + + + + + $(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 (Assume that A is paused and B is restarted at this point)) + + + $(I (假设此处 A 暂停 B 继续)) + + + + + $(I (Assume that A is paused and B is started at this point)) + + + $(I (假设此处 A 暂停 B 启动)) + + + + + $(I (Assume that B is paused and A is restarted at this point)) + + + $(I (假设此处 B 暂停 A 继续)) + + + + + $(I Decorating) the names of member variables is a common practice in object oriented programming. + + + 在面向对象编程中,$(I 修饰)变量名是十分常用的技巧。 + + + + + $(I In parallel) means that operations are executed on multiple cores at the same time: +) + + + $(I 并行) 意味着将要执行的一组操作可以被分派到多个内核同时执行。 +) + + + + + $(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 +$(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 封装:) 控制成员的访问($(I 封装也可用于结构,只是到本章之前一直未提及。)) +) + + + + + $(LI +$(B Inheritance:) Acquiring members of another type +) + + + $(LI +$(B 继承:) 获取另一个类型的成员 +) + + + + + $(LI +$(B Polymorphism:) Being able to use a more special type in place of a more general type +) + + + $(LI +$(B 多态性:) 能够使用较特定的类型取代较通用的类型 +) + + + + + $(LI +$(IX each, std.algorithm) $(IX map, vs. each) $(C std.algorithm.each) is similar to $(C std.algorithm.map). + + + $(LI +$(IX each, std.algorithm) $(IX map, vs. each) $(C std.algorithm.each) 与 $(C std.algorithm.map) 相似。 + + + + + $(LI +$(IX iota, std.range) $(C std.range.iota) generates the elements of a given value range lazily. + + + $(LI +$(IX iota, std.range) $(C std.range.iota) 懒式生成给定范围里的元素值。 + + + + + $(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 randomSample, std.random) $(C std.random.randomSample) 会从给定的范围里随机选取一些样本元素,但不会更改它们的顺序。 + + + + + $(LI +$(IX randomShuffle, std.random) $(C std.random.randomShuffle) shuffles the elements of a range randomly. + + + $(LI +$(IX randomShuffle, std.random) $(C std.random.randomShuffle) 会随机选取范围里的元素,顺序不确定。 + + + + + $(LI +Although both programming models use operating system threads, in parallelism threads are encapsulated by the concept of task. + + + $(LI +虽然两者都涉及线程操作,但并行线程被封装成了任务。 + + + + + $(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 +In parallelism, tasks are independent from each other. + + + $(LI +在并行中,任务之间相互独立。 + + + + + $(LI +It is fully eager. + + + $(LI +它是即时计算。 + + + + + $(LI +It works with $(C RandomAccessRange) ranges. + + + $(LI +它适用于 $(C RandomAccessRange) 范围。 + + + + + $(LI +Many other functions may be chained as well: + + + $(LI +许多别的函数可以嵌套使用: + + + + + $(LI +Parallelism is easy to use, and as long as tasks are independent it is easy to produce programs that work correctly. + + + $(LI +并行易于使用,并且只要任务相互独立,便可以轻松地生成可正常工作的程序。 + + + + + $(LI +Since the initial value of an array is an empty array anyway, the following technique would achieve the same result: + + + $(LI +由于数组的初始值无论如何都是一个空数组,那么下面的技术将得到一样的结果: + + + + + $(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). + + + $(LI +使用一个 $(I 匿名函数) (在 $(LINK2 /ders/d.cn/lambda.html, 后面章节) 会讲解它)来指定需要删除的元素。 + + + + + $(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) 返回的是一个分片(即动态数组),其中包含关联数组的所有键。 + + + + + $(LI +The explanations are included as code comments: + + + $(LI +解释包含在代码说明中: + + + + + $(LI +The goal is to store multiple grades per student. + + + $(LI +目标是存储每个学生的多个成绩。 + + + + + $(LI +The main purpose of parallelism is to take advantage of microprocessor cores to improve the performance of programs. + + + $(LI +并行的主要目的是利用微处理器的多核提高程序的性能。 + + + + + $(LI +There are three mistakes (bugs) in this program. + + + $(LI +程序有三个错误(bug)。 + + + + + $(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 Generator) presents a fiber as an $(C InputRange).) + + + $(LI $(C Generator) 可以将纤程呈现为一个 $(C InputRange) 类型。) + + + + + $(LI $(C OnCrowding.ignore): The message is discarded.) + + + $(LI $(C OnCrowding.ignore):多余的消息将被抛弃。) + + + + + $(LI $(C Variant) matches any type of message.) + + + $(LI $(C Variant) 用来匹配所有类型的消息。) + + + + + $(LI $(C __gshared) provides data sharing as in C and C++ languages.) + + + $(LI $(C __gshared) 提供了与 C 和 C++ 共享数据的能力。) + + + + + $(LI $(C amap()) calls functions with the elements of a $(C RandomAccessRange) fully-eagerly in parallel.) + + + $(LI $(C amap()):以即时取值的方式对 $(C RandomAccessRange) 中的元素并行调用指定的函数。) + + + + + $(LI $(C amap): Calls functions with the elements of a $(C RandomAccessRange) fully-eagerly in parallel.) + + + $(LI $(C amap):以并行即时取值的方式对 $(C RandomAccessRange) 中的元素应用指定的函数。) + + + + + $(LI $(C asyncBuf()) iterates the elements of an $(C InputRange) semi-eagerly in parallel.) + + + $(LI $(C asyncBuf()):以半延时取值的方式并行迭代 $(C InputRange) 中的元素。) + + + + + $(LI $(C asyncBuf): Iterates the elements of an $(C InputRange) semi-eagerly in parallel.) + + + $(LI $(C asyncBuf):以并行半延时取值的方式迭代 $(C InputRange) 中的元素。) + + + + + $(LI $(C catch) and $(C finally) cannot be used without a $(C try) block.) + + + $(LI 没有一个 $(C try) 块, $(C catch) 和 $(C finally) 不能使用。) + + + + + $(LI $(C done): Specifies whether the task has been completed; rethrows the exception if the task has been terminated with an exception. + + + $(LI $(C done):指明任务是否已完成;如果任务已经因为异常而中止,则重新抛出该异常。 + + + + + $(LI $(C executeInNewThread()): Starts the task in a new thread.) + + + $(LI $(C executeInNewThread()):在新线程中启动任务。) + + + + + $(LI $(C executeInNewThread(int priority)): Starts the task in a new thread with the specified priority. + + + $(LI $(C executeInNewThread(int priority)):在新线程中启动任务,并指定线程优先级。 + + + + + $(LI $(C map()) calls functions with the elements of an $(C InputRange) semi-eagerly in parallel.) + + + $(LI $(C map()):以半延时取值的方式对 $(C InputRange) 中的元素并行调用指定的函数。) + + + + + $(LI $(C map()), $(C amap()), and $(C reduce()) can take multiple functions and return the results as tuples.) + + + $(LI $(C map())、$(C amap()) 或 $(C reduce()) 可以传入多个函数并以元组方式让出返回结果。) + + + + + $(LI $(C map): Calls functions with the elements of an $(C InputRange) semi-eagerly in parallel.) + + + $(LI $(C map):以并行半延时取值的方式对 $(C InputRange) 中的每一个元素应用指定的函数。) + + + + + $(LI $(C ownerTid) is the thread id of the owner of the current thread.) + + + $(LI $(C ownerTid) 为当前线程的所有者的 ID。) + + + + + $(LI $(C parallel()) accesses the elements of a range in parallel.) + + + $(LI $(C parallel()) 可以并行访问范围中的元素。) + + + + + $(LI $(C parallel): Accesses the elements of a range in parallel.) + + + $(LI $(C parallel):并行访问范围中的元素。) + + + + + $(LI $(C receiveOnly()), $(C receive()), and $(C receiveTimeout()) wait for messages.) + + + $(LI $(C receiveOnly())、$(C receive()) 和 $(C receiveTimeout()) 用于等待消息。) + + + + + $(LI $(C reduce()) makes calculations over the elements of a $(C RandomAccessRange) in parallel.) + + + $(LI $(C reduce()):并行归约计算 $(C RandomAccessRange) 中的元素。) + + + + + $(LI $(C reduce): Makes calculations over the elements of a $(C RandomAccessRange) in parallel.) + + + $(LI $(C reduce):使用指定的函数并行归约计算 $(C RandomAccessRange) 中的元素。) + + + + + $(LI $(C register()), $(C unregister()), and $(C locate()) allow referring to threads by name.) + + + $(LI $(C register())、$(C unregister()) 和 $(C locate()) 允许程序员通过线程名访问线程。) + + + + + $(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 $(C send()) and $(C prioritySend()) send messages.) + + + $(LI $(C send()) 和 $(C prioritySend()) 用于发送消息。) + + + + + $(LI $(C setMaxMailboxSize()) limits the size of mailboxes.) + + + $(LI $(C setMaxMailboxSize()) 用来限制邮箱大小。) + + + + + $(LI $(C spawn()) and $(C spawnLinked()) start threads.) + + + $(LI $(C spawn()) 和 $(C spawnLinked()) 用于启动线程。) + + + + + $(LI $(C static this()) is executed once for each thread; $(C shared static this()) is executed once for the entire program.) + + + $(LI $(C static this()) 会为每个线程执行一次;$(C shared static this()) 则只会在整个程序中执行一次。) + + + + + $(LI $(C synchronized) is for preventing other threads from intervening when a thread is executing a certain piece of code.) + + + $(LI $(C synchronized) 可防止其他线程在当前现成的操作执行到一半时横插一脚致使结果错误。) + + + + + $(LI $(C task): Creates tasks that are executed in parallel.) + + + $(LI $(C task):创建并行执行的任务。) + + + + + $(LI $(C thisTid) is the thread id of the current thread.) + + + $(LI $(C thisTid) 为当前线程的 ID。) + + + + + $(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 $(C yieldForce()):如果任务没有启动,则启动它;如果任务已经完成,则返回任务函数的返回值;如果任务正在执行,则以不占用微处理器的方式等待该任务完成;如果任务在执行中抛出了一个异常,它将在此处重新抛出那个异常。) + + + + + $(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 .remove, associative array) $(C .remove) removes the specified key and its value from the array.) + + + $(LI $(IX .remove, associative array) $(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 .state, Fiber) The execution state of a fiber is determined by its $(C .state) property: + + + $(LI $(IX .state, Fiber) 纤程的执行状态可以通过它的 $(C .state) 特性来检测: + + + + + $(LI $(IX .values) $(C .values) returns a copy of all values as a dynamic array.) + + + $(LI $(IX .values) $(C .values) 返回全部值的动态数组副本。) + + + + + $(LI $(IX EXEC, Fiber.State) $(C EXEC): The fiber is currently executing.) + + + $(LI $(IX EXEC, Fiber.State) $(C EXEC): 纤程当前正在执行。) + + + + + $(LI $(IX Fiber, core.thread) A fiber can be created as an object of class $(C core.thread.Fiber) with a callable entity: + + + $(LI $(IX Fiber, core.thread) 纤程可创建为一个 $(C core.thread.Fiber) 对象,同时带上一个可调用实体: + + + + + $(LI $(IX HOLD, Fiber.State) $(C HOLD): The fiber is paused, meaning that it can be started or resumed.) + + + $(LI $(IX HOLD, Fiber.State) $(C HOLD): 纤程暂停,即表示它可以被开始或继续。) + + + + + $(LI $(IX MailboxFull) $(C OnCrowding.throwException): A $(C MailboxFull) exception is thrown when sending the message.) + + + $(LI $(IX MailboxFull) $(C OnCrowding.throwException):向发送者线程中抛出$(C MailboxFull) 异常。) + + + + + $(LI $(IX OnCrowding) $(C OnCrowding.block): The sender waits until there is room in the mailbox.) + + + $(LI $(IX OnCrowding) $(C OnCrowding.block):阻塞发送者直到邮箱中有空闲空间。) + + + + + $(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): The fiber has terminated. + + + $(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): 纤程已中止。 + + + + + $(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 call, Fiber) A fiber is started and resumed by its $(C call()) member function: + + + $(LI $(IX call, Fiber) 纤程可以通过成员函数 $(C call()) 来开始和继续: + + + + + $(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. + + + $(LI $(IX fiber function) 一个纤程可以执行一个不接受任何参数也不返回任何内容的可调用实例(如函数指针、委托等)。 + + + + + $(LI $(IX getMember) $(C __traits(getMember)) produces a $(I symbol) useful when accessing a member. + + + $(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。 + + + + + $(LI $(IX locate) $(C locate()): Returns the thread that is associated with the specified name. + + + $(LI $(IX locate) $(C locate()):返回线程名关联的线程。 + + + + + $(LI $(IX register, concurrency) $(C register()): Associates a thread with a name.) + + + $(LI $(IX register, concurrency) $(C register()):给线程关联一个名字。) + + + + + $(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 spinForce) $(C spinForce()):与 $(C yieldForce()) 功能相似。不同的是在等待时它将以占用微处理器为代价换取更快的检测任务完成的速度。) + + + + + $(LI $(IX unregister) $(C unregister()): Breaks the association between the specified name and the thread.) + + + $(LI $(IX unregister) $(C unregister()):解除线程和线程名之间的关联。) + + + + + $(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.) + + + $(LI $(IX workForce) $(C workForce()):与 $(C yieldForce()) 功能相似。不同的是,它在等待当前任务完成时会将启动一个新的任务。) + + + + + $(LI $(IX yield, Fiber) A fiber pauses itself ($(I yields) execution to its caller) by $(C Fiber.yield()): + + + $(LI $(IX yield, Fiber) 纤程通过 $(C Fiber.yield()) 可以将自己暂停($(I 跳转) 到调用函数): + + + + + $(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. + + + $(LI 类也可以被定义为 $(C synchronized),这样同一时间只能有一个线程调用这个类实例对象的成员函数。 + + + + + $(LI A class destructor must not access a member that is managed by the garbage collector. + + + $(LI 类的析构函数不能访问由垃圾回收器管理的成员。 + + + + + $(LI A class destructor must not allocate new memory that is managed by the garbage collector. + + + $(LI 类的析构函数一定不要分配由垃圾回收器管理的新内存。 + + + + + $(LI A fiber and its caller are executed on the same thread (i.e. not at the same time).) + + + $(LI 纤程及其调用函数都在同一个线程里执行(即不会同时执行)。) + + + + + $(LI A fiber pauses itself by $(I yielding) to its caller and the caller resumes its fiber by $(I calling) it again.) + + + $(LI 纤程通过 $(I 跳出) 到调用函数的方式来暂停自己;而调用函数通过再次 $(I 调用) 纤程的方式来恢复它。) + + + + + $(LI A function pointer of type $(C bool function(Tid)): The specified function is called.) + + + $(LI 类型为 $(C bool function(Tid) 的函数指针):调用指定的函数。) + + + + + $(LI A later $(C call()) will resume the function right after the fiber's last $(C Fiber.yield()) call. + + + $(LI 随后的 $(C call()) 会在纤程最后一次调用 $(C Fiber.yield()) 的那个地方恢复它。 + + + + + $(LI Assigning an empty associative array.) + + + $(LI 用一个空的关联数组赋值。) + + + + + $(LI Assignment associates a variable with an object. + + + $(LI 赋值会把一个变量与一个对象相关联。 + + + + + $(LI Assigns the initial value to $(C result)) + + + $(LI 把初始值赋给 $(C result)) + + + + + $(LI Because concurrency by data sharing is hard to implement correctly, prefer concurrency by message passing, which is the subject of this chapter.) + + + $(LI 基于数据共享的并发难以正确实现,因此推荐使用本章讲解的基于消息传递的并发。) + + + + + $(LI Because fiber functions do not take parameters, $(C fibonacciSeries()) cannot be used directly as a fiber function. + + + $(LI 因为纤程函数不接受参数,因此 $(C fibonacciSeries()) 不能被直接用作纤程函数。 + + + + + $(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 Constructing and using the fiber explicitly through its member functions exposes $(I lower level) implementation details, compared to alternative designs.) + + + $(LI 与另外的设计方式相比,通过成员函数的方式构建和使用纤程会暴露$(I底层)的实现细节。) + + + + + $(LI Each member is tested with $(C hasUDA) to determine whether it has the $(C Encrypted) attribute. + + + $(LI 每个成员都使用 $(C hasUDA) 来测试,以便确定它是否拥有 $(C Encrypted) 特性。 + + + + + $(LI Even then, prefer $(I message passing concurrency), which has been the topic of the previous chapter.) + + + $(LI 若要使用并发,优先选择上一章的 $(I 基于消息传递的并发) 模型。) + + + + + $(LI Exceptions may be thrown during message passing: $(C MessageMismatch), $(C OwnerTerminated), $(C LinkTerminated), $(C MailboxFull), and $(C PriorityMessageException).) + + + $(LI 消息传递的过程中也可能会抛出异常:$(C MessageMismatch)、$(C OwnerTerminated)、$(C LinkTerminated)、$(C MailboxFull) 以及 $(C PriorityMessageException)。) + + + + + $(LI Executes the expression $(C result = func(result, element)) for every element) + + + $(LI 对每一个元素执行 $(C result = func(result, element)) 这样的表达式) + + + + + $(LI Fibers enable multiple call stacks per thread instead of the default single call stack per thread.) + + + $(LI 通过纤程,可以实现每个线程拥有多个调用栈;而默认情况下是单个线程单个调用栈。) + + + + + $(LI Fibers provide cooperative multitasking, which has different trade-offs from preemptive multitasking.) + + + $(LI 纤程能够提供协作式的多任务(它与抢占式的多任务相比有不同的好处)。) + + + + + $(LI Fibers simplify algorithms that rely heavily on the call stack.) + + + $(LI 纤程可以大大简化那些严重依赖于调用栈的算。) + + + + + $(LI Fibers simplify asynchronous input/output operations.) + + + $(LI 纤程可以简化异步输入/输出操作。) + + + + + $(LI It is an error to execute operations in parallel unless those operations are independent from each other.) + + + $(LI 操作只有在相互独立时才能并行执行;否则会出错。) + + + + + $(LI Only $(C shared) data can be shared; $(C immutable) is implicitly $(C shared).) + + + $(LI 只有用 $(C shared) 定义的变量才能共享;$(C immutable) 隐含了 $(C shared)。) + + + + + $(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 与将元素都复制到调用函数的上下文的设计相比,大家都不太愿意通过 $(C ref) 参数的方式来呈现各个元素。) + + + + + $(LI Removing them one-by-one from the associative array.) + + + $(LI 从关联数组中逐个移除它们。) + + + + + $(LI Returns the final value of $(C result)) + + + $(LI 返回最终 $(C result) 的值) + + + + + $(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 Tasks can explicitly be created, started, and waited for by $(C task()), $(C executeInNewThread()), and $(C yieldForce()), respectively.) + + + $(LI 任务可以显示地用 $(C task()) 创建、用 $(C executeInNewThread()) 启动,以及用 $(C yieldForce()) 来等待完成。) + + + + + $(LI The $(C core.atomic) module enables safe data sharing that can be multiple times faster than $(C synchronized).) + + + $(LI $(C core.atomic) 模块提供安全的数据共享方案,而且还比 $(C synchronized) 快很多倍。) + + + + + $(LI The $(C core.sync) package includes many other concurrency primitives.) + + + $(LI $(C core.sync) 包包含了许多其他经典的并发基本操作。) + + + + + $(LI The act of copying associates an additional variable with an object. + + + $(LI 复制操作将增加一个与对象关联的变量。 + + + + + $(LI The call stack enables efficient allocation of local state and simplifies certain algorithms, especially the recursive ones.) + + + $(LI 调用栈可以让本地状态获得高效分配,并且可以简化某些算法(尤其是递归算法)。) + + + + + $(LI The caller starts and resumes the fiber by its $(C call()) member function.) + + + $(LI 调用函数通过成员函数 $(C call()) 来开始和恢复纤程。) + + + + + $(LI The color attribute of each member is determined with $(C colorAttributeOf()), which we will see below.) + + + $(LI 每个成员的 color 属性可以使用 $(C colorAttributeOf()) 来检测。下面便来看看这个方法。) + + + + + $(LI The duration between each step: This information is used for determining when the robot's next step will be. + + + $(LI 每一步的间隔时间:决定机器人何时走下一步。 + + + + + $(LI The exceptions that are escaped from tasks can be caught later by most of the parallelism functions like $(C yieldForce()).) + + + $(LI 任务执行过程中抛出的异常可以在随后被类似 $(C yieldForce()) 的并行函数捕获。) + + + + + $(LI The fiber function above takes a reference to an $(C int). + + + $(LI 上面这个函数接收的是一个 $(C int) 型引用参数。 + + + + + $(LI The members of the type are determined by $(C __traits(allMembers)).) + + + $(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) + + + + + $(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 机器人的编号 (id):这个参数会随着消息传回线程所有者,这样我们就可以通过它确认消息的来源。 + + + + + $(LI The origin: This is where the robot starts moving from. + + + $(LI 起点:机器人的初始位置。 + + + + + $(LI The owner cannot automatically catch exceptions that are thrown from the worker.) + + + $(LI 所有者线程无法自动捕获工作线程中的异常。) + + + + + $(LI The solution above does not provide a range interface, making it incompatible with existing range algorithms.) + + + $(LI 上面的解决方法并没有提供范围接口,因此它无法与已有的范围算法兼容。) + + + + + $(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.) +) +) + + + $(LI 有一个 $(C d) 在字面量 $(STRING "résumé"d) 的末尾,指定了它的类型是一个 $(C dchar) 型数组。) +) +) + + + + + $(LI This program uses two indexes to make a slice: + + + $(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 When iterating over $(C RandomAccessRange) ranges: + + + $(LI 在迭代 $(C RandomAccessRange) 范围时: + + + + + $(LI When iterating over non-$(C RandomAccessRange) ranges: + + + $(LI 在迭代非 $(C RandomAccessRange) 范围时: + + + + + $(LI When iterating over the result ranges of $(C asyncBuf()) or parallel $(C map()) (both are explained later in this chapter): + + + $(LI 在迭代 $(C asyncBuf()) 或 $(C map()) 返回的范围时(稍后本章会介绍这两个函数): + + + + + $(LI When needed, $(C TaskPool) objects other than $(C taskPool) can be used.) + + + $(LI 如有必要,也可以使用 $(C TaskPool) 对象来代替 $(C taskPool) 。) + + + + + $(LI When the current element is ready for use, the fiber pauses itself by calling $(C Fiber.yield()).) + + + $(LI 在当前元素准备好时,纤程会通过调用 $(C Fiber.yield()) 来暂停自己。) + + + + + $(LI When threads do not depend on other threads, prefer $(I parallelism), which has been the topic of the previous chapter. + + + $(LI 如果线程间相互独立,推荐使用上一章的 $(I 并行)。 + + + + + $(LI When threads do not depend on other threads, prefer $(I parallelism). + + + $(LI 如果线程相互独立,优先选择$(I 并行)。 + + + + + $(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 类的析构函数一定不分配由垃圾回收器管理的新内存。这是因为垃圾回收器没有被要求保证在垃圾回收周期内能分配新的对象。) + + + + + $(LINK2 /ders/d.en/inheritance.html, Polymorphism) is for abstracting parts of programs from each other and is achieved by class $(I interfaces). + + + $(LINK2 /ders/d.cn/inheritance.html, 多态性) 是从类之间抽象出部分代码,通过 $(I 接口) 实现的。 + + + + + $(MONO +$(B +Operation Value Meaning) + + + $(MONO +$(B +操作 值 含义) + + + + + $(MONO +$(B Operation Thread A Thread B) +──────────────────────────────────────────────────────────────────────────── + + + $(MONO +$(B 操作 线程 A 线程 B) +──────────────────────────────────────────────────────────────────────────── + + + + + $(MONO +The call stack grows upward +as function calls get deeper. + + + $(MONO +随着函数调用的深入, +调用栈会不断往上增长。 + + + + + $(OL +$(LI +Providing the index of the element to remove. + + + $(OL +$(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 TaskPool.finish()) tells the object to stop processing when all of its current tasks are completed. + + + $(P +$(C TaskPool.finish()) 会在所有任务完成后停止程序进程。 + + + + + $(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. + + + $(P +$(C amap()) 的确比 $(C map()) 的速度快。但相应的代价是它需要提前准备好一个足够大的数组来储存结果。 + + + + + $(P +$(C asyncBuf()) can be used outside of $(C foreach) loops as well. + + + $(P +$(C asyncBuf()) 也可以在 $(C foreach) 外使用。 + + + + + $(P +$(C asyncBuf()) takes a range and an optional $(I buffer size) that determines how many elements to be made available during each wave: +) + + + $(P +$(C asyncBuf()) 有两个形参:一个是范围;另一个是可选的$(I 缓冲区大小)。缓冲区大小决定了每轮将有多少元素被加载到缓冲区。 +) + + + + + $(P +$(C atomicOp) can be used with other binary operators as well. + + + $(P +$(C atomicOp) 也可以用在其他二元操作符上。 + + + + + $(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. + + + $(P +$(C is) 确定两个类变量是否提供对同一对象的访问。 + + + + + $(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 +$(C main()) 将 $(C spawn()) 的返回值储存在 $(C worker) 变量中并通过它来给工作线程发送消息。 + + + + + $(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. + + + $(P +只有在非 $(C RandomAccessRange) 中与$(I 工作单元大小)相同数量的元素被顺序处理时,$(C parallel()) 才会开始并行执行。 + + + + + $(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 receiveOnly()) can expect only one type of message. + + + $(P +$(C receiveOnly()) 只能接收一种类型的消息。 + + + + + $(P +$(C reduce()) is another high-level algorithm commonly found in many functional languages. + + + $(P +$(C reduce()) 也是一个经常在函数式编程中使用的高阶函数。 + + + + + $(P +$(C reduce()) uses the elements in sequence to reach the final value of the result: +) + + + $(P +$(C reduce()) 将按照顺序使用元素: +) + + + + + $(P +$(C scope) statements can be specified as blocks as well: +) + + + $(P +$(C scope) 语句也可以像块一样使用: +) + + + + + $(P +$(C send()) sends messages and $(C receiveOnly()) waits for a message of a particular type. + + + $(P +$(C send()) 可用于发送消息,而 $(C receiveOnly()) 可用于等待待定类型的消息。 + + + + + $(P +$(C spawn()) takes a function pointer as a parameter and starts a new thread from that function. + + + $(P +$(C spawn()) 需要一个函数指针作为参数,并且会从该函数启动一个新线程。 + + + + + $(P +$(C spawnLinked()) is used in the same way as $(C spawn()). + + + $(P +$(C spawnLinked()) 与 $(C spawn()) 用法相同。 + + + + + $(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). + + + $(P +$(C std.algorithm.map())、$(C taskPool.map())、$(C taskPool.amap()) 和 $(C taskPool.reduce()) 都允许传入多个函数处理元素,并返回类型为 $(C Tuple) 的结果。 + + + + + $(P +$(C std.algorithm.map) takes the function as its template parameter, and the range as its function parameter. + + + $(P +$(C std.algorithm.map) 有一个模版参数用来接收函数,有一个函数形参用来接收范围。 + + + + + $(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 +$(C std.concurrency) does not provide such a convenience for general exception types. + + + $(P +$(C std.concurrency) 并未提供这种捕获异常的方法。 + + + + + $(P +$(C taskPool) contains appropriate number of tasks depending on the environment that the program runs under. + + + $(P +$(C taskPool) 需要容纳的任务总数取决于程序运行的具体环境。 + + + + + $(P +$(C taskPool.map()) from the $(C std.parallelism) module works essentially the same as $(C std.algorithm.map). + + + $(P +$(C std.parallelism) 模块中的 $(C taskPool.map()) 和 $(C std.algorithm.map) 的功能相同。 + + + + + $(P +$(C taskPool.reduce()) should be used only under these considerations. + + + $(P +只有在满足以上条件的情况下才可以使用 $(C taskPool.reduce())。 + + + + + $(P +$(C thisTid()) returns the identifier of the $(I current) thread. + + + $(P +$(C thisTid()) 返回$(I 当前)线程的 ID。 + + + + + $(P +$(C variable) that is modified inside $(C worker()) is not the same $(C variable) that is seen by $(C main()). + + + $(P +$(C worker()) 修改的 $(C variable) 与 $(C main()) 访问的 $(C variable) 并不相同。 + + + + + $(P +$(C yieldForce()) can be called in a $(C try-catch) block to catch the exceptions that are thrown by the task. + + + $(P +可以在 $(C try-catch) 语句块中调用 $(C yieldForce()) 来捕获由 task 抛出的异常。 + + + + + $(P +$(HILITE $(I $(B Note:) This feature is not supported by dmd 2.074.0.)) +) + + + $(P +$(HILITE $(I $(B 注意:) dmd 2.074.0 并不支持此功能。)) +) + + + + + $(P +$(HILITE $(I $(B Note:) This feature is not supported by dmd 2.078.0.)) +) + + + $(P +$(HILITE $(I $(B 注意:) dmd 2.078.0 不支持此功能。)) +) + + + + + $(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:) If the shorter equivalents of the expression above are used (i.e. $(C ++(*value)) and $(C &#8209;&#8209;(*value))), then the compiler warns that such read-modify-write operations on $(C shared) variables are deprecated.) +) + + + $(P +$(I $(B 注意:) 如果将上方的等式换成自增或自检(例如, $(C ++(*value)) 和 $(C &#8209;&#8209;(*value))),编译器会警告:对 $(C shared) 变量执行的读取-修改-写入操作已被弃用。) +) + + + + + $(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 +$(I $(B 注意:)实际上,当新递归函数直接返回调用自己的结果时,编译器会使用一种名叫“尾调用优化”的技术——它可以去除每次递归调用时生成的各个帧。) +) + + + + + $(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. + + + $(P +$(I $(B 注意:) 等待锁是一个相对昂贵的操作,它可能会显著降低程序的执行速度。 + + + + + $(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:) Prefer message-passing to signal a thread.) +) + + + $(P +$(I $(B 注意:) 推荐使用消息传递向线程发送控制信号。) +) + + + + + $(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 +$(I $(B 注意:)在不同的系统上运行上面的程序可能会有不同的执行时间,但大致都是“4 秒除以内核数”的结果。) +) + + + + + $(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). + + + $(P +$(I $(B 注意:)之所以上面的代码需要一个独立的 $(C averageGrade()) 函数是因为像 $(C TaskPool.map) 这样的成员模版函数存在无法使用局部委托的限制。 + + + + + $(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:) The program above uses the following features from Phobos:) +) + + + $(P +$(I $(B 请注意:) 上面这个程序使用了下面几个源自 Phobos 库里的功能:) +) + + + + + $(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 ()) As has been mentioned in the previous chapter, when there is no argument to pass, functions can be called without parentheses: +) + + + $(P +$(IX ()) 我们在之前的章节中提到过,如果调用函数时不需要传递参数,那我们可以省略圆括号: +) + + + + + $(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 @) 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 @property) Properties are member functions that are used like member variables. + + + $(P +$(IX @property) 特性实际上就是成员函数,但我们却可以像使用成员变量一样调用它们。 + + + + + $(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. + + + $(P +$(IX CPU bound) $(IX I/O bound) $(IX thread performance) 每个操作系统都会限制线程同时运行的个数。 + + + + + $(P +$(IX CPU) $(IX microprocessor) The brain of the computer is the microprocessor (or the CPU, short for $(I central processing unit)). + + + $(P +$(IX CPU) $(IX microprocessor) 计算机的大脑是微处理器(CPU,即 $(I 中央处理单元) 的简称)。 + + + + + $(P +$(IX MessageMismatch) If the types do not match, a $(C MessageMismatch) exception is thrown: +) + + + $(P +$(IX MessageMismatch) 如果类型不匹配,则程序将会抛出一个 $(C MessageMismatch) 异常: +) + + + + + $(P +$(IX OOP) $(IX object oriented programming) $(IX user defined type) Similar to structs, $(C class) is a feature for defining new types. + + + $(P +$(IX OOP) $(IX object oriented programming) $(IX user defined type) 与结构相似,$(C class) 具有定义新类型的功能。 + + + + + $(P +$(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D中,类提供 $(I 面向对象编程) (OOP) 模型。下面是OOP的主要方面: +) + + + $(P +$(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D语言中,类提供 $(I 面向对象编程) (OOP) 模型。下面是OOP的主要方面: +) + + + + + $(P +$(IX State, Fiber) $(C Fiber.State) is an enum with the following values: +) + + + $(P +$(IX State, Fiber) $(C Fiber.State) 是一个枚举类型,有下面几种值: +) + + + + + $(P +$(IX Thread.sleep) To simulate long-lasting operations, the following examples call $(C Thread.sleep()) from the $(C core.thread) module. + + + $(P +$(IX Thread.sleep) 下面的例子使用了 $(C core.thread) 模块中的 $(C Thread.sleep()) 来模拟长耗时操作。 + + + + + $(P +$(IX Variant, concurrency) $(C std.variant.Variant) is a type that can encapsulate any type of data. + + + $(P +$(IX Variant, concurrency) $(C std.variant.Variant) 类型可以封装任意类型的数据。 + + + + + $(P +$(IX __gshared) An exception of this rule is a variable that is defined as $(C __gshared): +) + + + $(P +$(IX __gshared) 除非使用 $(C __gshared) 定义变量: +) + + + + + $(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) 属性的实际含义完全由程序员根据程序的需要来决定。 + + + + + $(P +$(IX core) Most modern microprocessors consist of more than one $(I core), each of which can operate as an individual processing unit. + + + $(P +$(IX core) 大多数现代的微处理器都包含一个以上的$(I 内核),其中的每一个内核都可以当作单独的处理单元使用。 + + + + + $(P +$(IX coroutine) $(IX green thread) $(IX thread, green) A fiber is a $(I thread of execution) enabling a single thread achieve multiple tasks. + + + $(P +$(IX coroutine) $(IX green thread) $(IX thread, green) 纤程(Fiber)是一个$(I执行线程),它可以让单个线程完成多个任务。 + + + + + $(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. + + + $(P +$(IX deadlock) 我们会用一个例子来解释程序为什么出错。示例中有一个线程想要将账户 A 中的资金转到账户 B,而另一个线程想要将账户 B 中的资金转到账户 A。 + + + + + $(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 finalizer versus destructor) However, different from structs, class destructors are not executed at the time when the lifetime of a class object ends. + + + $(P +$(IX finalizer versus destructor) 不过,与结构有所不同的是,类的析构函数在类对象的生命期结束时并不会被执行。 + + + + + $(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. + + + $(P +$(IX flush, std.stdio) $(I $(B 注意:)正常情况下向类似 $(C stdout) 这样的输出流直接输出的字符并不会立刻显示出来。 + + + + + $(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). + + + $(P +$(IX fold, std.algorithm) $(C reduce()) 与 $(C std.algorithm.fold) 相同。我们已经在 $(LINK2 /ders/d.cn/ranges.html, Ranges) 一章中学习到了相关知识。 + + + + + $(P +$(IX foreach, parallel) $(C parallel()) accesses the elements of a range in parallel. + + + $(P +$(IX foreach, parallel) $(C parallel()) 以并行的方式访问范围中的元素。 + + + + + $(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 immutable, concurrency) On the other hand, since $(C immutable) variables cannot be modified, there is no problem with sharing them directly. + + + $(P +$(IX immutable, concurrency) 另一方面,由于 $(C immutable) 变量无法被修改,它们可以直接共享。 + + + + + $(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. + + + $(P +$(IX local state) 函数的参数、非静态变量、返回值、临时表达式,以及在它执行期间需要的所有附加信息组合成了它的$(I本地状态)。 + + + + + $(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. + + + $(P +$(IX lock) $(C synchronized) 会在后台创建一个锁,同一时间只有一个线程能持有这个锁。 + + + + + $(P +$(IX map, std.algorithm) It helps to explain $(C map()) from the $(C std.algorithm) module before explaining $(C taskPool.map()). + + + $(P +$(IX map, std.algorithm)在介绍 $(C taskPool.map()) 之前,先了解一下 $(C std.algorithm) 模块里的 $(C map()),这样有助于理解本节的内容。 + + + + + $(P +$(IX message) $(B Message): Data that is passed between threads are called messages. + + + $(P +$(IX message) $(B 消息):在线程间传递的数据叫做消息。 + + + + + $(P +$(IX owner) $(B Owner): Any thread that starts another thread is called the owner of the new thread. + + + $(P +$(IX owner) $(B 所有者):任何启动另一个线程的线程即为该新线程的所有者。 + + + + + $(P +$(IX parallelism vs. concurrency) $(IX concurrency vs. parallelism) The following are the differences between parallelism and concurrency: +) + + + $(P +$(IX parallelism vs. concurrency) $(IX concurrency vs. parallelism) 并发和并行的区别主要有以下几点: +) + + + + + $(P +$(IX reduce, std.algorithm) As with $(C map()), it helps to explain $(C reduce()) from the $(C std.algorithm) module first. + + + $(P +$(IX reduce, std.algorithm)与 $(C map()) 一样,先来了解下 $(C std.algorithm) 模块里的 $(C reduce())。 + + + + + $(P +$(IX scalar) That definition can be read as $(I 5 double values). + + + $(P +$(IX 标量) 这个定义可以理解为 $(I 5 个 double 值)。 + + + + + $(P +$(IX setMaxMailboxSize) $(C setMaxMailboxSize()) is used for limiting the number of messages that a mailbox can hold. + + + $(P +$(IX setMaxMailboxSize)$(C setMaxMailboxSize()) 可以限制邮箱保存的消息数量。 + + + + + $(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)). + + + $(P +$(IX stack frame) 为函数调用的本地状态分配的存储空间叫$(I帧(frame)) 或$(I栈帧(stack frame))。 + + + + + $(P +$(IX sum, std.algorithm) $(I $(B Note:) The code above is only for demonstration. + + + $(P +$(IX sum, std.algorithm) $(I $(B 注意:)上面的代码仅用于演示。 + + + + + $(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. + + + $(P +$(IX task) $(IX executeInNewThread) $(IX yieldForce) 当任务无法与范围元素相对应或者无法通过它们来表示时,程序员可以自己按照以下三步手动处理: + + + + + $(P +$(IX thread id) $(B Thread identifier): Every thread has an id, which is used for specifying recipients of messages. + + + $(P +$(IX thread id) $(B 线程 ID):每个线程都有一个 ID,你可以用它来指定消息的接收者。 + + + + + $(P +$(IX thread) $(B Thread): Operating systems execute programs as work units called $(I threads). + + + $(P +$(IX thread) $(B 线程):操作系统执行程序的工作单元叫做 $(I 线程)。 + + + + + $(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). + + + $(P +$(IX thread) 执行程序某一部分的执行流被称为 $(I 执行线程) 或 $(I 线程)。 + + + + + $(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 +$(IX thread_joinAll) $(C main()) 末尾处的 $(C thread_joinAll()) 会阻塞所有者线程,等待所有工作线程终止。 + + + + + $(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): +) + + + $(P +$(IX totalCPUs) 在了解实现方法之前,我们先来看下如何通过使用 $(C std.parallelism.totalCPUs) 来获取系统可用的处理器核心数: +) + + + + + $(P +$(IX worker) $(B Worker): Any thread that is started by an owner is called a worker. + + + $(P +$(IX worker) $(B 工作线程):由所有者启动的任何线种都叫做工作线程。 + + + + + $(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. + + + $(P +$(IX yield, std.concurrency) 还请注意:此时,它是 $(C std.concurrency.yield) 函数,而非我们上面使用的那个 $(C Fiber.yield) 成员函数。 + + + + + $(P +$(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期结束时,类的析构函数并不执行。正如上面我们看到的,析构函数在一个垃圾回收周期内的未来某个时候执行。(通过这样的区分,更准确的说,类析构函数应该叫 $(I 终结函数)) 。 +) + + + $(P +$(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期结束时,类的析构函数并不执行。正如上面我们看到的,析构函数在一个垃圾回收周期内的未来某个时候执行。(通过这样的区分,更准确的说,类的析构函数应该叫 $(I 终结函数)) 。 + + + + + $(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.cn/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) +) + + + + + $(P +A $(C static assert) inside the $(C std.concurrency) module prevents accessing $(I mutable) data from another thread: +) + + + $(P +$(C std.concurrency) 模块中的 $(C static assert) 阻止线程访问其他线程中的$(I 可变)数据: +) + + + + + $(P +A better way would be to represent the concept of area as a property. + + + $(P +所以我们最好用特性来实现面积。 + + + + + $(P +A class variable on the other hand is a language feature for accessing class objects. + + + $(P +另一方面,类变量一种访问类对象的语言功能。 + + + + + $(P +A compiler is another tool that reads the instructions of a program from source code. + + + $(P +编译器是另一种从源码中读取程序指令的工具。 + + + + + $(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 +这种设计的缺点是对象很容易出现不一致性:虽然矩形永远都是“长 × 宽 == 面积”,但是如果成员变量能被随意修改的话,对象的一致性将极易被破坏。 + + + + + $(P +A lambda function or an object of a type that defines the $(C opCall) member can also be used as the task function. + + + $(P +lambda 函数和定义了 $(C opCall) 的对象都可以被当作任务函数使用。 + + + + + $(P +A well known example of this problem is a function that tries to transfer money from one bank account to another. + + + $(P +这个问题有一个著名的例子:想要编写一个函数将某个银行账户中的资金转到另一个账户。 + + + + + $(P +According to that output, the elements are produced and used sequentially. + + + $(P +从输出结果来看,这些元素都被依次生成和使用。 + + + + + $(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, 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 +除此之外,由于在最后串行运算的时候使用的函数与之前相同,函数的返回值必须与函数的参数类型相同或可以相互转换。 + + + + + $(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 +All of the messages in this simple program go from the worker to the owner. + + + $(P +本例中的所有消息都是从工作线程向线程所有者传递的。 + + + + + $(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. + + + $(P +所有程序的所有在操作系统指定时间活动的线程将会在微处理器的每个核心上执行。 + + + + + $(P +Almost every range needs to store some information to remember its state of iteration. + + + $(P +几乎所有的范围都需要存储某些信息,以便记住迭代状态。 + + + + + $(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.cn/Programming_in_D.pdf, PDF)、$(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.epub, EPUB)、$(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.azw3, AZW3) 和 $(LINK_DOWNLOAD http://ddili.org/ders/d.cn/Programming_in_D.mobi, MOBI)。 + + + + + $(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. + + + $(P +注意不要混淆了变量和函数的名字:成员变量的名字多了一个 $(C _)。 + + + + + $(P +Also observe that total number of swaps is 10 times 10 thousand. + + + $(P +除此之外我们还可以看到程序一共启动了 10 个线程,每个线程执行 1 万次交换。 + + + + + $(P +Alternatively, a subclass of $(C Fiber) can be defined and the fiber function can be passed to the constructor of the superclass. + + + $(P +另外,它也可以定义为 $(C Fiber) 的子类,并将纤程函数传递给父类的构造函数。 + + + + + $(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 it is possible that the program can indeed produce that result, most of the time the actual outcome would be one of the following: +) + + + $(P +虽然程序的确可以得到我们预期的那种结果,但是实际上大多数情况下程序的输出都是下面这样的: +) + + + + + $(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 $(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. + + + $(P +由于上面的成员函数 $(C print()) 实际上已经可以顺序访问每个元素,因此没必要再去为树实现一个 $(C InputRange)。 + + + + + $(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. + + + $(P +虽然像生成 Fibonacci 序列这样的简单任务并不能突显出纤程的优势,但是为了简化说明,我们还是会实现一个纤程版本,并通过它来了解一下常用的纤程操作。 + + + + + $(P +Although the program above gets compiled successfully, in most cases it would work incorrectly. + + + $(P +虽然上面这个程序顺利通过了编译,但在大多数情况下它并不能正确工作。 + + + + + $(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 optimizations are effective, they cannot be applied automatically to layers higher than the very low-level operations. + + + $(P +虽然这些优化效果显著,但是它们通常只能自动应用在非常低级别的操作上。 + + + + + $(P +Although these statements are closely related to exceptions, they can be used without a $(C try-catch) block. + + + $(P +虽然这些语句只在特殊情况下使用,但是没有 $(C try-catch) 块也能用。 + + + + + $(P +Although we have achieved generating the Fibonacci series with a fiber, that implementation has the following shortcomings: +) + + + $(P +虽然我们成功通过纤程生成了 Fibonacci 序列,但是这个实现存在以下几点不足: +) + + + + + $(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. + + + $(P +索引不必是恒定值;变量的值也能用作索引,这会让数组更有用。 + + + + + $(P +An interpreter is a tool (a program) that reads the instructions from source code and executes them. + + + $(P +解释器是一种能从源码中读取指令并执行相应命令的工具(程序)。 + + + + + $(P +Another difference from structs is that some member functions are automatically inherited from the $(C Object) class. + + + $(P +与结构不同的是一些成员函数自动继承自 $(C Object) 类。 + + + + + $(P +Another method involves more than one thread reading from and writing to the same data. + + + $(P +另外一种共享消息的方法是多个线程读写同一块数据。 + + + + + $(P +Another method would be to send the actual exception object itself to the owner. + + + $(P +另外一种方法是直接将异常对象发送回所有者。 + + + + + $(P +Another overload of $(C task()) takes the function as its first function parameter instead: +) + + + $(P +$(C task()) 函数的另一个重载形式是将函数作为其第一个形参: +) + + + + + $(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 +另一种确保同一时间只有一个线程能修改变量的方法是原子操作。这个功能是由处理器、编译器或操作系统提供的。 + + + + + $(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 +Array elements can be removed with the $(C remove()) function from the $(C std.algorithm) module. + + + $(P +可以使用模块 $(C std.algorithm) 里的 $(C remove()) 函数删除数组元素。 + + + + + $(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, $(C main()) receives the elements of the series through $(C current) and prints them: +) + + + $(P +最后,$(C main()) 通过 $(C current) 接收序列的各个元素,并输出它们: +) + + + + + $(P +As a result, assigning the quarter of its current value to $(C area) ends up halving both sides of the rectangle: +) + + + $(P +对这个版本的 $(C area) 赋值时矩形两边的长度都会改变: +) + + + + + $(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: +) + + + $(P +最后,在一个有 4 核的系统上,将 $(C Student) 范围传递给 $(C parallel()) 可以使上面的程序在 1 秒之内完成: +) + + + + + $(P +As a result, the elements that are produced by a fiber function are used conveniently as an $(C InputRange): +) + + + $(P +最后,纤程函数产生所有元素都可以方便地当作 $(C InputRange) 来使用: +) + + + + + $(P +As a result, the objects that are associated with $(C var1) and $(C var2) are different. + + + $(P +最后,与 $(C var1) 和 $(C var2) 关联的那些对象并不相同。 + + + + + $(P +As a simple example, let's consider a rectangle struct that consists of two members: +) + + + $(P +我们使用下面这个包含两个成员的矩形结构体作为示例: +) + + + + + $(P +As an example, let's write the function above with a $(C scope(failure)) statement: +) + + + $(P +例如,让我们用 $(C scope(failure)) 语句写一下面的函数: +) + + + + + $(P +As an extreme example, objects may even begin their lives in inconsistent states: +) + + + $(P +有一个极端例子,对象在其生命期开始时就已经处在不一致的状态: +) + + + + + $(P +As can be seen, at the end of the previous scenario both $(C i) and $(C j) end up having the value 1. + + + $(P +经过这种情况的执行后 $(C i) 和 $(C j) 的值最后都变成了 1。 + + + + + $(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: +) + + + $(P +与之前 $(C parallel()) 和 $(C map()) 的例子一样,只需要导入 $(C std.parallelism) 模块并调用 $(C taskPool.reduce()) 就可以利用多核心的资源了: +) + + + + + $(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). + + + $(P +在 $(LINK2 /ders/d.cn/null_is.html, $(CH4 null) 值和 $(CH4 is) 运算符一章)已提到过,类变量可以为 $(C null)。 + + + + + $(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 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. + + + $(P +函数的调用层次会在函数调用其他函数时变得更深,而在函数返回时变浅,因此调用栈的大小也会相应地增大和减小。 + + + + + $(P +As obvious from its behavior, $(C reduce()) uses a loop in its implementation. + + + $(P +就像我们看到的那样,$(C reduce()) 使用一个循环来实现其功能。 + + + + + $(P +As seen in the outputs, the blocks of the $(C scope) statements are executed in reverse order. + + + $(P +在输出中我们看到,$(C scope) 语句块是按相反的顺序执行的。 + + + + + $(P +As soon as a new thread is started, the owner and the worker start executing separately as if they were independent programs: +) + + + $(P +新线程启动后,所有者和工作线程将会独立执行,看上去它们就像是独立的程序: +) + + + + + $(P +As tasks are executed on separate threads, the exceptions that they throw cannot be caught by the thread that started them. + + + $(P +因为所有任务都执行在一个独立线程上,所以启动它们的那个线程并不能捕捉到这些任务抛出的异常。 + + + + + $(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. + + + $(P +当编译器按照语言规则编译程序时,一旦碰到 $(I 非法) 指令就会停止编译。 + + + + + $(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: +) + + + $(P +$(C area) 是由矩形的长宽相乘计算得到的,所以这次它将始终与长宽保持一致: +) + + + + + $(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: +) + + + $(P +采用这种方法不会产生不同类型的 $(C Task) 模版实例,因此可以把它们放在同一个数组里: +) + + + + + $(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 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. + + + $(P +上一章的 $(C std.parallelism) 自动捕获 task 执行中抛出的异常并在所有者的线程中重新抛出。 + + + + + $(P +As we will see later in $(LINK2 /ders/d.en/memory.html, the Memory Management chapter), class destructors must observe the following rules: +) + + + $(P +在后面的 $(LINK2 /ders/d.en/memory.html, 内存管理一章) 将会看到,类的析构函数必须遵循以下几条规则: +) + + + + + $(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 +As you would also remember, comparing a reference to $(C null) by the $(C ==) or the $(C !=) operator is an error. + + + $(P +大家都还记得吧,引用与运算符 $(C null) 不能通过运算符 $(C ==) 或 $(C !=) 进行比较。 + + + + + $(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 +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: +) + + + $(P +假设它在执行过程中定义了一个名为 $(C result) 的变量,那 $(C reduce()) 就是按照下面这几步运作的: +) + + + + + $(P +Atomic operations enable $(I lock-free data structures) as well, which are beyond the scope of this book. + + + $(P +原子操作还能让我们实现$(I 无锁数据结构),但这已经超出了本书的范围。 + + + + + $(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 +Because the consumer is slower than the producer, the memory that the program above uses would grow continuously. + + + $(P +因为消费者处理消息的速度远低于生产者产生消息的速度,程序的内存占用会持续增长。 + + + + + $(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: +) + + + $(P +这次两个 $(C synchronized) 块使用了同一个锁,因此在同一时间它们当中只有一个可以执行。 +) + + + + + $(P +Before seeing an example of $(C setMaxMailboxSize()), let's first cause a mailbox to grow continuously. + + + $(P +在展示 $(C setMaxMailboxSize()) 示例之前,我们先来创建一个消息数量会持续增长的邮箱。 + + + + + $(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. + + + $(P +在这些场景的背后,源自 $(C std.parallelism) 模块的所有并行算法使用的任务对象都是 $(C TaskPool) 容器里的元素。 + + + + + $(P +Being character arrays, strings can contain control characters like $(STRING '\n') as well. + + + $(P +作为字符数组,字符串能包含像 $(STRING '\n') 这样的控制字符。 + + + + + $(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). + + + $(P +由于机器码太靠近硬件,因此它并不适合用于表示 $(I 扑克牌) 或 $(I 学生记录) 等较为高级的概念。 + + + + + $(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 +By the time parallel $(C foreach) ends, all of the operations inside the loop have been completed for all of the elements. + + + $(P +对每个元素的所有操作完成后,并行的 $(C foreach) 才算完成执行。 + + + + + $(P +Class objects are constructed by the $(C new) keyword; they do not have names. + + + $(P +类对象由关键字 $(C new) 构造;它们没有名字。 + + + + + $(P +Class types can be defined as $(C synchronized) as well. + + + $(P +类也可以定义为 $(C synchronized)。 + + + + + $(P +Class variable and class object are separate concepts. + + + $(P +类变量和类对象是独立的概念。 + + + + + $(P +Classes are defined by the $(C class) keyword instead of the $(C struct) keyword: +) + + + $(P +类由 $(C class) 关键字定义而不是 $(C struct) 关键字: +) + + + + + $(P +Concurrency is similar to but different from the topic of the previous chapter, parallelism. + + + $(P +并发(concurrency)与上一章的并行(parallelism)很相似,但还是存在较多差异。 + + + + + $(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. + + + $(P +如果只是一个二元运算的话,我们没必要使用会影响效率的 $(C synchronized) 块。 + + + + + $(P +Conversely, the owner of a worker thread is obtained by the $(C ownerTid()) function. + + + $(P +相应地,使用 $(C ownerTid()) 可以获得工作线程的所有者的 ID。 + + + + + $(P +Copying affects only the variables, not the object. + + + $(P +复制只影响变量,而不是对象。 + + + + + $(P +D supports both models of concurrency: message passing and data sharing. + + + $(P +D 语言支持两种并发模型:消息传递和数据共享。 + + + + + $(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 +During the execution of $(C bar()), the call stack would consist of three frames storing the local states of those currently active function calls: +) + + + $(P +在 $(C bar()) 的执行期间,调用栈里面有 3 帧,分别存储的是那些当前正激活函数调用的本地状态: +) + + + + + $(P +Encapsulation is achieved by $(I protection attributes), which we will see in $(LINK2 /ders/d.en/encapsulation.html, a later chapter). + + + $(P +封装是通过 $(I 保护属性) 来实现的,关于这一点在 $(LINK2 /ders/d.cn/encapsulation.html, 后面章节) 会看到。 + + + + + $(P +Every thread has a private mailbox that holds the messages that are sent to that thread. + + + $(P +每一个线程都有一个用来保存消息的邮箱。 + + + + + $(P +Evidently, the assignment to $(C .length) operates like a function. + + + $(P +就像你看到的那样,对 $(C .length) 的赋值就像是调用了一个函数。 + + + + + $(P +Fibers are useful for similar cases where using a call stack is much easier than maintaining state explicitly. + + + $(P +对于使用调用栈比显示维护状态更容易实现的情形,也同样可以使用纤程。 + + + + + $(P +Fibers enable multiple call stacks per thread. + + + $(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, $(C FibonacciSeries) that we have defined earlier was keeping two member variables to calculate the $(I next next) number in the series: +) + + + $(P +例如,我们之前定义的 $(C FibonacciSeries)会一直记住两个变量,以便计算序列里的 $(I 下一个 next) 成员: +) + + + + + $(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. + + + $(P +例如,虽然两个函数有相同的函数签名,但两个通过 $(C task()) 函数模版创建的 $(C Task) 示例类型是不同的。 + + + + + $(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, 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()): +) + + + $(P +例如,当下面程序的主线程开始执行 $(C bar()) 函数时, 会有 3 层激活的函数调用,即 $(C main()) 调用了 $(C foo()),而 $(C foo()) 又调用了 $(C bar()): +) + + + + + $(P +For example, the following $(C receive()) call specifies two message handlers that handle messages of types $(C int) and $(C string), respectively: +) + + + $(P +例如,下面这个 $(C receive()) 使用了两个委托分别用来处理类型为 $(C int) 和 $(C string) 的消息: +) + + + + + $(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, 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. + + + $(P +例如,下面是 $(C insert()) 和 $(C print()) 的递归实现,它们并未定义任何变量,并且与树里包含的元素数量无关。 + + + + + $(P +For example, the sum of the squares of the elements of an array can be calculated as in the following program: +) + + + $(P +例如,下面这个计算数组元素的平方和的程序: +) + + + + + $(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: +) + + + $(P +例如,当 $(C left.print()) 的某个递归调用在输出左子树的所有元素之后返回时,当前 $(C print()) 调用的本地状态已表明需要输出一个空格字符: +) + + + + + $(P +For example, when the following numbers are entered, +) + + + $(P +例如,当输入了下面的数字, +) + + + + + $(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 +为了能够给多个代码块加上同一个锁,必须在 $(C synchronized) 后加一个圆括号,并在其中指定锁对象: +) + + + + + $(P +For plain arrays, index values are not stored at all. + + + $(P +对于简单数组,索引值根本就没有存储。 + + + + + $(P +For reasons like safety and performance, some languages have been designed to be compiled. + + + $(P +出于对安全及效率等原因的考量,某些语言被设计为可编译的。 + + + + + $(P +For that assignment to actually change the area of the rectangle, the two members of the struct must be modified accordingly. + + + $(P +改变矩形面积时也要将长和宽修改为对应的值。 + + + + + $(P +For that syntax to work, a constructor must be defined explicitly by the programmer. + + + $(P +那样的语法要通过编译,就需要程序员显式的定义构造函数。 + + + + + $(P +Further, $(C printAsXML()) and the attributes that it considers can be used with other types as well: +) + + + $(P +此外,$(C printAsXML()) 和它涉及到的属性还可以与其他类型一起使用: +) + + + + + $(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: +) + + + $(P +就像我们在 $(LINK2 /ders/d.cn/function_overloading.html, 函数重载) 一章中提到过的,这两个函数可以有相同的名字。 +) + + + + + $(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 +Here is the complete program: +) + + + $(P +下面是完整的程序: +) + + + + + $(P +How can you solve this problem? + + + $(P +怎么解决这个问题? + + + + + $(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. + + + $(P +我们曾在 $(LINK2 /ders/d.cn/encapsulation.html, 封装和访问控制) 一章中告诉过你:限制外部代码对成员变量的访问是非常重要的。 + + + + + $(P +However, programming languages adhere to much more strict and formal rules than any spoken language. + + + $(P +然而,编程语言依然比口语更严格、正规。 + + + + + $(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: +) + + + $(P +但特性提供了成员变量所不能提供的功能:对 $(C .length) 赋值将会改变分片的真实长度。向数组中添加新元素时就包含这一过程: +) + + + + + $(P +However, there are important differences between classes and structs. + + + $(P +然而,类与结构之间有重要的区别。 + + + + + $(P +However, there are important differences in the way $(C taskPool.reduce()) works. + + + $(P +不过,与之前的函数相比,$(C taskPool.reduce()) 的行为有一个非常重要的不同点。 + + + + + $(P +I will start using that form after introducing the $(C string) type below. + + + $(P +在介绍了下面的 $(C string) 类型之后,我们将开始使用这种格式。 + + + + + $(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 std.algorithm.map) 是一个即时取值的算法,那表示操作开始和操作结束的信息应该在程序最开始的时候一下全部显示出来。 + + + + + $(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 the receiver does not have a message handler that matches the type of the priority message, then a $(C PriorityMessageException) is thrown: +) + + + $(P +如果消息接收者没有能与优先级消息匹配的处理函数,它会抛出一个 $(C PriorityMessageException) 异常: +) + + + + + $(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 $(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". + + + $(P +In $(LINK2 /ders/d.cn/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". + + + + + $(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: +) + + + $(P +对于循环,范围中的元素通常是一个一个按顺序被处理的,即上一个循环的操作结束后才会进行下一次循环: +) + + + + + $(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 +在多线程里,因为每个线程都只负责自己的任务,所以每个线程都会有自己的调用栈,用于维护其执行状态。 + + + + + $(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 fact, $(C parallel()) constructs a new $(C Task) object for every worker thread and starts that task automatically. + + + $(P +实际上,$(C parallel()) 为每个工作线程构建并自动启动了一个 $(C Task)。 + + + + + $(P +In general, classes are very similar to structs. + + + $(P +一般情况下,类与结构非常相似。 + + + + + $(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. + + + $(P +在大多数情况下,$(C yieldForce()) 是最适合用来等待任务完成的函数;$(C yieldForce()) 会将调用它的线程挂起,一直到任务完成为止。 + + + + + $(P +In most cases, the features of the $(C core.atomic) module can be several times faster than using $(C synchronized) blocks. + + + $(P +在大多数情况下 $(C core.atomic) 模块中的功能要比 $(C synchronized) 块执行速度快。 + + + + + $(P +In order to avoid making a potentially fully-lazy input range a fully-eager range, it iterates the elements in $(I waves). + + + $(P +为了防止将输入的可能为延迟取值的范围转换为即时取值的范围,它会以$(I 轮动)的方式迭代这些元素。 + + + + + $(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 summary, the owner is identified by $(C ownerTid) and the worker is identified by the return value of $(C spawn()). + + + $(P +总之,调用 $(C ownerTid) 获取其所有者 ID,通过 $(C spawn()) 的返回值获取工作线程 ID。 + + + + + $(P +In the call above, $(C asyncBuf()) makes two elements ready in its buffer. + + + $(P +通过上面 $(C asyncBuf()) 的调用,缓冲区中已经准备好了两个元素。 + + + + + $(P +In the case of $(C taskPool.reduce()), the initial values of the results must be specified as a tuple: +) + + + $(P +如果向 $(C taskPool.reduce()) 传入多个函数,那为其指定的初始值也需要改为元组类型: +) + + + + + $(P +In the code above, $(C variable2) is being initialized by $(C variable1). + + + $(P +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。 + + + + + $(P +In the code above, we expect that the value of $(C i) is incremented before the value of $(C j) is incremented. + + + $(P +对于上面的代码我们认为 $(C i) 将在 $(C j) 之前自增 1。 + + + + + $(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: +) + + + $(P +对于之前我们写过的程序,我们都假定程序中的表达式都是按照确定的顺序执行的,或者说至少通常情况下它们是顺序执行的。 +) + + + + + $(P +In the simple programs that we have used above, it was easy to pass the thread ids of owners and workers. + + + $(P +之前我们看到的程序都很简单,所以在线程间传递线程 ID 还是比较方便的。 + + + + + $(P +It is equally wrong to allocate new memory $(I indirectly) from the garbage collector in a destructor. + + + $(P +在析构函数里 $(I 间接地) 从垃圾回收器里分配新的内存,这种做法同样是错的。 + + + + + $(P +It is not possible to add elements to fixed-length arrays: +) + + + $(P +不可能给定长数组添加元素: +) + + + + + $(P +It is possible but highly unlikely that the result may even end up being "2 and 1" as well. + + + $(P +当然最终结果也有可能是“2 and 1”,只不过这种情况的可能性比较小而已。 + + + + + $(P +It is possible to define variables with the $(C auto) keyword, which we will see in a later chapter: +) + + + $(P +也可以使用 $(C auto) 关键字定义变量,这个我们将在后面的一章中看到: +) + + + + + $(P +It is possible to send more than one value as a part of the same message. + + + $(P +也可以在消息中一次发送多个值,这些值都会成为同一消息的一部分。 + + + + + $(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. + + + $(P +只有在确定在每一次迭代中对元素的操作相互独立后,程序员才能调用 $(C parallel()) 来实现并行。 + + + + + $(P +It may not be easy to chose between regular member functions and properties. + + + $(P +并没有一种简单的方法能帮你快速在常规成员函数和特性间做出选择。 + + + + + $(P +It may not make sense to wait for messages beyond a certain time. + + + $(P +可能经过一段时间后就不再需要继续等待消息了。 + + + + + $(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 +Lambda functions and objects of types that define the $(C opCall()) member function can also be passed to $(C receive()) as message handlers. + + + $(P +lambda 函数和定义了 $(C opCall()) 成员函数的对象都可以传递给 $(C receive()) 作为消息处理器。 + + + + + $(P +Leaving members like $(C width) and $(C height) open to $(C public) access is acceptable only for very simple types. + + + $(P +像 $(C width) 和 $(C height) 简单类型的成员变量,$(C public) 访问权限也是可以接受的。 + + + + + $(P +Let's assume that a third property of this type becomes a requirement, which should provide the area of the rectangle: +) + + + $(P +现在假设我们需要为这个类型添加一个新特性用于表示这个矩形的面积: +) + + + + + $(P +Let's compare these three algorithms using the $(C Student) example. + + + $(P +下面用 $(C Student) 作为示例,对这三个算法做个比较。 + + + + + $(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 consider the following code that we saw 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 consider the following function that calculates the sum of the elements of a slice. + + + $(P +一起来看看下面这个函数,它会计算一个分片里所有元素的总和。 + + + + + $(P +Let's consider the state where $(C i) is 1 and $(C j) is 2. + + + $(P +先来看下 $(C i) 为 1 $(C j) 为 2 的情况。 + + + + + $(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: +) + + + $(P +现在我们重温一下使用五个值的例子,用数组重写一下它: +) + + + + + $(P +Let's see an example of this in the following program that has two separate code blocks that access the same shared variable. + + + $(P +下面的例子包含两个独立的访问共享变量的代码块。 + + + + + $(P +Let's use what we have seen so far in a simulation program. + + + $(P +现在我们在一个模拟程序里实践一下刚了解到的内容。 + + + + + $(P +Like every variable in D, the elements of arrays are automatically initialized. + + + $(P +像在 D 语言中的每个变量,数组的元素自动初始化。 + + + + + $(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. + + + $(P +与大部分容器一样,大家也会想要这个树提供范围接口,以便其中的元素可以与已有的范围算法一起使用。 + + + + + $(P +Like the other parallel algorithms, $(C taskPool.reduce()) executes the functions in parallel by using elements in different tasks. + + + $(P +$(C taskPool.reduce()) 在不同的任务中并行执行函数,这与其它的并行算法相同。 + + + + + $(P +Looking merely at that usage, one might think that $(C .length) has been implemented as a member variable: +) + + + $(P +如果只看它的语法,你可能会认为 $(C .length) 是一个成员变量: +) + + + + + $(P +Messages can be sent with higher priority than regular messages by $(C prioritySend()). + + + $(P +可以使用 $(C prioritySend()) 发送高标准优先级的消息。 + + + + + $(P +Messages of type $(C int) would match $(C intHandler()) and messages of type $(C string) would match $(C stringHandler()). + + + $(P +$(C int) 消息匹配 $(C intHandler()),而 $(C string) 消息匹配 $(C stringHandler())。 + + + + + $(P +Most CPU architectures use machine code specific to that particular architecture. + + + $(P +大多数 CPU 都使用特定架构的机器码。 + + + + + $(P +Multiple attributes can be specified separately or as a parenthesized list of attributes. + + + $(P +多个属性可以分别指定,也可以采用括号列表形式。 + + + + + $(P +Mutable variables that need to be shared must be defined with the $(C shared) keyword: +) + + + $(P +需要共享的可变变量必须使用关键字 $(C shared) 来定义: +) + + + + + $(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. + + + $(P +正常情况下程序将会在某个操作系统指派的用于运行程序的处理器内核上运行。 + + + + + $(P +Note how the members are made $(C private) so that they can only be accessed by corresponding property functions. + + + $(P +现在成员变量被封装为 $(C private),所以只有与之相关联的特性函数可以访问它。 + + + + + $(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. + + + $(P +请注意:$(C i) 的生命期即为 $(C main()) 函数作用域,所以为了防止出现错误应确保 $(C main()) 函数在工作线程终止后才返回。 + + + + + $(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 +On the other hand, it may not be necessary to wait for preceding elements to be processed before starting to produce the successive elements. + + + $(P +但我们并不需要严格的按照顺序等待上一个元素被处理完后才开始计算下一个元素的值。 + + + + + $(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: +) + + + $(P +另一方面,工作线程需要 $(C int) 类型的消息并对其进行计算,之后将计算得到的 $(C double) 类型的结果返回给其所有者: +) + + + + + $(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 of the reasons why data sharing is not safe is $(I race conditions). + + + $(P +$(I 竞态条件)是数据共享不够安全的原因之一。 + + + + + $(P +One thing that the worker can do is to catch the exception explicitly and to send it as a special error message. + + + $(P +工作线程能做的就是手动捕获异常并将其作为特殊的错误信息发送给所有者。 + + + + + $(P +One way of achieving that requirement is to define a third member: +) + + + $(P +一种方法是在矩形结构体中定义第三个成员变量: +) + + + + + $(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 +Operations that are executed in parallel with other operations of a program are called $(I tasks). + + + $(P +程序中以并行的方式执行的操作叫做$(I 任务(task))。 + + + + + $(P +Other than the fact that $(C opAssign) cannot be overloaded for classes, operator overloading is the same as structs. + + + $(P +虽然 $(C opAssign) 不能被类重载,但与结构一样,可以实现运算符重载。 + + + + + $(P +Parallel $(C amap()) works the same as parallel $(C map()) with two differences: +) + + + $(P +并行 $(C amap()) 与并行 $(C map()) 作用基本相似,但有以下两点不同: +) + + + + + $(P +Parallel $(C reduce()) is faster in many other calculations like the calculation of the math constant $(I pi) (π) by quadrature. + + + $(P +不过对于许多算法来说并行 $(C reduce()) 要比其串行版本快,例如计算 $(I pi) (π)的算法。 + + + + + $(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 +Please see the online documentation of Phobos for the other member functions of $(C Task). + + + $(P +要获得更多有关成 $(C Task) 员函数的信息请参见 Phobos 在线文档。 + + + + + $(P +Programming languages are designed as efficient ways of programming a CPU, capable of representing higher-level concepts. + + + $(P +编程语言的设计目标是用来对 CPU 高效编程,且有能力描述高级概念。 + + + + + $(P +Properties allow using member functions like member variables. + + + $(P +特性能让我们使用像访问成员变量一样的语法调用成员函数。 + + + + + $(P +Recursion is the situation where a function calls itself either directly or indirectly. + + + $(P +递归指的是这样一种情况:函数直接或间接地调用自己 + + + + + $(P +Same as structs, the members are accessed by the $(I dot) operator: +) + + + $(P +与结构一样,可以使用 $(I 点) 运算符来访问成员: +) + + + + + $(P +Similar to the $(C length) property of slices, the properties of user-defined types can be used in assignment operations as well: +) + + + $(P +与分片的 $(C length) 特性一样,用户定义的特性也可以用于赋值: +) + + + + + $(P +Similarly to $(C parallel()), $(C asyncBuf()) iterates $(C InputRange) ranges in parallel. + + + $(P +$(C asyncBuf()) 与 $(C parallel()) 相似,都是用来并行迭代 $(C InputRange) 的。 + + + + + $(P +Similarly, $(C char[]) cannot be used where a $(C string) is needed. + + + $(P +同样的,$(C char[]) 不能被用到需要 $(C string) 的地方。 + + + + + $(P +Similarly, $(C shared static ~this()) is for final operations that must be executed only once per program. + + + $(P +同样地,$(C shared static ~this()) 适用于结束操作——每个程序只会执行一次来释放资源。 + + + + + $(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 +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 each thread gets its own copy of data, $(C spawn()) does not allow passing references to thread-local variables. + + + $(P +由于每个线程都有一份单独的数据拷贝,$(C spawn()) 不允许以引用的形式传递线程内的值。 + + + + + $(P +Since it is eager, all of the results are ready by the time $(C amap()) returns: +) + + + $(P +因为它是即时计算,所以在 $(C amap()) 返回时所有元素都已被运算完成: +) + + + + + $(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 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. + + + $(P +如果处理每个学生需要花费 1 秒,那按照顺序迭代学生总共需要花费 4 秒。 + + + + + $(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. + + + $(P +线程数相同且加减次数相同,可能有人就会认为 $(C number) 的最终结果是 0。 + + + + + $(P +Since they are actually arrays, all of the array operations can be applied to strings as well. + + + $(P +由于它们实际上是数组,所有数组的操作也都能应用到字符串上。 + + + + + $(P +Some languages like Perl, Python and Ruby have been designed to be very flexible and dynamic, making code analysis harder. + + + $(P +一些语言被设计得非常灵活与动态,诸如 Perl、Python 和 Ruby,这使得代码分析困难重重。 + + + + + $(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 definition of $(C Rectangle) still presents $(C width) and $(C height) as if they are member variable: +) + + + $(P +$(C Rectangle) 的定义依旧存在 $(C width) 和 $(C height),它们也依旧表现的像是成员变量。 +) + + + + + $(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 information can be stored in the following $(C Job) struct: +) + + + $(P +这些信息可以储存在下面这个 $(C Job) 结构中: +) + + + + + $(P +That property function enables the struct to be used as if it has a third member variable: +) + + + $(P +特性函数让我们能像使用成员变量一样使用它: +) + + + + + $(P +That solution may be slow especially for large arrays. + + + $(P +对于大型数组这恐怕会非常慢。 + + + + + $(P +The $(C OwnerTerminated) and $(C LinkTerminated) exceptions can be received as messages as well. + + + $(P +$(C OwnerTerminated) 和 $(C LinkTerminated) 都可以作为消息在线程间传递。 + + + + + $(P +The $(C TaskPool) constructor takes the number of threads to use during the parallel operations that are later started through it. + + + $(P +$(C TaskPool) 构造函数有一个参数。通过指定这个参数就可以控制之后并行执行的线程个数。 + + + + + $(P +The $(C anOperation()) function is started twice in the following program. + + + $(P +在下方的程序中 $(C anOperation()) 函数被调用两次。 + + + + + $(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: +) + + + $(P +下面这个 $(C calculate()) 接收一个 $(C string) 消息,将其转换为 $(C double) 并加 0.5,之后将运算的结果作为消息传递回去: +) + + + + + $(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. + + + $(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 setMaxMailboxSize()) call above sets the main thread's mailbox size to 1000. + + + $(P +$(C setMaxMailboxSize()) 将邮箱大小限制为 1000。 + + + + + $(P +The $(C static this()) block above would be executed once for the main thread and once for the worker thread: +) + + + $(P +上面这个 $(C static this()) 会为主线程执行一次,为工作线程执行一次: +) + + + + + $(P +The $(C std.concurrency.Generator) class addresses all of these issues. + + + $(P +$(C std.concurrency.Generator) 类考虑到了上面的所有问。 + + + + + $(P +The $(C strip()) expression above returns a new string that does not contain the trailing control characters. + + + $(P +上面的 $(C strip()) 表达式返回一个不包含尾随控制符的新字符串。 + + + + + $(P +The $(C to!double()) call above would throw an exception if the string cannot be converted to a $(C double) value. + + + $(P +如果该字符串不能转换为一个 $(C double) 值,则调用 $(C to!double()) 会抛出异常。 + + + + + $(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 address of the mutable variable $(C isDone) cannot be passed between threads. + + + $(P +可变变量 $(C isDone) 的地址无法在线程间传递: + + + + + $(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 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 +对 $(C .length) 赋值并不是简单的改变某个变量,实际上语言在后台做了很多复杂的工作:判断数组是否有足够的空间容纳新的元素,如果没有则在内存中分配更大的空间并将已存在的元素移动到新位置;最后再将添加的新元素以 $(C .init) 的方式初始化。 + + + + + $(P +The atomic operations of D are in the $(C core.atomic) module. + + + $(P +D 语言中的原子操作都在 $(C core.atomic) 模块里。 + + + + + $(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. + + + $(P +赋值操作不能改变类。 + + + + + $(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 biggest difference from structs is that structs are $(I value types) and classes are $(I reference types). + + + $(P +与结构的最大区别在于结构是 $(I 值类型) 而类是 $(I 引用类型)。 + + + + + $(P +The caller's execution resumes when the fiber yields. + + + $(P +当纤程跳转时,调用函数会继续执行。 + + + + + $(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. + + + $(P +下面这个表格展示了 $(C for) 循环中的 3 行语句是如何执行的。 + + + + + $(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. + + + $(P +当编译器看到某段可疑但又不是错误的代码时,会发出 $(I 编译警告)。 + + + + + $(P +The complete program has more explanations: +) + + + $(P +列出整个程序代码更能说明问题: +) + + + + + $(P +The concept of compilation and the function of the compiler must also be understood when using $(I compiled) languages like D. +) + + + $(P +当使用类似 D 语言的 $(I 编译型) 语言时,需要理解编译的概念和编译器的功能。 +) + + + + + $(P +The correctness of the program requires extra attention when mutable data is shared between threads. + + + $(P +为了保证程序的正确性,我们需要为那些在线程间共享的可变数据付出额外的精力。 + + + + + $(P +The default value of buffer size is 100. + + + $(P +缓冲区的默认大小为 100。 + + + + + $(P +The default value of work unit size is 100 and is suitable for most cases. + + + $(P +工作单元大小的默认值是 100。在大多数情况下这个大小是合适的。 + + + + + $(P +The definition of array variables is very similar to the definition of normal variables. + + + $(P +数组变量的定义与正常变量的定义非常相似。 + + + + + $(P +The details of $(C Variant) are outside of the scope of this chapter. + + + $(P +有关 $(C Variant) 的详细内容已超出本章范围。 + + + + + $(P +The distribution of threads to cores has some minimal cost. + + + $(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 elements are produced and used lazily. + + + $(P +这些元素都是采用惰性方式生成和使用的。 + + + + + $(P +The examples in this chapter call $(C Thread.sleep) to slow down threads to demonstrate that they run at the same time. + + + $(P +本章中的例子调用 $(C Thread.sleep) 减慢线程执行的速度来更方便的展示线程运行的情况。 + + + + + $(P +The examples in this chapter may look simplistic. + + + $(P +本章中的例子看起来都很简单。 + + + + + $(P +The exceptions that the worker may throw cannot be caught by the owner. + + + $(P +所有者无法捕获由工作线程抛出的异常。 + + + + + $(P +The exclamation mark appears after those characters instead of being printed right after the name. + + + $(P +感叹号出现在了那些字符之后,而不是在名字之后立即输出。 + + + + + $(P +The execution time of the program can be measured in a terminal by $(C time): +) + + + $(P +可以使用终端中的 $(C time) 来测量程序的执行时间。 +) + + + + + $(P +The fact that $(C std.algorithm.map) operates lazily is very powerful in many programs. + + + $(P +在很多程序里,$(C std.algorithm.map) 的懒式操作都会非常有用。 + + + + + $(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 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. + + + $(P +又由于所有任务都是用相同的初始值,这些初始值会被多次使用,因此 $(C taskPool.reduce()) 的运算结果可能和 $(C std.algorithm.reduce()) 不同。 + + + + + $(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. + + + $(P +在 $(C cas()) 开始执行时,如果变量的值还是等于 $(C currentValue),则说明自当前线程读入后这个变量没有被其他线程修改。 + + + + + $(P +The final values of the results that each task produces are themselves used in the same $(C result) calculation one last time. + + + $(P +每个任务都会得到一个结果,这些最终结果将会按照与计算元素相同的方法计算,得到最终的 $(C result)。 + + + + + $(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 first parameter of $(C receiveTimeout()) determines how long the message should be waited for. + + + $(P +$(C receiveTimeout()) 的第一个参数决定等待消息时要等待多长时间。 + + + + + $(P +The following additional definition of $(C area()) enables using that property in assignment operations and effectively modifying the area of $(C Rectangle) objects: +) + + + $(P +在下面示例里,新增 $(C area()) 定义之后,我们可以在赋值中使用该特性,并高效地更改 $(C Rectangle) 对象的面积: +) + + + + + $(P +The following are common operations of fibers. + + + $(P +下面是纤程的几个常用操作。 + + + + + $(P +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))的属性: +) + + + + + $(P +The following example calls $(C parallel()) on a local $(C TaskPool) object: +) + + + $(P +下面这个例子使用了一个局部 $(C TaskPool) 对象来调用 $(C parallel())。 +) + + + + + $(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: +) + + + $(P +下面这个例子使用了 $(C OnCrowding.throwException)。它将在邮箱满时抛出 $(C MailboxFull) 异常: +) + + + + + $(P +The following functions re-read the current value and call $(C cas()) until the operation succeeds. + + + $(P +下面这个函数使用将重新读取当前值并调用 $(C cas()) 直到操作成功。 + + + + + $(P +The following is the equivalent of the class definition above: +) + + + $(P +下面的类和上面的是等价的: +) + + + + + $(P +The following program demonstrates multiple functions with $(C std.algorithm.map). + + + $(P +下面这个例子演示如何向 $(C std.algorithm.map) 传入多个函数并使用其返回的结果。 + + + + + $(P +The following program simulates independent robots moving around randomly in a two dimensional space. + + + $(P +下面这个程序模拟的是两个互不相关的机器人在二维空间中随机移动。 + + + + + $(P +The following program starts two threads that find each other by their names. + + + $(P +下面这个程序启动了两个线程。它们会通过线程名找到对方。 + + + + + $(P +The following three functions define an interface to an associative array that every thread has access to: +) + + + $(P +下面这三个函数定义了一个接口。通过这个接口每个线程都可以访问一个关联线程与线程名的数组: +) + + + + + $(P +The function may be specified by the $(C =>) syntax as a $(I lambda expression) as we have seen in earlier chapters. + + + $(P +这个函数也可以是由 $(C =>) 语法声明的 $(I lambda 表达式)。 + + + + + $(P +The function that enables such an assignment syntax is also named as $(C area) and is also marked by $(C @property). + + + $(P +只需你将函数命名为 $(C area) 并将其标记为 $(C @property),你就能够使用赋值语法调用它。 + + + + + $(P +The functions above work correctly without the need for $(C synchronized) blocks. + + + $(P +上面这个程序在不使用 $(C synchronized) 块的情况下也能正确工作。 + + + + + $(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 highlighted parts of the code are explained below: +) + + + $(P +下面来解释一下这段代码的高亮部分: +) + + + + + $(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). + + + $(P +程序出错的原因是多个线程访问同一块可变数据(并且其中至少有一条线程修改了数据)。 + + + + + $(P +The interpreter must read and understand the instructions every time the program is executed. + + + $(P +每一次执行程序之前,解释器都必须重新读入源码并了解它们的含义。 + + + + + $(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 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)): +) + + + $(P +上面这一行和下面这一行功能相同,但原子操作会使 $(C +=) 不被其他线程打断(或者说它们$(I 像原子一样)不可拆分): +) + + + + + $(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 +程序的输出顺序可能会和上面有所不同,具体情况取决于操作系统对线程的调度。 + + + + + $(P +The main thread reports the messages that it sends and the messages that it receives: +) + + + $(P +主线程会将它发送的和接收的消息一起输出: +) + + + + + $(P +The name of a specific day can be accessed by its index in that array: +) + + + $(P +在数组里通过索引访问特定的英文日期名: +) + + + + + $(P +The name of this function is the abbreviation of "compare and swap". + + + $(P +这个函数的名字是“比较并交换(compare and swap)”的缩写。 + + + + + $(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. + + + $(P +新增的特性函数利用 $(C std.math) 模块中的 $(C sqrt) 函数来开平方根。 + + + + + $(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 operating system can pause threads at unpredictable times for unpredictable durations. + + + $(P +操作系统会不定期的将线程暂停一段时间。 + + + + + $(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. + + + $(P +上面这个看似简单的操作实际上包含三个步骤:读取变量的值、将其加一、将结果写回变量所在的内存。 + + + + + $(P +The optional character that is specified after string literals determines the type of the elements of the string: +) + + + $(P +在字符串字面量之后指定的可选字符决定了字符串的元素类型: +) + + + + + $(P +The optional second parameter of $(C amap()) is the work unit size as well: +) + + + $(P +$(C amap()) 的第二个参数也是工作单元大小,也同样是可选的: +) + + + + + $(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 of one of the threads is highlighted: +) + + + $(P +其中一个线程的输出被高亮了: +) + + + + + $(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: +) + + + $(P +从程序的输出可以看出 $(C map()) 是延迟取值的;它调用 $(C averageGrade()) 的行为与 $(C foreach) 循环相似: +) + + + + + $(P +The output of the program in the environment that this chapter has been written is the following: +) + + + $(P +在本章编写时使用的环境上运行上面的代码将会输出这样的结果: +) + + + + + $(P +The output of the program indicates that first the calculations are performed in parallel, and then their results are calculated sequentially. + + + $(P +程序的输出信息表明起初对元素的计算是并行的,而之后对结果的计算是串行的。 + + + + + $(P +The output of the program indicates that the messages are handled by matching functions on the receiver's side: +) + + + $(P +程序的输出说明了接收端的函数是如何匹配和处理消息的: +) + + + + + $(P +The output of the program lists all attributes of all members of $(C Person): +) + + + $(P +此程序的输出会列出 $(C Person) 的所有成员的所有属性: +) + + + + + $(P +The output of the program lists the attributes of $(C Person.name): +) + + + $(P +这个程序的输出内容是 $(C Person.name) 的各个属性: +) + + + + + $(P +The output of the program should be similar to the following. + + + $(P +程序的输出类似下面这个样子: + + + + + $(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) 成员: +) + + + + + $(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): +) + + + $(P +这个程序的输出表明由任务抛出的异常并不会立刻导致整个程序终止运行(它只终止了那个任务): +) + + + + + $(P +The output: +) + + + $(P +输出: +) + + + + + $(P +The owner above waits for a message for up to 600 milliseconds. + + + $(P +上面的线程所有者将等待消息 600 毫秒。 + + + + + $(P +The owner can catch the exception to do something special like terminating gracefully: +) + + + $(P +所有者线程可以捕获这个异常并执行某些操作,比如优雅地退出: +) + + + + + $(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). + + + $(P +在下面程序里,所有者线程会向工作线程发送 $(C int) 类型的消息,并等待工作线程返回 $(C double) 类型的消息。 + + + + + $(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: +) + + + $(P +由于工作线程已被终止,所有者只会收到将“1.2”变为 1.7 的消息的反馈。而它并不知道工作线程已经终止,所有者线程会被阻塞来等待永远不会到达的消息: +) + + + + + $(P +The owner simply waits for these messages in an infinite loop. + + + $(P +线程所有者仅仅通过一个死循环等待消息。 + + + + + $(P +The owner simply waits for these messages in an unconditional loop. + + + $(P +所有者线程只是在一个无条件的循环里等待这些消息。 + + + + + $(P +The parameters that the thread function takes are passed to $(C spawn()) as its second and later arguments. + + + $(P +线程函数所需的参数可以通过 $(C spawn()) 的第二个及以后面的参数传入。 + + + + + $(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. + + + $(P +纤程的厉害之处在于它虽然不是线程,但是它有其自己的调用栈,从而可以高效地让每个线程拥有多个调用栈。 + + + + + $(P +The previous chapter was about threads sharing information through message passing. + + + $(P +在上一章中,我们使用消息传递实现线程间的信息共享。 + + + + + $(P +The program automatically waits for all of the threads to finish executing. + + + $(P +程序在所有线程执行完毕后才会退出。 + + + + + $(P +The program can now be compiled and will print the modified string: +) + + + $(P +现在程序能通过编译并且打印修改后的字符串: +) + + + + + $(P +The program is terminated with an exception: +) + + + $(P +这个程序会抛一个异常,并中断: +) + + + + + $(P +The program outputs the sum of 25 and 100, the squares of 5 and 10: +) + + + $(P +这个程序将输出 5 和 10 的平方的和: +) + + + + + $(P +The program prints every movement until terminated: +) + + + $(P +程序会不停地显示所有机器人的运动信息,除非手动终止: +) + + + + + $(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. + + + $(P +原因是,运算符 $(C ==) 会查询对象成员的值,并尝试通过一个潜在的 $(C null) 变量访问成员,这将引发一个内存访问错误。 + + + + + $(P +The reason why recursive tree algorithms like $(C print()) are trivial is due to the automatic management of the call stack. + + + $(P +为何像 $(C print()) 那样的树递归算法无关紧要,主要归因于调用栈的自动管理。 + + + + + $(P +The reason why the $(C shared) specifiers are necessary is explained in the next chapter. + + + $(P +下一章会解释为什么此处必须使用 $(C shared) 说明符。 + + + + + $(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. + + + $(P +下面这两个处在竞态条件的线程可以用来解释为什么之前的程序不能得到正确的结果。 + + + + + $(P +The results can also be stored in a $(C RandomAccessRange) that is passed to $(C amap()) as its third parameter: +) + + + $(P +还可以通过 $(C amap()) 的第三个参数传递一个 $(C RandomAccessRange) 进去来储存运算结果: +) + + + + + $(P +The return type of $(C thisTid()) is $(C Tid), which has no significance for the program. + + + $(P +$(C thisTid()) 返回的类型为 $(C Tid),它对程序没有意义。 + + + + + $(P +The return value of $(C spawn()), which I have been ignoring until this point, is the id of the worker thread: +) + + + $(P +我们之前一直没有用到的 $(C spawn()) 的返回值即为工作线程的 ID: +) + + + + + $(P +The scenario above is just one example that is sufficient to explain the incorrect results of the program. + + + $(P +上面那种情况只是为了解释程序得出错误结果的原因而创建的例子。 + + + + + $(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. + + + $(P +$(C map()) 的第二个参数与 $(C asyncBuf()) 的第二个参数含义相同:它决定了 $(C map()) 储存结果的缓冲区的大小。 + + + + + $(P +The second parameter of $(C parallel()) has an overloaded meaning and is ignored in some cases: +) + + + $(P +$(C parallel()) 的重载为其第二个参数赋予了多个含义,或使其在某些情况下被忽略: +) + + + + + $(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 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. + + + $(P +解决方法是为对象定义一个顺序并按照这个顺序锁住对象。如果使用 $(C synchronized) 语句这个过程将被自动实现。 + + + + + $(P +The syntax of associative arrays is similar to the array syntax. + + + $(P +关联数组的语法与数组相似。 + + + + + $(P +The table grows automatically with each association. + + + $(P +随着每次的关联,该表会自动增长。 + + + + + $(P +The task function above has been specified as a template parameter to $(C task()) as $(C task!anOperation). + + + $(P +上面的任务函数是 $(C task()) 的一个参数模版 $(C task!anOperation) 的实参。 + + + + + $(P +The thread function that moves each robot sends the id of the robot and its movement to the owner thread continuously: +) + + + $(P +移动机器人的线程会不断地将对应机器人的 ID 和它的移动情况发送给所有者线程: +) + + + + + $(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 +The worker above terminates after sending two messages. + + + $(P +发送两条消息后工作线程立刻终止。 + + + + + $(P +The worker can catch that exception to exit gracefully: +) + + + $(P +工作线程也可以通过捕获这个异常来优雅地退出: +) + + + + + $(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 anonymous functions below that are passed to $(C receive()) as message handlers. + + + $(P +有 3 个匿名函数被传递给了 $(C receive()) 来作为消息处理器。 + + + + + $(P +There are three functions to wait for the completion of a task: +) + + + $(P +有三个用来等待任务完成的函数: +) + + + + + $(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 different ways of using $(C remove()): +) + + + $(P +$(C remove()) 的使用方式有以下两种: +) + + + + + $(P +There are two important points worth stressing here: +) + + + $(P +还有两点值得强调: +) + + + + + $(P +There can be only one value per key. + + + $(P +每个键有且仅有一个对应值。 + + + + + $(P +There is no need for a special lock type in D because any class object can be used as a $(C synchronized) lock. + + + $(P +D 语言中没有专门的锁类型,任何类型都可以作为 $(C synchronized) 锁。 + + + + + $(P +There is only one copy of such a variable in the entire program and all threads can share that variable. + + + $(P +这样的话,程序中只会有一 $(C globallyShared),它会在所有线程间共享。 + + + + + $(P +These machine code instructions are special numbers, which represent various operations supported by the CPU. + + + $(P +机器码指令都是特定的数字,它们表示 CPU 支持的各种操作。 + + + + + $(P +These operators operate on class variables. + + + $(P +这些运算符应用在类变量上。 + + + + + $(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. + + + $(P +本章仅涉及数组的部分功能。 + + + + + $(P +This chapter covers the following range algorithms. + + + $(P +本章涉及以下范围(range)算法。 + + + + + $(P +This chapter will introduce classes at a high level, underlining the fact that they are reference types. + + + $(P +本章将深入介绍类,特别强调一点,类是引用类型。 + + + + + $(P +This exception can be received as a message as well. + + + $(P +这个异常也可以被当作消息发送。 + + + + + $(P +This exception is thrown when receiving a message from the owner if the owner has been terminated. + + + $(P +如果所有者线程已被终止,工作线程在接收消息时会抛出这个异常。 + + + + + $(P +This feature is closely related to properties because properties are used almost always without parentheses. + + + $(P +这个功能与特性很像,因为在使用特性时都不带圆括号。 + + + + + $(P +This function applies its template parameter to its two function parameters. + + + $(P +这个函数会将它的模版参数应用到两个函数参数上。 + + + + + $(P +This function can also be called simply as $(C parallel()). + + + $(P +此函数可以简单地使用 $(C parallel()) 来调用。 + + + + + $(P +This is important for controlled modifications of members. + + + $(P +对成员变量的写入控制也是非常重要的。 + + + + + $(P +This method of defining variables individually does not scale to cases where even more variables are needed. + + + $(P +这种定义个别变量的方法不能扩展到需要更多变量的情况下。 + + + + + $(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. + + + $(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 +This time the exception is caught by the main thread instead of terminating the program: +) + + + $(P +这次异常将会被主线程捕获而不是终止程序: +) + + + + + $(P +This time the operations are executed in waves of three elements: +) + + + $(P +这次每轮将会对三个元素进行操作: +) + + + + + $(P +This time the reason of the failure is printed by the owner: +) + + + $(P +这次错误的原因会被所有者输出: +) + + + + + $(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. + + + $(P +对于指定了 $(C shared) 的模块变量来说,重复初始化有可能导致并发中的竞态条件而造成程序出错。 + + + + + $(P +Threads can detect that the receiver of a message has terminated. + + + $(P +线程可以检测消息的接收者是否已经终止。 + + + + + $(P +To see an example of a race condition let's consider multiple threads sharing the same mutable variable. + + + $(P +下面这个竞态条件的例子就是多个线程共享同一个可变变量。 + + + + + $(P +To see an example of this let's use $(C reduce()) with a function that is slowed down again artificially: +) + + + $(P +下面来看一个示例,将 $(C reduce()) 与一个人为减慢的函数一起使用: +) + + + + + $(P +To see how this program is better than the previous one, imagine needing to read 20 values. + + + $(P +要看一看这个程序怎样才能比以前的更好,让我们设想需要读取 20 个值。 + + + + + $(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. + + + $(P +为了能突出 $(C asyncBuf()) 的作用,示例中的范围与之前的范围有些不同:遍历一次要耗时半秒,处理一个元素也要耗费半秒。 + + + + + $(P +Two other $(C __traits) expressions are useful when dealing with user defined attributes: +) + + + $(P +在处理自定义属性时,还有其他两个 $(C __traits) 表达式可以使用: +) + + + + + $(P +Unfortunately, marking those blocks individually with $(C synchronized) is not sufficient, because the anonymous locks of the two blocks would be independent. + + + $(P +很可惜,直接使用 $(C synchronized) 并不能起到我们预期的效果,因为两个代码块的匿名锁是相互独立的。 + + + + + $(P +Unlike an interpreter, the compiler reads and understands the source code only once, during compilation. + + + $(P +不像解释器,编译器一次性地在编译期读入和理解源码。 + + + + + $(P +Unlike most other programming languages, data is not automatically shared in D; data is thread-local by default. + + + $(P +与其他大部分编程语言的不同之处在于,D 语言里的数据不会自动共享(默认情况下,数据仅限于线程本地。 + + + + + $(P +Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially: +) + + + $(P +不像结构,构造函数参数按顺序分配给成员时,类没有自动构造对象: +) + + + + + $(P +Unlike threads, the caller is paused while the fiber is executing. + + + $(P +与线程不同的是,在纤程执行的同时调用函数会被暂停执行。 + + + + + $(P +Using $(C Generator), we can easily present the elements of a tree as an $(C InputRange) as well. + + + $(P +利用 $(C Generator),我们还可以轻易地将树的元素当作一个 $(C InputRange)。 + + + + + $(P +Using the assignment operator is sufficient to build the association between a key and a value: +) + + + $(P +使用赋值运算符就足以构建键与值的关联: +) + + + + + $(P +Values that are passed as parts of a single message appear as a tuple on the receiver's side. + + + $(P +如果在一次消息中传递多个值的话,接收者会将它们看作一个元组。 + + + + + $(P +Violating these rules is undefined behavior. + + + $(P +违反这些规则即会产生未定义行为。 + + + + + $(P +We are familiar with this feature from slices. + + + $(P +在学习分片(slice)的时候我们已经多次用到它。 + + + + + $(P +We can make the following observations about the use of these blocks: +) + + + $(P +对于这些块的用法,我们可以作以下观测: +) + + + + + $(P +We have already seen that $(C static this()) can be used for initializing modules, including their variables. + + + $(P +我们已经见到过 $(C static this()),它是用来初始化模块和模块中包含的变量的。 + + + + + $(P +We have been taking advantage of the call stack in every program that we have written so far. + + + $(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 above how $(C Rectangle) can be used as if it has a third member variable. + + + $(P +之前我们通过定义特性为 $(C Rectangle) 添加了“第三个变量”。 + + + + + $(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. + + + $(P +在之前 $(LINK2 /ders/d.cn/foreach_opapply.html, $(C foreach) 结构体和类) 一章中我们了解到: $(C foreach) 块中的表达式将会被包装成委托传递给对象的 $(C opApply()) 成员函数。 + + + + + $(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 seen that the two tools that are used most in D programming are $(I the text editor) and $(I the compiler). + + + $(P +用 D 编程时经常会用到两个工具,它们分别是 $(I 文本编辑器) 和 $(I 编译器)。 + + + + + $(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 +We will see below that this exception can be received as a message as well. + + + $(P +之后我们会看到也可以将这个异常当作消息发送。 + + + + + $(P +When $(C parallel()) works on the results of $(C asyncBuf()) or $(C map()), it ignores the work unit size parameter. + + + $(P +若使用 $(C parallel()) 处理 $(C asyncBuf()) 或 $(C map()) 的返回值时,它将忽略工作单元大小参数。 + + + + + $(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. + + + $(P +对于上面的初始参数 $(C [1, 2, 3]) ,当最后使用它的空分片调用 $(C sum()) 时,调用栈的相关部分构成了下面这些帧。 + + + + + $(P +When blocks of code need to be synchronized on more than one object, those objects must be specified together. + + + $(P +如果几块代码需要锁住多个对象来同步,指定在一个 $(C synchronized) 中。 + + + + + $(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 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 +当需要 synchronize 多个代码块时,最好是使用多个 $(C synchronized) 关键字指定多个锁。 + + + + + $(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. + + + $(P +如果将来的某一天这个成员变量需要在外部修改,那我们仅需为其添加一个特性函数。 + + + + + $(P +When the actual object needs to be copied, the class must have a member function for that purpose. + + + $(P +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。 + + + + + $(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 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 +如果传入的函数是以 $(C =>) 声明的,那它的第一个参数(即上方程序中的 $(C a))代表当前运算结果(使用 reduce 的参数 $(C 0) 初始化),第二个参数(即上方程序中的 $(C b))代表当前元素。 + + + + + $(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 +When there is no property function that modifies a member variable, then that member is effectively read-only from the outside: +) + + + $(P +如果成员变量没有写特性,那在对象外部看来它将是只读的: +) + + + + + $(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. + + + $(P +对于某些类似 $(C FibonacciSeries) 那样的范围,维护迭代状态并非什么难事;但是对于其他的范围(如二叉搜索树那样的递归数据结构)则是困难重重。 + + + + + $(P +You may also want to investigate the $(C core.sync) package, which contains classic concurrency primitives in the following modules: +) + + + $(P +你也可以深入看看 $(C core.sync) 包中,在它的以下模块中包含了很多经典的并发基本操作: +) + + + + + $(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) 的值决定了循环的重复次数(迭代)。 + + + + + $(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 $(I $(B 注意:) 如果你还记得我们在 $(LINK2 /ders/d.cn/const_member_functions.html, $(CH4 const ref) 参数和 $(CH4 const) 成员函数) 一章中所讲的,函数声明中的 $(C const) 说明符表示函数不会修改其对象。) +) + + + + + $(P $(I $(B Note:) The $(C .length) property of fixed-length arrays cannot be modified.) +) + + + $(P $(I $(B 注意:) 无法修改定长数组的 $(C .length) 特性。) +) + + + + + $(P $(LINK2 /ders/d.en/ix.html, $(B The Index Section)) (useful for keyword searches)) + + + $(P $(LINK2 /ders/d.cn/ix.html, $(B 索引)) (可以使用关键字搜索)) + + + + + $(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 +$ 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 +... + + + $(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 0 +Finished working on student 0 +85 $(SHELL_NOTE 像 foreach 一样迭代元素) +Started working on student 1 +Finished working on student 1 +86 +... + + + + + $(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 + + + $(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 1 $(SHELL_NOTE 所有元素已被提前准备好) +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 + + + + + $(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 + + + $(SHELL +$ time ./deneme +$(SHELL_OBSERVED +Started working on student 1 $(SHELL_NOTE 并行) +Started working on student 2 $(SHELL_NOTE 但是顺序不确定) +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 + + + + + $(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 + + + $(SHELL +$ time ./deneme +$(SHELL_OBSERVED +started - element: 3, result: 0 $(SHELL_NOTE 首先,使用任务并行计算) +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 接着,顺序计算结果) +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 + + + + + $(SHELL +Before: résumé +After : re�sueé $(SHELL_NOTE_WRONG INCORRECT) +) + + + $(SHELL +Before: résumé +After : re�sueé $(SHELL_NOTE_WRONG 不正确) +) + + + + + $(SHELL +Final value: -672 $(SHELL_NOTE_WRONG not zero) +) + + + $(SHELL +Final value: -672 $(SHELL_NOTE_WRONG 不是 0) +) + + + + + $(SHELL +Final value: 0 $(SHELL_NOTE correct result) +) + + + $(SHELL +Final value: 0 $(SHELL_NOTE 正确结果) +) + + + + + $(SHELL +Initializing per-program variable at 6B0120 $(SHELL_NOTE only once) +Initializing per-thread variable at 7FBDB36557D0 +Initializing per-thread variable at 7FBDB3554670 +) + + + $(SHELL +Initializing per-program variable at 6B0120 $(SHELL_NOTE 只有一次) +Initializing per-thread variable at 7FBDB36557D0 +Initializing per-thread variable at 7FBDB3554670 +) + + + + + $(SHELL +Owner : Tid(std.concurrency.MessageBox) +Worker: Tid(std.concurrency.MessageBox) +) + + + $(SHELL +Owner : Tid(22310e53100) +Worker: Tid(22310e53000) +) + + + + + $(SHELL +There are 4 cores on this system. + + + $(SHELL +此系统里有 4 个内核。 + + + + + $(SHELL +before: 1 and 2 +after : 1 and 1 $(SHELL_NOTE_WRONG incorrect result) +) + + + $(SHELL +before: 1 and 2 +after : 1 and 1 $(SHELL_NOTE_WRONG 错误结果) +) + + + + + $(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE correct result) +) + + + $(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE 正确结果) +) + + + + + $(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE expected result) +) + + + $(SHELL +before: 1 and 2 +after : 1 and 2 $(SHELL_NOTE 期望的结果) +) + + + + + $(SHELL +before: 1 and 2 +after : 2 and 2 $(SHELL_NOTE_WRONG incorrect result) +) + + + $(SHELL +before: 1 and 2 +after : 2 and 2 $(SHELL_NOTE_WRONG 错误结果) +) + + + + + $(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) +) + + + $(SHELL +main is continuing +mayThrow() is started +mayThrow() is throwing an exception $(SHELL_NOTE 抛出) +main is waiting for the task +Detected an error in the task: 'Error message' $(SHELL_NOTE 捕获) +) + + + + + $(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) +) + + + $(SHELL +main is continuing +mayThrow() is started +mayThrow() is throwing an exception $(SHELL_NOTE 抛出) +main is waiting for the task +object.Exception@deneme.d(10): Error message $(SHELL_NOTE 终止) +) + + + + + $(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 waiting for a message that will never arrive) +) + + + $(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)):表达式总是在退出作用域时被执行, 无论是否成功或出现异常。) + + + + + $(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 +$(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)) +) + + + $(UL +$(LI $(LINK2 /ders/d.cn/struct.html, 结构)) +$(LI $(LINK2 /ders/d.cn/member_functions.html, 成员函数)) +$(LI $(LINK2 /ders/d.cn/const_member_functions.html, $(CH4 const ref) 参数和 $(CH4 const) 成员函数)) +$(LI $(LINK2 /ders/d.cn/special_functions.html, 构造函数和其它特殊函数)) +$(LI $(LINK2 /ders/d.cn/operator_overloading.html, 运算符重载)) +) + + + + + $(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 ! + + + + + &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) +&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;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) + + + &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: + + + ($(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))获取成员的。)) + + + + + (By default, the first element is the $(C .init) value). + + + 默认情况下,第 1 个元素为 $(C .init) 值。 + + + + + (By this distinction, class destructors should have more accurately been called $(I finalizers)). + + + (基于此点差异,类的析构函数被叫作 $(I 终结函数) 会更加确切)。 + + + + + (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.) +) + + + (因此,我建议你最好在非并行代码中使用 $(C fold())。因为它可以利用链式范围表达式的 $(LINK2 /ders/d.cn/ufcs.html, UFCS)。 +) + + + + + (Note that $(C insert()) is recursive indirectly through $(C insertOrSet()).) +) + + + 请注意,$(C insert()) 通过 $(C insertOrSet()) 间接实现的递归。 +) + + + + + (Priority is an operating system concept that determines execution priorities of threads.)) + + + (优先级是一个操作系统概念,它决定了线程执行的优先次序。)) + + + + + (Some implementations require that tree nodes have an additional $(C Node*) to point at each node's parent.) +) + + + (有些实现要求树节点拥有一个附加的 $(C Node*),让其指向每个节点的父节点。) +) + + + + + (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: +) + + + (这也适用于 $(C immutable) 因为它已隐含 $(C shared)。)解决方案是使用 $(C shared static this()) 块,它只会在程序中执行一次: +) + + + + + (The first $(C call()) starts the function.)) + + + (第一次调用 $(C call()) 会启动该函数。)) + + + + + (The parameter could be qualified as $(C out) instead of $(C ref) as well).) + + + (这个参数除了使用 $(C ref) 来修饰以外,还可以使用 $(C out) )。) + + + + + (There is also $(C prioritySend()), $(C receive()), and $(C receiveTimeout()), which will be explained later below.) +) + + + (此外还有 $(C prioritySend())、$(C receive()) 和 $(C receiveTimeout()) ——在后面章节介绍它们。) +) + + + + + (There is also $(C std.algorithm.each), which is for generating side effects for each element, as opposed to producing a result from it.) +) + + + 标准库中还有一个与之相似的 $(C std.algorithm.each)。但不同的是它并不会返回新的范围来储存结果,而是直接将结果应用到传入的范围的元素上。 +) + + + + + (Warning: Caesar cipher is a very weak encryption + + + (警告:Caesar cipher 是一种强度很弱的 + + + + + (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 对象) 变量1 变量2 + + + + + ) +$(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[]) 是一个切片,这意味着,一旦代码编译成功,它将会访问右手侧的全部字符。 + + + + + * program is running. + + + * 失去响应。 + + + + + * The default color is Color.black. + + + * 默认颜色为 Color.black。 + + + + + * anOperation(). + + + * anOperation() 的任务对象。 + + + + + * arbitrarily returns the value 1 to simulate functions that + + + * 会随意返回值 1,以便模拟函数正在 + + + + + * associative array is being defined with an extra + + + *关联数组在定义时附加了额外的空格, + + + + + * attributes of its members. + + + * 以 XML 格式输出它。 + + + + + * attributes. + + + * 属性。 + + + + + * been started directly from within main has been + + + * 直接在 main 函数中启动的操作 + + + + + * being called again, this time directly in main. + + + * ‘anOperation()’一次。 + + + + + * blue. + + + * 蓝色。 + + + + + * calculated so far. + + + * 计算出来。 + + + + + * completed, because it has been started by a regular + + + * 已经执行完成,因为它是一个常规 + + + + + * completed. + + + * 返回。 + + + + + * do calculations. + + + * 进行计算。 + + + + + * encrypted. + + + * 的那个符号。 + + + + + * function call, not as a task. + + + * 函数调用,而不是一个任务。 + + + + + * function parameters. + + + * 传递给任务函数。 + + + + + * if 'root' is 'null'). + + + * (即'root' 为 'null')。 + + + + + * in sorted order. + + + * 生成下一个树节点。 + + + + + * initializing its node. + + + * 初始化它的节点。 + + + + + * member has that attribute, Color.black otherwise. + + + * 则输出它的值,否则输出 Color.black。 + + + + + * method.) */ +auto encrypted(string value) { + + + * 加密方法。) */ +auto encrypted(string value) { + + + + + * not needed anymore. + + + * 因为不再需要它。 + + + + + * of the neighbors at eight directions, or the specified + + + * 八个方向中的一个,也可能是 + + + + + * operations; it returns only when the task has been + + + * 操作,只有在任务完成时它才会 + + + + + * order. + + + * 的接口。 + + + + + * position itself. + + + * 指定那个位置本身。 + + + + + * returned range is empty if the tree has no elements (i.e. + + + * 如果树没有元素,则返回的范围为空 + + + + + * robots. + + + * 的信息。 + + + + + * see the 'foreach' loop in the next chapter. + + + *看到‘foreach’循环。 + + + + + * set of '10 * n' numbers. + + + * 随机选取'n'个数来初始化这个树。 + + + + + * space in between to help distinguish the value type: */ + + + * 以易于区分值类型: */ + + + + + * specified here are passed to the task function as its + + + * 将会被作为将要并行执行的任务函数的实参 + + + + + * std.range.Generator. + + + * std.range.Generator的名字冲突。 + + + + + * string. + + + * 版本。 + + + + + * terminates. + + + *使其终止 + + + + + * the implementation of struct Tree below and should not be + + + * Tree 结构,而不应该 + + + + + * the task function, i.e. anOperation(). + + + * 任务执行的函数的返回值,如 anOperation()。 + + + + + * the value type is int[], i.e. an array of ints. + + + * 值类型为 int[] ,即整型数组。 + + + + + * then sets the 'ref' parameter to that element. + + + * 并且会设置一个 'ref' 参数引用该元素。 + + + + + * tree by means of 'root' being equal to 'null'. + + + * 即'root'为'null'时。 + + + + + * used directly. + + + * 被直接使用。 + + + + + * used for appending the new grade to that array: */ + + + * 将用于附加新成绩:*/ + + + + + * whether 'theTask' has completed its operations + + + * ‘theTask’执行的操作是否已经 + + + + + * with the remaining elements. + + + * 用余下的元素调用自己。 + + + + + * yet. + + + * 完成。 + + + + + *value += 1; // NOT atomic +--- + + + *value += 1; // 非原子的 +--- + + + + + --- +/* The equivalent of the printAsXML!Person instance. + + + --- +/* printAsXML!Person 实例的等同函数。 + + + + + --- +/* WARNING: Your system may become unresponsive when this + + + --- +/* 注意:在运行这个程序时你的系统可能会 + + + + + --- +// ... at the worker ... + + + --- +// ... 工作线程中 ... + + + + + --- +int a; // thread-local +immutable int b; // shared by all threads + + + --- +int a; // 仅限线程本地 +immutable int b; // 所有线程共享 + + + + + /* 'foreach' is similar but superior to 'for'. + + + /* ‘foreach’类似于‘for’,但比它更有优势。 + + + + + /* ... other operations may be executed here ... */ + + + /* ... 可在此处执行其他操作... */ + + + + + /* A translation from the nodes to the elements. + + + /* 从节点转换成元素。 + + + + + /* Add the front element to the current sum and call self + + + /* 将最前面的元素与当前总和相加,然后 + + + + + /* As 'theTask' continues executing, 'anOperation()' is + + + /* 在‘theTask’执行的时候,main 函数会直接调用 + + + + + /* At this point we are sure that the operation that has + + + /* 此处我们可以确认 + + + + + /* Construct a task object that will execute + + + /* 构建一个用于执行 + + + + + /* Failed to send; will try again later. + + + /* 无法发送消息;稍候会重新发送。 + + + + + /* First print the elements of the left sub-tree */ + + + /* 首先输出左子树里的元素 */ + + + + + /* Inserts the element to the specified sub-tree, potentially + + + /* 将元素插入到指定的子树,同时会根据情况 + + + + + /* Inserts the element to this tree. + + + /* 往树里插入元素。 + + + + + /* Larger elements go under the right sub-tree. + + + /* 更大的元素会进入到右子树。 + + + + + /* Lastly, print the elements of the right sub-tree */ + + + /* 最后,输出右子树里的元素 */ + + + + + /* Next call() will continue from this point */ // (3) + + + /* 下一次的 call() 会继续从这个点开始执行 */ // (3) + + + + + /* No element at or under this node */ + + + /* 此节点或者其下面没有元素 */ + + + + + /* No element to add. + + + /* 没有需要添加的元素。 + + + + + /* Note: print() member function is removed because it is + + + /* 请注意:成员函数 print() 已被移除, + + + + + /* On the other hand, it is not certain at this point + + + /* 另一方面,但在此处我们无法确定 + + + + + /* Populate the tree with those numbers. + + + /* 使用这些数来初始化树。 + + + + + /* Populates the tree with 'n' random numbers picked out of a + + + /* 从一组有'10 * n'个数的集合里 + + + + + /* Print the movement of this robot. + + + /* 输出机器人的运动情况。 + + + + + /* Printing the grades of "emre": */ + + + /* 打印“emre”的成绩: */ + + + + + /* Prints the elements in sorted order. + + + /* 按排序方式输出元素。 + + + + + /* Prints the first letter of 'id' every half a second. + + + /* 每半秒打印一次‘id’的首字母函数。 + + + + + /* Prints the specified object in XML format according to the + + + /* 根据指定对象的各个成员的属性 + + + + + /* Provides access to the elements of the tree in sorted + + + /* 提供顺序访问树里的各个元素 + + + + + /* Ready to collect information about the movements of the + + + /* 准备好接收有关机器人的移动情况 + + + + + /* Represents the nodes of a binary tree. + + + /* 二叉树的节点表示。 + + + + + /* Returns a neighbor of the specified Position. + + + /* 返回指定位置周围的坐标。 + + + + + /* Returns a random position around 0,0. + + + /* 返回一个坐标在 0,0 周边的随机位置。 + + + + + /* Returns an InputRange to the nodes of the tree. + + + /* 返回一个 InputRange 到树的节点。 + + + + + /* Returns at most one step from the specified coordinate. + + + /* 返回一个坐标,它相对从指定坐标最多变化一步。 + + + + + /* Returns the Caesar-encrypted version of the specified + + + /* 返回指定字符串的 Caesar 加密 + + + + + /* Returns the value of the Colored attribute if the specified + + + /* 如果被特定要求的成员拥有 Colored 属性, + + + + + /* Robots with various restDurations. + + + /* 不同移动时间间隔的机器人。 + + + + + /* Sending a negative value to the worker so that it + + + /* 向工作线程发送一个负数 + + + + + /* Smaller elements go under the left sub-tree. + + + /* 更小的元素会进入到左子树。 + + + + + /* Specifies that the symbol that it is assigned to should be + + + /* 特别要求加密应用此自定义属性 + + + + + /* Specifies the color of the symbol that it is assigned to. + + + /* 特别指定应用此自定义属性的那个符号的颜色。 + + + + + /* Start a mover thread for each robot. + + + /* 为每一个机器人启动一个移动线程。 + + + + + /* Start the task object */ + + + /* 启动任务对象 */ + + + + + /* The array of ints that correspond to "emre" is being + + + /* 与“emre” 对应的整型数组 + + + + + /* The key type of this associative array is string and + + + /* 该关联数组的键类型为 string, + + + + + /* Then print this element */ + + + /* 然后,输出当前元素 */ + + + + + /* This alias is used for resolving the name conflict with + + + /* 这个别名用于解决与 + + + + + /* This is the actual Tree representation. + + + /* 实际的 Tree 表示。 + + + + + /* This is the fiber function that generates each element and + + + /* 这个纤程函数会生成每个元素, + + + + + /* This is the fiber function that yields the next tree node + + + /* 这里就是纤程函数,它会按排序方式 + + + + + /* This is the first element of this sub-tree. + + + /* 当前子树的第一个元素。 + + + + + /* 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. + + + /* 此成员被特定要求输出为红色。 + + + + + /* Two grades for each student */ + + + /* 每个学生有两个成绩 */ + + + + + /* Wait for a message composed of Tid, int, and double. + + + /* 等待一个包含 Tid、int 和 double 类型的消息。 + + + + + // in a class destructor + + + // 显式分配内存 + + + + + // false: overcast + + + // false:阴天 + + + + + // has terminated. + + + // 它将抛出异常。 + + + + + // thrown if the owner + + + // 拥有者线程已经终止, + + + + + // thrown if the worker + + + // 工作线程已经终止, + + + + + // true : sunny + + + // true :晴天 + + + + + // ... at the owner ... + + + // ... 所有者线程 ... + + + + + // A thread whose partner is named "first" + + + // 兄弟线程为 "first" + + + + + // A thread whose partner is named "second" + + + // 兄弟线程为 "second" + + + + + // 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: + + + // 连接: + + + + + // For the unregister() calls to succeed, main() must wait + + + // 为了能成功调用 unregister(),main() 需要等待 + + + + + // Hoping to signal the worker to terminate: + + + // 希望通知工作线程终止: + + + + + // Inconsistent object: The area is not 10 * 20 == 200. + + + // 不一致的对象:面积不是10 * 20 == 200. + + + + + // Information about the students of a school + + + // 有关学校学生的信息 + + + + + // Note: dmd 2.074.0 does not support this feature. + + + // 注意:dmd 2.074.0 并不支持此功能。 + + + + + // Note: dmd 2.078.0 does not support this feature. + + + // 注意:dmd 2.078.0 不支持此功能。 + + + + + // Printing the array elements in a loop + + + // 在循环中输出数组元素 + + + + + // Reading the value + + + // 从输入流中读取值 + + + + + // Reading the values in a loop + + + // 在循环中从输入流读取元素值 + + + + + // Signalling the worker to terminate: + + + // 通知工作线程终止: + + + + + // Spends time for each message + + + // 每条消息都要花费一点时间来处理 + + + + + // 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 + + + // 使用动态数组的原因是不知道有多少 + + + + + // Wait for a while to simulate a long-lasting operation + + + // 在此处暂停一会以模拟耗时长的操作 + + + + + // Wait for all threads to finish their tasks + + + // 等待所有线程完成它们的任务 + + + + + // 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. + + + // 每个月的天数。 + + + + + // until the workers terminate. + + + // 工作线程终止。 + + + + + // values are going to be read from the input + + + // 值要从输入流中读取 + + + + + // whether the value is odd or even. + + + //相应的数组。 + + + + + // writeln. + + + // writeln 函数。 + + + + + <img style="border-width:0; float:left; margin:0 2em 1em 1em;" src="$(ROOT_DIR)/ders/d.en/cover_thumb.png" height="180"/> + + + <img style="border-width:0; float:left; margin:0 2em 1em 1em;" src="$(ROOT_DIR)/ders/d.cn/cover_thumb.png" height="180"/> + + + + + == != < <= > >= || &&)) +$(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 .. + + + + + @$(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. + + + $(C scope(failure)) 的好处是靠近它的表达式可以还原已写的另一个表达式。 + + + + + A continuously growing mailbox puts stress on the entire system and may point to a design flaw in the program. + + + 邮箱中持续增加的消息不仅会加重整个系统的负担,还会成为程序设计的瑕疵。 + + + + + A problem that this may cause is when we try to replace a two-code-unit character with a single-code-unit character: +) + + + 当我们试着用一个单字节字符替换一个双字节字符时问题就来了: +) + + + + + A race condition occurs when more than one thread accesses the same mutable data in an uncontrolled order. + + + 当多个线程以无法控制的顺序访问同一块数据时就会引发竞态条件。 + + + + + A thread that is busily working at a given time is said to be $(I CPU bound) at that point in time. + + + 在指定运行时间消耗大量 CPU 资源的线程叫做 $(I CPU 密集型)。 + + + + + Accessing an array with an invalid index causes the program to be terminated with an error. + + + 访问具有无效索引的数组将导致出错从而终止程序。 + + + + + According to this definition, arrays are containers. + + + 根据定义,数组就是容器。 + + + + + Ada, C, C++, and D are some of them. + + + Ada、C、C++ 和 D 都属于这样的语言。 + + + + + Additionally, $(C map()) is lazy while $(C each()) is eager. + + + 另外,$(C map()) 采取的是懒式执行方式,而 $(C each()) 则是立即执行。 + + + + + Additionally, interpreters usually perform very little analysis on the code before executing it. + + + 此外,解释器在执行代码之前对代码的分析大都非常有限。 + + + + + Additionally, the languages that are easy to compile are usually less dynamic than those that run in an interpreter. + + + 此外,容易编译的语言与在解释器中运行的语言相比往往缺乏灵活性。 + + + + + Additionally, this program takes advantage of a special message type to signal to the worker when it is time to exit: +) + + + 此外,这个程序还使用了特殊的消息类型来通知工作线程退出: +) + + + + + Again, these calls can be described as $(I if the value of the variable equals this old value, replace with this new value): +) + + + 你可以这样描述这种调用:$(I 如果变量值没有被其它线程改变则将新值赋给它): +) + + + + + All members may have already been finalized when the destructor is executing.) + + + 当析构函数执行时,全部成员应该已经终结。) + + + + + All of the features that we have seen in this chapter can be applied to a separate $(C TaskPool) object. + + + 本章介绍的所有功能都可以通过使用一个单独的 $(C TaskPool) 来调用。 + + + + + All of the operations of the program are normally executed on that thread. + + + 通常情况下程序的所有操作都将在这个线程中完成。 + + + + + Allowing user code to freely modify member variables always ends up causing issues with code maintenance. + + + 允许外部代码随意修改成员变量可能会对将来的维护工作造成极大的不便。 + + + + + Almost always a better design is to use property functions: +) + + + 但最好还是使用特性函数包装: +) + + + + + 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, although these examples use the $(C std.concurrency) module, the concepts of this chapter apply to the $(C core.thread) module as well. + + + 此外,虽然本章的示例都在使用 $(C std.concurrency) 模块,但这些概念同样适用于 $(C core.thread) 模块。 + + + + + Also, unlike arrays, the elements of hash tables are not stored side-by-side. + + + 不像数组,hash 表的元素不是逐个排列存储的。 + + + + + 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 spinForce()) 会使处理器忙碌,但它非常适合等待耗时短的任务。 + + + + + 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 module-level variables may give the impression of being accessible by all threads, each thread actually gets its own copy: +) + + + 虽然每个线程都可以访问模块级的变量,但实际上它们访问的都是对应变量在自己线程中的副本: +) + + + + + Although simple, arrays are the most common data structure used to store a collection of values. + + + 虽然简单,但数组是用于存储值的集合中最常见的数据结构。 + + + + + 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. + + + 虽然从语义上看这种判断是正确的,但实际上我们预计的这种情况很少发生:微处理器和编译器使用的优化技术会将彼此独立的值储存在处理器寄存器中。 + + + + + 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. + + + 虽然两个线程执行的都是 $(C swapper()) 函数,但别忘了由于 $(C temp) 是是个局部变量,每个线程都会拥有一个独立的 $(C temp) 副本。 + + + + + Although this difference makes associative arrays use more memory, it also allows them to use $(I sparse) key values. + + + 虽然这种不同让关联数组使用更多内存,但它也允许它们使用$(I 稀疏)键值。 + + + + + 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. + + + 虽然这种方法在大多数情况下都工作的很好,但就如我们在 $(LINK2 /ders/d.cn/templates.html, 模版) 一章中看到的那样:每个不同的模版实例都是一个不同的类型。 + + + + + An array that stores the names of the days of the week can be defined like this: +) + + + 一个存储一周当中每天的英文名称的数组可以定义下面这样: +) + + + + + An effective usage is with $(C foreach) loops. + + + 它可以在 $(C foreach) 循环中起到非常大的作用。 + + + + + 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. + + + 比如说服务器程序就是并发的,它需要同时处理多个客户端的请求。 + + + + + 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. + + + 或者所有者线程将同一个变量地址发给多个工作线程,这些工作线程可通过修改或读取这个变量来从其他线程获取信息。 + + + + + Any operations that are carried out by that function, including other functions that it may call, would be executed on the new thread. + + + 该函数(包括它可能调用的其他函数)包含的所有操作都会在新线程中执行。 + + + + + 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 of the $(I race conditions) that they are in, they inadvertently spoil the operations of other threads. + + + 由于它们处在$(I 竞态条件),这些线程会在不经意间破坏其他线程的操作。 + + + + + As a result, even operations as simple as incrementing a variable may be paused mid operation: +) + + + 也就是说,即使是一个简单的自增操作也可能会在执行到一半时被操作系统暂停: +) + + + + + As a result, most interpreters discover programming errors only after they start executing the program. + + + 最后,大多数解释器在执行程序之后方能发现编程错误。 + + + + + As a result, the program uses invalid indexes and attempts to access elements that are not parts of the arrays. + + + 结果是,程序使用无效的索引,试图访问不在数组中的元素。 + + + + + As a result, they cannot be members of the same array: +) + + + 因此,它们不能被放在同一个数组里: +) + + + + + 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. + + + 在线程执行期间,函数会调用其他函数,因此它们的帧会在概念上重叠在一起,从而形成一堆的帧。 + + + + + As it has been mentioned in that chapter, message passing is a safe method of concurrency. + + + 前面章节已提到,消息传递是一种比较安全的并发方法。 + + + + + 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. + + + 出于性能设计的考量,我们需要谨慎分析并确定线程的类型。 + + + + + As its function parameters, it takes a value to be used as the initial value of the result, and a range. + + + 除了用于接收函数的模版形参,它需要传入一个运算结果的初始值和一个范围。 + + + + + As the $(C foreach) loop normally operates on elements one after the other, $(C aSlowOperation()) would be called for each student sequentially. + + + 因为 $(C foreach) 循环通常是顺序操作元素的,学生的 $(C aSlowOperation()) 也会被顺序调用。 + + + + + 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. + + + 由于操作系统暂停和继续线程的不确定性,这两个线程操作的执行顺序也是不确定的。 + + + + + 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 these two concepts both involve executing programs on threads, and as parallelism is based on concurrency, they are sometimes confused with each other. + + + 这两个概念都涉及多线程,且并行是基于并发的,刚接触它们时感到有些迷惑也属正常。 + + + + + As we have seen above, the destructor is executed some time in the future during a garbage collection cycle. + + + 正如上面看到的,析构函数会在未来垃圾回收周期内的某个时候被执行。 + + + + + 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 具有三种独立的字符类型。 + + + + + As we will see below, it is also possible to receive $(C OwnerTerminated) and $(C LinkTerminated) exceptions as messages. + + + 就像我们下面看到的那样,可以将 $(C OwnerTerminated) 和 $(C LinkTerminated) 当作消息传递。 + + + + + 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. + + + 关联数组通过键值对存储它们的元素。 + + + + + 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: +) + + + 假定最左边 3 位表示操作码,最右边 5 位表示用于运算的值,该 8 位 CPU 上机器码示例程序如下所示: +) + + + + + At the lowest level these machine code instructions are implemented as electrical signals. + + + 它们在最底层由电子信号实现。 + + + + + BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) &gt; $(LINK2 /ders/index.html, Books) &gt; $(LINK2 /ders/d.en/index.html, Prg in D) + + + BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) &gt; $(LINK2 /ders/index.html, Books) &gt; $(LINK2 /ders/d.cn/index.html, D 语言编程) + + + + + BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) &gt; $(LINK2 /ders/index.html, Books) &gt; Prg in D + + + BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) &gt; $(LINK2 /ders/index.html, Books) &gt; D 语言编程 + + + + + 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. + + + 因为在内存中数组元素是逐个存储的,索引值就是元素位置与数组的起始位置的相对值。 + + + + + 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: +) + + + 因为默认情况下数据都是本地线程所有,因此每个线程都必须执行一次 $(C static this()),以便为所有线程初始化模块级的变量: +) + + + + + 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: +) + + + 由于异常会立刻终止工作线程,所有者只能收到第一条消息的反馈: +) + + + + + 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. + + + 因为通常情况下这个操作时运行在一个处理器核心上的,那么如果对每个元素的操作是独立的,这将造成不必要的执行速度的降低。 + + + + + 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. + + + 在这层,编码的舒适度不是主要考虑因素,所以直接按照 CPU 机器码形式编写程序非常困难。 + + + + + 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. + + + 由于数组里可能拥有多个具有相同元素的 $(I 分片) ,$(C remove()) 实际上不会改变原有数组的元素个数。 + + + + + Being value types, $(C o) and $(C i) are copied automatically. + + + 做为值类型,$(C o) 和 $(C i) 自动被复制。 + + + + + By this definition, classes are $(I user defined types). + + + 根据此定义,类是 $(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? + + + 在没有读下面这段话之前你能指出剩下的问题吗? + + + + + 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. + + + 类变量没有成员,类对象有。 + + + + + Class variables do not have values themselves; the actual class objects must be constructed by the $(C new) keyword. + + + 类变量并不拥有值本身;实际的类对象必须使用关键字 $(C new) 来构造。 + + + + + Classes will be explained in more detail in later chapters. + + + 稍后的章节中将展示类的更多细节。 + + + + + Compared to regular threads that are commonly used in parallelism and concurrency, it is more efficient to switch between fibers. + + + 与并行和并发里普遍使用的常规线程相比,纤程之间的切换具有更高的效率 + + + + + Compilers usually perform advanced analysis on the code, which help with producing fast programs and catching programming errors before the program even starts running. + + + 编译器素来对源码进行深入的分析,这有助于生成更高效的程序,甚至于在程序执行之前就捕捉到编程错误。 + + + + + Concurrency is about making a program run on more than one thread at a time. + + + 并发可以让一个程序同时在多个线程上运行。 + + + + + Concurrency is easy only when it is based on $(I message passing). + + + 并发只有在基于 $(I 消息传递) 实现时才比较简单。 + + + + + Concurrency is the topic of the next chapter. + + + 关于并发,下一章会主要讲解它。 + + + + + Concurrency makes use of threads explicitly. + + + 而并发则需要显式利用线程。 + + + + + Concurrency on the other hand, is a concept that may be needed even on a single-core environment. + + + 而并发则是一个在单核环境里也可以使用的概念。 + + + + + Consider $(I concurrency) only when threads depend on operations of other threads.) + + + 只有在线程间有相互依赖的操作时才考虑 $(I 并发)。) + + + + + Contents + + + 内容 + + + + + D programs are written in text editors. + + + D 程序需要在文本编辑器里编写。 + + + + + D programs start executing with $(C main()) on a thread that has been assigned to that program by the operating system. + + + D 语言程序在操作系统指定的线程上执行 $(C main()) 函数。 + + + + + DERLEME_HATASI_METIN=compilation ERROR + + + 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 + + + DESCRIPTION=D 语言的数组基本操作 + + + + + DESCRIPTION=D programming language tutorial from the ground up. + + + DESCRIPTION=全新编写的 D 编程语言教程。 + + + + + DESCRIPTION=Enabling using member functions like member variables. + + + DESCRIPTION=像访问成员变量一样调用成员函数 + + + + + DESCRIPTION=Executing multiple threads that share data. + + + DESCRIPTION=执行多条共享数据的线程 + + + + + DESCRIPTION=Generators and cooperative multitasking by fibers. + + + DESCRIPTION=Generators 和基于纤程的协作式多任务. + + + + + DESCRIPTION=Parallel programming that enables taking advantage of microprocessor cores + + + DESCRIPTION=并行编程来利用多核心微处理器的运算能力 + + + + + 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=Starting multiple threads in the D programming language and the interactions of threads by message passing. + + + 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 introduction of the compiler and compiled languages + + + DESCRIPTION=编译与编译型编程语言简介 + + + + + 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 语言的字符串 + + + + + 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. + + + 定义一个能存储每个学生的多个成绩的关联数组。 + + + + + Despite being an unrealistic tool, it is still useful in this chapter to demonstrate the power of parallelism. + + + 虽然使用了一个和真实任务有差别的工具,但本章的示例还是能很好的展现出并行编程的威力。 + + + + + Different from an interpreter, it does not execute the code; rather, it produces a program written in another language (usually machine code). + + + 与解释器不同的是,它不立即执行代码,而是生成一个用其他语言(通常为机器码)表示的程序。 + + + + + Different from structs, classes provide the $(I object oriented programming) (OOP) paradigm in D. The major aspects of OOP are the following: +) + + + 不同于结构的是,D 语言中的类提供的是 $(I 面向对象编程) (OOP)模型。OOP 的主要内容有以下几个方面: +) + + + + + 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. + + + 由于提供的默认值 100 相对较高,$(C parallel()) 会给人一种错觉:在处理较短的非 $(C RandomAccessRange) 范围时效率不高。 + + + + + Each task works on the elements that it is assigned to and calculates a $(C result) that corresponds to the elements of that task. + + + 但每一个任务都会去计算分派给它的元素并最终得出一个对应的 $(C result)。 + + + + + Elements are produced in parallel while they are being used: +) + + + 在元素被使用时,剩下的元素也在被并行求值 +) + + + + + Especially note how $(C byNode()) is implemented as an adaptor over the recursive function $(C nextNode()): +) + + + 特别需要注意的是,$(C byNode()) 的实现其实就是一个基于递归函数 $(C nextNode()) 的适配器: +) + + + + + Even its $(C toString()) function is not overloaded: +) + + + 它甚至没重载 $(C toString()): +) + + + + + Even so, explicit $(C TaskPool) objects may be created and used as needed. + + + 即便如此,也可以在需要的时候显示创建和使用 $(C TaskPool) 对象。 + + + + + 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) 语句,让程序能按一致的顺序撤消前边表达式的副作用。 + + + + + Fibers are similar to $(I coroutines) and $(I green threads). + + + 纤程与$(I协程(Coroutine))和$(I绿色线程)相似。 + + + + + For classes, the meaning of $(C opAssign) is always $(I associating a class variable with a class object). + + + 对于类, $(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. + + + 例如,$(C char[]) 是一个字符串类型。 + + + + + For example, $(C dayNumbers) would have two key-value pairs after the operations above. + + + 例如,上面的操作结束后 $(C dayNumbers) 将有两个键值对。 + + + + + For example, $(C iota(10)) is a range of $(C int) elements from $(C 0) to $(C 9). + + + 例如,$(C iota(10)) 为一个 $(C int) 型元素构成的范围,元素值的范围是 $(C 0) 到 $(C 9)。 + + + + + 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, 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)). + + + 例如,一个保存了七月每天的气温的数组能汇集 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, 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). + + + 例如,有一个 $(C Student) 类,它通过姓名和成绩来表示学生, 此时 $(C Student) $(I 对象)的成员会存储这些信息。 + + + + + For example, both of the following variables have the same attributes: +) + + + 例如,下面的变量拥有的属性是相同的: +) + + + + + 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. + + + 例如,对于虚构的 8 位 CPU 来说,数字 4 可能表示载入操作,数字 5 可能表示存储操作,而数字 6 可能表示递增操作。 + + + + + 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()). + + + 例如,对于上述代码,解释器会首先执行 $(C a_card_has_been_played()) ,而后根据其执行结果选择性地执行 $(C display_the_card())。 + + + + + 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, 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. + + + 例如,如果输出信息的顺序非常重要,那么上面程序中的 $(C parallel()) 调用将会使程序出错。 + + + + + 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, memory used for the elements of a dynamic array is allocated by the garbage collector as well. + + + 例如,用于一个动态数组的元素的内存由垃圾回收器来分配。 + + + + + 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: +) + + + 例如,当 $(C bar()) 返回之后,它的帧并不再需要,而其空间随后会被其他函数调用使用: +) + + + + + 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 following code assigns the $(C Encrypted) attribute to the declaration of $(C name): +) + + + 例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: +) + + + + + For example, the following code removes the element at index 1. + + + 例如,下面这段代码将删除索引1上的那个元素。 + + + + + For example, the following code removes the elements of the array that are equal to 42. + + + 例如,下面这段代码将删除数组里等于 42 的所有元素。 + + + + + For example, the following code uses the return value of $(C asyncBuf()) as an $(C InputRange) which operates semi-eagerly: +) + + + 例如,下面这个例子就将 $(C asyncBuf()) 的返回值作为一个半延迟取值的 $(C InputRange): +) + + + + + For example, the following code would make ready the results of the function calls for three elements at a time: +) + + + 例如,下面的代码每次都会为三个元素准备好函数调用的结果: +) + + + + + For example, the following function can be used as a fiber function: + + + 例如,下面这个函数便可以用作纤程函数: + + + + + For example, the following program that tries to pass the address of a $(C bool) variable to another thread cannot be compiled: +) + + + 例如,下面这个程序,试图传递 $(C bool) 值的地址只会导致编译错误: +) + + + + + For example, the month can be determined by the value of the $(C monthIndex) variable below: +) + + + 例如,下面通过变量 $(C monthIndex) 的值来确定该月: +) + + + + + 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. + + + 例如,所有者线程在启动工作线程的同时向其传递了一个 $(C bool) 值的地址,工作线程可通过读取这个值来判断是否需要终止。 + + + + + For example, the result of the first function is the first member of the tuple. + + + 例如,第一个函数的结果即为元组的第一个元素。 + + + + + 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 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". + + + 例如,字符串是“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 example: + + + 例如: + + + + + 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 and in general, a compiled program runs faster compared to executing that program with an interpreter. + + + 出于这个原因,通常情况下被编译的程序较被解释器所执行的程序运行更快。 + + + + + For that reason, $(C immutable) implies $(C shared): +) + + + 因此,$(C immutable) 隐含了 $(C shared): +) + + + + + For that reason, $(C taskPool.reduce()) may execute slower in short examples as in this chapter as will be observed in the following output. + + + 因此,在某些耗时少的例子(例如本章中的某些例子)中 $(C taskPool.reduce()) 可能会比非并行版本执行速度慢。 + + + + + For that reason, class variables must always be compared by the $(C is) and $(C !is) operators. + + + 因此,类变量必须总是通过运算符 $(C is) 和 $(C !is) 进行比较。 + + + + + For that reason, member variables better be encapsulated either by regular member functions or by property functions. + + + 所以,我们最好将对成员变量的访问封装在常规成员函数或特性函数中。 + + + + + For that reason, running a program with an interpreter is usually slower than running the compiled version of the same program. + + + 这也就是为什么使用解释器执行程序往往要比直接执行编译版本的同一程序要慢。 + + + + + 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. + + + 因此,所有抛出的异常都会自动地由任务自已来捕获,然后在调用类似 $(C yieldForce()) 的 $(C Task) 成员函数时,重新抛出。 + + + + + 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. + + + 所以指定的初始值应为$(I 恒等值)。例如本例中的 $(C 0) 就不会产生任何副作用。 + + + + + For that reason, the result of the remove operation must be assigned back to the same array variable. + + + 基于此,删除操作的结果必须回赋给同一个数组变量。 + + + + + For that reason, to fully understand and appreciate fibers, one must first understand the $(I call stack) of a thread. + + + 因此,想要完全理解和用好纤程,你必须先要理解线程的$(I调用栈(call stack))。 + + + + + For that reason, usually there is no need to create any other $(C TaskPool) object. + + + 因此,通常不需要创建其他的 $(C TaskPool) 对象。 + + + + + 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()) 不能如愿读取字符串: +) + + + + + For this function to work correctly in a multi-threaded environment, both of the accounts must first be locked. + + + 为了能让函数在多线程环境中正确运行,每个账户都要先被锁锁住。 + + + + + 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.) +) + + + 幸运的是大多数程序可以使用 $(I 原子操作) 替代 $(C synchronized) 块(随后会介绍它)。) +) + + + + + 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. + + + 这时,从程序员的角度来看,执行指令就分三个步骤:写代码、编译和执行生成的文件。 + + + + + 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. + + + 从程序员的角度来看,依赖解释器执行指令只涉及两个步骤:编写源代码和将源代码交由解释器处理。 + + + + + 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()) 要读取多少字符,它持续读,直到整个输入结束。 + + + + + Further, once the tree has an $(C InputRange) interface, the $(C print()) member function would not be needed anymore; hence it is removed. + + + 而且,在该树有了 $(C InputRange) 接口之后,其成员函数 $(C print()) 不再需要(因此,完全可以将其删除)。 + + + + + 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 次,每个单值变量来一次。 + + + + + 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. + + + 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, because their meanings may not be clear, attributes consisting of literal values like $(C 42) are discouraged: +) + + + 不过,因为它们的含义可能不是很清楚,因此不赞成大家使用文字量值(如 $(C 42) )构成的属性: +) + + + + + 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. + + + 但如果范围中的每个元素都注定要被作为实参传递给函数而且每次操作又都是独立的的话,我们根本就没必要使用速度较慢的延迟取值而不是并行操作。 + + + + + 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. + + + 然而如果把这 4 个操作分配给 4 个内核执行,它们将会被同时处理。那么处理完这 4 个学生总共耗时 1 秒。 + + + + + However, in many cases it is not necessary for the operations of preceding students to be completed before starting the operations of successive students. + + + 然而大部分时候这种处理的顺序并不是必须的。 + + + + + However, regular member functions could also be used instead of properties: +) + + + 当然,除了特性,普通成员函数也可以实现我们需要的功能: +) + + + + + However, that is almost never the case: +) + + + 但是,这个程序几乎不会得出这个结果: +) + + + + + However, the exceptions can be caught and sent explicitly by the worker. + + + 但你也可以在工作线程中手动捕获异常并将其发送给所有者。 + + + + + However, the following attempt would be incorrect: +) + + + 但下面这个程序并不能实现我们的要求: +) + + + + + However, the grades cannot be inserted as in the following code because each grade would overwrite the previous one: +) + + + 然而,不能像下面的代码这样插入成绩,因为后来的成绩将覆盖前一个: +) + + + + + However, the issues that they convey appear in real programs at greater scales. + + + 但是在实际编程中它们代表的问题通常规模很大。 + + + + + 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. + + + 但是,警告几乎总是指示一个实际错误或糟糕的代码风格,所以把警告当作错误对待是一个常见的做法。 + + + + + I recommend that you consider this module as long as the operations that need synchronization are less than a block of code. + + + 所以如果需要同步的只是一些简单操作而不是一块代码的话,我建议你优先考虑这个模块。 + + + + + I will not attempt to implement $(C InOrderRange) here but I encourage you to implement or at least research tree iterators. + + + 这里不会去实现 $(C InOrderRange),但是鼓励大家去实现或者试着研究一下树的迭代方法。 + + + + + If so, $(C cas()) assigns $(C newValue) to the variable and returns $(C true). + + + 这样的话 $(C cas()) 会将 $(C newValue) 赋给这个变量并返回 $(C true)。 + + + + + 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. + + + 如果大部分线程都是 I/O 密集型的,那么程序不需要担心由于线程数超过内核数而造成性能下降的问题。 + + + + + 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. + + + 如果 $(C Student) 对象相互独立,不去使用那些可能处在空闲状态的微处理器核心是非常浪费的。 + + + + + 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: +) + + + 如果用户最后这样结束输入,我们看到换行符已作为字符串的一部分被读取: +) + + + + + If there is no thread associated with that name, then $(C Tid.init) is returned.) + + + 如果没有线程关联到这个名字,则返回 $(C Tid.init)。) + + + + + Illegal instructions are the ones that are outside the specifications of the language. + + + 非法指令指的是超出语言规范的地方。 + + + + + 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 D, it is sufficient to specify the objects in the same $(C synchronized) statement for the code to avoid such deadlocks: +) + + + 对于 D 语言,在同一个 $(C synchronized) 中指定这些对象即可有效避免死锁的情况: +) + + + + + In concurrency, it is normal for threads to depend on results of other threads. + + + 而对于并发,线程间的相互依赖是很常见的。 + + + + + In fact, it would be a bug if they did depend on results of other tasks that are running 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). + + + 实际上,上一章讲解的任务是基于线程的,只不过这些线程是由 $(C std.parallelism) 自动启动。 + + + + + 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, a thread can execute a member function only if no other thread is executing a member function on the same object.) + + + 换句话说,线程只能在没有其他线程调用这个类的实例的成员函数的情况下才能操作它。) + + + + + In other words, arrays map indexes to values. + + + 换句话说,数组映射索引到值。 + + + + + In other words, class variables may not be providing access to any object. + + + 换句话说,类变量可以不提供对任何对象的访问。 + + + + + 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 parallelism, it wraps $(C yieldForce()): +) + + + 而在并行中,它只封装了 $(C yieldForce()): +) + + + + + In such cases $(C taskPool.reduce()) from the $(C std.parallelism) module can be used for taking advantage of all of the cores. + + + 在这种情况下你就可以使用 $(C std.parallelism) 模块中的 $(C taskPool.reduce()) 来利用多核心的运算能力。 + + + + + In such cases the template parameters of $(C receiveOnly()) must match the types of the tuple members: +) + + + 此时,$(C receiveOnly()) 的模版参数的类型要与每一个元组成员的类型对应: +) + + + + + In such cases, it may be faster to have each thread execute more than one iteration of the loop. + + + 此时,让每个线程去执行循环的多个迭代反而会更快些。 + + + + + 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 that case, the types of the members of the tuples would be different as well: +) + + + 此时,元组成员的类型也不一定相同: +) + + + + + In the case of integers, the elements get sorted from the smallest value to the greatest value. + + + 对于整数,元素按从小到大排序。 + + + + + In the following example, the fiber function is a member function: +) + + + 下面的示例里,纤程函数是一个成员函数: +) + + + + + In the simplest definition, strings are nothing but arrays of characters. + + + 在最简单的定义中,字符串只不过是字符数组。 + + + + + In this case, the constructor must be defined as $(C pure) as well. + + + 此时,构造函数必须同时定义为 $(C pure) 。 + + + + + Inheritance is for acquiring $(I implementations) of other types. + + + 继承是用于获取其它类型的 $(I 实现)。 + + + + + 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: +) + + + 我们要定义一个名为 $(C area) 的函数来计算面积而不是定义成员变量来表示面积: +) + + + + + Instead, $(C parallel()) reuses the internal buffer of the result range. + + + $(C parallel()) 将会使用这两个函数返回的范围中的缓冲区。 + + + + + Instead, a parameter-less $(LINK2 /ders/d.en/lambda.html, delegate) is used as an adaptor to be passed to the $(C Fiber) constructor.) + + + 其实,无参的 $(LINK2 /ders/d.cn/lambda.html, 委托) 可被当作适配器传递给 $(C Fiber) 的构造函数。) + + + + + Instead, the comparison must be done by the $(C is) or the $(C !is) operator, accordingly: +) + + + 相反,必须相应地使用运算符 $(C is) 或 $(C !is): +) + + + + + Instead, the elements are accessed by specifying the $(I element number) within square brackets: +) + + + 相反,通过指定方括号内的元素位置数就可以访问元素: +) + + + + + Instead, the program terminates with an error. + + + 若有错误,程序会终止。 + + + + + It + + + + + + + + It accesses the element that + + + 它访问对应于 + + + + + It achieves this task by calling itself recursively with a slice that is one element shorter than the one that it has received. + + + 它采用递归调用自己的方式(所用的分片是比它所接收到那个分片少一个元素)实现了这一个功能。 + + + + + It allows an empty + + + 它允许为空树, + + + + + 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 can continue working on other things if a message does not arrive within that time: +) + + + 如果消息超时它还会继续执行其他操作: +) + + + + + It consumes more memory to gain speed. + + + 它是通过消耗更多的内存来获得更快速度的。 + + + + + It defines an array that consists + + + 它定义了一个可以存储 + + + + + It dispatches messages to message handling delegates. + + + 它会把这些消息分发给各个消息处理委托。 + + + + + It identifies the robots by the robot ids that are sent as parts of the messages. + + + 它通过消息中的机器人 ID 来识别机器人。 + + + + + It is a lazy algorithm: It calls the function as needed. + + + 这是个懒式算法:函数只有在需要时才会被调用。 + + + + + It is an even + + + 如果值被 2 + + + + + It is commonly called without the function parentheses: +) + + + 通常调用它的时候不需要带圆括号: +) + + + + + It is easy to see an example of such a problem simply by trying to allocate an object in a class destructor: +) + + + 尝试在类的析构函数中分配一个对象,通过这种方式可以轻易地重现这种的问题: +) + + + + + It is not possible that they can ever have any other value after that point. + + + 此处不可能会再有其他的值。 + + + + + It is possible that each thread may have just locked its respective $(C from) object, hoping next to lock its $(C to) object. + + + 每个线程都会先锁住各自的 $(C from) 对象,然后再尝试去锁 $(C to) 对象。 + + + + + It is the owner thread that $(I serializes) the printing process simply by receiving messages from its message box one by one. + + + 所有者线程仅仅是将收件箱中的消息一个一个取出来并$(I 按顺序)输出。 + + + + + It is used by specifying the current and the desired values of the variable at the same time: +) + + + 使用方法是同时指定当前值和期望值: +) + + + + + It is used differently because the $(STRING " %s") format string and the $(C &) operator are not needed: +) + + + 不同的是没有 $(STRING " %s") 格式字符串并且不需要 $(C &) 运算符: +) + + + + + 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. + + + 若使用传统的基于锁的数据共享实现的并发模型,则很难写出正确的并发程序。 + + + + + It may also mean that the thread may never get to the most recent messages. + + + 这也意味着线程永远只能拿到许久之前接收的消息。 + + + + + It may be one + + + 它既可能是 + + + + + 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 must be $(C reset()) before it can be used again.) + + + 通过 $(C reset()) 重置后,它可以被再次使用。) + + + + + It prints the first letter of $(C id) to indicate which task it is working for. + + + 它将打印出 $(C id) 的第一个字符,这样我们就可以通过这个字符来判断程序正在等待哪一个任务执行完成。 + + + + + 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. + + + 如果是同一对象,返回 $(C true) ,否则为 $(C false) 。 + + + + + It returns a range that consists of the results of applying that function to the elements of the range: +) + + + 它会返回一个新的范围来储存应用函数后得到的结果: +) + + + + + It stores the elements in a buffer as they are produced by the range, and serves the elements from that buffer to its user. + + + 它将范围中的元素储存在缓冲区中,需要时用户再从缓冲区中获取元素。 + + + + + It uses this parameter to communicate the current element to its caller. + + + 它使用此参数来与将当前元素与其调用者联系在一起。 + + + + + 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 behavior can be described as $(I mutate the variable if it still has its currently known value). + + + 它的操作的理念是:$(I 如果变量的值与已知当前值相同,则修改变量)。 + + + + + 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()): +) + + + 它的第一个参数是包含数据的输入行,而其余的参数就与 $(C readf()) 的一模一样: +) + + + + + Its return value is $(C true) if a message has been received within that time, $(C false) otherwise. + + + 如果在指定时间内接收到了消息,函数返回值为 $(C true) ;如果超时则返回 $(C false)。 + + + + + Its return value is the return value of + + + 它的返回值即为 + + + + + 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. + + + 它的三个参数分别指代的是邮箱、最大保存消息数量和邮箱被填满之后需要进行的操作。 + + + + + Just like $(C map()), it takes one or more functions as template parameters. + + + 和 $(C map()) 一样,它也可以接收一个或多个函数作为模版实参。 + + + + + KEYWORDS=d programming language tutorial book + + + KEYWORDS=D 编程 语言 教程 书籍 + + + + + 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 concurrency thread + + + KEYWORDS=d programming language tutorial book concurrency thread 编程 语言 教程 书籍 并发 线程 + + + + + KEYWORDS=d programming language tutorial book concurrency thread data sharing + + + KEYWORDS=d programming language tutorial book concurrency thread data sharing 编程 语言 教程 书籍 并发 线程 数据 共享 + + + + + + KEYWORDS=d programming language tutorial book novice beginner + + + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 + + + + + KEYWORDS=d programming language tutorial book parallel programming + + + KEYWORDS=d programming language tutorial book parallel programming 编程 语言 教程 书籍 并行 + + + + + KEYWORDS=d programming language tutorial book scope + + + KEYWORDS=D 语言编程教程 scope + + + + + KEYWORDS=d programming language tutorial book string + + + 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 + + + KEYWORDS=D 语言 编程 教程 书籍 教程 类 + + + + + KEYWORDS=d programming lesson book tutorial property + + + KEYWORDS=d programming lesson book tutorial property 编程 课程 书籍 教程 特性 + + + + + 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 assume that $(C Student) has a member function that returns the average grade of the student. + + + 假设 $(C Student) 有一个计算并返回学生平均分的成员函数。 + + + + + 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) 语句就是关于离开作用域时应执行的表达式: +) + + + + + MAIN_TITLE=D.ershane Solutions +CLASS=solution +COZUM_BOLUMU = $(H4 $0) +LANG=en +LANGUAGE=english +SUB_AUTHOR= + + + MAIN_TITLE=D 语言答案 +CLASS=答案 +COZUM_BOLUMU = $(H4 $0) +LANG=zh-cn +LANGUAGE=chinese +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 + + + + + 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: +) + + + 只需要导入 $(C std.parallelism) 模块并将上面代码中的 $(C students) 换为 $(C parallel(students)) 即可充分利用系统中全部核心的运算能力。 +) + + + + + Message passing normally involves more complicated communication in many kinds of programs. + + + 当然在许多程序中消息传递不止这么简单。 + + + + + Messages may be composed of any type and any number of variables. + + + 任何类型任何长短的数据都可以被称为消息。 + + + + + Messages that do not match the handlers that are specified earlier in the argument list always match a $(C Variant) handler: +) + + + 如果消息无法与参数列表前面指定的各个处理函数相匹配,那么它们将会与一个 $(C Variant) 类型的处理函数匹配: +) + + + + + More features will be introduced later in $(LINK2 /ders/d.en/slices.html, the Slices and Other Array Features chapter). + + + 更多功能将在后面 $(LINK2 /ders/d.cn/slices.html, 分片和其他数组功能一章) 介绍。 + + + + + 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. + + + 另外,编译器往往是针对某一特定平台进行编译,程序需要重新编译以在不同的处理器或操作系统上运行。 + + + + + Most of the features that we have seen for structs in the following chapters apply to classes as well: +) + + + 在下面的章节中我们已经看到结构的大部分特性也适用于类: +) + + + + + 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. + + + 在 $(LINK2 /ders/d.cn/ranges.html, 范围) 及后面章节里看到的大部分范围示例为了完成任务都会存储某些状态。 + + + + + MyClass variable; // does not reference an object + + + MyClass variable; // 没有引用对象 + + + + + Node * left; // Left sub-tree + + + Node * left; // 左子树 + + + + + Node * right; // Right sub-tree + + + Node * right; // 右子树 + + + + + Normally, all of the algorithms use the same container object named $(C taskPool). + + + 一般情况下,所有算法都会使用同一个名为 $(C taskPool) 的容器对象。 + + + + + Note how $(C fibonacciSeries()) below is written as a simple function. + + + 请注意,下面的 $(C fibonacciSeries()) 是如何被写成为一个函数的。 + + + + + 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) 成员。 + + + + + Note that the return types of the functions need not be the same, as seen in the $(C quarterOf()) and $(C tenTimes()) functions below. + + + 注意,这些函数的返回类型不一定相同,如下面示例中的 $(C quarterOf()) 和 $(C tenTimes()) 函数。 + + + + + Note that there is no need for the $(C Lock) class anymore either: +) + + + 请注意,现在不再需要 $(C Lock) 类: +) + + + + + 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. + + + 这与单线程有着极大的不同:像本章上面的程序如果写成单线程的话,应该将 $(C try-catch) 包裹住可能会抛出异常的代码。 + + + + + Observe that it starts ten threads that all access the same two variables $(C i) and $(C j). + + + 你看它启动了 10 个线程,这些线程都要去访问变量 $(C i) 和 $(C j)。 + + + + + Obviously, the race conditions would be much more complicated in the case of the ten threads of this example. + + + 10 条线程的实际情况要比例子复杂得多。 + + + + + On the other hand, a program that did not use an array would have to have 20 variable definitions. + + + 另一方面,这个程序若不使用数组那将必须定义 20 个变量。 + + + + + On the other hand, having to compile the program every time it is changed is a complication and a potential source of human errors. + + + 另一方面,每次改变代码都必须重新编译,既复杂又易引起人为错误。 + + + + + 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). + + + 另一方面,如果 $(C cas()) 发现变量的值不再等于 $(C currentValue) ,那么它将直接返回 $(C false),不再修改变量的值。 + + + + + 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. + + + 与之相对的是消耗大量时间等待事件、用户输入、来自互联网的数据或调用了 $(C Thread.sleep) 等情况的线程。 + + + + + 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. + + + 每轮在缓冲区中载入一定数量的元素用于并行迭代。只有当上一轮缓冲的元素被 $(C popFront()) 消耗完后,它才会开始为下一轮迭代缓冲元素。 + + + + + One solution is to have the worker catch the exception to be sent as a message. + + + 一种解决方案是在工作线程中捕获潜在的由接收信息引发的异常。 + + + + + One way of avoiding these race conditions is to mark the common code with the $(C synchronized) keyword. + + + 一种解决方案是使用关键字 $(C synchronized) 标记公共代码以消除竞态条件。 + + + + + Only the programmer can determine that certain high-level operations are independent and that they can be executed in parallel. + + + 只有程序员才能判断哪些高级别的操作是互相独立、可以并行化的。 + + + + + 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. + + + 只有持有锁的那个线程才可以执行,其他线程都需要等待持有锁的线程执行完成并释放 $(C synchronized) 锁。 + + + + + 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. + + + 除极个别情况外,一般存取单个元素所花费的时间不依赖于关联数组中元素的个数。 + + + + + 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). + + + 不然的话就可能出现几个线程分别拿到了不同的锁,而它们又在等待其他线程手中的锁的情况,即$(I 死锁)。 + + + + + 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.) +) + + + 其实,想要计算某个范围里所有元素的总和,可以使用 $(C std.algorithm.sum)——它针对浮点类型使用了特殊的算法来实现更精确的计算。) +) + + + + + PROBLEM_COK_COZUMSUZ_METIN=the solutions will be posted later... + + + PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… + + + + + Partly because they are anonymous, it is not possible to access class objects directly. + + + 另一方面,类变量是用于访问类对象的一种语言特性。 + + + + + Passing thread ids from thread to thread may be overly complicated in programs that use more than a couple of threads. + + + 一旦线程数增加,它将会大大增加程序的复杂度。 + + + + + Plain arrays can use only integers as indexes. + + + 简单数组只能使用整型做索引。 + + + + + Problems like a mismatched parenthesis, a missing semicolon, a misspelled keyword, etc. all cause compilation errors. + + + 诸如圆括号未匹配、分号丢失、关键字拼错等的问题都会引起编译错误。 + + + + + Produce the full name that contains the proper capitalization of the first and last names. + + + 生成一个姓名首字母大写的全名。 + + + + + Programming languages are easier for humans to understand, closer to natural languages: +) + + + 编程语言更易于人类理解,更接近自然语言: +) + + + + + Programming languages do not have to deal with hardware constraints; their main purposes are ease of use and expressiveness. + + + 编程语言不处理底层硬件约束,它们的主要目的是使用和表现上的简易舒适。 + + + + + Programs can consist of multiple threads that are being actively executed at the same time. + + + 程序可由多个同时执行操作的线程组成。 + + + + + Rather, it has to move some of the elements of the array one or more positions to the left. + + + 由此,它不得不将原数组的某些元素向左移动一个或多个位置。 + + + + + Recursion greatly simplifies certain kinds of algorithms like the ones that are classified as $(I divide-and-conquer). + + + 对于某些类型的算法(如$(I分治法)),使用递归会显得特别简单。 + + + + + Remember to read the frame contents from bottom to top: +) + + + 一定要记得从下往上来看帧的内容: +) + + + + + 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=Compiler + + + SUBTITLE=编译器 + + + + + SUBTITLE=Data Sharing Concurrency + + + SUBTITLE=数据共享与并发 + + + + + SUBTITLE=Message Passing Concurrency + + + SUBTITLE=基于消息传递的并发 + + + + + SUBTITLE=Parallelism + + + SUBTITLE=并行 + + + + + SUBTITLE=Programming in D + + + SUBTITLE=D 语言编程 + + + + + SUBTITLE=Properties + + + SUBTITLE=特性 + + + + + SUBTITLE=Strings + + + SUBTITLE=字符串 + + + + + SUBTITLE=Strings Solution + + + SUBTITLE=字符串 习题解答 + + + + + SUBTITLE=User Defined Attributes (UDA) + + + SUBTITLE=自定义属性(UDA) + + + + + Short for "read line", $(C readln()) reads until the end of the line. + + + 它是“read line”的缩写,$(C readln()) 读取到行尾。 + + + + + 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). + + + 因为 $(C reduce()) 只有一个初始值,所以每个任务都会使用相同的初始值来初始化 $(C result)(就是之前代码中我们的参数 $(C 0))。 + + + + + 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.) +) + + + 因为 $(C write) 并不会输出换行符,而为了能够在下面的程序中观察并行执行的情况,我们使用 $(C stdout.flush()) 使缓冲区中的数据能在未到达行尾时就被发送至 $(C stdout)。) +) + + + + + Since it takes one second for each element, the whole range takes ten seconds to process in this program: +) + + + 因为处理一个元素耗时 1 秒,整个程序总共花费 10 秒。 +) + + + + + 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 one call stack maintains the execution state of one task, multiple call stacks enable a thread work on multiple tasks. + + + 因为一个调用栈可以维护一个任务的执行状态,因此多个调用栈并可以实现一个线程处理多个任务。 + + + + + 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. + + + 由于同一时间只有一个执行 $(I synchronized) 代码的线程,我们就可以安全的进行交换。 + + + + + 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: +) + + + 由于交换总次数一样,我们会很自然的认为交换后的值与初始值相同,即 1 和 2: +) + + + + + 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 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. + + + 由于代表 A 和 B 的 $(C from) 对象已分别被两个线程锁住,它们将无法获取另一个线程的 $(C to) 对象(即刚刚被锁住的 B 和 A)。 + + + + + Since the operating system pauses and starts individual threads in unspecified ways, the behavior of a program that has race conditions is unpredictable. + + + 由于操作系统会无法预期地暂停和继续线程的执行,含有竞态条件的程序的行为是无法预期的。 + + + + + Since the worker has been started by $(C spawnLinked()), the owner is notified of the worker's termination by a $(C LinkTerminated) exception: +) + + + 由于工作线程是通过 $(C spawnLinked()) 启动的,它将通过向所有者线程抛出 $(C LinkTerminated) 异常以通知其工作线程已终止。 +) + + + + + Since there is no other variable for $(C variable1)'s original object, that object will be destroyed by the garbage collector. + + + 由于 $(C variable1) 的原始对象没有别的变量,该对象将由垃圾回收器销毁。 + + + + + So, the two code blocks would still be accessing the same variable concurrently: +) + + + 因此,这两块代码还是会同时访问那个变量: +) + + + + + Sometimes regular member functions feel more natural and sometimes properties. + + + 有时常规成员函数更加自然,有时特性更加简洁。 + + + + + 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. + + + 字符串是在过去三章中介绍的两种功能的组合:字符和数组。 + + + + + Such threads are said to be $(I I/O bound) at those times. + + + 这种线程被称作 $(I I/O 密集型)。 + + + + + Tasks are represented by the type $(C std.parallelism.Task). + + + 任务的表示类型为 $(C std.parallelism.Task)。 + + + + + Telling the CPU what to do is called $(I coding), and the instructions that are used when doing so are called $(I machine code). + + + 告知 CPU 要做什么事情这在过程叫做 $(I 编码),而何时该做何事的指令则叫做 $(I 机器码)。 + + + + + That is the reason why the messages that are printed by $(C aSlowOperation()) are in mixed order in the output above. + + + 这就是上面程序中 $(C aSlowOperation()) 输出的信息是乱序的原因。 + + + + + That value can be compared against the expected number of data items so that the input can be validated. + + + 该值可与数据项的预期数相比较,以便确定输入的有效性。 + + + + + The + + + 这个 + + + + + The $(C dmd) compiler switch to enable warnings as errors is $(C -w). + + + 编译器 $(C dmd) 将警告视作错误的编译选项是 $(C -w)。 + + + + + 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 length) property of a slice returns the number of elements of that slice: +) + + + 分片的 $(C length) 特性返回了其包含元素的个数: +) + + + + + 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 concept that a class type represents in a program is provided by a class object. + + + 实际的概念是,在程序中,一个类类型由一个类对象表示。 + + + + + The actual object is not copied. + + + 实际的对象没有被复制。 + + + + + The advantages of the call stack is especially clear for recursive functions. + + + 调用栈对递归函数尤其著显。 + + + + + The attributes are determined by $(C __traits(getAttributes)) at compile time and the code is compiled according to those attributes. + + + 编译时可以通过 $(C __traits(getAttributes)) 来判定属性,而相应代码则会根据那些属性来编译。 + + + + + The buffer size that produces the best performance would be different under different situations. + + + 能使程序获得最大性能的缓冲区大小会随着使用情况的不同而有所不同。 + + + + + The calculations that are performed sequentially are highlighted: +) + + + 被高亮的部分为串行运算的输出: +) + + + + + 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). + + + 调用栈隐形不仅包含当前元素是什么的信息,而且还包含了程序的执行过程是如何到达该元素的信息(例如,执行过程在处理完左节点或右节点之后,接下来会到达什么节点。) + + + + + The call to $(C core.thread.thread_joinAll) above is to make a thread wait for all of its child threads to terminate. + + + 所以,我们调用 $(C core.thread.thread_joinAll) 函数阻塞主线程来等待子线程执行完毕。 + + + + + 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 current result is carried over to the next recursion step as the second parameter: +) + + + 当前结果则通过下一个递归的第二个参数带回: +) + + + + + The default value is specified as the second parameter of $(C .get()): +) + + + 默认值被指定为 $(C .get()) 的第2个参数: +) + + + + + The default value of the number of threads is one less than the number of cores on the system. + + + 默认的线程数量比系统内核数少 1。 + + + + + The definitions of those variables were the following: +) + + + 下面各项是这些变量的定义: +) + + + + + The delegate that matches the type of the particular message handles it. + + + 如果委托参数的类型与消息类型相同,则把消息交由对应的委托处理。 + + + + + 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 execution of each thread may involve many cycles of starting and suspending. + + + 每个线程的执行都可能包含多轮启动和挂起。 + + + + + The fact that the $(C m) and $(C t) letters are printed in mixed order indicates that the operations are executed in parallel: +) + + + 字符 $(C m) 和字符 $(C t) 交替输出表明这些操作是并行执行的: +) + + + + + 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. + + + 灵活使用模块 $(C std.parallelism) 中的功能来尽可能地利用所有内核的运算能力,使程序能以更快的速度运行。 + + + + + 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 $(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 code demonstrates this for the $(C OwnerTerminated) exception: +) + + + 下面的代码演示了如何传递 $(C OwnerTerminated) 异常: +) + + + + + The following equivalents of the $(C incrementer()) and $(C decrementer()) functions that use $(C atomicOp) are correct as well. + + + 下面是$(C incrementer()) 和 $(C decrementer()) 函数使用 $(C atomicOp) 之后的等效情况。 + + + + + The following example starts a task that executes a lambda: +) + + + 下面这个例子就是让任务执行了一个 lambda 函数: +) + + + + + The following function considers the $(C Encrypted) and $(C Colored) attributes of each member when producing the output: +) + + + 下面这个函数在生成输出内容时会使用到每个成员的 $(C Encrypted) 和 $(C Colored) 属性: +) + + + + + The following message consists of three parts: +) + + + 下面这个消息就是由三个部分组成: +) + + + + + The following methods would empty the array in a single step. + + + 而下面的方法将用一步清空数组。 + + + + + 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. + + + 程序还定义了一个 $(C Exit) 类型来通知线程退出。 + + + + + The following program defines an empty class named $(C Lock) to use its objects as locks: +) + + + 下面这个程序定义了一个空的 $(C Lock) 类作为锁: +) + + + + + The following program sends the reason of the failure as a $(C CalculationFailure) message. + + + 下面这个程序就把出错的原因封装在 $(C CalculationFailure) 消息中传递回去。 + + + + + The following program uses $(C map()) to call the $(C averageGrade()) member function on each element: +) + + + 下面这个程序使用 $(C map()) 来调用每一个元素的成员函数 $(C averageGrade()): +) + + + + + The following worker handles messages by lambda functions. + + + 下面这个工作线程使用 lambda 函数处理消息。 + + + + + The function parameters that are + + + 在此处被指定的函数实参 + + + + + 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 intermediate owner thread below simply exits after sending two messages to its worker. + + + 下方程序中处在中间层的线程所有者在发送两条消息后就立即退出。 + + + + + The lengths of those arrays cannot be changed during the execution of the program. + + + 在程序的执行过程中数组的长度不可修改。 + + + + + The local state of a function is allocated and initialized automatically at run time every time that function is called. + + + 函数的本地状态会在该函数每次被调用时自动分配和初始化。 + + + + + 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. + + + 使用 $(C spawn()) 启动的线程和使用 $(LINK2 /ders/d.cn/parallelism.html, $(C task())) 启动的线程之间最大的差异在于,$(C spawn()) 允许线程间消息传递。 + + + + + The main difference between the two is that their function parameters are reversed. + + + 两者的不同点在于它们的形参顺序正好相反。 + + + + + The member variables can only be modified by the $(C Rectangle) type itself to ensure the consistency of its objects. + + + 只有 $(C Rectangle) 的成员函数能修改成员变量可以保证对象的一致性。 + + + + + The movement of each robot is handled by a separate thread that takes three pieces of information when started: +) + + + 每个机器人的移动都是由一个独立的线程控制的。线程在启动时需要传入三个参数: +) + + + + + The number of elements of an array is called the $(I length) of the array. + + + 数组元素的个数称为数组的 $(I length)。 + + + + + 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. + + + 邮箱中的消息个数会随着程序接收和处理消息的速度而有所变化。 + + + + + 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). + + + 唯一的不同之处在于,不是通过 $(C return) 返回一个元素,而是通过 $(C yield()) 返回多个的元素(要本示例里是$(I无限多个元素) )。 + + + + + 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. + + + 唯一不同的是 $(C taskPool.map()) 是以半延迟取值的方式调用函数操作元素,并将结果储存在缓冲区中。 + + + + + The only difference is that the number of values associated with the variable is specified in square brackets. + + + 唯一的区别是,与变量相关联的值的个数在方括号中指定。 + + + + + The operating system decides when and under what condition to start and suspend each thread. + + + 操作系统将会决定在合适何种情况启动或挂起线程。 + + + + + The operating system starts and executes each thread on a core and then suspends it to execute other threads. + + + 操作系统可在一个核心上启动和执行线程,在需要时将其挂起来让出核心运算资源去执行另一个线程。 + + + + + The other differences outlined below are mostly due to this fact. + + + 下面的其它不同大部分与此有关。 + + + + + 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: +) + + + 这个程序的输出表明有两个线程:一个用于运行 $(C main()),另一个则由 $(C spawn()) 启动。它们同时相互独立地执行: +) + + + + + 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. + + + 如果处于忙碌中的工作线程的数量超过系统内核的数量,那么系统的整体性能有可能下降。 + + + + + The owner can use the exception object or simply rethrow it: +) + + + 所有者既可以处理异常对象也可以重新抛出: +) + + + + + The owner simply prints every movement: +) + + + 所有者会简单地将其运动情况输出: +) + + + + + The program above would require a single change: replacing 5 with 20. + + + 修改一下上面的程序:用 20 替换 5。 + + + + + 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: +) + + + 程序会将同一个变量的地址传递给这两个函数:一个函数将其加 1,一个函数将其减 1,加减次数相同: +) + + + + + The program can safely continue after the $(C foreach) loop. + + + 在 $(C foreach) 循环完成后,程序可以安全地继续执行下面的代码。 + + + + + The program is free to start other threads to be able to work on multiple tasks at the same time. + + + 程序也可以自由地创建线程以实现在同一时间执行多个任务的功能。 + + + + + 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 program would take less time if other elements could be produced while the front element is in use: +) + + + 如果能在上一个元素被使用时就开始计算下一个元素,那程序消耗的时间就会大大减小。 +) + + + + + The program would work correctly with the following change: +) + + + 经过下面的修改之后程序能正确执行: +) + + + + + The programming model that supports threads that depend on other threads is called $(I concurrency). + + + 支持线程依赖的编程模型叫做$(I 并发(concurrency))。 + + + + + The reason why it is surprising is that for such data structures, the same algorithms are trivial when implemented recursively. + + + 让人惊讶的是,对于这种数据结构,同样的算法在递归实现时并非什么难事。 + + + + + The recursion continues until the slice becomes empty. + + + 这个递归会一直持续到接收到的分片为空才会结束。 + + + + + The recursive calls are highlighted. + + + 其中的递归调用是亮点。 + + + + + The result is what has been + + + 此时,结果已被 + + + + + The results of individual functions correspond to the elements of the tuple in the order that the functions are specified. + + + 传入函数的顺序将决定 tuple 中与之对应的结果的顺序。 + + + + + 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 sender may have been busy temporarily or may have terminated with an exception. + + + 消息的发送者可能正在忙碌或因异常终止。 + + + + + The size of this buffer is determined by the second parameter. + + + 缓冲区的大小由第二个形参决定。 + + + + + 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 stack of frames of currently active function calls is the $(I call stack) of that thread. + + + 当前正激活的函数调用的帧构成的栈即为该线程的$(I调用栈)。 + + + + + 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. + + + 在同步块执行后 $(C i) 与 $(C j) 只会有种情况:要么是“1 and 2”,要么是“2 and 1”。 + + + + + The template parameter must be a $(I binary operator) like $(STRING "+"), $(STRING "+="), etc. +) + + + 它的模版参数必须是一个$(I 二元运算符),如 $(STRING "+")、$(STRING "+=") 等。 +) + + + + + The third parameter is the work unit size as in $(C parallel()); the difference being its default value, which is $(C size_t.max): +) + + + 第三个参数为工作单元大小,与 $(C parallel()) 中对应的参数作用相同但默认值不同;此处它的默认值为 $(C size_t.max): +) + + + + + The thread may be paused at any point between these steps to be continued after an unpredictable time. + + + 线程可能会暂停在这三步中的任何一步上,停顿一段时间后才会继续。 + + + + + The threads continue sending messages back and forth until the owner sends a negative $(C int). + + + 工作线程会不停地返回消息,一直到所有者线程发送一个 $(C int) 型的负数为止。 + + + + + The threads in the following program receive the addresses as two variables and swap their values a large number of times: +) + + + 这些线程将会收到两个变量的地址,并将它们的值对调。对调过程将执行多次: +) + + + + + The two variables start providing access to the same object. + + + 这两个变量可访问同一个对象。 + + + + + The two worker threads in the following program print four numbers each. + + + 下面程序中的两个工作线程分别输出四个数字。 + + + + + The value of each parameter is indicated after an $(C ==) sign. + + + 每个参数的值会在 $(C ==) 符号之后标明。 + + + + + The value of the member is encrypted if it has that attribute. + + + 如果该成员拥有此属性,则它的值会被加密。 + + + + + The value that is used on the right-hand side of the assignment becomes the only parameter of this function. + + + 等号右侧的值将作为函数唯一的参数传入函数。 + + + + + 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。 + + + + + The work unit size determines the number of elements that each thread should execute at each of its iterations: +) + + + 工作单元大小决定了每个线程在每次迭代时应该执行的元素个数。 +) + + + + + The worker in the following program sends messages back to back but the owner spends some time for each message: +) + + + 下面这个工作线程会不停地向主线程发送消息,但主线程处理消息的速度就没有工作线程这么快了,每条消息主线程都会花费一点时间来处理: +) + + + + + The worker thread above can be tested by the following program: +) + + + 上面的工作线程可以用下面程序来测试: +) + + + + + Their curly brackets are highlighted: +) + + + 它们的花括号已被高亮: +) + + + + + There are four choices for the last parameter: +) + + + 最后一个参数有四个选项: +) + + + + + There would be a compilation error without that free-standing function: +)) + + + 如果不使用独立的函数的话,程序将不能通过编译: +)) + + + + + These algorithms should be used only when the operations that are to be executed $(I in parallel) are truly independent from each other. + + + 只有当元素操作之间没有相互依赖时,它们才能被 $(I 并行) 执行。 + + + + + These final calculations are executed sequentially, not in parallel. + + + 这个最终的计算是顺序执行的,而非并行。 + + + + + These languages have traditionally been used with an interpreter. + + + 因此,这些语言传统上就需使用解释器。 + + + + + These limits can be set for each user, for the whole system, or for something else. + + + 这种限制可能是对用户的,也可能是对整个操作系统的,当然也可能是对其他某些级别。 + + + + + These machine code instructions are determined under hardware constraints during the design stage of the architecture. + + + 而这些机器码指令取决于底层架构设计阶段时的硬件约束。 + + + + + These messages are handled before the other messages that are already in the mailbox: +) + + + 这些高优先级消息会比其他邮箱中的消息先被处理: +) + + + + + These threads continuously send messages to each other until instructed to terminate by an $(C Exit) message: +) + + + 线程会不停地互相发送信息,只有在收到 $(C Exit) 消息后它们才会终止: +) + + + + + These three functions are explained further in the comments of the following program. + + + 在下方程序的注释中有对这三个函数详细的解释。 + + + + + They are defined by the $(C @property) attribute. + + + 所需的额外工作就是在函数的前面加一个 $(C @property)。 + + + + + They are instead stored in an output buffer until a line of output is completed. + + + 它们将会被储存在输出缓冲区中,一直到整行输出完成为止都会显示。 + + + + + They are very fast data structures that work like mini databases and are used in many programs. + + + 它们是高速的数据结构,如迷你数据库一样地运作,在很多程序里都用到了。 + + + + + They can execute different parts of different programs at the same time. + + + 它们可同时执行不同程序的不同部分。 + + + + + They grow automatically as key-value pairs are added. + + + 它们随着键值对的添加而自动增长。 + + + + + They map the values of one type to the values of another type. + + + 它们映射一种类型的值到另一种类型的值上。 + + + + + They take the starting number as the thread function parameter: +) + + + 线程函数的参数为初始数字: +) + + + + + This allows the owner to catch such exceptions: +) + + + 它使得所有者线程可以捕获工作线程的异常: +) + + + + + This array is used to hold the number + + + 这个数组用于存储 + + + + + This behavior cannot be changed. + + + 该行为不能被修改。 + + + + + This can be demonstrated by printing the entire table: +) + + + 这可以通过打印整个表来验证: +) + + + + + This can be done by defining an $(C opSlice()) member function: +) + + + 通过定义一个 $(C opSlice()) 成员函数可以达到这一目的功能: +) + + + + + This causes an $(C OwnerTerminated) exception to be thrown at the worker thread: +) + + + 这会导致工作线程抛出 $(C OwnerTerminated) 异常: +) + + + + + This cost may sometimes be significant especially when the operations of the loop are completed in a very short time. + + + 但在某些情况下这种开销会显得极其昂贵,尤其是在每次循环的操作耗时都非常短的时候。 + + + + + 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 distinction may be undesirable in certain situations where seemingly $(I equivalent) task objects would actually have different types. + + + 两个看起来 $(I 相同) 的任务对象实际上不是同一个类型,在某些特定的情况下这种差别可能是我们不想看到的。 + + + + + This fact can be observed by printing both the values and the addresses of the variables: +) + + + 从这些变量的值和地址输出可以看出来: +) + + + + + 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 an important difference that affects the way attributes are used at compile time. + + + 它们有着明显的差异,它会对编译时属性的使用方式产生影响。 + + + + + This is because garbage collectors are not required to guarantee that the object and its members are finalized in any specific order. + + + 这是因为垃圾回收器没有被要求保证该对象及其成员按任何特定顺序终结。 + + + + + This is because garbage collectors are not required to guarantee that they can allocate new objects during a garbage collection cycle.) + + + 这是因为垃圾回收器没有被要求保证在垃圾回收周期内能分配新的对象。) + + + + + This is because later code may depend on previous variables. + + + 这是因为后边的代码依赖于前边的变量。 + + + + + This is necessary for it to know what to do when its $(C popFront()) is called next time. + + + 它需要知道当下次调用 $(C popFront()) 时应该做什么。 + + + + + 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 is the owner thread: +) + + + 下面是所有者线程: +) + + + + + This makes it possible for the main thread to catch exceptions that are thrown by a task. + + + 如此一来,主线程便可以捕获任务中抛出的各种异常。 + + + + + This may be seen as a limitation for some applications. + + + 对某些程序来说可能是个限制。 + + + + + This means that all of the non-static member functions of that type are synchronized on a given object of that class: +) + + + 即表示:当给定了该类的某个对象时,它的所有非静态成员函数的类型都是 synchronized: +) + + + + + This ordering takes each character's Unicode code to be its place in a hypothetical grand Unicode alphabet. + + + 这种排序需要在一个假设的大字母表中让每个字符的 Unicode 编码找到它的位置。 + + + + + This produced program is responsible for the execution of the instructions that were written by the programmer. + + + 而后,这个被生成的程序负责执行程序员写就的指令。 + + + + + This range simply produces integers up to the specified limit: +) + + + 这个范围的作用就是提供一个不高于指定上限的整数。 +) + + + + + This result will be used later in main. + + + 稍后会在 main 函数中使用这个结果。 + + + + + This situation is called a $(I deadlock). + + + 这个现象就是$(I 死锁)。 + + + + + This type is used in + + + 此类型只适用于下面的 + + + + + This undeterministic order of thread execution may not matter if the operations of the $(C Student) objects are truly independent from each other. + + + 如果对 $(C Student) 对象的操作是相互独立的,那以一种不确定的顺序执行线程也不会有什么副作用。 + + + + + This variation can confuse new programmers. + + + 这种变化可能会让编程新手感到困惑。 + + + + + Thread.sleep(500.msecs); /* half a second */ + + + Thread.sleep(500.msecs); /* 半秒 */ + + + + + 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. + + + 要修改长度,就必须修改源代码,而且程序必须重新编译。 + + + + + To demonstrate how parallel algorithms are faster, let's again slow this function down with $(C Thread.sleep()). + + + 为了展示并行算法到底有多快,我们需要再次使用 $(C Thread.sleep()) 将这个函数变慢。 + + + + + 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. + + + 为了能够提供这样的功能,我们假设矩形的边长是$(I 可变的)。如此一来,为保持恒等式“长 × 宽 == 面积”成立,可以更改该矩形的各条边。 + + + + + To identify those separate variables, they are renamed as $(C tempA) and $(C tempB) below. + + + 为了区分这两个 $(C temp),我们将其分别称为 $(C tempA) 和 $(C tempB)。 + + + + + To prevent that, the owner may limit the size of its mailbox before starting the worker: +) + + + 为了防止出现这种情况,所有者线程会在启动工作线程之前限制其邮箱的大小: +) + + + + + To reduce this complexity, it is possible to assign names to threads, which are globally accessible from any thread. + + + 为了降低复杂度,可以为线程分配一个命名——所有线程都可以通过名字访问该线程。 + + + + + Top of the call stack → ┌──────────────┐ + + + 调用栈的顶部 → ┌──────────────┐ + + + + + Top of the call stack → ├──────────────┤ + + + 调用栈的顶部 → ├──────────────┤ + + + + + 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;}) 语法构造。 + + + + + User defined attributes is purely a compile-time feature. + + + 自定义属性完全是一项编译时功能。 + + + + + Using an array in a way that would require allocating a new memory block for the elements is undefined behavior as well: +) + + + 用这种方式使用一个数组,那将需要为未定义行为的元素分配一个新的内存块: +) + + + + + Using such a specific type is more expressive than sending the arbitrary value of -1 like it was done in the previous example. + + + 相对于使用像 -1 这样的任意值,用一个特定的类型来传递特定的消息会让程序更易读。 + + + + + Variables which only store a single value are called scalar variables. + + + 只存储一个值的变量称为标量变量。 + + + + + We can contrast the two definitions as follows: +) + + + 我们可以对比两种定义如下: +) + + + + + 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." +) + + + 从上面的输出中我们可以看到,在函数 $(C main()) 输出 “main is done.” 并退出之后,$(C worker()) 仍然在继续执行。 +) + + + + + We have already seen this above when assigning to an element of an array: +) + + + 这在上面给数组的元素赋值时我们已经看到: +) + + + + + We have seen the $(C Tuple) type in the $(LINK2 /ders/d.en/tuples.html, Tuples chapter) before. + + + 我们之前已经在 $(LINK2 /ders/d.cn/tuples.html, Tuples) 一章中了解过 $(C Tuple)。 + + + + + We will + + + 在下一章我们将 + + + + + We will cover message passing in this chapter and data sharing in the next chapter. + + + 本章主要讲解消息传递,而数据共享则被放置到下一章。 + + + + + We will cover the $(C pure) keyword in $(LINK2 /ders/d.en/functions_more.html, a later chapter). + + + 我们会在$(LINK2 /ders/d.cn/functions_more.html, 后面章节)对关键字 $(C pure) 进行讲解。 + + + + + We will cover these concepts in later chapters.) +) + + + 在稍后的章节中我们将涵盖这些概念。) +) + + + + + We will implement a tree range later below. + + + 下面我们来实现一个树范围. + + + + + We will see Phobos ranges in a later chapter. + + + 在稍后的一章中我们将看到 Phobos 的 range。 + + + + + We will see an example of this difference below. + + + 下面来看一个与该差异有关的例子。 + + + + + We will see examples of these later below. + + + 在后面会看到有关它们的示例。 + + + + + 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 only two of its functions in this chapter: +) + + + 本章我们只会接触到其中两种: +) + + + + + We will see this below. + + + 随后就会看到这个。 + + + + + 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) 关联数组。 + + + + + What should the implementation be? + + + 具体的实现应该是什么呢? + + + + + When a message arrives, it is compared to the message type of each delegate. + + + 当接收到消息时,它会将消息类型与每个委托的类型进行比较。 + + + + + When a worker that has been started by $(C spawnLinked()) terminates, a $(C LinkTerminated) exception is thrown at the owner: +) + + + 当由 $(C spawnLinked()) 创建的线程终止时,拥有者线程将会抛出 $(C LinkTerminated) 异常。 +) + + + + + 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 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. + + + 如果长和宽都是面积的平方根,那它们的乘积就一定是我们指定的面积。 + + + + + When defining arrays, the $(C []) characters are written after the type of the elements and specify the number of elements. + + + 当我们定义数组时,$(C []) 写在元素类型之后,并指定元素个数。 + + + + + When no initial value is specified, the first element of the range is used instead. + + + 如果没有指定初始值,它会把范围的第一个元素作为初始值。 + + + + + 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 that is the case, the microprocessor would execute operations like the increments above in parallel. + + + 当出现这种情况时,微处理器会并行处理上面那样的自增操作。 + + + + + When the length can change during the execution of the program, that array is a $(I dynamic array). + + + 当长度可以在程序的执行过程中进行修改时,该数组就是一个$(I 动态数组)。 + + + + + Whether $(C i) or $(C j) is modified at each step is indicated by highlighting that variable: +) + + + 按照从上到下的顺序,操作 1 第一个执行,操作 6 最后一个执行。高亮的步骤修改了 $(C i) 和 $(C j): +) + + + + + While $(C map()) generates a new result for each element, $(C each()) generates side effects for each element. + + + 由于 $(C map()) 会为每个元素生成一个新的结果,因此 $(C each()) 也会为每个元素生成新值。 + + + + + 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 个元素 +--- + + + + + atomicOp!"+="(*value, 1); // atomic +--- + + + atomicOp!"+="(*value, 1); // 原子的 +--- + + + + + auto arr = [ 1 ]; // ← WRONG: Allocates indirectly + + + auto arr = [ 1 ]; // ← 错误:在类的析构函数里 + + + + + auto c = new C(); // ← WRONG: Allocates explicitly + + + auto c = new C(); // ← 错误:在类的析构函数里 + + + + + auto floating = message[2]; // of type double +--- + + + auto floating = message[2]; // double 类型 +--- + + + + + auto integer = message[1]; // of type int + + + auto integer = message[1]; // int 类型 + + + + + auto m = receiveOnly!int(); // ← An exception is + + + auto m = receiveOnly!int(); // ← 如果 + + + + + auto message = receiveOnly!double(); $(CODE_NOTE Expecting $(HILITE double)) +} +--- + + + auto message = receiveOnly!double(); $(CODE_NOTE 期望 $(HILITE double)) +} +--- + + + + + auto sender = message[0]; // of type Tid + + + auto sender = message[0]; // Tid 类型 + + + + + break; + + + break; + + + + + calculator.send("hello"); // ← incorrect input + + + calculator.send("hello"); // ← 错误的输入 + + + + + current = 0; // Note that 'current' is the parameter + + + current = 0; // 请注意,'current' 即为该参数 + + + + + 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; // 初始时为空 + + + + + nextNode(node.left); // First, elements on the left + + + nextNode(node.left); // 首先,是左边的所有元素 + + + + + nextNode(node.right); // Finally, elements on the right +} + + + nextNode(node.right); // 最后,是左边的所有元素 +} + + + + + number = int.init; // 0 for int +--- +) + + + number = int.init; // int 值:0 +--- +) + + + + + ownerTid.send("hello"); $(CODE_NOTE Sending $(HILITE string)) +} + + + ownerTid.send("hello"); $(CODE_NOTE 发送 $(HILITE string)) +} + + + + + ownerTid.send(20); +} // ← Terminates after sending two messages +--- + + + ownerTid.send(20); +} // ← 发送两条消息后立刻终止 +--- + + + + + ownerTid.send(42); // ← Produces messages continuously + + + ownerTid.send(42); // ← 持续产生消息 + + + + + real 0m1.005s $(SHELL_NOTE now only 1 second) +user 0m0.004s +sys 0m0.004s) +) + + + real 0m1.005s $(SHELL_NOTE 现在只需 1 秒) +user 0m0.004s +sys 0m0.004s) +) + + + + + real 0m10.006s $(SHELL_NOTE 10 seconds total) +user 0m0.000s +sys 0m0.004s) +) + + + real 0m10.006s $(SHELL_NOTE 共 10 秒) +user 0m0.000s +sys 0m0.004s) +) + + + + + real 0m10.007s $(SHELL_NOTE 10 seconds total) +user 0m0.004s +sys 0m0.000s) +) + + + real 0m10.007s $(SHELL_NOTE 共 10 秒) +user 0m0.004s +sys 0m0.000s) +) + + + + + real 0m3.005s $(SHELL_NOTE 3 seconds total) +user 0m0.000s +sys 0m0.004s) +) + + + real 0m3.005s $(SHELL_NOTE 共 3 秒) +user 0m0.000s +sys 0m0.004s) +) + + + + + real 0m4.003s $(SHELL_NOTE 4 seconds total) +user 0m0.000s +sys 0m0.000s) +) + + + real 0m4.003s $(SHELL_NOTE 共 4 秒) +user 0m0.000s +sys 0m0.000s) +) + + + + + real 0m4.005s $(SHELL_NOTE 4 seconds total) +user 0m0.004s +sys 0m0.000s +) +) + + + real 0m4.005s $(SHELL_NOTE 共 4 秒) +user 0m0.004s +sys 0m0.000s +) +) + + + + + real 0m4.007s $(SHELL_NOTE 4 seconds total) +user 0m0.000s +sys 0m0.004s) +) + + + real 0m4.007s $(SHELL_NOTE 共 4 秒) +user 0m0.000s +sys 0m0.004s) +) + + + + + real 0m5.006s $(SHELL_NOTE parallel reduce is slower in this example) +user 0m0.004s +sys 0m0.000s) +) + + + real 0m5.006s $(SHELL_NOTE 在本例中并行 reduce 更慢一点) +user 0m0.004s +sys 0m0.000s) +) + + + + + real 0m6.007s $(SHELL_NOTE now 6 seconds) +user 0m0.000s +sys 0m0.004s) +) + + + real 0m6.007s $(SHELL_NOTE 现在耗时 6 秒) +user 0m0.000s +sys 0m0.004s) +) + + + + + return; + + + return; + + + + + slice$(HILITE .length = 5); // The slice now has 5 elements + + + slice$(HILITE .length = 5); // slice现在有 5 个元素 + + + + + spawn(&worker, &i); // ← compiles + + + spawn(&worker, &i); // ← 编译正常 + + + + + 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é”一样 + + + + + synchronized (from) { $(CODE_NOTE_WRONG INCORRECT) + + + synchronized (from) { $(CODE_NOTE_WRONG 错误) + + + + + synchronized (from, to) { $(CODE_NOTE correct) + + + synchronized (from, to) { $(CODE_NOTE 正确) + + + + + task($(HILITE &)bar, 2) ]; $(CODE_NOTE compiles) +} +--- + + + task($(HILITE &)bar, 2) ]; $(CODE_NOTE 编译通过) +} +--- + + + + + variable + + + 变数 + + + + + worker.send(-1); // ← to terminate the worker +} +--- + + + worker.send(-1); // ← 终止工作线程 +} +--- + + + + + worker.send(2); +} // ← Terminates after sending two messages + + + worker.send(2); +} // ← 发送两条消息后立刻终止 + + + + + write(' '); // ← Call stack implies this is next + + + write(' '); // ← 调用栈表明这就是下一步 + + + + + 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 +} +--- + + + + + writeln; // Same as the previous line +--- + + + writeln; // 与上一行相同 +--- + + + + + yieldForce() waits for the task to complete its + + + yieldForce() 会等待该任务完成 + + + + + {} 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, 运算符优先级)) +) + + + + + {} 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/static_foreach.html, static foreach)) +$(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/static_foreach.html, static foreach)) +$(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, 运算符优先级)) +) + + + + + │ ... │ ← main's frame + + + │ ... │ ← 主函数的帧 + + + + + │ arr == [1, 2, 3] │ ← first call to sum + + + │ arr == [1, 2, 3] │ ← 第一次调用 sum + + + + + │ arr == [2, 3] │ ← second call to sum + + + │ arr == [2, 3] │ ← 第二次调用 sum + + + + + │ arr == [3] │ ← third call to sum + + + │ arr == [3] │ ← 第三次调用 sum + + + + + │ arr == [] │ ← final call to sum + + + │ arr == [] │ ← 最后一次调用 sum + + + + + │ int b │ ← foo's frame + + + │ int b │ ← foo 的帧 + + + + + │ int b │ ← main's frame + + + │ int b │ ← main 的帧 + + + + + │ int c │ +Bottom of the call stack → └──────────────┘ +) + + + │ int c │ +调用栈的底部 → └──────────────┘ +) + + + + + │ int param │ ← bar's frame + + + │ int param │ ← bar 的帧 + + + + + │ int y │ ← foo's frame + + + │ int y │ ← foo 的帧 + + + + + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: +) + + + 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它: +) + + + + + diff --git a/ddili/src/ders/d.cn/aa.cozum.d b/target/aa.cozum.d similarity index 84% rename from ddili/src/ders/d.cn/aa.cozum.d rename to target/aa.cozum.d index ddd01df..deebfdb 100644 --- a/ddili/src/ders/d.cn/aa.cozum.d +++ b/target/aa.cozum.d @@ -9,7 +9,7 @@ $(LI $(UL $(LI -$(C .keys) property(属性)返回一个切片(即 动态数组),它包含了关联数组全部的键。迭代这个切片,通过为每个键调用 $(C .remove) 函数来移除元素,就会形成一个空的关联数组: +特性 $(C .keys) 返回的是一个分片(即动态数组),其中包含关联数组的所有键。采用迭代此分片,并对每个键调用 $(C .remove) 函数来移除元素的方法,最后会得到一个空的关联数组: --- import std.stdio; @@ -67,7 +67,7 @@ $(LI ) $(LI -目标是存储每个学生的多个成绩。由于多个成绩可以用一个动态数组存储,从 $(C string) 到 $(C int[]) 映射的关联数组将能用在这儿。成绩能附加到存储在关联数组中的动态数组上: +目标是存储每个学生的多个成绩。由于多个成绩可以用一个动态数组存储,从 $(C string) 到 $(C int[]) 映射的关联数组将能用在此处。成绩能附加到存储在关联数组中的动态数组上: --- import std.stdio; @@ -90,7 +90,7 @@ void main() { --- $(P -成绩也可以作为数组字面量一次性赋值: +成绩也可以作为数组文本一次性赋值: ) --- diff --git a/ddili/src/ders/d.cn/aa.d b/target/aa.d similarity index 95% rename from ddili/src/ders/d.cn/aa.d rename to target/aa.d index ccd0602..9fc58d4 100644 --- a/ddili/src/ders/d.cn/aa.d +++ b/target/aa.d @@ -7,7 +7,7 @@ $(P ) $(P -在 $(LINK2 /ders/d.cn/arrays.html, 数组) 一章中我们已经看到简单数组作为容器逐个存储元素,按索引访问它们。一个存储了星期的英文名称的数组可以这样定义: +在 $(LINK2 /ders/d.cn/arrays.html, 数组) 一章中我们已经看到简单数组作为容器逐个存储元素,按索引访问它们。一个存储一周当中每天的英文名称的数组可以定义下面这样: ) --- @@ -122,7 +122,7 @@ $(P --- $(P -The output: +输出: ) $(SHELL @@ -146,7 +146,7 @@ $(IX :, 关联数组) 有时候一些键与值的映射在定义关联数组的 writeln(dayNumbers["Tuesday"]); // 打印 1 --- -$(H5 $(IX remove) 移除键值对) +$(H5 删除键-值对) $(P 通过 $(C .remove()) 可以移除键值对: @@ -169,7 +169,7 @@ $(C .clear) 移除全部元素: dayNumbers.clear; // 清空关联数组 --- -$(H5 $(IX in, 关联数组) 确定键的存在) +$(H5 $(IX in, 关联数组) 确定某个键是否存在) $(P $(C in) 运算符确定一个给定的键是否存在于关联数组中: @@ -182,7 +182,7 @@ $(C in) 运算符确定一个给定的键是否存在于关联数组中: // 键 “purple” 在表中 } else { - //表中不存在 键 “purple” + // 这张表里不存在 “purple” 键 } --- @@ -203,7 +203,7 @@ $(SHELL -1 ) -$(H5 Properties(属性)) +$(H5 特性) $(UL @@ -225,7 +225,7 @@ $(LI $(IX .sizeof, associative array) $(C .sizeof) 数组$(I 引用)大小(它 $(LI $(IX .get) $(C .get) 值存在即返回相应值,否则返回默认值。) -$(LI $(IX .remove) $(C .remove) 从数组中移除指定的键和值。) +$(LI $(IX .remove, associative array) $(C .remove) 从数组里删除指定的键及其值。) $(LI $(IX .clear) $(C .clear) 移除全部元素。) @@ -267,7 +267,7 @@ void main() { $(PROBLEM_COK $(PROBLEM -除 $(C .clear) 函数外还有什么方法能移除关联数组的全部键值对? ($(C .clear) 是最自然的方法。) 至少还有三种方法: +除 $(C .clear) 函数外还有什么方法能移除关联数组的全部键值对? ($(C .clear) 是最自然的方法。) 至少有三种方法: $(UL diff --git a/target/alias.d b/target/alias.d new file mode 100644 index 0000000..0985e45 --- /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 摘要) + +$(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/ddili/src/ders/d.en/alias_this.d b/target/alias_this.d similarity index 87% rename from ddili/src/ders/d.en/alias_this.d rename to target/alias_this.d index 26965b1..24559c4 100644 --- a/ddili/src/ders/d.en/alias_this.d +++ b/target/alias_this.d @@ -7,7 +7,7 @@ We have seen the individual meanings of the $(C alias) and the $(C this) keyword ) $(P -$(IX automatic type conversion) $(IX type conversion, automatic) $(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. +$(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 @@ -75,7 +75,7 @@ $(C alias this) enables using D classes in designs that could benefit from multi ) $(P -$(HILITE $(I $(B Note:) dmd 2.071, the compiler that was used last to compile the examples in this chapter, allowed only one $(C alias this) declaration.)) +$(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 @@ -116,7 +116,7 @@ class TeachingAssistant { /* The following two 'alias this' declarations will enable * this type to be used both as a Student and as a Teacher. * - * Note: dmd 2.071 did not support multiple 'alias this' + * 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; diff --git a/ddili/src/ders/d.en/arithmetic.cozum.d b/target/arithmetic.cozum.d similarity index 100% rename from ddili/src/ders/d.en/arithmetic.cozum.d rename to target/arithmetic.cozum.d diff --git a/ddili/src/ders/d.en/arithmetic.d b/target/arithmetic.d similarity index 98% rename from ddili/src/ders/d.en/arithmetic.d rename to target/arithmetic.d index bd22d23..3fe00de 100644 --- a/ddili/src/ders/d.en/arithmetic.d +++ b/target/arithmetic.d @@ -98,7 +98,7 @@ 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 have seen in the $(LINK2 /ders/d.en/types.html, Fundamental Types chapter) are the following: +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: ) @@ -710,6 +710,10 @@ For example, assuming that $(C a) and $(C b) are two $(C int) variables, the fol } --- +$(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 diff --git a/target/arrays.cozum.d b/target/arrays.cozum.d new file mode 100644 index 0000000..c84a1cd --- /dev/null +++ b/target/arrays.cozum.d @@ -0,0 +1,133 @@ +Ddoc + +$(COZUM_BOLUMU 数组) + +$(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; + + // 计数器通常命名作‘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 +解释包含在代码说明中: + +--- +import std.stdio; +import std.algorithm; + +void main() { + // 使用动态数组的原因是不知道有多少 + // 值要从输入流中读取 + int[] odds; + int[] evens; + + writeln("Please enter integers (-1 to terminate):"); + + while (true) { + + // 从输入流中读取值 + int value; + readf(" %s", &value); + + // 特殊值 -1 中断循环 + if (value == -1) { + break; + } + + // 根据值的奇偶性,把值添加到 + //相应的数组。如果值被 2 + // 除时没有余数,那这个数就是偶数。 + if ((value % 2) == 0) { + evens ~= value; + + } else { + odds ~= value; + } + } + + // 分别排序奇偶数的数组 + sort(odds); + sort(evens); + + // 连接两个数组从而形成一个新数组 + int[] result; + result = odds ~ evens; + + writeln("First the odds then the evens, sorted:"); + + // 在循环中输出数组元素 + int i; + while (i < result.length) { + write(result[i], " "); + ++i; + } + + writeln(); +} +--- + +) + +$(LI +程序有三个错误(bug)。前两个与 $(C while) 循环有关: 循环条件都使用 $(C <=) 运算符而不使用 $(C <) 运算符。结果是,程序使用无效的索引,试图访问不在数组中的元素。 + +$(P +自行调试第三个错误对你来说更有益,建议你在修复了前两个 bug 之后先运行一下程序。你将注意到程序不会输出结果。在没有读下面这段话之前你能指出剩下的问题吗? +) + +$(P +当第一个 $(C while) 循环结束时 $(C i) 值为 5,该值导致第二个循环的逻辑表达式的值为 $(C false),是它阻止进入第二个循环。 解决办法就是在第二个 $(C while) 循环前重设 $(C i) 为 0,比如使用语句 $(C i = 0;) +) + +) + +) + +Macros: + SUBTITLE=数组习题解答 + + DESCRIPTION=D 语言编程习题解答:数组 + + KEYWORDS=D 语言编程教程 数组 习题解答 diff --git a/target/arrays.d b/target/arrays.d new file mode 100644 index 0000000..a80f4cd --- /dev/null +++ b/target/arrays.d @@ -0,0 +1,602 @@ +Ddoc + +$(DERS_BOLUMU $(IX array) 数组) + +$(P +在上一章的一个练习中我们定义过五个变量,并用它们做过特定计算。下面各项是这些变量的定义: +) + +--- + double value_1; + double value_2; + double value_3; + double value_4; + double value_5; +--- + +$(P +这种定义个别变量的方法不能扩展到需要更多变量的情况下。想象一下,需要一千个值;定义从 $(C value_1) 到 $(C value_1000) 一千个变量,这几乎是不可能的。 +) + +$(P +数组在这种情况下是有用的:数组功能允许我们定义一个把多个值存储到一起的单个变量。虽然简单,但数组是用于存储值的集合中最常见的数据结构。 +) + +$(P +本章仅涉及数组的部分功能。更多功能将在后面 $(LINK2 /ders/d.cn/slices.html, 分片和其他数组功能一章) 介绍。 +) + +$(H5 定义) + +$(P +数组变量的定义与正常变量的定义非常相似。唯一的区别是,与变量相关联的值的个数在方括号中指定。我们可以对比两种定义如下: +) + +--- + int singleValue; + int[10] arrayOfTenValues; +--- + +$(P +上面的第一行定义了一个存储单个值的变量,就像我们以前定义过的变量那样。第二行定义了一个存储连续十个值的变量。换句话说,它是一个可存储十个整数值的数组。你也可以把它定义为同类型的十个变量,或作为数组定义的简称。 +) + +$(P +因此,上面的五个独立的变量可以等效的使用下面的语法定义为含五个值的数组: +) + +--- + double[5] values; +--- + +$(P +$(IX 标量) 这个定义可以理解为 $(I 5 个 double 值)。请注意,我选择了数组变量的名字为复数,以避免它与单值变量混淆。只存储一个值的变量称为标量变量。 +) + +$(P +总之,数组变量的定义包括值的类型、 值的个数和涉及数组值的变量名称: +) + +--- + $(I 类型名称)[$(I 值的个数)] $(I 变量名称); +--- + +$(P +值的类型也可以是用户定义的类型。(我们将在后面看到用户定义类型。)例如: +) + +--- + // 保存所有城市气象信息的 + // 数组。在这里,bool 值可能意味着 + // false:阴天 + // true :晴天 + bool[cityCount] weatherConditions; + + // 保存一百个箱子重量的数组 + double[100] boxWeights; + + // 有关学校学生的信息 + StudentInformation[studentCount] studentInformation; +--- + +$(H5 $(IX container) $(IX element) 容器和元素) + +$(P +聚集特定类型元素的数据结构称为 $(I 容器)。根据定义,数组就是容器。例如,一个保存了七月每天的气温的数组能汇集 31 个 double 值,形成一个 $(I $(C double) 类型元素的容器)。 +) + +$(P +容器的变量称为 $(I 元素)。数组元素的个数称为数组的 $(I length)。 +) + +$(H5 $(IX []) 元素的访问) + +$(P +为了与前一章练习中的变量有所区别,我们给变量的名字加上了下划线与数字,像 $(C value_1) 这样。让一个数组在一个名字下存储所有的值,那是不可能也没必要的。相反,通过指定方括号内的元素位置数就可以访问元素: +) + +--- + values[0] +--- + +$(P +这个表达式可以理解为 $(I 数组 values 位置 0 处的元素)。换句话说,对数组输入 $(C values[0]) 而不是键入 $(C value_1) 。 +) + +$(P +还有两点值得强调: +) + +$(UL + +$(LI $(B 从零开始编号:) 虽然人们习惯于从 1 开始给项目分配编号,但数组是从 0 开始的。以前被我们编号为 1、2、3、4 和 5 的值在数组中编号为 0、1、2、3 和 4。这种变化可能会让编程新手感到困惑。 +) + +$(LI $(B$(C[]) 的两种不同用法:) 不要混淆 $(C []) 的两种独特用法。当我们定义数组时,$(C []) 写在元素类型之后,并指定元素个数。访问元素时,$(C []) 写在数组名称之后,并指定要访问的元素位置数: + +--- + // 这个是数组的定义。它定义了一个可以存储 + // 12个元素的数组。这个数组用于存储 + // 每个月的天数。 + int[12] monthDays; + + // 这个是数组的访问。它访问对应于 + // 十二月的元素,并设置它的值为 31。 + monthDays[11] = 31; + + // 这是另一个访问。它访问对应于 + // 一月的元素,并把它的值传给 + // writeln 函数。 + writeln("January has ", monthDays[0], " days."); +--- + +$(P +$(B 提醒:) 一月和十二月的元素位置数分别是 0 和 11;而不是 1 和 12。 +) + +) + +) + +$(H5 $(IX index) Index(索引)) + +$(P +元素的位置数称为 $(I index),访问元素的行为称为$(I 索引)。 +) + +$(P +索引不必是恒定值;变量的值也能用作索引,这会让数组更有用。例如,下面通过变量 $(C monthIndex) 的值来确定该月: +) + +--- + writeln("This month has ", monthDays[monthIndex], " days."); +--- + +$(P +当 $(C monthIndex) 的值为 2,上面的表达式将输出 $(C monthDays[2]) 的值,即三月的天数。 +) + +$(P +只有与数组长度的差值在 0 与 1 之间的索引才有效。例如,一个具有三个元素的数组的有效索引是 0、1 和 2。访问具有无效索引的数组将导致出错从而终止程序。 +) + +$(P +数组是容器,其中的元素在计算机的内存中是逐个放置的。例如,下面数组的元素保存了每个月的天数(假定一年中二月有 28 天): +) + +$(MONO + 索引 → 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 注意:) 上面的索引仅用于演示;并没有存储在计算机的内存中。) +) + +$(P +索引 0 处元素的值为 31(一月的天数);索引1 处元素的值为 28(二月的天数)依次类推。 +) + +$(H5 $(IX 定长数组) $(IX 动态数组) $(IX 静态数组) 定长数组与动态数组) + +$(P +当数组的长度是在写程序时指定时,该数组就是一个$(I 定长数组)。当长度可以在程序的执行过程中进行修改时,该数组就是一个$(I 动态数组)。 +) + +$(P +上面我们定义的数组都为定长数组,因为元素的个数在写程序时已指定为 5 和 12。在程序的执行过程中数组的长度不可修改。要修改长度,就必须修改源代码,而且程序必须重新编译。 +) + +$(P +因为省略了长度,所以定义动态数组比定长数组简单: +) + +--- + int[] dynamicArray; +--- + +$(P +在程序的执行过程中这种数组的长度可以增加或减少。 +) + +$(P +定长数组也被称为静态数组。 +) + +$(H5 $(IX .length) 使用 $(C .length) 来获取或设置元素的个数) + +$(P +数组也有 properties(属性),在这儿我们只会看到 $(C .length)。$(C .length) 返回数组元素的个数: +) + +--- + writeln("The array has ", array.length, " elements."); +--- + +$(P +另外,通过对这个 property 指定一个值就可以修改动态数组的 length: +) + +--- + int[] array; // 初始时为空 + array.length = 5; // 现在有 5 个元素 +--- + +$(H5 一个数组例子) + +$(P +现在我们重温一下使用五个值的例子,用数组重写一下它: +) + +--- +import std.stdio; + +void main() { + // 该变量用作循环计数器 + int counter; + + // 定义了一个可包含五个 double + // 类型元素的数组 + double[5] values; + + // 在循环中从输入流读取元素值 + 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; + } + + // 经过计算的五个数值 + //将在循环中输出 +} +--- + +$(P $(B 观测:) $(C counter) 的值决定了循环的重复次数(迭代)。当它的值小于 $(C values.length) 时,迭代循环可以保证每个元素只执行了一次。在每个迭代结束时该变量值都会递增,$(C values[counter]) 表达式指的是数组的逐个元素:$(C values[0])、$(C values[1]) 等等。 +) + +$(P +要看一看这个程序怎样才能比以前的更好,让我们设想需要读取 20 个值。修改一下上面的程序:用 20 替换 5。另一方面,这个程序若不使用数组那将必须定义 20 个变量。此外,因为您无法使用循环遍历 20 个值,您也不得不把那几行代码重复 20 次,每个单值变量来一次。 +) + +$(H5 $(IX 初始化,数组) 初始化元素) + +$(P +像在 D 语言中的每个变量,数组的元素自动初始化。元素的初始值取决于元素类型:$(C int) 的为 0,$(C double) 的为 $(C double.nan) 等等。 +) + +$(P +上面 $(C values) 数组的所有元素都初始化为 $(C double.nan): +) + +--- + double[5] values; // 元素都是 double.nan +--- + +$(P +显然,元素的值可以在以后程序执行期间发生变化。这在上面给数组的元素赋值时我们已经看到: +) + +--- + monthDays[11] = 31; +--- + +$(P +从输入流中读取值时也发生了: +) + +--- + readf(" %s", &values[counter]); +--- + +$(P +有时在数组定义时元素的期望值是已知的。在这种情况下,元素的初始值可以在分配操作的右手侧方括号内指定。让我们来看一看这个程序,读取来自用户的月数,并输出当月的天数: +) + +--- +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 +正如你所看到的,$(C monthDays) 数组在定义的时候就已初始化。另外请注意, 1-12 范围内的月份数字转换为了有效的数组索引范围 0-111-12 范围外的任何值都将导致程序出现错误而终止。 +) + +$(P +当初始化数组时,也可以在右手侧使用单个值。在这种情况下,所有的数组元素都初始化为该值: +) + +--- + int[10] allOnes = 1; // 所有的元素都设置为 1 +--- + +$(H5 数组的基本操作) + +$(P +数组提供了适用于所有元素的方便操作。 +) + +$(H6 $(IX 复制, 数组) 复​​制定长数组) + +$(P +赋值运算符将所有元素从右手侧复制到左手侧: +) +--- + int[5] source = [ 10, 20, 30, 40, 50 ]; + int[5] destination; + + destination $(HILITE =) source; +--- + +$(P +$(I $(B 注意:) 赋值运算符的含义与动态数组完全不同。我们将在后面的章节中看到这一点。) +) + +$(H6 $(IX ~=) $(IX 附加, 数组) $(IX 添加元素, 数组) 给动态数组添加元素) + +$(P +$(C ~=) 运算符是指在动态数组的尾部添加新的元素: +) + +--- + int[] array; // 空数组 + array ~= 7; // 数组现在等于 [7] + array ~= 360; // 数组现在等于 [7, 360] + array ~= [ 30, 40 ]; // 数组现在等于 [7, 360, 30, 40] +--- + +$(P +不可能给定长数组添加元素: +) + +--- + int[$(HILITE 10)] array; + array ~= 7; $(DERLEME_HATASI) +--- + +$(H6 $(IX remove, array) 从动态数组里删除元素) + +$(P +可以使用模块 $(C std.algorithm) 里的 $(C remove()) 函数删除数组元素。由于数组里可能拥有多个具有相同元素的 $(I 分片) ,$(C remove()) 实际上不会改变原有数组的元素个数。由此,它不得不将原数组的某些元素向左移动一个或多个位置。基于此,删除操作的结果必须回赋给同一个数组变量。 +) + +$(P +$(C remove()) 的使用方式有以下两种: +) + +$(OL +$(LI +提供需要删除的那个元素的索引。例如,下面这段代码将删除索引1上的那个元素。 +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 10, 20, 30, 40 ]; + $(HILITE array =) array.remove($(HILITE 1)); // 回赋给 array + writeln(array); +} +--- + +$(SHELL +[10, 30, 40] +) + +$(LI +使用一个 $(I 匿名函数) (在 $(LINK2 /ders/d.cn/lambda.html, 后面章节) 会讲解它)来指定需要删除的元素。例如,下面这段代码将删除数组里等于 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)); // 回赋给 array + writeln(array); +} +--- + +$(SHELL +[10, 20, 30, 40] +) +) + +$(H6 $(IX ~, 连接) $(IX 连接, 数组) 连接数组) + +$(P + $(C ~) 运算符通过连接两个数组从而创建一个新数组。$(C ~=) 将两边的数组连接起来,并把结果赋给左边那个数组: +) + +--- +import std.stdio; + +void main() { + int[10] first = 1; + int[10] second = 2; + int[] result; + + result = first ~ second; + writeln(result.length); // 输出 20 + + result ~= first; + writeln(result.length); // 输出 30 +} +--- + +$(P +左侧数组是定长数组时 $(C ~=) 运算符不能使用: +) + +--- + int[20] result; + // ... + result $(HILITE ~=) first; $(DERLEME_HATASI) +--- + +$(P +如果数组大小不一致,会导致赋值期间出错而终止程序: +) + +--- + 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(排序)) 排序元素) + +$(P +$(C std.algorithm.sort) 可以对许多类型集合中的元素进行排序。对于整数,元素按从小到大排序。为了使用 $(C sort()) 函数,必须先导入 $(C std.algorithm) 模块。(在后面的章节我们将看到这些函数。) +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 4, 3, 1, 5, 2 ]; + $(HILITE sort)(array); + writeln(array); +} +--- + +$(P +输出: +) + +$(SHELL +[1, 2, 3, 4, 5] +) + +$(H6 $(IX reverse(反转)) 反转元素) + +$(P +$(C std.algorithm.reverse) 反转元素的位置(第一个元素成为最后一个元素,以此类推): +) + +--- +import std.stdio; +import std.algorithm; + +void main() { + int[] array = [ 4, 3, 1, 5, 2 ]; + $(HILITE reverse)(array); + writeln(array); +} +--- + +$(P +输出: +) + +$(SHELL +[2, 5, 1, 3, 4] +) + +$(PROBLEM_COK + +$(PROBLEM +编写一个程序,要求用户输入多少值,然后全部读取。让程序使用 $(C sort()) 函数排序元素,然后使用 $(C reverse()) 函数反转排序的元素。 +) + +$(PROBLEM +编写一个程序,从输入流中读取数字,并先后按顺序分别输出奇数和偶数。并专门用值 -1 来确定数字的结束;不处理该值。 + +$(P +例如,当输入了下面的数字, +) + +$(SHELL +1 4 7 2 3 8 11 -1 +) + +$(P +程序会输出以下内容: +) + +$(SHELL +1 3 7 11 2 4 8 +) + +$(P +$(B 提示:) 你可以把元素放入单独的数组。使用 $(C %)(求余数)运算符来确定数字是奇数还是偶数。 +) + +) + +$(PROBLEM +以下为预期不起作用的程序。程序要求从输入流中读取五个数字,并把这些数字放入一个数组。然后程序会输出这些平方。若有错误,程序会终止。 + +$(P +请修复程序的 bug,让它按预期的方式执行: +) + +--- +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=数组 + + DESCRIPTION=D 语言的数组基本操作 + + KEYWORDS=D 编程语言 教程 定长 动态 + + +$(Ergin) diff --git a/ddili/src/ders/d.en/assert.cozum.d b/target/assert.cozum.d similarity index 99% rename from ddili/src/ders/d.en/assert.cozum.d rename to target/assert.cozum.d index 37ea2be..92280b5 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/assert.d b/target/assert.d similarity index 97% rename from ddili/src/ders/d.en/assert.d rename to target/assert.d index e905094..e83bdfe 100644 --- a/ddili/src/ders/d.en/assert.d +++ b/target/assert.d @@ -92,11 +92,11 @@ Each $(C assert) checks its assumption and terminates the program with an $(C As ) $(SHELL -core.exception.$(HILITE AssertError)@deneme(3): Assertion failure +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 3 of file $(C deneme.d). +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 @@ -108,11 +108,11 @@ The other syntax of $(C assert) allows printing a custom message when the $(C as --- $(P -The output: +输出: ) $(SHELL -core.exception.AssertError@deneme.d(3): $(HILITE Age cannot be negative.) +core.exception.AssertError@deneme.d(2): $(HILITE Age cannot be negative.) ) $(P @@ -232,7 +232,7 @@ $(C assert) is also the fundamental tool that is used in $(I unit testing) and $ $(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 they $(I should) have any side effects. +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 @@ -254,7 +254,7 @@ dmd deneme.d -release ) $(P -This would allow programs run faster by not evaluating potentially slow logical expressions of the $(C assert) checks. +This would allow programs to run faster by not evaluating potentially slow logical expressions of the $(C assert) checks. ) $(P diff --git a/ddili/src/ders/d.en/assignment.cozum.d b/target/assignment.cozum.d similarity index 100% rename from ddili/src/ders/d.en/assignment.cozum.d rename to target/assignment.cozum.d diff --git a/ddili/src/ders/d.en/assignment.d b/target/assignment.d similarity index 100% rename from ddili/src/ders/d.en/assignment.d rename to target/assignment.d diff --git a/ddili/src/ders/d.en/auto_and_typeof.cozum.d b/target/auto_and_typeof.cozum.d similarity index 97% rename from ddili/src/ders/d.en/auto_and_typeof.cozum.d rename to target/auto_and_typeof.cozum.d index e32b529..6637d0b 100644 --- a/ddili/src/ders/d.en/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/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/ddili/src/ders/d.en/bit_operations.cozum.d b/target/bit_operations.cozum.d similarity index 100% rename from ddili/src/ders/d.en/bit_operations.cozum.d rename to target/bit_operations.cozum.d diff --git a/ddili/src/ders/d.cn/bit_operations.d b/target/bit_operations.d similarity index 91% rename from ddili/src/ders/d.cn/bit_operations.d rename to target/bit_operations.d index 619e314..d35fd95 100644 --- a/ddili/src/ders/d.cn/bit_operations.d +++ b/target/bit_operations.d @@ -153,7 +153,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -310,7 +310,7 @@ Let's first define a function which will be useful later when examining how bit import std.stdio; void print(uint number) { - writefln("%032b %08x %10s", number, number, number); + writefln(" %032b %08x %10s", number, number, number); } void main() { @@ -323,7 +323,7 @@ Here is the same value printed in the binary, hexadecimal, and decimal number sy ) $(SHELL_SMALL -00000111010110111100110100010101 075bcd15 123456789 + 00000111010110111100110100010101 075bcd15 123456789 ) $(H6 $(IX ~, bitwise complement) $(IX complement, bitwise operator) Complement operator: $(C ~)) @@ -338,9 +338,9 @@ This operator converts each bit of a value to its opposite: The bits that are 0 --- uint value = 123456789; - write(" "); print(value); + print(value); writeln("~ --------------------------------"); - write(" "); print($(HILITE ~)value); + print($(HILITE ~)value); --- $(P @@ -372,10 +372,10 @@ $(C &) is a binary operator, written between two expressions. The microprocessor uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("& --------------------------------"); - write(" "); print(lhs $(HILITE &) rhs); + print(lhs $(HILITE &) rhs); --- $(P @@ -426,10 +426,10 @@ $(C |) is a binary operator, written between two expressions. The microprocessor uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("| --------------------------------"); - write(" "); print(lhs $(HILITE |) rhs); + print(lhs $(HILITE |) rhs); --- $(SHELL_SMALL @@ -472,10 +472,10 @@ $(I Xor) is the short for $(I exclusive or). This is a binary operator as well. uint lhs = 123456789; uint rhs = 987654321; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("^ --------------------------------"); - write(" "); print(lhs $(HILITE ^) rhs); + print(lhs $(HILITE ^) rhs); --- $(SHELL_SMALL @@ -515,7 +515,7 @@ Regardless of its value, $(I xorring) a variable with itself always produces 0: --- $(SHELL_SMALL -00000000000000000000000000000000 00000000 0 + 00000000000000000000000000000000 00000000 0 ) $(H6 $(IX >>) $(IX right shift, bitwise operator) Right-shift operator: $(C >>)) @@ -535,12 +535,12 @@ The following example produces a result by shifting a value by two bits to the r --- $(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 will get the value 0: +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 + 000001110101101111001101000101$(HILITE 01) 075bcd15 123456789 + $(HILITE 00)000001110101101111001101000101 01d6f345 30864197 ) $(P @@ -566,12 +566,12 @@ Because the leftmost bit of the original value is 1, all of the new bits of the ) $(SHELL_SMALL -$(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 111)10000000000000010000001100000 f0002060 4026540128 + $(U 1)0000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 111)10000000000000010000001100000 f0002060 4026540128 ) $(P -When the leftmost bit is 0, then all of the news bits are 0: +When the leftmost bit is 0, then all new bits are 0: ) --- @@ -581,8 +581,8 @@ When the leftmost bit is 0, then all of the news bits are 0: --- $(SHELL_SMALL -$(U 0)1000000000000010000001100000$(HILITE 000) 40010300 1073808128 -$(HILITE 000)01000000000000010000001100000 08002060 134226016 + $(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 >>>)) @@ -598,8 +598,8 @@ This operator works similarly to the regular right-shift operator. The differenc --- $(SHELL_SMALL -10000000000000010000001100000$(HILITE 000) 80010300 2147549952 -$(HILITE 000)10000000000000010000001100000 10002060 268443744 + 10000000000000010000001100000$(HILITE 000) 80010300 2147549952 + $(HILITE 000)10000000000000010000001100000 10002060 268443744 ) $(H6 $(IX <<) $(IX left shift, bitwise operator) Left-shift operator: $(C <<)) @@ -619,14 +619,14 @@ The bits on the left-hand side are lost and the new bits on the right-hand side ) $(SHELL_SMALL -$(HILITE 0000)0111010110111100110100010101 075bcd15 123456789 -0111010110111100110100010101$(HILITE 0000) 75bcd150 1975308624 + $(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 have seen in $(LINK2 /ders/d.en/arithmetic.html, the Integers and Arithmetic Operations chapter), these operators assign the result back to the left-hand operand. +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 @@ -658,10 +658,10 @@ As an extreme example, let's consider two values that both have alternating bits uint lhs = 0xaaaaaaaa; uint rhs = 0x55555555; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("| --------------------------------"); - write(" "); print(lhs | rhs); + print(lhs | rhs); --- $(SHELL_SMALL @@ -685,10 +685,10 @@ As an extreme example, let's consider the last two values again. Since none of t uint lhs = 0xaaaaaaaa; uint rhs = 0x55555555; - write(" "); print(lhs); - write(" "); print(rhs); + print(lhs); + print(rhs); writeln("& --------------------------------"); - write(" "); print(lhs & rhs); + print(lhs & rhs); --- $(SHELL_SMALL @@ -708,11 +708,11 @@ To understand how this works, it helps to see one of the expressions as the $(I uint expression = 0x00ff00ff; uint bitsToSet = 0x10001000; - write("before : "); print(expression); - write("to set to 1: "); print(bitsToSet); + write("before :"); print(expression); + write("to set to 1:"); print(bitsToSet); expression $(HILITE |=) bitsToSet; - write("after : "); print(expression); + write("after :"); print(expression); --- $(P @@ -739,11 +739,11 @@ One of the expressions can be seen as the $(I actual) expression and the other e uint expression = 0x00ff00ff; uint bitsToClear = 0xffefffef; - write("before : "); print(expression); - write("bits to clear: "); print(bitsToClear); + write("before :"); print(expression); + write("bits to clear:"); print(bitsToClear); expression $(HILITE &=) bitsToClear; - write("after : "); print(expression); + write("after :"); print(expression); --- $(P @@ -780,8 +780,8 @@ The bit that is being $(I queried) is highlighted: ) $(SHELL_SMALL -000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 -00000000000000010000000000000000 00010000 65536 + 000001110101101$(HILITE 1)1100110100010101 075bcd15 123456789 + 00000000000000010000000000000000 00010000 65536 yes, 1 ) @@ -972,9 +972,9 @@ A mask consists of a number of 1 bits that would $(I cover) the specific part of uint value = 123456789; uint mask = 0x000000ff; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print(value & mask); + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); --- $(P @@ -982,9 +982,9 @@ The bits that are covered by the mask are highlighted. All of the other bits are ) $(SHELL_SMALL -value : 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 -mask : 00000000000000000000000011111111 000000ff 255 -result: 000000000000000000000000$(HILITE 00010101) 00000015 21 +value : 000001110101101111001101$(HILITE 00010101) 075bcd15 123456789 +mask : 00000000000000000000000011111111 000000ff 255 +result: 000000000000000000000000$(HILITE 00010101) 00000015 21 ) $(P @@ -995,9 +995,9 @@ Let's apply the same method to the 0xc0a80102 IPv4 address with a mask that woul uint value = 0xc0a80102; uint mask = 0xff000000; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print(value & mask); + write("value :"); print(value); + write("mask :"); print(mask); + write("result:"); print(value & mask); --- $(P @@ -1005,9 +1005,9 @@ 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 +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: $(HILITE 11000000)000000000000000000000000 c0000000 3221225472 ) $(P @@ -1018,15 +1018,15 @@ However, note that the printed result is not the expected 192 but 3221225472. Th uint value = 0xc0a80102; uint mask = 0xff000000; - write("value : "); print(value); - write("mask : "); print(mask); - write("result: "); print((value & mask) $(HILITE >> 24)); + 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) +value : $(HILITE 11000000)101010000000000100000010 c0a80102 3232235778 +mask : 11111111000000000000000000000000 ff000000 4278190080 +result: 000000000000000000000000$(HILITE 11000000) 000000c0 $(HILITE 192) ) $(PROBLEM_COK diff --git a/ddili/src/ders/d.en/blurbs.d b/target/blurbs.d similarity index 100% rename from ddili/src/ders/d.en/blurbs.d rename to target/blurbs.d diff --git a/ddili/src/ders/d.en/breadcrumbs.ddoc b/target/breadcrumbs.ddoc similarity index 52% rename from ddili/src/ders/d.en/breadcrumbs.ddoc rename to target/breadcrumbs.ddoc index 66bdd25..6389e6e 100644 --- a/ddili/src/ders/d.en/breadcrumbs.ddoc +++ b/target/breadcrumbs.ddoc @@ -1,4 +1,4 @@ -BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > Prg in D +BREADCRUMBS_INDEX=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > D 语言编程 -BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > $(LINK2 /ders/d.en/index.html, Prg in D) +BREADCRUMBS_FULL=$(LINK2 /index.html, Main Page) > $(LINK2 /ders/index.html, Books) > $(LINK2 /ders/d.cn/index.html, D 语言编程) diff --git a/ddili/src/ders/d.en/cast.d b/target/cast.d similarity index 99% rename from ddili/src/ders/d.en/cast.d rename to target/cast.d index 501a86a..f4051a8 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.cn/characters.d b/target/characters.d similarity index 98% rename from ddili/src/ders/d.cn/characters.d rename to target/characters.d index 40902fb..3e6e3df 100644 --- a/ddili/src/ders/d.cn/characters.d +++ b/target/characters.d @@ -49,7 +49,7 @@ IBM Corporation has defined a set of tables, each one of which assign the codes ) $(P -Despite being much 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. +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 @@ -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/ddili/src/ders/d.cn/class.d b/target/class.d similarity index 53% rename from ddili/src/ders/d.cn/class.d rename to target/class.d index 9f62f1e..88e47bb 100644 --- a/ddili/src/ders/d.cn/class.d +++ b/target/class.d @@ -3,34 +3,34 @@ Ddoc $(DERS_BOLUMU $(IX class) 类) $(P -$(IX OOP) $(IX 面向对象编程) 与结构相似, $(C class) 具有定义新类型的功能。不同于结构的是,在D语言中,类提供$(I 面向对象编程)(OOP)模型。下面是OOP的主要方面: +$(IX OOP) $(IX object oriented programming) $(IX user defined type) 与结构相似,$(C class) 具有定义新类型的功能。根据此定义,类是 $(I 自定义类型)。不同于结构的是,D 语言中的类提供的是 $(I 面向对象编程) (OOP)模型。OOP 的主要内容有以下几个方面: ) $(UL $(LI -$(B 封装:)($(I 封装用于结构也不错,但到本章为止也没提及。)) +$(B 封装:) 控制成员的访问($(I 封装也可用于结构,只是到本章之前一直未提及。)) ) $(LI -$(B 继承:)获取另一个类型的成员 +$(B 继承:) 获取另一个类型的成员 ) $(LI -$(B 多态性:)能用更特殊的类型替代更通用的类型 +$(B 多态性:) 能够使用较特定的类型取代较通用的类型 ) ) $(P -封装是通过$(I 保护属性)来实现, 在 $(LINK2 /ders/d.cn/encapsulation.html, 稍后的一章) 中将会看到。继承是用于获取其它类型的$(I 实现) 。$(LINK2 /ders/d.cn/inheritance.html, 多态性) 是从类之间抽象出部分代码,通过$(I 接口)来实现。 +封装是通过 $(I 保护属性) 来实现的,关于这一点在 $(LINK2 /ders/d.cn/encapsulation.html, 后面章节) 会看到。继承是用于获取其它类型的 $(I 实现)。$(LINK2 /ders/d.cn/inheritance.html, 多态性) 是从类之间抽象出部分代码,通过 $(I 接口) 实现的。 ) $(P -本章将深入介绍类, 强调一个事实,即类是引用类型。稍后的章节中将展示类的更多细节。 +本章将深入介绍类,特别强调一点,类是引用类型。稍后的章节中将展示类的更多细节。 ) -$(H5 与结构的比较) +$(H5 与结构对比) $(P 一般情况下,类与结构非常相似。在下面的章节中我们已经看到结构的大部分特性也适用于类: @@ -51,29 +51,29 @@ $(P $(H6 类是引用类型) $(P -与结构的最大区别是结构是$(I 值类型)而类是$(I 引用类型)。下面的其它不同大部分与此有关。 +与结构的最大区别在于结构是 $(I 值类型) 而类是 $(I 引用类型)。下面的其它不同大部分与此有关。 ) $(H6 $(IX null, class) $(new, class) 类变量可以为 $(C null)) $(P -在 $(LINK2 /ders/d.cn/null_is.html, $(CH4 null) 值和 $(CH4 is) 运算符一章) 中, 本书已简要的提到,类变量可以是 $(C null)。换句话说,类变量可以不提供对任何对象的访问。类变量没有值本身;实际的类对象必须由 $(C new) 关键字构造。 +在 $(LINK2 /ders/d.cn/null_is.html, $(CH4 null) 值和 $(CH4 is) 运算符一章)已提到过,类变量可以为 $(C null)。换句话说,类变量可以不提供对任何对象的访问。类变量并不拥有值本身;实际的类对象必须使用关键字 $(C new) 来构造。 ) $(P -正如你所记得的,一个引用与 $(C null) 通过 $(C ==) 或者 $(C !=) 运算符比较是一个错误。相反,必须用 $(C is) 或 $(C !is) 运算符比较,因此: +大家都还记得吧,引用与运算符 $(C null) 不能通过运算符 $(C ==) 或 $(C !=) 进行比较。相反,必须相应地使用运算符 $(C is) 或 $(C !is): ) --- MyClass referencesAnObject = new MyClass; assert(referencesAnObject $(HILITE !is) null); - MyClass variable; // 没有引用一个对象 + MyClass variable; // 没有引用对象 assert(variable $(HILITE is) null); --- $(P -原因是 $(C ==) 运算符会查询对象成员的值,并尝试通过一个潜在的 $(C null) 变量访问成员,这将引发一个内存访问错误。因此,类变量必须总是通过 $(C is) 和 $(C !is) 运算符做比较。 +原因是,运算符 $(C ==) 会查询对象成员的值,并尝试通过一个潜在的 $(C null) 变量访问成员,这将引发一个内存访问错误。因此,类变量必须总是通过运算符 $(C is) 和 $(C !is) 进行比较。 ) $(H6 $(IX variable, class) $(IX object, class) 类变量与类对象) @@ -83,15 +83,15 @@ $(P ) $(P -类对象由 $(C new) 关键字构造;它们没有名子。实际的概念是,在程序中,一个类类型由一个类对象表示。比如,假设一个 $(C Student) 类用姓名和成绩表示学生,那 $(C Student) $(I 对象)的成员将存储这些信息。部分原因是因为它们是匿名的,直接访问类对象那是不可能的。 +类对象由关键字 $(C new) 构造;它们没有名字。实际的概念是,在程序中,一个类类型由一个类对象表示。例如,有一个 $(C Student) 类,它通过姓名和成绩来表示学生, 此时 $(C Student) $(I 对象)的成员会存储这些信息。另一方面,类变量是用于访问类对象的一种语言特性。 ) $(P -另一方面,类变量是用于访问类对象的一种语言特性。虽然语法上看起来是在类$(I 变量)上执行,但实际上调度了一个类$(I 对象)。 +另一方面,类变量一种访问类对象的语言功能。虽然语法上看起来是在类 $(I 变量) 上执行,但实际上调度了一个类 $(I object)。 ) $(P -让我们考虑一下下面的我们以前在 $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型和引用类型一章) 中看到过的代码: +一起来看看下面这段代码,之前在 $(LINK2 /ders/d.cn/value_vs_reference.html, 值类型和引用类型一章)已见过,如下所示: ) --- @@ -104,7 +104,7 @@ $(P ) $(MONO - (匿名的 MyClass 对象) variable1 variable2 + (匿名 MyClass 对象) 变量1 变量2 ───┬───────────────────┬─── ───┬───┬─── ───┬───┬─── │ ... │ │ o │ │ o │ ───┴───────────────────┴─── ───┴─│─┴─── ───┴─│─┴─── @@ -132,11 +132,11 @@ $(P --- $(P -在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。这俩变量可访问同一对象。 +在上面的代码中, $(C variable2) 由 $(C variable1) 初始化。这两个变量可访问同一个对象。 ) $(P -当需要复制实际的对象时,类必须有一个针对此目的的成员函数。为与数组兼容,该函数可以命名为 $(C dup())。该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它: +当需要复制实际的对象时,类必须有一个针对此目的的成员函数。为与数组兼容,该函数可以命名为 $(C dup()). 该函数必须创建并返回一个新的类对象。让我们在有各种类型成员的类上看看它:: ) --- @@ -160,11 +160,11 @@ class Foo { --- $(P -$(C dup()) 成员函数利用 $(C Foo) 的构造函数,创建并返回新的对象。注意,构造函数通过数组的 $(C .dup) 属性显式复制 $(C s) 成员。做为值类型,$(C o) 和 $(C i) 自动被复制。 +$(C dup()) 成员函数利用 $(C Foo) 的构造函数,创建并返回新的对象。注意,构造函数通过数组的 $(C .dup) 属性显式复制 $(C s) 成员。做为值类型,$(C o) 和 $(C i) 自动被复制。 ) $(P -下面的代码演示 $(C dup()) 创建一个新的对象的用法: +下面的代码利用 $(C dup()) 创建一个新的对象: ) --- @@ -173,16 +173,20 @@ $(P --- $(P -结果是,$(C var1) 和 $(C var2) 相关联的对象是不同的。 +最后,与 $(C var1) 和 $(C var2) 关联的那些对象并不相同。 ) $(P -同样的,可以由命名为 $(C idup()) 的适当的成员函数提供对象的 $(C immutable) 副本: +同样地,可以通过名为 $(C idup()) 的成员函数的提供对象的 $(C immutable) 副本:此时,构造函数必须同时定义为 $(C pure) 。我们会在$(LINK2 /ders/d.cn/functions_more.html, 后面章节)对关键字 $(C pure) 进行讲解。 ) --- class Foo { // ... + this(S o, const char[] s, int i) $(HILITE pure) { + // ... + + } immutable(Foo) idup() const { return new immutable(Foo)(o, s, i); } @@ -275,7 +279,7 @@ $(P $(H6 析构函数) $(P -像结构一样,析构函数的名称是 $(C ~this): +像结构一样,析构函数的名称是 $(C ~this): ) --- @@ -285,30 +289,30 @@ $(P --- $(P -$(IX 终结与析构) 然而,不同于结构,在一个类对象的生命期结束时,类的析构函数并不执行。正如上面我们看到的,析构函数在一个垃圾回收周期内的未来某个时候执行。(通过这样的区分,更准确的说,类的析构函数应该叫$(I 终结函数)) 。 +$(IX finalizer versus destructor) 不过,与结构有所不同的是,类的析构函数在类对象的生命期结束时并不会被执行。正如上面看到的,析构函数会在未来垃圾回收周期内的某个时候被执行。(基于此点差异,类的析构函数被叫作 $(I 终结函数) 会更加确切)。 ) $(P -我们将在后面 $(LINK2 /ders/d.cn/memory.html, 内存管理一章) 中看到,类的析构函数必须遵守以下规则: +在后面的 $(LINK2 /ders/d.en/memory.html, 内存管理一章) 将会看到,类的析构函数必须遵循以下几条规则: ) $(UL -$(LI 一个类的析构函数不得访问由垃圾回收器管理的成员。这是因为垃圾回收器没有被要求保证该对象及其成员按任何特定顺序终结。当析构函数执行时,全部成员应该已经终结。) +$(LI 类的析构函数不能访问由垃圾回收器管理的成员。这是因为垃圾回收器没有被要求保证该对象及其成员按任何特定顺序终结。当析构函数执行时,全部成员应该已经终结。) -$(LI 类的析构函数一定不分配由垃圾回收器管理的新内存。这是因为垃圾回收器没有被要求保证在垃圾回收周期内能分配新的对象。) +$(LI 类的析构函数一定不要分配由垃圾回收器管理的新内存。这是因为垃圾回收器没有被要求保证在垃圾回收周期内能分配新的对象。) ) $(P -违反这些规则是不确定的行为。通过尝试在类的析构函数中分配一个对象,很容易看到一例这样的问题: +违反这些规则即会产生未定义行为。尝试在类的析构函数中分配一个对象,通过这种方式可以轻易地重现这种的问题: ) --- class C { ~this() { - auto c = new C(); // ← 错误:在一个类的析构函数中 - // 明确分配 + auto c = new C(); // ← 错误:在类的析构函数里 + // 显式分配内存 } } @@ -318,32 +322,32 @@ void main() { --- $(P -程序由异常中断: +这个程序会抛一个异常,并中断: ) $(SHELL -core.exception.$(HILITE 无效的内存操作错误)@(0) +core.exception.$(HILITE InvalidMemoryOperationError)@(0) ) $(P -在析构函数里$(I 间接地)从垃圾回收器里分配新的内存,也同样是错的。例如,用于一个动态数组的元素的内存由垃圾回收器来分配。用这种方式使用一个数组,那将需要为未定义行为的元素分配一个新的内存块: +在析构函数里 $(I 间接地) 从垃圾回收器里分配新的内存,这种做法同样是错的。例如,用于一个动态数组的元素的内存由垃圾回收器来分配。用这种方式使用一个数组,那将需要为未定义行为的元素分配一个新的内存块: ) --- ~this() { - auto arr = [ 1 ]; // ← 错误:在一个类的析构函数中 - // 间接分配 + auto arr = [ 1 ]; // ← 错误:在类的析构函数里 + // 显式分配内存 } --- $(SHELL -core.exception.$(HILITE 无效的内存操作错误)@(0) +core.exception.$(HILITE InvalidMemoryOperationError)@(0) ) $(H6 成员访问) $(P -与结构一样,用$(I 点)运算符访问成员: +与结构一样,可以使用 $(I 点) 运算符来访问成员: ) --- @@ -352,23 +356,23 @@ $(P --- $(P -虽然语法上看起来像访问$(I 变量)的成员,实际上是$(I 对象)的成员。类变量没有成员,类对象有。$(C king) 变量并没有 $(C shape) 成员,匿名对象有。 +虽然语法上看起来像访问 $(I 变量) 的成员,实际上是 $(I 对象) 的成员。类变量没有成员,类对象有。$(C king) 变量并没有 $(C shape) 成员,匿名对象有。 ) $(P -$(I $(B 注:)在上面的代码中,一般不这样直接访问成员。若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的一章) 中解释。) +$(I $(B 注:) 在上面的代码中,一般不这样直接访问成员。若确实需要这样的语法,应该首选属性,这将在 $(LINK2 /ders/d.cn/property.html, 后面的章节) 中解释。) ) $(H6 运算符重载) $(P -虽然 $(C opAssign) 不能被类重载,但与结构一样,可以实现运算符重载。对于类, $(C opAssign) 意味着$(I 一个类变量总是关联着一个类对象)。 +虽然 $(C opAssign) 不能被类重载,但与结构一样,可以实现运算符重载。对于类, $(C opAssign) 意味着 $(I 一个类变量总是关联着一个类对象)。 ) $(H6 成员函数) $(P -虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是$(I 可重写的) 。在 $(LINK2 /ders/d.cn/inheritance.html,继承一章) 中我们将看到相关内容。 +虽然成员函数的定义与用法与结构相同,有个重要的不同:类成员函数默认是 $(I 可重写的) 。在 $(LINK2 /ders/d.cn/inheritance.html,继承章节) 我们将看到相关内容。 ) $(P @@ -384,7 +388,7 @@ class C { --- $(P -与结构不同的是一些成员函数自动继承自 $(C Object) 类。在 $(LINK2 /ders/d.cn/inheritance.html, 下一章) 中我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 +与结构不同的是一些成员函数自动继承自 $(C Object) 类。在 $(LINK2 /ders/d.cn/inheritance.html, 下一章节) 我们将看到怎样通过$(C override) 关键字来修改 $(C toString) 的定义。 ) $(H6 $(IX is, 运算符) $(IX !is) $(C is) 和 $(C !is) 运算符) @@ -404,7 +408,7 @@ $(C is) 确定两个类变量是否提供对同一对象的访问。如果是同 --- $(P -由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。即使这两个对象由同一字符 $(C'♔') 参数构造, 它们仍是两个单独的对象。 +由于 $(C myKing) 和 $(C yourKing) 变量来自不同的对象,$(C !is) 运算符返回 $(C true)。即使这两个对象由同一字符 $(C'♔') 参数构造,, 它们仍是两个单独的对象。 ) $(P @@ -420,14 +424,14 @@ $(P 上面的两个变量都提供对同一对象的访问。 ) -$(H5 摘要) +$(H5 小结) $(UL $(LI 类和结构虽然有共同特点,但还是有很大的差异。 ) -$(LI 类是引用类型。 $(C new) 关键字构造一个匿名 $(I class 对象)并返回一个 $(I class 变量)。 +$(LI 类是引用类型。The $(C new) 关键字构造一个匿名 $(I class 对象) 并返回一个 $(I class 变量)。 ) $(LI 不与任何对象相关联的类变量为 $(C null)。检查 $(C null) 必须使用 $(C is) 或 $(C !is),而不是 $(C ==) 或 $(C !=)。 @@ -444,6 +448,6 @@ $(LI 赋值会把一个变量与一个对象相关联。该行为不能被修改 Macros: SUBTITLE=类 - DESCRIPTION=D语言基本的面向对象编程(OOP)功能。 + DESCRIPTION=基本的 D 语言面向对象编程(OOP) 功能。 - KEYWORDS=D 语言编程教程 class + KEYWORDS=D 语言 编程 教程 书籍 教程 类 diff --git a/ddili/src/ders/d.cn/compiler.d b/target/compiler.d similarity index 50% rename from ddili/src/ders/d.cn/compiler.d rename to target/compiler.d index 1537422..e077451 100644 --- a/ddili/src/ders/d.cn/compiler.d +++ b/target/compiler.d @@ -1,23 +1,23 @@ Ddoc -$(DERS_BOLUMU $(IX 编译) 编译) +$(DERS_BOLUMU $(IX compilation) 编译) $(P -用 D 编程时经常会用到两个工具,它们分别是$(I 文本编辑器)和$(I 编译器)。D 程序正是在文本编辑器里写就。 +用 D 编程时经常会用到两个工具,它们分别是 $(I 文本编辑器) 和 $(I 编译器)。D 程序需要在文本编辑器里编写。 ) $(P -当使用类似 D 语言的$(I 编译型)语言时,需要理解编译的概念和编译器的功能。 +当使用类似 D 语言的 $(I 编译型) 语言时,需要理解编译的概念和编译器的功能。 ) -$(H5 $(IX 机器码) 机器码) +$(H5 $(IX machine code) 机器码) $(P -$(IX CPU) $(IX 微处理器) 计算机的大脑是微处理器(或 CPU ,$(I 中央处理单元)的简称)。告知 CPU 要做事叫做$(I 编码),而何时该做何事的指令则叫做$(I 机器码)。 +$(IX CPU) $(IX microprocessor) 计算机的大脑是微处理器(CPU,即 $(I 中央处理单元) 的简称)。告知 CPU 要做什么事情这在过程叫做 $(I 编码),而何时该做何事的指令则叫做 $(I 机器码)。 ) $(P -大多数 CPU 都使用特定架构的机器码,而这些机器码指令取决于底层架构设计阶段时的硬件约束。它们在最底层由电子信号实现。在这层,编码的舒适度不是主要考虑因素,所以直接按照 CPU 机器码形式编写程序非常困难。 +大多数 CPU 都使用特定架构的机器码。而这些机器码指令取决于底层架构设计阶段时的硬件约束。它们在最底层由电子信号实现。在这层,编码的舒适度不是主要考虑因素,所以直接按照 CPU 机器码形式编写程序非常困难。 ) $(P @@ -26,7 +26,7 @@ $(P $(MONO $(B -Operation Value Meaning) +操作 值 含义) 100 11110 LOAD 11110 101 10100 STORE 10100 110 10100 INCREMENT 10100 @@ -34,13 +34,13 @@ Operation Value Meaning) ) $(P -由于太贴近硬件,机器码不适合表示$(I 扑克牌)或$(I 学生记录)此类较高层次的概念。 +由于机器码太靠近硬件,因此它并不适合用于表示 $(I 扑克牌) 或 $(I 学生记录) 等较为高级的概念。 ) -$(H5 $(IX 编程语言) 编程语言) +$(H5 $(IX programming language) 编程语言) $(P -编程语言的设计目标是用来对 CPU 高效编程,且有能力描述高级概念。编程语言不处理底层硬件约束,它们的主要目的是使用和表现上的简易舒适,更易于人类理解,有点类似于自然语言: +编程语言的设计目标是用来对 CPU 高效编程,且有能力描述高级概念。编程语言不处理底层硬件约束,它们的主要目的是使用和表现上的简易舒适。编程语言更易于人类理解,更接近自然语言: ) $(MONO @@ -53,42 +53,42 @@ $(P 然而,编程语言依然比口语更严格、正规。 ) -$(H5 $(IX 解释器) 解释器) +$(H5 $(IX interpreter) 解释器) $(P -解释器是一种能从源码中读取指令并执行相应命令的工具(程序)。比如对于上述代码,解释器会首先执行 $(C a_card_has_been_played()),而后根据其执行结果选择性地执行 $(C display_the_card())。从程序员的角度来看,依赖解释器执行指令只涉及两个步骤:一,写代码,二,交由解释器执行。 +解释器是一种能从源码中读取指令并执行相应命令的工具(程序)。例如,对于上述代码,解释器会首先执行 $(C a_card_has_been_played()) ,而后根据其执行结果选择性地执行 $(C display_the_card())。从程序员的角度来看,依赖解释器执行指令只涉及两个步骤:编写源代码和将源代码交由解释器处理。 ) $(P -每一次执行程序之前,解释器都必须重新读入源码并了解它们的含义。这也就是为什么使用解释器执行程序往往要比直接执行编译版本的同一程序要慢。此外,解释器在执行代码之前对代码的分析大都非常有限,以至于大多数解释器在执行程序之后方能发现编程错误。 +每一次执行程序之前,解释器都必须重新读入源码并了解它们的含义。这也就是为什么使用解释器执行程序往往要比直接执行编译版本的同一程序要慢。此外,解释器在执行代码之前对代码的分析大都非常有限。最后,大多数解释器在执行程序之后方能发现编程错误。 ) $(P 一些语言被设计得非常灵活与动态,诸如 Perl、Python 和 Ruby,这使得代码分析困难重重。因此,这些语言传统上就需使用解释器。 ) -$(H5 $(IX 编译器) 编译器) +$(H5 $(IX compiler) 编译器) $(P -编译器是另一种从源码中读取程序指令的工具。与解释器不同的是,它不立即执行代码,而是生成一个用其他语言(通常为机器码)表示的程序。而后,这个被生成的程序负责执行程序员写就的指令。这时,从程序员的角度来看,执行指令就分三个步骤:一,写代码,二,编译,三,执行生成的文件。 +编译器是另一种从源码中读取程序指令的工具。与解释器不同的是,它不立即执行代码,而是生成一个用其他语言(通常为机器码)表示的程序。而后,这个被生成的程序负责执行程序员写就的指令。这时,从程序员的角度来看,执行指令就分三个步骤:写代码、编译和执行生成的文件。 ) $(P -不像解释器,编译器一次性地在编译期读入和理解源码。出于这个原因,通常情况下被编译的程序较被解释器所执行的程序运行更快。编译器素来对源码进行深入的分析,这有助于生成更高效的程序,甚至于在程序执行之前就捕捉到编程错误。而在另一方面,每次改变代码都必须重新编译,既复杂又易引起人为错误。另外,编译器往往是针对某一特定平台进行编译,程序需要重新编译以在不同的处理器或操作系统上运行。此外,容易编译的语言与在解释器中运行的语言相比往往缺乏灵活性。 +不像解释器,编译器一次性地在编译期读入和理解源码。出于这个原因,通常情况下被编译的程序较被解释器所执行的程序运行更快。编译器素来对源码进行深入的分析,这有助于生成更高效的程序,甚至于在程序执行之前就捕捉到编程错误。另一方面,每次改变代码都必须重新编译,既复杂又易引起人为错误。另外,编译器往往是针对某一特定平台进行编译,程序需要重新编译以在不同的处理器或操作系统上运行。此外,容易编译的语言与在解释器中运行的语言相比往往缺乏灵活性。 ) $(P -出于对安全及效率等原因的考量,某些语言被设计为可编译的,如 Ada、C、C++ 和 D。 +出于对安全及效率等原因的考量,某些语言被设计为可编译的。Ada、C、C++ 和 D 都属于这样的语言。 ) -$(H6 $(IX 错误, 编译) $(IX 编译错误) 编译错误) +$(H6 $(IX error, compilation) $(IX compilation error) 编译错误) $(P -当编译器按照语言规则编译程序时,一旦碰到$(I 非法)指令就会停止编译。非法指令指的是超出语言规范的地方。诸如圆括号未匹配、分号丢失、关键字拼错等的问题都会引起编译错误。 +当编译器按照语言规则编译程序时,一旦碰到 $(I 非法) 指令就会停止编译。非法指令指的是超出语言规范的地方。诸如圆括号未匹配、分号丢失、关键字拼错等的问题都会引起编译错误。 ) $(P -当编译器看到某段可疑但又不是错误的代码时,会发送$(I 编译警告)。但是,警告几乎总是指示一个实际错误或糟糕的代码风格,所以把警告当作错误对待是一个常见的做法。$(I DMD) 将警告视作错误的编译选项是 $(C -w)。 +当编译器看到某段可疑但又不是错误的代码时,会发出 $(I 编译警告)。但是,警告几乎总是指示一个实际错误或糟糕的代码风格,所以把警告当作错误对待是一个常见的做法。编译器 $(C dmd) 将警告视作错误的编译选项是 $(C -w)。 ) $(Ergin) @@ -98,4 +98,4 @@ Macros: DESCRIPTION=编译与编译型编程语言简介 - KEYWORDS=D 编程语言教程 + KEYWORDS=D 编程 语言 教程 书籍 diff --git a/ddili/src/ders/d.cn/concurrency.d b/target/concurrency.d similarity index 70% rename from ddili/src/ders/d.cn/concurrency.d rename to target/concurrency.d index 4b22ca0..af43f1f 100644 --- a/ddili/src/ders/d.cn/concurrency.d +++ b/target/concurrency.d @@ -1,43 +1,43 @@ Ddoc -$(DERS_BOLUMU $(IX 并发, 消息传递) $(IX 并发消息传递) 并发消息传递) +$(DERS_BOLUMU $(IX concurrency, message passing) $(IX message passing concurrency) 基于消息传递的并发) $(P -虽然并发(concurrency)与并行(parallelism)很相似,但我们不能将其混为一谈。这两个概念都涉及多线程,且并行是基于并发的,在刚接触它们时有些迷惑也是正常的。 +并发(concurrency)与上一章的并行(parallelism)很相似,但还是存在较多差异。这两个概念都涉及多线程,且并行是基于并发的,刚接触它们时感到有些迷惑也属正常。 ) $(P -$(IX 并行 vs. 并发) $(IX 并发 vs. 并行) 下面是并发和并行的区别: +$(IX parallelism vs. concurrency) $(IX concurrency vs. parallelism) 并发和并行的区别主要有以下几点: ) $(UL $(LI -并行的主要目的是利用多核心的运算能力提高程序的性能。而并发这个概念在单核心系统中也有用到。并发即程序同时运行多个线程。比如说服务器程序就是并发的,它需要在同一时间处理多个客户端的请求。 +并行的主要目的是利用微处理器的多核提高程序的性能。而并发则是一个在单核环境里也可以使用的概念。并发可以让一个程序同时在多个线程上运行。比如说服务器程序就是并发的,它需要同时处理多个客户端的请求。 ) $(LI -在并行中,任务之间相互独立。事实上如果同时运行的任务依赖其他任务的结果就可能会引发程序的 bug。而对于并发,线程间的相互依赖是很常见的。 +在并行中,任务之间相互独立。事实上如果同时运行的任务依赖其他任务的结果就可能会引发程序错误。而对于并发,线程间的相互依赖是很常见的。 ) $(LI -虽然两者都涉及线程操作,但并行用 task 对线程做了包装。而并发则需要显式调用线程。 +虽然两者都涉及线程操作,但并行线程被封装成了任务。而并发则需要显式利用线程。 ) $(LI -并行上手容易,由于任务相互独立的缘故我们写出的程序很少出错。并发则只有在基于$(I 消息传递)时才比较简单。若使用传统的基于锁的数据共享模型,则很难写出正确的程序。 +并行易于使用,并且只要任务相互独立,便可以轻松地生成可正常工作的程序。并发只有在基于 $(I 消息传递) 实现时才比较简单。若使用传统的基于锁的数据共享实现的并发模型,则很难写出正确的并发程序。 ) ) $(P -D 语言支持两种并发模型:消息传递和数据共享。我们将会在本章中学习到消息传递,在下一章中学习数据共享。 +D 语言支持两种并发模型:消息传递和数据共享。本章主要讲解消息传递,而数据共享则被放置到下一章。 ) -$(H5 概念) +$(H5 相关概念) $(P -$(IX thread) $(B 线程):操作系统执行程序的工作单元叫做$(I 线程)。D 语言程序在操作系统指定的线程上执行 $(C main()) 函数。通常情况下程序的所有操作都将在这个线程中完成。程序也可以自由地创建线程以实现在同一时间执行多个任务的功能。实际上上一章我们学习的 task 就是基于线程的,只不过这些线程是由 $(C std.parallelism) 自动维护的。 +$(IX thread) $(B 线程):操作系统执行程序的工作单元叫做 $(I 线程)。D 语言程序在操作系统指定的线程上执行 $(C main()) 函数。通常情况下程序的所有操作都将在这个线程中完成。程序也可以自由地创建线程以实现在同一时间执行多个任务的功能。实际上,上一章讲解的任务是基于线程的,只不过这些线程是由 $(C std.parallelism) 自动启动。 ) $(P @@ -53,25 +53,25 @@ $(P ) $(P -$(IX messgae) $(B 消息):在线程间传递的数据叫做消息。任何类型任何长短的数据都可以被称为消息。 +$(IX message) $(B 消息):在线程间传递的数据叫做消息。任何类型任何长短的数据都可以被称为消息。 ) $(P -$(IX thread id) $(B 线程 ID):每一个线程都有一个 ID,你可以使用它们指定消息的接收者。 +$(IX thread id) $(B 线程 ID):每个线程都有一个 ID,你可以用它来指定消息的接收者。 ) $(P -$(IX owner) $(B 所有者):启动线程的线程即为被启动线程的所有者。 +$(IX owner) $(B 所有者):任何启动另一个线程的线程即为该新线程的所有者。 ) $(P -$(IX worker) $(B 工作线程):被所有者启动的线叫做工作线程。 +$(IX worker) $(B 工作线程):由所有者启动的任何线种都叫做工作线程。 ) $(H5 $(IX spawn) 启动线程) $(P -$(C spawn()) 需要一个函数指针,新线程将会从指定的函数启动。函数中进行的包括函数调用在内的所有操作都将在新线程中执行。与 $(LINK2 /ders/d.cn/parallelism.html, $(C task())) 启动的线程相比,$(C spawn()) 启动的线程与之最大的不同在于 $(C spawn()) 允许线程间消息传递。 +$(C spawn()) 需要一个函数指针作为参数,并且会从该函数启动一个新线程。该函数(包括它可能调用的其他函数)包含的所有操作都会在新线程中执行。使用 $(C spawn()) 启动的线程和使用 $(LINK2 /ders/d.en/parallelism.html, $(C task())) 启动的线程之间最大的差异在于,$(C spawn()) 允许线程间消息传递。 ) $(P @@ -103,7 +103,7 @@ void main() { --- $(P -本章中的例子调用 $(C Thread.sleep) 减慢线程执行的速度来更方便的展示线程运行的情况。程序的输出显示有两个线程,一个运行 $(C main()),另一个通过 $(C spawn()) 创建,它们同时执行并相互独立: +本章中的例子调用 $(C Thread.sleep) 减慢线程执行的速度来更方便的展示线程运行的情况。这个程序的输出表明有两个线程:一个用于运行 $(C main()),另一个则由 $(C spawn()) 启动。它们同时相互独立地执行: ) $(SHELL @@ -121,11 +121,11 @@ main is done. ) $(P -程序在所有线程执行完毕后才会退出。从上面的输出中我们可以看到,$(C main()) 函数在打印 “main is done.”退出后 $(C worker()) 还在继续执行。 +程序在所有线程执行完毕后才会退出。从上面的输出中我们可以看到,在函数 $(C main()) 输出 “main is done.” 并退出之后,$(C worker()) 仍然在继续执行。 ) $(P -线程函数需要的参数应通过 $(C spawn()) 的二个参数传入。下面程序中的两个工作线程分别打印四个数字。线程函数的参数为初始数字。 +线程函数所需的参数可以通过 $(C spawn()) 的第二个及以后面的参数传入。下面程序中的两个工作线程分别输出四个数字。线程函数的参数为初始数字: ) --- @@ -163,11 +163,11 @@ $(HILITE 23) ) $(P -程序的输出顺序可能会和上面有所不同。具体情况取决于操作系统对线程的调度。 +程序的输出顺序可能会和上面有所不同,具体情况取决于操作系统对线程的调度。 ) $(P -$(IX CPU 密集型) $(IX I/O 密集型) $(IX 线程性能) 每个操作系统都有对同时运行线程个数的限制。这种限制可能是对用户的,也可能是对整个操作系统的,当然也可能是对其他某些级别。如果忙碌的工作线程数量比系统中处理器核心数多,系统的整体性能就有可能下降。在指定运行时间消耗大量 CPU 资源的线程叫做 $(I CPU 密集型)。与之相对的是消耗大量时间等待事件、用户输入、来自互联网的数据或调用了 $(C Thread.sleep) 等情况的线程。这种线程被称作 $(I I/O 密集型)。如果大部分线程都是 I/O 密集型的,那程序就不需要担心由于线程数多余核心数而造成的性能下降的问题。基于对性能设计的考量,我们需要谨慎分析并确定线程的类型。 +$(IX CPU bound) $(IX I/O bound) $(IX thread performance) 每个操作系统都会限制线程同时运行的个数。这种限制可能是对用户的,也可能是对整个操作系统的,当然也可能是对其他某些级别。如果处于忙碌中的工作线程的数量超过系统内核的数量,那么系统的整体性能有可能下降。在指定运行时间消耗大量 CPU 资源的线程叫做 $(I CPU 密集型)。与之相对的是消耗大量时间等待事件、用户输入、来自互联网的数据或调用了 $(C Thread.sleep) 等情况的线程。这种线程被称作 $(I I/O 密集型)。如果大部分线程都是 I/O 密集型的,那么程序不需要担心由于线程数超过内核数而造成性能下降的问题。出于性能设计的考量,我们需要谨慎分析并确定线程的类型。 ) $(H5 $(IX Tid) $(IX thisTid) $(IX ownerTid) 线程 ID) @@ -195,12 +195,12 @@ void main() { --- $(P -$(C thisTid()) 返回值的类型为 $(C Tid),对我们来说这个类型并没有什么卵用。它甚至连 $(C toString()) 都没重载: +$(C thisTid()) 返回的类型为 $(C Tid),它对程序没有意义。它甚至没重载 $(C toString()): ) $(SHELL -Owner : Tid(std.concurrency.MessageBox) -Worker: Tid(std.concurrency.MessageBox) +Owner : Tid(22310e53100) +Worker: Tid(22310e53000) ) $(P @@ -212,21 +212,21 @@ $(P --- $(P -与之相对的是在工作线程中使用 $(C ownerTid()) 获取其所有者的 ID。 +相应地,使用 $(C ownerTid()) 可以获得工作线程的所有者的 ID。 ) $(P -小结一下:调用 $(C ownerTid) 获取其所有者 ID,通过 $(C spawn()) 的返回值获取工作线程 ID。 +总之,调用 $(C ownerTid) 获取其所有者 ID,通过 $(C spawn()) 的返回值获取工作线程 ID。 ) $(H5 $(IX send) $(IX receiveOnly) 消息传递) $(P -D 语言使用 $(C send()) 发送消息,使用 $(C receiveOnly()) 等待指定类型的消息。(除了它们,标准库还提供了其他实用函数,像 $(C prioritySend())、$(C receive())、$(C receiveTimeout()) 。我们之后会在本章一一介绍。) +$(C send()) 可用于发送消息,而 $(C receiveOnly()) 可用于等待待定类型的消息。(此外还有 $(C prioritySend())、$(C receive()) 和 $(C receiveTimeout()) ——在后面章节介绍它们。) ) $(P -下面这个程序中,线程所有者会向工作线程发送 $(C int) 类型的消息并等待工作线程返回 $(C double) 类型的消息。工作线程会不停地返回消息直到线程所有者发送一个负的 $(C int)。这是所有者线程: +在下面程序里,所有者线程会向工作线程发送 $(C int) 类型的消息,并等待工作线程返回 $(C double) 类型的消息。工作线程会不停地返回消息,一直到所有者线程发送一个 $(C int) 型的负数为止。下面是所有者线程: ) --- @@ -240,7 +240,7 @@ void $(CODE_DONT_TEST)main() { } /* 向工作线程发送一个负数 - * 使其终止*/ + *使其终止*/ $(HILITE worker.send)(-1); } --- @@ -250,7 +250,7 @@ $(C main()) 将 $(C spawn()) 的返回值储存在 $(C worker) 变量中并通 ) $(P -另一边,工作线程需要 $(C int) 类型的消息并对其进行计算,之后将计算得到的 $(C double) 类型的结果返回给其所有者: +另一方面,工作线程需要 $(C int) 类型的消息并对其进行计算,之后将计算得到的 $(C double) 类型的结果返回给其所有者: ) --- @@ -277,7 +277,7 @@ sent: 4, received: 0.8 ) $(P -也可以在一次消息中发送多个值,这些值都会成为这次消息的一部分。下面这个消息就是由三个部分组成: +也可以在消息中一次发送多个值,这些值都会成为同一消息的一部分。下面这个消息就是由三个部分组成: ) --- @@ -285,20 +285,20 @@ $(P --- $(P -如果在一次消息中传递多个值的话,接收者会将它们看作一个 tuple。这样的话 $(C receiveOnly()) 的模版参数的类型要与每一个 tuple 成员的类型对应: +如果在一次消息中传递多个值的话,接收者会将它们看作一个元组。此时,$(C receiveOnly()) 的模版参数的类型要与每一个元组成员的类型对应: ) --- /* 等待一个包含 Tid、int 和 double 类型的消息。*/ auto message = receiveOnly!($(HILITE Tid, int, double))(); - auto sender = message[0]; // Tid - auto integer = message[1]; // int - auto floating = message[2]; // double + auto sender = message[0]; // Tid 类型 + auto integer = message[1]; // int 类型 + auto floating = message[2]; // double 类型 --- $(P -$(IX MessageMismatch) 如果类型不匹配,程序将会抛出一个 $(C MessageMismatch) 异常: +$(IX MessageMismatch) 如果类型不匹配,则程序将会抛出一个 $(C MessageMismatch) 异常: ) --- @@ -311,12 +311,12 @@ void workerFunc() { void main() { spawn(&workerFunc); - auto message = receiveOnly!double(); $(CODE_NOTE 需要 $(HILITE double)) + auto message = receiveOnly!double(); $(CODE_NOTE 期望 $(HILITE double)) } --- $(P -输出为: +输出: ) $(SHELL @@ -325,13 +325,13 @@ Unexpected message type: expected 'double', got 'immutable(char)[]' ) $(P -所有者无法捕获由工作线程抛出的异常。一种解决方案是在工作线程中捕获潜在的由接收信息引发的异常。我们之后会见到的。 +所有者无法捕获由工作线程抛出的异常。一种解决方案是在工作线程中捕获潜在的由接收信息引发的异常。随后就会看到这个。 ) -$(H6 例子) +$(H6 示例) $(P -现在我们来实践一下刚刚学到的东西。 +现在我们在一个模拟程序里实践一下刚了解到的内容。 ) $(P @@ -340,7 +340,7 @@ $(P $(UL -$(LI 机器人的编号(ID):这个参数会随着消息传回线程所有者,这样我们就可以通过它确认消息的来源。 +$(LI 机器人的编号 (id):这个参数会随着消息传回线程所有者,这样我们就可以通过它确认消息的来源。 ) $(LI 起点:机器人的初始位置。 @@ -384,7 +384,7 @@ void robotMover(Job job) { --- $(P -线程所有者仅仅通过一个死循环等待消息。它通过消息中的机器人 ID 来识别机器人,并将将其运动情况输出: +The owner simply waits for these messages in an unconditional loop. 它通过消息中的机器人 ID 来识别机器人。所有者会简单地将其运动情况输出: ) --- @@ -426,7 +426,7 @@ struct Movement { string toString() { return ((from == to) - ?format("%s (idle)", from) + ? format("%s (idle)", from) : format("%s -> %s", from, to)); } } @@ -445,20 +445,20 @@ class Robot { } } -/* 返回一个 0,0 周围的随机位置。*/ +/* 返回一个坐标在 0,0 周边的随机位置。*/ Position randomPosition() { return Position(uniform!"[]"(-10, 10), uniform!"[]"(-10, 10)); } -/* 从指定坐标移动一步,也可能不移动。返回移动后的坐标。*/ +/* 返回一个坐标,它相对从指定坐标最多变化一步。*/ int randomStep(int current) { return current + uniform!"[]"(-1, 1); } /* 返回指定位置周围的坐标。它既可能是 * 八个方向中的一个,也可能是 - * 指定位置本身。*/ + * 指定那个位置本身。*/ Position randomNeighbor(Position position) { return Position(randomStep(position.line), randomStep(position.column)); @@ -507,7 +507,7 @@ void main() { while (true) { auto message = receiveOnly!MovementMessage(); - /* 显示机器人的运动信息。*/ + /* 输出机器人的运动情况。*/ writefln("%s %s", robots[message.robotId], message.movement); } @@ -538,14 +538,14 @@ $(P 这个程序展现了并发的强大之处:机器人的移动可以在单独的线程中独立计算,而且它们之间无需相互交换信息。所有者线程仅仅是将收件箱中的消息一个一个取出来并$(I 按顺序)输出。 ) -$(H5 $(IX delegate, 消息传递) 接收不同类型的消息) +$(H5 $(IX delegate, message passing) 接收不同类型的消息) $(P -$(C receiveOnly()) 只能接收指定的那一个类型的消息。而 $(C receive()) 可以接收多种类型的消息。它通过消息处理委托来处理消息。当它接收到消息时,它会比较消息类型与委托的参数类型。如果委托参数的类型与消息类型相同,它就会把消息交由对应的委托处理。 +$(C receiveOnly()) 只能接收一种类型的消息。而 $(C receive()) 可以接收多种类型的消息。它会把这些消息分发给各个消息处理委托。当接收到消息时,它会将消息类型与每个委托的类型进行比较。如果委托参数的类型与消息类型相同,则把消息交由对应的委托处理。 ) $(P -例如下面这个 $(C receive()) 使用了两个委托来分别处理 $(C int) 和 $(C string) 型的消息: +例如,下面这个 $(C receive()) 使用了两个委托分别用来处理类型为 $(C int) 和 $(C string) 的消息: ) --- @@ -572,7 +572,7 @@ $(CODE_NAME workerFunc)void workerFunc() { --- $(P -$(C int) 消息匹配 $(C intHandler()),而 $(C string) 消息匹配 $(C stringHandler())。可以用下面这个程序测试这个新鲜出炉的工作线程: +$(C int) 消息匹配 $(C intHandler()),而 $(C string) 消息匹配 $(C stringHandler())。上面的工作线程可以用下面程序来测试: ) --- @@ -592,7 +592,7 @@ void main() { --- $(P -程序的输出表明了接收端的函数是如何匹配和处理消息的: +程序的输出说明了接收端的函数是如何匹配和处理消息的: ) $(SHELL @@ -604,11 +604,11 @@ exiting ) $(P -Lambda 函数和定义了 $(C opCall()) 成员函数的对象都可以传递给 $(C receive()) 作为消息处理器。下面这个工作线程使用 lambda 函数处理消息。程序还定义了一个 $(C Exit) 类型来通知线程退出。相对于使用像 -1 这样的任意值,用一个特定的类型来传递特定的消息会让程序更易读。 +lambda 函数和定义了 $(C opCall()) 成员函数的对象都可以传递给 $(C receive()) 作为消息处理器。下面这个工作线程使用 lambda 函数处理消息。程序还定义了一个 $(C Exit) 类型来通知线程退出。相对于使用像 -1 这样的任意值,用一个特定的类型来传递特定的消息会让程序更易读。 ) $(P -有三个匿名函数被传递给了 $(C receive()) 来作为消息处理器。它们的花括号已被高亮: +有 3 个匿名函数被传递给了 $(C receive()) 来作为消息处理器。它们的花括号已被高亮: ) --- @@ -651,7 +651,7 @@ void main() { $(H6 接收任意类型的消息) $(P -$(IX Variant, 并发) $(C std.variant.Variant) 类型可以封装任意类型的数据。如果消息无法与参数列表中 $(C Variant) 之前的任一个处理函数匹配,那它最终将匹配 $(C Variant) 处理函数: +$(IX Variant, concurrency) $(C std.variant.Variant) 类型可以封装任意类型的数据。如果消息无法与参数列表前面指定的各个处理函数相匹配,那么它们将会与一个 $(C Variant) 类型的处理函数匹配: ) --- @@ -680,7 +680,7 @@ void main() { --- $(P -输出为: +输出: ) $(SHELL @@ -688,7 +688,7 @@ Unexpected message: SpecialMessage() ) $(P -有关 $(C Variant) 的详细信息已经超出了本章范围。 +有关 $(C Variant) 的详细内容已超出本章范围。 ) $(H5 $(IX receiveTimeout) 在指定的时间内等待消息) @@ -698,7 +698,7 @@ $(P ) $(P -$(C receiveTimeout()) 的第一个参数决定等待消息时要等待多长时间。如果在指定时间内接收到了消息,函数将返回 $(C true)。如果超时则返回 $(C false)。 +$(C receiveTimeout()) 的第一个参数决定等待消息时要等待多长时间。如果在指定时间内接收到了消息,函数返回值为 $(C true) ;如果超时则返回 $(C false)。 ) --- @@ -725,7 +725,7 @@ void main() { if (!received) { writeln("... no message yet"); - /* ... 可在此处执行其他操作 ... */ + /* ... 可在此处执行其他操作... */ } } } @@ -744,7 +744,7 @@ Waiting for a message received: hello ) -$(H5 $(IX exception, 并发) 工作线程中的异常) +$(H5 $(IX exception, concurrency) 工作线程中的异常) $(P 上一章的 $(C std.parallelism) 自动捕获 task 执行中抛出的异常并在所有者的线程中重新抛出。它使得所有者线程可以捕获工作线程的异常: @@ -778,7 +778,7 @@ $(CODE_NAME calculate)void calculate() { --- $(P -如果字符串不能被转换为 $(C double) 值,$(C to!double()) 会抛出一个异常。由于异常会立刻终止工作线程,所有者只能收到第一条消息的反馈: +如果该字符串不能转换为一个 $(C double) 值,则调用 $(C to!double()) 会抛出异常。由于异常会立刻终止工作线程,所有者只能收到第一条消息的反馈: ) --- @@ -812,7 +812,7 @@ result 0: 1.7 ) $(P -工作线程能做的就是手动捕获异常并将其作为特殊的错误信息发送给所有者。下面这个程序就把出错的原因封装在 $(C CalculationFailure) 消息中传递回去。除此之外,这个程序还使用了特殊的消息类型来通知工作线程退出: +工作线程能做的就是手动捕获异常并将其作为特殊的错误信息发送给所有者。下面这个程序就把出错的原因封装在 $(C CalculationFailure) 消息中传递回去。此外,这个程序还使用了特殊的消息类型来通知工作线程退出: ) --- @@ -864,24 +864,24 @@ void main() { }, (CalculationFailure message) { - writefln("ERROR!'%s'", message.reason); + writefln("ERROR! '%s'", message.reason); }); } } --- $(P -这次错误的原因会被所有者打印出来: +这次错误的原因会被所有者输出: ) $(SHELL result 0: 1.7 -result 1: ERROR!'no digits seen' +result 1: ERROR! 'no digits seen' result 2: 3.9 ) $(P -另外一种方法是直接将将异常对象发送回所有者。所有者既可以处理异常对象也可以重新抛出: +另外一种方法是直接将异常对象发送回所有者。所有者既可以处理异常对象也可以重新抛出: ) --- @@ -893,7 +893,7 @@ $(P ownerTid.send(exc); }}, -// ... 所有者线程中 ... +// ... 所有者线程 ... receive( // ... @@ -903,7 +903,7 @@ $(P --- $(P -我们会在下一章解释为什么此处必须使用 $(C shared) 说明符。 +下一章会解释为什么此处必须使用 $(C shared) 说明符。 ) $(H5 检测线程终止) @@ -915,7 +915,7 @@ $(P $(H6 $(IX OwnerTerminated) $(C OwnerTerminated) 异常) $(P -如果所有者线程已被终止,工作线程在接收消息时就会抛出这个异常。下方程序中处在中间层的线程所有者在发送两条消息后就立即退出。这会导致工作线程抛出 $(C OwnerTerminated) 异常: +如果所有者线程已被终止,工作线程在接收消息时会抛出这个异常。下方程序中处在中间层的线程所有者在发送两条消息后就立即退出。这会导致工作线程抛出 $(C OwnerTerminated) 异常: ) --- @@ -935,7 +935,7 @@ void intermediaryFunc() { void workerFunc() { while (true) { auto m = receiveOnly!int(); // ← 如果 - // 拥有者线程已经终止 + // 拥有者线程已经终止, // 它将抛出异常。 writeln("Message: ", m); } @@ -943,7 +943,7 @@ void workerFunc() { --- $(P -输出为: +输出: ) $(SHELL @@ -975,7 +975,7 @@ void workerFunc() { --- $(P -输出为: +输出: ) $(SHELL @@ -1003,7 +1003,7 @@ void main() { while (true) { auto m = receiveOnly!int(); // ← 如果 - // 工作线程已经终止 + // 工作线程已经终止, // 它将抛出异常。 writeln("Message: ", m); } @@ -1046,7 +1046,7 @@ $(P --- $(P -输出为: +输出: ) $(SHELL @@ -1082,19 +1082,19 @@ $(C OwnerTerminated) 和 $(C LinkTerminated) 都可以作为消息在线程间 } --- -$(H5 收件箱管理) +$(H5 邮箱管理) $(P -每一个线程都有一个用来保存消息的收件箱。收件箱中的消息个数会随着程序接收和处理消息的速度而有所变化。收件箱中持续增加的消息不仅会加重整个系统的负担,还会成为程序设计的瑕疵。这也意味着线程永远只能拿到许久之前接收的消息。 +每一个线程都有一个用来保存消息的邮箱。邮箱中的消息个数会随着程序接收和处理消息的速度而有所变化。邮箱中持续增加的消息不仅会加重整个系统的负担,还会成为程序设计的瑕疵。这也意味着线程永远只能拿到许久之前接收的消息。 ) $(P -$(IX setMaxMailboxSize) $(C setMaxMailboxSize()) 可以限制收件箱保存的消息数量。它的三个参数分别指代的是收件箱、最大保存消息数量和收件箱被填满之后需要进行的操作。最后一个参数有四个选项: +$(IX setMaxMailboxSize)$(C setMaxMailboxSize()) 可以限制邮箱保存的消息数量。它的三个参数分别指代的是邮箱、最大保存消息数量和邮箱被填满之后需要进行的操作。最后一个参数有四个选项: ) $(UL -$(LI $(IX OnCrowding) $(C OnCrowding.block):阻塞发送者直到收件箱中有空闲空间。) +$(LI $(IX OnCrowding) $(C OnCrowding.block):阻塞发送者直到邮箱中有空闲空间。) $(LI $(C OnCrowding.ignore):多余的消息将被抛弃。) @@ -1105,7 +1105,7 @@ $(LI 类型为 $(C bool function(Tid) 的函数指针):调用指定的函数 ) $(P -在接触 $(C setMaxMailboxSize()) 的例子之前,我们先来创建一个消息数量会持续增长的收件箱。下面这个工作线程会不停地向主线程发送消息,但主线程处理消息的速度就没有工作线程这么快了,每条消息主线程都会花费一点时间来处理: +在展示 $(C setMaxMailboxSize()) 示例之前,我们先来创建一个消息数量会持续增长的邮箱。下面这个工作线程会不停地向主线程发送消息,但主线程处理消息的速度就没有工作线程这么快了,每条消息主线程都会花费一点时间来处理: ) --- @@ -1134,7 +1134,7 @@ void main() { --- $(P -因为消费者处理消息的速度远低于生产者产生消息的速度,程序的内存占用会持续增长。为了防止出现这样的情况,线程所有者会在启动工作线程前限制收件箱大小: +因为消费者处理消息的速度远低于生产者产生消息的速度,程序的内存占用会持续增长。为了防止出现这种情况,所有者线程会在启动工作线程之前限制其邮箱的大小: ) --- @@ -1147,11 +1147,11 @@ void $(CODE_DONT_TEST)main() { --- $(P -$(C setMaxMailboxSize()) 将收件箱大小限制为 1000. $(C OnCrowding.block) 会阻塞消息发送者的线程直到收件箱中有空闲空间。 +$(C setMaxMailboxSize()) 将邮箱大小限制为 1000。$(C OnCrowding.block) 会使得发送者线程等待,一直到邮箱中有足够空间为止。 ) $(P -下面这个例子使用了 $(C OnCrowding.throwException)。它将在收件箱满时抛出 $(C MailboxFull) 异常: +下面这个例子使用了 $(C OnCrowding.throwException)。它将在邮箱满时抛出 $(C MailboxFull) 异常: ) --- @@ -1187,7 +1187,7 @@ void main() { $(H5 $(IX prioritySend) $(IX PriorityMessageException) 消息优先级) $(P -可以使用 $(C prioritySend()) 发送高标准优先级的消息。这些高优先级消息会比其他收件箱中的消息先被处理: +可以使用 $(C prioritySend()) 发送高标准优先级的消息。这些高优先级消息会比其他邮箱中的消息先被处理: ) --- @@ -1206,7 +1206,7 @@ Priority message $(H5 线程名) $(P -之前我们看到的程序都很简单,所以在线程间传递线程 ID 还是比较方便的。一旦线程数增加,它将会大大增加程序的复杂度。为了降低这种复杂度,我们可以为线程命名。所有线程都可以通过线程名访问这个线程。 +之前我们看到的程序都很简单,所以在线程间传递线程 ID 还是比较方便的。一旦线程数增加,它将会大大增加程序的复杂度。为了降低复杂度,可以为线程分配一个命名——所有线程都可以通过名字访问该线程。 ) $(P @@ -1236,12 +1236,12 @@ struct Exit { } void main() { - // 兄弟线程为“second” + // 兄弟线程为 "second" auto first = spawn(&player, "second"); $(HILITE register)("first", first); scope(exit) $(HILITE unregister)("first"); - // 兄弟线程为“first” + // 兄弟线程为 "first" auto second = spawn(&player, "first"); $(HILITE register)("second", second); scope(exit) $(HILITE unregister)("second"); @@ -1287,7 +1287,7 @@ $(IX thread_joinAll) $(C main()) 末尾处的 $(C thread_joinAll()) 会阻塞 ) $(P -输出为: +输出: ) $(SHELL @@ -1307,15 +1307,15 @@ $(H5 小结) $(UL -$(LI 如果线程相互独立,推荐使用上一章的 $(I parallelism)。只有线程间有相互依赖的操作时再考虑 $(I concurrency)。) +$(LI 如果线程间相互独立,推荐使用上一章的 $(I 并行)。只有在线程间有相互依赖的操作时才考虑 $(I 并发)。) -$(LI 基于数据共享的并行难以编写出正确的代码,所以推荐使用本章讲解的消息传递并行。) +$(LI 基于数据共享的并发难以正确实现,因此推荐使用本章讲解的基于消息传递的并发。) $(LI $(C spawn()) 和 $(C spawnLinked()) 用于启动线程。) -$(LI $(C thisTid) 为当前线程的线程 ID。) +$(LI $(C thisTid) 为当前线程的 ID。) -$(LI $(C ownerTid) 为当前线程所有者的线程 ID。) +$(LI $(C ownerTid) 为当前线程的所有者的 ID。) $(LI $(C send()) 和 $(C prioritySend()) 用于发送消息。) @@ -1323,21 +1323,21 @@ $(LI $(C receiveOnly())、$(C receive()) 和 $(C receiveTimeout()) 用于等待 $(LI $(C Variant) 用来匹配所有类型的消息。) -$(LI $(C setMaxMailboxSize()) 用来限制收件箱大小。) +$(LI $(C setMaxMailboxSize()) 用来限制邮箱大小。) $(LI $(C register())、$(C unregister()) 和 $(C locate()) 允许程序员通过线程名访问线程。) $(LI 消息传递的过程中也可能会抛出异常:$(C MessageMismatch)、$(C OwnerTerminated)、$(C LinkTerminated)、$(C MailboxFull) 以及 $(C PriorityMessageException)。) -$(LI 所有者无法自动捕获工作线程中的异常。) +$(LI 所有者线程无法自动捕获工作线程中的异常。) ) macros: - SUBTITLE=并发消息传递 + SUBTITLE=基于消息传递的并发 - DESCRIPTION=在 D 语言中启动多个线程并通过消息传递实现多线程交互 + DESCRIPTION=在 D 语言中启动多个线程并通过消息传递实现多线程交互。 - KEYWORDS=D 编程语言教程 线程并发 + KEYWORDS=d programming language tutorial book concurrency thread 编程 语言 教程 书籍 并发 线程 MINI_SOZLUK= diff --git a/ddili/src/ders/d.cn/concurrency_shared.d b/target/concurrency_shared.d similarity index 69% rename from ddili/src/ders/d.cn/concurrency_shared.d rename to target/concurrency_shared.d index ed0ae7e..1c44772 100644 --- a/ddili/src/ders/d.cn/concurrency_shared.d +++ b/target/concurrency_shared.d @@ -1,13 +1,13 @@ Ddoc -$(DERS_BOLUMU $(IX 并发数据共享) $(IX 并发, 数据共享) 并发数据共享) +$(DERS_BOLUMU $(IX data sharing concurrency) $(IX concurrency, data sharing) 基于数据共享的并发) $(P -在上一章中,我们使用消息传递实现线程间的信息共享。消息传递是并发中相对安全的概念,这点我们已经提到了多次。 +在上一章中,我们使用消息传递实现线程间的信息共享。前面章节已提到,消息传递是一种比较安全的并发方法。 ) $(P -另外一种共享消息的方法是多个线程读写同一块数据。比如所有者线程在启动工作线程的同时向其传递了一个 $(C bool) 值的地址,工作线程可通过读取这个值来判断是否需要终止。或者所有者线程将同一个变量地址发给多个工作线程,这些工作线程可通过修改或读取这个变量来从其他线程获取信息。 +另外一种共享消息的方法是多个线程读写同一块数据。例如,所有者线程在启动工作线程的同时向其传递了一个 $(C bool) 值的地址,工作线程可通过读取这个值来判断是否需要终止。或者所有者线程将同一个变量地址发给多个工作线程,这些工作线程可通过修改或读取这个变量来从其他线程获取信息。 ) $(P @@ -15,13 +15,13 @@ $(I 竞态条件)是数据共享不够安全的原因之一。当多个线程以 ) $(P -本章中的例子看起来都很简单。但是在实际编程中它们代表的问题通常规模很大。除此之外,虽然本章的例子使用的都是 $(C std.concurrency) 模块,但它们所包含的概念同样适用于 $(C core.thread) 模块。 +本章中的例子看起来都很简单。但是在实际编程中它们代表的问题通常规模很大。此外,虽然本章的示例都在使用 $(C std.concurrency) 模块,但这些概念同样适用于 $(C core.thread) 模块。 ) $(H5 共享不是自动的) $(P -D 语言的数据不会自动共享,默认情况下数据只能在其自己的线程中使用。这与其他编程语言有很大的区别。虽然每个线程都可以访问模块级的变量,但实际上它们访问的都是对应变量在自己线程中的副本: +与其他大部分编程语言的不同之处在于,D 语言里的数据不会自动共享(默认情况下,数据仅限于线程本地。虽然每个线程都可以访问模块级的变量,但实际上它们访问的都是对应变量在自己线程中的副本: ) --- @@ -48,7 +48,7 @@ void main() { --- $(P -$(C worker()) 中修改的 $(C variable) 与 $(C main()) 访问的 $(C variable) 是不同的。这一点你可以从输出的变量值和地址看出来: +$(C worker()) 修改的 $(C variable) 与 $(C main()) 访问的 $(C variable) 并不相同。从这些变量的值和地址输出可以看出来: ) $(SHELL @@ -57,7 +57,7 @@ After the worker is terminated: 0 (@7F26C68127D0) ) $(P -由于每个线程都有一份单独的数据拷贝,$(C spawn()) 不允许以引用的形式传递线程内的值。比如下面这个程序,试图传递 $(C bool) 值的地址只会导致编译错误: +由于每个线程都有一份单独的数据拷贝,$(C spawn()) 不允许以引用的形式传递线程内的值。例如,下面这个程序,试图传递 $(C bool) 值的地址只会导致编译错误: ) --- @@ -104,13 +104,13 @@ __gshared int globallyShared; --- $(P -这样的话程序中只会有一 $(C globallyShared),它会在所有线程间共享。在与像 C 和 C++ 这样默认自动数据共享的语言编写的库交互时,$(C __gshared) 是必须的。 +这样的话,程序中只会有一 $(C globallyShared),它会在所有线程间共享。在与 C 和 C++ 这样的语言编写的库(它们默认自动共享数据)交互时,$(C __gshared) 必不可少。 ) -$(H5 $(IX shared) 用 $(C shared) 在线程间共享数据) +$(H5 $(IX shared) 用 $(C shared) 在线程间共享数据)) $(P -需要共享的可变变量必须使用 $(C shared) 关键自定义: +需要共享的可变变量必须使用关键字 $(C shared) 来定义: ) --- @@ -136,11 +136,11 @@ void main() { --- $(P -$(I $(B 注:) 推荐使用消息传递向线程发送控制信号。) +$(I $(B 注意:) 推荐使用消息传递向线程发送控制信号。) ) $(P -$(IX immutable, 并发) $(IX immutable, concurrency) 而由于 $(C immutable) 变量无法被修改,它们可以直接共享。所以实际上 $(C immutable) 隐含了 $(C shared): +$(IX immutable, concurrency) 另一方面,由于 $(C immutable) 变量无法被修改,它们可以直接共享。因此,$(C immutable) 隐含了 $(C shared): ) --- @@ -154,14 +154,14 @@ void worker($(HILITE immutable(int)) * data) { void main() { $(HILITE immutable(int)) i = 42; - spawn(&worker, &i); // ← 通过编译 + spawn(&worker, &i); // ← 编译正常 thread_joinAll(); } --- $(P -输出为: +输出: ) $(SHELL @@ -169,7 +169,7 @@ data: 42 ) $(P -注意 $(C i) 的生命期即为 $(C main()) 函数作用域,所以为了防止出现错误应确保 $(C main()) 函数在工作线程终止后才返回。所以我们调用了 $(C core.thread.thread_joinAll) 函数阻塞主线程来等待子线程执行完毕。 +请注意:$(C i) 的生命期即为 $(C main()) 函数作用域,所以为了防止出现错误应确保 $(C main()) 函数在工作线程终止后才返回。所以,我们调用 $(C core.thread.thread_joinAll) 函数阻塞主线程来等待子线程执行完毕。 ) $(H5 竞态条件示例) @@ -222,7 +222,7 @@ $(P $(SHELL before: 1 and 2 -after : 1 and 2 $(SHELL_NOTE 预期结果) +after : 1 and 2 $(SHELL_NOTE 期望的结果) ) $(P @@ -248,7 +248,7 @@ $(P ) $(P -先来看下 $(C i) 为 1 $(C j) 为 2 的情况。虽然两个线程执行的都是 $(C swapper()) 函数,但别忘了由于 $(C temp) 是是个局部变量,每个线程都会拥有一个独立的 $(C temp) 副本。为了区分这两个 $(C temp),我们将其分别称为 $(C tempA) 和 $(C tempB) 。 +先来看下 $(C i) 为 1 $(C j) 为 2 的情况。虽然两个线程执行的都是 $(C swapper()) 函数,但别忘了由于 $(C temp) 是是个局部变量,每个线程都会拥有一个独立的 $(C temp) 副本。为了区分这两个 $(C temp),我们将其分别称为 $(C tempA) 和 $(C tempB)。 ) $(P @@ -256,7 +256,7 @@ $(P ) $(MONO -$(B Operation Thread A Thread B) +$(B 操作 线程 A 线程 B) ──────────────────────────────────────────────────────────────────────────── 1: int temp = *second; (tempA==2) @@ -277,17 +277,17 @@ $(B Operation Thread A Thread B) ) $(P -经过这种情况的执行后 $(C i) 和 $(C j) 的值最后都变成了 1。此处不可能会再有其他的值了。 +经过这种情况的执行后 $(C i) 和 $(C j) 的值最后都变成了 1。此处不可能会再有其他的值。 ) $(P 上面那种情况只是为了解释程序得出错误结果的原因而创建的例子。10 条线程的实际情况要比例子复杂得多。 ) -$(H5 $(IX synchronized) 使用 $(C synchronized) 避免竞态条件) +$(H5 $(IX synchronized) $(C synchronized) 避免竞态条件) $(P -程序出错的原因是多个线程访问同一块可变数据(并且其中至少有一条线程修改了数据)。一种解决方案是使用 $(C synchronized) 关键字标记公共代码以消除竞态条件。经过下面的修改程序就能正确执行了: +程序出错的原因是多个线程访问同一块可变数据(并且其中至少有一条线程修改了数据)。一种解决方案是使用关键字 $(C synchronized) 标记公共代码以消除竞态条件。经过下面的修改之后程序能正确执行: ) --- @@ -301,7 +301,7 @@ $(P --- $(P -输出为: +输出: ) $(SHELL @@ -310,11 +310,11 @@ after : 1 and 2 $(SHELL_NOTE 正确结果) ) $(P -$(IX lock) $(C synchronized) 会在后台创建一个锁,同一时间只有一个线程能持有这个锁。只有持有锁的那个线程才可以执行,其他线程都需要等待持有锁的线程执行完成并释放 $(C synchronized) 锁。由于同一时间只有一个执行 $(I synchronized) 代码的线程,我们就可以安全的进行交换。在同步块执行后 $(C i) 与 $(C j) 只会有种情况:要么是“1 和 2”,要么是“2 和 1”。 +$(IX lock) $(C synchronized) 会在后台创建一个锁,同一时间只有一个线程能持有这个锁。只有持有锁的那个线程才可以执行,其他线程都需要等待持有锁的线程执行完成并释放 $(C synchronized) 锁。由于同一时间只有一个执行 $(I synchronized) 代码的线程,我们就可以安全的进行交换。在同步块执行后 $(C i) 与 $(C j) 只会有种情况:要么是“1 and 2”,要么是“2 and 1”。 ) $(P -$(I $(B 注:)等待锁是一个相对昂贵的操作,它可能会显著降低程序的执行速度。幸运的是大多数程序可以使用$(I 原子操作)替代 $(C synchronized) 块。一会我们会介绍它。) +$(I $(B 注意:) 等待锁是一个相对昂贵的操作,它可能会显著降低程序的执行速度。幸运的是大多数程序可以使用 $(I 原子操作) 替代 $(C synchronized) 块(随后会介绍它)。) ) $(P @@ -322,7 +322,7 @@ $(P ) $(P -下面的例子包含两个独立的访问共享变量的代码块。程序会将同一个变量的地址传递给这两个函数,一个函数对其加 1,一个函数对其减 1,加减次数相同: +下面的例子包含两个独立的访问共享变量的代码块。程序会将同一个变量的地址传递给这两个函数:一个函数将其加 1,一个函数将其减 1,加减次数相同: ) --- @@ -340,11 +340,11 @@ void decrementer(shared(int) * value) { --- $(P -$(I $(B 注:)如果将上方的等式换成自增或自检(比如 $(C ++(*value)) 和 $(C ‑‑(*value))),编译器会警告:对 $(C shared) 变量执行的读取-修改-写入操作已被弃用。) +$(I $(B 注意:) 如果将上方的等式换成自增或自检(例如, $(C ++(*value)) 和 $(C ‑‑(*value))),编译器会警告:对 $(C shared) 变量执行的读取-修改-写入操作已被弃用。) ) $(P -然而直接使用 $(C synchronized) 并不能起到我们预期的效果,因为两个代码块的匿名锁是相互独立的。所以这两块代码还是会同时访问那个变量: +很可惜,直接使用 $(C synchronized) 并不能起到我们预期的效果,因为两个代码块的匿名锁是相互独立的。因此,这两块代码还是会同时访问那个变量: ) --- @@ -384,7 +384,7 @@ void main() { --- $(P -线程数相同且加减次数相同,可能有人就会认为 $(C number) 的最终结果是 0。然而这个程序几乎不会得出这个结果: +线程数相同且加减次数相同,可能有人就会认为 $(C number) 的最终结果是 0。但是,这个程序几乎不会得出这个结果: ) $(SHELL @@ -392,15 +392,20 @@ Final value: -672 $(SHELL_NOTE_WRONG 不是 0) ) $(P -为了能够给多个代码块套上同样的锁,你必须在 $(C synchronized) 后加一个圆括号并在其中指定锁对象: +为了能够给多个代码块加上同一个锁,必须在 $(C synchronized) 后加一个圆括号,并在其中指定锁对象: +) + +$(P +$(HILITE $(I $(B 注意:) dmd 2.078.0 不支持此功能。)) ) --- + // 注意:dmd 2.078.0 不支持此功能。 synchronized ($(I lock_object), $(I another_lock_object), ...) --- $(P -D 中没有专门的 lock 类型,任何类型都可以作为 $(C synchronized) 锁。下面这个程序定义了一个空的 $(C Lock) 类作为锁: +D 语言中没有专门的锁类型,任何类型都可以作为 $(C synchronized) 锁。下面这个程序定义了一个空的 $(C Lock) 类作为锁: ) --- @@ -444,7 +449,7 @@ void main() { --- $(P -这次两个 $(C synchronized) 块使用了同一个锁,同一时间它们中只有一个可以执行。这样最终结果就与我们预期的 0 一样了: +这次两个 $(C synchronized) 块使用了同一个锁,因此在同一时间它们当中只有一个可以执行。 ) $(SHELL @@ -452,7 +457,7 @@ Final value: 0 $(SHELL_NOTE 正确结果) ) $(P -类也可以定义为 $(C synchronized)。这样同一时间只能有一个线程调用类的示例对象的非静态成员函数: +类也可以定义为 $(C synchronized)。即表示:当给定了该类的某个对象时,它的所有非静态成员函数的类型都是 synchronized: ) --- @@ -507,26 +512,31 @@ void transferMoney(shared BankAccount from, --- $(P -$(IX 死锁) 我们会用一个例子来解释程序为什么出错。示例中有一个线程想要将账户 A 中的资金转到账户 B,而另一个线程想要将账户 B 中的资金转到账户 A。每个线程都会先锁住各自的 $(C from) 对象,然后再尝试去锁 $(C to) 对象。由于代表 A 和 B 的 $(C from) 对象已分别被两个线程锁住,它们将无法获取另一个线程的 $(C to) 对象(即刚刚被锁住的 B 和 A)。这个现象就是$(I 死锁)。 +$(IX deadlock) 我们会用一个例子来解释程序为什么出错。示例中有一个线程想要将账户 A 中的资金转到账户 B,而另一个线程想要将账户 B 中的资金转到账户 A。每个线程都会先锁住各自的 $(C from) 对象,然后再尝试去锁 $(C to) 对象。由于代表 A 和 B 的 $(C from) 对象已分别被两个线程锁住,它们将无法获取另一个线程的 $(C to) 对象(即刚刚被锁住的 B 和 A)。这个现象就是$(I 死锁)。 ) $(P 解决方法是为对象定义一个顺序并按照这个顺序锁住对象。如果使用 $(C synchronized) 语句这个过程将被自动实现。对于 D 语言,在同一个 $(C synchronized) 中指定这些对象即可有效避免死锁的情况: ) +$(P +$(HILITE $(I $(B 注意:) dmd 2.078.0 不支持此功能。)) +) + --- void transferMoney(shared BankAccount from, shared BankAccount to) { + // 注意:dmd 2.078.0 不支持此功能。 synchronized (from, to) { $(CODE_NOTE 正确) // ... } } --- -$(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()) 单次初始化和使用 $(C shared static ~this()) 单次析构) +$(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()) 用于单次初始化;$(C shared static ~this()) 用于单次析构) $(P -我们已经见到过 $(C static this()),它是用来初始化模块和模块中包含的变量的。由于默认情况下每个线程都会有一个数据的副本,$(C static this()) 需要在每个线程中执行一次来初始化对应线程中的模块级变量: +我们已经见到过 $(C static this()),它是用来初始化模块和模块中包含的变量的。因为默认情况下数据都是本地线程所有,因此每个线程都必须执行一次 $(C static this()),以便为所有线程初始化模块级的变量: ) --- @@ -558,12 +568,12 @@ executing static this() ) $(P -对于指定了 $(C share) 的模块变量来说,重复初始化有可能导致并发中的竞态条件而造成程序出错。(这也适用于 $(C immutable) 因为它已隐含 $(C shared)。)解决方案是使用 $(C shared static this()) 块,它只会在程序中执行一次: +对于指定了 $(C shared) 的模块变量来说,重复初始化有可能导致并发中的竞态条件而造成程序出错。(这也适用于 $(C immutable) 因为它已隐含 $(C shared)。)解决方案是使用 $(C shared static this()) 块,它只会在程序中执行一次: ) --- -int a; // 每个线程拥有一个独立副本 -immutable int b; // 被所有线程共享 +int a; // 仅限线程本地 +immutable int b; // 所有线程共享 static this() { writeln("Initializing per-thread variable at ", &a); @@ -577,7 +587,7 @@ $(HILITE shared) static this() { --- $(P -输出为: +输出: ) $(SHELL @@ -587,7 +597,7 @@ Initializing per-thread variable at 7FBDB3554670 ) $(P -$(C shared static ~this()) 也一样,每个程序只会执行一次来释放资源。 +同样地,$(C shared static ~this()) 适用于结束操作——每个程序只会执行一次来释放资源。 ) $(H5 $(IX atomic operation) 原子操作) @@ -597,13 +607,13 @@ $(P ) $(P -D 语言中的原子操作都在 $(C core.atomic) 模块中。本章我们只会接触到其中两种: +D 语言中的原子操作都在 $(C core.atomic) 模块里。本章我们只会接触到其中两种: ) $(H6 $(IX atomicOp, core.atomic) $(C atomicOp)) $(P -这个函数会将它的模版参数应用到两个函数参数上。它的模版参数必须是一个$(I 二元运算符),比如 $(STRING “+”),$(STRING “+=”)。 +这个函数会将它的模版参数应用到两个函数参数上。它的模版参数必须是一个$(I 二元运算符),如 $(STRING "+")、$(STRING "+=") 等。 ) --- @@ -623,7 +633,7 @@ $(P --- $(P -如果只是一个二元运算的话,我们没必要使用会影响效率的 $(C synchronized) 块。下面是使用 $(C atomicOp) 的等价 $(C incrementer()) 和 $(C decrementer()) 函数。注意现在不再需要 $(C Lock) 类了: +如果只是一个二元运算的话,我们没必要使用会影响效率的 $(C synchronized) 块。下面是$(C incrementer()) 和 $(C decrementer()) 函数使用 $(C atomicOp) 之后的等效情况。请注意,现在不再需要 $(C Lock) 类: ) --- @@ -659,7 +669,7 @@ $(P --- $(P -如果 $(C cas()) 开始执行后变量的值还是等于 $(C currentValue),则说明自当前线程读入后这个变量没有被其他线程修改。这样的话 $(C cas()) 会将 $(C newValue) 赋给这个变量并返回 $(C true)。如果 $(C cas()) 发现变量的值不再等于 $(C currentValue) 了,它将直接返回 $(C false),不再修改变量的值。 +在 $(C cas()) 开始执行时,如果变量的值还是等于 $(C currentValue),则说明自当前线程读入后这个变量没有被其他线程修改。这样的话 $(C cas()) 会将 $(C newValue) 赋给这个变量并返回 $(C true)。另一方面,如果 $(C cas()) 发现变量的值不再等于 $(C currentValue) ,那么它将直接返回 $(C false),不再修改变量的值。 ) $(P @@ -701,7 +711,7 @@ $(P ) $(P -$(C core.sync) 中的模块包含了一些古老的并发工具,感兴趣的话可以去了解一下: +你也可以深入看看 $(C core.sync) 包中,在它的以下模块中包含了很多经典的并发基本操作: ) $(UL @@ -720,9 +730,9 @@ $(H5 小结) $(UL -$(LI 如果线程相互独立,优先选择$(I 并行)。只有线程间有相互依赖的操作时再考虑$(I 并发)。) +$(LI 如果线程相互独立,优先选择$(I 并行)。只有在线程间有相互依赖的操作时才考虑 $(I 并发)。) -$(LI 若要使用并发,优先选择上一章的$(I 并发消息传递)模型。) +$(LI 若要使用并发,优先选择上一章的 $(I 基于消息传递的并发) 模型。) $(LI 只有用 $(C shared) 定义的变量才能共享;$(C immutable) 隐含了 $(C shared)。) @@ -736,13 +746,14 @@ $(LI $(C static this()) 会为每个线程执行一次;$(C shared static this( $(LI $(C core.atomic) 模块提供安全的数据共享方案,而且还比 $(C synchronized) 快很多倍。) -$(LI $(C core.sync) 包包含许多其他古老的并发工具。) +$(LI $(C core.sync) 包包含了许多其他经典的并发基本操作。) ) macros: - SUBTITLE=并发数据共享 + SUBTITLE=数据共享与并发 + + DESCRIPTION=执行多条共享数据的线程 - DESCRIPTION=执行多条线程并在线程间共享数据 + KEYWORDS=d programming language tutorial book concurrency thread data sharing 编程 语言 教程 书籍 并发 线程 数据 共享 - KEYWORDS=D 编程语言教程 线程并发 数据共享 diff --git a/ddili/src/ders/d.en/cond_comp.d b/target/cond_comp.d similarity index 92% rename from ddili/src/ders/d.en/cond_comp.d rename to target/cond_comp.d index 24b6041..6ea1ec8 100644 --- a/ddili/src/ders/d.en/cond_comp.d +++ b/target/cond_comp.d @@ -30,69 +30,6 @@ $(P Unit tests and contracts are about program correctness; whether they are included in the program should not change the behavior of the program. ) -$(UL - -$(LI -Template specializations are compiled into the program only for specific types. When a specialization is not actually used in the program, the specialization is not compiled: - ---- -void swap(T)(ref T lhs, ref T rhs) { - T temp = lhs; - lhs = rhs; - rhs = temp; -} - -unittest { - auto a = 'x'; - auto b = 'y'; - swap(a, b); - - assert(a == 'y'); - assert(b == 'x'); -} - -void swap(T $(HILITE : uint))(ref T lhs, ref T rhs) { - lhs ^= rhs; - rhs ^= lhs; - lhs ^= rh; // TYPO! -} - -void main() { -} ---- - -$(P -The $(C uint) specialization above has been implemented by taking advantage of the $(C ^) ($(I xor)) operator, presumably under the belief that it would be executed faster than the general algorithm. ($(I $(B Note:) To the contrary, on most modern microprocessors this method is slower than the one that uses a temporary variable.)) -) - -$(P -Despite the typo at the end of that specialization, since the $(C uint) specialization is never actually used, the program gets compiled without any errors. -) - -$(P -$(I $(B Note:) This is another example of how important unit tests are; the mistake would have been noticed if there were a unit test for that specialization: -) -) - ---- -unittest { - $(HILITE uint) i = 42; - $(HILITE uint) j = 7; - swap(i, j); - - assert(i == 7); - assert(j == 42); -} ---- - -$(P -This example shows that template specializations are also compiled under certain conditions. -) - -) - -) - $(P The following are the features of D that are specifically for conditional compilation: ) @@ -701,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/ddili/src/ders/d.cn/const_and_immutable.d b/target/const_and_immutable.d similarity index 98% rename from ddili/src/ders/d.cn/const_and_immutable.d rename to target/const_and_immutable.d index e6113c0..e17473a 100644 --- a/ddili/src/ders/d.cn/const_and_immutable.d +++ b/target/const_and_immutable.d @@ -67,7 +67,7 @@ We have seen earlier in the $(LINK2 /ders/d.en/enum.html, $(C enum) chapter) tha --- $(P -As long as their values can be determined at compile time, $(C enum) variables can be initialized by the return values of functions as well: +As long as their values can be determined at compile time, $(C enum) variables can be initialized with return values of functions as well: ) --- @@ -89,6 +89,11 @@ void main() { } --- +$(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: ) @@ -143,18 +148,6 @@ $(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. ) -$(P -$(C enum) constants can be initialized with results of function calls: -) - ---- - enum a = makeArray(); // called at compile time ---- - -$(P -This is possible with D's $(I compile time function execution) (CTFE) feature, which we will see in $(LINK2 /ders/d.en/functions_more.html, a later chapter). -) - $(H6 $(IX variable, immutable) $(C immutable) variables) $(P @@ -728,7 +721,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -739,7 +732,7 @@ olleh ) -$(H5 Summary) +$(H5 摘要) $(UL 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/ddili/src/ders/d.cn/contracts.cozum.d b/target/contracts.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/contracts.cozum.d rename to target/contracts.cozum.d diff --git a/ddili/src/ders/d.cn/contracts.d b/target/contracts.d similarity index 99% rename from ddili/src/ders/d.cn/contracts.d rename to target/contracts.d index f4e83f2..a439c59 100644 --- a/ddili/src/ders/d.cn/contracts.d +++ b/target/contracts.d @@ -245,7 +245,7 @@ We have seen in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) ) $(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 have seen in the $(LINK2 /ders/d.en/assert.html, $(C assert) and $(C enforce) chapter): +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 diff --git a/ddili/src/ders/template/copyright.d b/target/copyright.d similarity index 85% rename from ddili/src/ders/template/copyright.d rename to target/copyright.d index 3d18fe4..9cd52e6 100644 --- a/ddili/src/ders/template/copyright.d +++ b/target/copyright.d @@ -22,7 +22,7 @@ $(BR) $(BR) $(P -Copyleft (ɔ) 2009-2015 Ali Çehreli +Copyleft (ɔ) 2009-2017 Ali Çehreli ) $(BR) @@ -87,18 +87,14 @@ Other ebook versions are generated with Calibre ) $(BR) $(P -Printed by IngramSpark -) -$(P -ISBN 978-0-692-52957-7 -) -$(BR) -$(P -or by CreateSpace -) -$(P -ISBN 978-1515074601 +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
  • +
diff --git a/ddili/src/ders/d.cn/cozum_ozel.ddoc b/target/cozum_ozel.ddoc similarity index 61% rename from ddili/src/ders/d.cn/cozum_ozel.ddoc rename to target/cozum_ozel.ddoc index 1f22b3b..bf84091 100644 --- a/ddili/src/ders/d.cn/cozum_ozel.ddoc +++ b/target/cozum_ozel.ddoc @@ -1,5 +1,5 @@ -MAIN_TITLE=D语言教室 -CLASS=solution +MAIN_TITLE=D 语言答案 +CLASS=答案 COZUM_BOLUMU = $(H4 $0) LANG=zh-cn LANGUAGE=chinese diff --git a/ddili/src/ders/d.cn/derse_ozel.ddoc b/target/derse_ozel.ddoc similarity index 80% rename from ddili/src/ders/d.cn/derse_ozel.ddoc rename to target/derse_ozel.ddoc index 9af4d71..fba5bd7 100644 --- a/ddili/src/ders/d.cn/derse_ozel.ddoc +++ b/target/derse_ozel.ddoc @@ -1,10 +1,10 @@ -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 -HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS Programming in D RSS Feed) +HORIZNAV_CONTENT_DERSE_OZEL=$(LINK2 /ders/d.cn/rss.xml, RSS RSS 订阅) $(BR) $(LINK2 /ders/d.cn/index.html, 下载或购买 $(IMG book.png)) @@ -39,9 +39,9 @@ GERI_METIN=上一章 ILERI_METIN=下一章 PROBLEM_METIN=练习 PROBLEM_COK_METIN=练习 -PROBLEM_TEK_COZUMSUZ_METIN=答案将稍后发表…… -PROBLEM_COK_COZUMSUZ_METIN=答案将稍后发表…… -COZUM_METIN=解答 -COZUMLER_METIN=解答 +PROBLEM_TEK_COZUMSUZ_METIN=答案将随后公布…… +PROBLEM_COK_COZUMSUZ_METIN=答案将随后公布…… +COZUM_METIN=答案 +COZUMLER_METIN=答案 -DERLEME_HATASI_METIN=编译出错啦 +DERLEME_HATASI_METIN=编译出错 diff --git a/ddili/src/ders/d.cn/destroy.d b/target/destroy.d similarity index 99% rename from ddili/src/ders/d.cn/destroy.d rename to target/destroy.d index 8d1273d..cb22730 100644 --- a/ddili/src/ders/d.cn/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: @@ -351,7 +351,7 @@ $(SHELL $(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 have seen in the $(LINK2 /ders/d.en/exceptions.html, Exceptions chapter). +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 @@ -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/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/ddili/src/ders/d.en/encapsulation.d b/target/encapsulation.d similarity index 99% rename from ddili/src/ders/d.en/encapsulation.d rename to target/encapsulation.d index b55e100..9b1d306 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.cn/enum.cozum.d b/target/enum.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/enum.cozum.d rename to target/enum.cozum.d diff --git a/ddili/src/ders/d.en/enum.d b/target/enum.d similarity index 96% rename from ddili/src/ders/d.en/enum.d rename to target/enum.d index 9d38a6f..6490b24 100644 --- a/ddili/src/ders/d.en/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 @@ -205,7 +205,11 @@ $(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). ) -$(H5 Properties) +$(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: @@ -241,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 new file mode 100644 index 0000000..ca4c3c3 --- /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 +输出: +) + +$(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 摘要) + +$(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/ddili/src/ders/d.cn/fibers.d b/target/fibers.d similarity index 68% rename from ddili/src/ders/d.cn/fibers.d rename to target/fibers.d index 530e306..d54b618 100644 --- a/ddili/src/ders/d.cn/fibers.d +++ b/target/fibers.d @@ -1,27 +1,27 @@ Ddoc -$(DERS_BOLUMU $(IX fiber) Fibers) +$(DERS_BOLUMU $(IX fiber) 纤程) $(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). +$(IX coroutine) $(IX green thread) $(IX thread, green) 纤程(Fiber)是一个$(I执行线程),它可以让单个线程完成多个任务。与并行和并发里普遍使用的常规线程相比,纤程之间的切换具有更高的效率纤程与$(I协程(Coroutine))和$(I绿色线程)相似。 ) $(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. +协程可以让每个线程有多个调用栈。因此,想要完全理解和用好纤程,你必须先要理解线程的$(I调用栈(call stack))。 ) -$(H5 $(IX call stack) $(IX program stack) Call stack) +$(H5 $(IX call stack) $(IX program 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. +$(IX local state) 函数的参数、非静态变量、返回值、临时表达式,以及在它执行期间需要的所有附加信息组合成了它的$(I本地状态)。函数的本地状态会在该函数每次被调用时自动分配和初始化。 ) $(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. +$(IX stack frame) 为函数调用的本地状态分配的存储空间叫$(I帧(frame)) 或$(I栈帧(stack frame))。在线程执行期间,函数会调用其他函数,因此它们的帧会在概念上重叠在一起,从而形成一堆的帧。当前正激活的函数调用的帧构成的栈即为该线程的$(I调用栈)。 ) $(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()): +例如,当下面程序的主线程开始执行 $(C bar()) 函数时, 会有 3 层激活的函数调用,即 $(C main()) 调用了 $(C foo()),而 $(C foo()) 又调用了 $(C bar()): ) --- @@ -44,58 +44,58 @@ void bar(int param) { --- $(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: +在 $(C bar()) 的执行期间,调用栈里面有 3 帧,分别存储的是那些当前正激活函数调用的本地状态: ) $(MONO -The call stack grows upward -as function calls get deeper. ▲ ▲ +随着函数调用的深入, +调用栈会不断往上增长。▲ ▲ │ │ - Top of the call stack → ┌──────────────┐ - │ int param │ ← bar's frame + 调用栈的顶部 → ┌──────────────┐ + │ int param │ ← bar 的帧 │ string[] arr │ ├──────────────┤ │ int x │ - │ int y │ ← foo's frame + │ int y │ ← foo 的帧 │ return value │ ├──────────────┤ │ int a │ - │ int b │ ← main's frame + │ int b │ ← main 的帧 │ 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: +函数的调用层次会在函数调用其他函数时变得更深,而在函数返回时变浅,因此调用栈的大小也会相应地增大和减小。例如,当 $(C bar()) 返回之后,它的帧并不再需要,而其空间随后会被其他函数调用使用: ) $(MONO $(LIGHT_GRAY ┌──────────────┐) $(LIGHT_GRAY │ int param │) $(LIGHT_GRAY │ string[] arr │) - Top of the call stack → ├──────────────┤ + 调用栈的顶部 → ├──────────────┤ │ int a │ - │ int b │ ← foo's frame + │ int b │ ← foo 的帧 │ return value │ ├──────────────┤ │ int a │ - │ int b │ ← main's frame + │ int b │ ← main 的帧 │ 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) +$(H6 $(IX 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). +递归指的是这样一种情况:函数直接或间接地调用自己对于某些类型的算法(如$(I分治法)),使用递归会显得特别简单。 ) $(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: +一起来看看下面这个函数,它会计算一个分片里所有元素的总和。它采用递归调用自己的方式(所用的分片是比它所接收到那个分片少一个元素)实现了这一个功能。这个递归会一直持续到接收到的分片为空才会结束。当前结果则通过下一个递归的第二个参数带回: ) --- @@ -103,13 +103,13 @@ 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); } @@ -119,52 +119,52 @@ void main() { --- $(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.) +$(IX sum, std.algorithm) $(I $(B 注意:)上面的代码仅用于演示。其实,想要计算某个范围里所有元素的总和,可以使用 $(C std.algorithm.sum)——它针对浮点类型使用了特殊的算法来实现更精确的计算。) ) $(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: +对于上面的初始参数 $(C [1, 2, 3]) ,当最后使用它的空分片调用 $(C sum()) 时,调用栈的相关部分构成了下面这些帧。每个参数的值会在 $(C ==) 符号之后标明。一定要记得从下往上来看帧的内容: ) $(MONO ┌─────────────────────────┐ - │ arr == [] │ ← final call to sum + │ arr == [] │ ← 最后一次调用 sum │ currentSum == 6 │ ├─────────────────────────┤ - │ arr == [3] │ ← third call to sum + │ arr == [3] │ ← 第三次调用 sum │ currentSum == 3 │ ├─────────────────────────┤ - │ arr == [2, 3] │ ← second call to sum + │ arr == [2, 3] │ ← 第二次调用 sum │ currentSum == 1 │ ├─────────────────────────┤ - │ arr == [1, 2, 3] │ ← first call to sum + │ arr == [1, 2, 3] │ ← 第一次调用 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.) +$(I $(B 注意:)实际上,当新递归函数直接返回调用自己的结果时,编译器会使用一种名叫“尾调用优化”的技术——它可以去除每次递归调用时生成的各个帧。) ) $(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) +$(H5 用法) $(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: +$(LI $(IX fiber function) 一个纤程可以执行一个不接受任何参数也不返回任何内容的可调用实例(如函数指针、委托等)。例如,下面这个函数便可以用作纤程函数: --- void fiberFunction() { @@ -174,7 +174,7 @@ 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: +$(LI $(IX Fiber, core.thread) 纤程可创建为一个 $(C core.thread.Fiber) 对象,同时带上一个可调用实体: --- import core.thread; @@ -185,7 +185,7 @@ import core.thread; --- $(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: +另外,它也可以定义为 $(C Fiber) 的子类,并将纤程函数传递给父类的构造函数。下面的示例里,纤程函数是一个成员函数: ) --- @@ -206,19 +206,19 @@ class MyFiber : $(HILITE Fiber) { ) -$(LI $(IX call, Fiber) A fiber is started and resumed by its $(C call()) member function: +$(LI $(IX call, Fiber) 纤程可以通过成员函数 $(C call()) 来开始和继续: --- 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()): +$(LI $(IX yield, Fiber) 纤程通过 $(C Fiber.yield()) 可以将自己暂停($(I 跳转) 到调用函数): --- void fiberFunction() { @@ -231,12 +231,12 @@ void fiberFunction() { --- $(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: +$(LI $(IX .state, Fiber) 纤程的执行状态可以通过它的 $(C .state) 特性来检测: --- if (fiber.state == Fiber.State.TERM) { @@ -245,16 +245,16 @@ $(LI $(IX .state, Fiber) The execution state of a fiber is determined by its $(C --- $(P -$(IX State, Fiber) $(C Fiber.State) is an enum with the following values: +$(IX State, Fiber) $(C Fiber.State) 是一个枚举类型,有下面几种值: ) $(UL -$(LI $(IX HOLD, Fiber.State) $(C HOLD): The fiber is paused, meaning that it can be started or resumed.) +$(LI $(IX HOLD, Fiber.State) $(C HOLD): 纤程暂停,即表示它可以被开始或继续。) -$(LI $(IX EXEC, Fiber.State) $(C EXEC): The fiber is currently executing.) +$(LI $(IX EXEC, Fiber.State) $(C EXEC): 纤程当前正在执行。) -$(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.) +$(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): 纤程已中止。通过 $(C reset()) 重置后,它可以被再次使用。) ) @@ -262,14 +262,14 @@ $(LI $(IX TERM, Fiber.State) $(IX reset, Fiber) $(C TERM): The fiber has termina ) -$(H5 Fibers in range implementations) +$(H5 范围实现里的纤程) $(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 have seen in $(LINK2 /ders/d.en/ranges.html, the Ranges) and later chapters have been storing some kind of state to achieve their tasks. +几乎所有的范围都需要存储某些信息,以便记住迭代状态。它需要知道当下次调用 $(C popFront()) 时应该做什么。在 $(LINK2 /ders/d.en/ranges.html, 范围) 及后面章节里看到的大部分范围示例为了完成任务都会存储某些状态。 ) $(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: +例如,我们之前定义的 $(C FibonacciSeries)会一直记住两个变量,以便计算序列里的 $(I 下一个 next) 成员: ) --- @@ -292,11 +292,11 @@ struct FibonacciSeries { --- $(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. +对于某些类似 $(C FibonacciSeries) 那样的范围,维护迭代状态并非什么难事;但是对于其他的范围(如二叉搜索树那样的递归数据结构)则是困难重重。让人惊讶的是,对于这种数据结构,同样的算法在递归实现时并非什么难事。 ) $(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()).) +例如,下面是 $(C insert()) 和 $(C print()) 的递归实现,它们并未定义任何变量,并且与树里包含的元素数量无关。其中的递归调用是亮点。请注意,$(C insert()) 通过 $(C insertOrSet()) 间接实现的递归。 ) --- @@ -307,21 +307,21 @@ 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. */ +/* 二叉树的节点表示。此类型只适用于下面的 + * Tree 结构,而不应该 + * 被直接使用。*/ struct Node { int element; - Node * left; // Left sub-tree - Node * right; // Right sub-tree + Node * left; // 左子树 + Node * right; // 右子树 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 { @@ -331,16 +331,16 @@ struct Node { } 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)(); @@ -348,11 +348,11 @@ struct Node { } } -/* 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 { @@ -360,17 +360,17 @@ void insertOrSet(ref Node * node, int element) { } } -/* This is the actual Tree representation. It allows an empty - * tree by means of 'root' being equal to 'null'. */ +/* 实际的 Tree 表示。它允许为空树, + * 即'root'为'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(); @@ -378,8 +378,8 @@ struct Tree { } } -/* Populates the tree with 'n' random numbers picked out of a - * set of '10 * n' numbers. */ +/* 从一组有'10 * n'个数的集合里 + * 随机选取'n'个数来初始化这个树。*/ Tree makeRandomTree(size_t n) { auto numbers = iota((n * 10).to!int) .randomSample(n, Random(unpredictableSeed)) @@ -387,7 +387,7 @@ Tree makeRandomTree(size_t n) { randomShuffle(numbers); - /* Populate the tree with those numbers. */ + /* 使用这些数来初始化树。*/ auto tree = Tree(); numbers.each!(e => tree.insert(e)); @@ -401,41 +401,41 @@ void main() { --- $(P -$(I $(B Note:) The program above uses the following features from Phobos:) +$(I $(B 请注意:) 上面这个程序使用了下面几个源自 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). +$(IX iota, std.range) $(C std.range.iota) 懒式生成给定范围里的元素值。默认情况下,第 1 个元素为 $(C .init) 值。例如,$(C iota(10)) 为一个 $(C int) 型元素构成的范围,元素值的范围是 $(C 0) 到 $(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. +$(IX each, std.algorithm) $(IX map, vs. each) $(C std.algorithm.each) 与 $(C std.algorithm.map) 相似。由于 $(C map()) 会为每个元素生成一个新的结果,因此 $(C each()) 也会为每个元素生成新值。另外,$(C map()) 采取的是懒式执行方式,而 $(C each()) 则是立即执行。 ) $(LI -$(IX randomSample, std.random) $(C std.random.randomSample) picks a random sampling of elements from a given range without changing their order. +$(IX randomSample, std.random) $(C std.random.randomSample) 会从给定的范围里随机选取一些样本元素,但不会更改它们的顺序。 ) $(LI -$(IX randomShuffle, std.random) $(C std.random.randomShuffle) shuffles the elements of a range randomly. +$(IX randomShuffle, std.random) $(C std.random.randomShuffle) 会随机选取范围里的元素,顺序不确定。 ) ) $(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: +与大部分容器一样,大家也会想要这个树提供范围接口,以便其中的元素可以与已有的范围算法一起使用。通过定义一个 $(C opSlice()) 成员函数可以达到这一目的功能: ) --- struct Tree { // ... - /* Provides access to the elements of the tree in sorted - * order. */ + /* 提供顺序访问树里的各个元素 + * 的接口。*/ struct InOrderRange { - $(HILITE ... What should the implementation be? ...) + $(HILITE ... 具体的实现应该是什么呢?...) } InOrderRange opSlice() const { @@ -445,22 +445,22 @@ struct Tree { --- $(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.) +由于上面的成员函数 $(C print()) 实际上已经可以顺序访问每个元素,因此没必要再去为树实现一个 $(C InputRange)。这里不会去实现 $(C InOrderRange),但是鼓励大家去实现或者试着研究一下树的迭代方法。(有些实现要求树节点拥有一个附加的 $(C Node*),让其指向每个节点的父节点。) ) $(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). +为何像 $(C print()) 那样的树递归算法无关紧要,主要归因于调用栈的自动管理。调用栈隐形不仅包含当前元素是什么的信息,而且还包含了程序的执行过程是如何到达该元素的信息(例如,执行过程在处理完左节点或右节点之后,接下来会到达什么节点。) ) $(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: +例如,当 $(C left.print()) 的某个递归调用在输出左子树的所有元素之后返回时,当前 $(C print()) 调用的本地状态已表明需要输出一个空格字符: ) --- void print() const { if (left) { left.print(); - write(' '); // ← Call stack implies this is next + write(' '); // ← 调用栈表明这就是下一步 } // ... @@ -468,25 +468,25 @@ For example, when a recursive call to $(C left.print()) returns after printing t --- $(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. +虽然像生成 Fibonacci 序列这样的简单任务并不能突显出纤程的优势,但是为了简化说明,我们还是会实现一个纤程版本,并通过它来了解一下常用的纤程操作。下面我们来实现一个树范围. ) --- import core.thread; -/* This is the fiber function that generates each element and - * then sets the 'ref' parameter to that element. */ +/* 这个纤程函数会生成每个元素, + * 并且会设置一个 'ref' 参数引用该元素。*/ void fibonacciSeries($(HILITE ref) int current) { // (1) - current = 0; // Note that 'current' is the parameter + current = 0; // 请注意,'current' 即为该参数 int next = 1; while (true) { $(HILITE Fiber.yield()); // (2) - /* Next call() will continue from this point */ // (3) + /* 下一次的 call() 会继续从这个点开始执行 */ // (3) const nextNext = current + next; current = next; @@ -510,7 +510,7 @@ void main() { $(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 上面这个函数接收的是一个 $(C int) 型引用参数。它使用此参数来与将当前元素与其调用者联系在一起。(这个参数除了使用 $(C ref) 来修饰以外,还可以使用 $(C out) )。) $(LI When the current element is ready for use, the fiber pauses itself by calling $(C Fiber.yield()).) @@ -643,15 +643,17 @@ 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) { - alias RangeType = typeof(byNode(tree.root)); + if (tree.root) { + return byNode(tree.root); - return (tree.root - ? byNode(tree.root) - : new RangeType(() {})); $(CODE_NOTE Empty range) + } else { + alias RangeType = typeof(return); + return new RangeType(() {}); $(CODE_NOTE Empty range) + } } --- @@ -813,7 +815,7 @@ FlowData parseFlowData(string line) { size_t id; string data; - const items = formattedRead(line, " %s %s", &id, &data); + const items = line.formattedRead!" %s %s"(id, data); enforce(items == 2, format("Bad input '%s'.", line)); return FlowData(id, data); @@ -992,7 +994,7 @@ Instead of terminating the fiber and losing the entire sign-on flow, the fiber c --- $(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): +Wrapping that line with a $(C try-catch) statement inside an unconditional loop would be sufficient to keep the fiber alive until there is data that can be converted to a $(C uint): ) --- @@ -1008,7 +1010,7 @@ Wrapping that line with a $(C try-catch) statement inside an infinite loop would --- $(P -This time the fiber remains in an infinite loop until data is valid: +This time the fiber remains in an unconditional loop until data is valid: ) $(SHELL @@ -1075,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/ddili/src/ders/d.cn/files.cozum.d b/target/files.cozum.d similarity index 77% rename from ddili/src/ders/d.cn/files.cozum.d rename to target/files.cozum.d index 6d4b5d6..f931bdb 100644 --- a/ddili/src/ders/d.cn/files.cozum.d +++ b/target/files.cozum.d @@ -27,8 +27,8 @@ void main() { --- Macros: - SUBTITLE=文件练习解答 + SUBTITLE=Files Solutions - DESCRIPTION=D 编程语言练习解答:基本文件操作 + DESCRIPTION=Programming in D exercise solutions: basic file operations - KEYWORDS=D 编程语言教程 if else 解答 + 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/ddili/src/ders/d.en/floating_point.cozum.d b/target/floating_point.cozum.d similarity index 100% rename from ddili/src/ders/d.en/floating_point.cozum.d rename to target/floating_point.cozum.d diff --git a/ddili/src/ders/d.cn/floating_point.d b/target/floating_point.d similarity index 87% rename from ddili/src/ders/d.cn/floating_point.d rename to target/floating_point.d index c8a4da6..ab04310 100644 --- a/ddili/src/ders/d.cn/floating_point.d +++ b/target/floating_point.d @@ -354,18 +354,14 @@ $(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 Comparing floating point values) +$(H5 $(IX unordered) Unorderedness) $(P -We have seen the following comparison operations for integers: equal to ($(C ==)), not equal to ($(C !=)), less than ($(C <)), greater than ($(C >)), less than or equal to ($(C <=)), and greater than or equal to ($(C >=)). Floating point types have many more comparison operators. +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 -Since the special value $(C .nan) represents invalid floating point values, some comparisons with other values are not meaningful. For example, it is meaningless to ask whether $(C .nan) or 1 is greater. -) - -$(P -$(IX unordered) For that reason, floating point values introduce another comparison concept: unordered. Being unordered means that at least one of the values is $(C .nan). +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 @@ -373,8 +369,6 @@ The following table lists all the floating point comparison operators. All of th ) $(P -$(IX <>) -$(IX !<>) 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. ) @@ -402,27 +396,6 @@ The last column indicates whether the operation is meaningful if one of the oper <=is less than or equal to $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no) - - !<>=is not less than, not greater than, nor equal to $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - - - <>is less than or greater than $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_NO no) - - <>=is less than, greater than, or equal to $(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_NO no) - - - !<=is not less than nor equal to $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - - - !<is not less than $(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - - !>=is not greater than nor equal to $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - - - !>is not greater than $(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - - !<>is not less than nor greater than $(UNORDERED_FALSE false)$(UNORDERED_FALSE false)$(UNORDERED_TRUE true)$(UNORDERED_TRUE true)$(UNORDERED_YES yes) - diff --git a/ddili/src/ders/d.en/for.cozum.d b/target/for.cozum.d similarity index 100% rename from ddili/src/ders/d.en/for.cozum.d rename to target/for.cozum.d diff --git a/ddili/src/ders/d.en/for.d b/target/for.d similarity index 100% rename from ddili/src/ders/d.en/for.d rename to target/for.d diff --git a/ddili/src/ders/d.en/foreach.cozum.d b/target/foreach.cozum.d similarity index 100% rename from ddili/src/ders/d.en/foreach.cozum.d rename to target/foreach.cozum.d diff --git a/ddili/src/ders/d.en/foreach.d b/target/foreach.d similarity index 95% rename from ddili/src/ders/d.en/foreach.d rename to target/foreach.d index f438c75..c63ea1e 100644 --- a/ddili/src/ders/d.en/foreach.d +++ b/target/foreach.d @@ -251,19 +251,24 @@ $(C foreach) can also be used with objects of user-defined types that define the $(H5 $(IX counter, foreach) The counter is automatic only for arrays) $(P -The automatic counter is provided only when iterating over arrays. When a counter is needed while iterating over other types of containers, the counter can be defined and incremented explicitly: +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 i = 0; + size_t $(HILITE i) = 0; foreach (element; container) { // ... ++i; } --- +) $(P -Such a variable is also needed when counting a specific condition. For example, the following code counts only the values that are divisible by 10: +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: ) --- @@ -288,7 +293,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -386,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/ddili/src/ders/d.en/foreach_opapply.cozum.d b/target/foreach_opapply.cozum.d similarity index 99% rename from ddili/src/ders/d.en/foreach_opapply.cozum.d rename to target/foreach_opapply.cozum.d index d80ae74..4e24bfe 100644 --- a/ddili/src/ders/d.en/foreach_opapply.cozum.d +++ b/target/foreach_opapply.cozum.d @@ -145,7 +145,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.cn/foreach_opapply.d b/target/foreach_opapply.d similarity index 99% rename from ddili/src/ders/d.cn/foreach_opapply.d rename to target/foreach_opapply.d index 34b5c9d..7dbf4de 100644 --- a/ddili/src/ders/d.cn/foreach_opapply.d +++ b/target/foreach_opapply.d @@ -314,7 +314,6 @@ The following is a definition of $(C NumberRange) that is implemented according struct NumberRange { int begin; int end; - // (2) (1) int opApply(int delegate(ref int) operations) const { int result = 0; @@ -535,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/ddili/src/ders/d.en/foreword1.d b/target/foreword1.d similarity index 100% rename from ddili/src/ders/d.en/foreword1.d rename to target/foreword1.d diff --git a/ddili/src/ders/d.de/foreword2.d b/target/foreword2.d similarity index 100% rename from ddili/src/ders/d.de/foreword2.d rename to target/foreword2.d diff --git a/ddili/src/ders/d.en/formatted_input.cozum.d b/target/formatted_input.cozum.d similarity index 100% rename from ddili/src/ders/d.en/formatted_input.cozum.d rename to target/formatted_input.cozum.d diff --git a/ddili/src/ders/d.en/formatted_input.d b/target/formatted_input.d similarity index 100% rename from ddili/src/ders/d.en/formatted_input.d rename to target/formatted_input.d diff --git a/ddili/src/ders/d.cn/formatted_output.cozum.d b/target/formatted_output.cozum.d similarity index 57% rename from ddili/src/ders/d.cn/formatted_output.cozum.d rename to target/formatted_output.cozum.d index 95a3ca1..62ccc2a 100644 --- a/ddili/src/ders/d.cn/formatted_output.cozum.d +++ b/target/formatted_output.cozum.d @@ -1,10 +1,10 @@ Ddoc -$(COZUM_BOLUMU 格式化输出) +$(COZUM_BOLUMU Formatted Output) $(OL -$(LI 如果你使用格式化说明符,那这个任务就会变得非常简单: +$(LI We have already seen that this is trivial with format specifiers: --- import std.stdio; @@ -29,7 +29,7 @@ void main() { ) $(LI -注意如果你想要输出 $(C %),那必须在格式化字符串中将其写作 $(C %%): +Remembering that the $(C %) character must appear twice in the format string to be printed as itself: --- import std.stdio; @@ -49,8 +49,8 @@ void main() { Macros: - SUBTITLE=格式化输出习题解答 + SUBTITLE=Formatted Output Solutions - 练习DESCRIPTION=D 编程语言练习解答:格式化输出 + DESCRIPTION=Programming in D exercise solutions: Formatted output - KEYWORDS=D 编程语言教程 格式化输出 解答 + KEYWORDS=programming in d tutorial formatted output solution diff --git a/ddili/src/ders/d.en/formatted_output.d b/target/formatted_output.d similarity index 95% rename from ddili/src/ders/d.en/formatted_output.d rename to target/formatted_output.d index de84f11..5368f61 100644 --- a/ddili/src/ders/d.en/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 @@ -617,6 +617,30 @@ $(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 diff --git a/ddili/src/ders/d.de/frontispiece.d b/target/frontispiece.d similarity index 100% rename from ddili/src/ders/d.de/frontispiece.d rename to target/frontispiece.d diff --git a/ddili/src/ders/d.en/function_overloading.cozum.d b/target/function_overloading.cozum.d similarity index 100% rename from ddili/src/ders/d.en/function_overloading.cozum.d rename to target/function_overloading.cozum.d diff --git a/ddili/src/ders/d.en/function_overloading.d b/target/function_overloading.d similarity index 99% rename from ddili/src/ders/d.en/function_overloading.d rename to target/function_overloading.d index 137ef29..ec6da36 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/function_parameters.cozum.d b/target/function_parameters.cozum.d similarity index 100% rename from ddili/src/ders/d.en/function_parameters.cozum.d rename to target/function_parameters.cozum.d diff --git a/ddili/src/ders/d.cn/function_parameters.d b/target/function_parameters.d similarity index 96% rename from ddili/src/ders/d.cn/function_parameters.d rename to target/function_parameters.d index 43021da..dacc44e 100644 --- a/ddili/src/ders/d.cn/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 @@ -747,17 +747,19 @@ Calculating $(H6 $(IX scope) $(C scope)) $(P -This keyword specifies that a parameter will not be used beyond the scope of the function: +$(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. ) -$(COMMENT_XXX We don't use the DERLEME_HATASI macro below because the codetester fails because 'scope' does not cause a compilation error.) +$(SHELL +$(SHELL_OBSERVED $) dmd -dip1000 deneme.d +) --- int[] globalSlice; -int[] foo($(HILITE scope) int[] parameter) { - globalSlice = parameter; // ← compilation ERROR - return parameter; // ← compilation ERROR +$(HILITE @safe) int[] foo($(HILITE scope) int[] parameter) { + globalSlice = parameter; $(DERLEME_HATASI) + return parameter; $(DERLEME_HATASI) } void main() { @@ -767,11 +769,7 @@ void main() { --- $(P -That function violates the promise of $(C scope) in two places: It assigns the parameter to a global variable, and it returns it. Both of those would make it possible for the parameter to be accessed after the function finishes. -) - -$(P -($(I $(B Note:) dmd 2.071, the latest compiler used to compile the examples in this chapter, did not yet support the $(C scope) keyword.)) +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)) @@ -911,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/ddili/src/ders/d.en/functions.cozum.d b/target/functions.cozum.d similarity index 100% rename from ddili/src/ders/d.en/functions.cozum.d rename to target/functions.cozum.d diff --git a/ddili/src/ders/d.en/functions.d b/target/functions.d similarity index 98% rename from ddili/src/ders/d.en/functions.d rename to target/functions.d index 48b2119..9ec9d24 100644 --- a/ddili/src/ders/d.en/functions.d +++ b/target/functions.d @@ -136,7 +136,7 @@ This time there is an additional information that concerns some of the steps: "g ) $(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 the $(I function parameter list), separated by commas from each other. The parameter list rests inside of the parentheses that comes after the name of the function. +$(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 @@ -642,7 +642,7 @@ The three commented groups of lines of the first version of the program have bee ) $(P -Another important benefit of removing comment lines is that comments tend to become outdated as the code gets modified over the time. Comments are sometimes forgotten to be updated along with the code and become either useless or, even worse, misleading. For that reason, it is beneficial to try to write programs without the need for comments. +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 diff --git a/ddili/src/ders/d.cn/functions_more.d b/target/functions_more.d similarity index 97% rename from ddili/src/ders/d.cn/functions_more.d rename to target/functions_more.d index 3f165c2..9dba3bd 100644 --- a/ddili/src/ders/d.cn/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 @@ -573,6 +573,22 @@ 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 @@ -655,7 +671,7 @@ int foo(int[] arr, size_t i) $(HILITE nothrow) { --- $(P -As in purity, the compiler automatically deduces whether a template, delegate, or anonymous function is $(C nothrow). +As with purity, the compiler automatically deduces whether a template, delegate, or anonymous function is $(C nothrow). ) $(H6 $(IX @nogc) $(C @nogc) functions) @@ -714,7 +730,7 @@ Error: @nogc function 'deneme.foo' $(HILITE cannot call non-@nogc function) $(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 in purity, the compiler infers the safety level of templates, delegates, anonymous functions, and $(C auto) functions. +$(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) @@ -907,7 +923,7 @@ $(SHELL_SMALL foo is called 1 times. ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/ddili/src/ders/d.en/goto.d b/target/goto.d similarity index 99% rename from ddili/src/ders/d.en/goto.d rename to target/goto.d index abc3cbe..73bd19b 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.cn/hello_world.cozum.d b/target/hello_world.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/hello_world.cozum.d rename to target/hello_world.cozum.d diff --git a/ddili/src/ders/d.cn/hello_world.d b/target/hello_world.d similarity index 91% rename from ddili/src/ders/d.cn/hello_world.d rename to target/hello_world.d index 259d82e..e1d396a 100644 --- a/ddili/src/ders/d.cn/hello_world.d +++ b/target/hello_world.d @@ -31,7 +31,7 @@ $(IX gdc) $(IX ldc) At the time of writing this chapter, there are three D compi ) $(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.071. +$(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 @@ -100,19 +100,13 @@ 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 only the name of the compiler: +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 only the name) -DMD64 D Compiler v2.069.0 -Copyright (c) 1999-2015 by Digital Mars written by Walter Bright -Documentation: http://dlang.org/ -Config file: /etc/dmd.conf -Usage: - dmd files.d ... { -switch } - - files.d D source files +$(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) ... @@ -135,11 +129,11 @@ The complete list of $(C dmd) command line switches can be found in the $(LINK2 ) $(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: +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 $(HILITE -run) hello.d -w -unittest +$(SHELL_OBSERVED $) dmd -de -w -unittest $(HILITE -run) hello.d Hello world! $(SHELL_NOTE the program is automatically executed) ) @@ -190,11 +184,11 @@ $(IX keyword) $(B Keyword): Special words that are a part of the core features o ) $(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 __MODULE__), $(C __LINE__), $(C __FUNCTION__), $(C __PRETTY_FUNCTION__), $(C __gshared), $(C __traits), $(C __vector), and $(C __parameters). +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 in the upcoming chapters with the exception of these keywords: $(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. +$(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 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..a2f9f97 --- /dev/null +++ b/target/index.d @@ -0,0 +1,143 @@ +Ddoc + +$(DERS_BOLUMU 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 +以上各个版本受诸多因素影响而存在差异,如价格、运送时间、运送成本、关税与其他费用,以及当地书店是否有售等等 +) + +
+ +$(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 +$(LINK_DOWNLOAD /ders/d.en/Programming_in_D_code_samples.zip, 点击此处可下载 $(C .zip) 文件形式的示例代码。) +) + +$(H5 在线版本) + +$(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/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.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)) +$(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.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=D 语言编程 + + DESCRIPTION=全新编写的 D 编程语言教程。 + + KEYWORDS=d programming language tutorial book novice beginner D 编程语言 教程 书籍 新手 初学者 + + BREADCRUMBS=$(BREADCRUMBS_INDEX) + +SOZLER= + +MINI_SOZLUK= diff --git a/ddili/src/ders/d.en/inheritance.cozum.d b/target/inheritance.cozum.d similarity index 99% rename from ddili/src/ders/d.en/inheritance.cozum.d rename to target/inheritance.cozum.d index 1529983..d7adb5d 100644 --- a/ddili/src/ders/d.en/inheritance.cozum.d +++ b/target/inheritance.cozum.d @@ -173,7 +173,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.en/inheritance.d b/target/inheritance.d similarity index 99% rename from ddili/src/ders/d.en/inheritance.d rename to target/inheritance.d index 8c8a1d2..37a2dc6 100644 --- a/ddili/src/ders/d.en/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.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..7952bc0 --- /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 +输出: +) + +$(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/ddili/src/ders/d.en/interface.d b/target/interface.d similarity index 98% rename from ddili/src/ders/d.en/interface.d rename to target/interface.d index 0834f41..5c90c21 100644 --- a/ddili/src/ders/d.en/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: @@ -354,7 +354,7 @@ Although the example above contains a $(C struct), $(C static) member functions $(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 available only for classes and interfaces; they are not relevant to structs because structs do not support inheritance. +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 @@ -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/ddili/src/ders/d.en/invariant.d b/target/invariant.d similarity index 99% rename from ddili/src/ders/d.en/invariant.d rename to target/invariant.d index d06fbce..66f4f54 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/io.cozum.d b/target/io.cozum.d similarity index 100% rename from ddili/src/ders/d.en/io.cozum.d rename to target/io.cozum.d diff --git a/ddili/src/ders/d.cn/io.d b/target/io.d similarity index 100% rename from ddili/src/ders/d.cn/io.d rename to target/io.d diff --git a/target/is_expr.d b/target/is_expr.d new file mode 100644 index 0000000..ed870ac --- /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 +输出: +) + +$(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 +输出: +) + +$(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 +输出: +) + +$(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/ddili/src/ders/d.de/ix.d b/target/ix.d similarity index 100% rename from ddili/src/ders/d.de/ix.d rename to target/ix.d diff --git a/ddili/src/ders/d.en/lambda.d b/target/lambda.d similarity index 98% rename from ddili/src/ders/d.en/lambda.d rename to target/lambda.d index dc29fab..f2513e4 100644 --- a/ddili/src/ders/d.en/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 @@ -1112,7 +1112,7 @@ As seen in its declaration, this overload of $(C toString()) does not return a $ ) $(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: +$(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. ) --- @@ -1124,7 +1124,7 @@ struct Point { int y; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "(%s,%s)", x, y); + sink.$(HILITE formattedWrite)!"(%s,%s)"(x, y); } } @@ -1134,7 +1134,7 @@ struct Color { ubyte b; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "RGB:%s,%s,%s", r, g, b); + sink.$(HILITE formattedWrite)!"RGB:%s,%s,%s"(r, g, b); } } @@ -1143,7 +1143,7 @@ struct ColoredPoint { Point point; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "{%s;%s}", color, point); + sink.$(HILITE formattedWrite)!"{%s;%s}"(color, point); } } @@ -1151,7 +1151,7 @@ struct Polygon { ColoredPoint[] points; void toString(void delegate(const(char)[]) sink) const { - $(HILITE formattedWrite)(sink, "%s", points); + sink.$(HILITE formattedWrite)!"%s"(points); } } @@ -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/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/ddili/src/ders/d.en/lifetimes.d b/target/lifetimes.d similarity index 95% rename from ddili/src/ders/d.en/lifetimes.d rename to target/lifetimes.d index 6054431..0cd7a3e 100644 --- a/ddili/src/ders/d.en/lifetimes.d +++ b/target/lifetimes.d @@ -11,7 +11,7 @@ Before getting to structs and classes, it will be better to talk about some impo ) $(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 called struct and class variables specifically as $(I objects). I will continue calling both of these concepts variables in this chapter. +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 @@ -25,7 +25,7 @@ The time between when a variable is defined and when it is $(I finalized) is the ) $(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 has been defined would make that variable unavailable. +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 @@ -306,8 +306,8 @@ Variables can be finalized in two ways: ) $(UL -$(LI $(B When the lifetime ends): The finalization happens upon the end of life of the variable.) -$(LI $(B Some time in the future): The finalization happens at an indeterminate time in the future by the garbage collector.) +$(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 diff --git a/ddili/src/ders/d.en/literals.cozum.d b/target/literals.cozum.d similarity index 100% rename from ddili/src/ders/d.en/literals.cozum.d rename to target/literals.cozum.d diff --git a/ddili/src/ders/d.cn/literals.d b/target/literals.d similarity index 99% rename from ddili/src/ders/d.cn/literals.d rename to target/literals.d index d63f3c3..017fb0a 100644 --- a/ddili/src/ders/d.cn/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/ddili/src/ders/d.en/logical_expressions.cozum.d b/target/logical_expressions.cozum.d similarity index 100% rename from ddili/src/ders/d.en/logical_expressions.cozum.d rename to target/logical_expressions.cozum.d diff --git a/ddili/src/ders/d.cn/logical_expressions.d b/target/logical_expressions.d similarity index 100% rename from ddili/src/ders/d.cn/logical_expressions.d rename to target/logical_expressions.d 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/ddili/src/ders/d.en/main.cozum.d b/target/main.cozum.d similarity index 100% rename from ddili/src/ders/d.en/main.cozum.d rename to target/main.cozum.d diff --git a/ddili/src/ders/d.en/main.d b/target/main.d similarity index 99% rename from ddili/src/ders/d.en/main.d rename to target/main.d index cdf121c..44ff113 100644 --- a/ddili/src/ders/d.en/main.d +++ b/target/main.d @@ -233,7 +233,7 @@ import std.stdio; int main(string[] args) { if (args.length != 3) { - stderr.writefln("ERROR! Correct usage:\n" + stderr.writefln("ERROR! Correct usage:\n" ~ " %s word1 word2", args[0]); return 1; } @@ -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/ddili/src/ders/d.en/member_functions.cozum.d b/target/member_functions.cozum.d similarity index 100% rename from ddili/src/ders/d.en/member_functions.cozum.d rename to target/member_functions.cozum.d diff --git a/ddili/src/ders/d.en/member_functions.d b/target/member_functions.d similarity index 100% rename from ddili/src/ders/d.en/member_functions.d rename to target/member_functions.d diff --git a/ddili/src/ders/d.cn/memory.d b/target/memory.d similarity index 98% rename from ddili/src/ders/d.cn/memory.d rename to target/memory.d index 0c7f3a6..9fb8845 100644 --- a/ddili/src/ders/d.cn/memory.d +++ b/target/memory.d @@ -358,7 +358,7 @@ $(LI $(C NO_INTERIOR): Specifies that only pointers to the block's first address ) $(P -$(IX |) The values of $(C enum BlkAttr) are suitable to be used as bit flags that we have seen 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: +$(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: ) --- @@ -473,7 +473,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -872,10 +872,10 @@ $(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 Calls the constructor of the object on that memory location. Only after this step the object becomes $(I placed) on that memory area. +$(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 Configure the memory block so it has all the necessary flags and infrastructure to properly destroy the object when freed. +$(LI Configures the memory block so it has all the necessary flags and infrastructure to properly destroy the object when freed. ) ) @@ -943,7 +943,7 @@ void main() { /* Reserve room for all Student objects. * - * Warning! The objects that are accessible through this + * 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 = @@ -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/ddili/src/ders/d.en/mixin.d b/target/mixin.d similarity index 92% rename from ddili/src/ders/d.en/mixin.d rename to target/mixin.d index f7b69f6..6755c23 100644 --- a/ddili/src/ders/d.en/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 @@ -421,7 +421,51 @@ $(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 Example) +$(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 样例) $(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/ddili/src/ders/d.en/modules.d b/target/modules.d similarity index 100% rename from ddili/src/ders/d.en/modules.d rename to target/modules.d 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/ddili/src/ders/d.en/nested.d b/target/nested.d similarity index 98% rename from ddili/src/ders/d.en/nested.d rename to target/nested.d index e84dbb9..eff727d 100644 --- a/ddili/src/ders/d.en/nested.d +++ b/target/nested.d @@ -3,7 +3,7 @@ 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 have seen in $(LINK2 /ders/d.en/lambda.html, the Function Pointers, Delegates, and Lambdas chapter). +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 @@ -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/ddili/src/ders/d.cn/null_is.d b/target/null_is.d similarity index 99% rename from ddili/src/ders/d.cn/null_is.d rename to target/null_is.d index f0c81da..f8349d3 100644 --- a/ddili/src/ders/d.cn/null_is.d +++ b/target/null_is.d @@ -3,7 +3,7 @@ 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 need not be referencing any object: +We saw in earlier chapters that a variable of a reference type needs not reference a particular object: ) --- @@ -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/ddili/src/ders/d.cn/object.cozum.d b/target/object.cozum.d similarity index 55% rename from ddili/src/ders/d.cn/object.cozum.d rename to target/object.cozum.d index b3bca78..d85c92a 100644 --- a/ddili/src/ders/d.cn/object.cozum.d +++ b/target/object.cozum.d @@ -5,7 +5,7 @@ $(COZUM_BOLUMU $(CH4 Object)) $(OL $(LI -对于相等性比较,$(C rhs) 属非 $(C null) 并且成员相等就足够了: +For the equality comparison, $(C rhs) being non-$(C null) and the members being equal would be sufficient: --- enum Color { blue, green, red } @@ -28,7 +28,7 @@ class Point { ) $(LI -当右手侧对象的类型也是 $(C Point),首先根据 $(C x) 成员的值来比较,然后是 $(C y) 成员的值: +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 { @@ -52,7 +52,7 @@ class Point { ) $(LI -注意下面的 $(C opCmp) 函数内部,要转换到 $(C const TriangularArea) 类型是不可能的。当 $(C rhs) 是 $(C const TriangularArea),那么它的成员 $(C rhs.points) 也将是 $(C const) 。由于 $(C opCmp) 的参数属非$(C const),它将不可能给 $(C point.opCmp) 传递 $(C rhs.points[i]) 参数。 +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 { @@ -75,21 +75,21 @@ class TriangularArea { immutable comparison = point.opCmp(rhs.points[i]); if (comparison != 0) { - /* 排序顺序已经 - * 确定。简单的返回结果。*/ + /* The sort order has already been + * determined. Simply return the result. */ return comparison; } } - /* 对象被认为是相等的,因为所有的 - * point已经相等。*/ + /* The objects are considered equal because all of + * their points have been equal. */ return 0; } override size_t toHash() const { - /* 由于 'points' 成员是一个数组,我们能 - * 为数组类型利用现有的 - toHash 算法。*/ + /* Since the 'points' member is an array, we can take + * advantage of the existing toHash algorithm for + * array types. */ return typeid(points).getHash(&points); } } @@ -101,8 +101,8 @@ class TriangularArea { Macros: - SUBTITLE=Object 习题解答 + SUBTITLE=Object Solutions - DESCRIPTION=D语言编程习题解答:Object + DESCRIPTION=Programming in D exercise solutions: Object - KEYWORDS=D语言编程教程 Object + KEYWORDS=programming in d tutorial Object diff --git a/target/object.d b/target/object.d new file mode 100644 index 0000000..345ce39 --- /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 +输出: +) + +$(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/ddili/src/ders/d.cn/operator_overloading.cozum.d b/target/operator_overloading.cozum.d similarity index 98% rename from ddili/src/ders/d.cn/operator_overloading.cozum.d rename to target/operator_overloading.cozum.d index 0c5b29f..816f5cd 100644 --- a/ddili/src/ders/d.cn/operator_overloading.cozum.d +++ b/target/operator_overloading.cozum.d @@ -156,7 +156,7 @@ struct Fraction { * fraction is before, a positive value if this fraction * is after, and zero if both fractions have the same sort * order. */ - int opCmp(const ref Fraction rhs) const { + 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 @@ -176,7 +176,7 @@ struct Fraction { * 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 ref Fraction rhs) const { + bool opEquals(const Fraction rhs) const { /* Checking whether the return value of opCmp is zero * is sufficient here. */ return opCmp(rhs) == 0; diff --git a/ddili/src/ders/d.en/operator_overloading.d b/target/operator_overloading.d similarity index 97% rename from ddili/src/ders/d.en/operator_overloading.d rename to target/operator_overloading.d index 181d23f..67885f5 100644 --- a/ddili/src/ders/d.en/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++; --- @@ -377,7 +377,17 @@ The compiler picks the option that is a better match than the other. ) $(P -In most cases it is not necessary to define $(C opBinaryRight), except for the $(C in) operator: It usually makes more sense to define $(C opBinaryRight) for $(C in). +$(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 @@ -1432,7 +1442,7 @@ void $(CODE_DONT_TEST)main() { --- $(P -The output: +输出: ) $(SHELL @@ -1502,6 +1512,54 @@ $(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 @@ -1668,7 +1726,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.en/operator_precedence.d b/target/operator_precedence.d similarity index 93% rename from ddili/src/ders/d.en/operator_precedence.d rename to target/operator_precedence.d index 23ce93c..db4d51e 100644 --- a/ddili/src/ders/d.en/operator_precedence.d +++ b/target/operator_precedence.d @@ -83,7 +83,7 @@ Some of the terms used in the table are explained later below. -$(C == != > < >= <= !> !< !>= !<= <> !<> <>= !<>= in !in is !is) +$(C == != > < >= <= in !in is !is) Comparison operators Unordered with respect to bitwise operators, cannot be chained @@ -295,21 +295,26 @@ $(I $(B Note:) This is just an example; otherwise, $(C a = 1) is not a $(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 value of the left-hand side expression is ignored and the value of the right-hand side expression becomes the value of the comma operator: +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; - int b = (foo()$(HILITE ,) bar()$(HILITE ,) ++a + 10); + foo()$(HILITE ,) bar()$(HILITE ,) ++a; assert(a == 2); - assert(b == 12); --- $(P -The two comma operators above chain three expressions. Although the first two expressions ($(C foo()) and $(C bar())) do get executed, their values are ignored and the overall comma expression produces the value of the last expression ($(C ++a + 10)). +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 diff --git a/ddili/src/ders/d.cn/parallelism.d b/target/parallelism.d similarity index 62% rename from ddili/src/ders/d.cn/parallelism.d rename to target/parallelism.d index 2734640..d8a85d1 100644 --- a/ddili/src/ders/d.cn/parallelism.d +++ b/target/parallelism.d @@ -3,26 +3,26 @@ Ddoc $(DERS_BOLUMU $(IX parallelism) 并行) $(P -$(IX core) 大多数现代微处理器架构都包含一个以上的$(I 核心), 而其中的每一个核心都可以作为单独的运算单元使用。它们可在同时执行不同程序的不同片段。灵活使用模块 $(C std.parallelism) 中的功能来尽可能地利用所有核心的运算能力,使程序能以更快的速度运行。 +$(IX core) 大多数现代的微处理器都包含一个以上的$(I 内核),其中的每一个内核都可以当作单独的处理单元使用。它们可同时执行不同程序的不同部分。灵活使用模块 $(C std.parallelism) 中的功能来尽可能地利用所有内核的运算能力,使程序能以更快的速度运行。 ) $(P -本章涉及以下操作 range 的算法。只有当元素操作之间没有相互依赖时,它们才能被$(I 并行)执行。$(I 并行)意味着将要执行的一组操作可以被分派到多个核心同时执行。 +本章涉及以下范围(range)算法。只有当元素操作之间没有相互依赖时,它们才能被 $(I 并行) 执行。$(I 并行) 意味着将要执行的一组操作可以被分派到多个内核同时执行。 ) $(UL -$(LI $(C parallel):并行访问 range 中的元素。) +$(LI $(C parallel):并行访问范围中的元素。) $(LI $(C task):创建并行执行的任务。) -$(LI $(C asyncBuf):以并行半延时(semi-eagerly)取值的方式迭代 $(C InputRange) 中的元素。) +$(LI $(C asyncBuf):以并行半延时取值的方式迭代 $(C InputRange) 中的元素。) -$(LI $(C map):以并行半延时(semi-eagerly)取值的方式对 $(C InputRange) 中的每一个元素应用指定的函数。) +$(LI $(C map):以并行半延时取值的方式对 $(C InputRange) 中的每一个元素应用指定的函数。) -$(LI $(C amap):以并行即时(fully-eagerly)取值的方式对 $(C RandomAccessRange) 中的元素应用指定的函数。) +$(LI $(C amap):以并行即时取值的方式对 $(C RandomAccessRange) 中的元素应用指定的函数。) -$(LI $(C reduce):使用指定的函数并行归约计算 $(C RandomAccessRange) 中的元素。) +$(LI $(C reduce):使用指定的函数并行归约计算 $(C RandomAccessRange) 中的元素。) ) @@ -44,7 +44,7 @@ $(P ) $(P -对于循环,range 中的元素通常是一个一个按顺序被处理的,即上一个循环的操作结束后才会进行下一次循环: +对于循环,范围中的元素通常是一个一个按顺序被处理的,即上一个循环的操作结束后才会进行下一次循环: ) --- @@ -57,11 +57,11 @@ $(P --- $(P -正常情况下程序将会在某个操作系统指派的用于运行程序的处理器核心上运行。因为 $(C foreach) 循环通常是顺序操作元素的,学生的 $(C aSlowOperation()) 也会被顺序调用。然而大部分时候这种处理的顺序并不是必须的。如果 $(C Student) 对象相互独立,不去使用那些可能处在空闲状态的微处理器核心是非常浪费的。 +正常情况下程序将会在某个操作系统指派的用于运行程序的处理器内核上运行。因为 $(C foreach) 循环通常是顺序操作元素的,学生的 $(C aSlowOperation()) 也会被顺序调用。然而大部分时候这种处理的顺序并不是必须的。如果 $(C Student) 对象相互独立,不去使用那些可能处在空闲状态的微处理器核心是非常浪费的。 ) $(P -$(IX Thread.sleep) 下面的例子使用 $(C core.thread) 模块中的 $(C Thread.sleep()) 来模拟耗时长的操作。$(C Thread.sleep()) 会将操作挂起一段时间,其时间长短可在代码中指定。当然在下面的例子中 $(C Thread.sleep) 只是用来模拟长耗时的任务,因为它并不需要处理器核心处理实际的工作,它只是占用时间。虽然使用了一个和真实任务有差别的工具,但本章的示例还是能很好的展现出并行编程的威力。 +$(IX Thread.sleep) 下面的例子使用了 $(C core.thread) 模块中的 $(C Thread.sleep()) 来模拟长耗时操作。$(C Thread.sleep()) 会将操作挂起一段时间,其时间长短可在代码中指定。当然在下面的例子中 $(C Thread.sleep) 只是用来模拟长耗时的任务,因为它并不需要处理器核心处理实际的工作,它只是占用时间。虽然使用了一个和真实任务有差别的工具,但本章的示例还是能很好的展现出并行编程的威力。 ) --- @@ -114,7 +114,7 @@ sys 0m0.000s ) $(P -如果处理每个学生需要花费 1 秒,那按照顺序迭代学生总共需要花费 4 秒。然而如果把这 4 个操作分配给 4 个核心执行,它们将会被同时处理。那么处理完这 4 个学生总共耗时 1 秒。 +如果处理每个学生需要花费 1 秒,那按照顺序迭代学生总共需要花费 4 秒。然而如果把这 4 个操作分配给 4 个内核执行,它们将会被同时处理。那么处理完这 4 个学生总共耗时 1 秒。 ) $(P @@ -135,17 +135,17 @@ $(P ) $(SHELL -There are 4 cores on this system. +此系统里有 4 个内核。 ) $(H5 $(IX parallel) $(C taskPool.parallel())) $(P -还可以使用一种简单的方法使用这个函数:直接以 $(C parallel()) 的形式调用。 +此函数可以简单地使用 $(C parallel()) 来调用。 ) $(P -$(IX foreach, parallel) $(IX foreach, 并行) $(C parallel()) 以并行的方式访问 range 中的元素。它可以在 $(C foreach) 循环中起到非常大的作用。只需要导入 $(C std.parallelism) 模块并将上面代码中的 $(C students) 换为 $(C parallel(students)) 即可充分利用系统中全部核心的运算能力。 +$(IX foreach, parallel) $(C parallel()) 以并行的方式访问范围中的元素。它可以在 $(C foreach) 循环中起到非常大的作用。只需要导入 $(C std.parallelism) 模块并将上面代码中的 $(C students) 换为 $(C parallel(students)) 即可充分利用系统中全部核心的运算能力。 ) --- @@ -155,11 +155,11 @@ import std.parallelism; --- $(P -在之前 $(LINK2 /ders/d.cn/foreach_opapply.html, $(C foreach) 结构体和类) 一章中我们了解到: $(C foreach) 块中的表达式将会被包装成委托传递给对象的 $(C opApply()) 成员函数。$(C parallel()) 返回一个 range 对象,它将决定如何将处理元素的$(C 委托)分发给独立的处理器核心执行。 +在之前 $(LINK2 /ders/d.cn/foreach_opapply.html, $(C foreach) 结构体和类) 一章中我们了解到: $(C foreach) 块中的表达式将会被包装成委托传递给对象的 $(C opApply()) 成员函数。$(C parallel()) 返回一个范围对象,它将决定如何将处理元素的 $(C delegate) 分发给独立的处理器核心执行。 ) $(P -在一个 4 核心的系统上运行上面的程序,传递给 $(C parallel()) 的 $(C Student) range 使处理时间缩小到 1 秒: +最后,在一个有 4 核的系统上,将 $(C Student) 范围传递给 $(C parallel()) 可以使上面的程序在 1 秒之内完成: ) $(SHELL @@ -179,19 +179,19 @@ sys 0m0.004s) ) $(P -$(I $(B 注:)在不同的系统上运行上面的程序可能会有不同的执行时间,但大致都是“4 秒除以核心数”这样的结果。) +$(I $(B 注意:)在不同的系统上运行上面的程序可能会有不同的执行时间,但大致都是“4 秒除以内核数”的结果。) ) $(P -$(IX thread) 执行程序某一部分的执行流被称为$(I 执行线程)或$(I 线程)。程序可由多个同时执行操作的线程组成。操作系统可在一个核心上启动和执行线程,在需要时将其挂起来让出核心运算资源去执行另一个线程。每个线程的执行都可能包含多轮启动和挂起。 +$(IX thread) 执行程序某一部分的执行流被称为 $(I 执行线程) 或 $(I 线程)。程序可由多个同时执行操作的线程组成。操作系统可在一个核心上启动和执行线程,在需要时将其挂起来让出核心运算资源去执行另一个线程。每个线程的执行都可能包含多轮启动和挂起。 ) $(P -所有程序的所有在操作系统指定时间活动的线程将会在微处理器的每个核心上执行。操作系统将会决定在合适何种情况启动或挂起线程。这就是上面程序中 $(C aSlowOperation()) 输出的信息是乱序的原因。 如果对 $(C Student) 对象的操作是相互独立的,那以一种不确定的顺序执行线程也不会有什么副作用。 +所有程序的所有在操作系统指定时间活动的线程将会在微处理器的每个核心上执行。操作系统将会决定在合适何种情况启动或挂起线程。这就是上面程序中 $(C aSlowOperation()) 输出的信息是乱序的原因。如果对 $(C Student) 对象的操作是相互独立的,那以一种不确定的顺序执行线程也不会有什么副作用。 ) $(P -只有在确定在每一次迭代中对元素的操作相互独立后,程序员才能调用 $(C parallel()) 来实现并行。举个例子:如果输出信息的顺序非常重要,那么上面程序中的 $(C parallel()) 调用将会使程序出错。支持线程的编程模型依赖线程的$(I 并发)调用。 并发性是下一章的话题。 +只有在确定在每一次迭代中对元素的操作相互独立后,程序员才能调用 $(C parallel()) 来实现并行。例如,如果输出信息的顺序非常重要,那么上面程序中的 $(C parallel()) 调用将会使程序出错。支持线程依赖的编程模型叫做$(I 并发(concurrency))。关于并发,下一章会主要讲解它。 ) $(P @@ -210,10 +210,10 @@ $(C parallel()) 的重载为其第二个参数赋予了多个含义,或使其 $(UL -$(LI 在迭代 $(C RandomAccessRange) range 时: +$(LI 在迭代 $(C RandomAccessRange) 范围时: $(P -虽然将线程分派给核心的开销极小。但在某些情况下这个开销会显得极其昂贵,尤其是在每次循环的操作耗时都非常短的时候。在这种情况下,让一个线程去执行多个迭代反而会使处理速度加快。工作单元大小决定了线程在其迭代中要处理元素的个数。 +虽然将线程分派给内核的开销极小。但在某些情况下这种开销会显得极其昂贵,尤其是在每次循环的操作耗时都非常短的时候。此时,让每个线程去执行循环的多个迭代反而会更快些。工作单元大小决定了每个线程在每次迭代时应该执行的元素个数。 ) --- @@ -228,19 +228,18 @@ $(P ) -$(LI 在迭代非 $(C RandomAccessRange) range 时: +$(LI 在迭代非 $(C RandomAccessRange) 范围时: $(P -只有在非 $(C RandomAccessRange) 中的与$(I 工作单元大小)数目相同的元素被顺序处理后,$(C parallel()) 才会开始并行执行。 -由于提供的默认值 100 相对较高,$(C parallel()) 会给人一种错觉:在处理较短的非 $(C RandomAccessRange) range 时效率不高。 +只有在非 $(C RandomAccessRange) 中与$(I 工作单元大小)相同数量的元素被顺序处理时,$(C parallel()) 才会开始并行执行。由于提供的默认值 100 相对较高,$(C parallel()) 会给人一种错觉:在处理较短的非 $(C RandomAccessRange) 范围时效率不高。 ) ) -$(LI 在迭代 $(C asyncBuf()) 或 $(C map()) 返回的 range 时(稍后本章会介绍这两个函数): +$(LI 在迭代 $(C asyncBuf()) 或 $(C map()) 返回的范围时(稍后本章会介绍这两个函数): $(P -若使用 $(C parallel()) 处理 $(C asyncBuf()) 或 $(C map()) 的返回值时,它将忽略工作单元大小参数。$(C parallel()) 将会使用这两个函数返回的 range 中的缓冲区。 +若使用 $(C parallel()) 处理 $(C asyncBuf()) 或 $(C map()) 的返回值时,它将忽略工作单元大小参数。$(C parallel()) 将会使用这两个函数返回的范围中的缓冲区。 ) ) @@ -250,7 +249,7 @@ $(P $(H5 $(IX Task) $(C Task)) $(P -程序中以并行的方式执行的操作叫做$(I 任务)。标准库中的 $(C std.parallelism.Task) 表示的就是我们所说的任务。 +程序中以并行的方式执行的操作叫做$(I 任务(task))。任务的表示类型为 $(C std.parallelism.Task)。 ) $(P @@ -258,15 +257,15 @@ $(P ) $(P -$(IX task) $(IX executeInNewThread) $(IX yieldForce) 如果要在处理 range 中元素之外的地方使用 task,程序员需要按照以下三步手动处理任务:使用 $(C task()) 创建 task,使用 $(C executeInNewThread()) 启动 task,使用 $(C yieldForce()) 等待 task 完成。在下方程序的注释中有对这三个函数详细的解释。 +$(IX task) $(IX executeInNewThread) $(IX yieldForce) 当任务无法与范围元素相对应或者无法通过它们来表示时,程序员可以自己按照以下三步手动处理:使用 $(C task()) 来创建任务对象;使用 $(C executeInNewThread()) 来启动任务对象;使用 $(C yieldForce()) 来等待任务对象。在下方程序的注释中有对这三个函数详细的解释。 ) $(P -在下方的程序中 $(C anOperation()) 函数被调用两次。它将打印出 $(C id) 的第一个字符,这样我们就可以通过这个字符来判断程序正在等待哪一个 task 执行完成。 +在下方的程序中 $(C anOperation()) 函数被调用两次。它将打印出 $(C id) 的第一个字符,这样我们就可以通过这个字符来判断程序正在等待哪一个任务执行完成。 ) $(P -$(IX flush, std.stdio)$(I $(B 注:)正常情况下向类似 $(C stdout) 这样的输出流直接输出的字符并不会立刻显示出来。它们将会被储存在输出缓冲区中,当缓冲区中累计了完整的一行时,stdout 才会将其显示在屏幕上。因为 $(C write) 并不会输出换行符,而为了能够在下面的程序中观察并行执行的情况,我们使用 $(C stdout.flush()) 使缓冲区中的数据能在未到达行尾时就被发送至 $(C stdout)。) +$(IX flush, std.stdio) $(I $(B 注意:)正常情况下向类似 $(C stdout) 这样的输出流直接输出的字符并不会立刻显示出来。它们将会被储存在输出缓冲区中,一直到整行输出完成为止都会显示。因为 $(C write) 并不会输出换行符,而为了能够在下面的程序中观察并行执行的情况,我们使用 $(C stdout.flush()) 使缓冲区中的数据能在未到达行尾时就被发送至 $(C stdout)。) ) --- @@ -275,9 +274,9 @@ import std.parallelism; import std.array; import core.thread; -/* 每半秒打印一次‘id’的首字母函数 - * 会随便返回一个值,假装进行了运算。 - * 下面的程序中这个返回值为 1。稍后会在 main 函数中使用本函数返回的结果。*/ +/* 每半秒打印一次‘id’的首字母函数。它 + * 会随意返回值 1,以便模拟函数正在 + * 进行计算。稍后会在 main 函数中使用这个结果。*/ int anOperation(string id, int duration) { writefln("%s will take %s seconds", id, duration); @@ -291,13 +290,13 @@ int anOperation(string id, int duration) { } void main() { - /* 构建一个将要执行 - * anOperation() 的 task。在此处被指定的函数实参 + /* 构建一个用于执行 + * anOperation() 的任务对象。在此处被指定的函数实参 * 将会被作为将要并行执行的任务函数的实参 * 传递给任务函数。*/ auto theTask = $(HILITE task!anOperation)("theTask", 5); - /* 启动 task */ + /* 启动任务对象 */ theTask.$(HILITE executeInNewThread()); /* 在‘theTask’执行的时候,main 函数会直接调用 @@ -307,14 +306,14 @@ void main() { /* 此处我们可以确认 * 直接在 main 函数中启动的操作 * 已经执行完成,因为它是一个常规 - * 函数调用,而不是 task。*/ + * 函数调用,而不是一个任务。*/ - /* 但在此处我们无法确定 + /* 另一方面,但在此处我们无法确定 * ‘theTask’执行的操作是否已经 - * 完成。yieldForce() 会等待 task 完成 + * 完成。yieldForce() 会等待该任务完成 * 操作,只有在任务完成时它才会 * 返回。它的返回值即为 - * 任务执行的函数的返回值。例如 anOperation()。*/ + * 任务执行的函数的返回值,如 anOperation()。*/ immutable taskResult = theTask.$(HILITE yieldForce()); writeln(); @@ -324,7 +323,7 @@ void main() { --- $(P -程序的输出和下面这样差不多:字符 $(C m) 和字符 $(C t) 无序的输出顺序表明这些操作是并行的: +程序的输出类似下面这个样子:字符 $(C m) 和字符 $(C t) 交替输出表明这些操作是并行执行的: ) $(SHELL @@ -335,11 +334,11 @@ All finished; the result is 2. ) $(P -上面的任务函数是 $(C task()) 的一个参数模版 $(C task!anOperation) 的实参。虽然这种方法在大多数情况下都工作的很好,但就如我们在 $(LINK2 /ders/d.cn/templates.html, 模版) 一章中看到的那样:每个不同的模版实例都是一个不同的类型。两个看起来$(I 相同)的 task 对象实际上不是同一个类型,在某些特定的情况下这种差别可能是我们不想看到的。 +上面的任务函数是 $(C task()) 的一个参数模版 $(C task!anOperation) 的实参。虽然这种方法在大多数情况下都工作的很好,但就如我们在 $(LINK2 /ders/d.cn/templates.html, 模版) 一章中看到的那样:每个不同的模版实例都是一个不同的类型。两个看起来 $(I 相同) 的任务对象实际上不是同一个类型,在某些特定的情况下这种差别可能是我们不想看到的。 ) $(P -比如,虽然两个函数有相同的函数签名,但两个通过 $(C task()) 函数模版创建的 $(C Task) 示例类型是不同的。因此它们不能被放在同一个数组里: +例如,虽然两个函数有相同的函数签名,但两个通过 $(C task()) 函数模版创建的 $(C Task) 示例类型是不同的。因此,它们不能被放在同一个数组里: ) --- @@ -365,7 +364,7 @@ Error: $(HILITE incompatible types) for ((task(1)) : (task(2))): ) $(P -$(C task()) 函数的另一个重载将函数作为其第一个形参: +$(C task()) 函数的另一个重载形式是将函数作为其第一个形参: ) --- @@ -377,7 +376,7 @@ $(C task()) 函数的另一个重载将函数作为其第一个形参: --- $(P -这种方法并不会产生不同类型的 $(C Task) 模版实例,这样你就可以把它们放在同一个数组里了: +采用这种方法不会产生不同类型的 $(C Task) 模版实例,因此可以把它们放在同一个数组里: ) --- @@ -393,7 +392,7 @@ double bar(int i) { void main() { auto tasks = [ task($(HILITE &)foo, 1), - task($(HILITE &)bar, 2) ]; $(CODE_NOTE compiles) + task($(HILITE &)bar, 2) ]; $(CODE_NOTE 编译通过) } --- @@ -407,10 +406,10 @@ lambda 函数和定义了 $(C opCall) 的对象都可以被当作任务函数使 $(HILITE }), 42); --- -$(H6 $(IX exception, 并行化) 异常处理) +$(H6 $(IX exception, parallelism) 异常处理) $(P -因为 task 是执行在另外一个线程上的,所以它们抛出的异常并不能被启动任务的线程捕捉到。因此,task 抛出的异常将会被 task 自动捕获。当调用像 $(C yieldForce()) 这样的 $(C Task) 的成员函数时,异常将会被重新抛出。这样我们就可以在主线程中捕获 task 中抛出的异常了。 +因为任务都是在一个单独的线程上执行的,所以启动它们的那个线程并不能捕捉到它们抛出的异常。因此,所有抛出的异常会自动被任务自已捕获到,然后在调用类似 $(C yieldForce()) 那样的 $(C Task) 成员函数时,再重新抛出。这样便可以在主线程里捕获任务中抛出的各种异常。 ) --- @@ -438,7 +437,7 @@ void main() { --- $(P -程序的输出显示由 task 抛出的异常并不会立刻导致整个程序终止运行(它只终止了对应的 task): +这个程序的输出表明由任务抛出的异常并不会立刻导致整个程序终止运行(它只终止了那个任务): ) $(SHELL @@ -450,7 +449,7 @@ object.Exception@deneme.d(10): Error message $(SHELL_NOTE 终止) ) $(P -可以在 $(C try-catch) 语句块中调用 $(C yieldForce()) 来捕获由 task 抛出的异常。这与单线程有着极大的不同:像本章上面的程序如果写成单线程的话,应该将 $(C try-catch) 包裹住可能会抛出异常的代码。而在多线程中,它只需包裹 $(C yieldForce()): +可以在 $(C try-catch) 语句块中调用 $(C yieldForce()) 来捕获由 task 抛出的异常。这与单线程有着极大的不同:像本章上面的程序如果写成单线程的话,应该将 $(C try-catch) 包裹住可能会抛出异常的代码。而在并行中,它只封装了 $(C yieldForce()): ) --- @@ -474,11 +473,11 @@ main is waiting for the task Detected an error in the task: 'Error message' $(SHELL_NOTE 捕获) ) -$(H6 $(C Task) 的成员函数) +$(H6 $(C Task)) 的成员函数 $(UL -$(LI $(C done):指明 task 是否完成;如果任务中有捕获异常,此处将重新抛出。 +$(LI $(C done):指明任务是否已完成;如果任务已经因为异常而中止,则重新抛出该异常。 --- if (theTask.done) { @@ -491,28 +490,28 @@ $(LI $(C done):指明 task 是否完成;如果任务中有捕获异常,此 ) -$(LI $(C executeInNewThread()):在新线程中启动 task。) +$(LI $(C executeInNewThread()):在新线程中启动任务。) -$(LI $(C executeInNewThread(int priority)):在新线程中启动 task,并指定线程优先级。(优先级是一个操作系统概念,它决定了线程执行的优先次序。)) +$(LI $(C executeInNewThread(int priority)):在新线程中启动任务,并指定线程优先级。(优先级是一个操作系统概念,它决定了线程执行的优先次序。)) ) $(P -有三个用来等待 task 完成的函数: +有三个用来等待任务完成的函数: ) $(UL -$(LI $(C yieldForce()):如果 task 没有启动,则启动 task;如果 task 已经完成,则返回任务函数的返回值;如果 task 正在执行,则以不耗费处理器资源的方式等待 task 完成;如果 task 在执行中抛出了一个异常,它将在此处重新抛出那个异常。) +$(LI $(C yieldForce()):如果任务没有启动,则启动它;如果任务已经完成,则返回任务函数的返回值;如果任务正在执行,则以不占用微处理器的方式等待该任务完成;如果任务在执行中抛出了一个异常,它将在此处重新抛出那个异常。) -$(LI $(IX spinForce) $(C spinForce()):与 $(C yieldForce()) 功能相似。不同的是在等待时它将以消耗更多处理器资源为代价换取更快的检测 task 完成的速度。) +$(LI $(IX spinForce) $(C spinForce()):与 $(C yieldForce()) 功能相似。不同的是在等待时它将以占用微处理器为代价换取更快的检测任务完成的速度。) -$(LI $(IX workForce) $(C workForce()):与 $(C yieldForce()) 功能相似。不同的是在等待任务完成时它将在当前线程中启动一个新的 task。) +$(LI $(IX workForce) $(C workForce()):与 $(C yieldForce()) 功能相似。不同的是,它在等待当前任务完成时会将启动一个新的任务。) ) $(P -在大多数情况下 $(C yieldForce()) 都是最适合用来等待任务完成的函数,$(C yieldForce()) 将阻塞调用它的线程直到任务完成。虽然在等待时 $(C spinForce()) 会使处理器忙绿,但它非常适合等待耗时短的 task。$(C workForce()) 则适合用需要在启动 task 的同时当前线程的情况。 +在大多数情况下,$(C yieldForce()) 是最适合用来等待任务完成的函数;$(C yieldForce()) 会将调用它的线程挂起,一直到任务完成为止。虽然在等待时 $(C spinForce()) 会使处理器忙碌,但它非常适合等待耗时短的任务。$(C workForce()) 则适合用需要优先启动其他任务,而不是先考虑将当前线程挂起的情况。 ) $(P @@ -522,15 +521,15 @@ $(P $(H5 $(IX asyncBuf) $(C taskPool.asyncBuf())) $(P -$(C asyncBuf()) 与 $(C parallel()) 相似,都是用来并行迭代 $(C InputRange) 的。它将 range 中的元素储存在缓冲区中,需要时用户再从缓冲区中获取元素。 +$(C asyncBuf()) 与 $(C parallel()) 相似,都是用来并行迭代 $(C InputRange) 的。它将范围中的元素储存在缓冲区中,需要时用户再从缓冲区中获取元素。 ) $(P -为了防止将输入的可能为延迟取值(fully-lazy)的 range 转换为即时取值(fully-eager)的 range,它会$(I 轮)的方式进行迭代。每轮在缓冲区中载入一定数量的元素用于并行迭代。只有当上一轮缓冲的元素被 $(C popFront()) 消耗完后,它才会开始为下一轮迭代缓冲元素。 +为了防止将输入的可能为延迟取值的范围转换为即时取值的范围,它会以$(I 轮动)的方式迭代这些元素。每轮在缓冲区中载入一定数量的元素用于并行迭代。只有当上一轮缓冲的元素被 $(C popFront()) 消耗完后,它才会开始为下一轮迭代缓冲元素。 ) $(P -$(C asyncBuf()) 有两个形参:一个是 range,另一个是可选的$(I 缓冲区大小)。缓冲区大小决定了每轮将有多少元素被加载到缓冲区。 +$(C asyncBuf()) 有两个形参:一个是范围;另一个是可选的$(I 缓冲区大小)。缓冲区大小决定了每轮将有多少元素被加载到缓冲区。 ) --- @@ -538,7 +537,7 @@ $(C asyncBuf()) 有两个形参:一个是 range,另一个是可选的$(I 缓 --- $(P -为了能突出 $(C asyncBuf()) 的作用,示例中的 range 与之前的 range 有些不同:遍历一次要耗时半秒,处理一个元素也要耗费半秒。这个 range 的作用就是提供一个不高于指定上限的整数。 +为了能突出 $(C asyncBuf()) 的作用,示例中的范围与之前的范围有些不同:遍历一次要耗时半秒,处理一个元素也要耗费半秒。这个范围的作用就是提供一个不高于指定上限的整数。 ) --- @@ -575,7 +574,7 @@ void main() { --- $(P -range 中的元素是延迟取值的。因为处理一个元素耗时 1 秒,整个程序总共花费 10 秒。 +这些元素都是采用惰性方式生成和使用的。因为处理一个元素耗时 1 秒,整个程序总共花费 10 秒。 ) $(SHELL @@ -597,7 +596,7 @@ sys 0m0.000s) ) $(P -从输出可以看出,元素是被依次求值并使用的。 +从输出结果来看,这些元素都被依次生成和使用。 ) $(P @@ -638,7 +637,7 @@ Using element 7 Using element 8 Using element 9 -real 0m6.007s $(SHELL_NOTE 现在只耗时 6 秒) +real 0m6.007s $(SHELL_NOTE 现在耗时 6 秒) user 0m0.000s sys 0m0.004s) ) @@ -648,7 +647,7 @@ $(P ) $(P -$(C asyncBuf()) 也可以在 $(C foreach) 外使用。比如下面这个例子就将 $(C asyncBuf()) 的返回值作为一个半延迟(semi-eagerly)取值的 $(C InputRange): +$(C asyncBuf()) 也可以在 $(C foreach) 外使用。例如,下面这个例子就将 $(C asyncBuf()) 的返回值作为一个半延迟取值的 $(C InputRange): ) --- @@ -657,22 +656,22 @@ $(C asyncBuf()) 也可以在 $(C foreach) 外使用。比如下面这个例子 writeln($(HILITE asyncRange.front)); --- -$(H5 $(IX map, parallel) $(IX map, 并行) $(C taskPool.map())) +$(H5 $(IX map, parallel) $(C taskPool.map())) $(P -$(IX map, std.algorithm) 在介绍 $(C taskPool.map()) 之前,先了解一下 $(C std.algorithm) 模块的 $(C map()) 对理解本节的内容是非常有帮助的。在大多数函数式编程语言中你都能找到 $(C std.algorithm.map) 这样的算法。它会对 range 中的每一个元素应用指定的函数,并将函数的返回值组合为 range 返回。这是一个延迟求值的算法,只有在返回的结果被使用时它才会调用指定的函数进行运算。(标准库中还有一个与之相似的 $(C std.algorithm.each)。但不同的是它并不会返回新的 range 来储存结果,而是直接将结果应用到传入的 range 的元素上。) +$(IX map, std.algorithm)在介绍 $(C taskPool.map()) 之前,先了解一下 $(C std.algorithm) 模块里的 $(C map()),这样有助于理解本节的内容。在许多函数式编程语言里都能找到与 $(C std.algorithm.map) 类似的算法。它会用范围里元素一个接一个地调用函数,并返回一个范围(其组成元素是每次调用该函数时得到的结果)。这是个懒式算法:函数只有在需要时才会被调用。标准库中还有一个与之相似的 $(C std.algorithm.each)。但不同的是它并不会返回新的范围来储存结果,而是直接将结果应用到传入的范围的元素上。 ) $(P -实际上对很多程序来说, $(C std.algorithm.map) 的延迟取值是非常有用的。但如果 range 中的每个元素都注定要被作为实参传递给函数而且每次操作又都是独立的的话,我们根本就没必要使用速度较慢的延迟取值而不是并行操作。$(C std.parallelism) 模块中的 $(C taskPool.map()) 和 $(C taskPool.amap()) 可以有效利用多核心的计算能力,在大多数情况下它可以加快程序的运行。 +在很多程序里,$(C std.algorithm.map) 的懒式操作都会非常有用。但如果范围中的每个元素都注定要被作为实参传递给函数而且每次操作又都是独立的的话,我们根本就没必要使用速度较慢的延迟取值而不是并行操作。$(C std.parallelism) 模块里的 $(C taskPool.map()) 和 $(C taskPool.amap()) 会充分利用多核,并在大部分情况下加快程序的运行速度。 ) $(P -我们会用 $(C Student) 作为例子来比较个三个算法。我们假设 $(C Student) 有一个计算并返回学生平均分的成员函数。为了能够突出并行算法的速度,我们还是使用 $(C Thread.sleep()) 让整个过程消耗更多的时间。 +下面用 $(C Student) 作为示例,对这三个算法做个比较。假设 $(C Student) 有一个计算并返回学生平均分的成员函数。为了展示并行算法到底有多快,我们需要再次使用 $(C Thread.sleep()) 将这个函数变慢。 ) $(P -$(C std.algorithm.map) 有一个模版参数用来接收函数,有一个函数形参用来接收 range 。它会返回一个新的 range 来储存应用函数后得到的结果: +$(C std.algorithm.map) 有一个模版参数用来接收函数,有一个函数形参用来接收范围。它会返回一个新的范围来储存应用函数后得到的结果: ) --- @@ -748,7 +747,7 @@ $(P ) $(P -$(C std.parallelism) 模块中的 $(C taskPool.map()) 和 $(C std.algorithm.map) 的功能相同。唯一不同的是 $(C taskPool.map()) 是以半延迟(semi-eagerly)取值的方式调用函数操作元素,并将结果储存在缓冲区中。缓冲区的大小由第二个形参决定。例如下面的代码,每次将会准备三个对元素应用函数后的结果: +$(C std.parallelism) 模块中的 $(C taskPool.map()) 和 $(C std.algorithm.map) 的功能相同。唯一不同的是 $(C taskPool.map()) 是以半延迟取值的方式调用函数操作元素,并将结果储存在缓冲区中。缓冲区的大小由第二个形参决定。例如,下面的代码每次都会为三个元素准备好函数调用的结果: ) --- @@ -762,7 +761,7 @@ double averageGrade(Student student) { --- $(P -$(I $(B 注:) 之所以上面的代码需要一个独立的 $(C averageGrade()) 函数是因为像 $(C TaskPool.map) 这样的成员模版函数存在无法使用局部委托的限制。如果不使用独立的函数的话,程序将不能通过编译: +$(I $(B 注意:)之所以上面的代码需要一个独立的 $(C averageGrade()) 函数是因为像 $(C TaskPool.map) 这样的成员模版函数存在无法使用局部委托的限制。如果不使用独立的函数的话,程序将不能通过编译: )) --- @@ -826,17 +825,17 @@ $(C map()) 的第二个参数与 $(C asyncBuf()) 的第二个参数含义相同 $(H5 $(IX amap) $(C taskPool.amap())) $(P -并行 $(C amap()) 与 并行 $(C map()) 作用相似,但有两点不同: +并行 $(C amap()) 与并行 $(C map()) 作用基本相似,但有以下两点不同: ) $(UL $(LI -它是即时求值的。 +它是即时计算。 ) $(LI -它是用于处理 $(C RandomAccessRange) ranges 的。 +它适用于 $(C RandomAccessRange) 范围。 ) ) @@ -846,7 +845,7 @@ $(LI --- $(P -因为它是即时求值的,所以在 $(C amap()) 返回时所有元素都已被运算完成: +因为它是即时计算,所以在 $(C amap()) 返回时所有元素都已被运算完成: ) $(SHELL @@ -889,7 +888,7 @@ sys 0m0.004s) ) $(P -$(C amap()) 的确比 $(C map()) 的速度快。但相应的代价是它需要提前准备好一个足够大的数组来储存结果。它是通过消耗更多的内存来换取更快的速度。 +$(C amap()) 的确比 $(C map()) 的速度快。但相应的代价是它需要提前准备好一个足够大的数组来储存结果。它是通过消耗更多的内存来获得更快速度的。 ) $(P @@ -910,18 +909,18 @@ $(P taskPool.amap!averageGrade(students, 2, $(HILITE results)); --- -$(H5 $(IX reduce, parallel) $(IX reduce, 并行) $(C taskPool.reduce())) +$(H5 $(IX reduce, parallel) $(C taskPool.reduce())) $(P -$(IX reduce, std.algorithm) 与 $(C map()) 一样,先来了解下 $(C std.algorithm) 模块的 $(C reduce()) 有助于本节的我们讲解的知识。 +$(IX reduce, std.algorithm)与 $(C map()) 一样,先来了解下 $(C std.algorithm) 模块里的 $(C reduce())。 ) $(P -$(IX fold, std.algorithm) $(C reduce()) 与 $(C std.algorithm.fold) 相同。我们已经在 $(LINK2 /ders/d.cn/ranges.html, Ranges) 一章中学习到了相关知识。两者的不同点在于它们的形参顺序正好相反。(因此,我建议你最好在非并行代码中使用 $(C fold())。因为它可以利用链式 range 表达式的 $(LINK2 /ders/d.cn/ufcs.html, UFCS)。) +$(IX fold, std.algorithm) $(C reduce()) 与 $(C std.algorithm.fold) 相同。我们已经在 $(LINK2 /ders/d.cn/ranges.html, Ranges) 一章中学习到了相关知识。两者的不同点在于它们的形参顺序正好相反。(因此,我建议你最好在非并行代码中使用 $(C fold())。因为它可以利用链式范围表达式的 $(LINK2 /ders/d.cn/ufcs.html, UFCS)。 ) $(P -$(C reduce()) 也是一个经常在函数式编程中使用的高阶函数。和 $(C map()) 一样,它也可以接收一个或多个函数作为模版实参。除了用于接收函数的模版形参,它需要传入一个运算结果的初始值和一个 range。$(C reduce()) 将传入的函数作为运算方法对每个元素进行计算并按照合并到结果中。传入的函数应有两个参数:一个表示当前运算的总结果,一个表示 range 中的元素。如果没有指定初始值,它会把 range 的第一个元素作为初始值。 +$(C reduce()) 也是一个经常在函数式编程中使用的高阶函数。和 $(C map()) 一样,它也可以接收一个或多个函数作为模版实参。除了用于接收函数的模版形参,它需要传入一个运算结果的初始值和一个范围。$(C reduce()) 将传入的函数作为运算方法对每个元素进行计算并按照合并到结果中。如果没有指定初始值,它会把范围的第一个元素作为初始值。 ) $(P @@ -939,7 +938,7 @@ $(LI 返回最终 $(C result) 的值) ) $(P -例如下面这个计算数组元素的平方和的程序: +例如,下面这个计算数组元素的平方和的程序: ) --- @@ -968,7 +967,7 @@ $(P ) $(P -为了展现出 $(C reduce()) 的威力,我们的例子依旧使用一个被人为减慢的函数来模拟长耗时的操作: +下面来看一个示例,将 $(C reduce()) 与一个人为减慢的函数一起使用: ) --- @@ -1027,7 +1026,7 @@ import std.parallelism; --- $(P -但与之前的函数相比, $(C taskPool.reduce()) 的行为有一个非常重要的不同点。 +不过,与之前的函数相比,$(C taskPool.reduce()) 的行为有一个非常重要的不同点。 ) $(P @@ -1035,11 +1034,11 @@ $(C taskPool.reduce()) 在不同的任务中并行执行函数,这与其它的 ) $(P -每个任务都会得到一个结果,这些最终结果将会按照与计算元素相同的方法计算,得到最终的 $(C result)。这个最终结果的计算是串行的。因此,在某些耗时少的例子(比如本章中的某些例子)中 $(C taskPool.reduce()) 可能会比非并行版本执行速度慢。 +每个任务都会得到一个结果,这些最终结果将会按照与计算元素相同的方法计算,得到最终的 $(C result)。这个最终的计算是顺序执行的,而非并行。因此,在某些耗时少的例子(例如本章中的某些例子)中 $(C taskPool.reduce()) 可能会比非并行版本执行速度慢。 ) $(P -又由于所有任务都是用相同的初始值,这些初始值会被多次使用,因此 $(C taskPool.reduce()) 的运算结果可能和 $(C std.algorithm.reduce()) 不同。所以指定的初始值应为$(I 恒等值)。比如本例中的 $(C 0) 就不会产生任何副作用。 +又由于所有任务都是用相同的初始值,这些初始值会被多次使用,因此 $(C taskPool.reduce()) 的运算结果可能和 $(C std.algorithm.reduce()) 不同。所以指定的初始值应为$(I 恒等值)。例如本例中的 $(C 0) 就不会产生任何副作用。 ) $(P @@ -1063,13 +1062,13 @@ $(P $(SHELL $ time ./deneme $(SHELL_OBSERVED -started - element: 3, result: 0 $(SHELL_NOTE 起初时使用任务并行计算) +started - element: 3, result: 0 $(SHELL_NOTE 首先,使用任务并行计算) 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 之后串行计算结果) +$(HILITE started - element: 1, result: 0) $(SHELL_NOTE 接着,顺序计算结果) finished - element: 4, result: 4 finished - element: 2, result: 2 $(HILITE finished - element: 1, result: 1) @@ -1081,7 +1080,7 @@ $(HILITE started - element: 4, result: 6) $(HILITE finished - element: 4, result: 10) Result: 10 -real 0m5.006s $(SHELL_NOTE 在本例中并行算法比串行算法慢) +real 0m5.006s $(SHELL_NOTE 在本例中并行 reduce 更慢一点) user 0m0.004s sys 0m0.000s) ) @@ -1090,14 +1089,14 @@ $(P 不过对于许多算法来说并行 $(C reduce()) 要比其串行版本快,例如计算 $(I pi) (π)的算法。 ) -$(H5 多个函数和 tuple 结果) +$(H5 多个函数和元组结果) $(P -$(C std.algorithm.map())、$(C taskPool.map())、$(C taskPool.amap()) 和 $(C taskPool.reduce()) 都允许传入多个函数处理元素,并返回类型为 $(C Tuple) 的结果。我们之前已经在 $(LINK2 /ders/d.cn/tuples.html, Tuples) 一章中了解过 $(C Tuple)。传入函数的顺序将决定 tuple 中与之对应的结果的顺序。例如:第一个函数的结果即为 tuple 的第一个元素。 +$(C std.algorithm.map())、$(C taskPool.map())、$(C taskPool.amap()) 和 $(C taskPool.reduce()) 都允许传入多个函数处理元素,并返回类型为 $(C Tuple) 的结果。我们之前已经在 $(LINK2 /ders/d.cn/tuples.html, Tuples) 一章中了解过 $(C Tuple)。传入函数的顺序将决定 tuple 中与之对应的结果的顺序。例如,第一个函数的结果即为元组的第一个元素。 ) $(P -下面这个例子演示如何向 $(C std.algorithm.map) 传入多个函数并使用其返回的结果。注意这些函数的返回值类型不一定非要相同,就像下面例子中的 $(C quarterOf()) 和 $(C tenTimes()),一个返回 double 一个返回 string。因此 tuples 中的元素类型也不一定相同: +下面这个例子演示如何向 $(C std.algorithm.map) 传入多个函数并使用其返回的结果。注意,这些函数的返回类型不一定相同,如下面示例中的 $(C quarterOf()) 和 $(C tenTimes()) 函数。此时,元组成员的类型也不一定相同: ) --- @@ -1126,7 +1125,7 @@ void main() { --- $(P -输出为: +输出: ) $(SHELL @@ -1137,7 +1136,7 @@ $(SHELL ) $(P -如果向 $(C taskPool.reduce()) 传入多个函数,那为其指定的初始值也需要改为 tuple 类型: +如果向 $(C taskPool.reduce()) 传入多个函数,那为其指定的初始值也需要改为元组类型: ) --- @@ -1147,22 +1146,21 @@ $(P $(H5 $(IX TaskPool) $(C TaskPool)) $(P -当我们享受 $(C std.parallelism) 模块带来的便利时,标准库在后台默默地维护着 $(C TaskPool) 容器中的 task 对象。正常情况下,所有算法使用的都是一个容器,即 $(C taskPool)。 +在这些场景的背后,源自 $(C std.parallelism) 模块的所有并行算法使用的任务对象都是 $(C TaskPool) 容器里的元素。一般情况下,所有算法都会使用同一个名为 $(C taskPool) 的容器对象。 ) $(P -$(C taskPool) 包含的 task 的个数取决于程序运行的环境。因此通常我们不需要手动创建 $(C TaskPool) 对象。即便如此我们还是可以在需要的时候显示创建 $(C TaskPool) 对象。 +$(C taskPool) 需要容纳的任务总数取决于程序运行的具体环境。因此,通常不需要创建其他的 $(C TaskPool) 对象。即便如此,也可以在需要的时候显示创建和使用 $(C TaskPool) 对象。 ) $(P -$(C TaskPool) 构造函数有一个参数。通过指定这个参数就可以控制之后并行执行的线程个数。参数的默认值不会比处理器核心数大。本章介绍的所有功能都可以通过使用一个单独的 $(C TaskPool) 来调用。 +$(C TaskPool) 构造函数有一个参数。通过指定这个参数就可以控制之后并行执行的线程个数。默认的线程数量比系统内核数少 1。本章介绍的所有功能都可以通过使用一个单独的 $(C TaskPool) 来调用。 ) $(P 下面这个例子使用了一个局部 $(C TaskPool) 对象来调用 $(C parallel())。 ) - --- import std.stdio; import std.parallelism; @@ -1187,25 +1185,25 @@ $(H5 小结) $(UL -$(LI 只有在操作相互独立时才可以将它们并行化。否则将出错。) +$(LI 操作只有在相互独立时才能并行执行;否则会出错。) -$(LI $(C parallel()) 并行访问 range 中的元素。) +$(LI $(C parallel()) 可以并行访问范围中的元素。) -$(LI Tasks 可手动通过 $(C task()) 创建、通过 $(C executeInNewThread()) 启动、通过 $(C yieldForce()) 等待完成。) +$(LI 任务可以显示地用 $(C task()) 创建、用 $(C executeInNewThread()) 启动,以及用 $(C yieldForce()) 来等待完成。) -$(LI task 中操作抛出的异常可在之后调用类似 $(C yieldForce()) 这样的并行函数时捕获。) +$(LI 任务执行过程中抛出的异常可以在随后被类似 $(C yieldForce()) 的并行函数捕获。) -$(LI $(C asyncBuf):以并行半延时(semi-eagerly)取值的方式迭代 $(C InputRange) 中的元素。) +$(LI $(C asyncBuf()):以半延时取值的方式并行迭代 $(C InputRange) 中的元素。) -$(LI $(C map):以并行半延时(semi-eagerly)取值的方式对 $(C InputRange) 中的每一个元素应用指定的函数。) +$(LI $(C map()):以半延时取值的方式对 $(C InputRange) 中的元素并行调用指定的函数。) -$(LI $(C amap):以并行即时(fully-eagerly)取值的方式对 $(C RandomAccessRange) 中的元素应用指定的函数。) +$(LI $(C amap()):以即时取值的方式对 $(C RandomAccessRange) 中的元素并行调用指定的函数。) -$(LI $(C reduce):使用指定的函数并行归约计算 $(C RandomAccessRange) 中的元素。) +$(LI $(C reduce()):并行归约计算 $(C RandomAccessRange) 中的元素。) -$(LI $(C map())、$(C amap()) 或 $(C reduce()) 可以传入多个函数并返回 tuples 类型的结果。) +$(LI $(C map())、$(C amap()) 或 $(C reduce()) 可以传入多个函数并以元组方式让出返回结果。) -$(LI 如果需要的话,可以使用 $(C TaskPool) 对象而不是 $(C taskPool) 进行操作。) +$(LI 如有必要,也可以使用 $(C TaskPool) 对象来代替 $(C taskPool) 。) ) @@ -1214,6 +1212,6 @@ macros: DESCRIPTION=并行编程来利用多核心微处理器的运算能力 - KEYWORDS=D 编程语言教程 并行编程 + KEYWORDS=d programming language tutorial book parallel programming 编程 语言 教程 书籍 并行 MINI_SOZLUK= diff --git a/ddili/src/ders/d.en/parameter_flexibility.cozum.d b/target/parameter_flexibility.cozum.d similarity index 99% rename from ddili/src/ders/d.en/parameter_flexibility.cozum.d rename to target/parameter_flexibility.cozum.d index 2830145..1dfb531 100644 --- a/ddili/src/ders/d.en/parameter_flexibility.cozum.d +++ b/target/parameter_flexibility.cozum.d @@ -90,7 +90,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.cn/parameter_flexibility.d b/target/parameter_flexibility.d similarity index 90% rename from ddili/src/ders/d.cn/parameter_flexibility.d rename to target/parameter_flexibility.d index 434d109..dcf0efa 100644 --- a/ddili/src/ders/d.cn/parameter_flexibility.d +++ b/target/parameter_flexibility.d @@ -22,7 +22,7 @@ Some of the parameters of some functions are called mostly by the same values. T ) --- -import std.algorithm; +$(CODE_NAME printAA)import std.algorithm; // ... @@ -34,12 +34,16 @@ void printAA(in char[] title, auto keys = sort(aa.keys); - foreach (i, key; keys) { - // No separator before the first element - if (i != 0) { - write(elementSeparator); - } + // 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]); } @@ -48,18 +52,20 @@ void printAA(in char[] title, --- $(P -That function is being called below by $(STRING ":") as the key separator and $(STRING ", ") as the element separator: +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 @@ -100,7 +106,7 @@ The parameter values can still be specified when needed, and not necessarily all --- $(P -The output: +输出: ) $(SHELL @@ -117,7 +123,7 @@ The following call specifies both of the parameters: --- $(P -The output: +输出: ) $(SHELL @@ -139,11 +145,12 @@ $(IX special keyword) $(IX keyword, special) The following special keywords act $(UL -$(LI $(IX __MODULE__) $(C __MODULE__): Name of the module) -$(LI $(IX __FILE__) $(C __FILE__): Name of the source file) -$(LI $(IX __LINE__) $(C __LINE__): Line number) -$(LI $(IX __FUNCTION__) $(C __FUNCTION__): Name of the function) -$(LI $(IX __PRETTY_FUNCTION__) $(C __PRETTY_FUNCTION__): Full signature of the function) +$(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)) ) @@ -182,7 +189,7 @@ import std.stdio; void func(int parameter, string functionName = $(HILITE __FUNCTION__), string file = $(HILITE __FILE__), - size_t line = $(HILITE __LINE__)) { + int line = $(HILITE __LINE__)) { writefln("Called from function %s at file %s, line %s.", functionName, file, line); } @@ -206,15 +213,15 @@ $(IX special token) $(IX token, special) In addition to the above, there are als $(UL -$(LI $(IX __DATE__) $(C __DATE__): Date of compilation) +$(LI $(IX __DATE__) $(C __DATE__): Date of compilation as $(C string)) -$(LI $(IX __TIME__) $(C __TIME__): Time of compilation) +$(LI $(IX __TIME__) $(C __TIME__): Time of compilation as $(C string)) -$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Date and time of compilation) +$(LI $(IX __TIMESTAMP__) $(C __TIMESTAMP__): Date and time of compilation as $(C string)) -$(LI $(IX __VENDOR__) $(C __VENDOR__): Compiler vendor (e.g. $(STRING "Digital Mars D"))) +$(LI $(IX __VENDOR__) $(C __VENDOR__): Compiler vendor as $(C string) (e.g. $(STRING "Digital Mars D"))) -$(LI $(IX __VERSION__) $(C __VERSION__): Compiler version as an integer (e.g. the value $(C 2069) for version 2.069)) +$(LI $(IX __VERSION__) $(C __VERSION__): Compiler version as $(C long) (e.g. the value $(C 2072L) for version 2.072)) ) @@ -324,7 +331,7 @@ As long as the mandatory parameters are specified, the rest are optional: --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.en/pointers.cozum.d b/target/pointers.cozum.d similarity index 99% rename from ddili/src/ders/d.en/pointers.cozum.d rename to target/pointers.cozum.d index 6947c09..6d8971d 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/pointers.d b/target/pointers.d similarity index 99% rename from ddili/src/ders/d.en/pointers.d rename to target/pointers.d index dc0a9f4..fec78b6 100644 --- a/ddili/src/ders/d.en/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 @@ -1096,14 +1096,14 @@ The actual type of $(C element) above is a pointer to the same type of the eleme $(H5 When to use pointers) -$(H6 When required by libraries) - $(P -Although pointer parameters are rare in D libraries, we have already seen that $(C readf()) is one function that requires pointers. +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 -As another example, the following function from the GtkD library takes a pointer as well: +Pointers can appear on C and C++ library bindings. For example, the following function from the GtkD library takes a pointer: ) --- @@ -1308,7 +1308,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/ddili/src/ders/d.en/pragma.d b/target/pragma.d similarity index 99% rename from ddili/src/ders/d.en/pragma.d rename to target/pragma.d index 4ce7484..1955d4a 100644 --- a/ddili/src/ders/d.en/pragma.d +++ b/target/pragma.d @@ -173,8 +173,7 @@ These pragmas can affect the function that they appear in, as well as they can b ) --- -pragma(inline, false) -$(HILITE {) +pragma(inline, false) $(HILITE {) // Functions defined in this scope should not be inlined // ... $(HILITE }) diff --git a/ddili/src/ders/d.en/preface.d b/target/preface.d similarity index 92% rename from ddili/src/ders/d.en/preface.d rename to target/preface.d index 4f4541e..6fffa49 100644 --- a/ddili/src/ders/d.en/preface.d +++ b/target/preface.d @@ -39,7 +39,7 @@ I am indebted to the following people who have been instrumental during the evol ) $(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, and Chen Lejia. +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 @@ -47,7 +47,7 @@ Thanks especially to Luís Marques who, through his hard work, improved every ch ) $(P -Thanks to Luís Marques, Steven Schveighoffer, Andrej Mitrović, Robbin Carlson, and Ergin Güney for their suggestions that elevated this book from my Inglish to English. +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 @@ -59,7 +59,7 @@ Ebru, Damla, and Derin, thank you for being so patient and supportive while I wa $(BR) $(BR) Ali Çehreli$(BR) -Mountain View, $(I April 2016) +Mountain View, $(I May 2017) ) ) diff --git a/ddili/src/ders/d.cn/property.d b/target/property.d similarity index 71% rename from ddili/src/ders/d.cn/property.d rename to target/property.d index 8e66a05..84c1272 100644 --- a/ddili/src/ders/d.cn/property.d +++ b/target/property.d @@ -1,13 +1,13 @@ Ddoc -$(DERS_BOLUMU $(IX property) 属性) +$(DERS_BOLUMU $(IX property) 特性) $(P -属性能让我们使用像访问成员变量一样的语法调用成员函数。 +特性能让我们使用像访问成员变量一样的语法调用成员函数。 ) $(P -在学习 slice 的时候我们已经多次用到它。slice 的 $(C length) 属性返回了其包含元素的个数: +在学习分片(slice)的时候我们已经多次用到它。分片的 $(C length) 特性返回了其包含元素的个数: ) --- @@ -28,15 +28,15 @@ struct SliceImplementation { --- $(P -但属性提供了成员变量所不能提供的功能:对 $(C .length) 赋值将会改变 slice 的真实长度。向数组中添加新元素时就包含这一过程: +但特性提供了成员变量所不能提供的功能:对 $(C .length) 赋值将会改变分片的真实长度。向数组中添加新元素时就包含这一过程: ) --- - slice$(HILITE .length = 5); // slice 现在有 5 个元素 + slice$(HILITE .length = 5); // slice现在有 5 个元素 assert(slice.length == 5); --- -$(P $(I $(B 注:)无法修改定长数组的 $(C .length) 属性。) +$(P $(I $(B 注意:) 无法修改定长数组的 $(C .length) 特性。) ) $(P @@ -48,7 +48,7 @@ $(P ) $(P -$(IX @property) 属性实际上就是成员函数,但我们却可以像使用成员变量一样调用它们。所需的额外工作就是在函数的前面加一个 $(C @property)。 +$(IX @property) 特性实际上就是成员函数,但我们却可以像使用成员变量一样调用它们。所需的额外工作就是在函数的前面加一个 $(C @property)。 ) $(H5 不使用圆括号的函数调用) @@ -63,10 +63,10 @@ $(IX ()) 我们在之前的章节中提到过,如果调用函数时不需要 --- $(P -这个特性和属性很像,它们都不需要圆括号。 +这个功能与特性很像,因为在使用特性时都不带圆括号。 ) -$(H5 返回值的属性函数) +$(H5 返回值的特性函数) $(P 我们使用下面这个包含两个成员的矩形结构体作为示例: @@ -80,7 +80,7 @@ struct Rectangle { --- $(P -现在假设我们需要为这个类型添加一个新属性用于表示这个矩形的面积: +现在假设我们需要为这个类型添加一个新特性用于表示这个矩形的面积: ) --- @@ -89,7 +89,7 @@ $(P --- $(P -一种方案是在矩形结构体中定义第三个成员变量: +一种方法是在矩形结构体中定义第三个成员变量: ) --- @@ -105,16 +105,16 @@ $(P ) $(P -作为一个极端的例子,对象在其生命期开始时就已经处在不一致的状态了: +有一个极端例子,对象在其生命期开始时就已经处在不一致的状态: ) --- - // 不一致的对象:面积不是 10 * 20 == 200。 + // 不一致的对象:面积不是10 * 20 == 200. auto garden = Rectangle(10, 20, $(HILITE 1111)); --- $(P -所以我们最好用属性来实现面积。我们要定义一个名为 $(C area) 的函数来计算面积而不是定义成员变量来表示面积: +所以我们最好用特性来实现面积。我们要定义一个名为 $(C area) 的函数来计算面积而不是定义成员变量来表示面积: ) --- @@ -128,11 +128,11 @@ struct Rectangle { } --- -$(P $(I $(B 注:)如果你还记得我们在 $(LINK2 /ders/d.cn/const_member_functions.html, $(CH4 const ref) 参数和 $(CH4 const) 成员函数) 一章中所讲的,函数声明中的 $(C const) 说明符表示函数不会修改其对象。) +$(P $(I $(B 注意:) 如果你还记得我们在 $(LINK2 /ders/d.cn/const_member_functions.html, $(CH4 const ref) 参数和 $(CH4 const) 成员函数) 一章中所讲的,函数声明中的 $(C const) 说明符表示函数不会修改其对象。) ) $(P -属性函数让我们能像使用成员变量一样使用它: +特性函数让我们能像使用成员变量一样使用它: ) --- @@ -148,10 +148,10 @@ $(SHELL The area of the garden: 200 ) -$(H5 用于赋值的属性函数) +$(H5 用于赋值的特性函数) $(P -和 slice 的 $(C length) 属性一样,用户定义的属性也可以用于赋值: +与分片的 $(C length) 特性一样,用户定义的特性也可以用于赋值: ) --- @@ -159,7 +159,7 @@ $(P --- $(P -改变矩形面积时也要将长和宽修改为对应的值。为了能够提供这样的功能,我们假设矩形的边长是$(I 可变的)。这样这个矩形就保持恒等式“长 × 宽 == 面积”成立。 +改变矩形面积时也要将长和宽修改为对应的值。为了能够提供这样的功能,我们假设矩形的边长是$(I 可变的)。如此一来,为保持恒等式“长 × 宽 == 面积”成立,可以更改该矩形的各条边。 ) $(P @@ -167,7 +167,7 @@ $(P ) $(P -下面新增的 $(C area()) 让我们可以对 $(C area) 使用赋值运算符,而且对它赋值的确会改变 $(C Rectangle) 对象的面积: +在下面示例里,新增 $(C area()) 定义之后,我们可以在赋值中使用该特性,并高效地更改 $(C Rectangle) 对象的面积: ) --- @@ -202,7 +202,7 @@ void main() { --- $(P -新增的属性函数利用 $(C std.math) 模块中的 $(C sqrt) 函数来开平方根。如果长和宽都是面积的平方根,那它们的乘积就一定是我们指定的面积。 +新增的特性函数利用 $(C std.math) 模块中的 $(C sqrt) 函数来开平方根。如果长和宽都是面积的平方根,那它们的乘积就一定是我们指定的面积。 ) $(P @@ -214,10 +214,10 @@ The area of the garden: 200 New state: 5 x 10 = 50 ) -$(H5 属性不是唯一的解决方案) +$(H5 特性不是唯一的解决方案) $(P -之前我们通过定义属性为 $(C Rectangle) 添加了“第三个变量”。当然,除了属性,普通成员函数也可以实现我们需要的功能: +之前我们通过定义特性为 $(C Rectangle) 添加了“第三个变量”。当然,除了特性,普通成员函数也可以实现我们需要的功能: ) --- @@ -265,18 +265,18 @@ $(P } --- -$(H5 何时使用属性) +$(H5 何时使用特性) $(P -并没有一种简单的方法能帮你快速在常规成员函数和属性间做出选择。有时常规成员函数更加自然,有时属性更加简洁。 +并没有一种简单的方法能帮你快速在常规成员函数和特性间做出选择。有时常规成员函数更加自然,有时特性更加简洁。 ) $(P -我们曾在 $(LINK2 /ders/d.cn/encapsulation.html, 封装和访问控制) 一章中告诉过你:限制外部代码对成员变量的访问是非常重要的。允许外部代码随意修改成员变量可能会对将来的维护工作造成极大的不便。所以,我们最好将对成员变量的访问封装在常规成员函数或属性函数中。 +我们曾在 $(LINK2 /ders/d.cn/encapsulation.html, 封装和访问控制) 一章中告诉过你:限制外部代码对成员变量的访问是非常重要的。允许外部代码随意修改成员变量可能会对将来的维护工作造成极大的不便。所以,我们最好将对成员变量的访问封装在常规成员函数或特性函数中。 ) $(P -像 $(C width) 和 $(C height) 简单类型的成员变量,$(C public) 访问权限也是可以接受的。但最好还是使用属性函数包装: +像 $(C width) 和 $(C height) 简单类型的成员变量,$(C public) 访问权限也是可以接受的。但最好还是使用特性函数包装: ) --- @@ -310,7 +310,7 @@ public: --- $(P -现在成员变量被封装为 $(C private),所以只有与之相关联的属性函数可以访问它。 +现在成员变量被封装为 $(C private),所以只有与之相关联的特性函数可以访问它。 ) $(P @@ -328,7 +328,7 @@ $(C Rectangle) 的定义依旧存在 $(C width) 和 $(C height),它们也依 --- $(P -如果成员变量没有写属性,那在对象外部看来它将是只读的: +如果成员变量没有写特性,那在对象外部看来它将是只读的: ) --- @@ -340,12 +340,12 @@ $(P ) $(P -如果将来的某一天这个成员变量需要在外部修改,那我们仅需为其添加一个属性函数。 +如果将来的某一天这个成员变量需要在外部修改,那我们仅需为其添加一个特性函数。 ) Macros: - SUBTITLE=属性 + SUBTITLE=特性 DESCRIPTION=像访问成员变量一样调用成员函数 - KEYWORDS=D 编程语言教程 属性 + KEYWORDS=d programming lesson book tutorial property 编程 课程 书籍 教程 特性 diff --git a/ddili/src/ders/d.en/ranges.d b/target/ranges.d similarity index 97% rename from ddili/src/ders/d.en/ranges.d rename to target/ranges.d index e007576..284b652 100644 --- a/ddili/src/ders/d.en/ranges.d +++ b/target/ranges.d @@ -49,11 +49,11 @@ A very successful library that abstracts algorithms and data structures from eac ) $(P -Although they are a very useful abstraction, iterators do have some weaknesses. D's ranges were designed by Andrei Alexandrescu partly to overcome these weaknesses. D's standard library Phobos takes great advantage of ranges that are the subject of this chapter. +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 the seminal paper $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357, On Iteration) and demonstrates how they are superior to iterators. +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) @@ -155,7 +155,7 @@ Conversely, because ranges abstract algorithms away from data structures, implem $(H5 Phobos ranges) $(P -The ranges in this chapter are different from number ranges that are written in the form $(C begin..end). We had seen how number ranges are used with the $(C foreach) loop and with slices: +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: ) --- @@ -167,11 +167,11 @@ The ranges in this chapter are different from number ranges that are written in --- $(P -When I write $(I range), I mean a Phobos range in this chapter. +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 that they are based on. The following are all of the ranges with their requirements, sorted from the simplest to the more capable: +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 @@ -523,7 +523,7 @@ import std.array; --- $(P -The output: +输出: ) $(SHELL @@ -570,7 +570,7 @@ $(MONO_NOBOLD ) $(P -As you can see, that output does not match what we have seen 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. +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 @@ -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/ddili/src/ders/d.en/ranges_more.d b/target/ranges_more.d similarity index 99% rename from ddili/src/ders/d.en/ranges_more.d rename to target/ranges_more.d index 2c28e54..083b0a6 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.cn/scope.d b/target/scope.d similarity index 78% rename from ddili/src/ders/d.cn/scope.d rename to target/scope.d index 9f24a10..23fdc6d 100644 --- a/ddili/src/ders/d.cn/scope.d +++ b/target/scope.d @@ -12,7 +12,7 @@ $(P $(UL -$(LI 没有一个 $(C try) 块,$(C catch) 和 $(C finally) 不能使用。) +$(LI 没有一个 $(C try) 块, $(C catch) 和 $(C finally) 不能使用。) $(LI 属于块的某些变量,块范围内有可能访问不到: @@ -31,7 +31,7 @@ void foo(ref int r) { --- $(P -上面这个函数首先修改引用参数,当出现异常时再恢复修改。不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。$(I ($(B 注:)这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的一章) 中解释。)) +上面这个函数首先修改引用参数,当出现异常时再恢复修改。不幸的是,$(C addend) 只能在定义它的 $(C try) 块里访问。$(I ($(B 注:) 这与命名作用域,以及对象生存期有关,这将在 $(LINK2 /ders/d.cn/lifetimes.html, 后面的章节) 中解释。)) ) ) @@ -46,7 +46,7 @@ $(C scope) 语句与 $(C catch) 和 $(C finally) 有相似功能,但在许多 ) $(UL -$(LI $(C scope(exit)):表达式总是在退出作用域时被执行,无论是否成功或出现异常。) +$(LI $(C scope(exit)):表达式总是在退出作用域时被执行, 无论是否成功或出现异常。) $(LI $(C scope(success)):表达式只在成功退出作用域时被执行。) @@ -58,7 +58,7 @@ $(P ) $(P -例如,让我们用 $(C scope(failure)) 语句写一下下面的函数: +例如,让我们用 $(C scope(failure)) 语句写一下面的函数: ) --- @@ -73,7 +73,7 @@ void foo(ref int r) { --- $(P -上面的 $(C scope(failure)) 语句确保 $(C r -= addend) 表达式在因异常退出时被执行。$(C scope(failure)) 的好处是靠近它的表达式可以还原已写的另一个表达式。 +上面的 $(C scope(failure)) 确保 $(C r -= addend) 表达式在因异常退出时被执行。$(C scope(failure)) 的好处是靠近它的表达式可以还原已写的另一个表达式。 ) $(P @@ -108,7 +108,7 @@ void test() { --- $(P -如果没有抛出异常,函数的输出只包括 $(C scope(exit)) 和 $(C scope(success)) 表达式: +如果没有抛出异常, 函数的输出只包括 $(C scope(exit)) 和 $(C scope(success)) 表达式: ) $(SHELL @@ -119,7 +119,7 @@ when exiting 1 ) $(P -如果抛出异常,输出包括 $(C scope(exit)) 和 $(C scope(failure)) 表达式: +如果抛出异常, 输出包括 $(C scope(exit)) 和 $(C scope(failure)) 表达式: ) $(SHELL diff --git a/ddili/src/ders/d.en/slices.cozum.d b/target/slices.cozum.d similarity index 89% rename from ddili/src/ders/d.en/slices.cozum.d rename to target/slices.cozum.d index 9667aea..4b73230 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/slices.d b/target/slices.d similarity index 98% rename from ddili/src/ders/d.en/slices.d rename to target/slices.d index 598d64f..b83f15b 100644 --- a/ddili/src/ders/d.en/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 @@ -472,7 +472,7 @@ $(SHELL $(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 before hand, it is possible to reserve capacity for the elements: +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: ) --- @@ -508,10 +508,6 @@ $(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. ) -$(P -$(I $(B Note:) dmd 2.071, the compiler that was used to compile the examples in this chapter, did not fully support this feature yet. For that reason, some of the examples below use only fixed-length arrays.) -) - --- import std.stdio; @@ -526,7 +522,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL @@ -557,7 +553,7 @@ This feature works not only using two arrays; it can also be used with an array --- $(P -The output: +输出: ) $(SHELL @@ -574,7 +570,7 @@ To assign a specific value to all elements: --- $(P -The output: +输出: ) $(SHELL @@ -764,7 +760,7 @@ $(SHELL [[0, 0, 0], [0, 0, 0]] ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/ddili/src/ders/d.cn/special_functions.d b/target/special_functions.d similarity index 99% rename from ddili/src/ders/d.cn/special_functions.d rename to target/special_functions.d index 00accc3..b82cb08 100644 --- a/ddili/src/ders/d.cn/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/ddili/src/ders/d.en/stream_redirect.cozum.d b/target/stream_redirect.cozum.d similarity index 100% rename from ddili/src/ders/d.en/stream_redirect.cozum.d rename to target/stream_redirect.cozum.d diff --git a/ddili/src/ders/d.en/stream_redirect.d b/target/stream_redirect.d similarity index 100% rename from ddili/src/ders/d.en/stream_redirect.d rename to target/stream_redirect.d diff --git a/ddili/src/ders/d.cn/strings.cozum.d b/target/strings.cozum.d similarity index 80% rename from ddili/src/ders/d.cn/strings.cozum.d rename to target/strings.cozum.d index 525c51a..103c767 100644 --- a/ddili/src/ders/d.cn/strings.cozum.d +++ b/target/strings.cozum.d @@ -5,7 +5,7 @@ $(COZUM_BOLUMU 字符串) $(OL $(LI -尽管,Phobos 模块中的一些函数易于处理字符串,但库文档通常要比教程简短。因此此时你或许会非常迷惑,尤其是对于 Phobos 中的 range。在稍后的一章中我们将看到 Phobos 的 range。 +尽管 Phobos 模块中的一些函数易于处理字符串,但库文档通常比教程简短。此刻你可能会发现 Phobos 的 range 尤其让人迷惑。在稍后的一章中我们将看到 Phobos 的 range。 ) $(LI diff --git a/ddili/src/ders/d.cn/strings.d b/target/strings.d similarity index 81% rename from ddili/src/ders/d.cn/strings.d rename to target/strings.d index 7b4a0a7..7ef8951 100644 --- a/ddili/src/ders/d.cn/strings.d +++ b/target/strings.d @@ -38,7 +38,7 @@ $(P ) $(SHELL -What is your name? Mert +What is your name?Mert $(SHELL_NOTE 虽然按了 Enter 键,但输入没有中断) $(SHELL_NOTE (让我们假设在这儿第二次按了 Enter 键)) ) @@ -50,11 +50,11 @@ $(P $(SHELL Hello Mert $(SHELL_NOTE_WRONG 名字之后的换行符) -! $(SHELL_NOTE_WRONG (在感叹号前还有一个)) +!$(SHELL_NOTE_WRONG (在感叹号前还有一个)) ) $(P -感叹号出现在了那些字符之后,而不是在名字之后立即被输出。 +感叹号出现在了那些字符之后,而不是在名字之后立即输出。 ) $(P @@ -81,11 +81,11 @@ $(C readln()) 也存储换行符。这就让程序有办法确定输入是否包 $(SHELL What is your name?Mert Hello Mert -! $(SHELL_NOTE_WRONG 感叹号前有换行符) +!$(SHELL_NOTE_WRONG 感叹号前有换行符) ) $(P -这样的控制字符,就像所有位于字符串两端的空白字符一样,能被 $(C std.string.strip) 移除: +像字符串两端的空白字符这样的控制字符能被 $(C std.string.strip) 移除: ) --- @@ -104,16 +104,16 @@ void main() { --- $(P -上面的 $(C strip()) 表达式返回一个不包含尾部控制符的新字符串。返回值再赋值给 $(C name),得到预期的输出: +上面的 $(C strip()) 表达式返回一个不包含尾随控制符的新字符串。返回值再赋值给 $(C name),得到预期的输出: ) $(SHELL -What is your name? Mert -Hello Mert! $(SHELL_NOTE 没有换行符) +What is your name?Mert +Hello Mert!$(SHELL_NOTE 没有换行符) ) $(P -$(C readln()) 没有参数也可以使用。在这种情况下它$(I 返回)刚刚读入的行。将 $(C readln()) 的结果放到 $(C strip())中,能得到更短且可读性更好的语法: +$(C readln()) 没有参数也可以使用。在这种情况下它$(I 返回)刚刚读入的行。把 $(C readln()) 的结果放到 $(C strip()) 中,能得到更短且可读性更好的语法: ) --- @@ -127,7 +127,7 @@ $(P $(H5 $(IX formattedRead) 使用 $(C formattedRead) 函数来解析字符串) $(P -一旦从输入流或其他任何来源中读取了一行字符,就可以用 $(C std.format) 模块的 $(C formattedRead()) 函数来解析并转换它所包含的独立数据。它的第一个参数是包含数据的输入行,而其余的参数就与用于 $(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()) 的一模一样: ) --- @@ -143,7 +143,7 @@ void main() { string name; int age; - $(HILITE formattedRead)(line, " %s %s", &name, &age); + $(HILITE formattedRead)(line, " %s %s", name, age); writeln("Your name is ", name, ", and your age is ", age, '.'); @@ -156,11 +156,11 @@ Your name is $(HILITE Mert), and your age is $(HILITE 30). ) $(P -$(C readf()) 和 $(C formattedRead()) 函数都$(I 返回)它们所能够成功解析及转换的项目个数。该值可与所期望的数据个数相比较,以便确定输入的有效性。例如,像上面的 $(C formattedRead()) 函数期望去读$(I 两个)数据(一个 $(C string) 型 name 和一个 $(C int) 型 age),下面的检查能够确保它确实如此: +$(C readf()) 和 $(C formattedRead()) 函数 都可以$(I 返回)成功解析及转换的项目个数。该值可与数据项的预期数相比较,以便确定输入的有效性。例如,像上面的 $(C formattedRead()) 函数期望去读$(I 两个)项目(一个 $(C string) 型 name 和一个 $(C int) 型 age),下面的检查确定它真是这样: ) --- - $(HILITE uint items) = formattedRead(line, " %s %s", &name, &age); + $(HILITE uint items) = formattedRead(line, " %s %s", name, age); if ($(HILITE items != 2)) { writeln("Error: Unexpected line."); @@ -186,10 +186,10 @@ $(P 我们已经看到单引号用于定义字符字面量。字符串字面量用双引号定义。$(STRING 'a') 是一个字符;$(STRING "a") 是一个包含单字符的字符串。 ) -$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string)、$(C wstring) 和 $(C dstring) 是 immutable(不可变)的) +$(H5 $(IX string) $(IX wstring) $(IX dstring) $(IX char[]) $(IX wchar[]) $(IX dchar[]) $(IX immutable) $(C string)、$(C wstring) 和 $(C dstring) 是 immutable (不可变的)) $(P -对应着三种字符类型,分别存在三种字符串类型:$(C char[])、$(C wchar[]) 和 $(C dchar[])。 +对应着三种字符类型,分别存在着三种字符串类型:$(C char[])、$(C wchar[]) 和 $(C dchar[])。 ) $(P @@ -218,9 +218,9 @@ $(P ) $(OL -$(LI 像 $(STRING "hello") 这样的字符串字面量的类型是 $(C string),而不是 $(C char[]),因此它们是不可变的。 +$(LI 像 $(STRING "hello") 这样的字符串字面量的类型是 $(C string),而不是 $(C char[]),因此它们是 immutable。 ) -$(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编译成功,它将会提供对右手侧全部字符的访问。 +$(LI 左手侧的 $(C char[]) 是一个切片,这意味着,一旦代码编译成功,它将会访问右手侧的全部字符。 ) ) @@ -251,7 +251,7 @@ Hello ) $(P -同样的,$(C char[]) 不能被用到需要 $(C string) 的地方。这种情况下,$(C .idup) property 能被用来从一个可变的 $(C char[]) 变量中获取一个不可变的 $(C string) 变量。例如,如果 $(C s) 的变量类型是 $(C char[]),下面这行将编译失败: +同样的,$(C char[]) 不能被用到需要 $(C string) 的地方。这种情况下,$(C .idup) property 能被用来从一个可变的 $(C char[]) 变量中生成一个不可变的 $(C string) 变量。例如,如果 $(C s) 的变量类型是 $(C char[]),下面这行将编译失败: ) --- @@ -259,7 +259,7 @@ $(P --- $(P -当 $(C s) 的类型是 $(C char[]),上面右手侧赋值的表达式类型即也是 $(C char[]),$(C .idup) 可用来从存在的字符串中生成不可变的字符串: +当 $(C s) 的类型是 $(C char[]),上面右手侧赋值的表达式类型也是 $(C char[]),$(C .idup) 可用来从存在的字符串中生成不可变的字符串: ) --- @@ -336,7 +336,7 @@ $(LI 有一个 $(C d) 在字面量 $(STRING "résumé"d) 的末尾,指定了 ) $(P -无论如何,请记住使用 $(C dchar[]) 和 $(C dstring) 并不能解决所有的操作 Unicode 字符的问题。例如,如果用户输入文本“résumé”,即使使用 $(C dchar) 字符串,你和你的程序仍然不能确保字符串的长度会是 6。如果至少其中一个‘é’字符编码为单个编码单元,而是一个‘e’与一个重音符组合单元的组合,那么它就可能会更长。为避免处理这种以及许多其它的 Unicode 问题,在你的程序里就要考虑使用一个支持 Unicode 的文本处理库。 +无论如何,请记住使用 $(C dchar[]) 和 $(C dstring) 并不能解决所有的操作 Unicode 字符的问题。例如,如果用户输入文本“résumé”,即使使用 $(C dchar) 字符串,你和你的程序仍然不能确保字符串的长度会是6。如果至少其中一个‘é’字符没有做为单个编码点编码,而是一个‘e’与一个组合重音符的组合,那么它就可能会更长。为避免处理这种以及许多其它的 Unicode 问题,在你的程序里就要考虑使用一个支持 Unicode 的文本处理库。 ) $(H5 $(IX literal, string) 字符串字面量) @@ -376,7 +376,7 @@ $(P $(H5 $(IX concatenation, string) 字符串连接) $(P -由于它们实际上是数组,所有数组的操作也都能应用到字符串上。$(C ~) 可以连接两个字符串,$(C ~=) 则能够附加字符串到一个已存在的字符串上: +由于它们实际上是数组,所有数组的操作也都能应用到字符串上。$(C ~) 可以连接两个字符串,$(C ~=) 则能够把字符串附加到一个已存在的字符串上: ) --- @@ -413,7 +413,7 @@ $(I $(B 注:)除了 Unicode 编码顺序之外,Unicode 不定义字符的排 ) $(P -我们以前把比较运算符 $(C <)、$(C >=) 等等用于整型和浮点数值。同样的操作也能用于字符串,但含义不同:字符串按$(I 字典顺序)排序。这种排序需要在一个假设的大字母表中让每个字符的 Unicode 编码找到它的位置。在这个假设的字母表中,$(I 更少)和$(I 更多)的概念就被$(I 之前)和$(I 之后)代替: +我们以前把比较运算符 $(C <),$(C >=) 等等用于整型和浮点数值。同样的操作也能用于字符串,但含义不同:字符串按$(I 字典顺序)排序。这种排序需要在一个假设的大字母表中让每个字符的 Unicode 编码找到它的位置。在这个假设的字母表中,$(I 更少)和$(I 更多)的概念就被$(I 之前)和$(I 之后)代替: ) --- @@ -463,7 +463,7 @@ $(P ) $(P -因为字符串是数组(进一步而言,是 $(I range)),所以 $(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块中的函数对与于字符串也都非常有用。 +因为字符串是数组(进一步而言,是 $(I range)),所以 $(C std.array)、$(C std.algorithm) 和 $(C std.range) 模块中的函数对于字符串也都非常有用。 ) $(PROBLEM_COK @@ -473,7 +473,7 @@ $(PROBLEM ) $(PROBLEM -写一个使用 $(C ~) 运算符的程序:让用户都以小写键入英文名字和姓氏。生成一个姓名首字母大写的全名。例如,字符串是“ebru”和“domates”,程序应该打印出“Ebru Domates”。 +写一个使用 $(C ~) 运算符的程序:让用户都以小写键入英文名字和姓氏,生成一个姓名首字母大写的全名。例如,字符串是“ebru”和“domates”,程序应该打印出“Ebru Domates”。 ) $(PROBLEM @@ -484,7 +484,7 @@ $(P ) $(P -与它们的文档中所指出一样,$(C indexOf()) 和 $(C lastIndexOf()) 的返回类型不是 $(C int),也不是 $(C size_t),而是 $(C ptrdiff_t)。您可能就需要定义该类型的变量: +与它们的文档中所指出的一样,$(C indexOf()) 和 $(C lastIndexOf()) 的返回类型既不是 $(C int) 也不是 $(C size_t),而是 $(C ptrdiff_t)。您可能就需要定义该类型的变量: ) --- @@ -506,6 +506,6 @@ $(P Macros: SUBTITLE=字符串 - DESCRIPTION=D语言的字符串 + DESCRIPTION=D 语言的字符串 KEYWORDS=D 语言教程 string diff --git a/ddili/src/ders/d.cn/struct.cozum.d b/target/struct.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/struct.cozum.d rename to target/struct.cozum.d diff --git a/ddili/src/ders/d.cn/struct.d b/target/struct.d similarity index 98% rename from ddili/src/ders/d.cn/struct.d rename to target/struct.d index dc428b3..1ddfb15 100644 --- a/ddili/src/ders/d.cn/struct.d +++ b/target/struct.d @@ -7,7 +7,7 @@ As has been mentioned several times earlier in the book, fundamental types are n ) $(P -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. 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. +$(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 @@ -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/ddili/src/ders/d.en/switch_case.cozum.d b/target/switch_case.cozum.d similarity index 100% rename from ddili/src/ders/d.en/switch_case.cozum.d rename to target/switch_case.cozum.d diff --git a/ddili/src/ders/d.en/switch_case.d b/target/switch_case.d similarity index 99% rename from ddili/src/ders/d.en/switch_case.d rename to target/switch_case.d index b2e9e6d..201d5d8 100644 --- a/ddili/src/ders/d.en/switch_case.d +++ b/target/switch_case.d @@ -166,7 +166,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL diff --git a/ddili/src/ders/d.en/templates.d b/target/templates.d similarity index 99% rename from ddili/src/ders/d.en/templates.d rename to target/templates.d index ee58655..3e3408d 100644 --- a/ddili/src/ders/d.en/templates.d +++ b/target/templates.d @@ -477,7 +477,7 @@ Unfortunately, $(C Point) is inadequate at higher precisions than $(C int) can p ) $(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: +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: ) --- @@ -638,7 +638,7 @@ $(P In that usage, the second and third parameters are $(C int) and $(C char), respectively. ) -$(H5 Every different template instantiation is a distinct type) +$(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: @@ -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: @@ -1057,7 +1057,7 @@ $(LI Template arguments can be specified explicitly after an exclamation mark. T ) -$(LI Every different instantiation of a template is a different type. +$(LI Every template instantiation yields a distinct type. --- assert(typeid(ClassTemplate!$(HILITE int)) != diff --git a/ddili/src/ders/d.cn/templates_more.d b/target/templates_more.d similarity index 98% rename from ddili/src/ders/d.cn/templates_more.d rename to target/templates_more.d index 8671a2d..aab262f 100644 --- a/ddili/src/ders/d.cn/templates_more.d +++ b/target/templates_more.d @@ -270,7 +270,7 @@ Union templates are similar to struct templates. The shortcut syntax is availabl ) $(P -As an example, let's design a more general version of the $(C IpAdress) $(C union) that we have seen 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): +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): ) --- @@ -281,7 +281,7 @@ union IpAddress { --- $(P -The $(C bytes) array was an easy access to the four segments of the IPv4 address. +The $(C bytes) array provided easy access to the four segments of the IPv4 address. ) $(P @@ -432,7 +432,7 @@ class Bulb : ColoredObject$(HILITE !Frequency) { --- $(P -However, as explained in $(LINK2 /ders/d.en/templates.html, the Templates chapter), "every different instantiation of a template is a different type". Accordingly, the interfaces $(C ColoredObject!RGB) and $(C ColoredObject!Frequency) are unrelated interfaces, and $(C PageFrame) and $(C Bulb) are unrelated classes. +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) @@ -599,7 +599,7 @@ void main() { --- $(P -The output: +输出: ) $(SHELL_SMALL @@ -652,7 +652,7 @@ void func(T, } void main() { - func(42); $(CODE_NOTE $(HILITE line 14)) + func(42); $(CODE_NOTE $(HILITE line 12)) } --- @@ -661,7 +661,7 @@ Although the special keywords appear in the definition of the template, their va ) $(SHELL -Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 14). +Instantiated at function deneme.$(HILITE main) at file deneme.d, $(HILITE line 12). ) $(P @@ -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 @@ -1493,7 +1493,7 @@ A template definition is considered by the compiler only if its constraints eval ) $(P -Since templates are a compile-time feature, template constraints must be evaluable at compile time. The $(C is) expression that we have seen 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. +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) @@ -1867,7 +1867,7 @@ public: } void toString(void delegate(const(char)[]) sink) const { - formattedWrite(sink, "%(%(%5s %)\n%)", rows); + sink.formattedWrite!"%(%(%5s %)\n%)"(rows); } /* Assigns the specified value to each element of the @@ -2039,7 +2039,7 @@ void main() { } --- -$(H5 Summary) +$(H5 摘要) $(P The earlier template chapter had the following reminders: @@ -2055,7 +2055,7 @@ $(LI Specifying template parameter lists is sufficient to make function, struct, $(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 different instantiation of a template is a different type.) +$(LI Each template instantiation yields a different type.) $(LI Template arguments can only be deduced for function 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/ddili/src/ders/template/to_be_continued.d b/target/to_be_continued.d similarity index 75% rename from ddili/src/ders/template/to_be_continued.d rename to target/to_be_continued.d index d327691..1a7f124 100644 --- a/ddili/src/ders/template/to_be_continued.d +++ b/target/to_be_continued.d @@ -4,7 +4,7 @@ $(B) $(H4 The End) $(P -You can use $(LINK2 /ders/d.de/rss.xml, the RSS feed) to be notified about new chapters. +You can use $(LINK2 /ders/d.en/rss.xml, the RSS feed) to be notified about new chapters. ) Macros: diff --git a/ddili/src/ders/d.en/tuples.d b/target/tuples.d similarity index 99% rename from ddili/src/ders/d.en/tuples.d rename to target/tuples.d index 63fb3a8..164c8fe 100644 --- a/ddili/src/ders/d.en/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/ddili/src/ders/d.en/types.cozum.d b/target/types.cozum.d similarity index 100% rename from ddili/src/ders/d.en/types.cozum.d rename to target/types.cozum.d diff --git a/ddili/src/ders/d.en/types.d b/target/types.d similarity index 100% rename from ddili/src/ders/d.en/types.d rename to target/types.d diff --git a/target/uda.d b/target/uda.d new file mode 100644 index 0000000..97fcfc3 --- /dev/null +++ b/target/uda.d @@ -0,0 +1,421 @@ +Ddoc + +$(DERS_BOLUMU $(IX user defined attributes) $(IX UDA) 自定义属性(UDA)) + +$(P +所有声明(如结构、类和变量等)都可以加上属性,以便在编译时访问这些属性来调整该部分代码的编译方式。自定义属性完全是一项编译时功能。 +) + +$(P +$(IX @)自定义属性的语法格式:符号 $(C @) 的后面紧跟属性名,并且需要放置在与之相关的那个声明前面。例如,下面代码会将 $(C Encrypted) 属性赋予 $(C name) 声明: +) + +--- + $(HILITE @Encrypted) string name; +--- + +$(P +多个属性可以分别指定,也可以采用括号列表形式。例如,下面的变量拥有的属性是相同的: +) + +--- + @Encrypted @Colored string lastName; $(CODE_NOTE 单独分开) + @$(HILITE $(PARANTEZ_AC))Encrypted, Colored$(HILITE $(PARANTEZ_KAPA)) string address; $(CODE_NOTE 组合一起) +--- + +$(P +属性除了可以是自定义类型值或基础类型值以外,还可是以类型名。不过,因为它们的含义可能不是很清楚,因此不赞成大家使用文字量值(如 $(C 42) )构成的属性: +) + +--- +$(CODE_NAME Encrypted)struct Encrypted { +} + +enum Color { black, blue, red } + +struct Colored { + Color color; +} + +void main() { + @Encrypted int a; $(CODE_NOTE 类型名) + @Encrypted() int b; $(CODE_NOTE 对象) + @Colored(Color.blue) int c; $(CODE_NOTE 对象) + @(42) int d; $(CODE_NOTE 文字量(不赞成)) +} +--- + +$(P +上面 $(C a) 和 $(C b) 的属性属于不同类别:$(C a) 的属性是 $(C Encrypted) 类型,而 $(C b) 的属性是 $(C Encrypted) 类型的一个$(I 对象)。它们有着明显的差异,它会对编译时属性的使用方式产生影响。下面来看一个与该差异有关的例子。 +) + +$(P +$(IX __traits) $(IX getAttributes) 属性的实际含义完全由程序员根据程序的需要来决定。编译时可以通过 $(C __traits(getAttributes)) 来判定属性,而相应代码则会根据那些属性来编译。 +) + +$(P +下面代码展示的是如何使用使用$(C __traits(getAttributes))来访问某个特定 $(C struct) 成员(如 $(C Person.name))的属性: +) + +--- +$(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 +这个程序的输出内容是 $(C Person.name) 的各个属性: +) + +$(SHELL +Encrypted +Colored(cast(Color)1) +) + +$(P +在处理自定义属性时,还有其他两个 $(C __traits) 表达式可以使用: +) + +$(UL + +$(LI $(IX allMembers) $(C __traits(allMembers)) 会以字符串列表的形式生成某个类型(或模块)的所有成员。) + +$(LI $(IX getMember) $(C __traits(getMember)) 会生成一个 $(I 符号),可以在访问某个成员时使用它。它的第一个参数是符号(如类型或者变量名),第二个参数是字符串。它会生成一个符号,其组成部分包含了它的第一个参数、一个小数点和它的第二个参数。例如,$(C __traits(getMember, Person, $(STRING "name"))) 生成的符号是 $(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 +此程序的输出会列出 $(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) 另一个有用的工具是 $(C std.traits.hasUDA),它可检测某个符号是否拥有某个特定的属性。下面的 $(C static assert) 会顺利通过,因为 $(C Person.name) 拥有 $(C Encrypted) 属性: +) + +--- +import std.traits; + +// ... + +static assert(hasUDA!(Person.name, Encrypted)); +--- + +$(P +$(C hasUDA) 除了可以与属性类型的特定值一起使用外,还可以与属性类型一起使用。下面的两个 $(C static assert) 都会顺利通过,因为 $(C Person.name) 拥有 $(C Colored(Color.blue)) 属性: +) + +--- +static assert(hasUDA!(Person.name, $(HILITE Colored))); +static assert(hasUDA!(Person.name, $(HILITE Colored(Color.blue)))); +--- + +$(H5 样例) + +$(P +一起来设计一个函数模板,让它以XML格式输出一个 $(C struct) 对象的所有成员的值。下面这个函数在生成输出内容时会使用到每个成员的 $(C Encrypted) 和 $(C Colored) 属性: +) + +--- +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 +下面来解释一下这段代码的高亮部分: +) + +$(OL + +$(LI 该类型的所有成员可以通过 $(C __traits(allMembers)) 获得。) + +$(LI 每个成员值都被转换为 $(C string),以便后面输出时使用。例如,当成员为 $(STRING "name") 时,对应的表达式会变成 $(C object.name.to!string)。) + +$(LI 每个成员都使用 $(C hasUDA) 来测试,以便确定它是否拥有 $(C Encrypted) 特性。如果该成员拥有此属性,则它的值会被加密。(因为 $(C hasUDA) 需要 $(I 符号) 才能工作,请参考一下如何使用 $(C __traits(getMember)) 以符号方式(如 $(C Person.name))获取成员的。)) + +$(LI 每个成员的 color 属性可以使用 $(C colorAttributeOf()) 来检测。下面便来看看这个方法。) + +) + +$(P +函数模板 $(C colorAttributeOf()) 可以实现成下面的样子: +) + +--- +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 +当编译时计算完成时,函数模板 $(C printAsXML()) 会根据 $(C Person) 类型实例化,并与下面这个函数相似: +) + +--- +/* printAsXML!Person 实例的等同函数。*/ +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 +列出整个程序代码更能说明问题: +) + +--- +import std.stdio; +import std.string; +import std.algorithm; +import std.conv; +import std.traits; + +/* 特别要求加密应用此自定义属性 + * 的那个符号。*/ +struct Encrypted { +} + +enum Color { black, blue, red } + +/* 特别指定应用此自定义属性的那个符号的颜色。 + * 默认颜色为 Color.black。*/ +struct Colored { + Color color; +} + +struct Person { + /* 此成员被特定要求加密,并且输出为 + * 蓝色。*/ + @Encrypted @Colored(Color.blue) string name; + + /* 这个成员没有自定义 + * 属性。*/ + string lastName; + + /* 此成员被特定要求输出为红色。*/ + @Colored(Color.red) string address; +} + +/* 如果被特定要求的成员拥有 Colored 属性, + * 则输出它的值,否则输出 Color.black。*/ +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; +} + +/* 返回指定字符串的 Caesar 加密 + * 版本。(警告:Caesar cipher 是一种强度很弱的 + * 加密方法。) */ +auto encrypted(string value) { + return value.map!(a => dchar(a + 1)); +} + +unittest { + assert("abcdefghij".encrypted.equal("bcdefghijk")); +} + +/* 根据指定对象的各个成员的属性 + * 以 XML 格式输出它。*/ +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 +上面程序的输出内容包含那些拥有正确颜色的成员和被加密过的 $(C name) 成员: +) + +$(SHELL +<Person> + <name color="blue">Bmjdf</name> $(SHELL_NOTE 蓝色且加密) + <lastName color="black">Davignon</lastName> + <address color="red">Avignon</address> $(SHELL_NOTE 红色) +</Person> +<Person> + <name color="blue">Cfo</name> $(SHELL_NOTE 蓝色且加密) + <lastName color="black">de Bordeaux</lastName> + <address color="red">Bordeaux</address> $(SHELL_NOTE 红色) +</Person> +) + +$(H5 自定义属性的好处) + +$(P +自定义属性的好处在于能够更改声明的属性,且不需要更改程序的其他部分。例如,$(C Person) 的所有成员在 XML 格式输出里都会被加密,类似下面内容: +) + +--- +struct Person { + $(HILITE @Encrypted) { + string name; + string lastName; + string address; + } +} + +// ... + + printAsXML(Person("Cindy", "de Cannes", "Cannes")); +--- + +$(P +输出: +) + +$(SHELL +<Person> + <name color="black">Djoez</name> $(SHELL_NOTE 已加密) + <lastName color="black">ef!Dbooft</lastName> $(SHELL_NOTE 已加密) + <address color="black">Dbooft</address> $(SHELL_NOTE 已加密) +</Person> +) + +$(P +此外,$(C printAsXML()) 和它涉及到的属性还可以与其他类型一起使用: +) + +--- +struct Data { + $(HILITE @Colored(Color.blue)) string message; +} + +// ... + + printAsXML(Data("hello world")); +--- + +$(P +输出: +) + +$(SHELL +<Data> + <message color="blue">hello world</message> $(SHELL_NOTE 蓝色) +</Data> +) + +$(H5 小结) + +$(UL + +$(LI 自定义属性可用于任何声明。) + +$(LI 自定义属性可以是类型名,也可以是具体值。) + +$(LI 自定义属性在编译时可以通过 $(C hasUDA) 和 $(C __traits(getAttributes)) 来访问,以便达到更改程序编译方式的目的。) + +) + +macros: + SUBTITLE=自定义属性(UDA) + + DESCRIPTION=为声明加上自定义属性、在编译时检测属性,并根据那些属性编译代码。 + + KEYWORDS=d programming language tutorial book user defined attributes UDA D 编程语言 教程 书籍 自定义属性 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/ddili/src/ders/d.en/union.d b/target/union.d similarity index 98% rename from ddili/src/ders/d.en/union.d rename to target/union.d index baff597..80d5279 100644 --- a/ddili/src/ders/d.en/union.d +++ b/target/union.d @@ -3,7 +3,7 @@ Ddoc $(DERS_BOLUMU $(IX union) Unions) $(P -Unions allow more than one member share the same memory area. They are a low-level feature inherited from the C programming language. +Unions, a low-level feature inherited from the C programming language, allow more than one member to share the same memory area. ) $(P @@ -221,7 +221,7 @@ import core.bitop; --- $(P -The output: +输出: ) $(SHELL_SMALL diff --git a/ddili/src/ders/d.en/unit_testing.cozum.d b/target/unit_testing.cozum.d similarity index 100% rename from ddili/src/ders/d.en/unit_testing.cozum.d rename to target/unit_testing.cozum.d diff --git a/ddili/src/ders/d.cn/unit_testing.d b/target/unit_testing.d similarity index 100% rename from ddili/src/ders/d.cn/unit_testing.d rename to target/unit_testing.d diff --git a/ddili/src/ders/d.en/value_vs_reference.d b/target/value_vs_reference.d similarity index 98% rename from ddili/src/ders/d.en/value_vs_reference.d rename to target/value_vs_reference.d index c231911..8c457a4 100644 --- a/ddili/src/ders/d.en/value_vs_reference.d +++ b/target/value_vs_reference.d @@ -121,6 +121,9 @@ $(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: ) @@ -567,7 +570,7 @@ class MyClass { void printHeader() { immutable dchar[] header = - " Type of variable" + " Type of variable" ~ " a == b &a == &b"; writeln(); @@ -674,7 +677,7 @@ $(IX replicate, std.array) The $(C replicate()) function of the $(C std.array) m ) -$(H5 Summary) +$(H5 摘要) $(UL diff --git a/ddili/src/ders/d.en/variables.cozum.d b/target/variables.cozum.d similarity index 100% rename from ddili/src/ders/d.en/variables.cozum.d rename to target/variables.cozum.d diff --git a/ddili/src/ders/d.en/variables.d b/target/variables.d similarity index 100% rename from ddili/src/ders/d.en/variables.d rename to target/variables.d diff --git a/ddili/src/ders/d.en/while.cozum.d b/target/while.cozum.d similarity index 100% rename from ddili/src/ders/d.en/while.cozum.d rename to target/while.cozum.d diff --git a/ddili/src/ders/d.cn/while.d b/target/while.d similarity index 100% rename from ddili/src/ders/d.cn/while.d rename to target/while.d diff --git a/ddili/src/ders/d.cn/writeln.cozum.d b/target/writeln.cozum.d similarity index 100% rename from ddili/src/ders/d.cn/writeln.cozum.d rename to target/writeln.cozum.d diff --git a/ddili/src/ders/d.cn/writeln.d b/target/writeln.d similarity index 100% rename from ddili/src/ders/d.cn/writeln.d rename to target/writeln.d