Notes

--------Joel’s Stuff---------

Walking backwards in Arrays

 

Walking arrays backwards are useful for all-sorts of things such as removing items from an array.

 

Three main ways to do this, take your pick.

 

1.

            for ( int n = A.length; n>0; )

                        A[--n] …;

 

2.

            for ( int n = A.length-1; n>=0; n-- )

                        A[n] …;

 

3.

            for ( int n = A.length; --n>0; )

                        A[n] …;

 

Which is preferable? Well that’s really up to your style of coding and what you are doing.

 

1 has extra possibilities. If you add brackets you can write code before and after n-1.

            for ( int n = A.length; n>0; )

            {

                        //Code before n-1

                        A[--n];

                        //Code after n-1

            }

 

If you’re a classic stick-to-the-format type of person then 2 is probably your cup-of-tea. It keeps the loop in its general format and there’s no dirty pre-logic stuff.

 

If you’re a keyboard-speed-freak then 3 is probably your motor car. It has slightly less code then the other two.

 

 

Joel Anderson

 

D arrays can be sorted

 

Forget loops, D has sorting as one of an array’s attributes.           

 

This will sort the array A in-place.

 

            int[] A;

            ...

            A.sort;

 

 

Joel Anderson

 

D arrays can be resized on the fly

 

Forget realloc, D can resize array’s dynamically.

 

This will resize the array A by one.

 

            int[] A;

            ...

       A.length++;

 

Joel Anderson

 

Comparing arrays

D allows you to quickly compare arrays with “==”.

 

            int[] A;

            int[] B;

            ...

            if (A == B) //Then both are equal in length and items.

 

But don’t worry referential testing is still supported with “===”

 

            if (A === B) //Then both references are equal.

 

Joel Anderson

 

 

HTML Imbedded code

D can automatically extract code from html pages and compile it like normal code.  What are the advantages of this? Apart from beginning able to keep code with documentation future D IDE’s may be able to use this feature for automatic syntax highlighting and object link embedding.

 

Joel Anderson

 

Notes

If you have any notes you would like to share, email Mathew Wilson dmd@synesis.com.au or Joel Anderson anderson@firestar.com.au. They must be between one-sentence => two-medium-paragraphs and/or small snippets of code. Otherwise they’ll be considered for the hints and tips section for the Journal.

 

Joel Anderson

 

High Cohesion and Low Coupling

 

Programs should be lowly coupled and highly cohesive. Why? What does this mean?

 

Coupling

Programs are highly coupled when they are heavily reliant on one another. This is considered NOT A GOOD THING. For example a car that only uses one type of tyre and that tyre only being useable on that car. In this case the tyre is not reusable. You can’t very well take the tyre and use it on another car. High coupling make programs less reusable.

 

Cohesion

Cohesion is the way things work together. Take the car example again. Suppose the tyre was so generalised that it even works on your-next-door-neighbours billycart. The car may be reduced a maximum of 10km/h. Low cohesion makes components less flexible/efficient together.

 

It’s often difficult to get the balance just right between these two concepts but just it in the back of your mind and your programs will be all the better. Remember, high cohesion and low coupling = good thing but low cohesion and high coupling = bad thing.

 

Joel Anderson

 

 

Speed Optimisation with For Loops

 

When using for loops (or any loop for that matter) make sure that you’re not reloading the same value every time around the loop.

 

Don’t

for (int n=0; n < getSizeof(X); n++)

{

}

//Where getSizeof returns a constant value

 

Instead,

int Size = getSizeof(X);

for (int n=0; n < Size; n++)

{

}

 

That way your not calling the fucntion getSizeof(), getSizeof(X) times.

 

Joel Anderson

 

Speed Optimisation for linear searches

 

The basic search looks like.

 

int n=0;

while (n<A.length)

{

            if (A[n]==Search)

            {

                        //Value found at n

                        break;

            }

            n++;

}

 

//Where A is the value we are searching for

 

This can be improved to…

 

int n=0;

while (n<A.length && A[n++]!=Search) {};

 

if (n != Max) {…} //Then the value is found at n-1

 

A comparison can be saved by making the last character in the array the value being searched for.

           

A.length = A.length + 1;

A[A.length] = Search;

 

int n=0;

while (A[n++]!=Search) {};

 

if (n != Max) {…} //Then the value is found at n-1

 

A.length = A.length - 1;

 

Joel Anderson

 

 

Building a Toolbox for the future

When programming, try to program in such a way that components in the program are reusable in other programs.  Try to keep things separate and independent of other parts of the program.  These independent parts may become tools of the future, so design them well.  The advantages for programming like this normally out weigh the extra time spent.  Hands will dance; they won’t have to experience about daysharvoo so much (reduced repetition). The complier will sing, because these tools should have been already thoroughly debugged in the previous programs and hence no error report.  Whenever the possibility arrises, program for the future.

 

Joel Anderson

 

D newsgroup

Did you know that there is a D newsgroup on news.digitalmars.com server? Here you can ask/suggest anything about D and even talk to the creator Walter.

 

Joel Anderson

 

 

 

-----------Pavel’s Stuff---------------

 

D Stacks

 

D array can be used to easily construct a stack:

 

     int[] stack;

     ...

     stack ~= 666;    // push a value;

     ...

     a = stack[stack.length-1];    // get last element

     ...

     stack.length = stack.length - 1;    // pop last element

Pavel Minayev

 

 

 

 

 

----------Walters Stuff------------

 

 

 

Header Files

C and C++ rely heavilly on textual inclusion of header files. This frequently results in the compiler having to recompile tens of thousands of lines of code over and over again for every source file, an obvious source of slow compile times. What header files are normally used for is more appropriately done doing a symbolic, rather than textual, insertion. This is done with the import statement. Symbolic inclusion means the compiler just loads an already compiled symbol table. The needs for macro "wrappers" to prevent multiple #inclusion, funky #pragma once syntax, and incomprehensible fragile syntax for precompiled headers are simply unnecessary and irrelevant to D.

 

The syntax:

 

            #include <stdio.h>

           

is expressed in D as:

            import stdio;

 

Walter

 

Getting the Size of a Type

 

The C Way

            sizeof(int)

            sizeof(char *)

            sizeof(double)

            sizeof(struct Foo)

 

The D Way

Use the size property:

 

        int.size

        (char *).size

        double.size

        Foo.size

 

Walter

 

Get the max and min values of a type

 

The C Way

            #include <limits.h>

            #include <math.h>

 

            CHAR_MAX

            CHAR_MIN

            ULONG_MAX

            DBL_MIN

 

The D Way

        char.max

        char.min

        ulong.max

        double.min

 

Walter

 

Primitive Types

 

C to D types

        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            =>      extended

            _Imaginary long double =>    imaginary

            _Complex long double   =>    complex

 

 

Although char is an unsigned 8 bit type, and wchar is an unsigned 16 bit type, they have their own separate types in order to aid overloading and type safety.

 

Ints and unsigneds in C are of varying size; not so in D.

 

Walter

 

Taking the Modulus of a floating point number

 

The C Way

       #include <math.h>

 

       float f = fmodf(x,y);

       double d = fmod(x,y);

       long double e = fmodl(x,y);

 

The D Way

D supports the modulus ('%') operator on floating point operands:

 

       float f = x % y;

       double d = x % y;

       extended e = x % y;

 

Walter

 

Dealing with NAN's in floating point compares

 

The C Way

C doesn't define what happens if an operand to a compare is NAN, and few C compilers check for it (the Digital Mars C compiler is an exception, DM's compilers do check for NAN operands).

 

       #include <math.h>

 

       if (isnan(x) || isnan(y))

               result = FALSE;

       else

               result = (x < y);

 

The D Way

D offers a full complement of comparisons and operators that work with NAN arguments.

 

       result = (x < y);        // false if x or y is nan

 

 

Walter

 

Assert's are a necessary part of any good defensive coding strategy

 

The C Way

C doesn't directly support assert, but does support __FILE__ and __LINE__ from which an assert macro can be built. In fact, there appears to be practically no other use for __FILE__ and __LINE__.

 

       #include <assert.h>

 

       assert(e == 0);

 

The D Way

D simply builds assert into the language:

 

       assert(e == 0);

 

Walter

 

Initializing all elements of an array

 

The C Way

       #define ARRAY_LENGTH        17

       int array[ARRAY_LENGTH];

       for (i = 0; i < ARRAY_LENGTH; i++)

               array[i] = value;

 

The D Way

       int array[17];

       array[] = value;

 

Walter

 

Looping through an array

 

The C Way

The array length is defined separately, or a clumsy sizeof() expression is used to get the length.

 

       #define ARRAY_LENGTH        17

       int array[ARRAY_LENGTH];

       for (i = 0; i < ARRAY_LENGTH; i++)

               func(array[i]);

 

or:

       int array[17];

       for (i = 0; i < sizeof(array) / sizeof(array[0]); i++)

               func(array[i]);

 

The D Way

The length of an array is accessible the property "length".

 

       int array[17];

       for (i = 0; i < array.length; i++)

               func(array[i]);

 

Walter

 

Creating an array of variable size

 

The C Way

C cannot do this with arrays. It is necessary to create a separate variable for the length, and then explicitly manage the size of the array:

               #include <stdlib.h>

 

               int array_length;

               int *array;

               int *newarray;

 

               newarray = (int *) realloc(array, (array_length + 1) * sizeof(int));

               if (!newarray)

                   error("out of memory");

               array = newarray;

               array[array_length++] = x;

 

The D Way

D supports dynamic arrays, which can be easilly resized. D supports all the requisite memory management.

               int array[];

 

               array[array.length++] = x;

 

Walter

 

 

Formatted printing

 

The C Way

printf() is the general purpose formatted print routine:

               #include <stdio.h>

 

               printf("Calling all cars %d times!\n", ntimes);

 

The D Way

What can we say? printf() rules:

               import stdio;

 

               printf("Calling all cars %d times!\n", ntimes);

 

Walter

 

Functions that have no arguments

 

The C Way

               void function(void);

 

The D Way

D is a strongly typed language, so there is no need to explicitly say a function takes no arguments, just don't declare it has having arguments.

               void function()

               {

                   ...

               }

 

Walter

 

 

Goto Statements

 

The C Way

The much maligned goto statement is a staple for professional C coders. It's necessary to make up for sometimes inadequate control flow statements.

The D Way

Many C-way goto statements can be eliminated with the D feature of labelled break and continue statements. But D is a practical language for practical programmers who know when the rules need to be broken. So of course D supports the goto!

 

Walter

 

 

Struct tag name space

 

The C Way

It's annoying to have to put the struct keyword every time a type is specified, so a common idiom is to use:

               typedef struct ABC { ... } ABC;

 

The D Way

Struct tag names are not in a separate name space, they are in the same name space as ordinary names. Hence:

               struct ABC { ... };

 

Walter

 

Declaring struct types and variables.

 

The C Way

Is to do it in one statement ending with a semicolon:

           struct Foo { int x; int y; } foo;

 

Or to separate the two:

           struct Foo { int x; int y; };        // note terminating ;

           struct Foo foo;

 

The D Way

Struct definitions and declarations can't be done in the same statement:

           struct Foo { int x; int y; }        // note there is no terminating ;

           Foo foo;

 

which means that the terminating ; can be dispensed with, eliminating the confusing difference between struct {} and function & block {} in how semicolons are used.

 

Walter

 

 

Getting the offset of a struct member.

 

The C Way

Naturally, another macro is used:

           #include <stddef>

           struct Foo { int x; int y; };

 

           off = offsetof(Foo, y);

 

The D Way

An offset is just another property:

           struct Foo { int x; int y; }

 

           off = Foo.y.offset;

 

 

Union initializations.

 

The C Way

Unions are initialized using the "first member" rule:

           union U { int a; long b; };

           union U x = { 5 };                // initialize member 'a' to 5

 

Adding union members or rearranging them can have disastrous consequences for any initializers.

 

Walter

 

The D Way

In D, which member is being initialized is mentioned explicitly:

           union U { int a; long b; }

           U x = { a:5 }

 

avoiding the confusion and maintenance problems.

 

Walter

 

Struct initializations.

 

The C Way

Members are initialized by their position within the {}'s:

           struct S { int a; int b; };

           struct S x = { 5, 3 };

 

This isn't much of a problem with small structs, but when there are numerous members, it becomes tedious to get the initializers carefully lined up with the field declarations. Then, if members are added or rearranged, all the initializations have to be found and modified appropriately. This is a minefield for bugs.

The D Way

Member initialization is done explicitly:

           struct S { int a; int b; }

           S x = { b:3, a:5 }

 

The meaning is clear, and there no longer is a positional dependence.

 

Walter

 

Arrays that parallel an enum

 

The C Way

Consider:

       enum COLORS { red, blue, green, max };

       char *cstring[max] = {"red", "blue", "green" };

 

This is fairly easy to get right because the number of entries is small. But suppose it gets to be fairly large. Then it can get difficult to maintain correctly when new entries are added.

 

The D Way

   enum COLORS { red, blue, green }

 

    char cstring[COLORS.max + 1][] =

    [

            COLORS.red   : "red",

            COLORS.blue  : "blue",

            COLORS.green : "green",

    ];

 

Not perfect, but better.

 

Walter