Clojure是否有完全包含的范围函数?

6

Clojure中的range函数包含start,但不包含end(如果提供了)。在核心库中是否有一个可以提供完全包含(起始和结束)范围的函数?

我发现在某些情况下必须调整结束值的代码(例如在列表推导中向下而不是向上进行范围选择)难以阅读。例如:

(range n -1 -1)

我是否在文档中遗漏了它,还是有更简洁的方法来做到这一点?

我对guava Range API有些喜爱,因此我正在寻找类似灵活性的东西。


2
为什么你写成(range n (- 1) (- 1))而不是(range n -1 -1) - Diego Basch
好的,很有道理!我会更新问题。 - leeor
5个回答

5

没有标准的inclusive-range函数 - 根据Kevin Ingle的术语。

你可以在range的基础上构建一个:

(defn inclusive-range
  ([] (range))
  ([end] (range (inc end)))
  ([start end] (range start (inc end)))
  ([start end step] (range start (+ end step) step)))

但是,如果存在不整齐的结尾,你就会遇到麻烦。例如,
(inclusive-range 10 11.5)
;(10 11 12)

我想这不是你想要的。

1
如果您喜欢简洁,当缩短为“inc-range”时,这就成了一种双关语。 - Cogwheel
当最后一步可能超过“end”时,正确的行为应该是不明显的。 (10 11 12) 看起来并没有本质上的错误。@JohnCollins的 irange 产生了 (10 11),这也看起来并没有本质上的错误。inclusive-range 的一个更重要的问题是由于浮点误差:(inclusive-range 0 0.8 0.1) ;=> (0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999)irange 在这里得到了正确的结果:(0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999) - Mars
1
@Mars 我更喜欢JohnCollins的版本 https://dev59.com/Io_ea4cB1Zd3GeqPT-Ds#68476365。如果你将他的 <= 替换为 <,你就得到了标准的 range - Thumbnail
@JohnCollins的版本是我采用的。然而,展示多种解决方案也是值得的,所以即使你喜欢另一种方法,我也很感激。在某些方面,它更简单、更优雅。 - Mars

1
据我所知,没有核心函数,但是您可以为自己的使用定义一个函数:
(defn inclusive-range [start end] (range start (inc end)))

或者更一般地说,定义一个仅添加结尾的函数:

(defn inclusive-range [start end step] (flatten (list (range start end step) end)))

正如评论中指出的那样,上述解决方案可能不是最理想的。您提到您习惯于使用guava的Range,所以您也可以导入它并使用:

(import 'com.google.common.collect.Range)
(-> (Range/closed 1 10)
    (.contains 10))  ; returns true

在我的情况下,范围正在减少,因此需要比这更通用的范围。 - leeor
第二个例子不仅不符合惯用语(在Clojure中几乎从不使用flatten),而且还有很多问题,例如(inclusive-range 0 1 -1) - Diego Basch

1
也许你想编写类似这样的函数:
(defn my-range [start & {:keys [up-to down-to]}]
  (cond (and (nil? up-to) (nil? down-to)) (range (inc start))
        (nil? down-to) (range start (inc up-to))
        :else (range start (dec down-to) -1)))

user> (my-range 10)
(0 1 2 3 4 5 6 7 8 9 10)

user> (my-range 0 :up-to 10)
(0 1 2 3 4 5 6 7 8 9 10)

user> (my-range 10 :down-to -10)
(10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)

它略显啰嗦,但符合简单的范围行为,并添加了一些甜蜜的语法。


1
你可以这样定义一个等同于 range 但包含边界的函数:
(defn irange
  ([start end step]
   (take-while (if (pos? step) #(<= % end) #(>= % end)) (iterate #(+ % step) start)))
  ([start end]
   (irange start end 1))
  ([end]
   (irange 0 end))
  ([] (range)))

我认为这应该是被接受的答案。 (请参见我的评论@Thumbnail的答案。) - Mars

0

在Clojure中没有标准的包容范围函数,但是你可以通过以下方式实现

(defn inclusive-range [n] (map inc (range 0 n)))
(inclusive-range 10)

range 0也会被递增,因此保持为-1

相同问题的另一种解决方案

(defn inclusive-range [count] (range 0 (+ 1 count) ))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接