I am trying to write a function which, given a collection and a sequence of replacements, replaces any lists in the collection with the next replacement in the order they appear.
For example:
(substitute '(+ 1 (* 2 3) 4 (* 5 6) [:a :b]) => (+ 1 :a 4 :b)
(substitute '[1 (2 3 4) (5 6 7)] [:x :y :z]) => [1 :x :y]
(substitute '[(1 2 3) (4 5 6) (7 8 9)] [:x :y]) => [:x :y (7 8 9)]
Currently I have this:
(defn substitute
[form syms]
(if (seq form)
(lazy-seq
(if (and (not-empty syms) (list? (first form)))
(cons
(first syms)
(substitute (rest form) (rest syms)))
(cons
(first form)
(substitute (rest form) syms))))))
However I have two problems. First, I want the output to be the same type as form. I tried doing (into (empty form) (substitute form syms)) but this causes the output to be reversed when form is a list. Second, I am struggling to find a way to make this work on maps (I want to check for a list in both the key and value of each entry).
Any tips or pointers would be much appreciated. Thanks.
Here's an approach using clojure.walk/prewalk to traverse the form in order (pre-order) and using an atom to track what syms remain for substitution:
(defn substitute [form syms]
(let [syms' (atom syms)
depth (atom 0)]
(walk/prewalk
(fn [v]
(cond
(= 1 (swap! depth inc)) v ;; don't examine the input form itself
(list? v) (if-let [sym (first #syms')]
(do (swap! syms' rest)
sym)
v)
:else v))
form)))
The depth atom is to ensure we don't act on the first value which will be form itself, and if it were a list we wouldn't want to substitute the whole thing. At first I just checked (not= form v) but thought that could backfire if your form contains nested forms identical/equal to the outer form. I suspect there's a better way to accomplish this!
prewalk (and postwalk) also relieve you of having to worry about the type of collection you're walking i.e. lists will come out in the correct order.
(substitute '(+ 1 (* 2 3) 4 (* 5 6)) [:a :b])
=> (+ 1 :a 4 :b)
(substitute '[1 (2 3 4) (5 6 7)] [:x :y :z])
=> [1 :x :y]
(substitute '[(1 2 3) (4 5 6) (7 8 9)] [:x :y])
=> [:x :y (7 8 9)]
Using prewalk also allows this to work on maps w/o additional effort:
(substitute {:foo '(1 2 3) '(4 5 6) "hey"} [:a :b])
=> {:foo :a, :b "hey"}
You can also use prewalk-demo to illustrate how the form is traversed:
(walk/prewalk-demo {:foo '(1 2 3) '(4 5 6) "hey"})
Walked: {:foo (1 2 3), (4 5 6) "hey"}
Walked: [:foo (1 2 3)]
Walked: :foo
Walked: (1 2 3)
Walked: 1
Walked: 2
Walked: 3
Walked: [(4 5 6) "hey"]
Walked: (4 5 6)
Walked: 4
Walked: 5
Walked: 6
Walked: "hey"
Thanks to everyone who replied. I think I may have cracked it now.
(defn map-to-vec
[map]
(reduce-kv (fn [vec k v] (into vec [k v])) [] map))
(defn substitute-seq
[form syms]
(if (seq form)
(lazy-seq
(if (and (not-empty syms) (list? (first form)))
(cons
(first syms)
(substitute-seq (rest form) (rest syms)))
(cons
(first form)
(substitute-seq (rest form) syms))))))
(defn substitute
[form syms]
(cond
(list? form) (apply list (sub-syms-seq form syms))
(map? form)
(reduce
(fn [m [k v]] (conj m [k v]))
(empty form)
(partition 2 (sub-syms-seq (map-to-vec form) syms)))
(coll? form) (into (empty form) (sub-syms-seq form syms))))
I wrote a function map-to-vec which converts a map into a vector, such that {:a 1 :b 2 :c 3} becomes [:a 1 :b 2 :c 3] and added a helper function which ensures that the type of the output is the same as the input and, in the case of the input being a map, performs the main function (now called substitute-seq) on the map-to-veced map and then converts it back into a map again.
I'm sure there's still a better and more efficient way to do this but I suppose this works.
Related
When translate a function flatten from Scheme to Clojure, I have encountered
an error "Execution error (IllegalArgumentException)", Please feel free to comment.
Clojure (Encounter error)
(defn flatten [x]
(cond
(empty? x) '()
(not (coll? x)) (list x)
:else (conj (flatten (first x)) (flatten (rest x)))))
(flatten '((1) 2 ((3 4) 5) ((())) (((6))) 7 8 ()))
Scheme
> (define (flatten x)
(cond ((null? x) '())
((not (pair? x)) (list x))
(else (append (flatten (car x))
(flatten (cdr x))))))
> (flatten '((1) 2 ((3 4) 5) ((())) (((6))) 7 8 ()))
(1 2 3 4 5 6 7 8)
Here is an implementation that works for me
(defn flatten [x]
(cond
(not (coll? x)) (list x)
(empty? x) '()
:else (concat (flatten (first x)) (flatten (rest x)))))
(comment
(flatten '((1) 2 ((3 4) 5) ((())) (((6))) 7 8 ())))
;returns (1 2 3 4 5 6 7 8)
The only difference is that I put first the (not (coll? x)) and then the empty? (because (empty? 3) will throw an error)
The other change is the concat instead of conj. I don't think it's recommended to use concat because it can throw a Stack Overflow Error (see : https://stuartsierra.com/2015/04/26/clojure-donts-concat) but it works in this case ...
One more version, based on Alan Thompson's solution:
(defn flat [data]
(reduce (fn [result e]
(if (not (sequential? e))
(conj result e)
(into result (flat e))))
[] data))
Tests:
(clojure.test/are [result arg]
(= result (flat arg))
[1 2 3] [1 2 3]
[1 2 3] [1 [2 3]]
[1 2 3] [1 [2 [3]]]
[0 1 2 3 4 5 6 7] [[0] [1 2 3] [4 5 [6 7]]])
=> true
Here is an alternate version that may be a bit more straightforward:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn flatten
[data]
(loop [result []
items data]
(if (empty? items)
result
(let [item (first items)
items-next (rest items)]
(if (not (sequential? item))
(recur
(conj result item)
items-next)
(recur
(into result (flatten item))
items-next))))))
(dotest
(is= (flatten [1 2 3]) [1 2 3])
(is= (flatten [1 [2 3]]) [1 2 3])
(is= (flatten [1 [2 [3]]]) [1 2 3])
(is= (flatten [[0] [1 2 3] [4 5 [6 7]]]) [0 1 2 3 4 5 6 7])
; Cannot flatten a scalar value - we don't want (flatten 3) => [3]
(throws? (flatten 3)))
Note that we avoid tricks like wrapping every scalar value in a list, so we don't get the non-intuitive result (flatten 3) => [3].
Build using my favorite template project.
I need to quote without namespace and combine it with unquoting. Something like:
'[a b ~c]
Unfortunately, unquoting works only with syntactic quoting:
`[a b ~c]
But then it expands to
[user/a user/b 7]
I would like to expand without namespaces.
What was suggested on clojurians slack channel is as follows:
Use a combination of "quote unquote" for symbols to get rid of namespaces:
`[~'a ~'b ~c]
and this works perfectly.
As a reference, I have been working on a similar capability that doesn't require defensive treatment like ~'a for each symbol you wish to remain unchanged. It isn't published yet, but here is the technique:
;-----------------------------------------------------------------------------
(defn unquote-form?
[arg]
(and (list? arg)
(= (quote unquote) (first arg))))
(defn unquote-splicing-form?
[arg]
(and (list? arg)
(= (quote unquote-splicing) (first arg))))
(defn quote-template-impl
[form]
(walk/prewalk
(fn [item]
(cond
(unquote-form? item) (eval (xsecond item))
(sequential? item) (let [unquoted-vec (apply glue
(forv [it item]
(if (unquote-splicing-form? it)
(eval (xsecond it))
[it])))
final-result (if (list? item)
(t/->list unquoted-vec)
unquoted-vec)]
final-result)
:else item))
form))
(defmacro quote-template
[form]
(quote-template-impl form))
and unit tests to show it in action:
;-----------------------------------------------------------------------------
(def vec234 [2 3 4])
(dotest
(is (td/unquote-form? (quote (unquote (+ 2 3)))))
(is (td/unquote-splicing-form? (quote (unquote-splicing (+ 2 3)))))
(is= (td/quote-template {:a 1 :b (unquote (+ 2 3))})
{:a 1, :b 5})
(is= (td/quote-template {:a 1 :b (unquote (vec (range 3)))})
{:a 1, :b [0 1 2]})
(is= (td/quote-template {:a 1 :b (unquote vec234)})
{:a 1, :b [2 3 4]})
(let [result (td/quote-template (list 1 2 (unquote (inc 2)) 4 5))]
(is (list? result))
(is= result (quote (1 2 3 4 5))))
(is= (td/quote-template [1 (unquote-splicing vec234) 5]) ; unqualified name OK here
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote-splicing (t/thru 2 4)) 5])
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote (t/thru 2 4)) 5])
[1 [2 3 4] 5])
)
So, instead of Clojure's syntax-quote (i.e. backquote), you can use quote-template. Then, use either unquote or unquote-splicing to insert values into the quoted template without having the namespace prepended to other symbols.
I know it is probably not the answer you are looking for by why not use code to construct that thing you want? Why force it to be all done in one expression? You could for example use
(conj '[a b] c)
I am trying to get into Lisps and FP by trying out the 99 problems.
Here is the problem statement (Problem 15)
Replicate the elements of a list a given number of times.
I have come up with the following code which simply returns an empty list []
I am unable to figure out why my code doesn't work and would really appreciate some help.
(defn replicateList "Replicates each element of the list n times" [l n]
(loop [initList l returnList []]
(if (empty? initList)
returnList
(let [[head & rest] initList]
(loop [x 0]
(when (< x n)
(conj returnList head)
(recur (inc x))))
(recur rest returnList)))))
(defn -main
"Main" []
(test/is (=
(replicateList [1 2] 2)
[1 1 2 2])
"Failed basic test")
)
copying my comment to answer:
this line: (conj returnList head) doesn't modify returnlist, rather it just drops the result in your case. You should restructure your program to pass the accumulated list further to the next iteration. But there are better ways to do this in clojure. Like (defn replicate-list [data times] (apply concat (repeat times data)))
If you still need the loop/recur version for educational reasons, i would go with this:
(defn replicate-list [data times]
(loop [[h & t :as input] data times times result []]
(if-not (pos? times)
result
(if (empty? input)
(recur data (dec times) result)
(recur t times (conj result h))))))
user> (replicate-list [1 2 3] 3)
;;=> [1 2 3 1 2 3 1 2 3]
user> (replicate-list [ ] 2)
;;=> []
user> (replicate-list [1 2 3] -1)
;;=> []
update
based on the clarified question, the simplest way to do this is
(defn replicate-list [data times]
(mapcat (partial repeat times) data))
user> (replicate-list [1 2 3] 3)
;;=> (1 1 1 2 2 2 3 3 3)
and the loop/recur variant:
(defn replicate-list [data times]
(loop [[h & t :as data] data n 0 res []]
(cond (empty? data) res
(>= n times) (recur t 0 res)
:else (recur data (inc n) (conj res h)))))
user> (replicate-list [1 2 3] 3)
;;=> [1 1 1 2 2 2 3 3 3]
user> (replicate-list [1 2 3] 0)
;;=> []
user> (replicate-list [] 10)
;;=> []
Here is a version based on the original post, with minimal modifications:
;; Based on the original version posted
(defn replicateList "Replicates each element of the list n times" [l n]
(loop [initList l returnList []]
(if (empty? initList)
returnList
(let [[head & rest] initList]
(recur
rest
(loop [inner-returnList returnList
x 0]
(if (< x n)
(recur (conj inner-returnList head) (inc x))
inner-returnList)))))))
Please keep in mind that Clojure is mainly a functional language, meaning that most functions produce their results as a new return value instead of updating in place. So, as pointed out in the comment, the line (conj returnList head) will not have an effect, because it's return value is ignored.
The above version works, but does not really take advantage of Clojure's sequence processing facilities. So here are two other suggestions for solving your problem:
;; Using lazy seqs and reduce
(defn replicateList2 [l n]
(reduce into [] (map #(take n (repeat %)) l)))
;; Yet another way using transducers
(defn replicateList3 [l n]
(transduce
(comp (map #(take n (repeat %)))
cat
)
conj
[]
l))
One thing is not clear about your question though: From your implementation, it looks like you want to create a new list where each element is repeated n times, e.g.
playground.replicate> (replicateList [1 2 3] 4)
[1 1 1 1 2 2 2 2 3 3 3 3]
But if you would instead like this result
playground.replicate> (replicateList [1 2 3] 4)
[1 2 3 1 2 3 1 2 3 1 2 3]
the answer to your question will be different.
If you want to learn idiomatic Clojure you should try to find a solution without such low level facilities as loop. Rather try to combine higher level functions like take, repeat, repeatedly. If you're feeling adventurous you might throw in laziness as well. Clojure's sequences are lazy, that is they get evaluated only when needed.
One example I came up with would be
(defn repeat-list-items [l n]
(lazy-seq
(when-let [s (seq l)]
(concat (repeat n (first l))
(repeat-list-items (next l) n)))))
Please also note the common naming with kebab-case
This seems to do what you want pretty well and works for an unlimited input (see the call (range) below), too:
experi.core> (def l [:a :b :c])
#'experi.core/
experi.core> (repeat-list-items l 2)
(:a :a :b :b :c :c)
experi.core> (repeat-list-items l 0)
()
experi.core> (repeat-list-items l 1)
(:a :b :c)
experi.core> (take 10 (drop 10000 (repeat-list-items (range) 4)))
(2500 2500 2500 2500 2501 2501 2501 2501 2502 2502)
Introduction
The following function iteratively traverses a tree structure made of nested vectors. It tests each leaf against a predicate. The paths to all leaves which pass that truth-test are returned in a Trie structure. The later describes all found paths in a non-redundant way.
(defn get-trie-of-matches [is? tree]
(loop [[tree i path fk] [tree 0 [] nil]
accum {}]
(cond
(>= i (count tree)) ;; end of level / go up
(if (nil? fk) accum (recur fk accum))
(vector? (tree i)) ;; level down
(recur [(tree i) 0 (conj path i) [tree (inc i) path fk]] accum)
(is? (tree i)) ;; match
(let [new-accum (assoc-in accum (conj path i) {})]
(recur [tree (inc i) path fk] new-accum))
:else ;; next on same level
(recur [tree (inc i) path fk] accum))))
For further explanations see this post.
Example
Consider the following tree
(def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
Applied to the function, using even? as a predicate:
(get-trie-of-matches even? tree)
=> {2 {3 {0 {}, 1 {}}}, 4 {0 {}}}
The result describes the three paths to even numbers in tree. Namely 2-3-0, 2-3-1 and 4-0.
Problem
Even though the above function works, there might be better ways to construct the Trie while traversing the tree. At the moment a hash-map is flooded. On each match via assoc-in. The algorithm traverses the tree structure relatively from level to level but attaches each path in a global fashion to accum, which is not necessary. Also this method is only possible since a hashmap is used. It might anyways be better to use a sequential data-structure for the Trie in order to facilitate further iterations over it. This could not be adopted to the above method.
Question
How could a Trie be created from within the above function get-trie-of-matches without relying on hash-map specific 'global' 'write' functions?
I would propose to take a look at clojure's walk api.
It allows you to recursively apply some function to nested collections.
In this case you could use postwalk:
user> (require '[clojure.walk :as w])
user> (w/postwalk-demo [1 3 [4 [6] 7] [[8]]])
Walked: 1
Walked: 3
Walked: 4
Walked: 6
Walked: [6]
Walked: 7
Walked: [4 [6] 7]
Walked: 8
Walked: [8]
Walked: [[8]]
Walked: [1 3 [4 [6] 7] [[8]]]
[1 3 [4 [6] 7] [[8]]]
The key thing is you can replace any item at every step:
user> (w/postwalk #(if (coll? %) (reverse %) (inc %))
[1 3 [4 [6] 7] [[8]]])
(((9)) (8 (7) 5) 4 2)
Here we increment all the numbers, and reverse all the collections, keeping the nested structure.
Now applying to your task:
You could walk through your tree, keeping just even number's indices and not empty collections (e.g. collections containing even numbers, and not empty collections):
;; helper function
(defn empty-coll? [item]
(and (coll? item) (not (seq item))))
(defn make-trie [pred tree]
(w/postwalk
#(if (coll? %)
(keep-indexed (fn [idx item]
(cond (empty-coll? item) nil
(coll? item) (list idx item)
item idx
:else nil))
%)
(pred %))
tree))
in repl:
user> (def tree [7 9 [7 5 3 [4 6 9] 9 3] 1 [2 7 9 9]])
#'user/tree
user> (make-trie even? tree)
((2 ((3 (0 1)))) (4 (0)))
user> (make-trie #(> % 7) tree)
(1 (2 ((3 (2)) 4)) (4 (2 3)))
The structure is similar to your map. In fact you can produce any structure you want with minor changes to the function, for example your map structure:
(defn make-trie-map [pred tree]
(w/postwalk
#(if (coll? %)
(into {}
(keep-indexed (fn [idx item]
(cond (empty-coll? item) nil
(coll? item) {idx item}
item {idx {}}
:else nil))
%))
(pred %))
tree))
user> (make-trie-map even? tree)
{2 {3 {0 {}, 1 {}}}, 4 {0 {}}}
#(delay (delay 1)) ; equals to unrealized delay object, containing 1.
Is there an easy (without a head-first macross which tests each block with realized?) way to deref all inner delays recursively?
Arthur's code certainly works for the example you've posted, but here is an example of how you could handle mixed sequences:
(defn deref-delays [x]
(cond
(sequential? x) (map deref-delays x)
(= clojure.lang.Delay (class x)) (recur #x)
:else x))
(deref-delays [1 2 (delay 3) (delay [4 (delay (delay 5)) 6]) 7])
;(1 2 3 (4 5 6) 7)
I'm assuming that if there where many nested delays, you want a function to retrieve the value from the inner most delay?
(defn recursive-deref [delays]
(if (= clojure.lang.Delay (type delays))
(recur #delays)
delays))
#'user/recursive-deref
user> (recursive-deref delays)
1
You can use clojure.walk to do this and preserve structure.
(defn deref-walk [x]
(clojure.walk/prewalk
(fn [e]
(if (delay? e)
(deref-walk (deref e))
e))
x))
Then
(deref-walk (delay {:a 1 :b (delay [1 2 (delay 3) (delay {:c 4})])}))
Results in
{:a 1 :b [1 2 3 {:c 4}]}