I have a datatype defined with defrecord, which contains two vectors:
(defrecord MyType [a b])
(def mytype (->MyType [1 2 3] [4 5 6]))
I want to have a function update both vectors and return a new MyType. The only way I can think of to do that is via nested assoc calls:
(defn mutate-mytype [mytype x y]
(assoc mytype :a (assoc (:a mytype) x y)
:b (assoc (:b mytype) x y)))
Example output:
user=> (mutate-mytype mytype 1 7)
#user.MyType{:a [1 7 3], :b [4 7 6]}
Question: Is there a better way to write that mutate-mytype method?
Your implementation is perfectly fine.
There are a few alternatives, e.g. you might consider using assoc-in and the -> threading operator:
(defn mutate-mytype [mytype x y]
(-> mytype
(assoc-in [:a x] y)
(assoc-in [:b x] y)))
This doesn't really have any advantages over your approach in this case, but it might make the code more readable if you had more deep nesting going on.
Related
My question is whether given a hashmap
(def my-map {'x 1 'y 2 'z})
I can apply it to an anonymous function,
(fn [x y z] (+ x (* y z))
so that the arguments match the keys in the map, somthing like
(apply-ish my-map (fn [x y z] (+ x (* y z)))
Is there an easy fix to this problem? I feel like there is but I cant figure it out.
You can use map destructuring:
user> (def my-map {'x 1 'y 2 'z 3})
#'user/my-map
user> ((fn [{x 'x y 'y z 'z}] (+ x (* y z))) my-map)
7
You can simplify a bit with this form of desctructuring:
user> ((fn [{:syms [x y z]}] (+ x (* y z))) my-map)
7
or if you use keywords for your map keys:
user> (def my-map2 {:x 1 :y 2 :z 3})
#'user/my-map2
user> ((fn [{:keys [x y z]}] (+ x (* y z))) my-map2)
7
personally, i would not modify the function to accept the map as the arg, since it makes the function itself way less generic. The alternative (and idiomatic i guess, for any language) solution is to select needed keys from the map before passing them to function. That is quite easy, since both map and symbol (and keyword too) have function semantics in clojure:
user> (apply f (map my-map ['x 'y 'z]))
;;=> 7
user> (apply f ((juxt 'x 'y 'z) my-map))
;;=> 7
The following code:
(into {} [[:a 1][:b 2][:c 3][:d 4][:e 5]])
...produces a map(?) of keyword / value pairs. I don't quite understand the significance of the double square brackets and I am assuming it is an example of destructuring?
Thanks,
~Caitlin
It's not a destructuring, it's just an example of using into core function.
into is a function used to conjoin two collection by repeatedly adding elements from the second collection to the first one with conj function.
So, (into {} [[:a 1][:b 2]]) is just a synonym for
(-> {} (conj [:a 1]) (conj [:b 2]))
This answer is a supplement to Leonid's. One can think of a Clojure map as a collection of "map entries", key/value pairs. These are sometimes printed so that they look like 2-element vectors, though they are not 2-element vectors. Nevertheless, if you want to convert something into a map using into, it makes sense that you should pass the data that will turn into map entries in the form of 2-element vectors.
=> (def foo {:a 1 :b 2 :c 3})
#'/foo
=> (find foo :b)
[:b 2]
=> (class (find foo :b))
clojure.lang.MapEntry
=> (map identity foo)
([:c 3] [:b 2] [:a 1])
=> (map class (map identity foo))
(clojure.lang.MapEntry clojure.lang.MapEntry clojure.lang.MapEntry)
=> (list [:c 3] [:b 2] [:a 1])
([:c 3] [:b 2] [:a 1])
=> (map class (list [:c 3] [:b 2] [:a 1]))
(clojure.lang.PersistentVector clojure.lang.PersistentVector clojure.lang.PersistentVector)
I want to write a function that concatenates vectors or matrices, which can take arbitrary inputs. To combine two vectors I've written the follow code. It also also matrices to be combined such that columns are lengthened.
(defn concats
([x y] (vec (concat x y))))
Where I am stuck is extending the input to n vectors or matrices, and combining matrices to make longer rows.
Ex) (somefunction [[:a :b] [:c :d]] [[1 2] [3 4]] 2]
[[:a :b 1 2] [:c :d 3 4]]
The 2 in the input designates level to concatenate.
If you're not interested in "how it works", here's the solution right up front (note that level is zero-indexed, so what you've called the 1st level I'm calling the 0th level):
(defn into* [to & froms]
(reduce into to froms))
(defn deep-into*
[level & matrices]
(-> (partial partial mapv)
(iterate into*)
(nth level)
(apply matrices)))
The short answer for how it works is this: it iteratively builds up a function that will nest the call to into* at the correct level, and then applies it to the supplied matrices.
Regular old into, given a vector first argument, will concatenate the elements of the second argument onto the end of the vector. The into* function here is just the way I'm doing vector concatting on a variable number of vectors. It uses reduce to iteratively call into on some accumulated vector (which starts as to) and the successive vectors in the list froms. For example:
user> (into* [1 2] [3 4] [5 6])
> [1 2 3 4 5 6]
Now for deep-into*, I had to recognize a pattern. I started by hand-writing different expressions that would satisfy different "levels" of concatenation. For level 0, it's easy (I've extrapolated your example somewhat so that I can make it to level 2):
user> (into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d]] [[1 2] [3 4]]]
As for level 1, it's still pretty straightforward. I use mapv, which works just like map except that it returns a vector instead of a lazy sequence:
user> (mapv into* [[[:a :b] [:c :d]]] [[[1 2] [3 4]]])
> [[[:a :b] [:c :d] [1 2] [3 4]]]
Level 2 is a little more involved. This is where I start using partial. The partial function takes a function and a variable number of argument arguments (not a typo), and returns a new function that "assumes" the given arguments. If it helps, (partial f x) is the same as (fn [& args] (apply f x args)). It should be clearer from this example:
user> ((partial + 2) 5)
> 7
user> (map (partial + 2) [5 6 7]) ;why was six afraid of seven?
> (7 8 9)
So knowing that, and also knowing that I'll want to go one level deeper, it makes some sense that level 2 looks like this:
user> (mapv (partial mapv into*) [[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
Here, it's mapping a function that's mapping into* down some collection. Which is kind of like saying: map the level 1 idea of (mapv into* ...) down the matrices. In order to generalize this to a function, you'd have to recognize the pattern here. I'm going to put them all next to each other:
(into* ...) ;level 0
(mapv into* ...) ;level 1
(mapv (partial mapv into*) ...) ;level 2
From here, I remembered that (partial f) is the same as f (think about it: you have a function and you're giving it no additional "assumed" arguments). And by extending that a little, (map f ...) is the same as ((partial map f) ...) So I'll re-write the above, slightly:
(into* ...) ;level 0
((partial mapv into*) ...) ;level 1
((partial mapv (partial mapv into*)) ...) ;level 2
Now an iterative pattern is becoming clearer. We're calling some function on ... (which is just our given matrices), and that function is an iterative build-up of calling (partial mapv ...) on into*, iterating for the number of levels. The (partial mapv ...) part can be functionalized as (partial partial mapv). This is a partial function that returns a partial function of mapving some supplied arguments. This outer partial isn't quite necessary because we know that the ... here will always be one thing. So we could just as easily write it as #(partial mapv %), but I so rarely get a chance to use (partial partial ...) and I think it looks pretty. As for the iteration, I use the pattern (nth (iterate f initial) n). Perhaps another example would make this pattern clear:
user> (nth (iterate inc 6) 5)
> 11
Without the (nth ...) part, it would loop forever, creating an infinite list of incrementing integers greater than or equal to 5. So now, the whole thing abstracted and calculated for level 2:
user> ((nth (iterate (partial partial mapv) into*) 2)
[[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
Then, using the -> macro I can factor out some of these nested parantheses. This macro takes a list of expressions and recursively nests each into the second position of the successive one. It doesn't add any functionality, but can certainly make things more readable:
user> ((-> (partial partial mapv)
(iterate into*)
(nth 2))
[[[:a :b][:c :d]]] [[[1 2][3 4]]])
> [[[:a :b 1 2] [:c :d 3 4]]]
From here, generalizing to a function is pretty trivial--replace the 2 and the matrices with arguments. But because this takes a variable number of matrices, we will have to apply the iteratively-built function. The apply macro takes a function or macro, a variable number of arguments, and finally a collection. Essentially, it prepends the function or macro and the supplied arguments onto the final list, then evaluates the whole thing. For example:
user> (apply + [1 5 10]) ;same as (+ 1 5 10)
> 16
Happily, we can stick the needed apply at the end of the (-> ...). Here's my solution again, for the sake of symmetry:
(defn deep-into*
[level & matrices]
(-> (partial partial mapv)
(iterate into*)
(nth level)
(apply matrices)))
Using the concats function you listed in the question:
user=> (map concats [[:a :b] [:c :d]] [[1 2] [3 4]])
([:a :b 1 2] [:c :d 3 4])
this doesn't take into account the level as you listed, but it handles the input given
Taking arbitrary number of arguments needs a replacement concats function
(defn conc [a b & args]
(if (nil? (first args))
(concat a b)
(recur (concat a b) (first args) (rest args))))
Here are two examples
user=> (map conc [[:a :b] [:c :d]] [[1 2] [3 4]] [["w" "x"] ["y" "z"]])
((:a :b 1 2 "w" "x") (:c :d 3 4 "y" "z"))
user=> (map conc [[:a :b] [:c :d] [:e :f]] [[1 2] [3 4] [5 6]] [["u" "v"] ["w" "x"] ["y" "z"]])
((:a :b 1 2 "u" "v") (:c :d 3 4 "w" "x") (:e :f 5 6 "y" "z"))
Here are two different solutions for a function which will return a vector that's the concatenation of an arbitrary number of input collections:
(defn concats [& colls]
(reduce (fn [result coll]
(into result coll))
[]
colls))
(defn concats [& colls]
(vec (apply concat colls)))
The [& arg-name] notation in the argument lists is how you specify that the function is "variadic" - meaning it can accept a variable number of arguments. The result is that colls (or whatever name you pick) will be a sequence of all the arguments in excess of the positional arguments.
Functions can have multiple arities in Clojure, so you can also do things like this:
(defn concats
([x]
(vec x))
([x y]
(vec (concat x y)))
([x y & colls]
(vec (apply concat (list* x y colls)))))
However, only one of the overloads can be variadic, and its variadic part must come last (i.e. you can't do [& more n], only [n & more].
The Clojure.org page on special forms has more useful information on argument lists in Clojure (in the section on fn).
The function below correctly handles the example input/output you provided. Unfortunately I don't think I understand how you want the levels (and associated numeric input) to work well enough to generalize it as far as you're looking for.
(defn concats [x y]
;; only works with two inputs
(vec (map-indexed (fn [i v] (into v (nth y i)))
x)))
(concats [[:a :b] [:c :d]] [[1 2] [3 4]]) ;=> [[:a :b 1 2] [:c :d 3 4]]
But maybe it will give you some ideas anyway, or if you can add more information (especially examples of how different levels should work) I'll see if I can be more help.
The question doesn't really explain what I want to do but I couldn't think of anything else.
I have an empty map in the outer let function in a piece of code, and an integer array.
I want to iterate through the integer array, perform a simple task, and keep appending the resulting map to the variables in the outer variables.
(let [a {} ;outer variables
b {}]
(doseq [x [1 2 3]]
(let [r (merge a {x (* x x)}) ;I want to append this to a
s (merge b {x (+ x x)})] ;and this to b
(println (str "--a--" r "--b--" s)))))
But as soon as I get out of doseq, my a and b vars are still empty. I get that the scope of a and b doesn't extend outside of doseq for it to persist any changes done from within and that they are immutable.
How do I calculate the values of a and b in such cases, please? I tried to extract the functionality of doseq into another function and calling let with:
(let [a (do-that-function)])
etc but even then I couldn't figure out a way to keep track of all the modifications within doseq loop to then send back as a whole.
Am I approaching this in a wrong way?
Thanks
edit
Really, what I'm trying to do is this:
(let [a (doseq [x [1 2 3]] {x (* x x)})]
(println a))
but doseq returns nil so a is going to be nil :-s
All variables in clojure are immutable. If you need a mutable state you should use atoms or refs.
But in your case you can simply switch from doseq to for:
(let [a (for [x [1 2 3]] {x (* x x)})]
(println a))
Here is an example of solving your problem with atoms:
(let [a (atom {})
b (atom {})]
(doseq [x [1 2 3]]
(swap! a assoc x (* x x))
(swap! b assoc x (+ x x)))
(println "a:" #a)
(println "b:" #b))
But you should avoid using mutable state as far as possible:
(let [l [1 2 3]
a (zipmap l (map * l l))
b (zipmap l (map + l l))]
(println "a:" a)
(println "b:" b))
The trick is to think in terms of flows of data adding to existing data making new data, instead of changing past data. For your specific problem, where a data structure is being built, reduce is typically used:
(reduce (fn [result x] (assoc result x (* x x))) {} [1 2 3])
hehe, I just noticed that "reduce" might seem confusing given that it's building something, but the meaning is that a collection of things is "reduced" to one thing. In this case, we give reduce an empty map to begin with, which binds to result in the fn, and each successive mapping over the collection results in a new result, which we add to again with assoc.
You could also say:
(into {} (map (fn [x] [x (* x x)]) [1 2 3]))
In your question you wanted to make multiple things at once from a single collection. Here's one way to do that:
(reduce (fn [[a b] x] [(assoc a x (* x x)) (assoc b x (+ x x))]) [{} {}] [1 2 3])
Here we used destructuring syntax to refer to our two result structures - just make a picture of the data [with [vectors]]. Note that reduce is still only returning one thing - a vector in this case.
And, we could generalize that:
(defn xfn [n fs]
(reduce
(fn [results x] (map (fn [r f] (assoc r x (f x x))) results fs))
(repeat (count fs) {}) (range n)))
=> (xfn 4 [* + -])
({3 9, 2 4, 1 1, 0 0} {3 6, 2 4, 1 2, 0 0} {3 0, 2 0, 1 0, 0 0})
The result is a list of maps. And if you wanted to take intermediate steps in the building of these results, you could change reduce to reductions. Generally, map for transforming collections, reduce for building a single result from a collection.
In Clojure, I have a collection coll of 2-elements vectors. I would like to create the collection obtained by applying f and g on the first and second elements on every vector of the collection, respectively. I think this is related to the list comprehension construct.
(def coll [[1 1000] [2 2000] [3 3000]])
IS there an idiomatic way for creating the following result?
[[f(1) g(1000)] [f(2) g(2000)] [f(3) g(3000)]]
Again, list comprehension FTW:
(vec (for [[x y] [[1 1000] [2 2000] [3 3000]]] [(f x) (g y)]))
Yes,
(vec (map (fn [[p1 p2]] [(f p1) (g p2)])
[[1 1000] [2 2000] [3 3000]]))
To write this from scratch, I would do exactly what skuro did - it's simple, easy, and readable. But I also wrote a higher-order function to abstract this some time ago, named knit. So now I would write this as
(map (knit f g) [[1 1000] [2 2000] [3 3000]])