在循环中调用函数(Common Lisp)

时间:2018-04-12 19:44:56

标签: function loops common-lisp clisp

我正在制作一个控制台Lisp生存游戏,我正在尝试添加一个函数,直到a = b,显示"。"每一秒。然后,当a = b时,设置一个"伤害"变量为true,如果/当该变量为真时,减去" health"直到" use-medkit"用户调用函数并且"伤害"变量设置为false并退出两个循环。

我遇到的问题是当我被提示使用" use-medkit"函数和我键入它,它不会评估我输入的任何内容并保持从" health"中减去1。如何在循环运行时调用用户输入的函数?

这是我的代码:

(setq a (random 11)) ; Random from 0 - 10
(setq b (random 11)) ; ^^^^^^^^^^^^^^^^^^
(setq hurt 0)
(setq repair 0)
(setq health 999)

(defun use-medkit () 
    (setq repair 1))

(defun get-hurt ()
    (loop
        (progn 
            (setq a (random 11))
            (setq b (random 11))
            (progn  
                (princ ".")
                (sleep 1)))

        (if (eq a b) (progn     
                        (setq hurt 1)
                        (when (eq hurt 1) (progn        
                                            (format t "~%You are hurt!~%You will lose 1 hp every 10 seconds~%~%Type use-medkit to stop the bleeding~%")
                                            (loop
                                                (progn 
                                                    (- 1 health)
                                                    (sleep 10))
                                                    ;(format t "health: ~A~%" health)
                                                (when (eq repair 1) (progn 
                                                                 (return "You stopped the bleeding") (setq hurt 0) (setq repair 0))))))))))

1 个答案:

答案 0 :(得分:1)

所以一个程序不能同时做两件事。特别是如果你正忙着打印点,睡觉并从999中减去1,那么你就不会停下来看看是否还有另一个命令。

不幸的是,解决这个问题很难。终端中最好的解决方案可能会使用像ncurses这样的东西。此外,没有标准的方法来控制输入缓冲。代替这一点,这里有一个简单的方法,你可以做一些并发和一些提示。您可能希望使用适当的异步库。

(defun maybe-read (input-stream recording-stream)
  (when (listen input-stream)
    (let ((char (read-char input-stream)))
      (if (char= char #\Newline)
         t
         (progn (write-char char recording-stream) (maybe-read))))))

(defun make-partial-reader (input-stream)
  (list input-stream (make-string-output-stream)))
(defun partial-read (reader)
  (when (apply #'maybe-read reader)
     (get-output-stream-string (second reader))))

(defun how-long-until (time)
  (let ((gap
          (/ (- time (get-internal-run-time)) internal-time-units-per-second)))
    (cond ((< gap 0) (values 0 :late))
          ((<= gap 0.001) (values 0 :now))
          (T (values (- gap 0.001) :soon)))))
(defun sleep-until (time)
  (multiple-value-bind (span type)
        (how-long-until time)
    (when (> span 60) (warn “long wait!”)
    (case type
      (:late nil)
      (:now t)
      (:soon
        (sleep span)
        (unless (sleep-until time) (warn “poor timekeeping”))
        t))))
(defmacro with-prompt-and-scheduler ((schedule) (line &optional (input *standard-input*)) &body handle-line-input)
  (let ((reader (gensym)) (insched (gensym)))
    `(let ((,reader (make-partial-reader ,input) (,insched)))
        (flet ((,schedule (in fun &aux (at (+ (get-internal-run-time) (* in internal-time-units-per-second))))
                 (if (null ,insched) (push (cons at fun) schedule)
                    (loop for s on ,insched
                          for ((at2) . y) = s
                      if (< at at2)
                       do (psetf (car s) (cons at fun)
                                 (cdr s) (cons (car s) (cdr s)))
                          (finish-loop)
                      unless y do (setf (cdr s) (acons at fun nil)) (finish-loop)))))
         (loop
           (if ,insched
               (let ((,insched (pop ,insched)))
                 (when (sleep-until (car ,insched))
                   (let ((,line (partial-read ,reader)))
                     (when ,line ,@handle-line-input)))
                 (funcall (cdr ,insched)))
               (let ((,line (concatenate 'string (get-output-stream-string (second ,reader)) (read-line (first ,reader)))))
                 ,@handle-line))))))))

然后你可以使用它:

(let ((count 0))
  (with-prompt-and-scheduler (schedule) (line)
    (let ((n (read-from-string line)))
      (when (realp n)
        (schedule n (let ((x (incf count))) (lambda () (format t "Ding ~a ~a~%" x count) (finish-output))))))))

在运行输入10后,然后在下一行5.如果你那么快,你会得到:

Ding 2 2
Ding 1 2

第一行出现在5秒后,第二行出现在10点之后。如果你很慢,你应该得到:

Ding 1 1
Ding 2 2

第一行输入10后10秒,第二行输入5后5秒。

希望这可以让你了解如何使程序看起来一次做两件事。