查找列表中出现一个数字的次数

时间:2011-01-25 04:16:28

标签: list functional-programming scheme

如果我们有list A (1 2 1 1 2 3 3 4 4 4)我们如何获得新的list B ( (1 . 30) (2 . 20) (3 . 20) (4 . 30))?如果number_before_dot是列表A`中的%number_before_dot。例如,30是列表A中的%1,列表A中的20%2 ..等等。 (1.30)是一对,可以由(cons 1 30)

制作

2 个答案:

答案 0 :(得分:1)

我认为您要做的是计算列表中与每个元素相等的百分比。您使用了“unique”这个词,但由于您的列表没有唯一元素,因此有点令人困惑。这是基于您的示例输入和输出,其中列表(1 2 1 1 2 3 3 4 4 4)由“30%的”组成。

您可以将其大致分解为包含以下步骤的递归算法:

  1. 如果输入列表为空,则返回空列表。
  2. 否则,获取第一个元素。计算列表中出现的次数。
  3. 计算百分比,并使用此百分比计算cons元素。
  4. 从列表的cdr中删除所有第一个项目。
  5. 递归此新列表,并cons列出(element . percentage)对列表。
  6. 要完成第一部分,我们使用filter

    > (filter (lambda (x) (eq? (car A) x)) A)
    (1 1 1)
    

    使用您的列表A,这将返回列表(1 1 1)。然后我们可以使用length来获取它出现的次数:

    > (length (filter (lambda (x) (eq? (car A) x)) A))
    3
    

    要计算百分比,除以整个列表中的元素数量,或(length A)并乘以100:

    > (* 100 (/ (length (filter (lambda (x) (eq? (car A) x)) A)) (length A)))
    30
    

    使用元素cons (car A)很容易获得最终列表的对。

    要执行第二步,我们可以使用remove,它是filter的反转:它将返回原始列表中不满足谓词函数的所有元素的列表:

    > (remove (lambda (x) (eq? (car A) x)) A)
    (2 2 3 3 4 4 4)
    

    这是我们想要递归的列表。请注意,在每个步骤中,您需要具有原始列表(或原始列表的长度)和此新列表。所以你需要通过一个额外的参数或定义一个内部定义以某种方式使它可用于递归过程。

    我可能会有更有效的方式,或者其他方式,但这是我在阅读问题时提出的解决方案。希望它有所帮助!

    (define (percentages all)
      (let ((len (length all))) ; pre-calculate the length
        ;; this is an internal definition which is called at ***
        (define (p rest)
          (if (null? rest)
              rest
              ;; equal-to is a list of all the elements equal to the first
              ;; ie something like (1 1 1)
              (let ((equal-to (filter (lambda (x) (eq? (car rest) x))
                                      rest))
                    ;; not-equal-to is the rest of the list
                    ;; ie something like (2 2 3 3 4 4 4)
                    (not-equal-to (remove (lambda (x) (eq? (car rest) x))
                                          rest)))
                (cons (cons (car rest) (* 100 (/ (length equal-to) len)))
                      ;; recurse on the rest of the list
                      (p not-equal-to)))))
        (p all))) ; ***
    

答案 1 :(得分:1)

问题的表述与run-length encoding的概念非常接近。在游程编码方面,您可以使用一个简单的策略:

  1. 排序。
  2. 游程编码。
  3. 缩放游程长度以获得百分比。
  4. 您可以像这样实现行程编码:

    (define (run-length-encode lst)
      (define (rle val-lst cur-val cur-cnt acc)
        (if (pair? val-lst)
            (let ((new-val (car val-lst)))
              (if (eq? new-val cur-val)
                  (rle (cdr val-lst) cur-val (+ cur-cnt 1) acc)
                  (rle (cdr val-lst) new-val 1 (cons (cons cur-val cur-cnt) acc))))
            (cons (cons cur-val cur-cnt) acc)))
      (if (pair? lst)
          (reverse (rle (cdr lst) (car lst) 1 '()))
          '()))
    

    和缩放看起来像:

    (define (scale-cdr count-list total-count)
      (define (normalize pr)
        (cons (car pr) (/ (* 100 (cdr pr)) total-count)))
      (map normalize count-list))
    

    现在我们需要对列表进行排序。我将在球拍中使用sort功能(根据需要进行调整)。然后,计算列表中每个数字的百分比的函数是:

    (define (elem-percent lst)
      (scale-cdr (run-length-encode (sort lst <)) (length lst)))
    

    一些使用示例:

    > (elem-percent '())
    '()
    > (elem-percent (list 1 2 3 4 5))
    '((1 . 20) (2 . 20) (3 . 20) (4 . 20) (5 . 20))
    > (elem-percent (list 1 2 1 1))
    '((1 . 75) (2 . 25))
    > (elem-percent (list 1 2 1 1 2 3 3 4 4 4))
    '((1 . 30) (2 . 20) (3 . 20) (4 . 30))