As it turns out, designing a fast algorithm involves a lot more than what is covered in the textbooks. This is why something like Timsort can generally outperform quicksort even though a naive algorithms course overview may well come to the opposite conclusion.
If my job is not to design these algorithms, why does it matter if I can write a naive implementation of one?
I mean, it's true that there's a lot of code that really is at the FizzBuzz level:
switch (c) {
case 'h': case 'D': menu.left(); break;
case 'j': case 'B': menu.down(); break;
case 'k': case 'A': menu.up(); break;
case 'l': case 'C': menu.right(); break;
default: /* no op */ break;
}
def find(self, name):
for thing in self.contents:
if thing.is_a(name):
return thing
vaders.hits(bullets, (vader, bullet) => {
blam(vader, 2.5)
score += vader.bounty
vader.die()
bullet.die()
})
if (vaders.where(({y}) => y > 400).count()) startGame(120)
if (!vaders.count()) startLevel()
And I think you should write as much of your code as possible in such a painfully obvious way. Every once in a while, though, you do need to write something that can't be written quite that simply. Often, the reason why is something in your problem domain: async def take_token(self):
self.update()
while not self.tokens:
await asyncio.sleep(self.recharge / 10)
self.update()
self.tokens -= 1
// Animate explosions.
poof.move()
poof.each(p => {if (--p.ttl < 0) p.die()})
// Chain-reaction explosions.
poof.about_every(64).each(p => {
if (poof.count() < 1024) blam(p, p.w/12)
})
// Limit number of explosion objects to guarantee responsivity
if (poof.count() > 1024) poof.about_every(2).die()
You need to be able to reason through what your code will do once you've written it, and you need to be able to come up with possible ways to write it that might work. Often the code you need for your problem domain isn't already in the standard library, so you have to write it. If it's on npm, you have to be able to read it to see whether it might work.Reversing a linked list is just about the simplest task that requires that kind of reasoning, and it has the advantage (or disadvantage) that understanding the puzzle doesn't require, or benefit from, any problem-domain knowledge. You could substitute binary search or insertion sort or something. Another example I got, which takes a few more lines of code, was "implement an integer stack ADT which can always tell you the maximum number on the stack in constant time".
I think the problem is that there are over a dozen different kinds of "seniority" but we collapse them all into one term. I got so frustrated I wrote a blog post about it: https://www.mooreds.com/wordpress/archives/2812
Also there are ancillary skills that make the problem much less difficult, so if you can't solve it, you don't have them. Joel Spolsky famously said that nobody can reverse a linked list without drawing diagrams on paper, and that's probably true if you're doing it imperatively in C. But it's in the category of "problems that become much simpler with functional styles of programming." The following took me 5 minutes, most of which was struggling with the syntax of OCaml that I'd forgotten:
$ ocaml
OCaml version 4.08.1
# type 'a list = Cons of ('a * 'a list) | Nil;;
type 'a list = Cons of ('a * 'a list) | Nil
# Cons(3, Cons(41, Nil));;
- : int list = Cons (3, Cons (41, Nil))
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' (Cons(x, ys)) in rev lst Nil ;;
val reverse : 'a list -> 'a list = <fun>
# reverse(Cons(3, Cons(41, Nil)));;
- : int list = Cons (41, Cons (3, Nil))
The full session with all my dumb errors: $ ocaml
OCaml version 4.08.1
# type 'a list = Cons of ('a * 'a list) | Nil;;
type 'a list = Cons of ('a * 'a list) | Nil
# Cons 3 (Cons 41 Nil) ;;
Error: Syntax error
# Cons(3, Cons(41, Nil));;
- : int list = Cons (3, Cons (41, Nil))
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' Cons(x, ys) ;;
Error: Syntax error
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' Cons(x, ys) in rev lst Nil ;;
Error: The constructor Cons expects 1 argument(s),
but is applied here to 0 argument(s)
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' (Cons(x, ys)) in rev lst Nil ;;
val reverse : 'a list -> 'a list = <fun>
# reverse Cons(3, Cons(41, Nil));;
Error: This function has type 'a list -> 'a list
It is applied to too many arguments; maybe you forgot a `;'.
# reverse(Cons(3, Cons(41, Nil)));;
- : int list = Cons (41, Cons (3, Nil))
#
(These error messages look appalling, but the OCaml interpreter was underlining my errors, which of course didn't paste.)Similarly, there are problems that become much easier with other paradigms of programming, such as backtracking search.
Anyways it is a bad question to ask because it is very easy and is frequently studied so candidates just memorise the answer.
Obviously yes? That's a basic assignment for a data structure class given to freshmen and sophomores. One would expect that a "senior" developer (degreed or self-taught) would be better than a college sophomore. At the FAANG level, it would be a phone screen question and a softball one at that.