Acum câteva luni am primit o scrisoare de la un prieten:
Subiect: Puteți să vă dezvăluiți și să-mi explicați această linie de cod?
Text: Conteaza-ma prost, dar ... Nu inteleg si voi fi recunoscator daca il explici in detaliu. Acesta este un detector de raze în 128 de caractere. Cred că e delicios.
index.html
Am observat că variabila k este doar o constantă, așa că am scos-o din linie și am redenumit întârzierea.
code.js
var întârziere = 64;
var remiză = «pentru (n + = 7, i = întârziere, P = 'p \ n.'; j- = 1 / întârziere; P + = P [i% 2 (i% 2 * j-j + n / întârziere? ^ j) 1: 2]) j = întârziere / i; p.innerHTML = P ";
var n = setInterval (tragere, întârziere);
În continuare, var draw a fost doar un șir care a fost executat ca o funcție de eval cu o periodicitate setInterval, deoarece setInterval poate lua ambele funcții și șiruri de caractere. Am mutat var draw la o funcție explicită, dar a păstrat șirul original pentru referință doar pentru caz.
var întârziere = 64;
var p = document.getElementById ("p"); // <—————
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var = funcția () pentru (n + = 7, i = întârziere, P = 'p.n'; i = n / întârziere ^ j) 1. 2]) j = întârziere / i; p.innerHTML = P;
>
>;
var n = setInterval (tragere, întârziere);
Apoi am declarat variabilele i, p și j și le-am transferat la începutul funcției.
var întârziere = 64;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var draw = funcția () var i = întârziere; // <—————
var P = "p.n";
var j;
pentru (n + = 7; i> 0; P + = P [i% 2. (i% 2 * j - j + 1. 2]) j = întârziere / i; p.innerHTML = P;
i - = 1 / întârziere;
>
>;
var n = setInterval (tragere, întârziere);
Am descompus buclă și mi-a transformat-o într-o buclă. Dintre cele trei părți ale fostei rămas doar o parte din CHECK_EVERY_LOOP, și orice altceva (RUNS_ONCE_ON_INIT; DO_EVERY_LOOP) a suferit în afara buclei.
var întârziere = 64;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var draw = funcția () var i = întârziere;
var P = "p.n";
var j;
n + = 7;
în timp ce (i> 0) / <———————-
// Actualizați codul HTML
p.innerHTML = P;
j = întârziere / i;
i - = 1 / întârziere;
P + = P [i% 2. (i% 2 * j - j + n / întârziere ^ j) 1. 2];
>
>;
var n = setInterval (tragere, întârziere);
Aici am extins operatorul ternar (condiția .Dacă dacă este adevărat, face dacă este fals) în P + = P [i% 2. (i% 2 * j - j + 1. 2];
Această valoare (index) este folosită pentru a schimba șirul P, așa că vom apela indexul și vom transforma șirul în P + = P [index];
var întârziere = 64;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var draw = funcția () var i = întârziere;
var P = "p.n";
var j;
n + = 7;
în timp ce (i> 0) // Update HTML
p.innerHTML = P;
j = întârziere / i;
i - = 1 / întârziere;
indice index;
permite iIsOdd = (i% 2! = 0); // <—————
P + = P [index];
>
>;
var n = setInterval (tragere, întârziere);
M-am întins 1 din indexul de valoare = (i% 2 * j - j + n / delay ^ j) 1 în altă declarație if.
Iată o modalitate dificilă de a verifica paritatea rezultatului în paranteze, când 0 pentru o valoare uniformă și 1 pentru un număr impar. Este un operator AND bitwise. Funcționează astfel:
1 1 = 1
0 1 = 0
Deci, ceva 1 transformă "ceva" într-o reprezentare binară și, de asemenea, termină în fața unității numărul necesar de zerouri pentru a se potrivi cu dimensiunea "ceva" și returnează pur și simplu rezultatul AND al ultimului bit. De exemplu, 5 în format binar este egal cu 101, deci dacă aplicăm operația logică AND la unitate pe ea, obținem următoarele:
0 1 // 0 - randament egal 0
1 1 // 1 - întoarcere parțială 1
2 1 // 0 - randament egal 0
3 1 // 1 - întoarcere parțială 1
4 1 // 0 - randament egal 0
5 1 // 1 - întoarcere parțială 1
Rețineți că am redenumit și restul indexului la magie, deci codul cu extensia 1 va arata astfel:
var întârziere = 64;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var draw = funcția () var i = întârziere;
var P = "p.n";
var j;
n + = 7;
în timp ce (i> 0) // Update HTML
p.innerHTML = P;
j = întârziere / i;
i - = 1 / întârziere;
indice index;
permite iIsOdd = (i% 2! = 0);
P + = P [index];
>
>;
var n = setInterval (tragere, întârziere);
Apoi, am desfasurat P + = P [index]; în declarația comutator. In acest moment, a devenit clar că indicele poate lua doar una dintre cele trei valori - 0, 1 sau 2. Se înțelege de asemenea că P variabilă este întotdeauna inițializată cu valori var P = „P. N.“;, unde 0 indică p, 1 indică pe. și 2 puncte la n - un caracter de linie nouă
var întârziere = 64;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
var draw = funcția () var i = întârziere;
var P = "p.n";
var j;
n + = 7;
în timp ce (i> 0) // Update HTML
p.innerHTML = P;
j = întârziere / i;
i - = 1 / întârziere;
indice index;
permite iIsOdd = (i% 2! = 0);
dacă (iIsOdd) lăsați magic = (i% 2 * j - j + n / delay ^ j);
da magicIsOdd = (magia% 2! = 0); // 1
dacă (magicIsOdd) / &1
indice = 1;
> alt index = 0;
>
> alt index = 2;
>
var n = setInterval (tragere, întârziere);
Am înțeles operatorul var n = setInterval (remiză, întârziere); Metoda setInterval returnează numere întregi începând cu una, mărind valoarea pentru fiecare apel. Acest întreg poate fi folosit pentru clearInterval (adică, pentru a anula). În cazul nostru, setInterval este numit o singură dată, iar variabila n este pur și simplu setată la 1.
De asemenea, am redenumit întârzierea la DELAY pentru a reaminti că este doar o constantă.
Și nu în ultimul rând, am pus paranteze în% i 2 * j - j + n / DELAY ^ j pentru a indica faptul că ^ (XOR logic) prioritate mai mică decât operatorii%, *, -, +, și /. Cu alte cuvinte, toate calculele de mai sus se efectuează mai întâi și numai atunci ^. Asta se dovedește (i% 2 * j - j + n / DELAY) ^ j).
Clarificare: Mi sa spus că am plasat în mod eronat p.innerHTML = P; // Actualizați codul HTML într-o buclă, așa că am eliminat-o de acolo.
const DELAY = 64; // aproximativ 15 cadre pe secundă 15 cadre pe secundă * 64 secunde = 960 de cadre
var n = 1;
var p = document.getElementById ("p");
// var draw = "pentru (n + = 7, i = întârziere, P = 'p. \ n'; i- = 1 / întârziere; /delay^j)1:2])j=delay/i;p.innerHTML=P »;
/ **
* Desenează o fotografie
* 128 caractere cu 32 caractere = 4096 de caractere în total
* /
var draw = funcția () var i = DELAY; 64
var P = "p.n"; // Prima linie, referință pentru caracterele de utilizat
var j;
j = DELAY / i;
i - = 1 / DELAY;
indice index;
permite iIsOdd = (i% 2! = 0);
dacă (iIsOdd) lăsați magic = ((i% 2 * j - j + n / DELAY) ^ j); // <——————
da magicIsOdd = (magia% 2! = 0); // 1
dacă (magicIsOdd) / &1
indice = 1;
> alt index = 0;
>
> alt index = 2;
>
setInterval (desen, 64);
Rezultatul final poate fi văzut aici.
Partea 2. Înțelegerea codului
Ce se întâmplă aici? Să ne dăm seama.
Inițial valoarea i este setată la 64 prin var i = DELAY;, atunci fiecare ciclu se reduce la 1/64 (0.015625) prin i - = 1 / DELAY;. Buclele continuă atâta timp cât este mai mare decât zero (în timp ce (i> 0)
Imaginea este alcătuită din 32 de linii, cu 128 de caractere în fiecare. În mod convenabil, 64 x 64 = 128 x 32 = 4096. Valoarea lui i poate fi chiar un (nui nu lasa iIsOdd = (i% 2 = 0);) dacă i este chiar număr strict. .. Astfel apar de 32 de ori, atunci când acesta este egal cu 64, 62, 60 și 32, etc. Aceste index ori ia indicele valoric 2 = 2, și se adaugă la linia newline: P + = «n»; // aka P [2]. Restul de 127 de caractere din șir vor fi p sau.
Dar când să instalați p, și când.
Ei bine, pentru început, știm exact ce să instalăm. dacă valoarea ciudată lasă magia = ((i% 2 * j - j + n / DELAY) ^ j); sau setați p dacă "magic" este egal.
var P = "p.n";
dacă (magicIsOdd) / &1
indice = 1; al doilea caracter în P.
> alt index = 0; // primul caracter în P - p
>
Dar când magia este uniformă și când cea ciudată? Aceasta este o întrebare de un milion de dolari. Înainte de a trece la ea, să definim încă un lucru.
Dacă ștergeți + n / DELAY, lăsați magic = ((i% 2 * j - j + n / DELAY) ^ j), atunci obțineți o imagine statică care nu se mișcă deloc:
Acum, uita-te la magie fără + n / DELAY. Cum a apărut această imagine frumoasă?
(i% 2 * j - j) ^ j
Acordați atenție la ceea ce se întâmplă în fiecare ciclu:
j = DELAY / i;
i - = 1 / DELAY;
Cu alte cuvinte, putem exprima j în termenii unui i finit ca j = DELAY / (i + 1 / DELAY). Dar din moment ce 1 / DELAY este prea mic, pentru acest exemplu, puteți să scădeți + 1 / DELAY și să simplificați expresia la j = DELAY / i = 64 / i.
În acest caz, putem rescrie (i% 2 * j - j) ^ j ca i% 2 * 64 / i - 64 / i) ^ 64 / i.
Utilizăm un calculator de grafică online pentru a desena diagrame ale unora dintre aceste funcții.
Mai întâi, atrage i% 2.
Se pare o diagramă frumoasă cu valori y de la 0 la 2.
Dacă desenați 64 / i, avem următorul grafic:
Dacă desenați întreaga stângă a expresiei, veți obține un grafic care arată ca o combinație a celor două.
În final, dacă desenăm două funcții una lângă cealaltă, vedem următoarele.
Ce spun aceste grafice?
Să ne amintim întrebarea la care încercăm să răspundem, adică cum a surprins această imagine statică frumoasă:
Știm că dacă "magia" (i% 2 * j - j) ^ j are o valoare uniformă, atunci adăugați p și adăugați pentru un număr impar.
Să creștem primele 16 linii ale graficului nostru, unde am valori de la 64 la 32.
Returnează 0 dacă ambii biți sunt 1 sau ambii sunt 0.
j noastră pornește de la una și se deplasează încet la egalitate de puncte, oprindu-se chiar lângă el, astfel încât să putem presupune că este întotdeauna unitatea în timp ce rotunjirea în jos (Math.floor (1.9999) === 1), și avem nevoie de mai mult de un articol de pe partea stângă pentru a obține rezultatul zero și a ne da p.
Cu alte cuvinte, fiecare diagonală verde reprezintă un rând în graficul nostru. Deoarece pentru primele 16 de rânduri de valoarea j este întotdeauna mai mare decât 1, dar mai puțin de 2, atunci putem obține valoarea ciudat numai în cazul în care partea stângă a expresiei (i% 2 * j - j) ^ j, acesta este i% 2 * I / 64 - i / 64, adică diagonala verde, va fi, de asemenea, mai mare de 1 sau mai mică -1.
1 ^ 1 0 - chiar p
1.1 ^ 1.1 // 0 - chiar p
0,9 ^ 1 // 1 - impar.
0 ^ 1 // 1 - impar.
-1 ^ 1 / -2 - chiar p
-1,1 ^ 1,1 // -2 - chiar p
Dacă ne uităm la graficul nostru, atunci linia diagonală cea mai din dreapta părăsește abia peste 1 și mai jos -1 (puține numere chiar și câteva simboluri p). Următorul se duce puțin mai departe de aceste limite, cel de-al treilea - chiar și puțin mai departe etc. Numărul liniei 16 abia se află în limitele între 2 și -2. După linia 16, vedem că graficul nostru static își schimbă caracterul.
După linia 16, valoarea lui j intersectează limita 2, astfel încât rezultatul așteptat se modifică. Acum obținem un număr par, dacă linia diagonală verde este mai mare de 2 sau mai mică -2, sau în cadrul cadrelor 1 și -1, dar nu le atinge. De aceea vedem două sau mai multe grupuri de p simboluri în imagine începând cu linia a 17-a.
Dacă vă uitați la cele câteva linii inferioare din imaginea animată, veți observa că ele nu urmează același model datorită fluctuației mari din grafic.
Acum, reveniți la + n / DELAY. În cod, vedem că valoarea lui n începe cu 8 (1 de la setInteval și plus 7 pentru fiecare apel de metodă). Apoi se incrementează cu 7 de fiecare dată când setInteval este declanșat.
După atingerea valorii de 64, graficul se modifică după cum urmează.
Rețineți că j este încă unul, dar acum jumătatea stângă a diagonalei roșii în intervalul de aproximativ 62-63 este de aproximativ zero, iar jumătatea dreaptă în jurul valorii de 63-64 este de aproximativ unu. Așa cum caracterele apar în ordine descrescătoare 64 - 62, se poate aștepta ca jumătatea din dreapta a diagonalei în regiunea 63-64 (1 ^ 1 = 0 // chiar) se adaugă p caractere teanc și jumătatea stângă a diagonalei în regiunea 62-63 ( 1 ^ 0 = 1 // ciudat) va adăuga o grămadă de puncte. Toate acestea vor crește de la stânga la dreapta, ca un text simplu.
În acest timp, numărul de simboluri p a crescut la o valoare constantă. De exemplu, în primul rând, jumătate din toate valorile vor fi întotdeauna egale. Acum simbolurile p și. va schimba locurile.
De exemplu, când n este incrementat cu 7 la următorul apel setInterval, graficul se schimbă ușor.
Rețineți că diagonala pentru primul rând (aproape de marcajul 64) sa mutat cu aproximativ un mic pătrat în sus. Deoarece cele patru pătrate mari sunt de 128 de caractere, într-un pătrat mare vor fi 32 de simboluri și într-un mic pătrat 32/5 = 6,4 sivoli (aproximativ). Dacă ne uităm la redarea HTML, atunci primul rând a fost într-adevăr mutat spre dreapta pentru 7 caractere.
Și un ultim exemplu. Iată ce se întâmplă dacă sunați setInterval șapte ori mai mult și n va fi 64 + 9 × 7.
Pentru primul rând, j este încă 1. Acum, jumătatea superioară a diagonalei roșii lângă semnul 64 este de aproximativ două și capătul inferior este aproximativ unu. Aceasta inversează imaginea într-o altă direcție, pentru că acum 1 ^ 2 = 3 // ciudat -. și 1 ^ 1 = 0 // egal - p. Deci, vă puteți aștepta la multe puncte, urmate de simbolurile p.
Va arăta așa.
Graficul este fixat fără sfârșit.
Sper că munca noastră are un sens. Este puțin probabil să fiu eu vreodată așa ceva, dar a fost interesant să înțeleg acest cod.