Просмотр списка карт

Я новичок в Clojure, и у меня есть простой вопрос

Допустим, у меня есть список, состоящий из карт. У каждой карты есть :name и :age

Мой код:

(def Person {:nom rob :age 31 } )
(def Persontwo {:nom sam :age 80 } )
(def Persontthree {:nom jim :age 21 } )
(def mylist (list Person Persontwo Personthree))

Теперь, как я могу пройти по списку. Скажем, например, что у меня есть данное :name. Как пройтись по списку, чтобы увидеть, соответствует ли какая-либо из карт :name моему :name. И затем, если есть соответствующая карта, как мне получить позицию индекса этой карты?

-Спасибо


person Community    schedule 14.07.2009    source источник
comment
Вы видели программирование на Clojure? Это отличная книга для изучения clojure. pragprog.com/titles/shcloj/programming-clojure   -  person seth    schedule 14.07.2009
comment
Вам действительно нужен индекс? Имейте в виду, что индексирование связанного списка является неэффективной операцией — O(N) для одного индекса, поэтому, если вы сделаете это в цикле для всех элементов, это будет O(N^2). Может быть, вы бы предпочли получить соответствующую карту?   -  person Pavel Minaev    schedule 14.07.2009


Ответы (5)


(defn find-person-by-name [name people] 
   (let
      [person (first (filter (fn [person] (= (get person :nom) name)) people))]
      (print (get person :nom))
      (print (get person :age))))

EDIT: приведенное выше было ответом на вопрос, каким он был до редактирования вопроса; вот обновленный - filter и map начали путаться, поэтому я переписал его с нуля, используя loop:

; returns 0-based index of item with matching name, or nil if no such item found
(defn person-index-by-name [name people] 
    (loop [i 0 [p & rest] people]
        (cond
            (nil? p)
                nil
            (= (get p :nom) name) 
                i
            :else
                (recur (inc i) rest))))
person Pavel Minaev    schedule 14.07.2009
comment
Вау, спасибо большое. Я знал, что должен сделать это с фильтром, однако я не был точно уверен в синтаксисе. Еще раз спасибо - person ; 14.07.2009

Это можно сделать с помощью doseq:

(defn print-person [name people]
  (doseq [person people]
    (when (= (:nom person) name)
      (println name (:age person)))))
person Jonas    schedule 14.07.2009
comment
Я могу ошибаться (на самом деле мне пришлось прочитать введение Clojure, чтобы получить ответ выше), но разве это не будет повторять всю последовательность, выводя все совпадающие элементы, а не только первый? И если в последовательности гарантированно будет только один соответствующий элемент, не будет ли она, следовательно, излишне повторять остальную часть? - person Pavel Minaev; 14.07.2009
comment
Да, это правильно. Я думал, что это был вопрос. Возможно, я неправильно истолковал. - person Jonas; 14.07.2009
comment
Но теперь вопрос изменился, так что я думаю, это не имеет значения :-) - person Jonas; 14.07.2009

Я бы посоветовал посмотреть на функцию фильтра. Это вернет последовательность элементов, соответствующих некоторому предикату. Пока у вас нет дублирования имен (а ваш алгоритм, похоже, диктует это), это будет работать.

person Steve Rowe    schedule 14.07.2009

Поскольку вы изменили свой вопрос, я даю вам новый ответ. (Я не хочу редактировать свой старый ответ, так как это сделает комментарии очень запутанными).

Может быть, есть лучший способ сделать это...

(defn first-index-of [key val xs]
  (loop [index 0
         xs xs]
    (when (seq xs)
      (if (= (key (first xs)) val)
        index
        (recur (+ index 1)
               (next xs))))))

Эта функция используется следующим образом:

> (first-index-of :nom 'sam mylist)
1
> (first-index-of :age 12 mylist)
nil
> (first-index-of :age 21 mylist)
2
person Jonas    schedule 14.07.2009

Как насчет использования positions из clojure.contrib.seq (Clojure 1.2)?

(use '[clojure.contrib.seq :only (positions)])
(positions #(= 'jim (:nom %)) mylist)

Он возвращает последовательность совпадающих индексов (вы можете использовать first или take, если хотите сократить список).

person ponzao    schedule 28.11.2011