简体   繁体   English

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

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

I was going to post this to the codereview stackexchange but I saw that you should only post working code. 我打算将其发布到codereview stackexchange,但我看到您应该仅发布工作代码。 I asked this question earlier: Reordering parentheses using associative property in Racket 我之前问过这个问题: 在Racket中使用关联属性对括号重新排序

In case you don't check the link basically I want to rearrrange a list of symbols so that this: 万一您根本不检查链接,我想重新排列符号列表,这样:

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

or this: 或这个:

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

This is the code I've written so far: 这是我到目前为止编写的代码:

(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)))

Am I on the right track? 我在正确的轨道上吗? If not does anyone have any pointers? 如果没有,没有人有任何指针吗? Am I creating the lists wrong? 我创建的列表有误吗? If you need any more information let me know or if I should comment the code better also let me know. 如果您需要更多信息,请让我知道,或者如果我应该对代码进行更好的注释,请告诉我。

In case you don't check the earlier question, this is the answer I got (which was very helpful): 如果您不检查前面的问题,这是我得到的答案(非常有帮助):

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))))

The following is the grammar you have defined for your syntax: 以下是您为语法定义的语法:

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

As such, we can start by defining predicates that test whether a given expression is valid or not, using the rules set above: 这样,我们可以使用上面设置的规则从定义谓词开始以测试给定表达式是否有效:

(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))))))

Now we can say, for example: 现在我们可以说,例如:

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

With that in place, rewrite can be written as the right-associative form of an expression, if the expression is valid fpip, and #f otherwise: 如果表达式有效,则rewrite可以写为表达式的右关联形式,如果该表达式是有效的fpip,则为#f

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

Next, we will define rewrite-fpip to be a procedure that accepts and transforms any valid fpip, as follows: 接下来,我们将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)))))))

Thus we can have: 因此,我们可以拥有:

> (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)))))

That they tell you not to use the flattening in your solution doesn't mean you can't use the flattening in the derivation of your solution. 他们告诉您不要在解决方案中使用展并不意味着您不能在解决方案的派生中使用展平。

Writing in an imaginary pattern-matching equational pseudocode (because it is much shorter and visually apparent, ie easier to follow), 用虚构的模式匹配方程式伪代码编写代码(因为它更短,更直观,也更容易理解),

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 []

Oh wait, I'm not flattening it here, I am building the normalized sum expressions here! 哦,等一下,我不是在这里压扁了, 在这里建设正规化和表情!

The only problem with this approach is the needless check for [] repeated over and over when we know it will only ever be true once -- it is we who write this code after all. 这种方法的唯一问题是,当我们知道 一次仅适用于一次时,便会反复检查[] ,而不必进行检查-毕竟是我们编写了这段代码。

Fusing this knowledge in, we get 融合这些知识,我们得到

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

Now all's left is to code this up in regular Scheme. 现在剩下的就是用常规Scheme编写代码了。 Scheme also has the advantage that the test can be much simplified to just 方案还具有可以将测试简化为

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

edit: The final function from the other answer in the same pseudocode is: 编辑:同一伪代码中另一个答案的最终功能是:

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

It rearranges the + nodes' tree structure before starting the work 1 , whereas the solution in this answer follows the input structure while transforming it. 开始工作1 之前重新排列+节点的树结构,而此答案中的解决方案在转换输入结构遵循输入结构。 As the result, thanks to the nested recursion the run time stack will only be as deep as the input structure, whereas with rewrite it will always be n levels deep at the deepest point, when the list is fully linearized on the stack (in the second rule), just before the sums are assembled on the way back up. 结果,由于嵌套的递归,运行时堆栈将仅与输入结构一样深,而通过rewrite ,当列表在堆栈上完全线性化时,它在最深处始终为n层深度。第二条规则),即在将总和存入备份路径之前。

But the first rule in rewrite is tail recursive, and the second is tail recursive modulo cons , so rewrite can be rewritten in a tail-recursive style as a whole, with few standard modifications. 但是rewrite的第一个规则是尾递归,而第二个规则是尾递归模态cons ,因此rewrite可以整体上以尾递归样式进行重写,几乎不需要标准修改。 Which is definitely a plus. 这绝对是一个加号。

On the other hand this new code will have to surgically modify (ie mutate) the + nodes (see the Wikipedia article linked above, for details), so you'll have to choose your implementation of this data type accordingly. 另一方面,此新代码将必须通过外科手术修改(即变异) +节点(有关详细信息,请参见上面链接的Wikipedia文章),因此您必须相应地选择此数据类型的实现。 If you use lists, this means using set-car! 如果使用列表,则意味着使用set-car! and/or set-cdr! 和/或set-cdr! ; ; otherwise you can implement them as Racket's #:mutable structures . 否则,您可以将它们实现为Racket的#:mutable结构 When the result is built, you could convert it to a regular list with an additional O(n) traversal, if needed. 构建结果后,如果需要,可以将其转换为带有附加O(n)遍历的常规列表。


1 reminiscent of the old gopher trick from John McCarthy, burrowing into the input structure, with reified continuations. 1使人联想起约翰·麦卡锡(John McCarthy) 的古老gopher把戏 ,钻研了输入结构,并进行了延续。

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

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