Skip to content Skip to sidebar Skip to footer

Notation For Intervals?

I want to make a Python class for intervals of real numbers. Syntax most closely related to mathematical notation would be Interval([a, b)) or, even better, Interval[a, b) to const

Solution 1:

It's impossible to "fix" syntactically invalid python by making a custom class.

I think the closest you can get to the mathematical interval notation in python is

Interval('[a, b)')

This way becomes even more lightweight if you are passing intervals as arguments to a function and the function converts it's arguments to an appropriate type before using them. Example:

def do_foo(interval, bar, baz):
    interval=Interval(interval)
    # do stuff

do_foo('[3,4)', 42, true)

Possible implementation:

import re

classInterval:
    def__init__(self, interval):
        """Initialize an Interval object from a string representation of an interval
           e.g: Interval('(3,4]')"""ifisinstance(interval, Interval):
            self.begin, self.end = interval.begin, interval.end
            self.begin_included = interval.begin_included
            self.end_included = interval.end_included
            return
        number_re = '-?[0-9]+(?:.[0-9]+)?'
        interval_re = ('^\s*'
                       +'(\[|\()'# opeing brecket
                       + '\s*'
                       + '(' + number_re + ')'# beginning of the interval
                       + '\s*,\s*'
                       + '(' + number_re + ')'# end of the interval
                       + '\s*'
                       + '(\]|\))'# closing brecket
                       + '\s*$'
                      )
        match = re.search(interval_re, interval)
        if match isNone:
            raise ValueError('Got an incorrect string representation of an interval: {!r}'. format(interval))
        opening_brecket, begin, end, closing_brecket = match.groups()
        self.begin, self.end = float(begin), float(end)
        if self.begin >= self.end:
            raise ValueError("Interval's begin shoud be smaller than it's end")
        self.begin_included = opening_brecket == '['
        self.end_included = closing_brecket == ']'# It might have been batter to use number_re = '.*' and catch exeptions float() raises insteaddef__repr__(self):
        return'Interval({!r})'.format(str(self))

    def__str__(self):
        opening_breacket = '['if self.begin_included else'('
        closing_breacket = ']'if self.end_included else')'return'{}{}, {}{}'.format(opening_breacket, self.begin, self.end, closing_breacket)

    def__contains__(self, number):
        if self.begin < number < self.end:
            returnTrueif number == self.begin:
            return self.begin_included
        if number == self.end:
            return self.end_included

Solution 2:

You can't change Python's existing syntax rules (without changing the whole language), but you can get usably close to what you want:

classInterval(object):
    def__init__(self, left_bracket, a, b, right_bracket):
        iflen(left_bracket) !=1or left_bracket notin'[(':
            raise ValueError(
                'Unknown left bracket character: {!r}'.format(left_bracket))
        iflen(right_bracket) !=1or right_bracket notin'])':
            raise ValueError(
                'Unknown right bracket character: {!r}'.format(right_bracket))

        if a < b:
            self.lower, self.upper = a, b
        else:
            self.lower, self.upper = b, a

        self.left_bracket, self.right_bracket = left_bracket, right_bracket

        if left_bracket == '[':
            if right_bracket == ']':
                self._contains = (
                    lambda self, val: self.lower <= val <= self.upper)
            else:
                self._contains = (
                    lambda self, val: self.lower <= val <  self.upper)
        else:
            if right_bracket == ']':
                self._contains = (
                    lambda self, val:  self.lower < val <= self.upper)
            else:
                self._contains = (
                    lambda self, val:  self.lower < val <  self.upper)

    __contains__ = lambda self, val: self._contains(self, val)

    def__str__(self):
        return'{}{}, {}{}'.format(self.left_bracket, self.lower, self.upper,
                                   self.right_bracket)

    def__repr__(self):
        return'{}({!r}, {}, {}, {!r})'.format(self.__class__.__name__,
                self.left_bracket, self.lower, self.upper, self.right_bracket)

if __name__ == '__main__':
    interval1 = Interval('[', 1, 3, ']')  # closed interval
    interval2 = Interval('[', 1, 3, ')')  # half-open intervalprint('{} in {}? {}'.format(3, interval1,  3in interval1))
    print('{} in {}? {}'.format(3, interval2,  3in interval2))

Output:

3 in [1, 3]? True
3 in [1, 3)? False

Note: The a and b arguments can be any type that can be compared.

Solution 3:

You cannot make this exact syntax work. But you could do something like this by overriding the relevant comparison methods:

a <=Interval() < b

This whole expression could then return a new Interval object that includes everything greater than or equal to a and strictly less than b. Interval() by itself could be interpreted as the fully open interval from negative to positive infinity (i.e. the unbounded interval of all real numbers), and Interval() < b by itself could refer to an interval bounded from above but not from below.

NumPy uses a similar technique for array comparison operations (where A < B means "return an array of ones and zeros that correspond to whether or not each element of A is less than the respective element of B").

Post a Comment for "Notation For Intervals?"