2012-08-08 5 views
3

Я хотел бы получить имя var, определенное вне функции, изнутри функции. Имя должно быть именем, которое я использовал в области исходного определения, а не вложенными привязками, где я фактически пытаюсь использовать это имя.Как получить имя аргумента в Clojure?

Так что я хотел бы сделать что-то вроде (академический пример):

(defn f1 [x1] (println "hello, you passed var name >>" (get-var-name x1) "<<") 
(defn f2 [x2] (f1 x2)) 
(defn f3 [x3] (let [zzz x3] (f2 zzz)) 
(def my-var 3.1414926) 
(f3 my-var) 
user> hello, you passed var name >>my-var<< 

Я могу сделать этот макрос на основе некоторых вещей я нашел:

(defmacro get-var-name [x] 
    `(:name (meta (var ~x)))) 

Это работает при вызове например, из РЕПЛ, но компилятор дроссели, когда вызывается из «внутри» области видимости, например

(defn another-func [y] 
    (get-var-name y)) 

компилятор говорит «, говоря U nable для разрешения var y ". (macroexpand...) показывает, что он пытается найти локальную переменную y в текущем пространстве имен, а не исходную переменную в текущем пространстве имен. Я думаю, (var...) ищет только пространства имен, поэтому это предотвращает работу макроса как внутри функции, так и с помощью другой привязки, такой как let.

I думаю, Я застрял, чтобы вручную получить имя переменной из той же области, где я определяю переменную и передаю ее в качестве дополнительного параметра. Есть ли более элегантный способ передать информацию имени var через цепочку привязок до точки, где она используется? Это было бы плохой зад.

благодаря

ответ

8

Это не возможно, чтобы получить имя вара, используемый во внешней сфере внутри функций - функция только получает значениепередаются в качестве параметра во время выполнения, а не сам вара.

Единственное, что вы могли бы сделать, это использовать макросы вместо функций на каждом уровне. Это позволяет передавать вар сам через различные макросы во время компиляции:

(defmacro f1 [x1] `(println "hello, you passed var name >>" ~(str x1) "<<")) 
(defmacro f2 [x2] `(f1 ~x2)) 
(defmacro f3 [x3] (let [zzz x3] `(f2 ~zzz))) 

(f3 my-var) 
=> hello, you passed var name >> my-var << 

Это очень некрасиво - вы, конечно, не хотите писать весь код с макросами только, чтобы получить эту возможность! Это может иметь смысл, хотя в некоторых специализированных обстоятельствах, например. если вы создаете какой-то макрос DSL.

+4

Это, сто раз это. Другие ответы (до сих пор) согласуются с предположением автора вопроса о том, что вары задействованы и фактически работают, что не так (как показывает последний пример в вопросе). – amalloy

0

Почему не просто передать (get-var-name _symbol-here_) к функциям, где вы хотите использовать имя уага внутри тела? Например, используя одни и те же определения get-var-name, f2, f3 и my-var, которые вы дали выше (но изменение f1 немного):

(defmacro get-var-name [x] 
    `(:name (meta (var ~x)))) 
(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<")) 
(defn f2 [x2] (f1 x2)) 
(defn f3 [x3] (let [zzz x3] (f2 zzz))) 
(def my-var 3.1414926) 
=> (f3 (get-var-name my-var)) 
hello, you passed var name >> my-var << 
=> nil 

Может быть, иногда вы будете также хотите обратиться к значению что символ относится к телу функции. Например, скажем, в f1 вы также хотите распечатать значение, на которое ссылается это имя var. Вот как вы могли бы сделать это:

(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<" 
         "\nwhich refers to value >>" @(resolve x1) "<<")) 
=> (f3 (get-var-name my-var)) 
hello, you passed var name >> my-var << 
which refers to value >> 3.1414926 << 
=> nil 

Обратите внимание на @(resolve x1) --this это то, возвращая значение, my-var относится к (и, в свою очередь my-var это значение ссылается x1).

Кроме того, я хочу упомянуть, что ваша текущая реализация get-var-name будет генерировать исключения, когда переданный ей аргумент либо не является символом, либо является символом, но в настоящее время не привязан к значению. Это поведение, которое вы хотите?

Если то, что я предлагаю не ответить на ваш вопрос, то кажется, что вы не хотите иметь передать (get-var-name _symbol-here_) к функциям, которые могут в конечном итоге, используя имя Var, а по какой-то причине очень хочется быть в состоянии сделать (get-var-name ...) изнутри тела функции. Если это так, почему вы хотите это сделать? Или, если вы чувствуете, что я не ответил на ваш вопрос по какой-то другой причине, в чем причина?

+0

Спасибо, но мне нужно получить-var-name из тела функции. В основном я пытаюсь добавить имя переменной с более высоким охватом в какой-то другой текст на несколько уровней в вызове функции. Я думаю, что могу попробовать это с помощью '(binding ...)' перед тем, как начать цепочку вызовов. – Sonicsmooth

+0

@Sonicsmooth Я думаю, что вижу. Мне было бы интересно увидеть реальную (то есть неакадемическую) версию того, что вы делаете/пытаетесь сделать. –

+0

... ok Я тоже отказался от '(binding ...)'. Это требует дополнительного манекена var в моем пространстве имен и делает его уродливым. – Sonicsmooth

1

Вы можете передать фактический вар функции, а не вар разрешенного значения, используя #' читатель макрос, как показано ниже:

user=> (defn f1 [x1] (println "hello, you passed var name >>" (:name (meta x1)) "<<")) 
#'user/f1 
user=> (defn f2 [x2] (f1 x2)) 
#'user/f2 
user=> (defn f3 [x3] (let [zzz x3] (f2 zzz))) 
#'user/f3 
user=> (def my-var 3.1414926) 
#'user/my-var 
user=> (f3 #'my-var) 
hello, you passed var name >> my-var << 

В случае, если вы хотите, чтобы значение, связанное с вар, вы можете использовать var-get функции для этого.

+0

Спасибо, этот ответ на самом деле вызвал многочасовое исследование, проведенное мною в качестве символов-> vars-> ценностей в clojure. Он работает от repl с vars, но все же не работает с let-bounded символами. – Sonicsmooth

Смежные вопросы