1

我编写了一个函数(如下所列),该函数返回一个函数,该函数从注释中定义的无限集中返回 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)

为了阐明函数的行为,我将包含测试代码(全部通过)。

(deftest range-right-left-test
    (is (= nil ((range-right-left 7 3) -1)))
    (is (= [] ((range-right-left 7 3)  0)))
    (is (= [7] ((range-right-left 7 3)  1)))
    (is (= [7 10] ((range-right-left 7 3)  2)))
    (is (= [4 7 10] ((range-right-left 7 3)  3)))
    (is (= [4 7 10 13] ((range-right-left 7 3)  4)))
    (is (= [1 4 7 10 13] ((range-right-left 7 3)  5))))

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

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

我希望能够做到:

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

惰性序列严格从左到右增长似乎是默认行为。我试图开发一个可以双向增长的惰性序列,但无济于事。我非常感谢这样做的建议。

4

3 回答 3

3

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

(defn left [[left v right]]
  [(next left) (first left) (cons v right)])
(defn right [[left v right]]
  [(cons v left) (first right) (next right)])
(defn curr [[left v right]] v)

所以现在我们可以将你的函数定义为

(defn range-right-left [init-val step]
  [(next (iterate #(- % step) init-val))
   init-val
   (next (iterate #(+ % step) init-val))])

然后我们可以通过使用leftright移动我们的集合视图并curr提取当前元素来获取任何给定元素:

(def zipper (range-right-left 4 10))
(-> zipper left left left curr) ; => -26

我们还可以创建两个实用函数:

(defn left-seq [[left v right]]
  (cons v left))
(defn right-seq [[left v right]]
  (cons v right))

这使我们有能力做一些接近你想要的事情

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

注意:拉链比您在这里所做的要通用得多,因此这种方法对于您的特定用途可能有点过分。如果您的顺序对您来说不重要,那么我建议您只使用道文的方法,将序列的两侧交错。

于 2013-07-30T00:21:00.287 回答
1

如果您只想获取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如果您确实需要按排序顺序获得无限流的切片,则可以只使用它。

于 2013-07-29T23:04:00.613 回答
0

我建议这样做:

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

根据您最初希望使用惰性序列实现的目标,如果它是性能,这就是您的解决方案。

现在你可以做的是创建一个类似的惰性序列

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

你可以在惰性序列上使用 (nth ls n) ,就像你在惰性序列上使用 (take n ls) 一样来获得你的双增长序列。感谢 memoize,只会添加开头和结尾尚未计算的缺失元素。

于 2013-07-30T14:55:34.367 回答