【问题标题】:Scheme function to reverse a list反转列表的方案函数
【发布时间】:2014-02-27 05:58:17
【问题描述】:

对于我的编程语言课程,我应该在 Scheme 中编写一个函数来反转列表,而不使用预制的反转函数。到目前为止,我得到的是

(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (CONS (reverseList(CDR lst)) (CAR lst)))
))

我遇到的问题是,如果我输入一个列表,比如说(a b c),它会给我(((() . c) . b) . a)

我应该如何获得一个没有多组括号和. 的干净列表?

【问题讨论】:

  • 您了解每个. 的含义以及为什么会看到这么多括号吗?这里的答案将为您指出正确的解决方案,WillNess 的解决方案涵盖了您所看到的内容,但您也可能从Dot notation in schemeRecursive range in Lisp adds a period? 中受益。您可能还会发现 How to Reverse a List? 很有用。
  • 更改算法。您的算法在反向列表的头部插入一个空列表。并且对于部分反转它继续这样做。我认为您的意思是连接列表,以便您得到一个平面列表,而不是列表列表的列表......列表。使用追加而不是缺点

标签: list scheme reverse


【解决方案1】:

您的实现的问题是 cons 没有接收 list 作为其第二个参数,因此您正在构建的答案不是正确的列表,请记住:正确的列表由consing 一个带有列表的元素构造,最后一个列表为空。

一种可能的解决方法是使用辅助函数在累加器参数中构建答案,consing 反向元素 - 顺便说一下,这个解决方案是尾递归的:

(define (reverse lst)
  (reverse-helper lst '()))

(define (reverse-helper lst acc)
  (if (null? lst)
      acc
      (reverse-helper (cdr lst) (cons (car lst) acc))))

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

【讨论】:

  • reverse-helper 的更好名称是 append-reverse,因为这就是 SRFI 1 中调用的函数,它看起来不像是人工助手,而更像是一个真正的函数。 :-)
【解决方案2】:

你已经成功了一半。结果中元素的顺序是正确的,只有结构需要修复。

你想要的是执行这个转换:

 (((() . c) . b) . a)           ; input
 --------------------
 (((() . c) . b) . a)   ()      ; trans-
  ((() . c) . b)       (a)      ;      for-
   (() . c)          (b a)      ;         mation
    ()             (c b a)      ;               steps
      --------------------
                   (c b a)      ;               result

这很容易编码。中间值的carcdr 立即可供我们使用。在每一步,下一个中间结果由(cons (cdr interim-value) interim-result) 构造,interim-result 以一个空列表开始,因为这是我们在这里构造的 - list

(define (transform-rev input)
   (let step  ( (interim-value    input)       ; initial set-up of 
                (interim-result    '() ) )     ;  the two loop variables
     (if (null? interim-value)
       interim-result                  ; return it in the end, or else
       (step  (car interim-value)      ; go on with the next interim value
              (cons                    ;      and the next interim result
                  (... what goes here? ...)
                  interim-result )))))

interim-result 用作累加器。这就是所谓的“累加器技术”。 step 表示使用“named-let”语法编码的循环步骤。

所以整体反转是

(define (my-reverse lst)
   (transform-rev 
       (reverseList lst)))

您能否调整transform-rev 使其能够接受原始列表作为输入,从而跳过reverseList 调用?您只需要更改数据访问部分,即如何获得下一个中间值,以及添加到中间结果中的内容。

【讨论】:

    【解决方案3】:
    (define (my-reverse L)
     (fold cons '() L)) ;;left fold
    

    【讨论】:

    • 你能扩展你的答案吗?这被标记为低质量。
    • @CodeMaverick 折叠从一个初始值开始(在这种情况下为'()),然后使用给定函数(在这种情况下为cons)更新列表中每个项目的值(在左折叠的情况下从左到右)。由于cons 从右到左构建列表,并且由于输入元素从左到右(由于左折叠)来自传入列表,因此结果列表与原始列表相反。同样,您可以使用右折叠来复制列表,它从右到左获取输入元素。
    • Fold /fold-left 也是尾递归的,无论输入列表有多大,堆栈都不会溢出。
    【解决方案4】:

    遍历列表并继续将列表中的汽车附加到递归调用中。

    (define (reverseList lst)
    (COND
    ((NULL? lst) '())
    (ELSE (APPEND (reverseList(CDR lst)) (LIST (CAR      lst))))
    ))
    

    【讨论】:

    • 使用append 建立一个列表有点不受欢迎,这意味着你没有以正确的方式重复出现。除了效率低下之外,还可以将其视为Schlemiel the Painter's algorithm。大多数时候您想使用cons 和尾递归来构建列表,请参阅我的答案。
    • 读起来很有趣。我想我是众多有基本缺陷的毕业生之一 :-) 谢谢。
    • 辅助函数有什么理由或好处吗?在我看来,您可以将其代码放入原始函数中,[将 acc 替换为 '()] 并且一切正常?
    • 我把它写成一个助手,因为原来的函数只接收一个参数,但是我们需要两个参数来实现尾递归。通常我会把它写成一个名为let,以避免创建两个函数,但这对于OP来说可能更难理解
    • 在 lisp 中追加并不是低效的,你不是在移动数据,只是设置指针。不在乎奥斯卡洛佩兹说什么。算法没问题,所以没问题。
    【解决方案5】:

    不要使用cons,试试append

    (define (reverseList lst)
        (if (null? lst)
            '()
            (append (reverseList (cdr lst)) (list (car lst)) )
        )
    )
    

    示例运行将是:

    1]=> (reverseList '(a b c 1 2 + -))
    
    >>> (- + 2 1 c b a)
    

    car 只会给你一个符号,但cdr 会给你一个列表

    始终确保为append 提供两个列表。

    如果您不向cons 提供两个列表,它将为您提供点对(a . b) 而不是列表。

    更多信息请参见Pairs and Lists

    【讨论】:

    • 这个答案似乎没有添加任何东西,因为已经有另一个答案几乎相同的代码。此外,在这样的递归调用中使用append 通常是一个坏主意,因为append 本身已经是一个线性时间操作。 reverseList 的这种实现具有二次时间复杂度,这对于大型列表来说是一个真正的问题。但它可以具有(并且应该具有)线性时间复杂度,例如,通过使用累加器并像其他一些答案一样使用累加器。
    猜你喜欢
    • 2016-01-16
    • 2016-11-16
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 2011-08-04
    相关资源
    最近更新 更多