我试图检查 lisp 中 char 数组的每个元素是否等于某些重要值。我使用 dotimes 进行迭代和 if 语句,但我对 lisp 很陌生,所以我遇到了很多错误,但我不知道为什么。有人可以解释一下吗?
(defun gppinterpreter (filename)
(let ((count 0)
(my-array (make-array '(1000))))
(with-open-file (stream filename)
(do ((char (read-char stream nil)
(read-char stream nil)))
((null char))
(setf (aref my-array count) char)
(incf count)))
(print my-array))
(let (c "a"))
(dotimes (n count)
(setf (aref (my-array n)) c)
(if(= c "+")
(format t "OP_PLUS ~%"))
(if(= c "-")
(format t "OP_MINUS ~%"))
(if(= c "/")
(format t "OP_DIV ~%"))
(if(= c "*")
(if(= (aref my-array n+1) "*")
(format t "OP_DBLMULT ~%"))
(format t "OP_MULT ~%"))
(if(= c "(")
(format t "OP_OP ~%"))
(if(= c ")")
(format t "OP_CP ~%"))
(if(= c " ")
(format t "OP_OC ~%"))
(if(= c " ")
(format t "OP_CC ~%"))
(if(= c ",")
(format t "OP_COMMA ~%"))
)
)
Run Code Online (Sandbox Code Playgroud)
=用于比较数字,而不是字符。使用eql,char=或char-equal.
使用字符而不是字符串进行比较:"a"->#\a
使用case而不是多个ifs。
(setf (aref (my-array n)) c)有一对额外的括号。->(setf (aref my-array n) c)
确保变量的c值是字符而不是字符串。
除了其他答案之外,我认为您应该能够简化代码并将其分成不同的部分:
(format t "...~%")等可以被分解,这有助于提高可读性和可维护性(如果您很好地分解事物,则可以在一个地方进行修改,以防代码发生变化)。您还可以使用casewhere 子句是字符,case将值与 进行比较eql。注意:您的问题中一定有一些拼写错误,我不知道 OP_OC 和 OP_CC 代表什么,但它们关联的字符串只是空格;在这里我使用括号,但请随意调整:
(defun gpp-interpreter (stream)
(loop ;; keep looping until `(return)` is called
(write-line ;; write the string that is return by case
(case (read-char stream nil nil) ;; read a character, or NIL on error
(#\+ "OP_PLUS")
(#\- "OP_MINUS")
(#\/ "OP_DIV")
(#\* (case (peek-char t stream nil nil)
(#\*
;; peek did not read from the stream,
;; we must read now the character
(read-char stream)
"OP_DBLMULT")
(t "OP_MULT")))
(#\( "OP_OP")
(#\) "OP_CP")
(#\[ "OP_OC")
(#\] "OP_CC")
(#\, "OP_COMMA")
(t (return))))))
Run Code Online (Sandbox Code Playgroud)
例如:
(with-input-from-string (in "*/-()[],***")
(gpp-interpreter in))
Run Code Online (Sandbox Code Playgroud)
印刷:
OP_MULT
OP_DIV
OP_MINUS
OP_OP
OP_CP
OP_OC
OP_CC
OP_COMMA
OP_DBLMULT
OP_MULT
Run Code Online (Sandbox Code Playgroud)
通常在 Lisp 中,您可以将符号与语法树中的元素关联起来,而不是使用字符串。
另外,我不仅将write它们输出到标准输出,而且接受在每个标记上调用的回调函数,以便词法分析器更加通用。
(defun gpp-interpreter (stream callback)
(loop
(funcall callback
(case (read-char stream nil nil)
(#\+ 'plus)
(#\- 'minus)
(#\/ 'div)
(#\* (case (peek-char t stream nil nil)
(#\* (prog1 'exponent (read-char stream)))
(t 'mult)))
(#\( 'paren-open)
(#\) 'paren-close)
(#\[ 'bracket-open)
(#\] 'bracket-close)
(#\, 'comma)
(t (return))))))
Run Code Online (Sandbox Code Playgroud)
例如,这里的自定义函数将它们格式化为输出:
(with-input-from-string (in "*/-()[],***")
(gpp-interpreter in (lambda (s) (format t "~a~%" s))))
Run Code Online (Sandbox Code Playgroud)
但我们也可以将所有读取的标记收集到一个向量中:
(with-input-from-string (stream "*/-()[],***")
(let ((result (make-array 1 :fill-pointer 0 :adjustable t)))
(prog1 result
(gpp-interpreter stream
(lambda (token)
(vector-push-extend token
result
(array-total-size result)))))))
#(MULT DIV MINUS PAREN-OPEN PAREN-CLOSE BRACKET-OPEN BRACKET-CLOSE COMMA
EXPONENT MULT)
Run Code Online (Sandbox Code Playgroud)
首先,我们构建一个向量,对于每个正在读取的标记,该标记被推送到向量的末尾。如果向量中没有足够的空间,则其大小将加倍(按当前的大小增长array-total-size)。