Hra Života v jednom řádku APL Tento program je k dispozici v "Dr.Dobbs", únor 2007
Vysvětlení Pokud nejste obeznámeni s zprostředkovat to Game of Life nebo APL programovací jazyk, doporučuji konzultovat s velmi krátký popis APL a popis Hra Života . Kromě toho, na této stránce naleznete IBM APL2 tlumočníka a několik jazykových odkazů. Tento podrobný vysvětlení programu, APL, že vypíše N generace počáteční konfigurace M Game of Life bude také sloužit jako krátké a velmi neformální kurz APL. Než začneme rozluštit (to se zdá být správné slovo zde) program LIFE, zkusme vypracovat strategii, jak napsat tento program v jedné linii, bez ohledu na to, jak to zní nemožné. Život je cyklický hra svou povahou a - jistě - algoritmus provádění této hry zřejmě by se měl skládat z několika cyklů. To znamená, že hlavní smyčka tohoto programu je tok generacemi. Dalším smyčka je ten, který skenuje matici pro výpočet buňky, které jsou mrtví, živý nebo novorozence. Ještě další smyčka vypočítává počet sousedů každé dané buňce. Nyní, myslíte si, že je to: nemůžeme použít ani jediný smyčku uvnitř jedné linii, natož tři vnořené smyčky! Ale počkejte chvíli - APL nám umožňuje překonat téměř všechny, i ty zoufalé situaci. Za prvé, my nepotřebujeme smyčku pro skenování matrice, protože APL nám umožňuje pracovat s celou matrice obvykle pomocí jediného funkce. Totéž platí pro počet sousedů. Pokud jde o hlavní smyčky, která govenrs změnu generací - bude existovat nic takového vůbec! Nechť N být několik generací chceme spočítat. Můžeme zapsat všechny smyčky (to je podprogramy, které tvoří tělo smyčky) jako jeden velmi dlouhý řádek. Nyní vše, co musíte udělat, je provést tento řádek. Není to dobré, - říká čtenář. - Co když budeme potřebovat 1000 generace? Budeme zapsat 1000 smyčky, které dělají totéž pak? Rozhodně ne! Jak jsme řekli dříve, všechny tyto smyčky jsou dělají totéž, takže budeme prostě zapsat pouze jednu smyčku a pak APL nám pomůže znásobit ho n-krát pomocí funkce ρ (RO), pro toto je funkce, která vytváří pole jakéhokoliv rozměru. Po dokončení budeme říkat úžasné APL funkci nazvanou Execute , Tato funkce spustí řetězec o to jako platný řádek APL programu. (Téměř rovná operátor v Lispu se nazývá eval). Tato funkce bude zodpovědný za realizaci "velmi dlouhé řady" zřetězených smyčky těl. Nakonec, budeme muset počítat všechny sousedy každé dané buňce našeho matrice. A to bude hlavní trik našeho programu. Pojďme představují živé buňky života jako 1 a prázdné buňky jako 0. Pro náš příklad budeme trvat 5x5 a dát ve středu velmi jednoduchý, ale zajímavý organismus s názvem semafor:
To je, jak se chová semafor:
atd., atd. Thas je důvod, proč v příští generaci budeme očekávat, že následující matici:
Co budeme dělat, najít součet všech sousedů každé buňce v dané matrici? Počet sousedů je rovno až 8, a tak, pokud se přesune našemu matrici 8 krát v každém možném směru, a potom přidat dohromady všechny 1s ve výsledných buněk - součet se bude rovnat počtu 1s (tj sousedů) obklopující danou buňku. Na následujícím obrázku můžete vidět všechny 8 matrice po takovém posunu:
Nyní, pojďme shrnout všechny tyto matrice kromě originální:
Zde je bližší pohled na některé z buněk, které ukazují na jejich sousedů v původní matice:
Máme matrice, kde každá buňka obsahuje počet sousedy z živých buněk původní matice! Nyní, v souladu s pravidly života ty buňky, které mají 2 nebo 3 sousedy budou žít v příští generaci, zatímco všechny ostatní budou umírat. Prázdné buňky, které mají přesně 3 sousedy se narodí v příští generaci. Provedení několik jednoduchých logických operací nad matici sousedů budeme mít další generaion naší semaforu. Mít tento plán akcí, můžeme začít podrobnou studii programu. A tady to je: Chcete-li dešifrovat programu budeme pohybovat zprava doleva, protože to je způsob, jakým se pohybuje tlumočník APL. Pro větší pohodlí (a pouze pro pohodlí, protože tam není nic v APL, který diktuje to), budeme dělit náš program na logické bloků způsobem zobrazené na následujícím obrázku:
Tímto způsobem to bude snazší sledovat události odvíjející. Mezi apostrofy je řetězec, který představuje "tělo" naší hlavní smyčky (blok 1): První věc, která se stane, zde je operace Přiložte provedena na původní matice M: ⊂M. Funkce Uzavírejte otočí pole jakýkoliv rozměr do skalární (to znamená, že pole ztrácí všechny její rozměry, zatímco jeho vnitřní obsah zůstává beze změny). Tato situace je poněkud zvláštní, takže obraz se může hodit:
Další funkce se nazývá Otočit: , Otáčí se matrice daný počet časů v poměru k jeho horizontální osy (jak je patrné z tvaru funkce). Abychom pochopili význam této funkce a vysvětlit symbol '' na své pravé straně, pojďme se nejprve podívat do levého argument Otočit, a sice výraz: (V, V ← 1 -1). Dva kroky probíhají zde: proměnná V dostane přiřazen s vektorem 1 -1 (to je reprezentován jako V ← 1 1), a pak je tato proměnná ihned použit (ve skutečnosti se tato proměnná mnohokrát použit v tomto programu ). Zde se setkáváme s novou funkci -, (čárka), který se nazývá Catenate a slouží k zřetězit dva prvky do jediného vektoru. Proto výraz V, V ← 1 -1 vyhodnocen vektoru se skládá ze čtyř částí: 1 -1 1 -1. Jak již bylo řečeno, vedlejším účinkem tohoto výrazu je inicializace proměnné V. Takže, máme následující (blok 2): Operátor "" (který se nazývá Každý) ovlivňuje funkci otáčení takovým způsobem, že každý prvek levé argumentu Otočení dostane protějšek odpovídající prvek righ argument otáčet a jeho otáčení matice M se stane dvojici od pair. (Jedno slovo o operátory: zatímco funkce pracovat na polích nebo skaláry, operátoři pracovat na funkce sám, tedy pokud argument funkce je vždy pole nebo skalár, argument operátora je vždy funkce). Samozřejmě, že operátor každý může být použit s jakýmkoliv jiným funkce APL. Poznámka toto: V případě, že jeden z argumentů funkce je skalární (a pamatovat si, že původní matice M byl otočen do skalární podle přiložit), tento skalární bude vektor s počtem stejných prvků, odpovídající počtu prvky vektoru, který je další argument funkce. To znamená, že můžeme vidět naši výraz jako: Nyní je snadné pochopit, co se děje. Matrix M je otočena čtyřikrát kolem své horizontální osy: jeden řádek nahoru, jeden řádek dolů, znovu jeden řádek nahoru a opět jeden řádek dolů. Zde je výsledek:
To, co jsme se sem dostali, je přechodný výsledek, samozřejmě. Vlastně, musíme přesunout každý z těchto matric vlevo a vpravo získat diagonální směny po všem. A protože již máme všechny potřebné znalosti, můžeme to udělat s tímto výrazem: Opět jsme použijte funkci Otočit, ale tentokrát to bude střídat matice okolo svislé osy (jako symbol implikuje). Operátor Každý bude fungovat na všech čtyřech matric podle levého argument Otočit: vlevo, vpravo, vpravo, vlevo. Kromě toho, získat maximální zisk variabilní V, budeme používat i tady, a místo vektoru 1 -1 -1 1 budeme psát (V, V). Zde Otočení se znovu použít, a je aplikován na V samotné. Možná, že tento výraz nepomůže tady objasnit věci, ale je to tak APL-like!
Teď už víme, co výsledek výrazu vypadá jako. Zavoláme to Blok 3:
Po matice M je posunut ve všech diagonálních směrech všechno zbývá, je přesunout ji nahoru, dolů, doleva a doprava. To znamená, že výsledek exprese bude:
A výsledek výrazu bude:
Nakonec se matrice se pohybuje ve všech 8 směrech. Chcete-li zjistit počet sousedy každé buňky musíme udělat vektor skládající se z matic máme tak daleko a shrnout všechny prvky vektoru. V APL můžeme udělat to jednoduše takto: + / (Block 5), (Block 4), (blok 3) Zde je představen jiný operátor. To je nazýváno Snížení a vypadá lomítkem: / operátor Redukce vloží svou funkci-argumentaci (v tomto případě funkce sčítání) mezi všemi prvky daného vektoru. Všimněte si také funkci Catenate, která vytvoří vektor všech bloků. Tady je to, co máme: (Matrix 1 bloku 5) + (matice 2 bloku 5) + (Matrix 1 bloku 4) + (matice 2 bloku 4) + (Matice 1 z bloku 3) + (2 matice z bloku 3) + (matice 3 z bloku 3) + (matice 4 bloku 3). A teď tu máme matici všech sousedů. Je to výsledek výrazu, který jsme nazvali Blok 6. (Měli bychom dodat, že sem je tu ještě jedna funkce provádí přes výsledné matici - zveřejnit: ⊃ Je to přesný opak funkce přiložte Tato funkce otočí skalární obsahující matici zpět do matrice s originálními rozměry..).
Dalším krokem, jak bylo řečeno, je nalézt všechny buňky s počtem sousedů se rovná 2 nebo 3, tak, že se bude nadále žít v příští generaci, a nalézt prázdné buňky s počtem sousedů se rovná 3, tak, aby nové buňky budou tam narodí. Nejprve se porovnat matrici sousedy s matricí naplněnou 2s (blok 7), tak jako výsledek získáme matici nul a jedniček, takovým způsobem, že ty odpovídají buněk, které mají přesně dva sousedy: 2 = T ← matice všech sousedů (Block 6) I v tomto případě dvě akce se provádí souběžně: proměnná T dostane hodnotu matice sousedů a zároveň je ve srovnání s maticí 2s (Všimněte si, že skalární "2" je rozšířen tak, aby matrice 2s, jejichž rozměry jsou stejné k matrici je v porovnání s -, které je, jak funguje APL). Zde je návod, jak srovnání vypadá ve skutečnosti:
A výsledkem toho je:
Jak vidíte, výsledná matice obsahuje v každé buňce výsledek srovnání odpovídajících buněk argumentu matic. 1 znamená, že buňky jsou si rovny, 0 - jinak. Pokud budeme provádět logické operace AND (∧), přes to a původní matice M (Block 8), že je 1s budou ponechány pouze v těch místech, kde obě matice mají 1s, dostaneme matici, kde se pouze živé buňky jsou ty, které mají přesně dva sousedů:
Opravdu, v původní matice M pouze ústřední buňka má přesně dvě sousedy. Stejným způsobem můžeme najít všechny buňky, která má přesně tři sousedy. Poznámka trik zde: to není důležité, zda původní buňka je prázdná nebo živý: musíme nechat živé buňky, jak žít a vyplnit prázdné buňky s nově narozených 1s. Výsledkem výrazu 3 = T (Block 9), kde T, jak si vzpomínáte, je matice sousedů, bude:
Tyto buňky se narodil v příští generaci. A konečně, musíme logicky přidat (pomocí tlačítka nebo (∨) provoz) obě matice z posledních dvou výrazů a - voila! - My jsme vypočítali příští generaci života pro matice M! Všichni spolu nyní: Blok 10 ← Matrice všech sousedů, že je (3 = T) ∨M∧2 = T ← matice všech sousedů dá výsledek:
což je skutečně příští generace semaforu. Nyní musíme vytisknout výsledné matrici a uděláme to pomocí výstupní funkce APL je názvem Quad: (To je dost, aby "přiřadit" matrici k této funkci). Nicméně, jsme neskončili. Pamatujte si, že až teď všechno, co jsme udělali, bylo výpočet jediné generaci. Kromě toho jsou všechny akce jsou stále "zmrazené" mezi apostrofy. Takže, co se stane s tohoto řetězce? Podívejte se na bloku 11 proměnlivá S je přiřazen s řetězcem označené Blok 1 Poznámka znovu:.. S není rovna smyslu (nebo hodnota) řetězce, S je roven řetězce samotné, tedy k sekvenci Znaky mezi apostrofy. Tento způsob, jak oddálit popravu bloku 1 až do okamžiku, kdy vše bude připraveno pro výpočet N generace života. (Nezapomeňte, že blok 1 počítá pouze jednu generaci). Chcete-li S z vektoru znaků do skalární budeme používat funkci přiložit, který je již dobře známý pro nás. Stěhování odešel budeme objevovat novou funkci - přetvořit (ρ) s levým argument n představující počet generací chceme spočítat.Tato funkce se zase se skalární S do vektoru z N stejných prvků, každý z nich obsahuje blok 1, že je "podprogram" pro výpočet jedné generace. Takže obecně máme vektor scalars sestávajících z vektorů postav. Konečný Metamorphose - ještě další funkce Zapsat (∈) slouží pro výrobu jednoduchého vektor z pole sestávající z libovolného počtu vnořených prvků. Tak, naše "komplex" vektor N vnořených elementů se stane jedním velmi dlouhá, ale jednoduché vektorové znaků. Jmenujme to nějak výjimečně zdůraznit význam tohoto vektoru, εκνµε, jako matematici často dělají. Zde je to, co věnovat pozornost: každému jednomu z podřetězců (tj každý blok 1) z ςεκτορ začíná se symbolem M (původní matice) a končí s operátorem přiřazení ← (jako obvykle, začátek je na pravé straně a konec je na levé straně).To je nejvíce vpravo χηαρακτερ - je původní matice M, a vektor sám vypadá takto (vnitřní obsah přeskočen):
A jsme tady! Nyní je jasné, co se děje: když řetězec α ϕε proveden zprava doleva, každý nově vypočtená hodnota matice M (tj každá nová generace of Life) budou zařazeny do téže proměnné M jako nové původní matice pro výpočet další generace! A počet generací, se bude rovnat N.
Jediný zbývající problém teď je to, co dělat s zcela vlevo, visí ve vzduchu přiřazení operátora ←. Podívejte se znovu na celého programu - na samém konci na šipku se děje přímo do symbolu Quad - který je zřetězen s ςεκτορčárkou (Catenate). Ale nemusí to znamená tisknout všechno ven? Přesně tak, to je to, co potřebujeme!
-
A konečně v závěru tohoto grandiózního zákona se setkáváme funkci (Execute) - ten jsme již zvyklí. Tato funkce provádí symbolickou řetězec, který je argument, tato funkce je. Samozřejmě, řetězec by měl containg syntakticky správný výraz APL. Takže, co je to funkce? To je APL interpret sám tváří jako nevinná funkce. A co bude výsledkem provedení našeho vektoru par excellence ℑ? Right - N matrice vytištěné na obrazovce, které představují N generace života počínaje od původní matice M. QED