TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

C++ – From goto to std::transform

88 pointsby jlemoineabout 11 years ago

17 comments

exDM69about 11 years ago
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 未加载
nlyabout 11 years ago
&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 未加载
ajtullochabout 11 years ago
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 未加载
stormbrewabout 11 years ago
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 未加载
jeorgunabout 11 years ago
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 未加载
pathikritabout 11 years ago
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 未加载
Jareabout 11 years ago
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 未加载
crawshawabout 11 years ago
Readability improved from squareVec1 to squareVec4, then went downhill. Hiding loops behind new names doesn&#x27;t make programming better.
评论 #7572467 未加载
评论 #7571756 未加载
评论 #7572513 未加载
评论 #7572449 未加载
vinkelhakeabout 11 years ago
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_Dukhanabout 11 years ago
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 未加载
heliumcraftabout 11 years ago
<p><pre><code> def square(vector) vector.collect { |i| i**2 } end</code></pre>
评论 #7571765 未加载
评论 #7571968 未加载
评论 #7571820 未加载
u124556about 11 years ago
`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 未加载
geocarabout 11 years ago
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 未加载
ww2about 11 years ago
The example is too trivial. you could have 100 ways to write &quot;hello world&quot;, but it does not matter.
GFK_of_xmaspastabout 11 years ago
Why were all the examples using iterators, and not square bracket indexing?
mytummyhertzabout 11 years ago
am i the only one who thinks the std::transform version is LESS readable than the for loop?
shultaysabout 11 years ago
<p><pre><code> but you still have to read the whole line </code></pre> Oh the pain!