我為 OCaml 中的基本算術運算式撰寫了一個尾遞回掃描器
句法
Exp ::= n | Exp Op Exp | (Exp)
Op ::= | - | * | /
type token =
| Tkn_NUM of int
| Tkn_OP of string
| Tkn_LPAR
| Tkn_RPAR
| Tkn_END
exception ParseError of string * string
let tail_tokenize s =
let rec tokenize_rec s pos lt =
if pos < 0 then lt
else
let c = String.sub s pos 1 in
match c with
| " " -> tokenize_rec s (pos-1) lt
| "(" -> tokenize_rec s (pos-1) (Tkn_LPAR::lt)
| ")" -> tokenize_rec s (pos-1) (Tkn_RPAR::lt)
| " " | "-" | "*" | "/" -> tokenize_rec s (pos-1) ((Tkn_OP c)::lt)
| "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ->
(match lt with
| (Tkn_NUM n)::lt' ->
(let lta = Tkn_NUM(int_of_string (c^(string_of_int n)))::lt' in
tokenize_rec s (pos-1) lta)
| _ -> tokenize_rec s (pos-1) (Tkn_NUM (int_of_string c)::lt)
)
|_ -> raise (ParseError ("Tokenizer","unknown symbol: "^c))
in
tokenize_rec s (String.length s) [Tkn_END]
在執行期間我得到
tail_tokenize "3 4";;
Exception: Invalid_argument "String.sub / Bytes.sub".
uj5u.com熱心網友回復:
你的例子是這樣的:
tail_tokenize "3 4"
第一個呼叫將如下所示:
tokenize_rec "3 4" 3 Tkn_END
由于 3 不小于 0,因此內部的第一個呼叫tokenize_rec將如下所示:
String.sub "3 4" 3 1
如果你自己嘗試這個,你會發現它是無效的:
# String.sub "3 4" 3 1;;
Exception: Invalid_argument "String.sub / Bytes.sub".
向后處理字串似乎有點奇怪,但要做到這一點,您需要從String.length s - 1.
uj5u.com熱心網友回復:
從錯誤訊息中可以清楚地看出這String.sub是問題所在。它的引數是s,pos最后1一個是常量,另外兩個直接來自函式引數。使用替換實際值的引數單獨運行它可能是一個好主意:
let s = "3 4" in
String.sub s (String.length s) 1
這樣做我們再次得到同樣的錯誤,希望現在清楚原因:你試圖從最后一個字符中獲取長度為 1 的子字串,這意味著它將嘗試越過字串的末尾,當然它可以不。
從邏輯上講,您可能會嘗試從中減去 1 pos,以便從最后一個字符之前開始獲取長度為 1 的子字串。但是你又得到了同樣的錯誤。那是因為您的終止條件是pos < 0,這意味著您將嘗試運行String sub s (0 - 1) 1。因此,您也需要調整終止條件。但是一旦你做到了,你應該是好的!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/429475.html
下一篇:C#比較字串中的字符
