Richard Suchenwirth 2002-12-16 - For people experienced in other languages, it may be interesting to compare code snippets between Tcl and other languages, to demonstrate similarities and differences. Please add more from your experience!
void countdown(int n) { | proc countdown {n} { int i; | for(i=n; i>0; i--) { | for {set i $n} {$i>0} {incr i -1} { printf("%d...\n", i); | puts $i... } | } } | }
C /* The above could be: */
void countdown(int n) { for (; n>0; n--) printf("%d...\n",n); }
AM The above proc could look like this in Fortran (90):
subroutine countdown( n ) integer :: n integer :: i do i = n,1,-1 write(*,*) i, '...' enddo endsubroutine
The main difference with either C or Tcl is that in Fortran the do-loop is very different kind of control construct: it is really an iteration over a predefined set of values, whereas in C and Tcl the three parts gouverning the iteration can be almost anything. (The Fortran control variable can be an integer only).
(define foo 42) | set foo 42 (define (square x) (* x x)) | proc square x {expr $x * $x} (define bar (square foo)) | set bar [square $foo] (define grill '(square foo)) | set grill {square $foo}
(define (abs x) | proc abs x { (cond ((> x 0) x) | expr { $x > 0? $x : ((= x 0) 0) | $x == 0? 0 : ((< x 0) (- x)))) | $x < 0? -$x} | } | or: proc abs x {expr abs($x)}
(define (abs x) | proc abs x { (if (< x 0) | if {$x < 0} { (- x) | expr -$x x)) | } else {return $x}
TV The C code from above:
int countdown(n) int n; { int i; for (i=n; i>0; i--) { printf("%d...\n", i); } }
in more traditional, non-cross source file argument checking, non-ansi notation, can also be represented in assembly code. For the above, using the gnu C compiler under cygwin, the following assembly can be generated, which looks obfuscated, because of the complex or hard to see through variable space and stack handling:
LC0: .ascii "%d...\12\0" .globl _countdown .def _countdown; .scl 2; .type 32; .endef _countdown: pushl %ebp movl %esp, %ebp subl $24, %esp movl 8(%ebp), %eax movl %eax, -4(%ebp) L10: cmpl $0, -4(%ebp) jg L13 jmp L11 L13: movl -4(%ebp), %eax movl %eax, 4(%esp) movl $LC0, (%esp) call _printf leal -4(%ebp), %eax decl (%eax) jmp L10 L11: leave ret
Or, with the optimizer (-O) on, and more verbose assembly code:
.text LC0: .ascii "%d...\12\0" .globl _countdown .def _countdown; .scl 2; .type 32; .endef _countdown: pushl %ebp movl %esp, %ebp pushl %ebx subl $20, %esp movl 8(%ebp), %ebx # n, i testl %ebx, %ebx # i jle L8 L6: movl %ebx, 4(%esp) # i movl $LC0, (%esp) call _printf decl %ebx # i testl %ebx, %ebx # i jg L6 L8: addl $20, %esp popl %ebx popl %ebp ret
This irrespective of another possible optimisation step in the assembler, to parallelize adjacent instuctions.
Of course the implicit register assignment of the counter and loop test variable, which apart from optimizer we could also define in the C variable declaration, doesn't make all to much sense in the light of the relatively time consuming printf call.
In Intel mnemonics, the last becomes:
.text LC0: .ascii "%d...\12\0" .globl _countdown .def _countdown; .scl 2; .type 32; .endef _countdown: push ebp mov ebp, esp push ebx sub esp, 20 mov ebx, DWORD PTR [ebp+8] # i, n test ebx, ebx # i jle L8 L6: mov DWORD PTR [esp+4], ebx # i mov DWORD PTR [esp], OFFSET FLAT:LC0 call _printf dec ebx # i test ebx, ebx # i jg L6 L8: add esp, 20 pop ebx pop ebp ret
With extreme optimisation by the compiler using -O4, the function may end up macrofied, so that it is no longer called as a subroutine.
See also BOOK Programming Language Examples Alike Cookbook, http://www.merd.net/pixel/language-study/scripting-language/ and CL's ill-maintained personal notes on language comparison [L1 ].