AI Programming (in C)
Aim of this lecture,
introduce
list data structure, use of list to represent object in AI,
programming
with recursion to manipulate list.
List
A list is a collection of elements. An element can be an atom
or
another list. An atom is either "name" or "number" (other kind
of
atom will be introduced later). A "name" is a string. A
number is an integer. A list can be printed on a screen using
parentheses. This is an example of a list containing two
names:
"A" and "B":
(
"A" "B" )
Here is many examples of list:
(1
2 3 4)
( (1 2) (3 4))
( ("A" 10) ("B" 20) ("C"
30))
The basic building block of list is a two-cell integer (called
"cell"). The first cell is called "head". The second
cell
is called "tail". An atom has its head storing the type: name
or
number, and its tail storing the value. For a name, the value
is
an index into a string-table. For an integer, the value is the
integer. A structure of a list consisted of single linked
list,
using two-cell (called "dot-pair" as oppossed to "atom"). The
head cell stores an index to an element (can be either an atom or
another list). The tail cell stores an index to the next
cell. At the end of list, the tail contains a NIL value.

<picture of data structure of list>
API (application program interface)
The library (ailib.c) contains many functions to create and
manipulate list.
name(string) creates an atom
"string"
number(n)
creates
an atom n
list(x)
creates
a list from an element x
cons(a,b)
creates
a list with its head a, its tail b
head(x) the head of
x,
the first element of a list x
tail(x) the
tail of x, the list x without the first element
print_list(x) print the
list
x to show it on the screen
isatom(x)
return
true if x is an atom
islist(x)
return
true if x is a list
There is an invariant showing the relationship between head, tail
and
cons:
cons(head(x),
tail(x))
== x
cons(number(1),
cons(number(2),
cons(number(3), cons(number(4), NIL)))) == (1 2 3 4)
cons(cons(number(1),
cons(number(2), NIL)),
cons( cons(number(3), cons(number(4), NIL)),
NIL)) == ((1 2 ) (3 4 ))
cons( cons(name("A"),
cons(number(10),NIL)) ,
cons(
cons(name("B"), cons(number(20), NIL)),
cons(
cons(name("C"), cons(number(30),NIL)), NIL)))
== ( ("A" 10) ("B" 20) ("C" 30))
Examples
Here is an example how to create a list and show it:
#include
"ailib.h"
int make_list(void){
return
cons(number(1), cons(number(2), cons(number(3), cons(number(4),
NIL))));
}
int main(void){
print_list(
make_list() );
}
Set up your C compiler IDE environment, including "ailib.c" and
"ailib.h" at the same directory as the application program.
Now, we will make a simple program to count the number of elements
of a
simple list.
int
len(int ls){
if(ls == NIL) return
0;
else return 1 +
len(tail(ls));
}
int test_len(void){
int a;
a = make_list();
printf("%d\n",
len(a));
}
The "len" function works like this: using a case
analysis,
if the list is empty (NIL) the number of element is 0, otherwise it
is
one pluses the length of the rest of the list (tail(ls)).
Now we extend the "len" to cope with a complex list (its element can
be
another list).
int
len2(int ls){
if(ls == NIL) return
0;
if(isatom(ls)) return
1;
else return
len2(head(ls))
+ len2(tail(ls));
}
int make_complex_list(void){
int a, b;
a =
cons(number(1),cons(number(2),NIL));
b =
cons(number(3),cons(number(4),NIL));
return cons(a,
cons(b,
NIL));
}
void test_len_complex(void){
int a;
a =
make_complex_list();
printf("%d\n",
len(a));
printf("%d\n",
len2(a));
}
If we apply "len" to a complex list, it will count the element, if
an
element is a list, it counts as one. "len" does not get "into"
the element when it is a list. "len2" checks if an element is
a
list, it is recursively walking into that list and count its
element.
The "len" function shows us how to traverse the list structure
recursively. Next, we learn how to "create" a duplicate of a
list
(using "cons"). It is quite similar to "len". Start with
a
simple list. We introduce a new function:
clone_atom(x) creates a duplicate of
an
atom x
int
copy_list(int ls){
if(ls == NIL) return
NIL;
else return
cons(clone_atom(head(ls)), copy_list(tail(ls)));
}
void test_copy(void){
int a, b;
a = make_list();
b = copy_list(a);
print_list(a);
}
We can extend "copy_list" to cope with a complex list, not much
different from "len2".
int
copy_list_complex(void){
if(ls == NIL) return
NIL;
if(isatom(ls)) return
clone_atom(ls);
else return cons(
copy_list_complex, copy_list_complex(tail(ls)));
}
void
test_copy_complex(void){
int a, b;
a =
make_list_complex();
b = copy_list2(a);
print_list(b);
}
If the list is empty "copy_list_complex" terminates by returning a
NIL. If input is an atom, return a duplicate of that
atom.
Otherwise the list is complex, walk into the first element and make
a
duplicate (recursively), and do the rest of the list, then "cons"
them
together. The "copy_list_complex" can copy a list with an arbitrary
length and shape!
Some excercises
1) Write a program to increment one of all elements of a
simple list of integers. (1
2 3 4) => (2 3 4 5)
2) Write a program to reverse a simple list. (1 2 3 4) => (4 3 2 1)
3) Write a program to divide a simple list into a list
with two elements, each is a list containing half of element of the
original. (assuming the length of the input list is a even
number)
4) Write a search function of an "associative
list".
An associative list contains a list of records, a record is a pair
of:
the first element is a "key" which is a a name, the second element
is
the value, an integer. Given a key, find the value associate
with
that key. Here is an example:
a
= cons(name("mango"), cons(number(3), NIL));
b = cons(name("orange"),
cons(number(5), NIL));
table = cons(a, cons(b,
NIL));
a table is ( ( "mango"
3 )
("orange" 5) )
search(table,
name("orange")) => 5
search(table, name("ice
cream"))
=> NIL
This is different from an ordinary array which index must be
integer.
Enjoy!
last update 17 Oct 2011