Byte
Byte

Reputation: 509

Multiple procedures within a racket conditional?

I am working on Project Euler problem # 3 in racket and am not sure how to use multiple procedures as one procedure in conditionals. Normally when using a procedural language, I'd use a 'while' loop and do some variable updating (hence why I use 'set!' in the following code (if there is a better way- as I've read from related questions/answers that it's not good practice to mutate variables in racket, then please offer an alternative) and have several procedures if a conditional is true. Putting multiple instructions as one output statement (by enclosing it in '()' doesn't seem to work but I am sure there is some way to accomplish this.

I've considered breaking it up into small functions and then making 2 larger functions (1 for the 'then' area and 1 for the 'else area) but it doesn't seem like the right solution (or at least not the conventional approach).

I have also included the error message for this specific code if that helps.

; Project Euler # 3
; What is the largest prime factor of the number 600851475143?

(define pL (list null))
(define divisor 2)

(define (pF dividend)
  (if (= dividend 1)
      pL
      (if (= (remainder dividend divisor) 0)
          **(**(append pL (list divisor))
          (set! dividend (/ dividend divisor))
          (set! divisor 2)
          (pF dividend)**)**
          **(**(set! divisor (+ divisor 1))
          (pF dividend)**)**)))

(pF 33)

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: '(() 11)
  arguments.:

I've made the 'then' and 'else' areas in bold (if it doesn't come out in bold, then it will be surrounded by ** ** (example: example). Hopefully it won't ruin the readability.

Thank you.

Upvotes: 2

Views: 4032

Answers (2)

C. K. Young
C. K. Young

Reputation: 223183

@HyperZ's answer is correct. However, extensive use of set! is not generally considered idiomatic Scheme, so I want to show a different way to go about it. I'll use the same algorithm, just reformulated in idiomatic Racket:

(define (factors dividend)
  (if (= dividend 1)
      '()
      (let loop ((divisor 2))
        (define-values (q r) (quotient/remainder dividend divisor))
        (if (zero? r)
            (cons divisor (factors q))
            (loop (add1 divisor))))))

This should be pretty straightforward to read:

  1. If the dividend is 1, return an empty list.
  2. Otherwise, look for a suitable divisor, starting with 2:
    1. Get the quotient and remainder from dividing the dividend by the divisor.
    2. If the remainder is zero, the result is the divisor, plus the factors of the quotient.
    3. Otherwise, add 1 to the divisor, and try again.

You can actually make the code even more readable by using a for/first comprehension instead of a manual inner loop:

(define (factors dividend)
  (if (= dividend 1)
      '()
      (for/first ((divisor (in-naturals 2))
                  #:when (zero? (remainder dividend divisor)))
        (cons divisor (factors (quotient dividend divisor))))))

Upvotes: 2

KeV
KeV

Reputation: 2891

To group multiple instructions in one statement you can use begin.

(if (= (remainder dividend divisor) 0)
    (begin (append pL (list divisor)) // Then 
           (set! dividend (/ dividend divisor))
           (set! divisor 2)
           (pF dividend))
    (begin (set! divisor (+ divisor 1)) // Else
           (pF dividend)))

Note that the return value of the last instruction will be the result of (begin ...).

However running the code with these changes will produce '(()). This is because (append pL (list divisor)) will return a new list. Hence this instruction is useless because you aren't catching the result.

I suppose you want to do (set! pL (append pL (list divisor))). Now it won't return an empty list anymore.

You would also want your pL variable to be defined like this (define pL '()). Now the first element of the result won't be an empty list anymore (which was caused by the first append).

I tested the modified code, it should work :)

Upvotes: 2

Related Questions