I have a variable "testtext".
Depending on a other variable "testvalue", thats always the value 1 or 2, it needs to be set to something.
So if testvalue is 1, i need to set testtext to "its one".
And when testvalue is 2, i need to set testtext to "its two".
Right now i have:
(cond
(= testvalue 1) (var-set testtext "its one")
(= testvalue 2) (var-set testtext "its two")
:else (var-set testtext "ERROR")
)
But i get the error "String cannot be cast to clojure.lang.Var"
So my question is, how do i properly set a string value, assuming that's what I did wrong.
You want something more like this:
(let [result (cond
(= testvalue 1) "its one"
(= testvalue 2) "its two"
:else "ERROR" ) ]
(println "result:" result))
Using var-set is very rare in Clojure. I can't answer in too much more detail without knowing your exact use-case.
If you really need something like a Java variable, you could use a Clojure atom:
(def result (atom nil))
(cond
(= testvalue 1) (reset! result "its one")
(= testvalue 2) (reset! result "its two")
:else (reset! result "ERROR" ))
(println "result:" #result))
You probably want something like:
(defn func
[test-value]
(let [test-var (cond
(= test-value 1) "it's one"
(= test-value 2) "it's two"
:else "ERROR")]
test-var))
=> #'user/func
(func 1)
=> "it's one"
(func 2)
=> "it's two"
(func 3)
=> "ERROR"
The let form let's you assign values to vars. Sticking it in a function is a convenient way to return the result.
On most contexts you don't want to modify an existing "variable's" value, instead you use let to bind a value to a symbol. It would be easier to answer if we knew the bigger picture, but as already mentioned atom is a datatype to which you can atomically swap in a new value.
I'd say this is more idiomatic, although not quite what you asked:
(def lookup-table
{1 "its one"
2 "its two"})
(defn make-lookup [i]
(get lookup-table i "ERROR"))
(doseq [i (range 4)]
(println i "=" (make-lookup i)))
Prints:
0 = ERROR
1 = its one
2 = its two
3 = ERROR
Instead of thinking how to assign a value to a variable, you should think what do you want to use that value for.
Related
I'm a newbie to Clojure. I think I'm trying to solve this procedurally and there must be a better (more functional) way to do this in Clojure...
The -main function can receive a variable number of 'args'. I would like to print them out but avoid an IndexOutOfBoundsException. I thought I could mimic Java and use a case fall-through to minimize the code involved, but that didn't work:
(defn -main [& args]
(println "There are" (str (count args)) "input arguments.")
(println "Here are args:" (str args))
(let [x (count args)]
(case x
(> x 0) (do
(print "Here is the first arg: ")
(println (nth args 0)))
(> x 1) (do
(print "Here is the 2nd arg: ")
(println (nth args 1)))
(> x 2) (do
(print "Here is the 3rd arg: ")
(println (nth args 2))))))
(doseq [[n arg] (map-indexed vector arguments)]
(println (str "Here is the argument #" (inc n) ": " (pr-str arg))))
map-indexes is like map but adds index number in the beginning.
So it goes item by item through arguments, packs index and item into a vector and by destructruing index number and item are mapped to [n arg].
Since clojure begins counting from 0, you use (inc n) to begin counting from 1. pr-str is pretty print string. The str joins all string components together.
there is also a handy formatting facility in clojure's core library: cl-format, which is the port of common lisp's format syntax. It includes a nice way to print out collections:
(require '[clojure.pprint :refer [cl-format]])
(let [args [:a :b :c :d]]
(cl-format true "~{here is the ~:r arg: ~a~%~}"
(interleave (rest (range)) args)))
;; here is the first arg: :a
;; here is the second arg: :b
;; here is the third arg: :c
;; here is the fourth arg: :d
some more information about what format can do is here
I have 2 bindings I'm calling path and callback.
What I am trying to do is to return the first non-empty one. In javascript it would look like this:
var final = path || callback || "";
How do I do this in clojure?
I was looking at the "some" function but I can't figure out how to combine the compjure.string/blank check in it. I currently have this as a test, which doesn't work. In this case, it should return nil I think.
(some (clojure.string/blank?) ["1" "2" "3"])
In this case, it should return 2
(some (clojure.string/blank?) ["" "2" "3"])
(first (filter (complement clojure.string/blank?) ["" "a" "b"]))
Edit: As pointed out in the comments, (filter (complement p) ...) can be rewritten as (remove p ...):
(first (remove clojure.string/blank? ["" "a" "b"]))
If you are so lucky to have "empty values" represented by nil and/or false you could use:
(or nil false "2" "3")
Which would return "2".
An equivalent to your JavaScript example would be:
(let [final (or path callback "")]
(println final))
If you want the first non blank string of a sequence you can use something like this:
(first (filter #(not (clojure.string/blank? %)) ["" "2" "3"]))
This will return 2
What i don't understand is your first example using the some function, you said that it should return nil but the first non blank string is "1".
This is how you would use the some function:
(some #(when-not (empty? %) %) ["" "foo" ""])
"foo"
(some #(when-not (empty? %) %) ["bar" "foo" ""])
"bar"
As others have pointed out, filter is another option:
(first (filter #(not (empty? %)) ["" "" "foo"])
"foo"
A third option would be to use recursion:
(defn first-non-empty [& x]
(let [[y & z] x]
(if (not (empty? y))
y
(when z (recur z)))))
(first-non-empty "" "bar" "")
"bar"
(first-non-empty "" "" "foo")
"foo"
(first-non-empty "" "" "")
nil
I used empty? instead of blank? to save on typing, but the only difference should be how whitespace is handled.
It was difficult for me to tell exactly what you wanted, so this is my understanding of what you are trying to do.
In my case, I wanted to find if an item in one report was missing in a second report. A match returned nil, and a non-match returned the actual item that did not match.
The following functions wind up comparing the value of a mapped value with a key.
Using something like find-first is probably what you want to do.
(defn find-first
"This is a helper function that uses filter, a comparision value, and
stops comparing once the first match is found. The actual match
is returned, and nil is returned if comparision value is not matched."
[pred col]
(first (filter pred col)))
(defn str-cmp
"Takes two strings and compares them. Returns 0 if a match; and nil if not."
[str-1 str-2 cmp-start-pos substr-len]
(let [computed-str-len (ret-lowest-str-len str-1 str-2 substr-len)
rc-1 (subs str-1 cmp-start-pos computed-str-len)
rc-2 (subs str-2 cmp-start-pos computed-str-len)]
(if (= 0 (compare rc-1 rc-2))
0
nil)))
(defn cmp-one-val
"Return nil if first key match found,
else the original comparision row is returned.
cmp-row is a single sequence of data from a map. i
cmp-key is the key to extract the comparision value.
cmp-seq-vals contain a sequence derived from
one key in a sequence of maps.
cmp-start and substr-len are start and stop
comparision indicies for str-cmp."
[cmp-row cmp-key cmp-seq-vals cmp-start substr-len]
(if (find-first #(str-cmp (cmp-key cmp-row) %1 cmp-start substr-len) cmp-seq-vals)
nil
cmp-row))
I did:
user=> (println (for [line (range 1 5)] (str "line=" line)))
and got:
(line=1 line=2 line=3 line=4)
but I wanted only line=1 line=2 line=3 line=4 as a string. How do I do this?
You need 'apply'.
(apply println (for [line (range 1 5)] (str "line=" line)))
Alternatively,
(println (apply str (interpose " " (map #(str "line=" %) (range 1 5)))))
What about this one. doseq is about doing side-effect on sequences and printing is a side-effect.
(doseq [line (range 1 5)
:let [msg (str "line=" line " ")]]
(print msg))
Instead of apply, you could alternatively use reduce like so:
user> (reduce #(str %1 " line=" %2) "" (range 1 5))
=> " line=1 line=2 line=3 line=4"
The reduce function is a function that takes a function (let's call if f), a "starting value", and then a list of things that will be used as the second argument to f. It lazily calls f on the starting value and the first item in the list, then calls f on what this returns and the second item in the list, then calls f on what this returns and the third item in the list etc., until it has exhausted all the items in the list (or rather--since it's lazy, it will only go through the whole list if you "ask it to").
If you don't like starting space, you could wrap the whole thing in triml (you'd have to do (use 'clojure.string) first). Or you could do (reduce #(str %1 "line=" %2 " ") (range 1 5)), which would put the space at the end.
My experience has been that anytime you can do something with apply, you can do it slightly more elegantly with reduce. More importantly, my reduce alternative has always usually been faster than my apply one. I certainly can't vouch that this will be true always, and I haven't done speed tests for your particular problem.
Edit
I did some rough timings, using my version (reduce) versus JohnJ's second suggestion (apply), and found that they were pretty similar for up to (range 1 100), but that by (range 1 500), the apply version was at least 4x faster.
In Clojure nil? checks for nil. How does one check for not nil?
I want to do the Clojure equivalent of the following Java code:
if (value1==null && value2!=null) {
}
Follow-up: I was hoping for a not nil check instead of wrapping it with not. if has a if-not counterpart. Is there such a counterpart for nil??
After Clojure 1.6 you can use some?:
(some? :foo) => true
(some? nil) => false
This is useful, eg, as a predicate:
(filter some? [1 nil 2]) => (1 2)
Another way to define not-nil? would be using the complement function, which just inverts the truthyness of a boolean function:
(def not-nil? (complement nil?))
If you have several values to check then use not-any?:
user> (not-any? nil? [true 1 '()])
true
user> (not-any? nil? [true 1 nil])
false
If you are not interested in distinguishing false from nil, you can just use the value as the condition:
(if value1
"value1 is neither nil nor false"
"value1 is nil or false")
In Clojure, nil counts as false for the purposes of conditional expressions.
As a result (not x) works actually works exactly the same as as (nil? x) in most cases (with the exception of boolean false). e.g.
(not "foostring")
=> false
(not nil)
=> true
(not false) ;; false is the only non-nil value that will return true
=> true
So to answer your original question you can just do:
(if (and value1 (not value2))
...
...)
condition: (and (nil? value1) (not (nil? value2)))
if-condition: (if (and (nil? value1) (not (nil? value2))) 'something)
EDIT:
Charles Duffy provides correct custom definition for not-nil?:
You want a not-nil? Easily done: (def not-nil? (comp not nil?))
If you want your test to return true when given false, then you need one of the other answers here. But if you just want to test that returns a truthy value whenever it's passed something other than nil or false, you can use identity. For example, to strip nils (or falses) from a sequence:
(filter identity [1 2 nil 3 nil 4 false 5 6])
=> (1 2 3 4 5 6)
You can try when-not :
user> (when-not nil (println "hello world"))
=>hello world
=>nil
user> (when-not false (println "hello world"))
=>hello world
=>nil
user> (when-not true (println "hello world"))
=>nil
user> (def value1 nil)
user> (def value2 "somevalue")
user> (when-not value1 (if value2 (println "hello world")))
=>hello world
=>nil
user> (when-not value2 (if value1 (println "hello world")))
=>nil
If you want a not-nil? function, then I'd suggest just defining it as follows:
(defn not-nil?
(^boolean [x]
(not (nil? x)))
Having said that it is worth comparing the usage of this to the obvious alternative:
(not (nil? x))
(not-nil? x)
I'm not sure that introducing an extra non-standard function is worth it for saving two characters / one level of nesting. It would make sense though if you wanted to use it in higher order functions etc.
One more option:
(def not-nil? #(not= nil %))
Say I have a map of this form:
(def m {:a "A" :b "B"})
and I want to do something if :a and :b are both not nil, I can do:
(if-let [a (:a m)]
(if-let [b (:b m)]
... etc ))
or
(if (and (:a m) (:b m))
(let [{a :a b :b} m]
... etc ))
or even
(if (every? m [:a :b])
(let [{a :a b :b} m]
... etc ))
Is there a neater (ie one-line) way to achieve this?
I think a macro may be necessary here to create the behavior you want. I have never written one (yet) but the following representation suggests to me that this might be fairly straightforward:
(let [{:keys [a b]} m]
(when (every? identity [a b])
(println (str "Processing " a " and " b))))
Using the :keys form of destructuring binding and every? enables a single specification of a vector of keys to destructure and check, and the bound locals are available in a following code block.
This could be used to make a macro such as (when-every? [keys coll] code-with-bindings)
I may update this answer with the macro code if I can take the time to work out how to do it.
You could use map destructuring -- a useful feature of Clojure. This also exploits the facts that and is short-circuiting, and any key in the first map not found in the second map gets nil, a falsy value:
(let [{a :a b :b} {:a 1 :b "blah"}]
(and a b (op a b)))
Okay, so it's two lines instead of one .... also this doesn't distinguish between nil and other falsy values.
not-any? is a nice shortcut for this:
user> (not-any? nil? [(m :a) (m :b)])
true
user> (not-any? nil? [(m :a) (m :b) (m :d)])
false
user>
I am not quite sure what you want to do if the keys have non-nil values or whether you want non-nil keys or values returned. So, I just solved it for non-nil keys being returned.
You'd use the following as an intermediate step as part of a final solution.
I'm showing all the steps I used, not to be pedantic, but to provide a complete answer. The namespace is repl-test. It has a main associated with it.
repl-test.core=> (def m {:a "A" :b "B" :c nil})
#'repl-test.core/m
repl-test.core=> (keys m)
(:a :c :b)
and then finally:
; Check key's value to determine what is filtered through.
repl-test.core=> (filter #(if-not (nil? (%1 m)) (%1 m)) (keys m) )
(:a :b)
By the way I found an ugly one-liner, which works because and returns the last thing in its argument list if they're all true:
(if-let [[a b] (and (:a m) (:b m) [(:a m)(:b m)])]
(println "neither " a " nor " b " is falsey")
(println "at least one of " a " or " b " is falsey"))