В прошлом я написал несколько компиляторов Пролога. И очень хотелось написать транслятор Пролога на Си, но это было невозможно, потому что в Прологе программа выполняется весьма необычным способом. Вызов функции может привести к многократному вызову функций по коду ниже. (Правильнее говорить предикат, а не функция). Например, в самом простом случае.
Первая строка выполнит многократный вызов второй строки. (Это зависит от компилятора, но предположим самый глупый компилятор). И на Си написать аналог этого примера было сложно. Конечно возможно, но машинный код был понятнее.
Вообще, в Прологе порядок строк может быть любой и на результат это не влияет. Каждая строка порождает множество вариантов и все они объединяются по И. Программа выше звучит как "Все что является клиентом"и "Всё что больше 18 лет".
Лямбда функции, появившиеся недавно в C++, позволяют красиво, то есть без хаков, представить на С++ программу на Прологе. Возьмем пример из Википеди (мне лень искать что то более интересное). Программа поиска общего делителя для массива чисел:
На C++ с Лямбдами это будет выглядеть так:
В принципе, можно компилировать с Пролога на Си красиво
А почему бы сразу не писать так? Компилятор пролога строит план выполнения, переставляет строки, объединяет предикаты, выбрасывает лишнее. А еще определяет, какая переменная будет входящей, а какая исходящей. В предикате A=B*C, входящей может быть любая переменная и даже все сразу. Вот за этим он и нужен.
А пригодится всё это для построения оптимизаторов.
Совершеннолетние_клиенты(Имя) :- Клиенты(Имя, Возраст), Возраст > 18;
Первая строка выполнит многократный вызов второй строки. (Это зависит от компилятора, но предположим самый глупый компилятор). И на Си написать аналог этого примера было сложно. Конечно возможно, но машинный код был понятнее.
Вообще, в Прологе порядок строк может быть любой и на результат это не влияет. Каждая строка порождает множество вариантов и все они объединяются по И. Программа выше звучит как "Все что является клиентом"и "Всё что больше 18 лет".
Лямбда функции, появившиеся недавно в C++, позволяют красиво, то есть без хаков, представить на С++ программу на Прологе. Возьмем пример из Википеди (мне лень искать что то более интересное). Программа поиска общего делителя для массива чисел:
% Верно, что НОД (A, 0) = A gcd2(A, 0, A). % Верно, что НОД(A, B) = G, когда A>0, B>0 и НОД(B, A % B) = G (% - остаток от деления) gcd2(A, B, G) :- A>0, B>0, N is mod(A, B), gcd2(B, N, G). gcdn(A, [], A). gcdn(A, [B|Bs], G) :- gcd2(A, B, N), gcdn(N, Bs, G). gcdn([A|As], G) :- gcdn(A, As, G).
На C++ с Лямбдами это будет выглядеть так:
void gcd2(int a, int b, std::function<void(int)> result) { // Верно, что НОД (A, 0) = A if(b==0) result(a); //Верно, что НОД(A, B) = G, когда A>0, B>0 и НОД(B, A % B) = G (% - остаток от деления) if(a>0 && b>0) gcd2(b, a % b, result); } void gcdn(int a, int* bb, int* be, std::function<void(int)> result) { if(bb==be) result(a); else gcd2(a, *bb, [&](int n) { gcdn(n, bb+1, be, result); }); } void gcdn(int* xb, int* xe, std::function<void(int)> result) { gcdn(*xb, xb+1, xe, result); } void test() { vector<int> numbers; numbers.push_back(36); numbers.push_back(6); numbers.push_back(10); gcdn(numbers.begin(), numbers.end(), [&](int n) { cout << n; }); }
В принципе, можно компилировать с Пролога на Си красиво
А почему бы сразу не писать так? Компилятор пролога строит план выполнения, переставляет строки, объединяет предикаты, выбрасывает лишнее. А еще определяет, какая переменная будет входящей, а какая исходящей. В предикате A=B*C, входящей может быть любая переменная и даже все сразу. Вот за этим он и нужен.
А пригодится всё это для построения оптимизаторов.