#791 Range wishlist: min, max, last, equiv

KevinKelley Sat 17 Oct 2009

Range class is one I use a lot, having first-class range literals is handy all over the place.

Every once in a while I wish for better semantics on the class, though. As it stands, Range acts as just a data-holder, for the start, end, and exclusive fields. Sometimes that's good, but sometimes you want a range to know something about the concept it represents. Here's 4 apis that I think Range should have:

min / max

Should return the min (max) of the elements in the range. Equivalent to range.toList.min.

last

Don't know about name confusion with end, but occasionally you need to get the final element in a range; would be nice not to have to use

r.exclusive? (r.end < r.start? r.end + 1 : r.end - 1): r.end

Having first and last would seem meaningful, but of course first would be completely redundant.

equiv (or equivalent)

I guess I can understand equals being like it is, but it seems like a real weird thing that 0..2 != 0..<3 ; I would think that equals should act like it does with lists: they're equal if their elements all match. At least there ought to be a method for that.

All these (to me at least) seem like they ought to have the same semantics as List; but without creating the list, of course.

DanielFath Sat 17 Oct 2009

I must ask what is the difference between max and last?

Max returns the largest element which unless I'm misinterpreting is the last element in the range?

I'm kind of weirded out by the fact that equals doesn't do that already. All in all I like your first and last suggestion except that I suggest making equals return true for 0..2 == 0..<3 and making a function called same return false for 0..2 == 0..<3 (since their boundaries aren't same).

However if there are plans to introduce some kind of joker or special characters into the range I could see this kind of reasoning being a problem.

KevinKelley Sat 17 Oct 2009

what is the difference between max and last?

Difference is with descending ranges: if the range is ascending, last == max; if descending, last == min.

(7..2).min  == 2
(7..2).max  == 7
(7..2).last == 2
(7..<2).min  == 3
(7..<2).max  == 7
(7..<2).last == 3

andy Sat 17 Oct 2009

0..2 != 0..<3

I agree that should evaluate to true.

As for the rest - seems cleaner and more useful to use toList and have the full List API at your disposal. Its a few extra chars, but you can already do all that with List:

(4..1).toList.min    =>  1
(4..1).toList.max    =>  4
(4..1).toList.first  =>  4
(4..1).toList.last   =>  1

DanielFath Sat 17 Oct 2009

Difference is with descending ranges: if the range is ascending, last == max; if descending, last == min.

My bad.

0..2 != 0..<3 I agree that should evaluate to true.

Hmm this seems like a big gotcha. I assume many new comers would expect equals works on the range content and not the range itself. So you could produce something that compares content like equalsContent to prevent some boiler plate code. Though it is not much of a hassle.

KevinKelley Sat 17 Oct 2009

but you can already do all that with List:

What bothers me about that is the time-complexity: creating a list almost has to be O(N) in the size of the range, while calculating those (min,max,last) values can be done in O(K) (just a very few machine instructions).

alexlamsl Sat 17 Oct 2009

What bothers me about that is the time-complexity: creating a list almost has to be O(N) in the size of the range

We can have Range extends List, or have Range.toList returns a synthetic List. Either way, the O(N) cost would be avoided.

alexlamsl Sat 17 Oct 2009

And whilst we are on the topic, it might be useful to have Range applicable to any Type that implements increment and decrement.

Range being a sub-type of List would seem pretty natural IMHO, as places where I would supply a List would fit Range equally well.

On a slightly unrelated note - a[list] would seem a strange but wonderful generalisation over a[range].

brian Sun 18 Oct 2009

I added isEmpty, min, max, first, last with the exact semantics of calling toList and then calling the respective method (without the overhead of building up the list).

changeset

A lazy/generator list would be cool and something I'd like to consider in the future (just not right now).

I don't really like another equals method.

KevinKelley Sun 18 Oct 2009

I added ...

Cool, those seem good and proper.

Any further thoughts on equals? Adding another equals is probably <em>wrong. Is there a reason to keep the current range.equals as it is?

I guess for Range there's various possible meanings for equality.

  • === - same object
  • equals - same object behavior (equal fields in a const obj)
  • min/max equal - range covers same values
  • first/last equal - same values, in same order

I'd probably vote for having the last one as the meaning of equals, but it's not as important with the new methods.

If Fan had a mechanism for memoizing construction of const objects that are side-effect-free, then === and current meaning of equals would conflate to both meaning identity. As is, maybe there's reason for keeping them separate?

brian Sun 18 Oct 2009

I think the current definition of equals is correct:

start..end != start..<end

In some uses of range you might say that you can normalize the exclusive end, but that isn't true in all cases. For example, in all our uses of ranges for slice operations, the notion of exclusive and inclusive is quite important.

Plus, I think for anything that serializes as a string, equality should hold true for toStr and fromStr (which is another case where the exclusive flag is semantically significant).

Login or Signup to reply.