-
Notifications
You must be signed in to change notification settings - Fork 1
/
dissoc.cljc
166 lines (149 loc) · 5.44 KB
/
dissoc.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
(ns dag_unify.dissoc
(:require
[dag_unify.core :as u]
[dag_unify.serialization :as s]
#?(:clj [clojure.tools.logging :as log])
#?(:cljs [cljslog.core :as log])))
;; 'dissoc-in' function defined here along with
;; its supporting functions.
;; the idea is to remove a value at a given
;; path from a dag, so that, as well as removing the given path,
;; all other paths in the dag that refer to that value are
;; also removed.
;; can be overridden to only dissoc
;; certain paths and not others:
;; see dissoc-test/dissoc-test-4.
(def ^:dynamic remove-path? (fn [path] true))
(declare dissoc-path)
(defn dissoc-in
"dissoc a path in a dag, as well as any other path in the dag to the same value."
[structure path]
(cond
(empty? path)
structure
true
(s/deserialize
(let [serialized (s/serialize structure)]
(dissoc-path serialized (map first serialized) path)))))
(declare aliases-of)
(declare dissoc-in-map)
(declare get-remainders-for)
(declare prefix?)
(defn dissoc-path [serialized pathsets path]
(if (not (empty? serialized))
(let [[reentrance-set value] (first serialized)
filtered-reentrance-set (remove #(remove-path? %) reentrance-set)
debug (if (not (= reentrance-set filtered-reentrance-set))
(log/debug (str "RE!=FRE: "
(vec reentrance-set) " != "
(vec filtered-reentrance-set))))]
(let [new-reentrance-set (if (some #(prefix? path %) reentrance-set)
filtered-reentrance-set
reentrance-set)
remainders (get-remainders-for
(set (cons path
(aliases-of path pathsets)))
reentrance-set)
debug (log/debug (str "remainders for path: " path ": " (vec remainders)))
new-value
(reduce (fn [value path]
(log/debug (str "DIM(value=" value "; path=" (vec path)))
(let [dim (dissoc-in-map value path)]
(log/debug (str " DIM: return value: " dim))
dim))
value
remainders)]
(log/debug (str "dissoc-path: serialized: " serialized
"; reentrance-set: " reentrance-set
"; new-reentrance-set: " (vec new-reentrance-set)
"; value at set: " value
"; path to remove: " path
"; new-value: " new-value))
(cons [new-reentrance-set new-value]
(dissoc-path (rest serialized) pathsets path))))))
(defn dissoc-in-map
"dissoc a nested path from the-map; e.g.:
(dissoc-in {:a {:b 42, :c 43}} [:a :b]) => {:a {:c 43}}."
[the-map path]
(log/debug (str "dissoc-in-map: the-map: " the-map "; path: " (vec path)))
(cond (or (empty? path)
(= :top the-map)
(= ::none (get the-map (first path) ::none)))
the-map
(and (empty? (rest path))
(empty? (dissoc the-map (first path))))
:top
(empty? (rest path))
(dissoc the-map (first path))
true
(merge
{(first path)
(dissoc-in-map (get the-map (first path))
(rest path))}
(dissoc the-map (first path)))))
(defn prefix?
"return true iff seq a is a prefix of seq b:
(prefix? [:a ] [:a :b]) => true
(prefix? [:a :b] [:a ]) => false
(prefix? [:a :b] [:a :c]) => false"
[a b]
(cond (empty? a) true
(empty? b) false
(= (first a) (first b))
(prefix? (rest a) (rest b))
true false))
(defn remainder
"if seq a is a prefix of seq b,
then return what is left of b besides
the common prefix of a.
if seq a is not a prefix, return nil."
[a b]
(cond (empty? a)
b
(empty? b)
nil
(= (first a) (first b))
(remainder (rest a) (rest b))))
(defn aliases-of
"given _path_ and a set of set of paths, for each subset s,
if a member m1 of s is a prefix of _path_, concatenate
each member other m2 of s to remainder(m2,path)."
[path reentrance-sets]
(concat
;; 1. find reentrance sets where some member of
;; some reentrance set is a prefix of _path_:
(->>
reentrance-sets
(mapcat
(fn [reentrance-set]
(->>
reentrance-set
(mapcat (fn [reentrance-path]
(->>
reentrance-set
(remove #(= % reentrance-path))
(map #(remainder % path))
(remove nil?)
(map #(concat reentrance-path %)))))))))
;; 2. get all paths in reentrance sets where
;; _path_ is a prefix of a member of the reentrance set.
;; TODO: pull 2. out into its own function; it's not
;; returning aliases of path, but rather prefixes.
(->>
reentrance-sets
(filter
(fn [reentrance-set]
(some #(prefix? path %)
reentrance-set)))
(reduce concat))))
(defn get-remainders-for [aliases-of-path reentrance-set]
(set
(cond (empty? reentrance-set)
aliases-of-path
true
(mapcat (fn [each-alias-of-path]
(remove nil?
(map (fn [each-reentrance-path]
(remainder each-reentrance-path each-alias-of-path))
reentrance-set)))
aliases-of-path))))