问题描述
sash> (rotate '(a b c d e f g h) 3)
-> ((a b c) (d e f g h i k))
sash> (rotate '(a b c d e f g h) -2)
-> (g h a b c d e f)
Hint: Use the predefined functions length
and append
, as well as the result of problem P17.
解法
普通实现
按照题目的提示,很直观得到:
(define rotate (lambda (ls N) (cond [(zero? N) ls] [else (let* ([n (if (positive? N) N (+ (length ls) N))] [s (split ls n)]) (append (cadr s) (car s)))])))
使用到了
length
、append
,以及P17定义的split
。CPS实现
(define split-2 (lambda (ls n k) (let f ([i 1] [s ls] [k k]) (cond [(null? s) (k '() '())] [(<= i n) (f (+ i 1) (cdr s) (lambda (ls1 ls2) (k (cons (car s) ls1) ls2)))] [else (f (+ i 1) (cdr s) (lambda (ls1 ls2) (k ls1 (cons (car s) ls2))))])))) (define return (lambda (ls1 ls2) (append ls2 ls1))) (define rotate (lambda (ls N) (cond [(zero? N) ls] [else (let ([n (if (positive? N) N (+ (length ls) N))]) (split-2 ls n return))])))
split-2
的功能仍然是分割的两个列表,这个定义没有改变。这里改变了return
的定义,用来组装split-2
得到的两个列表,P17要求返回这两个列表作为元素的列表,而该题目则要求按规则append
成一个列表。rotate
则包含了顶层逻辑,处理N
的各种情况对应的结果逻辑。其他方式
看到另外一种解法:
(define (rotate xs n)
(if (< n 0)
(reverse (rotate (reverse xs) (- n)))
(let ((parts (split xs n)))
(append (cadr parts) (car parts)))))
看似简洁,但有逻辑冗余:当n
为负时,会用-n
作为rotate
的参数,这时仍然要判断if
语句,实际上我们已经知道了-n
为正。
不建议将对n
的处理放入递归逻辑中。