The Little Schemer evens-only*&co

前端 未结 4 1426
失恋的感觉
失恋的感觉 2020-12-29 10:23

I\'m having difficulty understanding what\'s going on with The Little Schemer\'s evens-only*&co example on page 145.

Here\'s the code:



        
4条回答
  •  伪装坚强ぢ
    2020-12-29 10:40

    In equational pseudocode (a KRC-like notation, writing f x y for the call (f x y), where it is unambiguous), this is

    evens-only*&co l col 
       = col [] 1 0                                     , IF null? l
       = evens-only*&co (cdr l) 
                        ( newl product sum =>
                            col (cons (car l) newl)
                                (opx (car l) product)
                                sum )                   , IF atom? (car l) && even? (car l)
       = evens-only*&co (cdr l) 
                        ( newl product sum =>
                            col  newl  product  (op+ (car l) sum) )      , IF atom? (car l)
       = evens-only*&co (car l) 
                        ( anewl aproduct asum =>
                             evens-only*&co (cdr l)
                                            ( dnewl dproduct dsum =>
                                                 col (cons anewl    dnewl)
                                                     (opx  aproduct dproduct)
                                                     (op+  asum     dsum) ) )   , OTHERWISE
    

    This is a CPS code which collects all evens from the input nested list (i.e. a tree) while preserving the tree structure, and also finds the product of all the evens; as for the non-evens, it sums them up:

    • if l is an empty list, the three basic (identity) values are passed as arguments to col;

    • if (car l) is an even number, the results of processing the (cdr l) are newl, product and sum, and then they are passed as arguments to col while the first two are augmented by consing  ⁄  multiplying with the (car l) (the even number);

    • if (car l) is an atom which is not an even number, the results of processing the (cdr l) are newl, product and sum, and then they are passed as arguments to col with the third one augmented by summing with the (car l) (the non-even number atom);

    • if (car l) is a list, the results of processing the (car l) are anewl, aproduct and asum, and then the results of processing the (cdr l) are dnewl, dproduct and dsum, and then the three combined results are passed as arguments to col.

    [], 1 and 0 of the base case are the identity elements of the monoids of lists, numbers under multiplication, and numbers under addition, respectively. This just means special values that don't change the result, when combined into it.

    As an illustration, for '((5) 2 3 4) (which is close to the example in the question), it creates the calculation

    evens-only*&co [[5], 2, 3, 4] col
    =
    col  (cons []                   ; original structure w only the evens kept in,
               (cons 2              ;   for the car and the cdr parts
                  (cons 4 [])))
         (opx 1                     ; multiply the products of evens in the car and 
              (opx 2 (opx 4 1)))    ;   in the cdr parts
         (op+ (op+ 5 0)             ; sum, for the non-evens
              (op+ 3 0))     
    

    Similar to my other answer (to a sister question), here's another way to write this, with a patter-matching pseudocode (with guards):

    evens-only*&co  =  g   where
      g [a, ...xs...] col 
             | pair? a    = g a  ( la pa sa =>
                             g xs ( ld pd sd =>
                                            col [la, ...ld...] (* pa pd) (+ sa sd) ) )
             | even? a    = g xs ( l p s => col [ a, ...l... ] (* a  p )       s     )
             | otherwise  = g xs ( l p s => col         l            p   (+ a  s )   )
      g []            col =                 col []              1         0
    

    The economy (and diversity) of this notation really makes it all much clearer, easier to just see instead of getting lost in the word salad of long names for functions and variables alike, with parens overloaded as syntactic separators for list data, clause groupings (like in cond expressions), name bindings (in lambda expressions) and function call designators all looking exactly alike. The same uniformity of S-expressions notation so conducive to the ease of manipulation by a machine (i.e. lisp's read and macros) is what's detrimental to the human readability of it.

提交回复
热议问题