函数(OccurencesOfPrimes< list>),它计算(可能是嵌套的)列表中的素数数量

时间:2017-08-15 08:40:21

标签: lisp common-lisp

我正在解决问题,以便在lisp的列表中获取Prime的occurence。 输入: 编写一个函数(OccurencesOfPrimes < list >)来计算(可能是嵌套的)列表中的素数。

输出:示例:(OccurencesOfPrimes (((1)(2))(5)(3)((8)3)) returns 4

我正在使用以下代码,但收到的错误如下:

(

defun OccurencesOfPrimes (list)
        (loop for i from 2 to 100 
            do ( setq isPrime t)
            (loop for j from 2 to i
                never (zerop (mod i j))
                    (setq isPrime f)
                    (break)
            )
        )
        (if (setq isPrime t)
            (append list i)
        )
        )
    )

LOOP: illegal syntax near (SETQ ISPRIME F) in
(LOOP FOR J FROM 2 TO I NEVER (ZEROP (MOD I J)) (SETQ ISPRIME F) (BREAK)

任何帮助。

1 个答案:

答案 0 :(得分:3)

保持格式与语言的预期约定一致非常重要。它有助于阅读代码(特别是与其他程序员一起),并可以帮助您查看错误。

此外,您应该使用一个编辑器,该编辑器至少会保留括号的轨迹。在Emacs中,当您将光标放在第一个左括号中时,将突出显示匹配的括号。你可以发现你有一个没有用处的附加括号。

(

defun OccurencesOfPrimes (list)
        (loop for i from 2 to 100 
            do ( setq isPrime t)
            (loop for j from 2 to i
                never (zerop (mod i j))
                    (setq isPrime f)
                    (break)
            )
        )
        (if (setq isPrime t)
            (append list i)
        )
        ) ;; <- end of defun
    ) ;; <- closes nothing

在Lisp中,括号用于计算机,而缩进用于人类。工具可以根据结构(括号)自动缩进代码,并且您期望的缩进与正在计算的缩进之间的任何差异都暗示您的代码格式错误。如果你看一下表达式的缩进,你可以看到你在表单中的深度,这一点可以帮助你理解代码。

符号名称为dash-separated,而不是camlCased

您的代码,备注:

(defun occurences-of-primes (list)
  ;; You argument is likely to be a LIST, given its name and the way
  ;; you call APPEND below. But you never iterate over the list. This
  ;; is suspicious.
  (loop
     for i from 2 to 100 
     do
       (setq is-prime t) ;; setting an undeclared variable
       (loop
          for j from 2 to i
          never (zerop (mod i j))

            ;; the following two forms are not expected here according
            ;; to LOOP's grammar; setting IS-PRIME to F, but F is not
            ;; an existing variable. If you want to set to false, use
            ;; NIL instead.
            (setq is-prime f)

            ;; BREAK enters the debugger, maybe you wanted to use
            ;; LOOP-FINISH instead, but the NEVER clause above should
            ;; already be enough to exit the loop as soon as its
            ;; sub-expression evaluates to NIL.
            (break)))

  ;; The return value of (SETQ X V) is V, so here your test would
  ;; always succeed.
  (if (setq is-prime t)
      ;; Append RETURNS a new list, without modifying its
      ;; arguments. In particular, LIST is not modified. Note that "I"
      ;; is unknown at this point, because the bindings effective
      ;; inside the LOOP are not visible in this scope. Besides, "I"
      ;; is a number, not a list.
      (append list i)))

原始问题

  

编写一个函数,用于计算(可能是嵌套的)列表中所有素数的出现次数。

尽管家庭作业问题是“写一个功能”,但它并没有说你应该写一个大功能一次计算所有东西。你可以编写一个这样的大功能,但是如果你将问题分成子问题,你将会得到不同的辅助功能,其中包括:

  1. 更容易理解(他们做了一件事)
  2. 可以重复使用以构建其他功能
  3. 子问题是,例如:如何确定数字是否为素数?如何遍历树(也可能是嵌套列表)?怎么算 发生了什么?

    基本思想是编写一个“is-prime”函数,迭代树并在每个元素上调用“is-prime”;如果元素是素数并且之前从未见过,则将1添加到函数本地的计数器中。

    您还可以展平输入树,获取列表,然后对结果进行排序 列表;你迭代列表,同时跟踪最后一个 看到的价值:如果价值与前一个相同,那么你 已经知道这个数字是否为素数;如果先前的数字不同,那么 你必须首先测试这个数字是否为素数。

    你还可以抽象一些东西,并定义一个更高阶的树 - 步行者函数,它在树的每个叶子上调用一个函数。并编写另一个“memoizes”调用的高阶函数:它包围了一个  函数F,如果你使用与以前相同的参数调用F,  它返回存储的结果而不是重新计算它。

    实施例

    我将结合上述想法,因为如果你给老师的答案,你可能需要仔细解释每个部分的作用(如果可以的话,对你很好);这不一定是“最好的”答案,但它涵盖了很多东西。

    (defun tree-walk-leaves (tree function)
      (typecase tree
        (null nil)
        (cons
          (tree-walk-leaves (car tree) function)
          (tree-walk-leaves (cdr tree) function))
        (t (funcall function tree))))
    
    (defun flatten (tree &optional keep-order-p)
      (let ((flat nil))
        (tree-walk-leaves tree (lambda (leaf) (push leaf flat)))
        (if keep-order-p
            (nreverse flat)
            flat)))
    
    (defun prime-p (n)
      (or (= n 2)
          (and (> n 2)
               (oddp n)
               (loop
                  for d from 3 upto (isqrt n) by 2
                  never (zerop (mod n d))))))
    
    (defun count-occurences-of-prime (tree)
      (count-if #'prime-p (remove-duplicates (flatten tree))))
    
    (count-occurences-of-prime '(((1)(2))(5)(3)((8)3)))
    => 4
    

    相反,如果你不想删除重复项但是多次计算素数,你可以这样做:

    (count-if (memoize #'prime-p) (flatten tree))
    

    ...其中memoize是:

    (defun memoize (function &key (test #'equalp) (key #'identity))
      (let ((hash (make-hash-table :test test)))
        (lambda (&rest args)
          (let ((args (funcall key args)))
            (multiple-value-bind (result exists-p) (gethash args hash)
              (values-list
               (if exists-p
                   result
                   (setf (gethash args hash)
                         (multiple-value-list (apply function args))))))))))
    

    (如果没有重复,则memoize无效)