От файлового потока к списку ассоциаций в общем lisp - PullRequest
0 голосов
/ 09 декабря 2018

У меня есть файл, который начинается с (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, но он хранится в файле,Мне нужно иметь возможность получить эти данные в ассоциированном списке, чтобы добраться до А1 или А2 и т. Д. В качестве ключей, чтобы впоследствии получить их значения.Что я делаю сейчас, так это читаю файл построчно и выполняю над ним строковые операции.Но я думаю, что это действительно плохая практика.Вот мой код на данный момент:

(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 * (открыть "путь")), но когда я хочу сделать (Assoc 'A1(читай * s *)) или (ассоциативный КЛЮЧ (читай * s *)) это никуда меня не приведет.У вас есть идеи о том, как этого добиться?

Ответы [ 2 ]

0 голосов
/ 09 декабря 2018

@ 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)))
0 голосов
/ 09 декабря 2018

Вы можете использовать встроенную функцию 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 с именами слотов key, bcz, feats и так далее, а затем использовать средства доступа, такие как (entry-bcz x) вместо (second (assoc 'bcz x)).Это может помочь читабельности и эффективности, но для этого вам нужно создать объекты из этого представления данных на основе списка.

...