tags:

views:

548

answers:

12

I'm learning C++ and I want to make clean and readable code. I was wondering which way is better? (this is supposed to make the factorial of 9)

First Method:

int main(){
    int i = 1,r = i;
    while (i < 10) {
       r *= ++i;
    }
}

Second Method:

int main(){
    int i = 1,r = i;
    while (i < 10) {
       i++;
       r *= i
    }
}

The first may be harder to understand but it's one less line. Is it worth it? What about performance? Obviously it wouldn't matter in such a trivial example but it would be a good practice to make fast code from the beginning.

+27  A: 

That while can't get much simpler, but you can always switch to a for!

int r = 1;

for (int i = 1; i <= 10; i++) {
   r *= i;
}
LukeN
`r` is uninitialized.
Neil Williams
Yeah, I definitely prefer the for loop for this problem, but won't r *= throw an exception if you don't initialize it first?
walnutmon
probably want to set r to 1.
tloach
Done! Thanks :)
LukeN
@jboyd: it won't throw an exception, r will just have garbage in it.
Ferruccio
@jboyd - `r` will have to be initialized.
Brook Julias
`for` is good, meaningful names are good too.
msw
@jboyd @Ferruccio: `r` will be uninitialized, and when you use it you get undefined behavior.
GMan
the for statement needs to be: for(int i=2; i<= 10; i++)in order to be exactly the same as the while.
dig
Postfix increment is unnecessary, always use prefix as long as you do not needs the copy from the postfix. But +1 for using for, much more readable IMO.
daramarak
@LukeN: This is not equivalent to the OP's code. OP's code multiplies `r` by 2,3, ..., 10. Your code mutiplies `r` by 1, 2, ..., 9.
AndreyT
@AndreyT Thanks for noticing, hopefully fixed :)
LukeN
@AndreyT @LukeN While the OP's code calculates 10!, s/he claims that the while is supposed to calculate 9! So - is the comment wrong or does the code have a bug? ;)
mocj
@dig: good catch - but I'd bet that's a bug in the example `while` loops since the text mentioned that it's supposed to calculate 9!.
Michael Burr
@daramarak prematurely optimise much?
wds
@wds prefix is not about optimization, it is about reducing side effects.
daramarak
@daramarak in the example you commented on, there are no side effects. Using i++ in loops is a common idiom, using ++i here would just be more, not less confusing.
wds
@wds A copy is a side effect. In the case of the int, it is small indeed. This time it is acceptable, but to be safe every time you write what you intend to do, and if that is increment and fetch that is ++i.
daramarak
@daramarak in this case the intended effect is to increment i. This is the only effect of that statement. Hence, no side effects.
wds
+3  A: 

Personally, of the two, I'd use the first one; it's readable and clear. If you were to use the second one, use the prefix increment (++i) on the standalone line anyway; it's slightly faster (ignoring potential compiler optimizations) in the general case.

Also, I'd probably use a for loop in this case -- I'm assuming there's a reason you're not.

There's not a good general rule for this -- if the line had more operations in it and function calls etc, it'd start being too complex. You get a good feel for complexity vs brevity as you get more experience.

And finally, I wouldn't worry about micro optimizations like this as you code, especially if you're just learning.

Cory Petosky
thanks, I feared that 1st one was not so readable, and a for is a good idea, my book just used a while thats why I posted like that.
Cory, `++i` is __prefix__ increment, since the operator is _prefixing_ the operand. `i++` is postfix increment. Otherwise a very good point, __+1__ from me. . Most start with using `i++` on `int` and forget that this might hurt performance when `i` is some user-defined type.
sbi
Thanks for fixing my typo; that's what I get for mind-dumping random advice between compiles. :)
Cory Petosky
I would say that the most important point about the prefix increment is not optimization but good style and reduction of side effects. When using postfix you create a copy.
daramarak
+24  A: 
int factorial(int n) {
   int product = 1;
   for (int i = 1; i <= n; i++) {
      product *= i;
   }
   return product;
}

is way more readable (and generalized).

msw
Indeed. Code should be written for people first and for computers second.
Daniel Straight
I would like to give this a __+10__, if I only could. I hadn't even seen that that code is an iterative factorial, which just shows that names are _very_ relevant.
sbi
i like this the most though I voted for both of the for loop ones, the variable names here are clearer and this is a better example of what one should do on the job.
Cervo
This one is better than LukeN's.
Matthieu M.
It's basically the same except I didn't wrap it in a function and left the name "j" the keep it similar to OP's code :)
LukeN
+1 just for using decent variable and function names.
paxdiablo
+2  A: 
int main(){ 
   int i = 1,r = i; 
   while (i++ < 10) { 
      r *= i;
   } 

And forgetting some semi colons.

smdrager
+2  A: 

You could go a little more compact even...

int main() {
  int i = 10, r = 1;
  while (--i) r *= i;
}

not that that's really a good idea.

Grumdrig
Not if you want to maintain that code!
LukeN
+4  A: 

The second one, no doubt.

Mainly because if you're debugging the code, you always suspect the line r *= ++i; to be errorous.

Viktor Sehr
+1  A: 
int main()
{
    int answer = factorial(10);
}

int factorial(int n)
{
    if (n == 1) return 1;
    return n * factorial(n-1);
}
tster
Totally impractical but still I didn't see any other recursive versions. Now we just need someone to make a tail recursive version of it :) +1
Cervo
A: 

I think the other answers are better example of writing clear and readable code, but if you must have something that is very similar to the original code you posted, this might be a cleaner alternative.

int main(){
    int r, i = r = 1;
    while (i++ < 10) {
       r *= i;
    }
}

In my opinion, pre- and post-increment operators that are combined with assignments are one of the hardest code constructs to mentally parse. Pre- and post-increment in conditionals is a lot more common.

Lucas
you have a bug compared to original code - your loop is longer, wrong use of ++
Greg Domjan
I fail to see how `while (i++ < 10)`is any clearer than `i *= ++i;`
Robert Tuck
@Greg: Fixed a declaration bug, but the loops are identical. I just compiled and ran them both to be sure.
Lucas
A: 

prefer ++i to i++

++i increments and gives back the new value i++ is doing 2 steps, creating a temp, incrementing the value and returning the temp old value

As such in general usage where you can use ++i the code will be simpler to understand. Both your examples can use ++i so there is little difference.

prefer for to while when you have simple loops similar to for each.

for (int i = 2, r=1; i <= 10; ++i) {
  r *= i
}
Greg Domjan
The compiler will optimize away the temporaries if it can with `i++`, so this is a pretty out of date argument. And then you went and used `i==` in your own for loop, so -1 from me.
John Dibling
quite right, poor typing habbit ++i it should have been.The Compile MIGHT optimise out temporaries, or have little cost, my argument is not compiler speed but comprehension of purpose.
Greg Domjan
I don't really think anyone has any trouble understanding i++ as the last clause in a for loop. Some of us do have trouble understanding why you've declared r in the scope of the loop though, so once the loop is over you can't return it.
Peter
uhm, return r from main? well I guess if there was more to the example than doing something with r then discarding any result it might matter more, but as it is only used inside the loop I reduced the scope.And see Johns response above for an easy example of how i++ can easily be used incorrectly.
Greg Domjan
+1  A: 

Why not fold the intended number in the while loop? Main thing for me is code readability because someone else is going to come back later and have to figure it out. Also, factorial's get really big fast, so you might want to use a long. Try to use the best type for the job.

long main() {
    return factorial( 10 );
}

long factorial( int n ) {
    long ans = (long) n;

    // error checking here
    if (n <= 0) { return -1; }
    while (n-- > 0) {
        ans *= n;
    }
dminear
+1  A: 

As others has pointed out, try not to write 'smart code' but 'understandable code'. The solution given by msw is very good because the names of the variables shows what its purpose is.

Ismael
A: 

Method #1, up to limitations of some debugging tools.

Compact code should be preferred, the problem is that you cannot debug aggregate statements on many of the popular cheap debugging tools. This often make you break long expressions, extract variables, etc.

In case of simple code as this one, compactness really outweights. You can see some of the motivation here: http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.html

Pavel Radzivilovsky