Forall and a terminology question

Assuming we establish

length(L,Len), between(1, Len, N)

Then we further require:

nth1(N, L, N)

What terminology do we use to discuss L where:

L=[A,A,A] % L can be solved for any N

vs L where:

L=[A,B,C] % L can be solved to satisfy all N simultaneously?

Given the above, (and the latter L = [A,B,C])) what’s the idiomatic way to get L=[1,2,3]?

I can do:

findall(L,nth1(N,L,N), Ls), unified([L|Ls]).

unified([]).
unified([_]).
unified([X, X | T]) :- unified([X | T]).

I can not seem to get the above effect with any of forall,findall,bagof.

Edit:
Important clarifications added here:

Is the “range/3” a builtin ? It seems no doc for it in the SWIPL manual.

Should have been between. Had some other language stuck in my head apparently. Fixed.

Sounds like you want a bit more than numlist/3 provides:

% Like numlist but with variable Upper, starting from 1
range1(Upper, [1|Lst]) :-
    (   nonvar(Upper)
    ->  integer(Upper),
        Upper @>= 1
    ;   var(Upper)
    ),
    range1_(Lst, 1, Upper).

range1_([], Lower, Upper) :-
    (   Lower == Upper
    ->  !
    ;   Lower = Upper
    ).
range1_([Lower1|Lst], Lower, Upper) :-
    Lower1 is Lower + 1,
    range1_(Lst, Lower1, Upper).
?- range1(N, L).
N = 1,
L = [1] ;
N = 2,
L = [1, 2] ;
N = 3,
L = [1, 2, 3] ;

?- range1(3, L).
L = [1, 2, 3].

?- length(L, 3), range1(N, L).
L = [1, 2, 3],
N = 3.
1 Like

@brebs

My example used a list. But I don’t need a list (or numbers) per se.
Let me clarify further.

I asked

What terminology do we use to discuss L where
… it can be solved for any N
… it can be solved to satisfy all N simultaneously

Bold is to highlight the concept I’m interested in.

My other question was:

Given the above, (and the latter L = [A,B,C] )) what’s the idiomatic way to get L=[1,2,3] ?

My question (deliberately) was not “how do I get a list of numbers”.

I’m interested in the abstract notion of unifying a term to satisfy any valid constraint, vs all valid constraints simultaneously, with respect to some constraint.

I could have asked, given the above, AND the following:

name_key(surname).
name_key(forename).
name_key(middlename).

funny_word("Clown").
funny_word("Joker").

funny_name(FirstName, MiddleName, LastName, NameKey) :-
    (NameKey = surname, funny_word(LastName)); 
    (NameKey = forename, funny_word(FirstName));
    (NameKey = middlename, funny_word(MiddleName)).

What is the idiomatic “thing” that does:

solved_all(..., funny_name(FirstName, MiddleName, LastName, NameKey), ...)
solved_all(..., nth1(N,L,N), ...)

and gives me

FirstName="Clown" MiddleName="Clown" LastName="Clown"; 
FirstName="Clown" MiddleName="Clown" LastName="Joker"; 
... etc

and

[1,2,3].

This is not quite clear.
Prolog finds variable substitutions that satisfy the goal you give it. If you have multiple goals and you want the substitution to satisfy all them simultaneously, use conjunction (,/2). If you want it to satisfy at least one goal, use disjunction (;/2 or multiple clauses).
Does that make sense?

Well, I can find what I’m looking for, in Prolog, whatever it’s called. I’m asking for help in the terminology that clarifies this. So I’m not surprised it isn’t yet clear.

I gave the example with the list that returns [1,2,3].

The funny name example doesn’t quire work out as stated. My bad. The following does find “Clown” “Clown” to be the the funny name regardless of which name_key is used:

name_key(surname).
name_key(forename).

funny_word("Clown").

funny_name(FirstName, LastName, NameKey) :-
    (NameKey = surname, funny_word(LastName)); 
    (NameKey = forename, funny_word(FirstName)).
    
name_example(FirstName, LastName) :- 
    findall((First, Last), (name_key(NameKey), funny_name(First, Last, NameKey)) , Ls),
    unified([(FirstName, LastName)|Ls]). 

The way I read your definition of funny_name/3, it says that “a full name is funny if either the first name is funny or the last name is funny”.
FWIW I’d define it as:

funny_name(FirstName, _) :- funny_word(FirstName).
funny_name(_, LastName) :- funny_word(LastName).

I understand that you’re looking for FirstName = "Clown", LastName = "Clown". What I don’t understand is why you can’t use a conjuction to obtain it, i.e.:

funny_name(FirstName, LastName) :- 
    funny_word(FirstName),
    funny_word(LastName).

Then:

?- funny_name(FirstName, LastName).
FirstName = LastName, LastName = "Clown".

It’s putting the cart before the horse.

I’m not trying to build a list or find a name. These are examples.
Maybe putting it another way will help.
I want to know, if given A1 \/ A2 \/ ... An ... ==> B is there a way of finding (A1 ... An) such that A1 /\ A2 /\ ... /\ An?

It seems to me that you asked for foreach/2:

?- length(L,Len), foreach(between(1,Len,N),nth1(N,L,N)).
L = [],
Len = 0 ;
L = [1],
Len = 1 ;
L = [1, 2],
Len = 2 ;
L = [1, 2, 3],
Len = 3 ;
L = [1, 2, 3, 4],
Len = 4 ;
L = [1, 2, 3, 4, 5],
Len = 5 ;
L = [1, 2, 3, 4, 5, 6],
Len = 6 ;
L = [1, 2, 3, 4, 5, 6, 7],
Len = 7 

?- L=[A,A,A], length(L,Len), foreach(between(1,Len,N),nth1(N,L,N)).
false.

?- L=[A,B,C], length(L,Len), foreach(between(1,Len,N),nth1(N,L,N)).
L = [1, 2, 3],
A = 1,
B = 2,
C = Len, Len = 3.

For the name example - in the real world this would of course be much simpler:

funny_word("Clown").
funny_word("Joker").

name_example(FirstName, LastName) :- 
    funny_word(FirstName),
    funny_word(LastName).

But for the sake of the question I think you look for something like this:


name_key(surname).
name_key(forename).

has_name(Key,Value,Names) :-
    member(Key-Value, Names).

names(Names) :-
    foreach(name_key(Key),
	    (
		funny_word(Name),		
		has_name(Key,Name,Names)	    
	    )).

Example:

?- length(N,_), names(N).
N = [surname-"Clown", forename-"Clown"] ;
N = [forename-"Clown", surname-"Clown"] ;
N = [surname-"Joker", forename-"Joker"] ;
N = [forename-"Joker", surname-"Joker"] 

(Note that you need the has_name/3 predicate or something similar, because just plugging in member(Key-Name,Names) doesn’t work: Foreach bug?

1 Like