将带有列表的多行文本文件转换为 Common Lisp 中的哈希表

Convert multi-line text file with lists to hashtable in Common Lisp

假设我有一个格式如下的文本文件:

(:question
 (hello
  how
  are
  you))

(:answer
 (i
  am
  fine
  thanks))

我希望阅读然后将其转换为哈希表,其中第一个单词(以 : 开头)是键,然后内部列表是给定键的值。我怎样才能做到这一点?我已经尝试了几种方法来解决这个问题,但我找不到读取文件然后将其转换为哈希表的好方法。

我设法使用以下代码解决了问题:

(defun symbols-to-lowercase-strings (sym-list)
  (let ((newlist (list '())))
    (loop for symbol in sym-list
       do (progn
        (setf symbol (string symbol))
        (setf symbol (string-downcase symbol))
        (push symbol newlist)))
    (subseq newlist 0 (- (length newlist) 1))))

(defun read-file (filename)
  (let ((classes (make-hash-table :test #'equal))
    (class-lists NIL))
    (with-open-file (stream filename :direction :input)
      (loop
     for line = (read stream nil)
     while line
     collect line
     do (push (cons (car line) (cdr line)) class-lists))
      (loop for line in class-lists
     do (setf (gethash (car line) classes) (list (symbols-to-lowercase-strings (car (cdr line))) '(0)))))
    classes))

既然您发布了 ,可能值得比较如何更简单地完成它。 loop 宏支持一堆不同的子句,其中一些在这里非常方便。如果我知道我可以从流中读取表单(键值)的值,直到没有更多的值(在这种情况下,直到读取 nil,或者遇到流),我会做这样的事情:

(defun read-hashtable (&optional (stream *standard-input*))
  (loop
     with table = (make-hash-table)         ; the hash table
     with sentinel = (cons 1 1)             ; unique value for EOF
     for x = (read stream nil sentinel nil) ; read value, sentinel if EOF
     until (eq sentinel x)                  ; until EOF, indicated by sentinel
     do (setf (gethash (first x) table) (second x)) ; set a value in the table
     finally (return table)))                       ; finally return the table

那么你可以这样使用它:

(with-open-file (in ".../input.txt")
  (read-hashtable in))
;=> #<HASH-TABLE :TEST EQL :COUNT 2 {10056B2C43}>

如果您不喜欢 loop,使用 do 也很容易做到这一点:

(defun read-hashtable (&optional (stream *standard-input*))
  (do* ((sentinel (cons 1 1))
        (table (make-hash-table))
        (x (read stream nil sentinel nil) (read stream nil sentinel nil)))
       ((eq x sentinel) table)
    (setf (gethash (first x) table) (second x))))