Kdyz uz ten serial nechcete zrusit, mohli byste jej aspon prejmenovat: "JavaFX: 100+1 navod, jak neprogramovat v Jave"
Pouzivani vyjimek pro bezne rizeni programu je zlo! A testovat, jestli je cislo integer nebo double tim, ze ho zkusime zparsovat a pokud to hodi vyjimku, tak to asi nebude cele cislo, je zlo galaktickych rozmeru!
Problemu je tam hned nekolik. Vyjimky jsou zoufale pomale. Autor provadi konverzi cisla z retezce hned 2x. Jednou v metode isDouble() a podruhe Double.valueOf(String), coz taky nejsou zrovna 2x rychle operace. A vtipne na tom je, ze se da zadat i cislo, ktere neni cislem, tj. NaN.
Autorovi doporucuji nastudovat aspon par kapitol z knihy Effective Java od J. Blocha a priste na neco takoveho pouzit predkompilovany reg. vyraz.
Jakkoliv nechci obhajovat kvality tohoto seriálu, tvůj komentář mi přijde trochu mimo.
- výjimka se zde nepoužívá pro řízení programové logiky, pouze pro parsování čísla
- Výjimky jsou "zoufale pomalé", jen když jsou vrhnuté, a i tak je hlavní část zpomalení v samotném vytváření výjimky (vyplnění stack trace), ne ve vyhazování. Uživateli je navíc asi jedno, jestli kontrola toho, co napsal, potrvá 50 ns nebo 5 μs. Premature optimization is the root of all evil
- Udělat si vlastní parsování čísel pomocí regexů není zas tak jednoduché, a asi ani není cílem učit regexy v seriálu o JavaFX. Navíc se v principu v některých situacích může hodit možnost zapsat veliké číslo jako třeba 1.23E45, což by dále zkomplikovalo regex. V praxi bych stejně asi stejně s regexy nevynalézal kolo, a použil nějakou hotovou knihovnu na parsování čísel.
try {Double.valueOf} mi tedy přijde jako legitimní, jednoduché a funkční řešení pro použití v seriálu, který se nezabývá problematikou parsování čísel. Max. bych v něm uvítal větičku, že to má svoje mouchy. Ovšem vzhledem k tomu, že po všech těch (vcelku oprávněně) nenávistných komentářích se z toho stejně asi nikdo učit nebude, a příspěvky píšou jen ti, co mají chuť si zanadávat, je to stejně nejspíš jedno..
výjimka se zde nepoužívá pro řízení programové logiky, pouze pro parsování čísla
No, a presne to je to rizeni vypoctu.
Výjimky jsou "zoufale pomalé", jen když jsou vrhnuté, a i tak je hlavní část zpomalení v samotném vytváření výjimky (vyplnění stack trace), ne ve vyhazování. Uživateli je navíc asi jedno, jestli kontrola toho, co napsal, potrvá 50 ns nebo 5 μs. Premature optimization is the root of all evil
Nejde mi ani o to, ze je to v tomto pripade zanedbatelne zpomaleni, ale ze je to serial, ktery se snazi ucit neco zacatecniky. A prave v zacatecnickych programech tuto prasarnu vidim temer pravidelne. A pak se lidi divi, ze programy v jave jsou pomale.
try {Double.valueOf} mi tedy přijde jako legitimní, jednoduché a funkční řešení pro použití v seriálu, který se nezabývá problematikou parsování čísel.
Serial se zabyva tolika podruznymi nesmysly, takze regularni vyrazy nebo odkaz na knihovnu, ktera to resi by ho vubec nezabilo.
Ovšem vzhledem k tomu, že po všech těch (vcelku oprávněně) nenávistných komentářích se z toho stejně asi nikdo učit nebude,
A to je dobre. Prave proto je dobre na chyby v serialu upozornovat, aby ctenar/autor vedel, ze nektere veci se daji delat civilizovaneji.
Validace číselného vstupu pomocí regexpů se mi nezdá jako čisté řešení.
Když už bych to chtěl pořádně, nechal bych to na nějaké validační knihovně.
Pokud externí knihovnu nechci, odchytím vyjímku při parsování, přesně jako v článku a bude to pro většinu případů dostatečně dobré a dostatečně čitelné řešení.
Takže zrovna tohle bych článku za zlé neměl.
Tak jeste jednou a polopate.
Pokud mas metodu isDouble, ktera interne pouziva vyjimku, melo by u ni v dokumentaci byt: "pokud vraci false, je strasne pomala". V takovem pripade by to asi obstalo. Ale pouziti vyjimek na bezne situace (z pohledu metody isDouble je neciselny vstup bezna zalezitost) je prasarna.
Nicmene, pokud se podivas do kodu, tak je to tam udelano jeste hur.
if (isDouble(...)) {// zavolam si v metode zkusebne Double.valueOf(...) x = Double.valueOf(...) // zavolam to znovu. } else { // vypisu chybu }
Mnohem rozumnejsi by bylo pouzit vyjimky tak, jak se ma, tj. k osetreni vyjimecnych situaci (a ne kvuli jejich vedlejsimu efektu):
try { x = Double.valueOf(...) // konverzi provadim prave jednou } catch (NFE e) { // vypisu chybu }
Pali nepali, tu aplikaci podobne pitomosti z casoveho hlediska prodluzuji, pekne milisekunda tam, milisekunda onde. Ostatne se staci podivat, co se deje jeste predtim, nez je JVM "zahreje", to jsou dost hnusny veci.
Navic by se mel naucit cist javadoc, oni ti vyvojari Javy nejsou uplne padli na hlavu: https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#valueOf-java.lang.String-
Prostě JIT funguje pro tyto bastliče dost blbě:
1) pokud se něco volá jen několikrát (několik stovek volání je pořád málo), nepřerovná ti to
2) pokud se něco volá mockrát na to, aby JIT zapracoval, zaplatíš za to přerovnání těmi prvními voláními
Takže: panáček nahoře na to buď kašle úplně (fajn, taky mu to ded.kennedy řekl) nebo spoléhá na magický GIT o němž asi nic moc neví, tudíž on NENÍ v pozici, kdy může pouštět moudra o kořenech zla atd., které stejně každý bez přemýšlení zopakuje.
V čem bude program lepší, když reakce na tlačítko místo mikrosekundy bude trvat milisekundu? Takovéhle honění procesorových cyklů u takovéhle operace má asi podobný smysl jako snažit se zrychlit Škodu 120 tím, že vyměním erární šaltpáku za karbonovou. V té záplavě reflexe, vykreslování nenativních komponent a databázových dotazů je tohle naprosto nedůležité.
Když už se zabývat parsováním čísel, tak bych spíš řešil korektnost a exaktnost chování: používá se jako desetinný oddělovač tečka nebo čárka, je to závislé na systémovém locale, nemělo by to být konfigurovatelné, co z toho by bylo z pohledu uživatele nejvhodnější?
Stejně tak není vůbec třeba se zabývat ručním parsováním formátu data, když můžeme použít date picker.
V tomto ale nejsme ve sporu. Ja jen reagoval nad mavnuti rukou nad *dvojim* parsovanim toho stejneho cisla (+ obsluhou vyjimky) s tim, ze si to JIT prerovna a dale nespecifikovanym zpusobem zoptimalizuje.
Toto kdyby mi rekl v praci junior, tak dostane za ukol to dokazat, prinest vystup z generovaneho ASM (to JVM umi), kde to opravdu tak bude (z dlouhodobeho hlediska to neni ztrata casu, ale presne naopak postupny prerod juniora v programatora). Jinak proste takove veci rikat nema, bo to neni pravda (navic se to parsovani vola jinou metodou z jineho mista aplikace, tam uz vubec nema zadnou jistotu v magicky JIT).
Treti vec - vy skutecne dokazate predikovat budoucnost a rict, kde se vase metody pouziji? Co tak vidim z aplikaci stale provozovanych a psanych v Jave 1.2 a 1.4.2 (tu ma z nejakeho duvodu rado IBM :), tak se mnoho strasne stareho kodu znovupouziva, coz je samozrejme dobre, ale prave tehdy zacnou vybublavat tyto hovnokody (tedy uprime - to co je v clanku, jsem jeste nevidel - formatovani blbe, neidiomaticke konstrukce, to neni na produkci ani nahodou).
ad 3: ne. Ale rozdil je mezi psanim knihoven, kde tohle ma smysl vazne resit, a psanim aplikace, kde je dobre byt "o chlup lepsi nez je ted potreba", ale rozhodne nema smysl byt perfektni (ostatne jak perfektni? v budoucnu mohou byt hodne protichudne pozadavky s opacnymi naroky, uz jenom mnozstvi a pomer validnich a nevalidnich vstupu v tomhle asi zamicha s tim, jak vlastne vypada lepsi reseni a to se bavime jenom o vykonu).
(Jinak nic proti mikrooptimalizacim, nekdy je to sranda, nekdy je to potreba. Ale zrovna u toho parsovani bambilionu cisel ze "skoro csv" si s tim clovek chvili hraje... pak zjisti, ze vetsina problemu je v I/O... a pak se musi vratit o kus zpet a o uroven vys a nahradit "skoro csv" avrem. True story, bro.
Tim netvrdim, ze se ma psat umyslne pomalu, trebas sahnout hned po spravnem typu listu pro dany ucel ma smysl skoro vzdy a bez vetsiho premysleni, ale zejmena na zacatku to nema smysl prehanet.)
Přesně, při načítání vstupu z např. desktop aplikace, web app je to naprosto zanedbatelné. Stroj většinou stejně čeká 99% procesorového času ale u big data database kde by se takto chroustalo číslo za číslem(příp. záznam) je to zjevná blbost.
JE NUTNO ROZLIŠOVAT TYP PROJEKTU A NA TO POUŽÍVAT VHODNÉ POSTUPY. Je blbost na desktop aplikaci aplikovat techniky a postupy pro aplikační servery obsluhující tisíce, desetitisíce a stovky tisíc dotazů za sekundu.
Hledáte chyby tam kde nejsou. Kód nikoho nezajímá, takže si kontrolu čísla řešte jak chcete. Navíc u operací, které trvají možná pár nanosekund je fakt opravdu úplně jedno, jestli jich bude 10 nebo 100. Hledáte problémy kde nejsou, nebo respektive pokud Vám to vadí, tak to klidně dělejte správně, ale nebuzerujte ty lidi, kteří si podobnýma nesmyslama nechtějí ztěžovat svoji práci.
Chyby tam jsou, ze ty to nevnimas, je tvuj problem. Pravidelne delam code review a zacinajici programatori podobne prasarny pouzivaji jak na bezicim pasu s vysvetlenim "nasel jsem na internetu, ze se to tak dela". Ze to v tento okamzik neni uplne dulezite nevadi. Zacas se urcite objevi nejaky programator, ktery to pouzije v exponovane casti kodu, a vsichni si budou moci svorne zanadavat, jak je ta Java pomala.
Začínající programátoři dělají mnohem horší věci než že píšou pomalý kód, např. že jejich kód je nesrozumitelný, bez struktury, nerespektuje DRY princip, není otestovaný (ani testovatelný) atd.
Vzal by sis Porsche na Nürbugring? Pravděpodobně ano. Jel bys Porschetem do Globusu udělat nákup pro pětičlennou rodinu? Asi ne... Stejně tak exponovanou část kódu napíšeš optimalizovaně (potom, co si měřením ověříš, že je opravdu exponovaná - to určitě děláš, že? :) ), a tu, u které to je jedno, to napíšeš nejjednodušeji, jak to jde.
Jinak nemá vůbec smysl se bavit o nějaké Javě a high level konstrukcích a můžeme se všichni vrátit k assembleru.
Zrovna pro integery je ta parsovaci funkce naprogramovatelna jednoduse, to by mel kazdy programator zvladnout :) Nerikam pro float/double, to je humus, ale integery...? Dokonce to bude rychlejsi nez ten originalni Integer.parseInt(), protoze ten vola obecnejsi metodu s volitelnym radixem.
Je to tak tezke precist si celou diskuzi nez zareagujes? Goto...
Jsou i situace, kdy výjimky udělají kód čitelnější - viz článek Static exceptions (jazyk OCaml), část Use cases.
Dobře a jaká je tedy ta čitelnější varianta?
static Optional<Double> tryParseDouble(d) { try { return Optional.of(Double.parseDouble(d)); } catch (NumberFormatException e) { return Optional.empty(); } }
Anebo "doporučovaný" postup z Javadocu?
final String Digits = "(\\p{Digit}+)"; final String HexDigits = "(\\p{XDigit}+)"; // an exponent is 'e' or 'E' followed by an optionally // signed decimal integer. final String Exp = "[eE][+-]?"+Digits; final String fpRegex = ("[\\x00-\\x20]*"+ // Optional leading "whitespace" "[+-]?(" + // Optional sign character "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from section 3.10.2 of // The Java Language Specification. // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.("+Digits+")("+Exp+")?)|"+ // Hexadecimal strings "((" + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")[pP][+-]?" + Digits + "))" + "[fFdD]?))" + "[\\x00-\\x20]*");// Optional trailing "whitespace" if (Pattern.matches(fpRegex, myString)) Double.valueOf(myString); // Will not throw NumberFormatException else { // Perform suitable alternative action }
Fuj to je odporná syntaxe :) Proč devítky a ne třeba sedmičky? Co lomítko v RČ? Co devítimístná RČ? Co dělitelnost 11cti? Umí tohle všechno tahle syntaxe? Pokud ne, co nabízí navíc kromě omezení délky, která by se dala v nějaké fiktivní syntaxi specifikovat rozumněji, třeba
get rodne_cislo 10
?
Tedy jsem rád, že už se takhle věci nedělají. Jinak ovšem např. kamenná sekyrka nebyl vůbec špatný nástroj, viz třeba