Формат выходной строки в зависимости от типа данных - PullRequest
2 голосов
/ 29 января 2012

Я пишу функцию clojure для форматирования различных типов данных в виде строки.

Мое наивное решение:

(defn p [d]
       (cond
        (vector? d) (str "vector: " d)
        (list? d) (str "list: " d)))

#'user/p
user> (p [1 2 3])
"vector: [1 2 3]"
user> (p '(1 2 3))
"list: (1 2 3)"

Я раньше не использовал мультиметоды.Это хорошее применение, или есть другой способ избежать вонючего использования cond?

Ответы [ 3 ]

5 голосов
/ 29 января 2012

Я бы пошел за определение протокола форматирования и расширение его до нужных вам типов, как предлагает @rodnaph:

(defprotocol Format
  (fmt [this]))

(extend-protocol Format
  clojure.lang.IPersistentVector
    (fmt [this] (str "vector:" this))
  clojure.lang.IPersistentList
    (fmt [this] (str "list:" this)))

Однако я не знаю, какой из них будет иметь лучшую производительность, мультиметод или протоколрасширение.

Определение мультиметода может выглядеть следующим образом:

(defmulti fmt class)

(defmethod fmt 
  clojure.lang.IPersistentVector [this]
    (str "vector:" this))
(defmethod fmt 
  clojure.lang.IPersistentList [this]
    (str "list:" this))

РЕДАКТИРОВАТЬ: возможно, вы захотите проверить этот вопрос о протоколах против мультиметодов , как это довольно приятнообъяснил общие случаи использования для обоих.Согласно этой информации, в вашем сценарии лучше использовать протокол.

1 голос
/ 29 января 2012

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

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

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

(defmulti stringify class)
(defmethod stringify clojure.lang.PersistentVector [v] ...)
(defmethod stringify clojure.lang.PersistentArrayMap [m] ...)
;;; More dispatching on concrete class names

Что ж, как только вы увидите конкретные имена классов clojure.lang, появляющиеся в вашем коде, все виды аварийных сигналов должны бытьухожуОни слишком специфичны ... они сломаются, если изменится базовая библиотека Clojure, они не будут работать очень чисто с взаимодействием Java, они не будут охватывать пользовательские типы, которые реализуют Seqable ... короче,они являются нарушением абстракции.

Каждый раз, когда у вас возникнет искушение отправлять имена классов, будь то из Clojure, Java или сторонних библиотек, вы должны всегда тянуться к exten-typeвместо этого.

1 голос
/ 29 января 2012

(я нуб, но) Похоже, протокол лучше всего подходит для этого:

http://clojure.org/protocols

Затем вы можете определить различные реализации форматирования для каждого типа данных.Вы хотели бы поддержать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...