Warto być leniwym

08.12.2009 - Krzysztof Skrzętnicki
TrudnośćTrudnośćTrudność

Przyszłość teraz

Korzystając z leniwości możemy operować na wartościach, których jeszcze nie policzyliśmy w taki sposób, jak gdyby już były policzone. Możemy to wykorzystać do eleganckiego wyrażanie pewnych operacji.

Napiszemy teraz program, który zlicza liczbę linii podanych na standardowe wejście. Nie użyjemy wprost ani jednej operacji czytania tego wejścia - zamiast tego skorzystamy z funkcji getContents, która zwraca całe wejście, pomimo tego, że nie jest ono jeszcze przeczytane.

Tak stworzony strumień zachowuje się podobnie do nieskończonych strumieni - w momencie, gdy potrzeba więcej elementów wejścia, system automatycznie doczytuje odpowiedni kawałek.

1
2
3
4
5
6
7
8
import Text.Printf
 
policzLinie :: String -> Int
policzLinie xs = 1 + length (filter (=='\n') xs)
 
main = do
  input <- getContents
  putStrLn (printf "Liczba linii: %d" (policzLinie input))


O budowie funkcji policzLinie
W budowie tej funkcji wykorzystano dwie, mniejsze funkcje:
  1. filter - funkcja usuwająca z listy wszystkie elementy, które nie są zgodne z predykatem. W tym przypadku predykat stanowiła funkcja porównująca poszczególne znaki ze znakiem nowej linii '\n'.
  2. length - funkcja obliczająca długość listy
W efekcie funkcja policzLinie oblicza długość listy nowych linii na wejściu, czyli liczbę linii w ogóle.

Zauważmy, że funkcja policzLinie wykonuje się niezależnie od tego, czy mamy dostępne całe wyjście od razu, czy też nie. Jeżeli w danym momencie nie ma dostępnych danych na wejściu program zatrzymuje się i wznawia automatycznie, gdy pojawiają się dane. Kiedy dane skończą się, funkcja policzLinie trafia na koniec listy input, kończy swoje działanie i zwraca liczbę elementów równych \n, czyli znaków nowej linii.

$ \$ $ runhaskell test13.hs
ala ma
kota a ko
t
ma ale
Liczba linii: 4

Nowy świat, nowe możliwości, nowe zagrożenia

Podsumujmy informacje, które dzisiaj poznaliśmy. Przede wszystkim: Haskell jest językiem domyślnie leniwym. Jeżeli nie ma potrzeby, żadna z wartości nie będzie wyliczona. Pozwala to na tworzenie i operowanie na nieskończonych strukturach np. ciągach liczbowych. Możemy też dzięki temu w elegancki sposób definiować operacje wejścia/wyjścia, bez konieczności wykonywania w pętli operacji odczytu. Wykonane one będą automatycznie.

Z drugiej strony leniwość wprowadza pewne zagrożenia. Może okazać się ciężka do opanowania, szczególnie gdy zapomnimy o tym, że pewne operacje nie mogą się wykonać na strukturach nieskończonych.

Ostatecznie jednak wydaje się, że leniwość jest ciekawym narzędziem, które można wykorzystać do tworzenia interesujących rozwiązań.

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com