2

我已经开始编写一个函数来查找列表中元素的最后一次出现。我的想法是用来search计算指定符号的出现次数并返回它。然后我会将计数传递给removeLast它将删除元素。然后我会减少计数removeLast以促进基本情况。从我所看到的情况来看,使用set!通常是不好的做法。有没有更好/更优雅的方式来“记住”最后一次出现的符号。

(define (lastLess lis symbol)
  (define count 0)
   (set! count (search symbol lis count))
  (removeLast symbol lis count)
)

(define (search symbol lis count )
  ( cond ((null? lis) count)
     ( (eq? symbol (car lis)) (+ count (add1 (search symbol (cdr lis) count ))) )
     ( (pair? (car lis))(+ count(+ 0 (search symbol (car lis) count ))))
     ( else (+ count(+ 0 (search symbol (cdr lis) count))))
     )
)

(define (removeLast symbol lis count)
  (cond ((null? lis) '())
    ((eq? count 0) (cdr lis))
    ((eq? symbol (car lis)) ((set! count (sub1 count)) 
                             (cons (car lis)(removeLast symbol (cdr lis) count))
                            )
                            )
    ((pair? (car lis)) (removeLast symbol (car lis) count))
    (else (cons (car lis) (removeLast symbol (cdr lis) count )))
    )
)

按原样运行代码会((set! count (sub1 count))(cons (car lis)(removeLast symbol (cdr lis) count))))引发错误:

申请:不是程序;期望一个可以应用于给定参数的过程:# arguments...: '(e)

编辑:这是一个类的分配,所以多余reverse的 s 是不可接受的,我必须考虑嵌套列表。

4

3 回答 3

3

You should use built-in procedures for this. In particular, notice that remove deletes the first element of lis that is equal to symbol, so removing the last element is a simple matter of reversing the list:

(define (lastLess lis symbol)
  (reverse (remove symbol (reverse lis))))

(lastLess '(1 2 3 4 5 1) 1)
=> '(1 2 3 4 5)

The above solution doesn't require at all using set!, which as you suspect, is not recommended - although it's possible to solve this problem mutating the list, a functional solution is preferred in Scheme.

Of course, a more efficient solution could be written, one that only traverses the list once, but ask yourself: do you really need the added complexity of such a solution? is a high performance so important? if the answer is no, then stick to a simpler, clearer solution.

于 2013-06-05T01:00:09.523 回答
2

您遇到的错误来自该cond子句。你有额外的括号 (set!count ...)。

你的问题是你对 set 的痴迷!这个:

(define (lastLess lis symbol)
  (define count 0)
  (set! count (search symbol lis count))
  (removeLast symbol lis count))

本来可以做的

(define (lastLess lis symbol)
  (removeLast symbol lis (search symbol lis 0)))

或者我你想要分配,这在使用结果不止一个地方时很好

(define (lastLess lis symbol)
  (let ((count (search symbol lis 0)))
    (if (< 0 count) ; noe or more occurences
        (removeLast symbol lis count)
        lis)))

您的搜索过程将从列表中的列表开始,而不会以它所在的列表结束。(a b (c d b) a b)您的过程中的每个 for a 将返回 2 而不是 3。 + 可以有任意数量的参数,因此您不需要嵌套它们。尝试这个:

(define (search symbol lis count)
  (cond ((null? lis) count)
     ((eq? symbol (car lis)) (search symbol (cdr lis) (add1 count)))
     ((pair? (car lis)) (search symbol (cdr lis)
                                (search symbol (car lis) count)))
     (else (search symbol (cdr lis) count))))

请注意如何处理对。现在,当计数为零但符号匹配且计数为 1 时,您的 removeLast 不应跳过它所获得的任何内容。

祝你好运!

于 2013-06-05T10:00:56.460 回答
0

这仍然使用 set,但我认为这是一个更优雅的解决方案,因为 set 在匿名函数的闭包中使用,您将其作为参数作为 foldr 传递。在 if 找到第一个匹配项之后(记住从尾部工作,它会停用自身以作为缺点工作。由于 foldr 从列表的尾部工作,因此不需要额外的遍历。make-remover 是一个功能过程(一个输入将总是映射到相同的输出)但是输出是非功能性的,但这并不是什么大问题,因为你只使用它一次并把它扔掉。(无论如何你都不想要一对一的映射)

(define (remove-last x L)
 (let ((proc (make-remover x)))
  (foldr proc '() L)))


(define (make-remover removee)
 (let ((active? #t))
  (lambda (x y)
   (cond ((not active?) (cons x y))
         ((eq? x removee) (begin (set! active? #f) y))
         (else (cons x y))))))

这不会遍历树(在嵌套列表上工作),但是在将过程应用于子列表之前将 foldr 修改为将在子列表上折叠的 foldr-tree 应该不是很困难。

于 2013-06-08T15:44:13.773 回答