<!DOCTYPE Book PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
<!-- $Id: procedures.sgm,v 1.11 1997/10/30 15:49:30 tkg Exp $ -->
<book>

<bookinfo>
<title>DSSSL Documentation Project Procedures Library</title>
<subtitle>Revision 01</subtitle>
<subtitle>October 29, 1997</subtitle>
<legalnotice>
<para>
This software is copyrighted by its respective authors.  The following
terms apply to all files associated with the software unless explicitly
disclaimed in individual files.
</para>
<para>
The authors hereby grant permission to use, copy, modify, distribute,
and license this software and its documentation for any purpose, provided
that existing copyright notices are retained in all copies and that this
notice is included verbatim in any distributions. No written agreement,
license, or royalty fee is required for any of the authorized uses.
Modifications to this software may be copyrighted by their authors
and need not follow the licensing terms described here, provided that
the new terms are clearly indicated on the first page of each file where
they apply.
</para>
<para>
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</para>
<para>
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
</para>
</legalnotice>
</bookinfo>
<preface>
<title>Introduction</title>
<para>This collection of DSSSL procedures was collected from postings to the DSSSList, the DSSSL Users' Mailing List, and from people's direct contributions.</para>
<para>Thanks go to the following people for contributing procedures:
<simplelist>
<member>Vivek Agrawala</member>
<member>Tony Graham</member>
<member>G. Ken Holman</member>
<member>Dave Love</member>
<member>Chris Maden</member>
<member>Norman Walsh</member>
</simplelist>
and to the following people for contributing markup:
<simplelist>
<member>Vivek Agrawala</member>
<member>Tony Graham</member>
</simplelist>
</para>

</preface>

<chapter>
<title>ISO/IEC 10179 Procedures</title>
<sect1>
<title>Standard Procedures [8.5]</title>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022.  Replaces version submitted by Dave Love.</revremark>
</revision>
</revhistory>
</sect2info>
<title>assoc</title>
<synopsis>(assoc <replaceable>obj</> <replaceable>alist</>)</synopsis>
<para>Returns the pair from the <replaceable>alist</replaceable> associative list that has <replaceable>obj</replaceable> as its car, otherwise returns <literal>#f</literal> if no such pair exists.</para>
<programlisting>
(define (assoc obj alist)
  ;; Given an associative list, returns the pair that has obj as a car
  ;; or #f if no such pair exists
  ;; (("a" "b") ("c" "d")), "a" => ("a" "b")
  (let loop ((al alist))
    (if (null? al)
	#f
	(if (equal? obj (car (car al)))
	    (car al)
	    (loop (cdr al))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>caddr</title>
<synopsis>(caddr <replaceable>xs</replaceable>)</synopsis>
<para>Returns the <function>car</function> of the <function>cdr</function> of the <function>cdr</function> of <replaceable>xs</replaceable>.</para>
<programlisting>(define (caddr xs)
  (list-ref xs 2))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>cadr</title>
<synopsis>(cadr <replaceable>xs</replaceable>)</synopsis>
<para>Returns the <function>car</function> of the <function>cdr</function> of <replaceable>xs</replaceable>.</para>
<programlisting>(define (cadr xs)
  (list-ref xs 1))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>cddr</title>
<synopsis>(cddr <replaceable>xs</replaceable>)</synopsis>
<para>Returns the <function>cdr</function> of the <function>cdr</function> of <replaceable>xs</replaceable>.</para>
<programlisting>(define (cddr xs)
  (cdr (cdr xs)))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>even?</title>
<synopsis>(even? <replaceable>n</replaceable>)</synopsis>
<para>Returns <literal>#t</literal> if <replaceable>n</replaceable> is even, <literal>#f</literal> otherwise.</para>
<programlisting>(define (even? n)
  (zero? (remainder n 2)))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>expt</title>
<synopsis>(expt <replaceable>b</replaceable> <replaceable>n</replaceable>)</synopsis>
<para>Returns <replaceable>b</replaceable> raised to the <replaceable>n</replaceable> power.</para>
<programlisting>(define (expt b n)                      ; safe for -ve n, c.f. Bosak
  (letrec ((expt1 (lambda (n)
                    (if (zero? n)
                        1
                        (* b (expt1 (- n 1)))))))
    (if (< n 1)
        (/ (expt1 (- n)))
        (expt1 n))))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>list->string</title>
<synopsis>(list->string <replaceable>cs</replaceable>)</synopsis>
<para>Returns a string of the characters in <replaceable>cs</replaceable>.</para>
<programlisting>(define (list->string cs)
  (apply string cs))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>map</title>
<synopsis>(map <replaceable>f</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns a list of results from applying procedure <replaceable>f</replaceable> to corresponding elements of the lists making up the remainder of the arguments to the <function>map</function> procedure.  The lists must be of the same length, and <replaceable>f</replaceable> must accept as many arguments as there are lists.</para>
<programlisting>(define (map f #!rest xs)
   (let ((map1 (lambda (f xs)           ; bootstrap version for unary F
                 (let loop ((xs xs))
                   (if (null? xs)
                       '()
                       (cons (f (car xs))
                             (loop (cdr xs))))))))
     (cond ((null? xs)
            '())
           ((null? (cdr xs))
            (map1 f (car xs)))
           (else
            (let loop ((xs xs))
              (if (null? (car xs))
                  '()
                  (cons (apply f (map1 car xs))
                        (loop (map1 cdr xs)))))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>odd?</title>
<synopsis>(odd? <replaceable>n</replaceable>)</synopsis>
<para>Returns <literal>#t</literal> if <replaceable>n</replaceable> is odd, <literal>#f</literal> otherwise.</para>
<programlisting>(define (odd? n)
  (not (even? n)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>string->list</title>
<synopsis>(string->list <replaceable>s</replaceable>)</synopsis>
<para>Returns a list of the characters in <replaceable>s</replaceable>.</para>
<programlisting>(define (string->list s)
  (let ((l (string-length s)))
    (let loop ((i 0))
      (if (= i l)
          '()
          (cons (string-ref s i)
                (loop (+ i 1)))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>zero?</title>
<synopsis>(zero? <replaceable>n</replaceable>)</synopsis>
<para>Returns <literal>#t</literal> if <replaceable>n</replaceable> is zero, <literal>#f</literal> otherwise.</para>
<programlisting>(define (zero? n)
  (equal? 0 n))</programlisting>
</sect2>
</sect1>
<sect1>
<title>Derived Procedures [10.2]</title>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>ifollow</title>
<synopsis>(ifollow <replaceable>nl</>)</synopsis>
<para>Returns the node immediately following the <replaceable>nl</replaceable> node list.</para>
<programlisting>
(define (ifollow nl)
  (node-list-map (lambda (snl)
		   (let loop ((rest (siblings snl)))
		     (cond ((node-list-empty? rest)
			    (empty-node-list))
			   ((node-list=? (node-list-first rest) snl)
			    (node-list-first (node-list-rest rest)))
			   (else
			    (loop (node-list-rest rest))))))
		 nl))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>ipreced</title>
<synopsis>(ipreced <replaceable>nl</>)</synopsis>
<para>Returns the node immediately preceding the <replaceable>nl</replaceable> node list.</para>
<programlisting>
(define (ipreced nl)
  (node-list-map (lambda (snl)
		 (let loop ((prev (empty-node-list))
			    (rest (siblings snl)))
		   (cond ((node-list-empty? rest)
			  (empty-node-list))
			 ((node-list=? (node-list-first rest) snl)
			  prev)
			 (else
			  (loop (node-list-first rest)
				(node-list-rest rest))))))
		 nl))
</programlisting>
</sect2>


<sect2>
<sect2info>
<author>
<firstname>William</firstname><surname>Lindsay</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970728</date>
</revision>
</revhistory>
</sect2info>
<title>ipreced</title>
<synopsis>(ipreced <replaceable>nl</replaceable>)</synopsis>
<para>Returns the preceding sibling element or empty node list if this is the first child.</para>
<programlisting>
(define (ipreced nl)
  (node-list-map (lambda (snl)
                   (let loop ((prev (empty-node-list))
                              (rest (siblings snl)))
                     (cond ((node-list-empty? rest)
                            (empty-node-list))
                           ((node-list=? (node-list-first rest) snl)
                            prev)
                           (else
                            (loop (node-list-first rest)
                                  (node-list-rest rest))))))
                 nl))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Chris</firstname><surname>Maden</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971021</date>
<authorinitials>Chris Maden crism@ora.com</authorinitials>
<revremark>Copied from author's DSSSList post of 19971017</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-filter</title>
<synopsis>(node-list-filter <replaceable>proc</> <replaceable>nl</>)</synopsis>
<para>This procedure is defined in the DSSSL standard. Returns a
node-list containing just those members of <replaceable>nl</replaceable>
for which <replaceable>proc</replaceable> applied to a singleton node-list
containing just that member does not return #f.</para>
<para>The DSSSL standard (ISO/IEC 10179) implementation of
<function>node-list-filter</function> reverses the order of the
nodes passed to it. This altered version keeps the nodes in order.
<programlisting>
;; (node-list-filter) from ISO/IEC 10179 with small change to avoid
;; reversing the order of nodes
(define (node-list-filter proc nl)
  (node-list-reduce nl
        (lambda (result snl)
           (if (proc snl)
               (node-list result snl)
               result))
           (empty-node-list)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-last</title>
<synopsis>(node-list-last <replaceable>nl</>)</synopsis>
<para>Returns the last node in the <replaceable>nl</replaceable> node list.</para>
<programlisting>
(define (node-list-last nl)
  (node-list-ref nl
		 (- (node-list-length nl) 1)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-length</title>
<synopsis>(node-list-length <replaceable>nl</>)</synopsis>
<para>Returns the number of nodes in the <replaceable>nl</replaceable> node list.</para>
<programlisting>
(define (node-list-length nl)
  (node-list-reduce nl
                    (lambda (result snl)
                      (+ result 1))
                    0))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-reduce</title>
<synopsis>(node-list-reduce <replaceable>nl</replaceable> <replaceable>combine</replaceable> <replaceable>init</replaceable>)</synopsis>
<para>If the node-list <replaceable>nl</replaceable> is empty, returns <replaceable>init</replaceable>.  If not, returns the result of applying <function>node-list-reduce</function> to:<itemizedlist>
<listitem>
<para>a node-list containing all but the first member of <replaceable>nl</replaceable></para>
</listitem>
<listitem>
<para><replaceable>combine</replaceable>, and</para>
</listitem>
<listitem>
<para>the result of applying <replaceable>combine</replaceable> to <replaceable>init</replaceable> and the first member of <replaceable>nl</replaceable>.</para>
</listitem>
</itemizedlist>
</para>
<programlisting>(define (node-list-reduce nl combine init)
  (if (node-list-empty? nl)
      init
      (node-list-reduce (node-list-rest nl)
                        combine
                        (combine init (node-list-first nl)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-reduce</title>
<synopsis>(node-list-reduce <replaceable>nl</> <replaceable>proc</> <replaceable>init</>)</synopsis>
<para></para>
<programlisting>
(define (node-list-reduce nl proc init)
  (if (node-list-empty? nl)
      init
      (node-list-reduce (node-list-rest nl)
                        proc
                        (proc init (node-list-first nl)))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-some?</title>
<synopsis>(node-list-some? <replaceable>proc</replaceable> <replaceable>nl</replaceable>)</synopsis>
<para>Returns the node-list comprising the elements of node-list <replaceable>nl</replaceable> for which procedure <replaceable>proc</replaceable> returns <literal>#t</literal>.</para>
<programlisting>(define (node-list-some? proc nl)
  (node-list-reduce nl
                    (lambda (result snl)
                      (if (or result (proc snl))
                          #t
                          #f))
                    #f))</programlisting>
</sect2>
</sect1>
</chapter>

<chapter>
<title>Other Procedures</title>

<sect1>
<title>List</title>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>any?</title>
<synopsis>(any? <replaceable>pred?</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns <literal>#t</literal> if predicate <replaceable>pred?</replaceable> returns true when applied to any element of the list <replaceable>xs</replaceable>.</para>
<programlisting>;; Return #t if predicate `pred?' returns #t when applied to any
;; element of the `xs'.
(define (any? pred? xs)
  (let loop ((xs xs))
    (and (not (null? xs))
         (or (pred? (car xs))
             (loop (cdr xs))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>assoc-objs</title>
<synopsis>(assoc-objs <replaceable>alist</>)</synopsis>
<para>Returns a list of the objects in <replaceable>alist</replaceable> as an associative list.</para>
<programlisting>
(define (assoc-objs alist)
  ;; Returns a list of the objects in an associative list.
  ;; (("a" "b") ("c" "d")) => ("a" "c")
  (let loop ((result '()) (al alist))
    (if (null? al)
	result
	(loop (append result (list (car (car al)))) (cdr al)))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>break</title>
<synopsis>(break <replaceable>test?</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns 
<programlisting>;; Like `span', but with the sense of the test reversed.
(define (break test? xs)
  (span (compose not test?) xs))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>drop</title>
<synopsis>(drop <replaceable>x</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns the list <replaceable>xs</replaceable> less the first <replaceable>n</replaceable> elements.</para>
<programlisting>;; Return list `xs' less the first `n' elements.
(define (drop n xs)
  (list-tail xs n))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>dropwhile</title>
<synopsis>(dropwhile <replaceable>test?</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Remove leading elements of list <replaceable>xs</replaceable> for which <replaceable>test?</replaceable> returns true.</para>
<programlisting>;; Remove leading elements of list `xs' for which `test?' returns
;; true.
(define (dropwhile test? xs)
  (cond ((null? xs)
         '())
        ((test? (car xs))
         (dropwhile test? (cdr xs)))
        (else xs)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>list-member-find</title>
<synopsis>(list-member-find <replaceable>element</> <replaceable>element-list</>)</synopsis>
<para>Returns the offset within <replaceable>element-list</replaceable> of <replaceable>element</replaceable>, or <literal>-1</literal> if <replaceable>element</replaceable> is not a member of <replaceable>element-list</replaceable>.</para>
<para>The offset of the first element in <replaceable>element-list</replaceable> is 0.  Occurence of <replaceable>element</replaceable> is tested with <function>equal?</function>, so the elements may be objects of any time for which <function>equal?</function> will work.</para>
<programlisting>
(define (list-member-find element element-list)
  ;; Finds element in element-list and returns the index of its location.
  ;; The first element in a list has index 0.
  (let loop ((elemlist element-list) (count 0))
    (if (null? elemlist)
	-1
	(if (equal? element (car elemlist))
	    count
	    (loop (cdr elemlist) (+ count 1))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>list-member-get</title>
<synopsis>(list-member-get <replaceable>element-list</> <replaceable>count</>)</synopsis>
<para>Returns the element at offset <replaceable>count</replaceable> within <replaceable>element-list</replaceable>, or <literal>#f</literal> if <replaceable>element-list</replaceable> does not contain <replaceable>count</replaceable> elements.</para>
<programlisting>
(define (list-member-get element-list count)
  ;; Returns the count'th element from element-list.
  ;; The first element in a list has index 0.
  (let loop ((elemlist element-list) (idx count))
    (if (null? elemlist)
	#f
	(if (= idx 0)
	    (car elemlist)
	    (loop (cdr elemlist) (- idx 1))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>remove</title>
<synopsis>(remove <replaceable>x</replaceable> <replaceable>ys</replaceable>)</synopsis>
<para>Returns list <replaceable>ys</replaceable> without any elements equalling <replaceable>x</replaceable>.</para>
<programlisting>;; Remove any occurrences of `x' from list `ys'.
(define (remove x ys)
  (cond
   ((null? ys) ys)
   ((equal? x (car ys)) (remove x (cdr ys)))
   (else (cons (car ys) (remove x (cdr ys))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>remove-if</title>
<synopsis>(remove-if <replaceable>pred?</replaceable> <replaceable>ys</replaceable>)</synopsis>
<para>Returns list <replaceable>ys</replaceable> without any elements for which predicate <replaceable>pred?</replaceable> returns <literal>#t</literal>.</para>
<programlisting>;; Remove any elements  `x' from that answer #t to `pred?'.
(define (remove-if pred? ys)
  (cond
   ((null? ys) ys)
   ((pred? (car ys)) (remove-if pred? (cdr ys)))
   (else (cons (car ys) (remove-if pred? (cdr ys))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>sort</title>
<synopsis>(sort <replaceable><=</replaceable> <replaceable>l</replaceable>)</synopsis>
<para>Returns list <replaceable>l</replaceable> sorted using comparison function <replaceable><=</replaceable>.</para>
<programlisting>;; O'Keefe's smooth applicative merge sort: sort list `l' using
;; comparison function `<='.
(define (sort <= l)
  (letrec ((merge (lambda (xs ys)
                    (cond ((null? xs) ys)
                          ((null? ys) xs)
                          (else (if (<= (car xs)
                                        (car ys))
                                    (cons (car xs)
                                          (merge (cdr xs) ys))
                                    (cons (car ys)
                                          (merge xs (cdr ys))))))))
           (mergepairs (lambda (l k)
                         (if (null? (cdr l))
                             l
                             (if (= 1 (modulo k 2))
                                 l
                                 (mergepairs (cons (merge  (car l)
                                                           (cadr l))
                                                   (cddr l))
                                             (quotient k 2))))))
           (sorting (lambda (l a k)
                      (if (null? l)
                          (car (mergepairs a 0))
                          (sorting (cdr l)
                                   (mergepairs (cons (list (car l))
                                                     a)
                                               (+ k 1))
                                   (+ k 1))))))
    (cond ((not (list? l))
           (error "sort: second arg not a list"))
          ((not (procedure? <=))
           (error "sort: first arg not a procedure"))
          ((null? l)
           '())
          (else
           (sorting l '() 0)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>span</title>
<synopsis>(span <replaceable>test?</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns a pair of lists, one is the leading elements of the list <replaceable>xs</replaceable> for which <replaceable>test?</replaceable> returns <literal>#t</literal>, and the other is the rest of the elements of <replaceable>xs</replaceable>.</para>
<programlisting>;; From the list `xs', return a pair of lists comprising the leading
;; elements of `xs' for which `test?' returns true and the rest of
;; `xs'.  After the Haskell prelude.
(define (span test? xs)
  (if (null? xs)
      (cons '() '())
      (let ((x (car xs))                ; split the xs into head
            (xss (cdr xs)))             ; and tail
        (if (test? x)
            (let* ((spanned (span test? xss))
                   ;; and split the result of span into head and tail
                   (ys (car spanned))
                   (zs (cdr spanned)))
              (cons (cons x ys) zs))
            (cons '() xs)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>take</title>
<synopsis>(take <replaceable>n</replaceable> <replaceable>xs</replaceable>)</synopsis>
<para>Returns the first <replaceable>n</replaceable> elements of list <replaceable>xs</replaceable>.</para>
<programlisting>(define (take n xs)
  (let loop ((i 1) (xs xs))
    (if (or (> i n)
            (null? xs))
        '()
        (cons (car xs)
              (loop (+ 1 i) (cdr xs))))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>zip-with</title>
<programlisting>;; List zipping with the given `zipper' function.  Like `map', but the
;; list args can be unequal lengths.
(define (zip-with zipper #!rest xs)
  (if (any? null? xs)
      '()
      (cons (apply zipper (map car xs) )
            (apply zip-with zipper (map cdr xs)))))</programlisting>
</sect2>
</sect1>

<sect1>
<title>Character</title>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-down-char</title>
<synopsis>(case-fold-down-char <replaceable>ch</>)</synopsis>
<para>Returns the lowercase form of <replaceable>ch</replaceable> if <replaceable>ch</replaceable> is a member of the <literal>uppercase-list</literal> list, otherwise returns <replaceable>ch</replaceable>.</para>
<programlisting>
(define (case-fold-down-char ch)
  ;; Returns the lowercase form of ch or ch if ch is not an uppercase form
  (let ((idx (list-member-find ch uppercase-list)))
    (if (> idx 0)
	(list-member-get lowercase-list idx)
	ch)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-down-charlist</title>
<synopsis>(case-fold-down-charlist <replaceable>charlist</>)</synopsis>
<para>Returns <replaceable>charlist</replaceable> with all uppercase characters converted to lowercase.</para>
<programlisting>
(define (case-fold-down-charlist charlist)
  ;; Shifts all characters in charlist to lowercase
  (if (null? charlist)
      '()
      (cons (case-fold-down-char (car charlist)) 
	    (case-fold-down-charlist (cdr charlist)))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-up-char</title>
<synopsis>(case-fold-up-char <replaceable>ch</>)</synopsis>
<para>Returns the uppercase form of <replaceable>ch</replaceable> if <replaceable>ch</replaceable> is a member of the <literal>lowercase-list</literal> list, otherwise returns <replaceable>ch</replaceable>.</para>
<programlisting>
(define (case-fold-up-char ch)
  ;; Returns the uppercase form of ch or ch if ch is not a lowercase form
  (let ((idx (list-member-find ch lowercase-list)))
    (if (> idx 0)
	(list-member-get uppercase-list idx)
	ch)))

(define uppercase-list
  '(#\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M
    #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z))

(define lowercase-list
  '(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m
    #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-up-charlist</title>
<synopsis>(case-fold-up-charlist <replaceable>charlist</>)</synopsis>
<para>Returns <replaceable>charlist</replaceable> with all lowercase characters converted to uppercase.</para>
<programlisting>
(define (case-fold-up-charlist charlist)
  ;; Shifts all characters in charlist to uppercase
  (if (null? charlist)
      '()
      (cons (case-fold-up-char (car charlist)) 
	    (case-fold-up-charlist (cdr charlist)))))
</programlisting>
</sect2>
</sect1>

<sect1>
<title>String</title>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-down</title>
<synopsis>(case-fold-down <replaceable>str</>)</synopsis>
<para>Returns <replaceable>str</replaceable> with all uppercase characters converted to lowercase.</para>
<programlisting>
(define (case-fold-down str)
  ;; Returns str shifted to lowercase
  (apply string (case-fold-down-charlist (string-to-list str))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>case-fold-up</title>
<synopsis>(case-fold-up <replaceable>str</>)</synopsis>
<para>Returns <replaceable>str</replaceable> with all lowercase characters converted to uppercase.</para>
<programlisting>
(define (case-fold-up str)
  ;; Returns str shifted to uppercase
  (apply string (case-fold-up-charlist (string-to-list str))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>copy-string</title>
<synopsis>(copy-string <replaceable>string</> <replaceable>num</>)</synopsis>
<para>Returns the string that is the concatenation of <replaceable>num</replaceable> occurrences of <replaceable>string</replaceable>.</para>
<programlisting>
(define (copy-string string num)
  ;; Return string copied num times
  ;; (copy-string "x" 3) => "xxx"
  (if (<= num 0)
      ""
      (let loop ((str string) (count (- num 1)))
	(if (<= count 0)
	    str
	    (loop (string-append str string) (- count 1))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>directory-depth</title>
<synopsis>(directory-depth <replaceable>pathname</>)</synopsis>
<para>Returns the number of directory levels in the <replaceable>pathname</replaceable> string when the string is interpreted as a Unix-style pathname.</para>
<programlisting>
(define (directory-depth pathname)
  ;; Return the number of directory levels in pathname
  ;; "filename" => 0
  ;; "foo/filename" => 1
  ;; "foo/bar/filename => 2
  ;; "foo/bar/../filename => 1
  (let loop ((count 0) (pathlist (match-split pathname "/")))
    (if (null? pathlist)
	(- count 1) ;; pathname should always end in a filename
	(if (or (equal? (car pathlist) "/") (equal? (car pathlist) "."))
	    (loop count (cdr pathlist))
	    (if (equal? (car pathlist) "..")
		(loop (- count 1) (cdr pathlist))
		(loop (+ count 1) (cdr pathlist)))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>find-first-char</title>
<synopsis>(find-first-char <replaceable>string</> <replaceable>skipchars</> <replaceable>findchars</> <replaceable>pos</>)</synopsis>
<para>Returns the first character in <replaceable>string</replaceable> that is in <replaceable>findchars</replaceable> and not in <replaceable>skipchars</replaceable>.  <replaceable>findchars</replaceable> and <replaceable>skipchars</replaceable> are both strings.</para>
<para>If <replaceable>pos</replaceable> is supplied, the search begins at offset <replaceable>pos</replaceable> within <replaceable>string</replaceable>, otherwise the search begins at offset 0.</para>
<programlisting>
(define (find-first-char string skipchars findchars #!optional (pos 0))
  ;; Finds first character in string that is in findchars, skipping all
  ;; occurances of characters in skipchars.  Search begins at pos.  If
  ;; no such characters are found, returns -1.
  ;;
  ;; If skipchars is empty, skip anything not in findchars
  ;; If skipchars is #f, skip nothing
  ;; If findchars is empty, the first character not in skipchars is matched
  ;; It is an error if findchars is not a string.
  ;; It is an error if findchars is empty and skipchars is not a non-empty
  ;; string.
  ;;
  (let ((skiplist (if (string? skipchars)
		      (string-to-list skipchars)
		      '()))
	(findlist (string-to-list findchars)))
    (if (and (null? skiplist) (null? findlist))
	;; this is an error
	-2
	(if (or (>= pos (string-length string)) (< pos 0))
	    -1
	    (let ((ch (string-ref string pos)))
	      (if (null? skiplist) 
		  ;; try to find first
		  (if (member ch findlist)
		      pos
		      (if (string? skipchars)
			  (find-first-char string 
					   skipchars findchars (+ 1 pos))
			  -1))
		  ;; try to skip first
		  (if (member ch skiplist)
		      (find-first-char string skipchars findchars (+ 1 pos))
		      (if (or (member ch findlist) (null? findlist))
			  pos
			  -1))))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>initial-substring=?</title>
<synopsis>(initial-substring? <replaceable>a</replaceable> <replaceable>b</replaceable>)</synopsis>
<para>Returns <literal>#t</literal> if <replaceable>a</replaceable> matches the initial substring of <replaceable>b</replaceable>.</para>
<programlisting>;; Is `a' an initial substring of `b'?
(define (initial-substring? a b)
  (string=? a (substring b 0 (string-length a))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>match-split</title>
<synopsis>(match-split <replaceable>string</> <replaceable>target</>)</synopsis>
<para>Returns a list of strings.  The strings are <replaceable>string</replaceable> split at every occurrence of <replaceable>target</replaceable>, and the list includes the occurrences of <replaceable>target</replaceable>.</para>
<programlisting>
(define (match-split string target)
  ;; Splits string at every occurrence of target and returns the result
  ;; as a list. "this is a test" split at "is" returns
  ;; ("th" "is" " " "is" " a test")
  (let loop ((result '()) (current "") (rest string))
    (if (< (string-length rest) (string-length target))
	(append result (if (equal? (string-append current rest) "")
				  '()
				  (list (string-append current rest))))
	(if (equal? target (substring rest 0 (string-length target)))
	    (loop (append result
			  (if (equal? current "")
			      '()
			      (list current))
			  (list target))
		  ""
		  (substring rest (string-length target) (string-length rest)))
	    (loop result
		  (string-append current (substring rest 0 1))
		  (substring rest 1 (string-length rest)))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>match-split-string-list</title>
<synopsis>(match-split-string-list <replaceable>string-list</> <replaceable>target</>)</synopsis>
<para>Returns a list of strings.  The strings are the each of the strings in <replaceable>string-list</replaceable> split at every occurrence of <replaceable>target</replaceable>, and the list includes the occurrences of <replaceable>target</replaceable>.</para>
<programlisting>
(define (match-split-string-list string-list target)
  ;; Splits each string in string-list at target with match-split,
  ;; concatenates the results and returns them as a single list
  (let loop ((result '()) (sl string-list))
    (if (null? sl)
	result
	(loop (append result (match-split (car sl) target))
	      (cdr sl)))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>match-split-list</title>
<synopsis>(match-split-list <replaceable>string</> <replaceable>target-list</>)</synopsis>
<para>Returns a list of strings.  The strings are <replaceable>string</replaceable> split at every occurrence of each of the strings in <replaceable>target-list</replaceable>, and the list includes the occurrences of the target strings.</para>
<programlisting>
(define (match-split-list string target-list)
  ;; Splits string at every target in target-list with match-split,
  ;; returning the whole collection of strings as a list
  (let loop ((result (list string)) (tlist target-list))
    (if (null? tlist)
	result
	(loop (match-split-string-list result (car tlist))
	      (cdr tlist)))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>match-substitute-sosofo</title>
<synopsis>(match-substitute-sosofo <replaceable>string</> <replaceable>assoc-list</>)</synopsis>
<para>Returns the sosofo, from the <replaceable>assoc-list</replaceable> associative list of string and sosofo pairs, that matches <replaceable>string</replaceable>, otherwise returns the sosofo of <replaceable>string</replaceable> as a literal string if there is no match.</para>
<programlisting>
(define (match-substitute-sosofo string assoc-list)
  ;; Given a string and an associative list of strings and sosofos,
  ;; return the sosofo of the matching string, or return the literal
  ;; string as a sosofo.
  (if (assoc string assoc-list)
      (car (cdr (assoc string assoc-list)))
      (literal string)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>pad-string</title>
<synopsis>(pad-string <replaceable>string</> <replaceable>length</> <replaceable>padchar</>)</synopsis>
<para>Returns <replaceable>string</replaceable> padded in front with the <replaceable>padchar</replaceable> string until it is at least <replaceable>length</replaceable> characters.  If <replaceable>string</replaceable> is longer than or the same length as <replaceable>length</replaceable> characters, then <replaceable>string</replaceable> is returned without modification.  Note the <replaceable>padchar</replaceable> may be more than a single-character string.</para>
<programlisting>
(define (pad-string string length padchar)
  ;; Returns string, padded in front with padchar to at least length
  (let loop ((s string))
    (if (>= (string-length s) length)
	s
	(loop (string-append padchar s)))))
</programlisting>
</sect2>


<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-substring?</title>
<synopsis>(repl-substring? <replaceable>string</> <replaceable>target</> <replaceable>pos</>)</synopsis>
<para>Returns <literal>#t</literal> if <replaceable>target</replaceable> occurs in <replaceable>string</replaceable> at offset <replaceable>pos</replaceable>.</para>
<programlisting>
(define (repl-substring? string target pos)
  ;; Returns true if target occurs in string at pos
  (let* ((could-match (<= (+ pos (string-length target)) 
			 (string-length string)))
	 (match (if could-match 
		    (substring string pos (+ pos (string-length target))) "")))
    (and could-match (string=? match target))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-substring</title>
<synopsis>(repl-substring <replaceable>string</> <replaceable>target</> <replaceable>repl</> <replaceable>pos</>)</synopsis>
<para>Returns <replaceable>string</replaceable> with substring <replaceable>target</replaceable> replaced by <replaceable>repl</replaceable> if <replaceable>target</replaceable> occurs at character offset <replaceable>pos</replaceable>, otherwise returns <replaceable>string</replaceable> unaltered.</para>
<programlisting>
(define (repl-substring string target repl pos)
  ;; Replace target with repl in string at pos
  (let ((matches (repl-substring? string target pos)))
    (if matches
	(string-append
	 (substring string 0 pos)
	 repl
	 (substring string 
		    (+ pos (string-length target)) 
		    (string-length string)))
	string)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-string-list?</title>
<synopsis>(repl-string-list? <replaceable>string</> <replaceable>replace-list</> <replaceable>pos</>)</synopsis>
<para>Returns <literal>#t</literal> if any of the target strings in the sequence of target&ndash;replacement pairs of strings in <replaceable>replace-list</replaceable> occurs at character offset <replaceable>pos</replaceable> within <replaceable>string</replaceable>.</para>
<programlisting>
(define (repl-substring-list? string replace-list pos)
  ;; Given replace-list, a list of target, replacement pairs, return
  ;; true if any occurance of target occurs at pos
  ;; (repl-substring-list? "this is it" ("was" "x" "is" "y") 2)
  ;; returns true ("is" could be replaced by "y")
  (let loop ((list replace-list))
    (let ((target (car list))
	  (repl   (car (cdr list)))
	  (rest   (cdr (cdr list))))
      (if (repl-substring? string target pos)
	  #t
	  (if (null? rest)
	      #f
	      (loop rest))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-substring-list-target</title>
<synopsis>(repl-substring-list-target <replaceable>string</> <replaceable>replace-list</> <replaceable>pos</>)</synopsis>
<para>Returns the target string that would be replaced if <function>repl-substring-list</function> were called with the same arguments.</para>
<programlisting>
(define (repl-substring-list-target string replace-list pos)
  ;; Given replace-list, a list of target, replacement pairs, return
  ;; the target that would be replaced if repl-substring-list was 
  ;; called with the same arguments.
  (let loop ((list replace-list))
    (let ((target (car list))
	  (repl   (car (cdr list)))
	  (rest   (cdr (cdr list))))
      (if (repl-substring? string target pos)
	  target
	  (if (null? rest)
	      #f
	      (loop rest))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-string-list-repl</title>
<synopsis>(repl-string-list-repl <replaceable>string</> <replaceable>replace-list</> <replaceable>pos</>)</synopsis>
<para>Returns the replacement string that would be inserted if <function>repl-substring-list</function> were called with the same arguments.</para>
<programlisting>
(define (repl-substring-list-repl string replace-list pos)
  ;; Given replace-list, a list of target, replacement pairs, return
  ;; the replacement that would be used if repl-substring-list was 
  ;; called with the same arguments.
  (let loop ((list replace-list))
    (let ((target (car list))
	  (repl   (car (cdr list)))
	  (rest   (cdr (cdr list))))
      (if (repl-substring? string target pos)
	  repl
	  (if (null? rest)
	      #f
	      (loop rest))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>repl-substring-list</title>
<synopsis>(repl-substring-list <replaceable>string</> <replaceable>replace-list</> <replaceable>pos</>)</synopsis>
<para>Returns <replaceable>string</replaceable> with the first target from the sequence of target&ndash;replacement pairs in <replaceable>replace-list</replaceable> that occurs at character offset <replaceable>pos</replaceable> substituted by its replacement.</para>
<programlisting>
(define (repl-substring-list string replace-list pos)
  ;; Replace any single target in string at pos (the first one in
  ;; replace-list that matches).
  (if (repl-substring-list? string replace-list pos)
      (let ((target (repl-substring-list-target string replace-list pos))
	    (repl   (repl-substring-list-repl string replace-list pos)))
	(repl-substring string target repl pos))
      string))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>string-list-sosofo</title>
<synopsis>(string-list-sosofo <replaceable>string-list</> <replaceable>assoc-list</>)</synopsis>
<para>Returns a sosofo that is each of the sosofos from matching each of the strings in <replaceable>string-list</replaceable> with the string&ndash;sosofo pairs in the <replaceable>assoc-list</replaceable> associative list appended together.  Strings from <replaceable>string-list</replaceable> that do not have a match in <replaceable>assoc-list</replaceable> are included as literal sosofos.</para>
<programlisting>
(define (string-list-sosofo string-list assoc-list)
  ;; Take a list of strings and an associative list that maps strings
  ;; to sosofos and return an appended sosofo.
  ;; Given the string list ("what is " "1" " " "+" " " "1")
  ;; and the associative list 
  ;; (("1" (literal "one")) ("2" (literal "two")) ("+" (literal "plus")))
  ;; string-list-sosofo returns the equivalent of the sosofo
  ;; (literal "what is one plus one")
  (if (null? string-list)
      (empty-sosofo)
      (sosofo-append (match-substitute-sosofo (car string-list) assoc-list)
		     (string-list-sosofo (cdr string-list) assoc-list))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>string-replace</title>
<synopsis>(string-replace <replaceable>string</> <replaceable>target</> <replaceable>repl</>)</synopsis>
<para>Returns <replaceable>string</replaceable> with all occurrences of <replaceable>target</replaceable> replaced by <replaceable>repl</replaceable>.  Any occurrences of <replaceable>target</replaceable> within <replaceable>repl</replaceable> are not themselves replaced.</para>
<programlisting>
(define (string-replace string target repl)
  ;; Replace all occurances of target with repl in string.  If the
  ;; target occurs in repl, that occurance will _not_ be replaced.
  (let loop ((str string) (pos 0))
    (if (>= pos (string-length str))
	str
	(loop (repl-substring str target repl pos) 
	      (if (repl-substring? str target pos)
		  (+ (string-length repl) pos)
		  (+ 1 pos))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>string-replace-list</title>
<synopsis>(string-replace-list <replaceable>string</> <replaceable>replace-list</>)</synopsis>
<para>Returns <replaceable>string</replaceable> with every occurrence of every target string in the sequence of target&ndash;replacement pairs in <replaceable>replace-list</replaceable> substituted by its replacement.</para>
<programlisting>
(define (string-replace-list string replace-list)
  ;; Given replace-list, replace all occurances of every target in string
  ;; with its replacement.
  (let loop ((str string) (pos 0))
    (if (>= pos (string-length str))
	str
	(loop (repl-substring-list str replace-list pos) 
	      (if (repl-substring-list? str replace-list pos)
		  (+ (string-length 
		      (repl-substring-list-repl str replace-list pos)) 
		     pos)
		  (+ 1 pos))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>string-to-list</title>
<synopsis>(string-to-list <replaceable>string</>)</synopsis>
<para>Returns a list of the characters in <replaceable>string</replaceable>.</para>
<programlisting>
(define (string-to-list string)
  ;; Returns a list of the characters of string
  (let ((length (string-length string)))
    (let loop ((i 0))
      (if (= i length)
	  '()
	  (cons (string-ref string i) 
		(loop (+ i 1)))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>string-with-space</title>
<synopsis>(string-with-space <replaceable>string</>)</synopsis>
<para>Returns <replaceable>string</replaceable> with an appended blank, unless <replaceable>string</replaceable> is an empty string, in which case it returns an empty string.</para>
<programlisting>
(define (string-with-space string)
  ;; Returns string with an appended blank if string is not the empty
  ;; string.  Returns the empty string if string is the empty string.
  (if (equal? string "")
      ""
      (string-append string " ")))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>whitespaced-words</title>
<programlisting>(define whitespaced-words
  (curry words char-whitespace?))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>words</title>
<synopsis>(words <replaceable>pred?</replaceable> <replaceable>s</replaceable>)</synopsis>
<para>Returns a list of the &ldquo;words&rdquo; split from string <replaceable>s</replaceable>.  The word delimiters are characters for which predicate <replaceable>pred?</replaceable> returns <literal>#t</literal>.</para>
<programlisting>;; Split string `s' into words delimited by characters answering #t to
;; predicate `pred?'.  After the Haskell prelude.  See also Bird and
;; Wadler.
(define (words pred? s)
  (letrec ((words (lambda (s)
                    (let ((dropped (dropwhile pred? s)))
                      (if (null? dropped)
                          '()
                          (let ((broken (break pred? dropped)))
                            (cons (car broken)
                                  (words (cdr broken)))))))))
    (map list->string (words (string->list s)))))</programlisting>
</sect2>
</sect1>

<sect1>
<title>Length</title>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>parse-measurement</title>
<synopsis>(parse-measurement <replaceable>measure</>)</synopsis>
<para>Returns a two-item list of the magnitude and units components of <replaceable>measure</replaceable>.  If <replaceable>measure</replaceable> is not correctly formed, one or both of the magnitude and units will be returned as <literal>#f</literal>.</para>
<programlisting>
(define (parse-measurement measure)
  ;; Parses measurement and returns '(magnitude units).  Either may be #f
  ;; if measure is not a correctly formatted measurement.  Leading and
  ;; trailing spaces are skipped.
  (let* ((magstart  (find-first-char measure " " "0123456789."))
	 (unitstart (find-first-char measure " 0123456789." ""))
	 (unitend   (find-first-char measure "" " " unitstart))
	 (magnitude (if (< magstart 0)
			#f
			(if (< unitstart 0)
			    (substring measure 
				       magstart 
				       (string-length measure))
			    (substring measure magstart unitstart))))
	 (unit      (if (< unitstart 0)
			#f
			(if (< unitend 0)
			    (substring measure 
				       unitstart 
				       (string-length measure))
			    (substring measure unitstart unitend)))))
  (list magnitude unit))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>measurement-to-length</title>
<synopsis>(measurement-to-length <replaceable>measure</>)</synopsis>
<para>Returns a length object converted from the value of the <replaceable>measure</replaceable> string.</para>
<programlisting>
(define unit-conversion-alist
  (list
   '("default" 1pi)
   '("mm" 1mm)
   '("cm" 1cm)
   '("in" 1in)
   '("pi" 1pi)
   '("pc" 1pi)
   '("pt" 1pt)
   '("px" 1px)
   '("barleycorn" 2pi)))

(define (measurement-to-length measure)
  ;; Converts the string measure into a DSSSL length
  (let* ((pm (car (parse-measurement measure)))
	 (pu (car (cdr (parse-measurement measure))))
	 (magnitude (if pm pm "1"))
	 (units     (if pu pu (if pm "pt" "default"))
	 (unitconv  (assoc units unit-conversion-alist))
	 (factor    (if unitconv (car (cdr unitconv)) 1pt)))
    (* (string->number magnitude) factor)))
</programlisting>
</sect2>
</sect1>

<sect1>
<title>Node</title>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>absolute-child-number</title>
<synopsis>(absolute-child-number <replaceable>snl</replaceable>)</synopsis>
<para>Returns one plus the number of nodes preceding <replaceable>snl</replaceable>.  This is similar to <function>child-number</function>, but it also counts siblings with a GI different from that of <replaceable>snl</replaceable>.</para>
<programlisting>;; -- Similar to child-number, but also counts siblings with a GI
;; -- different than that of 'snl'.
(define (absolute-child-number snl)
  (+ 1 (node-list-length (preced snl))) )</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>ancestor-member</title>
<synopsis>(ancestor-member <replaceable>snl</> <replaceable>gilist</>)</synopsis>
<para>Returns the node that is the first ancestor of <replaceable>snl</replaceable> (including <replaceable>snl</replaceable> itself) whose GI is in the <replaceable>gilist</replaceable> list of strings, otherwise returns an empty node list.</para>
<programlisting>
(define (ancestor-member snl gilist)
  ;; Return the first ancestor of nd whose GI is in gilist
  (if (node-list-empty? snl)
      (empty-node-list)
      (if (member (gi snl) gilist)
	  nd
	  (ancestror-member (parent snl) gilist))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>G. Ken</firstname><surname>Holman</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971021</date>
<authorinitials>G. Ken Holman gkholman@CanadaMail.com</authorinitials>
<revremark>Copied from Ken's DSSSList post of 19971017</revremark>
</revision>
</revhistory>
</sect2info>
<title>ancestors</title>
<synopsis>(ancestors <replaceable>nl</replaceable>)</synopsis>
<para>Returns a node-list containing all ancestors
of <replaceable>nl</replaceable>.</para>
<para>The DSSSL standard has a typo in the definition of the
function <function>ancestors</function>. The following is the
corrected logic -- the third last line used to read
"(loop (parent snl)" which caused an infinite loop.</para>
<programlisting>(define (ancestors nl)
  (node-list-map (lambda (snl)
                   (let loop ((cur (parent snl))
                              (result (empty-node-list)))
                     (if (node-list-empty? cur)
                         result
                         (loop (parent cur)
                               (node-list cur result)))))
                 nl))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Vivek</firstname><surname>Agrawala</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Vivek Agrawala vivek@scr.siemens.com</authorinitials>
<revremark></revremark>
</revision>
</revhistory>
</sect2info>
<title>get-str-attr</title>
<synopsis>(get-str-attr <replaceable>attr-nm</> <replaceable>osnl</>)</synopsis>
<para>Returns the string value of attribute <replaceable>attr-nm</> of
node <replaceable>osnl</>.  Returns "ERROR: ..." if <replaceable>attr-nm</>
is not defined at <replaceable>osnl</>.</para>
<programlisting>
(define (get-string-attr attr-nm #!optional (osnl (current-node)))
  (or (attribute-string attr-nm osnl)
      (string-append "ERROR: No Attr " attr-nm) ))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Vivek</firstname><surname>Agrawala</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Vivek Agrawala vivek@scr.siemens.com</authorinitials>
<revremark></revremark>
</revision>
</revhistory>
</sect2info>
<title>get-str-attr-no-err</title>
<synopsis>(get-str-attr-no-err <replaceable>attr-nm</> <replaceable>osnl</>)</synopsis>
<para>Returns the string value of attribute <replaceable>attr-nm</> of
node <replaceable>osnl</>.  Returns an empty string if <replaceable>attr-nm</>
is not defined at <replaceable>osnl</>.</para>
<programlisting>
(define (get-str-attr-no-err attr-nm #!optional (osnl (current-node)))
  (or (attribute-string attr-nm osnl) "") )
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Vivek</firstname><surname>Agrawala</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Vivek Agrawala vivek@scr.siemens.com</authorinitials>
<revremark></revremark>
</revision>
</revhistory>
</sect2info>
<title>get-numeric-attr</title>
<synopsis>(get-numeric-attr <replaceable>attr-nm</> <replaceable>osnl</>)</synopsis>
<para>Returns the numeric value of attribute <replaceable>attr-nm</> of
node <replaceable>osnl</>.  Returns -1 if <replaceable>attr-nm</> is not
defined at <replaceable>osnl</>.</para>
<programlisting>
(define (get-numeric-attr attr-nm #!optional (osnl (current-node)))
  (let ((a-str (attribute-string attr-nm osnl)))
    (if a-str (string->number a-str)  -1) ))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>has-ancestor-member?</title>
<synopsis>(has-ancestor-member? <replaceable>nd</> <replaceable>gilist</>)</synopsis>
<para>Returns <literal>#t</literal> if the <replaceable>nd</replaceable> node has an ancestor whose GI is included in the <replaceable>gilist</replaceable> list of strings.</para>
<programlisting>
(define (has-ancestor-member? nd gilist)
  ;; Return #t if nd has an ancestor in gilist
  (node-list-empty? (ancestor-member nd gilist)))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Vivek</firstname><surname>Agrawala</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Vivek Agrawala vivek@scr.siemens.com</authorinitials>
<revremark></revremark>
</revision>
</revhistory>
</sect2info>
<title>has-child?</title>
<synopsis>(has-child? <replaceable>gi</> <replaceable>snl</>)</synopsis>
<para>Returns #t if <replaceable>snl</> has a child node with GI
<replaceable>gi</>. Returns #f otherwise.</para>
<programlisting>
(define (has-child? gi snl)
  (not (node-list-empty? (select-elements gi (children snl)))) )
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>map-node-list->list</title>
<synopsis>(map-node-list->list <replaceable>f</replaceable> <replaceable>nl</replaceable>)</synopsis>
<para>Returns a list that is the result of mapping procedure <replaceable>f</replaceable> over node-list <replaceable>nl</replaceable>.</para>
<programlisting>;; Map function `f' over node list `nl', returning an ordinary list.
;; (No node list constructor in Jade.)
(define (map-node-list->list f nl)
  (if (node-list-empty? nl)
      '()
      (cons (f (node-list-first nl))
            (map-node-list->list f (node-list-rest nl)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Vivek</firstname><surname>Agrawala</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Vivek Agrawala vivek@scr.siemens.com</authorinitials>
<revremark></revremark>
</revision>
</revhistory>
</sect2info>
<title>match-element?</title>
<synopsis>(match-element? <replaceable>pattern</> <replaceable>snl</>)</synopsis>
<para>This is a standard DSSSL procedure not currently available in Jade.
See section 10.2.5 of the DSSSL standard for complete semantics of this
function.</para>
<programlisting>
(define (match-element? pattern snl)
  (if (node-list-empty? (select-elements snl pattern)) #f #t) )
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>match-siblings</title>
<synopsis>(match-siblings <replaceable>osnl</replaceable>)</synopsis>
<para>Returns a node-list of siblings of <replaceable>osnl</replaceable>, or of the current node if <replaceable>osnl</replaceable> is not specified, with the same GI as the node.</para>
<programlisting>;; Node list of siblings with the same GI as node
(define (matching-siblings #!optional (node (current-node)))
  (select-elements (siblings node) (gi node)))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>next-matching-node</title>
<synopsis>(next-matching-node <replaceable>osnl</replaceable>)</synopsis>
<para>Returns either the following sibling with the same GI as <replaceable>osnl</replaceable> (or of the current node if <replaceable>osnl</replaceable> is not supplied) or an empty node-list if there is no following matching sibling.</para>
<programlisting>;; Return the following sibling with the same GI as `node' (or the
;; empty node list if none found).
(define (next-matching-node #!optional (node (current-node)))
  (node-list-ref (matching-siblings)
                 (child-number)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Norman</firstname><surname>Walsh</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Norman Walsh norm@berkshire.net</authorinitials>
<revremark>Submitted in private mail 19971022</revremark>
</revision>
</revhistory>
</sect2info>
<title>node-list-filter-by-gi</title>
<synopsis>(node-list-filter-by-gi <replaceable>nodelist</> <replaceable>gilist</>)</synopsis>
<para>Returns the node list of the elements in the <replaceable>node-list</replaceable> node list whose GI is included in the <replaceable>gilist</replaceable> list of strings.</para>
<programlisting>
(define (node-list-filter-by-gi nodelist gilist)
  ;; Returns the node-list that contains every element of the original
  ;; nodelist whose gi is in gilist
  (let loop ((result (empty-node-list)) (nl nodelist))
    (if (node-list-empty? nl)
	result
	(if (member (gi (node-list-first nl)) gilist)
	    (loop (node-list result (node-list-first nl)) 
		  (node-list-rest nl))
	    (loop result (node-list-rest nl))))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>prev-matching-node</title>
<synopsis>(prev-matching-node <replaceable>osnl</replaceable>)</synopsis>
<para>Returns either the previous sibling with the same GI as <replaceable>osnl</replaceable> (or of the current node if <replaceable>osnl</replaceable> is not supplied) or an empty node-list if there is no previous matching sibling.</para>
<programlisting>;; Return the preceding sibling with the same GI as `node' or the
;; empty node list.
(define (prev-matching-node #!optional (osnl (current-node)))
  (node-list-ref (matching-siblings)
                 (- (child-number osnl) 2)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>previous-element</title>
<synopsis>(previous-element <replaceable>osnl</replaceable>)</synopsis>
<para>Returns either a singleton node-list containing the previous sibling of <replaceable>osnl</replaceable>, or an empty node-list if <replaceable>osnl</replaceable> is the first sibling.</para>
<programlisting>;; In the absence of the full node machinery, return the preceding
;; sibling of `node' which is an element (or the empty node list if
;; none found).
(define (previous-element #!optional (node (current-node)))
  ;; cdr down the siblings keeping track of the last element node
  ;; visited and check the current car against `node'; if it matches,
  ;; return the noted previous.
  (let ((first (node-list-first (siblings))))
    (let loop ((previous (if (gi first)
                             first
                             (empty-node-list)))
               (current (node-list-rest (siblings))))
      (cond ((node-list-empty? current)
             (empty-node-list))
            ((node-list=? node (node-list-first current)) ; got it
             previous)
            (else (loop (if (gi (node-list-first current))
                            (node-list-first current)
                            previous)
                        (node-list-rest current)))))))</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>siblings</title>
<synopsis>(siblings <replaceable>osnl</replaceable>)</synopsis>
<para>Returns a node-list containing the siblings of <replaceable>osnl</replaceable>, including <replaceable>osnl</replaceable> itself.</para>
<programlisting>(define (siblings #!optional (osnl (current-node)))
  (children (parent osnl)))</programlisting>
</sect2>
</sect1>

<sect1>
<title>Flow Object</title>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>maybe-not-sosofo</title>
<synopsis>(maybe-not-sosofo <replaceable>predicate</replaceable> <replaceable>sosofo</replaceable>)</synopsis>
<para>Returns <literal>empty-sosofo</literal> if <replaceable>predicate</replaceable> evaluates to a true value, or <replaceable>sosofo</replaceable> if it does not.</para>
<programlisting>(define (maybe-not-sosofo predicate sosofo)
  (if predicate (empty-sosofo) sosofo))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>maybe-sosofo</title>
<synopsis>(maybe-not-sosofo <replaceable>predicate</replaceable> <replaceable>sosofo</replaceable>)</synopsis>
<para>Returns <replaceable>sosofo</replaceable> if <replaceable>predicate</replaceable> evaluates to a true value, or <literal>empty-sosofo</literal> if it does not.</para>
<programlisting>;; Conditionally use the sosofo or the empty one.
(define (maybe-sosofo predicate sosofo)
  (if predicate sosofo (empty-sosofo)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Chris</firstname><surname>Maden</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971022</date>
<authorinitials>Chris Maden crism@ora.com</authorinitials>
<revremark>Copied from author's DSSSList post of 19971017</revremark>
</revision>
</revhistory>
</sect2info>
<title>process-text</title>
<synopsis>(process-text <replaceable>snl</>)</synopsis>
<para>This procedure <emphasis>outlines</> a mechanism that preserves
entity references for special characters and SDATA entity references
in Jade output.
It relies on some DSSSL extensions implemented in Jade, and may not
work with other DSSSL engines.</para>
<para>If query construction rules are available in your DSSSL engine,
better solutions are possible.</para>
<programlisting>
;; Process the text under 'snl', replacing any special characters
;; and SDATA entities with appropriate entity references.
(define (process-text #!optional (osnl (current-node)))
  (let p-t-loop ((this-node (node-list-first (children snl)))
		 (other-nodes (node-list-rest (children snl))))
       (if (node-list-empty? this-node)
	   (empty-sosofo)
	   (sosofo-append (case (node-property 'class-name this-node)
		;; handle special characters
		((data-char) (case (node-property 'char this-node)
			   ;; ampersand
			   ((#\&) (make entity-ref name: "amp"))
			   ;; etc....
			   (else (process-node-list this-node))))
		;; handle SDATA entity references
		((sdata) (case (node-property 'system-data this-node)
		       ;; a with grave accent
		       (("[agrave]") (make entity-ref name: "agrave"))
		       ;; ampersand
		       (("[amp   ]") (make entity-ref name: "amp"))
		       ;; etc.... no else
			       ))
		(else (process-node-list this-node)))
		  (p-t-loop (node-list-first other-nodes)
			    (node-list-rest other-nodes))))))

;; An example use for a DocBook to HTML convertor, using the SGML backend.
;; Use (process-text) as the content-expression for any element construction
;; rule where the element has mixed-content.
;;(element CITETITLE
;;	 (make element
;;	       gi: "CITE"
;;	       attributes: (list (list "CLASS"
;;				       "CITETITLE"))
;;	       (process-text (current-node))))
</programlisting>
</sect2>

<sect2>
<sect2info>
<author>
<firstname>Tony</firstname><surname>Graham</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19971021</date>
<authorinitials>Tony Graham tgraham@mulberrytech.com</authorinitials>
<revremark>Based upon James Clark's DSSSList post of 19970429</revremark>
</revision>
</revhistory>
</sect2info>
<title>small-caps</title>
<synopsis>(small-caps <replaceable>children</>)</synopsis>
<para>This procedure is sets the <literal>glyph-subst-table</literal>
characteristic to a glyph table with glyphs for small caps letters in
place of the glyphs for lowercase letters.  Jade's RTF backend, at least,
recognises those glyphs and outputs the correct information for the text
to be formatted as small caps.</para>
<programlisting>
;; From code posted to the DSSSList by James Clark 4/29/97
(define small-caps-glyph-table
  (letrec ((signature (* #o375 256))
           (make-afii
            (lambda (n)
              (glyph-id (string-append "ISO/IEC 10036/RA//Glyphs::"
                                       (number->string n)))))
           (gen
            (lambda (from count)
              (if (= count 0)
                  '()
                  (cons (cons (make-afii from)
                              (make-afii (+ from signature)))
                        (gen (+ 1 from)
                             (- count 1)))))))
    (glyph-subst-table (gen #o141 26))))

(define (small-caps children)
  (make sequence
        glyph-subst-table: small-caps-glyph-table
        children))
</programlisting>
</sect2>
</sect1>

<sect1>
<title>Procedure</title>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>compose</title>
<synopsis>(compose <replaceable>f1</replaceable> <replaceable>f2</replaceable>)</synopsis>
<programlisting>;; Make a function equivalent to applying `f2' to its arguments and
;; `f1' to the result.
 (define (compose f1 f2)
   (lambda (#!rest rest) (f1 (apply f2 rest))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>const</title>
<synopsis>(const <replaceable>c</replaceable>)</synopsis>
<programlisting>;; Constant function evaluating to `c'.
(define (const c)
  (lambda (#!rest rest) c))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>curry</title>
<synopsis>(curry <replaceable>f</replaceable> <replaceable>arg</replaceable>)</synopsis>
<programlisting>;; Partially apply two-argument function `f' to `arg', returning a
;; one-argument function.
(define (curry f arg)
  (lambda (a) (f arg a)))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>curryn</title>
<synopsis>(curryn <replaceable>f</replaceable> <replaceable>rest</replaceable>)</synopsis>
<programlisting>;; n-ary variant
(define (curryn f #!rest rest)
  (lambda (#!rest args)
    (apply f (append rest args))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>foldl</title>
<synopsis>(foldl <replaceable>f</replaceable> <replaceable>zero</replaceable> <replaceable>xs</replaceable>)</synopsis>
<programlisting>;; Fold left with function `f' over list `xs' with the given `zero'
;; value.  (Like DSSSL `reduce' but normal arg order.)
(define (foldl f zero xs)
  (if (null? xs)
      zero
      (foldl f (f zero (car xs)) (cdr xs))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>foldl1</title>
<synopsis>(foldl1 <replaceable>f</replaceable> <replaceable>xs</replaceable>)</synopsis>
<programlisting>;; Fold left with list car as zero.
(define (foldl1 f xs)
  (cond ((null? xs)
         '())
        ((null? (cdr xs))
         (car xs))
        (else (foldl f (car xs) (cdr xs)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>foldr</title>
<synopsis>(foldr <replaceable>f</replaceable> <replaceable>zero</replaceable> <replaceable>xs</replaceable>)</synopsis>
<programlisting>;; Fold right, as above.
(define (foldr f zero xs)
  (if (null? xs)
      zero
      (f (car xs) (foldl f zero (cdr xs)))))</programlisting>
</sect2>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>id</title>
<synopsis>(id <replaceable>arg</replaceable>)</synopsis>
<para>Returns <replaceable>arg</replaceable>.</para>
<programlisting>(define (id arg) arg)</programlisting>
</sect2>
</sect1>

<sect1>
<title>Debugging</title>
<sect2>
<sect2info>
<author>
<firstname>Dave</firstname><surname>Love</surname>
</author>
<revhistory>
<revision>
<revnumber>1.0</revnumber>
<date>19970716</date>
<authorinitials>Dave Love d.love@dl.ac.uk</authorinitials>
<revremark>Copied from David's DSSSList post of 19970702</revremark>
</revision>
</revhistory>
</sect2info>
<title>my-debug</title>
<synopsis>(my-debug <replaceable>return-value</replaceable>)</synopsis>
<para>A version of debug that tries to print more helpful information
than <literal>&lt;unknown object ...</literal>.  Will need extending for any further
types added to Jade which don't have useful print methods.</para>
<programlisting>;; A version of debug that tries to print more helpful information
;; than `&lt;unknown object ...'.  Will need extending for any further
;; types added to Jade which don't have useful print methods.  Fixme:
;; should yield more information extracted from each type.
(define (my-debug x #!optional return-value)
  (debug (cond ((node-list? x)
                (if (node-list-empty? x)
                    (list 'empty-node-list x)
                    (list (if (named-node-list? x)
                              'named-node-list
                              'node-list)
                          (node-list-length x) x)))
               ((sosofo? x)
                (list 'sosofo x))
               ((procedure? x)
                (list 'procedure x))
               ((style? x)
                (list 'style x))
               ((address? x)
                (list 'address x))
               ((color? x)
                (list 'color x))
               ((color-space? x)
                (list 'color-space x))
               ((display-space? x)
                (list 'display-space x))
               ((inline-space? x)
                (list 'inline-space x))
               ((glyph-id? x)
                (list 'glyph-id x))
               ((glyph-subst-table? x)
                (list 'glyph-subst-table x))
               (else x))))</programlisting>
</sect2>
</sect1>

</chapter>
</book>
