在LISP中查找alist中的项。

huangapple go评论59阅读模式
英文:

LISP find item in alist

问题

I've got a a-list like

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

I need to create a function that find a book based on its title. After reading the docs I try:

(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)
)

I can't find the book, what am I doing wrong?

Note that none of the tests I have done (commented) have worked.

英文:

I've got a a-list like

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

I need to create a function that find a book based on its title. After reading the docs I try:

(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)
)

I can't find the book, what am I doing wrong?

Note that none of the tests I have done (commented) have worked

答案1

得分: 8

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

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

注意三件事情:

  • 你不应该使用setf来初始化全局变量-应该对全局变量使用defparameterdefvar,对局部变量使用let

这可以帮助你:defvar、defparameter、setf和setq之间的区别是什么?

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

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

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

我不会过多评论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-ifsearch(部分匹配/子字符串):

(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"))
英文:

Your data structure isn't an associative list. Alist is a list of pairs, so you should create it like this:

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

Note three things:

  • You shouldn't use setf to initialize a global variable- you should use defparameter or defvar for global variables and let for local ones.

This can help you: What's difference between defvar, defparameter, setf and setq

  • You should use only one quote, because expressions inside quoted data structure are also quoted. Also, this call (quote Autor 1) doesn't make any sense- quote is called with only one argument.

This can help you: When to use ' (or quote) in Lisp?

  • You should format your code properly- including parentheses.

I won't comment string-include so much- but some calls seem strange. Why do you call (string string1) (converting string to string) or (string= string1 s :end2 (length string1)) (string= compares strings, but result of (length string1) will be number)? You should not only write smaller and testable functions, but also test your code continuously in REPL to make sure everything works as you expect.

That buscar function seems a little bit better (but it still contains some strange calls like (format nil book) you didn't test before) and you can change it into something like this:

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

Test:

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

Or you can use 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"))

EDIT: Or find-if with search (partial match/ substring):

(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"))

答案2

得分: 3

你需要将你的代码分成更小的、可测试的函数,否则在编写代码时测试会变得困难。此外,对于(string-include string1 string2),我不清楚它的含义,添加一个注释或文档字符串到函数会帮助澄清它应该做什么,不仅对你自己,也对其他人有帮助。

更重要的是,当你提出问题时,说“没有一个测试起作用”有点不清楚:是解释器显示了一个错误吗?如果是的话,是哪一个错误?还是测试只是返回了NIL而没有提供更多的信息?

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

(:title 'Titulo 1)

...会被你的解释器解释为(:title (quote Titulo) 1)。你可以写下以下表达式来获得相同的形式:

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

我的意思是,在你的情况下,你的cons单元将关键字:title绑定到列表(Titulo 1),它有2个元素。如果你想要操作字符串,你需要将它们放在双引号之间,例如"Titulo 1"

还可以参考SEARCH

英文:

You have to split your code into smaller, testable functions, otherwise you are going to have difficulties testing along as you code. Furthermore, it is not clear to me what (string-include string1 string2) means, adding a comment above or a documentation string to the function would help clarify, for you and others, what the function should be doing.

More importantly, when you ask a question, saying "none of the tests have worked" is a bit unclear: did the interpreter show an error, and that case, which one? or did the test just return NIL without no further information?

The main problem is likely that your book titles are expressed as lists of symbols or numbers, whereas you are using string functions. What I mean is that:

(:title 'Titulo 1)

... is read by your interpreter as (:title (quote Titulo) 1). You can write the following expression to obtain the same form:

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

What I am saying is that in your case your a cons-cell binds the keyword :title to the list (Titulo 1), which has 2 elements. If you want to manipulate strings instead, you would need to write them between double-quotes, for example "Titulo 1".

See also SEARCH.

答案3

得分: 0

这部分内容是有关设计时问题的代码,涉及数据结构和API的问题。如果您有任何具体的问题或需要进一步的信息,请告诉我。

英文:

The problem is at design-time: you choose key-value data structure for a non key-value entities storing. Strictly saying your entity should be something like this:

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

but any instance of it will refer to the needless 'book symbol. Another hand, you can build API for a work with anyone entity which looks like (name . description-alist). Ok, now we deal how to looks our data entity and can write simple API to use it:

(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)))))

Now you can build API to working with book sequences:

(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*)))

Now you can use book-filter with keys. Or without it, in this case function just return *books* value. Let's try it:

;; 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")))

now try to find some book as we need:

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> 

It woks fine! Enjoy.

huangapple
  • 本文由 发表于 2023年3月21日 01:27:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75793461.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定