【问题标题】:Why is my Reverse Function Reversing All my Pairs BESIDES One?为什么我的反转函数反转了我所有的对除了一个?
【发布时间】:2020-03-09 00:44:23
【问题描述】:

即我得到了这个(所有可能的组合来改变 11):

(list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25)

我的代码应该返回:

((7 . 1) (4 . 5) (3 . 1) (3 . 10) (2 . 1) (6 . 25))

所以它更具可读性。

但是,它正在返回:

((7 . 1) (4 . 5) (1 . 3) (3 . 10) (2 . 1) (6 . 25))

我不知道如何颠倒我所有配对的顺序。

这是我的代码:

(define (rev pair) ;; function reverses a pair, not a list.
   (map (lambda (e) (cons (cdr e) (car e))) pair)) 
(define (rle coins)
  (if (null? coins)
      '()
      (let loop ((firsts (list (car coins)))
                 (but-firsts (cdr coins)))
        (if (or (null? but-firsts)
                (not (equal? (car firsts) (car but-firsts))))
            (rev (cons (cons (car firsts) (length firsts))
                       (rle but-firsts)))
            (rev (loop (cons (car but-firsts) firsts) (cdr but-firsts)))))))

这是我的测试:

(rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))

【问题讨论】:

  • 我不确定,但 rev 函数可能会很好地反转该对,但 rle / loop 递归恰好在该对上调用 rev 两次,将其切换回原始状态。或者,它以某种方式调用 rev 在该对的列表中的偶数次,但在其他对的列表中的奇数次

标签: scheme racket computer-science r5rs


【解决方案1】:

为什么不写一个经典的尾调用递归函数呢? 因为我认为命名循环是可怕的结构——读者不清楚。

(define (rle coins (acc '()))
  (if (null? coins)
      (reverse acc)
      (rle (cdr coins)
           (if (and (not (null? acc)) (equal? (cdar acc) (car coins)))
               (cons (cons (+ 1 (caar acc)) 
                           (cdar acc))
                     (cdr acc))
               (cons (cons 1 (car coins))
                           acc)))))

正确:

(rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))
;; '((7 . 1) (4 . 5) (3 . 1) (3 . 10) (2 . 1) (6 . 25))

【讨论】:

    【解决方案2】:

    首先,进行一些系统性的测试(测试用例要系统化,尽量小,不要大而随意):

    > (rle '(2))
    '((1 . 2))
    > (rle '(2 3))
    '((1 . 2) (3 . 1))
    > (rle '(2 3 4))
    '((1 . 2) (3 . 1) (1 . 4))
    > (rle '(2 3 4 5))
    '((1 . 2) (3 . 1) (1 . 4) (5 . 1))
    > (rle '(2 3 4 5 6))
    '((1 . 2) (3 . 1) (1 . 4) (5 . 1) (1 . 6))
    

    当所有元素都是唯一的时,看起来其他所有元素都是倒退的。

    > (rle '(5 5 6))
    '((5 . 2) (1 . 6))
    > (rle '(5 5 6 6))
    '((5 . 2) (6 . 2))
    > (rle '(5 5 6 6 6))
    '((5 . 2) (3 . 6))
    > (rle '(5 6 6 6))
    '((1 . 5) (6 . 3))
    

    当元素重复时,顺序似乎有点不可预测。

    让我们用计数器作为car 的三重元素来计算每个元素rev 的次数:

    ;; Flip the order of a pair.
    (define (flip x)
      (cons (cdr x) (car x)))
    
    ;; Add one to the car of each element and flip its cdr.
    (define (rev list)
      (map (lambda (x) (cons (+ 1 (car x)) (flip (cdr x)))) list))
    
    (define (rle coins)
      (if (null? coins)
          '()
          (let loop ((firsts (list (car coins)))
                     (but-firsts (cdr coins)))
            (if (or (null? but-firsts)
                    (not (equal? (car firsts) (car but-firsts))))
                (rev (cons (cons 0 (cons (car firsts) (length firsts)))
                           (rle but-firsts)))
                (rev (loop (cons (car but-firsts) firsts) (cdr but-firsts)))))))
    

    这将创建一个列表,说明rev 已应用于结果中的每个元素的次数。

    > (rle '(99))
    '((1 1 . 99))
    > (rle '(99 99))
    '((2 99 . 2))
    > (rle '(99 99 99))
    '((3 3 . 99))
    > (rle '(99 99 99 100))
    '((3 3 . 99) (4 100 . 1))
    > (rle '(99 99 99 100 100))
    '((3 3 . 99) (5 2 . 100))
    

    如您所见,每个元素都被“翻转”了很多次——每次递归一次。
    发生这种情况是因为您将 rev 应用于每个部分结果,而不仅仅是在最终列表中。
    被翻转了偶数次的元素是“向后”的。

    现在,您的示例输入:

    > (rle (list 1 1 1 1 1 1 1 5 5 5 5 1 1 1 10 10 10 1 1 25 25 25 25 25 25))
    '((7 7 . 1) (11 4 . 5) (14 1 . 3) (17 3 . 10) (19 2 . 1) (25 6 . 25))
    

    我们可以看到第三个元素被翻转了偶数次,这就是导致它向后翻转的原因。

    最好的解决方案是按照你想要的顺序构建你的配对,这样你就不需要在之后调整它们;实现留作练习。
    (实现是您的程序中的一个非常小的更改,并删除了rev。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-26
      • 2019-06-20
      • 1970-01-01
      • 2020-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多