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\$
8
  • \$\begingroup\$ Did you try extra paranthesis: funcs[([]...)]? Note that anything using a lambda in the index brackets is basically obfuscation. \$\endgroup\$ Commented Jul 12, 2014 at 19:25
  • \$\begingroup\$ @Nobody: Yes, I did, but couldn't find a way g++ would accept it. \$\endgroup\$ Commented Jul 12, 2014 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\$ Commented Jul 12, 2014 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\$ Commented Jul 12, 2014 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\$ Commented Jul 12, 2014 at 19:41

1 Answer 1

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\$
12
  • \$\begingroup\$ Are you using a C-style array for brevity, or because you're trying to avoid giving it an explicit size? \$\endgroup\$
    – Jamal
    Commented Jul 12, 2014 at 20:41
  • \$\begingroup\$ @Jamal: updated my answer to address your question. \$\endgroup\$
    – Edward
    Commented Jul 12, 2014 at 21:16
  • \$\begingroup\$ I meant std::array, actually (sorry, I should've made that clear). \$\endgroup\$
    – Jamal
    Commented Jul 12, 2014 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
    Commented Jul 8, 2017 at 17:36
  • 1
    \$\begingroup\$ Please remember to be nice. \$\endgroup\$
    – Edward
    Commented Jul 8, 2017 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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