LISP FUNCTION - 返回第一个元素中较大的列表的数字计数

时间:2017-01-18 15:41:48

标签: lisp common-lisp

我想解决一个lisp函数,该函数返回NUMBER(count)个大于列表中第一个数字的数字。该列表是一个数字的线性列表。

(defun foo (lst) 
  (cond ((null lst) 0)
        (car = k)
        ((> (car lst) k) 
        (1+ (foo (cdr lst))))
  (T (foo (cdr lst)))))

我的问题是我无法保留第一个元素并将其与其他元素进行比较。

2 个答案:

答案 0 :(得分:5)

让我们解决你的问题:

你有一组数字。真的,你有一个“特殊”的第一个号码,然后是其余的号码。具体来说,您可能只想要实数,因为“小于”在复杂(虚数)数字方面没有意义。

您可以使用first从列表中获取第一个数字,并使用rest获取其他数字。

其中,你要计算任何不大于第一个的数。

所以让我们从一些伪代码开始

(defun count-numbers-greater-than-first (list)
  ;; split out first and rest
  ;; call the real count function
  )

好吧,我们现在知道我们可以使用firstrest(也就是您使用的,历史上carcdr),所以:

(defun count-numbers-greater-than-first (list)
   (count-numbers-greater-than (first list) (rest list))

您可能已经知道>用于测试实数是否大于彼此。

快速浏览CLHS会发现一个名为count-if

的好函数
(defun count-numbers-not-greater-than (reference other-numbers)
    (count-if ??? other-numbers))

???必须是function类型的对象,或函数的名称。我们需要将reference(第一个数字)“curry”到该函数中。这意味着我们要创建一个新函数,该函数仅用于count-if的一次运行,该函数已经“关闭”了reference的值。

如果我们知道number始终是100,那么该功能将如下所示:

(defun greater-than-100 (number)
    (> number 100))

然后可以在count-if

中使用该函数
(defun count-numbers-greater-than (reference other-numbers)
    (count-if (function greater-than-100)
              other-numbers))

(defun count-numbers-greater-than (reference other-numbers)
    (count-if #'greater-than-100 other-numbers))

但这并没有解决将reference数字“加入”函数的问题。

如果没有达到亚历山大(我稍后会解释),你可以使用lambda表单在这里创建一个新的匿名函数。由于reference中可以使用count-numbers-not-greater-than,因此您可以在lambda内使用其值。让我们先换100:

(defun count-numbers-greater-than (reference other-numbers)
    (count-if (lambda (number) (> number 100))
              other-numbers))

现在我们可以使用reference

(defun count-numbers-greater-than (reference other-numbers)
    (count-if (lambda (number) (> number reference))
              other-numbers))

事实上,如果你愿意的话,你甚至可以将它合并回另一个函数:

(defun count-numbers-greater-than-first (list)
    (count-if (lambda (number) (> number (first list)))
              (rest list)))

亚历山大的事情

但亚历山大呢? Alexandria是一系列超级实用的函数函数,可以在Quicklisp或其他地方使用。

 (ql:quickload "alexandria")

 (use-package #:alexandria)

当然,您通常use在自己的defpackage

 (defpackage my-cool-program
   (:use :common-lisp :alexandria))

它提供的两件事是curryrcurry功能。事实证明,lambda函数存在一个非常常见的情况。你有一个现有的函数 - 这里,> - 你想要反复调用相同的值,以及你想在每次传递的一些未知值。

最终看起来很像这样:

 (lambda (x) (foo known x))

您可以使用curry更简洁地编写相同的内容:

 (curry #'foo known)

它也适用于任何数量的参数。 RCurry执行相同操作,但它将未知值“x”放在左侧,将您的已知值放在右侧。

 (lambda (x) (foo x known)) = (rcurry #'foo known)

编写count-if的另一种方法是:

(defun count-numbers-greater-than-first (list)
    (count-if (rcurry #'> (first list))
              (rest list)))

 * (count-numbers-greater-than-first '(10 9 8 7 11 12))

 2

答案 1 :(得分:3)

正确缩进的函数如下所示:

 
(defun foo (lst) 
  (cond ((null lst) 0) 
        (car = k)              ; strange cond term         
        ((> (car lst) k)        
         (1+ (foo (cdr lst))))
        (T (foo (cdr lst)))))   

我在你的cond中评论了第二个学期。这很奇怪。它首先评估变量 car(而不是函数#'car)。如果car不是nil,则它首先评估变量=(而不是函数#'=),因为它不是cond项中的最后一个结果表达式它扔掉它并返回最后一个k

其次你写的是你说你使用第一个元素作为比较,但你在函数中称它为k但它没有在任何地方定义。在进行递归之前需要做一些事情,因此你不能让实际函数进行递归,因为它每次都会占用第一个元素。这是labels可以使用的地方:

;; didn't call it foo since it's not very descriptive
(defun count-larger-than-first (list)
  (let ((first (car list)))
    (labels ((helper (list)
               (cond ((null list) 0)
                     ((> (car list) first) 
                      (1+ (helper (cdr list))))
                     (t (helper (cdr list))))))
      (helper (cdr list)))))

当然。既然您现在可以添加更多参数,我会添加一个累加器:

(defun count-larger-than-first (list)
  (let ((first (car list)))
    (labels ((helper (list acc)
               (cond ((null list) acc)
                     ((> (car list) first) 
                      (helper (cdr list) (1+ acc)))
                     (t (helper (cdr list) acc)))))
      (helper (cdr list) 0))))

当然递归可能会破坏堆栈,所以你应该在没有Common Lisp的情况下编写它:

(defun count-larger-than-first (list)
  (let ((first (car list)))
    (loop :for element :in (cdr list)
          :counting (> element first))))

还有更高阶的函数可能更合适:

(defun count-larger-than-first (list)
  (let ((first (car list)))
    (count-if (lambda (element) (> element first))
              (cdr list))))