Weblog o elegantním programování v PHP pro mírně pokročilé
Asi největší novinkou v Admineru 3.7.0 je možnost v SQL příkazu importovat více souborů najednou. Využívá se k tomu <input multiple>. Další změny jsou drobnější:
ALTER VIEW, ale ALTER PROCEDURE je bohužel k ničemu.lang(). Při kompilaci se totiž všechny identifikátory překladu (což je obvykle anglická verze) z důvody úspory místa nahradí číslem. Rozšíření se ale nekompilují, takže z nich chodí původní textový identifikátor. Nová verze to řeší tak, že když zkompilované překladové funkci přijde text, tak pohledem do anglického překladu zjistí jeho číslo a pro překlad použije to.LIMIT 1. Ta je jako pojistka pro případ editace záznamů v tabulce bez primárního klíče. Bohužel to způsobuje varování při statement-based replikaci, že tato klauzule způsobuje nejednoznačné výsledky. Varování ničemu nevadí, ale „zasírá logy“. Nová verze proto kontroluje, jestli se záznam edituje podle primárního klíče a klauzuli LIMIT 1 přidá jen pokud ne.SHOW TABLE STATUS u velkých databází hrozně pomalý. Adminer ho potřebuje pro zjištění komentářů a typů tabulek. Nová verze si proto tyto informace vytahuje z information_schema, což je rychlejší. Projeví se to na stránce pro vytvoření tabulky a v Adminer Editoru.bit se nově zobrazuje jako číslo v dvojkové soustavě. Hodnoty tohoto typu se vrací odlišně v MySQLnd a libmysql a jde o jedinou mně známou nekompatibilitu mezi těmito nízkoúrovňovými knihovnami. Adminer chování sjednocuje.point se v některých případech pracovalo jako s číslem, protože obsahuje podřetězec int, ha ha.SELECT *, HEX(binary) AS binary. To vedlo k dvojitému uvedení těchto dat v exportu. Nová verze to ošetřuje. Možná si říkáte, proč se prostě nevyjmenuje seznam všech sloupců. I když by se přeneslo míň dat, tak by dotaz byl podstatně míň přehledný, proto se používá verze s hvězdičkou.EXPLAIN v MySQL < 5.1, nová verze to opravuje.t a f. Jeden uživatel mi ale nahlásil, že mu je PostgreSQL vrací jako 1 a 0, což způsobovalo problémy při detekci NOT NULL sloupců a primarních klíčů. Nepodařilo se mi zjistit, čím je to způsobené, jestli nějakým nastavením PostgreSQL nebo nestandardním ovladačem, každopádně jsem Adminer upravil tak, aby přijímal oba formáty.Knihu 1001 tipů a triků pro PHP si můžete koupit přímo od autora včetně podpisu a věnování. Tento prodej jsem přerušil po odstěhování do USA, v červnu ale budu v ČR a knihu budu dočasně opět prodávat.
Pokud knihu ještě nemáte a nejste si jisti, jestli za to stojí, přečtěte si ohlasy a recenze jejich čtenářů.
Knihu si můžete objednat už teď, nejzazší termín pro objednání je 15. 6. 2013. Knihu lze koupit v papírové podobě, elektronická verze bohužel není k dispozici – já na její vydání nemám práva a nakladatel ji nechystá.
Po přestěhování do USA jsem svá školení PHP (Úvod, PHP 5, Bezpečnost a Výkonnost) předal Michalu Špačkovi a MySQL (Základ, Pokročilé) Tondovi Faltýnkovi a musím říct, že jsem s nimi stejně jako účastníci školení velmi spokojen.
Další termíny všech těchto školení jsou vypsána na červen tohoto roku a já bych vás na ně rád pozval. Tou dobou zrovna budu v Praze, takže se alespoň na některá školení přijdu podívat, i když výklad nechám na Michalovi a Tondovi. Přihlásit se můžete na stránkách jednotlivých školení, v případě jakýchkoliv dotazů můžete použít diskusi u tohoto článku nebo e-mail. Rád vás v červnu uvidím.
Adminer 3.6.4 přináší několik drobných vylepšení a oprav:
auto_increment sloupce), to jsem ale nakonec neudělal, protože zrušení tohoto výchozího řazení by bylo poněkud krkolomné.timestamp se nyní zobrazuje výběr pro možnost ON UPDATE CURRENT_TIMESTAMP, který dříve byl v políčku pro výchozí hodnotu, což bylo matoucí.EXPLAIN včetně informací o oddílech.innerHTML. Nově se proto používá čistý DOM.max_input_vars a děje se tak u všech formulářů.time_zone. Není to ale tak jednoduché, protože časové pásmo nastavené na jednom serveru nemusí vůbec existovat na jiném serveru. Nejběžnější hodnota této proměnné je navíc SYSTEM znamenající, že se má použít časové pásmo daného serveru. Nově se proto časové pásmo uvádí v hodinách, což je přenosné všude.Martin Malý zveřejnil sérii svých starších článků o tzv. Vězňově dilematu. Rád jsem si tuto úlohu připomněl. O co jde?
Představte si, že jste uvězněn spolu s vaším komplicem, jste držen každý zvlášť a jste zvlášť vyslýcháni. Máte možnost vypovídat proti tomu druhému nebo mlčet. Policie na vás skoro nic nemá a pokud se oba svorně rozhodnete mlčet, dostanete oba tak akorát podmínku. Pokud však pomůžete vašeho kolegu usvědčit, půjde on sedět na deset let a vy vyváznete bez trestu. Totéž platí i v opačném případě. A pokud se rozhodnete oba vypovídat proti tomu druhému, půjdete sedět na pět let oba. Jak se rozhodnete?
| já / protihráč | podraz | spolupráce |
|---|---|---|
| podraz | 1 | 10 |
| spolupráce | -10 | 6 |
Hra na toto téma spočívá v tom, že se proti sobě postaví dva algoritmy na předem neznámý počet kol a mají hrát tak, aby maximalizovaly svůj zisk při nějakém bodovém ohodnocení jednotlivých tahů.
Martin problém popisuje ve vztahu ke komunitám, do hry vnesl šum a mutování algoritmů a celé jeho dílo určitě stojí za přečtení. Mně problém přijde zajímavý z jiného pohledu.
Martin použil v simulacích tyto algoritmy:
- Kavka. Algoritmus, který nikdy nepodrazí, jedině když dojde k nějakému šumu, tak je jeho krok vnímán jako podraz. (Beránčí algoritmus)
- Podrazák. Vždy podrazí.
- Rozmar. Algoritmus, který se mezi spoluprací a podrazem rozhoduje zcela náhodně.
- Dobrý. Algoritmus se rozhoduje náhodně. Spolupracuje, ale v jednom případu ze čtyř podrazí.
- Špatný. Rozhoduje se náhodně, většinou podrazí, v jednom ze čtyř případů spolupracuje.
- TFT. Algoritmus začne spoluprací a pak opakuje poslední krok soupeře – podrazí za podraz, spolupracuje při spolupráci. (Čistící algoritmus)
- TF2T. Algoritmus, který začíná spoluprací a podrazí jen tehdy, když byl dvakrát za sebou podražen.
- Velký pes. Algoritmus, který začne spoluprací. Pokud v minulé hře oba spolupracovali, spolupracuje dál, pokud byl podražen, podrazí. Pokud v minulé hře podrazil a podraz se mu vyplatil (=soupeř spolupracoval), bude dál podrážet. Pokud podráží oba, nabídne spolupráci. (Pragmatický algoritmus)
- Malý pes. Chová se stejně jako velký pes, ale začíná podrazem.
Naprogramovat by se daly takhle:
<?php // true je spolupráce, false je podraz function kavka($history) { return true; } function podrazak($history) { return false; } function rozmar($history) { return (mt_rand(0, 1) == 0); } function dobry($history) { return (mt_rand(0, 3) != 0); } function spatny($history) { return (mt_rand(0, 3) == 0); } function tft($history) { return (!$history || array_pop($history)); } function tf2t($history) { return (count($history) < 2 || array_pop($history) || array_pop($history)); } function velky_pes($history) { $return = true; foreach ($history as $play) { if (!$play) { $return = !$return; } } return $return; } function maly_pes($history) { $return = false; foreach ($history as $play) { if (!$play) { $return = !$return; } } return $return; } ?>
Všechny algoritmy jsou dost primitivní – do historie se buď vůbec nedívají nebo se dívají jen jeden, maximálně dva kroky zpět. Pro Martinovy účely to úplně stačí a závěry z toho vyvodil zajímavé. Ale já jsem si řekl – jak by vypadal algoritmus, který by chtěl maximalizovat svůj zisk s využitím všech dostupných znalostí? Pokud by věděl, kdo proti němu stojí, tak má situaci docela jednoduchou:
<?php function optimalni($history, $opponent) { switch ($opponent) { case 'kavka': return false; case 'podrazak': return false; case 'rozmar': return false; case 'dobry': return false; case 'spatny': return false; case 'tft': return true; case 'tf2t': return (count($history) % 2 != 0); case 'velky_pes': return true; case 'maly_pes': return (bool)$history; } return true; } ?>
Netriviální protihráči:
Pokud by se hra hrála na předem známý počet kol, tak by se také vyplatilo v posledním kole každého podrazit.
A teď řekněme, že nevíme, kdo proti nám stojí – jak z předešlých tahů odvodit, kdo to je? Kvůli náhodným algoritmům je to ještě o něco těžší – nikdy nemůžeme mít jistotu. Vyplatí se zariskovat a v jednom tahu nabídnout spolupráci jen proto, abych zjistil, kdo proti mně stojí v příštím kole? Vyplatí se na to vůbec sestavovat pravidla nebo by bylo lepší vyzkoušet všechny možnosti a optimální strategii vypočítat? Já jsem sestavil pravidla začínající podrazem a spoluprací, náhodné algoritmy jsem nezohlednil:
<?php function ucici($history) { $opening = array(false, true); // v prvních dvou kolech vyzkoušíme reakce na podraz a spolupráci if (count($history) < count($opening)) { return $opening[count($history)]; } if (!$history[0]) { // podraz v prvním kole: Podrazák nebo Malý pes return $history[1]; // když v druhém kole spolupracoval, je to Malý pes a vyplatí se spolupracovat, jinak je to Podrazák } if (!$history[1]) { // podraz v druhém kole: TFT nebo Velký pes if (count($history) < 3) { return false; // ve třetím kole musíme Velkého psa naučit, že se mu nevyplatí podrážet } return true; } // TF2T nebo Kavka if (count($history) < 4) { return false; // ve třetím kole se vyplatí podrazit, ve čtvrtém vyzkoušíme, jestli to je TF2T } if (count($history) < 5) { return true; // v pátém kole TF2T naznačíme, že s ním můžeme spolupracovat, zisk z Kavky oželíme } if (!$history[4]) { // když v pátém kole podrazil, je to TF2T return (count($history) % 2 == 0); // v lichých kolech se vyplatí spolupracovat } return false; // jinak to je Kavka } ?>
Při jiném zahájení by pravidla samozřejmě vypadala jinak. Algoritmus není tak dobrý jako optimální (který ovšem podvádí) a v některých případech ani jako některé primitivní algoritmy, s velkým počtem kol se jim ale přibližuje. V součtu proti všem drtivě vyhrává:
| zisk | Kavka | Podrazák | TFT | TF2T | Velký pes | Malý pes | celkem |
|---|---|---|---|---|---|---|---|
| Kavka | 1200 | -2000 | 1200 | 1200 | 1200 | -2000 | 800 |
| Podrazák | 2000 | 200 | 218 | 236 | 1100 | 1100 | 4854 |
| TFT | 1200 | 178 | 1200 | 1200 | 1200 | 46 | 5024 |
| TF2T | 1200 | 156 | 1200 | 1200 | 1200 | -450 | 4506 |
| Velký pes | 1200 | -900 | 1200 | 1200 | 1200 | 1158 | 5058 |
| Malý pes | 2000 | -900 | 86 | 550 | 1198 | 1190 | 4124 |
| Optimální | 2000 | 200 | 1200 | 1600 | 1200 | 1190 | 7390 |
| Učící | 1984 | 178 | 1152 | 1576 | 1166 | 1190 | 7246 |
A co kdybych nevěděl, kdo proti mně stojí, ale ani bych nevěděl, jaké algoritmy existují? Co kdybych vypustil stádo divokých algoritmů do arény a nechal přežít ten nejlepší? Jak by takový algoritmus vypadal?
Pokud s tím někdo chce experimentovat, zde je program, který jsem použil pro testování algoritmů:
<?php function veznovo_dilema($algos, $prices) { $profits = array_fill_keys($algos, array_fill_keys($algos, 0)); foreach ($algos as $home) { foreach ($algos as $away) { $home_history = array(); $away_history = array(); for ($i = 0; $i < 100; $i++) { $home_play = $home($away_history, $away); $away_play = $away($home_history, $home); $home_history[] = $home_play; $away_history[] = $away_play; $profits[$home][$away] += $prices[$away_play][$home_play]; $profits[$away][$home] += $prices[$home_play][$away_play]; } } } return $profits; } $algos = array('kavka', 'podrazak', 'tft', 'tf2t', 'velky_pes', 'maly_pes', 'optimalni', 'ucici'); $prices = array( false => array(false => 1, true => -10), true => array(false => 10, true => 6), ); foreach (veznovo_dilema($algos, $prices) as $algo => $val) { echo "$algo\t" . implode("\t", $val) . "\t" . array_sum($val) . "\n"; } ?>
Ve skutečném světě taky pracujeme s delší historií, pamatujeme si křivdy z minula a při svém rozhodování je chtě nechtě zohledňujeme. Co se algoritmizuje těžko, je první dojem – pokud se nám někdo nezdá, tak se k němu chováme jinak, než pokud je nám sympatický.
Starší články naleznete v archivu.