【问题标题】:Removing repeated elements in lists删除列表中的重复元素
【发布时间】:2014-07-06 05:43:07
【问题描述】:

我在编写一些代码时遇到了一些问题,但我无法找到错误所在。编程语言在scheme中,问题如下:

只留下不重复的元素。 例如:(a b a a c c) -> (a b)

我已经写了下面的代码。

    (define x '(a b a a a c c))
    (display x)
    (define z '())
    (define (removeReps y)
    (if (null? y)
      '()
      (if( = (car y) (removeReps (cdr y)))  '() (append z (car y)))))
    (removeReps x)
    (display z)

为了全面披露,这是一个家庭作业,但我无法解决它。

谢谢。

【问题讨论】:

    标签: list functional-programming scheme lisp


    【解决方案1】:

    另一个似乎可以处理测试用例的递归解决方案。

    ;rep is the last repeated elem, input is the input list and first is boolean
    (define notrepeatedrec
      (lambda (rep input first)
        (cond ((null? input) (if (eq? first #t) (list rep) '()))
              ((eq? rep (car input)) (notrepeatedrec (car input) (cdr input) #f))
              (else (if (eq? first #t) 
                      (cons rep (notrepeatedrec (car input) (cdr input) #t))
                      (notrepeatedrec (car input) (cdr input) #t))))))
    
    ;helper function to start the recursion
    (define notrepeated 
      (lambda (lst)
        (notrepeatedrec '() lst #f)))
    

    【讨论】:

    • 除了 '(x a a x b b x c c x) 测试用例,我编辑了一个简单的修复,你能告诉我它错过了哪个测试用例吗?谢谢一百万!
    • 谢谢你!我喜欢使用标志而不是计数器来判断前一个元素是否多次出现的想法。征得您的许可,我会将其添加到我的第一个解决方案中;)
    • 当然!你和克里斯在记录背后的整个思考过程方面做得很好!
    【解决方案2】:

    解决方案不是那么那么简单,您必须在迭代时跟踪找到的前一个元素,并且还要有一个标志告诉您前一个元素是否多次出现。这是我的镜头,假设输入列表不为空(如果输入列表为空,处理这种情况很简单,留给读者练习):

    (define (removeReps lst)
      ; we need some extra parameters, use a named let for iteration
      (let loop ([lst (cdr lst)]  ; list to process, assuming non-empty
                 [prev (car lst)] ; pick first as "previous" value
                 [first #t])      ; flag: is this the first element in sequence?
        (cond ((null? lst)        ; if we reached the end of the list
               (if first          ; edge case: handle last element
                   (list prev)    ; if it was the first in sequence add it
                   '()))          ; otherwise skip it and end list
              ; if current element equals previous element
              ((equal? (car lst) prev)
               ; skip it, advance recursion and update flag
               (loop (cdr lst) prev #f))
              ; new element, if previous element had exactly one repetition
              (first
               ; add it to output, advance recursion, update prev and flag
               (cons prev (loop (cdr lst) (car lst) #t)))
              ; new element, if previous element had more than one repetition
              (else
               ; skip it, advance recursion, update prev and flag
               (loop (cdr lst) (car lst) #t)))))
    

    更新

    我真的很喜欢 @chris 在 Haskell 中的实现:更高级别并利用现有过程而不是显式递归,它适用于空列表,并且转换为 Scheme 并不难(它在 Scheme 中比在 Haskell 中更冗长,不过。)这是使用 Racket 和 SRFI-1 的span 程序的另一个选项,请查看@chris 的答案以了解其工作原理:

    (require srfi/1) ; import `span`
    
    (define (group lst)
      (match lst
        ['() '()]
        [(cons x xs)
         (let-values (((ys zs) (span (curry equal? x) xs)))
           (cons (cons x ys) (group zs)))]))
    
    (define (removeReps lst)
      (filter-map (lambda (x) (and (= (length x) 1) (first x)))
                  (group lst)))
    

    或者更便携,无需使用特定于 Racket 的程序和特殊形式:

    (require srfi/1) ; import `span`
    
    (define (group lst)
      (if (null? lst)
          '()
          (let ((x  (car lst))
                (xs (cdr lst)))
            (let-values (((ys zs) (span (lambda (e) (equal? e x)) xs)))
              (cons (cons x ys)
                    (group zs))))))
    
    (define (removeReps lst)
      (map car
           (filter (lambda (x) (= (length x) 1))
                   (group lst))))
    

    让我们用一些边缘情况来测试这些过程 - 它与上述任何实现都按预期工作:

    (removeReps '(a b a a a c c))
    => '(a b)
    
    (removeReps '(x a a x b b x c c x))
    => '(x x x x)
    
    (removeReps '(a a a))
    => '()
    
    (removeReps '(a b b c c))
    => '(a)
    
    (removeReps '(a a b b c c d))
    => '(d)
    

    【讨论】:

      【解决方案3】:

      另一种可能的解决方案如下

      import Data.List
      
      removeDups :: Eq a => [a] -> [a]
      removeDups = map head . filter ((== 1) . length) . group
      

      用 Haskell 编写并使用库函数 grouplength(==)filterheadmap

      由于上面的内容可能不太容易阅读,我将逐步完成定义

      首先对构成上述定义的各个部分进行文字描述

      1. 首先将列表分组成包含相同元素的子列表。
      2. 对于每一个,检查它的长度是否正好为 1。如果是,请保留它,否则将其丢弃。
      3. 从列表的结果列表(我们知道每个元素的长度正好为 1)中,我们实际上只需要单例元素,它们对应于单个列表的 heads

      现在是一些代码。一个将列表元素组合在一起的函数,只要它们相等就可以定义如下(对不起,我使用的是 Haskell 语法,因为我对方案不是很熟悉,但应该很容易翻译):

      group :: Eq a -> [a] -> [[a]]
      group []     =  []
      group (x:xs) =  (x:ys) : group zs
        where (ys, zs) = span (== x) xs
      

      其中span 是另一个库函数,给定一些谓词p,它将其输入列表拆分为满足p 的初始元素段all 和列表的其余部分。为了完整起见,可以定义如下

      span :: (a -> Bool) -> [a] -> ([a], [a])
      span _ [] =  ([], [])
      span p xs@(x:xs')
        | p x =  let (ys, zs) = span p xs' in (x:ys, zs)
        | otherwise =  ([], xs)
      

      mapfilterhead 比这些更标准,我确信它们是方案库的一部分(可能是 group)。

      我想我的主要观点是,只要将问题分成小块的子问题(使用一些预定义的函数)并组合结果,解决方案就很简单。

      【讨论】:

      • 这是一个很好的 Haskell 解决方案,但我认为 OP 对 Lisp 或 Scheme 实现感兴趣......
      • 是的。但正如我提到的,我不熟悉Scheme ;)。我很确定它相当于headfiltermapgroupspan 怎么样?那么将我的解决方案翻译成任何其他函数式语言应该是微不足道的。
      • 当然,Scheme 中有 head filtermap 的等价物。 Groupspan 在标准方案中没有直接等价物(span 在 SRFI-1 中可用),但正如您指出的那样,看起来很容易翻译
      • 这看起来很有趣,所以我在 Scheme 中翻译了您的实现,请参阅我的更新答案。正如预期的那样,这并不太难。
      【解决方案4】:

      在 Common Lisp 中,我会为列表使用堆栈 api:

      (defun elim-dup (l)
          (if (null l)
              nil
              (progn
                  (setf new ())
                  (dolist (x l)
                      (setq elem (pop l))
                      (if (not (member elem new))
                          (push elem new)))
                  new)))
      

      如果您喜欢原始顺序,请添加:

      (reverse new)
      

      为避免破坏原始列表,您必须取第 n 个元素而不是 pop(并维护一个计数器)。 pop 更直接但具有破坏性。

      【讨论】:

        【解决方案5】:

        可能可以接受以下内容:

        ;; expr list -> list
        ;; produce a list where leading elements equal to EXPR have been dropped
        (define (drop-in-front elt list)
          (cond [(null? list) list]                            ; empty list, we're done
                [(equal? elt (first list))                     ; first element matches
                 (drop-in-front elt (rest list))]              ; drop and repeat
                [#t list]))                                    ; no match, we're done
        
        ;; list -> list
        ;; produce a list where blocks of repeated elements have been dropped
        (define (remove-repeated list)
          (cond [(or (null? list)                              ; empty list
                     (null? (cdr list)))                       ; or list with one element
                 list]                                         ; we're done
                [(equal? (first list)                 
                         (second list))                        ; two matching elements in front
                 (remove-repeated                              ; continue with
                  (drop-in-front (first list)                  ; list where block in front
                                 (rest list)))]                ; has been dropped
                [#t                                            ; first elt different from snd
                 (cons (first list)                            ; stick it in front of
                       (remove-repeated (rest list)))]))       ; cleaned up rest of list
        

        我真诚地希望你的课程能尽快提供一些风格指南。

        如果有机会,可以试试看Introduction to Systematic Program Design的讲座或者看看How to Design Programs

        【讨论】:

          猜你喜欢
          • 2020-01-19
          • 2020-08-02
          • 2018-05-02
          • 1970-01-01
          • 2020-11-02
          • 2015-01-05
          • 2012-05-09
          • 2018-01-01
          • 1970-01-01
          相关资源
          最近更新 更多