为简单起见,使用“head-sentinel 技巧”以自上而下的方式构建结果列表:
(define (rle lst)
(if (null? lst)
'()
(let ((res (list 1))) ; head sentinel
(let loop ((p res) ; result's last cons cell
(elt (car lst))
(cnt 1)
(lst (cdr lst)))
(if (and (not (null? lst))
(equal? elt (car lst)))
(loop p elt (+ cnt 1) (cdr lst))
(begin
(set-cdr! p (list (if (= 1 cnt) elt (list elt cnt))))
(if (null? lst)
(cdr res) ; skip the head in result, on return
(loop (cdr p) (car lst) 1 (cdr lst)))))))))
正如@uselpa 解释的那样,这称为游程编码;为了结果的一致性,我建议(x 1)
对非重复元素使用表示。
而错误“错误:应用程序:不是程序;预期程序”,正如其他人所说,意味着系统期望找到一个程序但发现了其他东西,所以不能应用它。Scheme 期望在 list: 中找到一个作为第一个形式的过程(proc args ...)
,并尝试将其应用于参数。但是在您的代码中,它不是一个过程,而是一些其他类型的数据。
如果您的方案有左折叠,或者reduce
,您可以运行两次 - 首先收集统一的结果,然后在反转时应用您的特殊格式(左折叠的结果通常以相反的顺序构建):
(define (fold f init lst) ; if fold is not defined,
(reduce f init (cons init lst))) ; define it in terms of reduce
(define (rle lst)
(fold (lambda (x acc) ; NB! MIT-Scheme: (acc x)
(if (= 1 (cadr x)) (cons (car x) acc) (cons x acc)))
'()
(fold (lambda (x acc) ; NB! MIT-Scheme: (acc x)
(if (or (null? acc) (not (equal? (caar acc) x)))
(cons (list x 1) acc)
(cons (list x (+ (cadar acc) 1)) (cdr acc))))
'()
lst)))