Несколько месяцев назад я получил от друга такое письмо:
Тема: Можешь объяснить мне эту одну строчку кода?
Текст: Считай меня тупым, но… я не понимаю её и буду благодарен, если растолкуешь подробно. Это трассировщик лучей в 128 символах. Мне кажется, он восхитительный.
Эта строчка JavaScript отрисует анимацию, которая показана на изображении под катом. В браузере она запускается здесь. Скрипт написан автором www.p01.org, где вы можете найти эту и много других классных демок.
![](https://habrastorage.org/getpro/habr/post_images/264/07a/cb2/26407acb273d35bfb8bb5f1bd21d8244.gif)
Вызов принят!
Первым делом я оставил HTML в HTML, код JavaScript перенёс в файл
index.html
Я заметил, что там переменная
code.js
Далее,
Ещё я заметил, что элемент
Затем я объявил переменные
Я разложил цикл
Здесь я развернул тернарный оператор
Это значение (индекс) используется для сдвига строки P, так что назовём
Я разложил
Здесь хитрый способ проверки на чётность результата в круглых скобках, когда для чётного значения возвращается 0, а для нечётного — 1.
Следовательно,
Другими словами, пятёрка — нечётное число, а результатом 5 AND 1 (5 & 1) будет 1. В консоли JavaScript легко проверить соблюдение этой логики.
Обратите внимание, что я также переименовал остальную часть
Далее я развернул
Я разобрался с оператором
Я также переименовал
И последнее, но не менее важное, я поместил круглые скобки в
Уточнение: Мне указали, что я ошибочно поместил
Окончательный результат выполнения можно увидеть здесь.
Так что здесь происходит? Давайте разберёмся.
Изначально значение
Изображение состоит из 32 строк, со 128 символами в каждой. Очень удобно, что 64 ? 64 = 32 ?128 = 4096. Значение
Но когда устанавливать
Ну, для начала нам точно известно, что следует установить
Но когда
Если убрать
![](https://habrastorage.org/getpro/habr/post_images/3cf/20f/8fa/3cf20f8fa0be07b8816672ea816ddf21.png)
Теперь посмотрим на
Обратите внимание, что получается в каждом цикле:
Другими словами, мы может выразить
В таком случае мы можем переписать
Используем онлайновый графический калькулятор для отрисовки графиков некоторых из этих функций.
Прежде всего, отрисуем
Выходит симпатичный график со значениями y от 0 до 2.
![](https://habrastorage.org/getpro/habr/post_images/8d7/f6c/2db/8d7f6c2db6c533fad32cfe3cf199d7a6.png)
Если отрисовать
![](https://habrastorage.org/getpro/habr/post_images/c36/4bd/d35/c364bdd35185f6ca36dc66918c4deb39.png)
Если отрисовать всю левую сторону выражения, то получится график, который выглядит как сочетание двух предыдущих.
![](https://habrastorage.org/getpro/habr/post_images/c7b/677/692/c7b677692270a570eb2b2e2dc1c577aa.png)
В конце концов, если мы отрисуем две функции рядом друг с другом, то увидим следующее.
![](https://habrastorage.org/getpro/habr/post_images/323/cc4/599/323cc4599c40a0f4913130b970aa8672.png)
Давайте припомним вопрос, на который мы пытаемся ответить, то есть каким образом получилась такая красивая статическая картинка:
![](https://habrastorage.org/getpro/habr/post_images/3cf/20f/8fa/3cf20f8fa0be07b8816672ea816ddf21.png)
Мы знаем, что если «магия»
Увеличим первые 16 строк нашего графика, где
![](https://habrastorage.org/getpro/habr/post_images/365/840/8a5/3658408a5bca2a1248b55e25cbe42411.png)
Побитовый XOR в JavaScript отбросит все значения справа от запятой, так что это равнозначно применению метода
Он вернёт 0, если оба бита равны 1 или оба равны 0.
Наша
Другими словами, каждая зелёная диагональ представляет собой один ряд в нашем графике. Поскольку для первых 16 рядов значение j всегда больше 1, но меньше 2, то мы можем получить нечётное значение только в том случае, если левая сторона выражения
Вот некоторые результаты из консоли JavaScript, чтобы посмотреть результаты вычислений: 0 или ?2 означают, что результат чётный, а 1 соответствует нечётному числу.
Если посмотреть на наш график, то там самая правая диагональная линия едва выходит выше 1 и ниже ?1 (мало чётных чисел — мало символов
![](https://habrastorage.org/getpro/habr/post_images/d77/ae6/4e8/d77ae64e8aba2174921af344da5b50a7.png)
После 16-й строки значение
Если присмотреться к нескольким самым нижним линиям в анимированной картинке, то вы заметите, что они не следуют одному и тому же шаблону из-за большой флуктуации графика.
Теперь вернёмся к
После достижения значения 64 график изменяется следующим образом.
![](https://habrastorage.org/getpro/habr/post_images/9da/b80/7db/9dab807dbf5f8e0ef5cdef4c4b3025ab.png)
Обратите внимание, что
Рендеринг HTML для такого условия выглядит следующим образом (вы можете жёстко вбить значение
![](https://habrastorage.org/getpro/habr/post_images/80e/05a/865/80e05a865983e43d7fe7678a0120bdb6.png)
К этому моменту количество символов
Для примера, когда
![](https://habrastorage.org/getpro/habr/post_images/ec2/d0e/87d/ec2d0e87df529c6a949ebe5de5ef3daa.png)
Обратите внимание, что диагональ для первого ряда (около отметки 64) сдвинулась примерно на один маленький квадратик вверх. Поскольку четыре больших квадратов представляют собой 128 символов, в одном большом квадрате будет 32 символа, а в одном маленьком квадрате 32/5 = 6,4 сивола (примерно). Если посмотрим на рендеринг HTML, то там первый ряд действительно сдвинулся вправо на 7 символов.
![](https://habrastorage.org/getpro/habr/post_images/eb1/1f1/5b7/eb11f15b78c6b11938ebdc48ba766e12.png)
И один последний пример. Вот что происходит, если вызвать setInterval ещё семь раз, а
![](https://habrastorage.org/getpro/habr/post_images/e22/6e2/e95/e226e2e95789f61e8cbac690381820c1.png)
Для первого ряда
Выглядеть это будет так.
![](https://habrastorage.org/getpro/habr/post_images/960/c6e/60d/960c6e60d109d03971e019bfc0feb4c5.png)
График бесконечно зациклен.
Надеюсь, наша работа имеет какой-то смысл. Вряд ли я когда-нибудь смог бы самостоятельно придумать нечто подобное, но было интересно разобраться в этом коде.
Тема: Можешь объяснить мне эту одну строчку кода?
Текст: Считай меня тупым, но… я не понимаю её и буду благодарен, если растолкуешь подробно. Это трассировщик лучей в 128 символах. Мне кажется, он восхитительный.
<pre id=p><script>n=setInterval("for(n+=7,i=k,P='p.\\n';i-=1/k;P+=P[i%2?(i%2*j-j+n/k^j)&1:2])j=k/i;p.innerHTML=P",k=64)</script>
Эта строчка JavaScript отрисует анимацию, которая показана на изображении под катом. В браузере она запускается здесь. Скрипт написан автором www.p01.org, где вы можете найти эту и много других классных демок.
![](https://habrastorage.org/getpro/habr/post_images/264/07a/cb2/26407acb273d35bfb8bb5f1bd21d8244.gif)
Вызов принят!
Часть I. Извлекаем читаемый код
Первым делом я оставил HTML в HTML, код JavaScript перенёс в файл
code.js
, а p
закавычил в id="p"
.index.html
<script src="code.js"></script>
<pre id="p"></pre>
Я заметил, что там переменная
k
— просто константа, так что убрал её из строчки и переименовал в delay
.code.js
var delay = 64;
var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var n = setInterval(draw, delay);
Далее,
var draw
был просто строкой, которая исполнялась как функция eval
с периодичностью setInterval, поскольку setInterval может принимать и функции, и строки. Я перенёс var draw
в явную функцию, но сохранил изначальную строку для справки на всякий случай.Ещё я заметил, что элемент
p
в действительности ссылался на элемент DOM с идентификатором p
, объявленным в HTML, который я недавно закавычил. Оказывается, на элементы в JavaScript можно ссылаться по их идентификатору, если id состоит только из букв и цифр. Я добавил document.getElementById("p")
, чтобы сделать код понятнее.var delay = 64;
var p = document.getElementById("p"); // < --------------
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
for (n += 7, i = delay, P = 'p.\n'; i -= 1 / delay; P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2]) {
j = delay / i; p.innerHTML = P;
}
};
var n = setInterval(draw, delay);
Затем я объявил переменные
i
, p
и j
и перенёс их в начало функции.var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay; // < ---------------
var P ='p.\n';
var j;
for (n += 7; i > 0 ;P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2]) {
j = delay / i; p.innerHTML = P;
i -= 1 / delay;
}
};
var n = setInterval(draw, delay);
Я разложил цикл
for
и преобразовал его в цикл while
. Из трёх частей прежнего for
осталась только одна часть CHECK_EVERY_LOOP, а всё остальное (RUNS_ONCE_ON_INIT; DO_EVERY_LOOP) перенёс за пределы цикла.var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) { // <----------------------
//Update HTML
p.innerHTML = P;
j = delay / i;
i -= 1 / delay;
P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2];
}
};
var n = setInterval(draw, delay);
Здесь я развернул тернарный оператор
( condition ? do if true : do if false) in P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2];
.i%2
проверяет, является переменная i
чётной или нечётной. Если она четная, то просто возвращает 2. Если нечётная, то возвращает «магическое» значение magic (i % 2 * j - j + n / delay ^ j) & 1;
(подробнее об этом чуть позже).Это значение (индекс) используется для сдвига строки P, так что назовём
index
и превратим строку в P += P[index];
.var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;
j = delay / i;
i -= 1 / delay;
let index;
let iIsOdd = (i % 2 != 0); // <---------------
if (iIsOdd) { // <---------------
index = (i % 2 * j - j + n / delay ^ j) & 1;
} else {
index = 2;
}
P += P[index];
}
};
var n = setInterval(draw, delay);
Я разложил
& 1
из значения index = (i % 2 * j - j + n / delay ^ j) & 1
в ещё один оператор if
.Здесь хитрый способ проверки на чётность результата в круглых скобках, когда для чётного значения возвращается 0, а для нечётного — 1.
&
— это побитовый оператор AND. Он работает так:- 1 & 1 = 1
- 0 & 1 = 0
Следовательно,
something & 1
преобразует "something" в двоичное представление, а также добивает перед единицей необходимое количество нулей, чтобы соответствовать размеру "something", и возвращает просто результат AND последнего бита. Например, 5 в двоичном формате равняется 101
, так что если мы применим на ней логическую операцию AND с единицей, то получится следующее: 101
AND 001
001
Другими словами, пятёрка — нечётное число, а результатом 5 AND 1 (5 & 1) будет 1. В консоли JavaScript легко проверить соблюдение этой логики.
0 & 1 // 0 - even return 0
1 & 1 // 1 - odd return 1
2 & 1 // 0 - even return 0
3 & 1 // 1 - odd return 1
4 & 1 // 0 - even return 0
5 & 1 // 1 - odd return 1
Обратите внимание, что я также переименовал остальную часть
index
в magic
, так что код с развёрнутым &1
будет выглядеть следующим образом:var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;
j = delay / i;
i -= 1 / delay;
let index;
let iIsOdd = (i % 2 != 0);
if (iIsOdd) {
let magic = (i % 2 * j - j + n / delay ^ j);
let magicIsOdd = (magic % 2 != 0); // &1 < --------------------------
if (magicIsOdd) { // &1 <--------------------------
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}
P += P[index];
}
};
var n = setInterval(draw, delay);
Далее я развернул
P += P[index];
в оператор switch
. К этому моменту стало понятно, что index
может принимать только одно из трёх значений — 0, 1 или 2. Также понятно, что переменная P
всегда инициализируется со значениями var P ='p.\n';
, где 0 указывает на p
, 1 указывает на .
, а 2 указывает на \n
— символ новой строкиvar delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;
j = delay / i;
i -= 1 / delay;
let index;
let iIsOdd = (i % 2 != 0);
if (iIsOdd) {
let magic = (i % 2 * j - j + n / delay ^ j);
let magicIsOdd = (magic % 2 != 0); // &1
if (magicIsOdd) { // &1
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}
switch (index) { // P += P[index]; <-----------------------
case 0:
P += "p"; // aka P[0]
break;
case 1:
P += "."; // aka P[1]
break;
case 2:
P += "\n"; // aka P[2]
}
}
};
var n = setInterval(draw, delay);
Я разобрался с оператором
var n = setInterval(draw, delay);
. Метод setInterval возвращает целые числа, начиная с единицы, увеличивая значение при каждом вызове. Это целое число может использоваться для clearInterval (то есть для отмены). В нашем случае setInterval вызывается всего один раз, а переменная n
просто установилась в значение 1.Я также переименовал
delay
в DELAY
для напоминания, что это всего лишь константа.И последнее, но не менее важное, я поместил круглые скобки в
i % 2 * j - j + n / DELAY ^ j
для указания, что у ^
(побитового XOR) меньший приоритет, чем у операторов %
, *
, ?
, +
и /
. Другими словами, сначала выполнятся все вышеупомянутые вычисления, а уже потом ^
. То есть получается (i % 2 * j - j + n / DELAY) ^ j)
.Уточнение: Мне указали, что я ошибочно поместил
p.innerHTML = P; //Update HTML
в цикл, так что я убрал его оттуда.const DELAY = 64; // approximately 15 frames per second 15 frames per second * 64 seconds = 960 frames
var n = 1;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
/**
* Draws a picture
* 128 chars by 32 chars = total 4096 chars
*/
var draw = function() {
var i = DELAY; // 64
var P ='p.\n'; // First line, reference for chars to use
var j;
n += 7;
while (i > 0) {
j = DELAY / i;
i -= 1 / DELAY;
let index;
let iIsOdd = (i % 2 != 0);
if (iIsOdd) {
let magic = ((i % 2 * j - j + n / DELAY) ^ j); // < ------------------
let magicIsOdd = (magic % 2 != 0); // &1
if (magicIsOdd) { // &1
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}
switch (index) { // P += P[index];
case 0:
P += "p"; // aka P[0]
break;
case 1:
P += "."; // aka P[1]
break;
case 2:
P += "\n"; // aka P[2]
}
}
//Update HTML
p.innerHTML = P;
};
setInterval(draw, 64);
Окончательный результат выполнения можно увидеть здесь.
Часть 2. Понимание кода
Так что здесь происходит? Давайте разберёмся.
Изначально значение
i
установлено на 64 посредством var i = DELAY;
, а затем каждый цикл оно уменьшается на 1/64 (0,015625) через i -= 1 / DELAY;
. Цикл продолжается, пока i
больше нуля (код while (i > 0) {
). Поскольку за каждый проход i
уменьшается на 1/64, то требуется 64 цикла, прежде чем оно уменьшится на единицу (64/64 = 1). В целом уменьшение i
произойдёт 64?64 = 4096 раз, чтобы уменьшиться до нуля.Изображение состоит из 32 строк, со 128 символами в каждой. Очень удобно, что 64 ? 64 = 32 ?128 = 4096. Значение
i
может быть чётным (не нечётным let iIsOdd = (i % 2 != 0);
), если i
является строго чётным числом. Такое произойдёт 32 раза, когда оно равняется 64, 62, 60 и т. д. Эти 32 раза index
примет значение 2 index = 2;
, а к строке добавится символ новой строки: P += "\n"; // aka P[2]
. Остальные 127 символов в строке примут значения p
или .
.Но когда устанавливать
p
, а когда .
?Ну, для начала нам точно известно, что следует установить
.
при нечётном значении let magic = ((i % 2 * j - j + n / DELAY) ^ j);
, или установить p
, если «магия» чётная.var P ='p.\n';
...
if (magicIsOdd) { // &1
index = 1; // second char in P - .
} else {
index = 0; // first char in P - p
}
Но когда
magic
чётное, а когда нечётное? Это вопрос на миллион долларов. Перед тем как перейти к нему, давайте определим ещё кое-что.Если убрать
+ n/DELAY
из let magic = ((i % 2 * j - j + n / DELAY) ^ j);
, то получится статическая картинка, на которой вообще ничего не двигается:![](https://habrastorage.org/getpro/habr/post_images/3cf/20f/8fa/3cf20f8fa0be07b8816672ea816ddf21.png)
Теперь посмотрим на
magic
без + n/DELAY
. Как получилась эта красивая картинка?(i % 2 * j - j) ^ j
Обратите внимание, что получается в каждом цикле:
j = DELAY / i;
i -= 1 / DELAY;
Другими словами, мы может выразить
j
через конечное i
как j = DELAY/ (i + 1/DELAY)
. Но поскольку 1/DELAY слишком малое число, то для этого примера можно отбросить + 1/DELAY
и упростить выражение до j = DELAY/i = 64/i
.В таком случае мы можем переписать
(i % 2 * j - j) ^ j
как i % 2 * 64/i - 64/i) ^ 64/i
.Используем онлайновый графический калькулятор для отрисовки графиков некоторых из этих функций.
Прежде всего, отрисуем
i%2
.Выходит симпатичный график со значениями y от 0 до 2.
![](https://habrastorage.org/getpro/habr/post_images/8d7/f6c/2db/8d7f6c2db6c533fad32cfe3cf199d7a6.png)
Если отрисовать
64/i
, то получим такой график:![](https://habrastorage.org/getpro/habr/post_images/c36/4bd/d35/c364bdd35185f6ca36dc66918c4deb39.png)
Если отрисовать всю левую сторону выражения, то получится график, который выглядит как сочетание двух предыдущих.
![](https://habrastorage.org/getpro/habr/post_images/c7b/677/692/c7b677692270a570eb2b2e2dc1c577aa.png)
В конце концов, если мы отрисуем две функции рядом друг с другом, то увидим следующее.
![](https://habrastorage.org/getpro/habr/post_images/323/cc4/599/323cc4599c40a0f4913130b970aa8672.png)
О чём говорят эти графики?
Давайте припомним вопрос, на который мы пытаемся ответить, то есть каким образом получилась такая красивая статическая картинка:
![](https://habrastorage.org/getpro/habr/post_images/3cf/20f/8fa/3cf20f8fa0be07b8816672ea816ddf21.png)
Мы знаем, что если «магия»
(i % 2 * j - j) ^ j
принимает чётное значение, то нужно добавить p
, а для нечётного числа нужно добавить .
.Увеличим первые 16 строк нашего графика, где
i
имеет значения от 64 до 32.![](https://habrastorage.org/getpro/habr/post_images/365/840/8a5/3658408a5bca2a1248b55e25cbe42411.png)
Побитовый XOR в JavaScript отбросит все значения справа от запятой, так что это равнозначно применению метода
Math.floor
, который округляет число в меньшую сторону.Он вернёт 0, если оба бита равны 1 или оба равны 0.
Наша
j
начинается с единицы и медленно продвигается к двойке, останавливаясь прямо около неё, так что можем считать её всегда единицей при округлении в меньшую сторону (Math.floor(1.9999) === 1
), и нам нужна ещё одна единица с левой стороны, чтобы получить в результате ноль и дать нам p
.Другими словами, каждая зелёная диагональ представляет собой один ряд в нашем графике. Поскольку для первых 16 рядов значение j всегда больше 1, но меньше 2, то мы можем получить нечётное значение только в том случае, если левая сторона выражения
(i % 2 * j - j) ^ j
, она же i % 2 * i/64 — i/64
, то есть зелёная диагональ, тоже будет выше 1 или ниже ?1.Вот некоторые результаты из консоли JavaScript, чтобы посмотреть результаты вычислений: 0 или ?2 означают, что результат чётный, а 1 соответствует нечётному числу.
1 ^ 1 // 0 - even p
1.1 ^ 1.1 // 0 - even p
0.9 ^ 1 // 1 - odd .
0 ^ 1 // 1 - odd .
-1 ^ 1 // -2 - even p
-1.1 ^ 1.1 // -2 - even p
Если посмотреть на наш график, то там самая правая диагональная линия едва выходит выше 1 и ниже ?1 (мало чётных чисел — мало символов
p
). Следующая выходит чуть дальше за эти границы, третья — ещё чуть дальше и т. д. Линия номер 16 едва удерживается в границах между 2 и ?2. После линии 16 мы видим, что наш статический график меняет свой характер.![](https://habrastorage.org/getpro/habr/post_images/d77/ae6/4e8/d77ae64e8aba2174921af344da5b50a7.png)
После 16-й строки значение
j
пересекает лимит 2, так что меняется ожидаемый результат. Теперь мы получим чётное число, если зелёная диагональная линия выше 2 или ниже ?2, или внутри рамок 1 и ?1, но не соприкасается с ними. Вот почему мы видим на картинке две или больше групп символов p
начиная с 17-й строки.Если присмотреться к нескольким самым нижним линиям в анимированной картинке, то вы заметите, что они не следуют одному и тому же шаблону из-за большой флуктуации графика.
Теперь вернёмся к
+ n/DELAY
. В коде мы видим, что значение n
начинается с 8 (1 от setInteval и плюс 7 на каждый вызов метода). Затем оно увеличивается на 7 при каждом срабатывании setInteval. После достижения значения 64 график изменяется следующим образом.
![](https://habrastorage.org/getpro/habr/post_images/9da/b80/7db/9dab807dbf5f8e0ef5cdef4c4b3025ab.png)
Обратите внимание, что
j
по-прежнему находится около единицы, но теперь левая половина красной диагонали в пределах примерно 62-63 находится примерно около нуля, а правая половина в пределах примерно 63-64 — около единицы. Поскольку наши символы появляются в убывающем порядке от 64 к 62, то можно ожидать, что правая половина диагонали в районе 63-64 (1 ^ 1 = 0 // even) добавит кучку символов p
, а левая половина диагонали в районе 62-63 (1 ^ 0 = 1 // odd) добавит кучку точек. Всё это будет нарастать слева направо, как обычный текст.Рендеринг HTML для такого условия выглядит следующим образом (вы можете жёстко вбить значение
n
в редакторе CodePen и посмотреть). Это совпадает с нашими ожиданиями.![](https://habrastorage.org/getpro/habr/post_images/80e/05a/865/80e05a865983e43d7fe7678a0120bdb6.png)
К этому моменту количество символов
p
выросло до постоянной величины. Например, в первом ряду половина всех значений всегда будут чётными. Теперь символы p
и .
будут только меняться местами.Для примера, когда
n
увеличивается на 7 на следующем вызове setInterval, график немного изменится.![](https://habrastorage.org/getpro/habr/post_images/ec2/d0e/87d/ec2d0e87df529c6a949ebe5de5ef3daa.png)
Обратите внимание, что диагональ для первого ряда (около отметки 64) сдвинулась примерно на один маленький квадратик вверх. Поскольку четыре больших квадратов представляют собой 128 символов, в одном большом квадрате будет 32 символа, а в одном маленьком квадрате 32/5 = 6,4 сивола (примерно). Если посмотрим на рендеринг HTML, то там первый ряд действительно сдвинулся вправо на 7 символов.
![](https://habrastorage.org/getpro/habr/post_images/eb1/1f1/5b7/eb11f15b78c6b11938ebdc48ba766e12.png)
И один последний пример. Вот что происходит, если вызвать setInterval ещё семь раз, а
n
будет равняться 64+9?7.![](https://habrastorage.org/getpro/habr/post_images/e22/6e2/e95/e226e2e95789f61e8cbac690381820c1.png)
Для первого ряда
j
по-прежнему равняется 1. Теперь верхняя половина красной диагонали около отметки 64 примерно упирается в два, а нижний конец около единицы. Это переворачивает картинку в другую сторону, поскольку теперь 1^2 = 3 // odd - .
и 1 ^ 1 = 0 //even - p
. Так что можно ожидать кучу точек, за которыми пойдут символы p
.Выглядеть это будет так.
![](https://habrastorage.org/getpro/habr/post_images/960/c6e/60d/960c6e60d109d03971e019bfc0feb4c5.png)
График бесконечно зациклен.
Надеюсь, наша работа имеет какой-то смысл. Вряд ли я когда-нибудь смог бы самостоятельно придумать нечто подобное, но было интересно разобраться в этом коде.
Поделиться с друзьями
pm_wanderer
Очень круто.
Вспомнил assembler и вирус на байткоде, размером в 21 байт.
Victor_Grigoryev
Программа из одной строчки на Perl также входит в золотой фонд «Краткость — сестра таланта».
mwizard
это та, которая патч Бармина?
nckma
Но разве сейчас не в моде на первом месте читаемость кода?
TheShock
Увы, сейчас в моде «модность», а не «читаемость». Пишут не кратко и многословно, при этом — максимально непонятно, чтобы как можно больше усложнить вход новичкам, а также поддержку кода.
kloppspb
Не все такие идиоты. А разбираться приходится…
Dmitry_5
Если бы было так, то писали бы на паскале, а не джавах. Ну и ошибок на три порядка меньше в коде было бы
Astrohas
эта ли программа?
Cyberneticist
дык уже не работает — надо /* или ключ --no-preserve-root прописывать
Dmitry88
mafia8
Откуда картинка?
Dark_Daiver
Вероятно из фильма «Прибытие» (2016)
old_bear
Прибытие
AllexIn
Если вы хоть чуть-чуть интересовались темой, «как составить понятное послание инопланетному разуму», также если вы лингвист или хоть что-то знаете о математике — не смотрите. Будете весь фильм фейспалмить на моментах типа тыкания в себя рукой или писанины руками имён…
bjornd
Если вы специалист в робототехнике, не смотрите «Матрица»
Если вы физик-теоретик, не смотрите «Интерстеллар»
Если вы конструируете космические корабли, не смотрите «Звездные войны»
Если вы работаете в ФБР, не смотрите «Семь» и «Молчание ягнят»
Если вы сидели в немецком концлагере, не смотрите «Жизнь прекрасна»
Если вы участвовали в высадке в Нормандии, не смотрите «Спаси рядового Райана»
Если вы сидели в тюрьме, не смотрите «Побег из Шоушенка»
Если вы чванливый эксперт, вообще держитесь подальше от массовой культуры
AllexIn
Не совсем. Я, например, без пробелм смотрю пердежные комедии.
Ну я жду что будет пердежная комедия, её и получаю.
Прибытие везде называли лучшим НФ фильмом 2016, фильм который берет не спецэффектами, а мыслью, фильм, который показывает работу лингвиста такой, какая она есть. Я ждал, что это будет хотя бы чуть-чуть экранизация хотя бы послания Аресибо.
Понятно, что есть критика Аресибо, есть более новые и актуальные работы. Но ок, Аресибо тоже сойдет. А в итоге? В итоге фильм просто плюет в лицо всем, кто хотя бы думал об универсальном общении.
Я не говорю про специалистов. Я сам не специалист, ни в математики, ни в лингвистике, и универсальным общением интересуюсь чисто из любопытства. Но даже мне хватает знаний чтобы фейспалмить.
Серьезно. Она показала табличку, и получила фидбек от пришельцев. Всё. Более нам никакой активности с её стороны не показывают. И это объявляют самым крутым достижением с начала изучения пришельцев.
bjornd
Больше вопросов не имею
RussDragon
Поздравляю, вы совершенно не поняли посыл фильм/книги. Не знаю, кто и что писал про него, но Прибытие – экранизация «История твоей жизни», НФ повести под авторством Теда Чана.
Главная идея – язык формирует мышление и мировозрение. Что, собственно, и показывается на протяжении всего фильма.
AllexIn
Да как её не понять, когда там прямым текстом в середине фильма «они думают подругому, одновременно зная и начало и конец фразы» и «их дар — это язык, он меняет способ мышления».
Но эта идея во-первых — достаточно серьезно критикуется. Особенно на фоне того, что легко перепутать причину и следствие.
А во-вторых — идея идеей, а история рассказана очень плохо, с игнорированием всех источников и опыта по теме универсального языка.
vba
А я вроде слышал что как раз эпизод с высадкой на "американском" пляже не так уж и далек от реальности, в отличии последующего сюжета.
Я бы тут скорее написал: Если вы участвовали в обороне Москвы в 1941, не смотрите «12 Панфиловцев»
bjornd
В одной только статье о фильме на Wikipedia три абзаца исторических неточностей, а представляете сколько их может найти хорошо-подготовленный эксперт? Нет никакого смысла требовать от художественного произведения следования точным историческим и научным фактам, для этого есть документалистика. А вольность обращения с фактами зачастую просто необходима авторам для реализации художественного замысла, хотя еще чаще это приходится делать, чтобы адаптировать произведение для массового рынка. Например в первоначальном сценарии Матрицы машины использовали тела людей для построения вычислительной нейронной сети, но позже для облегчения восприятия зрителем машины стали использовать людей в качестве источника энергии, хоть эта идея и не выдерживает никакой критики с научной точки зрения.
kloppspb
Это было шикарно обыграно в ТБВ. Впрочем, всё было в Симпсонах…
JackVlg
8 сезон, 21 серия :)
marcor
> каждая зелёная диагональ представляет собой один ряд в нашем графике
То ли у меня они отображаются как синие, то ли я не на то смотрю.
Spiritschaser
Хех. Perl тоже был повсюду и на нём писали однострочники.
Надеюсь, JS/ES также закончит.
Alter2
Неплохо бы иметь упаковщик который код в такие строчки сжимает.
OneFive
Есть такой замечательный сайт как https://www.dwitter.net/top анимация на js в 140 символов.
CarambaPirat
А где почитать спецификации как js ссылается на DOM элемент по его id? Быстрый поиск не дал результатов. Может кто знает.
Очень любопытный код. Распарсил в кодепен и в голове. Принцип ясен, но главная функция — это надо было придумать, конечно!
Odrin
Если не ошибаюсь, изначально доступ по id элемента сделали в IE, потом другие браузеры переняли эту практику, а позднее это было внесено в спецификацию.
vmir88
После этой одной строчки текста мне резко захотелось закрыть вкладку и прочитать оригинал. Извините.
wd39
Работаю с Mathieu, он действительно классный специалист и делает потрясающие демки!
Odrin
Это не так, id может быть любым и ссылка на DOM элемент доступна через window['any-id'].