2017-09-26 65 views
1

對Clojure和一般編程來說還是很新的,所以請原諒這個愚蠢的問題。如何從Clojure中的條件循環中返回一個惰性序列?

的問題是:

查找n和k,使得數多達n(不包括)的總和等於數從n + 1個的總和至k(含)。

我的解決方案(其中正常工作)是定義了以下功能:

(defn addd [x] (/ (* x (+ x 1)) 2)) 
(defn sum-to-n [n] (addd(- n 1))) 
(defn sum-to-k [n=1 k=4] (- (addd k) (addd n))) 
(defn is-right[n k] 
    (= (addd (- n 1)) (sum-to-k n k))) 

然後運行下面的循環:

(loop [n 1 k 2] 
    (cond 
    (is-right n k) [n k] 
    (> (sum-to-k n k) (sum-to-n n))(recur (inc n) k) 
    :else (recur n (inc k)))) 

這隻能返回一個答案,但如果我手動集合n k我可以得到不同的值。但是,我想定義一個返回所有值的惰性序列的函數,以便:

(= [6 8] (take 1 make-seq)) 

如何儘可能有效地執行此操作?我嘗試了各種各樣的東西,但沒有多少運氣。

感謝

:編輯:

我想我想出了做一個更好的方式,但它的回報「讓應該是一個載體」。 Clojure的文檔是沒有太大的幫助......

繼承人的新代碼:

(defn calc-n [n k] 
(inc (+ (* 2 k) (* 3 n)))) 

(defn calc-k [n k] 
(inc (+ (* 3 k)(* 4 n)))) 

(defn f 
    (let [n 4 k 6] 
     (recur (calc-n n k) (calc-k n k)))) 

(take 4 (f)) 
+0

在你的編輯中,你缺少'f'的參數向量。 – madstap

回答

-1

如果你不覺得「滾動你自己」,這是一個替代解決方案。我還通過重命名/重新格式化了一些算法。

主要區別在於,您將循環復發視爲t/lazy-gen表單內的無限循環。當您找到想要保留的值時,可以使用t/yield表達式創建輸出的延遲序列。這種結構是Clojure版本的生成器函數

(ns tst.demo.core 
    (:use tupelo.test) 
    (:require [tupelo.core :as t])) 

(defn integrate-to [x] 
    (/ (* x (+ x 1)) 2)) 
(defn sum-to-n [n] 
    (integrate-to (- n 1))) 
(defn sum-n-to-k [n k] 
    (- (integrate-to k) (integrate-to n))) 
(defn sums-match[n k] 
    (= (sum-to-n n) (sum-n-to-k n k))) 

(defn recur-gen [] 
    (t/lazy-gen 
    (loop [n 1 k 2] 
     (when (sums-match n k) 
     (t/yield [n k])) 
     (if (< (sum-to-n n) (sum-n-to-k n k)) 
     (recur (inc n) k) 
     (recur n (inc k)))))) 

(t/spyx (take 5 (recur-gen))) 

你可以找到所有的細節in the Tupelo Library

2

是的,你可以創建一個懶序列,從而使下一個迭代將採取前一次迭代的結果。這裏是我的建議:

(defn cal [n k] 
    (loop [n n k k] 
    (cond 
     (is-right n k) [n k] 
     (> (sum-to-k n k) (sum-to-n n))(recur (inc n) k) 
     :else (recur n (inc k))))) 

(defn make-seq [n k] 
    (if-let [[n1 k1] (cal n k)] 
     (cons [n1 k1] 
      (lazy-seq (make-seq (inc n1) (inc k1)))))) 

(take 5 (make-seq 1 2)) 
;;=> ([6 8] [35 49] [204 288] [1189 1681] [6930 9800]) 
+1

你爲什麼使用'if-let'?這似乎是一個簡單的「讓」是所有需要的。 –

+2

'if-let'允許一個終止的序列,如果將來某個'cal'的實現可以確定不可能找到更多的答案。例如,比較一下'take'的定義,它的核心看起來像'(defn take [n xs](lazy-seq(if(zero?n)(),if-let [coll(seq xs )](cons(first coll)(take(dec n(rest coll))))))))''。 – amalloy

+0

是的,像amalloy說,if-let需要終止lazy-seq –

0

這第一個功能可能有一個數學名稱更好,但我不知道數學很好。我會用inc(增量)而不是(+ ,,, 1),但這只是個人偏好。

(defn addd [x] 
    (/ (* x (inc x)) 2)) 

我會在這裏稍微清理間距和使用dec(遞減)功能。

(defn sum-to-n [n] 
    (addd (dec n))) 

(defn sum-n-to-k [n k] 
    (- (addd k) (addd n))) 

在某些語言謂詞,返回布爾函數, 有像is-oddis-whatever名稱。在clojure中,它們通常是 ,稱爲odd?whatever?。 問號不是語法,只是名稱的一部分。

(defn matching-sums? [n k] 
    (= (addd (dec n)) (sum-n-to-k n k))) 

環路特殊形式是一種像一個匿名函數 爲RECUR跳回。如果沒有循環形式,則循環跳回 到封閉函數。 另外,不知道該怎麼稱呼這個,所以我就把它叫做f

(defn f [n k] 
    (cond 
    (matching-sums? n k) [n k] 
    (> (sum-n-to-k n k) (sum-to-n n)) (recur (inc n) k) 
    :else (recur n (inc k)))) 

(comment 

    (f 1 2) ;=> [6 8] 

    (f 7 9) ;=> [35 49] 

) 

現在,爲您的實際問題。如何製作一個懶惰的序列。你可以使用lazy-seq宏,就像minhtuannguyen的答案一樣,但是有一個更簡單,更高層次的方法。使用iterate函數。 iterate取一個函數和一個值,並返回值,隨後通過調用的值的功能,隨後通過調用值的函數的無限序列等

(defn make-seq [init] 
    (iterate (fn [n-and-k] 
      (let [n (first n-and-k) 
        k (second n-and-k)] 
       (f (inc n) (inc k)))) 
      init)) 

(comment 

    (take 4 (make-seq [1 2])) ;=> ([1 2] [6 8] [35 49] [204 288]) 
) 

可以由被簡化位在匿名函數的參數向量中使用解構。

(defn make-seq [init] 
    (iterate (fn [[n k]] 
      (f (inc n) (inc k))) 
      init)) 

編輯: 關於在f的重複計算。

通過使用let保存計算結果,可以避免爲每個數字計算多次addd

(defn f [n k] 
    (let [to-n (sum-to-n n) 
     n-to-k (sum-n-to-k n k)] 
    (cond 
     (= to-n n-to-k) [n k] 
     (> n-to-k to-n) (recur (inc n) k) 
     :else (recur n (inc k))))) 
+0

這是我想要的,但是這不是效率很低嗎?如果我理解正確,每次打電話給f時你都會重複很多工作。有沒有辦法將n和k的下一個值設置爲之前的值+ 1? – armincerf

+0

@armincerf'iterate'本身效率不高,在這個例子中,anon函數將被調用4次,每次都使用前一個值。然而,'f'函數確實重複了一些計算。 – madstap

+0

@armincerf我已經編輯了我的答案。這是你的意思嗎? – madstap

1

剛剛產生candidatess懶序列與iterate然後過濾他們也許應該是你所需要的:

(def pairs 
    (->> [1 2] 
     (iterate (fn [[n k]] 
        (if (< (sum-to-n n) (sum-n-to-k n k)) 
        [(inc n) k] 
        [n (inc k)]))) 
     (filter (partial apply is-right)))) 

user> (take 5 pairs) 
;;=> ([6 8] [35 49] [204 288] [1189 1681] [6930 9800]) 

語義上它就像手動生成一個懶序列,並應被視爲有效,但這個可能更習慣