exercism / d

Exercism exercises in D.
https://exercism.org/tracks/d
MIT License
19 stars 31 forks source link

Building a training set of tags for d #203

Closed ErikSchierboom closed 1 year ago

ErikSchierboom commented 1 year ago

Hello lovely maintainers :wave:

We've recently added "tags" to student's solutions. These express the constructs, paradigms and techniques that a solution uses. We are going to be using these tags for lots of things including filtering, pointing a student to alternative approaches, and much more.

In order to do this, we've built out a full AST-based tagger in C#, which has allowed us to do things like detect recursion or bit shifting. We've set things up so other tracks can do the same for their languages, but its a lot of work, and we've determined that actually it may be unnecessary. Instead we think that we can use machine learning to achieve tagging with good enough results. We've fine-tuned a model that can determine the correct tags for C# from the examples with a high success rate. It's also doing reasonably well in an untrained state for other languages. We think that with only a few examples per language, we can potentially get some quite good results, and that we can then refine things further as we go.

I released a new video on the Insiders page that talks through this in more detail.

We're going to be adding a fully-fledged UI in the coming weeks that allow maintainers and mentors to tag solutions and create training sets for the neural networks, but to start with, we're hoping you would be willing to manually tag 20 solutions for this track. In this post we'll add 20 comments, each with a student's solution, and the tags our model has generated. Your mission (should you choose to accept it) is to edit the tags on each issue, removing any incorrect ones, and add any that are missing. In order to build one model that performs well across languages, it's best if you stick as closely as possible to the C# tags as you can. Those are listed here. If you want to add extra tags, that's totally fine, but please don't arbitrarily reword existing tags, even if you don't like what Erik's chosen, as it'll just make it less likely that your language gets the correct tags assigned by the neural network.


To summarise - there are two paths forward for this issue:

  1. You're up for helping: Add a comment saying you're up for helping. Update the tags some time in the next few days. Add a comment when you're done. We'll then add them to our training set and move forward.
  2. You not up for helping: No problem! Just please add a comment letting us know :)

If you tell us you're not able/wanting to help or there's no comment added, we'll automatically crowd-source this in a week or so.

Finally, if you have questions or want to discuss things, it would be best done on the forum, so the knowledge can be shared across all maintainers in all tracks.

Thanks for your help! :blue_heart:


Note: Meta discussion on the forum

ErikSchierboom commented 1 year ago

Exercise: hello-world

Code

module helloworld_test;

import std.stdio;

void main() {
    assert(hello() == "Hello, World!");
    assert(hello("Alice") == "Hello, Alice!");
    assert(hello("Bob") == "Hello, Bob!");
    assert(hello("") == "Hello, !");

    writeln("All tests passed.");
}

string hello() {
    return "Hello, World!";
}

string hello(string name) {
    return "Hello, " ~ name ~ "!";
}

Tags:

construct:string
construct:assert
construct:import
construct:module
construct:function
construct:parameter
construct:return
construct:unittest
construct:visibility
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:exceptions
ErikSchierboom commented 1 year ago

Exercise: leap

Code

bool is_leap(int year){
    if(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
    {
        return true;
    }
    return false;
}

unittest {
  assert(!is_leap(2015));
  assert(is_leap(2016));
  assert(!is_leap(2100));
  assert(is_leap(2000));
}

void main()
{

}

Tags:

construct:boolean
construct:if
construct:int
construct:integral
construct:invocation
construct:logical-and
construct:logical-or
construct:number
construct:parameter
construct:return
construct:unittest
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
ErikSchierboom commented 1 year ago

Exercise: gigasecond

Code

module gigasecond;

import std.stdio;
import std.datetime;

void main() {
    assert(gsAnniversary(DateTime(2011, 4, 25)) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(gsAnniversary(DateTime(1977, 6, 13)) == DateTime(2009, 2, 19, 1, 46, 40));
    assert(gsAnniversary(DateTime(1959, 7, 19)) == DateTime(1991, 3, 27, 1, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 22, 0, 0)) == DateTime(2046, 10, 2, 23, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 23, 59, 59)) == DateTime(2046, 10, 3, 1, 46, 39));

    //check that it doesn't mutate the argument
    auto d = DateTime(2011, 4, 25);
    assert(gsAnniversary(d) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(d == DateTime(2011, 4, 25));

    //For fun add a test for your own gigasecond anniversary
    assert(gsAnniversary(DateTime(1974, 11, 8)) == DateTime(2006, 7, 17, 1, 46, 40));

    writeln("All tests pass");
}

DateTime gsAnniversary(DateTime start) {
    return start + dur!"seconds"(1_000_000_000);
}

Tags:

construct:add
construct:assert
construct:auto
construct:comment
construct:constructor
construct:date
construct:double
construct:floating-point-number
construct:function
construct:import
construct:integral-number
construct:invocation
construct:module
construct:number
construct:parameter
construct:return
construct:string
construct:underscore
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
uses:DateTime
uses:std.datetime
uses:std.stdio
ErikSchierboom commented 1 year ago

Exercise: gigasecond

Code

module gigasecond;

import std.stdio;
import std.datetime;

DateTime gsAnniversary(DateTime date) {
  return date + seconds(1_000_000_000);
}

void main() {
    assert(gsAnniversary(DateTime(2011, 4, 25)) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(gsAnniversary(DateTime(1977, 6, 13)) == DateTime(2009, 2, 19, 1, 46, 40));
    assert(gsAnniversary(DateTime(1959, 7, 19)) == DateTime(1991, 3, 27, 1, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 22, 0, 0)) == DateTime(2046, 10, 2, 23, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 23, 59, 59)) == DateTime(2046, 10, 3, 1, 46, 39));

    //check that it doesn't mutate the argument
    auto d = DateTime(2011, 4, 25);
    assert(gsAnniversary(d) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(d == DateTime(2011, 4, 25));

    //For fun add a test for your own gigasecond anniversary

    writeln("All tests pass");
}

Tags:

construct:add
construct:assert
construct:auto
construct:comment
construct:constructor
construct:date-time
construct:double
construct:explicit-conversion
construct:floating-point-number
construct:function
construct:hexadecimal-number
construct:import
construct:integral-number
construct:invocation
construct:lambda
construct:module
construct:number
construct:parameter
construct:return
construct:underscored-number
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
technique:type-conversion
uses:DateTime
uses:std.datetime.DateTime
ErikSchierboom commented 1 year ago

Exercise: gigasecond

Code

module gigasecond;

import std.stdio;
import std.datetime;
import std.math;

void main() {
    assert(gsAnniversary(DateTime(2011, 4, 25)) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(gsAnniversary(DateTime(1977, 6, 13)) == DateTime(2009, 2, 19, 1, 46, 40));
    assert(gsAnniversary(DateTime(1959, 7, 19)) == DateTime(1991, 3, 27, 1, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 22, 0, 0)) == DateTime(2046, 10, 2, 23, 46, 40));
    assert(gsAnniversary(DateTime(2015, 1, 24, 23, 59, 59)) == DateTime(2046, 10, 3, 1, 46, 39));

    //check that it doesn't mutate the argument
    auto d = DateTime(2011, 4, 25);
    assert(gsAnniversary(d) == DateTime(2043, 1, 1, 1, 46, 40));
    assert(d == DateTime(2011, 4, 25));

    //For fun add a test for your own gigasecond anniversary

    writeln("All tests pass");
}

DateTime gsAnniversary(DateTime start_time)
{
    start_time += (10.pow(9)).seconds;
    return start_time;
}

Tags:

construct:assignment
construct:date-time
construct:double
construct:floating-point-number
construct:function
construct:import
construct:integral-number
construct:invocation
construct:method
construct:module
construct:number
construct:parameter
construct:return
construct:statement
construct:std
construct:user-defined-numeric-conversions
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:type-conversion
uses:DateTime
uses:DateTimeOps
ErikSchierboom commented 1 year ago

Exercise: rna-transcription

Code

module rna_transcription;

import std.exception : assertThrown;
import std.stdio;

static char[char] complement;

void main() {
    assert(dnaComplement("C") == "G");
    assert(dnaComplement("G") == "C");
    assert(dnaComplement("T") == "A");
    assert(dnaComplement("A") == "U");

    assert(dnaComplement("ACGTGGTCTTAA") == "UGCACCAGAAUU");

    assertThrown(dnaComplement("U"));
    assertThrown(dnaComplement("XXX"));
    assertThrown(dnaComplement("ACGTXXXCTTAA"));

    writeln("All tests passed");
}

string dnaComplement(string x)
{
    complement = [
        'G' : 'C', 
        'C' : 'G', 
        'T' : 'A', 
        'A' : 'U'
        ];
    string output = ""; 
    foreach(letter; x)
    {
        if (auto v = letter in complement) 
        {
            output ~= complement[letter];
        } 
        else 
        {
            throw new Exception("Nucleotide does not have a matching complement");
        }
    }
    return output; 
}

Tags:

construct:assignment
construct:char
construct:constructor
construct:dictionary
construct:foreach
construct:if
construct:implicit-conversion
construct:indexing
construct:initializer
construct:invocation
construct:nested-function
construct:return
construct:string
construct:throw
construct:try
construct:variable-shadowing
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:exceptions
technique:looping
ErikSchierboom commented 1 year ago

Exercise: raindrops

Code

import std.stdio;
import std.conv : to;

string convert(int nbr) {
    string output = "";

    if (nbr % 3 == 0)
        output ~= "Pling";
    if (nbr % 5 == 0)
        output ~= "Plang";
    if (nbr % 7 == 0)
        output ~= "Plong";

    if (output.length == 0)
        return nbr.to!string;

    return output;
}

void main() {
    assert(convert(1) == "1");
    assert(convert(3) == "Pling");
    assert(convert(5) == "Plang");
    assert(convert(7) == "Plong");
    assert(convert(6) == "Pling");
    assert(convert(9) == "Pling");
    assert(convert(10) == "Plang");
    assert(convert(14) == "Plong");
    assert(convert(15) == "PlingPlang");
    assert(convert(21) == "PlingPlong");
    assert(convert(25) == "Plang");
    assert(convert(35) == "PlangPlong");
    assert(convert(49) == "Plong");
    assert(convert(52) == "52");
    assert(convert(105) == "PlingPlangPlong");

    writeln("All tests passed");
}

Tags:

construct:assignment
construct:assert
construct:if
construct:int
construct:integralNumber
construct:invocation
construct:lambda
construct:number
construct:return
construct:string
construct:template
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
ErikSchierboom commented 1 year ago

Exercise: etl

Code

module etl;

import std.array : array;
import std.algorithm.sorting : sort;
import std.algorithm.comparison : equal;
import std.ascii : toLower;

unittest
{

    // test associative array equality
    bool aaEqual (const int[dchar] lhs, const int[dchar] rhs)
    {
        auto lhs_pairs = lhs.byKeyValue.array;
        auto rhs_pairs = rhs.byKeyValue.array;
        lhs_pairs.sort!(q{a.key < b.key});
        rhs_pairs.sort!(q{a.key < b.key});

        return equal!("a.key == b.key && a.value == b.value")(lhs_pairs, rhs_pairs);
    }

    immutable int allTestsEnabled = 1;

    // transform one value
    {
        immutable string[int] old = [1: "A"];

        const auto actual = transform(old);
        const int[dchar] expected = ['a': 1];

        assert(aaEqual(expected, actual));
    }

    static if (allTestsEnabled)
    {
        // transform more values
        {
            immutable string[int] old = [1: "AEIOU"];

            const auto actual = transform(old);
            const int[dchar] expected = ['a': 1, 'e': 1, 'i': 1, 'o': 1, 'u': 1];

            assert(aaEqual(expected, actual));
        }

        // transforms more keys
        {
            immutable string[int] old = [1: "AE", 2: "DG"];

            const auto actual = transform(old);
            const int[dchar] expected = ['a': 1, 'e': 1, 'd': 2, 'g': 2];

            assert(aaEqual(expected, actual));
        }

        // transforms a full dataset
        {
            immutable string[int] old = [1: "AEIOULNRST",
                      2: "DG",
                      3: "BCMP",
                      4: "FHVWY",
                      5: "K",
                      8: "JX",
                      10: "QZ"];

            const auto actual = transform(old);

            const int[dchar] expected = ['a': 1, 'b': 3,  'c': 3, 'd': 2, 'e': 1,
                  'f': 4, 'g': 2,  'h': 4, 'i': 1, 'j': 8,
                  'k': 5, 'l': 1,  'm': 3, 'n': 1, 'o': 1,
                  'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1,
                  'u': 1, 'v': 4,  'w': 4, 'x': 8, 'y': 4,
                  'z': 10];

            assert(aaEqual(expected, actual));
        }

    }

}

void main ()
{
}

int[dchar] transform(immutable string[int] old_score)
{
    int[dchar] new_score;
    string[int] input = [1 : "AEIOU"];
    foreach(score, letters; old_score)
    {
        import std.stdio;
        foreach(letter; letters)
        {
            new_score[letter.toLower] = score;
        }
    }
    return new_score;
}

Tags:

construct:assignment
construct:auto
construct:boolean
construct:comment
construct:const
construct:foreach
construct:function
construct:if
construct:import
construct:int
construct:integral
construct:invocation
construct:lambda
construct:module
construct:number
construct:parameter
construct:return
construct:static if
construct:string
construct:unittest
construct:variable
construct:visibility modifier
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:object-oriented
technique:enumeration
technique:higher-order-functions
technique:looping
uses:std.algorithm.comparison.equal
uses:std.algorithm.sorting.sort
uses:std.array.array
uses:std.ascii.toLower
ErikSchierboom commented 1 year ago

Exercise: etl

Code

module etl;

import std.array : array;
import std.algorithm.sorting : sort;
import std.algorithm.comparison : equal;
import std.uni : toLower;

int[dchar] transform(immutable string[int] old) {
  int[dchar] result;

  foreach(int score, string letter; old) {
    dchar c = toLower(letter)[0];
    result[c] = score;
  }

  return result;
}

unittest
{

  // test associative array equality
  bool aaEqual (const int[dchar] lhs, const int[dchar] rhs)
  {
    auto lhs_pairs = lhs.byKeyValue.array;
    auto rhs_pairs = rhs.byKeyValue.array;
    lhs_pairs.sort!(q{a.key < b.key});
    rhs_pairs.sort!(q{a.key < b.key});

    return equal!("a.key == b.key && a.value == b.value")(lhs_pairs, rhs_pairs);
  }

  immutable int allTestsEnabled = 0;

  // transform one value
  {
    immutable string[int] old = [1: "A"];

    const auto actual = transform(old);
    const int[dchar] expected = ['a': 1];

    assert(aaEqual(expected, actual));
  }

  static if (allTestsEnabled)
  {
    // transform more values
    {
      immutable string[int] old = [1: "AEIOU"];

      const auto actual = transform(old);
      const int[dchar] expected = ['a': 1, 'e': 1, 'i': 1, 'o': 1, 'u': 1];

      assert(aaEqual(expected, actual));
    }

    // transforms more keys
    {
      immutable string[int] old = [1: "AE", 2: "DG"];

      const auto actual = transform(old);
      const int[dchar] expected = ['a': 1, 'e': 1, 'd': 2, 'g': 2];

      assert(aaEqual(expected, actual));
    }

    // transforms a full dataset
    {
      immutable string[int] old = [1: "AEIOULNRST",
                2: "DG",
                3: "BCMP",
                4: "FHVWY",
                5: "K",
                8: "JX",
                10: "QZ"];

      const auto actual = transform(old);

      const int[dchar] expected = ['a': 1, 'b': 3,  'c': 3, 'd': 2, 'e': 1,
            'f': 4, 'g': 2,  'h': 4, 'i': 1, 'j': 8,
            'k': 5, 'l': 1,  'm': 3, 'n': 1, 'o': 1,
            'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1,
            'u': 1, 'v': 4,  'w': 4, 'x': 8, 'y': 4,
            'z': 10];

      assert(aaEqual(expected, actual));
    }

  }

}

Tags:

construct:assignment
construct:auto
construct:boolean
construct:char
construct:comment
construct:const
construct:foreach
construct:function
construct:if
construct:import
construct:indexing
construct:int
construct:integral
construct:invocation
construct:lambda
construct:module
construct:number
construct:parameter
construct:return
construct:static if
construct:string
construct:template
construct:unittest
construct:variable
construct:visibility modifier
paradigm:functional
paradigm:imperative
paradigm:metaprogramming
paradigm:object-oriented
technique:enumeration
technique:higher-order-functions
technique:looping
ErikSchierboom commented 1 year ago

Exercise: robot-name

Code

module robot;

import std.regex;
import std.stdio;
import std.random;
import std.format;
import std.algorithm;

unittest
{

// test for properly formatted name
{
    auto pattern = regex(`^[A-Z]{2}\d{3}`);
    auto theRobot = new Robot();

    // test the regex pattern
    assert(matchAll("VAV224", pattern).empty);
    assert(matchAll("V221", pattern).empty);
    assert(matchAll("190", pattern).empty);
    assert(matchAll("12345", pattern).empty);
    assert(matchAll("SB1", pattern).empty);
    assert(matchAll("TT", pattern).empty);

    writefln("Robot name: %s", theRobot.name);

    // test that the name respects the pattern
    // that is: "2 uppercase letters followed by 3 digits"
    assert(!matchAll(theRobot.name, pattern).empty);
}

immutable int allTestsEnabled = 1;

static if (allTestsEnabled)
{
// test name stickiness
{
    auto theRobot = new Robot();
    auto name = theRobot.name;

    writefln("Robot name: %s", theRobot.name);
    assert(name == theRobot.name);
}

// test different names for different Robots
{
    auto erTwoDeeTwo = new Robot();
    auto beeBeeEight = new Robot();

    writefln("Robot name: %s", erTwoDeeTwo.name);
    writefln("Robot name: %s", beeBeeEight.name);
    assert(erTwoDeeTwo.name != beeBeeEight.name);
}

// test name reset
{
    auto theRobot = new Robot();
    auto nameOne = theRobot.name;
    theRobot.reset();
    auto nameTwo = theRobot.name;

    writefln("Robot name: %s", nameOne);
    writefln("Robot name: %s", nameTwo);
    assert(nameOne != nameTwo);
}

// collision test
{
    foreach(i; 1..10_000)
    {
        auto theRobot = new Robot();
    }

    writefln("Collisons: %s that is %s%%", Robot.collisons, (Robot.collisons/10_000.0f) * 100);
}
}

}

class Robot
{
    static string[] names;
    static uint collisons = 0;
    string name;

    this()
    {
        this.name = gen_random_name();
        if (canFind(names, name)) {
            collisons += 1;
        } else {
            names ~= name;
        }

    }

    public void reset()
    {
        this.name = gen_random_name();
    }
    private string gen_random_name()
    {
        char[2] chars;
        int id;

        foreach(k; 0..2) {
            chars[k] = uniform('A', 'Z');
        }
        id = uniform(0, 1000);
        string res = format("%s%03d", chars, id);
        return res;
    }

}
void main ()
{
}

Tags:

construct:assignment
construct:auto
construct:class
construct:comment
construct:constructor
construct:divide
construct:double
construct:enum
construct:field
construct:float
construct:floating-point-number
construct:foreach
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:method
construct:multiply
construct:number
construct:parameter
construct:return
construct:string
construct:template
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:enumeration
technique:higher-order-functions
technique:looping
technique:randomness
technique:regular-expression
uses:Regex
ErikSchierboom commented 1 year ago

Exercise: difference-of-squares

Code

module squares;

struct squares
{
    uint squareOfSum;
    uint sumOfSquares;
    uint sumOfNumber;
    uint upTo;

    this(uint upperBound)
    {
        this.upTo = upperBound;
        foreach(ele; 0..this.upTo+1) {
            this.sumOfSquares += ele^^2;
            this.sumOfNumber += ele;
        }
        this.squareOfSum = this.sumOfNumber^^2;

    }

    @property uint difference()
    {
        return this.squareOfSum - this.sumOfSquares;
    }
}

Tags:

construct:add
construct:assignment
construct:expression
construct:foreach
construct:getter
construct:integral-number
construct:invocation
construct:method
construct:module
construct:number
construct:parameter
construct:property
construct:return
construct:struct
construct:subtract
construct:uint
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:enumeration
technique:looping
ErikSchierboom commented 1 year ago

Exercise: roman-numerals

Code

module roman_numerals;

import std.stdio;

string convert(int n)
{
    string ans = "";
    string[] M = ["","M","MM","MMM"];
    string[] C = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"];
    string[] X = ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"];
    string[] I = ["","I","II","III","IV","V","VI","VII","VIII","IX"];
    ans = M[n/1000] ~ C[(n%1000)/100] ~ X[(n%100)/10] ~ I[(n%10)];
    return (ans);
}

unittest
{

immutable int allTestsEnabled = 1;

// one_yields_I
{
    writefln("Conversion of 1: %s", convert(1));
    assert("I" == convert(1));
}

static if (allTestsEnabled)
{
// two_yields_II
{
    writefln("Conversion of 2: %s", convert(2));
    assert("II" == convert(2));
}

// three_yields_III
{
    writefln("Conversion of 3: %s", convert(3));
    assert("III" == convert(3));
}

// four_yields_IV
{
    writefln("Conversion of 4: %s", convert(4));
    assert("IV" == convert(4));
}

// five_yields_V
{
    writefln("Conversion of 5: %s", convert(5));
    assert("V" == convert(5));
}

// six_yields_VI
{
    writefln("Conversion of 6: %s", convert(6));
    assert("VI" == convert(6));
}

// nine_yields_IX
{
    writefln("Conversion of 9: %s", convert(9));
    assert("IX" == convert(9));
}

// twenty_seven_yields_XXVII
{
    writefln("Conversion of 27: %s", convert(27));
    assert("XXVII" == convert(27));
}

// forty_eight_yields_XLVIII
{
    writefln("Conversion of 48: %s", convert(48));
    assert("XLVIII" == convert(48));
}

// fifty_nine_yields_LIX
{
    writefln("Conversion of 59: %s", convert(59));
    assert("LIX" == convert(59));
}

// ninety_three_yields_XCIII
{
    writefln("Conversion of 93: %s", convert(93));
    assert("XCIII" == convert(93));
}

// one_hundred_forty_one_yields_CXLI
{
    writefln("Conversion of 141: %s", convert(141));
    assert("CXLI" == convert(141));
}

// one_hundred_sixty_three_yields_CLXIII
{
    writefln("Conversion of 163: %s", convert(163));
    assert("CLXIII" == convert(163));
}

// four_hundred_two_yields_CDII
{
    writefln("Conversion of 402: %s", convert(402));
    assert("CDII" == convert(402));
}

// five_hundred_seventy_five_yields_DLXXV
{
    writefln("Conversion of 575: %s", convert(575));
    assert("DLXXV" == convert(575));
}

// nine_hundred_eleven_yields_CMXI
{
    writefln("Conversion of 911: %s", convert(911));
    assert("CMXI" == convert(911));
}

// one_thousand_twenty_four_yields_MXXIV
{
    writefln("Conversion of 1024: %s", convert(1024));
    assert("MXXIV" == convert(1024));
}

// three_thousand_yields_MMM)
{
    writefln("Conversion of 3000: %s", convert(3000));
    assert("MMM" == convert(3000));
}
writefln("All tests passed.");
}

}

void main ()
{
}

Tags:

construct:assignment
construct:assert
construct:comment
construct:divide
construct:if-then-else
construct:import
construct:indexing
construct:integral-number
construct:integral-division
construct:invocation
construct:module
construct:number
construct:parameter
construct:return
construct:string
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:reflective
technique:testing
ErikSchierboom commented 1 year ago

Exercise: series

Code

module series;

import std.exception;
import std.algorithm;
import std.array;

import std.string: indexOfNeither;
int[] digits(string numstring)
{
    if (indexOfNeither(numstring, "0123456789") != -1) {
        throw new Exception("Invalid input!");
    }
    // allocate on heap!
    int[] res = new int[numstring.length];
    //res = numstring.map!(a => a -'0').array();
    foreach(idx, e; numstring) {
        res[idx] = e - '0';
    }
    return res;
}

int[][] slice(string numstring, uint cutting)
{
    if (numstring.length < cutting) {
        throw new Exception("Cutting length cannot exceed string length!");
    }
    int[] splitted_num = digits(numstring);
    ulong slicing_length = numstring.length + 1 - cutting;
    int[][] res = new int[][slicing_length];
    foreach(idx; 0..slicing_length) {
        res[idx] = splitted_num[idx..idx+cutting];
    }
    return res;

}

unittest
{

immutable int allTestsEnabled = 1;

// short_digits
{
    const int[] expected = [0, 1, 2, 3, 4, 5];
    const int[] actual = digits("012345");

    assert(equal(actual, expected));
}

static if (allTestsEnabled)
{
// long_digits
{
    const int[] expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    const int[] actual = digits("0123456789");

    assert(equal(actual, expected));
}

// keeps_the_digit_order_if_reversed
{
    const int[] expected = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
    const int[] actual = digits("9876543210");

    assert(equal(actual, expected));
}

// keeps_arbitrary_digit_order
{
    const int[] expected = [9, 3, 6, 9, 2, 3, 4, 6, 8];
    const int[] actual = digits("936923468");

    assert(equal(actual, expected));
}

// can_slice_by_1
{
    const int[][] expected = [[0], [1], [2], [3], [4]];
    const int[][] actual = slice("01234", 1);

    assert(equal(actual, expected));
}

// can_slice_by_2
{
    const int[][] expected = [[9, 8], [8, 2], [2, 7], [7, 3], [3, 4], [4, 6], [6, 3]];
    const int[][] actual = slice("98273463", 2);

    assert(equal(actual, expected));
}

// can_slice_by_3
{
    const int[][] expected = [[0, 1, 2], [1, 2, 3], [2, 3, 4]];
    const int[][] actual = slice("01234", 3);

    assert(equal(actual, expected));
}

// can_slice_by_3_with_duplicate_digits
{
    const int[][] expected = [[3, 1, 0], [1, 0, 0], [0, 0, 1]];
    const int[][] actual = slice("31001", 3);

    assert(equal(actual, expected));
}

// can_slice_by_4
{
    const int[][] expected = [[3, 1, 0], [1, 0, 0], [0, 0, 1]];
    const int[][] actual = slice("31001", 3);

    assert(equal(actual, expected));
}

// can_slice_by_5
{
    const int[][] expected = [[8, 1, 2, 2, 8]];
    const int[][] actual = slice("81228", 5);

    assert(equal(actual, expected));
}

// exception_if_not_enough_digits_to_slice
{
    assertThrown(slice("01032987583", 12));
}

// exception_if_invalid_input
{
    assertThrown(slice("01032i987583", 12));
}

// exception_if_invalid_input_2
{
    assertThrown(digits("01032i987583"));
}

}

}

void main ()
{
}

Tags:

construct:add
construct:allocate-on-heap
construct:assert
construct:assignment
construct:char
construct:comment
construct:const
construct:constructor
construct:foreach
construct:if
construct:immutable
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:length
construct:long
construct:module
construct:new-expression
construct:number
construct:parameter
construct:return
construct:static-if
construct:string
construct:subtract
construct:throw
construct:throw-expression
construct:typedef
construct:unittest
construct:ulong
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:enumeration
technique:exceptions
technique:higher-order-functions
technique:looping
uses:Exception
ErikSchierboom commented 1 year ago

Exercise: triangle

Code

module triangle;

import std.exception;
import std.algorithm: sort;
enum TriangleType
{
    equilateral,
    isosceles,
    scalene
}

bool isValidTriangle(double a, double b, double c)
{
    if (a <0 || b<0 || c<0 ) {
        return false;
    }

    auto sorted = [a,b,c].sort();
    if (sorted[0] + sorted[1] > sorted[2]) {
        return true;
    } else {
        return false;
    }
}

TriangleType kind(double a, double b, double c)
{
    if (!isValidTriangle(a,b,c)) {
        throw new Exception("Not valid triganlge slides data");
    }

    auto sorted = [a,b,c].sort();

    if(sorted[0] == sorted[2]) {
        return TriangleType.equilateral;
    } else if (sorted[0] == sorted[1] || sorted[1] == sorted[2]) {
        return TriangleType.isosceles;

    } else {
        return TriangleType.scalene;
    }
}

unittest
{

immutable int allTestsEnabled = 1;

// equilateral_triangles_have_equal_sides
{
    assert(TriangleType.equilateral == kind(2, 2, 2));
}

static if (allTestsEnabled)
{
// larger_equilateral_triangles_also_have_equal_sides
{
    assert(TriangleType.equilateral == kind(10, 10, 10));
}

// isosceles_triangles_have_last_two_sides_equal
{
    assert(TriangleType.isosceles == kind(3, 4, 4));
}

// isosceles_triangles_have_first_and_last_sides_equal
{
    assert(TriangleType.isosceles == kind(4, 3, 4));
}

// isosceles_triangles_have_first_two_sides_equal
{
    assert(TriangleType.isosceles == kind(4, 4, 3));
}

// isosceles_triangles_have_in_fact_exactly_two_sides_equal
{
    assert(TriangleType.isosceles == kind(10, 10, 2));
}

// scalene_triangles_have_no_equal_sides
{
    assert(TriangleType.scalene == kind(3, 4, 5));
}

// scalene_triangles_have_no_equal_sides_at_a_larger_scale_too
{
    assert(TriangleType.scalene == kind(10, 11, 12));
}

// scalene_triangles_have_no_equal_sides_in_descending_order_either
{
    assert(TriangleType.scalene == kind(5, 4, 2));
}

// very_small_triangles_are_legal
{
    assert(TriangleType.scalene == kind(0.4, 0.6, 0.3));
}

// triangles_with_no_size_are_illegal
{
    assertThrown(kind(0, 0, 0));
}

// triangles_with_negative_sides_are_illegal
{
    assertThrown(kind(3, 4, -5));
}

// triangles_violating_triangle_inequality_are_illegal
{
    assertThrown(kind(1, 1, 3));
}

// larger_triangles_violating_triangle_inequality_are_illegal
{
    assertThrown(kind(7, 3, 2));
}

}

}

void main ()
{
}

Tags:

construct:add
construct:assert
construct:auto
construct:boolean
construct:class
construct:comment
construct:constructor
construct:double
construct:enum
construct:if
construct:implicit-conversion
construct:indexing
construct:initializer
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:logical-or
construct:method
construct:number
construct:parameter
construct:return
construct:sort
construct:string
construct:throw
construct:unittest
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:object-oriented
technique:boolean-logic
technique:exceptions
technique:higher-order-functions
uses:Exception
ErikSchierboom commented 1 year ago

Exercise: crypto-square

Code


module crypto;
import std.stdio: writeln;
import std.math: sqrt, ceil;
import std.algorithm.iteration: filter, map;
import std.ascii: isAlphaNum;
import std.range: appender;
import std.conv: to;
import std.algorithm.comparison: min;
import std.string: strip, toLower;

struct Cipher
{
private:
    string plainText_;
    string normalizedText_;
    string[] plainTextSegments_;
    bool isNormalized_ = false;
public:
    this(string plainText) {
        plainText_ = plainText;
        normalizedText_ = buildNormalizedPlainText();
        plainTextSegments_ = buildPlainTextSegments();
    }
    string normalizePlainText() {
        return normalizedText_;
    }
    uint size() {
        return normalizedText_.length.to!float.sqrt.ceil.to!uint;
    }
    string[] plainTextSegments() {
        return plainTextSegments_;
    }
    Cipher* normalize() {
        isNormalized_ = true;
        return &this;
    }
    string cipherText() {
        auto appender = appender!string;
        appender.reserve(this.normalizedText_.length);
        foreach(j; 0..size()) {
            foreach(segment; plainTextSegments_) {
                if (j < segment.length ) {
                    appender ~= segment[j];
                }
            }
            if (isNormalized_) {
                appender ~= ' ';
            }
        }
        debug(2) writeln(appender.data);
        import std.string;
        return appender.data.strip;
    }

private:
    string buildNormalizedPlainText() {
        auto appender = appender!string;
        appender.reserve(plainText_.length);
        appender ~= plainText_.filter!isAlphaNum.map!toLower;
        return to!string(appender.data);
    }
    string[] buildPlainTextSegments() {
        auto columns = size();
        auto rows = (normalizedText_.length.to!float / columns).ceil.to!uint;
        debug(2) writeln(normalizedText_.length, ' ', rows, ' ', columns); 
        auto appender = appender!(string[]);
        appender.reserve(rows);
        foreach(i; 0..rows) {
            auto limit = min( (i+1) * columns , normalizedText_.length);
            appender ~= normalizedText_[i * columns..limit];
        }
        debug(2) writeln(appender.data);
        return appender.data;
    }
}

unittest
{
immutable int allTestsEnabled = 1;

// normalize_strange_characters
{
    auto theCipher = new Cipher("s#$%^&plunk");
    assert("splunk" == theCipher.normalizePlainText());
}
static if (allTestsEnabled)
{

// normalize_numbers
{
    auto theCipher = new Cipher("1, 2, 3 GO!");
    assert("123go" == theCipher.normalizePlainText());
}

// size_of_small_square
{
    auto theCipher = new Cipher("1234");
    assert(2U == theCipher.size());
}

// size_of_slightly_larger_square
{
    auto theCipher = new Cipher("123456789");
    assert(3U == theCipher.size());
}

// size_of_non_perfect_square
{
    auto theCipher = new Cipher("123456789abc");
    assert(4U == theCipher.size());
}

// size_of_non_perfect_square_less
{
    auto theCipher = new Cipher("zomgzombies");
    assert(4U == theCipher.size());
}

// plain_text_segments_from_phrase
{
    const string[] expected = ["neverv", "exthin", "eheart", "withid", "lewoes"];
    auto theCipher = new Cipher("Never vex thine heart with idle woes");
    const auto actual = theCipher.plainTextSegments();

    assert(expected == actual);
}

// plain_text_segments_from_complex_phrase
{
    const string[] expected = ["zomg", "zomb", "ies"];
    auto theCipher = new Cipher("ZOMG! ZOMBIES!!!");
    const auto actual = theCipher.plainTextSegments();

    assert(expected == actual);
}

// Cipher_text_short_phrase
{
    auto theCipher = new Cipher("Time is an illusion. Lunchtime doubly so.");
    assert("tasneyinicdsmiohooelntuillibsuuml" == theCipher.cipherText());
}

// Cipher_text_long_phrase
{
    auto theCipher = new Cipher("We all know interspecies romance is weird.");
    assert("wneiaweoreneawssciliprerlneoidktcms" == theCipher.cipherText());
}

// normalized_Cipher_text1
{
    auto theCipher = new Cipher("Madness, and then illumination.");
    assert("msemo aanin dnin ndla etlt shui" == theCipher.normalize.cipherText());
}

// normalized_Cipher_text2
{
    auto theCipher = new Cipher("Vampires are people too!");
    assert("vrel aepe mset paoo irpo" == theCipher.normalize.cipherText());
}
}

}

Tags:

construct:add
construct:alias
construct:assert
construct:assignment
construct:auto-construct
construct:boolean
construct:char
construct:class
construct:comment
construct:constructor
construct:divide
construct:explicit-conversion
construct:field
construct:float
construct:foreach
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:length
construct:method
construct:multiply
construct:number
construct:parameter
construct:return
construct:string
construct:struct
construct:subtract
construct:template
construct:typedef
construct:uint
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:enumeration
technique:higher-order-functions
technique:looping
technique:type-conversion
ErikSchierboom commented 1 year ago

Exercise: pangram

Code

module pangram;

import std.string;

unittest
{
    immutable bool all_tests_enabled = true;

    assert(!isPangram(""));

    static if (all_tests_enabled)
    {
        assert(isPangram("the quick brown fox jumps over the lazy dog"));
        // missing x
        assert(!isPangram("a quick movement of the enemy will jeopardize five gunboats"));
        assert(!isPangram("the quick brown fish jumps over the lazy dog"));
        // test underscores
        assert(isPangram("the_quick_brown_fox_jumps_over_the_lazy_dog"));
        // test pangram with numbers
        assert(isPangram("the 1 quick brown fox jumps over the 2 lazy dogs"));
        // test missing letters replaced by numbers
        assert(!isPangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"));
        // test pangram with mixed case and punctuation
        assert(isPangram("\"Five quacking Zephyrs jolt my wax bed\""));
        // pangram with non-ascii characters
        assert(isPangram("Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich"));
    }
}

void main ()
{
}

bool isPangram(string s)
{
    char[] letters = [
        'a', 'b', 'c', 'd', 'e', 'f', 'g',
        'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u',
        'v', 'w', 'x', 'y', 'z',
    ];
    s = s.toLower();
    foreach (char l; letters)
    {
        auto found = false;
        foreach (char c; s)
        {
            if (c == l)
            {
                found = true;
                break;
            }
        }
        if (!found)
            return false;
    }
    return true;
}

Tags:

construct:assignment
construct:auto
construct:boolean
construct:break
construct:char
construct:comment
construct:foreach
construct:if
construct:immutable
construct:import
construct:initializer
construct:invocation
construct:module
construct:parameter
construct:return
construct:string
construct:toLower
construct:unittest
construct:variable
construct:visibility
paradigm:imperative
paradigm:reflective
technique:enumeration
technique:looping
ErikSchierboom commented 1 year ago

Exercise: two-fer

Code

module two_fer;
import std.range;

string twoFer() {
    return "One for you, one for me.";
}

string twoFer(string name) {
    return "One for " ~ name ~ ", one of me.";
}

unittest
{
    immutable int allTestsEnabled = 0;

    // No name given
    assert(twoFer() == "One for you, one for me.");

    static if (allTestsEnabled)
    {
        // A name given
        assert(twoFer("Alice") == "One for Alice, one for me.");

        // Another name given
        assert(twoFer("Bob") == "One for Bob, one for me.");
    }

}

Tags:

construct:string-concatenation
construct:assert
construct:comment
construct:if-else
construct:import
construct:integral-number
construct:invocation
construct:module
construct:number
construct:parameter
construct:return
construct:string
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:metaprogramming
paradigm:object-oriented
technique:testing
ErikSchierboom commented 1 year ago

Exercise: two-fer

Code

module two_fer;

string twoFer(string name="you"){
   return "One for "~name~", one for me.";
}

unittest
{
    immutable int allTestsEnabled = 1;

    // No name given
    assert(twoFer() == "One for you, one for me.");

    static if (allTestsEnabled)
    {
        // A name given
        assert(twoFer("Alice") == "One for Alice, one for me.");

        // Another name given
        assert(twoFer("Bob") == "One for Bob, one for me.");
    }

}

Tags:

construct:string-concatenation
construct:assignment
construct:comment
construct:default-argument
construct:function
construct:if
construct:immutable
construct:int
construct:integral-number
construct:module
construct:number
construct:parameter
construct:return
construct:static-if
construct:string
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:functional
paradigm:reflective
technique:compile-time-metaprogramming
technique:concatenation
uses:two_fer
ErikSchierboom commented 1 year ago

Exercise: resistor-color

Code

module resistor_color;

import std.algorithm.searching: countUntil;

private immutable string[] colors = ["black",
                                    "brown",
                                    "red",
                                    "orange",
                                    "yellow",
                                    "green",
                                    "blue",
                                    "violet",
                                    "grey",
                                    "white"];
struct ResistorColor{

    static int colorCode(string color){
        //2 hour searching for a right funcition (-_-)
        return cast(int) colors.countUntil(color);
    }

    static string[] colors(){
        return colors[];
    }

}

unittest
{
    immutable int allTestsEnabled = 1;

    // Black
    assert(ResistorColor.colorCode("black") == 0);

    static if (allTestsEnabled)
    {
        // White
        assert(ResistorColor.colorCode("white") == 9);

        // Orange
        assert(ResistorColor.colorCode("orange") == 3);

        // Colors
        assert(ResistorColor.colors == [
                "black", "brown", "red", "orange", "yellow", "green", "blue",
                "violet", "grey", "white"
                ]);
    }

}

Tags:

construct:assert
construct:assignment
construct:cast
construct:comment
construct:enum
construct:if
construct:import
construct:initializer
construct:int
construct:integral-number
construct:invocation
construct:module
construct:number
construct:parameter
construct:return
construct:static-if
construct:struct
construct:string
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:type-conversion
ErikSchierboom commented 1 year ago

Exercise: luhn

Code

module luhn;

import std.range;
import std.algorithm.iteration;
import std.string;

bool valid(string input)
{
    if (input.indexOfNeither("0123456789 ") != -1)
    {
        return false;
    }

    auto digits = input.filter!(c => c != ' ')
        .map!(c => c - '0')
        .array;

    if (digits.length < 2)
    {
        return false;
    }

    auto odd = digits.retro.stride(2);
    auto even = digits.retro.dropOne.stride(2).map!(n => n * 2 - ((n >= 5) ? 9 : 0));

    return (odd.sum + even.sum) % 10 == 0;
}

unittest
{
    // Single digit strings can not be valid
    assert(!valid("1"));

    // A single zero is invalid
    assert(!valid("0"));

    // A simple valid SIN that remains valid if reversed
    assert(valid("059"));

    // A simple valid SIN that becomes invalid if reversed
    assert(valid("59"));

    // A valid Canadian SIN
    assert(valid("055 444 285"));

    // Invalid Canadian SIN
    assert(!valid("055 444 286"));

    // Invalid credit card
    assert(!valid("8273 1232 7352 0569"));

    // Valid number with an even number of digits
    assert(valid("095 245 88"));

    // Valid number with an odd number of spaces
    assert(valid("234 567 891 234"));

    // Valid strings with a non-digit added at the end become invalid
    assert(!valid("059a"));

    // Valid strings with punctuation included become invalid
    assert(!valid("055-444-285"));

    // Valid strings with symbols included become invalid
    assert(!valid("055# 444$ 285"));

    // Single zero with space is invalid
    assert(!valid(" 0"));

    // More than a single zero is valid
    assert(valid("0000 0"));

    // Input digit 9 is correctly converted to output digit 9
    assert(valid("091"));

    /*
    Convert non-digits to their ascii values and then offset them by 48
    sometimes accidentally declare an invalid string to be valid.  This test is
    designed to avoid that solution.
    */

    // Using ascii value for non-doubled non-digit isn't allowed
    assert(!valid("055b 444 285"));

    // Using ascii value for doubled non-digit isn't allowed
    assert(!valid(":9"));
}

Tags:

construct:add
construct:assert
construct:auto
construct:boolean
construct:comment
construct:if
construct:implicit-conversion
construct:import
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:length
construct:map
construct:module
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:ternary
construct:test
construct:unittest
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
ErikSchierboom commented 1 year ago

This is an automated comment

Hello :wave: Next week we're going to start using the tagging work people are doing on these. If you've already completed the work, thank you! If you've not, but intend to this week, that's great! If you're not going to get round to doing it, and you've not yet posted a comment letting us know, could you please do so, so that we can find other people to do it. Thanks!