这个自应用阶乘函数的类型是什么? < >我在C++中编写了匿名阶乘函数,并用g+4.4.2编译了我的代码。 它工作得很好。然而,我不知道我的函数的类型 #include<iostream> #include<functional> using std::function; int main() { //tested at g++ 4.9.2 //g++ -std=c++1y -o anony anony.cpp auto fac = [](auto self,auto n)->auto{ if(n < 1) return 1; else return n * self(self,n-1); }; std::cout<<fac(fac,3)<<std::endl;//6 return 0; } struct aaaa // Not its real name. { template<typename a, typename b> auto operator()(a self, b n) const { } };
我必须定义一些递归类型来解决它:这个自应用阶乘函数的类型是什么? < >我在C++中编写了匿名阶乘函数,并用g+4.4.2编译了我的代码。 它工作得很好。然而,我不知道我的函数的类型 #include<iostream> #include<functional> using std::function; int main() { //tested at g++ 4.9.2 //g++ -std=c++1y -o anony anony.cpp auto fac = [](auto self,auto n)->auto{ if(n < 1) return 1; else return n * self(self,n-1); }; std::cout<<fac(fac,3)<<std::endl;//6 return 0; } struct aaaa // Not its real name. { template<typename a, typename b> auto operator()(a self, b n) const { } };,c++,haskell,types,c++14,generic-lambda,C++,Haskell,Types,C++14,Generic Lambda,我必须定义一些递归类型来解决它: data Y a = Y ((Y a)->a->a) fac2 self 0 = 1 fac2 self n = n * ((applY self self) (n-1)) where applY (Y f1) f2 = f1 f2 fact2 = fac2 $ Y fac2 那么,为什么g++能得到准确的fac函数类型,g++认为fac函数是什么类型?下面的表达式auto fac=是一个lambda表达式,编译器会自动从中生成一个闭包对象
data Y a = Y ((Y a)->a->a)
fac2 self 0 = 1
fac2 self n = n * ((applY self self) (n-1))
where applY (Y f1) f2 = f1 f2
fact2 = fac2 $ Y fac2
那么,为什么g++能得到准确的
fac
函数类型,g++认为fac
函数是什么类型?下面的表达式auto fac=
是一个lambda表达式,编译器会自动从中生成一个闭包对象。该对象的类型是唯一的,并且只有编译器知道
从N4296开始,§5.1.2/3[expr.prim.lambda]
lambda表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型,称为闭包类型,其属性如下所述。该类类型既不是聚合类型(8.5.1),也不是文本类型(3.9)。闭包类型在包含相应lambda表达式的最小块作用域、类作用域或命名空间作用域中声明
请注意,因此,即使是两个相同的lambda表达式也将具有不同的类型。比如说,
auto l1 = []{};
auto l2 = []{}; // l1 and l2 are of different types
您的lambda表达式是一个C++14通用lambda,编译器将其转换为类似于以下内容的类:
struct __unique_name
{
template<typename Arg1, typename Arg2>
auto operator()(Arg1 self, Arg2 n) const
{
// body of your lambda
}
};
struct\uuu唯一\u名称
{
模板
自动运算符()(Arg1 self,Arg2 n)常量
{
//你的lambda的尸体
}
};
我无法评论Haskell部分,但是递归表达式在C++中工作的原因是因为在每个调用中只传递了一个关闭对象实例(<代码> FA< <代码> >)。“代码>操作程序()/<代码>是一个模板,它可以推断lambda的类型,即使它不是一个你可以命名的。
< p> C++ >代码> FAC < /C> >实际上不是一个函数,而是一个具有成员函数的结构。#include<iostream>
#include<functional>
using std::function;
int main()
{
//tested at g++ 4.9.2
//g++ -std=c++1y -o anony anony.cpp
auto fac = [](auto self,auto n)->auto{
if(n < 1)
return 1;
else
return n * self(self,n-1);
};
std::cout<<fac(fac,3)<<std::endl;//6
return 0;
}
struct aaaa // Not its real name.
{
template<typename a, typename b>
auto operator()(a self, b n) const
{
}
};
因此,函数的参数不是函数本身,而是将其作为成员的对象。这样做的一个效果是函数的类型(即
运算符()
的类型)不会出现在运算符()
函数本身的类型中。(self的类型是定义函数的结构。) 模板部分不是此工作所必需的;这是
fac
功能的非通用版本:
如果我们保留模板并将operator()
重命名为applY
:
// The Y type
template<typename a>
struct Y
{
// The wrapped function has type (Y<a>, a) -> a
a applY(const Y<a>& self, a n) const
{
if(n < 1)
return 1;
else
return n * self.applY(self, n-1);
}
};
template<typename a>
a fac(a n)
{
Y<a> y;
return y.applY(y, n);
}
self
是一个函数,fac2
的类型必须是
X -> Int -> Int
对于某些X
由于
self
是一个函数,self$n-1
是一个Int,因此self
的类型也是X->Int->Int
但是X
会是什么呢?它必须与
self
本身的类型相同,即X->Int->Int
但这意味着
self
的类型是(代替X
):
因此类型X
也必须是
(X -> Int -> Int) -> Int -> Int
因此,self
的类型必须是
((X -> Int -> Int) -> Int -> Int) -> Int -> Int
以此类推,无限期。也就是说,在Haskell中,类型是无限的
<> P. > Haskell的解决方案本质上明确地介绍了C++通过其成员结构生成的必要间接性。
,正如其他人指出的,lambda充当一个涉及模板的结构。然后问题变成:为什么Haskell不能输入自应用程序,而C++可以?
#include<iostream>
#include<functional>
using std::function;
int main()
{
//tested at g++ 4.9.2
//g++ -std=c++1y -o anony anony.cpp
auto fac = [](auto self,auto n)->auto{
if(n < 1)
return 1;
else
return n * self(self,n-1);
};
std::cout<<fac(fac,3)<<std::endl;//6
return 0;
}
struct aaaa // Not its real name.
{
template<typename a, typename b>
auto operator()(a self, b n) const
{
}
};
答案在于C++模板与Haskell多态函数的区别。比较这些:
-- valid Haskell
foo :: forall a b. a -> b -> a
foo x y = x
// valid C++
template <typename a, typename b>
a foo(a x, b y) { return x; }
Haskell定义不会进行类型检查:当x
为任意类型时,不能保证可以执行x+1
。相反,C++代码很好。a
的某些替换导致不正确的代码,这一事实目前并不重要
推迟此检查会导致允许一些“无限类型的值”。Python或Scheme等动态语言进一步将这些类型错误推迟到运行时,当然可以很好地处理自应用程序。当您将
auto
替换为某些类型时,例如int
编译器应该告诉您它无法推断类型并给出它们的名称。但是我还没有测试它,但是为什么g++能推断出我的fac函数的正确类型呢?作为补充,编写Haskell版本的更好方法是将它分为递归位(即,一般来说,fix::(a->a)->a
和fix f=f(fix f)
)和非递归位。非递归位是fact1 recur n=如果n==0,那么1 else n*recur(n-1)
,您会注意到这(a)不是递归的,(b)有一个有限且可推断的类型fact1::(Int->Int)->(Int->Int)
,并且(c)实现一个阶乘的“步骤”。然后我们通过组合递归和非递归位来“完成”它:fact=fix-fact1
。好吧,我不会把fix
称为Y组合器。它具有相同的效果,但是有许多固定点组合,其中Y
和fix
只是两个例子。哦,是的,我现在记得了:Data.Function
中的定义使fix(1:)
成为一个循环链表,而不是无限链表,对于其他类似的东西也是如此。它可以让您将数据结构捆绑在一起,而更明显的fixf=f(fixf)
则不能。
-- valid Haskell
foo :: forall a b. a -> b -> a
foo x y = x
// valid C++
template <typename a, typename b>
a foo(a x, b y) { return x; }
-- INVALID Haskell
foo :: forall a b. a -> b -> a
foo x y = x+1
// valid C++
template <typename a, typename b>
a foo(a x, b y) { return x+1; }