Quintus Prolog Manual
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:
- reading Prolog terms using read/1
- 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.
- library(read_in) and library(read_sent) are for reading English sentences.
They return a list of words, which you can then parse using a Definite
Clause Grammar (built into the Prolog system).
- library(ctypes), library(prompt), library(read_const), library(continued),
library(lineio), and library(ask) are more general in purpose.
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
- a sequence of letters (see is_alpha/1) which is converted to lowercase
and returned as a Prolog atom
- a sequence of decimal digits (see is_digit/1) which is returned as
a Prolog integer (plus and minus signs become separate atoms).
- any graphic character other than a letter or digit, which is returned
as a single-character Prolog atom
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
- a sequence of letters, which is converted to lowercase and returned
as a compound term atom(Word). For example, "THIS" would
be returned as atom(this)
- a sequence of decimal digits, which is returned as a compound term
integer(Value). For example, "0123" would be returned
as integer(123). Plus and minus signs become separate atoms.
- a sequence of characters in double quotes, which is returned as a compound
term string(X), where X is an atom containing the characters
between the quotes. Two adjacent quotes are read as one, so the input string
"Double "" Quote" is returned as string('Double "
Quote').
- apostrophe s ('s) is returned as the atom 'aposts'.
- apostrophe not followed by s (') is returned as the atom 'apost'.
- any other graphic character is returned as a single-character Prolog
atom.
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
- Default may be an atom (the first character of whose name will
be used), a string (whose first character will be used) or an ASCII code,
and will be written in brackets before the question mark; and
- if the user just presses carriage return, Default will be used
as the answer.
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_file/3 does not add a question mark and space to the prompt; you
must put them in the question yourself.
- Although the first call to ask_file/3 found that it was possible
to open call.xref for output, it did not open it, so the second
call to ask_file/3 could not find any such file.
- 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