Kaz Kylheku
8/3/2015 3:01:00 PM
On 2015-08-03, Taoufik Dachraoui <dachraoui.taoufik@gmail.com> wrote:
> On Sunday, August 2, 2015 at 5:15:13 PM UTC+1, Taoufik Dachraoui wrote:
>> Hi
>>
>> The macro DEFORBIT (a sequence starting with x0 is called an orbit around x0) is used to define
>> any function that can be expressed as a sequence.
>>
>> (deforbit (foo args (initial-elements form) &key (index (gensym "N"))))
>>
>> For example:
>>
>> ? (deforbit %fact () (((a 1)) (* a n)) :index n)
>> %FACT
>> ? (funcall (%fact) 5)
>> 120
>> ? (deforbit %fib () (((a 0) (b 1)) (+ a b)))
>> %FIB
>> ? (funcall (%fib) 10)
>> 55
>> ? (deforbit %iter (f x) (((a (funcall f x))) (funcall f a)))
>> %ITER
>> ? (deforbit %ack () (((f (lambda (n) (1+ n)))) (%iter f 1)))
>> %ACK
>> ? (funcall (funcall (%ack) 4) 1)
>> 65533
>>
>> The interesting thing is that functions defined using DEFORBIT are iterative and thus much faster
>> than their corresponding recursive definition (the recursive ackermann function on my computer
>> failed to compute (ackermann 4 1) because of stack overflow, and Fibonacci of 10000 run for ever.
>>
>> The following shows how fast it is when using orbits instead of recursive functions:
>>
>> ? (time (funcall (%fib) 10000))
>> (FUNCALL (%FIB) 10000)
>> took 22 milliseconds (0.022 seconds) to run.
>> 2 milliseconds (0.002 seconds, 9.09%) of which was spent in GC.
>> During that period, and with 2 available CPU cores,
>> 19 milliseconds (0.019 seconds) were spent in user mode
>> 3 milliseconds (0.003 seconds) were spent in system mode
>> 4,457,848 bytes of memory allocated.
>> 336447648764317832666216120051075433103021484606800639065647699746800814421666623681555955136337340255820653326808361593737347904838652682630408924>> 630564318873545443695598274916066020998841839338646527313000888302692356736131351175792974378544137521305205043477016022647583189065278908551543661>> 595829872796829875106312005754287834532155151038708182989697916131278562650331954871402142875326981879620469360978799003509623022910263681314931952>> 756302278376284415403605844025721143349611800230912082870460889239623288354615057765832712525460935911282039252853934346209042452489294039017062338>> 889910858410651831733604374707379085526317643257339937128719375877468974799263058370657428301616374089691784263786242128352581128205163702980893320>> 999057079200643674262023897831114700540749984592503606335609338838319233867830561364353518921332797329081337326426526339897639227234078829281779535>> 805709936910491754708089318410561463223382174656373212482263830921032977016480547262438423748624114530938122065649140327510866433945175121615265453>> 613331113140424368548051067658434935238369596534280717687753283482343455573667197313927462736291082106792807847180353291311767789246590899386354593>> 278945237776744061922403376386740040213303432974969020283281459334188268176838930720036347956231171031012919531697946076327375892535307725523759437>> 884345040677155557790564504430166401194625809722167297586150269684431469520346149322911059706762432685159928347098912847067408620085871350162603120>> 719031720860940812983215810772820763531866246112782455372085323653057759564300725177443150515396009051686032203491632226408852488524331580515348496>> 224348482993809050704834824493274537326245677558790891871908036620580095947431500524025327097469953187707243768259074199396322659841474981936092852>> 239450397071654431564213281576889080587831834049174345562705202235648464951961124602683139709750693826487066132645076650746115126775227486215986425>> 307112984411826226610571635150692600298617049454250474913781151541399415506712562711971332527636319396069028956502882686083622410820505624307017949>> 76171121233066073310059947366875
>>
>>
>> ? (time (funcall (funcall (%ack) 4) 1))
>> (FUNCALL (FUNCALL (%ACK) 4) 1)
>> took 12 milliseconds (0.012 seconds) to run.
>> During that period, and with 2 available CPU cores,
>> 12 milliseconds (0.012 seconds) were spent in user mode
>> 0 milliseconds (0.000 seconds) were spent in system mode
>> 352 bytes of memory allocated.
>> 65533
>>
>>
>> -Taoufik
>
> Recursive functions are slow because of the stack usage (I do not understand what do you
> mean by correct use of recursion); your fib function is tail recursive and it is basically a loop,
> and it is less clear then the mathematical definition of fibonacci function:
> fib(n) = fib(n-2) + fib(n -1)
The above recursive version of the fib function isn't tail recursive. It makes
two calls to itself, which have to return so that it can calculate their sum.
The main problem with the above recursive fib isn't so much the stack usage, as
the overlapping subcases, which make it take exponential time. That bites
even for reasonably small values of n, where stack usage wouldn't be a concern.
Memoization will reduce the problem to merely that of linear stack usage.
If you want a tail-recurive fib, you have to express it differently from the
above. For example, like this:
(defun fib-rec (a b count)
(if (zerop count)
a
(fib-rec b (+ a b) (1- count))))
(defun fib (n)
(fib-rec 1 1 n))
(fib 0) -> 1
(fib 1) -> 1
(fib 2) -> 2
(fib 3) -> 3
(fib 4) -> 5
(fib 5) -> 8
....
> The orbit is very close to the mathematical definition.
Yes, well orbits are evidently based on seuqences which are defined by
*recurrences*. Orbits are close to the recurrences.
We know that we can calculate the elements of a recurrent sequence by
starting with some initial values and applying the recurrence; and
you've come up with a syntactic sugar for specifying the initial
conditions and recurrence in a capsule.
Here is a problem: some recurrences rely on random access into the the entire
preceding sequence, not only on the last handful of elements.
For instance, calculation of some (f n) may rely on (f (floor n 3)).