You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Racket LL1解析器代码报错:application: not a procedure,求排查

问题排查与修复

1. 直接触发错误的原因:测试用例列表格式错误

你在run-tests的调用中,没有将所有测试用例包裹在一个单个引号列表里。当前写法中,'("1" #t)是一个列表,但后面的("1+1" #t)会被Racket解析为尝试调用字符串"1+1"作为过程,这就导致了application: not a procedure错误。

修正后的测试用例写法

(run-tests '(("1" #t)
             ("1+1" #t)
             ("1 / 3" #t)
             (" √10 " #t)
             ("579438593 + 666 - 555" #t)
             ("1000" #t)
             ("2^64" #t)
             ("√2 + √64 * 233 + 6" #t)
             ("" #f)
             ("aaa" #f)
             ("/44" #f)
             ("579438593 +" #f)
             ("1000 * + 111" #f)
             ("2 + 6 4" #f)
             ("3.14 + 7" #f)
             ("√√2" #f)))

注意所有测试二元组都被包含在'(...)内部,形成一个嵌套列表。

2. 解析器逻辑的其他问题

除了测试用例的错误,你的LL(1)解析器还有几个逻辑缺陷,会导致部分合法表达式被错误判定为无效:

问题1:√的字符匹配错误

你在parse-S和parse-A中使用(equal? (car exp) "√"),但exp是通过string->list生成的字符列表,car exp是单个字符,而"√"是字符串,两者永远不相等。应该改为字符匹配:(char=? (car exp) #\√)。

问题2:未处理多位数数字

当前parse-S仅检查第一个字符是数字,就直接调用parse-A,但后续的数字字符会被parse-A判定为非法(因为parse-A只识别运算符)。需要在parse-S中跳过所有连续的数字字符,再进入parse-A。

问题3:parse-tokens的循环逻辑不完善

原loop函数仅检查开头是否能被parse-S匹配,但未确保parse-S处理完整个表达式后没有剩余非法字符。需要调整为:当parse-S匹配成功后,检查剩余字符是否都是空白或结束。

修正后的完整代码

#lang racket

(define (is-number? str)
  (let ((maybe-number (string->number str)))
    (and maybe-number #t)))

(define (parse-expression expr)
  (define (parse-tokens expr)
    (define (skip-digits exp)
      (cond [(empty? exp) exp]
            [(char-numeric? (car exp)) (skip-digits (cdr exp))]
            [else exp]))
    
    (define (parse-S exp)
      (cond
        [(empty? exp) #f]
        [(char-numeric? (car exp)) 
         (parse-A (skip-digits exp))]
        [(char=? (car exp) #\√) 
         (if (and (not (empty? (cdr exp))) (char-numeric? (cadr exp)))
             (parse-A (skip-digits (cdr exp)))
             #f)]
        [else #f]))
    
    (define (parse-A exp)
      (cond
        [(empty? exp) #t]
        [(char-whitespace? (car exp)) (parse-A (cdr exp))]
        [(member (car exp) '(#\+ #\- #\* #\/ #\^)) 
         (let ([rest (cdr exp)])
           (if (empty? rest)
               #f
               (parse-S rest)))]
        [(char=? (car exp) #\√) 
         (if (and (not (empty? (cdr exp))) (char-numeric? (cadr exp)))
             (parse-A (skip-digits (cdr exp)))
             #f)]
        [else #f]))
    
    (let loop ([expr expr])
      (cond
        [(empty? expr) #t]
        [(char-whitespace? (car expr)) (loop (cdr expr))]
        [else (parse-S expr)]))
  
  (parse-tokens (string->list expr)))

(define (analyze-expression expr)
  (parse-expression expr))

(define (test-expressions)
  (define (run-tests tests)
    (for ([test tests])
      (define result (analyze-expression (first test)))
      (define expected (second test))
      (printf "Expression: ~a, Expected: ~a, Result: ~a -> ~a\n" 
              (first test) expected result (equal? result expected))))
  
  (run-tests '(("1" #t)
               ("1+1" #t)
               ("1 / 3" #t)
               (" √10 " #t)
               ("579438593 + 666 - 555" #t)
               ("1000" #t)
               ("2^64" #t)
               ("√2 + √64 * 233 + 6" #t)
               ("" #f)
               ("aaa" #f)
               ("/44" #f)
               ("579438593 +" #f)
               ("1000 * + 111" #f)
               ("2 + 6 4" #f)
               ("3.14 + 7" #f)
               ("√√2" #f))))

(test-expressions)

关键修改说明

  • 添加skip-digits函数:用于跳过连续的数字字符,支持多位数解析。
  • 修正√的匹配逻辑:使用char=?匹配字符,同时检查√后是否有合法数字。
  • 完善parse-A的空白处理:允许运算符前后有空白字符。
  • 优化测试输出:增加预期结果对比,方便验证正确性。

内容的提问来源于stack exchange,提问作者Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:55:55