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

时间:2018-10-10 14:54:24

标签: lisp common-lisp binary-search

我正在尝试编写一个(高阶函数),该函数接受一个向量和一个函数,并对该函数进行二进制搜索,即,如果返回-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,但从未使用过。怎么了?

2 个答案:

答案 0 :(得分:5)

(defun bin-search (ls fpred)

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

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

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

       (labels (binsearch (ls fpred l m)

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

此外,您不需要传递fpred作为参数,它不会从binsearch的一次调用更改为另一次调用。您只需从本地函数内部引用bin-search的{​​{1}}参数即可。

fpred

当编写 (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

答案 1 :(得分:1)

修复(据说)所有问题,现在可以工作了。非常感谢。

(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替换为&aux arg->减少一个缩进级别
  • 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