Clojure迭代一个向量并向前看/看起来很恐怖

时间:2016-02-02 18:54:15

标签: dictionary clojure iteration

我必须迭代一个矢量,而矢量又将地图作为其项目。我需要比较下一张地图,有时我需要查看我们之前看过的地图。所以有必要展望未来/看后面的功能。我当前的方法是有效的,但我想这是丑陋的,单一的Clojure,我认为必须有更好的(更规范的)方法来实现这一点。

(let [result (apply str (map (fn [{position-before :position compound-before :compund } ; previous term (unfortunately we need to compare all three)
                                        {:keys [word position line tag stem compound grammarpos] :or {grammarpos "0" stem "" } } ; this maps to the current word
                                        {position-ahead :position compound-ahead :compound line-ahead :line}] ; this is for lookahead
                (do some stuff)) ;; now we have position, which is our current position, position-before and position-after to compare with each other
                ;; this is how we map:
                (into  '[{}] (conj grammar '[{}]))
                (next (into  '[{}] (conj grammar '[{}])))
                (next (next (into  '[{}] (conj grammar '[{}]))))))])

对于数据示例的请求,这是向量的一部分:

[{:tag "0", :position "0", :line "0", :stem "dev", :grammarpos "2625", :word "deva"} {:tag "0", :position "0", :line "0", :stem "deva", :grammarpos "4", :word "deva"}]

工作是比较位置,复合等的值,有时向前看,有时看后面。

3 个答案:

答案 0 :(得分:12)

你可以迭代你的向量的partition,大小为3,步长为1.然后,对于向量中的每个元素,你也可以得到之前和之后你可以在迭代时学习forreduce

一些例子:https://clojuredocs.org/clojure.core/partition

答案 1 :(得分:3)

如果您需要每个项目的所有前面和后面的项目,您可以将for列表理解与解构结合起来。

例如:

user> (def items [:a :b :c :d :e :f :g])
#'user/items

user> (for [index (range (count items))
            :let [[before [current & after]] (split-at index items)]]
        {:before before :current current :after after})

({:before (), :current :a, :after (:b :c :d :e :f :g)} 
 {:before (:a), :current :b, :after (:c :d :e :f :g)} 
 {:before (:a :b), :current :c, :after (:d :e :f :g)} 
 {:before (:a :b :c), :current :d, :after (:e :f :g)} 
 {:before (:a :b :c :d), :current :e, :after (:f :g)} 
 {:before (:a :b :c :d :e), :current :f, :after (:g)} 
 {:before (:a :b :c :d :e :f), :current :g, :after nil})

你只需逐一分割每个项目的索引,然后从结果中取出第一项(前),第二项(当前),第二项(后)的其余部分

也是一种不太可读的方式(但是对于大型集合来说可能更有效率,因为它不会在每一步都执行take / drop,而是添加/删除单个项目到coll)< / p>

user> (take (count items)
            (iterate
             (fn [[before current after]]
               [(conj before current) (first after) (rest after)])
             [[] (first items) (rest items)]))

([[] :a (:b :c :d :e :f :g)] 
 [[:a] :b (:c :d :e :f :g)] 
 [[:a :b] :c (:d :e :f :g)] 
 [[:a :b :c] :d (:e :f :g)] 
 [[:a :b :c :d] :e (:f :g)] 
 [[:a :b :c :d :e] :f (:g)] 
 [[:a :b :c :d :e :f] :g ()])

答案 2 :(得分:1)

如果你想做一些非常复杂的事情,或许zippers将是一个更好的解决方案。

例如,假设您从以下开始:

(def x
  [{:tag "0" :dups 0}
   {:tag "1" :dups 0}
   {:tag "1" :dups 0}
   {:tag "3" :dups 0}])

您的要求是使用相同的名称递增所有连续标记的dups计数器,并在它们之间添加“---”标记。

使用拉链,解决方案将如下所示:

(require '[clojure.zip :as zip :refer [root node]])

(defn complex-thing [zip]
  (if (zip/end? zip) ;; are we done?
    (root zip) ;; return the "updated" vector
    (let [current-node (node zip)
          before-node (node (zip/prev zip))] ;; you can access any map in the vector, both before or after
      (if (= (:tag current-node) (:tag before-node))
        (recur (-> zip
                   zip/prev ;; move to the previous map
                   (zip/edit update :dups inc) ;; increment it
                   zip/next ;; move back to the current map
                   (zip/edit update :dups inc)
                   (zip/insert-left {:tag "----"}) ;; insert "---" before the current tag
                   zip/next)) ;; move to next map to process
        (recur (zip/next zip))))))

(complex-thing (zip/next (zip/next (zip/vector-zip x)))) ;; start from the second element of the vector

[{:tag "0", :dups 0} 
 {:tag "1", :dups 1} 
 {:tag "----"} 
 {:tag "1", :dups 1} 
 {:tag "3", :dups 0}]