When evaling this super simple core.match expression I get:
(match [(class "3.14")]
[Integer] "Integer"
[Double] "Doubler")
; => "Integer"
How can this be correct, am I missing something fundamental about core.match? Doing a macroexpand-1 on this form gives me:
=> (clojure.core/let [ocr-2751 (class "3.14")] (clojure.core/let [Integer ocr-2751] "Integer"))
Any pointers appreciated.
Like #Arthur said, normally core.match will bind values to symbols. However, apparently, it first tries to match against locals. Who knew?
Anyway, bind the classes as locals in a let before matching and you're good to go:
(let [Integer java.lang.Integer
String java.lang.String]
(match [(class "3.14")]
[Integer] "Integer"
[String] "String"))
core.match allows you to give a name to one of the values in a match clause like so (from the examples)
(let [x 1 y 2]
(match [x y]
[1 b] b
[a 2] a
:else nil))
In this example if the first matching value is a one, then in the expression used to generate the result, the second value will be accessible under the name b.
Because any symbol in a match clause is interpreted as an instruction to bind the corresponding value to that name, in your case the name Integer is being bound to the value java.lang.String
user> (match [(class "3.14")]
[Integer] Integer
[Double] "Doubler")
java.lang.String
user> (match [(class "3.14")]
[name-to-bind] name-to-bind
[Double] "Doubler")
java.lang.String
It's not clear from the documentation that there is a way to use core.match to evaluate the match clause instead of binding to it. It is possible to workaround this by matching against a string, though it looses some of the elegance :
user> (match [(str (class (int 3)))]
["class java.lang.Integer"] "Integer"
["class java.lang.String"] "String"
["class java.lang.Double"] "Double")
"Integer"
user> (match [(str (class "3.14"))]
["class java.lang.Integer"] "Integer"
["class java.lang.String"] "String"
["class java.lang.Double"] "Double")
"String"
user> (match [(str (class 3.14))]
["class java.lang.Integer"] "Integer"
["class java.lang.String"] "String"
["class java.lang.Double"] "Double")
"Double"
Related
I have a something like this:
(def a "text")
(def b "text")
(def c nil)
(def d 8)
(def e "")
(def testlist (list a b c d e ))
Now, is there any way to get the string of the variable names? I assume a 'no' is the most likely answer.
name does not seem to work, as it only returns the value. Does the list only contain the values after def?
EDIT: What i forgot and that may be essential to this question: i can neither use eval nor can i use defmacro, both are not allowed (for safety etc. reasons). So, yeah...
you could use macro to do this (just for fun. i don't think it is a viable usecase at all)
user> (defmacro list+ [& syms]
`(with-meta (list ~#syms) {:vars (quote ~syms)}))
#'user/list+
user> (def testlist (list+ a b c d e))
#'user/testlist
user> (-> testlist meta :vars)
(a b c d e)
user> (defn nil-vals-names [data]
(for [[v name] (map vector data (-> data meta :vars))
:when (nil? v)]
name))
#'user/nil-vals-names
user> (nil-vals-names testlist)
(c)
You will not be able to get a string from the variable names since Clojure will evaluate them as soon as possible to produce the testlist
=> (def testlist (a b c d e ))
("text" "text" nil 8 "")
However, you can quote the list to retrieve the symbol associated to each variable name
=> (def testlist (quote (a b c d e ))) ;; equivalent to '(a b c d e ))
(a b c d e)
And then transform these symbols into strings with the str function
=> (map str testlist)
("a" "b" "c" "d" "e")
Later, you can eval this list to retrieve the value in the context of your namespace
=> (map eval testlist)
("text" "text" nil 8 "")
Note that using eval with an external input (e.g. read-line) can create a security risk in Clojure and other languages.
Moreover, the list has to be evaluated in the same namespace as its definition. Otherwise, Clojure will not be able to resolve the symbols.
=> (ns otherns)
=> (map eval user/testlist)
java.lang.RuntimeException: Unable to resolve symbol: a in this context
The best practice in most case would be to use macros
It's quite unclear what are you trying to achieve, bit still there is a possible way.
meta function take a reference and returns a map with :name field that holds a sting with the variable name:
user=> (def foo 42)
#'user/foo
user=> (meta #'foo)
{:line 1, :column 1,
:file "/some/tmp/file.clj",
:name foo,
:ns #namespace[user]}
This seems paradoxical:
(def foo ["some" "list" "of" "strings"])
`[ ~#(apply concat (map (fn [a] [a (symbol a)]) foo)) ]
; ["some" some "list" list "of" of "strings" strings]
; Changing only the outer [] into {}
`{ ~#(apply concat (map (fn [a] [a (symbol a)]) foo)) }
; RuntimeException Map literal must contain an even number of forms
; However, this works:
`{"some" some "list" list "of" of "strings" strings}
; {"list" clojure.core/list, "of" user/of, "strings" user/strings, "some" clojure.core/some}
Whats going on?
The exception is triggered by the reader because it can't read a literal map with one element which is your unsplice form before evaluation.
Workaround:
`{~#(apply concat (map (fn [a] [a (symbol a)]) foo)) ~#[]}
Unless you are writing a macro, it may be easiest to say:
(into {} (map (fn [a] [a (symbol a)]) foo))
;=> {"some" some, "list" list, "of" of, "strings" strings}
I am trying to define a type definition for this datastructure:
[[:id :int :null false :pk true :autoinc true]
[:fooname [:varchar 40] :null false]
[:age :int :null false]]
And then I have a filter function which removes every vector containing :id as the first element like this:
(defn filter-id-columns [l]
(remove #(= :id (first %)) l))
But, no matter what I try I always get a type error like this:
Type Error (leiningen/code_generator.clj:23:19) Polymorphic function first could not be applied to arguments:
Polymorphic Variables:
x
Domains:
(t/HSequential [x t/Any *])
(t/Option (t/EmptySeqable x))
(t/NonEmptySeqable x)
(t/Option (clojure.lang.Seqable x))
Arguments:
t/Any
Ranges:
x :object {:path [(Nth 0)], :id 0}
nil
x
(t/Option x)
in: (first p1__27917#)
in: (first p1__27917#)
I understand that I am providing the wrong type for the input argument, however, I fail to find the right thing.
Any ideas on how to fix this?
Function parameters default to Any. You must expand your local function definition like this: (t/fn [x :- (U nil (Seqable Any))] ...).
Playing around with Clojure, I noticed that ('+ 2 2) didn't throw an error like I would've expected--it returns 2. I've spent a few minutes playing around:
(def f (cast clojure.lang.IFn 'a-symbol))
(f 5) ;; => nil
(f 5 5) ;; => 5
(f 5 5 5) ;; ArityException Wrong number of args (3) passed to: Symbol
(f "hey") ;; => nil
(f "foo" "bar") ;; => "bar"
(f "foo" "bar" "baz") ;; => ArityException Wrong number of args (3) passed to: Symbol
As far as I can tell, symbols are being cast to some function with the name Symbol, that takes two arguments and returns the second one. I'm guessing it has something to do with the implementation of the symbol class?
When called as a function symbols (like keywords) look them selves up in the map passed as the second argument
user> (def my-map '{a 1 b 2 c 3})
#'user/my-map
user> ('a my-map)
1
user> ('a my-map :not-found)
1
user> ('z my-map :not-found)
:not-found
and return the third argument, if it was passed, to indicate when nothing was found. In your example when you look up a symbol in something that is not a map, for instance the number 5, it doesn't find it:
user> ('z 4 :not-found)
:not-found
user> ('z 'z :not-found)
:not-found
And returns the third argument, or nil if no third argument was passed.
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))