简体   繁体   中英

Clojure, implement range, why this solution doesn't work

I want to implement range function of clojure, why following code won't work ?

(fn [low high]
  (loop[low low
        ret []]
    (if(= low high)
    (list ret)
    (recur (inc low) (concat ret [low])))))

I've given two correct implementations below. Your problem is you were wrapping your accumulator, which was already a list, in another sequence when you called (list ret).

(defn my-range [low high]
  (loop [low low
        ret []]
    (if (= low high)
      (seq ret)
      (recur (inc low) (conj ret low)))))

(defn my-range2 [low high]
  (take-while
    (partial > high)
    (iterate inc low)))

Edit: range doesn't include the upper limit parameter in its returned seq, changed partial in my-range2 from >= to > .

There are several problems with your code.

As the previous answers point out

  • (list ret) is wrong. Replace it with (seq ret) . Note: To return exactly () , not nil for an empty sequence, use (if (seq ret) (seq ret) ()) . Nil-punning probably makes this needless.
  • concat is over the top. Use conj instead.

Also,

  • Guard against runaway: (my-range 10 0) loops forever. Replace = with >= .

Making these changes,

(defn my-range [low high]
  (loop [low low, ret []]
    (if (>= low high)
      (if (seq ret) (seq ret) ())
      (recur (inc low) (conj ret low)))))

The remaining problem is that this is not lazy: it develops the whole vector before returning. For example,

(take 5 (my-range 42 (java.lang.Integer/MAX_VALUE)))

... seems to take forever, though you only want five elements. A simple lazy version is

(defn my-range [low high]
  (lazy-seq
    (if (>= low high) () (cons low (my-range (inc low) high)))))

Then

(take 5 (my-range 42 (java.lang.Integer/MAX_VALUE)))
;(42 43 44 45 46)

... is immediate.

RedDeckWins's my-range2 does just as well, and is neater.

EDIT: range returns a list, not a vector, changed return statement to (if (seq ret) (seq ret) ()) in first implementation of my-range .

Your code is fine, except for (list ret) part.

list function creates new list with given elements (in your example, with single ret element). If you want to convert your sequential, you should use seq function instead.

I would also recommend you not to use concat function here, because its lazy and may cause a StackOwerflowError producing long sequences.

The rest of your code is fine.

For working example see RedDeckWins's answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM