Tuesday, October 16, 2007

SICP 1.1.7 (Factor)

I’m going back to the idea of treating each langauge in a separate post. Hopefully this will make the comments a bit more manageable (especially since I cross posted the last one onto my erlang blog as well).


There were several factor related comments on the last post that I wanted to address before I moved on to section 1.1.7.

Anonymous Is Factor “done”? Seems like it’s still very much a work in progress.

Can you comment on your choice of Factor and what has made it enjoyable for you?

Anonymous is right, Factor isn’t done yet. It is getting close though, and for what I’m doing, it seems to be “done enough”. I’m using it because I’ve been thinking about learning a stack based language for a while, just to learn to think a little bit differently. So far, it’s fun because it’s doing just that.

And the related:

Ed Borasky Factor? What does Factor have that Forth doesn’t have? At least Forth has an ANS standard and some vendors and a few thousand person-decades of programmer experience.

Ed, I think the big thing that drew me to Factor over Forth was an active Factor community that I sort of tripped over. Sometimes, it’s the squeaky wheel that draws a user.

The last two weren’t specifically related to Factor, but did show better ways of solving exercise 1.3 (without resorting to lists), so I worked up a factor solution in their vein:

: min ( a b -- a b ) 2dup <
  [ swap ] when ;
: top-two ( a b c -- d e ) min rot min -rot ;
: sum-of-squares ( a b -- c ) sq swap sq + ;
: sum-squares-of-larger ( x,y,z -- x ) top-two sum-of-squares ;

I agree that this is a much better way of solving the problem.

SICP 1.1.7

Okay, on to some code. I’m going to use the built-in sq and abs words from now on. There’s no sense in continuing to use mine when there’s a perfectly good version of the word already there. SICP built a series of procedures to find square roots using Newton’s method: sqrt, sqrt-iter, improve, average, and good-enough?. I rewrote these into the following Factor:

: average ( a b  -- c ) + 2 /f ;
: improve-guess ( num guess -- guess ) dup swapd /f average ;
: good-enough? ( num guess -- ? ) sq - abs 0.001 < ;
: sqrt-iter ( num guess -- guess ) 
  2dup good-enough? 
  [ ]
  [ swap dup swapd swap improve-guess sqrt-iter ] if ;
: sqrt ( num -- root ) 1.0 sqrt-iter ;

These are nice and fairly concise, but sqrt-iter seems pretty ugly. I checked back in with the good folks on #concatenative (who have been immensely helpful) and they agreed, saying “swap dup swapd swap is a code smell”. They pointed out the dupd word, which does the same thing. so a better version of sqrt-iter would be:

: sqrt-iter ( num guess -- guess )
   2dup good-enough? 
   [ ]
   [ dupd improve-guess sqrt-iter ] if ;

Which can be improved still further into:

: sqrt-iter ( num guess -- guess )
   2dup good-enough? 
   [ dupd improve-guess sqrt-iter ] unless ;

I’m sure there are ways this could be improved yet further. Leave me a comment with your ideas. I’ll try to post my Ruby version tomorrow and Erlang on Thursday.

No comments: