以下函数用于计算带括号的中缀表示法:
(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
请注意,此函数不处理优先级,如果没有使用括号,它将从左到右严格解决。
答案 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
会破坏词汇范围。
如果你想要中缀语法而不遇到这些问题,有几个选项,所有这些选项都涉及在编译时转换的语法。这样就可以保留词法范围。
#lang sweet-exp
quote
语言会修改阅读器,以便每当它看到sweet-exp
时,它会将内部表达式作为中缀读取。您可以选择编写{ ... }
而不是{{3 * 6} + 5}
,它将是等效的。
(+ (* 3 6) 5)
(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)
您可以像这样定义一个简单的中缀宏:
#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