Is there a Clojure equivalent of Ruby's range for testing inclusion in range?

#1

So I was reading a Clojure solution to a exercise problem, where they did something really nice to check if a number is inside some boundaries:

(def bounds (set (range 100)))
...
(if (bounds x) ... ...)

What I liked was the check part (bounds x) instead of something like (and (>= x 0) (< x 100)).

I’ve seem this technique for changing verifications of the form a = b with a belongs to {b}. And I like it also because it makes the code easier to extend:

(#{a b c} x)

Instead of …

(or (= a x) (= b x) (= c x))

But, the problem with the approach with bounds is that all the 100 numbers are created (memorized) in order to simply check if a number is between to limits.

In ruby, there is the concept of “range” that can be used to generate sequence of elements, but also allows you to check membership without using memory. I.e:

(0..99).includes? x

And you have a similar style to the “bouds” example to check if a number is in the range, but without using memory.

I’m wondering if we have something similar to that in Clojure. Do we?

#2
(< 10 x 100) ; Checks if x is between 10 and 100 exclusive

(<= 10 x 100) ; Checks if x is between 10 and 100 inclusive
2 Likes
#3

Appart from my other answer, I realized maybe you were more asking if there is a way to check inclusion inside a range without realizing it, and not if there’s a simpler way to check inclusion between numbers.

The answer I’m pretty sure is no. The range function creates a LazySeq. Which is different from Ruby’s range, which creates a Range.

A LazySeq is like a singly linked list of some sort. You don’t have a reference to its end, just to its start, and you have no concept of the things in between the first and last element in the sequence.

So ranges aren’t first class in Clojure as far as I know. Thus what you’re asking doesn’t exist.

You could probably create a deftype Range which implements Seqable, and keeps the start and end elements, and an includes? that is polymorphic on the type of start. But there’s not one already in Clojure. Actually, that could be a fun learning exercise to attempt doing.

2 Likes
#4

Yes, this is exactly what I was looking for. Well, actually an “already included implementation” in the core, but you gave me an idea there. Thank you.

#5

I remember when I understood that this was what Python was doing. “How can this be so fast???” followed by, “Wow, that was impressive. You magically made my code faster while I was going for a slow solution”

#6

clojure.spec.alpha/int-in-range? might be what you’re looking for:

(if (s/int-in-range? 0 100 x) ...)

That’s 0 <= x < 100.

1 Like
#7

Thank you.

Is it right to use spec for business logic?

#8

We use clojure.spec.alpha very heavily in our production code for all sorts of data validation purposes. Different parts of spec are great for dev, test, and production usage.