简体   繁体   中英

How should I pass a String argument to clojure.core/apply

I have the following piece of code:

  (condp apply "string argument"
    predicate1? "Result1"
    predicate2? "Result2"
    predicate3? "Result3"
                "Else")

Currently, "string argument" , when passed to apply gets interpreted as a seq, therefore each character becomes a parameter to the predicate:

(apply predicate1? \s \t \r \i \n ...)

A simple solution would be to wrap the argument in a vector:

(condp apply ["string argument"] ...)

I'm not sure this is very idiomatic. Is there a better solution?

I don't think there is a more idiomatic way than using cond directly, binding "string argument" to a symbol and passing it to each predicate. Everything else looks confusing to people reading your code and involves extra function calls.

Extra magic could be achieved with the following helper macro:

(defmacro pcond
  "Translates clauses [pred result-expr] to a cond form:

   (cond
    (pred expr) result-expr
    ...)

   A single result-expr can follow the clauses, and it will be appended
   to the cond form under a generated test expression that returns logical 
   true."
  [expr & clauses]
  (let [expr-sym (gensym "expr")
        else-kw (keyword (gensym "else"))
        step (fn [[pred-form clause-form]]
               (if (= else-kw clause-form)
                 [else-kw pred-form]
                 [(list pred-form expr-sym) clause-form]))
        body (->> clauses
                  (partition 2 2 [else-kw])
                  (mapcat step))]
    `(let [~expr-sym ~expr]
       (cond ~@body))))

You can use it so

(pcond "String argument"
    predicate1 "Result1"
    predicate2 "Result2"
    "Else")

It macroexpands directly to cond:

(clojure.core/let [expr13755 "String argument"] 
   (clojure.core/cond 
     (predicate1 expr13755) "Result1" 
     (predicate2 expr13755) "Result2" 
     :else13756 "Else"))

You can use the anonymous function #(%1 %2) for what you are trying to do. IMO it's better than wrapping the condp expression in a vector, but either way works, neither is really straigthforward.

(condp #(%1 %2) "string argument"
    string? "string"
    map?    "map"
    vector? "vector"
    "something else")

;= Result1

EDIT (copied from the comments)

I'm no expert on macros, but what I've heard over and over is that you shouldn't write a macro if a function will do and condp provides all the elements to use the power of Clojure functions. An approach that doesn't involve macros and improves readability could be defining a function (def apply-pred #(%1 %2)) and use it with condp .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM