November 04, 2021

On Wednesday, 3 November 2021 at 20:36:08 UTC, russhy wrote:

>

Keeping things simple helps debugging!

I'd still have to run your program to be sure of its simple logic, though. The real star d feature that would help with debugging is unittest:

enum sign { negatives = 'n', positives = 'p', both = 'b' }
enum parity { evens = 'e', odds = 'o', both = 'b' }

bool simple(int n, sign s, parity p) {
    if (n < 0 && s == sign.positives) return false;
    if (n > 0 && s == sign.negatives) return false;
    if (n & 1) {
        if (p == parity.evens) return false;
    } else {
        if (p == parity.odds) return false;
    }
    return true;
}

unittest {
    import std.algorithm : filter, equal;
    auto numbers = [-3, 14, 47, -49, -30, 15, 4, -82, 99, 26];
    alias match = (ns, s, p) => ns.equal(numbers.filter!(n => simple(n, s, p)));

    assert(match(numbers, sign.both, parity.both));
    assert(match([-3, -49], sign.negatives, parity.odds));
    assert(match([-30, -82], sign.negatives, parity.evens));
    assert(match([14, 4, 26], sign.positives, parity.evens));
    assert(match([47, 15, 99], sign.positives, parity.odds));
    assert(match([-3, -49, -30, -82], sign.negatives, parity.both));
    assert(match([14, -30, 4, -82, 26], sign.both, parity.evens));
}
November 06, 2021

On Thursday, 4 November 2021 at 00:53:11 UTC, jfondren wrote:

>

On Wednesday, 3 November 2021 at 20:36:08 UTC, russhy wrote:

>

Keeping things simple helps debugging!

I'd still have to run your program to be sure of its simple logic, though. The real star d feature that would help with debugging is unittest:

What about the number 0 (zero)? If 0 is considered to be excluded from the domain how would one define in D a type which cannot represent 0 in the first place? Follow-up question: Are there language facilities such that one could initialize an array of these non-zero-ints like?:

   nzint [] numbers = [ -2, -1, 1, 2 ];

If 0 is included in the domain the classification of the values into negative and positive ones is not complete since 0 is neither.

enum sign { negatives = 'n', positives = 'p', both = 'b' }
enum parity { evens = 'e', odds = 'o', both = 'b' }

bool simple(int n, sign s, parity p) {
    if (n < 0 && s == sign.positives) return false;
    if (n > 0 && s == sign.negatives) return false;
    if (n & 1) {
        if (p == parity.evens) return false;
    } else {
        if (p == parity.odds) return false;
    }
    return true;
}

unittest {
    import std.algorithm : filter, equal;
    version (have_zero) {
        auto numbers = [-2, -1, 0, 1, 2];
    }
    else {
        auto numbers = [-2, -1, 1, 2];
    }
    alias match = (ns, s, p) => ns.equal(numbers.filter!(n => simple(n, s, p)));

    assert(match(numbers, sign.both, parity.both));
    assert(match([-1], sign.negatives, parity.odds));
    assert(match([-2], sign.negatives, parity.evens)); // fails when we have_zero
    assert(match([2], sign.positives, parity.evens));
    assert(match([1], sign.positives, parity.odds));
    assert(match([-2, -1], sign.negatives, parity.both));
    assert(match([-2, 2], sign.both, parity.evens));
}
$ dmd -version=have_zero -checkaction=context -unittest -main -run a.d
a.d(27): [unittest] false != true
1/1 modules FAILED unittests

November 06, 2021

On Saturday, 6 November 2021 at 13:27:55 UTC, kdevel wrote:

>

On Thursday, 4 November 2021 at 00:53:11 UTC, jfondren wrote:

>

On Wednesday, 3 November 2021 at 20:36:08 UTC, russhy wrote:

>

Keeping things simple helps debugging!

I'd still have to run your program to be sure of its simple logic, though. The real star d feature that would help with debugging is unittest:

What about the number 0 (zero)?

Not treating it as positive is a bug that I introduced in a rewrite.

>

If 0 is considered to be excluded from the domain how would one define in D a type which cannot represent 0 in the first place? Follow-up question: Are there language facilities such that one could initialize an array of these non-zero-ints like?:

   nzint [] numbers = [ -2, -1, 1, 2 ];

In general, you use a constructor that tests for non-zero and you take care to maintain the invariant. D has some tools (like invariant) to help with that, but it's not a dependent-typed language.

For this specific example, you can sort of do that with std.typecons.Nullable

unittest {
    import std.typecons : Nullable, nullable;
    import std.algorithm : map;
    import std.array : array;

    alias NonZero(T) = Nullable!(T, 0);

    NonZero!int[] numbers = [-2, -1, 0, 1, 2].map!(i => NonZero!int(i)).array;

    int sum;
    int invalids;
    foreach (n; numbers) {
        if (n.isNull) invalids++;
        else sum += n.get;
    }
    assert(sum == 0);
    assert(invalids == 1);
}
November 06, 2021

On Saturday, 6 November 2021 at 13:48:59 UTC, jfondren wrote:

>

On Saturday, 6 November 2021 at 13:27:55 UTC, kdevel wrote:

>

On Thursday, 4 November 2021 at 00:53:11 UTC, jfondren wrote:

>

On Wednesday, 3 November 2021 at 20:36:08 UTC, russhy wrote:

>

Keeping things simple helps debugging!

As simple as possible, but not simpler!

[...]

> >

What about the number 0 (zero)?

Not treating it as positive is a bug that I introduced in a rewrite.

Mathematically a whole number (integer) is positive iff it is greater than 0. Since 0 is not greater than 0 0 is not positive. The vocabulary of the program shall be changed such that the term "positive" is replaced with "non-negative". Or one introduces one (0) or two (-0, +0) new signs ...

November 08, 2021
On Tuesday, 2 November 2021 at 23:45:39 UTC, pascal111 wrote:
> Next code originally was a classic C code I've written, it's pure vertical thinking, now, I converted it successfully to D code, but I think I made no much changes to make it has more horizontal thinking style that it seems D programmers care in horizontal thinking style. Is there any additions I can make in the code to make it suitable with D style or it's fine?
>

Oh man! some of those answers... whoooooohah....

Here's mine, with a little of that horizontal thinking ;-)

// =============================================

module test;

import std.stdio : write, writeln, writefln, readf;
import std.algorithm : filter, sort;
import std.array: array;

void main()
{
    // NOTE: 0 is neither positive or negative, but is considered to be even

    int[] numbers = [-3, 14, 47, -49, -30, 15, 4, -82, 99, 26, 0];
    int[] result;

    char answer1;
    write("Would you like in list (n=negatives, p=positives, b=both)? ");
    readf(" %c", answer1);
    debug { writeln("You selected ", answer1); }
    switch(answer1)
    {
        case 'n' :
            result = numbers.filter!(a => (a < 0)).array;
            break;
        case 'p' :
            result = numbers.filter!(a => (a > 0)).array;
            break;
        case 'b' :
            result = numbers;
            break;
        default :
            writefln("Invalid answer." );
            throw new Exception("Bad input!!"); // don't waste my time!
    }


    char answer2;
    write("Would you like in list (e=evens, o=odds, b=both)? ");
    readf(" %c", answer2);
    debug { writeln("You selected ", answer2); }
    switch(answer2)
    {
        case 'e' :
            result = result.filter!(a => (a % 2 == 0)).array;
            break;
        case 'o' :
            result = result.filter!(a => (a % 2 == 1)).array;
            break;
        case 'b' :
            break;
        default :
            writefln("Invalid answer." );
        throw new Exception("Bad input!!"); // don't waste my time!
    }


    writeln(result.sort());

}

// =============================================

November 08, 2021
On Monday, 8 November 2021 at 12:04:26 UTC, forkit wrote:
>
>         case 'o' :
>             result = result.filter!(a => (a % 2 == 1)).array;

oops.

case 'o' :
            result = result.filter!(a => (a % 2 != 0)).array;


November 09, 2021
On Monday, 8 November 2021 at 12:04:26 UTC, forkit wrote:
> On Tuesday, 2 November 2021 at 23:45:39 UTC, pascal111 wrote:
>> Next code originally was a classic C code I've written, it's pure vertical thinking, now, I converted it successfully to D code, but I think I made no much changes to make it has more horizontal thinking style that it seems D programmers care in horizontal thinking style. Is there any additions I can make in the code to make it suitable with D style or it's fine?
>>
>
> Oh man! some of those answers... whoooooohah....
>
> Here's mine, with a little of that horizontal thinking ;-)
>
> ....

And just had a little fun transposing my D code back into C.

They both produce exactly the same output.

But I tell ya.. the cognitive load .. well.. it increased dramatically ;-)

increased cognitive load bad!

// ==========================================

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>

void displayResults(const char answer1, const char answer2);

int main(void)
{
    char answer1, answer2;

    while (true)
    {
        printf("Would you like in list (n=negatives, p=positives, b=both)? ");
        scanf_s(" %c", &answer1, 1);
        if (answer1 == 'n' || answer1 == 'p' || answer1 == 'b')
        {
            break;
        }
        printf("%c is not a valid answer. Try again.\n", answer1);
    }

    while (true)
    {
        printf("Would you like in list (e=evens, o=odds, b=both)? ");
        scanf_s(" %c", &answer2, 1);
        if (answer2 == 'e' || answer2 == 'o' || answer2 == 'b')
        {
            break;
        }
        printf("%c is not a valid answer. Try again.\n", answer2);
    }

    displayResults(answer1, answer2);

    putchar('\n');
    return EXIT_SUCCESS;
}

void displayResults(const char answer1, const char answer2)
{
    // NOTE: 0 is neither positive or negative, but is considered to be even

    int numbers[11] = {-3, 14, 47, -49, -30, 15, 4, -82, 99, 26, 0};

    // an array to store results from answer1
    int result1[sizeof(numbers) / sizeof(numbers[0])];

    // the final array that consolidates the results from answer1 and answer2
    // must be at least this size in case it stores all elements from numbers.
    int finalResult[sizeof(numbers) / sizeof(numbers[0])];

    int result1Count = 0; // to keep count of number of elements aquired in answer1
    int result2Count = 0; // to keep count of number of elements aquired in answer2

    // my version of a string builder ;-)
    const int msgSize = 50; // should be large enough to hold the entire message.
    char *const resultMsg = (char *)malloc(msgSize);
    if (resultMsg == NULL)
    {
        exit(EXIT_FAILURE); // not enough memory!! really??!!
    }
    strcpy_s(resultMsg, 15, "\nHere are all ");

    switch (answer1)
    {
        case 'n':
            for (size_t i = 0; i < sizeof(numbers) / sizeof(int); i++)
            {
                if (numbers[i] < 0)
                    result1[result1Count++] = numbers[i];
            }
            memcpy((char *)resultMsg + 14, "[negative] \0", 12);
            break;
        case 'p':
            for (size_t i = 0; i < sizeof(numbers) / sizeof(int); i++)
            {
                if (numbers[i] > 0)
                    result1[result1Count++] = numbers[i];
            }
            memcpy((char *)resultMsg + 14, "[positive] \0", 12);
            break;
        case 'b':
            for (size_t i = 0; i < sizeof(numbers) / sizeof(int); i++)
            {
                result1[result1Count++] = numbers[i];
            }
            break;
        default:
            // how could it ever get here??
            printf("Bad input!!\n%c is not a valid answer.", answer1);
            assert(0); // don't waste my time!
    }

    // debug
    /*
    {
        printf("\n[ result1Count after processing answer1 is: %d ]\n ", result1Count);
        for (int i = 0; i < result1Count; i++)
        {
            printf("%d ", result1[i]);
        }
        putchar('\n');
    }
    */

    switch (answer2)
    {
        case 'e':
            for (int i = 0; i < result1Count; i++)
            {
                if ((result1[i] % 2) == 0)
                    finalResult[result2Count++] = result1[i];
            }

            if (answer1 == 'b')
            {
                memcpy((char *)resultMsg + 14, "[even]\0", 7);
            }
            else
            {
                memcpy((char *)resultMsg + 25, "[even]\0", 7);
            }

            break;
        case 'o':
            for (int i = 0; i < result1Count; i++)
            {
                if ((result1[i] % 2) != 0)
                    finalResult[result2Count++] = result1[i];
            }

            if (answer1 == 'b')
            {
                memcpy((char *)resultMsg + 14, "[odd]\0", 6);
            }
            else
            {
                memcpy((char *)resultMsg + 25, "[odd]\0", 6);
            }

            break;
        case 'b':
            for (int i = 0; i < result1Count; i++)
            {
                finalResult[result2Count++] = result1[i];
            }
            break;
        default:
            // how could it ever get here??
            printf("Bad input!!\n%c is not a valid answer.", answer2);
            assert(0); // don't waste my time!
    }

    // debug
    /*
    {
        printf("\n[ result2Count after processing answer2 is: %d ]\n ", result2Count);
        for (int i = 0; i < result2Count; i++)
        {
            printf("%d ", finalResult[i]);
        }
        putchar('\n');
    }
    */

    // sort the part of the array that we're interested in, in ascending order.
    int t;
    for (int i = 0; i < result2Count; i++)
    {
        for (int j = i + 1; j < result2Count; j++)
        {
            if (finalResult[i] > finalResult[j])
            {
                t = finalResult[i];
                finalResult[i] = finalResult[j];
                finalResult[j] = t;
            }
        }
    }

    printf("%s numbers:\n", resultMsg);

    // print all the elements of the array
    for (int i = 0; i < result2Count; i++)
    {
        printf("%d ", finalResult[i]);
    }

    free(resultMsg);
}


// =====================================================


November 10, 2021
On Tuesday, 9 November 2021 at 11:03:09 UTC, forkit wrote:

> They both produce exactly the same output.

But do vastly different things.

> But I tell ya.. the cognitive load .. well.. it increased dramatically ;-)

Of course it did. Cuz you overthunk it. Dramatically.

Your D version allocates memory, for no good reason. I mean, to store results, but you already have storage for them, so kinda pointless. Your C version, OTOH, stores results on stack (so again, why did you allocate for them in D?..), but allocates some "string builder" for... what, exactly?

The program is filter, or sort + partition. Requires 0 allocations in C or D.
November 10, 2021
On Wednesday, 10 November 2021 at 04:54:58 UTC, Stanislav Blinov wrote:
> On Tuesday, 9 November 2021 at 11:03:09 UTC, forkit wrote:
>
>> They both produce exactly the same output.
>
> But do vastly different things.
>
>> But I tell ya.. the cognitive load .. well.. it increased dramatically ;-)
>
> Of course it did. Cuz you overthunk it. Dramatically.
>
> Your D version allocates memory, for no good reason. I mean, to store results, but you already have storage for them, so kinda pointless. Your C version, OTOH, stores results on stack (so again, why did you allocate for them in D?..), but allocates some "string builder" for... what, exactly?
>
> The program is filter, or sort + partition. Requires 0 allocations in C or D.

Thanks for your feedback.

This was not production code. It took a few minutes to do it on my pc, just for a little fun. So I doubt that I 'overthunk it' in such a small amount of time ;-)

Also, my thought process began from the basis that the number data was immutable, and that the final data need to be stored, so that it could be manipulated etc... at will. I also wanted the use to a message, as to what they chose (hence the manipulation of memory for a message that could not be know in advance of the user making those choices).

btw. My pc has 24GB of main memory, and my CPU 8MB L3 cache. So I really don't give a damn about allocations .. not one little bit ;-)

Now if I were running a million processes across 1000's of servers, I probably would give a damn.
November 10, 2021
On Wednesday, 10 November 2021 at 04:54:58 UTC, Stanislav Blinov wrote:
> On Tuesday, 9 November 2021 at 11:03:09 UTC, forkit wrote:
>
>> They both produce exactly the same output.
>
> But do vastly different things.
>
>> But I tell ya.. the cognitive load .. well.. it increased dramatically ;-)
>
> Of course it did. Cuz you overthunk it. Dramatically.
>
> Your D version allocates memory, for no good reason. I mean, to store results, but you already have storage for them, so kinda pointless. Your C version, OTOH, stores results on stack (so again, why did you allocate for them in D?..), but allocates some "string builder" for... what, exactly?
>
> The program is filter, or sort + partition. Requires 0 allocations in C or D.

And.. in any case, the take away from this thread should not be about nonallocating coding, but rather the interesting comment from the originl op..

"Wow! your code seem so nice, I like it although I don't know how exactly it works."