Есть ли общепринятые метода работы с необязательными параметрами в Scheme? Вот, для ясности, пример функции:
;; string-prefix? s1 s2 [start1 [end1 [start2 [end2]]]] -> boolean
(define (string-prefix? s1 s2 . optional) ...)
Можно для каждой функции делать уникальный разбор списка optional, но это не кошерно. Наверняка есть какой-то общепринятый макрос, который все используют?
Я вот попытался написать:
(define-syntax optional-args
(syntax-rules ()
((_ (args (var0 default0) ...) body ...)
(let-values (((var0 ...)
(if (> (length args) (length '(var0 ...)))
(syntax-error "too many args")
(apply values
(let loop ((a args) (d (list default0 ...)) (acc '()))
(if (null? d) (reverse acc)
(if (null? a)
(loop '() (cdr d) (cons (car d) acc))
(loop (cdr a) (cdr d) (cons (car a) acc)))))))))
body ...))))
Применяется так:
(define (string-prefix? prefix str . opt)
...
(optional-args
(opt (start1 0) (end1 prefix-length)
(start2 0) (end2 str-length))
...)))
Но мне не нравится, что он очень медленно работает. Такой вариант значительно быстрее:
(define (string-prefix1? prefix str . opt)
(let ((opt-length (length opt))
...)
(let ((start1 (if (> opt-length 0) (nth opt 0) 0))
(end1 (if (> opt-length 1) (nth opt 1) prefix-length))
(start2 (if (> opt-length 2) (nth opt 2) 0))
(end2 (if (> opt-length 3) (nth opt 3) str-length)))
...)))
Но я не смог написать макрос, который бы разворачивался в такую конструкцию. Вот заготовка:
(define-syntax optional
(syntax-rules ()
((_ args ((var0 ...) (default0 ...)) body ...)
'(let ((len (length args)))
(let ((var0 default0) ...)
body ...)))))
Должно быть по идее так:
(let ((var0 (if (> (length args) X) (nth args X) default0)) ...)
где X - порядковый номер подстановки (arg0 ...), но я не нашел способа определить этот номер. Может быть кто-то подскажет? Или скажет, почему это не нужно?
Делается это всё для R7RS на chibi-scheme.