Blog | Grahamův problém

Grahamův problém

Pro vyjádření síly programovacího jazyka používá blogger, investor, programátor a zakladatel Y Combinatoru Paul Graham následující zadání: Vytvoř akumulátorovou funkci, které předáš parametr a ona vrátí funkci očekávající další parametr. Po předání parametru ti vrátí součet a změní svůj stav tak, že příští zavolání bude sčítat předchozí hodnotu s dalším parametrem.

V imaginárním jazyce bude použití:

acc = vytvor_accum(5)
acc(1) # 6
acc(1) # 7
acc(3) # 10

Implementuj danou funkcionalitu v různých jazycích a pak porovnávej počet syntaktických prvků, které jsi musel použít pro splnění zadání. Porovnej řešení v PHP:

class Foo
{
        private $val;

        function __construct($val)
        {
                $this->val = $val;
        }

        function __invoke($val)
        {
                return $this->val += $val;
        }
}
V JavaScriptu:
var foo;
foo = function(n) {
        return function (i) {
                return n = n + i;
        }
}

A v LiveScriptu:

foo = (n) -> -> n := n + it

Dané řešení (stejně jako všechna ostatní) má jednu podstatnou nevýhodu. Pracuje s vnitřním stavem, který je mutable. To není moc správné. Pokud se naučíme psát své funkce tak, aby pro stejné parametry vždy vracely stejné hodnoty, překladač může všechny další výskyty dané funkce v systému doplnit rovnou hodnotou. Plus to umožňuje udělat další agresivní optimalizace (to je důvod, proč je Lisp mnohdy rychlejší než Java a srovnatelný s C++).

Abychom mohli tento problém vyřešit, můžeme použít monády. Neděste se divně znějícího termínu, využijem tu asi nejjednodušší monádu. Abyste mohli používat monády, nemusíte rozumět monádám.

Poznámka: Co je monáda? Monáda je dvojice funkcí. První funkce (wrap) kóduje nějakou proměnnou do funkce vracející tuto proměnnou. Druhá funkce (bind) provádí transformaci nad hodnotou a znovu hodnotu zabalí. Pokud nad jedněmi daty děláme více operací, můžeme mít těchto funkcí i víc.

Řešení problému v PHP:

$wrap = function($n) {
        return function() use ($n) {
                return $n;
        };
};

$bind = function($mon, $val) use ($wrap) {
        return $wrap($mon() + $val);
};

Použití:

$mon1 = $wrap(5);
$mon1(); # 5
$mon2 = $bind($mon1, 5);
$mon2(); # 10
$mon3 = $bind($mon2, 10);
$mon3(); # 20

Jak vidíte, $monX je funkce, která po svém zavolání vrací hodnotu, kterou chceme. Samotná operace je ve funkci bind. Vyhnuli jsme se vnitřnímu stavu tím, že jsme ho učinili explicitním.

Jak se totéž napíše v LiveScriptu?

wrap = (n) -> -> n
bind = (mon, val) -> wrap mon! + val

Použití je už potom podobné:

mon1 = wrap 5
mon1! # 5
mon2 = bind mon, 5
mon2! # 10
mon3 = bind mon2, 10
mon3! # 20

Poté, co jsem vždy horoval o to, aby lidi nepsali moc dlouhé funkce, metody a třídy a jako takové měkké limity s možností opostatněných výjimek jsem dával cca 75 výjimečně 150 řádek na třídu (nevíte, proč na mě Javisti vždycky tak divně koukají, když toto říkám?), 10 výjimečně 15 na metodu nebo funkci, nejspíš budu muset přehodnotit svá tvrzení. Realitou je, že v extrémně úsporných jazycích je 15 řádek na funkci možná až příliš.

Programování

Předejte zkušenosti i dalším a sdílejte tento článek!



Jiří Knesl
Business & IT konzultant

Jiří Knesl poprvé začal programovat v roce 1993. Od té doby, díky skvělým učitelům a později zákazníkům, měl možnost neustále růst v oboru vývoje webových aplikací a informačních systémů. v roce 2002 se přidal zájem o ekonomii a v roce 2006 o organizaci práce. Vším tím se konstantně profesně zabývá jak ve svém podnikání, tak i u zákazníků. Za posledních 5 let vydal na tato témata přes 400 článků.

Prohlédněte si moje reference

Mám zkušenosti z rozsáhlých projektů pro korporace, velké podniky, střední i malé firmy, ale i pro startupy v cloudu. Zvyšoval jsem jejich know-how, pomáhal nastavovat jejich organizační strukturu, byl lektorem a mentorem v náročných situacích. Podívejte se, jak vidí můj přínos samotní klienti.

Sledujte mé postřehy na sociálních sítích