My answer is written with SWI-Prolog in mind, and detailed information on any built-in predicates I mention can be had by searching the documentation. However, I think everything I say here holds for other prominent Prolog implementations. Please correct me if I'm wrong.
In Prolog, we work with predicates rather than functions, (or, we only use functions that return truth-values). I will use the following predicate to refer to your example list in my Prolog queries:
ex([ [005,"Chester",100],[001,"Bob",99],[002,"Andy",77] ]).
For writing formatted text to the standard output stream, we'll use the the predicate format/2
. It's signature is format(+Format, :Arguments)
, where Format
is an atom including special sequences to facilitate interpolation. Escape sequences begin with the tilde, ~
, and the full menu can be found in the SWI-Prolo documentation on format/2
. Arguments
is a list of prolog terms used to replace the sequences in Format
. In this example, we'll only need ~w
, which writes an argument and ~n
, which writes a newline. As a general example: E.g.,
?- format('Write ~w, then ~w, followed by ~w~n~n', ['this', 'that', 'the other']).
Write this, then that, followed by the other
true.
You're representing a person as a list of the form [ID, Name, Grade
], so we only need to write a predicate that will let us add the N
in "No.N", and then we can use the person-list for the rest of the arguments to format/2
:
display_person(N, Person) :-
format('No.~w: ID="~w", Name="~w", Grade=~w~n', [N|Person]).
In the second argument of format/2
, N
is prefixed as the head of the argument list. We can test the predicate like so:
?- ex([P|Ps]), display_person(1, P).
No.1: ID="5", Name="Chester", Grade=100
P = [5, "Chester", 100],
Ps = [[1, "Bob", 99], [2, "Andy", 77]].
That takes care of displaying each individual person-list. Now we just need to iterate through your list of people and display each member. We'll need a two-place predicate in order to keep track of the increasing value of N
for the 'No.N' portion of the display, but we only need a one-place predicate to pass the list to the display predicate. This requirement leads us to use the common pattern of using a front-end predicate supported by an auxiliary predicate:
display_people(People) :-
display_people(1, People). %% Initializes the call to display_people/2
with the starting value 1
display_people(_, []).
display_people(N, [Person|People]) :-
display_person(N, Person),
M is N+1,
display_people(M, People).
Running a test using the list from ex/1
:
?- ex(Ps), display_people(Ps).
No.1: ID="5", Name="Chester", Grade=100
No.2: ID="1", Name="Bob", Grade=99
No.3: ID="2", Name="Andy", Grade=77
Ps = [[5, "Chester", 100], [1, "Bob", 99], [2, "Andy", 77]] ;
false.
However, the pattern p([X|Xs]) :- q(X), p(Xs)
is just what the maplist/n
predicate is for, so we can save ourselves a bit of typing, and arguably make the declarative meaning of the program clearer by writing the following:
display_people(People) :-
length(People, NumPeople),
numlist(1, NumPeople, Nums),
maplist(display_person, Nums, People).
numlist(Low, High, Nums)
returns a list, Nums
, with values from Low
to High
. In the preceding definition, maplist(display_person, Nums, People)
will essentially call display_person(N, P)
for each pair of N
in Nums
and P
in People
.