Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Added IsValid, Bind, and BindToValid. Renamed Last to End #1981

Merged
merged 2 commits into from
Dec 15, 2017

Conversation

KrzysztofCwalina
Copy link
Member

I added some helpers and overrode ToString to make it easier to work with ranges.
Renamed Last to End as "Last" is confusing given the value is exclusive.

@ahsonkhan, @joshfree, @eerhardt, @jcouv

}

public bool IsBound => First != UnboundedFirst && Last != UnboundedLast;
public bool IsBound => First != UnboundedFirst && End != UnboundedEnd;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't First == UnboundedFirst? I thought only End is exclusive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property returns true if both of the ends are bound. Otherwise false. The property can be used to see if some operations on the range would work, e.g. GetEnumerator works only for bound ranges. "Unbound" means "I don't know/care what the value is". It does not make sense to enumerate such ranges.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. So, you can't enumerate from [int.MinValue..int.MinValue + 5] similar to int.MaxValue?

int.MaxValue being the sentinel for End makes sense given End is exclusive (for example [0..int.MaxValue] works fine. Are we treating the lower bound the same way?

}
public static Range Construct(int first, int last)
=> new Range(first, last);
public static Range Construct(int first, int end)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add new line

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

else // First is bounded
{
if (First < 0) return false;
if (End == UnboundedEnd) return First <= length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should First be < length here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it so that range can start after the last element. This is to allow empty ranges.

}

/// <summary>
/// Returns true if this Range is a valid range for a zero based index of sspecified length.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spelling - sspecified

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix. thanks

return End <= length;
}

throw new NotImplementedException();
Copy link
Member

@ahsonkhan ahsonkhan Dec 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we ever reach here? It doesn't look like it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah, I will remove.

throw new NotImplementedException();
}

public Range Bind(int length)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a comment here explaining what Bind is meant to do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

@@ -106,7 +141,7 @@ public void Errors()
});
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
// MaxValue is used as a sentinel for Last, so Last cannot endup being it.
// MaxValue is used as a sentinel for End, so End cannot endup being it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spelling endup

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix

Copy link
Member

@ahsonkhan ahsonkhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise, LGTM.

{
public const int UnboundedFirst = Int32.MinValue;
public const int UnboundedLast = Int32.MaxValue;
public const int UnboundedEnd = Int32.MaxValue;

public readonly int First;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First should be renamed to Start to follow naming convention as @jcouv mentioned earlier in the previous PR: Start and End.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I am not thrilled with Start. It sounds like an action is about to start. That's why I originally did not use End. But I will think about the naming a bit more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @YohDeadfall as Start seems to fit with End. However, I also see @KrzysztofCwalina point. I like First and Last better, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Begin would be fine, but unfortunately it shouldn't be used for historical reasons since everywhere in .NET only Start/End is used for such situations in parameter names, fields, properties. Also Begin and End are used for pre-Task async operations in method names.


public bool Contains(int value)
{
if (!IsBound) throw new InvalidOperationException("Unbound ranges cannot contain");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's unbound, then it contains any value. If it has unbound start, check the end only. Same for ranges with unbound start.

if (IsBound) return Start <= value && value < End;
if (Start == UnboundStart) return value < End;
if (End == UnboundEnd) return Start <= value;
return false;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not forget to check direction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically yes, but what are the scenarios for calling Contains on unbound ranges?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exceptions are okay for GetEnumerator since we can't iterate over infinity sequences, but Contains can work and therefore should. Otherwise, it wouldn't be useful, and developers would be managed to write they own implementation. For example:

class Example
{
    private Range _range;

    public Example(Range range) => _range = range;

    public void DoSomething(int value)
    {
        if (_range.Contains(value))
        {
        }
        else
        {
        }
    }
}

What if range provided to the constructor is unbound? Should I use (_range.Start == Range.UnboundStart || _range.Start <= value) && (_range.End == Range.UnboundEnd || value < _range.End) to know if the value is in _range to be sure that the code will not throw?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the in pattern matching will use Contains.

switch (variable)
{
    case in ..0: // should not throw
        break;
    // ...
    case in 5..: // should not throw
        break;
}

}

public bool IsBound => First != UnboundedFirst && Last != UnboundedLast;
public bool IsBound => First != UnboundedFirst && End != UnboundedEnd;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsBound should take into account that the range could be reversed:

  • Range(UnboundedLast, UnboundedFirst) is unbound;
  • Range(5, UnboundedFirst) is unbound;
  • Range(UnboundedLast, 5) is unbound too.

@KrzysztofCwalina KrzysztofCwalina deleted the RangeHelpers branch January 3, 2018 00:20
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants