まず、入れ子構造をインデントで表現するスタイルが良いので、視覚的に分かりやすいです。また、それぞれif
の句、結果および代替を独自の行に配置します。
(define (isPrimeHelper x k)
(if (= x k)
#t ; consequent
(if (= (remainder x k) 0) ; alternative
;; ^^ indentation
#f ; consequent
(isPrimeHelper x (+ k 1))))) ; alternative
(define (printPrimesUpTo n)
(define result '())
(define (helper x)
(if (= x (+ 1 n))
result ; consequent
(if (isPrime x) ; alternative
(cons x result) )) ; no alternative!
;; ^^ indentation
( helper (+ x 1)))
( helper 1 ))
helper
これで、関数が最後に行うことは、常にインクリメントされた値で自分自身を呼び出すことであることがはっきりとわかりますx
。停止条件はありません。つまり、これは無限ループです。
もう 1 つのことは、呼び出しによっての値(cons x result)
がまったく変更されないことです。result
そのためには、次のように設定する必要があります(set! result (cons x result))
。begin
また、この式は値ではなく副作用のために評価されるため、グループに入れる必要があります。
(define (helper x)
(if (= x (+ 1 n))
result
(begin
(if (isPrime x)
(set! result (cons x result)) ) ; no alternative!
(helper (+ x 1)) )))
通常、 を明示的に使用することset!
は悪いスタイルと見なされます。ループを表現する標準的な方法の 1 つは、名前付き letを使用した末尾再帰コードとして、通常は正規名 " " を使用することです (ただし、任意の名前にすることができます)。loop
(define (primesUpTo n)
(let loop ((x n)
(result '()))
(cond
((<= x 1) result) ; return the result
((isPrime x)
(loop (- x 1) (cons x result))) ; alter the result being built
(else (loop (- x 1) result))))) ; go on with the same result
これは、テールコール最適化が存在する場合、実際には以前のバージョンと同等です。