Как Превратить Одну Крутую Хрень В Еще Более Крутую Хрень Или Функциональный Язычок На Коленке

«Бросая камешки в воду, смотрите на круги, которые они образуют; иначе такие метания будут пустой забавой».

К.

Прутков Однажды, бесцельно тратя время и деньги своего работодателя на серфинг в Интернете, я наткнулся на описание языка В любое время и на какое-то время был очарован.

Язык поражает своей безумной простотой.

Его принципы: 1) Строки программного кода когда-нибудь обязательно будут выполнены, но порядок их выполнения не имеет ничего общего с порядком, в котором они написаны.

2) Переменные? Мы даже не контролируем порядок исполнения; нам не нужны никакие переменные.

3) Структуры данных? Ты шутишь! То есть программа рассматривается как набор (пул) строк, подлежащих исполнению, а интерпретатор выбирает оттуда случайным образом строку, выполняет ее команды и выбрасывает ее из пула.

И так до тех пор, пока в бассейне ничего не останется.

Надо признать, что автор этого безумия практически придерживался концепции.

Почти, потому что порядок выполнения в программе еще можно организовать, так же, как можно создавать переменные, используя возможность добавления строк в пул исполняемых файлов.

Итак, в языке существуют следующие конструкции: Строка кода добрый

  
  
  
  
  
  
  
   

line-number statement;

Выражения состоят из команд, разделенных запятыми.

Среди них: Номер строки

line-number#number-of-times-to-add/remove

Например, команда в строке 1

1 4,5#3,-6;

Добавит 1 строку с номером 4, 3 строки с номером 5 в пул выполнения и удалит строку с номером 6 из пула.

Обратите внимание, что программа с бесконечным циклом будет выглядеть лаконично и, следовательно, гениально.



1 1;

Интерпретатор выберет из пула первую строку (другой строки там нет) и добавит ее обратно в пул.

Кстати, очень полезная конструкция, как будет видно далее.

Вывод на дисплей Просто команда печати, которая выводит на экран то, что указано в скобках.

Помимо этих команд существуют следующие конструкции: отложить Если выражение в скобках возвращает true, команда не будет выполнена, а строка будет возвращена обратно в пул.

Например

1 defer (2) 3;

Пока строка 2 присутствует в пуле, команда добавления строки 3 в пул не будет выполнена.

снова Если выражение в скобках возвращает true, команда будет выполнена, но строка будет возвращена в пул.

Например:

1 again (2) 3;

Строка 3 всегда будет добавляться в пул, но если в пуле есть строка 2, то строка 1 после этого не будет удалена из пула.

Обратите внимание, что выражение (2) эквивалентно ( N(2) > 0 ), где N() — функция, возвращающая количество строк номеров вызовов в пуле.

забывать Если выражение в скобках возвращает true, команда не будет выполнена, а строка будет удалена из пула.

Например:

1 forget(2) 3;

Автор языка удалил это выражение из языка, но мне пришлось его вернуть, как описано ниже.

Опять же, отложить и забыть можно комбинировать, но каждый модификатор должен появляться в строке только один раз.

Собственно всё, весь язык.

На сайте есть две программы с описанием языка: 99 бутылок пива

1 defer (4 || N(1)<N(2) && N(2)<N(3)) print(N(1)+" bottles of beer on the wall, "+N(1)+" bottles of beer,"); 2 defer (4 || N(1)==N(2)) print("Take one down and pass it around,"); 3 defer (4 || N(2)==N(3)) print(N(1)+" bottles of beer on the wall."); 4 1#98,2#98,3#98;

и Числа Фибоначчи (вычисляет ряд Фибоначчи от 1-го до 100-го элемента.

ну.

если подождать):

1 again (1) defer (3 || N(1)<=N(2) || N(7)>99) 2#N(1),3,7; 2 again (2) defer (3 || N(2)<=N(1) || N(7)>99) 1#N(2),3,7; 3 defer (5) print(N(1)+N(2)); 4 defer (5) print("1"); 5 4,-3,7; 6 defer (4) 3; 7 7; 8 defer (N(7)<100) -1#N(1),-2#N(2),-7#100,-3; 9 defer (3 || 6) 1,3;

Если первая программа — чистое баловство, то вторая делает что-то значимое, и это удивительно.

Казалось бы, ничего хорошего из такого баловства выйти не может по определению.

Однако вот оно.

И поэтому мне хотелось что-то со всем этим сделать.

Результат оказался несколько неожиданным, но обо всём по порядку.

Сразу будет очевидно, что эти программы неудобны: чтобы вычислить ряд Фибоначчи не до сотого, а, скажем, до 20-го элемента, нужно изменить текст в четырех местах программы.

Это немного раздражает. Давайте удостоверимся, что программа может принимать входные параметры.

Например вот так:

1 again (1) defer (3 || N(1)<=N(2) || N(7)>( @1 - 1 )) 2#N(1),3,7; 2 again (2) defer (3 || N(2)<=N(1) || N(7)>( @1 - 1 )) 1#N(2),3,7; 3 defer (5) print(N(1)+N(2)); 4 defer (5) print("1"); 5 4,-3,7; 6 defer (4) 3; 7 7; 8 defer (N(7)<@1) -1#N(1),-2#N(2),-7#100,-3; 9 defer (3 || 6) 1,3;

Сохраним текст программы в файл fib.src. Вызов программы будет выглядеть так:

java -cp Whenever.jar Whenever fib.src 20

Уже хорошо.

Чтобы закрепить свой успех, я написал программу по поиску наибольшего общего делителя двух натуральных чисел, вот она:

1 defer(2) again(N(3) - (N(3)/N(4))*N(4) != 0) 6#N(3),-3#N(3),3#N(4),-4#N(4),4#(N(6) - (N(6)/N(3))*N(3)),-6#N(6); 2 3#( @1 - 1 ),4#( @2 - 1 ),-6; 3 3; 4 4; 5 defer(1) print("NOD of " + @1 + " and " + @2 + " is " + N(4)),-3#N(3),-4#N(4); 6 6;

При вызове программы нужно указать два числа, сначала большее, затем меньшее.

Давайте проанализируем эту программу; Строки 3 и 4 используются как ячейки памяти для хранения двух чисел.

Точнее, в качестве переменных используется количество вхождений соответствующих строк в пул.

Строка 6 используется в качестве промежуточной переменной таким же извращенным образом (другого пути язык не оставляет).

Строка 2 запускает весь процесс.

Он добавляет в пул N строк 3 и M строк 4 и удаляет из пула строку 6. После чего он удаляется из пула навсегда.

Строка 1 фактически выполняет всю работу.

Выполнение строки 1 откладывается до тех пор, пока строка 2 не появится в пуле (defer(2)).

Кроме того, строка НЕ выбрасывается из пула до тех пор, пока не будет найден НОД (снова).

Выражение в скобках даст остаток от числа строк 3, разделенного на количество строк 4 (используется целочисленная математика).

Фактические действия в строке 1 выполняются слева направо: В пул добавляется столько 6 строк, сколько в пуле на этом этапе имеется 3 строки.

Все строки 3 удалены из пула Добавляется столько строк, сколько на данный момент имеется 4 строк (эти две команды можно объединить в одну -3#(N(3) - N(4)) Удалить все строки 4 Добавляем 4 строки в сумме == остаток от деления большего числа на меньшее.

Опять же, эти две команды можно объединить в одну.

Удалить все 6 строк из пула (скрипач не нужен) Строка 5 печатает результат и очищает за собой все, что осталось из пула.

Его выполнение задерживается до тех пор, пока строка 1 присутствует в пуле, и она останется там до тех пор, пока не будет найден НОД.

Замечу, что в оригинальной версии языка печать нельзя совмещать с другими командами; там надо было бы написать

5 defer(1) print("NOD of " + @1 + " and " + @2 + " is " + N(4)); .

7 defer(5) -3#N(3),-4#N(4);

Да, я подумал.

Сделаем так, чтобы при добавлении строки в пул ей можно было передавать параметры.

Поскольку в синтаксисе уже используются круглые скобки и запятые и мне не хотелось слишком всё переписывать, я использовал квадратные скобки и знак процента % для разделителя параметров.

Здесь возникает одна тонкость.

При добавлении строки в пул он должен фактически создавать еще одну строку с новым номером, а не увеличивать количество выполнений для существующей.

(На самом деле для «чистоты» необходимо увеличить количество исполнений строк с одинаковыми параметрами, но я это пока не реализовал).

Здесь у меня вдруг появилось еще одно измерение, еще одна переменная — номер текущей строки.

Чтобы иметь возможность его использовать, я ввел еще одно ключевое слово — self. Ура! Теперь мы можем сделать что-то полезное.

Например, перепишем тот же ряд Фибоначчи:

1 forget(self>@3) print("iter: " + self + " value: " + @1 ),1[@1+@2%@1%@3];

В программу передаются три параметра – 1, 0 и номер числа, до которого необходимо произвести расчет. К счастью, важен только третий параметр, и программу можно переписать так, чтобы она принимала только его.

Тогда, кстати, от себя можно будет избавиться, но программа жутко разрастется до двух целых строк, а это не очень приятно.

Примечания: Мне пришлось 1) восстановить права на забвение и 2) убедиться, что печать выполнялась вместе с другими командами (в оригинале могли быть только команды печати или другие).

А вот НОД:

1 forget( @2 == 0 ) 1[@2%(@1 - (@1 / @2) * @2)],2[@2%(@1 - (@1 / @2) * @2)]; 2 forget( @2 > 0 ) print("Nod is: " + @1 );

Опять же, можно сделать выполнение независимым от номера большего параметра, но мне лень.

И для начала проверьте, является ли число простым:

1 defer(5) forget( @2 == 0 || @2 == 1 ) 4[@1%(@2 - 1)],2[@1%(@2 - 1)],3[@1%(@2 - 1)]; 2 defer(5) forget( @2 == 0 || @1 != (@1 / @2) * @2 || @1 == @2 || @2 == 1 ) print( "Number " + @1 + " is not prime"); 3 defer(5) forget( @2 != 1 ) print( "Number " + @1 + " is prime"); 4 defer(5) forget( @2 == 0 || @1 == (@1 / @2) * @2 ) 1[@1%@2]; 5 1[@1%@1];

Здесь я использую еще один прием: если параметр не указан, он становится нулевым.

Это пирожки с котятами.

Что-то мне подсказывает, что я могу попробовать решить комбинаторные задачи, используя эту фигню.

Но сейчас мне лень придумывать решения.

Хотя… Теги: #программирование #Функциональное программирование #когда угодно #Аномальное программирование #Алгоритмы

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.