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

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:

None

ErikSchierboom commented 10 months ago
# Exercise: hello-world

## Code

```prolog
% 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

```prolog
%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
ErikSchierboom commented 10 months ago
# Exercise: triangle

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: space-age

## Code

```prolog
% 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
ErikSchierboom commented 10 months ago
# Exercise: isogram

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: hamming

## Code

```prolog
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). differingchars([ | 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
ErikSchierboom commented 10 months ago
# Exercise: hamming

## Code

```prolog
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). :- hammingdistance("ATA", "AGTG", ). % fails

```

## Tags:
- construct:add
ErikSchierboom commented 10 months ago
# Exercise: hamming

## Code

```prolog
% insert cut because prolog wants to backtrack..?

hamming_distance([], [], Dist, Dist) :- !. hammingdistance([], , , ) :- fail, !. hammingdistance(,[],, ) :- fail, !. % use a /4 version to house the acc hamming_distance([H|T1],[H|T2],Dist,Acc) :- hamming_distance(T1,T2,Dist,Acc). hammingdistance([|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
ErikSchierboom commented 10 months ago
# Exercise: anagram

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: anagram

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: rna-transcription

## Code

```prolog
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

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: pascals-triangle

## Code

```prolog
% 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 addrows([], , 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
ErikSchierboom commented 10 months ago
# Exercise: dominoes

## Code

```prolog
% 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 comparepieces(, [], [(_, F)], F). % RE-COMPARE WITH FINAL PIECE FLIPPED comparepieces(, [], [(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
ErikSchierboom commented 10 months ago
# Exercise: dominoes

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: satellite

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: satellite

## Code

```prolog
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
ErikSchierboom commented 10 months ago
# Exercise: wordy

## Code

```prolog
:- 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(syntaxerror, "Not a number"))), !, evaluate([Tmp|Rest], Result), !. evaluate([Lhs,'minus', Rhs|Rest], Result) :- catch(Tmp #= Lhs - Rhs, , throw(error(syntaxerror, "Not a number"))), !, evaluate([Tmp|Rest], Result), !. evaluate([Lhs,'multiplied','by', Rhs|Rest], Result) :- catch(Tmp #= Lhs * Rhs, , throw(error(syntaxerror, "Not a number"))), !, evaluate([Tmp|Rest], Result), !. evaluate([Lhs,'divided','by',Rhs|Rest], Result) :- catch(Tmp #= Lhs div Rhs, , throw(error(syntaxerror, "Not a number"))), !, evaluate([Tmp|Rest], Result), !. evaluate([, Other|], ) :- atom(Other), Other \= 'plus', Other \= 'minus', Other \= 'divided', Other \= 'multiplied', throw(error(unknown_operationerror, 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
ErikSchierboom commented 10 months ago
# Exercise: wordy

## Code

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

sentence(Result) --> beginning, " ", expression(Result), ending. sentence(_) --> beginning, {throw(error(syntaxerror, "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