Compile-time calculation: Difference between revisions
(→{{header|OCaml}}: more precisions about the internal representation) |
|||
Line 70: | Line 70: | ||
grep 86400 sec.s |
grep 86400 sec.s |
||
imull $<span style="color:red">86400</span>, %eax |
imull $<span style="color:red">86400</span>, %eax |
||
If you wish to verify this property in your own projects, you have to know that integer values most often have their OCaml internal representation in the assembly, which for an integer <code>x</code> its OCaml internal representation will be <code>(((x) << 1) + 1)</code>. So for example if we modify the previous code for: |
|||
<lang ocaml>let conv = 24 * 60 * 60 |
|||
let days_to_seconds n = |
|||
(n * conv) |
|||
;;</lang> |
|||
# (24 * 60 * 60) lsl 1 + 1 ;; |
|||
- : int = 172801 |
|||
grep 172801 sec.s |
|||
movl $<span style="color:red">172801</span>, camlSec |
|||
=={{header|Oz}}== |
=={{header|Oz}}== |
Revision as of 19:09, 25 January 2010
Some programming languages allow calculation of values at compile time. For this task, calculate 10! at compile time. Print the result when the program is run.
Discuss what limitations apply to compile-time calculations in your language.
Warnings:
- This task is a draft, you can modify it if you find a way to enhance it.
C++
<lang cpp>#include <iostream>
template<int i> struct Fac {
static const int result = i * Fac<i-1>::result;
};
template<> struct Fac<1> {
static const int result = 1;
};
int main(int argc, char* argv[])
{
std::cout << "10! = " << Fac<10>::result << "\n";
}</lang>
Compile-time calculations in C++ look quite different from normal code. We can only use templates, type definitions and a subset of integer arithmetic. It is not possible to use iteration. C++ compile-time programs are similar to programs in pure functional programming languages, albeit with a peculiar syntax.
Common Lisp
Assuming a definition from Factorial function#Common Lisp:
<lang lisp>(defmacro ct-factorial (n)
(factorial n))</lang>
The factorial
function must be defined before a use of the ct-factorial
macro is evaluated or compiled.
If the data resulting from the compile-time calculation is not necessarily a number or other self-evaluating object then the macro must quote it to avoid it being interpreted as code (a form):
<lang lisp>(defmacro ct-factorial (n)
`(quote ,(factorial n)))</lang>
or <lang lisp>(defmacro ct-factorial (n)
`',(factorial n))</lang>
Forth
During a word definition, you can drop out of the compilation state with [ and go back in with ]. (This is where the naming conventions for [CHAR] and ['] come from.) There are several flavors of LITERAL for compiling the result into the word.
<lang forth>: fac ( n -- n! ) 1 swap 1+ 2 max 2 ?do i * loop ;
- main ." 10! = " [ 10 fac ] literal . ;
see main
- main
.\" 10! = " 3628800 . ; ok</lang>
OCaml
OCaml don't calculate operations that involve functions calls, as for example factorial 10, but OCaml does calculate simple mathematical operations at compile-time, for example in the code below (24 * 60 * 60)
will be replaced by its result 86400
.
<lang ocaml>let days_to_seconds n =
let conv = 24 * 60 * 60 in (n * conv)
- </lang>
It is easy to verify this using the argument -S
to keep the intermediate assembly file:
ocamlopt -S sec.ml
grep 86400 sec.s
imull $86400, %eax
If you wish to verify this property in your own projects, you have to know that integer values most often have their OCaml internal representation in the assembly, which for an integer x
its OCaml internal representation will be (((x) << 1) + 1)
. So for example if we modify the previous code for:
<lang ocaml>let conv = 24 * 60 * 60
let days_to_seconds n =
(n * conv)
- </lang>
# (24 * 60 * 60) lsl 1 + 1 ;; - : int = 172801
grep 172801 sec.s
movl $172801, camlSec
Oz
<lang oz>functor import
System Application
prepare
fun {Fac N} {FoldL {List.number 1 N 1} Number.'*' 1} end Fac10 = {Fac 10}
define
{System.showInfo "10! = "#Fac10} {Application.exit 0}
end</lang>
Code in the prepare
section of a functor is executed at compile time. External modules that are used in this code must be imported with a require
statement (not shown in this example). Such external functors must have been compiled before the current functor is compiled (ozmake
will automatically take care of this).
It is possible to export variables that are defined in the prepare
statement. However, such variables must not be stateful entities, e.g. it is not possible to export a cell that was defined at compile time.
Perl
There are few limits on code you can put in BEGIN
blocks, which are executed at compile-time. Unfortunately, you can't in general save the compiled form of a program to run later. Instead, perl
recompiles your program every time you run it.
<lang perl>my $tenfactorial; print "$tenfactorial\n";
BEGIN
{$tenfactorial = 1; $tenfactorial *= $_ foreach 1 .. 10;}</lang>
Tcl
In Tcl, compilation happens dynamically when required rather than being a separate step. That said, it is possible to use the language's introspection engine to discover what code has been compiled to, making it easy to show that known-constant expressions are compiled to their results. Generating the expression to compile is then simple enough.
<lang tcl>proc makeFacExpr n {
set exp 1 for {set i 2} {$i <= $n} {incr i} { append exp " * $i" } return "expr \{$exp\}"
} eval [makeFacExpr 10]</lang> How to show that the results were compiled? Like this: <lang tcl>% tcl::unsupported::disassemble script [makeFacExpr 10] ByteCode 0x0x4de10, refCt 1, epoch 3, interp 0x0x31c10 (epoch 3)
Source "expr {1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10}" Cmds 1, src 45, inst 3, litObjs 1, aux 0, stkDepth 1, code/src 0.00 Commands 1: 1: pc 0-1, src 0-44 Command 1: "expr {1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10}" (0) push1 0 # "3628800" (2) done
</lang> As you can see, that expression was transformed into just a push of the results (and an instruction to mark the end of the bytecode segment).