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.
Andrew
%======
% 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))
(output-list-item-number))
(define (indent)
(set! lst (append lst (list 1)))
(display lst) (newline)
(set! indent-level (+ indent-level 1))
(output-list-item-number))
(define (unindent)
(if (> indent-level 0)
(begin
(set! indent-level (- indent-level 1))
(set! lst (drop-right lst 1))
(inc)
(output-list-item-number)
)))
(define (reset)
(set! lst (drop-right lst (- (length lst) 1)))
(list-set! lst 0 1)
(set! indent-level 0)
(output-list-item-number))
(define (output-list-item-number)
(let loop ((l lst) (str ""))
(if (null? l)
str
(begin
(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))
))
counter)))
% 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 }
%======