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}