2010-07-03 99 views
7

下面是我的代码,它将列表(carVal)的汽车元素和列表(初始化为空)作为参数。我想将元素追加到列表中,但同样不起作用。在方案中添加元素列表

(define populateValues 
    (lambda (carVal currVal) 
     (append currVal(list carVal)) 
     (display currVal))) 

显示屏始终显示空列表()。任何人都可以帮我理解为什么?

回答

21

嗯,有append!作为一种原始的,它解决了大部分的问题,如前所述,方案往往皱眉在突变时,这是可能的,但通常可以避免,因此所有突变的程序在其末尾都有一个!(称为爆炸)。

另外,set!不改变数据,它改变了环境,它使得一个变量指向另一个东西,原始数据保持不变。

Scheme中的突变数据非常麻烦,但是,为了让我自己实现append!看看它是如何做:

(define (append! lst . lsts) 
    (if (not (null? lsts)) 
     (if (null? (cdr lst)) 
      (begin 
      (set-cdr! lst (car lsts)) 
      (apply append! (car lsts) (cdr lsts))) 

      (apply append! (cdr lst) lsts)))) 

注意使用set-cdr!,这是一个真正的突变,它仅适用于对,它在内存中的数据发生突变,不像'设置“!。如果一对被传递给一个函数并且使用set-cdr进行了变异!或set-car!,它会在程序中的每一处发生变异。

这服从SRFI追加!规范说,它应该是可变的,它应该返回一个未定义的值,例如。

(define l1 (list 1 2 3 4)) 

(define l2 (list 2 3 4)) 

(define l3 (list 3 1)) 

(append! l1 l2 l3) 

l1 

l2 

l3 

该款显示器:

(1 2 3 4 2 3 4 3 1) 
(2 3 4 3 1) 
(3 1) 

中可见,追加!可以采取无数的论点,它会改变他们,但最后。

虽然计划可能不是您理想的语言。使用追加!如前所述是非标准的,相反,append是首选的,它不会变异并被调用它的返回值。这是我实现这样:

(define (append . lsts) 
    (cond 
    ((null? lsts) '()) 
    ((null? (car lsts)) (apply append (cdr lsts))) 
    (else (cons (caar lsts) (apply append (cdar lsts) (cdr lsts)))))) 


> (append (list 1 2 3) (list 4 5 6) (list 'granny 'porn)) 
(1 2 3 4 5 6 granny porn) 

这都说明在没有突变,大量使用递归 的和没有用测序更熟悉方案的风格。

编辑:如果你只是想一些元素添加到列表中,而不是本身连接两个虽然:

(define (extend l . xs) 
    (if (null? l) 
     xs 
     (cons (car l) (apply extend (cdr l) xs)))) 

(define (extend! l . xs) 
    (if (null? (cdr l)) 
     (set-cdr! l xs) 
     (apply extend! (cdr l) xs))) 

(extend '(0 1 2 3) 4 5 6) 

(define list1 '(0 1 2 3)) 

(extend! list1 4 5 6) 

list1 

哪个做你所期望的

+0

谢谢你的答案..顺便说一句......'奶奶','色情'..你可能想改变他们..否则你可能会被投票:) – 2010-07-16 04:40:49

+0

@ darkie15它不会使答案不那么'有用'或'明确',如果人们因为这些事情想要降低它的话,那么这个网站已经失去了。此外,无论如何,你已经得到了答案。 =) 此外,其他人可以编辑它,如果他们想。 – Zorf 2010-07-16 19:18:09

2

(append foo bar)返回级联foobar。它不会更改foobar

0

您必须用set!更新currVal的值。你的榜样应该有

(set! currVal (append currVal (list carVal)) 
(display currVal) 
+0

请注意,这将改变函数内的'currVal',但在外面没有可见的效果。 – 2010-07-03 21:13:27

5
  1. append创建一个新名单,它不修改现有的一个。
  2. 这是因为一般情况下,方案(和本例中的球拍)是一种喜欢功能风格的语言。
  3. 你可能会更接近set! - 但即使这样也会让你失望,因为它只会修改本地绑定。
  4. 请注意,在球拍特别是,列表是不可变的,所以有没有可以改变列表。
  5. 此外,即使您可以用这种方式修改列表,但由于您必须重复扫描整个列表,因此累积长列表是非常低效的方式。
  6. 最后,如果你在这个级别有问题,那么我强烈建议去在HtDP
+0

我需要拿出这个功能。你会在这种情况下建议什么? – 2010-07-03 20:54:30

+0

你可以使用'box',这是一种指向(可变)值的指针。 **但是**我怀疑你确实需要这个功能 - 新手们通常认为他们必须这样做,因为他们习惯于突变是做事情的唯一方式。 – 2010-07-03 20:58:11

0

你真的需要想想什么确切的功能你正在寻找

如果你想改变引用列表的地方,那么你必须做相当于追加! (如其他答案中所述)。但是这很危险,因为你可能有其他的代码是不可改变的,如果你打算这么做的话,你的程序需要有一个!最终标志着这种危险。

一个廉价的逼近,你想要做什么,更实用的风格,就是:

(define (populateValues carVal currVal) 
(let ((ll (append currVal (list carVal)))) 
    (display ll) 
    ll)) 

注意,它使一个新的列表,不追加,显示结果,并返回新名单作为一种价值。如果您无法访问中间值,则这是一种有用的调试技术:绑定到变量,显示或记录它,然后返回它。