TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

C++ – From goto to std::transform

88 点作者 jlemoine大约 11 年前

17 条评论

exDM69大约 11 年前
What I find painful about writing pseudo-functional C++ using &lt;algorithm&gt; and other tricks is that you have to allocate storage for intermediate values. This makes C++ algorithms a lot less composable than similar things in other languages.<p>So if I wanted to do something like (in Haskell):<p><pre><code> sumOfOddSquares xs = sum . map (^2) . filter odd $ xs </code></pre> In C++, I would need to allocate temporary storage for the intermediate values.<p><pre><code> std::vector odd_numbers; std::copy_if(xs.begin(), xs.end(), back_inserter(odd_numbers), isOdd); std::transform(odd_numbers.begin(), odd_numbers.end(), odd_numbers.begin(), square); std::accumulate(odd_numbers.begin(), odd_numbers.end(), 0, plus); </code></pre> This becomes quite painful very quickly. You can use solutions like boost::filter_iterator, but soon you&#x27;re knee deep in C++ templates and the long compile times and awful error messages that follow.<p>The example above is a bit contrived since it can be easily be done with a single accumulate, but you get the idea...<p><pre><code> std::accumulate(xs.begin(), xs.end(), 0, [](int sum, int i) { return sum + ((i&amp;1) ? i*i : 0); }); </code></pre> So while you can have lots of fun with &lt;algorithm&gt;, it&#x27;s quite limited in what it can do.
评论 #7572983 未加载
评论 #7573004 未加载
nly大约 11 年前
&lt;algorithm&gt; is so 90s. In C++14 you&#x27;ll be able to do this:<p><pre><code> auto square_copy = [](auto v) { for (auto&amp; e: v) { e *= e; } return v; }; </code></pre> Another option:<p><pre><code> auto square = [](auto&amp; v) { v *= v; }; auto map = [](auto range, auto fun) { transform (begin(range), end(range), fun); return range; }; vector&lt;int&gt; vec = {1,2,3,4,5,6}; auto squared = map (vec, square); </code></pre> Note that, in both of these cases, if you use std::move() to gift a container to these functions, they will not actually allocate any memory and will effectively operate in-place.
评论 #7574045 未加载
ajtulloch大约 11 年前
Using Facebook&#x27;s `folly::gen` library [1], you can get it down to:<p><pre><code> vector&lt;int&gt; squareVec3(const vector&lt;int&gt;&amp; v) { return from(v) | mapped([](int i){ return i * i; }) | as&lt;vector&gt;(); } </code></pre> which is pretty slick. There are a bunch of the usual LINQ&#x2F;functional operators in `folly::gen` that are quite nice. As an example, to filter out the odd elements of `v`, you can do<p><pre><code> vector&lt;int&gt; squareVecFiltered(const vector&lt;int&gt;&amp; v) { return from(v) | filter([](int i){ return i % 2 == 0; }) | mapped([](int i){ return i * i; }) | as&lt;vector&gt;(); } </code></pre> <a href="https://github.com/facebook/folly/tree/master/folly/gen" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;folly&#x2F;tree&#x2F;master&#x2F;folly&#x2F;gen</a>
评论 #7573802 未加载
评论 #7573044 未加载
stormbrew大约 11 年前
C++ is evolving at a kind of breakneck pace lately, and it&#x27;s already a lot different from the language it used to be. Auto, range loops, and lambdas are making it much more pleasant to work in while still maintaining a lot of performance advantages.<p>I know it&#x27;s pretty popular to hate on C++, but I&#x27;m pretty excited about where it&#x27;s going, personally.
评论 #7571815 未加载
评论 #7571700 未加载
评论 #7571753 未加载
jeorgun大约 11 年前
I&#x27;m not really sure why references are being used here, if you&#x27;re just making a copy straight-away.<p><pre><code> vector&lt;int&gt; square(vector&lt;int&gt; vcopy) { for (int &amp;i : vcopy) i *= i; return vcopy; } </code></pre> is shorter, (IMO) much more readable, and on my tests, measurably faster than the reference version. If you really want the std::accumulate version,<p><pre><code> vector&lt;int&gt; square(vector&lt;int&gt; vcopy) { accumulate(begin(vcopy), end(vcopy), begin(vcopy), [](int i) { return i * i; }); return vcopy; } </code></pre> works just as well.<p>Still, I guess it&#x27;s beside the point.
评论 #7575237 未加载
评论 #7573080 未加载
pathikrit大约 11 年前
After coding in languages in like Scala&#x2F;Python for a while, it took me a while to parse this. Scala equivalent: `def square(a: List[Int]) = a map {i =&gt; i*i}`
评论 #7571939 未加载
Jare大约 11 年前
Convenience overloads for common use cases (iterating over the full vector, accumulating on a default-constructed accumulator, etc) would be great to have as standard. Are there and I have missed them? If not, what&#x27;s the rationale? (this goes for the STL in general, not specifically these high order algos)
评论 #7571804 未加载
crawshaw大约 11 年前
Readability improved from squareVec1 to squareVec4, then went downhill. Hiding loops behind new names doesn&#x27;t make programming better.
评论 #7572467 未加载
评论 #7571756 未加载
评论 #7572513 未加载
评论 #7572449 未加载
vinkelhake大约 11 年前
Just as a side note: if you&#x27;re looking to write a convenience wrapper like the author&#x27;s transformVec, do not have it take the function as `const function&lt;T(T)&gt;&amp;`. Instead, do something like:<p><pre><code> template &lt;typename T, typename F&gt; vector&lt;T&gt; transformVec(const vector&lt;T&gt;&amp; v, F op) </code></pre> Using std::function in a case where you just want something callable just makes the optimizer&#x27;s job a lot harder. Using std::function is a good idea if you actually need type erasure (for example if you want to pass something callable to a function that is <i>not</i> a template).
评论 #7575226 未加载
Marat_Dukhan大约 11 年前
The author probably thinks that all versions are equally fast, but in fact they are just equally slow. Neither gcc 4.8 nor icc 14 can use vectorization for this loop (likely because of aliasing).
评论 #7572263 未加载
heliumcraft大约 11 年前
<p><pre><code> def square(vector) vector.collect { |i| i**2 } end</code></pre>
评论 #7571765 未加载
评论 #7571968 未加载
评论 #7571820 未加载
u124556大约 11 年前
`result = [i * i for i in v]` looks much more readable to me.<p>What if you need to square every other element? copy_if and transform then don&#x27;t look so pretty anymore. `result = [i * i for i in v if i % 2 == 0]` is still more readable.<p>What if you need to do something else and you don&#x27;t know if there is a transform like function for it?
评论 #7573347 未加载
geocar大约 11 年前
It seems like it&#x27;s the loop itself that is redundant. Why couldn&#x27;t I just do:<p><pre><code> squaredVec = vec * vec </code></pre> or<p><pre><code> squaredVec = vec ^ 2</code></pre>
评论 #7573770 未加载
ww2大约 11 年前
The example is too trivial. you could have 100 ways to write &quot;hello world&quot;, but it does not matter.
GFK_of_xmaspast大约 11 年前
Why were all the examples using iterators, and not square bracket indexing?
mytummyhertz大约 11 年前
am i the only one who thinks the std::transform version is LESS readable than the for loop?
shultays大约 11 年前
<p><pre><code> but you still have to read the whole line </code></pre> Oh the pain!