我想解决一个lisp函数,该函数返回NUMBER(count)个大于列表中第一个数字的数字。该列表是一个数字的线性列表。
(defun foo (lst)
(cond ((null lst) 0)
(car = k)
((> (car lst) k)
(1+ (foo (cdr lst))))
(T (foo (cdr lst)))))
我的问题是我无法保留第一个元素并将其与其他元素进行比较。
答案 0 :(得分:5)
让我们解决你的问题:
你有一组数字。真的,你有一个“特殊”的第一个号码,然后是其余的号码。具体来说,您可能只想要实数,因为“小于”在复杂(虚数)数字方面没有意义。
您可以使用first
从列表中获取第一个数字,并使用rest
获取其他数字。
其中,你要计算任何不大于第一个的数。
所以让我们从一些伪代码开始
(defun count-numbers-greater-than-first (list)
;; split out first and rest
;; call the real count function
)
好吧,我们现在知道我们可以使用first
和rest
(也就是您使用的,历史上car
和cdr
),所以:
(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))
它提供的两件事是curry
和rcurry
功能。事实证明,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))))