Related
I need to find the first element on the list which satisfies a user provided goal. I mean something like maplist/2 but which succeeds when the goal can be applied to at least one element. include/3 would be an option but I'm after a more optimal solution that can stop after finding the first element.
I think the interface could look like:
first(:Goal, List, First)
and it should be semidet in the SWI-Prolog sense.
It's fairly easy to code it but I'd prefer an existing rule. Is there a relevant rule in the "standard" libraries; I'm using SWI-Prolog.
Cheers,
Jacek
I don't think there's a standard predicate that does this. But as you say, it would be very easy to code. I would probably write something like this, which has a pattern like the member/2 predicate:
includes_item(Goal, [X|_], X) :-
call(Goal, X).
includes_item(Goal, [_|T], X) :-
includes_item(Goal, T, X).
As #false indicates in the comments, this in fact can be more clearly written using member/2:
includes_item(Goal, List, Item) :-
member(Item, List),
call(Goal, Item).
includes_item(:Goal, List, Item) succeeds for each Item in List that satisfies :Goal. For example:
3 ?- includes_item('>'(3), [1,2,3,-2, 4, 5], X).
X = 1 ;
X = 2 ;
X = -2 ;
false.
You can then use once/1 to obtain only the first item without a choice point:
first(Goal, List, Item) :-
once(includes_item(Goal, List, Item)).
And now you get:
4 ?- first('>'(3), [1,2,3,-2, 4, 5], X).
X = 1.
5 ?-
I'm trying to make this:
times([x, x], [1, 5, 9, 8], Result).
The second list is replicated by the number of elements in the first one.
The result is : [1, 5, 9, 8, 1, 5, 9, 8]
I've tried this but is not working properly:
times( [ ], _, [ ] ) :- !.
times( [ _ | List1 ], List2, Result ) :- append( [], List2, Result ),
times( List1, List2, Result ).
Thanks in advance!
Firstly, you do not need your cut in the base case. It unnecessarily prunes the solution search:
replicate([], _, []). % The empty list is the result of replicating any list by []
Then your recursive rule, replicate([_|List1], List2, Result) seems to have a reasonable format/head. But you have logic issues in the body. If you were to describe the logic, it would not make sense. In particular, you're using Result in two different places for two different meanings, and that will result in unexpected failure. The predicate just needs to be thought out logically for what it means:
Result is the replication of List2 by [_|List1] if SubResult is the replication of List2 by List1 and Result is the result of appending SubResult to List2.
Note the use of a "subresult" (SubResult) here which is distinguished from the main result (Result). It's important that these be different variables.
If you write that as Prolog, you get:
replicate([_|List1], List2, Result) :-
replicate(List1, List2, SubResult),
append(List2, SubResult, Result).
I didn't test this, but this should basically do it. You should try it and resolve any residual issues yourself as part of your Prolog learning process. I also did not consider whether there's a more effective approach to the overall problem but am just resolving your issues with your current approach.
Another would be to use maplist/2 and append/2. You can use maplist/3 to get a list of lists, then use append/2 to get your result:
replicate(List1, List2, Result) :-
length(List1, Len),
length(R1, Len),
maplist(=(List2), R1),
append(R1, Result).
With a little more thought, this can be solved using simple recursive list handling. In this case, you would recursively unify the head of the result with the head of each element in the second list, then continue this process for each element in the first list.
replicate(Rep, List, Res) :-
replicate(Rep, List, List, Res).
replicate([], _, _, []).
replicate([R|Rs], List, [X|Xs], [X|Res]) :-
replicate([R|Rs], List, Xs, Res).
replicate([R|Rs], List, [], Res) :-
replicate(Rs, List, List, Res).
We can literally substitute the second list for each element in the first, then call append/2 (SWI Prolog has one):
nreplicate( Xs, Ys, Result) :-
maplist( subst(Ys), Xs, Zs),
append( Zs, Result ).
subst(Ys, _, Ys).
I'm trying to rotate a list in prolog recursively but it does not work as expected.
Code:
rot([],[]).
rot([H|T1], [T2|H]):-rot(T1,T2).
Output:
?- rot([1,2,3], V).
V = [[[[]|3]|2]|1]
Expected output:
?- rot([1,2,3], V).
V = [3,2,1]
Could anyone explain me why my code does not work?
Since Prolog is untyped, you can indeed write something like [List|Element], but if you want a list to make sense, the only way you can construct lists is like [Element|List]. So [T2|H] does not make sense at all. In that case T2 should be an element, and H a list (or the empty list []).
You will need to define two predicates:
the main predicate (rot/2) that simply pops the head from the given list and calls the recursive predicate; and
the recursive predicate (here rot/3) that simply passes all elements of the given list and emits the original head as tail element.
Together this works like:
%main predicate rot/2
rot([],[]).
rot([H|T1],T2) :-
rot(T1,H,T2).
%recursive predicate rot/3
rot([],Last,[Last]).
rot([H|T1],Last,[H|T2]) :-
rot(T1,Last,T2).
Your code doesn't work because in an expression like [H|T], H is an element of the list and T is the tail of the list--also a list. For instance:
?- [H|T] = [1,2,3].
H = 1,
T = [2, 3].
So what happens when you switch that around?
?- [H|T] = [1,2,3], X = [T|H].
H = 1,
T = [2, 3],
X = [[2, 3]|1].
See the problem?
The problem is with the second clause. What I do is to rotate the tail of the first list inside L1 and then call append with L1 and the first element and assign the result to L (the second argument)
my-append([], L, L).
my-append([H|T], L, [H|R]) :- my-append(T, L, R).
rot([], []).
rot([H|T], L) :- rot(T, L1), my-append(L1, H, L).
I need to remove only one occurrence in the list. Actually doesn't matter if it's first or last. One match needs to be removed.
I'm having trouble understanding why the following doesn't work as intended.
deleteOne(_,[],[]).
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
deleteOne(Term, [Head|Tail], [Head|TailResult]) :-
deleteOne(Term, Tail, TailResult), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 5, 2, 3, 1].
It works when I replace term with an empty String or some random String.
deleteOne(Term, [Term|Tail], Result) :-
deleteOne("", Tail, Result), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 2, 3, 1].
But I don't think this is the best solution for many reasons. Not for my current problem, but for example longer lists. Or if a list contains empty String - don't know if this is possible in Prolog.
Why wont the first example work? And what other solutions are there?
Your first one doesn't work because this doesn't make much sense:
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
That means the result of the next one has to have the current result as its head.
An better solution would be this:
delete_one(_, [], []).
delete_one(Term, [Term|Tail], Tail).
delete_one(Term, [Head|Tail], [Head|Result]) :-
delete_one(Term, Tail, Result).
If you want it to be determinative, add a cut on the second clause. As is, it can do this:
?- delete_one(2, [1, 2, 3, 1, 2, 3], X).
X = [1,3,1,2,3] ? ;
X = [1,2,3,1,3] ? ;
X = [1,2,3,1,2,3] ? ;
no
To delete only the first occurrence of an item X from a list L.Here I used cut operation.
delete(X,[X|T],T):-!.
delete(X,[Y|T],[Y|T1]):-delete(X,T,T1).
I'm working on a predicate only_atoms/2(List+, Result-) that I'd like to filter non atoms.
For example :
only_atoms([1, 2, X, h(Y), 'aba'], Result).
should return
Result = [1, 2, 'aba'].
I do not care about the order.
Here is the piece of code I came up with :
only_atoms([], []) :- !.
only_atoms([Head | Tail], [Head | Result]) :-
atom(Head),
!,
only_atoms(Tail, Result).
only_atoms([_ | Tail], Result) :-
only_atoms(Tail, Result).
I thought this was the right kind of reasoning to handle such a problem but seem to be wrong since it yields me [](edit : it actually yields [aba], see precisions below, my bad !) no matter what. I'd appreciate some help !
A first hint: for 1 and 2, atom returns false.
By the way, I was looking for the filter predicate, in the standard library it happens to be called include, it's usually better if you use what the language already provides ;-)
?- include(atom, [1, 2, X, h(Y), 'aba'], Result).
Result = [aba].
or if you wanted just to filter out variables:
?- exclude(var, [1, 2, X, h(Y), 'aba'], Result).
Result = [1, 2, h(Y), aba].
Another by the way, one curious difference between your only_atoms and using include(atom, ...) is that yours will unify variables in the first list with atoms in the second list, whereas the include won't.
?- only_atoms([1, x, 2, Y], [x, y]).
Y = y.
?- include(atom, [1, x, 2, Y], [x, y]).
false.
Those subtleties of Prolog always astonish me (I guess that's because I didn't pay enough attention at the university xD).
You probably need to force the Head not to be an atom on the alternate clause, otherwise it is an option for atoms as well.
This returns Result = ['aba'] for me.
only_atoms([], []).
only_atoms([Head | Tail], [Head | Result]) :- atom(Head), !, only_atoms(Tail, Result).
only_atoms([Head | Tail], Result) :- \+atom(Head), !, only_atoms(Tail, Result).
Alternatively, you could try using findall/3.
atoms_list(List, Result) :- findall(Item, (member(Item, List), atom(Item)), Result).