tran li <litra...@hotmail.com> writes:

> Recently I found that it is possible to use OOP in LilyPond Scheme by
> using the `oop goops` module so I would like to reconstruct some of my
> code in my `lily-score` project.

With all due respect: I would recommend more focus on using Scheme in
LilyPond Scheme first.  You don't seem to have a good grasp of its
idioms, and the Goops module's way of providing OOP features is so
generic that it doesn't even proscribe a certain programming style.
That makes it very likely that your code will evolve to be unreadable to
Scheme programmers.

Here is an example:

> And the Bezier-related code:
>
> ```ly
> #(define (binom n k)
>   ;; binomial coefficient $\binom{n}{k} = \frac{n!}{k!(n - k)!}$
>   (if (> k (/ n 2)) (set! k (- n k)))
>   (let*
>     ((result 1))
>     (for-each
>       (lambda (i)
>         (let* ((p (- n i))
>                (q (- k i)))
>           (set! result (* result (/ p q)))))
>       (iota k))
>     result))
> ```

Scheme idioms are inherently recursive, math definitions are inherently
recursive.  Yet you use a loop with variables and assignments here, and
with arithmetic involving i.  More Schemish would be

#(define (binom n k)
  (let loop ((n n) (k (min k (- n k))) (q 1))
    (if (positive? k)
        (/ (* n (loop (1- n) (1- k) (1+ q))) q)
        1)))

It doesn't build a whole unnecessary list with (iota k).  It doesn't
reassign variables but recurses.  It doesn't unnecessarily trigger
rational math (hint: (/ p q) rarely is an integer).  Note that it still
relies on n being an integer and at least k, but you can fix this with
the initial condition, like

#(define (binom n k)
  (let loop ((n n)
             (k (if (and (integer? n) (>= n k)) (min k (- n k)) k))
             (q 1))
    (if (positive? k)
        (/ (* n (loop (1- n) (1- k) (1+ q))) q)
        1)))

Whether or not the use case warrants looking at those exceptions (it
probably doesn't), the idiom is more Schemish.  However, one also might
want to check that the _use_ of binom is not better replaced by
recursive evaluation:

> #(define ((bernstein-poly n k) t)
>   (* (binom n k) (expt (- 1 t) (- n k)) (expt t k)))
>
> #(define ((bezier cpts) t)
>   ;; parameterization for bezier curves
>   (let* ((x 0) (y 0)
>          (n (- (length cpts) 1))(k 0))
>     (for-each (lambda (cpt)
>         (let* ((x_k (car cpt))
>                (y_k (cdr cpt))
>                (coef ((bernstein-poly n k) t)))
>           (set! x (+ x (* x_k coef)))
>           (set! y (+ y (* y_k coef))))
>         (set! k (+ k 1)))
>       cpts)
>     (cons x y)))
> ```

Here you calculate succesive values of (binom n k) in a loop, each time
going from scratch rather than using the last value.

Wrapping that kind of complexity problem in OOP boxes will make it
harder to fix it.

-- 
David Kastrup

Reply via email to