Skip to content

Commit

Permalink
satellite: add instructions (#443)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom authored Sep 15, 2024
1 parent 20c3bdc commit d6e2316
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 73 deletions.
8 changes: 8 additions & 0 deletions exercises/practice/satellite/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hints

## General

- A tree is a recursive data structure that has two cases:
- `nil`: a leaf node, which doesn't have a value
- `node(Left, Value, Right)`: a regular node, which has a value and a left and right node
- For both pre-order and in-order traversal, you'll need to define rules that handle the two cases for trees
23 changes: 14 additions & 9 deletions exercises/practice/satellite/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
# Instructions append

Try using Prolog DCGs. **But watch out for left recursion!**
You should implement this exercise using a definite clause grammar (DGC).
DCGs can be used not only to parse sequences (lists), but also to generate, complete and check those sequences.

For extra bonus points:
## Library

Make tree_traversals also work in "reverse," i.e. one of the traversal
lists is a variable, for example:
The stub implementation files includes the following line:

```prolog
:- use_module(library(dcg/basics)).
```
?- tree_traversals(node(nil,5,node(nil,9,nil)),Pre,[5,9])
Pre = [5, 9]
```

and also in-order traversal as variable and pre-order as a list.
This ensures that the `dcg/basics` library can be used, which should be enough to solve this exercise.

## Resources

The following are useful resources to get started with DCG's in Prolog:

This should be simple using DCGs.
- [metalevel.at](https://www.metalevel.at/prolog/dcg)
- [wikipedia](https://en.wikipedia.org/wiki/Definite_clause_grammar)
- [swipl](https://www.swi-prolog.org/pldoc/man?section=DCG)
36 changes: 8 additions & 28 deletions exercises/practice/satellite/.meta/satellite.example.pl
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
% Reference Implementation
%
% Rebuilding Binary Trees from Traversals (medium)
% Inorder and Preorder
%
:- use_module(library(dcg/basics)).

/* Default content can be this stub.
tree_traversals(Tree, Preorder, Inorder) :- fail.
*/
preorder(nil) --> [].
preorder(node(L, V, R)) --> [V], preorder(L), preorder(R).

preorder(nil) --> [].
preorder(node(L,V,R)) --> [V],preorder(L),preorder(R).

% Left recursive definitions can cause infinite loops.
% E.g. in a naive implementation
% ?- phrase(inorder(T),[a,b,c]).
% unifies once and then loops.
% (remove extra arguments from inorder//3 below to see this in action).
% Notice the second rule directly consumes one item from the traversal.
% The loop can be broken by applying the second rule only if there are
% still items in the traversal. To encode this requirement the traversal's
% length can be used to count the number of times the 2nd rule has been
% applied. Each time the 2nd rule is applied an _arbitrary_ item is dropped
% ([_|B1]) corresponding to the one item directly consumed by the rule. When
% the traversal has been completely consumed the extra argument is [] which
% does not match [_|B1] so the recursion is "bounded".
inorder(nil, Ls,Ls) --> [].
inorder(node(L,V,R), [_|B1],B3) --> inorder(L, B1,B2),[V],inorder(R, B2,B3).
inorder(nil) --> [].
inorder(node(L, V, R)) --> inorder(L), [V], inorder(R).

tree_traversals(Tree, Preorder, Inorder) :-
phrase(preorder(Tree),Preorder),
phrase(inorder(Tree,Inorder,_),Inorder).
phrase(preorder(Tree), Preorder),
phrase(inorder(Tree), Inorder),
!.
2 changes: 2 additions & 0 deletions exercises/practice/satellite/satellite.pl
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
:- use_module(library(dcg/basics)).

tree_traversals(Tree, Preorder, Inorder).
103 changes: 67 additions & 36 deletions exercises/practice/satellite/satellite_tests.plt
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,72 @@ pending :-

:- begin_tests(tree_traversals).

test(build_empty_tree, [condition(true), all( T = [ nil ] )]) :-
tree_traversals(T,[],[]).

test(build_one_item_tree, [condition(pending), all( T = [ node(nil,a,nil) ] )]) :-
tree_traversals(T,[a],[a]).

test(aix_france, [condition(pending), all( Tree = [
node(node(nil, i, nil), a, node(node(nil, f, nil), x, node(nil, r, nil)))
])]) :-
tree_traversals(Tree, [a,i,x,f,r], [i,a,f,x,r]).

test(diff_length_traversal, [condition(pending), fail]) :-
tree_traversals(_,[a,b],[b,a,r]).

test(diff_node_values_same_length, [condition(pending), fail]) :-
tree_traversals(_,[x,y,z],[a,b,c]).

% Bonus (find multiple trees)

test(with_repetitions, [condition(pending), set( Tree = [
node(node(node(node(node(nil, o, nil), l, nil), o, nil), r, nil), p, node(nil, g, nil))
, node(node(node(nil, o, node(nil, l, node(nil, o, nil))), r, nil), p, node(nil, g, nil))
])]) :-
tree_traversals(
Tree
, [p,r,o,l,o,g]
, [o,l,o,r,p,g]
).

% basic tests for extra points (unifying in opposite direction)

test(find_Inorder_from_empty, condition(pending)) :-
tree_traversals(nil,[],I), I = [].

test(find_Preorder_from_empty, condition(pending)) :-
tree_traversals(nil,P,[]), P = [].
test(inorder_from_empty_tree, condition(true)) :-
Tree = nil,
tree_traversals(Tree, Inorder, _),
Inorder == [].

test(inorder_from_single_node_tree, condition(pending)) :-
Tree = node(nil, a, nil),
tree_traversals(Tree, Inorder, _),
Inorder == [a].

test(inorder_from_tree_with_two_leafs, condition(pending)) :-
Tree = node(node(nil, b, nil), a, node(nil, c, nil)),
tree_traversals(Tree, Inorder, _),
Inorder == [a, b, c].

test(inorder_from_nested_tree, condition(pending)) :-
Tree = node(node(nil, b, nil), a, node(node(node(nil, e, nil), d, nil), c, nil)),
tree_traversals(Tree, Inorder, _),
Inorder == [a, b, c, d, e].

test(preorder_from_empty_tree, condition(pending)) :-
Tree = nil,
tree_traversals(Tree, _, Preorder),
Preorder == [].

test(preorder_from_single_node_tree, condition(pending)) :-
Tree = node(nil, a, nil),
tree_traversals(Tree, _, Preorder),
Preorder == [a].

test(preorder_from_tree_with_two_leafs, condition(pending)) :-
Tree = node(node(nil, b, nil), a, node(nil, c, nil)),
tree_traversals(Tree, _, Preorder),
Preorder == [b, a, c].

test(inorder_from_nested_tree, condition(pending)) :-
Tree = node(node(nil, b, nil), a, node(node(node(nil, e, nil), d, nil), c, nil)),
tree_traversals(Tree, _, Preorder),
Preorder == [b, a, e, d, c].

test(tree_from_empty_inorder_and_preorder_paths, condition(pending)) :-
Inorder = [],
Preorder = [],
tree_traversals(Tree, Inorder, Preorder),
Tree == nil.

test(tree_from_inorder_and_preorder_paths_with_one_element, condition(pending)) :-
Inorder = [a],
Preorder = [a],
tree_traversals(Tree, Inorder, Preorder),
Tree == node(nil, a, nil).

test(tree_from_inorder_and_preorder_paths_for_nested_tree, condition(pending)) :-
Inorder = [a, b, c, d, e],
Preorder = [b, a, e, d, c],
tree_traversals(Tree, Inorder, Preorder),
Tree == node(node(nil, b, nil), a, node(node(node(nil, e, nil), d, nil), c, nil)).

test(cannot_create_tree_from_inorder_and_preorder_paths_with_different_lengths, [condition(pending), fail]) :-
Inorder = [a, b, c],
Preorder = [b, a],
tree_traversals(_, Inorder, Preorder).

test(cannot_create_tree_from_inorder_and_preorder_paths_with_different_elements, [condition(pending), fail]) :-
Inorder = [a, b, c],
Preorder = [e, d, f],
tree_traversals(_, Inorder, Preorder).

:- end_tests(tree_traversals).

0 comments on commit d6e2316

Please sign in to comment.