Захват источника Clojure в качестве значения [duplicate]

Возьмите список как объект

obj = []

list(filter(lambda x:callable(getattr(obj,x)),obj.__dir__()))

Вы получаете:

['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__delitem__',
 '__dir__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__gt__',
 '__iadd__',
 '__imul__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__reversed__',
 '__rmul__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'append',
 'clear',
 'copy',
 'count',
 'extend',
 'index',
 'insert',
 'pop',
 'remove',
 'reverse',
 'sort']
9
задан harpo 9 February 2012 в 22:05
поделиться

3 ответа

Вы можете использовать макрос clojure.repl/source, чтобы получить источник символа:

user> (source max)
(defn max
  "Returns the greatest of the nums."
  {:added "1.0"
   :inline-arities >1?
   :inline (nary-inline 'max)}
  ([x] x)
  ([x y] (. clojure.lang.Numbers (max x y)))
  ([x y & more]
   (reduce1 max (max x y) more)))
nil

Но это только часть ответа. AFAICT source ищет исходное имя файла и номер строки, которые определяют данный символ, а затем печатает исходный код из файла. Поэтому source не будет работать с символами, для которых у вас нет источника, то есть с кодом компиляции AOT.

Возвращаясь к исходному вопросу, вы можете думать о source как о чтении метаданных, связанных с данным символом, и просто распечатать их. То есть это обман. Это никоим образом не возвращает вам «код как данные», где с кодом я имею в виду скомпилированную функцию clojure.

На мой взгляд, «код как данные» относится к функции lisps, где исходный код эффективен структуру данных lisp, и, следовательно, ее можно прочитать считывающим устройством. То есть я могу создать структуру данных, которая является допустимым кодом lisp, и eval, что.

Например:

user=> (eval '(+ 1 1))
2

Здесь '(+ 1 1) - литеральный список, который читается читателем clojure, а затем оценивается как код clure.

Обновление: Yehonathan Sharvit спрашивал в одном из комментариев, если можно изменить код для функции. Следующий фрагмент читает в источнике для функции, изменяет результирующую структуру данных и, наконец, оценивает структуру данных, в результате чего определяется my-nth новая функция:

(eval
 (let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
   `(~(first src) my-nth ~@(nnext src))))

syntax-quote строка заменяет nth на my-nth в форме defn.

6
ответ дан liwp 26 August 2018 в 10:09
поделиться

Вы можете получить исходный код в последних версиях clojure с функцией source.

user=> (source nth)
(defn nth
  "Returns the value at the index. get returns nil if index out of
  bounds, nth throws an exception unless not-found is supplied.  nth
  also works for strings, Java arrays, regex Matchers and Lists, and,
  in O(n) time, for sequences."
  {:inline (fn  [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~@nf)))
   :inline-arities #{2 3}
   :added "1.0"}
  ([coll index] (. clojure.lang.RT (nth coll index)))
  ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
nil

, чтобы получить строку как значение, которое вы можете обернуть в with-out-str:

user=> (with-out-str (source nth))
"(defn nth\n  \"Returns the value at the index. get returns nil if index out of\n  bounds, nth throws an exception unless not-found is supplied.  nth\n  also works for strings, Java arrays, regex Matchers and Lists, and,\n  in O(n) time, for sequences.\"\n  {:inline (fn  [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~@nf)))\n   :inline-arities #{2 3}\n   :added \"1.0\"}\n  ([coll index] (. clojure.lang.RT (nth coll index)))\n  ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))\n"
user=> 
2
ответ дан Arthur Ulfeldt 26 August 2018 в 10:09
поделиться

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

user=> (def foo (fn [] (+ 2 2)))
#'user/foo
user=> (source foo)
Source not found
nil
user=> (defn foo2 [] (+ 2 2))
#'user/foo2
user=> (source foo2)
Source not found
nil

Копаем немного ...

user=> (source source)
(defmacro source
  "Prints the source code for the given symbol, if it can find it.
  This requires that the symbol resolve to a Var defined in a
  namespace for which the .clj is in the classpath.

  Example: (source filter)"
  [n]
  `(println (or (source-fn '~n) (str "Source not found"))))
nil
user=> (source clojure.repl/source-fn)
(defn source-fn
  "Returns a string of the source code for the given symbol, if it can
  find it.  This requires that the symbol resolve to a Var defined in
  a namespace for which the .clj is in the classpath.  Returns nil if
  it can't find the source.  For most REPL usage, 'source' is more
  convenient.

  Example: (source-fn 'filter)"
  [x]
  (when-let [v (resolve x)]
    (when-let [filepath (:file (meta v))]
      (when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
        (with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
          (dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
          (let [text (StringBuilder.)
                pbr (proxy [PushbackReader] [rdr]
                      (read [] (let [i (proxy-super read)]
                                 (.append text (char i))
                                 i)))]
            (read (PushbackReader. pbr))
            (str text)))))))
nil

Так что, похоже, он пытается загрузить исходный файл из класса, чтобы попытаться выплюнуть его для вас. Одна вещь, которую я узнал при работе с Clojure, - это то, что 9 раз из 10 полезно посмотреть на источник.

0
ответ дан Bill 26 August 2018 в 10:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: