2013-09-14 4 views
8

Мне нужно что-то, что дает мне последовательность действительных значений, переданных функции, аналогичную значению arguments в функции javascript.Получить список аргументов Clojure

Я знаю, что я могу захватить весь список аргументов функции с помощью

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

Возможно ли получить необработанную последовательность значений, переданных функции?

+0

, если я не ошибаюсь, функции JavaScript принимают переменное число аргументов, а значение аргументов в JavaScript, таким образом, более или менее аналогично синтаксису vararg в clojure, о котором вы уже указали. – Kevin

+0

Насколько я могу судить по документации, у Clojure нет ничего подобного. – Barmar

+1

Обратите внимание, что вы можете присвоить имя деструктурированному значению: '[{: keys [a]: as m} b]'. Тогда у вас будет доступ к нему в целом: '[m b]'. –

ответ

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

К тому времени тело функции выполняется, параметры уже деструктурированный. Вы можете определить свой собственный макрос defn и выставить эти значения. Я знаю, что Lighttable делает это в своем Instarepl для отображения значений аргументов.

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] 

Если вы просто хотите доступ к деструктурированным значениям, описанных и доступу к значению арга, вы можете использовать позволить:

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

Вы также можете сделать комбинацию из них.

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 [email protected](map vector h) [email protected])] 
     (let [~args *arguments*] 
      [email protected]))))) 

(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, но я думаю, что это иллюстрирует идею.

Он работает, захватывая вектор args, а затем создавая вектор той же длины, что и у оригинала args, но без разрушения; используя это как вектор аргументов defn. Затем он массирует этот вектор в виде плоского вектора без &, который он присваивает *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)} 
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 ответе, я нашел решение моей проблемы.

0

Вы можете использовать макрос, чтобы связать аргументы символа, _args в этом примере.

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

Вы можете использовать _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] 
Смежные вопросы