我正在编写library,可以对范围进行地图/折叠操作。我需要与运营商合作。我对函数式编程不太熟悉,我暂时选择了*
用于地图,||
用于折叠。因此,要在区间cos(x)
中找到(强力算法)最大值8 < x < 9
:
double maximum = ro::range(8, 9, 0.01) * std::cos || std::max;
在上面,ro::range
可以替换为任何STL容器。
如果有地图/折叠运算符的约定,我不想有所不同。我的问题是:是否有数学符号或者是否有任何语言使用运算符进行map / fold?
**编辑**
对于那些问过的人,下面是RO目前可以做什么的小型演示。 scc
是一个小实用程序,可以评估C ++代码段。
// Can print ranges, container, tuples, etc directly (vint is vector<int>) :
scc 'vint V{1,2,3}; V'
{1,2,3}
// Classic pipe. Alogorithms are from std::
scc 'vint{3,1,2,3} | sort | unique | reverse'
{3, 2, 1}
// Assign 42 to [2..5)
scc 'vint V=range(0,9); range(V/2, V/5) = 42; V'
{0, 1, 42, 42, 42, 5, 6, 7, 8, 9}
// concatenate vector of strings ('add' is shotcut for std::plus<T>()):
scc 'vstr V{"aaa", "bb", "cccc"}; V || add'
aaabbcccc
// Total length of strings in vector of strings
scc 'vstr V{"aaa", "bb", "cccc"}; V * size || (_1+_2)'
9
// Assign to c-string, then append `"XYZ"` and then remove `"bc"` substring :
scc 'char s[99]; range(s) = "abc"; (range(s) << "XYZ") - "bc"'
aXYZ
// Remove non alpha-num characters and convert to upper case
scc '(range("abc-123, xyz/") | isalnum) * toupper'
ABC123XYZ
// Hide phone number:
scc "str S=\"John Q Public (650)1234567\"; S|isdigit='X'; S"
John Q Public (XXX)XXXXXXX
答案 0 :(得分:10)
这真的是一个评论,而不是一个真正的答案,但它太长,不适合评论。
至少如果我对术语的记忆正确,地图基本上是std::transform
,折叠是std::accumulate
。假设这是正确的,我认为尝试编写自己的文章充其量是不明智的。
如果你想使用地图/折叠样式语义,你可以这样做:
std::transform(std::begin(sto), std::end(sto), ::cos);
double maximum = *std::max_element(std::begin(sto), std::end(sto));
虽然std::accumulate
更像是通用折叠,但std::max_element
基本上是fold(..., max);
如果您更喜欢单一操作,则可以执行以下操作:
double maximum = *(std::max_element(std::begin(sto), std::end(sto),
[](double a, double b) { return cos(a) < cos(b); });
我敦促您重新考虑为此目的重载运营商。我上面给出的任何一个例子都应该对几乎任何合理的C ++程序员都清楚。你给出的例子对大多数人来说都是完全不透明的。
在更一般的层面上,我要求在重载运营商时要格外小心。正确使用时,运算符重载非常好 - 能够为任意精度整数,矩阵,复数等事物重载运算符,使用那些 类型的代码呈现代码比代码更易读和可理解运算符。
不幸的是,当你以意想不到的方式使用运算符时,恰恰恰恰相反 - 这些用法肯定是极意外 - 事实上,这些都非常令人惊讶。如果这些运算符在特定领域得到充分理解,可能会有问题(但至少有一点理由),但与C ++中的其他用法相反。然而,在这种情况下,你似乎正在发明一个“整个布料”的符号 - 我不知道任何人使用任何运算符C ++支持重载意味着折叠或映射(也没有任何其他视觉上相似或类似的东西)办法)。简而言之,以这种方式使用重载是一个糟糕且不合理的想法。
答案 1 :(得分:5)
在我所知的语言中,没有标准的折叠方式。 Scala使用运算符/:
和:\
以及方法名称,Lisp有reduce
,Haskell有foldl
。
map
更常见于我所知道的所有语言中的map
。
答案 2 :(得分:2)
以下是准人类可读的中缀C ++语法中fold
的实现。请注意,代码不是很强大,只能证明这一点。它支持更常见的3参数fold
运算符(范围,二元运算和中性元素)。
这很容易被滥用的方式(你刚刚说过“强奸”吗?)操作员超载,以及用900磅重的炮弹射击自己的最佳方法之一。
enum { fold } fold_t;
template <typename Op>
struct fold_intermediate_1
{
Op op;
fold_intermediate_1 (Op op) : op(op) {}
};
template <typename Cont, typename Op, bool>
struct fold_intermediate_2
{
const Cont& cont;
Op op;
fold_intermediate_2 (const Cont& cont, Op op) : cont(cont), op(op) {}
};
template <typename Op>
fold_intermediate_1<Op> operator/(fold_t, Op op)
{
return fold_intermediate_1<Op>(op);
}
template <typename Cont, typename Op>
fold_intermediate_2<Cont, Op, true> operator<(const Cont& cont, fold_intermediate_1<Op> f)
{
return fold_intermediate_2<Cont, Op, true>(cont, f.op);
}
template <typename Cont, typename Op, typename Init>
Init operator< (fold_intermediate_2<Cont, Op, true> f, Init init)
{
return foldl_func(f.op, init, std::begin(f.cont), std::end(f.cont));
}
template <typename Cont, typename Op>
fold_intermediate_2<Cont, Op, false> operator>(const Cont& cont, fold_intermediate_1<Op> f)
{
return fold_intermediate_2<Cont, Op, false>(cont, f.op);
}
template <typename Cont, typename Op, typename Init>
Init operator> (fold_intermediate_2<Cont, Op, false> f, Init init)
{
return foldr_func(f.op, init, std::begin(f.cont), std::end(f.cont));
}
foldr_func
和foldl_func
(左右折叠的实际算法)在别处定义。
像这样使用:
foo myfunc(foo, foo);
container<foo> cont;
foo zero, acc;
acc = cont >fold/myfunc> zero; // right fold
acc = cont <fold/myfunc< zero; // left fold
fold
这个词在这里被用作穷人新保留词的一种。可以定义此语法的几种变体,包括
<<fold/myfunc<< >>fold/myfunc>>
<foldl/myfunc> <foldr/myfunc>
|fold<myfunc| |fold>myfunc|
内部运算符必须具有与外部运算符相同或更高的优先级。这是C ++语法的限制。
对于map
,只需要一个中间体,语法可以是例如。
mapped = cont |map| myfunc;
实施它是一个简单的练习。
哦,请不要在制作中使用这种语法,除非你非常清楚自己在做什么,甚至可能会这样做;)