Пост посвящается программистам, которые отфренживают, руководствуясь мотивами вида "АААА ПАНИКА ПАНИКА ТЕПЕРЬ ПЛАХОВ ВСЕ ВРЕМЯ БУДЕТ ПИСАТЬ О вставьте название дисциплины, которая кажется им скучной".
По мотивам записей
fregimus'а и
timur0 (
1,
2).
Под катом - программа на самом крутом в мире эзотерическом чисто функциональном языке программирования. Решает числовые ребусы (демонстрация на одном конкретном примере, "УДАР + УДАР = ДРАКА").
На С++. В compile-time.
#include
struct EmptyList {
};
template
struct IntList {
static const int Head = N;
typedef T Tail;
};
#define LIST1(N1) IntList
#define LIST2(N1,N2) IntList
#define LIST3(N1,N2,N3) IntList
#define LIST4(N1,N2,N3,N4) IntList
#define LIST5(N1,N2,N3,N4,N5) IntList
#define NUM2(x,y) 10*(x) + (y)
#define NUM3(x,y,z) 100*(x) + 10*(y) + (z)
#define NUM4(w,x,y,z) 1000*(w) + 100*(x) + 10*(y) + (z)
#define NUM5(v,w,x,y,z) 10000*(v) + 1000*(w) + 100*(x) + 10*(y) + (z)
#define DIFFER3(x,y,z) ((x) != (y) && (x) != (z) && (y) != (z))
#define DIFFER4(x,y,z,w) ((x) != (y) && (x) != (z) && (x) != (w) && DIFFER3(y,z,w))
#define DIFFER5(x,y,z,w,v) ((x) != (y) && (x) != (z) && (x) != (w) && (x) != (v) && DIFFER4(y,z,w,v))
template struct LSolve {
static const int Answer0 = LSolve >::Answer;
static const int Answer1 = LSolve >::Answer;
static const int Answer2 = LSolve >::Answer;
static const int Answer3 = LSolve >::Answer;
static const int Answer4 = LSolve >::Answer;
static const int Answer5 = LSolve >::Answer;
static const int Answer6 = LSolve >::Answer;
static const int Answer7 = LSolve >::Answer;
static const int Answer8 = LSolve >::Answer;
static const int Answer9 = LSolve >::Answer;
static const int Answer = Answer0 + Answer1 + Answer2 + Answer3 + Answer4 + Answer5 + Answer6 + Answer7 + Answer8 + Answer9;
};
template struct LSolve {
static const int Answer =
NUM4(U,D,A,R) + NUM4(U,D,A,R) == NUM5(D,R,A,K,A) && (U != 0) && (D != 0) && DIFFER5(U,D,A,R,K) ?
NUM5(D,U,R,A,K) : 0;
};
typedef LSolve Solve;
int main(int argc, const char* argv[]) {
printf("ДУРАК=%d\n", Solve::Answer);
}
Условия самого ребуса расположены в строчке номер 42, что символизирует.
Четвертый gcc компилирует данный brainfuck на ура, msvc бесконечно долго думает, после чего рожает internal compiler error. Неудачник.
В процессе написания подобной программы быдлокодер вроде меня за какой-то час может получить кучу дополнительного понимания того, зачем на самом деле нужна хвостовая рекурсия, где лучше ленивые вычисления, а где "трудолюбивые", чем компиляторы отличаются друг от друга, почему темплейты все-таки не полноценный ФЯП, хоть и Тьюринг-полны, и т.д. и т.п.
Естественно, код можно улучшать дальше хоть до бесконечности; например, отказаться от макросов NUMx и DIFFERx, заменив их на темплейты, специфицированные для IntList'ов; убрать десятикратное копирование в середине; обобщить до случая, когда решение не единственно, сделав тип Answer'а не int'ом, а IntList'ом, и т.д. и т.п.
Лично я этим заниматься не буду. Потому что эта программа - просто аналог красивых картинок в постах ибигданов и фрицморгенов, попытка заинтересовать, не больше. Нет, она работает, тут всё честно. И написать её было не совсем уж тривиально (попробуйте сами, так, чтобы собиралось хотя бы одним распространенным компилятором и за разумное время; подводные камни есть). Но я намеренно выбрал максимально бесполезную задачу и абсурдный способ её решения, и если вы этого не почувствовали, значит, остаток поста - про вас.
Мы все занимаемся изготовлением черных ящиков. Известно, что у них должно быть на входе и на выходе, весь хаос реального мира остаётся где-то снаружи. Нам остаётся улучшать эти чёрные ящики, делать их более изящными и красивыми. Да?
Конечно, нет. Интерфейсы никто не зафиксирует за нас; тот, кто будет пытаться - явный
pointy-haired boss. Большая часть усилий, которые мы тратим на совершенствование техник программирования, на изучение новых языков и технологий, на освоение новых трюков, пропадает впустую, если цель их не в том, чтобы научиться делать новые черные ящики, с новыми свойствами. Совершенствуя старые, вы лишь усиливаете их зависимости от статичности интерфейса, и делаете все более хрупкими.
Старайтесь тратить хотя бы половину тех усилий, которые вы тратите на изучение теории категорий, языков семейства ML, или тонкостей оптимизации под ghc, на что-нибудь, что расположено вне ваших черных ящиков. Мой пример можно переписать на хаскеле, и он станет красивее, быстрее и понятнее; но от этого в алгоритме не появится backtracking, он не превратится в хороший constraint solver, а вы не узнаете ни байта о том, зачем они применяются, и какими должны быть на самом деле.
Сколько-нибудь талантливый человек рано или поздно встает перед выбором: тратить творческую энергию на содержательные вещи, или на игру в бисер. Мне хотелось показать, что любое безумие, любую оторванную от реальности чушь можно превратить в красивую головоломку, любую дурь - в увлекательное исследование. Надеюсь, получилось.