Все, я начинаю смотреть на язык Clojure и имел пару вопросов о чем-то, что я пытаюсь сделать. Широкая задача состоит в том, чтобы исказить функцию последовательности every?
кому: all?
. Я уверен, что существует функция или макрос, который делает искажение (или что-то вдоль тех строк), но я хотел видеть, было ли это возможно с некоторыми основными конструкциями, я знаю к настоящему времени. Мой подход был для определения вызванной функции all?
это применяет его аргументы every?
реализация.
Мне любопытно видеть, может ли это быть сделано агностиком, таким образом, я хотел к параметру, чтобы моя функция псевдонима взяла два аргумента, новое имя (как Ключевое слово) и старое название (как ссылка на функцию). В борьбе к этой цели я встретился с двумя проблемами.
1) Определение именованных функций с Ключевыми словами бросает ошибки. По-видимому, это хочет clojure.lang.IObj
.
user=> (defn :foo "bar")
java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to clojure.lang.IObj (NO_SOURCE_FILE:0)
Существует ли функция для кастинга Ключевого слова к IObj или других средств параметризовать название недавно определенной функции с некоторыми предоставленными значению? (В Ruby, define_method среди других методов делает это),
irb(main)> self.class.instance_eval do
irb(main)* define_method(:foo) { "bar" }
irb(main)> end
=> #<Proc>
irb(main)> foo
=> "bar"
2) Соберите все аргументы функции в единственную переменную. Даже основные функции такой как (+ 1 2 3 4)
возьмите переменную сумму аргументов. Все функциональные методы определения, которые я видел до сих пор, берут определенную сумму аргументов, без пути только к агрегировали все в списке для обработки в теле функции. Еще раз то, для чего я иду, сделано в Ruby как так:
irb(main)> def foo(*args)
irb(main)> p args
irb(main)> end
=> nil
irb(main)> foo(1, 2, 3)
[1, 2, 3]
=> nil
Спасибо за любую справку можно предоставить мне!
Я отвечу в виде маркированного списка, так как вопросы можно аккуратно разделить на несколько отдельных вопросов.
Что-то, что неявно содержится в том, что будет следовать, но что, возможно, заслуживает отдельной пули: объекты верхнего уровня, созданные def
& Co. (и, в частности, defn
) являются варсами. Итак, на самом деле вам нужно создать псевдоним Var ; функции - это просто обычные значения, у которых на самом деле нет имен (за исключением того, что они могут иметь имя, привязанное к себе локально внутри их тела; однако это не имеет отношения к рассматриваемой проблеме).
В Clojure действительно есть «макрос псевдонимов» - clojure.contrib.def / defalias
:
(используйте '[clojure.contrib.def: only [defalias]])
(defalias foo bar)
; => foo теперь можно использовать вместо bar
Преимущество этого метода перед (def foo bar)
заключается в том, что он копирует поверх метаданных (таких как строка документации); похоже, он даже работает с макросами в текущем HEAD, хотя я помню ошибку, которая предотвращала это в более ранних версиях.
Варки называются символами, а не ключевыми словами. Символьные литералы в Clojure (и других Lisp) не начинаются с двоеточия (: foo
- это ключевое слово, а не символ). Таким образом, чтобы определить функцию с именем foo
, вы должны написать
(defn foo [...] ...)
defn
- это вспомогательный макрос, упрощающий создание новых переменных, содержащих функции, позволяя программисту использовать сочетание def
и fn
синтаксис.Итак, defn
не может быть и речи для создания переменных с уже существующими значениями (которые могут быть функциями), как это требуется для создания псевдонимов; используйте вместо него defalias
или просто def
.
Чтобы создать вариативную функцию, используйте следующий синтаксис:
(fn [x y & args] ...)
x
и y
будут обязательными позиционными аргументами; остальные аргументы, переданные функции (любое их количество), будут собраны в последовательность и доступны под именем args
. Вам не нужно указывать какие-либо «обязательные позиционные аргументы», если они не нужны: (fn [& args] ...)
.
Чтобы создать Var, содержащий вариативную функцию, используйте
(defn foo [x y & args] ...)
Чтобы применить функцию к некоторым аргументам, которые вы собрали в seqable объект (например, args
seq в приведенных выше примерах или, возможно, вектор и т. Д.), Используйте apply
]:
(определить все? [& Аргументы]
(применить каждые? args))
Если вы хотите написать функцию для создания псевдонимов - в отличие от макроса - вам необходимо изучить функции intern
, with-meta
, мета
- и, возможно, разрешить
/ ns-resolve
, в зависимости от того, должна ли функция принимать символы или Vars. Я оставлю заполнение деталей в качестве упражнения для читателя. : -)
Все, что вам нужно сделать, это привязать каждый? функция для всех? символ, который выполняется через def:
(def all? every?)
Подробнее об этом см. Макрос Clojure для создания синонима функции
Не думаю, что я могу здесь что-то добавить к существующим объяснениям, за исключением, возможно, заполнения пары пробелов в словаре путешественника Ruby по сбору аргументов и деструктуризации:
(defn foo [& args] ; Ruby: def foo(*args)
(println args))
user=> (foo 1 2 3)
(1 2 3)
(defn foo [& args]
(+ args))
user=> (foo 1 2 3)
java.lang.ClassCastException ; + takes numbers, not a list
(defn foo [& args]
(apply + args)) ; apply: as Ruby proc.call(*args)
user=> (foo 1 2 3)
6
(defn foo [& args]
(let [[a b & other] args] ; Ruby: a, b, *other = args
(println a b other)))
user=> (foo 1 2 3)
1 2 (3)