ruby picture

RCR 282: Add Comparable#bound_by

Submitted by flgr (Thu Oct 14 10:10:35 UTC 2004)


It is frequently necessary to limit a number to an upper and lower boundary. <tt>Comparable#bound_by</tt> simplifies this.


One often needs to limit values so that they are not outside specified boundaries, for example when programming games: You usually have one or more objects that are not allowed to go outside the map. (A quick survey on #ruby-lang seemed that others also frequently need to limit values via boundaries.)

Currently this is usually done like this:

new_x = x + vel_x
new_x = 0 if new_x < 0
new_x = map_width if new_x > map_width

This is quite longish and repeats variable names a lot.


Therefore it should in my opinion be possible to write the above as the following:

new_x = (x + vel_x).bound_by(0, map_width)


The above could also be rewritten like this in current Ruby:

new_x = [[x + vel_x, 0].max, map_width].min

But I think the risk of introducing an error with this (swapping min with max, for example) is too high.

Another issue is that the above will construct two temporary Arrays -- this might well be a performance inconvenience, because this construct does commonly appear in inner loops that get executed a lot.

There also seems to be some other languages that have this feature, though they usually use shorter names (like <tt>fit</tt>) -- I think that a Module like Comparable should not risk name collisions by using a name that can have many meanings.


module Comparable
  def bound_by(lower, upper)
    return lower if self &lt; lower
    return upper if self &gt; upper
    return self
ruby picture
Comments Current voting

This seems to be something that isn't a bad idea, but does it apply to Comparable objects in general? -- Austin Z.

Would'nt it be better to have some kind of BoundedObject ?

-- gabriele renzi

How would the BoundedObject class look? What would it do?

-- Florian Gross

Well, suppose you create a Bounded instance with another class, and two bounds. Whenever you try to perform some operation over it such as '+' it could check if the boundary still is ok (via <=>) or return the max value. So in case x is a value Bounded beetween 0 and max_width in your example, new_x = (x + vel_x) would return x or 0.

But now that I think of it it may be overcomplicated :/

-- gabriele renzi

Range might be used for this, if it is suitably improved --something I've been working on a bit. It's not far off as it is. First Range would require a reverse_each tied to a reverese_succ, or some such naming, then by defining special objects as bounds such that succ and reverse_succ only goes so far (or loop back around or throw an error, as the the case may be), then that should do it.

-- T.

But why not put it into Comparable? After all there is also Comparable#between? (which takes an lower and upper limit) so I think that we don't always need a Range object when we want to provide upper and lower constraints. (Plus this is faster because no temporary Range needs to be constructed -- it is only a small point, but it can be important.)

-- Florian Gross

This doesn't make much sense. It seems it would be much easier to make a class and override the = method.

-- Olathe

I don't see how that would help with changing the logic. In fact the sample code from above could have been in an accessor if you prepend an @ to new_x. Are you sure you understand what this RCR is about?

-- Florian Gross

Strongly opposed 1
Opposed 1
Neutral 0
In favor 2
Strongly advocate 1
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .