Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-

Happy Numbers

Name: Tynach 2009-09-03 19:34

Hey, I just found this board on 4chan (didn't know about it before), and I wanted to know what you guys thought of my program that checks to see if a number is happy. It was originally written in Python, but as a means to learn C, I re-wrote it in C a while back. Recently, I decided I wanted to find out if I really understood pointers as well as I thought, so I re-wrote the C version from scratch, though implemented some of the same code (though heavily modified to work with my new format) to use pointers so it uses less RAM.

Here's the source:

#include <stdio.h>

void check(char *);
char checkh(long long int, long long int *);
void usragain(char *, char *);
void getnum(long long int *);
void flushin();

int main()
{
    char run = 1;
    char state = 0;
    printf("Happy Number Checker v 2.0\n\n");
    while(run == 1) {
        switch (state) {
        case 0:
            check(&state);
            break;
        case 1:
            usragain(&run, &state);
            break;
        }
    }
    return 0;
}

void check(char *ret)
{
    long long int orig;
    getnum(&orig);
    *ret = checkh(orig, &orig);
    if (*ret == 0) {
        printf("%lld is not happy. Try again.\n\n", orig);
    } else if (*ret == 1) {
        printf("%lld is happy!\n\n", orig);
    }
}

char checkh(long long int guess, long long int *orig)
{
    long long int tmp = guess;
    long long int sum;
    char digit;
    char loop = 1;
   
    while (loop == 1) {
        sum = 0;
        while (tmp != 0) {
            digit = tmp % 10;
            sum += digit * digit;
            tmp /= 10;
        }
        if (sum == 1) {
            loop = 0;
            return 1;http://www.qso.com/carlautta/colin/Beta/happy.c
        } else if (sum == 42 || sum == *orig) {
            loop = 0;
            return 0;
        } else {
            loop = 1;
            tmp = sum;
        }
    }
}

void usragain(char *yn, char *state)
{
    *yn = 2;
    printf("Would you like to check another number (y/n)? ");
    while (*yn == 2) {
        flushin();
        scanf("%c", yn);
        printf("\n");
        if (*yn == 'y') {
            *yn = 1;
            *state = 0;
        } else if (*yn == 'n') {
            *yn = 0;
        } else {
            printf("Invalid input, type y or n.\n");
            *yn = 2;
        }
    }
}

void getnum(long long int *guess)
{
    int test = 0;
    while (test == 0) {
        printf("Please insert a number: ");
        test = scanf("%lld", guess);
        if (test == 0) {
            flushin();
            printf("\n\nInvalid input. Try again.\n\n");
        }
    }
}

void flushin()
{
    int ch = 0;
    while ((ch = getc(stdin)) != EOF && ch != '\n') {
        continue;
    }
}

I'm a newb to text boards here on 4chan, so I may be missing a way to insert code properly (in some forums it's and). Please tell me if I fail at this and should become an hero.

Name: Anonymous 2009-09-03 19:39

Never forget to Buttsort your flushin()

[b][i]void flushin() { int ch = 0; while ((ch = getc(stdin)) != EOF && ch != '[u][/u]n') { continue; } }[/i][/b]

Name: Anonymous 2009-09-03 19:40

Gee, what's wrong with the people lately? Please use the code tags.

Name: Anonymous 2009-09-03 19:53

>>1
Hi FrozenVoid

Name: Anonymous 2009-09-03 20:03

Name: Anonymous 2009-09-03 20:17

>>1 IHBT

Name: Tynach 2009-09-03 21:31

I just realized 2 things:

1: That the post about using the code tags was not sarcasm, and I will use them from now on;
2: That I posted the URL to a copy of the code, inside the code, by accident, preventing it from compiling.

Both of these are fixed now.

#include <stdio.h>

void check(char *);
char checkh(long long int, long long int *);
void usragain(char *, char *);
void getnum(long long int *);
void flushin();

int main()
{
    char run = 1;
    char state = 0;
    printf("Happy Number Checker v 2.0\n\n");
    while(run == 1) {
        switch (state) {
        case 0:
            check(&state);
            break;
        case 1:
            usragain(&run, &state);
            break;
        }
    }
    return 0;
}

void check(char *ret)
{
    long long int orig;
    getnum(&orig);
    *ret = checkh(orig, &orig);
    if (*ret == 0) {
        printf("%lld is not happy. Try again.\n\n", orig);
    } else if (*ret == 1) {
        printf("%lld is happy!\n\n", orig);
    }
}

char checkh(long long int guess, long long int *orig)
{
    long long int tmp = guess;
    long long int sum;
    char digit;
    char loop = 1;
   
    while (loop == 1) {
        sum = 0;
        while (tmp != 0) {
            digit = tmp % 10;
            sum += digit * digit;
            tmp /= 10;
        }
        if (sum == 1) {
            loop = 0;
            return 1;
        } else if (sum == 42 || sum == *orig) {
            loop = 0;
            return 0;
        } else {
            loop = 1;
            tmp = sum;
        }
    }
}

void usragain(char *yn, char *state)
{
    *yn = 2;
    printf("Would you like to check another number (y/n)? ");
    while (*yn == 2) {
        flushin();
        scanf("%c", yn);
        printf("\n");
        if (*yn == 'y') {
            *yn = 1;
            *state = 0;
        } else if (*yn == 'n') {
            *yn = 0;
        } else {
            printf("Invalid input, type y or n.\n");
            *yn = 2;
        }
    }
}

void getnum(long long int *guess)
{
    int test = 0;
    while (test == 0) {
        printf("Please insert a number: ");
        test = scanf("%lld", guess);
        if (test == 0) {
            flushin();
            printf("\n\nInvalid input. Try again.\n\n");
        }
    }
}

void flushin()
{
    int ch = 0;
    while ((ch = getc(stdin)) != EOF && ch != '\n') {
        continue;
    }
}


Are there any bugs you guys see, or any ways I could optimize it? I welcome critique.

Name: Tynach 2009-09-03 21:35

>>4
I don't understand what you mean.

>>6
I don't know 90% of the acronyms I hear. Clarify?

>>2
Never. Note, however, that I had to write a flushin() because fflush didn't work on stdin... It's only for output streams. Also, flushin() is based on a function I found elsewhere as a solution to this very problem, though slightly modified by myself.

Name: Anonymous 2009-09-03 21:56

>>8
I weep thinking about how those mean guys here at /prog/ are going to hax your anus.

Name: Haxus the Helpful 2009-09-03 22:28

>>8
>>4 I don't understand what you mean.
FrozenVoid is a teenage poster on this board who happens to write the shittiest C code known to man.  Some think he is just a troll, some think he really is a dumbass pretentious teenager who doesn't know anything.  Regardless, your code is x100 better than anything FrozenVoid writes.

I don't know 90% of the acronyms I hear. Clarify?
I Have Been Trolled

I'll take a look at your code in a few.

Name: Anonymous 2009-09-03 22:57

Brotip: Don't post your email in the email field.

Name: Anonymous 2009-09-03 22:59

I don't know 90% of the acronyms I hear. Clarify?
SICP
Structure and Interpretation of Computer Programs.
It's what you need to know to be cool.
TYCJSMUA, too.

Name: Anonymous 2009-09-03 23:11

1. You should give your functions names that will tell the reader what the function does without having to analyse the code.  For instance, what does check() check?  Or checkh() for that matter?  Why are there two?  It may make sense if you wrote the program, but if you come back to this code in a couple months it may be confusing, and certainly if another person wanted to work on your code they'd have to have a good look at both functions to understand what they mean.  As it stands, only getnum() and flushin() tell me exactly what it does just by the name of the function.

2. Instead of using long long int, include stdint.h and use uint64_t as the datatype.  This will portably give you an unsigned 64-bit integer, and is also much less ugly than "unsigned long long int".  (Note: You may need to use the %llu token instead of %llu in your printf if you do this.)

3. Using a char to define the state datatype is unnecessary and misleading.  Use an int, or int8_t instead.

4. In your flushin() function:
   while ((ch = getc(stdin)) != EOF && ch != '\n') {
       continue;
   }
It is unnecessary to use "continue" in the body of the loop.  You can just end the while(); with a semi-colon like that.  You also don't need to store getc(stdin) in a variable.  This function could be re-written as

void flushin()
{
    while ((getc(stdin) != EOF) && (getc(stdin) != '\n'));
}


5. Why does usragain() need to take a char* for the yn argument?  You can just declare char yn local to the function.

6. Again you are performing math with a char in checkh().  Use an integer if you want to store numbers -- if it must be 1 byte, use int8_t or uint8_t as defined in stdint.h.

If you fix this shit then maybe I'll take another look at how you're doing things...  I don't feel like examining every little problem, but for instance, you have a variable defined in the main function (run) that keeps your main while loop running.  However, when the program is terminated, run is not set to 0 in main.  You pass a pointer to it to the usragain function, and that function first stores a character in the pointer and then changes it to either 0 or 1 again.  That's awful design, in my opinion.

Name: Anonymous 2009-09-03 23:11

Here's a CL implementation as I'm bored:

(defun split-digits (n)
  (labels ((rec (n acc)
         (multiple-value-bind (quot rem) (truncate n 10)
           (if (zerop quot)
           (cons n acc)
           (rec quot (cons rem acc))))))
    (rec n nil)))

(defun square (x) (* x x))

(defun sum-of-squares (list)
  (apply #'+ (mapcar #'square list)))

(defun square-and-sum-digits (n)
  (sum-of-squares (split-digits n)))

(defun is-happy-number (n)
  (let ((sum n))
    (loop      
       (setf sum (square-and-sum-digits sum))
       (cond
     ((= sum 1) (return t))
     ((= sum 4) (return nil))))))

(defun find-happy-numbers (list)
  (remove-if-not #'is-happy-number list))

(defun range (n m)
  (loop for i from n to m collect i))

(defun find-happy-numbers-under-value (n)
  (find-happy-numbers (range 1 n)))

; Test run:

;(find-happy-numbers-under-value 500)
; =>
;(1 7 10 13 19 23 28 31 32 44 49 68 70 79 82 86 91 94 97 100 103 109 129
; 130 133 139 167 176 188 190 192 193 203 208 219 226 230 236 239 262 263
; 280 291 293 301 302 310 313 319 320 326 329 331 338 356 362 365 367 368
; 376 379 383 386 391 392 397 404 409 440 446 464 469 478 487 490 496)


Code was extended to find happy numbers within a range/list too.

Name: Anonymous 2009-09-03 23:15

oops, make that:

       (case sum      
     (1 (return t))
     (4 (return nil)

Name: Anonymous 2009-09-03 23:16

>>13
(Note: You may need to use the %llu token instead of %llu in your printf if you do this.)
Oh duh, I meant "use %llu instead of %%lld".  sage for stupid typo.

ALSO, I said you could use
while ((getc(stdin) != EOF) && (getc(stdin) != '\n'));
but I just realized that is a very bad idea.  Store getc(stdin) in ch or else it will be checking the buffer twice.

void flushin()
{
    char ch;
    while (((ch=getc(stdin)) != EOF) && (ch != '\n'));
}

Name: Anonymous 2009-09-03 23:51

What the hell is a happy number.

Name: Anonymous 2009-09-04 0:01

more like gay number, amirite

Name: Anonymous 2009-09-04 0:55

>>14

(defun sum-of-squares (list)
  (apply #'+ (mapcar #'square list)))

Avoid defining SQUARE since you use it only once in your code, and write SUM-OF-SQUARES like this:
(reduce #'+ list :key (lambda (x) (expt x 2)))
Or with LOOP:
(loop for i in list summing (expt i 2))

IS-HAPPY-NUMBER can be rewritten (and renamed) to:

(defun happy-numberp (n)                           
  (loop for sum = n then (square-and-sum-digits sum)
        if (= sum 1) do (return t) 
        else if (= sum 4) (return)))


You can write split-digits as:

(defun split-digits (n &optional (divisor 10))
  (loop for i = n then (car x)                      
        for x = (multiple-value-list (truncate i divisor))
        when (zerop i) do (return l)
        collecting (cadr x) into l))

Name: The Doctor 2009-09-04 1:01

>>17
Well, for starters, it's--

No, really, I'm done explaining it to people. As if.

Name: Anonymous 2009-09-04 1:33

>>19
I'm aware of how to do it using LOOP, however I chose to use a tail-recursive version since /prog/ has many Scheme fans who seem to hate its usage.

Btw... shouldn't that name be written happy-number-p instead of happy-numberp. The CL naming conventions that I've read about say that one should use hyphen before p only if it was previously used and there's an exception to that rule when one prefixes the function name with a type, such as string-lessp and not string-less-p (the string- part is ignored in this case when one decides wether to hyphen the p).

Name: Anonymous 2009-09-04 1:38

>>21
I would write happyp.

Name: Anonymous 2009-09-04 1:51

HAPPY HAPPY HAPPY HAPPY HAPPY

Name: Anonymous 2009-09-04 2:37

>>21
I'm aware of how to do it using LOOP, however I chose to use a tail-recursive version since /prog/ has many Scheme fans who seem to hate its usage.
/prog/ has a mighty assembly of morons and a few people who can code, and even those are not credible enough to voice their own opiniond be taken seriously. Support for TCO, unlike scheme, is optional in an implementation. Support for all cases where TCO might apply is optional in an implementation (although this one is trivial and one would better change implementation instead of code). You're not required to use LOOP of course; DO and DO* are available (if you're interested, take a look at MVDO and MVDO* from PGs On Lisp, which provide additional features). What's more absurd is that while you seem to protest against the use of LOOP (or at least sympathize with those who do), IS-HAPPY-NUMBER makes use thereof to control flow. You can write this instead:

(defun happy-number-p (n)                 
  (do ((n n (square-and-sum-digits n)))
      ((member n '(1 4)) (= n 1)))


The CL naming conventions that I've read about
Conventions by whom? Are they normative or guidelines? I'd like to read them too. I have never read any naming conventions.

Name: Anonymous 2009-09-04 3:09

>>24
Support for TCO, unlike scheme, is optional in an implementation.

Most serious implementations have it, at least if you use the apropriate optimize declarations.

You're not required to use LOOP of course; DO and DO* are available
I do use all 3 in my code where appropriate. I'll take a look at MVDO and MVDO*, as I need to resume reading On Lisp anyway.

What's more absurd is that while you seem to protest against the use of LOOP (or at least sympathize with those who do),

I don't. I use LOOP freely in my code, but I simply wanted to avoid having pointless arguments about this, and just wrote a tail-recursive version. It seems that regardless if I use an interation construct or a tail-call version, someone will complain about it. It was just a toy example. Overall, the code I posted has many inefficiencies, and could be optimized in many ways. Thank you for the REDUCE variant, it seems that I have forgotten about the CALL-ARGUMENTS-LIMIT issue (not a real issue for the implementation I'm using, as the value of CALL-ARGUMENTS-LIMIT is #x1FFFFFFF, the same as MOST-POSITIVE-FIXNUM).

Conventions by whom? Are they normative or guidelines? I'd like to read them too. I have never read any naming conventions.

I don't know if there's a document describing them, but pretty much all CL code that I've read had used common naming conventions. I'm not sure if the Hyperspec talks about them, but the ones from http://www.cliki.net/Naming%20conventions seem to be used almost everywhere.

Name: Anonymous 2009-09-04 3:37

doh ho ho, happy numbers.
i wonder who came up with that name and why.

Name: Anonymous 2009-09-05 17:50

Where did this young man go?

Name: Anonymous 2009-09-05 18:06

>>27
He found out that /prog/ is filled with unfunny memes and left us :(

Name: UMH memesmith !gNlkr4vCuc 2009-09-05 19:35

OpenCOBOL passes many of the tests included in the NIST sponsored COBOL 85 test suite. While it passes over 9000 of the tests, OpenCOBOL does not claim conformance to any level of COBOL Standard.

Name: Tynach !QP7ssuHlgA 2009-09-06 17:58

Oh, I've been around.

Haven't re-written it yet, that's why I haven't posted here lately.

Name: Anonymous 2009-09-06 23:56

>>29
COBOL
over 9000
Back to um.

I'm so confused.

Name: Anonymous 2009-09-07 0:09

>>31
I'm so confused.
Thank you  for sharing your feelings now tell me what does being "confused" mean to you?

Don't change these.
Name: Email:
Entire Thread Thread List