I think the whole approach with print5, print25 is the "correct" solution, but if I were interviewing I would give the best score to the person that gave system("/usr/bin/seq 1000"); as the answer.
The idea of building up by having something that prints 5, and then using that to build a print 25, and so on, is good.
That then inspires another question: given a function, foo(), that we wish to call exactly 1000 times, what is the way to do it in the least amount of code given that we are only allowed the following:
1. calling foo().
2. defining new functions, whose bodies consist entirely of calls to previously defined functions or to foo.
3. calling those new functions.
For computing amount of code, let's take it as 1 line per function call, and 1 line per function defined.
So, for example (in pseudocode):
function foo_50
foo
foo
...
foo
foo_50
foo_50
...
foo_50
where there are 50 foo's in foo_50, and 20 calls to foo_50, would be a program of length 71. Breaking it down by powers of two:
function foo_2
foo
foo
function foo_4
foo_2
foo_2
...
function foo_512
foo_256
foo_256
foo_512
foo_256
foo_128
foo_64
foo_32
foo_8
would have length 33.
Is that the best we can do? How about for a general N? How about writing a program that given N writes the optimal program in the above form for N?
Whoops, `baz() and baz()` actually doesn't work, because foo may not return a truthy value (and bar definitely doesn't). One fix, which actually makes it even shorter, is just `baz(), baz()`.
Same goes for `bar(bar(...)) and bar()`; all the `and`s should be commands instead in my solution above. Of course, this is Python-specific.
Since these questions always seem to be "Demonstrate you can cheat creatively, but in a way I approve of", I have to throw out "Print out the first 1,000 terms from a properly seeded random number generator. Discovery of the seed is a more interesting question, though fairly straightforward."
They would? I don't follow. A random number generator only gives you one number at a time. The problem is still 'print X sequence without loops'. No answer has been given here, so there's no meaningful approval to be had.
It's not that using a random number generator is a cheat, it's that it is a (pointless?) lateral move rather than a solution.
I'm reminded of a scene from the movie Tin Cup. Kevin Costner challenges Don Johnson by saying something lime "have you ever won a tournament with just your 7 iron?"
Don replies with "why it never occurred to me to try".
This is exactly how I feel when I read something like this.
The ctor trick is genius. Compile-time loops (recursion counts as one) seems like a cheat. The dispatch table keyed on (i < 1000) is a straight-up cheat, as (obviously) is using the ternary or short-circuiting.
Actually the one that uses function pointers is by far the best, simplest way, and it doesn't segfault. Since x/1000 will always be floor(x/1000), once it reaches 1000 it calls nothing and exits cleanly.
I strongly disagree on the ctor trick. While it's certainly ingenious, you're just reusing a loop, be it one implemented in the C++ runtime. It's little more than a glorified map.
I swear to god, I was tired of this argument before I even finished reading it. If you take your example to the extreme, you should consider that a processor also operates in a loop. Maybe every solution is a glorified series of iterations.
Nothing personal, but your argument is always used in these situations, for no good reason. Please learn where to draw the line.
> I swear to god, I was tired of this argument before I even finished reading it
I'm sorry if i tire you, but i think you misinterpreted what i said. Every solution will boil down to a loop, necessarily, so my point is it's useless to make distinctions like , "this solution is a bit cheating, but this one isn't". As long as it respects the requirements, eg no loop or conditional statements, it is no cheating.
> Nothing personal, but your argument is always used in these situations
I realize my original comment was oversimplifying things, but i fail to see which situations you are talking about. My argument, or what i originally wanted it to be, is that the line you draw between map and the constructor trick is extremely subjective. In such case, the only way to distinguish valid or invalid solutions is to refer to the original requirements.
> Please learn where to draw the line.
And please keep your condescending attitude to yourself. I perfectly know where to draw the line, and i still consider the limit tptacek made to be ridiculous. If you have any real argument to oppose me instead of sheer oversimplification and exaggeration, please proceed, but from what i see you just needed to vent, and in a rather unpleasant manner.
The way you phrased your original post gave me the impression you were dismissing the constructor solution for the same reason tptacek dismissed the rest. I certainly agree with your point, as you may have read elsewhere in the thread.
Other than that, I don't assume a patronizing attitude when I say that arbitrarily labeling solutions to such problems as "cheating" is an infuriatingly useless argument over semantics, and nothing more. Particularly so when such questions are used in the context of a job interview; how would you feel if an interviewer wrongly rejected your answer based on what he read on SO, and whose answer she trusted more?
PS. The situations I was referring to is the myriad of such questions on SO; a problem with absurd constraints imposed. They're fun, until 5 people come along and decide going around labeling answers as "cheating". The Stack Overflow meta site has quite a few comments on that phenomenon.
Okay well, so i feel like we're in a case of violent agreement, and it's probably my fault because i didn't take the time to write a proper comment in the first time.
If you want to get into language-lawyer mode, expressions are most certainly not conditional statements [in C]. But what's the use of arguing? "More gimmicky" answers don't count into solving a gimmicky problem? Who even keeps score?
edit: clarification, in true language-lawyer fashion.
The preprocessor hack does not compile to code that would decompile back to an if() statement. The i/1000 code does not compile to code that would decompile back to an if() statement. The ctor hack does not compile to code that would decompile back to a loop and an if statement. The i<1000 code, on the other hand, does compile down to code that decompiles back up to an if() statement; that's because it's just a compact way of writing an if() statement.
Well, neither C nor C++ are assembly, and vice versa. That's my usual response to this kind of "arguments", in every such question; boy do they come up a lot.
Regardless, I'll repeat it; maybe it'll stick this time:
i < 0
is a conditional expression,
if (i < 0) {}
is a conditional statement.
Also, while the above could be (mis)taken for C code;
cmp eax, 0
jl foo
this one surely wouldn't.
And at this point I'll stop beating this particular horse.
I think the most interesting part is after the initial "this is dumb" and "don't work there" answers, it actually turned out to be a pretty interesting question.
Somehow the solutions that crash on exit are cheating too much. The above doesn't (obviously) compile to something which uses conditionals at the assembly level. This solution probably wouldn't be by the book. That takes a few more lines of code.
Technically, he's using relational operators
if/else are conditionals that evaluate the boolean datatypes that result from the relational operation.
Is it breaking the rule? Not really.
Is it a loophole? Probably.
The original question probably intended it to mean "no comparing" rather than "no conditional statements"
'without using loops' is very ill defined. All solutions that in some way generate all integers up to a thousand need to include something we would, in English, refer to as 'a loop'. The ctor trick also contains 'a loop'. What solutions would you consider valid, without resulting in semantic squabbling over whether 'i < 1000' is a conditional or a condition and whether that matters?
The loop in the array initialization is generated by the compiler for the static version, by the runtime for the dynamic allocation version. It's still a loop.
You're just re-using a loop already implemented in the runtime. That's quite the same thing as using map, though a tiny bit clever. But if you qualify map as cheat, well then the constructor trick is just a cleverer cheat.