2013-04-15 29 views
7

Mam różne scenariusze w pętlach, w których "podglądałem" lub "przeskakiwałem" do przodu podczas iteracji elementów do przetworzenia.Groovy 'Peeking' z przodu z iteratorem?

Jednym ze scenariuszy jest wyliczanie za pomocą linii pliku, a na końcu linii znajduje się znak "kontynuacja" wskazujący połączenie następnego wiersza z bieżącym wierszem. Jeśli po prostu zapętlam, to nie jest zbyt trudne, mogę po prostu przeczytać następną linię i uderzyć w mój licznik/indeks.

Nie jest tak oczywiste, jak to zrobić z moim iteratorem. Skutecznie chcę skonsumować kolejną linię bez zamykania się. Ale nie jestem nawet pewien, czy to możliwe. Czy istnieje jakiś dobry wzorzec projektu dla tego wzoru iteracji za pomocą zamknięcia, więc nie muszę uciekać się do mniej groźnej pętli? Czy jest to może forma iteratora z pewnym stosem do wypychania/popping przedmiotów do przetworzenia?

+3

I zazwyczaj używa dekorator wokół iteratora tak: https://gist.github.com/ataylor284/5389604 – ataylor

Odpowiedz

1

Zrobiłbym iterator, który byłby odpowiedzialny za łączenie linii. Dla przykładu kontynuacji wiersza iterator może pobrać linie odczytane z pliku w jego konstruktorze, a następnie odczytać jego linię z linii, odczytując naprzód, gdy znajdzie znak kontynuacji, aby znaki kontynuacji zostały usunięte przed następnym krok w rurociągu. Tak więc wszelka maszyna, której potrzebujesz, będzie zawarta w iteratorze.

1

Musiałem wdrożyć coś podobnego kiedyś. Miałem duży plik z oddzielonymi od rury danymi w każdym wierszu i dane mogły być kontynuowane w następnej linii, ale mogłem tylko wiedzieć, czy "zaglądałem" do następnej linii. Skończyłem używać ArrayList jako stosu połączonego z metodą podglądu:

def list = [1, 2, 3, 4, 5, 6, 7] 

list.metaClass.peek = { delegate[-1] } 

assert list.pop() == 7 
assert list.pop() == 6 
assert list.peek() == 5 
assert list.peek() == 5 
assert list.pop() == 5 
assert list.pop() == 4 
assert list.peek() == 3 
0

Interesujące pytanie.

Kluczowym problemem jest to, że musisz nosić stan przez iteracje.

Jednym ze sposobów, aby to zrobić, może być użycie zmiennej zewnętrznej (tu używam tablicę ciągów i List#each zamiast pliku i File#eachLine, ale powinny one być analogiczne):

def lines = [ 
    "Single line.", 
    "Long \\", 
    "line \\", 
    "continuation.", 
    "Single line." 
] 

def processLine(line) { println "Processing \"$line\""} 

def continuation = '' 
lines.each { line -> 
    line = continuation + line 
    if (line.endsWith('\\')) { 
    continuation = line.take(line.size() - 1) 
    } 
    else { 
    processLine(line) 
    continuation = '' 
    } 
} 

Another sposobem jest użycie iterator specjalnie przeznaczony do przewozu stan choć interations, jak Collection#inject:

lines = lines.inject([]) { list, line -> 
    if (list && list[-1].endsWith('\\')) 
    list[-1] = list[-1][0..-2] + line 
    else 
    list << line 
    list 
} 

lines.each { processLine(it) } 

W tym przypadku mamy pierwszy łączenia nieprzerwane linie, a następnie ich przetwarzanie.

W obu przypadkach wynik jest:

Processing "Single line." 
Processing "Long line continuation." 
Processing "Single line."