exercism / prolog

Exercism exercises in Prolog.
https://exercism.org/tracks/prolog
MIT License
29 stars 37 forks source link

Building a training set of tags for prolog #279

Closed iHiD closed 10 months ago

iHiD commented 10 months 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

iHiD commented 10 months ago

Exercise: hello-world

Code

% Please visit http://exercism.io/languages/prolog/installation
% for instructions on setting up prolog.
% Visit http://exercism.io/languages/prolog/tests
% for help running the tests for prolog exercises.

% Replace the goal below with
% your implementation.

hello_world('Hello World!').

hello_world(name, string_concat("Hello ", name, "!")).

Tags:

No tags generated

iHiD commented 10 months ago

Exercise: hello-world

Code

% Please visit http://exercism.io/languages/prolog/installing
% for instructions on setting up prolog.
% Visit http://exercism.io/languages/prolog/tests
% for help running the tests for prolog exercises.

% Replace the goal below with
% your implementation.

hello_world(Out) :-
    hello_world('World', Out).

hello_world(Name, Out) :-
    atom_concat('Hello ', Name, Str1),
    atom_concat(Str1, '!', Out).

Tags:

No tags generated

iHiD commented 10 months ago

Exercise: binary

Code

%Convert a binary number, represented as a string (e.g. '101010'), to its decimal equivalent using first principles.

binary(String, Dec):-
    string_length(String,Len), % gets the length of a String
    string_chars(String,StringList), % converts String to Characters in a list
    calculateValues(StringList,Len,0,Dec).

calculateValues([Head|Tail],Position,Acc,Result):-
    is(Pow,**(2,-(Position,1))), % from left to right calcs power of 2
    atom_number(Head,Nr), % converts character to number
    is(Value,*(Nr,Pow)), % multiply the Power by binary Number 0 or 1
    is(NewAcc,+(Acc,Value)), % accumulates multiplied values
    is(NewPosition,-(Position,1)), % decreases Position in a binary chain
    calculateValues(Tail,NewPosition,NewAcc,Result). % recursive call

% base case
calculateValues([],0,Result,Result).

%assertion tests
:-binary('101010', 42).
:-binary('010101', 21).

Tags:

construct:atom
construct:comment
construct:invocation
construct:is
construct:list
construct:multiply
construct:number
construct:parameter
construct:rule
construct:string
construct:term
construct:underscore
paradigm:functional
paradigm:logical
technique:recursion
iHiD commented 10 months ago

Exercise: triangle

Code

triangle(S1, S2, S3, "equilateral") :- valid_triangle(S1, S2, S3), S1 =:= S2, S2 =:= S3, !.
triangle(S1, S2, S3, "isosceles") :- valid_triangle(S1, S2, S3), S1 =:= S2, !.
triangle(S1, S2, S3, "isosceles") :- valid_triangle(S1, S2, S3), S2 =:= S3, !.
triangle(S1, S2, S3, "isosceles") :- valid_triangle(S1, S2, S3), S1 =:= S3, !.
triangle(S1, S2, S3, "scalene") :- valid_triangle(S1, S2, S3).

valid_triangle(S1, S2, S3) :- S1 > 0, S2 > 0, S3 > 0, S1 < S2+S3, S2 < S1+S3, S3 < S1+S2.

Tags:

construct:add
construct:assignment
construct:comma
construct:cut
construct:equals
construct:floating-point-number
construct:greater-than
construct:integer
construct:less-than
construct:number
construct:rule
construct:term
construct:variable
paradigm:declarative
paradigm:functional
paradigm:imperative
paradigm:logic
technique:control-flow
iHiD commented 10 months ago

Exercise: space-age

Code

% planet, year.
yearsOnE("Earth", 1).
yearsOnE("Mercury", 0.2408467).
yearsOnE("Venus", 0.61519726).
yearsOnE("Mars", 1.8808158).
yearsOnE("Jupiter", 11.862615).
yearsOnE("Saturn", 29.447498).
yearsOnE("Uranus", 84.016846).
yearsOnE("Neptune", 164.79132).

seconds("Earth", 31557600).

% +planet name, +age in sec, -years old
space_age(Planet, AgeSec, Years):-
    yearsOnE("Earth", Earth), % gets year of Earth that is 1
    yearsOnE(Planet, X), % proportion of years on planet X

    % gets seconds of 1 Earth year that is the number above
    seconds("Earth", EarthSeconds),

    is(ProportionYear,/(X,Earth)), % planet X year divided by 1
    is(ProportionSec,*(ProportionYear,EarthSeconds)), % then mult. by Earth sec
    is(TempYears,/(AgeSec,ProportionSec)), % Age in sec / by Proportion sec

    % test result is multiplied by 100 for some reason so...
    is(Years, round(*(TempYears,100))). % this result is rounded to integer

% assertion tests:
:- space_age("Earth",1000000000,3169).
:- space_age("Mercury", 2134835688, 28088).
:- space_age("Venus", 189839836, 978).
:- space_age("Mars", 2329871239, 3925).
:- space_age("Jupiter", 901876382, 241).
:- space_age("Saturn", 3000000000, 323).
:- space_age("Uranus", 3210123456, 121).
:- space_age("Neptune", 8210123456, 158).

Tags:

construct:comment
construct:division
construct:fact
construct:floating-point-number
construct:invocation
construct:is
construct:multiply
construct:number
construct:rule
construct:term
construct:variable
paradigm:functional
paradigm:logical
technique:math
iHiD commented 10 months ago

Exercise: isogram

Code

isogram(_,[]) :- !.
isogram(S, Str) :- 
    (\+member(S,[32,45])) -> (\+member(S,Str)),
    Str = [H|T],
    isogram(H,T).

isogram("") :- !.
isogram(Str) :- 
    string_lower(Str, Low),
    string_codes(Low, Chars),
    Chars = [H|T],
    isogram(H,T).

Tags:

construct:assignment
construct:char
construct:cut
construct:disjunction
construct:implicit-conversion
construct:integer
construct:invocation
construct:isogram
construct:list
construct:member
construct:number
construct:parameter
construct:pattern-matching
construct:prolog
construct:string
construct:tail-recursive
construct:variable
paradigm:functional
paradigm:logical
technique:boolean-logic
technique:recursion
iHiD commented 10 months ago

Exercise: hamming

Code

hamming_distance(Str1, Str2, Dist) :-
  string_length(Str1, Length),
  string_length(Str2, Length),
  atom_chars(Str1, Chars1),
  atom_chars(Str2, Chars2),
  differing_chars(Chars1, Chars2, Dist).

differing_chars([], [], 0).
differing_chars([H | T1], [H | T2], Dist) :- differing_chars(T1, T2, Dist).
differing_chars([_ | T1], [_ | T2], Dist) :- Dist2 is Dist - 1, differing_chars(T1, T2, Dist2).

Tags:

construct:atom
construct:char
construct:is
construct:list
construct:number
construct:parameter
construct:rule
construct:string
construct:subtract
construct:variable
paradigm:functional
paradigm:logic
technique:recursion
iHiD commented 10 months ago

Exercise: hamming

Code

hamming_distance(Str1, Str2, Dist):-
    string_chars(Str1, CharStr1),
    string_chars(Str2, CharStr2),
    length(CharStr1,Len1),
    length(CharStr2,Len2),
    (=(Len1, Len2) -> difference(CharStr1, CharStr2, 0, Dist);
     Dist = 'Enter new strings that match in length.').

difference([],[],Dist,Dist).
difference([H1|T1],[H2|T2],Count,Dist):-
    % if occurence of char does not match, increase counter
    (=(H1,H2) -> is(NewCount, Count); is(NewCount, +(Count, 1))),

     difference(T1,T2,NewCount,Dist).

% assertion tests
:- hamming_distance('GAGCCTACTAACGGGAT',
                    'CATCGTAATGACGGCCT',7).
%                    ^ ^ ^  ^ ^    ^^
:- hamming_distance("", "", 0).
:- hamming_distance("A", "A", 0).
:- hamming_distance("AGG", "AGA", 1).
:- hamming_distance("GGACGGATTCTG", "AGGACGGATTCT", 9).
:- hamming_distance("ATA", "AGTG", _). % fails

Tags:

construct:add
construct:assignment
construct:char
construct:comment
construct:directive
construct:disjunction
construct:if-then-else
construct:invocation
construct:length
construct:number
construct:parameter
construct:rule
construct:string
construct:term
construct:underscore
paradigm:declarative
paradigm:functional
paradigm:logical
technique:boolean-logic
technique:recursion
iHiD commented 10 months ago

Exercise: hamming

Code

% insert cut because prolog wants to backtrack..?
hamming_distance([], [], Dist, Dist) :- !.
hamming_distance([], _, _, _) :- fail, !.
hamming_distance(_,[],_, _) :- fail, !.
% use a /4 version to house the acc
hamming_distance([H|T1],[H|T2],Dist,Acc) :-
    hamming_distance(T1,T2,Dist,Acc).
hamming_distance([_|T1],[_|T2],Dist,Acc) :-
    NewAcc is Acc + 1,
    hamming_distance(T1,T2,Dist,NewAcc).
hamming_distance(Str1, Str2, Dist) :- 
    hamming_distance(Str1, Str2, Dist, 0).

Tags:

construct:add
construct:comment
construct:cut
construct:fail
construct:is
construct:numeric-type
construct:rule
construct:variable
iHiD commented 10 months ago

Exercise: anagram

Code

anagram(Word, Options, Matching) :-
  string_lower(Word, LCWord),
  string_chars(LCWord, WordChars),
  msort(WordChars, Sorted),
  include(is_anagram(Sorted), Options, Matching).

is_anagram(SortedWC,Word) :-
  string_lower(Word, LCWord),
  string_chars(LCWord, WordChars),
  msort(WordChars,SortedWC).

Tags:

construct:char
construct:include
construct:invocation
construct:lambda
construct:list
construct:parameter
construct:string
construct:term
construct:underscore_variable
construct:user_defined_function
paradigm:functional
paradigm:logical
paradigm:declarative
technique:higher_order
iHiD commented 10 months ago

Exercise: anagram

Code

anagram(Word, Options, Matching) :-
    include(is_anagram(Word), Options, Matching).

is_anagram(Word, Option) :-
    atom_codes(Word, W),
    atom_codes(Option, O),
    sort(W, X),
    sort(O, X).

Tags:

construct:atom
construct:codepoint
construct:fact
construct:invocation
construct:parameter
construct:sort
construct:term
construct:variable
paradigm:functional
paradigm:logic
technique:sorting
iHiD commented 10 months ago

Exercise: rna-transcription

Code

transcript('G', 'C').
transcript('C', 'G').
transcript('T', 'A').
transcript('A', 'U').

rna_transcription(Rna, Dna) :-
    string_chars(Rna, R),
    maplist(transcript, D, R),
    string_chars(Dna, D).

Tags:

No tags generated

iHiD commented 10 months ago

Exercise: pascals-triangle

Code

pascal(X,List):-genpascal(X,X,List).

genpascal(_,0,[]).
genpascal(1,1,[1]).
genpascal(X,X,[[1]|T]):-X1 is X-1,genpascal(X,X1,[[1]|T]).
genpascal(X,1,[H1,H2]):-X>1,pascalnewrow(H1,H2).
genpascal(X1,X2,[H1,H2|T]):-X2>0,pascalnewrow(H1,H2),X3 is X2-1,genpascal(X1,X3,[H2|T]).

pascalrow(1,[[1]]).
pascalrow(X,FullX):-X>1,X1 is X-1,pascalrow(X1,FullX1),pascalnewrow(FullX1,FullX).

pascalnewrow([H|T],[H|T1]):-pascalnewrowl([H|T],T1).

pascalnewrowl([H|[]],[H]).
pascalnewrowl([H1,H2|T1],[H12|T2]):-H12 is H1+H2,pascalnewrowl([H2|T1],T2).

Tags:

construct:add
construct:assignment
construct:is
construct:comma
construct:cut
construct:constructor
construct:divide
construct:float
construct:floating-point-number
construct:head
construct:if
construct:integer
construct:invocation
construct:is
construct:list
construct:number
construct:parameter
construct:rule
construct:tail
construct:variable
paradigm:declarative
paradigm:functional
paradigm:imperative
paradigm:logic
technique:divide-and-conquer
technique:recursion
iHiD commented 10 months ago

Exercise: pascals-triangle

Code

% base case
pascal(0, Triangle, Triangle):-!.
pascal(N, Result, Rows) :-
    % peel off the most recent row
    [LastRow|Rest] = Rows,
    % generate the next row
    pascal_step(LastRow, NextRow),
    M is N - 1,
    % append it to the accumulator
    NewRows = [NextRow|[LastRow|Rest]],
    % recurse. Cut to prevent backtracking
    pascal(M, Result, NewRows),!.
% start the iteration with one row [1]
pascal(N, Result) :-
    pascal(N, Result, [[1]]).

% better way of thinking of row generation is
% offset addition
% 1 3 3 1 0
% 0 1 3 3 1

% convenience predicate for adding rows
% base case
add_rows([], _, Result, Result).
% recurse through list, adding heads
% no need to worry about order of append
% since symmetric
add_rows([H1|T1],[H2|T2],Result,Acc) :-
    A1 is H1 + H2,
    add_rows(T1,T2,Result,[A1|Acc]).
% then pascal_step is easier to define
pascal_step(LastRow, NextRow):-
    % append 0 to right
    append(LastRow, [0], LastRowA),
    % append 0 to left
    append([0], LastRow, LastRowB),
    add_rows(LastRowA, LastRowB, NextRow,[]).

Tags:

construct:add
construct:append
construct:assignment
construct:atom
construct:brackets
construct:call
construct:char
construct:comment
construct:constructor
construct:cut
construct:divide
construct:fact
construct:head
construct:integer
construct:is
construct:list
construct:number
construct:parameter
construct:rule
construct:tail
construct:variable
paradigm:declarative
paradigm:functional
paradigm:imperative
paradigm:logical
technique:cutting
technique:divide-and-conquer
technique:recursion
iHiD commented 10 months ago

Exercise: dominoes

Code

% EMPTY LIST
can_chain([]):- !.

% SINGLETON INPUT, SAME NUMBERS ONLY
can_chain([(N, N)]):- !.

% LIST OF MORE THAN ONE ELEMENTS
can_chain([(A0, A1)|BZ]):-
    % COMPARE FIRST PIECE WITH EACH OF THE REST
    compare_pieces((A0, A1), [], BZ, A0);
    % RE-COMPARE, FLIP A
    compare_pieces((A1, A0), [], BZ, A1).

% COMPARING PIECES
%   INPUT: CURRENT PIECE, PIECES BEFORE IT, PIECES AFTER IT, VERY FIRST NUMBER
compare_pieces((A0, A1), BefPieces, [(B0, B1)|CZ], FirstN):-

    % (NUMBER A1 == NUMBER B0)
    A1 = B0 ->

        % COMPARE PIECE B WITH REMAINING PIECES
        %   APPEND BEFORE PIECES WITH REMAINING PIECES,
        %   AS THEY STILL HAVE TO BE USED TO MAKE A CHAIN.
        (append(BefPieces, CZ, NewAft),
        compare_pieces((B0, B1), [], NewAft, FirstN));

    % RE-COMPARISON, FLIP B
    A1 = B1 ->

        % COMPARE PIECE B WITH REMAINING PIECES
        %   APPEND BEFORE PIECES WITH REMAINING PIECES,
        %   AS THEY STILL HAVE TO BE USED TO MAKE A CHAIN.
        (append(BefPieces, CZ, NewAft),
        compare_pieces((B1, B0), [], NewAft, FirstN));

    % ELSE, COMPARE PIECE A WITH C
    (append(BefPieces, [(B0, B1)], NewBef),
    compare_pieces((A0, A1), NewBef, CZ, FirstN));

    % IF THERE ARE NO COMPATABLE PIECES YET, SEE IF THE REMAINING PIECES CAN BE CHAINED
    %   CHECK WHETHER CURRENT PIECE CAN END THIS CHAIN
    A1 = FirstN ->

    %   APPEND BEFORE AND AFTER NTO LEFTOVER PIECES
        (append(BefPieces, CZ, LeftoverPieces),

    %   CHECK WHETHER THE LEFTOVERS CAN FORM A CHAIN
        can_chain(LeftoverPieces));

    % IF ALL ELSE FAILS
    %   APPEND PIECE A TO THE BEFORE PIECES
    append(BefPieces, [(A0, A1)], AltNewBef),

    %   RE-ITERATE WITH PIECE B AS THE CURRENT PIECE
    compare_pieces((B0, B1), AltNewBef, CZ, FirstN).

% ONE PIECE REMAIN
%   LAST VALUE SHOULD EQUALS TO VERY FIRST VALUE
compare_pieces(_, [], [(_, F)], F).
%   RE-COMPARE WITH FINAL PIECE FLIPPED
compare_pieces(_, [], [(F, _)], F).

Tags:

construct:assignment
construct:atom
construct:boolean
construct:char
construct:comment
construct:cut
construct:disjunction
construct:if-then-else
construct:implicit-conversion
construct:list
construct:logical-variable
construct:parenthesized-expression
construct:rule
construct:term
construct:underscore-variable
construct:variable
paradigm:declarative
paradigm:functional
paradigm:logical
technique:backtracking
iHiD commented 10 months ago

Exercise: dominoes

Code

can_chain([]).

can_chain([(A, A)]).

can_chain([(A, B) | C]) :-
  writeln([(A, B) | C]),
  (select((D, A), C, E); select((A, D), C, E)),
  (select((B, F), E, G); select((F, B), E, G)),
  can_chain([(D, F) | G]).

Tags:

construct:char
construct:comma
construct:disjunction
construct:head-tail
construct:list
construct:open-closed-range
construct:parameter
construct:parentheses
construct:rule
construct:semicolon
construct:tuple
construct:write
paradigm:functional
paradigm:logic
technique:recursion
uses:write-string
iHiD commented 10 months ago

Exercise: satellite

Code

tree_traversals(Tree, Preorder, Inorder) :-
    preorder(Tree, Preorder, []),
    inorder(Tree, Inorder, []).

preorder(nil) --> [].
preorder(node(Left, Name, Right)) -->
    [Name], preorder(Left), preorder(Right).

inorder(nil) --> [].
inorder(node(Left, Name, Right)) -->
    inorder(Left), [Name], inorder(Right).

Tags:

construct:char
construct:comma
construct:fact
construct:invocation
construct:list
construct:node
construct:parameter
construct:rule
construct:term
construct:variable
paradigm:functional
paradigm:logic
technique:recursion
iHiD commented 10 months ago

Exercise: satellite

Code

inorder(nil) --> [].
inorder(node(Left,Name,Right))  --> inorder(Left), [Name], inorder(Right).

preorder(nil) --> [].
preorder(node(Left,Name,Right)) --> [Name], preorder(Left), preorder(Right).

tree_traversals(Tree, Preorder, Inorder) :- 
    phrase(preorder(Tree), Preorder),
    phrase(inorder(Tree), Inorder).

Tags:

construct:comma
construct:fact
construct:invocation
construct:list
construct:node
construct:parameter
construct:rule
construct:term
construct:underscore
construct:variable
paradigm:functional
paradigm:logic
technique:recursion
iHiD commented 10 months ago

Exercise: wordy

Code

:- use_module(library(clpfd)).

try_intify([], []) :- !.
try_intify([Hd|Tl], [Num|Rest]) :-
    atom_number(Hd, Num),
    try_intify(Tl, Rest), !.
try_intify([Hd|Tl], [Hd|Rest]) :-
    try_intify(Tl, Rest), !.

evaluate([Num], Num) :- !.
evaluate([Lhs,'plus', Rhs|Rest], Result) :-
    catch(Tmp #= Lhs + Rhs, _, throw(error(syntax_error, "Not a number"))), !,
    evaluate([Tmp|Rest], Result), !.
evaluate([Lhs,'minus', Rhs|Rest], Result) :-
    catch(Tmp #= Lhs - Rhs, _, throw(error(syntax_error, "Not a number"))), !,
    evaluate([Tmp|Rest], Result), !.
evaluate([Lhs,'multiplied','by', Rhs|Rest], Result) :-
    catch(Tmp #= Lhs * Rhs, _, throw(error(syntax_error, "Not a number"))), !,
    evaluate([Tmp|Rest], Result), !.
evaluate([Lhs,'divided','by',Rhs|Rest], Result) :-
    catch(Tmp #= Lhs div Rhs, _, throw(error(syntax_error, "Not a number"))), !,
    evaluate([Tmp|Rest], Result), !.
evaluate([_, Other|_], _) :-
    atom(Other),
    Other \= 'plus',
    Other \= 'minus',
    Other \= 'divided',
    Other \= 'multiplied',
    throw(error(unknown_operation_error, Other)), !.
evaluate(L, _) :- throw(error(syntax_error, L)).

wordy(Question, Answer) :-
    (Question \= "What is?"; throw(error(syntax_error, "No question"))), !,
    (atom_concat(RStripped, '?', Question); throw(error(unknown_operation_error, Question))), !,
    (atom_concat('What is ', Stripped, RStripped); throw(error(unknown_operation_error, Question))), !,
    atomic_list_concat(Words, ' ', Stripped),
    try_intify(Words, IWords),
    evaluate(IWords, Answer).

Tags:

construct:add
construct:atom
construct:catch
construct:char
construct:clpfd
construct:cut
construct:div
construct:error
construct:floating-point-number
construct:integer
construct:invocation
construct:lambda
construct:list
construct:module
construct:multiply
construct:number
construct:or
construct:parameter
construct:prolog
construct:string
construct:subtract
construct:throw
construct:underscore
construct:use_module
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:logic
paradigm:object-oriented
technique:exceptions
technique:higher-order-functions
iHiD commented 10 months ago

Exercise: wordy

Code

:- use_module(library(dcg/basics)).

sentence(Result) --> beginning, " ", expression(Result), ending.
sentence(_) --> beginning, {throw(error(syntax_error, "I can't solve this."))}.
sentence(_) --> {throw(error(unknown_operation_error, "I don't answer those questions."))}.

beginning --> "What is". 
ending --> "?".

:- table expression/3.
expression(Result) --> expression(N), " plus ", integer(M), {Result is N + M}, !.
expression(Result) --> expression(N), " minus ", integer(M), {Result is N - M}, !.
expression(Result) --> expression(N), " multiplied by ", integer(M), {Result is N * M}, !.
expression(Result) --> expression(N), " divided by ", integer(M), {Result is N / M}, !.
expression(Result) --> integer(Result), !.
expression(_) --> 
    integer(_), " ", string(_), 
    {throw(error(unknown_operation_error, "I don't know that operation."))}.

wordy(Question, Result) :- 
    string_codes(Question, StringCodes),
    once(phrase(sentence(Result), StringCodes, [])).

Tags:

construct:assignment
construct:char
construct:comment
construct:cut
construct:dcg
construct:divide
construct:error
construct:fact
construct:invocation
construct:is
construct:library
construct:module
construct:multiply
construct:number
construct:operator
construct:parameter
construct:prolog
construct:rule
construct:string
construct:table
construct:throw
construct:underscore
construct:use_module
construct:variable
construct:visibility
paradigm:declarative
paradigm:functional
paradigm:imperative
paradigm:logical
technique:exceptions
technique:laziness
technique:recursion
iHiD commented 10 months ago

@ErikSchierboom Just checking this was actually done (not just a remanent of testing) as none of the comments look edited.