You can build a tree of reversed digits for the first list, then traverse the tree for each reversed item in the second list. This may be more efficient if the list have many items.
same_endings(L1, L2, Endings):-
build_tree(L1, Tree),
maplist(same_ending(Tree), L2, Endings).
same_ending(Tree, N, N-Len):-
atom_chars(N, Digs),
reverse(Digs, RDigs),
same_ending1(RDigs, Tree, 0, Len).
same_ending1([], _, Len, Len).
same_ending1([Dig|Digs], Tree, Len, Len2):-
(select(Dig-SubTree, Tree, _) ->
(
succ(Len, Len1),
same_ending1(Digs, SubTree, Len1, Len2)
) ;
Len2=Len
).
build_tree(L, Tree):-
foldl(add_tree, L, [], Tree).
add_tree(N, Tree, NTree):-
atom_chars(N, Digs),
reverse(Digs, RDigs),
add_tree1(RDigs, Tree, NTree).
add_tree1([], Tree, Tree).
add_tree1([Dig|Digs], Tree, [Dig-SubTree1|Tree1]):-
(select(Dig-SubTree, Tree, Tree1) -> true; SubTree-Tree1=[]-Tree),
add_tree1(Digs, SubTree, SubTree1).
Test sample:
?- same_endings( [4432,2345,3243] , [3345,3232], Endings).
Endings = [3345-3, 3232-2].
You may modify this code a bit to obtain the actual items which have the same endings.
With a slight modification of the code above you can also list the actual numbers from the first list that have the same (maximum) ending on each item from the second list:
same_endings(L1, L2, Endings):-
build_tree(L1, Tree),
maplist(same_ending(Tree), L2, Endings).
same_ending(Tree, N, N-Len/LItems):-
atom_chars(N, Digs),
reverse(Digs, RDigs),
same_ending1(RDigs, Tree, 0, Len, SubTree),
length(SDigs, Len),
append(SDigs, _, RDigs),
reverse(SDigs, RSDigs),
same_ending2(SubTree, RSDigs, [], LItems).
same_ending1([], SubTree, Len, Len, SubTree).
same_ending1([Dig|Digs], Tree, Len, Len2, SubTree):-
(memberchk(Dig-DigSubTree, Tree) ->
(
succ(Len, Len1),
same_ending1(Digs, DigSubTree, Len1, Len2, SubTree)
) ;
Len2-SubTree=Len-Tree
).
same_ending2([], _, LItems, LItems).
same_ending2([Dig-DigSubTree|SubTree], Digs, MItems, LItems):-
(Dig=endmarker ->
(
number_chars(Item, Digs),
NItems=[Item|MItems]
) ;
same_ending2(DigSubTree, [Dig|Digs], MItems, NItems)
),
same_ending2(SubTree, Digs, NItems, LItems).
build_tree(L, Tree):-
foldl(add_tree, L, [], Tree).
add_tree(N, Tree, NTree):-
number_chars(N, Digs),
reverse(Digs, RDigs),
add_tree1(RDigs, Tree, NTree).
add_tree1([], Tree, [endmarker-[]|Tree]).
add_tree1([Dig|Digs], Tree, [Dig-SubTree1|Tree1]):-
(select(Dig-SubTree, Tree, Tree1) -> true; SubTree-Tree1=[]-Tree),
add_tree1(Digs, SubTree, SubTree1).
Test cases:
?- same_endings( [4432,2345,3243] , [3345,3232], Endings).
Endings = [3345-3/[2345], 3232-2/[4432]].
?- same_endings( [4432,2345,3243,2195345,2345] , [3345,3232,19232,2195345], Endings).
Endings = [3345-3/[2195345, 2345, 2345], 3232-2/[4432], 19232-2/[4432], 2195345-7/[2195345]].
There's no special treatment for the case when an item does not share any ending digit, the code just spits the whole list in that case.
If you want to get all the items with at least 1 matching ending digit, just change the procedures same_endings
and same_endings1
with these ones:
same_endings(L1, L2, Endings):-
build_tree(L1, Tree),
findall(Ending, (member(N, L2), same_ending(Tree, N, Ending)), Endings).
same_ending1([], SubTree, Len, Len, SubTree).
same_ending1([Dig|Digs], Tree, Len, Len2, SubTree):-
(select(Dig-DigSubTree, Tree, RestTree) ->
(
(
Len=0,
RestTree = [],
Len2=Len,
SubTree=RestTree
) ;
(
succ(Len, Len1),
same_ending1(Digs, DigSubTree, Len1, Len2, SubTree)
)
) ;
Len2-SubTree=Len-Tree
).