Как я могу получить имя функции из символа в clojure?

Предположим, я определяю x как функцию символа foo

(defn foo [x] x)

(def x foo)

Может ли имя "foo" быть обнаружено, если дано только x?

Есть ли способ в foo найти имя функции x - "foo" в этом случае?

(foo x)

Есть или есть возможность создать такую ​​функцию как:

(get-fn-name x)
foo

2 ответа

Решение

Подобный вопрос был задан недавно на этом сайте; смотрите здесь

Когда вы делаете (def x foo), вы определяете х как значение в foo"не"foo сама ". Однажды foo решил его значение, это значение больше не имеет никакого отношения к foo,

Поэтому, возможно, вы видите один из возможных ответов на ваш вопрос сейчас: не решайте foo когда вы идете, чтобы определить x, Вместо того, чтобы делать...

(def x foo)

...делать...

(def x 'foo)

Теперь, если вы попытаетесь получить значение x, ты получишь foo (буквально), а не значение, которое foo решает до.

user> x
=> foo

Тем не менее, это, вероятно, проблематично, потому что вы, возможно, также иногда захотите получить значение, которое foo решает использовать x, Однако, однако, вы сможете сделать это, выполнив:

user> @(resolve x)
=> #<user$foo user$foo@157b46f>

Если бы я должен был описать, что это делает, это было бы: "получить значение x разрешает, использует это как символ, затем разрешает этот символ к его переменной (не его значению) и разыменовывает этот переменный для получения значения ".

... Теперь давайте сделаем что-нибудь хакерское. Я не уверен, что советовал бы делать что-либо из того, что я собираюсь предложить, но вы спросили Can the name "foo" be discovered if only given x?и я могу думать о двух способах, которыми вы могли бы сделать это.

Метод № 1: пересмотреть имя fn var
Обратите внимание что foo а также x оба решают ниже:

(defn foo [a] (println a))
(def x foo)

user> foo
=> #<user$foo user$foo@1e2afb2>
user> x
=> #<user$foo user$foo@1e2afb2>

Теперь проверьте это:

user> (str foo)
=> "user$foo@1e2afb2"
user> (str x)
=> "user$foo@1e2afb2"

Здорово. Это работает только потому, что foo разрешается в функцию, которая имеет имя, подобное var, имя, которое будет таким же для x потому что это относится к той же функции. Обратите внимание, что "foo" содержится в строке, созданной (str x) (а также (foo x)). Это потому, что имя переменной функции, очевидно, создается с некоторой обратной ссылкой на символ, который использовался для его первоначального определения. Мы собираемся использовать этот факт, чтобы найти этот символ в любой функции.

Итак, я написал регулярное выражение, чтобы найти "foo" внутри этой строки имени функции var. Дело не в том, что он ищет "foo", а в том, что он ищет любую подстроку - в терминах регулярных выражений, ".*"- которому предшествует \$ характер - в терминах регулярных выражений "(?<=\$)"-, а затем \@ характер - в терминах регулярных выражений "(?=@)"...

user> (re-find #"(?<=\$).*(?=@)"
               (str x))
=> "foo"

Мы можем далее преобразовать это в символ, просто оборачивая (symbol ...) вокруг него:

user> (symbol (re-find #"(?<=\$).*(?=@)"
                       (str x)))
=> foo

Кроме того, весь этот процесс может быть обобщен на функцию, которая будет принимать функцию и возвращать символ, связанный с именем переменной этой функции - то есть символ, который был задан, когда функция была первоначально определена (этот процесс вообще не будет хорошо работать для анонимные функции).

(defn get-fn-init-sym [f]
  (symbol (re-find #"(?<=\$).*(?=@)" (str f))))

... или это, что я считаю, лучше читать...

(defn get-fn-init-sym [f]
  (->> (str f)
       (re-find #"(?<=\$).*(?=@)")
       symbol))

Теперь мы можем сделать...

user> (get-fn-init-sym x)
=> foo

Метод № 2: обратный поиск всех ns отображений на основе идентичности
Это будет весело.

Итак, мы собираемся взять все сопоставления пространства имен, а затем dissoc'x Исходя из этого, затем отфильтруйте то, что остается, основываясь на том, относится ли значение val в каждом отображении к тому же x решает до. Мы возьмем первое в этой отфильтрованной последовательности, а затем возьмем ключ в этом первом, чтобы получить символ.

user> (->> (dissoc (ns-map *ns*) 'x)
           (filter #(identical? (let [v (val %)]
                                  (if (var? v) @v v))
                                x))
           first
           key)
=> foo

Обратите внимание, что если вы заменили x с foo выше, вы получите x, На самом деле все, что это делает, возвращает возвращаемое им имя, которое соответствует тому же значению, что и x, Как и раньше, это можно обобщить на функцию:

(defn find-equiv-sym [sym]
  (->> (dissoc (ns-map *ns*) sym)
       (filter #(identical? (let [v (val %)]
                              (if (var? v) @v v))
                            @(resolve sym)))
       first
       key))

Основным отличием здесь является то, что аргумент должен быть символом в кавычках.

user> (find-equiv-sym 'x)
=> foo

это find-equiv-sym Функция действительно не очень хорошая. Проблемы будут возникать, когда в пространстве имен будет несколько вещей, которые будут иметь одинаковые значения. Вы можете вернуть этот список символов, которые разрешают идентичные вещи (вместо того, чтобы просто возвращать первый), а затем обработать его дальше оттуда. Было бы просто изменить текущую функцию, чтобы сделать эту работу: удалить последние две строки (first а также key) и заменить их на (map key),

В любом случае, я надеюсь, что это было так же весело и интересно для вас, как и для меня, но я сомневаюсь, что любой из этих хаков будет хорошим способом решения проблем. Я защищаю свое первое решение.

Непонятно, почему вы хотели бы сделать это - когда вы делаете (def x foo) вы эффективно даете имя x на новую переменную в вашем пространстве имен. Это имеет то же значение, что и foo (т.е. он содержит ту же функцию), но в остальном он полностью независим от foo. Это как две ссылки на один и тот же объект, если использовать аналогию с Java.

Почему вы должны продолжать хотеть получить имя foo?

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

(def foo 
  (with-meta
    (fn [x] x)
    {:original-function `foo}))

(def bar foo)

(defn original-function [v]
  "Returns the :original-function symbol from the metadata map"
  (:original-function (meta v)))

(original-function bar)
=> user/foo
Другие вопросы по тегам