~1 LOGIC PROGRAMMING LESSON 7 In this lesson, I describe an entirely different way of looking at Prolog from the Prolog-as-logic view I have taught so far. When we write programs, we not only need to make correct deductions; we must also be able to display what we've deduced, perhaps after reformatting or translating it in some way. For example, in a copy of Eliza, we must be able to display messages like WHY DO YOU SAY YOU HATE YOUR FATHER? Conversely, we must be able to read sentences typed at the terminal and turn them into facts in the knowledge base from which we can generate output. ~2 So we must be able to: (1) Display output on the terminal, or print it on a printer, or send it to a file. (2) Read input from the terminal, or from a file. (3) Place new facts into the knowledge base, and remove unwanted ones, as a program runs. ~3 We can do these in Prolog. But we need to use it in a different way from what we've already learnt. Prolog can be used, not only as a language for expressing facts, but also as a language for controlling the computer: as a language for giving commands, and stating in what order these commands are to be obeyed. When used like this, it resembles conventional languages like Basic and Pascal. We call this part of Prolog the CONTROL LANGUAGE, as distinct from the LOGIC LANGUAGE of previous lessons. Unfortunately, the syntax of the CONTROL LANGUAGE is identical to that of the LOGIC LANGUAGE. This means that, merely by looking at the form of a program, you can't tell whether it's to be treated as commands or facts. It also confuses programmers who forget which context they're working in. ~4 I shall now introduce Prolog as a control language. Please put to the back of your mind its use for logic. Eventually, you'll be skilled enough to switch quickly from one view to the other; but if you try doing so now, it will only confuse you. Bring to the front of your mind anything you know about other programming languages, so you can make analogies between them and Prolog as a control language. Before continuing, please give the command "prolog." to switch your Tutor into Prolog mode. ~5 Now, type these questions. write(1)? write(fred)? write(a_very_long_name)? ~6 In each case, Prolog displayed "write"s argument. In general, "write" takes one argument, and commands Prolog to display it on the terminal. "write" acts like "write" in Pascal, or "print" in Basic. ~7 Suppose we want to write messages that contain spaces. How do we do it? By using the quote marks '' around the message: write('Hello world')? write('WHY DO YOU SAY YOU HATE YOUR FATHER?')? Try these. What happens if you omit the quotes? ~8 All these arguments: fred a_very_long_name 'Hello world' 'WHY DO YOU SAY YOU HATE YOUR FATHER?' are atoms. At the end of Lesson 4, we saw that atoms like fred and a_very_long_name do not need quotes. But to use atoms that start with a capital letter, we need quotes, otherwise Prolog would think they are variables. And to use atoms that contain spaces or other funny characters, we again need quotes. If the message 'Hello world' did not have quotes, Prolog would think it was a variable followed by an atom, and not one single atom. Try this again by comparing write(Hello world)? write('Hello world')? ~9 With the first one, no quotes, you will have got a syntax error. Prolog has tried to treat Hello and world as two different items, and got upset because of the space between them. In the second one, the quotes prevent Prolog analysing what's inside. It just skips from the opening quote to the closing one, and treats everything between as one item. Note that Prolog doesn't display the quotes when commanded to write. ~10 Now, try write(X)? write('X')? ~11 The first will have displayed something like _1. That's because Prolog treats the capital X as a variable. When you write it as above, it has no value, and conventionally, variables without a value are displayed as an underline followed by a number (the number says something about where their contents is stored in the computer). To insulate the X from being treated as a variable, and make it into something that's printed just as it is, we must again quote it. ~12 So - when using "write", if you want to write a message, and it contains capitals, spaces, etc., put quotes round it. ~13 A point of terminology. Prolog programmers call "write" a PREDICATE, in the same way that they call ">" and "loves" predicates. This can confuse the beginner, because "predicate" implies something that has a meaning in logic, and "write" doesn't. For the moment, just accept this terminology. I shall try to refer to such predicates as COMMAND PREDICATES (though that's not standard terminology). Do not confuse command predicates with commands, like "next" and "analyse", that you give to the Tutor. The word "command" means the same in both - something you do to change things - but otherwise, there is no connection between the two. Like the comparison predicates of Lesson 5, some command predicates are BUILT-IN (provided by the implementor). "write" is one such. Others, you can write for yourself, and I will ask you to do so soon. ~14 Beginners sometimes have trouble when they want to write several things in one go. In Lesson 6, I explained why the question not(is_a(joe,man), lives_in(joe,paris) )? does not do as expected. The comma is treated as an argument separator, and this makes Prolog think you're calling a predicate called "not" with two arguments. Since there's no such thing, the question fails. Similarly with write('When',did,you,last,see,your,father)? The commas will be treated as argument separators, making Prolog think of this as a 7-argument predicate called "write". But the only version of "write" that's built-in is the one-argument form, so the question will fail. ~15 Now I shall introduce another command predicate: nl. Try the question nl? ~16 That commands Prolog to display a blank line. "nl" is short for "newline". ~17 How can we make Prolog obey more than one command predicate? Try the question write('Happy'), nl, write('Birthday'), nl? ~18 The comma, which means "and" in the logic language, means "do next" in the control language. It's like the semicolon in Pascal, or putting one statement after another in Basic. ~19 Now, type this definition: greet(A) :- write('Happy'), nl, write('Birthday'), nl, write('Dear '), write(A), write('!'), nl. It looks like a logical predicate, but it's in Prolog's control language, so you should interpret it differently, as a sequence of commands: To greet someone (A), do this: Write 'Happy', then take a newline. Write 'Birthday', then take a newline. Write 'Dear', then write the value of A, then write '!', then take a newline. ~20 Try questions like greet(john)? greet('John')? ~21 "greet" is like a procedure in Pascal. After the ":-" go the statements that define what commands it must obey. Before the ":-" come any arguments. Think of the ":-" as meaning "this is how to do it". Note incidentally that you can't say write( 'Dear ', A, '!' )? This would be calling a 3-argument write, and there is no such thing. Note also that "analyse", if you try to use it on "greet", will give some rather confusing output. It doesn't know about the control language, and assumes everything is in the logic language, so will translate "greet" as a logical predicate. Don't get confused by this! ~22 I have now introduced two command predicates for outputting ("write" and "nl"); and I've shown you how to sequence them with comma; and how to use them in definitions. In practice, "write" is tedious to use because, as in "greet", you need to do a separate "write" for each message. If you need to do output in your projects, I'll provide a more convenient command predicate. . ~23 I shall now turn to input. Prolog provides a command predicate called "read" for reading stuff typed at the terminal. I shall not describe it in detail, because (like "write"), it's not very convenient for the kind of tasks your projects require. I shall supply more convenient predicates if you need them. But, just to demonstrate, after the end of this section, please type the question read(X)? When you've done so, you will see the symbols |: at the left hand side of the screen. When they appear, type fred. and RETURN. ~24 If you did that correctly, you will have got the answer "X = fred". "read" reads something from the terminal, and puts it into the variable given as argument. The reason that read isn't very convenient is that what it reads has to be in Prolog syntax. Most things you'll want to type, such as sentences, aren't, and "read" will refuse to accept them. But as I said above, I can supply command predicates that will accept sentences, or other kinds of non-Prolog input, and leave them in a suitable form for your programs to process. ~25 Now, some practice with "write", "nl", and "read". To start with, can you write a command predicate that just says "Hello". ~26 That's not hard. Use a definition like say_hello :- write('Hello'). Then you can use it in a question like say_hello? ~27 But there's one thing that may confuse you. This predicate has NO ARGUMENTS - i.e. there is nothing in brackets after its name. That's not surprising. When a predicate has arguments, what it does must depend on their value. For example, "greet(john)?" will output a different message from "greet(winnie)?". But "say_hello" has to do the SAME thing every time, so its effect must not depend on anything else. Hence, there's no point at all in giving it arguments. This applies to command predicates. Unless a logic predicate has arguments, it can only be true every time, or false every time. Hence, logic predicates will always have arguments. ~28 Now, can you write a command predicate that (a) Outputs the message "Please type your name"; (b) Reads a name; (c) Outputs "You typed " followed by the name. When testing it, and typing in a name, you must type an atom (like "fred" above), followed by a dot: frank. dav. or whatever. ~29 Like "say_hello", this command predicate will have no arguments. If you can't see how to do it, elaborate the definition below: echo :- write( ... ), read( ... ), write( ... ). ~30 Well, you should have something like echo :- write('Please type your name'), nl, read(X), write('You typed '), write(X), nl. ~31 In Lesson 6, we saw how to do arithmetic. Can you write a command predicate that takes two numbers as arguments and writes The sum of ... and ... is ... where the dots are to be replaced by the two numbers and the sum respectively. ~32 If you're not sure, try elaborating on this framework: sum( A, B ) :- write(...), write(...), write(...), write(...), ... is ..., write(...), write(...), nl. where you should fill in the dots. ~33 You should have had something like: sum( A, B ) :- write('The sum of '), write(A), write(' and '), write(B), S is A+B, write(' is '), write(S), nl. Notice that I've chosen a new variable (S) to hold the sum. I've also put the message atoms in quotes to insulate the spaces and capital letters from Prolog, and I've had to use one "write" for each part of the message. ~34 Now, can you write a predicate that reads two numbers typed at the keyboard, and writes the same message as above. This predicate will not have any arguments; instead, it will call "read" twice to get the numbers. As with names, you can type numbers to "read" by following them by a dot: 2. 3. ~35 If you're having trouble, try filling in the dots here: doit :- read(A), read(A), write(...), write(...), write(...), write(...), ... is ..., write(...), write(...), nl. ~36 You should have had something like: doit :- read(A), read(B), write('The sum of '), write(A), write(' and '), write(B), S is A+B, write(' is '), write(S), nl. To call "doit", you'd give the question doit? You would then see the symbol |: appear. Type a number followed by a dot (and RETURN). You'd then see another |: . Type another number. Then the sum would appear. ~37 I have now talked about input and output, and arithmetic. In computer science, output is said to be a SIDE-EFFECT, because it doesn't just calculate results, but also changes some part of the program's environment. Arithmetic isn't a side-effect, because it doesn't change the environment. As the final part of this lesson, I shall discuss command predicates with a different kind of side-effect, that of altering the knowledge base. ~38 If you tried Traveller, you will have seen how, as a trader's status varies, these changes are recorded as facts in the database, and as corresponding text in the textbase. The way Traveller does this is by calling three predicates, "add", "adda", and "del". "add" and "adda" both take one argument. That argument is something more complicated than a variable, atom or number: it's a Prolog fact. They add this argument to the knowledge-base. "add" adds it to the end; "adda" to the start. ~39 Demonstrate this with questions like add( loves(john,mary) )? add( loves(mary,fred) :- likes(fred,wine) )? adda( loves(bert,ethel) )? adda( loves(mark,julia) :- likes(julia,beer) )? After each one, do a show. to see how the textbase has changed. Try asking questions about loves to see how the database has changed. ~40 What's the difference between "add" (or "adda"), and putting a fact in by typing it, as in 10 loves(john,mary). ? Why bother to have "add" and "adda" at all? Think about this before reading the next section. ~41 The point about "add" and "adda" is that they can be called under program control. You can include calls to them in your command predicates. This is exactly what "run" does in Traveller. To demonstrate, type in the following clause for predicate "match". match(X,Y) :- add( loves(X,Y) ). Then try questions such as match(john,mary)? ~42 Note that the values that X and Y have are "frozen" into the fact. ~43 There's also a command predicate for removing facts, called "del". x{facts!deleting at run-time|(ee} The question del( loves(john,mary) )? does the obvious thing. The question del( loves(X,mary) )? will remove the first fact about anyone who loves Mary. The question del(loves(john,X))? will remove the first fact about John loving anyone. Guess what del( loves(X,Y) )? will do. ~44 It removes the first fact about anyone loving at all. ~45 Note that these predicates are NOT standard Prolog. Standard Prolog has "assert" and "asserta" as equivalents to "add" and "adda"; "retract" as equivalent to "del". It also has something called "retractall" which is stronger than "retract" because it will remove ALL facts for a given predicate in one go. I have implemented add, adda and del for you to use with the Tutor, because they update the textbase, so you can see what's been added or deleted. Normal Prolog doesn't store text of facts in the same way, so it's harder to see what's going on. There is a way to do so, but it's not very convenient. ~46 You have now almost reached the end of Lesson 7. In it, I've introduced the notion of Prolog as a CONTROL LANGUAGE: a language for giving commands and controlling their order of execution. The control language uses the same symbols as Prolog's logic language (comma and :- for example), but they mean different things. I have introduced the predicates "write" and "nl" for OUTPUT; and "read" for INPUT. Messages for "write" that contain spaces or other odd characters should be enclosed in single quotes. Input for "read" must be in Prolog syntax, and followed by a dot. That's not always convenient, but there are other ways to do input. ~47 I have shown you the predicates "add" and "adda" for ADDING facts, and "del" for DELETING them. These AREN'T STANDARD PROLOG; their standard equivalents are "assert", "asserta", and "retract" (plus "retractall" to delete all facts for one predicate). These predicates take PROLOG FACTS as arguments. All others you've met take variables, numbers, or atoms (except for "is", which takes a formula as its second argument). ~48 You have now reached the end of Lesson 7. In the remaining lesson, I shall tell you how to combine the logic language and the control language, so you can use the results of inferences to control the way that commands operate. That will conclude the Logic Programming Tutor sessions; it will then be time to move to a system which makes it more convenient to write large programs. Please do any exercises for this lesson before proceeding to the next one.