我想我发现了你的问题。坦率地说,你还没有完全到那里。该作业明确指出,您必须在递归返回时建立结果。这是因为您不想在程序结束时reverse,或者在进行尾递归时执行append。
以下是解释每种方法的几个示例:
;; Using an accumulator
(define (double ls acc)
(if (empty? ls)
acc
(double (rest ls) (cons (* 2 (first ls)) acc))))
(double '(1 2 3) '())
;; = '(6 4 2)
;; So you could reverse the result: (reverse '(1 2 3) '()))
;; but this requires looping over the list again!
;; Using append
(define (double2 ls acc)
(if (empty? ls)
acc
(double2 (rest ls) (append acc (list (* 2 (first ls)))))))
(double2 '(1 2 3) '())
;; = '(2 4 6)
;; But the append operation is very expensive and you don't want that!
(define (double3 ls)
(if (empty? ls)
'()
(cons (* 2 (first ls)) (double3 (rest ls)))))
(double3 '(1 2 3))
;; Cons *after* the recursive call.
;; Requires more memory though, becuase you can not do tail call optimisation!
;; Info: http://c2.com/cgi/wiki?TailRecursion
所以对于函数slide-row-left/help的不同输入:
案例 1:前两个数相等
输入:'(2 2 4 -)
结果:'(4 4 '- '-)
您在这里要做的是将两个第一个元素 (4) 相加。然后你想计算列表的其余部分。列表的其余部分现在也应该包含一个额外的空白,因为将两个元素相加会使列表中的一个元素更短。因此,您在递归调用中将一个新空白传递给 acc 值。所以你首先要做的是计算列表的其余部分。因此,使用(drop ls 2) 和(cons '- acc) 递归调用。这会从列表中删除前 2 个元素。一旦你得到那个结果(结果将是'(4 '- '-),你只是在它前面cons你的总和。这导致总结果为'(4 4 '- '-)。
案例 2:前两个数字不同
输入:'(4 2 2 2)
结果:'(4 4 2 '-)
如果前两个数字不同,我们将无能为力。我们从列表中删除第一个数字 (4),剩下的是 '(2 2 2)。您不能删除前两个,因为第二个和第三个元素可能可以相加。您记住第一个元素并递归调用该函数以计算其余结果。该函数返回并为您提供'(4 2 '-)。现在你需要做的就是cons它前面的第一个元素,产生'(4 4 2 '-)。
案例 3:第一个元素为空白
输入:'(- 2 2 4)
结果:'(4 4 '- '-)
如果第一个数字是空白,您将无法对其进行任何操作。您可能认为在这种情况下 case 2 适用,但事实并非如此。应该在您的解决方案的后面放置一个空白。但是你怎么能这样做呢?很简单,您将空白放入蓄电池中。正如您很快将看到的,累加器基本上是您列表的末尾。因此,从列表中删除空白,留下'(2 2 4)。将'- 放在累加器前面,这使得累加器等于'('- <rest of acc>) 并进行递归调用。所以你用列表'(2 2 4) 和累加器'('- <rest of acc>) 来调用它。您的递归调用将产生'(4 4 '- '-)。因此,您不必再在它前面cons 任何东西,这只是您的结果。
案例 4:第二个元素为空白
输入:'(2 - 2 4)
结果:'(4 4 '- '-)
如果第二个数字是空白,情况就有点棘手了。您将不得不从列表中删除空白。所以你会想要(2 2 4)。为此,您可以使用(cons (first ls) (rest (rest ls)))。空白可以再次,不能被丢弃。您需要将其放在列表的末尾。列表的末尾在哪里?确实,蓄能器。所以你cons你想要在解决方案后面的元素(空白)到acc这样:(cons (second ls) acc)。同样,您将空白放在最后,然后进行递归调用。在递归调用的解决方案之前不需要放置任何元素,以便调用为您提供完整的答案。
案例 5:输入为空
输入:'()
结果:acc
如果您的输入为空,则需要返回 acc 值。由于您的输入为空,因此您需要返回列表的末尾,我们知道它是 acc 值。
案例6:输入长度为1
输入:'(4)
结果:(cons 4 acc)
如果输入只是一个元素,则不能对其应用任何总和或其他东西。你只有一个元素。所以,结果是 consd 在 acc 前面的那个元素。
来源
#lang racket
;; A shorthand for (first (rest ls))
(define (second ls) (first (rest ls)))
(define (blank? item) (equal? item '-))
(define (slide-row-left b)
(blank-help (slide-row-left/help b '()) '()))
(define (slide-row-left/help ls acc)
(cond [(empty? ls) acc]
[(= (length ls) 1) (cons (first ls) acc)]
;; When the first element is blank (e.g., '(- 2 4))
;; -> Discard the blank
;; -> call the function on the rest of the list.
[(blank? (first ls))
(slide-row-left/help (rest ls) (cons (first ls) acc))]
;; When the second element is blank (e.g., '(2 - 2 4))
;; -> Discard the second element
;; -> Run the function again with the entire list (without the blank)
[(blank? (second ls))
(slide-row-left/help (cons (first ls) (drop ls 2)) (cons (second ls) acc))]
;; If the first two elements are not equal:
;; -> drop 1 element from the list
;; -> cons this element to the result of the recursive call
[(not (equal? (first ls) (second ls)))
(let ([fst (first ls)]
[snd (second ls)]
[rst (rest ls)])
(cons fst (slide-row-left/help rst acc)))]
;; If the first two elements are the same:
;; -> drop 2 elements from the list
;; -> Sum them
;; -> cons the sum in front of the recursive call
[else
(let ([fst (first ls)]
[snd (second ls)]
[rst (drop ls 2)])
(cons (* 2 snd) (slide-row-left/help rst (cons '- acc))))]))
(define (blank-help ls acc)
(cond [(empty? ls) acc]
[(blank? (first ls)) (blank-help (rest ls) (cons (first ls) acc))]
[else (cons (first ls) (blank-help (rest ls) acc))]))
(define (check-expect x y)
(display x)
(display y)
(equal? x y))
(check-expect (slide-row-left '(2 2 4 -))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 - 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 - 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(- 2 2 4))(list 4 4 '- '-))
(check-expect (slide-row-left '(2 2 2 2))(list 4 4 '- '-))
(check-expect (slide-row-left '(4 2 2 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 4 2 2))(list 2 4 4 '-))
(check-expect (slide-row-left '(2 2 4 2))(list 4 4 2 '-))
(check-expect (slide-row-left '(2 2 4 4))(list 4 8 '- '-))
编辑
drop 函数用于删除列表的第一个 n 元素。它可以实现如下所示。但是,您也可以改用(rest (rest ls))。我用它是为了简洁。
放下
(define (drop ls n)
(cond [(empty? ls)
ls]
[(>= 0 n)
ls]
[else
(drop (rest ls) (- n 1))]))