繁体   English   中英

在球拍中将列表重新排列为左标准形式

[英]Rearranging list into left-normal form in Racket

我打算将其发布到codereview stackexchange,但我看到您应该仅发布工作代码。 我之前问过这个问题: 在Racket中使用关联属性对括号重新排序

万一您根本不检查链接,我想重新排列符号列表,这样:

'((a + b) + c) -> '(a + (b + c))

或这个:

'((a + b) + (c + d)) -> '(a + (b + (c + d)))

这是我到目前为止编写的代码:

(define (check? expr)
  (display expr)
  (newline)
  (cond ((null? expr) -1)
        ((and (atom? (car expr)) (atom? (cadr expr))) 0) ;case 0
        ((and (atom? (car expr)) (list? (cadr expr))) 1) ;case 1
        ((and (list? (car expr)) (null? (cdr expr))) (check? (car expr))) ;nested expression for example '((a b))
        ((and (list? (car expr)) (atom? (cadr expr))) 2) ;case 2
        ((and (list? (car expr)) (list? (cadr expr))) 3) ;case 3
        (else -1)))


(define (rewrite x)
  (display (check? x))
  (newline)
  (cond ((null? x))
        ((atom? x) x)
        ((= 0 (check? x))   x) ;case 0 is '(a + b)
        ((= 1 (check? x)) (cons (car x) (rewrite (cdr x)))) ;case 1 is '(a +  (b + c))
        ((= 2 (check? x)) (rewrite (list (caar x) (cons (cadar x) (cdr x))))) ;case 2 is ((b + c) + a)
        ((= 3 (check? x)) (rewrite ((list (caar x) (cons (cadar x) (cdr x))))))));case 3 is ((a + b) + (c + d))


;(rewrite '(((d c) b) a))
(rewrite '(a b))
(rewrite '(a (b c)))
(rewrite '((a b) (c d)))

我在正确的轨道上吗? 如果没有,没有人有任何指针吗? 我创建的列表有误吗? 如果您需要更多信息,请让我知道,或者如果我应该对代码进行更好的注释,请告诉我。

如果您不检查前面的问题,这是我得到的答案(非常有帮助):

var                               -> var
(var + var)                       -> (var + var)
(var + (fip1 + fpip2))            -> (var + (REWRITE (fip1 + fpip2))
((fip1 + fpip2) + var)            -> (REWRITE (fip1 + (fip2 + var))
((fip1 + fpip2) + (fip3 + fpip4)) -> (REWRITE (fip1 + (fip2 + (fip3 + fip4))))

以下是您为语法定义的语法:

var  ::=  a | b | c | d | e | f | g
fpip ::=  var | (fpip + fpip)

这样,我们可以使用上面设置的规则从定义谓词开始以测试给定表达式是否有效:

(define (var? e)
  (member e '(a b c d e f g)))

(define (fpip? e)
  (cond
    ((var? e) #t)
    ((or (not (pair? e))
         (null? e)
         (null? (cdr e))
         (null? (cddr e))
         (not (null? (cdddr e))))
     #f)
    (else (and (fpip? (car e))
               (equal? (cadr e) '+)
               (fpip? (caddr e))))))

现在我们可以说,例如:

> (fpip? 'a)
#t
> (fpip? '((a + b) + c))
#t
> (fpip? '((+(d + e) + f) + (a + (a + c))))
#f

如果表达式有效,则rewrite可以写为表达式的右关联形式,如果该表达式是有效的fpip,则为#f

(define (rewrite e)
  (if (not (fpip? e))
      #f
      (rewrite-fpip e)))

接下来,我们将rewrite-fpip定义为一个接受并转换任何有效 fpip的过程,如下所示:

(define (rewrite-fpip e)
  (cond
    ((not (pair? e)) e)                                         ;; var
    ((not (pair? (car e)))
     (list (car e) '+ (rewrite-fpip (caddr e))))                ;; (var + fpip)
    (else
     (rewrite-fpip                                              ;; (fpip + fpip)
      (list (caar e) '+ (list (caddar e) '+ (caddr e)))))))

因此,我们可以拥有:

> (rewrite 'a)
'a
> (rewrite '((a + b) + c))
'(a + (b + c))
> (rewrite '((a + b) + (c + d)))
'(a + (b + (c + d)))
> (rewrite '(((d + e) + f) + (a + (a + c))))
'(d + (e + (f + (a + (a + c)))))

他们告诉您不要在解决方案中使用展并不意味着您不能在解决方案的派生中使用展平。

用虚构的模式匹配方程式伪代码编写代码(因为它更短,更直观,也更容易理解),

flatten a         = flatten2 a []               ; [] is "an empty list"
flatten2 (a+b) z  = flatten2 a (flatten2 b z)   ; if it matches (a+b)
flatten2 a     [] = a                           ; if it doesn't, and the 2nd is []
flatten2 a     b  = a + b                       ; same, and the 2nd arg is not []

哦,等一下,我不是在这里压扁了, 在这里建设正规化和表情!

这种方法的唯一问题是,当我们知道 一次仅适用于一次时,便会反复检查[] ,而不必进行检查-毕竟是我们编写了这段代码。

融合这些知识,我们得到

normalize  a   = down a             ; turn EXPR ::= ATOM | EXPR + EXPR
down (a + b)   = up a (down b)
down a         = a                  ; into NormExpr ::= ATOM | ATOM + NormExpr
up   (a + b) z = up a (up b z)
up   a       b = a + b

现在剩下的就是用常规Scheme编写代码了。 方案还具有可以将测试简化为

(define (is-sum? a+b) (pair? a+b))

编辑:同一伪代码中另一个答案的最终功能是:

rewrite ((a + b) + c) = rewrite (a + (b + c))     ; rotate right!
rewrite (a + b)       = a + rewrite b             ; go in, if the first rule didn't match
rewrite a             = a                         ; stop, if the first two didn't match

开始工作1 之前重新排列+节点的树结构,而此答案中的解决方案在转换输入结构遵循输入结构。 结果,由于嵌套的递归,运行时堆栈将仅与输入结构一样深,而通过rewrite ,当列表在堆栈上完全线性化时,它在最深处始终为n层深度。第二条规则),即在将总和存入备份路径之前。

但是rewrite的第一个规则是尾递归,而第二个规则是尾递归模态cons ,因此rewrite可以整体上以尾递归样式进行重写,几乎不需要标准修改。 这绝对是一个加号。

另一方面,此新代码将必须通过外科手术修改(即变异) +节点(有关详细信息,请参见上面链接的Wikipedia文章),因此您必须相应地选择此数据类型的实现。 如果使用列表,则意味着使用set-car! 和/或set-cdr! ; 否则,您可以将它们实现为Racket的#:mutable结构 构建结果后,如果需要,可以将其转换为带有附加O(n)遍历的常规列表。


1使人联想起约翰·麦卡锡(John McCarthy) 的古老gopher把戏 ,钻研了输入结构,并进行了延续。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM