CHƯƠNG
6
LẬP TRÌNH TRONG MAPLE
6.1. Cơ sở dữ liệu của Maple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
158
6.2. Các câu lệnh có cấu trúc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
158
6.3. Toán tử break và next trong while - for . . . . . . . . . . . . . . . . . .
164
6.4. Tạo lập thủ tục hàm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
165
6.5. Tạo lập thủ tục đơn giản trong Maple . . . . . . . . . . . . . . . . . . .
167
6.6. Biến cục bộ, hàm return và hàm error . . . . . . . . . . . . . . . . . . .
168
6.7. Những tốn tử tính tốn trong Maple . . . . . . . . . . . . . . . . . . . .
175
6.8. trace và printlevel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
177
6.9. Xem mã thư viện nguồn của Maple . . . . . . . . . . . . . . . . . . . . .
181
6.10. Chuyển mã Maple ra C, fortran, latex . . . . . . . . . . . . . . . . . .
182
6.11. Một số thuật toán cổ điển. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
186
6.12. Phương pháp Newton trong giải tích số . . . . . . . . . . . . . . . .
192
6.13. Bài tập luyện tập . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
196
6.14. Bài tập tự giải . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
198
Cho đến chương này chúng ta tiếp xúc với Maple qua từng câu
lệnh, đó là các lệnh qua các biểu thức hoặc là lệnh gán dữ liệu. Chúng
ta có thể tập hợp tất cả thao tác lại và soạn thảo thành một tệp chứa
các lệnh ta cần để Maple thực hiện liên tục khi Maple đọc tệp này.
Như vậy ta đã tạo ra tập nguồn của chương trình với ý đồ xây dựng
của ta, lúc cần ta lại cho Maple thực hiện lại. Việc lập trình kiểu đơn
giản như vậy ai cũng làm được. Để có thể làm tốt hơn việc sắp xếp
các lệnh, chương này chúng ta thiết lập một số qui tắc xắp xếp các
lệnh đơn giản và hiệu quả nhất. Ngày nay nếu Pascal, C là các ngôn
ngữ chủ yếu để học cách lập trình thì Maple gắn bó với giới chun
mơn, ở đó họ tìm thấy đáp án cho các u cầu của mình, đó là tính
hiệu quả , đơn giản và cô đọng khi viết, nhưng cũng cho phép truy
nhập được vào nguồn tư liệu và chương trình hệ thống.
158
Chương 6. Lập trình trong Maple
6.1. Cơ sở dữ liệu của Maple
Nhắc lại những dữ liệu mà Maple có ngay từ chương đầu tiên đó
là: các loại số, số nguyên, số chấm động, các ký tự, mảng, bảng, ...;
các loại biến, hằng và hàm; các phép toán thực hiện trên dữ liệu. Khi
viết chương trình ta coi các dữ liệu trên là có sẵn, khơng khai báo
hoặc định nghĩa lại mà cứ dùng tự nhiên như khi ta thực hiện tương
tác với Maple.
Vẫn chỉ bằng các lệnh gán hoặc các lệnh theo hàm thì việc tổ
chức và điều khiển rất khó, thậm chí khơng thể sắp xếp theo ý đồ
thực hiện công việc của ta được. Maple cung cấp một số câu lệnh cấu
trúc điều khiển thực hiện của Maple cho hiệu quả, từ dưới đây chúng
ta nghiên cứu các lệnh cấu trúc này.
6.2. Các câu lệnh có cấu trúc
6.2.1. Câu lệnh điều kiện if - then - else - fi
Mẫu 1: Lựa chọn câu lệnh
if<điều kiện> then <Dãy lệnh 1> else <Dãy lệnh 2> fi;
Maple xác định giá trị biểu thức <điều kiện>:
1. Nếu giá trị của <điều kiện> bằng true Maple thực hiện:
<Dãy câu lệnh 1>
Sau đó kết thúc câu lệnh điều kiện.
2. Nếu giá trị của <điều kiện> bằng false Maple thực hiện:
<Dãy câu lệnh 2>
Sau đó kết thúc câu lệnh điều kiện.
Ví dụ 6.1. Sử dụng câu lệnh if mẫu 1
A. Gán giá trị cho hai biến a và b. Sử dụng câu lệnh if mẫu 1 để xác
định số lớn nhất trong hai số a và b.
>a:=12: b:=37:
>if a>= b then
print(‘số lớn nhất có giá trị:‘, a);
else
print(‘số lớn nhất có giá trị:‘, b);
fi;
số lớn nhất có giá trị:37
6.2. Các câu lệnh có cấu trúc
159
B. Hàm isprime(n) cho giá trị true nếu n là số nguyên tố và false
cho những số còn lại.
>if isprime(b) then
print(‘b là số nguyên tố‘);
else
print(‘b không là số nguyên tố‘);
fi;
b là số nguyên tố
Mẫu 2: Một trong nhiều lựa chọn
if <điều kiện(1)> then <Dãy câu lệnh 1>
elif <điều kiện (2)> then <Dãy câu lệnh 2>
...
elif <điều kiện (n)> then <Dãy câu lệnh n>
else
<Dãy câu lệnh mặc định>
fi;
Maple xác định giá trị của <điều kiện (i)>(i = 1..n) theo thứ
tự lần lượt.
1. Nếu giá trị của <điều kiện (i)> bằng true, Maple thực hiện:
<Dãy câu lệnh i>
Sau đó kết thúc câu lệnh điều kiện.
2. Nếu giá trị của <điều kiện (i)> đều là false, Maple thực hiện:
<Dãy câu lệnh mặc định>. Sau đó kết thúc câu lệnh điều kiện.
Ví dụ 6.2. Sử dụng câu lệnh if mẫu 2
Gán giá trị cho hai biến c và d. Sử dụng câu lệnh if mẫu 2 để xác
định số lớn nhất trong hai số c và d.
>c:=2: d:=137:
if c>d then print(’ c lớn hơn d’);
elif c = d then print(’c bằng d’);
else print(’ c nhỏ hơn d’);
fi;
c nhỏ hơn d
160
Chương 6. Lập trình trong Maple
Ví dụ 6.3. Hãy viết một thủ tục đưa ra giá trị của hàm
−2 x ≤ −3,
x2 x ≤ 2,
f (x) =
2
x < 4,
1
còn lại
và tính f (5).
f:=proc (x)
if x <= -3 then -2
elif x <= 2 then x^2
elif x < 4 then 2
else 1
fi; end:
>f(5);
1
6.2.2. Câu lệnh lặp While
Dùng để xây dựng chu trình có số lần lặp khơng xác định.
while <điều kiện lặp>do
<Dãy dòng lệnh>;
od;
Bước 1: Xác định giá trị của biểu thức <điều kiện lặp>.
Bước 2: -Nếu <điều kiện lặp> có giá trị là false, Maple ra khỏi chu
trình.
-Nếu <điều kiện lặp> có giá trị là true, Maple sẽ thực hiện
dịng lệnh>, sau đó trở lại Bước 1.
Nhận xét:
1.Thơng thường <Dãy dòng lệnh> được tạo thành từ một khối các
câu lệnh của Maple. Nói cách khác, thân chu trình thường là một
khối lệnh.
2.Thân chu trình trong câu lệnh while có thể được thực hiện một
lần hoặc nhiều lần và cũng có thể khơng được thực hiện lần nào nếu
ngay từ đầu biểu thức <điều kiện lặp> có giá trị false.
Ví dụ 6.4. Sử dụng while để tính ước chung lớn nhất. irem(a, b) là
hàm cho số dư trong phép chia a cho b. Đoạn chương trình tính ước
chung lớn nhất của hai số a và b như sau:
6.2. Các câu lệnh có cấu trúc
161
>irem(5, 3);
2
>a:=35: b:=15:
>while b <> 0 do
d:=irem(a, b);
a:=b; b:=d;
od:
>lprint(’Ước chung lớn nhất là: ’, a);
Ước chung lớn nhất là: 5
6.2.3. Câu lệnh lặp for
Mẫu 1: Dùng xây dựng chu trình lặp có số lần lặp xác định
for i from <giá trị đầu> by <thay đổi i> to <giá trị cuối>
do
<Dãy dòng lệnh>
od;
Bước 1: Biến điều khiển i nhận giá trị của biểu thức <Giá trị đầu>.
Bước 2: Maple kiểm tra điều kiện:
+ i <= <giá trị cuối> trường hợp <thay đổi i> >= 0
+ i >= <giá trị cuối> trường hợp <thay đổi i> <= 0
Bước 3: - Nếu điều kiện trên là sai thì Maple ra khỏi chu trình
- Nếu điều kiện trên là đúng thì:
a. Thực hiện <Dãy dịng lệnh>.
b. Biến điều kiện i nhận giá trị mới: i:=i + <thay đổi i>
c. Trở lại bước 2.
Chú ý: Chúng ta có thể lựa chọn việc khai báo from <giá trị đầu>
hoặc
by <thay đổi i>.
Nếu
from <giá trị đầu>
hoặc
by <thay đổi i> không được khai báo thì Maple tự động thiết
lập giá trị mặc định cho <thay đổi i>=1 và <giá trị đầu>=1.
Ví dụ 6.5. Sử dụng for và if mẫu 2 để khởi tạo một ma trận.
A. Khai báo một ma trận. Thiết lập giá trị cho các phần tử của A.
>A:=array(1..4, 1..4):
162
Chương 6. Lập trình trong Maple
>for i to 4 do
for j to 4 do
if i >j then A[i, j]:=j
elif i < j then A[i, j]:=i
else A[i, j]:=1
fi
od;
od:
>print (A);
1 1 1 1
1 1 2 2
1 2 1 3
1 2 3 1
n
B. Sử dụng for tính đồng thời các tổng ∑ ji với i = 2, 4, 6. .
j =1
>for i from 2 by 2 to 6 do
lprint(Tổng, j^i, j = 1..n là:);
print(expand(sum(j^i,j = 1..n)));
od;
Tổng j^2 j=1..n là: 1/3n3 + 1/2n2 + 1/6n
Tổng j^4 j = 1..n là: 1/5n5 + 1/2n4 + 1/3n3 − 1/30n
Tổng j^6 j = 1..n là: 1/42n − 1/6n3 + 1/2n5 + 1/2n6 + 1/7n7
Ví dụ 6.6. Viết thủ tục tính tổng 12 − 32 + 52 − 72 + · · · − 992 + 1012 .
So sánh thủ tục với hàm có sẵn trong Maple:
UMMM:=proc ()
local total, k;
total:=0;
for k by 2 to 101 do
total:=total+(-1)^((1/2)*k-1/2)*k^2
od end:
>SUMMM();
5201
>sum((-1)^(k+1)*(2*k-1)^2, k = 1 .. 51);
5201
Trong mẫu 2 của câu lệnh for có sử dụng hai hàm rất quan trọng:
>op(i, <biểu thức toán học>);
6.2. Các câu lệnh có cấu trúc
163
Lệnh op cho thành phần thứ i giữa các thành phần của biểu thức.
Ví dụ: op(1,2*x*y); kết quả là 2. Còn op(2,2*x*y); kết quả là x.
>nops(<biểu thức toán học>);
Lệnh nops cho số lượng các thành phần giữa các tốn tử trong
<biểu thức tốn học>.
Ví dụ: >nops(2*x*y); là 3, >nops([3,4]); là 2.
Mẫu 2 của for: Sử dụng giá trị của biến đếm vào biểu thức
for i to nops(<biểu thức>) do câu lệnh (i,<biểu thức>) od;
Bước 1: Biến điều khiển i nhận giá trị đầu trong <biểu thức> qua
hàm nops().
Bước 2: Maple thực hiện câu lệnh có (i, <biểu thức>)
Bước 3: Kiểm tra điều kiện biến i:
a. Nếu i là phần tử cuối cùng của <biểu thức> thì Maple ra khỏi chu
trình.
b. Nếu điều kiện trên sai, i nhận giá trị mới, là giá trị tiếp theo trong
<biểu thức>.
c. Trở lại bước 2.
Ví dụ 6.7. Sử dụng for mẫu 2.
Ví dụ này sử dụng câu lệnh for dạng 2 để tính tổng bình phương các
số chẵn trong danh sách aList.
>aList:=[1,2,3,4,5];
>s:=0;
>for i to nops (aList) do
if irem(op(i, aList), 2) = 0 then
s:=s + op(i, aList)^2
fi
od:
>s;
20
Ví dụ 6.8. Viết thủ tục MEMBER( D, w) tìm từ w có đúng
nằm trong từ điển D hay là không? Giả sử có từ điển
L := [ abacus, number, algorithm], kiểm tra từ algorithm và ossi f rage
có nằm trong từ điển này không?.
164
Chương 6. Lập trình trong Maple
>MEMBER:=proc (D, w)
local k;
for k to nops(D) do
if w = D[k] then return(true)
fi; od; false
end:
>L:=[abacus, number, algorithm];
L := [ abacus, number, algorithm]
>MEMBER(L, algorithm); MEMBER(L, ossifrage);
true
f alse
Mẫu 3 của for: Lặp lại lệnh theo giá trị của một biến trong
biểu thức
>for x in <biểu thức> do <Câu lệnh dùng x> od;
Lặp lại theo dãy giá trị x lấy trong <biểu thức>.
Ví dụ 6.9. Sử dụng for mẫu 3.
Ví dụ này sử dụng câu lệnh for mẫu 3 để tính tổng bình phương các
số lẻ trong danh sách aList.
>aList:=[1,2,3,4,5]: s:=0:
>for n in aList do
if irem(n, 2) = 1 then
s:=s + n^2
fi
od:
>s;
35
6.3. Toán tử break và next trong while - for
Trong chu trình lặp của câu lệnh while - for:
- Khi gặp toán tử điều kiển break, ngay lập tức chu trình lặp kết
thúc.
- Khi gặp tốn tử điều kiển next, ngay lập tức chuyển sang bước lặp
tiếp theo.
Ví dụ 6.10. Sử dụng tốn tử điều khiển break.
A. Tìm ba số nguyên tố đầu tiên trong tập hợp các số nguyên dương.
6.4. Tạo lập thủ tục hàm
165
>i:=2: j:=0:
>while i>0 do
if isprime(i) then
print(i, ‘là số nguyên tố‘);
j:=j + 1;
if j =3 then break fi;
fi;
i:=i + 1;
od:
2 là số nguyên tố
3 là số nguyên tố
5 là số nguyên tố
B. Ví dụ dưới đây sử dụng for dạng 2 và toán tử điều khiển next để
tính tổng bình phương các số chẵn khơng chia hết cho 3 trong danh
sách sList.
>aList:=[1,2,3,4,5,6,7,8,9];
>s:=0;
>for i to nops (aList) do
if ((irem(op(i, aList), 2) = 0) and
(irem(op(i, aList), 2) <>0)) then
s:=s + op(i, aList)^2
else next fi; od:
>s;
84
6.4. Tạo lập thủ tục hàm
Mẫu 1: Dùng -> định nghĩa hàm
<Tên hàm>:=(<Biến 1>, <Biến 2>,...)-> <Biểu thức có biến>;
<Tên hàm> là tên hàm được dùng sau này và khơng có ký tự trắng.
<Biến 1>,... là các đối số của hàm sau này đưa vào.
Ví dụ 6.11. Tạo hàm bằng ký hiệu ->.
>f:=(x,y)->simplify(x^2+y^2):
>f(sin(x),cos(x));
1
Ví dụ 6.12. Viết một hàm Maple đầu vào là một danh sách các số
và đầu ra là giá trị trung bình của các số trong danh sách. Cho
L = [−1, 2, 3, 3, 4].
166
Chương 6. Lập trình trong Maple
>AVERAGE:=X ->(sum(X[i], i = 1 .. nops(X)))/nops(X):
>AVERAGE([-1, 2, 3, 3, 4]);
11
5
Ví dụ 6.13. Độ cao của một đa thức là giá trị lớn nhất của giá trị
tuyệt đối các hệ số của đa thức p( x ). Hãy viết một hàm số đưa ra độ
cao của đa thức được đưa vào.
>HEIGHT:=(p, x)->max(map(abs, {coeffs(p, x)})):
>f:=11*x^3+3*x^2-8;
>HEIGHT(f, x);
11
Mẫu 2: Dùng ký hiệu < | >
<Tên hàm>:=<Biểu thức | Biến 1, Biến 2, ...>;
<Tên hàm> vẫn như trên và các biến là đối số của hàm theo Biểu
thức.
Ví dụ 6.14. Ví dụ định nghĩa hàm kiểu mới. Hàm tính tổng hai số.
>f:=<x+y| x,y>:
>f(-x,x);
0
Mẫu 3: Sử dụng hàm unapply
<Tên hàm>:=unapply(<Biểu thức>, <Biến 1>, <Biến 2>,...);
Ý nghĩa ký hiệu trong mẫu trên như các mẫu trước. Mẫu 3 là viết
tắt của mẫu 1.
Ví dụ 6.15. Dùng hàm số định nghĩa.
>f:=unapply(diff(z(x)^2-2x,x));
∂
f := z → 2z( x )( z( x )) − 2
∂x
>f(sin);
2 sin( x ) cos( x ) − 2
Trong Maple còn một lệnh để định nghĩa hàm từng khúc.
>f:=x->piecewise(<ĐK 1>,<giá trị 1>,<ĐK 2>,<giá trị 2>,...);
<ĐK i> là biểu thức đúng để có <giá trị i>, i = 1, 2, 3, ....
6.5. Tạo lập thủ tục đơn giản trong Maple
167
Ví dụ 6.16. Hàm từng khúc.
>f:=x->piecewise(x <= -3,
>f(x);
−2
x2
2
1
-2, x <= 2, x^2, x < 4, 2, 1):
x ≤ −3,
x ≤ 2,
x < 4,
còn lại
6.5. Tạo lập thủ tục đơn giản trong Maple
<Tên thủ tục>:=proc(<Dãy thơng số>)
<Biểu thức có chứa dãy thơng số>;
end;
Sau khi định nghĩa thủ tục chúng ta có thể gọi như các hàm của
Maple: <Tên thủ tục>(biểu thức 1, biểu thức 2).
Ví dụ 6.17. Định nghĩa một thủ tục tính tổng của hai số bất kỳ.
A. Định nghĩa hàm f với hai tham số x, y.
>f:=proc(x, y)
x + y;
end;
B. Sử dụng hàm f để tính tổng của hai số x = 27 và y = 46.
>f(27, 46);
73
C. Kết quả của hàm với x = w và y = z + 1.
>f(w, z+1);
w+z+1
D. Xây dựng thủ tục tìm số lớn nhất trong ba số bất kỳ.
>max3:=proc(a, b, c)
print(So lon nhat trong ba so, a, b, c);
if a < b then
if b < c then c else b fi
elif a < c then c else a
fi; end;
168
Chương 6. Lập trình trong Maple
E. Sử dụng thủ tục max3.
>max3(3, 2, 1);
3
>max3(13, 24, 18);
24
6.6. Biến cục bộ, hàm return và hàm error
Để định nghĩa một thủ tục trong Maple bao gồm việc khai báo
các biến cục bộ, cùng với việc thiết lập một số tùy chọn khác cùng các
câu lệnh.
<Tên thủ tục>:=proc (<Dãy thông số>)
local <Dãy biến cục bộ>;
options <Dãy tùy chọn>;
<Câu lệnh 1>;
. . .
<Câu lệnh n>;
end;
6.6.1. Sử dụng biến cục bộ
Biến cục bộ khai báo trong thủ tục sẽ được sử dụng chỉ khi thi
hành thủ tục và bị loại bỏ khi thủ tục kết thúc.
Ví dụ 6.18. Xây dựng thủ tục tính tổng lập phương của n số tự
nhiên liên tiếp.
>sum1:=proc(n)
local result, i;
result:=0;
for i to n do
result:=result + i^3;
od;
result;
end;
>sum1(3);
36
Với mỗi thủ tục, ta có thể kiểm tra để biết được số các đối số
truyền cho nó bằng việc sử dụng từ khóa nargs. Từ khóa args chứa
6.6. Biến cục bộ, hàm return và hàm error
169
đựng giá trị các đối số khi ta truyền vào cho hàm số. Sử dụng biến
chỉ số i theo mẫu args[i ], để truy nhập và xác định giá trị từng đối số
của thủ tục.
Ví dụ 6.19. Thủ tục dưới đây đưa ra số lớn nhất trong dãy số đưa
vào như đối số của hàm MaxN bằng cách dùng biến cục bộ và từ
khóa args cho phép ta đưa vào số lượng đối số bất kỳ.
A. Định nghĩa thủ tục: Khai báo biến cục bộ, kiểm tra các thông số
truyền cho thủ tục có kiểu số hay khơng (ngun, thực, phân số) ?
Sau đó so sánh các giá trị của đối số, số nào lớn hơn được được ghi
vào result. Khi duyệt hết số lượng đối số nargs thì result là giá trị lớn
nhất.
>MaxN:=proc()
local result, i;
if not(type([args],list(numeric))) then
print(’Error’);
elif nargs >0 then
result:=args[1];
for i from 2 to nargs do
if args[i] >result then
result:=args[i];
fi; od;
result;
fi;
end:
B. Sử dụng thủ tục MaxN.
>MaxN(3, 8, 36, 13, 42, 9);
42
>MaxN(22/13, 6, 118/3);
118/3
>MaxN( a, 27, t);
Error
C. MaxN sẽ không cho giá trị gì nếu khơng truyền thơng số cho nó.
>MaxN();
D. Biến cục bộ result khơng có giá trị ở ngồi thủ tục.
>result;
170
Chương 6. Lập trình trong Maple
6.6.2. Sử dụng hàm return
return được sử dụng khi muốn nhận lại một giá trị được tạo ra
trong thủ tục sau khi thủ tục đó kết thúc. Mẫu khai báo có dạng:
> return(<Một giá trị>);
Cho ra một giá trị trong thủ tục.
Ví dụ 6.20. Sử dụng hàm return tính đa thức Chebyshev bậc n:
pn ( x ). Đa thức Chebyshev được xác định bằng công thức truy hồi
p0 ( x ) = 1, p1 ( x ) = x, pn+1 ( x ) = 2xpn ( x ) − pn−1 ( x ) với mọi n ≥ 1.
A. Định nghĩa thủ tục tính đa thức Chebyshev từ bậc 0 đến n, cho
ra dạng bảng. Khởi động kết quả bằng hai đa thức Chebyshev đầu.
Những đa thức tiếp sau tính theo cơng thức truy hồi.
>Chebyshev:=proc (n)
local p, k;
p[0]:=1; p[1]:=x;
if n <= 1 then return(eval(p)) fi;
for k from 2 to n do
p[k]:=expand(2*x*p[k-1]-p[k-2])
od;
return(eval(p));
end:
B. Sử dụng thủ tục Chebyshev.
>a:=Chebyshev(5):
>seq(a[i], i = 0 .. 5);
1, x, 2x2 − 1, 4x3 − 3x, 8x4 − 8x2 + 1, 16x5 − 20x3 + 5x
C. Đưa ra giá trị của 6 đa thức đầu Chebyshev với x = 1/4.
>x:=1/4:
>seq(a[i], i = 0 .. 5);
1, 1/4, −7/8, −11/16, 17/32, 61/64
Ví dụ 6.21. Viết một thủ tục khi đưa hai số vào theo thứ tự, đầu ra
là thứ tự đảo ngược: Với lệnh SWAP(x,y); cho kết quả y, x.
6.6. Biến cục bộ, hàm return và hàm error
171
>SWAP:=proc (x, y)
local x1, y1, temp;
x1:=x; y1:=y; temp:=x1;
x1:=y1; y1:=temp;
return(x1, y1)
end:
>SWAP(5, 9);
9,5
>SWAP(x, y);
y, x
6.6.3. Sử dụng hàm error
Để đưa ra thông báo lỗi trong thủ tục, ta sử dụng hàm error:
error(<Câu thông báo lỗi>);
error đưa ra thơng báo có dạng:
Error, (in <tên hàm>) <Câu thông báo lỗi>
Kết hợp hàm error cùng với câu lệnh điều kiện if - then - else fi và một vài từ khóa của Maple có được một mẫu chuẩn để kiểm tra
kiểu của những thông số truyền cho thủ tục. Mẫu đó được viết theo
mẫu sau:
>if not type (argument, typename) then
error(wrong type argument to, procname,:, argument,
should be of type, typename);
fi;
Ví dụ 6.22. Thủ tục tính dãy Fibonacci.Trong thủ tục này kiểm tra
tham số n.
>F:=proc (n)
option remember;
if nargs <>1 or not type(n, integer) or n < 0 then
error(wrong number or ‘in‘(type of parameters, F))
else if n < 2 then n
else F(n-1)+F(n-2) fi
fi end:
>F(10);
55
Ví dụ 6.23. A. Sử dụng error để đưa ra thơng báo trong q trình
kiểm tra kiểu đối số truyền vào cho thủ tục tìm số lớn nhất của một
172
Chương 6. Lập trình trong Maple
dãy (sửa đổi Ví dụ 6.19).
>maxN:=proc ()
local result, i;
if nargs = 0 then error(‘Khong co doi so‘) fi;
if not (type([args], list(numeric))) then
return(procname(args)) fi;
result:=args[1];
for i from 2 to nargs do
if result < args[i] then result:=args[i] fi
od;
result;
end:
B. Sử dụng thủ tục maxN
>maxN(25/7, 34/16, 9/2);
9/2
>maxN();
Error, (in maxN) Khong co doi so
Hàm traperror cho phép ta tránh việc ngắt hoặc dừng máy khi
chương trình mắc phải lỗi. Hàm traperror được sử dụng để bắt lỗi
trong khi chạy chương trình. Kiểu bắt lỗi này được sử dụng khi ta
cho rằng việc tính tốn có thể mắc phải lỗi và ta muốn chương trình
thơng báo lỗi này nếu có. Biến lasterror là ghi nhận lỗi phát sinh
cuối cùng của error từ đó ta có thể dùng điều khiển chương trình.
Ví dụ 6.24. Hàm traperror và lasterror.
A. Hiển thị lỗi thực tế mắc phải
>(1/3) mod 12;
Error, the modular inverse does not exist
>lasterror;
the modular inverse does not exist
B. Chúng ta có thể bao phủ q trình tính tốn cùng với hàm traperror. Trong trường hợp này nhiều khi đưa ra thông báo lỗi, hoặc
tính một modul ngược.
>u:=traperror((1/4) mod 12);
u:=the modular inverse does not exist
>v: = traperror((1/5) mod 12);
v := 5
6.6. Biến cục bộ, hàm return và hàm error
173
Ví dụ 6.25. Đặt bẫy lỗi.
Thủ tục dưới đây minh hoạ cách đặt bẫy lỗi cho hàm normal.Vì hàm
normal khơng thực hiện với số dấu chấm động, nên ta tạo ra hàm
mới mynormal tốt hơn dùng với cả phân thức.
>mynormal:=proc (a)
local result;
result:=traperror(normal(a));
if result = lasterror then
if not result = floats then
result:=normal(convert(a, rational, exact)) else
error(result)
fi else
result;
fi end:
> mynormal((2.1+5.6*x)/(2.8+8.4*x));
0.7142857143
Hàm error và traperror có thể được sử dụng đồng thời để thốt
khỏi việc đang tính tốn. Ví dụ như, trong khi tính tốn giai thừa
số nguyên, khi các bước hồi qui quá sâu, tới một bước nào đấy muốn
thốt ra ngay ngồi. Bằng việc đặt bẫy lỗi lựa chọn, ta có thể thốt
khỏi q trình đang tính tốn bằng thơng báo lỗi hợp pháp.
Ví dụ 6.26. Sử dụng error / traperror
A. Chúng ta định nghĩa một thủ tục có đưa ra thơng báo lỗi nếu giá
trị nhập vào là âm. Giá trị lỗi là âm.
>List:=[7, 13, -8, 2, -4, -9, 5];
List := [7, 13, −8, 2, −4, −9, 5]
>err_neg:=proc(x);
if x<0 then error(x) else x fi
end:
B. Thủ tục này cho số âm đầu tiên trong danh sách hoặc ngược lại
bằng không.
>first_neg:=proc(a)
local r;
r:=traperror(map(err_neg, a));
if r = lasterror then r else 0 fi
end:
174
Chương 6. Lập trình trong Maple
>first_neg(List);
−8
6.6.4. Thủ tục lồng nhau
Có thể định nghĩa các thủ tục bên trong một thủ tục khác. Ví dụ
sau minh họa việc định nghĩa một thủ tục g() bên trong thủ tục f().
Như vậy các câu lệnh trong f() không thể tham chiếu tới bất kỳ một
biến cục bộ nào của g().
Ví dụ 6.27. Hai thủ tục f() và g() đều có một biến cục bộ cùng mang
tên y nhưng giá trị của chúngkhác nhau.
>f:=proc(x)
local g, y;
y:=x + 1;
g:=proc(a); a - y end;
g(x);
end:
>f(5);
5−y
Ví dụ 6.28. Thủ tục sau gọi thủ tục trước như một phần của chương
trình. Các số
1, 2, 3, ..., 2003
được viết lên bảng với thứ tự tăng dần. Bắt đầu ta xóa số thứ nhất,
số thứ ba, số thứ bảy,...còn lại số
2, 3, 5, 6, 8, 9, 11, 12, 14, ...
Quá trình này lặp lại lần nữa và số còn lại
3, 5, 8, 9, ....
Tiếp tục quá trình này cho đến chỉ cịn một số trên bảng trước khi
xóa hết. Vậy số cuối cùng để xóa là số mấy?
A. Trước tiên ta viết thủ tục SHRI NKLIST ( L) thể hiện sau một
bước lặp. Ví dụ lần đầu tiên có L := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], khi đó
SHRI NKLIST ( L) đưa ra [2, 3, 5, 6, 8, 9], nghĩa là xóa đi các phần tử
của L có vị trí chia hết cho 3. Sau đây là một phương án tạo ra thủ
6.7. Những tốn tử tính tốn trong Maple
175
tục SHRI NKLIST ( L), trong thủ tục có dùng hàm nops( A) là số phần
tử trong danh sách A, op( L) dãy phần tử trong danh sách L.
>SHRINKLIST:=proc (A)
local L, newindex, k, C;
L:=[]; newindex:=1;
for k to nops(A) do if mod(k, 3) <>1 then
C[newindex]:=A[k]; L:=[op(L), C[newindex]];
newindex:=newindex+1
fi; od;
eval(L)
end:
>L:=[1,2,3,4,5,6,7,8,9,10];
L := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>L:=SHRINKLIST(L);
L := [2, 3, 5, 6, 8, 9]
B. Chương trình chính: Dùng SHRI NKLIST () lặp lại trên mảng L
cho đến khi L chỉ còn 1 phần tử. SHRI NKLIST () tạo ra danh sách
mới L khơng cịn phần tử để xóa. Phần tử cuối cùng là số ta cần tìm.
>COMPUTELAST:=proc (X)
local Y; Y:=X;
while 1 < nops(Y) do Y:=SHRINKLIST(Y) od;
return(Y[1])
end:
>COMPUTELAST([seq(k, k = 1 .. 2003)]);
1598
6.7. Những tốn tử tính tốn trong Maple
Trong Maple những hàm có thể được coi như những biến, chúng
ta có thể thực hiện các phép toán trên chúng và tạo ra các biểu thức
hoặc tạo ra lớp hàm mới từ các hàm đã có. Maple có phép tốn trên
hàm để xây dựng các hàm mới từ những hàm đã có, ta gọi phép toán
như vậy là toán tử.
Bảng 6.1. Một số toán tử trong Maple.
Toán tử
Chức năng
@ Toán tử hợp
f@g tạo ra một hàm bằng cách hợp f và g. Như
(f@g)(x) là f ( g( x ))
176
Chương 6. Lập trình trong Maple
D (Tốn tử lấy
đạo hàm riêng)
Với hàm f thì biến D ( f ) lấy đạo hàm của hàm
số f . Với hàm có một vài biến g, D [1]( g) là
hàm số, do kết quả lấy đạo hàm của g ứng với
đối số thứ nhất, D [2]( g) là hàm số, do lấy đạo
hàm của g với đối số thứ hai, ...
@@ (Toán tử kết
hợp nhiều lần)
f@@n (với n là số nguyên không âm) là
hàm hợp n lần hàm f . Như (f@@3)(x) là
f ( f ( f ( x ))).
Ví dụ 6.29. Các toán tử.
A. Sử dụng toán tử D.
>f:=x ->x^2:
>df:=D(f);
d f := x − > 2x
>df(3);
6
B. Nếu đạo hàm của một hàm số khơng tường minh thì kết quả
đưa ra là cơng thức đó. Ví dụ dưới đây tính đạo hàm của hàm hợp
g( g( x )).
>D(g@@2);
D ( g)@gD ( g)
C. Lấy giá trị của hàm vừa tính tại x = 1.
>h:=%(1);
h := D ( g)( g(1))(1) D ( g)(1)(1)
>g:=log@tan + x^2;
g := log@tan + x2
>h;
4 ln(tan(1)) + 4
Ta có thể định nghĩa toán tử mới bằng việc thiết lập định nghĩa
thủ tục với tuỳ chọn option operator. Ví dụ sau minh hoạ cách định
nghĩa một toán tử hàm.
6.8. trace và printlevel
177
Ví dụ 6.30. Định nghĩa một tốn tử mới.
A. Định nghĩa một toán tử sai phân.
>Shift1:=proc(f)
local x;
option operator;
unapply(simplify(f(x+1) - f(x)), x);
end:
B. Áp dụng Shift cho sin chúng ta được một hàm.
>SSin:=Shift1(sin);
SSin := x − > sin( x + 1) − sin( x )
C. Tính giá trị hàm này tại các điểm cụ thể.
>SSin(Pi);
− sin(1)
>SSin(1.0);
.0678264420
D. Tính giá trị hàm này với đối số là ký hiệu.
>SSin(x^2);
sin( x2 + 1) − sin( x2 )
E. Áp dụng Shift hai lần hàm toán tử
>Shift1((x) -> x^2);
x − > 2x + 1
>Shift(%);
x− > 2
6.8. trace và printlevel
trace và printlevel đưa ra các thông tin chi tiết thực hiện từng
bước khi chạy một chương trình trên Maple. Ta có thể sử dụng chúng
xem các bước thực hiện của một thủ tục và từ đó có thể tìm ra vị trí
lỗi.
printlevel là một biến toàn cục (giá trị mặc định bằng 1) dùng
để điều khiển thông tin đưa ra của mỗi bước thực hiện chương trình.
Những thơng tin này gồm: các phép gán, biểu thức, toàn bộ một
thủ tục và danh sách kết quả tính tốn trong khi thực hiện chương
trình. Biến printlevel chỉ nhận giá trị nguyên dương. Với giá trị mặc
định printlevel = 1, thì Maple đưa ra kết quả của các câu lệnh trong
178
Chương 6. Lập trình trong Maple
Maple, những câu lệnh lặp trong vòng lặp for/while và if cũng sẽ
được hiển thị ra. Nếu gán printlevel = 0 thì kết quả của các câu lệnh
nồng nhau ở mức bất kỳ sẽ không được hiển thị ra, chỉ có những câu
lệnh trực tiếp trong chương trình nguồn mới hiện thị ra. Nếu gán
cho printlevel giá trị âm thì Maple khơng đưa ra kết quả mà chỉ đưa
ra các câu lệnh của chương trình. Nếu gán cho printlevel những giá
trị lớn hơn khác, thì kết quả bao gồm nhiều thông tin về kết quả tính
tốn trong thủ tục và những câu lệnh lồng nhau trong for/while, if
và toàn bộ thủ tục.
trace(<Tên thủ tục>) là một hàm dùng tìm kiếm những kết
quả của thủ tục đang thực hiện. Hàm trace thực hiện giống như
printlevel gán giá trị lớn, nhưng chỉ tác dụng trên <Tên thủ tục>.
Để thốt khỏi thủ
untrace(<Tên thủ tục>).
tục
trace,
ta
sử
dụng
hàm
Ví dụ 6.31. Dùng biến toàn cục printlevel
A. Gán cho printlevel giá trị đủ lớn để nó sẽ in ra tất cả những kết
quả trung gian trong q trình tính tốn.
>printlevel:=11: sum(i^2, i=1..n);
{--> enter sum, args = i^2, i = 1 .. n
input := i = 1..n
{--> enter Preprocess, args = i^2, i = 1 .. n
ind := i = 1..n
di f f s := {}
has_di f f s := f alse
<-- exit Preprocess (now in sum)=[[i^2,i,1,n],false,FAIL]}
input := [[i2 , i, 1, n], f alse, FAIL]
subsIndexed := {}
{--> enter SumTools:-DefiniteSum:-ClosedForm,
args = [[i^2, i, 1, n], false, FAIL]
input, has_di f f s, parametric := [i2 , i, 1, n], f alse, FAIL
s, x := i2 , i
ro f := f alse
<-- exit SumTools:-DefiniteSum:-ClosedForm
(now in sum) = (1/3)*(n+1)^3-(1/2)*(n+1)^2+(1/6)*n+1/6}
1
1
1
1
( n + 1)3 − ( n + 1)2 + n +
3
2
6
6
<-- exit sum (now at top level) = (1/3)*(n+1)^3
-(1/2)*(n+1)^2+(1/6)*n+1/6}
6.8. trace và printlevel
179
1
1
1
1
( n + 1)3 − ( n + 1)2 + n +
3
2
6
6
B. Giảm bớt giá trị của printlevel để nó sẽ chỉ in ra nhưng thơng
tin cần thiết trong q trình tính tốn.
>printlevel:=6; sum(i^2, i = 1 .. n);
{-->enter sum, args = i^2, i = 1 .. n
input := i = 1..n
input := [[i2 , i, 1, n], f alse, FAIL]
subsIndexed := {}
1
1
1
1
( n + 1)3 − ( n + 1)2 + n +
3
2
6
6
<-- exit sum (now at top level) = (1/3)*(n+1)^3
-(1/2)*(n+1)^2+(1/6)*n+1/6}
1
1
1
1
( n + 1)3 − ( n + 1)2 + n +
3
2
6
6
C. Thiết lập lại giá trị mặc định ban đầu cho printlevel để nó sẽ chỉ
in ra kết quả cuối cùng của q trình tính toán.
>printlevel:=1; sum(i^2, i = 1 ..
1
1
( n + 1)3 − ( n + 1)2 +
3
2
n);
1
1
n+
6
6
Ví dụ 6.32. Gỡ rối chương trình bằng trace
A. Định nghĩa một thủ tục tính giá trị: Đưa vào là tập hợp số S, trong
thủ tục có đa thức p( x ) tính giá trị tại mỗi giá trị của S. Thủ tục đưa
ra kết quả như là một tập hợp điểm các kết quả tính p( x ). Sau đây
là thủ tục có lỗi nên kết quả đưa ra không như mong muốn mà lại là
đa thức.
>EvalPolyAt:=proc(S)
local t, p, x, answer;
p:=x^4 - 3*x^3 - 1;
answer:={};
for t in S do
x:=t;
answer:=answer union {p};
od;
return(answer);
end:
>EvalPolyAt({2975, 5556, -1187});
x4 − 3x3 − 1
180
Chương 6. Lập trình trong Maple
B. Như vậy thủ tục trên đã khơng cho kết quả tính tốn như thiết
kế. Ta xem thông tin thủ tục tực hiện bằng trace. Lưu ý rằng ở đây
mặc dù x được gán, nhưng nó khơng có tác dụng đối với giá trị biến
p. Chúng ta thấy thủ tục chỉ thực hiện một bậc tính tốn trong biểu
thức.
>trace(EvalPolyAt);
EvalPolyAt
>EvalPolyAt({2, 3, 4});
-->enter EvalPolyAt, args = {2, 3, 4}
p := x4 − 3x3 − 1
answer := {}
x := 2
answer := { x4 − 3x3 − 1}
x := 3
answer := { x4 − 3x3 − 1}
x := 4
answer := { x4 − 3x3 − 1}
<-- exit EvalPolyAt = { x**4 - 3*x**3 - 1}
{ x4 − 3x3 − 1}
C. Chúng ta có thể minh hoạ việc p đã xác định bởi công thức. Với
giá trị x, p không được gán giá trị theo hàm đánh giá kết quả tại
bậc 1.
>p:=x^4 - 3*x^3 - 1;
p := x4 − 3x3 − 1
>x:=4;
x := 4
>eval(p, 1);
x4 − 3x3 − 1
D. Tuy nhiên ở mức cao nhất của Maple phép tính được thực hiện.
>p;
63
E. Bây giời ta sửa để thủ tục EvalPolyAt bằng cách dùng hàm sub.
Chú ý rằng thủ tục đưa ra tập hợp và liệt kê ra giữa {}. Khi ta thay
đổi nơi dung thủ tục thì Maple tự động tắt trace kiểm tra.
6.9. Xem mã thư viện nguồn của Maple
181
>EvalPolyAt:=proc(S)
local t, p, x, answer;
p:=x^4 - 3*x^3 - 1;
answer:=NULL;
for t in S do
answer:=answer, subs(x = t, p);
od;
return({answer});
end:
F. Chúng ta thấy thủ tục thực hiện đúng.
>EvalPolyAt({2, 3, 4});
{−9, −1, 63}
>EvalPolyAt({295756, 585756, -101987});
{7651205213943063955647, 117723931975457965555647,
108190226139646787969}
6.9. Xem mã thư viện nguồn của Maple
Như đã trình bày trong phần mở đầu, nhiệm vụ cơ bản của Maple
là thực hiện những cơng việc tính tốn tốn học. Nó bao gồm: những
phép toán trên số nguyên và đa thức, đưa ra màn hình hoặc một tệp
đưa vào, một tệp đưa ra và một phần hệ thống mà Maple đưa vào
bằng ngơn ngữ lập trình. Hạt nhân cơ bản của Maple được viết bằng
ngôn ngữ C.
Thư viện của Maple chứa đựng hầu hết hàm chức năng của hệ
thống toán học và ta có thể xem được mã nguồn của chúng. Để liệt
kê mã nguồn của một thủ tục trong thư viện của Maple bằng câu
lệnh interface(verboseproc = 2) ; sau đó là lệnh print cho thủ tục mà
ta muốn biết mã nguồn. Ví dụ muốn xem mã nguồn thủ tục đại số
tuyến tính trong gói thủ tục linalg và iroot.
Ví dụ 6.33. Hiển thị mã lập trình một thủ tục thư viện của Maple
A. Hiển thị một thủ tục tính đa thức đặc trưng của ma trận. Thủ tục
này kiểm tra tính đúng đắn của các lựa chọn tham số trước khi gọi
thủ tục để tính ma trân đặc trưng và định thức.