Python(MyRangeクラス 負数対応ver)

前回の投稿では負の数の対応をしていなかったので、関数オブジェクトを利用し、イテレータでstepに負の数が入った場合にも対応できるようにしました。

class MyRange:
    def __init__(self,*xs):
        first,end,step,f = self.check(xs)
        self.first = first
        self.end = end
        self.step = step
        self.f = f

    @staticmethod
    def err(message=""):
        raise SyntaxError(message)
    
    def iteration(self,step):
        def proc():            
            if step > 0:
                if self.num < self.end:
                    result = self.num
                    self.num += step
                    return result
                else:
                    raise StopIteration
            else:
                if self.num > self.end:
                    result = self.num
                    self.num += step
                    return result
                else:
                    raise StopIteration
        return proc
        
    def check(self,xs):
        first,step = 0,1
        is_num = lambda x:isinstance(x,int)
        n = len(xs)
        if n == 0 or n > 3 or not is_num(xs[0]):
            MyRange.err()
        elif n == 1:
            if xs[0] <= 0:
                end = 0
            else:
                end = xs[0]            
        elif n == 2:
            if not is_num(xs[1]):
                MyRange.err()
            elif xs[1] <= xs[0]:
                end = 0
            else:
                first = xs[0]
                end = xs[1]
        elif n == 3:
            if not is_num(xs[1]) or not is_num(xs[2]) or xs[2] == 0:                
                MyRange.err()            
            else:
                first = xs[0]
                end = xs[1]
                step = xs[2]
        f = self.iteration(step)
        return first,end,step,f
    
    def __contains__(self,x):
        num = self.first
        while num < self.end:
            if x == num:
                return True
            num += self.step
        return False
        
    def __str__(self):
        return "class MyRange"
    
    def __iter__(self):
        self.num = self.first
        return self
    
    def __next__(self):
        return self.f()      
        
    def __getitem__(self,i):
        if i >= self.__len__():
            raise IndexError       
        count = 0
        num = self.first
        while count <= i:
            result = num
            num += self.step
            count += 1
        return result
    
    def __len__(self):
        tmp_end = self.end // self.step
        tmp_first = self.first // self.step
        tmp_max = max([tmp_first,tmp_end])
        tmp_min = min([tmp_first,tmp_end])
        return tmp_max - tmp_min

mr = MyRange(20,-8,-5)
print("str:",str(mr))
print(4 in mr)
for i in mr:
    print(i)
print([*MyRange(20,-8,-5)])
print("len:",len(mr))
print("item:",mr[1])

def iteration(self,step):という高階関数かつクロージャを定義し、stepが正か負かでイテレータの挙動が変わる関数オブジェクトを返すものを実装しました。
check関数内でこの関数オブジェクトを受け取るようにし、__init__でself.fに格納して__next__で利用するようにしています。

他の特殊メソッドは前回投稿と同様の処理で対応可能なのですが、def __len__(self):では最大値と最小値が負を利用する場合、逆転してしまうのでmin、maxの組み込み関数を利用して、大きいほうから小さいほうを減算できるように改良しています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください