我正在创建一个函数,它代表Common Lisp中最小数字和第二个最小数字之间的最大数字和范围。
此功能可以使最小数量和最大数量。 (我查了一下)。但是这个功能不能成为第二个最小的数字'在其名单中......
我应该考虑哪些方法来解决这个问题? 我认为我已经满足了实现这一功能所需的足够条件。
我需要你的大帮助。 这个结果应该是这样的:
(我的范围'(0 7 10 2 3 -1))=> (-1 10)
这是我的代码。
(defun my-range (list-of-numbers)
(let ((largest (first list-of-numbers))
(smallest (first list-of-numbers))
(secsmallest (first list-of-numbers)))
(dolist (element1 (rest list-of-numbers) largest)
(when (> element1 largest)
(setf largest element1)))
(dolist (element2 (rest list-of-numbers) smallest)
(when (< element2 smallest)
(setf smallest element2)))
(dolist (element3 (remove smallest list-of-numbers) secsmallest)
(when (< element3 secsmallest)
(setf secsmallest element3))
(return (list (- smallest secsmallest) largest)))))
答案 0 :(得分:1)
要从列表中获取两个最小的元素,我们可以一次完成。我们维护了一个列表(最多)到目前为止我们看到的两个最小值。当我们扫描项目时,我们按顺序插入此列表,始终丢弃第三个最小值。当我们完成时,我们有两个最小值的列表。 (或者,如果列表只有那么多,则列出零值或一个值。)
有一种有效的算法可以从 n 的无序列表中选择 k 最小值:quickselect。
如果我们不关心招致 o(n log n)的表现,我们可以对整个列表进行排序,并从结果中选择前两个值。
我们想要两个项目的情况可以按照以下方式进行硬编码:
(defun least-two (list)
(cond
((null list) nil)
((null (cdr list)) (car list))
(t (let ((a (car list))
(b (cadr list)))
(unless (< a b)
(rotatef a b))
(dolist (el (cddr list) (values a b))
(cond
((< el a) (shiftf b a el))
((< el b) (setf b el))))))))
非常简单:我在SE浏览器编辑器中输入完全正如您所看到的那样,除了两个右括号外,它似乎工作正常。 :)
我冒昧地使用values
来返回多个值而不是列表。如果您想要一个列表,那么必须将其更改为(list a b)
,并且主cond
中的第二个案例也可以返回list
而不是(car list)
。
答案 1 :(得分:1)
使用单个dolist
进行简单的实施:
(defun my-range (list-of-numbers)
;; I don't know what should be returned if NIL is given
(check-type list-of-numbers cons)
(let* ((min (first list-of-numbers))
(min2 nil)
(max min))
(dolist (num (rest list-of-numbers) (list (- min (or min2 max)) max))
(if (<= num min)
(setf min2 min
min num)
(if (<= max num)
(setf max num)
(setf min2 num))))))
(my-range '(0 7 8 2 3 -1))
;=> (-1 8)
(my-range '(-1 0 0 0 0))
;=> (-1 0)
(my-range '(-1 0 0 0 -1))
;=> (0 0)
(my-range '(0))
;=> (0 0)
答案 2 :(得分:1)
我只是留下效率,并首先编写一个有效的解决方案:
对列表的副本进行排序,使前面的元素最小,然后选择前两个数字。
(defun two-smallest-numbers (input-list)
(subseq (sort (copy-list input-list) #'<) 0 2))
CL-USER 20 > (two-smallest-numbers '(83 3735 44562 483 83 2223 42232 3322))
(83 83)
或返回最小的不同数字:找到最小的数字,将其从列表中删除并再次找到最小的数字。返回两个数字。
CL-USER 22 > (defun two-smallest-different-numbers (input-list)
(flet ((smallest-number (list)
(reduce #'min list)))
(let ((s0 (smallest-number input-list)))
(list s0 (smallest-number (remove s0 input-list))))))
TWO-SMALLEST-NUMBERS
CL-USER 23 > (two-smallest-different-numbers '(83 3735 44562 483 83 2223 42232 3322))
(83 483)