我需要在Lisp中编写一个程序来查看列表中特定字符的出现次数。例如,以下列表中出现1 [1,2,3,1,1]
答案 0 :(得分:2)
Lisp中的列表是 cons 节点的序列:指针对 - 第一个到有效载荷数据,第二个到列表的其余部分。例如。对于[1,2,3,1,1]
,
.
/ \
1 .
/ \
2 .
/ \
3 ...... .
/ \
1 NIL
NIL
是表示空列表的特殊值,因此系统知道不再尝试探索它。在Scheme中,
(define NIL '())
递归列表处理范例由fold的概念捕获,其中每个节点 .
被替换为"使用二进制函数 f
,特殊节点NIL
替换为一些特殊的"零"值 z
,用于创建应用程序链(f 1 (f 2 (f 3 (... (f 1 z) ...))))
。在Scheme中,
(define (myfold f z list)
(cond
((null? list) z) ; replace NIL with the initial ("zero") value
(else
(f ; combine
(car list) ; the payload datum, and the delayed,
(lambda () ; by creating a function to calculate it,
(myfold f z ; result of recursively folding
(cdr list))))))) ; the rest of list
这样,组合函数f
必须处理两个值:一个是节点的有效载荷数据,另一个是递归折叠的(延迟)结果,与该节点相同的f
和z
, rest 。
(define (keep-equals v list)
(myfold
(lambda (a r) ; combine ...
(if (equal? v a)
(cons a ... ) ; the same thing goes over the dots, here
... )) ; and here
'() ; replace the NIL of the argument list with this
list))
由于递归折叠结果'通过在需要结果时创建要调用的函数来延迟计算,我们需要"强制" 执行计算,当我们确实需要那些结果,通过调用该函数。
如果你想计算出现次数而不是在列表中收集它们,你只需要使用不同的组合函数和不同的初始值("零" )价值。
特别是,我们通过 cons
为列表的其余部分构建一个列表(以NIL作为初始值,空列表) ;而我们计算通过递增一个计数器(以0作为该计数器的初始值)。
计算,例如通过折叠列表的长度,我们基本上将其元素分别变为1:length [a,b,c,d,e] == 1 + (1 + (1 + (1 + (1 + 0))))
。这里,组合函数需要有条件地增加计数器,只有当有效载荷数据是我们想要来计算它们时。
答案 1 :(得分:1)
我自己是lisp的新手,但我会这样做。我还没有看过Will的另一个答案,所以我会在发布之后检查出来。 member
函数既可以告诉您是否在列表中找到了某些内容,又可以从找到它的位置返回该列表的其余部分:
CL-USER> (member '1 '(0 1 2 3))
(1 2 3)
然后,您可以递归调用使用member
的函数,并从let
的变量中的返回值中递增计数器:
(defun find1 (alist)
(let ((count 0))
(labels ((findit (list)
(let ((part (member '1 list)))
(if part
(progn (incf count)
(findit (rest part)))
0))
count))
(findit alist))))
结果如下:
CL-USER> (find1 '(1 2 3 4 5))
1
CL-USER> (find1 '(1 1 2 3 4 5))
2
CL-USER> (find1 '(1 1 1 2 3 1 4 5 1 1))
6
使用progn
代替cond
if
UPDATE :以上是基于评论的上述更新且更优雅的版本,我认为它也有资格作为尾递归:
(defun find1 (alist &optional (accum 0))
(let ((part (member '1 alist)))
(if part
(find1 (rest part) (+ accum 1))
accum)))
这是在行动:
CL-USER> (find1 '(1 2 3 4))
1
CL-USER> (find1 '(1 1 1 1))
4
CL-USER> (find1 '(1 1 0 1 1))
4
CL-USER> (find1 '(0 2 1 0 1 1 0 1 1))
5
答案 2 :(得分:1)
我非常喜欢已经发布到这个问题的答案。但似乎它们都比必要的工作量更多。另一方面,考虑到所有人的想法,我几乎感到尴尬的是我的回答是多么简单。无论如何,这就是我所做的:
(defun count-things-in (needle haystack)
"Count the number of NEEDLEs in HAYSTACK."
(reduce '+
(map 'list
#'(lambda (straw)
(if (equalp straw needle) 1 0))
haystack)))
(count-things-in 1 '(1 2 3 1 1))
;; => 3
这非常简单:你只需要在HAYSTACK上映射一个函数,该函数对于一个EQUALP为NEEDLE的元素返回1,对于一个不为的元素返回0,然后将结果列表减少+
。对于给定的示例列表,映射操作会生成列表(1 0 0 1 1)
,然后reduce操作将其视为(1 + (0 + (0 + (1 + 1))))
,其值为3.
这种方法的好处包括使用足够松散的等式谓词来处理字符串和数字,以及使用不同类型但数量相同的数字 - 即(equalp 1 1.0)
=> t
;如果您需要不同的行为,请改用另一个等式谓词。使用标准的MAP和REDUCE函数,而不是实现自己的函数,也可以为您提供Lisp系统可以应用的任何优化的好处。
缺点包括不像其他任何人的实施那样令人印象深刻,并且可能不足以满足提问者的家庭作业问题的要求 - 而不是后者尤其让我感到沮丧,因为这个答案 满足规定的要求。