2
\$\begingroup\$

I've been chewing through a bit of The Little Schemer and the Peano examples got me thinking about operation size and time.

I got some help on making Peano Multiplication linear -- however (time ...) didn't show much difference between a lexically scoped version and the let-loop version.

I do however find a massive difference between the lexically scoped and let-loop versions when minting a Peano Exponent function.

The obvious question is why it makes such a difference (or perhaps it does not and my attempt at a lexically scoped Peano Exponent has some fatal error)?

-- i had thought i understood what the let-loop was doing in terms of storing the value in the parent enclosure (making the two roughly equivalent); in which way is this wrong?

;; lexically scoped version of Peano Exp
(define (exxp-lex b ex)
  (define (exxp-aux ex prod)
    (cond
      ((zero? ex) 1)
      ((= ex 1) (mX-let b prod))
      (else
        (exxp-aux (- ex 1) (mX-lex b prod)))))
  (exxp-aux ex 1))


;; let version of Peano Exp
(define (exxp-let b ex)
  (let loop ((ex ex)
              (prod 1))
    (cond
      ((zero? ex) 1)
      ((= ex 1) (mX-let prod b))
      (else
        (loop (- ex 1) (mX-let prod b))))
             ))

If it's relevant, I am using petite-chez scheme.

For reference, here are the Peano Multiplication functions.

;; a lexically scoped version Peano Multiplication
(define (mX-lex n m)
  (define (mX-aux m product)
    (if (zero? m)
      product
      (mX-aux (- m 1) (+ product n))))
  (mX-aux m 0))


;; using let for Peano Mult
(define (mX-let n m)
  (let loop ((m m)
             (product 0))
    (if (zero? m)
      product
      (loop (- m 1) (+ product n)))))
\$\endgroup\$
3
  • 1
    \$\begingroup\$ This doesn't address your question, but just as a point of interest: all the functions above are lexically scoped and in fact Scheme is always lexically scoped. What distinguishes the ones you're referring to as lexically scoped is that they use "internal definitions" whereas the others use "named let". As I said, this is neither here nor there regarding the question; I only mention it since you seem interested in learning more about Scheme. \$\endgroup\$
    – jbm
    Dec 11, 2013 at 3:10
  • \$\begingroup\$ @jbm thanks. I am very interested - is there a major difference between function operation/execution in either case? \$\endgroup\$
    – ricardo
    Dec 11, 2013 at 6:14
  • \$\begingroup\$ They should be the same in this case, as would an equivalent use of letrec. (In fact, many implementations translate named let to letrec, and may do something similar with internal definitions). However, locally scoped mutually recursive procedures can be expressed with internal definitions and letrec but not with named let. I see it as coming down to what best fits the logic and flow of your function(s), there shouldn't be a big performance difference between otherwise equivalent binding forms. \$\endgroup\$
    – jbm
    Dec 11, 2013 at 19:20

1 Answer 1

2
\$\begingroup\$

In exxp-aux, you call (mX-let b prod) and (mX-lex b prod). You probably meant the two calls to be identical.

In exxp-let, you call (mX-let prod b).

mX-lex and mX-let are both implemented such that they would be faster if you call them with the larger multiplicand first and the smaller one second, because the second parameter controls the number of additions. Since prod is likely to be larger than b, your exxp-aux will be slower.

\$\endgroup\$
4
  • \$\begingroup\$ +1, accepted. wow, i changed the order of the (mX-fun b prod) calls to (mX-fun prod b)` and the speed difference disappeared. Had no idea. Now the only thing that's different is the byte size of the operation -- why is the let-loop so much smaller? \$\endgroup\$
    – ricardo
    Dec 11, 2013 at 10:24
  • \$\begingroup\$ Funny how you had a 50-50 chance of getting the fast version, being oblivious to the performance difference due to the parameter ordering. I have no idea how to explain the difference in code size, though. \$\endgroup\$ Dec 11, 2013 at 10:28
  • \$\begingroup\$ How costly is testing parameter size and setting the order so as to maximise performance? I mean, at what point does it generally become worth it? \$\endgroup\$
    – ricardo
    Dec 11, 2013 at 18:46
  • \$\begingroup\$ Probably not costly. However, the point of the exercise was to implement Peano arithmetic, not performance. Would you still consider it a valid implementation if you conditionally swapped the arguments? If you wanted speed, you would just do (* b prod). \$\endgroup\$ Dec 11, 2013 at 18:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.