【问题标题】:From file stream to assoc-list in common lisp从文件流到普通 lisp 中的关联列表
【发布时间】:2019-05-10 14:58:29
【问题描述】:

我有一个以 (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,但它存储在一个文件中。我需要能够在关联列表中获取这些数据以到达 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 (阅读 *s*)) 它让我无处可去。您对如何实现这一目标有任何想法吗?

【问题讨论】:

  • 文件中到底有什么? defparameter 表单或以(((KEY 1) … 开头的表单?
  • @Svante 是的,它从 defparameter 部分开始,就像您一起定义一个 defparameter 但它在文件中。

标签: common-lisp file-read clos


【解决方案1】:

可以使用内置函数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 的结构或类,其插槽名称为 keybczfeats 等等,然后使用像 (entry-bcz x) 这样的访问器而不是 @ 987654336@。这可能有助于提高可读性和效率,但要做到这一点,您需要从这种基于列表的数据表示中创建对象。

【讨论】:

  • 感谢您的宝贵时间,这正是我要找的。一个程序生成这个文件,它总是这样的结构,所以我可以信任其中的内容。我想我会尝试创建一个类,然后创建对象以使其更具可读性。
【解决方案2】:

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

【讨论】:

  • 非常感谢。希望我能同时设置两个答案。
  • 欢迎您! @Karavana - 如果我能帮助你,我很高兴!)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多