Zauważyłem, że większość języków funkcjonalnych wykorzystuje listę pojedynczo połączoną (listę „wad”) jako najbardziej podstawowe typy list. Przykłady obejmują Common Lisp, Haskell i F #. Różni się to od języków głównego nurtu, w których rodzimymi typami list są tablice.
Dlaczego?
W przypadku Common Lisp (dynamicznie wpisywanego) mam wrażenie, że minusy są na tyle ogólne, że mogą być również podstawą list, drzew itp. To może być mały powód.
Jednak w przypadku języków o typie statycznym nie mogę znaleźć dobrego uzasadnienia, mogę nawet znaleźć kontrargumenty:
- Funkcjonalny styl zachęca do niezmienności, więc łatwość wstawiania połączonej listy jest mniej korzystna,
- Funkcjonalny styl zachęca do niezmienności, a więc także do udostępniania danych; tablica jest łatwiejsza do dzielenia się „częściowo” niż lista połączona,
- Równie dobrze możesz dopasowywać wzorce na zwykłej tablicy, a nawet lepiej (możesz na przykład łatwo spasować od prawej do lewej),
- Ponadto otrzymasz losowy dostęp za darmo,
- I (praktyczna zaleta), jeśli język jest wpisany statycznie, możesz zastosować regularny układ pamięci i uzyskać zwiększenie prędkości z pamięci podręcznej.
Dlaczego więc wolisz powiązane listy?
an array is easier to share "partially" than a linked list
należy wyjaśnić, co masz na myśli. Ze względu na ich rekurencyjny charakter, odwrotnie jest tak, jak rozumiem - możesz częściowo udostępnić połączoną listę, przechodząc przez dowolny węzeł, a tablica musiałaby spędzać czas na tworzeniu nowej kopii. Jeśli chodzi o udostępnianie danych, dwie połączone listy mogą wskazywać ten sam sufiks, co po prostu nie jest możliwe w przypadku tablic.Odpowiedzi:
Najważniejszym czynnikiem jest to, że możesz przejść do niezmiennej pojedynczo połączonej listy w czasie O (1), co pozwala rekurencyjnie budować listy n-elementów w czasie O (n) w następujący sposób:
Jeśli zrobiłbyś to przy użyciu niezmiennych tablic, środowisko wykonawcze byłoby kwadratowe, ponieważ każda
cons
operacja musiałaby skopiować całą tablicę, co prowadziłoby do kwadratowego czasu działania.Zakładam, że przez „częściowe” udostępnianie masz na myśli, że możesz pobrać podtablicę z tablicy w czasie O (1), podczas gdy z połączonymi listami możesz wziąć ogon tylko w czasie O (1) i wszystko inne potrzebuje O (n). To prawda.
Jednak w wielu przypadkach wystarczy wziąć ogon. I musisz wziąć pod uwagę, że możliwość taniego tworzenia podrzędnych nie pomaga, jeśli nie masz możliwości taniego tworzenia tablic. I (bez sprytnych optymalizacji kompilatora) nie ma sposobu, aby tanio budować tablicę krok po kroku.
źródło
Myślę, że sprowadza się to do łatwej implementacji list w kodzie funkcjonalnym.
Schemat:
Haskell:
Tablice są trudniejsze i nie są tak ładne do wdrożenia. Jeśli chcesz, aby były wyjątkowo szybkie, będą musiały być napisane niskim poziomem.
Dodatkowo rekurencja działa znacznie lepiej na listach niż na tablicach. Zastanów się, ile razy rekurencyjnie korzystałeś / generowałeś listę w porównaniu do zindeksowanej tablicy.
źródło
Pojedynczo połączona lista jest najprostszą trwałą strukturą danych .
Trwałe struktury danych są niezbędne do wydajnego, czysto funkcjonalnego programowania.
źródło
Możesz łatwo korzystać z węzłów Cons tylko wtedy, gdy masz język odśmiecania.
Węzły Cons mają wiele wspólnego z funkcjonalnym stylem programowania wywołań rekurencyjnych i niezmiennymi wartościami. Więc pasuje dobrze do modelu programisty umysłowego.
I nie zapomnij o przyczynach historycznych. Dlaczego nadal są nazywane węzłami Cons, a co gorsza nadal używają car i cdr jako akcesoriów? Ludzie uczą się z podręczników i kursów, a następnie z nich korzystają.
Masz rację, w prawdziwym świecie tablice są znacznie łatwiejsze w użyciu, zajmują tylko połowę miejsca w pamięci i są znacznie wydajniejsze ze względu na brak poziomu pamięci podręcznej. Nie ma powodu, aby używać ich w imperatywnych językach.
źródło
Listy połączone są ważne z następującego powodu:
Po wybraniu liczby takiej jak 3 i przekonwertowaniu jej na następną, taką jak
succ(succ(succ(zero)))
, a następnie skorzystaj z podstawienia za pomocą{succ=List node with some memory space}
, a{zero = end of list}
skończysz z połączoną listą (o długości 3).Rzeczywistą ważną częścią są liczby, podstawienia, przestrzeń pamięci i zero.
źródło