简体   繁体   English

检查 lisp 中数组的每个元素

[英]Checking an array's every element in lisp

I'm trying to check every element of a char array in lisp if it's equal to some significant values.我正在尝试检查 lisp 中 char 数组的每个元素是否等于某些重要值。 I'm using dotimes for iteration and if statements but I'm very new to lisp so I get lots of errors and I don't know why.我使用 dotimes 进行迭代和 if 语句,但我对 lisp 很陌生,所以我遇到很多错误,我不知道为什么。 Can someon explain?有人可以解释一下吗?

(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 ~%"))   
    )
)

= is for comparing numbers, not characters. =用于比较数字,而不是字符。 Use eql , char= or char-equal .使用eqlchar=char-equal

Use characters instead of a string to compare: "a" -> #\a使用字符而不是字符串进行比较: "a" -> #\a

Use case instead of multiple if s.case而不是多个if s。

(setf (aref (my-array n)) c) has a extra pair of parentheses. (setf (aref (my-array n)) c)有一对额外的括号。 -> (setf (aref my-array n) c) Make sure that the variable c has a character and not a string as its value. -> (setf (aref my-array n) c)确保变量c的值是字符而不是字符串。

Burcu, I think you are broadly on the right lines. Burcu,我认为你的思路是正确的。

In Common Lisp, things are nested with parentheses.在 Common Lisp 中,事物是用括号嵌套的。 So, the following doesn't do what you want it to do:因此,以下内容不会执行您想要执行的操作:

(let (c "a"))

The syntax is wrong.语法错误。 Because you may want to declare more than one value, there is one set of parentheses to contain them all, and additionally each declaration is within a pair of parentheses, like this:因为您可能想要声明多个值,所以有一组括号将它们全部包含在内,另外每个声明都在一对括号内,如下所示:

(let ( (first-variable first-value) (second-variable second-value) ) )

; for your case
(let ((c "a")) )

The last parenthesis also contains what you want to happen within the context of the let declaration.最后一个括号还包含您希望在 let 声明的上下文中发生的事情。

(let ((c "a")) do-something)

As it currently stands, your code is syntactically incorrect, and if the required parentheses are added the declaration will come and go without anything happening.就目前而言,您的代码在语法上是不正确的,如果添加了所需的括号,则声明将出现,并且 go 不会发生任何事情。

Next, you lookup each character.接下来,查找每个字符。 You use this code:您使用以下代码:

(setf (aref (my-array n)) c)

I think that you are trying to copy an element of my-array into the variable c .我认为您正在尝试将my-array的元素复制到变量c中。 The setf is the wrong way around, and you have one too many pair of parentheses, having this syntax: setf 是错误的方法,你有一对太多的括号,有这样的语法:

(setf target value)

; in your case
(setf c (aref my-array n))

Then you check against each case, using code like this:然后使用如下代码检查每种情况:

  (if(= c "+")
    (format t "OP_PLUS ~%"))

There are a few problems.有几个问题。 There must be at least one space after if , otherwise it becomes one symbol if(= , which is clearly not your intention. Then, you don't want to use = , which is reserved only for numbers. If c is a string then use string= , and if it is a character then use char= . Every computer language has some ugliness, and I guess that this is Common Lisp's ugliness. Clojure does this particular thing better. if后面必须至少有一个空格,否则它变成一个符号if(= ,这显然不是你的意图。那么,你不想使用= ,它只为数字保留。如果c是一个字符串,那么使用string= ,如果是字符则使用char= 。每种计算机语言都有一些丑陋,我猜这是 Common Lisp 的丑陋。Clojure 在这方面做得更好。

But, let's take a step back, and reassess.但是,让我们退后一步,重新评估一下。 What you're trying to do is to find the matching value for the provided value, so "+" has a matching symbol "OP-PLUS" .您要做的是找到提供值的匹配值,因此"+"有一个匹配符号"OP-PLUS" This is a lookup table, and the types are called alist and plist .这是一个查找表,类型称为alistplist The idea is that you provide a table, and then ask Lisp to search for your value.这个想法是你提供一个表格,然后让 Lisp 搜索你的值。 Probably an alist , something like this:可能是alist ,如下所示:

; Lookup table
(defparameter lookup-table
    '(("+" . "OP_PLUS")
      ("*" . "OP_MULT")
      ("-" . "OP_MINUS")
      ("/" . "OP_DIV")
     ))

; Search for item
(cdr (assoc "+" lookup-table :test #'string=))    ; => "OP_PLUS"
(cdr (assoc "*" lookup-table :test #'string=))    ; => "OP_MULT"
(cdr (assoc "silly" lookup-table :test #'string=)) ; => NIL

Please note, the spaces provided either side of the dot in the alist , and the single quote character, this is mandatory.请注意,在alist中的点两侧提供的空格和单引号字符,这是强制性的。 We have to specify the test with :test .我们必须用:test指定测试。 The assoc function returns a pair, like ("*". "OP_MULT") , and cdr just takes the second value. assoc function 返回一对,例如("*". "OP_MULT") ,而cdr只取第二个值。 If the item cannot be found then it returns NIL .如果找不到该项目,则返回NIL The advantage of doing it this way is that you can simply add new items to the bottom of the list.这样做的好处是您可以简单地将新项目添加到列表的底部。 I would also turn the lookup code into a small function, like this:我还将查找代码变成一个小的 function,如下所示:

(defun lookup (x lookup-table)
   (cdr (assoc x lookup-table :test #'string=)))

You can test the new lookup function in your REPL and ensure that it works.您可以在您的 REPL 中测试新的查找 function 并确保它有效。 Then, you know that at least that piece of your code works, and you have a useful function for next time.然后,您知道至少您的那段代码有效,并且您有一个有用的 function 以供下次使用。

I hope this is useful.我希望这很有用。

In addition to other answers, I think you should be able to simplify your code and separate it into different pieces:除了其他答案之外,我认为您应该能够简化代码并将其分成不同的部分:

  1. you can directly read from a stream, this is more general than a file and you can use other source of streams if necessary您可以直接从 stream 读取,这比文件更通用,如果需要,您可以使用其他流源
  2. don't read all the characters of a file into an array, your problem can be solved by processing characters as you read them不要将文件的所有字符读入数组,您的问题可以通过在读取字符时处理字符来解决
  3. avoid repetition: (format t "...~%") , etc. can be factorized, this helps with readability and maintainability (there is a single place to modify in case the code changes if you factorize things nicely).避免重复: (format t "...~%")等可以因式分解,这有助于提高可读性和可维护性(如果你很好地分解了代码,则可以在一个地方进行修改)。

You can also use a case where clauses are characters, case compare values with eql .您还可以使用子句是字符的case ,将值与eql进行case比较。 NB: there must be some typo in your question, I don't know what OP_OC and OP_CC stands for but their associated strings are just spaces;注意:您的问题中一定有一些错字,我不知道 OP_OC 和 OP_CC 代表什么,但它们相关的字符串只是空格; here instead I use brackets, but feel free to adapt:在这里,我使用括号,但可以随意调整:

(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))))))

For example:例如:

(with-input-from-string (in "*/-()[],***")
  (gpp-interpreter in))

prints:印刷:

OP_MULT
OP_DIV
OP_MINUS
OP_OP
OP_CP
OP_OC
OP_CC
OP_COMMA
OP_DBLMULT
OP_MULT

Instead of using strings, typically in Lisp you would associate symbols for elements in your syntax tree.通常在 Lisp 中,您会为语法树中的元素关联符号,而不是使用字符串。

Also, I would not just write them to the standard output, but accept a callback function that is called on each token, so that the lexer is a bit more generic.此外,我不会只write标准 output,而是接受在每个令牌上调用的回调 function,以便词法分析器更加通用。

(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))))))

For example, here the custom function format them to the output:例如,这里自定义的 function 将它们格式化为 output:

(with-input-from-string (in "*/-()[],***")
  (gpp-interpreter in (lambda (s) (format t "~a~%" s))))

But we can also collect all the read tokens in a vector:但我们也可以在一个向量中收集所有读取的标记:

(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)

First, we build a vector, and for each token being read, the token is pushed at the end of the vector.首先,我们构建一个向量,对于每个被读取的标记,标记被推到向量的末尾。 If there is not enough space in the vector, its size is doubled (it grows by the current array-total-size ).如果向量中没有足够的空间,它的大小会加倍(它会增长当前的array-total-size )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM