从文件流到普通 lisp 中的关联列表

From file stream to assoc-list in common lisp

我有一个以 (defparameter *myfile* '(((KEY 1) (A1 CAN) (A2 4) (SUR (((BCZ S) (FEATS NIL)) (DIR FS) (LADOM ALL) (((NNEW S) (FEATS NIL)) (DIR BS) (LADOM ALL) ((NNEW NP) (FEATS ((BIG NOM))))))) (SEM (LAM P (P "CAN"))) (PARAM 1.0)) ((KEY 2) (A1 KEDIYI) (A2 4)开头的文件............然后继续这样。

我猜这是一个 CLOS,但它存储在一个文件中。我需要能够在 assoc-list 中获取这些数据以到达 A1 或 A2 等作为之后获取它们的值的键。我现在所做的是逐行读取文件并对其进行字符串操作。但我认为这确实是一种不好的做法。这是我现在的代码;

(defun open_ded (path)
 (defvar last_id 0)
 (let ((in (open path :if-does-not-exist nil)))
  (when in
    (loop for line = (read-line in nil)
        while line 
            do 
                (if (setq key_id (findkeyid line)) ;search "KEY" and return its id value
                (setq last_id key_id)) ;if it is not nil, set it to last_id

而且我知道我可以使用 (defparameter *s* (open "path")) 获取整个文件,但是当我想做的时候 (assoc 'A1 (read *s*)) 或 ( assoc 'KEY (read *s*)) 它让我无处可去。您对如何实现这一点有什么想法吗?

您可以使用内置函数load读取文件:

(load "/tmp/data.lisp")

这将设置变量 *myfile*,然后您可以执行以下操作:

* (print *myfile*)

(((KEY 1) (A1 CAN) (A2 4)
  (SUR
   (((BCZ S) (FEATS NIL)) (DIR FS) (LADOM ALL)
    (((NNEW S) (FEATS NIL)) (DIR BS) (LADOM ALL)
     ((NNEW NP) (FEATS ((BIG NOM)))))))
  (SEM (LAM P (P "CAN"))) (PARAM 1.0))
 ((KEY 2) (A1 KEDIYI) (A2 4)))
(((KEY 1) (A1 CAN) (A2 4)
  (SUR
   (((BCZ S) (FEATS NIL)) (DIR FS) (LADOM ALL)
    (((NNEW S) (FEATS NIL)) (DIR BS) (LADOM ALL)
     ((NNEW NP) (FEATS ((BIG NOM)))))))
  (SEM (LAM P (P "CAN"))) (PARAM 1.0))
 ((KEY 2) (A1 KEDIYI) (A2 4)))

* (loop for i from 0
        for entry in *myfile*
        do (format t "Entry #~D: ~S~%" i entry))
Entry #0: ((KEY 1) (A1 CAN) (A2 4)
             (SUR
              (((BCZ S) (FEATS NIL)) (DIR FS) (LADOM ALL)
               (((NNEW S) (FEATS NIL)) (DIR BS) (LADOM ALL)
                ((NNEW NP) (FEATS ((BIG NOM)))))))
             (SEM (LAM P (P "CAN"))) (PARAM 1.0))
Entry #1: ((KEY 2) (A1 KEDIYI) (A2 4))
NIL

* (dolist (entry *myfile*)
     (print (assoc 'key entry)))

(KEY 1)
(KEY 2)
NIL

如果您不信任文件内容,并且想要阅读文件中的源代码而不是执行它(load 做的),你会使用 read。在那种情况下,还要将 *read-eval* 绑定到 nil 以防止使用 #. 否则会在读取时执行代码:

(with-open-file (f "/tmp/data.lisp")
  (let ((*read-eval* nil))
    (loop for form = (read f nil nil)
          while form
          do (print form)))

文件内容看起来像一个条目集合,每个条目都是一个键值对列表。它里面没有任何东西可以将它连接到 CLOS,尽管你可以定义一个名为 entry 的结构或 class 以及插槽名称 keybczfeats 等等等,然后使用像 (entry-bcz x) 这样的访问器而不是 (second (assoc 'bcz x))。这可能有助于提高可读性和效率,但为此您需要从这种基于列表的数据表示中创建对象。

@zut 很好地展示了如何在不评估内容的情况下读取文件。

我正在使用它并向您展示如何从第一个表达式中获取键值对。

;; read-in-first-expression from file
(defparameter *f* (with-open-file (f "~/Dropbox/cl/test-file.lisp")
            (let ((*read-eval* nil))
              (loop for form = (read f nil nil)
                        while form
                collect form))))
*f*
;; first expression (defparameter expression):
(defparameter *f-1st-expr* (first *f*))
*f-1st-expr*

;; ;; I was trying to get to the data part of the first expression trying/using:
;; (first (second (third *f-1st-expr*)))
;; (second (second (third *f-1st-expr*)))

;; let's say these are lists of small lists of length 2

(defun two-element-lists-to-alist (two-list)
  (mapcar (lambda (p) (cons (first p)
                (let ((el (cdr p)))
                  (if (and (atom (car el)) (= (length el) 1))
                  (car el)
                  el))))
      two-list))

(defun convert-to-alists (read-in-defparameter-expression)
  (let ((data (second (third read-in-defparameter-expression))))
    (mapcar #'two-element-lists-to-alist data)))

;; convert to list of alists
(defparameter *alists* (convert-to-alists *f-1st-expr*))

;; ;; one can now access within the list of a lists using assoc and elt
;; ;; the key-value pairs
;; (assoc 'KEY (elt *alists* 0))
;; (assoc 'KEY (elt *alists* 1))
;; (assoc 'A1  (elt *alists* 0))

;; write a function to directly access key-value-pair of nth alist in alists
(defun get-key-from-nth (alists key nth)
  (assoc key (elt alists nth)))

;; testing:
(get-key-from-nth *alists* 'A1 0) ;; (A1 . CAN)
(get-key-from-nth *alists* 'A1 1) ;; (A1 . KEDIYI)
(get-key-from-nth *alists* 'KEY 0) ;; (KEY . 1)
(get-key-from-nth *alists* 'KEY 1) ;; (KEY . 2)
;; works!

~/Dropbox/cl/test-file.lisp的内容是:

    (defparameter *myfile* 
    '(((KEY 1) (A1 CAN) (A2 4)
        (SUR (((BCZ S) (FEATS NIL)) (DIR FS) (LADOM ALL)
        (((NNEW S) (FEATS NIL)) (DIR BS) (LADOM ALL)
        ((NNEW NP) (FEATS ((BIG NOM)))))))
        (SEM (LAM P (P "CAN"))) (PARAM 1.0))
      ((KEY 2) (A1 KEDIYI) (A2 4) 'and-so-on)))