我试图将以下Haskell代码转换为C ++:
data List t = Nil | Cons t (List t)
将代数数据类型直接转换为无状态访问者模式会产生以下Java代码
interface List<T> {
<R> R accept(ListVisitor<T,R> v);
}
interface ListVisitor<T,R> {
R visitNil();
R visitCons(T head, List<T> tail);
}
class Nil<T> implements List<T> {
@Override
public <R> R accept(ListVisitor<T,R> v) {
return v.visitNil();
}
}
class Cons<T> implements List<T> {
public final T head;
public final List<T> tail;
public Cons(T head, List<T> tail) {
this.head = head;
this.tail = tail;
}
@Override
public <R> R accept(ListVisitor<T,R> v) {
return v.visitCons(head, tail);
}
}
以下是我到目前为止的C ++代码:
template<class T> class List;
template<class T, class R> class ListVisitor {
virtual R visitNil() = 0;
virtual R visitCons(T head, List<T> tail) = 0;
};
template<class T> class List {
template<class R> virtual R accept(ListVisitor<T,R> v) = 0;
};
请注意,Java版本使用虚拟通用函数accept
。当我将它翻译成C ++时,我最终得到了一个虚拟模板函数,这是C ++不允许的。
除了让accept
返回void
并要求访问者有状态之外,是否有解决方案?
更新 根据要求,以下是一些如何使用接口的示例(模智能指针和可能的编译错误):
template<class T> struct LengthVisitor : ListVisitor<T, int> {
bool visitNil() { return 0; }
bool visitCons(const T&, const List<T> &tail) { return 1 + tail.accept(*this); }
};
template<class T> struct ConcatVisitor : ListVisitor<T, const List<T> *> {
const List<T> *right;
ConcatVisitor(const List<T> *right) : right(right) {}
List<T> * visitNil() { return right; }
List<T> * visitCons(const T &head, const List<T> & tail) {
return new Cons(head, tail.accept(*this));
}
};
另一个例子,Java中的高级函数fold
可以在这里找到:http://hpaste.org/54650
答案 0 :(得分:13)
这肯定可以改进(例如,使用智能指针进行尾部所有权),但基本思路是:
template <typename T>
struct cons_list {
T head;
cons_list<T>* tail;
explicit cons_list(T head, cons_list *tail = nullptr)
: head(head), tail(tail) {}
template <template<typename> class Visitor>
typename Visitor<T>::return_type accept(const Visitor<T>& visitor) {
return visitor.visit(head, tail);
}
};
template <typename T>
struct some_visitor {
typedef void return_type;
return_type visit(T head, cons_list<T>* tail) const {
std::cout << head << '\n';
if (tail != nullptr) tail->accept(*this);
}
};
Demo。无需虚拟分派和类层次结构。 nullptr
是C ++ 11,但它在03上应该可以正常工作。
将accept
实现为自由函数并不使用空指针作为nil节点可能是一个更好的主意,但正如我所说,这是基本的事情。
注意:这或多或少是boost::static_visitor背后的想法。
完整的C ++ 11 Boost.Variant版本(需要模板别名)。未经测试,因为我附近没有g ++ 4.7。
struct nil_node {};
template <typename T> cons_node;
template <typename T>
using cons_list = boost::make_recursive_variant<
nil_node, cons_node<T>
>::type;
template <typename T>
struct cons_node {
T head;
cons_list<T> tail;
explicit cons_node(T head, const cons_list<T>& tail)
: head(head), tail(tail)
{}
};
template <typename T>
struct some_visitor : boost::static_visitor<T> {
void operator()(nil_node&) {}
void operator()(cons_node<T>& node) {
std::cout << node.head << '\n';
boost::apply_visitor(node.tail, *this);
}
};
int main() {
cons_node<int> x(1, cons_node<int>(2, cons_node<int>(3, nil_node())));
boost::apply_visitor(some_visitor<int>(), x);
};