2016-09-22 9 views
3

Ich möchte eine Python-Klasse für Intervalle von reellen Zahlen machen. Eine der mathematischen Notation am nächsten verwandte Syntax wäre Interval([a, b)) oder, noch besser, Interval[a, b), um das Intervall aller echten x, die a <= x < b erfüllen, zu konstruieren.Notation für Intervalle?

Ist es möglich, eine Klasse zu konstruieren, die mit dieser Syntax umgehen würde?

+3

Es ist nicht, weil Python seine eigene Interpretation von Klammern hat, können Sie versuchen, '[a, b)' in 'str' –

+0

In der Praxis nehmen Programmierer normalerweise ein halboffenes Intervall (geschlossen auf der linken Seite) Sie geben etwas anderes an. Das ist es, was 'range()' für Integer verwendet, und so wird die Slicesyntax normalerweise interpretiert. Daher sind die vollständig offenen, vollständig geschlossenen oder geschlossenen Fälle die Sonderfälle, die eine ungewöhnliche Syntax haben sollten. – Kevin

Antwort

4

Es ist unmöglich, syntaktisch ungültiges Python zu "reparieren", indem eine benutzerdefinierte Klasse erstellt wird.

ich glaube, die nächsten Sie auf die mathematische Intervall Notation in Python bekommen

Interval('[a, b)') 

ist diese Weise noch leichter wird, wenn Sie Intervalle als Argumente für eine Funktion sind vorbei und die Funktion wandelt es Argumente für eine ist geeigneten Typ, bevor Sie sie verwenden. Beispiel:

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

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

Possible implementation:

import re 

class Interval: 
    def __init__(self, interval): 
     """Initialize an Interval object from a string representation of an interval 
      e.g: Interval('(3,4]')""" 
     if isinstance(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 is None: 
      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 instead 

    def __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: 
      return True 
     if number == self.begin: 
      return self.begin_included 
     if number == self.end: 
      return self.end_included 
+0

Vielleicht nicht die nächste Sache ... – martineau

+0

@martineau Haben Sie ein Beispiel? –

0

Sie können diese genaue Syntax Arbeit machen. Aber Sie könnte durch die relevanten Vergleichsmethoden überschreiben so etwas tun:

a <= Interval() < b 

Diese ganze Ausdruck könnte dann wieder ein neues Interval Objekt, das alles größer als oder gleich ein und streng kleiner als b enthält. Interval() selbst könnte als das vollständig offene Intervall von der negativen zur positiven Unendlichkeit interpretiert werden (d. H. Das unbegrenzte Intervall aller reellen Zahlen), und Interval() < b könnte sich selbst auf ein Intervall beziehen, das von oben begrenzt ist, aber nicht von unten.

NumPy verwendet eine ähnliche Technik für Array-Vergleichsoperationen (wobei A < B bedeutet "gebe ein Array von Einsen und Nullen zurück, die der Frage entsprechen, ob jedes Element von A kleiner ist als das jeweilige Element von B").

+0

Diese Lösung scheint mir syntaktisch schlau und intuitiv zu sein wie 'Interval [1] (4)' und 'Interval (1) [4]'. –

1

Sie können nicht Python bestehenden Syntaxregeln ändern (ohne die ganze Sprache zu ändern), aber man kann benutzbar nahe kommen, was Sie wollen:

class Interval(object): 
    def __init__(self, left_bracket, a, b, right_bracket): 
     if len(left_bracket) !=1 or left_bracket not in '[(': 
      raise ValueError(
       'Unknown left bracket character: {!r}'.format(left_bracket)) 
     if len(right_bracket) !=1 or right_bracket not in '])': 
      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 interval 

    print('{} in {}? {}'.format(3, interval1, 3 in interval1)) 
    print('{} in {}? {}'.format(3, interval2, 3 in interval2)) 

Ausgang:

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

Hinweis: Die Argumente a und b können alle Typen sein, die verglichen werden können.

+0

@ АндрейБеньковский: Guter Fang mein Freund! Fest. Danke (auch wenn der Link in deinem Kommentar nicht funktioniert). – martineau