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

左から右に厳密に成長するのは、遅延シーケンスのデフォルトの動作のようです。双方向に成長できるレイジー seq を開発しようとしましたが、役に立ちませんでした。そのような提案をいただければ幸いです。

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

left次に、 and を使用しrightてコレクションのビューを移動しcurr、現在の要素を抽出することで、任意の要素を取得できます。

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

2 つのユーティリティ関数を作成することもできます。

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

注: ジッパーは、ここで行っていることよりもはるかに一般的であるため、このアプローチは特定の用途にはやり過ぎかもしれません。順序が重要でない場合は、シーケンスの両側をインターリーブする DaoWen のアプローチを使用することをお勧めします。

于 2013-07-30T00:21:00.287 に答える
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"

レイジー seq を使用して最初に達成したかったことに応じて、それがパフォーマンスである場合、これが解決策です。

今できることは、like の lazy-seq を作成することです

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

レイジー seq で (nth ls n) を使用するのと同じように、そのレイジー seq で (nth ls n) を使用して、二重に成長した seq を取得できます。memoize のおかげで、まだ計算されていない先頭と末尾の欠落要素だけが追加されます。

于 2013-07-30T14:55:34.367 に答える