Clojure карта-самая длинная - PullRequest
       0

Clojure карта-самая длинная

5 голосов
/ 16 декабря 2010

Я пытаюсь написать служебную функцию Clojure с именем map-longest (предложение альтернативного имени приветствуется). Эта функция будет иметь следующую «подпись»:

(map-longest fun missing-value-seq c1 & colls)

и будет вести себя аналогично map, за исключением того, что он продолжит обработку предоставленных коллекций до тех пор, пока не будет исчерпан самый длинный . Для коллекций короче самой длинной, когда у них заканчиваются значения, они берут их из missing-values-seq. Это должно быть лениво, но, очевидно, не может использоваться с бесконечными коллекциями.

Пример использования:

(print (apply str
  (map-longest #(str %1 \space %2 \space %3 \newline) (repeatedly "--")
    ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"])))

Это должно произвести следующий вывод:

a1 b1 c1
a2 b2 c2
a3 -- c3
-- -- c4

но я могу ошибиться.

Как мне это реализовать? Есть ли в библиотеке clojure.core или clojure-contrib что-то подобное? В качестве альтернативы missing-value-seq, было бы лучше передать вторую функцию для генерации пропущенных значений (например, #(identity "--") в моем примере)?

Вариант использования: я пишу небольшой текстовый пасьянс «Паук» в качестве упражнения для изучения Clojure / функционального программирования. Мне нужно иметь возможность отображать игровые таблицы (таблицы для пуристов: -)).

Ответы [ 2 ]

4 голосов
/ 16 декабря 2010

Вот решение:

(defn map-longest
  ([fn missing-value-fn c1]
    (map fn c1))
  ([fn missing-value-fn c1 & colls]
    (lazy-seq
      (when (not-every? empty? (conj colls c1))
        (let [firsts (map first (conj colls c1))]
          (cons
            (apply fn (map #(if (nil? %) (missing-value-fn) %) firsts))
            (apply map-longest
              (conj (map rest colls) (rest c1) missing-value-fn fn))))))))

Тест:

user=> (print (apply str 
         (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--") 
           ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"])))
a1 b1 c1
a2 b2 c2
a3 -- c3
-- -- c4
nil

Обратите внимание, что я выбрал missing-value-fn подход, а не missing-value-seq.

Обновление

Обновлен код для учета случая, упомянутого ffriend в комментариях.

Тест:

user=> (print (apply str
          (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--")
            ["a1" "a2" nil] ["b1" "b2"] ["c1" "c2" nil "c4"])))
a1 b1 c1
a2 b2 c2
-- -- --
-- -- c4
nil

Обратите внимание, что это заменит nil s в полях на значение, возвращенное missing-value-fn.

1 голос
/ 16 декабря 2010

Это не совсем нужная вам функция, но немного упрощенная версия, чтобы вы могли понять:

(defn first-or-val [col missing]
  (if (empty? col)
    missing
    (first col)))

(defn map-longest [f missing-value & cols]
  (loop [cols cols, ret '()]
    (cond (every? empty? cols) (reverse ret)
          :else (recur (map rest cols)
                       (conj ret (apply f (map #(first-or-val % missing-value)
                                               cols)))))))

Я опустил лень, и вы можете легко добавить ее с помощью delay и force.Я также изменил missing-value-seq на missing-value - я полагаю, это не проблема для вас заменить его последовательностью или генератором.

Пример:

(print (apply str 
          (map-longest #(str %1 \space %2 \space %3 \newline) "--"
                       ['a1 'a2 'a3] ['b1 'b2] ['c1 'c2 'c3 'c4])))

Результат:

a1 b1 c1
a2 b2 c2
a3 -- c3
-- -- c4
...