Světýlkův blog

Další pohled na věc
  • Pokud mezi vámi nepřeskočila jiskra, musíte ještě trochu zakřesat.

  • Musíš aktivně neutrácet, abys mohl pasivně šetřit.

  • Jakožto nositel dioptrických brýlí zahajuji operaci Smítko.

  • Zábava je umění přesvědčit svůj mozek, aby vypustil nějaký ten endorfin.

  • Nepřišel, a tím přišel o hodně.

  • Kotlin a tail recursion

    Ještě za mých “dávných” dob Scaly jsem si vzpomněl na takovou funkcionální pikantnost, která by se vám možná mohla hodit. Říká se jí tail recursion (optimization) a vztahuje se pouze na ten nejjednodušší typ rekurze, kdy funkce volá čistě jen samu sebe. Typicky by člověk tohle využil u procházení nějakých zanořených stromových struktur, ale abychom trochu potrápili mozkové závity a třeba se i někam dále posunuli, zkusíme si něco úplně jiného. Představme si, že jsme v ryze funkcionálním světě a chceme iterovat přes nějaký List a to bez použití jakýchkoli cyklů. Tzn. žádný while, ani for, nic … Je něco takového vůbec možné? Ano, je, a jak jistě tušíte, dá se to řešit právě pomocí rekurze.

    Challenge

    Ještě než vám ukážu svou verzi, která vás konec konců svou jednoduchostí možná až překvapí, zkuste trochu potrápit své mozkové závity a pokuste se naimplementovat metodu recursiveForeach() pouze za pomocí rekurze tak, aby jí šlo použít takovýmto způsobem:

    object TailRecursionTest {
    
        @JvmStatic
        fun main(args: Array) {
    
            val nums = listOf(1, 2, 3)
    
            recursiveForeach(nums) {
                println("The number is $it")
            }
        }
    }
    

    Řešení

    Jeden ze způsobů, jak to napsat, je např. tento:

        fun <T> recursiveForeach(list: List<T>, callback: (T) -> Unit)  {
            if (list.isNotEmpty()) {
                callback(list.first())
                recursiveForeach(list.drop(1), callback)
            }
        }
    

    a když daný kód spustím, na výstupu dostanu skutečně:

    The number is 1
    The number is 2
    The number is 3

    Limity stacku a klíčové slovo tailrec

    Jak jistě čekáte, tohle řešení má docela podstatnou nevýhodu, a sice pokud bude list příliš velký, dostanete StackOverflowError. Zkuste si např v kódu změnit:

    val nums = (1..10000).toList()
    

    Co s tím? Stačí před fun recursiveForeach přidat kouzelné slůvko tailrec. Co tohle udělá, je optimalizace tohoto rekurzivního kódu pomocí cyklů, takže již nedojde k zanořování a následnému přetečení zásobníku. Pokud spustíte kód nyní, StackOverflowError již nenastane. Když se podíváme pod pokličku a dekompilujeme si výsledný kód, uvidíme něco takového:

       public final void recursiveForeach(@NotNull List list, @NotNull Function1 callback) {
          while(true) {
             Intrinsics.checkParameterIsNotNull(list, "list");
             Intrinsics.checkParameterIsNotNull(callback, "callback");
             Collection var3 = (Collection)list;
             if (var3.isEmpty()) {
                return;
             }
    
             callback.invoke(CollectionsKt.first(list));
             list = CollectionsKt.drop((Iterable)list, 1);
          }
       }
    

    Sice rekurze až tak často nepíšu, ale někdy by se tato featura mohla určitě hodit 🙂 .

  • Bilance snowkitingové sezóny 2017/2018

    Svou druhou snowkite sezónu mám za sebou a tentokrát jsem si stanovil za cíl precizně trackovat, kolikrát se nám za celou zimu podařilo vyrazit za větrem, a kolik km jsem na lyžích celkově najel. Výsledky se mi zdají celkem fajn:

    Počet výjezdů: 11

    Celkově najeto: 137 km

    Cílem pro příští zimu budiž vyrazit alespoň 12x a najet alespoň 150km 🙂 .

  • Mám tam 150 errorů a žádný není můj. –Richard H.

  • U oběda

    Martin: Máš staré tričko,
    Pavel: Já vím, to je stable release.

  • Umění mezilidské řeči, je nepoddat se introvertní křeči.