从prolog中的列表列表中获取max元素

时间:2014-04-28 07:21:31

标签: prolog dcg

我是prolog的新手,作为一项学校作业,我需要在Prolog中写一个DCG然后写一个perdicate来确定一些东西。输入是一个String,如下所示:

Date,Exchange,Tape A,Tape A %,Tape B,Tape B %,Tape C,Tape C %,Total,Total %,Tape A Moving,Tape A % Moving,Tape B Moving,Tape B % Moving,Tape C Moving,Tape C % Moving,Total Moving, Total % Moving
02/18/2014,NASDAQ,473515614,13.80%,119199766,11.40%,520888794,27.50%,1113604174,17.50%,511929756,14.30%,145320601,12.30%,532617511,26.30%,1189867868,17.50%
02/18/2014,NYSE,721710805,21.10%,0,0.00%,0,0.00%,721710805,11.40%,707472219,19.70%,0,0.00%,0,0.00%,707472219,10.40%

第一行是标题。

我写了DCG解析器:

row([[Date,Stock,A,PerA,B,PerB,C,PerC,Total,PerTotal,A2,A3,B2,B3,C2,C3,T2,T3,'\n']|Rows]) -->
    date(Date), separe,stock(Stock),
    integer(A),separe,number(PerA),"%",separe,
    integer(B),separe,number(PerB),"%",separe,
    integer(C),separe,number(PerC),"%",separe,
    integer(Total),separe,number(PerTotal),"%",separe,
    integer(A2),separe,number(A3),"%",separe,
    integer(B2),separe,number(B3),"%",separe,
    integer(C2),separe,number(C3),"%",separe,
    integer(T2),separe,number(T3),"%",separe,
    row(Rows).

row(Rows) -->
    string(_),"\n" ,
    row(Rows).

row([]) --> [].

date(D/M/Y) -->
    integer(D), "/", integer(M), "/", integer(Y).

stock([F|Ks]) -->
    [F], {F \= 0' }, string(Ks).

separe --> white, whites.

它工作正常但是在解析输入后我需要找到具有最大总数的元素。 我做了类似的事情:

findMaxTotal([],X,Ret).
findMaxTotal([R1|Rest],X,Ret):-
    nth1(9,R1,X),
    findMaxTotal(Rest,Y,max(Ret,X)).

但它不起作用,我无法在SWI prolog中调试它以理解原因。

有没有人有关于如何在列表列表中找到最大值的任何提示?

谢谢!

2 个答案:

答案 0 :(得分:1)

我认为您可以使用findall/3max_list/2来完成此操作。见下面的例子。 Row包含第二个元素是数字的列表列表。使用findall/3将数字提取到单独的列表中,然后使用max_list/2找到最大数字。

?- Row = [['a', 5, test(a)], ['b', 10, test(b)], ['c', -1, test(c)]], 
findall(Value, (member(SubList, Row), nth1(2, SubList, Value)), ListValues),
max_list(ListValues, Max).

Row = [[a, 5, test(a)], [b, 10, test(b)], [c, -1, test(c)]],
ListValues = [5, 10, -1],
Max = 10.

这里我将它括在一个很好的谓词中:

findMaxInSubList(Data, ElemIndex, Max) :-
    findall(Value, (member(SubList, Data), nth1(ElemIndex, SubList, Value)), ListValues),
    max_list(ListValues, Max).

示例输入和输出:

?- sample_data(Row), findMaxInSubList(Row, 2, Max).
Row = [[a, 5, test(a)], [b, 10, test(b)], [c, -1, test(c)]],
Max = 10.

答案 1 :(得分:1)

原始尝试中的一个问题是此查询:

findMaxTotal(Rest,Y,max(Ret,X)).

Prolog在处理查询参数时不评估算术表达式。 max/2必须出现在is/2的第二个参数或要评估的算术比较器中。

原始代码的修复,这是一个经典的Prolog列表处理示例,将是:

findMaxTotal([R|Rest], Ret) :-
    findMaxTotal(T, R, Ret).

findMaxTotal([], X, X).
findMaxTotal([R|Rest], X, Ret):-
    nth1(9, R, X1),
    Y is max(X, X1),
    findMaxTotal(Rest, Y, Ret).

这实际上也可以按如下方式编写:

findMaxTotal([R|Rest], Ret) :-
    findMaxTotal(T, R, Ret).

findMaxTotal([], X, Ret) :-
    Ret is X.   % Evaluate X

findMaxTotal([R|Rest], X, Ret):-
    nth1(9, R, X1),
    findMaxTotal(Rest, max(X, X1), Ret).

此处,在基本案例findMaxTotal([], X, Ret)的正文中,Ret最终会根据X来评估max(max(max(max(3, 5), 2), 7), 6)max。 (原始列表的每个元素都会有maplist。)

这样的列表处理模式非常适合max_list/2谓词和谓词find_max_total(Data, ElemIndex, Max) :- maplist(nth1(ElemIndex), Data, Values), max_list(Values, Max). 的使用,这两种谓词都可以在SWI和GNU Prolog(以及其他人)中使用:

find_max_total(Data, 9, Max).

并且,在您的情况下,使用查询:

find_max_record([Datum|Data], ElemIndex, MaxDatum) :-
    nth1(ElemIndex, Datum, Value),
    find_max_record(Data, Datum, Value, ElemIndex, MaxDatum).

find_max_record([], MaxDatum, _, _, MaxDatum).
find_max_record([Datum|Data], MaxDatumSoFar, MaxValueSoFar, ElemIndex, MaxDatum) :-
    nth1(ElemIndex, Datum, Value),
    (   Value > MaxValueSoFar
    ->  NewMaxDatum = Datum,
        NewMaxValue = Value
    ;   NewMaxDatum = MaxDatumSoFar,
        NewMaxValue = MaxValueSoFar
    ),
    find_max_record(Data, NewMaxDatum, NewMaxValue, ElemIndex, MaxDatum).

<强>附录

如果您需要获取在给定列中具有最大值的整个记录​​,则第一个递归方法可能是最直接的方法。但是,我们不仅仅是携带价值,而且还带有整个记录。此外,我们需要一个明确的最大检查,以便我们知道最大值来自哪条记录:

maplist

作为上述['a', 5, test(a)]方法的类比,但效率不高,将使用我们想要比较的元素“索引”数据数据的每一行。诸如5-['a', 5, test(a)]之类的行变为maplist/3,然后我们使用max_list_ex/2来转换整个数据列表。然后我们可以将更通用的max_list/2 we can write using the more general(一个% Extended max_list/2 which does general term comparison max_list_ex([H|T], M) :- max_list_ex(T, H, M). max_list_ex([H|T], A, M) :- H @> A, max_list_ex(T, H, M). max_list_ex([H|T], A, M) :- H @=< A, max_list_ex(T, A, M). max_list_ex([], M, M). % Maps an index and a Datum (a list) to Elem-Datum index_elem(ElemIndex, Datum, Elem-Datum) :- nth1(ElemIndex, Datum, Elem). find_max_record(Data, ElemIndex, MaxRec) :- maplist(index_elem(ElemIndex), Data, IndexedData), max_list_ex(IndexedData, _-MaxRec). @`术语比较器)应用于该索引的数据列表并获得结果。:

{{1}}