5
votes

I'm working on my first-ever functional program in Clojure. I'm having some issues figuring out how to step through each item in a list, in each list in a list, and operate on it while keeping return values. I'm sure the issue comes from my unfamiliarity with Clojure and functional programming and was hoping someone could explain the best method to do the following:

psuedo-code algorithm:
for each lst in list
   for each item in lst
      return_values.append = do_something(item)

I first tried nesting two doseq functions and then calling my do_something function, which worked to call the function on the item, but didn't save my return values. I then tried a for and cons to an empty list, but was unable to get my return values outside of the for.

Would it be possible/preferable to break the list of lists down first? Could I still get a list of lists of return values?

In the end, I would like the result to be a list of lists of return values to match the input list of lists. If anyone could explain the best method for doing this in Clojure, and why, it would be much appreciated.

3

3 Answers

5
votes

Nested for loop will do the trick:

(for [lst my-list]
  (for [item lst] (do_something item)))

It will take nested list my-list (list of lists) and convert it into another nested list by applying do_something to each element.

In clojure, for returns a list of values already, so there is no need to handle it yourself. Furthermore, since all data structures in clojure are immutable, you can't do this by appending elements to initially empty list with cons.

0
votes

If you have a deeply nested list and you want to keep its structure, but transform the values, you can use clojure.walk/postwalk to operate on each value, e.g.:

(def nested '(1 (2 3 (4 5)) 6))

(defn transform-values [coll f]
  (clojure.walk/postwalk #(if (not (list? %))
                            (f %)
                            %)
                         coll))

(transform-values nested inc)

=> (2 (3 4 (5 6)) 7)

You can, of course, pass any function to transform-values.

0
votes

This can be done as a simple recursive walk. The first implementation that comes to mind for this would be the following for sequences:

(defn deep-walk
  [f data]
  (map (fn [s] (if (seq? s) 
                 (deep-walk f s) 
                 (f s))) 
        data))

And this slight variation for vectors:

(defn vec-deep-walk
  [f data]
  (vec (map (fn [s] (if (vector? s) 
                      (vec-deep-walk f s) 
                      (f s))) 
            data)))

Just a quick test with the following:

(vec-deep-walk (partial + 1) [1 [2 3] 4 [5 [6 7]]])

Gives the following output:

[2 [3 4] 5 [6 [7 8]]]

The walk functions take two parameters, the first is a function that takes a single parameter. This will be called for each non-seq/vector element in your data, which is passed as the second parameter. The results will be returned in a nested structure that is identical to the input structure.