用差异法实现Clojure中的序列推理

div*_*210 2 lisp performance functional-programming clojure immutability

我读了在Haskell,你可以创建这样一个序列:1,3..9]我用Clojure写了一个版本,虽然我很喜欢编程无状态空间复杂度是巨大的.什么是更有效的方法来做到这一点?

编辑:如果您有兴趣了解解决方案,可以阅读我的博客文章.

用例:

(infer-n [1 2] 10)     => [1 2 3 4 5 6 7 8 9 10]
(infer-n [1 4 9] 10)   => [1 4 9 16 25 ... 100]
(infer-range [9 7] 1)  => [9 7 5 3 1]
Run Code Online (Sandbox Code Playgroud)

码:

(defn diffs
  "(diffs [1 2 5 12 29]) => (1 3 7 17)" 
  [alist]
  (map - (rest alist) alist))

(defn const-diff
  "Returns the diff if it is constant for the seq, else nil.
   Non-strict version." 
  [alist]
  (let [ds (diffs alist)
        curr (first ds)]
    (if (some #(not (= curr %)) ds)
      nil
      curr)))

(defn get-next
  "Returns the next item in the list according to the
   method of differences.
   (get-next [2 4]) => 6"
  [alist]
  (+ (last alist)
     (let [d (const-diff alist)]
       (if (= nil d)
         (get-next (diffs alist))
         d))))

(defn states-of
  "Returns an infinite sequence of states that the
   input sequence can have.
  (states-of [1 3]) => ([1 3]
                        [1 3 5]
                        [1 3 5 7]
                        [1 3 5 7 9]...)"
  [first-state]
  (iterate #(conj % (get-next %)) first-state))

(defn infer-n
  "Returns the first n items from the inferred-list.
   (infer-n [1 4 9] 10) => [1 4 9 16 25 36 49 64 81 100]" 
  [alist n]
  (take n (map first (states-of alist))))

(defn infer-range
  "(infer-range [10 9] 1) => [10 9 8 7 6 5 4 3 2 1]" 
  [alist bound]
  (let [in-range (if (>= bound (last alist))
                    #(<= % bound)
                    #(>= % bound))]
    (last (for [l (states-of alist) :while (in-range (last l))] l))))
Run Code Online (Sandbox Code Playgroud)

A. *_*ebb 5

帮手

(defn diff [s] (map - (rest s) s))
Run Code Online (Sandbox Code Playgroud)

基于有限差分的方法推断序列

(defn infer-diff [s]
  (->> (take (count s) (iterate diff s)) 
       (map last) reverse 
       (drop-while zero?) ;optional 
       (iterate (partial reductions +))
       (map last) rest 
       (concat s)))
Run Code Online (Sandbox Code Playgroud)

例子

(take 10 (infer-diff [1 3]))
;=> (1 3 5 7 9 11 13 15 17 19)

(take 10 (infer-diff [1 4 9]))
;=> (1 4 9 16 25 36 49 64 81 100)
Run Code Online (Sandbox Code Playgroud)

说明

(def s (list 1 4 9))

(take (count s) (iterate diff s))
;=> ((1 4 9)
;    ( 3 5 )
;    (  2  ))

(map last *1)
;=> (9 5 2)

(reverse *1)
;=> (2 5 9)
Run Code Online (Sandbox Code Playgroud)

(2 5 9)连续一阶差分金字塔的片是所有你需要确定序列的其余部分.

(reductions + *1)
;=> (2 7 16) and the next in the sequence is at the end: 16

(reductions + *1)
;=> (2 9 25) and the next in the sequence is at the end: 25

(reductions + *1)
;=> (2 11 36) and the next in the sequence is at the end: 36
Run Code Online (Sandbox Code Playgroud)