2017-01-30 8 views
2

При выполненииМарка (карта F c1 c2) карта (количество c1) раз, даже если c2 имеет меньше элементов

(map f [0 1 2] [:0 :1]) 

f будет вызван дважды, с аргументами будучи

  • 0: 0
  • 1: 1

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

  • 0: 0
  • 1: 1
  • 2 ноль

Редактировать Решение вопроса на @ fl00r в комментариях.

Фактический прецедент, который вызвал этот вопрос, был необходим для того, чтобы всегда работать точно (count first-coll) раз, независимо от того, была ли вторая (или третья или ...) коллекция длиннее.

Немного поздно в игре сейчас и несколько несправедливо после принятия ответа, но если добавляется хороший ответ, который делает только то, что я специально просил - сопоставление (count first-coll) раз - я бы согласился с этим.

+1

Что должно быть (map f [0 1] [: 0: 1: 2]) 'return? – fl00r

ответ

5

Вы можете сделать:

(map f [0 1 2] (concat [:0 :1] (repeat nil))) 

В основном, площадку второй Coll с бесконечной последовательностью Nils. map останавливается, когда он достигает конца первой коллекции.

An (нетерпеливый) форма петли/RECUR, что подходит к концу длинной:

(loop [c1 [0 1 2] c2 [:0 :1] o []] 
    (if (or (seq c1) (seq c2))     
    (recur (rest c1) (rest c2) (conj o (f (first c1) (first c2)))) 
    o)) 

Или вы могли бы написать ленивую версию карты, которая сделала что-то подобное.

+0

Любите этот подход 'concat' – fl00r

+0

@ евгени-борисовский Есть ли что-то недостающее в этом ответе? Я хотел бы дать ответ, но я не вижу никаких проблем без внимания. – tar

0

просто для удовольствия

это можно легко сделать с do макрос Common Lisp в.Мы могли бы реализовать это в Clojure и сделать это (и многое другое забавные вещи) с ним:

(defmacro cl-do [clauses [end-check result] & body] 
    (let [clauses (map #(if (coll? %) % (list %)) clauses) 
     bindings (mapcat (juxt first second) clauses) 
     nexts (map #(nth % 2 (first %)) clauses)] 
    `(loop [[email protected]] 
     (if ~end-check 
     ~result 
     (do 
      [email protected] 
      (recur [email protected])))))) 

, а затем просто использовать его для отображения (обратите внимание, она может работать на более чем 2 colls):

(defn map-all [f & colls] 
    (cl-do ((colls colls (map next colls)) 
      (res [] (conj res (apply f (map first colls))))) 
     ((every? empty? colls) res))) 

в РЕПЛ:

user> (map-all vector [1 2 3] [:a :s] '[z x c v]) 
;;=> [[1 :a z] [2 :s x] [3 nil c] [nil nil v]] 
1

а общей ленивой версии, как это было предложено Alex Miller's answer, является

(defn map-all [f & colls] 
    (lazy-seq 
    (when-not (not-any? seq colls) 
     (cons 
     (apply f (map first colls)) 
     (apply map-all f (map rest colls)))))) 

Например,

(map-all vector [0 1 2] [:0 :1]) 
;([0 :0] [1 :1] [2 nil]) 

Вы, вероятно, хотите специализироваться map-all для одного и двух коллекций.

+0

Я принимаю это как ответ, поскольку он прост в использовании, а 'map-all' не создает никаких промежуточных последовательностей, по сравнению с' map', и могут быть добавлены перегрузки arity для одной, двух и трех коллекций, просто как сделано «map». –

+0

@EugeneBeresovsky Я обнаружил, что писать справочные версии - ясные, но возможно медленные - основные функции - это лучшие способы по-настоящему понять их. Это просто другое. – Thumbnail

+0

Я бы посоветовал не делать этого. Эта реализация выдает Stackoverflow: '(doall (map-all (постоянно 8) (повтор 5000 8) (повтор 5000 8)))' – ClojureMostly

 Смежные вопросы

  • Нет связанных вопросов^_^