2016-03-17 41 views
10

Próbuję zrozumieć iterator. Zauważam, że Python documentation uważa iterator za konstrukcję w stylu funkcjonalnym. Naprawdę tego nie rozumiem.Dlaczego iterator jest uważany za funkcjonalny w dokumentacji Pythona?

Czy to prawda, że ​​iterator ma w sobie stan. Więc kiedy wywołasz it.__next__(), mutujesz stan iteratora. O ile mi wiadomo, mutowanie stanu obiektu nie jest uważane za funkcjonalne, ponieważ programowanie funkcjonalne kładzie nacisk na niezmienność i komponowanie obiektu/zamknięcia.

W rzeczywistości pojawia się problem, ponieważ chcę napisać procedurę/funkcję schematu, która pobiera tokeny i zwraca iterator.

(define tokens->iterator 
    (lambda ls 
    (lambda() 
     (if (null? ls) 
      '*eoi* 
      (let ((tok (car ls))) 
      (set! ls (cdr ls)) 
      tok))))) 

Wskazówka muszę używać set! mutować ls, to w jaki sposób podchodzi się z tym pytaniem.

Aby go użyć,

(define it (tokens->iterator 1 '+ 2)) 

By to sprawdzić

[email protected](guile-user)> (it) 
$2 = 1 
[email protected](guile-user)> (it) 
$3 = + 
[email protected](guile-user)> (it) 
$4 = 2 
[email protected](guile-user)> (it) 
$5 = *eoi* 
[email protected](guile-user)> (it) 
$6 = *eoi* 

Tak dla zabawy, ja też przetłumaczyć to Python:

def tokens_to_iterator(*tup): 
    ls = list(tup) 
    def iterator(): 
     if not ls: 
      return "*eoi*" 
     else: 
      tok = ls.pop(0) 
      return tok 
    return iterator 

Podobnie, Metoda pop() usuwa i zwraca pierwszy element poprzez mutację Lista.

Aby go użyć,

it = tokens_to_iterator(1, "+", 2) 

By to sprawdzić

>>> it() 
1 
>>> it() 
'+' 
>>> it() 
2 
>>> it() 
'*eoi*' 
>>> it() 
'*eoi*' 

Może ktoś wyjaśnić w tej sprawie? Przy okazji, używam Pythona 3 i Schematu Guile na wypadek, gdyby ktoś był zainteresowany wypróbowaniem przykładów.

+4

"Funkcjonalny" i "bez możliwości zmiany stanu" to nie to samo. – zwol

+0

ponieważ ten stan jest ukryty w środku i niedostępny z zewnątrz; jest * enkapsulowany *. Zmienia się automatycznie przy każdym kolejnym połączeniu *, a ważność obiektu zostaje zachowana. Z punktu widzenia obserwatora zewnętrznego zachowuje się on zgodnie z niektórymi * prawami *, jest * zgodny * i nie może być dowolnie zmieniany z zewnątrz. ten stan jest tylko szczegółem implementacji. –

+0

@zwol Istnieje również "bez efektów ubocznych". Czy to jest to samo, co "bez mutacji"? Dlaczego "Funkcjonalny" i "niestabilny stan" nie są takie same? Czy nie wszystkie "funkcjonalne" naciski językowe na "niestabilny stan-wolny"? Czy tu brakuje mi czegoś? –

Odpowiedz

6

Masz doskonały punkt. Iteratory z pewnością nie są "czysto funkcjonalne", termin często używany do opisu idiomów, które w ogóle nie używają mutacji. Szerszy termin "funkcjonalny" jest jednak luźniej definiowany, aby wskazać programy, które używają względnie niewielkiej mutacji, które wykorzystują funkcje wyższego rzędu i pierwszej klasy, a może przede wszystkim wszystkie, "wykorzystują dziwne abstrakcje, które nie wyglądają jak C. "

Myślę, że szczerze mówiąc, że będę nie zadzwonić iteratory funkcjonalne. To znaczy: zgadzam się z tobą.

+3

"używaj dziwnych abstrakcji, które nie wyglądają jak C", jak monada ... –

3

Funkcjonalny styl służy do pracy z listami danych jako całością, a nie zbiorem wartości, które można zmieniać w trakcie kaprysu. Na przykład, jeśli masz listę numerów, a chcesz zmienić 3rd elementu, podejście niefunkcjonalne jest bezpośrednio zmienić:

>>> lst = ["a", "b", "c", "d", "e"] 
>>> lst[3] = "Z" 
>>> lst 
["a", "b", "c", "Z", "e"] 

Podejście funkcjonalne jest napisać funkcję, która podjęcia oryginał i zwraca nową listę z wprowadzoną zmianą, pozostawiając oryginał niezmieniony.

>>> lst = ["a", "b", "c", "d", "e"] 
>>> new_lst = [x if i != 3 else "Z" for (i, x) in enumerate(lst)] 
>>> lst 
["a", "b", "c", "d", "e"] 
>>> new_lst 
["a", "b", "c", "Z", "e"] 

Ani swoich iteratorów jest czysto funkcjonalne, ponieważ robią utrzymać stan zmienny, choć traktowana jako czarnej skrzynki można wykorzystać je funkcjonalnie ponieważ użytkownik z iteratora nie może wpłynąć na ten stan bezpośrednio.

czysto funkcjonalny iterator byłoby to funkcja, która przyjmuje jako wejście listę i obecnego stanu, i zwraca wartość i nowy stan zostać przekazany do następnego wywołania funkcji.

>>> state = 0 
>>> def it(lst, state): 
... if state is None: 
...  return None 
... return lst[state], state + 1 
... 
>>> lst = ["a", "b", "c", "d", "e"] 
>>> value, new_state = it(lst, state) 
>>> value 
'a' 
>>> state, new_state 
(0, 1) 
>>> it(lst, new_state) 
('b', 2) 
>>> state, new_state 
(0, 1) 
+0

Naprawiono.W Pythonie trudno jest wykazać czysto funkcjonalne techniki, ponieważ nie mają powiązań typu "let", aby tymczasowo nazwać wynik funkcji, co nie jest całkiem takie samo jak przypisanie zmiennych. (Dużą różnicą jest to, że możesz związać nazwę * raz *, nie możesz później zmienić wiązania.) – chepner

+0

Rozumiem, więc stan jest przekazywany jawnie, dzięki czemu twój iterator jest całkowicie funkcjonalny, ponieważ ten sam "pierwszy" i taki sam 'state', iterator zwróci ten sam element. Czy mogę dalej twierdzić, że iterator Pythona nie jest czysto funkcjonalny, ponieważ 'it .__ next __()' zwróci inny element z tym samym wejściem (co w tym przypadku jest niczym), dowodem na to, że iterator wewnętrznie utrzymuje stan? –

+0

Prawidłowo. Jest to jeden z powodów, dla którego po przejściu iteratora do 'itertools.tee', należy użyć tylko jednego z iteratorów zwracanych przez funkcję, a nie samego iteratora. – chepner