【问题标题】:The Little Schemer: stuck on multiinsertLR&co example小计划者:坚持 multiinsertLR&co 示例
【发布时间】:2012-08-26 01:36:05
【问题描述】:

经过一些工作,我能够理解 multirember&co 函数,但我无法从以下 multiinsertLR&co 代码中真正理解(第 143 页):

(define multiinsertLR&co
  (lambda (new oldL oldR lat col)
    (cond
     ((null? lat)
      (col '() 0 0))
     ((eq? (car lat) oldL)
      (multiinsertLR&co
       new
       oldL
       oldR
       (cdr lat)
       (lambda (newlat L R) 
         (col (cons new
                    (cons oldL newlat))
              (add1 L) R))))
     ((eq? (car lat) oldR)
      (multiinsertLR&co
       new
       oldL
       oldR
       (cdr lat)
       (lambda (newlat L R)
         (col (cons oldR
                    (cons new newlat))
              L (add1 R)))))
     (else
      (multiinsertLR&co
       new
       oldL
       oldR
       (cdr lat)
       (lambda (newlat L R)
         (col (cons (car lat)
                    newlat) L R)))))))

这本书似乎没有解释在评估函数时最初应该通过哪个collector,所以我使用了a-friend 收集器和last-friend 收集器,分别在第138 页和第140 页解释。使用任一收集器评估函数会导致以下错误(使用带有 petit chez 方案的跟踪函数):

>> (multiinsertLR&co 'salty 'fish 'chips '(chips and fish or fish and chips) last-friend)

|(multiinsertLR&co salty fish chips (chips and fish or fish and chips)
   #<procedure>)
|(multiinsertLR&co salty fish chips (and fish or fish and chips)
   #<procedure>)
|(multiinsertLR&co salty fish chips (fish or fish and chips) #<procedure>)
|(multiinsertLR&co salty fish chips (or fish and chips) #<procedure>)
|(multiinsertLR&co salty fish chips (fish and chips) #<procedure>)
|(multiinsertLR&co salty fish chips (and chips) #<procedure>)
|(multiinsertLR&co salty fish chips (chips) #<procedure>)
|(multiinsertLR&co salty fish chips () #<procedure>)
Exception: incorrect number of arguments to #<procedure>
Type (debug) to enter the debugger.

我多次查看代码,但找不到错误。如果有人有任何见解,请分享。如果有人也可以通过一些好的例子向我指出(相对而言)对延续的温和解释,那也将不胜感激。

【问题讨论】:

    标签: lambda scheme continuations the-little-schemer


    【解决方案1】:

    这是我解决 Chris 的第二个示例所经历的过程,因为我认为这可能对其他可能被卡住的人有所帮助。 (如有错误请指正!)

    ;; Anonymous functions (collectors) in mutliinsertLR&co here defined as named  
    ;; functions for reference purposes
    (define left-col
      (lambda (newlat L R)
        (col (cons new
                   (cons oldL newlat))
             (add1 L) R)))              ; L counter gets incremented
    
    (define right-col
      (lambda (new lat L R)
        (col (cons oldR
                   (cons new newlat))
             L (add1 R))))              ; R counter gets incremented
    
    (define else-col
      (lambda (newlat L R)
        (col (cons (car lat)
                   (newlat) L R))))     ; neither counter gets incremented
    
    ;; Let's step through an example to see how this works:
    ;;
    ;; 1. ENTRY TO FUNCTION
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (goo bar baz qux quux)
    ;; col  = list
    ;;
    ;; 1. Is lat null? No.
    ;; 2. Is car of lat equal to oldL? No.
    ;; 3. Is car of lat equal to oldR? No.
    ;; 4. Else? Yes. Recur on the function passing the new, oldL
    ;;    oldR, (cdr lat) and the new collector.
    ;;
    ;; 2. FIRST RECURSIVE STEP
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (bar baz qux quux)
    ;; col  = else-col
    ;;
    ;; - Is lat null? No.
    ;; - Is car of lat equal to oldL? Yes. Recur on the function
    ;;   passing new, oldL, oldR, (cdr lat), and the new col.
    ;;
    ;; 3. SECOND RECURSIVE STEP
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (baz qux quux)
    ;; col  = left-col
    ;;
    ;; - Is lat null? No.
    ;; - Is car of lat equal to oldL? No.
    ;; - Is car of lat equal to oldR? Yes. Recur on the function
    ;;   passing new, oldL, oldR, (cdr lat), and the new col.
    ;;
    ;; 4. THIRD RECURSIVE STEP
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (qux quux)
    ;; col  = right-col
    ;;
    ;; - Is lat null? No.
    ;; - Is car of lat equal to oldL? No.
    ;; - Is car of lat equal to oldR? No.
    ;; - Else? Yes. Recur on the function passing new, oldL, oldR,
    ;;   (cdr lat) and the new collector.
    ;;
    ;; 5. FOURTH RECURSIVE STEP
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (quux)
    ;; col  = else-col
    ;;
    ;; - Is the lat null? No.
    ;; - Is the car of lat equal to oldL? No.
    ;; - Is the car of lat equal to oldR? No.
    ;; - Else? Yes. Recur on the function passing new, oldL, oldR,
    ;;   (cdr lat) and the new collector.
    ;;
    ;; 6. FIFTH RECURSIVE STEP
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = ()
    ;; col  = else-col
    ;;
    ;; - Is the lat null? Yes. Call else-col, where newlat is (),
    ;;   L is 0 and R is 0.
    ;;
    ;; 7. ELSE-COL
    ;; newlat = ()
    ;; L      = 0
    ;; R      = 0
    ;; col    = else-col
    ;;
    ;; - Now call else-col on the (cons (car lat)), which is currently
    ;;   stored in memory as the atom "quux", onto newlat, as well as
    ;;   L and R.
    ;;
    ;; 8. ELSE-COL (again)
    ;; newlat = (quux)
    ;; L      = 0
    ;; R      = 0
    ;; col    = right-col
    ;;
    ;; - Now call right-col on the (cons (car lat)), which is currently
    ;;   stored in memory as the atom "qux", onto newlat, as well as L
    ;;   and R.
    ;;
    ;; 9. RIGHT-COL
    ;; newlat = (qux quux)
    ;; L      = 0
    ;; R      = 0
    ;; col    = left-col
    ;;
    ;; - Now call left-col on the cons of oldR and (cons new newlat),
    ;;   as well as L and the increment of R.
    ;;
    ;; 10. LEFT-COL
    ;; newlat = (baz foo qux quux)
    ;; L      = 0
    ;; R      = 1
    ;; col    = else-col
    ;;
    ;; - Now call else-col on the cons of new and (cons oldL newlat),
    ;;   as well as the increment of L and R.
    ;;
    ;; 11. ElSE-COL (last time)
    ;; newlat = (foo bar baz foo qux quux)
    ;; L      = 1
    ;; R      = 1
    ;; col    = list
    ;;
    ;; - Now we call list on the (cons (car lat)), which is currently
    ;;   stored in memory as the atom "goo", onto newlat, as well as L
    ;;   and R.
    ;; - This gives us the final, magical list:
    ;;   ((goo foo bar baz foo qux quux) 1 1)
    ;;
    ;; 12. FIFTH RECURSIVE STEP (redux)
    ;; new  = foo
    ;; oldL = bar
    ;; oldR = baz
    ;; lat  = (quux)
    ;; col  = else-col
    ;;
    ;; - Base case evaluated, with the result being
    ;;   ((goo foo bar baz foo qux quux) 1 1).
    ;; - Function ends.
    ;;
    ;; THE END
    

    【讨论】:

      【解决方案2】:

      如代码中的基本情况所示,您的收集器/延续必须采用 3 个参数。看来你传入的函数没有。


      很高兴你通过multirember&amp;co 工作:我写了一个关于它的答案,which you may wish to review

      无论如何,测试这些收集器/延续的一般方法是首先使用list 作为延续,因为list 是可变参数并简单地返回作为列表给出的项目。这是一个例子:

      > (multiinsertLR&co 'foo 'bar 'baz '(foo bar baz qux quux) list)
      '((foo foo bar baz foo qux quux) 1 1)
      

      哦,我看到两个foos!插入了哪一项,哪一项在原始列表中?

      > (multiinsertLR&co 'foo 'bar 'baz '(goo bar baz qux quux) list)
      '((goo foo bar baz foo qux quux) 1 1)
      

      啊,所以我们开始做点什么了!只要列表包含barfoo 就会插入到它之前;并且只要列表包含bazfoo 就会在其后插入。但是数字呢?

      > (multiinsertLR&co 'foo 'bar 'baz '(baz goo bar baz qux bar quux baz) list)
      '((baz foo goo foo bar baz foo qux foo bar quux baz foo) 2 3)
      

      这些数字看起来像计数器!每次“左”插入都会增加第一个计数器,每次“右”插入都会增加第二个计数器。


      在查看代码时,如果查看cond 的每个分支,您将看到左匹配情况、右匹配情况和不匹配情况(以及当然,一个列表结尾的情况,它设置了基本/初始值)。注意在每种情况下插入(和计数器增量)是如何发生的。既然你已经得到了multirember&amp;co,从这里开始这应该相当容易。万事如意!

      【讨论】:

      • 是的,你完全正确。我再次查看了堆栈跟踪,发现它在终端条件下失败了——事实上,a-friendlast-friend 函数使用两个参数而不是所需的三个参数。我将使用list 作为延续,使用您的方法再次对其进行测试。
      • 另外,我认为这个函数难以分析的原因是,在每个递归步骤中,新的延续由前一个延续、它之前的前一个延续......等等组成.我的观察是否正确?
      • 而且,我想我看到每个延续都是程序中任何特定点的环境及其状态的“快照”......所以每个匿名函数,当函数被传递为每个递归步骤中的一个参数,包含例如的值(car lat) 用于那个精确的时刻,当我们到达终止条件时最终评估该函数时,(car lat) 的状态将与我们最初将它作为参数传递时的状态相同。这听起来对吗?
      【解决方案3】:

      最近再次用我最喜欢的模式匹配伪代码重写它,1我们得到了更加紧凑和直观的效果

      multiinsertLR&co new oldL oldR lat col  =  g lat col 
        where
        g [a, ...t] col 
          | a == oldL  =  g t  ( newlat L R =>  col [new, oldL, ...newlat] (add1 L)  R  )
          | a == oldR  =  g t  ( newlat L R =>  col [oldR, new, ...newlat]  L  (add1 R) )
          | else       =  g t  ( newlat L R =>  col [a,         ...newlat]  L        R  )
        g [] col       =                        col []                      0        0
      

      所以LoldLs 在lat 中的计数; RoldRs 在lat 中的计数; new 是一个特殊元素,它被插入到正在构建的结果列表中,在输入原子列表latoldR 的右侧和oldL 元素的左侧:

      multii&co 0 1 2 [1,4,5,2,4,5,1,4,5,1] col 
      =
      col [0,1,4,5, 2,0,4,5, 0,1,4,5, 0,1 ] 3 1
      

      仅此而已。有了有用的符号,就很容易了。


      1thisthis

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-23
        • 1970-01-01
        • 1970-01-01
        • 2013-09-01
        • 1970-01-01
        • 2010-11-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多