tran li <[email protected]> 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