Quintus Prolog Manual


(PREV) (NEXT)

k-8: Obtaining User Input

k-8-1: Introduction

Quintus Prolog, DEC-10 Prolog, C-Prolog, and other similar Prolog systems offer only two methods of input:

  1. reading Prolog terms using read/1
  2. reading single characters using get0/1

There is a large gap between the two, and sometimes the input requirements of application programs lies in the gap. The Prolog library contains two sets of packages to fill the gap.

k-8-2: Classifying Characters -- library(ctypes)

One of the problems facing anyone who uses Prolog on more than one system is that different operating systems use different characters to signal the end of a line or the end of a file. We have


       (UNIX,VMS)      (UNIX,VMS)



end-of-line     31 (^_)         10 (LF, ^J)     10 (LF, ^J)

end-of-file     26 (^Z)         26 (^Z)         -1

")



It is conceivable that versions of C-Prolog or Quintus Prolog running under still other operating systems might be obliged to use some other end-of-line character. A prudent Prolog programmer will try to avoid writing these constants into his program. Indeed, a prudent Prolog programmer will try to avoid relying too much on the fact that Prolog uses the ASCII character set. Quintus Prolog addresses these problems by imitating the programming language C. The package library(ctypes) defines predicates that recognize or enumerate certain types of characters. Where possible, the names and the character sets have been borrowed from C. Except as indicated, all of the predicates in library(ctypes) check the type of a given character, or backtrack over all the characters of the appropriate type if given a variable.

is_endfile(-Char)
Char is the end-of-file character. There is only one such character. If get0/1 returns it, the end of the input file has been reached, and the file should not be read further. No special significance is attached to this character on output; it might not be a valid output character at all (as in Quintus Prolog) or it might simply be written out along with other text. The need for this predicate is largely obviated by the built-in predicate at_end_of_file/[0,1] in Release 3.
is_newline(-Char)
Char is the end-of-line character. There is only one such character. You can rely on it not being space, tab, or any printing character. It is returned by get0/1 at the end of an input line. The end-of-line character is a valid output character, and when written to a file ends the current output line. It should not be used to start lines, only to end them. The need for this predicate is largely obviated by the built-in predicate skip_line/[0,1] in Release 3.
is_newpage(-Char)
Char is the end-of-page character. There is at most one such character, and when it is defined at all it is the ASCII "formfeed" character. On some systems there may be no end-of-page character. This character is returned by get0/1 at the end of an input page. It is a valid output character, and when written to a file ends the current output page. It should not be used to start pages, only to end them.
is_endline(+Char)
UNIX systems permit more than one end-of-line character for terminal input; one of them is always C's "newline" character, another is the end-of-file character (^D or ^Z) if typed anywhere but as the first character of a line, and the last is the "eol" character which the user can set with the stty(1) command. is_endline/1 accepts most ASCII control characters, but not space, TAB, or DELETE, which covers all the line terminators likely to arise in practice. It should only be used to recognize line terminators; if passed a variable, it will generate an error message. The need for this predicate is largely obviated by the built-in predicate at_end_of_line/[0,1] in Release 3.
is_alnum(?Char)
is true when Char is the ASCII code of a letter or digit. It may be used to recognize alphanumerics or to enumerate them. Underscore `_' is not an alphanumeric character. (See is_csym/1 below.)
is_alpha(?Char)
is true when Char is the ASCII code of a letter. It may be used to recognize letters or to enumerate them. Underscore `_' is not a letter. (See is_csymf/1 below.)
is_ascii(?Char)

is true when Char is in the range 0..127. If Char is a variable, is_ascii/1 (like most of the predicates in this section) will try binding it to each of the acceptable values in turn (that is, it will enumerate them). Whether the end-of-file character satisfies is_ascii/1 or not is system-dependent.
is_char(?Char)
is true when Char is a character code in whatever the range happens to be. (In this version: ISO 8859/1.)
is_cntrl(?Char)

is true when Char is an ASCII control character; that is, when Char is the code for DEL (127) or else is in the range 0..31. Space is not a control character.
is_csym(?Char)
is true when Char is the code for a character that can appear in an identifier. C identifiers are identical to Prolog identifiers that start with a letter. Put another way, Char is a letter, digit, or underscore. There are C compilers which allow other characters in identifiers, such as "$". In such a system, C's version of iscsym/1 will accept those additional characters, but Prolog's will not.
is_csymf(?Char)
is true when Char is the code for a character that can appear as the first character of a C or Prolog identifier. Put another way, Char is a letter or an underscore.
is_digit(?Char)
is true when Char is the code for a decimal digit; that is, a character in the range 0..9.
is_digit(?Char, ?Weight)
is true when Char is the character code of a decimal digit, and Weight is its decimal value.
is_digit(?Char, ?Base, ?Weight)
is true when Char is the code for a digit in the given Base. Base should be an integer in the range 2..36. The digits (that is, the possible values of Char) are 0..9, A..Z, and a..z, where the case of a letter is ignored. Weight is the value of Char considered as a digit in that base, given as a decimal number. For example,

                    is_digit(97 /* a */, 16, 10)

                    is_digit(52 /* 4 */, 10,  4)

                    is_digit(70 /* F */, 16, 15)



             

This is a genuine relation; it may be used all possible ways. You can even use it to enumerate all the triples that satisfy the relation. Each argument must be either a variable or an integer.

is_graph(?Char)
is true when Char is the code for a "graphic" character, that is, for any printing character other than space. The graphic characters are the letters and digits, plus

            !  "  #  $  %  &  '  (  )  *  ;  <  =  >  ?  @

            [  \  ]  ^  _  `  {  |  }  ~  +  ,  -  .  /  :



            
is_lower(+Char)
is true when Char is the code for a lowercase letter, a..z.
is_paren(?Left, ?Right)
is true when Left and Right together form one of the delimiter pairs '(' and ')', '[' and ']', or '{' and '}'.
is_period(?Char)
is_period/1 recognizes each of the three punctuation marks that can end an English sentence. That is, is_period(Char) is true when Char is an exclamation point (!), a question mark (?), or a period (.). Note that if you want to test specifically for a period character, you should use the goal

                    Char is '.'



            
is_print(?Char)

is true when Char is any of the ASCII "printing" characters, that is, anything except a control character. All the "graphic" characters are "printing" characters, and so is the space character. When written to ordinary terminals, each printing character takes exactly one column, and Prolog code for lining up output in nice columns is entitled to rely on this. The width of a tab, and the depiction of other control characters than tab or newline, is not defined.
is_punct(?Char)
is true when Char is the code for a non-alphanumeric printing character; that is, Char is a space or one of the characters listed explicitly under is_graph/1. Note that underscore is a "punct" and so is the space character. The reason for this is that C defines it that way, and this package eschews innovation for innovation's sake.
is_quote(?Char)
is true when Char is one of the quotation marks ` (back-quote), ' (single-quote), or " (double-quote).
is_space(?Char)
is true when Char is the code for a white space character. This includes tab (9, ^I), linefeed (10, ^J), vertical tab (11, ^K), formfeed (12, ^L), carriage return (13, ^M), and space (32). These constitute the C definition of white space. For compatibility with DEC-10 Prolog, is_space/1 also accepts the (31, ^_) character.
is_upper(?Char)
is true when Char is the code for an uppercase letter, A..Z.
is_white(?Char)
is true when Char is a space or a tab. The reason for distinguishing between this and is_space/1 is that if you skip over characters satisfying is_space/1 you will also be skipping over the ends of lines and pages (though at least you will not run off the end of the file), while if you skip over characters satisfying is_white/1 you will stop at the end of the current line.
to_lower(?Char, ?Lower)
is true when Char is any ASCII character code, and Lower is the lowercase equivalent of Char. The lowercase equivalent of an uppercase letter is the corresponding lowercase letter. The lowercase equivalent of any other character is the same character. If you have a string (list of character codes) X, you can obtain a version of X with uppercase letters mapped to lowercase letters and other characters left alone by calling the library routine

                    maplist(to_lower, X, LowerCasedX)



             

In normal use of to_lower/2, Char is bound. If Char is uninstantiated, to_lower/2 will still work correctly, but will be less efficient. If you want to convert a lowercase letter Kl to its uppercase version Ku, do not use to_lower/2; to_lower(Ku, 97) has two solutions: 65 (A) and 97 (a). Use to_upper/2 instead.

to_upper(?Char, ?Upper)
is true when Char is any ASCII character code, and Upper is the uppercase equivalent of Char. The uppercase equivalent of a lowercase letter is the corresponding uppercase letter. The uppercase equivalent of any other character is the same character. If you have a string (list of character codes) X, you can obtain a version of X with lowercase letters mapped to uppercase and other characters left alone by calling the library routine

                    maplist(X, to_upper, UpperCasedX)



            

The System V macro isxdigit() is not represented in this package because isdigit/3 subsumes it. The System V macros _tolower() and _toupper() are not represented because to_lower/2 and to_upper/2 subsume them. The predicates needed for portability between operating systems are


is_endfile/1, is_endline/1, is_newline/1, is_newpage/1.



Remember: is_endfile/1 and is_endline/1 are for recognizing the end of an input file or the end of an input line, while is_newline/1 and is_newpage/1 return the character that you should give to put/1 to end a line or page of output.

k-8-3: Reading and Writing Lines -- library(lineio)

library(lineio) defines some commands for reading and writing lines of text.

get_line(-Chars, -Terminator)
reads characters from the current input stream until it finds an end-of-line character. Chars is unified with a list containing all the character codes other than the end-of-line character, and Terminator is unified with the end-of-line character. This allows you to check which character ended the line; in particular you should be prepared to handle the is_endfile(Terminator) case. When the end of a file is encountered, there may have been a partial line preceding it; so when is_endfile(Terminator), Chars may or may not be the empty list. get_line/2 is normally called with Chars unbound. A call to get_line/2 with Chars bound will behave similarly to get0/1 in that even if the line of characters does not unify with Chars, nevertheless the entire line is consumed and is irretrievable. Thus, if you call get_line("fred", Eol) and the next line of input is in fact "jim" or "frederica", the entire line will have been read before the call to get_line/2 fails. Only call get_line/2 with Chars bound when you want the line to be thrown away if it does not match. For example, if you want to skip until you encounter a line containing only a single "." (a convention some editors and some mailers use for the end of terminal input), you can write

                    ...

                    skip_through_line(".")



             

where

                    skip_through_line(X) :-

                            repeat,

                                get_line(X, _),

                            !.



             

(skip_through_line/1 is not in the library.)

get_line(-Chars)
is used for the common case in which you are uninterested in what the end-of-line character was, provided it was not end-of-file. get_line/1 reads a whole line, just like get_line/2, then checks that the line terminator was not the end-of-file character, and unifies the list of character codes with Chars. If Chars is instantiated and does not match the line that is read, or if the line terminator was end-of-file, get_line/1 fails quietly (with the same consequences regarding the loss of the non-matching text as with get_line/2 above).
fget_line(+Stream, ?Chars, ?Terminator)
like get_line/2 except that Stream is specified.
fget_line(+Stream, ?Chars)
like get_line/1 except that \Stream is specified.
put_chars(+Chars)
is a generalization of put/1. Chars should be instantiated to a (possibly empty) list of character codes. The corresponding characters are written to the current output stream. If you know the characters in advance, it is better to use write/1; for example,

                    put_chars("they do the same thing")



             

and


                    write('they do the same thing')



             

both write exactly the same characters to the current output stream, but the latter is more efficient. Use put_chars/1 when you already have the text you want to write as a list of character codes, write/1 when you have the text as an atom.

put_line(+Chars)
writes the list of character codes Chars, then writes a newline character. It produces exactly the same output that

                    put_chars(Chars), nl



             

would, but is generally more convenient. If you are reading lines from one file using get_line/1 and writing them to another, put_line/1 is the predicate to use.

k-8-4: Reading Continued Lines -- library(continued)

library(continued) is an extension of library(lineio). It defines two commands for reading continued lines.

read_oper_continued_line(-Line)
reads a line of text, using a convention rather like that of BCPL: an input line which ends with <op, newline> where op is a left parenthesis '(', left bracket '[', left brace '{', or a binary infix character from the set

                    + * - / # = < > ^ | & : ,



             

is taken to be continued; the op character is included in the combined Line, but the newline is not included.


| ?- compile([library(printchars),library(continued)]).

          . . .

| ?- read_oper_continued_line(Line).

|: command /option1=value1,

|:    /option2=value2



Line = "command /option1=value1,   /option2=value2"



| ?- read_oper_continued_line(Line).

|: Not continued!



Line = "Not continued!"



| ?- read_oper_continued_line(Line).

|: x^2+

|: 2*x+

|: 1



Line = "x^2+2*x+1"



             

It is likely that this will not be exactly the set of characters you want to act as continuation indicators, and you may want some <op> characters retained and others discarded. That is why we make the source code available: this file is intended mainly as an example.

read_unix_continued_line(-Line)
uses the UNIX convention (understood by sh, csh, cc, and several other programs) that a line terminated by a <backslash, new-line> pair is continued, and the backslash and newline do not appear in the combined Line. For example,

                    | ?- read_unix_continued_line(Line).

                    |: ab\

                    |: cde\

                    |: f



                    Line = "abcdef"



             

The following example is an extract from /etc/termcap:


| ?- unix(system('cat termcap-extract')).



dw|vt52|dec vt52:\

    :cr=^M:do=^J:nl=^J:bl=^G:\

    :le=^H:bs:cd=\EJ:ce=\EK:cl=\EH\EJ:cm=\EY%+ %+ :\

    :co#80:li#24:nd=\EC:ta=^I:pt:sr=\EI:up=\EA:\

    :ku=\EA:kd=\EB:kr=\EC:kl=\ED:kb=^H:

dx|dw2|decwriter II:\

    :cr=^M:do=^J:nl=^J:bl=^G:\

    :kb=^h:le=^H:bs:co#132:hc:os:



| ?- see('termcap-extract'),

|    read_unix_continued_line(Line),

|    seen.



Line = "dw|vt52|dec vt52:    :cr=^M:do=^J:nl=^J:bl=^G:

    :le=^H:bs:cd=\EJ:ce=\EK:cl=\EH\EJ:cm=\EY%+ %+ :

 :co#80:li#24:nd=\EC:ta=^I:pt:sr=\EI:up=\EA:    :ku=\E

A:kd=\EB:kr=\EC:kl=\ED:kb=^H:"



             

Note that only the backslashes at the ends of the lines have been discarded, and that the spaces at the beginning of the following lines have been retained.

k-8-5: Reading English Sentences

k-8-5-0: overview

There are two library files for reading sentences. One of them is library(read_in), which defines the single predicate read_in/1. library(read_in)was written to be used. library(read_sent) was originally written to be read and modified, as everyone has a different idea of how sentence reading should be done. Nevertheless, you may find read_sent/1 to be quite useful. Both sentence readers work by reading characters until some termination condition and then parsing a list of character codes using a Definite Clause Grammar. You can have any number of grammars in one Prolog program. You will probably find that read_in/1 does most of what you want, but if you want to do something different you may find it easier to modify read_sent/1.

k-8-5-1: library(read_in)

read_in(-Sentence)
reads characters from the current input stream until it finds end-of-file or a sentence terminator (see is_period/1) at the end of a line. There may be any number of tabs and spaces between that stop and the end of the line. It then breaks the characters up into "words", where a "word" is

The resulting list is returned in Sentence. The punctuation mark which terminated the sentence is included in the list. Here is an example:


            | ?- read_in(X).

            |: This is an example.  An example of read-in.  In

            |: it there are +00003 sentences!



            X = [this,is,an,example,.,an,example,of,read,-,

                 in,.,in,it,there,are,+,3,sentences,!]



             

Note that the end-of-line character, and any spaces and tabs following the sentence terminator, are consumed. It is important that the end-of-line character be consumed; otherwise subsequent prompts will behave unpredictably.

k-8-5-2: library(read_sent)

read_until(?Delimiters, -Answer)
reads characters from the current input until a character in the Delimiters string is read. The characters are accumulated in the Answer string, and include the closing delimiter. The end-of-file character always acts as a delimiter, even if it is not in the list of characters you supply.
trim_blanks(+RawInput, ?Cleaned)
removes leading and trailing layout characters from RawInput, and replaces internal groups of layout characters by single spaces.
"trim_blanks(<|TAB TAB a SP ^M ^E b ^Z|>, "a b") would be true."
chars_to_words(+Chars, ?Words)
parses a list of characters (read by read_until) into a list of tokens, where a token is
'X'
X a full stop or other punctuation mark
atom(X)
X a sequence of letters, e.g. atom(the)
integer(X)
X a sequence of digits, e.g. integer(12)
apost
'
aposts
's
string(X)
X "..sequence of any.."

Thus the string "the "Z-80" is on card 12." would be parsed as [atom(the),string('Z-80'),atom(is),atom(on),atom(card), integer(12),'.']. It is up to the sentence parser to decide what to do with these. Note that the final full stop, if any, is retained, as the parser may need it.

case_shift(+Mixed, ?Lower)
converts all the upper case letters in Mixed to lower case. Other characters (not necessarily letters!) are left alone. If you decide to accept other characters in words only chars_to_atom has to alter. See also lower/2 in library(case_conv).
read_line(-Chars)
reads characters up to the next newline or the end of the file, and returns them in a list, including the newline or end-of-file. Usually you want multiple spaces conflated to one, and the newline dropped. To do this, call trim_blanks on the result. For a routine which does not include the newline character in the result, see the predicate get_line/1 in library(lineio).
read_sent(-Sentence)
reads a single sentence from the current input stream. It reads characters up to the first sentence terminator (as defined by is_period/1) it finds, then throws characters away until it has reached the end of a line. The characters read are then broken up into "words", where a "word" is

The resulting string is returned in Sentence. Here is an example.


| ?- read_sent(X).

|: The predicate "read_sent" accepts sentences

|: that span more than 1 line, but not lines

|: that contain +2 or more sentences.  trash trash



X = [atom(the),atom(predicate),string(read_sent),

     atom(accepts),atom(sentences),atom(that),

     atom(span),atom(more),atom(than),integer(1),

     atom(line),',',atom(but),atom(not),atom(lines),

     atom(that),atom(contain),+,integer(2),atom(or),

     atom(more),atom(sentences),.]



             

This is more unwieldy than the output of read_in/1, but it does mean that your parser can tell the difference between words, numbers, and strings by pattern matching rather than having to use the "meta-logical" predicates atom/1, integer/1, and so forth.

k-8-6: Yes-no Questions, and Others -- library(ask)

The file library(ask) defines a set of commands for asking questions whose answer is a single character, and for asking for file names ({manual(k-6-5-1)}). library(ask) uses several commands from library(prompt), but if you want to use them in your program you should explicitly include the directive


        :- ensure_loaded(library(prompt)).



in your program. The principal such command is prompt/1, which is used to print the question or prompt.

yesno(+Question)
writes Question (using write/1) to the terminal, regardless of the current output stream, and reads an answer. The prompt is followed by "? ", so you should not put a question mark in the question yourself. The answer is the first character typed in response; anything following on the same line will be thrown away. If the answer is 'y' or 'Y', yesno/1 succeeds. If the answer is 'n' or 'N', yesno/1 fails. Otherwise it repeats the question. The user has to explicitly type a 'y' or 'n' before it will stop. Because the rest of the line is thrown away, the user can type "yes", "Yes", "You'd better not", and so forth with exactly the same effect as a plain 'y'. If the user just presses the return key, that is not taken as "yes".
yesno(+Question, +Default)
is like yesno/1 except that

For example,


            yesno('Do you want an extended trace', yes)



             

prints


            Do you want an extended trace [y]? _



             

and leaves the terminal's cursor where the underscore is. If the user presses carriage return, this call to yesno/1 will succeed. If the user answers "yes" it will succeed. If the user answers "no" it will fail. If the first non-layout character of the user's answer is neither "n", "N", "y", nor "Y", the question will be repeated.

ask(+Question, -Answer)
writes Question to the terminal as yesno/1 would, and reads a single character Answer. Answer must be a "graphic" character (a printing character other than space). ask/2 will continue asking until it is given such a character. The remainder of the input line will be thrown away.
ask(+Question, +Default, -Answer)
uses Default as the default character the way that yesno/2 does, and mentions the default in brackets just before the question mark. If the user presses carriage return, Default will be returned as his Answer. Answer can be instantiated, in which case the call to ask/2 or ask/3 will fail if the user does not give that answer. For example, yesno/2 could (almost) have been defined as

                    yesno(Question, Default) :-

                            ask(Question, Default, 0'y).



            
ask_chars(+Prompt, +MinLength, +MaxLength, -Answer)
writes Prompt to the terminal, and reads a line of characters from it. This response must contain between MinLength and MaxLength characters inclusive, otherwise the question will be repeated until an answer of satisfactory length is obtained. Leading and/or trailing layout characters are retained in the result, and are counted when determining the length of the answer. The list of character codes read is unified with Answer. Note that a colon and a space (": ") are added to the Prompt, so don't add such punctuation yourself. The end-user can find out what sort of input is required by typing a line which starts with a question mark. Therefore it is not possible to read such a line as data. See prompted_line/2 in library(prompt). Examples:

| ?- ask_chars('Label', 1, 8, Answer).

Label: 213456789

Please enter between 1 and 8 characters.

Do not add a full stop unless it is part of the answer.

Label: four



Answer = "four"



| ?- ask_chars('Heading', 1, 30, Answer).

Heading: ?

Please enter between 1 and 30 characters.

Do not add a full stop unless it is part of the answer.

Heading:    three leading spaces



Answer = "   three leading spaces"



            
ask_number(+Prompt, +Default, -Answer)
writes Prompt on the terminal, and reads a line from it in response. If, after "garbage" characters are thrown away, the line read represents a Prolog number, that number is unified with Answer. The "garbage" characters which are thrown away are layout characters (including spaces and tabs), underscores "_", and plus signs "+". For example, the input "+ 123_456" would be treated as if the user had typed "123456". The conversion is done by number_chars/2. If the user entered an integer, Answer will be unified with an integer. If the user entered a floating-point number, Answer will be unified with a floating-point number. No conversion is done. If the line contains only "garbage" characters and there is a Default argument, Answer is unified with Default. This happens regardless of whether or not Default is a number. If the input is unacceptable, the question will be repeated after an explanation of what is expected. The user can type "?" for help. Examples:

                    | ?- ask_number('Pick a number', X).

                    Pick a number: ?

                    Please enter a number followed by RETURN

                    Pick a number: 27



                    X = 27



                    | ?- ask_number('Say cheese', X).

                    Say cheese:

                    Please enter a number followed by RETURN

                    Say cheese: 3 . 141 _ 593



                    X = 3.14159



                    | ?- ask_number('Your guess', '100%', X).

                    Your guess [100%]: 38.

                    Please enter a number followed by RETURN

                    Your guess [100%]: 38



                    X = 38



                    | ?- ask_number('Your guess', '100%', X).

                    Your guess [100%]: <CR>



                    X = '100%'



            
ask_number(+Prompt, +Lower, +Upper[, +Default], -Answer)
These two predicates are a combination of ask_between/[4,5] and ask_number/[2,3]. They write the prompt to the terminal, read a line from it in response, throw away "garbage" characters, try to parse the result as a number, and check that it is between the Lower and Upper bounds. Lower and Upper may severally be integers or floating point numbers. Answer will be unified with an integer if the user typed an integer, with a floating-point number if the user typed a floating-point number, or with whatever Default happens to be if there is a Default and the user entered an empty line. If you want a floating-point result whatever the user typed, you will have to do your own conversion with is/2. Examples:

| ?- ask_number('Enter temperature in Fahrenheit',

                 32.0, 212.0, 77.0, Temp).

Enter temperature in Fahrenheit [77.0]: 10

Please enter a number between 32.0 and 212.0 followed

 by RETURN

Enter temperature in Fahrenheit [77.0]: 68



Temp = 68



            
ask_file(+Question, -Filename)
same as ask_file/3.
ask_file(+Question, +Mode, -FileName)
writes Question to the terminal and reads a file name from the terminal, regardless of the current I/O streams. If the user presses carriage return, ask_file/3 just fails; an empty file name is taken as an indication that the user has finished entering file names. A reply beginning with a question mark will cause a brief help message to be printed (explaining that a file name is wanted, and how to enter one), and the question will be repeated. Otherwise, ask_file/3 checks that the file can be opened in the mode specified by Mode (read, write, or append). If it is not possible to open the file in mode Mode, the operating system's error result is reported and the question is repeated. If it is possible to open the file in this mode, the name of the file is returned as FileName. However, ask_file/3 does not open the file for you, it simply checks that it is possible to open the file. Here is an example "dialogue":

| ?- ask_file('Where should the cross-reference go? ',

     write, File).

Where should the cross-reference go? ?

Please enter the name of a file which can be opened

in write mode, followed by a RETURN.  To end this

operation, just type a RETURN with no file name.

Where should the cross-reference go? call.pl



! Can't open call.pl in write mode.



! A file-system goal failed

Where should the cross-reference go? call.xref



File = 'call.xref'



| ?- ask_file('Next file: ', read, File).

Next file: call.pl



! Can't open call.pl in read mode.



! A file-system goal failed

Next file: call.xref



! Can't open call.xref in read mode.



! A file-system goal failed

Next file: <CR>



no



             

Points to note:

ask_between(+Prompt, +Lower, +Upper[, +Default], -Answer)
writes Prompt on the terminal, and reads a line in response. If the line read represents a Prolog integer between Lower and Upper inclusive, this line is unified with Answer. The line may contain only digits and perhaps a leading minus sign. If the line is empty and there is a Default argument, Answer is unified with Default. This happens regardless of whether Default is an integer or in the indicated range. If the answer read is not acceptable, the user is told what sort of answer is wanted and is prompted again. For example, after defining

                    p(X) :-

                       ask_between('Number of samples',1,20,

                                    [none],X),

                       integer(X).



             

the following conversation might take place.


                    | ?- p(X).

                    Number of samples [none]: ?

                    Please enter an integer between 1 and 20

                    Do not add a full stop.

                    Number of samples [none]: 0

                    Please enter an integer between 1 and 20

                    Do not add a full stop.

                    Number of samples [none]: 9



                    X = 9



                    | ?- p(X).

                    Number of samples [none]: <CR>

                    no



             

The prompt which is printed is "Prompt [Default]: " if there is a Default argument, "Prompt: " otherwise, so that you can use the same prompt whether or not there is a default argument.

ask_oneof(+Prompt, +Constants[, +Default], -Answer)
prints Prompt on the terminal, and reads a line in response. Constants should be a list of constants (terms which are acceptable as the first argument of name/2). If the user's response is the full name of one of the constants, Answer is unified with that constant. Failing that, if the user's response is a prefix of exactly one of the constants, Answer is unified with that constant. If the response is just a carriage return, and there is a Default argument, Answer is unified with Default (which need not be a constant, nor need it be an element of Constants). If nothing else works, the user is told what sort of response is wanted, and is prompted again. The prompt which is printed is "Prompt [Default]: " if there is a Default argument, "Prompt: " otherwise, so that you can use the same prompt whether or not there is a default argument.

You should find it straightforward to define your own simple queries using this kit. As a general rule, try to arrange things so that if the user types a question mark s/he is told what sort of response is wanted. All the queries defined in this section do that. The commands for reading English sentences do nothing special when their input is a single question mark. Here is an example of how you can build a query from them which does something sensible in this case.


ask_sentence(Prompt, Sentence) :-

    repeat,

        prompt(Prompt),

        read_in(X),

        (   X = [?] ->

            format(user_output,

                'Please enter an English sentence.~n', []),

            fail

        ;   true

        ),

    !,

    Sentence = X.



k-8-7: Other Prompted Input -- library(prompt)

library(prompt) defines several commands for reading prompted input from the terminal. In fact, library(ask) is built on top of this package.

prompt(+Prompt)
is used by all the commands in library(ask) to print the prompt or question. You may find it useful in constructing your prompted input commands. If Prompt is any term except a list, it is written to the terminal using write/1; if Prompt is a list of terms, each element of the list is written to the terminal using write/1, with no additional layout or punctuation. After writing Prompt to the terminal, the terminal output is flushed, so that Prompt will appear on the terminal before the user has to type an answer. prompt/1 ensures that Prompt always starts at the beginning of a line.
prompted_char(+Prompt, -Char)
writes Prompt to the terminal, and reads a line of characters from it. The first of these characters is returned as Char; the rest are discarded. The original case of the character is preserved. Note that Char might be a newline character or the end-of-file character.
prompted_line(+Prompt, -Chars)
prompted_line(+Prompt, -Chars, -Terminator)
These predicates write Prompt to the terminal, regardless of the current output stream; and read a list of character codes from the terminal, regardless of the current input stream. Prompt is written using write/1; it is normally a single atom, and should never be a list of character codes. prompted_line/3 also returns the end-of-line character, while prompted_line/2 simply checks that the end-of-line character is not end-of-file. When you want to ask the user of your program a question, use prompted_line/[2,3] instead of changing I/O streams yourself, or using ttyget0/1 and its associates. In order to ask the user of your program "Do you really want to stop?" and stop if the user says 'yes' or anything else beginning with a lowercase y, simply write

            conditional_halt :-

               prompted_line('Do you really want to stop? ',

                              [0'y|_]),

               halt.



             

You can use prompted_line/[2,3] without worrying what the current I/O streams are, or whether you need to call ttyflush/0 or not. Also, as with get_line/[1,2], an input line ends with the line terminator character. The user does not have to end prompted_line/[2,3] input with a ".", just with a carriage return.

k-8-8: Pascal-like Input -- library(readconstant)

library(readconst) provides a set of Pascal-like input commands. The commands are


        read_constant(X)                   skip_constant

        read_constant(Stream,X)            skip_constant(Stream)

        read_constants([X1,...,Xn])        skip_constants(N)

        read_constants(Stream,[X1..Xn])    skip_constants(Stream,N)

        prompted_constant(Prompt, X)

        prompted_constants(Prompt, [X1,...,Xn])



The idea is that these commands consume some number of "tokens" from the input stream (for read_constant/1, skip_constant/0, read_constants/1, and skip_constants/1 this is the current input stream; for read_constant/2, skip_constant/1, read_constants/2, and skip_constants/2 it is the Stream argument; for prompted_constant/2 and prompted_constants/2 it is the 'user_input' stream). prompted_constant/1 and prompted_constants/2 resemble Pascal's "readln" command; the others resemble Pascal's "read" command. These commands skip initial layout (spaces and control characters). There are two kinds of tokens: quoted tokens and unquoted tokens. A quoted token starts with a single quote (') or a double quote ("). It ends with the same character that it starts with. To include a quote in such a token, write the quote twice. This is the same as Prolog with the 'character_escapes' flag off. (There is currently no way of making read_constant/1 use C-style character escapes.) A token which starts with a single quote will be returned as a Prolog atom, even if it looks like a number. Again, this is the same as Prolog. For example, '5' will be returned as the atom '5', not the integer 5. A token which starts with a double quote will be returned as a list of character codes. For example, """" (four double quotes will be returned as the list [34]. Both '' (two single quotes) and "" (two double quotes) are acceptable tokens, meaning the atom with no characters in its name ('') and the empty list ([]) respectively. The character following the closing quote of a quoted token is always discarded. This character is normally a space, tab, newline, or comma. An unquoted token is anything else. Characters are read until a layout character or a comma is found. The comma or layout character which terminates the token is discarded. The other characters are given to the built-in predicate name/2, so the token will be returned as a number if it looks like a number; otherwise it will be returned as an atom. The syntax of numbers is perforce identical to the syntax of numbers in Quintus Prolog. In both cases, we have leading layout which is skipped, the token proper, and a terminating character which is discarded. If, for example, the input looks like


        fred, 1.2 ' ', last<NEWLINE>



and we call read_constants([A,B,C,D]), the bindings will be A=fred, B=1.2, C=' ', D=last, and the entire line will have been consumed. But if the input looks like


        fred, 1.2 ' ', last<SPACE><SPACE><NEWLINE>



the last <SPACE> and <NEWLINE> will be left in the input stream. The input stream can contain end-of-line comments, which begin with a percent sign (%) just as they do in Prolog. A comment will terminate an unquoted token, and will be skipped. Suppose you want a data file which contains a number and a file name. The file could look like this:

        % This is the data file.

        137 % is the number

        foobaz/other-file % is the file name



You could read it by calling


| ?- see('the-file'), read_constants([Nbr,File]), seen.



The following predicates are defined in library(readconst):

read_constant(-Constant)


reads a single constant from the current input stream, then unifies Constant with the result.
read_constant(+Stream, -Constant)


reads a single constant from Stream, then unifies Constant with the result.
read_constants(-[X1,...,XN])


The argument must be a proper list. N constants are read from the current input stream, then [X1,...,XN] is unified with a list of the results.
read_constants(+Stream, -[X1,...,XN])


The second argument must be a proper list. N constants are read from Stream, then [X1,...,XN] is unified with a list of the results.
skip_constant


reads a single constant from the current input stream, then throws it away. This produces the same effect as calling read_constant(_), but is more efficient, as it doesn't convert the constant from character form to Prolog form before discarding it.
skip_constant(+Stream)


reads a single constant from Stream and discards it. This produces the same effect as calling read_constant(Stream, _) but is more efficient.
skip_constants(+N)


reads N constants from the current input stream and discards them. N must be a non-negative integer.
skip_constants(+Stream, +N)


reads N constants from Stream and discards them. N must be a non-negative integer.
prompted_constant(+Prompt, -Constant)


writes Prompt to the terminal (to user_output) and reads one constant from it (from user_input) in response, then unifies Constant with the result. This command will flush the rest of the input line after it has read Constant, just like the commands in library(ask). Here is an example:

| ?- prompted_constant('Guess the magic number: ', X),

|    integer(X).

Guess the magic number: 27 is my guess



X = 27



             

The words "is my guess" and the new-line are discarded.

prompted_constants(+Prompt, -[X1,...,XN])


writes a prompt to the terminal (to user_output) and reads N constants from it (from user_input) in response, then unifies [X1,...,XN] with a list of the results. This command will flush the rest of the input line after it has read [X1,...,XN], just like the commands in library(ask).


Copyright (C) 1997 AI International Ltd
contact: product support sales information