如何按 2 个条件过滤列表?
How can i filter a list by 2 conditions?
我是 prolog 的新手,我找不到 按 2 个条件过滤列表的解决方案,将结果保存到 2 个变量中,然后根据它们评估答案。
在我的例子中,如果列表包含更多大于 0 的数字,则数字小于 0。
例如我得到一个包含元素的列表:
checklist([-2, 3.3, 12, -10.1, 14, -11, 123]) # true
checklist([-2.1, -3, -4, 14, 16.7]) # false
checklist([11.5, 2.5, -34.1, -1]) # false
我会在python中写这样的东西:
bigger_count = 0
lesser_count = 0
for num in list:
if num > 0:
bigger_count += 1
elif num < 0:
lesser_count += 1
print(bigger_count > lesser_count)
尤其是我不明白如何同时处理 2 个变量。
最简单的方法是使用带有所需额外状态的工作谓词。
checklist( Ns, P, N ) :- checklist(Ns, 0, 0, P, N )
checklist( [] , P , N , P , N ) .
checklist( [X|Xs] , P0 , N0 , P , N ) :- X > 0, P1 is P0+1, checklist(Xs,P1,N0,P,N).
checklist( [X|Xs] , P0 , N0 , P , N ) :- X < 0, B1 is N0+1, checklist(Xs,P0,N1,P,N).
另一种可能的解决方案是:
check(List) :-
check(List, Balance),
Balance > 0.
check([], 0).
check([X|Xs], B) :-
check(Xs, B0),
( X > 0 -> B is B0 + 1
; X < 0 -> B is B0 - 1
; B is B0 ).
示例:
?- check([-2, 3.3, 12, -10.1, 14, -11, 123]).
true.
?- check([-2.1, -3, -4, 14, 16.7]).
false.
?- check([11.5, 2.5, -34.1, -1]).
false.
?- check([1,-1]).
false.
如果您将列表中的所有值相加,您将得到一个正数或负数的结果,这取决于数字的大小,而不是数字的数量。有一个数学函数 sign
或 signum
将所有负值变成-1,所有正值变成+1,然后你可以把它们加起来,如果有更多的正输入,答案是肯定的,如果有更多的负输入,答案是否定的。否则他们会抵消,总和将为0。
如果您 maplist
sign
输入:
?- maplist(sign, [-2, 3.3, 12, -10.1, 14, -11, 123], X).
X = [-1, 1.0, 1, -1.0, 1, -1, 1]
然后:
?- sum_list(X, S).
S = 1.0
阳性结果,因此在这种情况下阳性多于阴性。
您可以 partition/4
将输入列表分为较小值和较大值:
?- partition(>(0), [-2, 3.3, 12, -10.1, 14, -11, 123], Lesser, Greater).
Lesser = [-2, -10.1, -11],
Greater = [3.3, 12, 14, 123]
然后用length/2
比较结果,看哪个长
(如果任何输入是 0
,这可能没有用,它们不在您的示例中。我认为这将使用更多内存来制作列表的副本。)。
https://www.swi-prolog.org/pldoc/doc_for?object=include/3
https://www.swi-prolog.org/pldoc/doc_for?object=convlist/3
如果你有 2 个条件,你需要遍历列表两次,但是可以处理两个不同谓词的代码的复杂性是否值得这样的麻烦?
无论如何,如果您是从头开始编写,您可以使用 DCG 来简化您的生活:
filter(_Pred, []) --> [].
filter(Pred, [X|Xs]) -->
( { call(Pred, X) }
-> [X]
; []
),
filter(Pred, Xs).
filter(Pred, List, Result) :-
phrase(filter(Pred, List), Result).
even(X) :- 0 is X mod 2.
?- filter(even, [1,2,3,4,5], X).
X = [2, 4]
我是 prolog 的新手,我找不到 按 2 个条件过滤列表的解决方案,将结果保存到 2 个变量中,然后根据它们评估答案。
在我的例子中,如果列表包含更多大于 0 的数字,则数字小于 0。
例如我得到一个包含元素的列表:
checklist([-2, 3.3, 12, -10.1, 14, -11, 123]) # true
checklist([-2.1, -3, -4, 14, 16.7]) # false
checklist([11.5, 2.5, -34.1, -1]) # false
我会在python中写这样的东西:
bigger_count = 0
lesser_count = 0
for num in list:
if num > 0:
bigger_count += 1
elif num < 0:
lesser_count += 1
print(bigger_count > lesser_count)
尤其是我不明白如何同时处理 2 个变量。
最简单的方法是使用带有所需额外状态的工作谓词。
checklist( Ns, P, N ) :- checklist(Ns, 0, 0, P, N )
checklist( [] , P , N , P , N ) .
checklist( [X|Xs] , P0 , N0 , P , N ) :- X > 0, P1 is P0+1, checklist(Xs,P1,N0,P,N).
checklist( [X|Xs] , P0 , N0 , P , N ) :- X < 0, B1 is N0+1, checklist(Xs,P0,N1,P,N).
另一种可能的解决方案是:
check(List) :-
check(List, Balance),
Balance > 0.
check([], 0).
check([X|Xs], B) :-
check(Xs, B0),
( X > 0 -> B is B0 + 1
; X < 0 -> B is B0 - 1
; B is B0 ).
示例:
?- check([-2, 3.3, 12, -10.1, 14, -11, 123]).
true.
?- check([-2.1, -3, -4, 14, 16.7]).
false.
?- check([11.5, 2.5, -34.1, -1]).
false.
?- check([1,-1]).
false.
如果您将列表中的所有值相加,您将得到一个正数或负数的结果,这取决于数字的大小,而不是数字的数量。有一个数学函数 sign
或 signum
将所有负值变成-1,所有正值变成+1,然后你可以把它们加起来,如果有更多的正输入,答案是肯定的,如果有更多的负输入,答案是否定的。否则他们会抵消,总和将为0。
如果您 maplist
sign
输入:
?- maplist(sign, [-2, 3.3, 12, -10.1, 14, -11, 123], X).
X = [-1, 1.0, 1, -1.0, 1, -1, 1]
然后:
?- sum_list(X, S).
S = 1.0
阳性结果,因此在这种情况下阳性多于阴性。
您可以 partition/4
将输入列表分为较小值和较大值:
?- partition(>(0), [-2, 3.3, 12, -10.1, 14, -11, 123], Lesser, Greater).
Lesser = [-2, -10.1, -11],
Greater = [3.3, 12, 14, 123]
然后用length/2
比较结果,看哪个长
(如果任何输入是 0
,这可能没有用,它们不在您的示例中。我认为这将使用更多内存来制作列表的副本。)。
https://www.swi-prolog.org/pldoc/doc_for?object=include/3
https://www.swi-prolog.org/pldoc/doc_for?object=convlist/3
如果你有 2 个条件,你需要遍历列表两次,但是可以处理两个不同谓词的代码的复杂性是否值得这样的麻烦?
无论如何,如果您是从头开始编写,您可以使用 DCG 来简化您的生活:
filter(_Pred, []) --> [].
filter(Pred, [X|Xs]) -->
( { call(Pred, X) }
-> [X]
; []
),
filter(Pred, Xs).
filter(Pred, List, Result) :-
phrase(filter(Pred, List), Result).
even(X) :- 0 is X mod 2.
?- filter(even, [1,2,3,4,5], X).
X = [2, 4]