在 Lisp 中检查数组的每个元素

0 arrays common-lisp

我试图检查 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)

Rai*_*wig 5

=用于比较数字,而不是字符。使用eql,char=char-equal.

使用字符而不是字符串进行比较:"a"->#\a

使用case而不是多个ifs。

(setf (aref (my-array n)) c)有一对额外的括号。->(setf (aref my-array n) c) 确保变量的c值是字符而不是字符串。


cor*_*ump 5

除了其他答案之外,我认为您应该能够简化代码并将其分成不同的部分:

  1. 您可以直接从流中读取,这比文件更通用,如果需要,您可以使用其他流源
  2. 不要将文件的所有字符读入数组,您的问题可以通过在读取字符时处理字符来解决
  3. 避免重复:(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)。