2014-08-30 53 views
2

我正在研究一個項目,以在實踐中學習Clojure。我做得很好,但有時候我卡住了。這一次,我需要轉換格式的序列:分組單詞和更多

[":keyword0" "word0" "word1" ":keyword1" "word2" "word3"] 

到:

[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]] 

我想至少有兩個小時,但我不知道這麼多的Clojure功能來撰寫一些有用的東西來以功能方式解決問題。

我認爲,這種轉變應該包括一些分區,這裏是我的嘗試:

(partition-by (fn [x] (.startsWith x ":")) *1) 

但結果是這樣的:再次

((":keyword0") ("word1" "word2") (":keyword1") ("word3" "word4")) 

現在我應該組...我懷疑我在這裏做正確的事情...另外,我需要將字符串(僅限於那些以:開頭的字符串)轉換爲關鍵字。我認爲這個組合應該可以工作:

(keyword (subs ":keyword0" 1)) 

如何編寫一個函數,以最習慣的方式執行轉換?

+1

[Clojure的的cheatsheet](http://clojure.org/cheatsheet)是一個很好的基準順便說一句 – noisesmith 2014-08-30 17:28:54

+0

@noisesmith,確實!我現在使用[ClojureDocs](http://clojuredocs.org/),但應該知道函數的名稱以在那裏查找信息。感謝您的建議! – Mark 2014-08-30 17:30:31

回答

1

這是一款高性能版,採用reduce

(reduce (fn [acc next] 
      (if (.startsWith next ":") 
      (conj acc [(-> next (subs 1) keyword)]) 
      (conj (pop acc) (conj (peek acc) 
            next)))) 
     [] data) 

或者,你可以擴展你的代碼,這樣

(->> data 
    (partition-by #(.startsWith % ":")) 
    (partition 2) 
    (map (fn [[[kw-str] strs]] 
      (cons (-> kw-str 
         (subs 1) 
         keyword) 
        strs)))) 
0

既然問題已經在這裏...這是我最大的努力:

(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"]) 

(->> data 
    (partition-by (fn [x] (.startsWith x ":"))) 
    (partition 2) 
    (map (fn [[[k] w]] (apply conj [(keyword (subs k 1))] w)))) 

我仍然在尋找這一個更好的解決方案或批評。

+2

我認爲''(應用conj [(關鍵字(subs k 1))] w)'在清晰度方面優於反向使用。 – noisesmith 2014-08-30 18:21:42

+0

@noisesmith,也許你說得對,反向是宏... – Mark 2014-08-30 18:24:27

+1

'(apply conj [x] y)'寫得更好(寫入[x] y)'。 – amalloy 2014-08-30 19:49:12

1
有關

什麼:

(defn group-that [ arg ] 
    (if (not-empty arg) 
    (loop [list arg, acc [], result []] 
     (if (not-empty list) 
     (if (.startsWith (first list) ":") 
      (if (not-empty acc) 
      (recur (rest list) (vector (first list)) (conj result acc)) 
      (recur (rest list) (vector (first list)) result)) 
      (recur (rest list) (conj acc (first list)) result)) 
     (conj result acc) 
     )))) 

在SEQ並且無需任何宏只是1個迭代。

+1

它必須是非常高效的,因爲它看起來很怪異:-)(所有高效的函數看起來都不好)。請注意,我的代碼也不使用宏。 – Mark 2014-08-30 19:18:31

+1

在記住在基準測試中添加doall的情況下@Mark,這個速度是原來速度的兩倍(Leon Grapenthin的基於清晰度的降低版本) – noisesmith 2014-08-30 19:54:21

0

首先,讓我們來構建,打破載體v成子向量函數,到處發生的休息屬性pred成立。

(defn breakv-by [pred v] 
    (let [break-points (filter identity (map-indexed (fn [n x] (when (pred x) n)) v)) 
     starts (cons 0 break-points) 
     finishes (concat break-points [(count v)])] 
    (mapv (partial subvec v) starts finishes))) 

對於我們的情況下,給定的

(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"]) 

然後

(breakv-by #(= (first %) \:) data) 

產生

[[] [":keyword0" "word0" "word1"] [":keyword1" "word2" "word3"]] 

注意,初始子向量是不同的:

  • 它有謂詞成立的元素。
  • 它可以是零長度。

所有的人

  • 開始與他們爲其斷言唯一元素和
  • 至少長1

所以breakv-by正確地與數據

行爲
  • 不以breaki開頭ng元素或
  • 有一系列突破元素。

對於問題的目的,我們需要什麼breakv-by稍微產生淤泥約:

(let [pieces (breakv-by #(= (first %) \:) data)] 
    (mapv 
    #(update-in % [0] (fn [s] (keyword (subs s 1)))) 
    (rest pieces))) 
;[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]]