首页 > 解决方案 > 使用 Prolog 将 csv 文件中的数据解析为给定格式

问题描述

我的 csv 文件包含以下数据:

  A  B  C
A -  4  5
B 8  -  6
C 2  3  -

我想要以下形式的事实:

num(a,b,4).
num(a,c,5).
num(b,a,8).
num(b,c,6).
num(c,a,2).
num(c,b,3).

类似的字母不应该有事实,比如 num(a,a,-)。

我正在使用 prolog 的 csv_read_file 作为:

csv_read_file(Path, Rows, [functor(num), arity(4)]), maplist(assert, Rows).

它给我的输出为:

Rows = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)]

这似乎是一个基本问题,但我无法考虑执行此操作的条件。任何帮助将不胜感激。

根据伊莎贝尔新手的回答:

Open :- csv_read_file('Path', Rows, [functor(num), arity(4)]), table_entry(Rows, Row).


header_row_entry(Header,Row,Entry):-
    arg(1, Row, RowName),
    functor(Header, _, Arity),
    between(2,Arity,ArgIndex),
    arg(ArgIndex, Header, ColumnName),
    arg(ArgIndex, Row, Value),
    Entry = num(RowName, ColumnName, Value),
    writeln(Entry).

table_entry(Entries, Entry):-
    Entries = [Header | Rows],
    member(Row, Rows),
    header_row_entry(Header, Row, Entry).

现在,任何人都可以解释我应该如何以及在何处使用 maplist 以事实形式转换行(暂时忽略“-”和小写的过滤),以便在查询时:

?-num(A,B,X).

我应该得到:

X=4

下一个任务是,我想在它上面实现深度优先搜索算法。任何有关此的细节将不胜感激。

标签: csvprologswi-prolog

解决方案


考虑一个表头num('', 'A', 'B', 'C')和表中的一行num('B', 8, -, 6)。由此,您要计算由行名(此处为'B')和列名标识的表条目:列名'A'用于第一个值(8),'B'第二个值( ) -'C'第三个值(6)。

这是一种简单的方法,涉及一些打字和强制性的复制和粘贴错误:

header_row_entry(Header, Row, Entry) :-
    Header = num('', ColumnName, _, _),
    Row = num(RowName, Value, _, _),
    Entry = num(RowName, ColumnName, Value).
header_row_entry(Header, Row, Entry) :-
    Header = num('', _, ColumnName, _),
    Row = num(RowName, _, Value, _),
    Entry = num(RowName, ColumnName, Value).
header_row_entry(Header, Row, Entry) :-
    Header = num('', _, _, ColumnName),
    Row = num(RowName, _, _, Value),
    Entry = num(RowName, ColumnName, Value).

这将枚举回溯行中的所有条目:

?- Header = num('', 'A', 'B', 'C'), Row = num('B', 8, -, 6),
      header_row_entry(Header, Row, Entry).
Header = num('', 'A', 'B', 'C'),
Row = num('B', 8, -, 6),
Entry = num('B', 'A', 8) ;
Header = num('', 'A', 'B', 'C'),
Row = num('B', 8, -, 6),
Entry = num('B', 'B', -) ;
Header = num('', 'A', 'B', 'C'),
Row = num('B', 8, -, 6),
Entry = num('B', 'C', 6).

要枚举整个表中的所有条目,仍然需要枚举所有行,然后如上所述枚举行条目。这是:

table_entry(Entries, Entry) :-
    Entries = [Header | Rows],
    member(Row, Rows),
    header_row_entry(Header, Row, Entry).

现在,给定你的桌子:

?- Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)], table_entry(Table, Entry).
Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)],
Entry = num('A', 'A', -) ;
Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)],
Entry = num('A', 'B', 4) ;
Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)],
Entry = num('A', 'C', 5) ;
Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)],
Entry = num('B', 'A', 8) ;
Table = [num('', 'A', 'B', 'C'), num('A', -, 4, 5), num('B', 8, -, 6), num('C', 2, 3, -)],
Entry = num('B', 'B', -) .  % etc.

根据您确切想要的内容,它仍然将行名和列名小写(downcase_atom例如,在 SWI-Prolog 中令人讨厌的名称)并过滤掉-条目。然后,您可以使用失败驱动的循环或通过使用收集所有条目并使用 断言来断言findall条目maplist

现在我们有了一个可行的解决方案,我们可能想要header_row_entry更好一点。我们可以使用arg/3更明确地捕获我们正在尝试将列名和值配对,这些列名和值在它们各自的标题和行术语中位于相同的参数位置:

header_row_entry(Header, Row, Entry) :-
    arg(1, Row, RowName),
    functor(Header, _, Arity),
    between(2, Arity, ArgIndex),
    arg(ArgIndex, Header, ColumnName),
    arg(ArgIndex, Row,    Value),
    Entry = num(RowName, ColumnName, Value).

这比上面的要短,适用于表中的任意数量的列。


推荐阅读