Closed ErikSchierboom closed 10 months ago
% 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, "!")).
% 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).
%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).
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
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.
% 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).
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).
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).
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
% 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).
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).
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).
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).
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).
% 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,[]).
% 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).
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]).
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).
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).
:- 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).
:- 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, [])).
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:
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