Skip to content
This repository has been archived by the owner on May 30, 2023. It is now read-only.

Commit

Permalink
Add pov exercise (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom authored May 5, 2023
1 parent d307a66 commit cc4dda8
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 0 deletions.
11 changes: 11 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,17 @@
"difficulty": 5,
"topics": null
},
{
"slug": "pov",
"name": "Pov",
"uuid": "50b63a14-c022-4f1f-8cdc-6383eef657d3",
"practices": [],
"prerequisites": [
"strings",
"vectors"
],
"difficulty": 10
},
{
"slug": "meetup",
"name": "Meetup",
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)
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": [
"ErikSchierboom"
],
"files": {
"solution": [
"src/pov.cljs"
],
"test": [
"test/pov_test.cljs"
],
"example": [
".meta/src/example.cljs"
]
},
"blurb": "Reparent a graph on a selected node.",
"source": "Adaptation of exercise from 4clojure",
"source_url": "https://www.4clojure.com/"
}
42 changes: 42 additions & 0 deletions exercises/practice/pov/.meta/src/example.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(ns pov
(:require [clojure.zip :as zip]))

(defn- remove-child
"Remove a child from a node"
[parent child]
(filter #(not= child %) parent))

(defn- reparent
"Take a node, and make it the parent of its parent"
[loc]
(let [current (zip/node loc)
parent (zip/up loc)
belonged-to (when parent
(-> parent
(zip/edit remove-child current)
reparent
list))]
(vec (concat current belonged-to))))

(defn- find-node
"Find the node whose identifier matches the given value"
[v tree]
(loop [loc (-> tree zip/vector-zip zip/next)]
(cond (= v (zip/node loc)) (zip/up loc)
(zip/end? loc) nil
:else (recur (zip/next loc)))))

(defn of
"Find a node by its identifier and raise to the root"
[s tree]
(when-let [loc (find-node s tree)]
(reparent loc)))

(defn path-from-to
"Find a path from src node to dest node"
[src dest graph]
(when-let [loc (find-node dest (of src graph))]
(let [dest-node (zip/node loc)
path (concat (zip/path loc) [dest-node])]
(map first path))))

48 changes: 48 additions & 0 deletions exercises/practice/pov/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.

[1b3cd134-49ad-4a7d-8376-7087b7e70792]
description = "Results in the same tree if the input tree is a singleton"

[0778c745-0636-40de-9edd-25a8f40426f6]
description = "Can reroot a tree with a parent and one sibling"

[fdfdef0a-4472-4248-8bcf-19cf33f9c06e]
description = "Can reroot a tree with a parent and many siblings"

[cbcf52db-8667-43d8-a766-5d80cb41b4bb]
description = "Can reroot a tree with new root deeply nested in tree"

[e27fa4fa-648d-44cd-90af-d64a13d95e06]
description = "Moves children of the new root to same level as former parent"

[09236c7f-7c83-42cc-87a1-25afa60454a3]
description = "Can reroot a complex tree with cousins"

[f41d5eeb-8973-448f-a3b0-cc1e019a4193]
description = "Errors if target does not exist in a singleton tree"

[9dc0a8b3-df02-4267-9a41-693b6aff75e7]
description = "Errors if target does not exist in a large tree"

[02d1f1d9-428d-4395-b026-2db35ffa8f0a]
description = "Can find path to parent"

[d0002674-fcfb-4cdc-9efa-bfc54e3c31b5]
description = "Can find path to sibling"

[c9877cd1-0a69-40d4-b362-725763a5c38f]
description = "Can find path to cousin"

[9fb17a82-2c14-4261-baa3-2f3f234ffa03]
description = "Can find path not involving root"

[5124ed49-7845-46ad-bc32-97d5ac7451b2]
description = "Can find path from nodes other than x"

[f52a183c-25cc-4c87-9fc9-0e7f81a5725c]
description = "Errors if destination does not exist"

[f4fe18b9-b4a2-4bd5-a694-e179155c2149]
description = "Errors if source does not exist"
10 changes: 10 additions & 0 deletions exercises/practice/pov/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/clojurescript {:mvn/version "1.10.773"}}

:aliases
{:test
{:extra-paths ["test"]
:extra-deps
{olical/cljs-test-runner {:mvn/version "3.8.0"}}
:main-opts ["-m" "cljs-test-runner.main"]}}}
9 changes: 9 additions & 0 deletions exercises/practice/pov/src/pov.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(ns pov)

(defn of [] ;; <- arglist goes here
;; your code goes here
)

(defn path-from-to [] ;; <- arglist goes here
;; your code goes here
)
145 changes: 145 additions & 0 deletions exercises/practice/pov/test/pov_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
(ns pov-test
(:require [clojure.test :refer [deftest is]]
pov))

;;; Inputs.

(def singleton [:x])

(def simple-tree [:parent [:sibling] [:x]])

(def large-flat
[:parent [:sib-a]
[:sib-b]
[:x]
[:sib-c]
[:sib-d]])

(def deeply-nested
[:level-0
[:level-1
[:level-2
[:level-3
[:level-4
[:x]]]]]])

(def cousins
[:grand-parent
[:parent
[:sib-1]
[:x]
[:sib-2]]
[:uncle
[:cousin-1]
[:cousin-2]]])

(def target-with-children
[:grand-parent
[:parent
[:x
[:child-1]
[:child-2]]
[:sibling
[:nephew]
[:niece]]]
[:aunt
[:cousin-1
[:2nd-cousin-1]
[:2nd-cousin-2]]
[:cousin-2
[:2nd-cousin-3]
[:2nd-cousin-4]]]])

;;; Expected results.

(def simple-pulled [:x [:parent [:sibling]]])

(def flat-pulled
[:x [:parent
[:sib-a]
[:sib-b]
[:sib-c]
[:sib-d]]])

(def nested-pulled
[:x
[:level-4
[:level-3
[:level-2
[:level-1
[:level-0]]]]]])

(def cousins-pulled
[:x
[:parent
[:sib-1]
[:sib-2]
[:grand-parent
[:uncle
[:cousin-1]
[:cousin-2]]]]])

(def with-kids-pulled
[:x
[:child-1]
[:child-2]
[:parent
[:sibling
[:nephew]
[:niece]]
[:grand-parent
[:aunt
[:cousin-1
[:2nd-cousin-1]
[:2nd-cousin-2]]
[:cousin-2
[:2nd-cousin-3]
[:2nd-cousin-4]]]]]])

(deftest singletons
(is (= singleton
(pov/of :x singleton))))

(deftest simple-trees
(is (= simple-pulled
(pov/of :x simple-tree))))

(deftest nested-trees
(is (= nested-pulled
(pov/of :x deeply-nested))))

(deftest extract-node-from-siblings
(is (= flat-pulled
(pov/of :x large-flat))))

(deftest moderate-trees
(is (= cousins-pulled
(pov/of :x cousins))))

(deftest complex-trees
(is (= with-kids-pulled
(pov/of :x target-with-children))))

(deftest not-found-cannot-reparent
(is (nil? (pov/of :not-found! target-with-children))))

(deftest not-found-input-empty
(is (nil? (pov/of :x []))))

(deftest not-found-input-nil
(is (nil? (pov/of :x nil))))

(deftest path-from-target-to-parent
(is (= [:x :parent]
(pov/path-from-to :x :parent simple-tree))))

(deftest path-from-target-to-sibling
(is (= [:x :parent :sib-c]
(pov/path-from-to :x :sib-c large-flat))))

(deftest path-from-x-to-2nd-cousin-1
(is (= [:x :parent :grand-parent :aunt :cousin-1 :2nd-cousin-1]
(pov/path-from-to :x :2nd-cousin-1 target-with-children))))

(deftest no-path
(is (nil? (pov/path-from-to :x :not-there! cousins))))

0 comments on commit cc4dda8

Please sign in to comment.