Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/lua/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Common lisp 向通用Lisp编译器保证算术表达式的结果是fixnum的最简单方法是什么?_Common Lisp - Fatal编程技术网

Common lisp 向通用Lisp编译器保证算术表达式的结果是fixnum的最简单方法是什么?

Common lisp 向通用Lisp编译器保证算术表达式的结果是fixnum的最简单方法是什么?,common-lisp,Common Lisp,我想告诉sbcl,以下函数将仅使用fixnum值调用,其结果符合fixnum: (defun layer (x y z n) (+ (* 2 (+ (* x y) (* y z) (* x z))) (* 4 (+ x y z n -2) (1- n)))) 我的第一次尝试是 (defun layer (x y z n) (declare (fixnum x y z n)) (the fixnum (+ (* 2 (+ (* x y) (* y z) (* x z

我想告诉sbcl,以下函数将仅使用fixnum值调用,其结果符合fixnum:

(defun layer (x y z n)
  (+ (* 2 (+ (* x y) (* y z) (* x z)))
     (* 4 (+ x y z n -2) (1- n))))
我的第一次尝试是

(defun layer (x y z n)
  (declare (fixnum x y z n))
  (the fixnum
    (+ (* 2 (+ (* x y) (* y z) (* x z)))
       (* 4 (+ x y z n -2) (1- n))))
但这个返回类型声明并不能保证所有中间结果都是fixnums,我通过查看sbcl生成的非常有用的编译注释发现了这一点。然后我做了这个:

(defmacro fixnum+ (&rest args)
  (reduce
    (lambda (x y) `(the fixnum (+ ,x ,y)))
    args))

(defmacro fixnum* (&rest args)
  (reduce
    (lambda (x y) `(the fixnum (* ,x ,y)))
    args))

(defun layer (x y z n)
  (declare (fixnum x y z n))
  (fixnum+ (fixnum* 2 (fixnum+ (fixnum* x y) (fixnum* y z) (fixnum* x z)))
     (fixnum* 4 (fixnum+ x y z n -2) (the fixnum (1- n)))))
而且效果很好。我的问题是:有没有更简单、更惯用的方法


例如,也许我可以重新声明+、-、*、1-的类型以保证fixnum结果?(我知道这通常是一个坏主意,但我可能希望在某些程序中这样做。)CHICKEN scheme有
(declare(fixnum算术))
可以实现我想要的功能:它(不安全地)假设fixnums上所有算术运算的结果都是fixnums。

您可以使用声明函数的类型

例如:

(defun foo (a b)
  (declare (ftype (function (&rest fixnum) fixnum) + * 1-)
           (type fixnum a b)
           (inline + * 1-)
           (optimize (speed 3) (safety 0) (debug 0) (space 0)))
  (+ a (* a (1- b))))
这有区别吗?

试试这个:

(defun layer (x y z n)
  (declare (optimize speed) (fixnum x y z n))
  (logand most-positive-fixnum
          (+ (* 2 (+ (* x y) (* y z) (* x z)))
             (* 4 (+ x y z n -2) (1- n)))))

编辑: 如评论中所述,SBCL-1.1.9(或更高版本)是这项工作所必需的。此外,通过内联子程序,还可以节省约40%的时间:

;;; From: https://gist.github.com/oantolin/6073417
(declaim (optimize (speed 3) (safety 0)))

(defmacro with-type (type expr)
  (if (atom expr)
      expr
      (let ((op (car expr)))
        (reduce
         (lambda (x y)
           `(the ,type
                 (,op ,@(if x (list x) '())
                      (with-type ,type ,y))))
         (cdr expr)
         :initial-value nil))))
 
(defun layer (x y z n)
  (declare (fixnum x y z n))
  (with-type fixnum
    (+ (* 2 (+ (* x y) (* y z) (* x z)))
       (* 4 (+ x y z n -2) (1- n)))))

(defun cubes (n)
  (declare (fixnum n))
  (let ((count (make-array (+ n 1) :element-type 'fixnum)))
    (loop for x of-type fixnum from 1 while (<= (layer x x x 1) n) do
      (loop for y of-type fixnum from x while (<= (layer x y y 1) n) do
        (loop for z of-type fixnum from y while (<= (layer x y z 1) n) do
          (loop for k of-type fixnum from 1 while (<= (layer x y z k) n) do
            (incf (elt count (layer x y z k)))))))
    count))

(defun first-time (x)
  (declare (fixnum x))
  (loop for n of-type fixnum = 1000 then (* 2 n)
        for k = (position x (cubes n))
        until k
        finally (return k)))

;;; With modarith and inlining
(defun first-time/inline (x)
  (declare (fixnum x))
  (labels
      ((layer (x y z n)
         (logand #.(1- (ash 1 (integer-length most-positive-fixnum)))
                 (+ (* 2 (+ (* x y) (* y z) (* x z)))
                    (* 4 (+ x y z n -2) (1- n)))))
       (cubes (n)
         (let ((count (make-array (+ n 1) :element-type 'fixnum)))
           (loop for x of-type fixnum from 1 while (<= (layer x x x 1) n) do
             (loop for y of-type fixnum from x while (<= (layer x y y 1) n) do
               (loop for z of-type fixnum from y while (<= (layer x y z 1) n) do
                 (loop for k of-type fixnum from 1 while (<= (layer x y z k) n)
                       do (incf (elt count (layer x y z k)))))))
           count)))
    (declare (inline layer cubes))
    (loop for n of-type fixnum = 1000 then (* 2 n)
          thereis (position x (cubes n)))))

#+(or) 
(progn
  (time (print (first-time 1000)))
  (time (print (first-time/inline 1000))))

;; 18522 
;; Evaluation took:
;;   0.448 seconds of real time
;;   0.448028 seconds of total run time (0.448028 user, 0.000000 system)
;;   100.00% CPU
;;   1,339,234,815 processor cycles
;;   401,840 bytes consed
;;   
;; 
;; 18522 
;; Evaluation took:
;;   0.259 seconds of real time
;;   0.260016 seconds of total run time (0.260016 user, 0.000000 system)
;;   100.39% CPU
;;   776,585,475 processor cycles
;;   381,024 bytes consed
  
;;;发件人:https://gist.github.com/oantolin/6073417
(说教(优化(速度3)(安全0)))
(带有类型(类型expr)的defmacro)
(如果(原子表达式)
expr
(出租(op(汽车出口)))
(减少
(λ(x y)
`(表格,类型)
(,op,@(如果x(列表x)'))
(带type,type,y)))
(cdr expr)
:初始值(零)
(除雾层(x y z n)
(声明(固定数量x y z n))
(带有fixnum类型)
(+(*2(+(*XY)(*YZ)(*XZ)))
(*4(+xyzn-2)(1-n())))
(defun立方体(n)
(声明(fixnum n))
(let((计数(生成数组(+n1):元素类型'fixnum)))

(fixnum类型的x从1开始循环,而(在他的书ANSI Common Lisp中,Paul Graham使用类型
显示了宏
,该宏将表达式及其所有子表达式包装在
表单中,同时确保正确处理给定两个以上参数的运算符

例如,
(使用fixnum(+1 2 3)类型)
将扩展到表单

(the fixnum (+ (the fixnum (+ (the fixnum 1) (the fixnum 2))) 
               (the fixnum 3))
带有辅助函数的宏的代码为

(defmacro with-type (type expr)
  `(the ,type ,(if (atom expr) 
                   expr
                   (expand-call type (binarize expr)))))

(defun expand-call (type expr)
  `(,(car expr) ,@(mapcar #'(lambda (a) 
                              `(with-type ,type ,a))
                          (cdr expr))))

(defun binarize (expr)
  (if (and (nthcdr 3 expr)
           (member (car expr) '(+ - * /)))
      (destructuring-bind (op a1 a2 . rest) expr
        (binarize `(,op (,op ,a1 ,a2) ,@rest)))
      expr))
中的书中的代码链接可在找到

该法典中的一条评论指出,“该法典由Paul Graham于1995年版权所有,但任何想要
使用它是免费的。”

声明层函数内联会导致更快的速度,即使在块编译打开时也是如此

在我的Apple Air M1上,层内联,块编译在Arm64版本的SBCL 2.1.2下以0.06秒的速度运行

CL-USER> (time (first-time 1000))
Evaluation took:
  0.060 seconds of real time
  0.060558 seconds of total run time (0.060121 user, 0.000437 system)
  101.67% CPU
  303,456 bytes consed
我刚刚记得在多维数据集中声明count数组也会有所帮助

(声明(类型(简单数组fixnum(*)计数))

如果不内联层函数,时间大约为0.2秒

CL-USER> (time (first-time 1000))
Evaluation took:
  0.201 seconds of real time
  0.201049 seconds of total run time (0.200497 user, 0.000552 system)
  100.00% CPU
  251,488 bytes consed
或者将图层功能转换为宏会使其速度更快

(defmacro layer (x y z n)
  (declare (fixnum x y z n))
  `(logand #.(1- (ash 1 (integer-length most-positive-fixnum)))
      (+ (* 2 (+ (* ,x ,y) (* ,y ,z) (* ,x ,z)))
         (* 4 (+ ,x ,y ,z ,n -2) (1- ,n)))))

CL-USER> (time (first-time 1000))
Evaluation took:
  0.047 seconds of real time
  0.047032 seconds of total run time (0.046854 user, 0.000178 system)
  100.00% CPU
  312,576 bytes consed
以普通基准测试为基准,平均运行时间仅为0.04秒:

CL-USER> (benchmark:with-timing (100) (first-time 1000))
-                SAMPLES  TOTAL     MINIMUM   MAXIMUM   MEDIAN    AVERAGE    DEVIATION  
REAL-TIME        100      3.985173  0.039528  0.06012   0.039595  0.039852   0.002046   
RUN-TIME         100      3.985848  0.039534  0.06014   0.039605  0.039858   0.002048   
USER-RUN-TIME    100      3.975407  0.039466  0.059829  0.039519  0.039754   0.002026   
SYSTEM-RUN-TIME  100      0.010469  0.00005   0.000305  0.000088  0.000105   0.00005    
PAGE-FAULTS      100      0         0         0         0         0          0.0        
GC-RUN-TIME      100      0         0         0         0         0          0.0        
BYTES-CONSED     100      50200736  273056    504320    504320    502007.38  23010.477  
EVAL-CALLS       100      0         0         0         0         0          0.0

这完全破坏了性能!我使用layer函数的程序运行时间为1.7秒,根本没有类型声明;对于我在问题中提到的声明(使用
fixnum+
宏),它运行时间为0.36秒;但是根据这个建议,它运行时间为15秒!(这不是打字错误)。编译说明中充满了“强制执行泛型-+”、“强制执行泛型-*”等。您链接到的文档表明了一个可能的罪魁祸首:优化不适用于*(在文档术语中,*不是一个“好”函数)。您使用的是哪个版本的SBCL?对于1.1.9,我在编译时没有看到任何性能说明。请参阅编辑。如果您可以隔离代码以生成基准,那将非常好。此要点包含我正在运行的实际代码。它当前使用您的函数版本,并在我的计算机上运行15秒(使用SBCL 1.1.8)。要测试将每个操作包装在
(fixnum…
中的解决方案,请使用
logand
注释该行,并取消对上面的行的注释;该行在我的计算机上运行时间为0.3秒。(顺便说一下,在此要点中,我将我的
fixnum+
fixnum*
宏替换为(我的版本)Paul Graham的
with type
宏,正如@TerjeD所建议的那样。它在性能上没有差别,但使代码看起来更漂亮。)好奇。在我的机器上,使用logand的版本仍然比使用type的版本略好:也许SBCL在最新版本中做了一些改进。我尝试了
(ftype(function(&rest-fixnum)fixnum)+*1-
并且编译器抱怨COMMON-LISP包被锁定;因此我添加了
(eval-when(:compile-top-level:execute)(解锁包'cl))
然后它编译得很干净,但是性能被破坏了!我的原始程序在1.7秒内运行,没有任何类型声明;使用我在问题中提到的声明(使用
fixnum+
宏),它的运行时间为0.36秒;但根据此建议,它的运行时间为5.8秒!是的!这起作用了,它提供了与“在
中包装所有内容”(fixnum…
"我开始使用的策略。如果没有
inline
声明,它将在我提到的6秒钟内运行,这比删除所有类型声明要慢得多……感谢您的帮助!鉴于这种方法需要解锁COMMON-LISP包以及ftype和inline声明,我想我可能会使用Paul Graham的方法用类型
宏包装所有中间结果的
(类型…
)的想法非常有启发性。但无论如何,谢谢你,这非常有启发性。哇,我从来没有见过像这样使用内联!干杯!这绝对比我拥有的界面好(但基本上是相同的解决方案)