最近在工作中的一些场景,让我想到了「编译期计算」这个概念,然而一直没有时间整理、复习下相关概念。
什么是编译期计算 「编译期计算」是指: 可以在编译期间执行的的运算或者这种能力
这并不是什么新的概念,最古老的语言之一的 C 的 宏 就具有很弱的编译期计算能力,而之后的编译语言中,凡是支持 元编程的,多多少少都能支持编译期间预算。
而另外一方面,编译期计算也是一种常见的编译优化手段,高优化的编译器会尽可能的,对在编译期间可以求值的计算过程进行 求值,然后内联其计算结果,比如:如果一个循环内所有变量都是常量,在 -O2 和 -O3 的情况下,GCC 生成的代码多半只有 循环结果,而没有循环的过程。
但是,相对而言,大部分编程语言所支持的 编译期计算 和 Lisp 比起来,都很弱
做为最古老的编程语言之一,Lisp 发明了很多 超时代 的特性,最出名的莫过于 垃圾回收(GC) 了,但是 Lisp 对 计算的控制粒度和灵活度,目前也鲜有对手。
Common Lisp 和 编译期计算 C++11 引入了一个关键字 constexpr,用来声明一个函数或者变量,在编译期是可能进行求值的,相比 宏 和 模板 , 这是一个更加强大的编译期计算的能力。
不过,这种能力却是 30 多年前的 Common Lisp 里面的内建功能,这里用一个求和函数进行演示
(defpackage ct (:use :cl) (:export :test1 :test2)) (in-package ct) ;;; 声明一个求和函数 sum ;;; 设定函数 sum 具有在编译期和执行时进行运算的能力 ;;; 默认情况下函数只能在执行时(execute)才能进行计算 (eval-when (:compile-toplevel :execute) (defun sum (&rest args) (apply #'+ args))) ;;; 声明函数 test1,使用求和函数求值 (defun test1 () (let ((x (sum 1 2 3 ))) x)) ;;; 声明函数 test2,使用语言自带的加法运算求值 (defun test2 () (let ((x (+ 1 2 3))) x)) 然后在 REPL 中执行编译和加载...