2014-09-06 85 views
1

我玩耍,並試圖創建自己的減排實施工作,到目前爲止,我有這個,與此測試數據的工作原理:與可變參數

((fn [func & args] 
    (reduce (fn [acc item] 
      (conj acc (func (last acc) item)) 
      )[(first args)] (first (rest args)))) * 2 [3 4 5] 

我不喜歡的是我如何我分開參數。

(first args)是我所期望的,即2,但(rest args)([3 4 5])所以我得到了這樣的(first (rest args))這是我不喜歡的剩餘部分。

我錯過了一些可以更容易處理可變參數的技巧嗎?

+1

注意,你可以使用'秒':'(第二[3 4 5])''是4'。 – bfontaine 2014-09-06 07:12:55

+0

您可能會發現有趣的[此博客](http://blog.jayfields.com/2010/07/clojure-destructuring.html)。 – Mark 2014-09-06 09:44:08

回答

3

變量參數就是在列表中獲取未指定數量的參數,因此所有的列表/解構操作都可以在這裏應用。

例如:

(let [[fst & rst] a-list] 
    ; fst is the first element 
    ; rst is the rest 
) 

這是更可讀的比:

(let [fst (first a-list) 
     rst (rest a-list)] 
    ; ... 
) 

可以進一步去獲取列表的第一和第二元件(假定它有> 1種元素)的一行:

(let [fst snd & rst] 
    ; ... 
) 

我最初誤讀你的問題,並認爲你試圖重新實現reduce功能。下面是一個簡單的實現,我寫了這個答案,簡化版,採用firstrest

(defn myreduce 
    ;; here we accept the form with no initial value 
    ;; like in (myreduce * [2 3 4 5]), which is equivalent 
    ;; to (myreduce * 2 [3 4 5]). Notice how we use destructuring 
    ;; to get the first/rest of the list passed as a second 
    ;; argument 
    ([op [fst & rst]] (myreduce op fst rst)) 
    ;; we take an operator (function), accumulator and list of elements 
    ([op acc els] 
    ;; no elements? give the accumulator back 
    (if (empty? els) 
     acc 
     ;; all the function's logic is in here 
     ;; we're destructuring els to get its first (el) and rest (els) 
     (let [[el & els] els] 
     ;; then apply again the function on the same operator, 
     ;; using (op acc el) as the new accumulator, and the 
     ;; rest of the previous elements list as the new 
     ;; elements list 
     (recur op (op acc el) els))))) 

我希望它可以幫助您瞭解如何使用列表解構,這可能是你在你的函數想要什麼工作。這是關於這個問題的relevant blog post

1

整理你的功能。

由於@bfontaine評論,你可以使用(second args)代替(first (rest args))

(defn reductions [func & args] 
    (reduce 
    (fn [acc item] (conj acc (func (last acc) item))) 
    [(first args)] 
    (second args))) 

它使用

  • func
  • (first args)
  • (second args)

...但忽略其餘的args

所以我們可以用解構來命名的args第一和第二元素 - initcoll似乎合適 - 給

(defn reductions [func & [init coll & _]] 
    (reduce 
    (fn [acc item] (conj acc (func (last acc) item))) 
    [init] 
    coll)) 

...其中_是被忽略的參數的傳統名稱,在這種情況下,一個序列。

我們可以擺脫它,簡化到

(defn reductions [func & [init coll]] ...) 

...然後

(defn reductions [func init coll] ...) 

... - 三個參數的簡單功能。

處理底層問題。

你的功能有兩個問題:

  • 緩慢
  • 缺乏懶惰。

步履維艱

閃爍的紅燈在這個函數是

(fn [acc item] (conj acc (func (last acc) item))) 

使用last這會掃描整個的每次調用時間acc,即使acc是向量。所以這個reductions需要的時間與coll長度的平方成比例:對於長序列無望地緩慢。

一個簡單的解決辦法是用(last acc)替換(acc (dec (count acc))),這需要有效的時間。

懶惰

我們仍然不能懶洋洋地使用功能產生什麼樣的缺乏。例如,它會是不錯的封裝階乘的順序是這樣的:

(def factorials (reductions * 1N (next (range))))) 

有了您的reductions,這個定義不會返回。

你必須完全重鑄你的功能,使其懶惰。讓我們修改標準-lazy - reductions採用解構:

(defn reductions [f init coll] 
    (cons 
    init 
    (lazy-seq 
     (when-let [[x & xs] (seq coll)] 
     (reductions f (f init x) xs))))) 

現在,我們可以定義

(def factorials (reductions * 1N (next (range)))) 

然後,例如,

(take 10 factorials) 
;(1N 1N 2N 6N 24N 120N 720N 5040N 40320N 362880N) 

另一種方法是推導從它本身開始的序列,就像鋪設軌道的鐵路機車一樣:

(defn reductions [f init coll] 
    (let [answer (lazy-seq (reductions f init coll))] 
    (cons init (map f answer coll)))) 

但是,這包含一個隱藏的遞歸(向我隱瞞,至少):

(nth (reductions * 1N (next (range))) 10000) 
;StackOverflowError ...