Haskell Hero

Interaktivní učebnice pro začínající Haskellisty

Vyhodnocování II

Od zadání k výsledku

Příklad

Vyhodnoťte krok po kroku výraz trikrat (5 + 2), kde

trikrat x = x + x + x


Jak začít? Vyhodnotit první 5 + 2, nebo to celé rozepsat podle definice funkce trikrat?

Odpověď zní: záleží na zvolené redukční strategii. Máme na výběr z následujícíh tří:

  • normální redukční strategie
  • striktní redukční strategie
  • líná redukční strategie

Haskell bez dalšího upřesnění vyhodnocuje líně.

Vyhodnocujeme normálně

Normální redukční stratregie spočívá ve vyhodnocování zvnějšku. Řečeno konkrétněji, pokud je vyhodnocovaný výraz ve tvaru F X a F se dá aplikovat na X, aplikuj F na X. V opačném případě vyhodnoť samotné F, opět podle normální strategie.

Kratčeji: Bez ohledu na vyhodnocení argumentů aplikuj funkci F.

Náš ukázkový výraz by se tedy podle normální strategie vyhodnotil následovně:

trikrat (5 + 2)

~>  (5 + 2) + (5 + 2) + (5 + 2)
~>     7    + (5 + 2) + (5 + 2)
~>     7    +    7    + (5 + 2)
~>     7    +    7    +    7
~>          14        +    7
~>  21

Vyhodnocujeme striktně

Striktní redukční stratregie spočívá ve vyhodnocování zevnitř. Řečeno konkrétněji, nejprve vyhodnoť argumenty na nezjednodušitelné výrazy a teprve potom aplikuj funkci.

Náš ukázkový výraz by se tedy podle striktní strategie vyhodnotil následovně:

trikrat (5 + 2)

~>  trikrat 7
~>  7 + 7 + 7
~>    14  + 7
~>  21

Vyhodnocujeme líně

Líné vyhodnocování funguje na stejném principu jako vyhodnocování normální. S tím rozdílem, že si pamatujeme výsledky vyhodnocení jednotlivých výrazů. Před každým vyhodnocením se nejdřív podíváme, jestli jsme již takový výraz někdy nevyhodnocovali. Pokud ano, použijeme výsledek z prvního vyhodnocení.

Náš příklad vyhodnocený líně by tedy vypadal takto:

trikrat (5 + 2)

~>  (5 + 2) + (5 + 2) + (5 + 2)
~>  7 + 7 + 7
~>    14  + 7
~>  21

Proč různé strategie?

Vždyť se přece zdá, že striktní strategie je nejefektivnější. Nemusí se kontrolovat, zda se výraz již jednou nepočítal, a je nejrychlejší. Tak proč další dvě strategie?

Například kvůli vyhodnocení výrazu take 3 [1..]. Seznam prvních tří prvků z nekonečného seznamu začínajícího jedničkou je [1,2,3]. Jak by vypadalo striktní vyhodnocení?

take 3 [1..]

~>  take 3 ( 1: [2..] )
~>  take 3 ( 1: 2: [3..] )
~>  take 3 ( 1: 2: 3: [4..] )
~>  take 3 ( 1: 2: 3: 4: [5..] )
~>  take 3 ( 1: 2: 3: 4: 5: [6..] )
~>  take 3 ( 1: 2: 3: 4: 5: 6: [7..] )
~>  ...
Výpočet by neskončil, jelikož striktní vyhodnocení požaduje úplné zjednodušení svých argumentů, což u vygenerování nekonečného seznamu, nedosáhneme. Normální strategií dospějeme k tomuto:
take 3 [1..]

~>  take 3 ( 1: [2..] )

~>  1 : take 2 [2..]

~>  1 : take 2 ( 2: [3..] )

~>  1 : 2 : take 1 [3..]

~>  1 : 2 : take 1 ( 3: [4..] )

~>  1 : 2 : 3 : take 0 [4..]

~>  1 : 2 : 3 : []
Jiným příkladem by mohlo být vyhodnocení logických výrazů.
False && _  =  False
True  && x  =  x
------------------------

False && ( and [True, True..] )

~>  False
Při striktním vyhodnocování by výpočet neskončil. Normálním/líným vyhodnocením se dobereme k výsledku po jednom kroku.

O línou nádstavbu jsme normální strategii doplnili proto, abychom zabránili vícenásobnému vyhodnocování stejných výrazů.