Reputation: 509
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
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:
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
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