获取Clojure参数列表

时间:2013-09-14 03:00:35

标签: clojure

我想要一些东西,它给我传递给函数的实际值序列,类似于javascript函数中的arguments值。

我知道我可以使用

获取整个函数参数列表
(defn fx [& args]
 args)

<= (fx {:a 1} 2)
=> ({:a 1} 2)

但这消除了我的功能上的arity。我想要像

这样的东西
(defn fx [{:keys [a]} b]
 (MAGIC_FUNCTION_THAT_RETURNS_THE_ARGS_VALUES))

<= (fx {:a 1} 2)
=> ({:a 1} 2)

是否可以获取传递给函数的原始值序列?

6 个答案:

答案 0 :(得分:4)

执行函数体时,参数已经被解构。您可以定义自己的defn宏并公开这些值。我知道Lighttable在他们的Instarepl中执行此操作以显示参数值。

答案 1 :(得分:2)

我不知道如你所描述的那样做的方法,但根据你想要做的事情,有一些选择。

如果您想要确保仅使用两个参数调用该函数,请考虑前提条件:

(defn fx [& args]
  {:pre [(= 2 (count args))]}
  args)

user=> (fx 1 2)
(1 2)
user=> (fx 1 2 3)
AssertionError Assert failed: (= 2 (count args))  user/fx (NO_SOURCE_FILE:1)

如果您想要跟踪功能的预期,但仍然可以访问所有参数的矢量,则可以添加自己的元数据:

(defn
  ^{:my.ns/arglists '([{:keys [a]} b])}
  fx [& args]
    args)

user=> (fx 1 2)
(1 2)
user=> (-> #'fx meta :my.ns/arglists first)
[{:keys [a]} b]

如果您只想访问您描述的结构化值并访问args值,可以使用let:

(defn fx [{:keys [a]} b]
  (let [args [{:a a} b]]
    [a b args]))

user=> (fx {:a 1 :c 3} 2)
[1 2 [{:a 1} 2]]
user=> (fx {:a 1 :c 3} 2 4)
ArityException Wrong number of args (3) passed to: user$fx  clojure.lang.AFn.throwArity (AFn.java:437)

您也可以将这些组合起来。

答案 2 :(得分:2)

使用参数销毁可以提供帮助。以下对我来说很好(据我所知,它也适用于旧版本的clojure)。

(defn example [ & [a b :as args]] [a b args])
(example 1 2) 
 => [1 2 (1 2)]

关键是你可以在&之后破坏论证。缺点是可以使用比预期更多的参数调用函数(例如(example 1 2 3)是一个有效的调用。如果这可能是一个问题,应该特别小心。

注意:我在搜索类似功能时遇到了这个问题。我一直在挖掘并使用here:as中的一个想法,正如this回答中提到的那样,我找到了解决问题的方法。

答案 3 :(得分:1)

不太好,因为它需要将params作为向量传递,但似乎很适合

user.main=> (defn fx [[{:keys [a] :as e} b :as o]] [a b e o])
#'user.main/fx
user.main=> (fx [{:a 1} 2])
[1 2 {:a 1} [{:a 1} 2]]
user.main=> 

答案 4 :(得分:0)

这是我能做的最好的。

(def ^:dynamic *arguments* nil)

(defn unstructure [form]
  (cond
   (or (vector? form) (map? form)) (gensym)
   (= form '&) '&
   :else form))

(defmacro bind-args-defn [name args & body]
  (let [simple-args (vec (map unstructure args))
        i (.lastIndexOf simple-args '&)
        [h r] (split-at (if (neg? i) (count simple-args) i) simple-args)
        r (drop 1 r)]
    `(defn ~name
       ~simple-args
       (binding [*arguments* (lazy-cat ~@(map vector h) ~@r)]
         (let [~args *arguments*]
           ~@body)))))

(bind-args-defn                            ;;my special val binding defn
 silly                                     ;;the name
 [{:keys [a]} [b & c] & d]                 ;;the arg vector
 {:vals *arguments* :a a :b b :c c :d d})  ;;the body

显然,这不接受可以传递给defn的全套defn选项(元数据,文档字符串,前置和后置,arities等),但我认为它说明了这个想法。

它的工作原理是捕获args向量,然后创建一个与原始simple-args长度相同但没有解构的args向量;使用它作为defn参数向量。然后,它会将此simple-args向量按到一个没有&的平面向量中,并将其分配给*arguments*。然后使用原始*arguments*向量对args进行解构。有点复杂,但它确实是我想要的。

> (silly {:a 1} [2 3 4] 5 6)
  {:vals ({:a 1} [2 3 4] 5 6), :a 1, :b 2, :c (3 4), :d (5 6)}

答案 5 :(得分:0)

在此示例中,您可以使用宏将参数绑定到符号_args

(defmacro defn-args [name args & body]
  `(defn ~name ~args
     (let [~'_args ~args]
       ~@body)))

然后,您可以在正文中使用_args来引用参数:

user> (defn-args foo [{:keys [a b]} y z] _args)

user> (foo {:a 1 :b 10} 2 3)
[{:a 1, :b 10} 2 3]