我一直在学习Common Lisp,我遇到了一个问题 我如何能够实现这样一个功能,允许用户输入一些单词,直到用户输入退出。 (实际上我想知道什么样的命令行交互功能API符合这样的要求)
e.g。 提示"请输入一个字:"在REPL中,然后将用户输入存储到全局 my-words 中,当用户输入"退出"时退出。
答案 0 :(得分:5)
您的规范有点不完整(例如,您的问题中的单词构成了什么?如果用户添加多个单词怎么办?如果输入为空怎么办?)。下面我使用CL-PPCRE将输入分成不同的单词并一次性添加它们,因为它似乎有用。在您的情况下,您可能希望添加更多错误检查。
如果您想与用户进行交互,则应该从*QUERY-IO*
流中读取和写入。在这里,我将根据您的要求提供一个带有全局变量的版本,以及另一个没有副作用的版本(除输入/输出外)。
定义全局变量并使用空的可调整数组对其进行初始化。 我正在使用一个数组,以便最后添加单词很容易,但你也可以使用一个队列。
(defvar *my-words* (make-array 10 :fill-pointer 0 :adjustable t))
以下函数会改变全局变量:
(defun side-effect-word-repl ()
(loop
(format *query-io* "~&Please input a word: ")
(finish-output *query-io*)
(let ((words (ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
(read-line *query-io*))))
(dolist (w words)
(when (string-equal w "exit") ; ignore case
(return-from side-effect-word-repl))
(vector-push-extend w *my-words*)))))
LOOP
使用简单的语法,其中只有表达式,没有循环 - 特定关键字。我首先将提示写入*QUERY-IO*
。 ~&
FORMAT
指令执行与FRESH-LINE
相同的操作。正如Rainer在评论中指出的那样,我们必须致电FINISH-OUTPUT
以确保在用户需要回复之前有效打印邮件。
然后,我从相同的双向流中读取整行,并将其拆分为单词列表,其中单词是一个非空白字符串。
使用DOLIST
,我遍历列表并使用VECTOR-PUSH-EXTEND
将单词添加到全局数组中。但是只要我加密"exit"
,我就会终止循环;因为我依赖STRING-EQUAL
,所以测试不区分大小写。
不鼓励使用如上所述的全局变量。如果您只需要提示返回单词列表,那么以下内容就足够了。在这里,我使用PUSH
/ NREVERSE
成语来构建生成的单词列表。
(defun pure-word-repl ()
(let ((result '()))
(loop
(format *query-io* "~&Please input a word: ")
(finish-output *query-io*)
(let ((words (ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
(read-line *query-io*))))
(dolist (w words)
(when (string-equal w "exit")
(return-from pure-word-repl (nreverse result)))
(push w result))))))
正如jkiiski评论的那样,在:word-boundary
分割单词可能会更好。我尝试了不同的组合,以下结果似乎令人满意的奇怪的示例字符串:
(mapcan (lambda (string)
(ppcre:split :word-boundary string))
(ppcre:split
'(:greedy-repetition 1 nil :whitespace-char-class)
"amzldk 'amlzkd d;:azdl azdlk"))
=> ("amzldk" "'" "amlzkd" "d" ";:" "azdl" "azdlk")
我首先删除所有空格并将字符串拆分为字符串列表,其中可以包含标点符号。然后,每个字符串本身在:word-boundary
分割,并与MAPCAN
连接以形成单独的单词列表。但是,我无法猜测您的实际需求是什么,所以您应该定义自己的SPLIT-INTO-WORDS
函数来验证和拆分输入字符串。
答案 1 :(得分:2)
CL-USER 23 > (progn
(format t "~%enter a list of words:~%")
(finish-output)
(setf my-words (read))
(terpri))
enter a list of words:
(foo bar baz)
或
CL-USER 28 > (loop with word = nil
do
(format t "~%enter a word or exit:~%")
(finish-output)
(setf word (read))
(terpri)
until (eql word 'exit)
collect word)
enter a word or exit:
foo
enter a word or exit:
bar
enter a word or exit:
baz
enter a word or exit:
exit
(FOO BAR BAZ)