LISP公共列表函数

LISP公共列表函数,lisp,Lisp,嘿,伙计们,我还有最后一个问题要解决。我需要创建: (myCommon L1 L2) Evaluates to a list of elements that are common in both lists L1 and L2. Assume L1 and L2 have no repeated elements. eg. (myCommon ‘(p a e g) ‘(a q r e)) → (a e) 我只能使用以下功能: (atom X) (quote X)

嘿,伙计们,我还有最后一个问题要解决。我需要创建:

(myCommon L1 L2)
Evaluates to a list of elements that are common in both lists L1 and L2.
Assume L1 and L2 have no repeated elements.
eg.  (myCommon ‘(p a e g) ‘(a q r e))  →  (a e)
我只能使用以下功能:

(atom X)            
(quote X)           
‘X          
(eq X Y)            
(cons X L)          
(car L)         
(cdr L)         
(list A B C)        
(if X Y Z)      .
(cond (C1 S1) (C2 S2) …… (Cn Sn))       
(lambda (P1 P2 …… Pn) E)        
(funcall F (P1 P2 …… Pn))   
我还可以使用我在作业中创建的函数。到目前为止,我已经创建了:

(defun myLast (L) 
  (if (eq (cdr L) '()) 
  (car L) 
  (myLast (cdr L))))               ;Evaluates to the last element of list L

(defun myCount (X L)
   (cond ((eq L '()) 0)
     ((eq X (car L))(+ 1 (myCount X (cdr L))))
     (+ (myCount X (cdr L)))))   ;Evaluates to number of occurrences of X in L

(defun myMember (X L)
   (cond ((eq L '()) '())
   ((eq X (car L)) t)
   (t (myMember X (cdr L)))))   ;Evaluates to true if X in L, false otherwise

这项作业的问题是,我不能和老师见面提问,因为他走了,而且我现在“有限的电子邮件访问权限”。我不能问关于如何解决这个问题的问题,我很困惑这个函数从哪里开始。我想我必须像L1的车一样使用myMember,检查它是否在L2中,是否将其放入新列表并递归添加到列表中。我不知道该怎么做。谁能帮我完成这学期的学业?谢谢大家!

你的想法很好。您应该查看myCount中使用的模式。您可以对myCommon使用几乎相同的模式

思考如何从递归中走出谷底。您正在构建一个列表,而不是一个数字,因此,不要将0作为终值,而是考虑列表的结尾是什么

对于递归子句,在应该包含项时,不要使用+1,而是使用将项添加到列表中的函数

记住在myCommon中只递归下一个列表。您应该一次查看一个元素,并将该元素与完整的第二个列表进行比较,就myCommon而言,第二个列表应该是一个常量

希望这能帮助你,在你以前的职能精神。但这是一种非常低效的实现交集函数的方法

帮助编译器生成更高效代码的一个常见技巧是使用带有第三个参数的helper函数,即在递归输入列表时生成的累加器。(累加器技巧允许您以称为tail recusive的样式编写函数。)


当您没有进行人为限制的练习来学习递归时,我更愿意使用迭代(
loop
dolist
)来解决这个问题,特别是当您使用common lisp时,它不要求编译器生成有效的代码,即使是尾部递归调用。但是,同样,如果您没有使用受限制的common lisp版本,您可以调用内置函数
intersection
:-)

你的想法很好。您应该查看myCount中使用的模式。您可以对myCommon使用几乎相同的模式

思考如何从递归中走出谷底。您正在构建一个列表,而不是一个数字,因此,不要将0作为终值,而是考虑列表的结尾是什么

对于递归子句,在应该包含项时,不要使用+1,而是使用将项添加到列表中的函数

记住在myCommon中只递归下一个列表。您应该一次查看一个元素,并将该元素与完整的第二个列表进行比较,就myCommon而言,第二个列表应该是一个常量

希望这能帮助你,在你以前的职能精神。但这是一种非常低效的实现交集函数的方法

帮助编译器生成更高效代码的一个常见技巧是使用带有第三个参数的helper函数,即在递归输入列表时生成的累加器。(累加器技巧允许您以称为tail recusive的样式编写函数。)


当您没有进行人为限制的练习来学习递归时,我更愿意使用迭代(
loop
dolist
)来解决这个问题,特别是当您使用common lisp时,它不要求编译器生成有效的代码,即使是尾部递归调用。但是,同样,如果您没有使用受限制的common lisp版本,您可以调用内置函数
intersection
:-)

“对于列表1中的每个元素,请检查它是否是列表2中的成员”“对于列表1中的每个元素,请检查它是否是列表2中的成员”“我真的很喜欢您如何“解释而不显示”。这是我上次读到的最好的答案之一:)(向上投票)实际上是一个散列,因此从第二个O(1)中的第一个列表中搜索每个元素与使其尾部递归一样重要,因为当列表变大时,这两个元素都很重要。CL没有tco要求,因此应该使用
loop
@sylvester:我不相信在大多数情况下,O(1)查找的好处超过了设置哈希表的成本。我想,要做到这一点,列表必须相当大,这真的是工会最常见的用例吗?我最初只会使用朴素的方法,如果有评测的保证,我会重新实现。@PederKlingenberg我相信你对于非常小的列表是正确的,但是随着朴素方法的时间平方的两倍,你会注意到编译代码的4k元素以上。顺便说一句:调用了内置函数,我测试过的实现之间的big-o不同。@sylvester 4k元素比我通常相交的元素大。但我完全同意你的观点,如果数据能够证明,基于哈希表的方法可能会更好。但是,在这个问题的上下文中,我认为信息太多了,我不想解释哈希表。我对尾部递归的暗示是因为这可能是OP的下一步,而提到内置解决方案是为了避免人们对lisp的憎恨,因为他们在学校里被要求很糟糕地重新实现原语,而没有被告知该语言提供了更好的设施。我真的很喜欢你“解释而不显示”的方式。这是我上次读到的最好的答案之一:)(向上投票)实际上是一个散列,因此从第二个O(1)中的第一个列表中搜索每个元素与使其尾部递归一样重要,因为当列表变大时,这两个元素都很重要。C