[blue]
+ X+1, если N=0
| X, если N=1, Y=0,
| 0, если N=2, Y=0,
A(N,X,Y)= | 1, если N=3, Y=0,
| 2, если N=>4, Y=0,
+ A(N-1,A(N,X,Y-1),X), если N#0, Y#0;
где N,X,Y - целые неотрицательные числа.
[/blue]
[blue]
#include <stdio.h>
void main(void)
{
printf("%dn",a(3,8,8));
}
int a(int n, int x, int y)
{
if(!n)
return x+1;
if(y)
return a(n-1,a(n,x,y-1),x);
switch(n)
{
case 1: return x;
case 2: return 0;
case 3: return 1;
}
return 2;
}
[/blue][blue]
; 16 :
; 17 : switch(n)
; 18 : {
0002d 4e dec esi
0002e 74 18 je SHORT $L861
00030 4e dec esi
00031 74 13 je SHORT $L869
00033 4e dec esi
00034 74 08 je SHORT $L870
00036 5f pop edi
; 22 : }
[/blue]
let rec a n x y = match (n, y) with (0, y) -> x+1 | (1, 0) -> x | (2, 0) -> 0 | (3, 0) -> 1 | (n, 0) -> 2 | (n, y) -> (a (n-1) (a n x (y-1)) x) ;; print_int(a 3 8 8); print_newline();;
[html_font color=blue] _Akkr_a_49: sub esp, 8 L106: cmp eax, 7 ja L101 mov edx, eax sar edx, 1 jmp [edx * 4 + L107] .DATA L107 DWORD L105 DWORD L104 DWORD L103 DWORD L102 .CODE L105: add ebx, 2 mov eax, ebx add esp, 8 ret L104: cmp ecx, 1 jne L101 mov eax, ebx add esp, 8 ret L103: cmp ecx, 1 jne L101 mov eax, 1 add esp, 8 ret L102: cmp ecx, 1 jne L101 mov eax, 3 add esp, 8 ret L101: cmp ecx, 1 je L100 mov DWORD PTR 0[esp], eax mov DWORD PTR 4[esp], ebx add ecx, -2 call _Akkr_a_49 L108: mov ebx, eax mov eax, DWORD PTR 0[esp] add eax, -2 mov ecx, DWORD PTR 4[esp] jmp L106 L100: mov eax, 5 add esp, 8 ret
=KRoN=>let rec a n x y = =KRoN=> match (n, y) with =KRoN=> (0, y) -> x+1 =KRoN=> | (1, 0) -> x =KRoN=> | (2, 0) -> 0 =KRoN=> | (3, 0) -> 1 =KRoN=> | (n, 0) -> 2 =KRoN=> | (n, y) -> (a (n-1) (a n x (y-1)) x) =KRoN=> ;; =KRoN=>print_int(a 3 8 8); =KRoN=>print_newline();; =KRoN=>
[html_font color=blue] =KRoN=>_Akkr_a_49: =KRoN=> sub esp, 8 - налицо отсутствие оформления фрэйма стэка - в спорах на этом форуме я не раз упоминал, что в современных языках вызов процедуры одно из самых дорогих удовольствий - фрэйм для локальных переменных, передача параметров через стек и т.д. Здесь произведена резервация под две временные переменные. Надо еще сказать, что параметры передаются через регистры - n - eax, x - ebx, y - ecx. Результат возвращается на eax. =KRoN=>L106: Запомните эту метку!!!! =KRoN=> cmp eax, 7 Все константы и вычисления сдвинуты по формуле 2*n+1 иниыми словами это сравнение cmp eax,3 =KRoN=> ja L101 есла n больше 3, то это общий случай =KRoN=> mov edx, eax иначе преобразуем это в индекс для косвенного перехода - таким образом закодирована первая часть match =KRoN=> sar edx, 1 =KRoN=> jmp [edx * 4 + L107] =KRoN=> .DATA Это собственно таблица косвенной адресации =KRoN=>L107 DWORD L105 n == 0 =KRoN=> DWORD L104 n == 1 =KRoN=> DWORD L103 n == 2 =KRoN=> DWORD L102 n == 3 =KRoN=> .CODE =KRoN=>L105: =KRoN=> add ebx, 2 x + 1 =KRoN=> mov eax, ebx вернуть на eax =KRoN=> add esp, 8 подровнять стек =KRoN=> ret =KRoN=>L104: =KRoN=> cmp ecx, 1 сравнение y == 0 =KRoN=> jne L10 =KRoN=> mov eax, ebx x =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L103: =KRoN=> cmp ecx, 1 y == 0 =KRoN=> jne L101 =KRoN=> mov eax, 1 вернуть 0 =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L102: =KRoN=> cmp ecx, 1 y == 0 =KRoN=> jne L101 =KRoN=> mov eax, 3 вернуть 1 =KRoN=> add esp, 8 =KRoN=> ret =KRoN=>L101: общий случай =KRoN=> cmp ecx, 1 y == 0 =KRoN=> je L100 =KRoN=> mov DWORD PTR 0[esp], eax внимание, сохраним n =KRoN=> mov DWORD PTR 4[esp], ebx и х во временные переменные =KRoN=> add ecx, -2 y - 1 =KRoN=> call _Akkr_a_49 вызовем функцию для вычисления a n x ( y - 1 ) - внутренний вызов. =KRoN=>L108: А вот здесь начинается второе место, где объектный верблюд выигрывает. ВНИМАНИЕ. Вместо вызова функции еще раз верблюд подготовил параметры на регистрах и перешел на ту метку, которую я вам советовал завпомнить - сразу после sub esp,8 - тем самым экономия сразу в двух местах - нет операции вызова - она заменена переходом (нет работы со стеком и т.д.) и еще - переиспользова- ние временных переменных без их дополнительного переразмещнния на стеке! =KRoN=> mov ebx, eax результат на eax - поместим его вторым параметром - как x =KRoN=> mov eax, DWORD PTR 0[esp] востановим n =KRoN=> add eax, -2 и отнимем 1 - т.е. мы имеем n - 1 первым параметром =KRoN=> mov ecx, DWORD PTR 4[esp] а бывший x передадим пос- ледним =KRoN=> jmp L106 А вот и тот самый переход вместо вызова!!!!! =KRoN=>L100: =KRoN=> mov eax, 5 здесь возратим 2 =KRoN=> add esp, 8 =KRoN=> ret
С++ Верблюд 2*12 - параметры 0 2*12 - временные переменные 1*8 2*4 - адрес возврата 1*4 - развернули один уровень 2*4 - оформление фрейма 0 2 возврата/вызова один вызов, один переход (двойная операция со стеком) Итого 64 байта на вызов 12 байтов
[html_font color=blue] : AA ( x y n — an ) ?DUP 0= IF DROP 1+ EXIT THEN ( x y n ) SWAP ?DUP IF ( x n y!=0 ) >R 2DUP R> 1- ( x n x n y-1 ) SWAP RECURSE ( x n a ) -ROT 1- RECURSE EXIT THEN ( x n ) DUP 1 = IF DROP ( x ) EXIT THEN DUP 2 = IF 2DROP 0 EXIT THEN 3 = IF 2DROP 1 EXIT THEN DROP 2 ; : A ( n x y — an ) ROT AA ; 3 3 3 A . CR BYE
_TEXT SEGMENT _n$ = 8 смещения до параметров на стеке _x$ = 12 относительно esp _y$ = 16 ?a@@YAHHHH@Z PROC NEAR ; a, COMDAT ; File D:Testsackermanackerman.cpp ; Line 12 push esi фрейм не оформляем - нет локальных переменных ; Line 14 mov esi, DWORD PTR _n$[esp] загрузили n - потеря у верблюда уже на регистре test esi, esi сравнили push edi сохранили регистр - потеря je SHORT $L768 mov ecx, DWORD PTR _y$[esp+4] теперь достаем y&x - потеря mov eax, DWORD PTR _x$[esp+4] $L764: ; Line 17 test ecx, ecx je SHORT $L723 ; Line 18 dec ecx готовимся к внутреннему вызову - и edi еще пихаем - а потом восстанавливаем mov edi, eax push ecx Подготовка параметров push eax push esi call ?a@@YAHHHH@Z ; a add esp, 12 ; 0000000cH выравнивание стека dec esi mov ecx, edi jne SHORT $L764 вот она замена рекурсии!!!! pop edi inc eax pop esi ; Line 27 ret 0 $L768: ; Line 14 mov eax, DWORD PTR _x$[esp+4] pop edi ; Line 15 inc eax pop esi ; Line 27 ret 0 $L723: ; Line 21 dec esi je SHORT $L721 dec esi je SHORT $L729 dec esi je SHORT $L730 pop edi ; Line 26 mov eax, 2 pop esi ; Line 27 ret 0 $L730: pop edi ; Line 24 mov eax, 1 pop esi ; Line 27 ret 0 $L729: ; Line 23 xor eax, eax $L721: pop edi pop esi ; Line 27 ret 0 ?a@@YAHHHH@Z ENDP ; a _TEXT ENDS
[blue]
PUBLIC @ackr@12
_y$ = 8
@ackr@12 PROC NEAR ; COMDAT
; _n$ = ecx
; _x$ = edx
; 9 : {
00000 56 push esi
00001 8b f1 mov esi, ecx
; 10 : if(!n)
00003 85 f6 test esi, esi
00005 57 push edi
00006 8b c2 mov eax, edx
00008 74 1c je SHORT $L884
0000a 8b 4c 24 0c mov ecx, DWORD PTR _y$[esp+4]
; 23 : }
0000e 8b ff npad 2
$L882:
; 12 :
; 13 : if(y)
00010 85 c9 test ecx, ecx
00012 74 18 je SHORT $L864
; 14 : return ackr(n-1,ackr(n,x,y-1),x);
00014 49 dec ecx
00015 51 push ecx
00016 8b d0 mov edx, eax
00018 8b ce mov ecx, esi
0001a 8b f8 mov edi, eax
0001c e8 00 00 00 00 call @ackr@12
00021 4e dec esi
00022 8b cf mov ecx, edi
00024 75 ea jne SHORT $L882
$L884:
00026 5f pop edi
; 11 : return x+1;
00027 40 inc eax
00028 5e pop esi
; 23 : }
00029 c2 04 00 ret 4
$L864:
; 15 :
; 16 : switch(n)
; 17 : {
0002c 4e dec esi
0002d 74 1c je SHORT $L862
0002f 4e dec esi
00030 74 17 je SHORT $L870
00032 4e dec esi
00033 74 0a je SHORT $L871
00035 5f pop edi
; 21 : }
; 22 : return 2;
00036 b8 02 00 00 00 mov eax, 2
0003b 5e pop esi
; 23 : }
0003c c2 04 00 ret 4
$L871:
0003f 5f pop edi
; 20 : case 3: return 1;
00040 b8 01 00 00 00 mov eax, 1
00045 5e pop esi
; 23 : }
00046 c2 04 00 ret 4
$L870:
; 18 : case 1: return x;
; 19 : case 2: return 0;
00049 33 c0 xor eax, eax
$L862:
0004b 5f pop edi
0004c 5e pop esi
; 23 : }
0004d c2 04 00 ret 4
@ackr@12 ENDP
PUBLIC _main
_main PROC NEAR ; COMDAT
; 5 : printf("%dn",ackr(3,8,8));
00000 6a 08 push 8
00002 6a 07 push 7
00004 ba 08 00 00 00 mov edx, 8
00009 b9 03 00 00 00 mov ecx, 3
0000e e8 00 00 00 00 call @ackr@12
00013 8b d0 mov edx, eax
00015 b9 02 00 00 00 mov ecx, 2
0001a e8 00 00 00 00 call @ackr@12
0001f 50 push eax
00020 68 00 00 00 00 push OFFSET FLAT:??_C@_03PMGGPEJJ@?$CFd?6?$AA@
00025 e8 00 00 00 00 call _printf
0002a 83 c4 08 add esp, 8
; 6 : }
0002d 33 c0 xor eax, eax
0002f c3 ret 0
_main ENDP
[/blue]
iackr: ; ebx = n, ecx = x, edx = y -> eax = ackr(n,x,y)
test ebx, ebx
jnz iackr_1
lea eax, [ecx+1]
ret
iackr_1:
test edx, edx
jz iackr_2
push ebx
push ecx
push edx
dec edx
call iackr
mov edx, ecx
mov ecx, eax
dec ebx
call iackr
pop edx
pop ecx
pop ebx
ret
iackr_2:
push ebx
dec ebx
jnz iackr_3
mov eax, ecx ;1
pop ebx
ret
iackr_3:
dec ebx
jnz iackr_4
xor eax, eax ;2
pop ebx
ret
iackr_4:
dec ebx
jnz iackr_5
mov eax, 1 ;3
pop ebx
ret
iackr_5:
mov eax, 2
pop ebx
ret