使用中缀表示法进行算术计算

时间:2017-02-14 04:54:46

标签: scheme racket

以下函数用于计算带括号的中缀表示法:

(define (mycalc ll)
  (cond
    [(= 1 (length ll))
     (first ll)]

    [(empty? (flatten(rest ll)))
     (first ll)]

    [(= 2 (length ll))
     (printf "Error: only 2 elements: ~a" ll)
     (exit)]

    [(list? (first ll))
     (mycalc (append (list (mycalc (first ll)) (second ll) (third ll)) (rest(rest(rest ll)))))]

    [(list? (third ll))
     (mycalc (append (list (first ll) (second ll) (mycalc (third ll)) (rest(rest(rest ll))))))]

    [(= 3 (length ll))
     ((second ll) (first ll) (third ll))]

    [else
     (mycalc (append (list ((second ll) (first ll) (third ll))) (rest(rest(rest ll)))))]))

测试:

(define L (list (list 3 * 6) + 5 + (list 2 - (list 2 * (list 21 / 3)))))
(mycalc L)

输出:

11

但是,它不适用于以下版本的列表:

(define L '((3 * 6) + 5 + (2 - (2 * (21 / 3)))))
(mycalc L)

以下是错误:

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: '*
  arguments...:

如何将'*识别为纠正此错误的函数?

编辑:

我将'match'用于'* etc,现在这个简短的功能效果很好:

(define (mycalc ll)
  (define (solve a b c)
    (match b
      ['+ (+ a c)]
      ['- (- a c)]
      ['* (* a c)]
      ['/ (/ a c)]))
  (cond
    [(= 1 (length ll))
     (first ll)]
    [(list? (first ll))
     (mycalc (cons (mycalc (first ll))
                   (rest ll)))]
    [(list? (third ll))
     (mycalc (append (list (first ll)
                           (second ll)
                           (mycalc (third ll)))
                     (rest(rest(rest ll)))))]
    [else
     (mycalc (cons (solve (first ll) (second ll) (third ll))
                   (rest(rest(rest ll)))))]))

测试:

(define L '((3 * 6) + 5 + (2 - (2 * (21 / 3)))))
(mycalc L)

输出:

11

请注意,此函数不处理优先级,如果没有使用括号,它将从左到右严格解决。

2 个答案:

答案 0 :(得分:1)

详细说明@Alex Knauth的答案:在第一种情况下,使用list来构建列表,不会引用各个列表元素,因此会对其进行评估。 (display L)应该产生或多或少的输出(详细信息因实现而异):

((3 #<procedure * (#:optional _ _ . _)> 6) #<procedure + (#:optional _ _ . _)>
  5 #<procedure + (#:optional _ _ . _)> (2 #<procedure - (#:optional _ _ . _)> 
 (2 #<procedure * (#:optional _ _ . _)> (21 #<procedure / (#:optional _ _ . _)> 3))))

您可以清楚地看到,该列表包含各种程序。然而,在引用整个列表的第二种情况下,不评估各个列表元素。因此,该列表不包含任何过程,只包含符号,(display L)生成此:

((3 * 6) + 5 + (2 - (2 * (21 / 3))))

答案 1 :(得分:1)

您尝试的主要问题是您正在使用quote,正如您所知道的那样,“{1}}会对列表中的元素进行”分发“,因此您最终会'(2 * x)转换为{{1} }}

(list '2 '* 'x)

这有两个问题:#lang racket (define (f x) (my-calc '(2 * x))) ; this is completely broken because of quote '*。这里'x不是函数;它是一个符号,类似于一个字符串,因为它与函数'*无关,只是它的字符恰好与绑定到*函数的标识符名称相匹配。 *也是如此,除了它甚至不是一个已知的东西:你甚至无法查找它!

两者的根源:'x会破坏词汇范围。

如果你想要中缀语法而不遇到这些问题,有几个选项,所有这些选项都涉及在编译时转换的语法。这样就可以保留词法范围。

1:#lang sweet-exp

quote语言会修改阅读器,以便每当它看到sweet-exp时,它会将内部表达式作为中缀读取。您可以选择编写{ ... }而不是{{3 * 6} + 5},它将是等效的。

(+ (* 3 6) 5)

2:(require infix)

#lang sweet-exp racket (define (f x) {2 * x}) (define (p x) {{3 * (sqr x)} + {6 * x} + 5}) 库提供infix作为宏,$将被解释为中缀,因此您可以选择编写@${ ... }而不是@${(3 * 6) + 5} }。

(+ (* 3 6) 5)

3:定义自己的宏

您可以像这样定义一个简单的中缀宏:

#lang at-exp racket
(require infix)

(define (f x)
  @${2 * x})

(define (p x)
  @${(3 * x^2) + (6 * x) + 5})

如果您希望将#lang racket (require syntax/parse/define) (define-syntax-parser infix #:literals [+ *] #:datum-literals [^] [(_ n:number) #'n] [(_ x:id) #'x] [(_ (a:expr + b:expr)) #'(+ (infix a) (infix b))] [(_ (a:expr * b:expr)) #'(* (infix a) (infix b))] [(_ (a:expr ^ b:expr)) #'(expt (infix a) (infix b))]) +多次链接在一起,例如*,则可以使用(x + y + z)扩展宏以重复一次或多次和...+用于将模式分组在一起。

~seq