如何在Prolog中使用DCG解析文本

问题描述 投票:1回答:1

因此,我目前正在尝试使用包含课程和参加该课程的任何人的学生证的文本文件来实现类似于该树的功能。

courses(
    [
     ('MATH2221',
      [
       201000001,
       201000002
      ]
     ),

     ('MATH2251',
      [
       201000002,
       201000003
      ]
     ),

     ('COMP2231',
      [
       201000003,
       201000001
      ]
     )
    ]
).

我正在解析的文本文件看起来像这样:

MATH2221
       201000001
       201000002

MATH2251
       201000002
       201000003

COMP2231
       201000003
       201000001

我在网上阅读到,使用DCG可能是解决此问题的最佳方法,因为每个学生都以制表符开头,所以ascii值为'9',然后课程以2个nl字符分隔。我真的在序言上迷失了,我只打算发布我目前正在使用的东西,因为其他所有东西都一团糟。是否有人有任何建议或至少可以帮助我了解DCG是什么?

:- debug.
:- [library(dcg/basics)].

load:-
    open('courses.txt',read,Stream),
         read,
         close(Stream).

read:-
    open('courses.txt',read,In),
    repeat,
    read_line_to_codes(In,X),write(X), nl,
    (X=end_of_file,!,
    nl); fail.
parsing tree prolog swi-prolog dcg
1个回答
0
投票

虽然您所要提出的想法很简单,并且转换为DCG似乎相对简单,但实际上,它需要经验和技巧来知道如何正确有效地做到这一点。

以下内容适用于Windows 10上的SWI-Prolog(线程,64位,版本8.1.21)

:- [library(dcg/basics)].

courses([Course|Courses]) -->
    course(Course),
    courses(Courses), !.
courses([]) --> [].

course(course(Course,Students)) -->
    string_without("\n", Course_codes),
    { string_codes(Course,Course_codes ) },
    "\n",
    students(Students),
    (
        empty_line
    ;
        []
    ).

students([Student|Students]) -->
    student(Student),
    students(Students).
students([]) --> [].

student(Student) -->
    "\t",
    (
        (
            string_without("\n",Student_codes),
            { string_codes(Student,Student_codes) },
            "\n"
        )
    ;
        remainder(Student_codes),
        { string_codes(Student,Student_codes) }
    ).

empty_line --> "\n".

load_courses :-
    Input = "\c
MATH2221\n\c
    \t201000001\n\c
    \t201000002\n\c
    \n\c
MATH2251\n\c
    \t201000002\n\c
    \t201000003\n\c
    \n\c
COMP2231\n\c
    \t201000003\n\c
    \t201000001\c
",
    string_codes(Input,Codes),
    DCG = courses(Courses),
    phrase(DCG,Codes,Rest),
    assertion( Rest == [] ),
    format('Courses: ~n',[]),
    print_term(Courses,[]).

示例运行:

?- load_courses.
Courses: 
[ course("MATH2221",["201000001","201000002"]),
  course("MATH2251",["201000002","201000003"]),
  course("COMP2231",["201000003","201000001"])
]
true.

在您的示例中,您正在从文件中读取数据,但是在本示例中,我将该数据硬编码到查询中,以便可以在任何地方复制该数据而无需复制文件。 Input使用\c,请参阅:Character Escape Syntax使输入的格式保持良好。

当从文件中加载数据时,请使用phrase_from_file/2phrase_from_file/3。另请检查可能有用的open_string/2

您使用library(dcg/basics)是正确的,但是将其用作谓词时要格外小心,因为期望输入是宪章代码,而不是原子或字符串。

[使用DCG解析文本时非常常用的谓词是string_without//2,但正如我所指出的,它与字符代码一起使用,因此需要string_codes/2才能将代码转换回字符串。同样,由于string_codes/2是标准谓词,因此需要将其与{}绑定在一起,以使DCG术语重写代码知道不进行翻译。

[创建示例时,我可以在最后一个学生之后添加\n并添加额外的一行,并使解析器非常简单,但选择遵循更现实的惯例,即不添加\n ;(或)部分,例如; []代表最后一个学生后的空行,; remainder//1代表最后一个学生后的空\n

由于我不知道您需要了解多少才能理解它,我也不想写几章详细介绍所有这些细节,所以请问是否有问题,我确实希望您使用该代码,并通过显示您尝试过的示例而不是仅仅询问您的原因来解释为什么您不理解该代码。


我真的只是在用I / O挣扎

这里是使用read_file_to_codes/3的代码的修改版本。请注意,read_file_to_codes / 3是少数几个直接使用文件名且不需要使用open/3

的谓词之一。
:- [library(dcg/basics)].

courses([Course|Courses]) -->
    course(Course),
    courses(Courses), !.
courses([]) --> [].

course(course(Course,Students)) -->
    string_without("\n", Course_codes),
    { string_codes(Course,Course_codes ) },
    "\n",
    students(Students),
    (
        empty_line
    ;
        []
    ).

students([Student|Students]) -->
    student(Student),
    students(Students).
students([]) --> [].

student(Student) -->
    spaces_or_tabs_plus,
    (
        (
            string_without("\n",Student_codes),
            { string_codes(Student,Student_codes) },
            "\n"
        )
    ;
        % remainder_open_list(Student_codes),
        remainder(Student_codes),
        { string_codes(Student,Student_codes) }
    ).

spaces_or_tabs_plus -->
    space_or_tab,
    spaces_or_tabs_star.

spaces_or_tabs_star -->
    space_or_tab,
    spaces_or_tabs_star.
spaces_or_tabs_star --> [].

space_or_tab -->
    (
        "\s"
    |
        "\t"
    ).

empty_line --> "\n".

example_01 :-
    Input = "\c
MATH2221\n\c
    \t201000001\n\c
    \t201000002\n\c
    \n\c
MATH2251\n\c
    \t201000002\n\c
    \t201000003\n\c
    \n\c
COMP2231\n\c
    \t201000003\n\c
    \t201000001\c
",
    string_codes(Input,Codes),
    parse_courses(Codes,Courses),
    display_courses(Courses).

example_02 :-
    File_name = "C:\\Users\\Groot\\Documents\\Projects\\Prolog\\SO_question_163_courses.txt",
    read_file_to_codes(File_name,Codes,[]),
    parse_courses(Codes,Courses),
    display_courses(Courses).

parse_courses(Codes,Courses) :-
    DCG = courses(Courses),
    phrase(DCG,Codes,Rest),
    assertion( Rest == [] ).

display_courses(Courses) :-
    format('Courses: ~n',[]),
    print_term(Courses,[]).

和一些示例运行

?- example_01.
Courses: 
[ course("MATH2221",["201000001","201000002"]),
  course("MATH2251",["201000002","201000003"]),
  course("COMP2231",["201000003","201000001"])
]
true.

?- example_02.
Courses: 
[ course("MATH2221",["201000001","201000002"]),
  course("MATH2251",["201000002","201000003"]),
  course("COMP2231",["201000003","201000001"])
]
true.

注意:

我尝试使用phrase_from_file/3dcg/basics来执行此操作,但是dcg/basics预期关闭列表phrase_from_file/3创建了惰性列表,并且在处理代码时将其重写为dcg/basics中的谓词并处理stream of end问题,这些问题是学习DCG时遇到的最大问题。

© www.soinside.com 2019 - 2024. All rights reserved.