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

Add pov exercise #97

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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 config.json
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,14 @@
"prerequisites": [],
"difficulty": 8
},
{
"slug": "pov",
"name": "POV",
"uuid": "6cbdabf6-4a22-43ec-b2e4-6611ea253237",
"practices": [],
"prerequisites": [],
"difficulty": 8
},
{
"slug": "rest-api",
"name": "REST API",
Expand Down
41 changes: 41 additions & 0 deletions exercises/practice/pov/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Instructions

Reparent a tree on a selected node.

A [tree][wiki-tree] is a special type of [graph][wiki-graph] where all nodes are connected but there are no cycles.
That means, there is exactly one path to get from one node to another for any pair of nodes.

This exercise is all about re-orientating a tree to see things from a different point of view.
For example family trees are usually presented from the ancestor's perspective:

```text
+------0------+
| | |
+-1-+ +-2-+ +-3-+
| | | | | |
4 5 6 7 8 9
```

But there is no inherent direction in a tree.
The same information can be presented from the perspective of any other node in the tree, by pulling it up to the root and dragging its relationships along with it.
So the same tree from 6's perspective would look like:

```text
6
|
+-----2-----+
| |
7 +-----0-----+
| |
+-1-+ +-3-+
| | | |
4 5 8 9
```

This lets us more simply describe the paths between two nodes.
So for example the path from 6-9 (which in the first tree goes up to the root and then down to a different leaf node) can be seen to follow the path 6-2-0-3-9.

This exercise involves taking an input tree and re-orientating it from the point of view of one of the nodes.

[wiki-graph]: https://en.wikipedia.org/wiki/Tree_(graph_theory)
[wiki-tree]: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)
67 changes: 67 additions & 0 deletions exercises/practice/pov/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
module [fromPov, pathTo]

Tree : [Empty, Node { label : Str, children : Set Tree }]

## Return all nodes on the path from the target node up to the root.
## If the node is not found, Err NotFound is returned.
nodesToRoot : Tree, Str -> Result (List Tree) [NotFound]
nodesToRoot = \tree, label ->
when tree is
Empty -> Err NotFound
Node node ->
if node.label == label then
Ok [Node node]
else
node.children
|> Set.walkUntil (Err NotFound) \state, child ->
when child |> nodesToRoot label is
Ok subPath -> subPath |> List.append (Node node) |> Ok |> Break
Err NotFound -> state |> Continue

## Drop the node with the given label, if it exists
drop : Tree, Str -> Tree
drop = \tree, label ->
when tree is
Empty -> Empty
Node node ->
if node.label == label then
Empty
else
filteredChildren =
node.children
|> Set.map \child -> drop child label
|> Set.dropIf \child -> child == Empty
Node { label: node.label, children: filteredChildren }

## Return the tree from the point of view of the node with the given label.
## Return Err NotFound if no such node is found.
fromPov : Tree, Str -> Result Tree [NotFound]
fromPov = \tree, from ->
rootPath : List Tree
rootPath = tree |> nodesToRoot? from
when rootPath |> List.first is
Err ListWasEmpty -> crash "Unreachable: nodesToRoot cannot return Ok []"
Ok Empty -> crash "Unreachable: target cannot be Empty"
Ok (Node target) ->
parent = rootPath |> List.get 1 |> Result.withDefault Empty
when parent is
Empty -> Ok tree # the target node is already the root of the tree
Node parentNode ->
treeWithoutTarget = tree |> drop from
fromParentPov = treeWithoutTarget |> fromPov? parentNode.label
newChildren = target.children |> Set.insert fromParentPov
Node { label: from, children: newChildren } |> Ok

## Find the list of nodes between the two given nodes and return their labels
## If either of these nodes don't exist, return Err NotFound
pathTo : Tree, Str, Str -> Result (List Str) [NotFound]
pathTo = \tree, from, to ->
fromTree = tree |> fromPov? from
fromTree
|> nodesToRoot? to
|> List.map \child ->
when child is
Empty -> crash "Unreachable: trees must never contain Empty children"
Node node -> node.label
|> Ok

19 changes: 19 additions & 0 deletions exercises/practice/pov/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"ageron"
],
"files": {
"solution": [
"Pov.roc"
],
"test": [
"pov-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"blurb": "Reparent a graph on a selected node.",
"source": "Adaptation of exercise from 4clojure",
"source_url": "https://www.4clojure.com/"
}
57 changes: 57 additions & 0 deletions exercises/practice/pov/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [fromPov, pathTo]

Tree : [Empty, Node { label : Str, children : Set Tree }]

{% macro roc_tree(tree) -%}
{%- set children = tree["children"] or [] -%}
{%- if children == [] -%}
Node { label: {{ tree["label"] | to_roc }}, children: Set.empty {} }
{%- else %}
Node {
label: {{ tree["label"] | to_roc }},
children: Set.fromList [
{%- for child in (tree["children"]) %}
{{ roc_tree(child) }},
{%- endfor %}
]
}
{%- endif %}
{%- endmacro %}


{% for supercase in cases %}
##
## {{ supercase["description"] }}
##

{% for case in supercase["cases"] -%}
# {{ case["description"] }}
{% if case["property"] == "fromPov" %}
expect
tree = {{ roc_tree(case["input"]["tree"]) | indent(8) }}
result = tree |> {{ case["property"] | to_camel }} {{ case["input"]["from"] | to_roc }}
{%- if case["expected"] %}
expected = {{ roc_tree(case["expected"]) | indent(8) }} |> Ok
result == expected
{%- else %}
result == Err NotFound
{%- endif %}
{% elif case["property"] == "pathTo" %}
expect
tree = {{ roc_tree(case["input"]["tree"]) | indent(8) }}
result = tree |> {{ case["property"] | to_camel }} {{ case["input"]["from"] | to_roc }} {{ case["input"]["to"] | to_roc }}
{%- if case["expected"] %}
expected = Ok {{ case["expected"] | to_roc }}
result == expected
{%- else %}
result == Err NotFound
{%- endif %}
{% else %}
# This test case is not supported yet: perhaps you can try implementing it?
{% endif %}
{% endfor %}
{% endfor %}
55 changes: 55 additions & 0 deletions exercises/practice/pov/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[1b3cd134-49ad-4a7d-8376-7087b7e70792]
description = "Reroot a tree so that its root is the specified node. -> Results in the same tree if the input tree is a singleton"

[0778c745-0636-40de-9edd-25a8f40426f6]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with a parent and one sibling"

[fdfdef0a-4472-4248-8bcf-19cf33f9c06e]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with a parent and many siblings"

[cbcf52db-8667-43d8-a766-5d80cb41b4bb]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with new root deeply nested in tree"

[e27fa4fa-648d-44cd-90af-d64a13d95e06]
description = "Reroot a tree so that its root is the specified node. -> Moves children of the new root to same level as former parent"

[09236c7f-7c83-42cc-87a1-25afa60454a3]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a complex tree with cousins"

[f41d5eeb-8973-448f-a3b0-cc1e019a4193]
description = "Reroot a tree so that its root is the specified node. -> Errors if target does not exist in a singleton tree"

[9dc0a8b3-df02-4267-9a41-693b6aff75e7]
description = "Reroot a tree so that its root is the specified node. -> Errors if target does not exist in a large tree"

[02d1f1d9-428d-4395-b026-2db35ffa8f0a]
description = "Given two nodes, find the path between them -> Can find path to parent"

[d0002674-fcfb-4cdc-9efa-bfc54e3c31b5]
description = "Given two nodes, find the path between them -> Can find path to sibling"

[c9877cd1-0a69-40d4-b362-725763a5c38f]
description = "Given two nodes, find the path between them -> Can find path to cousin"

[9fb17a82-2c14-4261-baa3-2f3f234ffa03]
description = "Given two nodes, find the path between them -> Can find path not involving root"

[5124ed49-7845-46ad-bc32-97d5ac7451b2]
description = "Given two nodes, find the path between them -> Can find path from nodes other than x"

[f52a183c-25cc-4c87-9fc9-0e7f81a5725c]
description = "Given two nodes, find the path between them -> Errors if destination does not exist"

[f4fe18b9-b4a2-4bd5-a694-e179155c2149]
description = "Given two nodes, find the path between them -> Errors if source does not exist"
20 changes: 20 additions & 0 deletions exercises/practice/pov/Pov.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module [fromPov, pathTo, isEquivalentTo]

Tree : [Empty, Node { label : Str, children : List Tree }]

## Are tree1 and tree2 identical, ignoring the order of the children?
isEquivalentTo : Tree, Tree -> Bool
isEquivalentTo = \tree1, tree2 ->
crash "Please implement the 'isEquivalentTo' function"

## Return the tree from the point of view of the node with the given label.
## Return Err NotFound if no such node is found.
fromPov : Tree, Str -> Result Tree [NotFound]
fromPov = \tree, from ->
crash "Please implement the 'fromPov' function"

## Return the labels of the nodes between the two given nodes
## If either of these nodes don't exist, return Err NotFound
pathTo : Tree, Str, Str -> Result (List Str) [NotFound]
pathTo = \tree, from, to ->
crash "Please implement the 'pathTo' function"
Loading
Loading