Dlaczego leniwa ocena jest przydatna?

Od dawna zastanawiam się, dlaczego leniwa ocena jest przydatna. Jeszcze nikt mi nie wyjaśnił w sposób, który ma sens; w większości kończy się to gotowaniem do "zaufaj mi".

Uwaga: nie mam na myśli memoizacji.

Author: Joel McCracken, 2008-11-05

22 answers

Głównie dlatego, że może być bardziej wydajny -- wartości nie muszą być obliczane, jeśli nie mają być używane. Na przykład, mogę przekazać trzy wartości do funkcji, ale w zależności od sekwencji wyrażeń warunkowych, w rzeczywistości może być użyty tylko podzbiór. W języku takim jak C, wszystkie trzy wartości i tak będą obliczane; ale w Haskell, Tylko niezbędne wartości są obliczane.

Pozwala również na fajne rzeczy, takie jak nieskończone listy. Nie mogę mieć nieskończonej listy w języku takim jak C, ale w Haskell to żaden problem. Listy nieskończone są dość często używane w niektórych dziedzinach matematyki, więc przydatna może być możliwość ich manipulowania.

 89
Author: mipadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 10:56:42

Użytecznym przykładem leniwej oceny jest użycie quickSort:

quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs) ++ [x] ++ quickSort (filter (>= x) xs)

Jeśli teraz chcemy znaleźć minimum z listy, możemy zdefiniować

minimum ls = head (quickSort ls)

Który najpierw sortuje listę, a następnie pobiera pierwszy element listy. Jednak z powodu leniwej oceny oblicza się tylko głowę. Na przykład, jeśli weźmiemy minimum z listy [2, 1, 3,] quickSort najpierw odfiltruje wszystkie elementy, które są mniejsze niż dwa. Następnie robi quickSort na tym (zwracając listę singletonów [1]) co już wystarczy. Z powodu leniwej oceny, reszta nigdy nie jest sortowana, oszczędzając dużo czasu obliczeniowego.

Jest to oczywiście bardzo prosty przykład, ale lenistwo działa tak samo w przypadku programów, które są bardzo duże.

Jest jednak minusem tego wszystkiego: trudniej jest przewidzieć szybkość działania i wykorzystanie pamięci programu. Nie oznacza to, że leniwe programy są wolniejsze lub zajmują więcej pamięci, ale dobrze jest to wiedzieć.

 69
Author: Chris Eidhof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 14:51:15

Uważam, że leniwa ocena jest przydatna w wielu sprawach.

Po Pierwsze, wszystkie istniejące leniwe języki są czyste, ponieważ bardzo trudno jest rozumować o skutkach ubocznych w leniwym języku.

Czyste języki pozwalają rozumować o definicjach funkcji za pomocą rozumowania równikowego.

foo x = x + 3

Niestety w Ustawieniach nie-leniwych, więcej poleceń nie powraca niż w Ustawieniach leniwych, więc jest to mniej przydatne w językach takich jak ML. Ale w leniwym języku można bezpiecznie rozumować o równość.

Po drugie, wiele rzeczy, takich jak' ograniczenie wartości ' w ML nie jest potrzebne w leniwych językach, takich jak Haskell. Prowadzi to do wielkiego deklutacji składni. Języki podobne do ML muszą używać słów kluczowych takich jak var lub fun. W Haskell te rzeczy rozpadają się do jednego pojęcia.

Po Trzecie, lenistwo pozwala pisać bardzo funkcjonalny kod, który można zrozumieć w kawałkach. W Haskell często zapisuje się ciało funkcji takie jak:

foo x y = if condition1
          then some (complicated set of combinators) (involving bigscaryexpression)
          else if condition2
          then bigscaryexpression
          else Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

To pozwala pracować "z góry na dół", chociaż rozumienie ciała funkcji. Języki podobne do ML zmuszają cię do użycia let, który jest oceniany ściśle. W związku z tym nie odważysz się "podnieść" klauzuli let do głównej części funkcji, ponieważ jeśli jest kosztowna (lub ma skutki uboczne), nie chcesz, aby zawsze była oceniana. Haskell może "odepchnąć" szczegóły do klauzuli where, ponieważ wie, że zawartość tej klauzuli będzie oceniana tylko w razie potrzeby.

W praktyce mamy tendencję do używania strażników i zawalania że dalej:

foo x y 
  | condition1 = some (complicated set of combinators) (involving bigscaryexpression)
  | condition2 = bigscaryexpression
  | otherwise  = Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

Po czwarte, lenistwo czasami oferuje znacznie bardziej eleganckie wyrażenie pewnych algorytmów. Leniwe "szybkie sortowanie" w Haskell jest jednoliniowe i ma tę zaletę, że jeśli spojrzysz tylko na kilka pierwszych przedmiotów, płacisz tylko koszty proporcjonalne do kosztów wyboru tylko tych przedmiotów. Nic nie stoi na przeszkodzie, aby robić to ściśle, ale prawdopodobnie będziesz musiał za każdym razem przekodować algorytm, aby osiągnąć tę samą wydajność asymptotyczną.

Po piąte, lenistwo pozwala definiowanie nowych struktur sterowania w języku. Nie możesz napisać nowego "jeśli".. więc .. else .."jak konstruować w ścisłym języku. Jeśli spróbujesz zdefiniować funkcję taką jak:

if' True x y = x
if' False x y = y

W języku ścisłym obie gałęzie będą oceniane niezależnie od wartości warunku. Jest jeszcze gorzej, jeśli wziąć pod uwagę pętle. Wszystkie ścisłe rozwiązania wymagają, aby język dostarczał pewnego rodzaju cytatów lub wyraźnej konstrukcji lambda.

W końcu, w tym samym duchu, jedne z najlepszych mechanizmy radzenia sobie z efektami ubocznymi w systemie typu, takimi jak monady, naprawdę mogą być skutecznie wyrażone tylko w leniwym otoczeniu. Można to zaobserwować porównując złożoność przepływów pracy F#do Haskell Monads. (Można zdefiniować monad w ścisłym języku, ale niestety często nie będzie monad prawo lub dwa z powodu braku lenistwa i przepływu pracy przez porównanie odebrać mnóstwo ścisłego bagażu.)
 61
Author: Edward KMETT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-16 23:58:49

Istnieje różnica między normalną oceną porządku a leniwą oceną(jak w Haskell).

square x = x * x

Ocena następującego wyrażenia...

square (square (square 2))

... z chętną oceną:

> square (square (2 * 2))
> square (square 4)
> square (4 * 4)
> square 16
> 16 * 16
> 256

... z normalną oceną zamówienia:

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * (square (square 2))
> ((2 * 2) * (square 2)) * (square (square 2))
> (4 * (square 2)) * (square (square 2))
> (4 * (2 * 2)) * (square (square 2))
> (4 * 4) * (square (square 2))
> 16 * (square (square 2))
> ...
> 256

... z leniwą oceną:

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * ((square 2) * (square 2))
> ((2 * 2) * (2 * 2)) * ((2 * 2) * (2 * 2))
> (4 * 4) * (4 * 4)
> 16 * 16
> 256

To dlatego, że leniwa ocena patrzy na drzewo składni i dokonuje przekształceń drzewa...

square (square (square 2))

           ||
           \/

           *
          / \
          \ /
    square (square 2)

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
        square 2

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
           *
          / \
          \ /
           2

... natomiast normalną ocenę zamówienia wykonuje tylko tekstowy rozszerzenia.

Dlatego, gdy korzystamy z leniwej oceny, zyskujemy większą moc (ocena kończy się częściej niż inne strategie) , podczas gdy wydajność jest równoznaczna z gorliwą oceną(przynajmniej w notacji O).

 28
Author: Thomas Danecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-19 18:38:54

Jeśli wierzysz Simonowi Peytonowi Jonesowi, leniwa ocena nie jest ważna per se , ale tylko jako "Koszula do włosów", która zmusiła projektantów do utrzymania języka w czystości. Współczuję temu punktowi widzenia.

Richard Bird, John Hughes i w mniejszym stopniu Ralf Hinze są w stanie robić niesamowite rzeczy z leniwą oceną. Czytanie ich pracy pomoże Ci to docenić. Dobrym punktem wyjścia są wspaniałe Sudoku solver i referat Hughesa na Dlaczego Programowanie Funkcyjne Ma Znaczenie .

 25
Author: Norman Ramsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-16 02:57:57

Leniwa ocena związana z procesorem tak samo jak śmieciarka związana z pamięcią RAM. GC pozwala udawać, że masz nieograniczoną ilość pamięci, a tym samym żądać tyle obiektów w pamięci, ile potrzebujesz. Runtime automatycznie odzyska bezużyteczne obiekty. LE pozwala Ci udawać, że masz nieograniczone zasoby obliczeniowe - możesz wykonać tyle obliczeń, ile potrzebujesz. Runtime po prostu nie wykona niepotrzebnych (dla danego przypadku) obliczeń.

Jaki jest praktyczny zalety tych "udających" modeli? Zwalnia programistę (do pewnego stopnia)z zarządzania zasobami i usuwa część kodu boilerplate ze źródeł. Jednak ważniejsze jest to, że można efektywnie ponownie wykorzystać swoje rozwiązanie w szerszym zestawie kontekstów.

Wyobraź sobie, że masz listę liczb S i liczbę N. musisz znaleźć najbliższą do liczby n liczbę m z listy S. możesz mieć dwa konteksty: pojedyncze N i pewną listę L z Ns (np. dla każdego N W L poszukujesz najbliższego M W S). Jeśli używasz leniwej oceny, możesz sortować S i stosować wyszukiwanie binarne, aby znaleźć najbliższy m do N. dla dobrego leniwego sortowania będzie to wymagało kroków O (size(S)) dla pojedynczych n i o(LN(size(S))*(size(S) + size(L))) dla równomiernie rozłożonych L. Jeśli nie masz leniwej oceny, aby osiągnąć optymalną wydajność, musisz zaimplementować algorytm dla każdego kontekstu.

 24
Author: Alexey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-29 21:43:33

Rozważ program tic-tac-toe. Posiada cztery funkcje:

  • funkcja generowania ruchu, która pobiera aktualną tablicę i generuje listę nowych tablic, z których każda ma jeden ruch.
  • następnie istnieje funkcja "move tree", która stosuje funkcję move generation do wyprowadzania wszystkich możliwych pozycji planszy, które mogą wynikać z tej.
  • istnieje funkcja minimax, która chodzi po drzewie (lub ewentualnie tylko jego część), aby znaleźć najlepszy następny ruch.
  • tam jest funkcją oceny planszy, która określa, czy jeden z graczy wygrał.

To tworzy ładny wyraźny rozdział obaw. W szczególności funkcja generowania ruchu i funkcje oceny planszy są jedynymi, które muszą zrozumieć zasady gry: drzewo ruchów i funkcje minimax są całkowicie wielokrotnego użytku.

Teraz spróbujmy zaimplementować szachy zamiast tic-tac-toe. W języku" chętnym " (tj. konwencjonalnym) to nie zadziała, ponieważ ruch drzewo nie zmieści się w pamięci. Tak więc teraz funkcje oceny i generowania ruchu muszą być mieszane z drzewem ruchu i logiką minimax, ponieważ logika minimax musi być używana do decydowania, które ruchy mają zostać wygenerowane. Nasza miła, czysta struktura modułowa znika.

Jednak w leniwym języku elementy drzewa ruchu są generowane tylko w odpowiedzi na wymagania funkcji minimax: całe drzewo ruchu nie musi być generowane, zanim puścimy minimax na górze element. Tak więc nasza czysta modułowa struktura nadal działa w prawdziwej grze.

 13
Author: Paul Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-26 16:50:21

Oto jeszcze dwie kwestie, które, jak sądzę, nie zostały jeszcze poruszone w dyskusji.

  1. Lenistwo jest mechanizmem synchronizacji w środowisku współbieżnym. Jest to lekki i łatwy sposób na stworzenie odniesienia do niektórych obliczeń i podzielenie się jego wynikami wśród wielu wątków. Jeśli wiele wątków spróbuje uzyskać dostęp do wartości niewycenionej, tylko jeden z nich ją wykona, a inne odpowiednio zablokują, otrzymując wartość, gdy stanie się dostępny.

  2. Lenistwo jest podstawą amortyzacji struktur danych w czystym otoczeniu. Jest to opisane przez Okasaki w czysto funkcjonalnych strukturach danych szczegółowo, ale podstawową ideą jest to, że leniwa ocena jest kontrolowaną formą mutacji krytyczną dla umożliwienia nam skutecznego wdrożenia pewnych typów struktur danych. Podczas gdy często mówimy o lenistwie zmuszającym nas do noszenia koszuli do włosów czystych, obowiązuje również druga droga: są parą synergicznego języka funkcje.

 12
Author: Edward Z. Yang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-02 04:19:25

Po włączeniu komputera i Windows powstrzymuje się od otwierania każdego katalogu na dysku twardym w Eksploratorze Windows i powstrzymuje się od uruchamiania każdego programu zainstalowanego na komputerze, dopóki nie wskażesz, że dany katalog jest potrzebny lub pewien program jest potrzebny, to jest" leniwa " ocena.

"leniwa" ocena to wykonywanie operacji wtedy i kiedy są one potrzebne. Jest przydatna, gdy jest cechą języka programowania lub biblioteki, ponieważ jest ogólnie trudniej wdrożyć leniwą ocenę na własną rękę niż po prostu wstępnie obliczyć wszystko z góry.

 9
Author: yfeldblum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 15:11:56
  1. Może zwiększyć wydajność. To oczywiste, ale nie Najważniejsze. (Zauważ również, że lenistwo może zabić efektywność - fakt ten nie jest od razu oczywisty. Jednak przechowując wiele tymczasowych wyników, zamiast natychmiast je obliczać, możesz zużyć ogromną ilość pamięci RAM.)

  2. Pozwala definiować konstrukcje sterowania przepływem w normalnym kodzie na poziomie użytkownika, a nie jest kodowany na twardo w języku. (Np. Java posiada pętle for; Haskell ma funkcję for. Java ma obsługę wyjątków; Haskell ma różne typy WYJĄTKÓW monad. C# has goto; Haskell ma kontynuację monad...)

  3. Pozwala to oddzielić algorytm generowania danych od algorytmu decydującego o ilości danych do wygenerowania. Możesz napisać jedną funkcję, która generuje nieskończoną listę wyników i inną funkcję, która przetwarza tyle z tej listy, ile decyduje o jej potrzebach. Więcej do chodzi o to, że możesz mieć pięć funkcji generatora i pięć funkcji konsumenckich, i możesz efektywnie tworzyć dowolną kombinację-zamiast ręcznie kodować 5 x 5 = 25 funkcji, które łączą obie akcje na raz. (!) Wszyscy wiemy, że odsprzęganie jest dobrą rzeczą.

  4. To mniej więcej zmusza do zaprojektowania czystego języka funkcjonalnego. Zawsze kuszące jest branie skrótów, ale w leniwym języku, najmniejsza nieczystość sprawia, że Twój kod jest dziki]} nieprzewidywalny, co zdecydowanie sprzeciwia się kroczeniu na skróty.

 8
Author: MathematicalOrchid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-02 09:20:24

Rozważ to:

if (conditionOne && conditionTwo) {
  doSomething();
}

Metoda doSomething() zostanie wykonana tylko wtedy, gdy conditionOne jest true i conditionTwo jest true. W przypadku, gdy warunek jest fałszywy, dlaczego trzeba obliczyć wynik warunku? Ocena stanu będzie w tym przypadku stratą czasu, zwłaszcza jeśli stan jest wynikiem jakiegoś procesu metodycznego.

To jeden z przykładów leniwego zainteresowania oceną...
 6
Author: romaintaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 15:07:33

Ogromną zaletą lenistwa jest możliwość pisania niezmiennych struktur danych z rozsądną amortyzacją. Prostym przykładem jest stos niezmienny (używając F#):

type 'a stack =
    | EmptyStack
    | StackNode of 'a * 'a stack

let rec append x y =
    match x with
    | EmptyStack -> y
    | StackNode(hd, tl) -> StackNode(hd, append tl y)

Kod jest rozsądny, ale dodanie dwóch stosów x i y zajmuje O (długość x) czasu w najlepszych, najgorszych i średnich przypadkach. Dodawanie dwóch stosów jest operacją monolityczną, dotyka wszystkich węzłów w stosie x.

Możemy ponownie zapisać strukturę danych jako leniwy stos:

type 'a lazyStack =
    | StackNode of Lazy<'a * 'a lazyStack>
    | EmptyStack

let rec append x y =
    match x with
    | StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
    | Empty -> y

lazy utwory autorstwa zawieszanie oceny kodu w jego konstruktorze. Po ewaluacji za pomocą .Force(), wartość zwracana jest buforowana i używana ponownie przy każdym kolejnym .Force().

W wersji lazy, appends są operacją O(1): zwraca 1 węzeł i zawiesza rzeczywistą przebudowę listy. Gdy otrzymasz nagłówek tej listy, oceni ona zawartość węzła, zmuszając go do zwrócenia nagłówka i utworzenia jednego zawieszenia z pozostałymi elementami, więc wzięcie nagłówka listy jest O(1) operacja.

Więc nasza leniwa lista jest w ciągłym stanie odbudowy, nie ponosisz kosztów odbudowy tej listy, dopóki nie przejdziesz przez wszystkie jej elementy. Korzystając z lenistwa, lista ta obsługuje o(1) konsygnację i dodawanie. Co ciekawe, ponieważ nie oceniamy węzłów przed ich dostępem, całkowicie możliwe jest skonstruowanie listy z potencjalnie nieskończonymi elementami.

Powyższa struktura danych nie wymaga, aby węzły były rekomputerowane na każdym przejeździe, więc są wyraźnie różni się od Vanilla IEnumerable w .NET.

 6
Author: Juliet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-19 19:04:34

Leniwa ocena jest najbardziej przydatna w przypadku struktur danych. Można zdefiniować tablicę lub wektor indukcyjnie określający tylko niektóre punkty w strukturze i wyrażający wszystkie inne w kategoriach całej tablicy. Pozwala to na bardzo zwięzłe i wydajne generowanie struktur danych.

Aby zobaczyć to w akcji, możesz zajrzeć do mojej biblioteki sieci neuronowych o nazwie instinct . To sprawia, że ciężkie wykorzystanie leniwej oceny dla elegancji i wysokiej wydajności. Na przykład Całkowicie pozbyłem się tradycyjnie imperatywnej kalkulacji aktywacji. Proste leniwe wyrażenie robi wszystko za mnie.

Jest to używane na przykład w funkcji aktywacyjnej , a także w algorytmie uczenia się wstecznego (mogę zamieścić tylko dwa linki, więc musisz sam poszukać learnPat funkcji w module AI.Instinct.Train.Delta). Tradycyjnie oba wymagają znacznie bardziej skomplikowanych algorytmów iteracyjnych.

 5
Author: ertes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-03 03:49:39

Ten fragment pokazuje różnicę między leniwą a nie leniwą oceną. Oczywiście ta funkcja Fibonacciego mogłaby być zoptymalizowana i używać leniwej oceny zamiast rekursji, ale to by zepsuło przykład.

Załóżmy, że Może musimy użyć do czegoś 20 liczb pierwszych, Przy nie leniwej ocenie wszystkie 20 liczb muszą być wygenerowane z góry, ale przy leniwej ocenie będą generowane tylko w razie potrzeby. W ten sposób zapłacisz tylko cenę obliczeniową, gdy potrzebne.

Przykładowe wyjście

Not lazy generation: 0.023373
Lazy generation: 0.000009
Not lazy output: 0.000921
Lazy output: 0.024205
import time

def now(): return time.time()

def fibonacci(n): #Recursion for fibonacci (not-lazy)
 if n < 2:
  return n
 else:
  return fibonacci(n-1)+fibonacci(n-2)

before1 = now()
notlazy = [fibonacci(x) for x in range(20)]
after1 = now()
before2 = now()
lazy = (fibonacci(x) for x in range(20))
after2 = now()


before3 = now()
for i in notlazy:
  print i
after3 = now()

before4 = now()
for i in lazy:
  print i
after4 = now()

print "Not lazy generation: %f" % (after1-before1)
print "Lazy generation: %f" % (after2-before2)
print "Not lazy output: %f" % (after3-before3)
print "Lazy output: %f" % (after4-before4)
 4
Author: Vinko Vrsalovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 15:38:27

Inni już podali wszystkie ważne powody, ale myślę, że użytecznym ćwiczeniem, które pomoże zrozumieć, dlaczego lenistwo ma znaczenie, jest próba napisania funkcji punktu stałego w ścisłym języku.

W Haskell funkcja punktu stałego jest bardzo prosta:

fix f = f (fix f)

To rozszerza się do

f (f (f ....

Ale ponieważ Haskell jest leniwy, ten nieskończony łańcuch obliczeń nie jest problemem; ocena odbywa się "od zewnątrz do wewnątrz" i wszystko działa cudownie:

fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)

Ważne, że nie ma znaczenia, że fix być leniwym, ale że f bądź leniwy. Gdy już otrzymałeś ścisły f, możesz albo rzucić ręce w powietrze i się poddać, albo eta rozszerzyć go i zaśmiecić rzeczy. (Jest to bardzo podobne do tego, co Noah mówił o tym, że jest to biblioteka , która jest ścisła / leniwa, a nie Język).

Teraz wyobraź sobie pisanie tej samej funkcji w ścisłej Scali:

def fix[A](f: A => A): A = f(fix(f))

val fact = fix[Int=>Int] { f => n =>
    if (n == 0) 1
    else n*f(n-1)
}

Oczywiście dostajesz stos przepełnienie. Jeśli chcesz, aby to zadziałało, musisz wykonać argument f call-by-need:

def fix[A](f: (=>A) => A): A = f(fix(f))

def fact1(f: =>Int=>Int) = (n: Int) =>
    if (n == 0) 1
    else n*f(n-1)

val fact = fix(fact1)
 4
Author: Owen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-28 02:57:23

Nie wiem, jak obecnie myślisz o rzeczach, ale uważam, że warto myśleć o leniwej ocenie jako o problemie z biblioteką, a nie o funkcji językowej.

Mam na myśli, że w językach ścisłych, mogę zaimplementować leniwą ocenę, budując kilka struktur danych, a w leniwych językach (przynajmniej Haskell), mogę poprosić o ścisłość, kiedy chcę. Dlatego wybór języka tak naprawdę nie sprawia, że Twoje programy są leniwe lub nie-leniwe, ale po prostu wpływa na to, co dostajesz domyślnie.

Once you pomyśl o tym w ten sposób, a następnie pomyśl o wszystkich miejscach, w których piszesz strukturę danych, którą możesz później użyć do generowania danych (bez patrzenia na nią zbytnio wcześniej), a zobaczysz wiele zastosowań do leniwej oceny.

 3
Author: Noah Lavine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-29 21:54:03

Najbardziej użytecznym wykorzystaniem leniwej oceny, jaką użyłem, była funkcja, która nazywała szereg funkcji podrzędnych w określonym porządku. Jeśli którakolwiek z tych podfunkcji nie powiodła się (zwróciła false), funkcja wywołująca musiała natychmiast powrócić. Więc mogłem to zrobić w ten sposób:

bool Function(void) {
  if (!SubFunction1())
    return false;
  if (!SubFunction2())
    return false;
  if (!SubFunction3())
    return false;

(etc)

  return true;
}

Lub bardziej eleganckie rozwiązanie:

bool Function(void) {
  if (!SubFunction1() || !SubFunction2() || !SubFunction3() || (etc) )
    return false;
  return true;
}

Gdy zaczniesz go używać, zobaczysz możliwości korzystania z niego coraz częściej.

 2
Author: Marc Bernier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-06 14:22:34

Bez leniwej oceny nie będziesz mógł napisać czegoś takiego:

  if( obj != null  &&  obj.Value == correctValue )
  {
    // do smth
  }
 2
Author: peeles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-29 11:17:32

Między innymi języki leniwe pozwalają na wielowymiarowe nieskończone struktury danych.

Podczas gdy scheme, python itp. zezwalają na jednowymiarowe nieskończone struktury danych ze strumieniami, możesz przemierzać tylko jeden wymiar.

Lenistwo jest przydatne dla tego samego problemu fringe, ale warto zwrócić uwagę na połączenie coroutines wymienione w tym linku.

 2
Author: shapr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 22:34:53

Leniwa ocena to rozumowanie równościowe biednego człowieka (którego można by się spodziewać, najlepiej wydedukowania właściwości kodu z właściwości typów i operacji).

Przykład, gdzie działa całkiem dobrze: sum . take 10 $ [1..10000000000]. Które nie przeszkadza nam być zredukowane do sumy 10 liczb, zamiast tylko jednego bezpośredniego i prostego obliczenia numerycznego. Bez leniwej oceny oczywiście stworzyłoby to gigantyczną listę w pamięci tylko po to, aby użyć jej pierwszych 10 elementów. Z pewnością byłoby to bardzo powolne, i może spowodować błąd braku pamięci.

Przykład, gdzie nie jest tak wielki, jak byśmy chcieli: sum . take 1000000 . drop 500 $ cycle [1..20]. Co faktycznie zsumuje 1 000 000 liczb, nawet jeśli w pętli zamiast w liście; mimo to powinno być zredukowane do tylko jednego bezpośredniego obliczenia numerycznego, z kilkoma warunkami i kilkoma formułami. Które byłoby o wiele lepsze niż podsumowanie 1 000 000 liczb. Nawet w pętli, a nie na liście (tj. po optymalizacji wylesiania).


Inna sprawa jest, umożliwia kodowanie w stylu rekurencja ogonowa modulo cons, a po prostu działa.

[[2]}por. pokrewna odpowiedź.
 2
Author: Will Ness,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:26

Jeśli przez "lazy evaluation" masz na myśli jak w combound booleans, jak w

   if (ConditionA && ConditionB) ... 

Wtedy odpowiedź jest po prostu taka, że im mniej CPU zużywa program, tym szybciej będzie działał... a jeśli fragment instrukcji przetwarzania nie będzie miał wpływu na wynik programu, to nie jest konieczne (a zatem strata czasu), aby je wykonać...

Jeśli otoh, masz na myśli to, co znam jako "leniwe inicjalizatory", jak w:

class Employee
{
    private int supervisorId;
    private Employee supervisor;

    public Employee(int employeeId)
    {
        // code to call database and fetch employee record, and 
        //  populate all private data fields, EXCEPT supervisor
    }
    public Employee Supervisor
    { 
       get 
          { 
              return supervisor?? (supervisor = new Employee(supervisorId)); 
          } 
    }
}

Cóż, ta technika pozwala kod klienta wykorzystujący klasę, aby uniknąć konieczności wywołania bazy danych dla rekordu danych przełożonego, z wyjątkiem sytuacji, gdy klient korzystający z obiektu pracownika wymaga dostępu do danych przełożonego... to sprawia, że proces tworzenia instancji pracownika jest szybszy, a jednak gdy potrzebujesz przełożonego, pierwsze połączenie do Właściwości przełożonego uruchomi połączenie z bazą danych, a dane zostaną pobrane i dostępne...

 1
Author: Charles Bretana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 15:14:07

Fragment z funkcji wyższego rzędu

Znajdźmy największą liczbę poniżej 100 000, która jest podzielna przez 3829. Aby to zrobić, po prostu filtrujemy zestaw możliwości, w których wiemy rozwiązanie leży.
largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0 

Najpierw tworzymy listę wszystkich liczb niższych niż 100 000, malejąco. Następnie filtrujemy go według naszego predykatu, a ponieważ liczby są sortowane w sposób malejący, największa liczba spełniająca nasze predykat jest pierwszym element filtrowanej listy. Nawet nie trzeba użyć skończonej listy dla naszego zestawu startowego. To lenistwo w znowu akcja. Bo kończymy tylko na głowicy filtrowanej lista, nie ma znaczenia czy filtrowana lista jest skończona czy nieskończona. Ocena kończy się w momencie znalezienia pierwszego odpowiedniego rozwiązania.

 0
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 17:48:58