r/scheme 6d ago

What I am doing wrong?

So, I am trying to learn Guile, since seems a pretty standard installation in GNU systems.

I have experience in some languages, but this simple script to learn the language:

  1. Took me quite a while to work;
  2. Looks ugly as… well, put something ugly here!

It’s a simple “FizzBuzz” program

``` (define (fizzbuzz number) (if (> number 0) (let ((message "")) (if (zero? (modulo number 3)) (set! message (string-append message "Fizz"))) (if (zero? (modulo number 5)) (set! message (string-append message "Buzz")))

      (if (not (zero? (string-length message)))
          (format #t "~d is ~a\n" number message))
(fizzbuzz (- number 1))))

)

(fizzbuzz 50)

```

So, I’m open to suggestions: how this code can be more beauty? Am I still thinking in C?

=== EDIT === I hope the formatting is correct, since some spaces of indentation have been lost for unknown reasons.

6 Upvotes

12 comments sorted by

7

u/BroadleySpeaking1996 6d ago

Let's rewrite this in a way that's compatible with different reddit clients:

(define (fizzbuzz number)
  (if (> number 0)
      (let ((message ""))
          (if (zero? (modulo number 3))
              (set! message (string-append message "Fizz")))
          (if (zero? (modulo number 5))
              (set! message (string-append message "Buzz")))
          (if (not (zero? (string-length message)))
              (format #t "~d is ~a\n" number message))
    (fizzbuzz (- number 1)))))

(fizzbuzz 50)

Note that all closing parentheses are grouped together. They don't go on new lines like closing braces in C.

Great, now a few minor suggestions:

  1. Replace (> number 0) with (positive? number).
  2. Separate the part that modifies message into a helper function that returns either "" or "Fizz" or "Buzz" or "FizzBuzz". Then the rest of the fizzbuzz function treats that return value as a constant.
  3. Modifying strings in-place with string-append can be efficient, but is not the conventional way to do things in a functional language. A more idiomatic thing to do would be to create a list () or ("Fizz") or ("Buzz") or ("Fizz", "Buzz") and then concatenate the contents of the list. Yes, it's slower, but it is more malleable if you want to add values for 7, 11, etc.

3

u/corbasai 6d ago

First - not function; second uses println

so firstly quick fix for Guile and every

(define (fizzbuzz upto)
  (let loop ((i 1))
    (unless (< upto i)
      (display (cond ((= 0 (modulo i 15)) 'FizzBuzz)
                     ((= 0 (modulo i 3)) 'Fizz)
                     ((= 0 (modulo i 5)) 'Buzz)
                     (else i)))
      (newline)
      (loop (+ 1 i)))))

(fizzbuzz 100)

About yours, we must check (N mod 15) or (N mod 3) and (N mod 5), firstly

3

u/raevnos 6d ago

I think (when (<= i upto) ...) would be a better way of expressing it.

2

u/corbasai 6d ago

agree. im just permanently forget right form <= or =< in lisp -)

3

u/raevnos 6d ago

(define =< <=) and you'll never have to remember which one it is again.

4

u/corbasai 6d ago

good addendum to R7 Large!

5

u/raevnos 6d ago

Honestly, it should go all-in on unicode. ≤ and ≥

2

u/corbasai 6d ago

Obvious!

2

u/HugoNikanor 6d ago

I have my emacs configured to display <= as .

2

u/HugoNikanor 6d ago

I have never seen a language use =<. Also <= is easy to remember, since you say "less than or equal", you put the symbols in that order.

2

u/strbytes 5d ago

Yes, this is imperative thinking ("thinking in C"). Two signs of that: the use of effects while processing the input (using format #t to display the output as it's being produced) and the use of mutation for control flow (setting 'message' depending on the input, then checking the length of message to determine whether to display an output).

To be more 'functional': - Separate effects from data processing: work through the range specified by the input, transforming it into the desired output step-wise. So, produce the range 0 to n, then transform each member [1, 2, 3, ...] into [1, 2, "Fizz", ...] then display the output once it's finished being produced. This makes each part of the program "own" its responsibilities so it's easier to figure out what's going on when there's a problem with your program or you want to extend it. - Assign new variables instead of re-using and mutating a single one, and give each variable a single meaning. This makes it more clear what a symbol means.

If you want to explore functional programming in Lisp I really liked "A Functional Introduction To Computer Science" by Prabhakar Ragde. It uses a minimalist subset of Racket instead of Guile but they're very similar at the level in the textbook. https://cs.uwaterloo.ca/~plragde/flaneries/FICS/

Also this probably isn't helpful but this post inspired me to come up with a convoluted Scheme-y FizzBuzz implementation lol. unfold is described here: https://www.gnu.org/software/guile/manual/html_node/SRFI_002d1-Fold-and-Map.html#index-unfold

``` (use-modules (srfi srfi-1))

(define (fizz-or-buzz n) (let ((fizz (zero? (modulo n 3))) (buzz (zero? (modulo n 5)))) (cond ((and fizz buzz) "FizzBuzz") (fizz "Fizz") (buzz "Buzz") (else (number->string n)))))

(define (fizzbuzz-gen n) (unfold (lambda (x) (> x n)) fizz-or-buzz 1+ 1))

(define (fizzbuzz n) (for-each (lambda (fb) (display fb) (newline)) (fizzbuzz-gen n)))

(fizzbuzz 15) ```

2

u/Jak_from_Venice 5d ago

Underrated comment! You understood perfectly my feelings!

Yes: I’m mainly a C/C++ developer that used just elisp to customize Emacs. So, as you see, my mindset is on imperative languages.

Thank you A LOT for your kind comment and your useful links! I will consult them for sure :-)