performance - 使用差分法在 Clojure 中实现序列推理

标签 performance clojure functional-programming lisp immutability

我在 Haskell 中读到,你可以创建这样的序列:[1,3..9] 我用 Clojure 写了一个版本,虽然我喜欢没有状态的编程,但空间复杂度很大。执行此操作的更有效方法是什么?

编辑:如果您有兴趣了解解决方案,您可以 read my blog post .

用例:

(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]

代码:

(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))))

最佳答案

助手

(defn diff [s] (map - (rest s) s))

基于有限差分法推断序列

(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)))

例子

(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)

解释

(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)

这个 (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

关于performance - 使用差分法在 Clojure 中实现序列推理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21503344/

相关文章:

clojure - 追加到嵌套关联结构中

optimization - Clojure 有短路逻辑吗?

clojure - 在 Clojure 中测试 "self-evaluating"原子的单个谓词

java - 改进搜索功能

Scala:使用迭代器的动态编程递归

scala - 有没有更聪明的方法来对猫做到这一点?

java - 为什么对于大量数据,函数式比命令式快,而对于少量数据,函数式比命令式慢?

java - 更好的数组列表性能选项来重置对象

java - 调用多个集合来填充 firestore 中的关系时,性能如何受到影响

sql - 加速使用 exists 的 sql 查询