September 10, 2018
Just Juxt #27: Transitive Closure (4clojure #84)
The transitive closure of a binary relation R on a set X is the smallest relation on X that contains R and is transitive.
For example, if X is a set of airports and x R y means "there is a direct flight from airport x to airport y" (for x and y in X), then the transitive closure of R on X is the relation R+ such that x R+ y means "it is possible to fly from x to y in one or more flights". Informally, the transitive closure gives you the set of all places you can get to from any starting place.
Write a function which generates the transitive closure of a binary relation. The relation will be represented as a set of 2 item vectors.
(ns live.test
(:require [cljs.test :refer-macros [deftest is testing run-tests]]))
(defn extensions [s]
(fn [[k v]]
(->> s
(filter #(= v (first %)))
(map (juxt (constantly k) second)))))
(defn step [[s _]]
(vector (->> s (mapcat (extensions s)) (into s))
s))
(defn trans-closure [r]
((comp ffirst
(partial drop-while (partial apply not=))
(partial iterate step)
(juxt identity (constantly nil))) r))
(deftest trans-closure-test
(is (let [divides #{[8 4] [9 3] [4 2] [27 9]}]
(= (trans-closure divides) #{[4 2] [8 4] [8 2] [9 3] [27 9] [27 3]})))
(is (let [more-legs
#{["cat" "man"] ["man" "snake"] ["spider" "cat"]}]
(= (trans-closure more-legs)
#{["cat" "man"] ["cat" "snake"] ["man" "snake"]
["spider" "cat"] ["spider" "man"] ["spider" "snake"]})))
(is (let [progeny
#{["father" "son"] ["uncle" "cousin"] ["son" "grandson"]}]
(= (trans-closure progeny)
#{["father" "son"] ["father" "grandson"]
["uncle" "cousin"] ["son" "grandson"]}))))
(run-tests)