Vyhodnocení zkratu - Short-circuit evaluation
![]() | tento článek potřebuje další citace pro ověření.srpen 2013) (Zjistěte, jak a kdy odstranit tuto zprávu šablony) ( |
![]() | Bylo navrženo, že Provozovatel Elvis být sloučeny do tohoto článku. (Diskutujte) Navrhováno od listopadu 2020. |
Strategie hodnocení |
---|
Vyhodnocení zkratu, minimální hodnocenínebo McCarthyho hodnocení (po John McCarthy ) je sémantika některých Booleovské operátory v některých programovací jazyky ve kterém je druhý argument proveden nebo vyhodnocen pouze v případě, že první argument nestačí k určení hodnoty výrazu: když je první argument A
funkce se vyhodnotí na Nepravdivé
, celková hodnota musí být Nepravdivé
; a když první argument NEBO
funkce se vyhodnotí na skutečný
, celková hodnota musí být skutečný
.
V programovacích jazycích s líné hodnocení (Lisp, Perl, Haskell ), obvyklí booleovští operátoři jsou zkratovaní. V ostatních (Ada, Jáva, Delphi ), jsou k dispozici jak zkratoví, tak standardní booleovští operátoři. U některých booleovských operací, jako exkluzivní nebo (XOR), není možné zkratovat, protože k určení výsledku jsou vždy nutné oba operandy.
Operátoři zkratu jsou ve skutečnosti kontrolní struktury spíše než jednoduché aritmetické operátory, protože tomu tak není přísný. v imperativní jazyk podmínky (zejména C a C ++ ), kde jsou vedlejší účinky důležité, zavádějí operátoři zkratu a bod posloupnosti - úplně vyhodnotí první argument, včetně jakéhokoli vedlejší efekty, před (volitelně) zpracováním druhého argumentu. ALGOL 68 použitý postupovat dosáhnout definované uživatelem zkratoví operátoři a postupy.
Použití zkratových operátorů bylo kritizováno jako problematické:
Podmíněné spojky - "cand" a "cor„zkrátka - jsou ... méně nevinní, než by se na první pohled mohlo zdát. cor nedistribuuje cand: porovnat
- (A cand B) cor C s (A cor C) cand (B cor C);
v případě ¬A ∧ C vyžaduje druhý výraz definici B, první nikoli. Protože podmíněné spojky tak komplikují formální uvažování o programech, je lepší se jim vyhnout.
Definice
V libovolném programovacím jazyce, který implementuje vyhodnocení zkratu, je výraz X a y
je ekvivalentní s podmíněný výraz -li X pak y jiný X
a výraz X nebo y
je ekvivalentní k -li X pak X jiný y
. V obou případech X je hodnocena pouze jednou.
Zobecněná definice výše obsahuje volně psané jazyky, které mají více než dva pravdivostní hodnoty Skutečný
a Nepravdivé
, kde operátoři zkratu mohou vrátit poslední vyhodnocený subexpresi. Tomu se v následující tabulce říká „poslední hodnota“. Pro jazyk s přísným zadáním je výraz zjednodušený -li X pak y jiný Nepravdivé
a -li X pak skutečný jiný y
respektive pro booleovský případ.
Přednost
Ačkoli A
bere přednost přes NEBO
v mnoha jazycích to není univerzální vlastnost vyhodnocení zkratu. Příklad dvou operátorů, kteří mají stejnou přednost a bytí levo-asociativní spolu navzájem je Shell POSIX syntaxe seznamu příkazů.[2](§2.9.3)
Následující jednoduchý vyhodnocovač zleva doprava vynucuje prioritu A
přes NEBO
podle a pokračovat
:
funkce zkrat-eval (operátory, hodnoty) nechat výsledek : = Pravda pro každého (op, val) v (operátory, hodnoty): -li op = "AND" && výsledek = False pokračovat jinak pokud op = "NEBO" && výsledek = Pravda vrátit se výsledek jiný výsledek := val vrátit se výsledek
Formalizace
Zkratová logika, s nebo bez vedlejších účinků, byla formalizována na základě Hoare je podmíněný. Výsledkem je, že operátory nezkratování lze definovat z logiky zkratu tak, aby měly stejnou posloupnost hodnocení.[3]
Podpora v běžných programovacích a skriptovacích jazycích
Jazyk | Dychtivý operátory | Zkratoví operátoři | Typ výsledku |
---|---|---|---|
Pokročilé programování podnikových aplikací (ABAP ) | žádný | a , nebo | Booleovský1 |
Ada | a , nebo | a pak , nebo jinak | Booleovský |
ALGOL 68 | a, &, ∧; nebo ∨ | andf, orf (definováno uživatelem) | Booleovský |
APL | ∧ , ∨ , ⍲ (nand), ⍱ (ani) atd. | :A pokud , :Nebo když | Booleovský1 |
awk | žádný | && , || | Booleovský |
Bash | žádný | && , || | Booleovský |
C, Cíl-C | žádný | && , || , ? [4] | int (&& ,|| ), závislé na opnd (? ) |
C ++2 | žádný | && , || , ? [5] | Boolean (&& ,|| ), závislý na opnd (? ) |
C# | & , | | && , || , ? , ?? | Boolean (&& ,|| ), závislý na opnd (? , ?? ) |
Značkovací jazyk ColdFusion (CFML) | žádný | A , NEBO , && , || | Booleovský |
D3 | & , | | && , || , ? | Boolean (&& ,|| ), závislé na opnd (? ) |
Eiffelova | a , nebo | a pak , nebo jinak | Booleovský |
Erlang | a , nebo | a také , nebo jinak | Booleovský |
Fortran4 | .a. , .nebo. | .a. , .nebo. | Booleovský |
Jít, Haskell, OCaml | žádný | && , || | Booleovský |
Jáva, MATLAB, R, Rychlý | & , | | && , || | Booleovský |
JavaScript, Julie | & , | | && , || | Poslední hodnota |
Laso | žádný | a , nebo , && , || | Poslední hodnota |
Kotlin | a , nebo | && , || | Booleovský |
Lisp, Lua, Systém | žádný | a , nebo | Poslední hodnota |
PŘÍUŠNICE (M) | & , ! | žádný | Číselné |
Modula-2 | žádný | A , NEBO | Booleovský |
Oberon | žádný | & , NEBO | Booleovský |
OCaml | žádný | && , || | Booleovský |
Pascal | a , nebo 5,9 | a pak , nebo jinak 6,9 | Booleovský |
Perl | & , | | && , a , || , nebo | Poslední hodnota |
Rubín | a , nebo | && , || | Poslední hodnota |
PHP | & , | | && , a , || , nebo | Booleovský |
Shell POSIX (seznam příkazů) | žádný | && , || | Poslední hodnota (výstup) |
Krajta | žádný[6] | a , nebo | Poslední hodnota |
Rez | & , | | && , || [7] | Booleovský |
Pokec | & , | | a: , nebo: 7 | Booleovský |
Standardní ML | Neznámý | a také , nebo jinak | Booleovský |
TTCN-3 | žádný | a , nebo [8] | Booleovský |
Visual Basic .NET | A , Nebo | A také , Nebo jinak | Booleovský |
Visual Basic, Visual Basic pro aplikace (VBA) | A , Nebo | Vyberte případ 8 | Číselné |
Wolfram jazyk | A @@ {...} , Nebo @@ {...} | A , Nebo , && , || | Booleovský |
ZTT | & , | | žádný | Booleovský |
1 ABAP a APL nemají žádný odlišný booleovský typ.
2 Při přetížení operátoři &&
a ||
jsou nedočkaví a mohou vrátit jakýkoli typ.
3 To platí pouze pro výrazy vyhodnocené za běhu, statické, pokud
a statické tvrzení
. Výrazy ve statických inicializátorech nebo konstantách manifestu používají dychtivé vyhodnocení.
4 Fortranští operátoři nejsou ani zkratoví, ani nedočkaví: jazyková specifikace umožňuje kompilátoru vybrat metodu optimalizace.
5 ISO / IEC 10206: 1990 Extended Pascal umožňuje, ale nevyžaduje zkratování.
6 ISO / IEC 10206: 1990 Extended Pascal podporuje a pak
a nebo jinak
.[9]
7 Smalltalk používá zkratkovou sémantiku, pokud argument argumentuje a:
je blok (např. false a: [Přepis: „Neuvidím mě]]
).
8 ZÁKLADNÍ jazyky, které podporovaly příkazy CASE, tak učinily pomocí systému podmíněného vyhodnocení, nikoli jako skokové tabulky omezené na pevné popisky.
9 Delphi a Free Pascal výchozí pro vyhodnocení zkratu. To může být změněno možnostmi kompilátoru, ale nezdá se, že by bylo široce používáno.
Běžné použití
Vyvarování se nežádoucích vedlejších účinků druhého argumentu
Obvyklý příklad použití a Na bázi C. Jazyk:
int denom = 0;-li (denom != 0 && počet / denom){ ... // zajišťuje, že výpočet num / denom nikdy nebude mít za následek chybu dělení podle nuly }
Zvažte následující příklad:
int A = 0;-li (A != 0 && myfunc(b)){ dělej něco();}
V tomto příkladu to vyhodnocení zkratu zaručuje myfunc (b)
není nikdy volána. To je proto, že a! = 0
hodnotí na Nepravdivé. Tato funkce umožňuje dva užitečné programovací konstrukty.
- Pokud první dílčí výraz zkontroluje, zda je nutný nákladný výpočet, a kontrola se vyhodnotí Nepravdivé, ve druhém argumentu lze eliminovat nákladné výpočty.
- Povoluje konstrukci, kde první výraz zaručuje podmínku, bez níž může druhý výraz způsobit a chyba běhu.
Oba jsou ilustrovány v následujícím fragmentu C, kde minimální vyhodnocení brání jak dereferenci nulového ukazatele, tak nadměrnému načítání paměti:
bool is_first_char_valid_alpha_unsafe(konst char *p){ vrátit se isalfa(p[0]); // SEGFAULT vysoce možné s p == NULL}bool is_first_char_valid_alpha(konst char *p){ vrátit se p != NULA && isalfa(p[0]); // 1) žádné nepotřebné provedení isalpha () s p == NULL, 2) žádné SEGFAULT riziko}
Idiomatický podmíněný konstrukt
Protože minimální vyhodnocení je součástí sémantické definice operátora a nikoli (volitelnou) optimalizací, mnoho vzorů kódování[který? ] spoléhali na to jako na stručnou (pokud idiomatickou) podmíněnou konstrukci. Mezi příklady patří:
Perl idiomy:
some_condition nebo zemřít; # Přerušte provádění, pokud je some_condition falsesome_condition a zemřít; # Zrušte provedení, pokud je podmínka true
Shell POSIX idiomy:[10]
modprobe -q some_module && echo "some_module installed" || echo "some_module not installed"
Tento idiom to předpokládá echo
nemůže selhat.
Možné problémy
Nevyzkoušená druhá podmínka vede k neprovedeným vedlejším účinkům
Přes tyto výhody může minimální vyhodnocení způsobit problémy programátorům, kteří si neuvědomují (nebo zapomínají), že se to děje. Například v kódu
-li (výrazA && myfunc(b)) { dělej něco();}
-li myfunc (b)
má provést nějakou požadovanou operaci bez ohledu na to, zda dělej něco()
je prováděno, například přidělování systémových prostředků, a výrazA
vyhodnotí jako nepravdivé myfunc (b)
se nespustí, což by mohlo způsobit problémy. Některé programovací jazyky, například Jáva, mají dva operátory, jednoho s minimálním hodnocením a druhého bez, aby se tomuto problému vyhnuli.
Problémy s nevykonanými příkazy vedlejších účinků lze snadno vyřešit správným stylem programování, tj. Nepoužíváním vedlejších účinků v booleovských příkazech, protože používání hodnot s vedlejšími účinky při hodnocení má tendenci obecně vytvářet neprůhledný a náchylný k chybám.[11]
Snížená účinnost díky omezujícím optimalizacím
Zkrat může vést k chybám predikce větve na moderní centrální procesorové jednotky (CPU) a dramaticky snížit výkon. Pozoruhodným příkladem je vysoce optimalizovaný paprsek s průsečíkem kódu pole zarovnaného k ose sledování paprsku.[je zapotřebí objasnění ] Někteří kompilátoři mohou takové případy detekovat a emitovat rychlejší kód, ale sémantika programovacího jazyka může takové optimalizace omezit.[Citace je zapotřebí ]
Příkladem kompilátoru, který není schopen optimalizovat pro takový případ, je Jáva Hotspot VM od roku 2012.[12]
Viz také
Reference
- ^ Edsger W. Dijkstra „O poněkud neuspokojivé korespondenci“, EWD1009-0, 25. května 1987 celý text
- ^ "Příkazový jazyk prostředí". pubs.opengroup.org.
- ^ Jan A. Bergstra, A. Ponse, D.J.C. Staudt (2010). "Zkratová logika". arXiv:1010.3674 [cs.LO ].CS1 maint: používá parametr autoři (odkaz)
- ^ Norma ISO / IEC 9899, oddíl 6.5.13
- ^ ISO / IEC IS 14882 koncept.
- ^ https://wiki.python.org/moin/BitwiseOperators
- ^ "std :: ops - Rust". doc.rust-lang.org. Citováno 2019-02-12.
- ^ ETSI ES 201 873-1 V4.10.1, oddíl 7.1.4
- ^ "and_then - GNU Pascal Manual". Gnu-pascal.de. Citováno 2013-08-24.
- ^ „Co || znamená v bash?“. stackexchange.com. Citováno 2019-01-09.
- ^ „Referenční transparentnost, jednoznačnost a rozložitelnost“ (PDF). Itu.dk. Citováno 2013-08-24.
- ^ Wasserman, Louis. „java - Jaké jsou případy, kdy je lepší použít bezpodmínečné AND (& místo &&)“. Přetečení zásobníku.