使用cons / car vs append在Racket

时间:2016-07-31 22:21:34

标签: scheme racket

我想制作一个正方形列表。我可以使用以下代码来完成:

(define (makelistsq n)
  (define outlist '())
  (for ((i n))
    (set! outlist (append outlist (list (* i i))))
    )
  outlist
  )

(makelistsq 5)

输出:

'(0 1 4 9 16)

但是,我已经看到,通常使用cons和car关键字来创建和添加列表。这种方法是否比上述附加方法有任何优势?因此,遵循更好或与上述相同:

(define (makelistsq2 n)
  (define outlist '())
  (for ((i n))
    (set! outlist (cons (* i i) outlist))
    )
  (reverse outlist)
  )

感谢您的回答/评论。

编辑:在此页Cons element to list vs cons list to element in Scheme上提到所有使用追加都是错误的:

  

在极少数情况下,您需要在最后添加一个元素   (并且相信我,这样做通常意味着你在想   算法错误)你可以使用追加

3 个答案:

答案 0 :(得分:4)

我不会重复我自己的回答:P。是的,使用append通常是构建输出列表的错误方法。但是你的实现都不正确 - 大多数时候,你必须忘记set!和循环。

递归是最佳选择,最后使用cons和(如果需要)reverse是构建列表的首选方法。实际上,使用内置过程可以更简洁地表达问题中的函数,这是编写函数式代码时的推荐方法:

(define (makelistsq2 n)
  (map (lambda (x) (* x x))
       (range n)))

为了完整起见,让我们看看我们如何从头开始编写程序,使用cons来构建输出 - 适用于没有内置过程可以满足您需要的极少数情况。这会生成一个递归过程,请注意,我们从零到n-1

(define (makelistsq2 n)
  (define (helper i)
    (if (= i n)
        '()
        (cons (* i i)
              (helper (add1 i)))))
  (helper 0))

上述解决方案很好,适用于小型列表。如果(且仅当)输出列表很大,您可能希望使用尾递归来构建它;这样更有效,因为它不需要额外的内存来进行迭代 - 我们将使用一个名为let,并注意这会产生一个迭代过程,从n-1变为零:

(define (makelistsq2 n)
  (let loop ((i (sub1 n)) (acc '()))
    (if (negative? i)
        acc
        (loop (sub1 i) (cons (* i i) acc)))))

无论您选择哪种实施方式,结果现在都符合预期:

(makelistsq2 5)
=> '(0 1 4 9 16)

答案 1 :(得分:2)

cons是一对的构造函数。列表不是由空列表cons终止的()链以外的其他任何内容。

append是一个在实现中使用cons的函数。以下是两个参数append的实现:

(define (append lst1 lst2)
  (if (null? lst1)
      lst2
      (cons (car lst1) 
            (append (cdr lst1) lst2))))

简单地说,它为每对lst1创建一对新对,然后附加lst2。它不是非常有效,所以在你的第一个例子中,除了为每个步骤明确指定的一个元素列表之外,它还必须创建一个4,3和2个元素列表。

使用列表时,它按照从头到尾的顺序进行迭代,但列表的创建从头到尾进行。通常它不可能反过来做某事,因此最终需要反转结果,但在你的情况下,它很容易倒计时而不是向上。

(define (make-square-list end)
  (let loop ((n (sub1 end)) (acc '()))
    (if (< n 0)
        acc
        (loop (sub1 n) 
              (cons (* n n) acc)))))

使用更高阶函数可以消除样板,但#!racket有一个替代方案可以使更短的代码称为for/list

(define (make-square-list end)
  (for/list ((n (range end)))
    (* n n)))

for/listmap基本相同,但作为一种特殊形式。

答案 2 :(得分:0)

我的版本:

#lang racket

(define (make-square-list n)
  (let loop ([count 1]
             [result_list '()])
    (if (<= count n)
        (loop (add1 count) (cons (* count count) result_list))
        (reverse result_list))))

(make-squqre-list 10)

(1 4 9 16 25 36 49 64 81 100)