如何根据基数从数字列表中进行配对?

时间:2013-08-08 02:41:05

标签: list scheme racket

我有一个列表'(1 2 1 1 4 5),并希望输出列表为'((1 3)(2 1)(4 1)(5 1))。我写了一个小代码,但我仍然坚持如何计算每个数字的基数,然后把它作为对列表。任何人都可以看看我的代码并提出一些想法吗?

(define set2bags
  (lambda (randlist)
    (cond ((null? randlist) '())
          (else
           (sort randlist)
           (makepairs randlist)))))

(define makepairs
  (lambda (inlist)
    (let ((x 0)) ((newlist '()))
      (cond ((zero? (car inlist)) '())
            (else
             (eq? (car inlist)(car (cdr inlist))) 
             (+ x 1) 
             (makepairs (cdr inlist)) 
             (append newlist (cons (car inlist) x)))))))

2 个答案:

答案 0 :(得分:2)

您当前的解决方案不正确 - 它甚至无法编译。让我们从头开始,使用named let遍历输入列表:

(define set2bags
  (lambda (randlist)
    (cond ((null? randlist) '())
          (else (makepairs (sort randlist >))))))

(define makepairs
  (lambda (inlist)
    (let loop ((lst inlist)
               (prv (car inlist))
               (num 0)
               (acc '()))
      (cond ((null? lst)
             (cons (list prv num) acc))
            ((= (car lst) prv)
             (loop (cdr lst) prv (add1 num) acc))
            (else
             (loop (cdr lst) (car lst) 1 (cons (list prv num) acc)))))))

现在它按预期工作:

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

诀窍是为基数保留一个计数器(我称之为num),并且只要相同的前一个元素(我将其命名为prv)等于当前元素,就递增它。每当我们找到不同的元素时,我们会在输出列表中添加一个新对(称为acc)并重置前一个元素和计数器。

答案 1 :(得分:0)

如果没有适当的格式化,您的代码很难阅读。 我注意到一个两个分支cond,它更容易被读作if。

在你的set2bags的else子句中,你调用(sort randlist)但保持原样。你真的想在下一个s-expression(makepairs(sort randlist))

中使用它

到目前为止,这是个不错的主意。

现在在makepairs中你应该有更好的抽象,比如让变量像first-first和different-first。如果inlist为null,那么该函数应该是空列表,否则它与汽车的列表是like-first的汽车列表和like-first的长度,而cdr是结果在不同的第一个列表上调用makepairs

(define (makepairs inlist)
 (let ((like-first (filter (lambda (x) (equal? x (car inlist)) inlist))
       (unlike-first (filter (lambda (x) (not (equal? x (car inlist))) inlist)))
  (if (null? inlist)
      '()
       (cons (list (car inlist) (length like-first)) (makepairs unlike-first)))))

更有效的版本

(define (makepairs inlist)
 (if (null? inlist)
     '()
     (let loop ((firsts (list (car inlist))) 
               (but-firsts (cdr inlist)))
       (if (or (null? but-firsts) 
               (not (equal? (car firsts) (car but-firsts))))
           (cons (list (car firsts) (length firsts)) 
                 (makepairs but-firsts))
           (loop (cons (car but-firsts) firsts) (cdr but-firsts))))))

]=> (makepairs (list 1 1 1 2 4 5))

;Value 17: ((1 3) (2 1) (4 1) (5 1))

如果你有自己的排序实现,比如说一个mergesort你可以把它写进合并部分以获得最佳效果。

(define (set2bags lst)
 (mergesort2bags lst <))

(define (mergesort2bags lst pred)
 (let* ((halves (divide-evenly lst))
        (first-half (car halves))
        (other-half (cadr halves)))
 (cond  ((null? lst) '())
        ((null? (cdr lst)) (list (list (car lst) 1)))
        (else
         (merge-bags 
             (mergesort2bags first-half pred)
             (mergesort2bags other-half pred)
             pred)))))

(define (divide-evenly lst)
 (let loop
  ((to-go lst)
   (L1 '())
   (l2 '()))
  (if (null? to-go)
      (list L1 L2)
      (loop (cdr to-go) (cons (car to-go) L2) L1))))

(define (merge-bags L1 L2 pred)
 (cond ((null? L1) L2)
       ((null? L2) L1)
       ((pred (caar L1) (caar L2))  
        (cons (car L1) (merge-bags (cdr L1) L2 pred)))
       ((equal? (caar L1) (caar L2))
        (cons (list (caar L1) (+ (cadar L1) (cadar L2))) 
              (merge-bags (cdr L1) (cdr L2) pred)))
       (else  (cons (car L2) (merge-bags L1 (cdr L2) pred)))))

(mergesort2bags (list 1 2 1 1 4 5) <)

;Value 46: ((1 3) (2 1) (4 1) (5 1))

我正在思考非常大的数据集,这种方法会有很多重复,这种方法会有所回报。