Это упражнение попросить осуществить cons
, car
а также cdr
функции, использующие только лямбда-функции.
Функция cons(a,b)
создать список a
с последующим b
, car(l)
возвращает первый элемент списка l
, а также cdr(l)
возвращает остальную часть списка.
Я работал со списками только из двух элементов, но когда вы пытаетесь обработать больше элементов, это терпит неудачу. Есть идеи?
#include <iostream>
#include <functional>
template<typename A>
A car( std::function< A(std::function<A(A,A)>) > m ){
return m( [](A a, A){ return a; } );
}
template<typename A>
A cdr( std::function< A(std::function<A(A,A)>) > m ){
return m( [](A, A b){ return b; } );
}
template<typename A>
std::function< A(std::function<A(A,A)>) >
cons( const A a, const A b){
return [=](std::function<A(A,A)> l){ return l(a,b); };
}
int main(){
//auto l = cons( cons( 1, 2 ), 3 ); // no matching function
auto l = cons( 1, 2 );
std::cout << car( l ) << ", " << cdr( l ) << std::endl;
return 0;
}
ОБНОВИТЬ
Я замечаю, что настоящая подпись минусов должна быть:
template<typename A,typename B, typename C>
std::function< C(std::function<C(A,B)>) >
cons( const A a, const B b){
return [=](std::function<C(A,B)> l){ return l(a,b); };
}
Потому что эта версия тоже не работала.
ОБНОВЛЕНИЕ 2
Это может быть упрощено (более читабельно) с помощью:
template<typename A, typename B, typename C>
using Cell<A,B,C> = std::function<C(A,B)>
cons
из вашего примера не может быть скомпилировано, потому что компилятор не может вывести тип возврата для лямбда-функции. Что вам нужно, так это полиморфная лямбда-функция. К сожалению, C ++ 11 не поддерживает полиморфные лямбды, но есть несколько способов реализовать cons
, car
а также cdr
в любом случае в C ++.
Первый (и, возможно, самый простой) способ реализации cons
, car
а также cdr
использует std::tuple
или же std::pair
, Эта реализация почти аналогична определениям в глава 2.1.3 книги.
#include <iostream>
#include <tuple>
template<typename A, typename B>
A car( std::tuple<A, B> m ) {
return std::get<0>(m);
}
template<typename A, typename B>
B car( std::tuple<A, B> m ) {
return std::get<1>(m);
}
template<typename A, typename B>
std::tuple<A, B>
cons(const A a, const B b) {
return std::tuple<A, B>(a, b);
}
int main() {
auto l = cons(cons(1, 2), 3);
std::cout << car(car(l)) << ", " << cdr(car(l)) << ", ";
std::cout << cdr(l) << std::endl;
return 0;
}
Но эта реализация не следует первоначальной идее упражнение 2.4. Чтобы следовать этому, мы могли бы просто создать свой собственный pair
класс и реализовать operator()
метод параметризован лямбда-типом возврата.
#include <iostream>
#include <functional>
template<typename A, typename B>
class pair {
A _a;
B _b;
public:
pair(const A a, const B b): _a(a), _b(b) {}
template<typename R>
R operator()(std::function<R(A, B)> func) {
return func(_a, _b);
}
};
И тогда мы могли бы реализовать cons
, car
а также cdr
просто используя это:
template<typename A, typename B>
A car(pair<A, B> p) {
return p( std::function<A(A, B)> ( [](A a, B) { return a; } ) );
}
template<typename A, typename B>
B cdr(pair<A, B> p) {
return p( std::function<B(A, B)> ( [](A, B b) { return b; } ) );
}
template<typename A, typename B>
pair<A, B>
cons(const A a, const B b) {
return pair<A, B>(a, b);
}
int main() {
auto p = cons(cons("a", 2), 3);
std::cout << car(car(p)) << ", " << cdr(car(p)) << ", ";
std::cout << cdr(p) << std::endl;
return 0;
}
Насколько я понимаю, C ++ 14 (условно названный C ++ 1y) представит некоторые функции, такие как общие лямбда-выражения, которые могут упростить реализацию.
ОБНОВИТЬ
После прочтения предложение общих (полиморфных) лямбда-выражений в C ++ 14 я пришел к следующей рабочей реализации:
#include <iostream>
auto car = [](auto func) { return func( [](auto a, auto b) { return a; } ); };
auto cdr = [](auto func) { return func( [](auto a, auto b) { return b; } ); };
auto cons = [](auto a, auto b) { return [=](auto func) { return func(a, b); }; };
int main() {
auto p = cons( cons("a", 2), 3 );
std::cout << car(car(p)) << ", " << cdr(car(p)) << ", ";
std::cout << cdr(p) << std::endl;
return 0;
}
Обратите внимание, что он будет работать только с компилятором, реализующим такое предложение (например, GCC 4.8, Clang 3.4) с использованием -std=c++1y
флаг.
Других решений пока нет …