为什么它有这么多缺点?

xie*_*pan 5 lisp sbcl common-lisp

当我在 SBCL (2.3.5) 中运行以下代码时,我对消耗的字节数感到惊讶。

(defun number-of-digits (num)
  (do ((n 1 (1+ n))
       (num (floor num 10) (floor num 10)))
      ((zerop num) n)))

(defun p25 ()
  (do ((a 0 (+ a b))
       (b 1 a)
       (n 0 (1+ n)))
      ((>= (number-of-digits a) 1000) n)))
Run Code Online (Sandbox Code Playgroud)
(time (p25))
Evaluation took:
  0.890 seconds of real time
  0.888141 seconds of total run time (0.878463 user, 0.009678 system)
  [ Run times consist of 0.004 seconds GC time, and 0.885 seconds non-GC time. ]
  99.78% CPU
  1,864,981,440 processor cycles
  368,895,776 bytes consed
  
4782
Run Code Online (Sandbox Code Playgroud)

显然,代码本身并不“cons”任何列表。所以我的问题是:

  1. 为什么有这么多字节消耗?它从何而来?

  2. 如何消除“consed bytes”?我的意思是这只是数字计算,我认为可能有办法使代码不包含任何字节(或只是几个字节)。

Rai*_*wig 5

让我们看看生成的最大数字:

CL-USER 44 > (integer-length 1070066266382758936764980584457396885083683896632151665013235203375314520604694040621889147582489792657804694888177591957484336466672569959512996030461262748092482186144069433051234774442750273781753087579391666192149259186759553966422837148943113074699503439547001985432609723067290192870526447243726117715821825548491120525013201478612965931381792235559657452039506137551467837543229119602129934048260706175397706847068202895486902666185435124521900369480641357447470911707619766945691070098024393439617474103736912503231365532164773697023167755051595173518460579954919410967778373229665796581646513903488154256310184224190259846088000110186255550245493937113651657039447629584714548523425950428582425306083544435428212611008992863795048006894330309773217834864543113205765659868456288616808718693835297350643986297640660000723562917905207051164077614812491885830945940566688339109350944456576357666151619317753792891661581327159616877487983821820492520348473874384736771934512787029218636250627816)
3319

CL-USER 45 > (ceiling * 8)
415
Run Code Online (Sandbox Code Playgroud)

因此 Lisp 将需要大约 415 个 8 位字节来表示堆上的这个数字。

您的代码创建了很多这样的数字,每个数字都是堆上的一个新对象。

或者,例如,您可以编写自己的基于数组的数字,其中使用数字数组来表示数字。这些数组可以在计算中重复使用。您将需要一个 + 运算,并且需要获取数组中的位数。


ex *_*ilo 4

Common Lisp 对某些实现定义的范围内的整数使用fixnum,对fixnum 范围之外的整数使用bignum 。使用 bignums 涉及到实现的 consing。

循环doinp25不断迭代,直到 in 的位数a达到一千或更多。这远远超出了固定数的范围(SBCL 中大约为 64 位)。您可以检查most-positive-fixnum以查看系统上最大的可表示修复号。一旦a超过这个阈值,它就会被视为 bignum,并且 consing 开始。当然,b也会受到这种bignum的待遇。

处理非常大的数字需要处理它们的表示,如果您不想使用 bignum,则必须实现自己的类似 bignum 的设施。