2015-06-24 22 views
5

Ich habe einen Iterator it, von dem ich annehme, dass er bereits sortiert ist, aber ich möchte eine Ausnahme auslösen, wenn dies nicht der Fall ist.Überprüfen, ob der Iterator sortiert ist

Daten vom Iterator sind nicht im Speicher, daher möchte ich sorted() nicht verwenden, da AFAIK den gesamten Iterator in eine Liste stellt.

Die Lösung, die ich jetzt bin mit ist der Iterator in einer Generatorfunktion so wickeln:

def checkSorted(it): 
    prev_v = it.next() 
    yield prev_v 
    for v in it: 
    if v >= prev_v: 
     yield v 
     prev_v = v 
    else: 
     raise ValueError("Iterator is not sorted") 

Damit ich es wie folgt verwenden:

myconsumer(checkSorted(it)) 

Kennt jemand, wenn Es gibt bessere Lösungen?

Ich weiß, dass meine Lösung funktioniert, aber es scheint ziemlich seltsam zu sein (zumindest für mich), ein Modul zu schreiben, um solch eine triviale Aufgabe zu erledigen. Ich bin auf der Suche nach einem einfachen Einzeiler oder builtin Lösung

+1

Dies aktualisiert nie 'prev_v'; es funktioniert nicht für 'iter ([1, 3, 2])'. – user2357112

+1

Ehrlich gesagt, das scheint gut genug zu sein. Es hat ordentliche Leistung und tut, was Sie brauchen (wenn Sie 'prev_v' aktualisieren). Warum brauchst du einen One-Liner? – nneonneo

+0

Verpasste eine Zeile! Vielen Dank. – Zac

Antwort

2

Als Alternative (wenn vorhanden) Ich schlage vor, itertools.izip_longest (und zip_longest in Python 3) zu verwenden, um einen Generator enthält aufeinanderfolgende Paare zu erstellen:

Sie können Verwenden Sie tee, um zwei unabhängige Iteratoren aus einem ersten Iterablen zu erstellen.

from itertools import izip_longest,tee 

def checkSorted(it): 
    pre,it=tee(it) 
    next(it) 
    for i,j in izip_longest(pre,it): 
    if j: 
     if i >= j:   
     yield i 
     else: 
     raise ValueError("Iterator is not sorted") 
    else : 
     yield i 

Demo:

it=iter([5,4,3,2,1]) 
print list(checkSorted(it)) 

[5, 4, 3, 2, 1] 

it=iter([5,4,3,2,3]) 
print list(checkSorted(it)) 

Traceback (most recent call last): 
    File "/home/bluebird/Desktop/ex2.py", line 19, in <module> 
    print list(checkSorted(it)) 
    File "/home/bluebird/Desktop/ex2.py", line 10, in checkSorted 
    raise ValueError("Iterator is not sorted") 
ValueError: Iterator is not sorted 

Hinweis: Eigentlich denke ich, besteht keine Notwendigkeit, die Werte Ihrer iterable wen Sie sie schlage ich vor, already.So als elegantere Möglichkeit haben zu erhalten zu verwenden ein Generator Ausdruck innerhalb all Funktion und gibt einen Bool-Wert:

from itertools import izip,tee 

def checkSorted(it): 
    pre,it=tee(it) 
    next(it) 
    return all(i>=j for i,j in izip(pre,it)) 
+0

Sie verlassen sich auf None und vergleichen <= alles? Es ist wahrscheinlich besser, einen expliziten Sentinel zu verwenden; Vergleiche mit None funktionieren in Python 3 nicht so, und ich denke nicht, dass sie in alternativen Python-Implementierungen so funktionieren. – user2357112

+0

@ user2357112 Ja, es sollte "izip" sein, auch ich habe die 'zip' in Python 3 erwähnt, also haben wir keine' None'! – Kasramvd

+0

Zunächst verwendet Ihr Code immer noch "izip_longest". Zweitens, wenn Sie 'izip 'verwenden, erhalten Sie nicht das letzte Eingabeelement. – user2357112

3

Grundsätzlich Ihre Lösung fast so elegant wie i t bekommt (Sie könnten es natürlich in ein Dienstprogramm einfügen, wenn Sie es allgemein nützlich finden). Man könnte, wenn man wollte es einen Infinity-Objekt verwenden Sie den Code ein wenig nach unten zu schneiden, aber dann muss man auch eine Klassendefinition enthalten, die den Code erneut wächst (es sei denn Sie die Klassendefinition Inline):

def checkSorted(it): 
    prev = type("",(), {"__lt__": lambda a, b: False})() 

    for x in it: 
     if prev < x: 
      raise ValueError("Not sorted") 
     prev = x 
     yield x 

Die erste Zeile verwendet die type, um zuerst eine Klasse zu erstellen und dann instanziieren. Objekte dieser Klasse vergleichen weniger als irgendetwas (Unendlichkeitsobjekt).

Das Problem mit einem Einzeiler ist, dass Sie mit drei Konstrukten zu tun haben: Sie müssen den Status (Zuweisung) aktualisieren, eine Ausnahme auslösen und eine Schleife machen. Sie können diese einfach mit Anweisungen ausführen, aber wenn Sie sie zu einem oneliner machen, müssen Sie versuchen, die Anweisungen in die gleiche Zeile zu setzen - was wiederum zu Problemen mit der Schleife und den if -Konstrukten führt.

Wenn Sie das Ganze in einen Ausdruck bringen wollen, werden Sie schmutzigen Tricks zu tun, diese verwenden, müssen die Zuordnung und die iterutils Looping kann vorsehen, und das Werfen kann unter Verwendung des throw Verfahren in einem Generator durchgeführt werden (die kann auch in einem Ausdruck zur Verfügung gestellt werden):

imap(lambda i, j: (i >= j and j or (_ for _ in()).throw(ValueError("Not sorted"))), *(lambda pre, it: (chain([type("",(), {"__ge__": lambda a, b: True})()], pre), it))(*tee(it))) 

die letzte it ist der Iterator Sie überprüfen möchten, und der Ausdruck ergibt ein kariertes Iterator. Ich stimme zu, dass es nicht gut aussieht und nicht offensichtlich, was es tut, aber Sie haben danach gefragt (und ich glaube nicht, dass Sie es wollten).

+0

Ich mag sehr die Idee eines unendlichen Objekts, aber die One-Liner-Lösung ist etwas "unwatchable" :) (obwohl es sehr schlau ist) – Zac

Verwandte Themen