Printing a triangle with 3 different symbols in C

I'm curious about that method. As a novice, I prefer the more verbose if-then-else statements, but I'm interested in knowing the pros and cons.

I imagine with experience, the conditional operator will become easier for me to read quickly, but it's hard to imagine it would ever be as intuitive as if-then-else. The whitespace and indentations that typically go with ifs are easy on the eyes!

On the other hand, if there are performance benefits to the more compact conditional operator, it would be good to know that too.

I must confess, when I first saw @WBahn's code, it looked like it must be a magic trick - how could it possibly work while being so small? Once I recognized that there are three separate conditionals embedded in 1 line of code, it was a little easier to understand, but it's still impressively compact and efficient.

I really like the use of a character array instead of more conditionals to decode the results of the modulo function into individual characters. Wish I had thought of that myself.

As usual, this is a great forum, exposing me to new ideas and different approaches all the time!
As far as the utility or efficiency of the shorthand version, I don't know - maybe @WBahn can provide some details.

I agree that it is good to be able to see/unpack the conditionals...as you say. While it, of course, pains me greatly to admit it :) - YES, his code is nice and tight....pity he (but not you or I) missed the very first instruction as the OP stated:

1. The program must ask how many rows the triangle will have.
Ok, I am just fooling around and not that any of the code posted qualifies, you might get a kick out of the the old C obfuscation contests that I used to see - wow talk about WTF moments!
 

WBahn

Joined Mar 31, 2012
30,062
It's interesting to see the different approaches.

For better or worse, I can tell you that when I first started (well, after understanding what the program was supposed to do which took a few posts), I was approaching it as printing a multi-character pattern. I did read your (@WBahn) 'hints', but disregarded them completely, because, after looking at it for a bit, I decided it was simply printing an alternating symbol ("$" or "*") with a separator ("-") on odd numbered columns and starting and ending with "*". But, with a rule that on even odd rows there was no alternation in the symbol on the column after the midpoint. That is, quite honestly, how I saw the task.

That change in alternate or don't alternate was what I think I was getting at with the "direction" idea, but I had not thought it through.

In retrospect, I certainly could have incorporated the spaces and first and last symbols into the loops rather than as separates- a little lazy there, but that happens a lot when playing around, I suppose. I had forgotten about the shorthand if-then-else until the end of writing - I almost never use that shorthand.

Both of you (@ebeowulf17 @WBahn) used a function apart from main(), I also never considered doing that.

It's a cool little assignment. I wonder what the OP, or the assignor came up with...we may never know unless @LewisMF comes back and tells us :)
For context, I used a function primarily so that I could incrementally develop the solution. I was going to put it all in main for posting purposes, but didn't want to add in the code to get input from the user and decided that leaving it as a function call making it clear that this is a formality. My focus was on the core of the problem and not on the entire assignment solution.

My first pass solution did nothing but print out a triangle of '*' characters while printing '.' characters where there were spaces. This function started as a sequence of for() loops, then became a sequence of while() loops. I then copied that to a new function and modified it to print '<' characters on the left side of the "tree", '>' on the right, and '#' down the center. This demonstrated that I had identified the partitions correctly. I then replaced my sequence of while loops with a single for() loop (inside the outer for() loop) that used a cascaded if()-else structure. In doing this (which, in fact, took a few iterations to actually do correctly as it revealed a little logic error that was surprisingly innocuous in the prior approach). The third pass then used the symbol array to put the correct symbols down the center. The fourth pass focused on the alternating symbols on the right side of the tree (which worked on the first shot) and the fifth on the symbols on the left side of the tree (which did not work on the first, second, or third attempts, someone to my surprise). Then it was time to see if I could combine the if()-else blocks into a single statement using the conditional operator without screwing things up, which involved making minor changes and rerunning the program and then, when I did screw things up, figuring out why (usually didn't take long) and going back to the prior version and trying again.

The whole process took just under forty minutes. It took about fifteen minutes to get to the point where I had a program printing the entire tree correctly. The rest of it was spent condensing it from a clean, easy to read program into a very condensed (and almost certainly faster) program that I doubt anyone could make heads or tails of, including me a few days from now. Knowing what it does, it should be moderately easy to figure out how it does it. But if you were just given the program and asked to describe what it does without actually running it, well good luck.
 

WBahn

Joined Mar 31, 2012
30,062
As far as the utility or efficiency of the shorthand version, I don't know - maybe @WBahn can provide some details.
At one point the shorthand version would almost certainly have provided a significant performance improvement. Notice that that conditional operator is NOT a shorthand for an if()-else statement -- it just seems like that because of the level of abstraction that most humans, including most textbook authors, naturally think at. An if()-else construct alternatively executes statements while the conditional operator alternatively evaluates expressions. If the latter is your goal then there are benefits to using the latter construct. But today's optimizing compilers are scary-clever and often detect when if()-else constructs are being done just to provide alternative expression evaluation and generate the code accordingly. But they also tend to be a bit fragile in being able to spot those patterns.
 

WBahn

Joined Mar 31, 2012
30,062
I imagine with experience, the conditional operator will become easier for me to read quickly, but it's hard to imagine it would ever be as intuitive as if-then-else. The whitespace and indentations that typically go with ifs are easy on the eyes!
Yes, it becomes much more natural to think in terms of conditional expression evaluation and becomes a lot more natural to read, but I doubt anyone gets to the point where the visual cues provided by properly formatted if()-else code are not a significant aid.

On the other hand, if there are performance benefits to the more compact conditional operator, it would be good to know that too.

I must confess, when I first saw @WBahn's code, it looked like it must be a magic trick - how could it possibly work while being so small? Once I recognized that there are three separate conditionals embedded in 1 line of code, it was a little easier to understand, but it's still impressively compact and efficient.

I really like the use of a character array instead of more conditionals to decode the results of the modulo function into individual characters. Wish I had thought of that myself.

As usual, this is a great forum, exposing me to new ideas and different approaches all the time!
I could almost certainly have gotten around the use of the remainder operator by exploiting the Chinese Remainder Theorem, CRT, to evaluate an expression that reduces, modulo-256, to the correct ASCII code for the desired symbol. Since the char data type will perform that modulo operation for free, that would be a win. But it likely would have resulted in several multiplication operations, which are very expensive on some architectures, as opposed to the single remainder operation in my code. As an intermediate approach (and one that could get around the relatively co-prime required by CRT), I could have evaluated expressions that, mod-256, evaluated to distinct values and used conditionals on those or, to eliminate the conditionals, could have been used as indices into a larger array containing the symbols at the right places.
 

Thread Starter

LewisMF

Joined Nov 15, 2014
100
First of all thank you guys for the replies, it has been great going through the different solutions provided. I had to hand in the assignment before the end of November and I must say, my code went in the direction of the solution provided by @RaymondGenovese

The assignement I had to give in was a triangle, but after handing it in, we got told to make it into a rhombus instead. Also the program should only print a rhombus that is between 0 and 20 rows, so: 0 < rows < 20 - so I figured I just had to print an inverse triangle under the original triangle with n-1 rows...

Here is the code:
C:
#include <stdio.h>

int main(){

   char ch;
   char ch1 = '*';
   char ch2 = '-';
   char ch3 = '$';
   int rows, i,j;

   printf("\nNumber of rows? ");
   scanf("%d",&rows);
   printf("\n\n");

   if((rows > 20) || (rows < 1)) {
       printf("Number of rows out of range!");
   }

   else {
       for(int i = 0; i < rows; i++) {      
           for(int j = i; j < rows - 1; j++) {  
             printf(" ");
            }
              for(int j = 0; j <= 2*i; j++) {      
                if((j==0) || (j==2*i)) {   
                       ch = '*';
                    printf("%c", ch);
                 }
                 else{
                       if(j % 2 != 0) {
                        printf("%c", ch2);
                       }
                      else{
                        if ((i % 2 !=0) && (j==i+1)) {
                               printf("%c", ch);
                           }
                         else {
                               if(ch == '$') {
                               ch='*';
                               printf("%c", ch);  
                            }
                            else{
                                ch='$';
                                printf("%c", ch);  
                            }
                         }
                       }
                 }
               }
               printf("\n");
        }

        for(int i = rows-1; i >= 0; i--) {  
                for(int j = rows - i; j >= 1; j-- ) {  
                    printf(" ");
                }
                for(int j = 0; j <= ((2*i)-2); j++) {      
                    if((j==0) || (j==(2*i)-2)) {   
                           ch = '*';
                        printf("%c", ch);
                     }
                     else {
                         if(j % 2 !=0) {
                             printf("%c", ch2);
                         }
                         else {
                             if((i % 2 != 0) && (j == i+2)) {
                                 printf("%c", ch);
                             }
                             else if(j == i) {
                                 printf("%c",  ch);
                            }
                            else {
                                if(ch == '$') {
                                    ch = '*';
                                    printf("%c", ch);
                                }
                                else{
                                    ch = '$';
                                    printf("%c", ch);
                                }
                            }
                         }
                     }
                   }
                printf("\n");
            }

        }

    return 0;
}
Moderators note : you can use code=c in the tag.
 
Last edited by a moderator:
Top