【问题标题】:Getting max element from a list of lists in prolog从序言中的列表列表中获取最大元素
【发布时间】:2014-04-28 07:21:31
【问题描述】:

我是 Prolog 的新手,作为一项学校作业,我需要在 Prolog 中编写一个 DCG,然后编写一个谓词来确定某些内容。输入是一个字符串,如下所示:

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.

它工作正常,但在我解析输入后,我需要找到具有最大 Total 的元素。 我虽然想做这样的事情:

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

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

有人对如何在列表列表中找到最大值有任何提示吗?

谢谢!

【问题讨论】:

  • 在 SWI-Prolog 中,您可以简单地使用 max_list(List, Max)

标签: prolog dcg


【解决方案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 的使用,这两种模式都在SWI 和GNU Prolog(可能还有其他)中可用:

find_max_total(Data, ElemIndex, Max) :-
    maplist(nth1(ElemIndex), Data, Values),
    max_list(Values, Max).

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

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/2max_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).

【讨论】:

  • 谢谢!有效!如果我可以添加另一个问题 - 我想返回项目本身,而不仅仅是最大值,但是当我尝试使用 nth1 (通过找到最大值的索引然后在列表列表中执行 nth1 )时,我得到了一个错误。 nth1 不允许不是数字的元素吗?还有其他方法吗?谢谢
  • @o2887 我将其添加到答案中
  • @o2887 顺便说一句,是的,nth1 适用于任何类型的元素。
【解决方案2】:

我认为您可以使用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.

【讨论】:

    猜你喜欢
    • 2018-02-12
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 2011-05-12
    • 2014-12-16
    • 1970-01-01
    • 2022-07-09
    • 1970-01-01
    相关资源
    最近更新 更多