# GÓC SÁNG TẠO > Khu vực lập trình > Pascal/Delphi/Kylix >  THUẬT TOÁN và KỸ THUẬT LẬP TRÌNH

## trungvu

*Topic này sẽ là nơi post những Thuật toán cũng như những Kĩ thuật lập trình hay (nhớ kèm theo ví dụ cho dễ hiểu).
Đề nghị các bạn không spam lung tung chỉ click nút Thanks khi thấy bài viết hữu ích.
Những bạn nào vi phạm sẽ bị Del bài.*

----------


## aukid412

Em xin đóng góp 1 giải thuật đảo chữ theo tiếng ( không phải đảo chữ cái ):


```
uses crt;
var a,b,c,d,i,h,dem:integer;t,t1,t2,t3:string;
begin
clrscr;
write('Nhap vao ');readln(t);
begin
while t[1]=' ' do delete(t,1,1);
while t[length(t)]=' ' do delete(t,length(t),1);
for a:=1 to length(t) do
while (t[a]=' ') and (t[a+1]=' ') do delete(t,a,1);
end;
t:=' '+t+' ';
for c:=1 to length(t) do
if t[c]=' ' then
t2:=copy(t,1,length(t)-c);
 
for b:=length(t) downto 1 do
if t[b]=' ' then
begin
t1:=t1+copy(t,b,length(t)-b);
delete(t,b,length(t)-b);
end;
t3:=t2+t1;
delete(t3,1,1);
write('Cau dao nghich la ','"',t3,'"');
readln;
end.
```

#-o

----------


## sownlee

_Kỹ thuật khử đệ quy_
​ *Trần Đức Thiện*
​_
_
_Đệ quy là quả tim trong các nghiên cứu lý thuyết cũng như thực hành tính toán, đã thể hiện rất nhiều sức mạnh và có ưu điểm trong nhiều bài toán. Tuy nhiên bài này tôi lại đi ngược với công việc chúng ta thường làm: khử đệ quy, đó là vấn đề cũng có nhiều thú vị và đáng để chúng ta xem xét._


Khử đệ quy ở đây là biến một thủ tục đệ quy thành một thủ tục chỉ chứa vòng lặp mà không ảnh hưởng gì đến các yếu tố khác, chứ không phải là thay đổi thuật toán. Ví dụ như trong các hàm đệ quy tính n! và số Fibonaci F(n) ta có thể thay bằng một vòng lặp để tính; Đó không phải là phương pháp khử đệ quy mà tôi muốn nói. Trong trường hợp tổng quát, khử đệ quy là một việc làm khá phức tạp và khó khăn. ở hàm n! hay F(n) ta có thể dùng một thuật toán không đệ quy, nhưng trong một số bài toán, đệ quy là bắt buộc. Bạn có thể nói rằng, vậy thì cứ sử dụng đệ quy, vừa ngắn gọn dễ hiểu, vừa dễ cài đặt. Nhưng có đôi khi, sự hạn hẹp của bộ nhớ dành cho chương trình con không cho phép chúng ta làm điều đó; hoặc chúng ta biết rằng, ngôn ngữ máy không có đệ quy, vì vậy các trình biên dịch đều phải có nhiệm vụ khử đệ quy. Và bạn có thể thực sự gặp rắc rối với thủ tục đệ quy của mình khi trong một môi trường lập trình mà không cung cấp khả năng gọi đệ quy. Khử đệ quy giúp bạn vẫn giữ được nguyên bản thuật toán đệ quy của mình mà không hề có lời gọi đệ quy, và như thế chương trình có thể chạy được trong bất kỳ môi trường lập trình nào.
Khử đệ quy thực chất là chúng ta phải làm công việc của một trình biên dịch đối với một thủ tục, đó là: Đặt tất cả các giá trị của các biến cục bộ và địa chỉ của chỉ thị kế tiếp vào ngăn xếp (Stack), quy định các giá trị tham số cho thủ tục và chuyển tới vị trí bắt đầu thủ tục, thực hiện lần lượt từng câu lệnh. Sau khi thủ tục hoàn tất thì nó phải lấy ra khỏi ngăn xếp địa chỉ trả về và các giá trị của các biến cục bộ, khôi phục các biến và chuyển tới địa chỉ trả về.


Để dễ theo dõi chúng ta lấy ví dụ với bài toán cụ thể là bài toán duyệt cây. Giả sử có một cây nhị phân lưu trữ trong biến động t được định nghĩa:

 


```
type pnode = ^node;
node = record
  inf : variable; { truong luu tru thong tin }
  l,r : pnode;
end;
var t : pnode;
```

 
Xuất phát từ nút gốc t, cần duyệt qua hết cây theo thứ tự từ trái qua phải. Chương trình con đệ quy sẽ như sau:

 


```
procedure Try(t : pnode);
begin
if t <> nil then
  begin
    visit(t);
    Try(t^.l);
    Try(t^.r);
  end;
end;
```



Trước hết có thể thấy rằng lệnh gọi đệ quy thứ hai có thể được khử dễ dàng bởi không có mã lệnh theo sau nó. Khi lệnh này thực hiện thì thủ tục Try( ) được gọi với tham số t^.r và khi lệnh gọi này kết thúc thì thủ tục Try hiện hành cũng kết thúc. Chương trình được viết lại như sau dùng goto:



```
     procedure try(t : pnode);
label 0;
begin
0 : if t = nil then exit;
visit(t);
try(t^.l);
t := t^.r;
goto 0;
end;
```

 
Đó là kỹ thuật rất nổi tiếng được gọi là khử đệ quy phần cuối. Việc khử lần gọi đệ quy còn lại đòi hỏi phải làm nhiều việc hơn. Giống như một trình biên dịch chúng ta phải tổ chức một ngăn xếp (Stack) để lưu trữ các biến cục bộ, các tham số, và sử dụng các thủ tục:

Push(t): Đặt biến t vào đỉnh Stack;
Hàm pop: lấy 1 giá trị ở đỉnh stack.
Hàm stackempty: Báo hiệu Stack đã rỗng.

ở đây không có giá trị trả về và chỉ có một biến cục bộ là t nên chúng ta sẽ nạp nó vào stack nếu chưa được xử lý và ở mỗi bước chúng ta lấy biến ở đỉnh stack ra để xử lý nó và các nút con tiếp theo của nó. Chương trình khử cả lời gọi đệ quy thứ hai sẽ như sau:

 


```
procedure try(t : pnode);
label 0,1,2;
begin
0: if t = nil then goto 1;
visit(t);
push(t);
t := t^.l;
goto 0;
2 : t := t^.r; goto 0;
1 : if stackempty then exit;
t := pop;
goto 2;
end;
```

 
Thủ tục trên chỉ là diễn giải thô của ý tưởng để các bạn dễ hiểu, vì vậy các chỉ thị goto còn rườm ra, chúng ta sẽ viết lại một cách có cấu trúc hơn như sau:

 


```
procedure try(t : pnode);
label 0;
begin
0: while t <> nil do
begin
visit(t);
push(t^.r);
t := t^.l
end;
if stackempty then exit;
t := pop;
goto 0;
end;
```

Bây giờ, loại bỏ hoàn toàn các chỉ thị goto và tránh trường hợp nạp các nút rỗng vào stack ta có thủ tục duyệt cây không đệ quy chuẩn như sau, các bạn sẽ thấy rằng về bản chất nó không khác thủ tục đệ quy là mấy: 




```
procedure try(t : pnode);
begin
  push(t);
  repeat
    t := pop;
    visit(t);
    if t^.l <> nil then push(t^.l);
    if t^.r <> nil then push(t^.r);
  until stackempty;
end;
```

 Để minh hoạ cụ thể hơn cho kỹ thuật này, tôi xin trình bày với các bạn chương trình sắp xếp nhanh(QuickSort) khử đệ quy:


```
Program Quick_sort_Khu_de_quy_Th;
  const
    inp = 'FileName.inp';
    out = 'FileName.out';
    maxstack = 1000;
    maxn = 1000;
  type
    it = longint;
  var
    a : array[1..maxn] of it;
    sl,sr : array[1..maxstack] of word;
    n,top : it;
    f : text;
   
  procedure push(l,r : word);
  begin
    inc(top);
    sl[top] := l;
    sr[top] := r;
  end;
   
  procedure pop(var l,r : word);
  begin
    l := sl[top];
    r := sr[top];
    dec(top);
  end;
   
  function stackEmpty : boolean;
  begin
    stackempty := top = 0;
  end;
   
  procedure init;
  begin
    top := 0;
  end;
   
  procedure nhap;
  var
    i : it;
  begin
    assign(f,inp);
    reset(f);
    readln(f,n);
    for i := 1 to n do read(f,a[i]);
    close(f);
  end;
   
  procedure sort(l1,r1 : word);
  var
    l,r,i,j : word;
    t,tg : it;
  begin
    push(l1,r1);
    repeat
      pop(l,r);
      i := l;
      j := r;
      t := a[(l+r) div 2];
      repeat
        while a[i] < t do inc(i);
        while t < a[j] do dec(j);
        if i <= j then
        begin
          tg := a[i];
          a[i] := a[j];
          a[j] := tg;
          inc(i);
          dec(j);
        end;
      until i > j;
   
      if i < r then push(i,r);
      if l < j then push(l,j);
    until stackEmpty;
  end;
   
  procedure xuat;
  var
    i : it;
  begin
    assign(f,out);
    rewrite(f);
    for i := 1 to n do write(f,a[i],' ');
    close(f);
  end;
   
  BEGIN
    nhap;
    init;
    sort(1,n);
    xuat;
  END.
```

 
Trong một lúc nào đó chúng ta có thể khảo sát tính hiệu quả của việc khử đệ quy. Còn bây giờ bạn vẫn có thể trung thành với thủ tục Try đệ quy của mình, nó thực sự ngắn gọn, dễ hiểu và dễ cài đặt. Dù có thể không dùng đến nhưng nghiên cứu thêm là một việc không hề thừa. Biết đâu sau này bạn trở thành một người viết chương trình dịch thì sao, thế thì nó − bài viết này rất bổ ích cho bạn rồi đấy. Chào thân ái và hẹn gặp lại.

----------


## bentremegumi

Mình xin cung cấp thêm 2 thuật toán tìm kiếm và sắp sếp (tăng dần) với thời gian tính toán tốt hơn làm tuần tự:
* Sắp xếp (Quick Soft):


```
Procedure QS(var a:array[1..n] of integer);
Procedure Soft(left,right:integer);
Var i,j,a,x:integer;
Begin
X:=A[(left+right) div 2];
i:=left;
j:=right;
Repeat
While (a<x) do inc(i);
While (A[j]>x) do dec(j);
If j<=j then
Begin
[i] a{biến trung gian}:=a;
[i] a:=a[j];
a[j]:=a;
inc(i);
dec(j);
End;
Until i>j;
If left<j then sort(left,j);
If i<right then sort(i,right);
Begin
sort(1,n);
End;
End.
```


_---------------------------------------------------------------------------_
_< Thuật toán trên được làm theo cách top-down, tức là cùng làm điều kiện (ở đây là xử lý phần tử giữa mảng với phần tử đầu (cuối) rồi xét điều kiện để hoán vị) cho mảng ban đầu rồi cho các mảng chia nhỏ theo phương thức nhị phân (từ phần tử đầu đến phần tử giữa mảng, từ phần tử liền sau phần tử giữa mảng đến phần tử cuối mảng >_
_Lưu ý: áp dụng cho mảng được sắp sếp theo thứ tự tăng dần, điều kiện để rút ngắn thời gian tính toán của một số bài tìm kiếm sự có mặt trong mảng._
_* Tìm kiếm:_
_
_

```
Function Tim(x:integer;a:array[1..n] of integer):word;
Var dau,giua,cuoi:integer;
Begin
dau:=1;cuoi:=n;
While cuoi>=dau do
Begin
giua:=(dau+cuoi) div 2;
If (a[giua]<x) then
dau:=giua+1
Else
If (x<a[giua]) then
cuoi:=giua-1;
Else
cuoi:=-1;
End;
If cuoi:=-1 then Tim:=giua
else Tim:=0;
End.
```


_-----------------------------------------------------------------------------------_
_< Thuật toán sử dụng chia mảng chứa dữ liệu thành các mảng nhỏ hơn, với cùng một thuật toán cho các mảng (nhỏ, lớn) cho ta một thời gian tính toán chấp nhận được >
Lưu ý: Thuật toán chỉ sử dụng được với mảng được sắp xếp theo thứ tự tăng dần.
> Có gì sai sót hoặc ý kiến cải thiện thuật toán xin liên hệ YM!:isukeshiro <[IMG]data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAA  l21bKAAAAA1BMVEXh5PJm+yKVAAAAAXRSTlMAQObYZgAAAApJR  EFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=[/IMG])_

----------


## duancanhotp

Thuật toán 8 quân hậu và mã đi tuần chắc hẳn khá quen thuộc với các bạn, nhưng nhiều bạn vẫn chưa có một chương trình tham khảo, với kinh nghiệm của mình, mình xin đưa ra giải thuật cho hai bài toán đó:
*8 quân hậu:*
*


```
[/U]
```

*

```
Var 
a:array[1..8] of integer;
b:array[2..16] of integer;
c:array[-7..7] of integer;
x:array[1..8] of integer;
i:integer;
Procedure print;
var j:integer;
Begin
For j:=1 to 8 do write(x[k]:4);
Writeln;
Readln;
end;
Procedure try(i:integer);
var j:integer;
Begin
If i>8 then print 
Else for j:=1 to 8 do
if (a[j]=0) and (b[i+j]=0) and (c[i-j]=0) then
Begin
x:=j;
a[j]:=1;b[i+j]:=1;c[i-j]:=1; {thông cảm, mình làm biếng xuống hàng rồi gõ space quá:emlaugh:}
try(+1);
a[j]:=0;b[i+j]:=0;c[i-j]:=0; {bạn biết lý do không xuống hàng rồi nhỉ :shifty:}
End;
End;
Procedure init;
var j:integer;
Begin
fillchar(a,sizeof(a),0);
fillchar(b,sizeof(b),0);
fillchar(c,sizeof(c),0);
end;
Begin
init;
try(1);
End;
```


_---------------------------------------------------_
_Mã đi tuần:_
*


```
[/U]
```

*

```
Const
a:array[1..8] of integer=(2,1,-1,-2,-2,-1,1,2);
b:array[1..8] of integer=(1,2,2,1,-1,-2,-2,-1);
n=5;nsq=25;
Type
index=1..n;
var
q:boolean;
dd:array[index,index] of integer;
s:set of index;
x,y:integer;
procedure init;
begin
fillchar(dd,size of(dd),0);
writeln('Cho biet toa do ban dau cua ngua');
readln(x,y);
dd[x,y]:=1;
q:=false;
s:=[1,2,3,4,5];
end;
procedure print;
var i,j:integer;
begin
q:=true;
for j:=1 to n do
begin
for j:=1 to n do write(dd[i,j]:5);
writeln;
end;
end;
procedure try(i);
var j,u,v:integer;
begin
if i>nsq then
print
else for j:=1 to 8 do
begin
u:=x+a[j];
v:=y+b[j];
if (u in s) and (v in s) and (dd[u,v]=0) then
begin
dd[u,v]:=i;
x:=u;y:=v;
try(i+1);
x:=u-a[j];y:=v-b[j];
dd[u,v]:=0;
end;
end;
end;
BEGIN
init;
try(2);
if q=false then writeln('no solution');
end;
```


_--------------------------------------------------------------------------------_
_2 bài trên là mình đóng góp chứ không phải ý tưởng của mình, có gì thắc mắc mấy bạn hỏi 1 lượt để mình trả lời, mình hiện tại chưa thật sự rõ giải thuật này :emlaugh:_[/B][/B]

----------


## jindo11111

*Sơ đồ đệ quy quay lui*

Về sơ đồ đệ quy quay lui hẳn là có nhiều người đã biết, song bên cạnh đó số người không biết không phải là ít, mình xin cung cấp sơ đồ như sau:
procedure try(i:integer);
var j:integer;
begin
if i>m then print{hàm xuất} else
for j:=1 to n do if *Nhận được* then
begin
*Ghi nhận nó;*
try(i+*...*);
*Xóa bỏ ghi nhận;* {back}
end;
end;
-----------
Sau đó khởi động với *try(1)*.
_ Hi vọng sơ đồ này sẽ giúp ít cho nhiều bạn muốn tìm hiểu về đệ quy:book:. Thân_

----------


## langocthao

*Công thức thu gọn*

*Tổng n số nguyên dương lẻ đầu tiên:* bạn sẽ phải dùng 1 dòng while hay repeat để tính giá trị tổng, bạn hãy quên nó đi, sử dụng công thức: _Tổng các số nguyên dương lẻ=n^2_.
*Tổng n số nguyên dương chẵn đầu tiên:* Được tính theo công thức f(n)=2*(f(n-1)+n). Dùng đệ quy nhá*.*
*Tổng số hoán vị n số tự nhiên đầu tiên (1<=n<=9*): bạn làm từng hoán vị rồi đếm à, hãy xem công thức mới nhé: n*!.*
*Tổng số tập hợp tạo bởi 2 phần tử của tập hợp a (mảng a):* Nghe phức tạp nhỉ, mình đã chứng minh, yên tâm nhé: Sum(x,1,n-1). (n là số phần tử mảng a).
*Tổng số đường thằng trong mặt phẳng tạo bởi n điểm bất kỳ:* chà, công thức hơi rắc rối: tổ hợp chập 2 của n: n!/(2*(n-2)!).
_Chúc các bạn học tốt. Thân._

----------


## seller79

Mình xin trình bày giải thuật đảo chuỗi thành chuỗi ngược lại mà không sử dụng đến for, while hay repeat...until... :


```
procedure xuly(i:integer);
var s1:string;
begin
  if i<1 then write(s)
     else
         begin
              s1:=copy(s,i,1);
              delete(s,i,1);
              s:=s+s1;
              xuly(i-1);
         end;
end;
```

Ai có cách khác post lên cho anh em học tập nhé!:book:

----------


## dangban321

Sau đây là giải thuật in ra hoán vị của n số nguyên dương đầu tiên, số n nằm trong file _f.inp_ , các hoán vị nằm trong tệp _f.out._



```
[/FONT]
const fi='f.inp';
      fo='f.out';
      max=20;
var i,n:integer;a:array[1..max] of byte;b:array[1..max]of boolean;
    f:text;
procedure print;
var k:integer;
begin
     for k:=1 to n do write(f,a[k]);
     writeln;
end;
procedure xuly(i:integer);
var j:integer;
begin
     if i>n then print
        else
            for j:=1 to n do
                if b[j] then
                   begin
                        a[i]:=j;
                        b[j]:=false;
                        xuly(i+1);
                        b[j]:=true;
                   end;
end;
procedure input;
begin
     assign(f,fi);
     reset(f);
     read(f,n);
     close(f);
end;
procedure output;
begin
     assign(f,fo);
     rewrite(f);
     xuly(1);
     close(f);
end;
BEGIN
     input;
     for i:=1 to n do b[i]:=true;
     output;
END.
[FONT=Courier New]
```

*Thân.:book:*

----------


## minhkiet0907

*Giải thuật tính số fibonacy cỡ lớn!*

Kỹ thuật tính hạng tử thứ n trong dãy fibonacy: 1 1 2 3 5 8 13 21 34 55 ....


```
uses crt;
var n,i:byte;a,b:extend;
begin
        clrscr;
        write('Nhap n ');
        readln(n);
        a:=1;
        b:=1;
        for i:=3 to n do
                if a>b then b:=b+a
                        else a:=a+b;
        if a>b then write('f(n)=',a:0:0)
                else write('f(n)=',b:0:0);
        readln;
end.
```

:book:

----------


## phamvulinh

"Kỹ thuật tính hạng tử thứ n trong dãy fibonacy: 1 1 2 3 5 8 13 21 34 55 ...."

thuật toán này :
gọi a,b là nghiệm của pt x^2-x-1=0 (a>b).

số Fibonacy thứ n được tính theo cồng thức :
F(n)= ((a^n)-(b^n))/sqrt(5);



```
uses crt;
var n:longint;
    a,b:extended;
begin
   clrscr;
   a:=(1+sqrt(5))/2;b:=abs((1-sqrt(5))/2);
   write('NHAP N : ');readln(n);
   a:=exp(n*ln(a));b:=exp(n*ln(b));
   if n mod 2 = 1 then b:=-b;
   writeln('SO FIBONACY THU N LA : ',(a-b)/sqrt(5):0:0);
   readln
end.
```

----------


## hautran200594

Mới gia nhập, em đóng góp code giải bài in ra n*m số nguyên dương đầu tiên theo thứ tự hình trôn ốc (cùng chiều kim đồng hồ) trên n dòng m cột


```
var fi,fo:text;
      a:array[1..100,1..100]of longint;
    n,m,i,j,d,c,mc,nd,so:longint;
begin
 assign(fi,'tronoc.inp');reset(fi);
 assign(fo,'tronoc.out');rewrite(fo);
 read(fi,n,m);
 i:=1;j:=0;so:=0;c:=1;d:=2;mc:=m;nd:=n;
 repeat
  j:=j+1;
  repeat
   so:=so+1;
   a[i,j]:=so;
   j:=j+1;
  until j>mc;
  j:=j-1;
  if so>=m*n then break;
  i:=i+1;
  repeat
   so:=so+1;
   a[i,j]:=so;
   i:=i+1;
  until i>nd;
  i:=i-1;
  if so>=m*n then break;
  repeat
   j:=j-1;
   so:=so+1;
   a[i,j]:=so;
  until j<=c;
  if so>=m*n then break;
  c:=c+1;
  repeat
   i:=i-1;
   so:=so+1;
   a[i,j]:=so;
  until i<=d;
  d:=d+1;
  mc:=mc-1;
  nd:=nd-1;
 until so>=m*n;
 for i:=1 to n do
  begin
   for j:=1 to m do write(fo,a[i,j],' ');
   writeln(fo);
  end;
 close(fi);close(fo);
end.
```

=====================
*Bạn hãy đưa đoạn code vào thẻ code nhé. Chúc bạn vui vẻ!*

----------


## skygame

Sàng Eratosthene:


```
uses crt;
const
        max=1000000000;
var
  n,i,j:longint;
  a:array[1..max] of boolean;
begin

        clrscr;
        write('Nhap n=');
        readln(n);
        fillchar(a,sizeof(a),false);
        for i:=2 to n do
                if a[i]=false then
                        begin
                                write(i:10);
                                j:=2;
                                while i*j<=n do
                                        begin
                                                a[i*j]:=true;
                                                j:=j+1;
                                        end;
                        end;
        readln;
end.
```

Ai có cách nào hay hơn thì đóng góp nhé, còn cái Sàng Atkin mình chưa hiểu rõ, ai biết thì đưa lên cho anh em học tập [IMG]data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAA  l21bKAAAAA1BMVEXh5PJm+yKVAAAAAXRSTlMAQObYZgAAAApJR  EFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=[/IMG]

----------


## Văn Chiến

*1. Kiểu số nguyên lớn* 
Có rất nhiều bài toán cần chúng ta phải tính toán với những số nguyên lớn. Chẳng hạn như tính giai thừa, tính số Fibonacci hay tìm các số nguyên tố lớn (chẳng hạn tìm các số nguyên tố lớn để dùng trong thuật toán mã hoá RSA). Với kiểu Integer của TP ta tìm được số nguyên tố lớn nhất có 5 chữ số. Với kiểu LongInt thì được 9 chữ số. Muốn tìm được các số to hơn thì phải dùng kiểu số thực (như comp hay extended). Nhưng có điều bất tiện là các kiểu số thực thì không dùng các phép toán div, mod được nên cài đặt rất khó khăn. 
Ngoài bài toán về tìm số nguyên tố lớn, với những bài toán khác như tính giai thừa, tính số Fibonacci,… kiểu integer của TP rất hạn chế. 
Một hạn chế thứ hai với kiểu integer của TP là hay gặp các lỗi tính toán số học. (Không biết bạn có đặt {$Q+} để phát hiện các lỗi như vậy chưa). Lỗi tính toán số học xảy ra khi chúng ta tính biểu thức có các hạng tử trong miền integer nhưng kết quả thì nằm ngoài miền (chẳng hạn 30000 + 30000). Những lỗi như vậy thường ít khi ta để ý, nhưng rất phiền toái. Sửa chúng thì cũng không khó lắm, chỉ cần chuyển đổi kiểu (thành LongInt hay Real) là OK. 
Với FP thì những hạn chế đó không thành vấn đề. Với lợi thế 32 bit (gấp đôi TP), FP cung cấp kiểu Int64, mới nghe chắc bạn cũng đoán được đó là kiểu số nguyên 64 bit. Với Int64 các bạn có thể tìm được các số nguyên tố 18 chữ số (cỡ tỉ tỉ) hay tính được giai thừa của 20. (Nếu vẫn muốn hơn thì ta phải xây dựng riêng một kiểu BigInt, ta sẽ làm điều đó trong phần sau). 
Trong trường hợp muốn tiết kiệm bộ nhớ, ta vẫn có thể sử dụng kiểu Byte (kích thước 1 byte), SmallInt (kích thức 2 byte). 
_Chú ý:_ với kiểu Integer, mặc định FP dùng kích thước 2 byte. Vì vậy khi muốn dùng kiểu nguyên lớn, ta nên khai báo rõ ràng. 

*2. Kiểu string lớn* 
Khi lập trình, chúng ta rất nhiều lần gặp vấn đề với các xâu tối đa 255 kí tự của TP (chẳng hạn bài toán xâu đối xứng, bài toán đếm từ…). Ta có thể giải quyết vấn đề bằng mảng kí tự (array of char) nhưng khi đó ta lại không thể dùng các phép toán trên xâu rất mạnh của Pascal. 
Không chỉ có cải tiến về kiểu nguyên, kiểu string trong FP cũng rất tuyệt vời. *String trong FP không còn hạn chế 255 kí tự của TP nữa mà có kích thước tối đa là 2.. tỉ kí tự*. Hơn nữa FP còn hỗ trợ kiểu xâu Unicode (WideString). Nếu bạn vẫn thích kiểu String cũ của TP, bạn có thể dùng kiểu ShortString. 
Bây giờ bạn có thể viết chương trình giải bài xâu đối xứng, xâu con chung với kiểu string của trên FP và hạn chế n cỡ 1000 một cách rất dễ dàng. Chúng ta sẽ tìm hiểu kĩ hơn về xâu trong FP ở một bài báo khác. 

*3. Viết hàm thuận lợi hơn* 
FP có rất nhiều cải tiến trong cách viết các hàm. Để so sánh, chúng ta sẽ xem xét một số ví dụ. Trong TP, chúng ta viết hàm tính giai thừa như sau: 



```
function gt(n:integer):integer; 
var  
i,tg:integer; 
begin 
tg:=1; 
for i:=1 to n do tg:=tg*i;  
gt:=tg; 
end;
```

Tại sao ta lại phải thêm một biến tg để lưu trữ kết quả trung gian? Đó là do trong TP với tên hàm ta chỉ được sử dụng duy nhất lệnh gán trị. Nếu đưa tên hàm vào biểu thức thì sẽ thực hiện lời gọi hàm. 
Điều này đã được FP làm cho free bằng cách *cho phép sử dụng tên hàm như một biến* (giống như Object Pascal dùng biến Result). Khi đó tên hàm có thể xuất hiện ở trong cách biểu thức tính toán ngay trong thân hàm mà không tạo ra lời gọi đệ quy. Hàm giai thừa trong FP có thể viết rất tiết kiệm biến như sau: 



```
function  Gt(n: integer): int64; 
begin 
gt := 1; 
for n := n downto 2 do gt :=  gt * n; 
end;
```

Vậy khi ta muốn gọi đệ quy thì sao? Thì chỉ việc thêm cặp dấu () và truyền tham số cần thiết. FP sẽ biết ta muốn gọi đệ quy khi ta có thêm cặp (). Hàm giai thừa viết kiểu đệ quy như sau: 



```
function Gt(n: integer):  int64; 
begin 
if n=0 then exit(1) 
else exit(n*gt(n-1)); 
end;
```

Trong cách viết này ta còn thấy một điều tiện lợi của FP: *dùng lệnh exit để trả lại kết quả cho hàm* (giống như C và Object Pascal sử dụng lệnh *return*). Bạn sẽ thấy sự tiện lợi của cách viết này khi viết các hàm dạng "_ phát hiện được phần tử đầu tiên rồi thoát_". 
Chẳng hạn hàm tìm vị trí của phần tử x trong mảng a có n phần tử. Viết trong TP ta phải viết như sau: 



```
function Find(x: integer): integer; 
Var 
i : integer; 
begin  
for i := 1 to n do 
if a[i] = x then begin 
Find := i; 
exit;  
end; 
Find := 0; 
end;
```

Hàm này viết trong FP thì ngắn ngọn hơn nhiều: 



```
function Find(x: integer): integer; 
Var 
i : integer;  
begin 
for i := 1 to n do 
if a[i]=x then exit(i); 
exit(0);  
end;
```

*4. Kết quả trả lại của hàm có thể là kiểu cấu trúc* 
Rất nhiều ngôn ngữ lập trình thông dụng như C, VB… chỉ cho phép kết quả trả lại của hàm là các kiểu cơ sở như: số nguyên, số thực, kí tự, con trỏ… Riêng TP thì có thêm kiểu xâu kí tự. Nếu muốn trả lại kết quả là kiểu cấu trúc như mảng hay bản ghi thì bạn chỉ có cách dùng tham biến thôi. Một số NNLT hướng đối tượng như C++, Java, Object Pascal có cho phép kết quả trả lại là một đối tượng, nhưng như vậy vẫn không free như FP: *FP cho phép kết quả của hàm có thể là kiểu cấu trúc*. 
Để hiểu hơn chúng ta cùng làm bài toán sau (Đề thi OLP2004): 
Gọi X là tập tất cả các xâu nhị phân không có 2 bit 1 liền nhau. Các xâu được đánh thứ tự theo trình tự được sinh ra (từ nhỏ đến lớn, bit 0 đứng trước bit 1).
Bài toán đặt ra: hãy xác định xâu nhị phân n bit ứng với số thứ tự m cho trước. Hạn chể: n <= 200. 
_Bài toán này có thuật giải như sau:_ 
Gọi L[k] là số các xâu nhị phân như vậy có k bit. Nếu bit thứ k của nó là bit 0 thì k-1 bit còn lại là tự do (tức là ta có L[k-1] dãy). Nếu thứ k của nó là bit 1 thì bit k -1 phải là 0, và k-2 bit còn lại free. Vậy ta có: L[k] = L[k-1] + L[k-2]. Chú ý: L[1]=2 và L[2]=3. 
Có công thức đó, ta tính số các xâu có n bit. Để xác định xâu nhị phân n bit có thứ tự m cho trước ta có nhận xét: nếu _m > L[n-1] thì nhất định bit thứ n phải là 1_ (vì thứ tự của xâu có bit 0 đứng trước xâu có bit 1, và có đúng L[n-1] xâu có bit thứ n là bit 0). Xâu n-1 bit còn lại có sẽ thứ tự là m-L[n-1]. _Ngược lại thì bit thứ n là bit 0_ và xâu n-1 bit còn lại có thứ tự là m. 
Do đó ta có thể làm như sau: 



```
for i:=n downto 1 do 
if m <= L[i-1] then 
x[i]  := 0 
else begin 
x[i] := 1; 
m := m - L[i-1]; 
end;
```

Tuy nhiên n có thể bằng 200 nên m và các giá trị L_ có thể xấp xỉ 2200, tức là cỡ 1070 (vì 210 xấp xỉ 103 ). Ta không thể dùng các kiểu số có sẵn mà phải tự xây dựng một kiểu số lớn hơn. 
Có nhiều người thích dùng xâu để biểu diễn số lớn, nhưng tôi thấy dùng mảng thì thích hợp hơn. Ta dùng mảng biểu diễn số nguyên lớn, mỗi phần tử của mảng là một chữ số. Các chữ số lưu trữ trong mảng theo chiều từ trái sang phải: chữ số hàng đơn vị là phần tử 1, chữ số hàng chục là phần tử 2… 
Ta khai báo kiểu số lớn đó như sau: 
const max = 100; 
type BigInt = array[1...max] of byte; 
Để cộng, trừ 2 số nguyên lớn biểu diễn bằng mảng, ta dùng thuật toán cộng kiểu thủ công (cộng các chữ số từ bậc thấp đến bậc cao, có nhớ). Các thủ tục cộng, trừ viết trong TP như sau: 
_[I]


```
procedure cong(var a,b,c : BigInt); 
var 
i,t :  integer; 
begin 
t := 0; 
for i:=1 to max do begin 
t := t + a[i] +  b[i]; 
c[i] := t mod 10; 
t := t div 10; 
end; 
end; 
procedure  tru(var a,b,c : BigInt); 
var 
i,t : integer; 
begin 
t := 0;  
for i:=1 to max do begin 
t := a[i] - b[i] - t; 
if t < 0 then  begin 
c[i] := 10 + t; 
t := 1; 
end else begin; 
c[i] := t; 
t  := 0; 
end 
end; 
end;
```

Ta khai báo L là mảng 200 phần tử kiểu BigInt. Gán L[1] là 2, L[2] là 3 rồi tính các phần tử khác bằng câu lệnh như sau: 
for i:=3 to n do cong(L[i-1],L[i-2],L_); 
Viết như vậy thì chương trình hoạt động tốt, nhưng không trực quan lắm. Nếu có thể viết L:=cong(L[i-1],L[i-2]) thì sẽ trong sáng hơn nhiều. Trong TP thì không thể, nhưng trong FP hoàn toàn có thể viết được như vậy. Sau đây là toàn bộ chương trình nguồn giải bài toán này viết bằng FP: 
_[I][I]


```
program Xau2bit1; 
const  
inp = ’fibo.inp’; 
out = ’fibo.out’; 
max = 100; 
type 
BigInt =  array[1..max] of byte; 
var 
n : integer; 
m : BigInt; 
s : string;  
kq: array[1..200] of byte; 
L : array[1..200] of BigInt; 
f : text;  
{==================================} 
procedure nhap; 
var 
c :  char; 
i : integer; 
begin 
assign(f,inp); 
reset(f);  
readln(f,n); 
readln(f,s); 
close(f); 
end;  
{==================================} 
function chuyen(s : string):  BigInt; 
var 
i : integer; 
begin 
for i:=1 to length(s) do  
chuyen[i]:=ord(s[i]) - ord('0'); 
for i:=i+1 to max do 
chuyen[i]:=0;  
end; 
function cong(var a,b : BigInt): BigInt; 
var 
i,c : integer;  
begin 
c := 0; 
for i:=1 to max do begin 
c := c + a[i] + b[i];  
cong[i] := c mod 10; 
c := c div 10; 
end; 
end; 
function  tru(var a,b : BigInt): BigInt; 
var 
i,c : integer; 
begin 
c := 0;  
for i:=1 to max do begin 
c := a[i] - b[i] - c; 
if c < 0 then  begin 
tru[i] := 10 + c; 
c := 1; 
end else begin; 
tru[i] := c;  
c := 0; 
end 
end; 
end; 
function nho(var a,b : BigInt):  boolean; 
var 
i : integer; 
begin 
for i := max downto 1 do 
if  a[i] < b[i] then 
exit(true) 
else 
if a[i] > b[i] then  exit(false); 
exit(true) 
end; 
{==================================}  
procedure tinh; 
var 
i : integer; 
begin 
m := chuyen(s);  
L[1]:=chuyen(’2’); 
L[2]:=chuyen(’3’); 
for i:=3 to n do 
L[i] :=  cong(L[i-1],L[i-2]); 
end; 
procedure tim; 
var 
i : integer;  
begin 
for i:=n downto 2 do 
if nho(m, L[i-1]) then 
kq[i] := 0  
else begin 
kq[i] := 1; 
m := tru(m,L[i-1]); 
end; 
if m[1] >  1 then kq[1] := 1 
else kq[1] := 0; 
end;  
{==================================} 
procedure inkq; 
var 
i :  integer; 
begin 
assign(f,out); 
rewrite(f); 
for i:=n downto 1 do  write(f,kq[i]); 
close(f); 
end; 
{==================================}  
BEGIN 
nhap; 
tinh; 
tim; 
inkq; 
END.
```

Trong chương trình này, ngoài 2 hàm cộng, trừ, tôi còn viết thêm 1 hàm để chuyển một xâu biểu diễn số nguyên thành một số nguyên kiểu BigInt và một hàm để so sánh 2 số kiểu BigInt. 
Đọc chương trình nguồn, các bạn cảm thấy thế nào? Tôi thì thấy rất thoải mái, vì bây giờ với FP có thể viết rất nhiều hàm mà trước đây trên TP phải viết bằng thủ tục một cách không rõ ràng lắm. Hàm trong FP có thể trả lại kiểu cấu trúc là một điều tôi thấy cực kì thú vị, vì chưa từng thấy một ngôn ngữ lập trình thông dụng nào làm được điều đó.

*1. Mảng mở (open array)* 
Để một biến kiểu mảng có thể làm tham số cho một chương trình con, ta bắt buộc phải khai báo trước về kiểu mảng đó. Chẳng hạn khai báo sau không hợp lệ cả trong TP và FP: 
function are (x,y: array[1..100] of real): real; 
Ta phải sửa lại thành khai báo như sau: 


```
type 
TArr1 = array[1..100] of real; 
function are (x,y: TArr1): real;
```

Vấn đề tạm thời được giải quyết. Tuy nhiên lại gặp một vấn đề mới là đối số truyền cho hàm area bắt buộc sẽ phải có kiểu TArr1. Vấn đề thứ hai là khai báo mảng phải xác định trước số phần tử, sẽ gây bất tiện khi số phần tử thực sự nhiều hơn hoặc ít hơn. 
FP có một cải tiến là cho phép khai báo tham số của chương trình là kiểu mảng mở. Chẳng hạn hàm area trên có thể khai báo trong FP như sau: 
function areăx,y: array of real): real; 
Vậy khi chạy làm thế nào để xác định được số phần tử thực sự của tham số? Cách đơn giản nhất là chúng ta thêm một tham số nữa để mô tả số phần tử. Nếu không thích như vậy, chúng ta có thể dùng hàm Low và High để xác định chỉ số đầu và cuối của mảng. Thông thường thì với mảng mở, chỉ số đầu là 0, chỉ số cuối là n-1 với n là số phần tử của mảng (giống như trong ngôn ngữ C). 
Sau đây là một ví dụ về hàm tính diện tích đa giác đơn bằng phương pháp hình thang viết trong FP: 


```
function  area(x,y: array of real): real; 
var 
i,n : integer; 
begin 
n :=  high(x); 
result := (x[0] - x[n]) * (y[0] + y[n]); 
for i := 0 to n-1 do  
result += (x[i+1] - x)*(y[i+1] + y); 
result := abs(result) / 2;  
end;
```

_Cách viết hàm có sử dụng một số cải tiến của FP: toán tử C-like, biến result là giá trị trả lại của hàm. 
Trong TP cũng có kiểu mảng mở như vậy (không tin bạn có thể thử khai báo). Tuy nhiên mảng mở trong FP có một điều thú vị mà trong TP không có, đó là mảng tạo trong khi chạy. 
Để dễ hiểu, bạn hãy quan sát lời gọi hàm area của tôi đối với một tứ giác có 4 đỉnh là Ăx1,y1), B(x2,y2), C(x3,y3), D(x4,y4). 
s := areă[x1,x2,x3,x4], [y1,y2,y3,y4]); 
Với lời gọi đó, khi chạy, chương trình sẽ tạo một mảng x gồm 4 phần tử x1,x2,x3,x4 và mảng y với 4 phần tử y1,y2,y3,y4. Nếu không có cách viết này, ta sẽ phải khai báo 2 mảng x,y và gán các phần tử vào một cách thủ công. Cách viết [a1,a2,...,an] được FP hiểu là một mảng mở ngoài cách hiểu thông thường là một tập hợp. 
2. Con trỏ 
Ai đã từng sử dụng ngôn ngữ C, nếu không cảm thấy khó khăn thì sẽ rất thích thú với sự uyển chuyển của con trỏ trong C: chẳng hạn có thể thực hiện các phép toán số học cộng, trừ với con trỏ, có thể dùng con trỏ như mảng. Con trỏ trong TP thì không được uyển chuyển như vậy. Trong TP ta chỉ có thể thực hiện một số phép toán như: gán trị cho con trỏ, so sánh 2 con trỏ hay truy cập vào phần tử mà con trỏ trỏ đến. 
FP đã cải tiến và con trỏ trong FP bây giờ uyển chuyển như trong C. Chúng ta có thể hiểu con trỏ trong FP như một biến nguyên 32 bit, giá trị của nó là một địa chỉ trong bộ nhớ. Do đó FP có một số phép toán đối với con trỏ như sau: 
Phép cộng : nếu p là một con trỏ kiểu X, i là một số nguyên, thì p+i cũng là một con trỏ kiểu X và p+i trỏ đến biến có địa chỉ cách biến p trỏ đến i phần tử kiểu X. Chẳng hạn nếu X là kiểu Int64, thì p+1 sẽ trỏ đến biến Int64 tiếp theo biến mà p trỏ đến. 
Phép trừ : nếu p, q là 2 con trỏ cùng kiểu thì p-q là độ lệch của chúng, có giá trị bằng số phần tử trong khoảng đó nhân với kích thước của kiểu mà chúng trỏ đến. 
Phép tham chiếu : nếu p là một con trỏ thì p^ là biến mà p trỏ đến. Ta cũng có p+i là con trỏ nên (p+i)^ cũng là biến mà p+i trỏ đến. Chú ý là cách viết này rất thường gặp trong C, nhưng trong TP thì hoàn toàn không có. FP còn cải tiến cho phép viết (p+i)^ dạng p, nghĩa là hoàn toàn giống C. Chú ý là p^ có thể viết là p[0]. 
Như vậy ta có thể coi một con trỏ như một mảng động. Ta có thể dùng các lệnh GetMem, FreeMem để cấp phát động bộ nhớ và dùng cú pháp của mảng để truy cập. 
Chương trình sau demo tính năng sử dụng con trỏ như mảng trong FP: 


```
uses crt,go32; 
var 
a : ^integer; 
n,i : integer; 
BEGIN 
n  := 100; 
GetMem(a,n); 
for i := 0 to n-1 do a := i; 
FreeMem(a);  
END. 
```

Với tính năng này, chúng ta có thể sử dụng các con trỏ như các mảng động (dynamic array), tức là khai báo mảng mà không cần xác định trước số phần tử, sau đó mảng được cấp phát động trong quá trình thực thi. 
Trong TP chúng ta dùng từ khoá absolute để xác định một vị trí cố định trong bộ nhớ. Khai báo đó gọi là địa chỉ tuyệt đối. Chẳng hạn để đo thời gian chạy của chương trình, người ta khai báo biến time kiểu longint tại địa chỉ 0:$46C, bởi vì vị trí đó là nơi máy tính lưu bộ đếm của đồng hồ. Tần số cập nhật của nó là 18.2 lần/giây. Một chương trình ví dụ của TP như sau: 


```
var 
time : LongInt  absolute 0:$46C; 
start : LongInt; 
i,n : LongInt; 
BEGIN 
start :=  time; 
for i := 1 to 10000000 do n := i; 
writeln('Runned in:  ',(time-start)/18.2:0:3); 
readln; 
END.
```

Trên máy tính của tôi, chương trình đơn giản này chạy mất gần 6 giây (5.814 giây). 
FP chạy trong môi trường 32 bit, ở chế độ bảo vệ. Trong chế độ bảo vệ, không có khái niệm địa chỉ tuyệt đối (để giải thích kĩ càng về điều này, chắc cần một bài báo rất dài). Để sử dụng khai báo absolute, ta phải khai báo sử dụng unit go32. Unit go32 cho phép thao tác vào vùng nhớ của DOS. Tất nhiên là chỉ có chương trình được dịch để chạy trên DOS 32 bit (target là DOS GO32v2) thì mới sử dụng được. 
Chương trình trên được sửa để chạy trên FP như sau: 


```
uses crt,go32; 
var 
time  : LongInt absolute 0:$46C; 
start : LongInt; 
i,n : LongInt; 
BEGIN  
start := time; 
for i := 1 to 10000000 do n := i; 
writeln('Runned in:  ',(time-start)/18.2:0:3); 
readln; 
END.
```

Thật kinh ngạc: trên máy tính của tôi chương trình đơn giản này chạy mất 0.000 giây!!! Tôi tăng hằng số 10000000 lên 10 lần (thêm một chữ số 0 vào) thì chương trình chạy trong 0.440 giây. Như vậy so với TP, FP nhanh hơn rất nhiều. Tôi chỉ có thể phỏng đoán là FP sinh ra chương trình mã 32 bit, lại được tối ưu hoá mã cho Pentium nên nhanh hơn TP vốn chỉ sinh mã 16 bit và hoàn toàn không có chức năng tối ưu mã. Đây có lẽ cũng là một lí do rất thuyết phục để thay thế TP bằng FP. 
3. Đồ hoạ trong FP 
TP phiên bản cuối cùng là 7.0 ra đời năm 1992. Vào hồi đó, phần cứng đồ hoạ còn khá yếu nên TP 7.0 chỉ hỗ trợ chế độ đồ hoạ cao nhất là 640x480x4 bit (16 màu). Bây giờ là năm 2004, đồ hoạ máy tính đã rất mạnh, nhưng nếu dùng TP thì ta cũng chỉ dùng được chế độ cao nhất đó thôi. Nếu có driver SVGA (SVGA256.BGI) thì ta có thể sử dụng được các chế độ 256 màu. Nhưng nếu dùng FP, chúng ta có thể có chế độ đồ hoạ cao hơn nhiều. Theo thử nghiệm thì tôi đã dùng được chế độ 800x600x16bit (64K màu) trong FP. Nghĩa là chẳng không thua kém nhiều các môi trường lập trình trên Windows. 
Unit Graph của FP tương thích hoàn toàn TP. Như vậy chúng ta vẫn có thể dùng các chương trình đồ hoạ được viết trên TP để dịch lại và chạy trong FP mà không cần sửa đổi gì. Hơn nữa, chúng ta có thể sử dụng những mode đồ hoạ với độ phân giải và số màu nhiều hơn với những thao tác vẽ đơn giản và quen thuộc của TP. 
Bảng sau là các driver và mode đồ hoạ mới trong FP. Chú ý là do FP là đa môi trường (multi platform), tức là sinh mã cho nhiều hệ điều hành và hệ máy khác nhau, do đó có thể những chế độ đồ hoạ không được một số hệ nào đó hỗ trợ. 
D1bit = 11; 
D2bit = 12; 
D4bit = 13; 
D6bit = 14; { 64 colors Half-brite mode - Amiga } 
D8bit = 15; 
D12bit = 16; { 4096 color modes HAM mode - Amiga } 
D15bit = 17; 
D16bit = 18; 
D24bit = 19; {chưa được hỗ trợ} 
D32bit = 20; {chưa được hỗ trợ} 
D64bit = 21; {chưa được hỗ trợ} 

detectMode = 30000; 
m320x200 = 30001; 
m320x256 = 30002; { amiga resolution (PAL) } 
m320x400 = 30003; { amiga/atari resolution } 
m512x384 = 30004; { mac resolution } 
m640x200 = 30005; { vga resolution } 
m640x256 = 30006; { amiga resolution (PAL) } 
m640x350 = 30007; { vga resolution } 
m640x400 = 30008; 
m640x480 = 30009; 
m800x600 = 30010; 
m832x624 = 30011; { mac resolution } 
m1024x768 = 30012; 
m1280x1024 = 30013; 
m1600x1200 = 30014; 
m2048x1536 = 30015; 
Còn sau đây là một chương trình demo về chế độ đồ hoạ cao của FP (800x600x16 bit). Nếu máy của bạn hỗ trợ chế độ này, bạn sẽ thấy một dải màu rất đẹp. 


```
uses graph; 
var 
gd, gm, i, error: integer;  
BEGIN 
gd := D16bit; 
gm := m800x600; 
initgraph(gd,gm,'');  
error := graphResult; 
if (error <> grOk) then begin  
writeln('800x600x16bit is not supported!'); 
halt(1) 
end; 
for i  := 1 to 600 do begin 
setColor(random(65536)); 
line(0,i,799,i); 
end;  
readln; 
closegraph; 
END.
```

4. Xử lí lỗi ngoại lệ 
Phần lớn các ngôn ngữ lập trình cao cấp đều có hỗ trợ xử lí lỗi ngoại lệ. Chẳng hạn Visual Basic có lệnh On Error, C++, Java có nhóm lệnh try catch, Delphi, FP có nhóm lệnh try... except. Nói một cách đơn giản là: trong khi phương pháp xử lí lỗi truyền thống né tránh lỗi (tức là kiểm tra trước để đảm bảo không có lỗi thì mới làm) thì phương pháp lập trình xử lí lỗi ngoại lệ là thực hiện bình thường và dự phòng trước tình huống nếu gặp lỗi. 
Chúng ta có thể xét một ví dụ là thực hiện một phép chia biến a cho biến b. Trong TP ta sẽ viết như sau: 


```
if b  <> 0 then c := a/b 
else write ('Error: b=0.');
```

Trong FP, sử dụng tính năng xử lí lỗi ngoại lệ, chúng ta có thể viết như sau: 


```
try 
c :=  a/b; 
except 
on EDivByZero do begin 
c := 0; 
write('Error.');  
end; 
end;
```

Các bạn có thể nói: như vậy rõ ràng phức tạp, rắc rối hơn. Tất nhiên, tôi đồng ý. Nhưng hãy nhìn vào ưu điểm của phương pháp xử lí lỗi ngoại lệ so với phương pháp truyền thống: - Phương pháp kiểm tra không phải lúc nào cũng lường trước được mọi tình huống và thường phải thực hiện những phép kiểm tra rất tốn thời gian. Phương pháp xử lỗi ngoại lệ không cần những kiểm tra như vậy nên đơn giản hơn. Hơn nữa có những tình huống phải thực hiện rồi mới biết là có lỗi (chẳng hạn đọc file, cộng 2 số..) - Phương pháp truyền thống xử lí lỗi một cách không thống nhất: ở mức nào của chương trình bạn cũng phải có những đoạn trình xử lí riêng và mỗi lỗi cũng phải có đoạn trình xử lí riêng. Với phương pháp xử lí lỗi ngoại lệ ta có thể chủ động đưa lỗi lên mức xử lí cao nhất, thậm chí tự gây ra lỗi để chuyển quyền điều khiển (bằng lệnh raise). 
- Phương pháp truyền thống sau khi gặp lỗi thường rất khó phục hồi lỗi. Phương pháp xử lí lỗi ngoại lệ có nhóm lệnh try finally sẽ đảm bảo dù gặp lỗi hay không những lệnh của khối finally đều được thực hiện. 
Phương pháp xử lí lỗi ngoại lệ là phương pháp rất hay nhưng không thể trình bày đơn giản trong một đoạn báo ngắn được. Các bạn nếu thấy thú vị thì có thể tự tìm hiểu trong các tài liệu của FP, Delphi hay Java. 
Như vậy qua một loạt bài báo, tôi đã trình bày về những điểm mạnh của FP so với TP, dưới góc độ một môi trường lập trình dành cho học sinh phổ thông hay sinh viên đại học làm quen lập trình hoặc rèn luyện chuẩn bị cho các kì thi HSG hay Olympic. Tất nhiên FP còn rất nhiều điểm mạnh khác như lập trình hướng đối tượng (OOP), đa nhiệm, đa luồng và đa nền (multi process - multi thread - multi platform), hỗ trợ trao tác CSDL và lập trình mạng nhưng đó là những lợi thế trong lập trình chuyên nghiệp, tạm thời chúng ta chưa xét đến. 
Chúng ta có thể tổng kết lại như sau những điểm mạnh của FP so với TP, những điểm đủ thuyết phục để dùng FP thay thế TP: 
- Không hạn chế bộ nhớ hay kích thước kiểu dữ liệu (mảng, xâu). 
- Hàm có nhiều cải tiến: trả lại kết quả kiểu cấu trúc, định nghĩa hàm trùng tên, định nghĩa phép toán, cho phép dùng tên hàm như biến, dùng biến result hoặc lệnh exit để trả lại kết quả. 
- Kiểu mảng mở và kiểu con trỏ uyển chuyển hơn, cho phép dùng con trỏ như mảng động. 
- Hỗ trợ các chế độ đồ hoạ cao cấp. 
- Cho phép lập trình xử lí lỗi ngoại lệ. 
- Tương thích hoàn toàn TP, cả về mã lệnh hay giao diện IDE. Sinh mã 32 bit tối ưu nên tốc độ nhanh hơn TP rất nhiều lần. Hơn nữa FP có thể sinh mã cho nhiều hệ máy và hệ điều hành khác nhau. 
Và một điều rất quan trọng là FP là phần mềm nguồn mở, hoàn toàn miễn phí. Khi luật bản quyền ở Việt Nam được thực thi nghiêm túc, Linux sẽ trở nên phổ biến và việc sử dụng các môi trường lập trình cao cấp như .NET, Java và HĐH Windows sẽ phải trả tiền bản quyền. Khi đó thành thạo FP sẽ là một lợi thế._

----------


## bomhao

Mình xin cung cấp hàm tính m mũ n mod k.


```
function Pow(n: longint): longint;
var
        tam: longint;
begin
        if n=0 then exit(1 mod k);
        tam:=Pow(n div 2);
        if odd(n) then exit(tam*tam*m mod k);
        Pow:=tam*tam mod k;
end;
```

----------


## giangnt

> Mình xin trình bày giải thuật đảo chuỗi thành chuỗi ngược lại mà không sử dụng đến for, while hay repeat...until... :
> 
> 
> ```
> procedure xuly(i:integer);
> var s1:string;
> begin
>   if i<1 then write(s)
>      else
> ...


Thanks bạn nhé, mình đang cần mấy bài này !

----------


## tandatcit

*Tiết kiệm một chút [IMG]data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAA  l21bKAAAAA1BMVEXh5PJm+yKVAAAAAXRSTlMAQObYZgAAAApJR  EFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=[/IMG]*




> Thuật toán 8 quân hậu và mã đi tuần chắc hẳn khá quen thuộc với các bạn, nhưng nhiều bạn vẫn chưa có một chương trình tham khảo, với kinh nghiệm của mình, mình xin đưa ra giải thuật cho hai bài toán đó:
> *8 quân hậu:*
> *
> 
> 
> ```
> [/U]
> ```
> 
> ...


*

Bạn nên tiết kiệm bộ nhớ bằng cách dùng kiểu Boolean cho các mảng a,b,c ,các mảng này thực chất chỉ để đánh dấu đã đặt quân hậu ở vị trí i,j hay chưa.*

----------


## ViệtNet

> Kỹ thuật tính hạng tử thứ n trong dãy fibonacy: 1 1 2 3 5 8 13 21 34 55 ....
> 
> 
> ```
> uses crt;
> var n,i:byte;a,b:extend;
> begin
>         clrscr;
>         write('Nhap n ');
> ...


Sao bài của bạn, máy mình cứ bào lỗi chỗ a,b:extend; nhỉ ?
Bạn xem lại giúp mình với !

----------


## khuvucmuabannhadat

> Sao bài của bạn, máy mình cứ bào lỗi chỗ a,b:extend; nhỉ ?
> Bạn xem lại giúp mình với !


 Bạn phải chỉnh lại mới dùng extend được thì phải [IMG]data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAA  l21bKAAAAA1BMVEXh5PJm+yKVAAAAAXRSTlMAQObYZgAAAApJR  EFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=[/IMG]
Hồi trước mình viết trong turbo. Bạn sửa lại kiểu int64 trong FP nhá!

----------

