4

I would like to split a list of words separated through integers into a list of lists.

Sample query and expected result:

?- separatewords([h,e,l,l,o,1,o,v,e,r,3,t,h,e,r,e], X).
X = [[h,e,l,l,o],[o,v,e,r],[t,h,e,r,e]].

The following things I already achieved: Splitting the list into one list before the first integer and one after the first integer:

Sample query with result:

?- take1word([h,e,l,l,o,1,o,v,e,r,3,t,h,e,r,e], X, Y).
X = [h,e,l,l,o], Y = [o,v,e,r,3,t,h,e,r,e].                 % OK

My code:

 take1word([H|T],[],T) :-
    integer(H).
 take1word([H|T],[H|Hs],Y) :-
    (  float(H), take1word(T,Hs,Y)
    ;  atom(H), take1word(T,Hs,Y)
    ).

For separating words my code is the following:

 separatewords([],[]).
 separatewords([H|T],L) :-  separatewords(T,[take1word([H|T],)|L]).

It only give me false as a result, but I don't know, what I am doing wrong.

repeat
  • 18,496
  • 4
  • 54
  • 166
ec-m
  • 779
  • 1
  • 5
  • 15

2 Answers2

2

You have an issue with take1word/3: it will take a word if there is an integer in the list, but it will not take the last word. You need to add another base clause to it:

take1word([], [], []).
take1word([H|T],[],T) :- integer(H).
take1word([H|T],[H|Hs],Y) :- float(H), take1word(T,Hs,Y); atom(H), take1word(T,Hs,Y).

Now your separatewords/2 will work:

separatewords([],[]).
separatewords(L, [W|T]) :- take1word(L,W,R), separatewords(R,T).

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
2

Here is how you could proceed in a logically-pure fashion.

Use splitlistIf/3 in tandem with the reified type test predicate integer_t/2!

integer_t(X,Truth) :- integer(X), !, Truth = true.
integer_t(X,Truth) :- nonvar(X),  !, Truth = false.
integer_t(X,true)  :- freeze(X,  integer(X)).
integer_t(X,false) :- freeze(X,\+integer(X)).

Let's see them both in action!

?- Xs=[ h,e,l,l,o,1,o,v,e,r,3,t,h,e,r,e ], splitlistIf(integer_truth,Xs,Yss).
Xs  = [ h,e,l,l,o,1,o,v,e,r,3,t,h,e,r,e ],
Yss = [[h,e,l,l,o],[o,v,e,r],[t,h,e,r,e]]. % succeeds deterministically

As the implementation is monotone, it can be safely used when we work with non-ground terms.

Consider the following two logically equivalent queries: Procedurally, they differ regarding the instantiation of items in Xs at the time the goal splitListIf(integer_t,Xs,Yss) is proven.

?- Xs = [_,_,_,_,_], Xs = [a,b,0,b,a], splitlistIf(integer_t,Xs,Yss).
Xs = [a,b,0,b,a], Yss = [[a,b],[b,a]].     % succeeds deterministically

?- Xs = [_,_,_,_,_], splitlistIf(integer_t,Xs,Yss), Xs = [a,b,0,b,a].
  Xs = [a,b,0,b,a], Yss = [[a,b],[b,a]]    % succeeds leaving behind choicepoint
; false.

The implementation is smart enough to leave behind choicepoint(s) only when necessary.

Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
  • [Related](http://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog) to the name finding. – false May 17 '15 at 21:22
  • CamelCase is not very Prolog-ish. `int` is not a type. `integer_truth/2`. Referring to state of instantiation (`Nonvar`) is not necessary: An implementation without constraints would quite correctly produce an instantiation error in stead. – false May 18 '15 at 15:33
  • The ground case is the thing to look at, first. What else apart from integers can be even? Natural numbers, but they are not an explicit type. And as domain the are `not_less_than_zero`. So `even_truth` looks good enough to me. On the other hand, most of your definitions accept expressions like `0+0` – false May 18 '15 at 16:14
  • Good point! What could / should I do about that? Type tests + exceptions? Or rather use the `clpfd_monotonic` flag in combination with `(?)/1` ? – repeat May 18 '15 at 16:24