几段纠结的C++代码,求解脱~

rularys 2012-07-28 02:26:51
最近接触些函数式编程语言,后来想起来C++好像在某个方面也支持函数式“编程”,于是找资料~ 记忆中好像有过一本书叫做什么来着~呃 哦对了,叫做《C 语言 什么来着~》作者是...谁来着??
不说这些了,我说到哪里了?哦对了,最近接触了些函数式编程语言,没事自己在C++也捣鼓了一番。这下面的几段代码是捣鼓出来了,可有根像刺一样的刺就像刺在了肉里,纠结,特冒雨上来求解脱:

首先下面是两个类似 D&A的《C++ Template Meta Programing》里面所说的“元函数类”的类,是为接着下来的类做准备的:


// Factorial G
struct G {

private:
template <typename f, int n>
struct Meta {

const static unsigned __int64 Value = n * f::Apply<n - 1>::Value;
};

template <typename f>
struct Meta<f,1> {

const static unsigned __int64 Value = 1 ;
};

public:
template<typename _f>
struct Apply {

struct Type {

template <int _n>
struct Apply {

typedef Meta<_f,_n> Type;
const static unsigned __int64 Value = Type::Value;
};
};
};
};

// Fibonacci G
struct G2 {
private:
template <typename self,int n>
struct Meta {

const static unsigned __int64 Value = self::Apply<n -1>::Value + self::Apply<n -2>::Value;
};

template <typename self>
struct Meta<self,1> {

const static unsigned __int64 Value = 1;
};

template <typename self>
struct Meta<self,0> {

const static unsigned __int64 Value = 0;
};

public:
template <typename _self>
struct Apply {

struct Type {

template <int _n>
struct Apply {

typedef Meta<_self,_n> Type;
const static unsigned __int64 Value = Type::Value;
};
};
};
};

貌似不太花吧;我解释一下里面的 Apply::Type::Apply::Type::Apply::Type::Apply ...
这是因为C++的模板机制不支持类似这样的特性:


template< typename T1,typename T2,typename T3,typename T4 /* ... */ >
struct TMFunction {

};

template TMFucntion1 = TMFunction <Type1>;
auto a = sizeof (TMFucntion1<Type2,Type3,Type4>);

template TMFucntion2 = TMFunction <Type1,Type2>;
auto b = sizeof (TMFucntion2<Type3,Type4>);

template TMFucntion3 = TMFunction <Type1><Type2><Type3>;
auto c = sizeof (TMFucntion3<Type4>);

auto d = sizeof (TMFunction <Type1> <Type2> <Type3> <Type4>);


请允许我偷懒用struct 来代替class,在这里用哪一个都能说明问题。

可能有人看出来了,如果把模板当作函数(元函数?),把模板参数作为其函数形参,那么上面这些特性是与 Partial evaluationhttp://en.wikipedia.org/wiki/Partial_evaluation有关的设定。哦,这与我标头里所提到的代码有关。我绕了个弯,用Apply<>::Type::Apply<>::Type::Apply<>::Type 来模拟<><><>;而且还模拟得似模似样,这多亏了我前面所提到的那本书。呃,好像我是从那里面抄来的~ 请知道的人不要声张。

哦,我记起来我要说什么了,就是像下面的一个 Y Combinator ,它只认识"元函数类":


template<typename G>
struct Y {

struct W {

template <typename F>
struct Apply {

typedef typename F::Apply<F>::Type Type;
};
};

struct F {

template<typename f>
struct Apply {

// 下面这两行代码(#1,#2)不能通过编译,因为这时 F::Apply<>::Type 还没有定义,
// 所以不能递归地用于G::Apply<>::Type 的定义

/*
typedef typename W::Apply<f>::Type t; //#1
typedef typename G::Apply<t>::Type Type; //#2
*/

// 只能以下面的办法来解决
// 这里 “Type”的定义 并不依赖于 "G::Apply<>::Type",所以 "G::Apply<>::Type" 使用它不会构成递归定义
// 但是这使得Y失去了通用性,因为"Type"必须再重复"G::Apply<>::Type" 的定义

/**/
struct Type {

template< int n>
struct Apply {

typedef typename W::Apply<f>::Type t; //temp t
typedef typename G::Apply<t>::Type::Apply<n>::Type Type;
const static unsigned __int64 Value = Type::Value;
};
};
/**/
};
};

// the result is here
typedef typename W::Apply<F>::Type Type;
};


里面的注释就是问题所在,也是纠结所在。若是#1和#2两行代码能起作用,那么在我心目中这个Y 就算合格了。可是我的思路断了,不知如何再绕过去,所以这个Y 只能用于 第二形参是 int 的元函数类,而且必须是没有第三个形参的。OTZ 求解脱!

PS : 关于 Y Combinator,我记得函数是编程里面是位明星,所以这里不做解释了;如有兴趣,可参考这里:http://en.wikipedia.org/wiki/Y_combinator
关于 Partial evaluation ,更多的信息可参考 lambda calculus 以及 Higher-order_functions http://en.wikipedia.org/wiki/First-class_function#Higher-order_functions:_passing_functions_as_arguments
...全文
368 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
蓝鹰 2012-08-03
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]

个人意见:模版和函数式编程都是语法糖。
[/Quote]糖还是可以适当吃以下的
rularys 2012-08-03
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 的回复:]
我大概看明白了,但是也没想出方法。不过,我倒是想问一下,这个 Y combinator 和 G 本身比起来有什么优势呢?我看 wiki 的例子,和你的例子,好像 Y(G) 能做的事,G 本身都能做,因此 Y 的能力来源于 G,如果 Y 不能带来额外的利益的话,为什么好要用它呢?
比如,运行时程序和模板元编程都能够完成类似功能的话,模板元编程能够带来在编译期完成运算的好处,所以比运行时有优势。但这个 Y combinator 貌似没有这种优势,你的 G 是在编译期完成计算的,所以 Y 也是,如果 G 不能,Y 也不能。想看一个例子,Y(G) 能干,而 G 干不了的。

[/Quote]

再深入下去就和我的初衷离地远了。
要详细讨论 Y combinator 的话,那话题蛮多的。你所说的没有错,但有一种情形你可能没有想到过:
当 G 不能自指的时候,怎么让 G 实现递归调用自身?
Y 的主要工作就是帮助这样的G实现自指递归的。当然在C++模板元编程中,模板元函数使能够引用自身进行递归的,所以 Y 在这里显得多余。

但是,怎么说呢,Y 也是一种思想,我仅仅是试图用 C++ TMP 来完成 它的 Y combinator,就像画画一样。也许画不出来,也许能画个轮廓,但无论结果如何,都能帮助我提高画画的技巧。
rularys 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 的回复:]
看不明白你说什么,我没学过函数式编程语言,也不知道 Y Combinator 是什么。你就不能写两行伪代码,秀一下这个 Y 和 G 到底用起来是什么样子的?就是再高深的理论,也不妨碍举几个例子吧。你陈述中一直在说模板元函数,可是代码里从头到尾都是类模板。我是真搞晕了。
[/Quote]

Y Combinator,简单的说它有这样的特性:
对于任意函数 G(当然,是至少有一个参数的函数):
Y(G) = G(Y(G))

这就是 Y 的全部。
我想我能给出的解释比不上 wikipedia 上的来得更详细;起先我给出链接了。G 在这里只是我给出的原函数类的形式,是普通的元函数类。例子,就是我给出的代码。Y Combinator 本来就是一种符号运算用的组合子,取再多的例子也是类似这样的,没有本质上的区别。

你心目中的模板元函数可能和我理解的不一样。
ri_aje 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 的回复:]
Y Combinator,简单的说它有这样的特性:
对于任意函数 G(当然,是至少有一个参数的函数):
Y(G) = G(Y(G))

这就是 Y 的全部。
我想我能给出的解释比不上 wikipedia 上的来得更详细;起先我给出链接了。G 在这里只是我给出的原函数类的形式,是普通的元函数类。例子,就是我给出的代码。Y Combinator 本来就是一种符号运算用的组合子,取再多的例子也是类似这样的,没有本质上的区别。

你心目中的模板元函数可能和我理解的不一样。
[/Quote]
我大概看明白了,但是也没想出方法。不过,我倒是想问一下,这个 Y combinator 和 G 本身比起来有什么优势呢?我看 wiki 的例子,和你的例子,好像 Y(G) 能做的事,G 本身都能做,因此 Y 的能力来源于 G,如果 Y 不能带来额外的利益的话,为什么好要用它呢?
比如,运行时程序和模板元编程都能够完成类似功能的话,模板元编程能够带来在编译期完成运算的好处,所以比运行时有优势。但这个 Y combinator 貌似没有这种优势,你的 G 是在编译期完成计算的,所以 Y 也是,如果 G 不能,Y 也不能。想看一个例子,Y(G) 能干,而 G 干不了的。
ri_aje 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 的回复:]
对于模板元函数 G<f,arg1,arg2,arg3,...> ,Y 能应用于G而不受限于其除以外的其它参数arg1,arg2,arg3,...的个数或者形式(模板参数类型,比如不管arg1 是typename 还是又是一个template,这里我用参数的形式来表达这个意思)。亦即是说,Y 的实现是应用于模板元函数上,但是它对于G 的限制仅限于G的第一个参数,就和通常的应用于函数式编程语言里的 Y Combinator 一样。
(这里G的形式可以是我给出的元函数类,或者别的形式,但是 Y 的实现可以不必为此负责,只要其中一种形式能实现Y,那么其它的G形式的Y也可以实现,所以我用元函数类来举例子)

PS:
模板元函数能否像通常的函数(并非仅是C中的函数)那样使用起来时意义简洁明了,我更倾向于将模板元编程理解成函数式编程,因为模板元函数更像无状态的纯函数。而我认为 Y 的实现是否“简洁”是检验这一门语言是否易于使用的一个参考点。
[/Quote]
看不明白你说什么,我没学过函数式编程语言,也不知道 Y Combinator 是什么。你就不能写两行伪代码,秀一下这个 Y 和 G 到底用起来是什么样子的?就是再高深的理论,也不妨碍举几个例子吧。你陈述中一直在说模板元函数,可是代码里从头到尾都是类模板。我是真搞晕了。
rularys 2012-07-31
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]

我想知道的不是这些。你说这些时已经把我捆绑在你的实现上了,我想知道你要解决的问题本身。就好像 c++ 标准只描述虚函数的行为,而不谈虚函数表和虚表指针一样,因为后者为实现细节,不属于问题本身。我想知道这个 Y combinator 需要支持的行为,以及你预期的使用方法和应该产生的效果;有可能我根本搞不出解决方案,或搞来搞去和你的类似,但这并不影响分析一个问题的正确流程是先了解问题本身,然后在讨论具体解法。
[/Quote]

对于模板元函数 G<f,arg1,arg2,arg3,...> ,Y 能应用于G而不受限于其除以外的其它参数arg1,arg2,arg3,...的个数或者形式(模板参数类型,比如不管arg1 是typename 还是又是一个template,这里我用参数的形式来表达这个意思)。亦即是说,Y 的实现是应用于模板元函数上,但是它对于G 的限制仅限于G的第一个参数,就和通常的应用于函数式编程语言里的 Y Combinator 一样。
(这里G的形式可以是我给出的元函数类,或者别的形式,但是 Y 的实现可以不必为此负责,只要其中一种形式能实现Y,那么其它的G形式的Y也可以实现,所以我用元函数类来举例子)

PS:
模板元函数能否像通常的函数(并非仅是C中的函数)那样使用起来时意义简洁明了,我更倾向于将模板元编程理解成函数式编程,因为模板元函数更像无状态的纯函数。而我认为 Y 的实现是否“简洁”是检验这一门语言是否易于使用的一个参考点。


ri_aje 2012-07-31
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

引用 7 楼 的回复:

我看你在主楼绕了半天,好像是在追求通用性。没学过 lambda calculus 和 Y combinator,wiki page 的解释也聊胜于无。你心目中 Y 的通用用法都是什么样的呢?写点儿伪代码展示一下除了给 Factorial 和 Fibonacci 当外壳的其他用法。
另外,你所有的 Fibonacci 都拼错了,除了注释里的那一个,正确的拼法是 F……
[/Quote]
我想知道的不是这些。你说这些时已经把我捆绑在你的实现上了,我想知道你要解决的问题本身。就好像 c++ 标准只描述虚函数的行为,而不谈虚函数表和虚表指针一样,因为后者为实现细节,不属于问题本身。我想知道这个 Y combinator 需要支持的行为,以及你预期的使用方法和应该产生的效果;有可能我根本搞不出解决方案,或搞来搞去和你的类似,但这并不影响分析一个问题的正确流程是先了解问题本身,然后在讨论具体解法。
赵4老师 2012-07-30
  • 打赏
  • 举报
回复
个人意见:模版和函数式编程都是语法糖。
kingdom_0 2012-07-30
  • 打赏
  • 举报
回复
语法糖,无处不在
ppsharp 2012-07-30
  • 打赏
  • 举报
回复
看得头都晕了
rularys 2012-07-30
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]

很多东西都是聊以慰藉那些逝去的东西,你一味的追求通用性的模板元编程,并且深陷其中而忘却的一些事情的本质。
就好比鲁迅先生笔下的华老栓,一心执着着给自己的儿子治病,却忘却了那药的作用和由来...
一味的追求程序模板化却忘却的语言本身存在的本和根
Strousstrup曾说过,C++的指导原则之一是不要为不使用的特性付出代价。
[/Quote]

想必woomevan有些误解。
在这里并非是执意追求通用性的模板元编程,更不用说是深陷其中了。仅仅是因为这一阵在接触函数式编程方面的应用,想起了C++的模板元编程其实也是接近于函数式编程,所以又回头考察。毕竟以前对FPL不了解,所以对C++模板的理解与应用都是没有深入。

我确实是看到了FPL的一些优点,所以想看看对C++的模板元编程能达到什么样的理解程度。如果有可能,我认为在C++之中使用自带的模板元编程会比引入一个新的FPL进行混合编程应用更加方便。

实在不行,放弃又有何不可。从一些实现以及自己的学习过程中,我也发现了不少类似这样的限制。有的能利用一些技巧绕过去,而有些特性的实现则太困难。除非有相当的价值,否则谁会绕着弯子而不走直路。

学习与讨论,旨在同求进步。谢谢woomevan给出你的看法
woomevan 2012-07-30
  • 打赏
  • 举报
回复
很多东西都是聊以慰藉那些逝去的东西,你一味的追求通用性的模板元编程,并且深陷其中而忘却的一些事情的本质。
就好比鲁迅先生笔下的华老栓,一心执着着给自己的儿子治病,却忘却了那药的作用和由来...
一味的追求程序模板化却忘却的语言本身存在的本和根
Strousstrup曾说过,C++的指导原则之一是不要为不使用的特性付出代价。
rularys 2012-07-30
  • 打赏
  • 举报
回复

struct G3 {

template<typename f,typename T1,typename T2, int n>
struct Meta {

enum {Value = n + f::Apply<T1,T2, n - 1>::Value };
};

template<typename f,typename T1,typename T2>
struct Meta<0> {

enum {Value = 0 };
};

template<_f>
struct Apply {

struct Type {

//#Apply
template<typename _T1,typename _T2,int _n>
struct Apply {

typedef Meta <_f,_T1,_T2,_n>::Type Type;
};
};
};
};
rularys 2012-07-30
  • 打赏
  • 举报
回复
上面代码中
template<typename f,typename T1,typename T2,int n>
struct Meta { ... };
int n 是手误,在这里是多余的
rularys 2012-07-30
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

我看你在主楼绕了半天,好像是在追求通用性。没学过 lambda calculus 和 Y combinator,wiki page 的解释也聊胜于无。你心目中 Y 的通用用法都是什么样的呢?写点儿伪代码展示一下除了给 Factorial 和 Fibonacci 当外壳的其他用法。
另外,你所有的 Fibonacci 都拼错了,除了注释里的那一个,正确的拼法是 Fibonacci。
[/Quote]

是为了通用性,当然,在此处的通用性也是有限制的,但是这些限制并不是最大的问题。问题是在最前面的指出的那一段代码和注释里面:


// 下面这两行代码(#1,#2)不能通过编译,因为这时 F::Apply<>::Type 还没有定义,
// 所以不能递归地用于G::Apply<>::Type 的定义

/*
typedef typename W::Apply<f>::Type t; //#1
typedef typename G::Apply<t>::Type Type; //#2
*/


假如上面的两行代码能工作的话,那么这个Y 的实现 就不用和 G 的其他参数绑定(第一个参数 "f" 除外):

struct G3 {

template<typename f,typename T1,typename T2,int n>
struct Meta {

// 函数体
/* ... */

// 递归
typedef f::Apply<T1,T2>::Type Type;
// 或者 enum {Value = f::Apply<T1,T2>::Value }
// 这里没有计算递归终结条件,但这个不是要讨论的重点,重点是递归实现了。
};

template<_f>
struct Apply {

struct Type {

struct Apply<typename _T1,typename _T2> { //#Apply

typedef Meta <_f,_T1,_T2>::Type Type;
};
};
};
}


当前的实现是 G函数的“#Apply” 这一行代码必须要在Y 之中重复写一遍,Y 才能应用于 G ;就相当于Y是定制的。

但如果前面指出的那两行被认为是无效的代码能工作的话,那么就不需要这样的定制:
typedef typename G::Apply<t>::Type Type;
这里的已经把 G::Apply<t>::Type 提取到Y之中,所以 “#Apply” 这一行代码也就能在 Y 之中运用了:


typedef Y<G3> func;
auto value = func::Apply<type1,type2>::Value; // 这里的func::Apply 就是 G3::Apply<>::Type::Apply<>


我这样解释可以么?

"另外,你所有的 Fibonacci 都拼错了,除了注释里的那一个,正确的拼法是 Fibonacci。"
这个问题我也知道,因为注释是用自然语言的,所以“Fibonacci”这个词我没有胡乱做修改。在代码中因为我无法写出两个名字一样的标识符(typedef Y<G2>::Type Fibonacc 以及 unsigned int Fibonac(int n) ),所以做了修改,希望不要介意
ri_aje 2012-07-29
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]

呃 忘记了贴上后面的代码。这是上面那个 Y 和两个 G 的例子:
C/C++ code

typedef Y<G>::Type Factorial;
typedef Y<G2>::Type Fibonacc;

std::cout << typeid(Factorial).name() << std::endl;
std::cout << typeid(Factorial::Apply<10>::Type).name() << std::endl;
std::cout << Factorial::Apply<20>::Value << std::endl;

std::cout << typeid(Fibonacc).name() << std::endl;
std::cout << typeid(Fibonacc::Apply<10>::Type).name() << std::endl;
std::cout << Fibonacc::Apply<93>::Value << std::endl;

std::cout << Fibonac(42) << std::endl;
[/Quote]
要实现支持例子中用法的模板貌似不是很难,我看你在主楼绕了半天,好像是在追求通用性。没学过 lambda calculus 和 Y combinator,wiki page 的解释也聊胜于无。你心目中 Y 的通用用法都是什么样的呢?写点儿伪代码展示一下除了给 Factorial 和 Fibonacci 当外壳的其他用法。
另外,你所有的 Fibonacci 都拼错了,除了注释里的那一个,正确的拼法是 Fibonacci
rularys 2012-07-28
  • 打赏
  • 举报
回复
长路漫漫其修远
rularys 2012-07-28
  • 打赏
  • 举报
回复
一楼 二楼 的话有见地。但有时候增长见识也是一种增长解决问题能力的一种办法。
rularys 2012-07-28
  • 打赏
  • 举报
回复
呃 忘记了贴上后面的代码。这是上面那个 Y 和两个 G 的例子:

typedef Y<G>::Type Factorial;
typedef Y<G2>::Type Fibonacc;

std::cout << typeid(Factorial).name() << std::endl;
std::cout << typeid(Factorial::Apply<10>::Type).name() << std::endl;
std::cout << Factorial::Apply<20>::Value << std::endl;

std::cout << typeid(Fibonacc).name() << std::endl;
std::cout << typeid(Fibonacc::Apply<10>::Type).name() << std::endl;
std::cout << Fibonacc::Apply<93>::Value << std::endl;

std::cout << Fibonac(42) << std::endl;


最后面的那个 Fibonac(42) 作为对比是这样定义的:

unsigned int Fibonac(int n)
{
if(n < 2)
return n;
return Fibonac(n - 1) + Fibonac(n - 2);
}


PS: 也许是我老了,也许是我的机器老了,Fibonac(42) 在我的机器上跑了差不多一分钟,我用的是VS 2010;
Fibonacc::Apply<93> 是 unsigned __int64 的溢出上限,但是即便是Fibonacc::Apply<100>,编译一样是几秒钟就完成。我真的是老了,求解脱
qq120848369 2012-07-28
  • 打赏
  • 举报
回复
你编程是为了解决问题,不是为了研究语言,解脱个什么劲啊。
加载更多回复(1)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧