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

时间:2016-08-17 09:03:12

标签: parsing lisp common-lisp

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

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

我有一些标有一些标记的示例问题,如" |"开始和" // s"为了和一节。问题文件将具有这样的层次结构:部分 - >多个子部分 - >每个子部分都有多个问题 - >每个问题都有多个答案,其中一个是正确的。

这个层次结构最终需要转换为json文件并推送到Android应用程序以供下游使用。

STEP-1:从源测试论文中读取后,这就是我的列表的样子:

    initializeDownloadUI();
    Intent launchIntent = ExpansionDownloader.this.getIntent();
                    Intent intentToLaunchThisActivityFromNotification = new Intent(
                            ExpansionDownloader.this,
                            ExpansionDownloader.this.getClass());
                    intentToLaunchThisActivityFromNotification
                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    intentToLaunchThisActivityFromNotification
                            .setAction(launchIntent.getAction());

                    if (launchIntent.getCategories() != null) {
                        for (String category : launchIntent.getCategories()) {
                            intentToLaunchThisActivityFromNotification
                                    .addCategory(category);
                        }
                    }

                    // Build PendingIntent used to open this activity from
                    // Notification
                    PendingIntent pendingIntent = PendingIntent.getActivity(
                            ExpansionDownloader.this, 0,
                            intentToLaunchThisActivityFromNotification,
                            PendingIntent.FLAG_UPDATE_CURRENT);
                    // Request to start the download
                    int startResult = DownloaderClientMarshaller
                            .startDownloadServiceIfRequired(this, pendingIntent,
                                    ExpansionService.class);

                    if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                        // The DownloaderService has started downloading the files,
                        // show progress
                        initializeDownloadUI();
                        return;
                    } // otherwise, download not needed so we fall through to
                        // starting the movie
                } catch (NameNotFoundException e) {
                    Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
                    e.printStackTrace();
                }

[PS.1] 请参阅帖子末尾的图例,了解对cdar值的解释,如h,p,t,v等,

[PS.2] 在此帖末尾附上的源文件示例

表示内容的consed对中的每辆车和代表该部分的cdr - 将对应于部分,子部分或问题等。

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

(("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"))

被cl-json消耗。

STEP-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 pair列表。我正在努力的是创建这个嵌套列表,如上所示,以将其提供给cl-json。

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

以下是我的问题:

a)构建Test纸源文件的这种n-ary树表示的正确方法是什么?

b)或者是否有更好或更简单的数据结构来代表这个?

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

步骤1和步骤3都没问题。第二步是我需要帮助的地方。问题是如何通过迭代kvlist连续添加子节点,并找到正确的父节点,当有多个父节点时添加子节点(例如,向第二个子节点添加一个问题) -section):

 {
  "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"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

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

t - title,v - version,d - date,p - section,s - sub section,q - question,i - instructions,n - type of question,c - correct reply,w - wrong answers

[PS.2]源文件:

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

1 个答案:

答案 0 :(得分:3)

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

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

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