15
\$\begingroup\$

Since today is apparently a good day for...unlikely FizzBuzz implementations, I decided to contribute another, this one in C++:

#include <iostream>
#include <functional>
#include <vector>
#include "xrange.h"

int main() {
    std::vector<std::function<void(int)> > funcs {
        [](int i) { std::cout << i << "\n"; },
        [](int i) { std::cout << "fizz\n";  },
        [](int i) { std::cout << "buzz\n";  },
        [](int i) { std::cout << "fizzbuzz\n"; }
    };

    auto test = [](int i){return (i % 3 == 0) + 2 * (i % 5 == 0); };

    for (auto i : xrange(1, 101))
        funcs[test(i)](i);
}

This uses the same xrange I posted in a previous answer.

I'd be particularly interested in eliminating having to use test, but a sequence like: [[ is parsed as introducing an attribute rather than using the return from a lambda as an index (and inserting white space doesn't seem to help). I could use .at() instead, but that imposes runtime overhead I'd also rather avoid.

\$\endgroup\$
  • \$\begingroup\$ Did you try extra paranthesis: funcs[([]...)]? Note that anything using a lambda in the index brackets is basically obfuscation. \$\endgroup\$ – Nobody Jul 12 '14 at 19:25
  • \$\begingroup\$ @Nobody: Yes, I did, but couldn't find a way g++ would accept it. \$\endgroup\$ – Jerry Coffin Jul 12 '14 at 19:26
  • \$\begingroup\$ As it is already ugly with the lambda you could get it to work with some arithmetic: funcs[0+[](int... and when talking about ugliness you could also use the explicit operator name: funcs.operator[]([]... \$\endgroup\$ – Nobody Jul 12 '14 at 19:32
  • 2
    \$\begingroup\$ @Nobody: Neither of these options strikes me as an improvement (but opinions obviously vary--I don't find lambdas ugly at all). \$\endgroup\$ – Jerry Coffin Jul 12 '14 at 19:39
  • 1
    \$\begingroup\$ So you say that (if it would work) you would prefer to use:funcs[[](int i){return (i % 3 == 0) + 2 * (i % 5 == 0); }(i)] over funcs[(i % 3 == 0) + 2 * (i % 5 == 0)] (which is already a mess)? \$\endgroup\$ – Nobody Jul 12 '14 at 19:41
15
\$\begingroup\$

I'd be inclined to make a simple fixed array instead of a std::vector for funcs:

const std::function<void(int i)> funcs[] {
    [](int i) { std::cout << i << "\n"; },
    [](int ) { std::cout << "fizz\n";  },
    [](int ) { std::cout << "buzz\n";  },
    [](int ) { std::cout << "fizzbuzz\n"; }
};

This is useful because it eliminates the overhead of memory allocations for a std::vector and the calls for constructor and destructor. As an embedded systems guy, I tend to think about whether code can be in ROM rather than RAM, and whether work can be done at compile-time rather than run-time.

Note, too that I've omitted the variable name for the last three lambdas. There's no real difference except that it prevents the compiler from generating a warning about "unused parameter i" in those three lines.

Also, I think you're overthinking the lambda. The test lambda isn't really necessary at all since you can call it like this:

funcs[ (i % 3 == 0) + 2 * (i % 5 == 0) ](i);

So (without xrange.h, although one could use that also) it would look like this:

#include <iostream>
#include <functional>

int main() {
    const std::function<void(int i)> funcs[] {
        [](int i) { std::cout << i << "\n"; },
        [](int ) { std::cout << "fizz\n";  },
        [](int ) { std::cout << "buzz\n";  },
        [](int ) { std::cout << "fizzbuzz\n"; }
    };

    for (int i=1; i <= 100; ++i)
        funcs[ (i % 3 == 0) + 2 * (i % 5 == 0) ](i);
}
\$\endgroup\$
  • \$\begingroup\$ Are you using a C-style array for brevity, or because you're trying to avoid giving it an explicit size? \$\endgroup\$ – Jamal Jul 12 '14 at 20:41
  • \$\begingroup\$ @Jamal: updated my answer to address your question. \$\endgroup\$ – Edward Jul 12 '14 at 21:16
  • \$\begingroup\$ I meant std::array, actually (sorry, I should've made that clear). \$\endgroup\$ – Jamal Jul 12 '14 at 21:26
  • 1
    \$\begingroup\$ C arrays allow a bunch of tremendously unsafe conversions like to raw pointer implicitly, don't behave as values for no good reason, and don't offer useful interfaces like begin() and end(). std::array is better in basically every way. And furthermore, if your goal is to avoid overhead, apart from this being a terrible micro-optimization, the std::function probably costs way more than the std::vector. \$\endgroup\$ – DeadMG Jul 8 '17 at 17:36
  • 1
    \$\begingroup\$ Please remember to be nice. \$\endgroup\$ – Edward Jul 8 '17 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.