Как связать элементы из вложенного вектора с соответствующими единичными значениями в Clojure?

Я изучаю Clojure и хочу больше узнать о последовательностях. У меня есть реальная проблема, которую я сократил до общей, но я не знаю, имеет ли она каноническое имя. Надеюсь, приведенный ниже пример проясняет ситуацию.

Скажем, у меня есть два вектора, src а также dst, Предметы в src вектор сам по себе является вектором, и мне нужно сопоставить каждый элемент в каждом векторе с соответствующим значением в dst,

(def src [ ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"] ])
(def dst [ "a" "b" "c" ])

Я хочу изготовить следующую карту:

{ :a1 "a", :a2 "a", :a3 "a", :b1 "b", :b2 "b", :c1 "c", :c2 "c", :c3 "c", :c4 "c" }

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

В Python это будет:

src = [['a1', 'a2', 'a3'], ['b1', 'b2'], ['c1', 'c2', 'c3', 'c4']]
dst = ['a', 'b', 'c']
result = {}
for (s, d) in zip(src, dst):
    for x in s:
        result[x] = d

В Clojure я попытался начать с:

(interleave src dst)
;=> (["a1" "a2"] "a" ["b1" "b2" "b3"] "b" ["c1"] "c")

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

Также, zipmap само по себе не слишком далеко

(zipmap src (map keyword dst))
;=> {["c1"] :c, ["b1" "b2" "b3"] :b, ["a1" "a2"] :a}
;bogus result

Теперь мне нужно перевернуть ключи и значения на карте, и все же повторить.

Мне не удалось построить for выражение либо:

(for [s src] (zipmap s dst)))
;=> ({"a2" "b", "a1" "a"} {"b3" "c", "b2" "b", "b1" "a"} {"c1" "a"})
;bogus result

Я подхожу к проблеме как к сопряжению двух векторов, но я не могу получить векторы от src вектор в положение, чтобы я мог просто zipmap каждый из них с dst,

Я подозреваю, что ответ действительно очевиден, но мой мозг все еще не работает достаточно функционально. Может быть, есть into {} и / или assoc где-то там

Есть указатели? Если вам интересно, реальная проблема, о которой я упомянул, - это картирование от кодонов РНК до аминокислот.

3 ответа

Решение

map может выполнить несколько последовательностей для итерации, например:

(map + [1 2 3] [4 5 6])
;; => (5 7 9)

Таким образом, это был бы способ получить значения, которые вы хотите обработать, в одну и ту же функцию, что приведет к обработке пар ["a1" "a2" "a3"]/"a", так далее...

(map
  (fn [src dst]
    ???)
  [["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"]]
  ["a" "b" "c"])

zipmap принимает последовательность ключей (которые у нас есть) и последовательность значений (которые мы должны построить из одного значения). repeat может использоваться для создания бесконечного ленивого seq на основе постоянного значения:

(take 3 (repeat "a"))
;; => ("a" "a" "a")

А также:

(zipmap ["a1" "a2" "a3"] (repeat "a"))
;; => {"a3" "a", "a2" "a", "a1" "a"}

Это делает исходный код похожим на это:

(map
  (fn [src dst]
    (zipmap src (repeat dst)))
  [["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"]]
  ["a" "b" "c"])
;; => ({"a3" "a", "a2" "a", "a1" "a"} {"b2" "b", "b1" "b"} {"c4" "c", "c3" "c", "c2" "c", "c1" "c"})

И, наконец, вы можете объединить все эти карты в одну, используя into, в результате чего этот последний кусок кода:

(into {} (map #(zipmap %1 (repeat %2)) src dst))
;; => {"a3" "a", "c2" "c", "c3" "c", "a1" "a", "b2" "b", "c4" "c", "a2" "a", "c1" "c", "b1" "b"}
user> (into {}
            (for [[sources, dest] (map list src dst),
                  source sources]
                 [(keyword source), dest]))
{:a2 "a", :b2 "b", :c3 "c", :a3 "a", :a1 "a", :b1 "b", :c4 "c", :c2 "c", :c1 "c"}

for понимание создает каждую пару источник / назначение, а затем into используется для заполнения хэш-карты из этих пар.

(into {} (mapcat (fn [ss d] (map #(vector (keyword %) d) ss)) src dst))

;{:a3 "a", :c1 "c", :c2 "c", :b2 "b",
; :b1 "b", :c4 "c", :c3 "c", :a2 "a", :a1 "a"}

... но я предпочитаю @ noisesmith's - for здесь лучше

Другие вопросы по тегам