How to Return Vector of Vectors

130 views
Skip to first unread message

octopusgrabbus

unread,
Jul 21, 2011, 6:48:52 PM7/21/11
to Clojure


(def accumail-url-keys ["CA", "STREET", "STREET2", "CITY", "STATE",
"ZIP", "YR", "BILL_NO", BILL_TYPE"])

(defn ret-params
"Generates all q-parameters and returns them in a vector of
vectors."
[all-csv-rows]
(let [param-vec [[]] ]
(doseq [one-full-csv-row all-csv-rows]
(let [accumail-csv-row one-full-csv-row
q-param (zipmap accumail-url-keys accumail-csv-row)
accu-q-param (first (rest (split-at 3 q-param)))
billing-param (first (split-at 3 q-param))]
(conj param-vec accu-q-param billing-param)))
param-vec))

I combine some internal data with each vector returned from clojure-
csv's parse-csv. I want to return all that in a new vector of vectors,
but param-vec is empty. What am I doing wrong?

tnx
cmn

Islon Scherer

unread,
Jul 21, 2011, 8:05:49 PM7/21/11
to Clojure
Simple: conj doesn't mutate the vector, it returns a new vector.
Clojure is a (mostly) immutable language, you're trying to solve the
problem in a imperative way, you should solve it in a functional way.

octopusgrabbus

unread,
Jul 21, 2011, 8:36:15 PM7/21/11
to Clojure
And do you have a suggestion for a functional way?

Ken Wesson

unread,
Jul 21, 2011, 10:13:49 PM7/21/11
to clo...@googlegroups.com
On Thu, Jul 21, 2011 at 8:36 PM, octopusgrabbus
<octopus...@gmail.com> wrote:
> And do you have a suggestion for a functional way?

Yes. Change

(doseq [one-full-csv-row all-csv-rows]
(let [accumail-csv-row one-full-csv-row
q-param (zipmap accumail-url-keys accumail-csv-row)
accu-q-param (first (rest (split-at 3 q-param)))
billing-param (first (split-at 3 q-param))]
(conj param-vec accu-q-param billing-param)))

to

(reduce
(fn [one-full-csv-row]
(let [q-param (zipmap accumail-url-keys one-full-csv-row)


accu-q-param (first (rest (split-at 3 q-param)))
billing-param (first (split-at 3 q-param))]

[accu-q-param billing-param]))
[]
all-csv-rows)

(This is assuming you want an output of the form

[[accu-q-param-1 billing-param-1][accu-q-param-2 billing-param-2]...].)

--
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

Ken Wesson

unread,
Jul 21, 2011, 10:15:07 PM7/21/11
to clo...@googlegroups.com
On Thu, Jul 21, 2011 at 10:13 PM, Ken Wesson <kwes...@gmail.com> wrote:
> On Thu, Jul 21, 2011 at 8:36 PM, octopusgrabbus
> <octopus...@gmail.com> wrote:
>> And do you have a suggestion for a functional way?
>
> Yes. Change
>
> (doseq [one-full-csv-row all-csv-rows]
>  (let [accumail-csv-row one-full-csv-row
>        q-param (zipmap accumail-url-keys accumail-csv-row)
>        accu-q-param (first (rest (split-at 3 q-param)))
>        billing-param (first (split-at 3 q-param))]
>    (conj param-vec accu-q-param billing-param)))
>
> to
>
> (reduce
>  (fn [one-full-csv-row]
>    (let [q-param (zipmap accumail-url-keys one-full-csv-row)
>          accu-q-param (first (rest (split-at 3 q-param)))
>          billing-param (first (split-at 3 q-param))]
>      [accu-q-param billing-param]))
>  []
>  all-csv-rows)

Er, that's weird. I don't know what happened there. Obviously that should be

(reduce
 (fn [param-vec one-full-csv-row]


   (let [q-param (zipmap accumail-url-keys one-full-csv-row)
         accu-q-param (first (rest (split-at 3 q-param)))
         billing-param (first (split-at 3 q-param))]

     (conj param-vec [accu-q-param billing-param])))
 []
 all-csv-rows)

octopusgrabbus

unread,
Jul 22, 2011, 5:39:38 AM7/22/11
to Clojure
Thanks for the example.

On Jul 21, 10:15 pm, Ken Wesson <kwess...@gmail.com> wrote:

Ulises

unread,
Jul 22, 2011, 6:05:49 AM7/22/11
to clo...@googlegroups.com
An alternative is to use partition together with interleave:

user> (partition 2 (interleave [ 1 2 ] [ \a \b ]))
((1 \a) (2 \b))

Combined with (map vec ...) you should get:
user> (map vec (partition 2 (interleave [ 1 2 ] [ \a \b ])))
([1 \a] [2 \b])
user>

U

octopusgrabbus

unread,
Jul 22, 2011, 8:47:53 AM7/22/11
to Clojure

On Jul 21, 10:15 pm, Ken Wesson <kwess...@gmail.com> wrote:
> On Thu, Jul 21, 2011 at 10:13 PM, Ken Wesson <kwess...@gmail.com> wrote:
> > On Thu, Jul 21, 2011 at 8:36 PM, octopusgrabbus
> > <octopusgrab...@gmail.com> wrote:
> >> And do you have a suggestion for a functional way?

Is all-csv-rows being re-bound with the results of [] and then
returned as the function's value? And again, thanks. This is exactly
what I was looking for.

(defn ret-params
"Generates all q-parameters and returns them in a vector of
vectors."
[all-csv-rows]
(reduce
(fn [param-vec one-full-csv-row]
(let [q-param (zipmap accumail-url-keys one-full-csv-row)
accu-q-param (first (rest (split-at 3 q-param)))
billing-param (first (split-at 3 q-param))]
(conj param-vec [accu-q-param billing-param])))
[]
all-csv-rows))

Ken Wesson

unread,
Jul 22, 2011, 8:22:57 PM7/22/11
to clo...@googlegroups.com
On Fri, Jul 22, 2011 at 8:47 AM, octopusgrabbus
<octopus...@gmail.com> wrote:
>
> On Jul 21, 10:15 pm, Ken Wesson <kwess...@gmail.com> wrote:
>> On Thu, Jul 21, 2011 at 10:13 PM, Ken Wesson <kwess...@gmail.com> wrote:
>> > On Thu, Jul 21, 2011 at 8:36 PM, octopusgrabbus
>> > <octopusgrab...@gmail.com> wrote:
>> >> And do you have a suggestion for a functional way?
>
> Is all-csv-rows being re-bound with the results of [] and then
> returned as the function's value?

Not really. Nothing's being rebound. But reduce is good for tasks that
amount to accumulating a result while looping over a collection.
(reduce + 0 [3 1 7 2 9]) gives 22 because it starts with 0, then adds
3, then adds 1, then adds 7 ... and (reduce conj [] [1 6 9 5 7]) would
produce [1 6 9 5 7] by taking an empty vector, generating a vector [1]
from it, then a vector [1 6], etc., and returning the end result. In
both cases the objects are not changing; 0 doesn't become 3 and then 4
and [] doesn't become [1] and then [1 6], rather, a reference to 0 is
replaced with a reference to 3 and then one to 4, and a reference to
[] with a reference to [1] and then one to [1 6]. The final reference
gets returned.

Locals aren't being rebound, though; if you stuck a (println
all-csv-rows) in there it would emit the same thing the function got
as parameter. A (println param-vec) inside the (fn ...) would show
changing values, but only because each time the fn got called it got
called with a different value as first argument. During the course of
a single run of the fn, param-vec isn't being rebound either.

But the effect is similar to an old-fashioned Java mutating loop like:

List paramList = new ArrayList();
for (Row v : allCSVRows) {
...
paramList.add(something);
}
return paramList;

but without the potential issues of having mutable state. The
immutable vectors make it closer to

List paramList = Collections.unmodifiableList(new ArrayList());
for (Row v : allCSVRows) {
...
paramList = Collections.unmodifiableList(new
ArrayList(paramList).add(something));
}
return paramList;

but the persistent nature of Clojure vectors makes the copying step
much more efficient than copying the ArrayList above would be, due to
structure sharing.

But then there's the immutable local variables, so conceptually we
aren't even mutating the local paramList to point to new List
instances. With Java you'd have to use a recursive function with no
TCO to do this. In Clojure, conceptually that's what we do do, with
(fn [x y] ... (recur a b)) or (loop [x 1 y 2] ... (recur a b)) and the
latter conceptually a shorthand for ((fn [x y] ... (recur a b)) 1 2)
and (recur) just the same as calling that enclosing function, only
with TCO. But the JVM doesn't really let you do this, so under the
hood it compiles to bytecode that uses mutating locals like paramList
above to carry the parameters through the recursion. But this mutation
is never exposed to the programmer! You can trust that param-vec will
behave as an immutable local for the duration of the fn or loop body,
and when it is "mutated" you're conceptually in a different invocation
of the (in the loop case, itself conceptual) function.

> And again, thanks. This is exactly
> what I was looking for.

You're welcome.

Reply all
Reply to author
Forward
0 new messages