LISP-通过其参数搜索特定功能的程序

时间:2019-03-14 08:42:49

标签: function lisp common-lisp parameter-list

对于一个课程项目,我必须用Lisp编写程序。

程序应包含最重要的lisp函数,其输入和输出参数以及可能的可选参数。

例如:函数-首先,输入-列表,输出-对象(列表的第一个成员)。

程序应以两种不同的方式工作:

  1. 您为程序指定一个函数名称,它应该返回函数参数。

  2. 您输入函数参数,如果存在带有这些参数的函数,则应返回该函数的名称。

我的问题:

  1. 在Lisp中处理这样的任务的正确方法是什么?我认为也许一棵树将是处理它的方法? (用所有函数和参数创建一棵树,然后编写一个处理它的程序)。

  2. 有没有比这更好的主意了?或从哪里/如何开始的一些建议?还是包含任何信息的教程?

此刻,我有点不知道如何开始。您能提供的任何帮助将不胜感激。

英语不是我的母语,所以我希望一切都可以理解。

问候。

3 个答案:

答案 0 :(得分:2)

首先来看一下准备常见的Lisp开发环境。之后,我认为您应该进行调查:

之类的东西。首先看一下两个常见的lisp函数:

这是一个小例子:

CL-USER> (defun my-sum (a b) "Add my-sum parameters A and B." (+ a b))
MY-SUM
CL-USER> (my-sum 2 3)
5 (3 bits, #x5, #o5, #b101)
CL-USER> (describe #'my-sum)
#<FUNCTION MY-SUM>
  [compiled function]


Lambda-list: (A B)
Derived type: (FUNCTION (T T) (VALUES NUMBER &OPTIONAL))
Documentation:
  Add my-sum parameters A and B.
Source form:
  (SB-INT:NAMED-LAMBDA MY-SUM
      (A B)
    "Add my-sum parameters A and B."
    (BLOCK MY-SUM (+ A B)))
; No values
CL-USER> (documentation 'my-sum 'function)
"Add my-sum parameters A and B."
CL-USER> (defun my-sum (a b) "Add my-sum parameters A and B." (declare (type fixnum a b)) (+ a b))
WARNING: redefining COMMON-LISP-USER::MY-SUM in DEFUN
MY-SUM
CL-USER> (describe #'my-sum)
#<FUNCTION MY-SUM>
  [compiled function]


Lambda-list: (A B)
Derived type: (FUNCTION (FIXNUM FIXNUM)
               (VALUES
                (INTEGER -9223372036854775808 9223372036854775806)
                &OPTIONAL))
Documentation:
  Add my-sum parameters A and B.
Source form:
  (SB-INT:NAMED-LAMBDA MY-SUM
      (A B)
    "Add my-sum parameters A and B."
    (DECLARE (TYPE FIXNUM A B))
    (BLOCK MY-SUM (+ A B)))
; No values

最后,最后一个技巧是处理describe输出的字符串:

CL-USER> (with-output-to-string (*standard-output*)
               (describe #'my-sum))
"#<FUNCTION MY-SUM>
  [compiled function]


Lambda-list: (A B)
Derived type: (FUNCTION (FIXNUM FIXNUM)
               (VALUES
                (INTEGER -9223372036854775808 9223372036854775806)
                &OPTIONAL))
Documentation:
  Add my-sum parameters A and B.
Source form:
  (SB-INT:NAMED-LAMBDA MY-SUM
      (A B)
    \"Add my-sum parameters A and B.\"
    (DECLARE (TYPE FIXNUM A B))
    (BLOCK MY-SUM (+ A B)))
"

答案 1 :(得分:2)

从表面上看,任务似乎是在内存中构造一个简单的符号数据库,可通过两种方式进行搜索。数据库中的条目应理解为功能。 “输出参数”可能被理解为一个或多个返回值。这些东西在ANSI Lisp中没有命名。对该任务的有用解释是无论如何都要给返回值提供符号标签。此外,我们也许可以将类型符号用于返回值和参数。因此,例如, cons 函数的数据库条目可能类似于:

(cons (t t) cons)   ;; function named cons takes two objects, returns a cons

类型t是ANSI Lisp中所有类型的超类型;意思是“任何值”。

可以将此类记录的列表放入某些全局变量中。然后,我们编写一个可能名为get-params-by-name的函数,使得:

(get-params-by-name 'cons) -> (t t)

和另一个:get-names-by-params

(get-names-by-params '(t t)) -> (cons)

此函数以列表的形式返回所有匹配的函数。多个功能可以具有此签名。

然后,诀窍是找到可选参数和rest参数的良好表示形式。该语言可能使用的是同一符号:

(list (&rest t) list)   ;; list takes rest arguments of any type, returns list

由于我们仅对精确匹配感兴趣,因此我们不必实际解析&rest表示法。当用户按参数查询时,使用相同的语法,他们的查询对象实际上是(&rest t)

equal函数可用于判断两个符号列表是否相同:

(equal '(&rest t) '(&rest t)) -> t
(equal '(t t) '(t t)) -> nil

因此练习并不困难:只需通过列表进行映射,寻找匹配项即可。

(defun get-name-by-params (database params)
  (let ((matching-entries (remove-if-not (lambda (entry)
                                            (equal (second entry) params))
                                          database)))
    (mapcar #'first matching-entries))) ;; just the names, please

此处,该函数将数据库列表作为参数,而不是引用全局变量。我们将其集成到其中的整个程序可以提供其他接口,但这是我们的低级查找功能。

测试:

[1]> (get-name-by-params '((cons (t t) cons) (list (&rest t) list)) '(integer string))
NIL
[3]> (get-name-by-params '((cons (t t) cons) (list (&rest t) list)) '(t t))
(CONS)
[4]> (get-name-by-params '((cons (t t) cons) (list (&rest t) list)) '(&rest t))
(LIST)

在作业到期之前,我会从讲师那里弄清楚这是否是对模糊要求的正确解释。

答案 2 :(得分:1)

鉴于这是一个课程项目,我将提供一个不完整的答案,让您填补空白。

程序应该做什么

我对您要执行的操作的解释是提供一个实用程序

  • 给定函数名称后,返回其参数列表(以下称为“ lambda列表”);
  • 给定一个lambda列表,将返回具有该lambda列表的所有功能。

因此,首先,您需要确定两个lambda列表是否相同。例如,(x)(y)一样,作为lambda列表吗?是的,它是:形式参数的名称仅在函数的实现中起作用,而您通常不知道它们:这两个lambda列表均表示“一个参数的函数”。

有趣的是各种可选参数:(a &optional b)显然与(a)不同,但是与(b &optional c)相同,但是与{{1} }?在这段代码中,我说的是,这是相同的:默认值和可选参数的当前参数不会改变lambda列表是否相同。这是因为很多时候这些都是函数的实现细节。

一个包裹

我们将其放入包装中,以便清楚了解接口是什么

(a &optional (b 1 bp))

记录信息

因此,首先,我们需要一种记录有关功能的信息的机制。我们将使用类似于(defpackage :com.stackoverflow.lisp.fdesc-search (:use :cl) (:export #:defun/recorded #:record-function-description #:clear-recorded-functions #:name->lambda-list #:lambda-list->names)) (in-package :com.stackoverflow.lisp.fdesc-search) 但记录信息的宏来进行此操作,我将其称为defun。我们希望能够甚至在程序存在之前就记录有关事物的信息,而我们可以通过将defun/recorded存储在列表中的“待定”记录中,一旦程序存在,它将拉开并正确记录。这样我们就可以在整个代码中使用defun/recorded

defun/recorded

匹配lambda列表,第一步

现在,我们希望能够匹配lambda列表。由于我们显然要将以lambda列表索引的内容存储在某种树中,因此我们只需要真正处理它们的匹配 elements 即可。而且(见上文)我们不在乎默认值之类的东西。我选择这样做的方法是,首先简化lambda列表以将其删除,然后匹配简化元素:还有其他方法。

;;; These define whether there is a recorder, and if not where pending ;;; records should be stashed ;;; (defvar *function-description-recorder* nil) (defvar *pending-function-records* '()) (defmacro defun/recorded (name lambda-list &body forms) "Like DEFUN but record function information." ;; This deals with bootstrapping by, if there is not yet a recording ;; function, stashing pending records in *PENDING-FUNCTION-RECORDS*, ;; which gets replayed into the recorder at the point it becomes ;; available. `(progn ;; do the DEFUN first, which ensures that the LAMBDA-LIST is OK (defun ,name ,lambda-list ,@forms) (if *function-description-recorder* (progn (dolist (p (reverse *pending-function-records*)) (funcall *function-description-recorder* (car p) (cdr p))) (setf *pending-function-records* '()) (funcall *function-description-recorder* ',name ',lambda-list)) (push (cons ',name ',lambda-list) *pending-function-records*)) ',name)) 进行了简化,simplify-lambda-list告诉您两个参数是否匹配:有趣的一点是,它需要知道必须完全匹配的lambda列表关键字,而其他所有内容都匹配。 argument-matches-p常量由CL标准方便地提供。

lambda-list-keywords

功能描述(部分)

关于功能的信息存储在称为(defun/recorded simplify-lambda-list (ll) ;; Simplify a lambda list by replacing optional arguments with inits ;; by their names. This does not validate the list (loop for a in ll collect (etypecase a (symbol a) (list (first a))))) (defun/recorded argument-matches-p (argument prototype) ;; Does an argument match a prototype. (unless (symbolp argument) (error "argument ~S isn't a symbol" argument)) (unless (symbolp prototype) (error "prototype ~S isn't a symbol" prototype)) (if (find-if (lambda (k) (or (eq argument k) (eq prototype k))) lambda-list-keywords) (eq argument prototype) t)) 的对象中:这里没有给出这些对象的定义,但是我们需要回答的一个问题是“做两个fdesc指的是同样的功能?好吧,如果函数的名称相同,它们也会这样做。请记住,函数名称不必是符号(允许使用fdesc),因此我们必须与(defun (setf x) (...) ...)而非equal进行比较:

eql

存储由Lambda列表(部分)索引的(defun/recorded fdescs-equivalent-p (fd1 fd2) ;; do FD1 & FD2 refer to the same function? (equal (fdesc-name fd1) (fdesc-name fd2)))

为了通过lambda列表有效地索引事物,我们构建了一棵树。该树中的节点称为fdesc,此处未给出其定义。

有些函数可以在树中实习lambda-list-tree-node,并返回由给定的lambda列表索引的fdesc列表。这里都没有实现,但这是他们的样子:

fdesc

这些功能的实现可能需要使用(defun/recorded intern-lambda-list (lambda-list tree-node fdesc) ;; return the node where it was interned ...) (defun/recorded lambda-list-fdescs (lambda-list tree-node) ;; Return a list of fdescs for a lambda list & T if there were any ;; or NIL & NIL if there were not (I don't think () & T is possible, ;; but it might be in some future version) ...) argument-matches-p

顶级数据库(部分)

现在,我们可以定义顶级数据库对象:用于通过lambda列表进行索引的树的根,以及用于通过名称进行索引的哈希表

fdescs-equivalent-p

请注意,(defvar *lambda-list-tree* (make-lambda-list-tree-node)) (defvar *tree-nodes-by-name* (make-hash-table :test #'equal)) 从名称映射到存储有关该功能的信息的节点:这样做是为了简化重定义,如以下功能所示:

*tree-nodes-by-name*

请注意,此函数首先查找(defun/recorded record-function-description (name lambda-list) "Record information about a function called NAME with lambda list LAMBDA-LIST. Replace any existing information abot NAME. Return NAME." (let ((fdesc (make-fdesc :name name :lambda-list lambda-list))) ;; First of all remove any existing information (multiple-value-bind (node foundp) (gethash name *tree-nodes-by-name*) (when foundp (setf (lambda-list-tree-node-values node) (delete fdesc (lambda-list-tree-node-values node) :test #'fdescs-equivalent-p)))) (setf (gethash name *tree-nodes-by-name*) (intern-lambda-list lambda-list *lambda-list-tree* fdesc))) name) 的所有现有信息,如果存在,它将从找到该信息的节点中将其删除。这样可以确保函数重定义不会在树中留下过时的信息。

此功能是name想知道的实际记录器,因此请告知:

defun/recorded

现在,下次我们调用(setf *function-description-recorder* #'record-function-description) 时,它将通过插入所有未决的定义来引导系统。

defun/recorded是该包API的一部分:可用于记录有关我们未定义的功能的信息。

用户界面功能

除了record-function-descriptiondefun/recorded之外,我们还需要一些函数使我们可以查询数据库,以及重置功能:

record-function-description

就是这样。

示例

在编译,加载和使用包后(添加了丢失的位),我们首先可以向其中注入一些有用的额外功能(这只是随机分散)

(defun/recorded clear-recorded-functions ()
  "Clear function description records.  Return no values"
  (setf *lambda-list-tree* (make-lambda-list-tree-node)
        *tree-nodes-by-name* (make-hash-table :test #'equal))
  (values))

(defun/recorded name->lambda-list (name)
  "Look up a function by name.
Return either its lambda list & T if it is found, or NIL & NIL if not."
  (multiple-value-bind (node foundp) (gethash name *tree-nodes-by-name*)
    (if foundp
        (values
         (fdesc-lambda-list
          (find-if (lambda (fd)
                     (equal (fdesc-name fd) name))
                   (lambda-list-tree-node-values node)))
         t)
      (values nil nil))))

(defun/recorded lambda-list->names (lambda-list)
  "find function names matching a lambda-list.
Return a list of name & T if there are any, or NIL & NIL if none.

Note that lambda lists are matched so that argument names do not match, and arguments with default values or presentp parameters match just on the argument."
  (multiple-value-bind (fdescs foundp) (lambda-list-fdescs lambda-list 
                                                           *lambda-list-tree*)
    (if foundp
        (values (mapcar #'fdesc-name fdescs) t)
      (values nil nil))))

现在我们可以进行一些查询:

> (dolist (x '(car cdr null))
    (record-function-description x '(thing)))
nil

> (dolist (x '(car cdr))
    (record-function-description `(setf ,x) '(new thing)))
nil

> (record-function-description 'cons '(car cdr))
cons

> (record-function-description 'list '(&rest args))

将事物存储在树中的示例

下面的一些代码演示了一种在树中存储信息的方法(通常称为tries)。 出于多种原因,以上无法使用,但阅读此书可能有助于实现缺少的部分。

 > (lambda-list->names '(x))
 (null cdr
       car
       lambda-list->names
       name->lambda-list
       com.stackoverflow.lisp.fdesc-search::simplify-lambda-list)
t

> (lambda-list->names '(&rest anything))
(list)
t
 > (name->lambda-list 'cons)
 (car cdr)
 t