Is there a 'proper' way to iterate over a two-dimensional sequence in Clojure?
Suppose I had a list of lists of numbers, like this
((1 2 3)
(4 5 6)
(7 8 9))
and I wanted to generate a new list of lists with each number incremented by one. Is there an easy way to do this in Clojure without relying on nested maps or loop/recurs? I've been able to do it, but my solutions are ugly and I find them difficult to understand when I re-read them.
Thanks
What you describe is precisely what clojure.walk is for:
(def matrix [[1 2 3]
[4 5 6]
[7 8 9]])
(use 'clojure.walk :only [prewalk])
(prewalk #(if (number? %) (inc %) %) matrix)
=> [[2 3 4] [5 6 7] [8 9 10]]
Note 1: it is idiomatic to use vectors instead of parentheses for literal sequential collections.
Note 2: walk preserves type.
You can always just use a list comprehension. I find myself using them quite often coming from an imperative background so I don't know how idiomatic it is. In your specific case, you can do:
(for [my-list my-matrix] (map inc my-list))
For the two-dimensional case, you could do something like:
(map #(map inc %) my-two-d-list)
That's not too bad to read: apply the function #(map inc %) to each element in a list.
For the higher-order case, you're basically talking about tree-traversal. You'd want a function that takes in a tree and a function, and applies that function to each node in the tree. You can find functions for this in clojure.walk.
The other answers by Sean and Matt both show concise and effective ways of getting the right result.
However there are some important extensions you can make to this:
It would be nice to handle the case of higher dimensions
It is good to wrap the functionality in a higher order function
Example code:
;; general higher order function
(defn map-dimensions [n f coll]
(if (= n 1)
(map f coll)
(map #(map-dimensions (dec n) f %) coll)))
;; use partial application to specialise to 2 dimensions
(def map-2d (partial map-dimensions 2))
(map-2d inc
'((1 2 3)
(4 5 6)
(7 8 9)))
=> ((2 3 4) (5 6 7) (8 9 10))
Since the introduction of core.matrix in 2013, this is now a much better way of handling operations over multi-dimensional arrays:
(use 'clojure.core.matrix)
(def M [[1 2 3]
[4 5 6]
[7 8 9]])
(emap inc M)
=> [[2 3 4 ]
[5 6 7 ]
[8 9 10]]
Advantages of using core.matrix:
Clean, idiomatic Clojure code
Lots of general purpose n-dimensional array manipulation functions - transpose, shape, reshape, slice, subarray etc.
Ability to plug in high performance array implementations (e.g. for big numerical arrays)
A belated answer, and maybe not exactly what is needed: you could try flatten. It will return a seq that you can iterate over:
(flatten '((1 2 3)
(4 5 6)
(7 8 9)))
user=> (1 2 3 4 5 6 7 8 9)
And in order to increment matrix elements and reassemble the matrix:
(partition 3 (map inc (flatten '((1 2 3)
(4 5 6)
(7 8 9)))))
Related
I am a beginner to functional programming and Clojure programming language and I'm resorting to recur pretty much for everything. I have a dataset in csv, imported as a map. I have extracted the info I need to use as vectors. Each column is a vector [1 5 10 8 3 2 1 ...] and I want to calculate the mean of each column. I wrote the following function:
(defn mean
"Calculate the mean for each column"
([columns]
(mean columns []))
([columns
means]
(if (empty? columns)
means
(recur (rest columns)
(conj means (float (/ (reduce + (first columns)) (count (first columns)))))))))
;; Calcule the mean for the following vectors
(mean [[1 2 3] [1 2 3] [1 2 3]])
; => [2.0 2.0 2.0]
Is this a functional way of solving this problem?
I'd break it down a little farther and use map instead of for. I personally like having many, smaller functions:
(defn mean [row]
(/ (apply + row) (count row)))
(defn mean-rows [rows]
(map mean rows))
But this is the same general idea as #Alan's answer.
The way you're doing it is already considered "functional". It should be said though that while using recur is good, usually you can achieve what you need more easily using reduce, or at least map. These options eliminate the need for explicit recursion, and generally lead to simpler, easier to understand code.
Here is a simple answer:
(defn mean
"Calculate the mean of each column"
[cols]
(vec ; this is optional if you don't want it
(for [col cols]
(/ ; ratio of num/denom
(reduce + col) ; calculates the numerator
(count col))))) ; the number of items
(mean [[1 2 3] [1 2 3] [1 2 3]]) => [2 2 2]
If you haven't seen it yet, you can get started here: https://www.braveclojure.com/
I recommend buying the printed version of the book, as it has more than the online version.
I would like to create a list of pairs from cols, and patch. cols would have much more elements. Element in patch would be repeated in the pairing.
For example,
(element-wise-patch '(1 3 5 7 9) '(2 4) '())
([1 2] [3 4] [5 2] [7 4] [9 2])
Here is my attempt to implement the semantics. I hope to learn more idiomatic, and simpler solution.
(defn element-wise-patch [cols patch patched]
(if (<= (count cols) (count patch))
(concat patched (map vector cols patch))
(let [[compatible remaining] (split-at (count patch) cols)]
(element-wise-patch remaining patch (concat patched (map vector compatible patch)))))
I feel that there might be already existing construct to do such patching pairing. Also my description might not be proper enough to associate similar solutions.
Please give me some pointer, or just help me define my problem clearer.
Thanks in advance for your help!
Quite simply:
(map vector [1 3 5 7 9] (cycle [2 4]))
Let's say I have a data structure like so:
[[1 2 3] [4 5 6] [[7 8 9] [10 11 12]]]
And what I want to end up with is:
[[1 2 3] [4 5 6] [7 8 9] [10 11 12]]
Is there any function that does this automatically?
Basically I'm converting/transforming a SQL result set to CSV, and there are some rows that will transform to 2 rows in the CSV. So my map function in the normal case returns a vector, but sometimes returns a vector of vectors. Clojure.data.csv needs a list of vectors only, so I need to flatten out the rows that got pivoted.
Mapcat is useful for mapping where each element can expand into 0 or more output elements, like this:
(mapcat #(if (vector? (first %)) % [%]) data)
Though I'm not sure if (vector? (first %)) is a sufficient test for your data.
A different approach using tree-seq:
(def a [[1 2 3] [4 5 6] [[7 8 9] [10 11 12]]])
(filter (comp not vector? first)
(tree-seq (comp vector? first) seq a))
I am stretching to use tree-seq here. Would someone with more experience care to comment on if there is a better way to return only the children other than using what is effectively a filter of (not branch?)
Clojure: Semi-Flattening a nested Sequence answers your question, but I don't want to mark this question as a duplicate of that one, since you're really asking a different question than he was; I wonder if it's possible to move his answer over here.
How to add two collections efficiently in clojure ?
I tried following one. I want to know is there any other method efficient than this.
(reduce #(conj %1 %2) collection01 collection02)
It depends on what you want to achieve. If what you want in the result is a collection of specified type, that contains all element of given collections, then into is appropriate: (into coll1 coll2) returns collection of type (type coll1) with elements from coll1 and coll2.
On the other hand, if you just want to iterate over many collections (i.e. create a sequence of elements in the collections) then it is more efficient to use concat:
user> (concat [1 2 3] (list 4 5 6))
(1 2 3 4 5 6)
use into:
user> (into [1 2 3] [4 5 6])
[1 2 3 4 5 6]
user> (doc into)
-------------------------
clojure.core/into
([to from])
Returns a new coll consisting of to-coll with all of the items of
from-coll conjoined.
nil
For example
(map #(+ 10 %1) [ 1 3 5 7 ])
Will add 10 to everything
Suppose I want to map everything to the constant 1. I have tried
(map #(1) [ 1 3 5 7 ])
But I don't understand the compiler error.
(map #(1) [ 1 3 5 7 ])
Won't work for two reasons:
#(1) is a zero-argument anonymous function, so it won't work with map (which requires a one-argument function when used with one input sequence).
Even if it had the right arity, it wouldn't work because it is trying to call the constant 1 as a function like (1) - try (#(1)) for example if you want to see this error.
Here are some alternatives that will work:
; use an anonymous function with one (ignored) argument
(map (fn [_] 1) [1 3 5 7])
; a hack with do that ignores the % argument
(map #(do % 1) [1 3 5 7])
; use a for list comprehension instead
(for [x [1 3 5 7]] 1)
; use constantly from clojure.core
(map (constantly 1) [1 3 5 7])
Of the above, I think the versions using constantly or for should be preferred - these are clearer and more idiomatic.
The anonymous function #(+ 10 %1) is equivalent to:
(fn [%1]
(+ 10 %1))
Whereas #(1) is equivalent to:
(fn []
(1))
And trying to call 1 as a function with no args just won't work.
I got this from clojure.org
by googling the words "clojure constant function" as I am just beginning to look at clojure
(map (constantly 9) [1 2 3])
cheers