Outline list counter

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

Outline list counter

Andrew Bernard
Hi All,

I am starting a new thread in relation to Gianmaria's original query about setting lists, as the previous thread has become long and I fear  this post would get lost at the bottom.

Having preached the virtues of using Scheme as it is intended, I put my money where my mouth is and coded one solution to Gianmaria's request for a set of functions to manage outline lists in markup. The following is I believe a rather nifty and good didactic example of the use of closure in Scheme, and how it can be used to effectively make a class with methods and encapsulated state while completely avoiding the overhead of object oriented features (which Scheme does not in any case have).

As explanation. make-outline-counter is a constructor for the class counter. The class contains methods for incrementing the counter, increasing and decreasing the indent level, and for resetting the counter. Make-outline counter returns a class, on which you can obtain methods specified by name, and the use the Scheme procedure invocation syntax to call them (The latter is the explanation for the double set of brackets).

#(define a (make-outline-counter)

This produces a as the class holding the state of the counter and the methods to operate on it.

Now (a 'inc) is a method that increments the counter. Call this procedure as follows:

((a 'inc))

This will return a string with the current counter list number in it. Similarly for the other methods. Note that the method name is a symbol not a string. I am borrowing the term method from the terminology of other languages, but there is no such thing in Scheme, just procedures here inside a closure. Likewise there are no classes in Scheme, and I have also borrowed that term, for ease of understanding. I am not making any claims that this is object oriented - far from it. Let there be no misunderstanding about that.

I hope this is useful. It's highly instructive if nothing else.



% Outline list counter class
% Andrew Bernard

\version "2.19.83"

#(define make-outline-counter
  (lambda ()
    (let ((lst (list 0))
      (indent-level 0))
      (define counter
    (lambda (method)
      (define (inc)
        (list-set! lst indent-level (+ (list-ref lst indent-level) 1))

      (define (indent)
        (set! lst (append lst (list 1)))
        (display lst) (newline)
        (set! indent-level (+ indent-level 1))

      (define (unindent)
        (if (> indent-level 0)
          (set! indent-level (- indent-level 1))
          (set! lst (drop-right lst 1))
      (define (reset)
        (set! lst (drop-right lst (- (length lst) 1)))
        (list-set! lst 0 1)
        (set! indent-level 0)

      (define (output-list-item-number)
          (let loop ((l lst) (str ""))
        (if (null? l)
              (loop (cdr l)
                (string-append str (format #f "~a." (car l)))

      (cond ((eq? method 'inc) inc)
        ((eq? method 'indent) indent)
        ((eq? method 'unindent) unindent)
        ((eq? method 'reset) reset))

% create counter
#(define a (make-outline-counter))

\markup { #((a 'inc)) }
{ c''4 }
\markup { #((a 'inc)) }
{ c''4 }
\markup { #((a 'inc)) }
{ c''4 }
\markup { #((a 'indent)) }
{ c''4 }
\markup { #((a 'inc)) }
{ c''4 }
\markup { #((a 'unindent)) }
{ c''4 }
\markup { #((a 'inc)) }
{ c''4 }
\markup { #((a 'indent)) }
{ c''4 }
\markup { #((a 'reset)) }
{ c''4 }


lilypond-user mailing list
[hidden email]