我試圖檢查 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 ~%"))
)
)
uj5u.com熱心網友回復:
=用于比較數字,而不是字符。使用eql,char=或char-equal。
使用字符代替字串進行比較:"a"->#\a
使用case而不是多個ifs。
(setf (aref (my-array n)) c)有一對額外的括號。->(setf (aref my-array n) c)
確保變數的值c是字符而不是字串。
uj5u.com熱心網友回復:
除了其他答案之外,我認為您應該能夠簡化代碼并將其分成不同的部分:
- 您可以直接從流中讀取,這比檔案更通用,如有必要,您可以使用其他流源
- 不要將檔案的所有字符讀入陣列,您的問題可以通過在讀取字符時處理字符來解決
- 避免重復:
(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))))))
例如:
(with-input-from-string (in "*/-()[],***")
(gpp-interpreter in))
印刷:
OP_MULT
OP_DIV
OP_MINUS
OP_OP
OP_CP
OP_OC
OP_CC
OP_COMMA
OP_DBLMULT
OP_MULT
通常在 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))))))
例如,這里的自定義函式將它們格式化為輸出:
(with-input-from-string (in "*/-()[],***")
(gpp-interpreter in (lambda (s) (format t "~a~%" s))))
但是我們也可以在一個向量中收集所有讀取標記:
(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)
首先,我們構建一個向量,對于讀取的每個標記,標記被推送到向量的末尾。如果向量中沒有足夠的空間,則其大小將加倍(按當前 增長array-total-size)。
uj5u.com熱心網友回復:
Burcu,我認為您大致上是正確的。
在 Common Lisp 中,事物用括號嵌套。因此,以下內容不會執行您希望它執行的操作:
(let (c "a"))
語法錯誤。因為你可能想要宣告多個值,所以有一組括號來包含它們,另外每個宣告都在一對括號內,如下所示:
(let ( (first-variable first-value) (second-variable second-value) ) )
; for your case
(let ((c "a")) )
最后一個括號還包含您希望在 let 宣告的背景關系中發生的事情。
(let ((c "a")) do-something)
就目前而言,您的代碼在語法上是不正確的,如果添加了所需的括號,則宣告會來來去去,而不會發生任何事情。
接下來,您查找每個字符。您使用此代碼:
(setf (aref (my-array n)) c)
我認為您正試圖將 的元素復制my-array到變數中c。setf 是錯誤的方法,您有一對太多的括號,具有以下語法:
(setf target value)
; in your case
(setf c (aref my-array n))
然后您檢查每種情況,使用如下代碼:
(if(= c " ")
(format t "OP_PLUS ~%"))
有幾個問題。后面必須至少有一個空格if,否則就成了一個符號if(=,這顯然不是你的本意。然后,您不想使用=,它僅為數字保留。如果c是字串則使用string=,如果是字符則使用char=。每種計算機語言都有一些丑陋之處,我想這就是 Common Lisp 的丑陋之處。Clojure 在這方面做得更好。
但是,讓我們退后一步,重新評估。您要做的是為提供的值找到匹配的值,因此" "有一個匹配的符號"OP-PLUS"。這是一個查找表,型別稱為alistand plist。這個想法是你提供一個表,然后讓 Lisp 搜索你的值。可能是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
請注意, 中的點兩側提供的空格alist和單引號字符,這是強制性的。我們必須用 指定測驗:test。該assoc函式回傳一個對,例如("*" . "OP_MULT"),并且cdr只取第二個值。如果找不到該專案,則回傳NIL。這樣做的好處是您可以簡單地將新專案添加到串列底部。我還將查找代碼變成一個小函式,如下所示:
(defun lookup (x lookup-table)
(cdr (assoc x lookup-table :test #'string=)))
您可以在 REPL 中測驗新的查找功能并確保它有效。然后,您知道至少您的那段代碼有效,并且下次您有一個有用的功能。
我希望這是有用的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/361798.html
上一篇:計算物件中的每個重復值并增加
