Clojure惰性序列,其中序列从两端增长

Clojure惰性序列,其中序列从两端增长,clojure,lazy-sequences,Clojure,Lazy Sequences,我编写了一个函数(如下所示),它返回一个函数,该函数从注释中定义的infiniate集合中返回n个项 ; Returns a function which in turn returns a vector of 'n' items, with init-val being the 'middle' value, ; Values to the 'right' of init-val being 'step' added to the (previously) last value, and it

我编写了一个函数(如下所示),它返回一个函数,该函数从注释中定义的infiniate集合中返回n个项

; Returns a function which in turn returns a vector of 'n' items, with init-val being the 'middle' value,
; Values to the 'right' of init-val being 'step' added to the (previously) last value, and items 'left' of
; 'init-val' being 'step' subtracted from the (previously) first value in the list. 
(defn range-right-left [init-val step] 
(fn [n] ; produce a vector containing 'n' items
    (loop [Vector (cond (zero? n) [] 
                (pos? n)  [init-val] 
                :else  nil)]
        (if (or (= (count Vector) n) (nil? Vector)) Vector ; base case(s)
            (recur
                (if (odd? (count Vector))
                    (conj Vector (+ (last Vector) step)) ; add 'step' to the last element of the vector and place it on the back of the vector 
                    (vec (cons (- (first Vector) step) Vector)))))))) ;else if Vector contains an even number of items, subtract step from the first item in the vector and place it on front of the resulting collection (converting to vector)
为了澄清函数的行为,我包含了测试的代码(所有测试都通过了)

不过,我真正想要的是“range right-left”返回一个惰性序列,而不是一个函数。换句话说,与其这样做,不如:

((range-right-left 7 3) 3)
我希望能够做到:

(take 3 (range-right-left 7 3))

惰性序列的默认行为似乎是严格地从左向右增长。我曾尝试开发一个可以双向增长的惰性seq,但没有效果。我非常感谢您的建议。

如果您只想获得等价类的
n
成员,那么您可以这样定义您的函数:

(defn range-right-left [x step]
  (interleave (iterate #(- % step) x)
              (iterate #(+ % step) (+ x step))))

(take 5 (range-right-left 7 3))
; => (7 10 4 13 1)

(sort (take 5 (range-right-left 7 3)))
; => (1 4 7 10 13) 

结果的顺序与以前不同,但正如示例所示,如果您确实需要无限流的切片按排序顺序进行排序,则可以使用
sort

如果您只想获得一个等价类的
n
成员,则可以这样定义您的函数:

(defn range-right-left [x step]
  (interleave (iterate #(- % step) x)
              (iterate #(+ % step) (+ x step))))

(take 5 (range-right-left 7 3))
; => (7 10 4 13 1)

(sort (take 5 (range-right-left 7 3)))
; => (1 4 7 10 13) 

结果的顺序与以前不同,但正如示例所示,一旦您将无限流的片段按顺序排序,您就可以使用
排序

您可以尝试使用拉链的方法

(defn left[[left v right]]
[(下一个左)(第一个左)(cons v右)])
(定义右[[左v右]]
[(cons v左)(右一)(右下)])
(除电流[[左v右]]v)
现在我们可以把你的函数定义为

(定义范围右-左[初始值步进]
[(下一步(迭代?(%step)初始值))
初始值
(下一步(迭代#(+%step)init val)))
然后,我们可以通过使用
left
right
移动集合视图和
curr
提取当前元素来获取任何给定元素:

(def拉链(范围从右到左4 10))
(->拉链左币);=>-26
我们还可以创建两个实用函数:

(定义左序列[[左v右]]
(左五)
(定义右顺序[[左v右]]
(右)
这让我们能够做一些接近你想要的事情

(取3(左序列(范围左7 3));=>(7 4 1)
(取3(右序列(范围从右到左7 3));=>(7 10 13)

注意:拉链远比您在这里使用的更通用,因此这种方法对于您的特定用途来说可能有些过分。如果您的订购对您来说不重要,那么我建议您只使用DaoWen的方法,将序列的两侧交错排列。

您可以尝试使用拉链的方法

(defn left[[left v right]]
[(下一个左)(第一个左)(cons v右)])
(定义右[[左v右]]
[(cons v左)(右一)(右下)])
(除电流[[左v右]]v)
现在我们可以把你的函数定义为

(定义范围右-左[初始值步进]
[(下一步(迭代?(%step)初始值))
初始值
(下一步(迭代#(+%step)init val)))
然后,我们可以通过使用
left
right
移动集合视图和
curr
提取当前元素来获取任何给定元素:

(def拉链(范围从右到左4 10))
(->拉链左币);=>-26
我们还可以创建两个实用函数:

(定义左序列[[左v右]]
(左五)
(定义右顺序[[左v右]]
(右)
这让我们能够做一些接近你想要的事情

(取3(左序列(范围左7 3));=>(7 4 1)
(取3(右序列(范围从右到左7 3));=>(7 10 13)

注意:拉链远比您在这里使用的更通用,因此这种方法对于您的特定用途来说可能有些过分。如果您的订购对您来说不重要,那么我建议您使用DaoWen的方法,将序列的两边交错排列。

我建议您这样做:

(defn from-right-left-range
  "Take n items from a range expanding from init by step in both
  directions."
  [init step n]
  (cond
    (= 0 n) '()
    (= 1 n) (list init)
    :else (let [d (* step (dec n))]
            (concat [(- init d)]
                    (from-right-left-range init step (dec n))
                    [(+ init d)]))))
这个递归函数可以被记忆,并且运行得更快

(dotimes [_ 5]
  (time (from-right-left-range 7 3 100)))
=> "Elapsed time: 0.129167 msecs"
   "Elapsed time: 0.065219 msecs"
   "Elapsed time: 0.061449 msecs"
   "Elapsed time: 0.069579 msecs"
   "Elapsed time: 0.060461 msecs"
现在记下:

(def from-right-left-range (memoize from-right-left-range))
(dotimes [_ 5]
  (time (from-right-left-range 7 3 100)))
=> "Elapsed time: 0.297716 msecs"
   "Elapsed time: 0.038473 msecs"
   "Elapsed time: 0.013715 msecs"
   "Elapsed time: 0.010902 msecs"
   "Elapsed time: 0.010372 msecs"
根据您最初希望通过使用惰性seqs实现的目标,如果是性能,这就是您的解决方案

现在您可以做的是创建一个类似的惰性seq

(defn right-left-ranges
  [init step]
  (map (partial from-right-left-range init step) (range)))

您可以在这个懒惰的seq上使用(n个ls n),就像您在懒惰的seq上使用(取n个ls)来获得您的双增长seq一样。多亏了memoize,才会添加开头和结尾处尚未计算的缺失元素。

我建议这样做:

(defn from-right-left-range
  "Take n items from a range expanding from init by step in both
  directions."
  [init step n]
  (cond
    (= 0 n) '()
    (= 1 n) (list init)
    :else (let [d (* step (dec n))]
            (concat [(- init d)]
                    (from-right-left-range init step (dec n))
                    [(+ init d)]))))
这个递归函数可以被记忆,并且运行得更快

(dotimes [_ 5]
  (time (from-right-left-range 7 3 100)))
=> "Elapsed time: 0.129167 msecs"
   "Elapsed time: 0.065219 msecs"
   "Elapsed time: 0.061449 msecs"
   "Elapsed time: 0.069579 msecs"
   "Elapsed time: 0.060461 msecs"
现在记下:

(def from-right-left-range (memoize from-right-left-range))
(dotimes [_ 5]
  (time (from-right-left-range 7 3 100)))
=> "Elapsed time: 0.297716 msecs"
   "Elapsed time: 0.038473 msecs"
   "Elapsed time: 0.013715 msecs"
   "Elapsed time: 0.010902 msecs"
   "Elapsed time: 0.010372 msecs"
根据您最初希望通过使用惰性seqs实现的目标,如果是性能,这就是您的解决方案

现在您可以做的是创建一个类似的惰性seq

(defn right-left-ranges
  [init step]
  (map (partial from-right-left-range init step) (range)))

您可以在这个懒惰的seq上使用(n个ls n),就像您在懒惰的seq上使用(取n个ls)来获得您的双增长seq一样。多亏了memoize,才会添加开头和结尾处尚未计算的缺失元素。

惰性序列只能向一个方向增长,因为它们有一个惰性尾巴。一旦你有了一个懒散序列的头部,你就不能回去“懒散地”改变它,因为它已经被评估过了。你想在这里干什么?似乎您应该能够用2个范围而不是1个范围来解决您的
范围右-左
问题。如果您提供有关该用例的更多详细信息,我们可能会建议一个替代解决方案。一个用例是能够从“r(mod m)”定义的等价类中获取n个项r’将是起始中间值。左边的下一项将从列表的第一项中减去m。同样,右边的下一项将通过在列表的最后一项中添加m来获得。我希望在lazy-s中定义此行为