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 #276

Closed ErikSchierboom closed 10 months ago

ErikSchierboom 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

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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:

ErikSchierboom 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: