我需要将固定格式的文本文件的内容读入Prolog中的列表列表(LL),但是我想从该行的列表中排除每行的第一个和最后一个元素。输入文件的第一行包括行数(LL中的列表数)和列数(LL中每个列表的元素数)。 带有3行4列的示例输入文件是
3 4
A B C D Cd
1 9 3 7 4 7
2 6 8 4 0 32
3 2 4 3 8 42
Ab 140 21 331 41 55
我想
LL = [[9,3,7,4],[6,8,4,0],[2,4,3,8]]
如何从LL中排除每行的第一个和最后一个元素?
我尝试阅读SWI-Prolog文档并在此处搜索相关线程,但未成功。
readAll( InStream, [W|L] ) :-
readWordNumber( InStream, W ), !,
readAll( InStream, L ).
readAll( InStream, [] ) :-
\+readWordNumber(InStream,_).
lst_2_lst_of_lst([], _N, []).
lst_2_lst_of_lst(L, N, LL) :-
lst_2_lst_of_lst_helper(L, 1, N, LL).
lst_2_lst_of_lst_helper([H|T], N, N, [[H]|LL]):-
lst_2_lst_of_lst(T, N, LL).
lst_2_lst_of_lst_helper([H|T], N1 , N, [[H|TMP]|LL]):-
N2 is N1 + 1,
lst_2_lst_of_lst_helper(T, N2 , N, [TMP| LL]).
致电后
...readAll(F,Input), ...
lst_2_lst_of_lst(Input, C, LL)
(C为4,从文本文件F的第一行读入)
我当前的结果看起来像这样
LL = [[1,9 3 7 4 7,2,6 8 4 0 32],[3,2 4 3 8 42,Ab,140 21 331 41]]
我希望它看起来像这样
LL = [[9,3,7,4],[6,8,4,0],[2,4,3,8]]
答案 0 :(得分:2)
我将解析文件和清除行的问题分开。 假设我们有一个谓词实际上捕获了令牌行。 然后可以应用以下内容:
cleanup([_,_|Data],Clean) :-
remove_last(Data,DataC),
maplist([[_|L],C]>>remove_last(L,C),DataC,Clean).
remove_last(L,C) :-
append(C,[_],L).
捕获令牌行可能是
readAll(InStream,[Line|Lines]) :-
read_a_line(InStream,Line),
readAll(InStream,Lines).
readAll(_InStream,[]).
read_a_line(F,L) :-
read_line_to_string(F,S),
S\=end_of_file,
tokenize_atom(S,L).
为说明SWI-Prolog的一些IO功能,请进行快速测试:
?- data(F),open_any(string(F),read,Stream,Close,[]),readAll(Stream,AllLines),cleanup(AllLines,Clean),Close.
F = "3 4\nA B C D Cd\n1 9 3 7 4 7\n2 6 8 4 0 32\n3 2 4 3 8 42\nAb 140 21 331 41 55",
Stream = <stream>(0x7f37b039e5d0),
Close = close(<stream>(0x7f37b039e5d0)),
AllLines = [[3, 4], ['A', 'B', 'C', 'D', 'Cd'], [1, 9, 3, 7, 4, 7], [2, 6, 8, 4, 0|...], [3, 2, 4, 3|...], ['Ab', 140, 21|...]],
Clean = [[9, 3, 7, 4], [6, 8, 4, 0], [2, 4, 3, 8]]
其中data(F)
实际上将F
绑定到示例文件中的字符串。
没有lambda,我们需要一个“使用一次”的谓词:例如
cleanup([_,_|Data],Clean) :-
remove_last(Data,DataC),
maplist(remove_first_and_last,DataC,Clean).
%maplist([[_|L],C]>>remove_last(L,C),DataC,Clean).
remove_first_and_last([_|L],C) :-
append(C,[_],L).
答案 1 :(得分:1)
不确定我是否理解您的要求。您的输入看起来有点像表格数据,但是也有点像某种文件格式。哪一个?实际定义如何?示例输入的第二行/第二行的重要性是什么?是“空白”列分隔符吗?问题可以继续。
这是我将如何解释您的问题:
nrow
和ncol
。nrow
行,使列表长nrow
:
ncol
列,并将它们放在整数列表中。写下来大约是辛勤工作的99%(不是说很难,但是对于这个问题,所有的“硬度”都在这里)。
现在,您可以继续进行简单的工作:编写代码。 SWI-Prolog提供了一个名为dcg/basics
的小型库。有了它,我想到了这个(急忙):
$ cat ignore.pl
:- use_module(library(dcg/basics)).
read_stuff_from_stream(Stuff, Stream) :-
phrase_from_stream(stuff(Stuff), Stream).
stuff(LL) -->
integer(Nrow), white, whites, integer(Ncol), blanks_to_nl, !,
string_without("\n", _Skip_this_line), "\n",
rows(Nrow, Ncol, LL),
remainder(_Skip_the_rest).
rows(0, _, []) --> !.
rows(Nrow, Ncol, [R|Rows]) --> { succ(Nrow0, Nrow) },
skip_column,
cols(Ncol, R),
string_without("\n", _Skip_rest_of_line), "\n", !,
rows(Nrow0, Ncol, Rows).
skip_column --> nonblanks(_Skip_this_column), white, whites.
cols(0, []) --> !.
cols(Ncol, [C|Cols]) --> { succ(Ncol0, Ncol) },
integer(C), white, whites, !,
cols(Ncol0, Cols).
这不是“干净的”代码,但这是一个起点。它适用于您给出的示例。
3 4
A B C D Cd
1 9 3 7 4 7
2 6 8 4 0 32
3 2 4 3 8 42
Ab 140 21 331 41 55
$ swipl -q
?- [ignore].
true.
?- setup_call_cleanup(open('example.txt', read, In), read_stuff_from_stream(Stuff, In), close(In)).
In = <stream>(0x55f44e03de50),
Stuff = [[9, 3, 7, 4], [6, 8, 4, 0], [2, 4, 3, 8]].
在10个不同方向上仍有改进的空间。如果您听不懂,请询问。
答案 2 :(得分:1)
使用DCG完成代码。
:- use_module(library(dcg/basics), except([eos/2])).
:- set_prolog_flag(double_quotes, codes).
parse(LL) -->
size(Rows,Columns),
header,
rows(Rows,Columns,LL),
footer.
size(Row,Columns) -->
integer(Row),
whites,
integer(Columns),
"\n".
header -->
string_without("\n",_),
"\n".
rows(Rows0,Columns,[Item|Items]) -->
row(Columns,Item),
{ Rows is Rows0 - 1 },
rows(Rows,Columns,Items).
rows(0,_Columns,[]) --> [].
row(Columns,Values) -->
integer(_), % Ignore first value
whites,
values(Columns,Values),
integer(_), % Ignore last value
"\n".
values(Columns0,[Item|Items]) -->
value(Item),
{ Columns is Columns0 - 1 },
values(Columns,Items).
values(0,[]) --> [].
value(Item) -->
integer(Item),
whites.
footer -->
rest_of_line, !.
rest_of_line -->
[_],
rest_of_line.
rest_of_line --> [].
readAll(LL) :-
phrase_from_file(parse(LL),'C:/ll.dat').
测试用例
:- begin_tests(data).
test(1) :-
Input = "\c
3 4\n\c
A B C D Cd\n\c
1 9 3 7 4 7\n\c
2 6 8 4 0 32\n\c
3 2 4 3 8 42\n\c
Ab 140 21 331 41 55\n\c
",
string_codes(Input,Codes),
DCG = parse(LL),
phrase(DCG,Codes,Rest),
assertion( LL == [[9,3,7,4],[6,8,4,0],[2,4,3,8]] ),
assertion( Rest == [] ).
test(2) :-
Input_path = 'C:/ll.dat',
DCG = parse(LL),
phrase_from_file(DCG,Input_path),
assertion( LL == [[9,3,7,4],[6,8,4,0],[2,4,3,8]] ).
:- end_tests(data).
测试用例示例
?- run_tests.
% PL-Unit: data .. done
% All 2 tests passed
true.
示例运行
?- readAll(LL).
LL = [[9, 3, 7, 4], [6, 8, 4, 0], [2, 4, 3, 8]].
无论何时处理列表,都应考虑使用DCG(Primer)。
数据作为字符代码处理,因此用于统一的值也必须是字符代码。人们不容易读取字符代码,因此Prolog可以选择将双引号引起来的项目转换为字符代码列表。在代码"abc"
中,在编译/咨询过程中将其翻译为[97,98,99]
。这是通过Prolog flag完成的。
:- set_prolog_flag(double_quotes, codes).
由于使用DCG非常普遍,因此dcg/basics的module中有一个预定义的公共谓词库。
SWI Prolog中有unit test。
使用单元测试\c可以简化格式化输入数据以便读取的操作。
驱动DCGs的谓词是短语,但是它带来了两个非常常见的变体。
phrase/2通常在未从文件读取数据时使用。在开发和测试DCG时,我也发现它很有用,因为您可以看到整个值流。当数据作为字符代码列表处理且输入为字符串时,通常会发现{/ 2 / {3}}与短语/ 2一起使用。 test(1)
string_codes/2通常在DCG工作并且想要直接从文件中读取数据时使用。
在SWI-Prolog调试器中查看单元测试。
如果要使用SWI-Prolog将调试器与测试用例一起使用,则您可以 用
启动调试器?- gtrace.
true.
然后运行特定的测试
[trace] ?- run_tests(data:1).