列表中的成员 - LISP

时间:2013-12-17 11:01:13

标签: lisp

我需要在Lisp中编写一个程序来查看列表中特定字符的出现次数。例如,以下列表中出现1 [1,2,3,1,1]

3 个答案:

答案 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必须处理两个值:一个是节点的有效载荷数据,另一个是递归折叠的(延迟)结果,与该节点相同的fz 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系统可以应用的任何优化的好处。

缺点包括不像其他任何人的实施那样令人印象深刻,并且可能不足以满足提问者的家庭作业问题的要求 - 而不是后者尤其让我感到沮丧,因为这个答案 满足规定的要求。