Is it possible to remove the let statement / avoid the intermediate 'x' in the following code?:
(let [x (f a)]
(when (pred? x) x))
I bumped into this problem in the following use case:
(let [coll (get-collection-somewhere)]
(when (every? some? coll) ; if the collection doesn't contain nil values
(remove true? coll))) ; remove all true values
So if the collection is free of nil values, only not-true values remain, like numbers, strings, or whatever.
So, I'm looking for something like this:
(defn pass-if-true [x pred?]
(when (pred? x) x))
Assuming that you don't want to define that pass-if-true function, the best you can do is an anonymous function:
(#(when (every? some? %)
(remove true? %))
(get-collection-somewhere))
You could also extract the predicate and transformation into parameters:
(#(when (%1 %3) (%2 %3))
(partial every? some?)
(partial remove true?)
(get-collection-somewhere))
The let form is necessary to prevent your collection-building function from running twice:
(f a) or (get-collection-somewhere)
This is a typical idiom and you are doing it correctly.
Of course, you don't need the let if you already have the collection and are not building inside this expression.
However, you may wish to see when-let:
https://clojuredocs.org/clojure.core/when-let
It can save some keystrokes in some circumstances, but this isn't one of them.
Related
I need to write a Clojure function which takes an unevaluated arbitrarily deep nesting of lists as input, and then determines if any item in the list (not in function position) is non-numeric. This is my first time writing anything in Clojure so I am a bit confused. Here is my first attempt at making the function:
(defn list-eval
[x]
(for [lst x]
(for [item lst]
(if(integer? item)
(println "")
(println "This list contains a non-numeric value")))))
I tried to use a nested for-loop to iterate through each item in every nested list. Trying to test the function like so:
=> (list-eval (1(2 3("a" 5(3)))))
results in this exception:
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn listeval.core/eval7976 (form-init4504441070457356195.clj:1)
Does the problem here lie in the code, or in how I call the function and pass an argument? In either case, how can I make this work as intended?
This happens because (1 ..) is treated as calling a function, and 1 is a Long, and not a function. First you should change the nested list to '(1(2 3("a" 5(3)))). Next you can change your function to run recursively:
(defn list-eval
[x]
(if (list? x)
(for [lst x] (list-eval lst))
(if (integer? x)
(println "")
(println "This list contains a non-numeric value"))))
=> (list-eval '(1(2 3("a" 5(3)))))
There is a cool function called tree-seq that does all the hard work for you in traversing the structure. Use it then remove any collections, remove all numbers, and check if there is anything left.
(defn any-non-numbers?
[x]
(->> x
(tree-seq coll? #(if (map? %) (vals %) %))
(remove (some-fn coll? number?))
not-empty
boolean))
Examples:
user=> (any-non-numbers? 1)
false
user=> (any-non-numbers? [1 2])
false
user=> (any-non-numbers? [1 2 "sd"])
true
user=> (any-non-numbers? [1 2 "sd" {:x 1}])
true
user=> (any-non-numbers? [1 2 {:x 1}])
false
user=> (any-non-numbers? [1 2 {:x 1 :y "hello"}])
true
If you want to consider map keys as well, just change (vals %) to (interleave (keys %) (vals %)).
quoting
As others have mentioned, you need to quote a list to keep it from being evaluated as
code. That's the cause of the exception you're seeing.
for and nesting
for will only descend to the nesting depth you tell it to. It is not a for loop,
as you might expect, but a sequence comprehension, like the the python list comprehension.
(for [x xs, y ys] y) will presume that xs is a list of lists and flatten it.
(for [x xs, y ys, z zs] z) Is the same but with an extra level of nesting.
To walk down to any depth, you'd usually use recursion.
(There are ways to do this iteratively, but they're more difficult to wrap your head around.)
side effects
You're doing side effects (printing) inside a lazy sequence. This will work at the repl,
but if you're not using the result anywhere, it won't run and cause great confusion.
It's something every new clojurian bumps into at some point.
(doseq is like for, but for side effects.)
The clojure way is to separate functions that work with values from functions that
"do stuff", like printing to the console of launching missiles, and to keep the
side effecting functions as simple as possible.
putting it all together
Let's make a clear problem statement: Is there a non number anywhere inside an
arbitrarily nested list? If there is, print a message saying that to the console.
In a lot of cases, when you'd use a for loop in other langs reduce is what you want in clojure.
(defn collect-nested-non-numbers
;; If called with one argument, call itself with empty accumulator
;; and that argument.
([form] (collect-nested-non-numbers [] form))
([acc x]
(if (coll? x)
;; If x is a collection, use reduce to call itself on every element.
(reduce collect-nested-non-numbers acc x)
;; Put x into the accumulator if it's a non-number
(if (number? x)
acc
(conj acc x)))))
;; A function that ends in a question mark is (by convention) one that
;; returns a boolean.
(defn only-numbers? [form]
(empty? (collect-nested-non-numbers form)))
;; Our function that does stuff becomes very simple.
;; Which is a good thing, cause it's difficult to test.
(defn warn-on-non-numbers [form]
(when-not (only-numbers? form)
(println "This list contains a non-numeric value")))
And that'll work. There already exists a bunch of things that'll help you walk a nested structure, though, so you don't need to do it manually.
There's the clojure.walk namespace that comes with clojure. It's for when you have
a nested thing and want to transform some parts of it. There's tree-seq which is explained
in another answer. Specter is a library which is
a very powerful mini language for expressing transformations of nested structures.
Then there's my utils library comfy which contains reduce versions of the
functions in clojure.walk, for when you've got a nested thing and want to "reduce" it to a single value.
The nice thing about that is that you can use reduced which is like the imperative break statement, but for reduce. If it finds a non-number it doesn't need to keep going through the whole thing.
(ns foo.core
(:require
[madstap.comfy :as comfy]))
(defn only-numbers? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (or (coll? x) (number? x))
ret
(reduced false)))
true
form))
Maybe by "any item in the list (not in function position)" you meant this?
(defn only-numbers-in-arg-position? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (and (list? x) (not (every? (some-fn number? list?) (rest x))))
(reduced false)
ret))
true
form))
I am programming something that doesn't have side-effects, but my code is not very readable.
Consider the following piece of code:
(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)
I'm trying to use csv_data in a block of code. What is a clean way of conditioning on the presence of a header_row? I've looked at if-let, but couldn't see how that could help here.
I have run into similar situations with functional for-loops as well where I'm binding the result to a local variable, and the code looks like a pile of expressions.
Do I really have to create a separate helper function in so many cases?
What am I missing here?
Use the cond->> macro
(let [csv_data (cond->> data_rows
header_row (cons header-row)]
)
It works like the regular ->> macro, but before each threading form a test expression has to be placed that determines whether the threading form will be used.
There is also cond->. Read more about threading macros here: Official threading macros guide
First, don't use underscore, prefer dashes.
Second, there is nothing wrong with a little helper function; after all, this seems to be a requirement for handling your particular data format.
Third, if you can change your data so that you can skip those decisions and have a uniform representation for all corner cases, this is even better. A header row contains a different kind of data (column names?), so you might prefer to keep them separate:
(let [csv {:header header :rows rows}]
...)
Or maybe at some point you could have "headers" and "rows" be of the same type: sequences of rows. Then you can concat them directly.
The ensure-x idiom is a very common way to normalize your data:
(defn ensure-list [data]
(and data (list data)))
For example:
user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil
And thus:
(let [csv (concat (ensure-list header) rows)]
...)
i would propose an utility macro. Something like this:
(defmacro update-when [check val-to-update f & params]
`(if-let [x# ~check]
(~f x# ~val-to-update ~#params)
~val-to-update))
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> (:header :data1 :data2)
user> (let [header-row nil
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> [:data1 :data2]
it is quite universal, and lets you fulfill more complex tasks then just simple consing. Like for example you want to reverse some coll if check is trueish, and concat another list...
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows
(fn [h d & params] (apply concat (reverse d) params))
[1 2 3] ['a 'b 'c])]
csv-data))
;;=> (:data2 :data1 1 2 3 a b c)
update
as noticed by #amalloy , this macro should be a function:
(defn update-when [check val-to-update f & params]
(if check
(apply f check val-to-update params)
val-to-update))
After thinking about the "cost" of a one-line helper function in the namespace I've came up with a local function instead:
(let [merge_header_fn (fn [header_row data_rows]
(if header_row
(cons header_row data_rows)
data_rows))
csv_data (merge_header_fn header_row data_rows) ]
...
<use csv_data>
...
)
Unless someone can suggest a more elegant way of handling this, I will keep this as an answer.
I have a lazy-seq of maps and I'm attempting to remove maps from that lazy-seq based on the return value from another function. The other function will return true or false depending on whether or not a call of get returns a value equal to the parameter. The problem is the function isn't working correctly and I'm not too sure why.
(defn filter-by-name "Filter by names" [name m]
(if (= name (get m :name_of_person)) true false))
;To be called on each map
(defn remove-nonmatching-values "Remove anything not matching" [filter-val all-maps]
(map #(remove (filter-by-name filter-val %)) all-maps))
;trying to call on the lazy seq
You only need to call remove on the sequence of maps.
(defn remove-nonmatching-values
"Remove anything not matching"
[filter-val all-maps]
(remove #(filter-by-name filter-val %) all-maps))
Check Clojure's remove doc
(remove pred coll)
Returns a lazy sequence of the items in coll for which
(pred item) returns false. pred must be free of side-effects.
Returns a transducer when no collection is provided.
A function that produces the test-function you need for a given name is
(defn name-matcher [name]
(fn [m] (= name (:name_of_person m))))
All you have to do is filter the maps accordingly:
(defn retain-matching-maps [name maps]
(filter (name-matcher name) maps))
For example,
(retain-matching-maps "hello" (list {:name_of_person "hello"} {:name_of_person "bye"}))
;({:name_of_person "hello"})
I have got rid of
the comments (which are implied by the function names)
the if (as noted by Guillermo)
the get (Keywords - or maps - are implicit get functions)
the double negative in the function name remove-nonmatching-values.
You could also use :name instead of :name-of-person. The more succinctly you express your program, the less likely you are to make mistakes.
Is there a way to generically get metadata for arguments to a function in clojure? The answer posted in this question does not, actually, work in general:
user> (defn foo "informative dox!" [] 1)
#'user/foo
user> (defmacro get-docs [func] `(:doc (meta (var ~func))))
#'user/get-docs
user> (get-docs foo)
"informative dox!"
user> (get-docs (identity foo))
; Evaluation aborted.
user> (defn process-docs [f] (let [docs (get-docs f)] (reverse docs)))
; Evaluation aborted.
The second-to-last line doesn't work because you can't call var on the list (identity foo), and the last line doesn't even compile because the compiler complains about being unable to resolve f.
Most of the solutions for this problem I've found rely on the idea that you have access to the symbol in the function's definition, or something like that, so that you can do something like (resolve 'f) or (var f). But I want something that I can use on the argument to a function, where you don't know that information.
Essentially, I'd like an expression I can put in place of the question marks below to get the metadata of #'map:
(let [x map] (??? x))
its a mouthful though possible:
(let [x map]
(:doc (meta (second (first (filter #(and (var? (second %))
(= x (var-get (second %))))
(ns-map *ns*)))))))
produces the desired result:
"Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
under the hood Namespaces are essentially maps of names to vars and the vars contain functions. you can search the contents of these vars for the one that matches the function you are seeking and then look at it's associated var and get the metadata from that var.
Can Clojure implement (g ∘ f) constructions like Haskell's g . f? I'm currently using workarounds like (fn [n] (not (zero? n))), which isn't nearly as nice :)
There is a function to do this in clojure.core called comp. E.g.
((comp not zero?) 5)
; => true
You might also want to consider using -> / ->>, e.g.
(-> 5 zero? not)
; => true
You can use Clojure's reader-macro shortcut for anonymous functions to rewrite your version in fewer keystrokes.
user=> (#(not (zero? %)) 1)
true
For the specific case of composing not and another function, you can use complement.
user=> ((complement zero?) 1)
true
Using not in combination with when and if is common enough that if-not and when-not are core functions.
user=> (if-not (zero? 1) :nonzero :zero)
:nonzero
Another way is (reduce #(%2 %1) n [zero? not]) but I like (comp not zero?), already mentioned by Michal, better.