Scheme 使用中缀符号的算术计算
以下函数用于计算带括号的中缀符号:Scheme 使用中缀符号的算术计算,scheme,racket,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))
(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)
(define L '((3 * 6) + 5 + (2 - (2 * (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...:
如何将“”*
识别为纠正此错误的函数
编辑:
我使用了“*etc”的“match”
,现在这个短函数运行良好:
(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 (list (list 3 * 6) + 5 + (list 2 - (list 2 * (list 21 / 3)))))
(mycalc L)
(define L '((3 * 6) + 5 + (2 - (2 * (21 / 3)))))
(mycalc L)
输出:
11
请注意,此函数不处理优先级,如果不使用括号,它将严格从左到右求解 详细说明@Alex Knauth的答案:在第一种情况下,使用
list
构建列表,单个列表元素不被引用,因此被评估<代码>(显示L)应产生大致如下的输出(具体细节因实现而异):
为了详细说明@Alex Knauth的回答:在第一种情况下,使用
list
构建列表,单个列表元素不被引用,因此被评估<代码>(显示L)应产生大致如下的输出(具体细节因实现而异):
您尝试的主要问题是使用的是
quote
,正如您所知,它将自己“分布”在列表的元素上,因此您最终将'(2*x)
转换为(列表“2'*'x)
这有两个问题,
'*
和'x
。这里的“*
不是一个函数;它是一个符号,类似于字符串,因为它与函数*
没有关系,只是它的字符恰好与绑定到*
函数的标识符名称匹配。这同样适用于'x
,只是它甚至不是已知的东西:你甚至无法查找它来找到它
两者的根:quote
破坏词法范围
如果您希望中缀语法不会遇到这些问题,那么有几个选项,所有这些选项都涉及在编译时转换的语法。这样就保留了词法范围
1:
sweet exp
语言修改读取器,以便每当它看到{…}
时,它都以中缀形式读取其中的表达式。您可以选择编写{{3*6}+5}
,而不是(+(*36)5)
,这是等效的
#lang sweet-exp racket
(define (f x)
{2 * x})
(define (p x)
{{3 * (sqr x)} + {6 * x} + 5})
2:
infix
库将$
作为宏提供,这样@${…}
将被解释为infix,因此您可以选择编写@${(3*6)+5}
而不是(+3*6)5)
3:定义自己的宏
您可以这样定义一个简单的中缀宏:
#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
将模式分组在一起
(define-syntax-parser infix
#:literals [+ *]
#:datum-literals [^]
[(_ n:number) #'n]
[(_ x:id) #'x]
[(_ (a:expr (~seq + b:expr) ...+)) #'(+ (infix a) (infix b) ...)]
[(_ (a:expr (~seq * b:expr) ...+)) #'(* (infix a) (infix b) ...)]
[(_ (a:expr ^ b:expr)) #'(expt (infix a) (infix b))])
(define (f x)
(infix (2 * x)))
(define (p x)
(infix ((3 * (x ^ 2)) + (6 * x) + 5)))
您尝试的主要问题是使用的是
quote
,正如您所知,它将自己“分布”在列表的元素上,因此您最终将'(2*x)
转换为(列表“2'*'x)
这有两个问题,
'*
和'x
。这里的“*
不是一个函数;它是一个符号,类似于字符串,因为它与函数*
没有关系,只是它的字符恰好与绑定到*
函数的标识符名称匹配。这同样适用于'x
,只是它甚至不是已知的东西:你甚至无法查找它来找到它
两者的根:quote
破坏词法范围
如果您希望中缀语法不会遇到这些问题,那么有几个选项,所有这些选项都涉及在编译时转换的语法。这样就保留了词法范围
1:
sweet exp
语言修改读取器,以便每当它看到{…}
时,它都以中缀形式读取其中的表达式。您可以选择编写{{3*6}+5}
,而不是(+(*36)5)
,这是等效的
#lang sweet-exp racket
(define (f x)
{2 * x})
(define (p x)
{{3 * (sqr x)} + {6 * x} + 5})
2:
infix
库将$
作为宏提供,这样@${…}
将被解释为infix,因此您可以选择编写@${(3*6)+5}
而不是(+3*6)5)
3:定义自己的宏
您可以这样定义一个简单的中缀宏:
#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
将模式分组在一起
(define-syntax-parser infix
#:literals [+ *]
#:datum-literals [^]
[(_ n:number) #'n]
[(_ x:id) #'x]
[(_ (a:expr (~seq + b:expr) ...+)) #'(+ (infix a) (infix b) ...)]
[(_ (a:expr (~seq * b:expr) ...+)) #'(* (infix a) (infix b) ...)]
[(_ (a:expr ^ b:expr)) #'(expt (infix a) (infix b))])
(define (f x)
(infix (2 * x)))
(define (p x)
(infix ((3 * (x ^ 2)) + (6 * x) + 5)))
”*
不是一个函数;它是一个符号,类似于字符串,因为它与函数*
没有关系,只是它的字符恰好与绑定到*
函数的标识符名称匹配。如果要使用中缀语法,有几个选项。您可以使用并编写{{3*6}+5}
,或者您可以使用并编写@${(3*6)+5}
,或者您可以使用宏来定义中缀语法。但无论您如何使用中缀语法,我建议您避免使用引号
,您可以将这些有用的注释合并到一个答案中。读者也更倾向于阅读答案而不是评论。因此,我已经将此作为一个答案,提供了更多的例子,并解释了quote
是如何破坏它的“*
不是一个函数;它是一个符号,类似于字符串,因为它与函数*
没有关系,只是它的字符恰好与绑定到*
函数的标识符名称匹配。如果要使用中缀语法,有几个选项。您可以使用并编写{{3*6}