Here's how you could do it using meta-predicates,
reified test predicates,
and lambda expressions.
:- use_module(library(lambda)).
First, we define the reified test predicate (>)/3
like this:
>(X,Y,Truth) :- (X > Y -> Truth=true ; Truth=false).
Next, we define three different implementations of busLineLonger/3
(named busLineLonger1/3
, busLineLonger2/3
, and busLineLonger3/3
) in terms of the following meta-predicates: maplist/3
, tfilter/3
, tfiltermap/4
, and tchoose/3
. Of course, in the end we will only need one---but that shouldn't keep us from exploring the various options we have!
Do two separate steps:
1. Select items of concern.
2. Project those items to the data of interest.
busLineLonger1(Ls0,N,IDs) :-
tfilter(\busStops(_,L)^(L>N), Ls0,Ls1),
maplist(\busStops(Id,_)^Id^true, Ls1, IDs).
#2: based on tfiltermap/4
Here, we use exactly the same lambda expressions as before, but we pass
them both to meta-predicate tfiltermap/4
. Doing so can help reduce
save some resources.
busLineLonger2(Ls,N,IDs) :-
tfiltermap(\busStops(_,L)^(L>N), \busStops(Id,_)^Id^true, Ls,IDs).
Here's how tfiltermap/4
can be implemented:
:- meta_predicate tfiltermap(2,2,?,?).
tfiltermap(Filter_2,Map_2,Xs,Ys) :-
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys).
:- meta_predicate list_tfilter_map_list(?,2,2,?).
list_tfilter_map_list([],_,_,[]).
list_tfilter_map_list([X|Xs],Filter_2,Map_2,Ys1) :-
if_(call(Filter_2,X), (call(Map_2,X,Y),Ys1=[Y|Ys0]), Ys1=Ys0),
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys0).
#3: based on tchoose/3
Here we do not use two separate lambda expressions, but a combined one.
busLineLonger3(Ls,N,IDs) :-
tchoose(\busStops(Id,L)^Id^(L>N), Ls,IDs).
Here's how tchoose/3
can be implemented:
:- meta_predicate tchoose(3,?,?).
tchoose(P_3,Xs,Ys) :-
list_tchoose_list(Xs,P_3,Ys).
:- meta_predicate list_tchoose_list(?,3,?).
list_tchoose_list([],_,[]).
list_tchoose_list([X|Xs],P_3,Ys1) :-
if_(call(P_3,X,Y), Ys1=[Y|Ys0], Ys1=Ys0),
list_tchoose_list(Xs,P_3,Ys0).
Let's see them in action!
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger1(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger2(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger3(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
Done!
So... what's the bottom line?
- Many meta-predicates are versatile and can be used in a lot of sitations similar to the one here.
- Implementing these meta-predicates is a one time effort that is amortized quickly.
- Many meta-predicates handle the "recursive part", which enables you to focus on actual work.
- Often, with meta-predicates (as with regular ones), "there's more than one way to do things".
- Depending on the concrete circumstances, using a particular meta-predicate may be better than using another one, and vice versa.
- For this question, I think, implementation #3 (the one using
tchoose/3
) is best.