LISP - 通过其参数搜索特定功能的程序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LISP - 通过其参数搜索特定功能的程序相关的知识,希望对你有一定的参考价值。
对于课程项目,我必须在lisp中编写一个程序。
该程序应包含最重要的lisp函数,它们的输入和输出参数以及可选参数。
例如:function - first,input - list,output - object(list的第一个成员)。
该计划应以两种不同的方式运作:
- 你给程序一个函数的名称,它应该返回函数参数。
- 输入函数参数,如果存在具有这些参数的函数,则应返回函数的名称。
我的问题:
- 在lisp中处理这样的任务的正确方法是什么?我想也许一棵树可以处理它? (制作一个包含所有函数和参数的树,然后编写一个处理它的程序)。
- 有没有人有更好的想法来接近这个任务?或者一些建议在哪里/如何开始?或包含任何信息的教程?
目前我有点迷失如何开始。您将给予的任何帮助将非常感谢。
英语不是我的第一语言,所以我希望一切都是可以理解的。
问候。
首先看一下准备常见的lisp开发环境。在那之后我认为你应该调查:
- 使用defun创建函数,
- declare types。
和那样的事情。看看两个常见的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)))
"
从表面上看,任务似乎是在内存中构建一个简单的符号数据库,可以通过两种方式进行搜索。数据库中的条目被理解为功能。 “输出参数”可以理解为一个或多个返回值。这些东西没有在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)
此函数作为列表返回所有匹配的函数。多个函数可以具有此签名。
然后诀窍是找到可选和休息参数的良好表示。它可能与语言使用的符号相同:
(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)
在作业到期之前,我会向导师澄清这是否是对模糊要求的正确解释。
鉴于这是一个课程项目,我将提供一个不完整的答案,让你填补空白。
该计划应该做什么
我对你被要求做的事情的解释是提供一个实用的工具
- 给定函数的名称返回其参数列表(在下面称为“lambda列表”);
- 给定一个lambda列表返回该lambda列表的所有函数。
所以,首先你需要决定两个lambda列表是否相同。作为一个例子,(x)
和(y)
一样,作为lambda列表?是的,它是:形式参数的名称仅在函数的实现中起作用,并且通常不会知道它们:这两个lambda列表都意味着“一个参数的函数”。
interestring的东西是各种类型的可选参数:(a &optional b)
显然与(a)
不同,但与(b &optional c)
相同,但它与(a &optional (b 1 bp))
相同吗?在这段代码中,我说是的,它是相同的:可选参数的默认值和当前参数不会改变lambda列表是否相同。那是因为这些都是函数的实现细节。
一套
我们将它放入一个包中,以便清楚界面是什么:
(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
。
;;; 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))
匹配lambda列表,第一步
现在我们希望能够匹配lambda列表。因为我们显然要在某种树中存储由lambda列表索引的东西,所以我们只需要能够处理它们的匹配元素。而且(见上文)我们并不关心默认值这样的事情。我选择这样做首先简化lambda列表以删除它们然后匹配简化元素:还有其他方法。
simplify-lambda-list
做了简化,argument-matches-p
告诉你两个参数是否匹配:有趣的是它需要知道lambda list关键字,它必须完全匹配,而其他所有东西都匹配。 lambda-list-keywords
常数由CL标准方便地提供。
(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
s的对象中:这里没有给出这些对象的定义,但我们需要回答的一个问题是“两个fdesc
s是指同一函数的版本吗?”好吧,如果函数的名称相同,它们就会这样做。请记住,函数名称不必是符号(允许使用(defun (setf x) (...) ...)
),因此我们必须与equal
进行比较而不是eql
:
(defun/recorded fdescs-equivalent-p (fd1 fd2)
;; do FD1 & FD2 refer to the same function?
(equal (fdesc-name fd1)
以上是关于LISP - 通过其参数搜索特定功能的程序的主要内容,如果未能解决你的问题,请参考以下文章