C++ 空范围折叠操作的结果
我正在写一个允许对范围进行一些函数编程操作的程序。范围是STL容器的泛化。我的问题是空范围折叠的结果应该是什么C++ 空范围折叠操作的结果,c++,functional-programming,C++,Functional Programming,我正在写一个允许对范围进行一些函数编程操作的程序。范围是STL容器的泛化。我的问题是空范围折叠的结果应该是什么 auto r = range(4); // lazy numeric range {0,1,2,3} auto r0 = range(0); // empty range {} vector<string> vs {"a", "bb"}; vector<string> vs0 {}; // this is obvious and impleme
auto r = range(4); // lazy numeric range {0,1,2,3}
auto r0 = range(0); // empty range {}
vector<string> vs {"a", "bb"};
vector<string> vs0 {};
// this is obvious and implemented part
cout << (r || add); // 6, || - folding op
cout << (r0 || add); // 0
cout << (vs || add); // "abb"
cout << (vs0|| add); // ""
cout << (r || mul); // 0
cout << (r0 || mul); // 1
cout << (r || max); // 3
// What result of these should be?
cout << (r0 || div); // ???
cout << (r0 || sub); // ???
cout << (r0 || max); // -∞ ???
cout << (r0 || min); // +∞ ???
cout << (r0 || ???); // result of arbitrary op?
auto r=范围(4);//惰性数值范围{0,1,2,3}
自动r0=范围(0);//空范围{}
向量vs{“a”,“bb”};
向量vs0{};
//这是显而易见的,也是实现的一部分
cout我假设您的“文件夹”是某个模板的实例,附带了一个二进制函数,可能还有一个初始值
传统上,fold被定义为递归地调用所述的二进制函数(initial,first),然后(old value,next),直到调用它的资源用完为止
没有这样的初始值,减法和除法的工作方式与您期望的相同(例如fold({1,2})是1/2)
因此,减法和除法的“文件夹”要么是“逆之和”和“逆之积”(即,fold(r)=1/fold(r),fold(r)=fold(r),这似乎很无聊),要么是根本不同的东西,在空容器上不起作用
max和min应该为给定类型生成最高和最低值,或者是第二种类型的文件夹,在空容器上没有意义
通过“不工作”,他们可以抛出异常,或者返回类似于boost::optional
——即在空列表中,他们不返回任何内容
文件夹类型可以使用一个函数来查找给定类型的初始值,该函数应解析为traits模板类或自由函数(类似于std::begin
)
编辑:根据下面的评论,对答案进行了改进
这里真正的诀窍是减法和除法没有左手恒等式,但有右手恒等式
只有右手标识的操作应表示为右手折叠,只有左手标识的操作应表示为左手折叠(aka、foldr和foldl)
也就是说,用二进制操作的标识表示列表{a,b,c}
上的折叠的自然方式是:
( (id *op* a) *op* b ) *op c
但对于没有左手标识的操作,这不起作用
但是,如果您反转折叠用手,则会得到以下结果:
a *op* (b *op* (c *op* id))
只要你有右手的身份就行
这对于div
和sub
非常重要--div
的右侧标识为1,sub
的右侧标识为0,但两者都没有左侧标识。(对于所有x
,不存在e-x=x
的元素e
。对于所有x
,存在e
的元素x-e=x
,即0)
求幂也是如此(它的右手标识为1
,但没有左手标识)
这仍然不符合folddiv
应该做什么的天真期望。它适用于长度为2的列表,但在长度为3的列表中会发生一些不直观的情况。但至少在数学上是合理的 通常,fold
函数由3个参数定义:要折叠的列表、要应用的函数和累加器的起始值。折叠空列表的返回值自然就是作为参数给出的起始值。另外,我只想说,我认为重载|
作为折叠运算符是个坏主意。我知道std::acculate
以及如何指定初始值。我的问题是,对于某些ops,这个值应该是多少。项目的目标不是让每个人都满意。它只适合于一些小范围的用户(C++OneLiner、Codejam等)。我是否可以建议为此实现命名中缀操作符,而不是以可能令人困惑的方式覆盖其他操作符?即,myVec*fold*sum
风格语法,而不是myVec | | sum
。(是的,这涉及到以奇怪的方式重写运算符,但不太容易混淆)@Reubentemplate-struct-NamedOperator{BinOp-op;}template-struct-LeftForm{BinOp-op;tlhs;LeftForm(ta,BinOp-b):op(b),lhs(a){};模板LeftForm操作符*(T left,NamedOperator nop){返回LeftForm(left,nop.op);}模板自动操作符*(LeftForm left,U rhs)->decltype(left.op(left.lhs,rhs)){返回left.op(left.lhs,rhs);}
容易吗?更难的是使语法干净地生成+sum+
&*mul*
等运算符。代码是半speudo-Code。您的假设是正确的-文件夹是带有trait方法的模板函子fold\u init\u value()
。在将折叠的定义(在我的头脑中)从R0+R1+R2…
替换为init+R0+R1…
之后,对于div
和sub
(1和0)的空范围,应该是什么值变得很清楚。谢谢。@leonid注意到这使得div和sub很无聊。它们只是以可分离的方式与add和mult不同(即,对序列应用统一的每元素变换,然后add或mult,得到相同的结果)。当错误发生时,你需要一种方法来传播它们,所以你可能想制定一种机制?我不明白-无聊
,可分离方式
。这是抽象代数吗?不幸的是,我不知道。顺便说一句,我找到了答案--@LeonidVolnitsky--“无聊”就像“不有趣”一样。“可分离”--对N个输入的操作相对于第二个操作而言是“可分离的”,前提是它可以表示为对N个输入中的每个输入的一元操作,然后是第二个操作。请注意,div
和sub
都没有左侧标识元素——没有元素e
,因此e-x=x
适用于所有x
,同样适用于e/x=x
。同时,存在一个元素e
,使得e+x=x
适用于所有