创建一个函数,该函数返回比前一个函数调用长一个长度的向量的所有切片

时间:2016-12-18 01:39:19

标签: clojure

例如,

(def sample-arr [10, 7, 5, 8, 11, 9])

(defn create-func [arr]
;; Code goes here)

(def result-function (create-func sample-arr))

(result-function) => [7, 5, 8, 11, 9], [10, 7, 5, 8, 11]
(result-function) => [7, 5, 8, 9], [10, 7, 5, 8], etc
(result-function) => {all subarays of length 2}

历史:我正在学习Clojure,我自己创建了这个问题,以便更好地理解Closure中闭包的工作原理。一切都是不可变的,我无法弄清楚如何将状态存储在我想写的闭包中。

3 个答案:

答案 0 :(得分:3)

在clojure中创建以这种方式保留状态的函数是非常不恰当的,更常见的是在函数式编程中。函数式编程通常是关于保持你的函数" referentially transparent",它可以简单地作为"函数将始终为相同的输入返回相同的值" (因此可以用其返回值替换函数调用)。你的函数显然不是引用透明的,因为它将根据调用历史记录返回一个不同的值,而不是仅依赖于它的输入参数(它不需要任何参数)。在您进行实验时,使用不可变数据结构很难实现。

更具FP风格的方法是制作构建一个"懒惰"列表中列表中的每个项目都是您建议的连续呼叫的结果:

(defn all-subs [input-arr]
  (map (fn[length] (partition length 1 input-arr)) (reverse (range 1 (count input-arr)))))

有了这个,你得到:

(def result (all-subs [10, 7, 5, 8, 11, 9]))

(first result)
=> ((10 7 5 8 11) (7 5 8 11 9))

(second result)
=> ((10 7 5 8) (7 5 8 11) (5 8 11 9))

(nth result 3)
=>((10 7) (7 5) (5 8) (8 11) (11 9))

这样你只能使用不可变结构,引用透明,甚至支持延迟计算!赢了!

答案 1 :(得分:0)

作为一种练习,我会选择类似的东西:

(defn all-partitions
  ([items] (all-partitions items (count items)))
  ([items n] (with-meta (partition n 1 items) 
                        {:next #(all-partitions items (dec n))})))

对此函数的调用返回分区,此结果的元数据包含对同一集合进行分区的函数,但组中只有一个项目:

(def res (all-partitions [1 2 3 4 5 6 7]))

(println res)
;;=> ((1 2 3 4 5 6 7))

(def res2 ((-> res meta :next)))

(println res2)
;;=> ((1 2 3 4 5 6) (2 3 4 5 6 7))

(def res3 ((-> res2 meta :next)))

(println res3)
;;=> ((1 2 3 4 5) (2 3 4 5 6) (3 4 5 6 7))

等等

答案 2 :(得分:0)

这是一个可变版本,使用原子和协议+记录只是为了好玩:

 (def n (atom 0))
 (def init-len (atom 0))
 (def final-result (atom nil))

 (defprotocol StateFunction
   (init [this])
   (iter [this])
   (reset [this]))

 (defrecord ResultFunction [coll]
   StateFunction
   (init [this]
         (let [len (count coll)]
           (do (reset! n len)
               (reset! init-len len)
               (reset! bcoll coll))))
   (iter [this]
         (let [delta (inc (- @init-len @n))]
           (if (nil? @final-result)
             (loop [base coll
                    result '()
                    i 0]
               (cond (>= i delta)
                       (let [_  (swap! n dec)
                             _ (if (zero? @n) (reset! final-result result))]
                         result)
                     :else
                       (recur (rest base) (conj result (take @n base)) (inc i))))
             @final-result)))
   (reset [this]
          (do (reset! n 0) (reset! init-len 0) (reset! bcoll []) (reset! final-result nil))))

 (defn create-func [coll]
   (let [my-fun (map->ResultFunction {:coll coll})]
     (init my-fun)
     my-fun))

 (def my-fun (create-func [1 2 3 4 5 6 7 8 9]))
 (iter my-fun) ;; gives next list of partitions, result at n = 1 is stored to avoir redo
 (reset my-fun) ;; resets atoms, you can next init them to restart the cycle

我希望有可能以一种我们可以拥有它的多个实例(记录中的局部原子)的方式包装它,尽管你可以返回更新的记录