2016-09-22 9 views
3

実数の区間に対してPythonクラスを作りたいと思っています。数学的記法に最も密接に関連する構文は、を満たすすべての実数xの間隔を構成するために、またはより良くはInterval[a, b)となる。間隔の表記法は?

この構文を処理するクラスを作成することは可能ですか?

+3

Pythonは角括弧を解釈しているので、 '[a、b]'を 'str'に入れてみることができます。 –

+0

実際には、プログラマーは通常半開間隔それ以外の場合は指定します。これは、range()が整数に対して何をするかであり、スライス構文が通常どのように解釈されるかです。したがって、完全に開いた、完全に閉じた、または閉じた右の場合は、特殊な構文を持つ特殊なケースです。 – Kevin

答えて

4

カスタムクラスを作成することによって構文的に無効なPythonを「修正」することは不可能です。

は、私はあなたがPythonで数学の間隔表記に得ることができる最も近いが、関数の引数としての間隔を渡していると関数は引数をだ変換した場合、この方法では、より軽量になり

Interval('[a, b)') 

だと思います適切なタイプを選択してください。例:

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

最も近いものではないかもしれません... – martineau

+0

@martineau例がありますか? –

0

あなたはこの正確な構文の仕事をすることはできません。しかし、あなたは関連の比較方法をオーバーライドすることにより、このような何かを行うことができます:

a <= Interval() < b 

この式全体は、より大きいか等しいとbより厳密に小さいすべてを含む新しいIntervalオブジェクトを返すことができます。 Interval()それ自体は、負から正の無限大までの完全開放間隔(すなわち、すべての実数の無制限の間隔)として解釈することができ、Interval() < bそれ自体は、上から境界をつけられた区間を指すことができるが、下からではない。

NumPyは、配列比較演算(A < Bは、Aの各要素がBの各要素よりも小さいかどうかに対応する1と0の配列を返すことを意味します)と同様の手法を使用します。

+0

私にとっては、この解決方法は構文的に賢明で、Interval [1](4)とInterval(1)[4]と直感的に反します。 –

1

あなたは(全体の言語を変更せずに)Pythonの既存の構文規則を変更することはできませんが、あなたが望むものにusably近い得ることができます。

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)) 

出力:

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

注: ab引数は、比較可能な任意の型にすることができます。

+0

@АндрейБеньковский:良いキャッチ私の友人!一定。ありがとうございます(あなたのコメントのリンクが壊れていても)。 – martineau