parsing - 在 Common Lisp 中读取文件并转换为基于 n 元树的嵌套列表?

标签 parsing lisp common-lisp

对于我的一个项目,我正在尝试使用 Common Lisp,特别是 SBCL(在这个过程中,学习它。这是动机之一。)

我需要阅读一个包含问题和答案的文件,基本上就像一个主要包含多项选择题答案的标准化测试。

我有一些示例问题,上面标有一些部分标记,例如“|”对于一个部分的开始和“//s”。试卷将具有如下层次结构:部分 -> 多个子部分 -> 每个子部分有多个问题 -> 每个问题将有多个答案,其中一个是正确的。

这个层级结构最终需要转化为json文件,推送给android应用供下游消费。

第 1 步:阅读源试卷后,我的列表将如下所示:

(("Test" . "t")
 ("0.1" . "v")
 ("today" . "d")
 ("General Knowledge" . "p")
 ("Science" . "s")
 ("what is the speed of light in miles per second?" . "q")
 ("Choose the best answer from the following" . "i")
 ("MCQ question" . "n")
 ("186000" . "c")
 ("286262" . "w")
 ("200000" . "w"))

[PS.1]有关 cdar 值(如 h、p、t、v 等)的解释,请参阅帖子末尾的图例,

[PS.2]本文末尾附上的源文件示例

cond 对中的每辆汽车代表内容,cdr 代表部分 - 将对应一个部分、子部分或问题等,

第 2 步: 最后我需要将其转换为以下格式 - 列表 -

((:QANDA . "Test") (:VERSION . "0.1") (:DATE . "today")
 (:SECTION
  ((:TITLE . "General Knowledge")
   (:SUBSECTION
    ((:SSTITLE . "Science")
     (:QUESTION
      ((:QUESTION . "what is the speed of light in miles per second?")
       (:DIRECTIONS . "Choose the best answer from the following")
       (:TYPE . "MCQ question")
       (:CHOICES ((:CHOICE . "186000") (:CORRECT . "Y"))
        ((:CHOICE . "286000") (:CORRECT . "N"))
        ((:CHOICE . "200000") (:CORRECT . "N"))))))))))

由 cl-json 使用。

第 3 步:cl-json 将从中生成适当的 json。

json 看起来像这样:

 {
  "qanda": "Test",
  "version": "0.1",
  "date": "today",
  "section": [
    {
      "title": "General Knowledge",
      "subsection": [
        {
          "sstitle": "Science",
          "question": [
            {
              "question": "what is the speed of light in miles per second?",
              "Directions": "Choose the best answer from the following",
              "type": "MCQ question",
              "choices": [
                {
                  "choice": "186000",
                  "Correct": "Y"
                },
                {
                  "choice": "286000",
                  "Correct": "N"
                },
                {
                  "choice": "200000",
                  "Correct": "N"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

我已经成功读取了源文件,生成了 consed 对列表。我努力的地方是创建如上所示的嵌套列表以将其提供给 cl-json。

经过一番努力后,我意识到这或多或少像一个 n 叉树问题。

这是我的问题:

a) 构造试卷源文件的这种 n 叉树表示的正确方法是什么?

b) 或者是否有更好或更简单的数据结构来表示它?

这是我尝试过的,其中 qtree 最初是 '() 并且 kvlist 是上面显示的 consed 对列表。这是一个不完整的代码,因为我尝试了 push.、consing 和 nconc(结果不可靠)。

Step-1 and Step 3 are fine. Step-2 is where I need help.The problem is to how to add child nodes successively by iterating through the kvlist and find the right parent to add the child when more than one parent is there (e.g., adding a question to the second sub-section):

(defun build-qtree (qtree kvlist)
  (cond
    ((eq '() kvlist) qtree)
    ((equal "h" (cdar kvlist))
     (push (car kvlist) qtree)
     (build-qtree qtree (cdr kvlist)))
    ((equal "p" (cdar kvlist))
     (nconc (last qtree) '((:SECTION))))
    (t
     (qtree)))) 

[PS.1] Legend: 这将在条件分支中使用,或者可能是 defstruct 或字典类型的列表等,

t - 标题,v - 版本,d - 日期,p - 部分,s - 子部分,q - 问题,i - 说明,n - 问题类型,c - 正确答案,w - 错误答案

[PS.2]源文件:

|Test//t
|0.1//v
|today//d
|General Knowledge//p
|Science//s
|what is the speed of light in miles per second?//q
|Choose the best answer from the following//i
|MCQ question//n
|186000//c
|286000//w
|200000//w

最佳答案

您有一个复杂示例的简单问题。这可能只是一个简单的解析问题:您需要的是语法和解析器。

示例语法。终端项目是大写的。 * 表示一个或多个

s = q
q = Q i*
i = I w*
w = W

简单解析器:

(defun example (sentence)
  (labels ((next-item ()
             (pop sentence))
           (empty? ()
             (null sentence))
           (peek-item ()
             (unless (empty?)
               (first sentence)))
           (expect-item (sym)
             (let ((item (next-item)))
               (if (eq item sym)
                   sym
                 (error "Parser error: next ~a, expected ~a" item sym))))
           (star (sym fn)
             (cons (funcall fn)
                   (loop while (eq (peek-item) sym)
                         collect (funcall fn)))))
    (labels ((s ()
               (q))
             (q ()
               (list (expect-item 'q) (star 'i #'i)))
             (i ()
               (list (expect-item 'i) (star 'w #'w)))
             (w ()
               (expect-item 'w)))
      (s))))

例子:

CL-USER 10 > (example '(q i w w w i w w))
(Q
 ((I (W
      W
      W))
  (I (W
      W))))

关于parsing - 在 Common Lisp 中读取文件并转换为基于 n 元树的嵌套列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38992281/

相关文章:

coding-style - 减少,或显式递归?

ssl - CL+SSL SSL Error : Unsafe legacy renegotiation disabled. 如何绕过或解决?

function - lisp函数的实现

php - 是否有完整的PHP命令行解析器?

list - lisp 编程中的变量

java - 插入命名属性值,a la Ant

按第二个对象排序缺点列表

common-lisp - 在 Common Lisp 中提取正则表达式匹配

c# - 如何使用 float.Parse 从 "5/2"等字符串中获取小数

python - Python 中的 XML 解析