How can I generate series of Pell numbers instead of a specific one in Lisp

前端 未结 3 1847
清酒与你
清酒与你 2021-01-21 15:27

How do I use cons or other way to print a list of Pell numbers till the Nth number?

(defun pellse (k)
   (if (or (zerop k) (= k 1))
       k
   (+ (* 2 (pellse (         


        
3条回答
  •  醉酒成梦
    2021-01-21 15:56

    Here is an approach to solving this problem which works by defining an infinite stream of Pell numbers. This is based on the ideas presented in SICP, and particularly section 3.5. Everyone should read this book.

    First of all we need to define a construct which will let us talk about infinite data structures. We do this by delaying the evaluation of all but a finite part of them. So start with a macro called delay which delays the evaluation of a form, returning a 'promise' (which is a function of course), and a function called force which forces the system to make good on its promise:

    (defmacro delay (form)
      ;; Delay FORM, which may evaluate to multiple values.  This has
      ;; state so the delayed thing is only called once.
      (let ((evaluatedp-n (make-symbol "EVALUATEDP"))
            (values-n (make-symbol "VALUES")))
        `(let ((,evaluatedp-n nil) ,values-n)
           (lambda ()
             (unless ,evaluatedp-n
               (setf ,evaluatedp-n t
                     ,values-n (multiple-value-list
                                (funcall (lambda () ,form)))))
             (values-list ,values-n)))))
    
    (defun force (promise)
      ;; force a promise (delayed thing)
      (funcall promise))
    

    (This implementation is slightly overcomplex for our purposes, but it's what I had to hand.).

    Now we'll use delay to define streams which are potentially infinite chains of conses. There are operations on these corresponding to operations on conses but prefixed by stream-, and there is an object called null-stream which corresponds to () (and is in fact the same object in this implementation).

    (defmacro stream-cons (car cdr)
      ;; a cons whose cdr is delayed
      `(cons ,car (delay ,cdr)))
    
    (defun stream-car (scons)
      ;; car of a delayed cons
      (car scons))
    
    (defun stream-cdr (scons)
      ;; cdr of a delayed cons, forced
      (force (cdr scons)))
    
    (defconstant null-stream
      ;; the empty delayed cons
      nil)
    
    (defun stream-null (stream)
      ;; is a delayed cons empty
      (eq stream null-stream))
    

    Now define a function pell-stream which returns a stream of Pell numbers. This function hand-crafts the first two elements of the stream, and then uses a generator to make the rest.

    (defun pell-stream ()
      ;; A stream of Pell numbers
      (labels ((pell (pn pn-1)
                 (let ((p (+ (* 2 pn) pn-1)))
                   (stream-cons p (pell p pn)))))
        (stream-cons 0 (stream-cons 1 (pell 1 0)))))
    

    And now we can simply repeatedly takes stream-cdr to compute Pell numbers.

    (defun n-pell-numbers (n)
      (loop repeat n
            for scons = (pell-stream) then (stream-cdr scons)
            collect (stream-car scons)))
    

    And now

    > (n-pell-numbers 20)
    (0
     1
     2
     5
     12
     29
     70
     169
     408
     985
     2378
     5741
     13860
     33461
     80782
     195025
     470832
     1136689
     2744210
     6625109)
    

    Note that, in fact, pell-stream can be a global variable: it doesn't need to be a function:

    (defparameter *pell-stream*
      (labels ((pell (pn pn-1)
                 (let ((p (+ (* 2 pn) pn-1)))
                   (stream-cons p (pell p pn)))))
        (stream-cons 0 (stream-cons 1 (pell 1 0)))))
    
    (defun n-stream-elements (stream n)
      (loop repeat n
            for scons = stream then (stream-cdr scons)
            collect (stream-car scons)))
    

    If we define a little benchmarking program:

    (defun bench-pell (n)
      (progn (n-stream-elements *pell-stream* n) n))
    

    Then it's interesting to see that this is clearly essentially a linear process (it slows down for later elements because the numbers get big and so operations on them take a long time), and that the stateful implementation of promises makes it much faster after the first iteration (at the cost of keeping quite a lot of bignums around):

    > (time (bench-pell 100000))
    Timing the evaluation of (bench-pell 100000)
    
    User time    =        2.020
    System time  =        0.803
    Elapsed time =        2.822
    Allocation   = 1623803280 bytes
    441714 Page faults
    100000
    
    > (time (bench-pell 100000))
    Timing the evaluation of (bench-pell 100000)
    
    User time    =        0.007
    System time  =        0.000
    Elapsed time =        0.006
    Allocation   = 1708248 bytes
    0 Page faults
    100000
    

提交回复
热议问题