Clojure Metaprogramming Question (для начинающих!) - PullRequest
11 голосов
/ 06 июля 2010

Все, я начинаю смотреть на язык 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

Спасибо за любую помощь, которую вы можете оказать мне!

Ответы [ 3 ]

17 голосов
/ 06 июля 2010

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

  • Нечто, что неявно содержится в том, что следует, но котороевозможно, требует отдельную пулю: объекты верхнего уровня, созданные def & Co. (и в частности defn), являются Vars.Так что вы на самом деле хотите сделать это псевдонимом Var ;функции - это просто обычные значения, которые на самом деле не имеют имен (за исключением того, что они могут иметь имя, привязанное к себе локально внутри своего тела; однако это не имеет никакого отношения к рассматриваемой проблеме).

  • В Clojure действительно есть «макрос псевдонимов» - clojure.contrib.def/defalias:

    (use '[clojure.contrib.def :only [defalias]])
    (defalias foo bar)
    ; => foo can now be used in place of bar
    

    Преимущество этого по сравнению с (def foo bar) состоит в том, что он копирует метаданные (такие как строка документации);похоже, что он работает с макросами в текущем HEAD, хотя я вспоминаю ошибку, которая препятствовала этому в более ранних версиях.

  • Вары именуются символами, а не ключевыми словами.Символические литералы в Clojure (и других Лиспах) не начинаются с двоеточий (:foo - это ключевое слово, а не символ).Таким образом, чтобы определить функцию с именем foo, вы должны написать

    (defn foo [...] ...)
    
  • defn - вспомогательный макрос, упрощающий создание новых функций, содержащих Vars,позволяя программисту использовать сочетание синтаксиса def & fn.Так что defn не подлежит обсуждению для создания Vars с уже существующими значениями (которые могут быть функциями), как это требуется для создания псевдонимов;используйте defalias или просто def вместо.

  • Для создания функции с переменным числом используйте следующий синтаксис:

    (fn [x y & args] ...)
    

    x и yпотребуются позиционные аргументы;остальные аргументы, переданные функции (любое их количество), будут собраны в последовательность и доступны под именем args.Вам не нужно указывать «требуемые позиционные аргументы», если они не нужны: (fn [& args] ...).

    Чтобы создать Var, содержащий функцию с переменным числом, используйте

    (defn foo [x y & args] ...)
    
  • Чтобы применить функцию к некоторым аргументам, которые вы собрали в объект seqable (например, args seq в приведенных выше примерах или, возможно, вектор и т. Д.), Используйте apply:

    (defn all? [& args]
      (apply every? args))
    
  • Если вы хотите написать функцию для создания псевдонимов - в отличие от макроса - вам нужно исследовать функции intern, with-meta, meta - и, возможно, resolve / ns-resolve, в зависимости от того, должна ли функция принимать символы или переменные.Я оставлю заполнение деталей в качестве упражнения для читателя.: -)

6 голосов
/ 06 июля 2010

Все, что вам нужно сделать, это связать все? функция для всех? символ, который делается через def:

(def all? every?)

Подробнее об этом см. Макрос Clojure для создания синонима для функции

3 голосов
/ 06 июля 2010

Не думаю, что я могу многое добавить к существующим объяснениям здесь, за исключением, возможно, заполнения пары пробелов в словаре путешественника 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...