Lisp 中的二叉搜索具有更高级别的功能



我正在尝试编写一个(高阶函数(,它接受一个向量和一个函数,并根据该函数进行二叉搜索,即如果它返回 -1,我们需要更低,对于 1 - 更高,对于 0,我们找到了正确的位置。 我想出了这样的东西,但似乎我在传递函数作为参数时做错了:

(defun bin-search (ls fpred)
(let ((l (length ls))
(x (aref ls (floor (length ls) 2))))
(labels (binsearch (ls fpred l m)
(case (funcall #'fpred (aref ls m))
(-1 (binsearch (ls fpred l (floor (- m l) 2))))
(0 (return-from binsearch m))
(1 (binsearch (ls fpred m (+ m (floor (- m l) 2)))))))
(binsearch ls fpred 0 l))))

编译器说变量FPRED被定义但从未使用过。怎么了?

(defun bin-search (ls fpred)

请使用有意义的名称,您有很多短名称或缩写,很难阅读。例如,ls让我想到了一个列表,可以简单地命名为list,但显然您正在使用向量,所以也许vecvector

(let ((l (length ls))
(x (aref ls (floor (length ls) 2))))

如果要重用同一 let 中定义的长度l,可以改用let*,并写入l而不是第二次出现(length ls)

(labels (binsearch (ls fpred l m)

标签的语法是绑定(name (<args>) <body>)的列表,因此您需要添加另一对括号,例如(labels ((binsearch (<args>) <body>)) ...

此外,您不需要将fpred作为参数传递,它不会从binsearch的一个调用更改为另一个调用。你可以从本地函数内部引用bin-searchfpred参数。

(case (funcall #'fpred (aref ls m))

当你写#'fpred时,相当于(function fpred),你在函数命名空间中寻找fpred。在这里,您要访问与名为fpred变量关联的函数,因此您可以删除#'部分。

(-1 (binsearch (ls fpred l (floor (- m l) 2))))

当你写(binsearch (ls fpred ...))时,这意味着:用一个值调用binsearch,通过调用函数ls参数获得fpred, ....括号很重要,您需要在此处删除它们。

(0 (return-from binsearch m))
(1 (binsearch (ls fpred m (+ m (floor (- m l) 2)))))))
(binsearch ls fpred 0 l))))

修复(据说(所有内容,它现在可以工作了。多谢。

(defun bin-search (vec fpred)
(let* ((l (length vec)))
(labels ((binsearch (vec l m)
(case (funcall fpred (aref vec m))
(-1 (binsearch vec  l (+ l (floor (- m l) 2))))
(0 (return-from binsearch m))
(1 (binsearch vec m (+ m (floor (- m l) 2)))))))
(binsearch vec 0 (floor l 2)))))

改进:

  • let而不是let*
  • 内部函数的名称已更改
  • 不需要return-from

应用的:

(defun bin-search (vec fpred)
(let ((l (length vec)))
(labels ((bin-search-aux (vec l m)
(case (funcall fpred (aref vec m))
(-1 (bin-search-aux vec l (+ l (floor (- m l) 2))))
( 0 m)
( 1 (bin-search-aux vec m (+ m (floor (- m l) 2)))))))
(bin-search-aux vec 0 (floor l 2)))))
  • let替换为&auxarg -> 减少一个缩进级别
  • vec不需要传递

应用的:

(defun bin-search (vec fpred &aux (l (length vec)))
(labels ((bin-search-aux (l m)
(case (funcall fpred (aref vec m))
(-1 (bin-search-aux l (+ l (floor (- m l) 2))))
( 0 m)
( 1 (bin-search-aux m (+ m (floor (- m l) 2)))))))
(bin-search-aux 0 (floor l 2)))))

测试:

CL-USER > (bin-search #(1 2 3 4 5 6 7 8 9)
(lambda (x)
(if (< x 7) 1 (if (> x 7) -1 0))))
6