[racket] First try with hashes in Racket

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Tue Apr 3 00:40:20 EDT 2012

Joe Gilray wrote at 04/03/2012 12:00 AM:
> *The bad news*: it still feels a little clunky.  It has a lot of 
> imperative statements and the memoization in the hash table feels too 
> much like a side-effect to me.
>
> Any more help?

Here's an implementation without mutations.  It's simple brute-force 
with memoization (I'm not aware of an algorithmic or numeric shortcut).

Note that it uses immutable hashes functionally, passing "memo" hashes 
into and out of every place that we would want to update the memoization.

Note also that I originally had "answer-n-list" to record multiple "n" 
having the same term-count, but then I added the "error" form to verify 
that this algorithm never has more than one "n" in "answer-n-list".

#lang racket/base

(define (f n)
   (if (even? n)
       (/ n 2)
       (+ 1 (* 3 n))))

(define (get-terms-count memo n)
   ;; Note: We don't have to detect cycles, at least not for max-n 999,999
   (cond ((hash-ref memo n #f) => (lambda (n-steps)
                                    (values memo n-steps)))
         (else (let*-values (((next-n)            (f n))
                             ((memo next-n-steps) (get-terms-count memo 
next-n))
                             ((n-steps)           (+ 1 next-n-steps)))
                 (values (hash-set memo n n-steps)
                         n-steps)))))

(define (euler-14 (max-n (- #e1e6 1)))
   (let loop ((answer-n-list      '(1))
              (answer-terms-count 1)
              (n                  max-n)
              (memo               #hash((1 . 1))))
     (if (= 1 n)
         (values answer-n-list answer-terms-count)
         (let-values (((memo n-terms-count) (get-terms-count memo n)))
           (cond ((< n-terms-count answer-terms-count)
                  (loop answer-n-list
                        answer-terms-count
                        (- n 1)
                        memo))
                 ((> n-terms-count answer-terms-count)
                  (loop (cons n '())
                        n-terms-count
                        (- n 1)
                        memo))
                 (else ; (= n-terms-count answer-terms-count)
                  (error 'euler-14 "seems to be unreachable for max-n 
999,999")
                  (loop (cons n answer-n-list)
                        answer-terms-count
                        (- n 1)
                        memo)))))))

(euler-14)

Neil V.

-- 
http://www.neilvandyke.org/

Posted on the users mailing list.