Reputation: 2378
I seem to have nasty problem with clojure I/O (or typesystem). The point is that this function, which I expect to consume collection of collections of strings and numbers or strings, and returns dictionary of strings associated with numbers, like
(costlist '( '("Milk" 4) '("Bread" 2) '("Milk")))
giving
{"Milk" 4, "Bread" 2 }
Defined by
(defn costlist [lst]
;returns list of costs and appropriate names
(let [snds (filter (fn [x] (not (identical? nil x))) (seconds lst))]
(zipmap
(firsts (take (count snds) (firsts lst)))
(snds lst))))
when consuming lists of type clojure.lang.PersistentList (which I converted from clojure.lang.LazySeq) throws error message
clojure.lang.LazySeq cannot be cast to clojure.lang.IFn
Which only confuses me, as any of its arguments don't seem to be LazySeq to me.
Upvotes: 3
Views: 2289
Reputation: 9266
Don't use '
inside '()
. You don't want to quote
'
to get quote
.
Given you write your list as:
'(("Milk" 4)("Bread" 2)("Milk"))
The most elegant probably be:
(def costlist (comp (partial into {})
(partial map vec)
(partial filter second))
similar to
(def costlist #(into {} (map vec (filter second %))))
Unfortunately this will create two lazy-seqs and one would be nicer for performance.
You could (use '(clojure.core [reducers :as r])
and compose yourself a cool reducing function:
(def r-fn (comp (r/map vec) (r/filter second)))
Which leaves you to
(def costlist #(into {} (r-fn %)))
Thanks to into
using reduce conj
your list elements will only be allocated once.
Since order does not matter for hash-maps, you can also user r/fold
. Then your list will be filtered and the elements converted to vectors in parallel (If your list has more than 512 elements).
(def constlist #(r/fold (r/monoid conj hash-map) (r-fn %)))
Depending on the size of the collection, reducers may be a bit too heavy. For a small size like the one from your example I recommend to write your own reducing function:
(def costlist #(reduce (fn [acc [k v]]
(if v
(assoc acc k v)
acc)) {} %)
Upvotes: 0
Reputation: 51450
The problem is that snds
is the lazy seq, so (snds lst)
throws an error. filter
always returns a lazy seq.
Your function also is too complicated. Try to make it simpler:
(defn costlist [lst]
(zipmap (map first lst)
(remove nil? (map second lst))))
Now you can do what you want:
(costlist (list '("Milk" 4) '("Bread" 2) '("Milk")))
I'm using list
because quote
prevents evaluation of an expression (see ToBeReplaced's answer):
=> '( '("Milk" 4) '("Bread" 2) '("Milk"))
((quote ("Milk" 4)) (quote ("Bread" 2)) (quote ("Milk")))
So, you should avoid using quote
for building lists.
Your solution also suggests that nil
values may occur only at the end of the list. You can easily fix it:
(defn costlist [lst]
(->> (filter (comp #{2} count) lst)
(map vec)
(into {})))
So, now
(costlist (list '("Milk" 4) '("Milk") '("Bread" 2)))
will work too.
Upvotes: 4
Reputation: 3584
It's hard to give you a precise answer because you are also using some of your own functions (seconds and firsts). However, you should consider whether you want costlist
to be the quoted form you give above.
The above is equivalent to:
((quote ("Milk" 4)) (quote ("Bread" 2)) (quote ("Milk")))
I think you want something more like:
(list '("Milk" 4) '("Bread" 2) '("Milk"))
In your example, the outermost quote is causing the inner quotes to be quoted!
Upvotes: 1