Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

satellite: add instructions #443

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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).
Loading