10

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

我知道我可以使用获取整个函数参数列表

(defn fx [& args]
 args)

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

但这消除了我的功能的重要性。我想要类似的东西

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

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

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

4

6 回答 6

4

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

于 2013-09-14T03:45:22.467 回答
3

使用参数破坏会有所帮助。以下对我来说很好(据我所知,它也适用于旧版本的clojure)。

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

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

注意:我在搜索类似功能时遇到了这个问题。我一直从这里挖掘和使用一个想法,:as正如这个答案中所建议的那样,我找到了解决我的问题的方法。

于 2015-07-29T12:13:56.307 回答
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)

如果您想跟踪函数的预期数量,但仍然可以访问所有 args 的向量,您可以添加自己的元数据:

(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)

你也可以做这些的组合。

于 2013-09-15T04:33:35.900 回答
2

_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]
于 2017-01-31T09:51:13.020 回答
1

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

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=> 
于 2013-09-14T03:22:10.763 回答
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 选项(元数据、文档字符串、pre 和 post、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)}
于 2013-09-15T05:19:16.313 回答