- Break down the requirements of a problem
Take your time, break down the components of a question, define anything that isn't clear, and ask questions if you are confused by anything.
Problem 1 bounds the sequence with a threshold below 1000 (non-inclusive). Your solution treats the threshold as an inclusive value. The problem wouldn't be seen because 1000 isn't a Fibonacci number, but if the problem included a Fibonacci value for a threshold, you would process and include an extra value.
Problem 2 asks that you return the Fibonacci sequence and the threshold is up to 1000. I would actually ask for clarification on that, as up to could mean inclusive or non-inclusive. Your solution implements the threshold as inclusive and this may not be right. Also, you return the max Fibonacci value below the threshold instead of the sequence. Your recruiter was likely looking for tail-call recursion, which is implemented. Unfortunately, other issues caused them to dislike the result.
- Look for opportunities to reuse code
Instead of writing 2 separate functions to do the same task, you could split the problem into components that provided the facilities to generate outputs or be used as inputs into other systems. For this problem, you could write an RecurringSequenceIterator
that maintained the state of the recurring values and the relation between those values. The recurrence relation for generating all Fibonacci's is next_fib = curr_fib + prev_fib
and for Even Fibonacci's is next_even_fib = 4 * curr_even_fib + prev_even_fib
. Pass these as functors into your iterator and you have a general purpose solution for generating the values of a recurring sequence.
With a recurring sequence generator, you can use one of the standard insert iterators to create a sequence up to a threshold for problem 2. It also lends itself to range construction in containers if you know the start and end range of your Fibonacci's.
- Use Templates to make generalized and reusable code
You've hard-coded the integral type into your function, resulting in a function that is only capable of counting up to Fib(46) before overflowing. uint32_t
can calculate to Fib(47), int64_t
to Fib(92), and uint64_t
to Fib(93). Perhaps I want to calculate some Fibonacci number that requires a 256-bit number. Templates provide a general purpose solution that isn't dependent on a specific type.
Verify that your code does what is supposed to. Provide functions with inputs that have known outputs and compare those results. Since you only provided the Fibonacci header and no test function, I won't comment on this further.
- Pick up a coding standard
Where ever you end up working, you'll be using their coding guidelines. Until then, you should be using something that fixes many of the basic problems. Keep in mind that coding standards are designed specifically for the needs of a company. Google, JSF, LLVM, Boost, Microsoft (book) all offer coding standards/guidelines/style guides based on the needs of their respective organization.
Most coding standards will sort libraries by reliability then name. Some coding standards address conditional includes as well.
#include
only what you need
Every time you #include
a library, you are increasing the compile time of a project. In your header, <iostream>
, <sstream>
, and <string>
are not used anywhere inside your Fibonacci
library.
- Use
constexpr
/const
where it makes sense
const
provides a correctness mechanism in which your function is limited in what it can do, whether it is modifying a variable or object state. It also provides compile-time type-checking, allowing you to catch errors much earlier (also allows the compiler to possibly make optimizations). Since it is apparent you are still coding using c++03, constexpr
doesn't exist. However, you should get used to using it with c++11 and later.
Consider your code:
static int fibHelper(int limit, int x1, int x2) {...}
limit
, x1
, x2
are values that do not change in the local function. Qualify them as const
.
static int fibHelper(int const limit, int const x1, int const x2) {...}
x
, x1
, x2
projects an order, but as someone who may have glossed over your code, I could interpret the ordering as x
, x1
, x2
. Better naming would allow you to enforce the ordering, such as using next_fib
, prev_fib
, curr_fib
to reflect an ordering of prev_fib
, curr_fib
, next_fib
.