About the Author

1040 Posts By svetylk0

  • 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.

  • Můj TaskMan je tzv. Prideware. Jsem na něho pyšný, a proto ho podsouvám všem okolo.

  • Šolichátor. –Pavel K.

  • Lateinit v Kotlinu a jeho výhoda oproti NullPointerException

    Kotlin občas na úrovni byte kódu generuje různý boilerplate, např. v podobě checků inicializace lateinit proměnných, a až donedávna mi unikala jednoduchá, avšak svým významem docela důležitá věc. Říkal jsem si, že pokud aplikace padne na NullPointerException v Javě, anebo na UninitializedPropertyAccessException v Kotlinu, tak že je to v důsledku totéž. Nicméně je zde jeden případ, kdy lateinit jednoznačně vede. Mějme tento kód v Javě:

    package com.svetylkovo.kotlin.lateinit;
    
    public class WithoutLateinit {
        private static String first;
        private static String second;
    
        public static void main(String[] args) {
            first = "First!";
            System.out.println("Total length is "+ String.valueOf(first.length() + second.length()));
        }
    }
    

    Pokud tento kód spustíme, dostaneme:

    Exception in thread "main" java.lang.NullPointerException
    at com.svetylkovo.kotlin.lateinit.WithoutLateinit.main(WithoutLateinit.java:9)

    Po nalezení řádku kódu, kde NullPointerException nastal, si můžeme jen lámat hlavu, která z těch dvou proměnných vlastně null byla a často (v reálných aplikacích) na to musíme ne-zcela triviálním způsobem přijít. Zkusme si stejnou situaci nasimulovat v Kotlinu s pomocí lateinit:

    package com.svetylkovo.kotlin.lateinit
    
    object LateinitTest {
        lateinit var first: String
        lateinit var second: String
    
        @JvmStatic
        fun main(args: Array<String>) {
            first = "First!"
            println("Total length is ${first.length + second.length}")
        }
    }
    

    Když spustíme tento kód, aplikace padne na:

    Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property second has not been initialized
    at com.svetylkovo.kotlin.lateinit.LateinitTest.main(LateinitTest.kt:10)

    kde stacktrace mluví za vše a místo toho, abych teprve začal hledat, co se vlastně stalo špatně, okamžitě vím, že to byla proměnná second, kterou jsem z nějakého (v tomto případě záměrného) důvodu zapomněl inicializovat.

    Posuďte sami, krom toho, že nás Kotlin dokáže NullPointerException zbavit již za překladu téměř úplně, nebylo by lepší v případech, kdy už by tato výjimka musela nastat, dostávat raději UninitializedPropertyAccessException s přesným popisem viníka, který za to může? 🙂

  • V zimě se i z obyčejného chodníku stává parkourové hřiště.

  • Kde je nalitej chlast, tam jsem nalitej já.