LISP 在列表中查找项目

问题描述 投票:0回答:3

我有一个像这样的A-list

(setf *books* '(
        (  
            (:title 'Titulo 1)
            (:autor (quote Autor 1) )
        ) 
        (
            (:title 'Título 2)
            (:autor (quote Autor 2) )
        )
       ; (...) 
))

我需要创建一个根据书名查找书籍的函数。阅读文档后我尝试:

(defun string-include (string1 string2)
  (let* ((string1 (string string1)) (length1 (length string1)))
    (if (zerop length1)
        nil 
        (labels ((sub (s)
                   (cond
                    ((> length1 (length s)) nil)
                    ((string= string1 s :end2 (length string1)) string1)
                    (t (sub (subseq s 1))))))
          (sub (string string2)))))
)
(
    defun buscar (books text)
        (remove-if-not 
                (lambda (book)
                   ;(search cadena (assoc :title book))
                   ;(string-include (string (cddr (assoc :title book))) cadena)
                   (string= (format nil (cddr (assoc :title book))) (format nil book))
                )
        books)
)

我找不到那本书,我做错了什么?

请注意,我所做(评论)的测试均无效

lisp common-lisp
3个回答
7
投票

您的数据结构不是关联列表。 Alist 是一个对列表,所以你应该这样创建它:

(defparameter *books* '(((:title . "Titulo 1")
                         (:autor . "Autor 1"))
                        ((:title . "Titulo 2")
                         (:autor . "Autor 2"))))

注意三件事:

  • 你不应该使用
    setf
    来初始化全局变量 - 你应该使用
    defparameter
    defvar
    全局变量和
    let
    局部变量。

这可以帮助你:defvar、defparameter、setf和setq有什么区别

  • 你应该只使用一个
    quote
    ,因为引用数据结构中的表达式也被引用。此外,此调用
    (quote Autor 1)
    没有任何意义 -
    quote
    仅使用一个参数调用。

这可以帮助您:何时在 Lisp 中使用 '(或引用)?

  • 你应该正确地格式化你的代码——包括括号。

我不会评论

string-include
那么多-但有些电话似乎很奇怪。为什么调用
(string string1)
(将字符串转换为字符串)或
(string= string1 s :end2 (length string1))
string=
比较字符串,但
(length string1)
的结果将是数字)?您不仅应该编写更小且可测试的函数,还应该在 REPL 中不断测试您的代码,以确保一切如您所愿。

那个

buscar
函数看起来好一点(但它仍然包含一些奇怪的调用像
(format nil book)
你之前没有测试过)你可以把它改成这样:

(defun buscar (books title)
  (remove-if-not (lambda (book) (string= (cdr (assoc :title book)) title))
                 books))

测试:

> (buscar *books* "Titulo 1")
(((:TITLE . "Titulo 1") (:AUTOR . "Autor 1")))

或者你可以使用

find

(defun search-by-title (books title)
  (find title books :test #'string= :key (lambda (book) (cdr (assoc :title book)))))

> (search-by-title *books* "Titulo 1")
((:TITLE . "Titulo 1") (:AUTOR . "Autor 1"))

编辑:

find-if
search
(部分匹配/子串):

(defun search-by-title (books title)
  (find-if (lambda (book) (search title (cdr (assoc :title book))))
           books))

> (search-by-title *books* "2")
((:TITLE . "Titulo 2") (:AUTOR . "Autor 2"))

3
投票

你必须将你的代码拆分成更小的、可测试的函数,否则你将很难在编写代码时进行测试。此外,我不清楚

(string-include string1 string2)
是什么意思,在上面添加注释或向函数添加文档字符串将有助于为您和其他人阐明函数应该做什么。

更重要的是,当你问一个问题时,说“none of the tests have worked”有点不清楚:解释器是否显示错误,那个案例,哪个?还是测试只是返回 NIL 而没有进一步的信息?

主要问题可能是您的书名表示为符号或数字列表,而您使用的是字符串函数。我的意思是:

(:title 'Titulo 1)

... 被您的口译员读作

(:title (quote Titulo) 1)
。您可以编写以下表达式来获得相同的形式:

(cons :title (cons (quote Titulo) (cons 1 nil)))

我的意思是,在您的情况下,您的cons-cell将关键字

:title
绑定到列表
(Titulo 1)
,它有2个元素。如果您想改为操作字符串,则需要将它们写在双引号之间,例如
"Titulo 1
".

另见

SEARCH


0
投票

问题出在设计时:您为非键值实体存储选择键值数据结构。严格来说你的实体应该是这样的:

'(book . ((:title . "title") (:author . "author")))

但它的任何实例都将引用不必要的

'book
符号。另一方面,您可以为与任何看起来像
(name . description-alist)
的实体一起工作构建API。好的,现在我们处理如何查看我们的数据实体并可以编写简单的 API 来使用它:

(defun book-new (title author)
  ;; make new book entity such as
  ;; '(book ((:author . author) (:title . title)))
  (let (description)
    (push (cons :title title) description)
    (push (cons :author author) description)
    (cons 'book description)))

(defun bookp (data)
  ;; return true if our entity is book
  (and (consp data)
       (eq 'book (car data))))


(defun book-description (book)
  ;; return description of the book or nil
  (cdr book))

(defun book-author (book)
  ;; return author of the book or nil
  (and (bookp book)
       (cdr (assoc :author (book-description book)))))

(defun book-title (book)
  ;; return title of the book or nil
  (and (bookp book)
       (cdr (assoc :title (book-description book)))))

现在您可以构建 API 来处理

book
序列:

(defparameter *books* nil)

(defun book-push (title author)
  (push (book-new title author) *books*))

(defun book-filter (&key
                    (title nil title-supplied-p)
                    (author nil author-supplied-p)
                    (test #'equal))
  (cond ((and title-supplied-p author-supplied-p)
         (find-if #'(lambda (book)
                      (and (funcall test (book-title book) title)
                           (funcall test (book-author book) author)))
                  *books*))
        (title-supplied-p
         (find-if #'(lambda (book)
                      (funcall test (book-title book) title))
                  *books*))
        (author-supplied-p
         (find-if #'(lambda (book)
                      (funcall test (book-author book) author))
                  *books*))
        (t *books*)))

现在您可以使用

book-filter
键。或者没有它,在这种情况下函数只返回
*books*
值。让我们试试吧:

;; from REPL add 3 new books
CL-USER> (mapcar #'(lambda (title author)
            (book-push title author))
        '("name 1" "name 2" "name 3")
        '("author 1" "author 2" "author 3"))
...
CL-USER> *books*
((BOOK (:AUTHOR . "author 3") (:TITLE . "name 3"))
 (BOOK (:AUTHOR . "author 2") (:TITLE . "name 2"))
 (BOOK (:AUTHOR . "author 1") (:TITLE . "name 1")))

现在试着找一些我们需要的书:

CL-USER> (book-filter :test #'string= :title "name 3")
(BOOK (:AUTHOR . "author 3") (:TITLE . "name 3"))
CL-USER> (book-filter :test #'string= :title "name 3" :author "Carlos Castaneda")
NIL
CL-USER> (book-filter :test #'string= :title "name 3" :author "author 3")
(BOOK (:AUTHOR . "author 3") (:TITLE . "name 3"))
CL-USER> (book-filter :test #'string= :author "author 1")
(BOOK (:AUTHOR . "author 1") (:TITLE . "name 1"))
CL-USER> (book-filter)
((BOOK (:AUTHOR . "author 3") (:TITLE . "name 3"))
 (BOOK (:AUTHOR . "author 2") (:TITLE . "name 2"))
 (BOOK (:AUTHOR . "author 1") (:TITLE . "name 1")))
CL-USER> 

很好用!享受吧。

© www.soinside.com 2019 - 2024. All rights reserved.