查找并删除列表中指定元素的最后一次出现[racket]

时间:2013-06-05 00:41:13

标签: recursion scheme racket

我已经开始编写一个函数,该函数应该找到列表中最后一个元素。我的想法是使用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))))会引发错误:

  

申请:不是程序;   期望一个可以应用于参数的过程    给出:#    参数...:     “(E)

编辑:这是课程的作业,因此不允许使用冗余的reverse,我必须考虑嵌套列表。

3 个答案:

答案 0 :(得分:3)

您应该使用内置程序。请特别注意remove删除lis第一个元素,该元素等于symbol,因此删除 last 元素是一个简单的事情就是反转清单:

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

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

上述解决方案根本不需要使用set!,因为您怀疑,不建议使用{{1}} - 虽然可以解决此问题,但是在方案中可以使用功能解决方案。

当然,可以编写一个更有效的解决方案,一个只遍历列表一次,但问问自己:你真的需要增加这种解决方案的复杂性吗?高性能如此重要?如果答案是否定的,那就坚持一个更简单,更清晰的解决方案。

答案 1 :(得分:2)

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

你的问题是你对集合的痴迷! 这样:

(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),您的过程将返回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))))

注意如何处理对。现在你的removeLast不应该跳过当count为零但是当符号匹配并且count为1时得到的任何东西。

祝你好运!

答案 2 :(得分:0)

这仍然使用set,但是我认为这是一个更优雅的解决方案,因为该set在匿名函数的闭包中使用,您作为foldr作为参数传递。如果找到第一个匹配(记住从尾部开始工作,它会使自身失效以作为缺点。由于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应该不会很困难,它会在将过程应用到子列表之前对子列表进行折叠。