Re: [racket-users] Functions that are sometimes tail recursive

2019-01-15 Thread Matthias Felleisen


> On Jan 15, 2019, at 10:13 AM, Will Jukes  wrote:
> 
> Hi everyone,
> 
> I was helping a student the other day with a problem where they were to write 
> a recursive function that uses trial division to return a list of the prime 
> factors of an integer (no fancy optimizations, these are high school kids who 
> don't have a lot of number theory). It looked something like this:
> 
> (define (factor n)
> (let loop ([k n] [d 2])
> (cond [(> d k)  '()]
>   [(zero? (modulo k d))  (cons d (loop (quotient k d) d))]
>   [else  (loop k (add1 d))])))
> 
> 
> 
> The purpose of this lesson was to illustrate the difference between proper 
> tail recursion and regular recursion (our course calls it natural recursion), 
> but it occurred to me that this function is actually tail recursive in the 
> else clause but not in the second clause. This struck me as highly desirable 
> behavior in this case (and possibly others), since even those numbers with 
> relatively many prime factors wouldn't create a very deep stack -- most 
> recursive calls would go to the third clause. I wanted to double check though 
> that Racket (or other Scheme compilers) would recognize the distinction and 
> optimize those calls that can be optimized. My understanding is that they 
> almost certainly would be, but I'm still learning about low-level programming 
> and the implementation is a bit of a black box to me.


It doesn’t optimize such calls, Racket implements them properly, that is, w/o 
consumption of stack space. 

[[ If your student’s curriculum is based on HtDP (your “natural recursion” 
phrase suggests so), he is likely to use Beginning or Intermediate Student 
language and the emphasis is on properly designing the function, not 
performance details. Here the key is that good design calls for an auxiliary 
accumulator-style function. As a teacher, I’d also be happier if the two were 
factored into two explicit function definitions (let-loop defines a function 
implicitly) 

;; N -> [Listof N] 
;; determine the factors of n 
(define (factor n0) 
  (local (;; N N -> [Listof N]
;; accu statement: n0 … n is factor-free of 2 … d (which is quite 
sophisticated for a high school student; good high school) 
(define (factor/acc n d)
   (cond 
  [(> d n) ‘()]
  [else (if (= (modulo n d) 0) (cons d (factor/acc (quotient n 
d) d)) (factor/acc n (add1 d)))])))
(factor/acc n0 2))

HtDP shows an alternative, simpler solutions for less talented college 
students. ]] 

— Matthias



-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] Functions that are sometimes tail recursive

2019-01-15 Thread Will Jukes
Hi everyone,

I was helping a student the other day with a problem where they were to 
write a recursive function that uses trial division to return a list of the 
prime factors of an integer (no fancy optimizations, these are high school 
kids who don't have a lot of number theory). It looked something like this:

(define (factor n)
(let loop ([k n] [d 2])
(cond [(> d k)  '()]
  [(zero? (modulo k d))  (cons d (loop (quotient k d) d))]
  [else  (loop k (add1 d))])))



The purpose of this lesson was to illustrate the difference between proper 
tail recursion and regular recursion (our course calls it natural 
recursion), but it occurred to me that this function is actually tail 
recursive in the else clause but not in the second clause. This struck me 
as highly desirable behavior in this case (and possibly others), since even 
those numbers with relatively many prime factors wouldn't create a very 
deep stack -- most recursive calls would go to the third clause. I wanted 
to double check though that Racket (or other Scheme compilers) would 
recognize the distinction and optimize those calls that can be optimized. 
My understanding is that they almost certainly would be, but I'm still 
learning about low-level programming and the implementation is a bit of a 
black box to me.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.