Chương 5: Hàm

Trở về Mục lục cuốn sách

1. Sự xung đột về tên

Hãy nhớ rằng tất cả các tập tin lệnh bạn viết đều chạy trong cùng một không gian làm việc, vì vậy nếu một chương trình làm thay đổi giá trị một biến thì tất cả các chương trình khác đều thấy được sự thay đổi đó. Với một ít các chương trình đơn giản, điều này không đáng kể, nhưng rồi sau này những tương tác giữa các chương trình trở nên không thể quản lý được.

Chẳng hạn, chương trình sau tính tổng của n số đầu tiên trong một dãy hình học, nhưng cũng có hiệu ứng phụ là gán các giá trị cho A1, total, ia.

A1 = 1;
total = 0;
for i=1:10
    a = A1 * 0.5^(i-1);
    total = total + a;
end
ans = total

Nếu bạn dùng bất cứ tến biến nào nêu trên trước khi gọi mã lệnh này thì bạn có thể sẽ ngạc nhiên khi thấy rằng sau khi chạy đoạn chương trình, các giá trị đó đã thay đổi. Nếu bạn có 2 đoạn chương trình dùng cùng tên biến, bạn có thể thấy rằng chúng hoạt động riêng biệt nhưng đổ vỡ khi bạn cố gắng kết hợp các chương trình lại. Kiểu tương tác này được gọi là xung đột về tên.

Khi số tập tin lệnh của bạn viết tăng lên, đồng thời cũng dài hơn và phức tạp hơn thì sự xung đột về tên càng nghiêm trọng. Để tránh được vấn đề này, cần tạo ra các hàm.

2. Hàm

Một hàm cũng giống như một tập tin lệnh, chỉ khác ở chỗ

  • Mỗi hàm có không gian làm việc riêng của nó, vì vậy bất kì biến nào được định nghĩa trong hàm đều chỉ tồn tại khi hàm đang chạy, và không ảnh hưởng đến các biến trong không gian làm việc khác, ngay cả khi các biến đó có cùng tên.
  • Các dữ liệu đầu vào và kết quả đầu ra của hàm đều được định nghĩa một cách cẩn thận để tránh sự tương tác không mong muốn.

Để định nghĩa một hàm, bạn cần tạo ra một tập tin M với tên gọi mong muốn, và đặt một lời định nghĩa hàm vào trong đó. Chẳng hạn, để tạo ra một hàm có tên myfunc, hãy tạo ra tập tin M là myfunc.m và đặt vào đó định nghĩa hàm sau.

function res = myfunc(x)
    s = sin(x)
    c = cos(x)
    res = abs(s) + abs(c)
end

Từ đầu tiên của tập tin phải là function, vì đó là dấu hiệu đẻ MATLAB nhận biết một tập tin hàm thay vì tập tin lệnh.

Một định nghĩa hàm chính là một câu lệnh phức hợp. Dòng đầu tiên là dấu của hàm; nó chỉ định các số liệu đầu vào và đầu ra của hàm. Trong trường hợp này biến đầu vào có tên là x. Khi hàm này được gọi, đối số do người dùng cung cấp sẽ được gán cho x.

Biến đầu ra được gọi là res, chữ viết tắt của “result” (kết quả). Bạn có thể gọi biến đầu ra bằng tên gì cũng được, nhưng theo thông lệ, tôi thường đặt nó là res. Thường thì việc cuối cùng mà một hàm thực hiện là gán một giá trị cho biến đầu ra.

Một khi bạn đã định nghĩa một hàm mới, bạn có thể gọi nó theo cách giống như gọi các hàm có sẵn trong MATLAB. Nếu bạn gọi hàm như một câu lệnh, MATLAB sẽ đặt kết quả vào ans:

>> myfunc(1)

s = 0.84147098480790

c = 0.54030230586814

res = 1.38177329067604

ans = 1.38177329067604

Nhưng thường thấy hơn (đồng thời là cách tốt hơn) là nên gán kết quả cho một biến:

>> y = myfunc(1)

s = 0.84147098480790

c = 0.54030230586814

res = 1.38177329067604

y = 1.38177329067604

Khi bạn gỡ lỗi một hàm mới, có thể bạn sẽ phải hiển thị các giá trị trung gian như thế này, nhưng một khi hàm đã chạy tốt, bạn sẽ phải thêm vào các dấu chấm phẩy để khiến nó trở thành một hàm lặng. Đa số các hàm có sẵn trong MATLAB đều là hàm lặng, chúng tính ra kết quả, nhưng không hiển thị gì (ngoại trừ những thông báo trong một số trường hợp).

Mỗi hàm có một không gian làm việc riêng của nó, vốn được tạo ra khi hàm bắt đầu chạy và bị xóa đi khi hàm kết thúc. Nếu bạn thử truy cập (đọc hay ghi) biến được định nghĩa bên trong hàm, bạn sẽ thấy nó không tồn tại.

>> clear
>> y = myfunc(1);
>> who

Your variables are: y

>> s
??? Undefined function or variable 's'.

Giá trị duy nhất của hàm mà bạn có thể truy cập được là kết quả của nó, ở đây là giá trị gán cho biến y.

Nếu bạn có các biến tên là s hoặc c trong không gian làm việc trước khi gọi myfunc, chúng sẽ vẫn ở đó ngay cả khi hàm kết thúc.

>> s = 1;

>> c = 1;
>> y = myfunc(1);
>> s, c

s = 1
c = 1

Như vậy ở trong hàm bạn có thể dùng bất kì tên biến nào bạn muốn mà không sợ xung đột.

3. Thông tin về hàm

Tại chỗ bắt đầu của mỗi tập tin hàm, bạn nên ghi một lời chú thích nhằm giải thích tác dụng của hàm được viết ra.

% res = myfunc (x)
% Compute the Manhattan distance from the origin to the
% point on the unit circle with angle (x) in radians.

function res = myfunc (x)
    s = sin(x);
    c = cos(x);
    res = abs(s) + abs(c);
end

khi bạn yêu cầu trợ giúp bằng help, MATLAB sẽ in ra lời chú thích mà bạn đã ghi.

>> help myfunc
  res = myfunc (x)
  Compute the Manhattan distance from the origin to the
  point on the unit circle with angle (x) in radians.

Có nhiều điều trong thông lệ viết các lời chú thích này. Một số chi tiết nên đưa vào bao gồm:

  • Dấu của hàm, bao gồm tên hàm, (các) biến đầu vào, và (các) biến đầu ra.
  • Một lời mô tả rõ ràng, ngắn gọn về công dụng của hàm. Lời mô tả phải khái quát: nên bỏ qua những chi tiết về cách hoạt động của hàm, mà chỉ bao gồm những thông tin mà người dùng hàm muốn biết. Bạn có thể thêm vào các chú thích bên trong hàm để lý giải những chi tiết.
  • Một lời giải thích ý nghĩa các biến đầu vào; chẳng hạn, trong trường hợp này cần lưu ý rằng x được coi là số đo góc tính theo ra-đian.
  • Các điều kiện đầu và điều kiện cuối.

4. Tên hàm

Có ba điều rất phải lưu ý khi bạn mới đặt tên hàm. Thứ nhất là tên “thực sự” của hàm được quyết định bởi tên tập tin chứ không phải tên mà bạn ghi ở dấu của hàm. Theo phong cách, bạn nên chắc rằng hai cái tên này phải luôn giống nhau, nhưng nếu bạn lầm, hoặc đã đổi tên của hàm, thì rất dễ bị rối.

Theo tinh thần cố ý gây lỗi, hãy thử đổi tên hàm từ myfunc sang something_else, và chạy lại hàm đó.

Đây là những gì bạn đưa vào trong myfunc.m:

function res = something_else (x)
    s = sin(x);
    c = cos(x);
    res = abs(s) + abs(c);
end

Và đây là những gì bạn nhận được:

>> y = myfunc(1);
>> y = something_else(1);
??? Undefined command/function 'something_else'.

Điều thứ hai là tên của tập tin không được phép chứa dấu cách. Chẳng hạn, nếu bạn viết một hàm và đặt tên tập tin là my func.m, thì mặc dù trình soạn thảo MATLAB vẫn đồng ý, nhưng khi thử chạy, bạn sẽ nhận được:

>> y = my func(1)
??? y = my func(1)
           |
Error: Unexpected MATLAB expression.

Điều thứ ba là tên hàm được viết có thể xung đột với các hàm lập sẵn của MATLAB. Chẳng hạn, nếu bạn tạo ra tập tin M có tên sum.m, rồi gọi sum, MATLAB có thể sẽ gọi hàm mới do bạn viết, chứ không phải hàm lập sẵn! Hàm nào được gọi sẽ tùy thuộc vào thứ tự của các thư mục xếp trong đường dẫn tìm kiếm, và (trong một số trường hợp) phụ thuộc vào đối số. Chẳng hạn, hãy đặt các lệnh sau vào tập tin có tên sum.m:

function res = sum(x)
   res = 7;
end

Rồi thử gõ:

>> sum(1:3)

ans = 6

>> sum

ans = 7

Trong trường hợp đầu MATLAB đã dùng hàm lập sẵn; ở trường hợp thứ hai nó thực hiện hàm bạn viết! Kiểu tương tác này có thể rất gây lẫn. Trước khi tạo ra hàm mới, bạn cần kiểm tra xem có sẵn hàm nào của MATLAB có cùng tên không. Nếu có, hãy đặt một tên khác!

5. Nhiều biến đầu vào

Các hàm có thể, và thường, nhận nhiều biến đầu vào. Chẳng hạn, hàm sau đây nhận hai biến đầu vào, ab:

function res = hypotenuse(a, b)
    res = sqrt(a^2 + b^2);
end

Nếu bạn còn nhớ Định lý Py-ta-go, bạn có thể đã hình dung ra là hàm này để tính chiều dài cạnh huyền của tam giác vuông nếu các cạnh góc vuông là ab. (Có một hàm MATLAB tên là hypot thực hiện điều tương tự.)

Nếu ta gọi nó từ Command Window với các đối số 3 và 4, ta có thể chắc rằng chiều dài cạnh thứ ba sẽ bằng 5.

>> c = hypotenuse(3, 4)

c = 5

Các đối số mà bạn cấp vào đây được gán cho các biến đầu vào theo đúng thứ tự, vì vậy ở trường hợp này 3 được gán cho a còn 4 được gán cho b. MATLAB kiểm tra để đảm bảo rằng bạn cung cấp đủ đối số; nếu cung cấp thiếu, bạn sẽ nhận được:

>> c = hypotenuse(3)
??? Input argument "b" is undefined.

Error in ==> hypotenuse at 2
    res = sqrt(a^2 + b^2);

Thông báo lỗi này dễ gây nhầm lẫn, vì nó gợi ý rằng trục trặc xảy ra ở hypotenuse thay vì ở lời gọi hàm. Hãy nhớ điều đó khi bạn gỡ lỗi.

Nếu cung cấp thừa đối số, bạn sẽ nhận được:

>> c = hypotenuse(3, 4, 5)
??? Error using ==> hypotenuse
Too many input arguments.

Đây là thông báo lỗi hay hơn.

6. Các hàm logic

Ở Mục toán tử logic ta đã dùng các toán tử logic để so sánh các giá trị. MATLAB cũng cung cấp các hàm logic để kiểm tra những điều kiện nhất định và trả lại những giá trị logic: 1 với nghĩa là “đúng” và 0 với nghĩa là “sai”.

Chẳng hạn, isprime dùng để kiểm tra xem số đã cho có phải là số nguyên tố hay không.

>> isprime(17)

ans = 1

>> isprime(21)

ans = 0

Các hàm isscalarisvector là để kiểm tra xem một giá trị là số vô hướng hay véc-tơ; nếu cả hai đều sai, bạn có thể tạm coi đối số là một ma trận.

Để kiểm tra xem một giá trị bạn đã tính có phải là số nguyên không, bạn có thể muốn dùng isinteger. Nhưng cách làm này sai. isinteger kiểm tra xem giá trị có thuộc về một trong bốn kiểu số nguyên (một chủ đề mà ta chưa bàn luận đến), chứ nó không kiểm tra xem một số có dấu phẩy động tình cờ nhận giá trị nguyên hay không.

>> c = hypotenuse(3, 4)

c = 5

>> isinteger(c)

ans = 0

Để làm điều này, ta cần tự viết một hàm logic, và sẽ gọi nó là isintegral:

function res = isintegral(x)
    if round(x) == x
        res = 1;
    else
        res = 0;
    end
end

Hàm này dùng được trong phần lớn các trường hợp, nhưng nhớ rằng các giá trị dấu phẩy động chỉ xấp xỉ đúng, trong một số trường hợp giá trị xấp xỉ là số nguyên nhưng giá trị thực lại không phải.

7. Một ví dụ phát triển tăng dần

Giả dụ rẳng ta muốn viết một chương trình tìm các “bộ ba số Py-ta-go”: những tập hợp số nguyên như 3, 4, và 5, là độ dài các cạnh của một tam giác vuông. Nói cách khác, ta muốn tìm các giá trị nguyên a, bc sao cho a2+b2=c2.

Sau đây là các bước mà ta sẽ làm theo để phát triển chương trình một cách tăng dần.

  • Viết một tập tin lệnh có tên find_triples và bắt đầu với một câu lệnh đơn giản như x=5.
  • Viết một vòng lặp để liệt kê các giá trị a từ 1 đến 3, và hiển thị chúng.
  • Viết một vòng lặp lồng ghép để liệt kê các giá trị b từ 1 đến 4, và hiển thị chúng.
  • Bên trong vòng lặp, gọi hypotenuse để tính c và hiển thị nó.
  • Dùng isintegral để kiểm tra xem c có bằng một số nguyên hay không.
  • Dùng một lệnh if để chỉ in ra những cặp giá trị a, bc nào thỏa mãn điều kiện.
  • Chuyển nội dung tập tin lệnh vào trong một hàm.
  • Khái quát hóa hàm này để nhận vào các biến chỉ định khoảng các giá trị cần được tìm kiếm.

Như vậy bản nháp đầu tiên của chương trình này là x=5, trông có vẻ ngốc nghếch, nhưng nếu bạn bắt đầu một cách đơn giản và mỗi lúc chỉ thêm vào ít một, bạn sẽ tránh được gỡ lỗi rất nhiều.

Sau đây là bản nháp thứ hai:

for a=1:3
    a
end

Ở mỗi bước, chương trình đều có thể chạy thử được: nó có in ra kết quả (hay một hiệu ứng thấy được) mà ta có thể kiểm tra.

8. Vòng lặp lồng ghép

Bản nháp thứ ba chứa một vòng lặp lồng ghép:

for a=1:3
    a
    for b=1:4
        b
    end
end

Vòng lặp trong được thực hiện 3 lần, mỗi lần với một giá trị khác nhau của a, vì vậy sau đây là kết quả (tôi đã chỉnh lại độ dãn cách để làm nổi bật cấu trúc):

>> find_triples

a = 1   b = 1
        b = 2
        b = 3
        b = 4

a = 2   b = 1
        b = 2
        b = 3
        b = 4

a = 3   b = 1
        b = 2
        b = 3
        b = 4

Bước tiếp theo là tính c với mỗi cặp giá trị của ab.

for a=1:3
    for b=1:4
        c = hypotenuse(a, b);
        [a, b, c]
    end
end

Để hiển thị các giá trị của a, bc, tôi dùng đến một đặc điểm của MATLAB mà ta chưa gặp. Toán tử ngoặc vuông tạo ra một ma trận mới mà, khi được hiển thị, sẽ cho thấy các giá trị trên cùng một dòng:

>> find_triples

ans = 1.0000    1.0000    1.4142
ans = 1.0000    2.0000    2.2361
ans = 1.0000    3.0000    3.1623
ans = 1.0000    4.0000    4.1231
ans = 2.0000    1.0000    2.2361
ans = 2.0000    2.0000    2.8284
ans = 2.0000    3.0000    3.6056
ans = 2.0000    4.0000    4.4721
ans = 3.0000    1.0000    3.1623
ans = 3.0000    2.0000    3.6056
ans = 3.0000    3.0000    4.2426
ans = 3         4         5

Bạn đọc tinh mắt sẽ phát hiện được rằng chúng ta đang lãng phí một ít công sức lập trình. Sau khi kiểm tra a=1b=2, sẽ chẳng cần kiểm tra a=2b=1. Ta có thể loại bỏ công việc thừa này bằng cách chỉnh lại khoảng lặp của vòng thứ hai:

for a=1:3
    for b=a:4
        c = hypotenuse(a, b);
        [a, b, c]
    end
end

Nếu bạn vẫn theo kịp nội dung, hãy chạy phiên bản mã lệnh này và chắc chắn rằng nó cho kết quả như mong muốn.

9. Điều kiện và cờ

Bước tiếp theo là kiểm tra xem giá trị c có nguyên không. Vòng lặp này gọi isintegral và in ra giá trị logic thu được.

for a=1:3
    for b=a:4
        c = hypotenuse(a, b);
        flag = isintegral(c);
        [c, flag]
    end
end

Bằng cách không hiển thị ab tôi làm cho việc soát kết quả dễ dàng hơn để đảm bảo rằng các giá trị của cflag trông đúng đắn.

>> find_triples

ans = 1.4142         0
ans = 2.2361         0
ans = 3.1623         0
ans = 4.1231         0
ans = 2.8284         0
ans = 3.6056         0
ans = 4.4721         0
ans = 4.2426         0
ans = 5              1

Tôi chọn các khoảng ab đều nhỏ (vì vậy số dòng kết quả đầu ra có thể kiểm soát được), nhưng phải bao gồm ít nhất là một bộ ba số Py-ta-go. Một khó khăn thường gặp khi gỡ lỗi là phải phát sinh đủ kết quả để cho thấy rằng mã lệnh có (hoặc không) hoạt động mà không quá thừa thãi.

Bước tiếp theo là dùng flag để chỉ hiển thị những bộ ba thỏa mãn yêu cầu:

for a=1:3
    for b=a:4
        c = hypotenuse(a, b);
        flag = isintegral(c);
        if flag
            [a, b, c]
        end
    end
end

Bây giờ kết quả đẹp và đơn giản hơn:

>> find_triples

ans = 3     4     5

10. Bao bọc và khái quát hóa

Dưới dạng tập tin lệnh, chương trình này có tác dụng phụ là đã gán các giá trị cho a, b, cflag, vốn sẽ làm chương trình khó dùng hơn khi các tên biến trên đang được sử dụng. Bằng cách bọc mã lệnh này vào trong một hàm, ta có thể tránh được sự xung đột về tên; quá trình này được gọi là bao bọc vì nó cô lập chương trình khỏi không gian làm việc.

Để đưa mã lệnh đã viết vào trong một hàm, ta phải viết thụt đầu dòng toàn bộ. Trình soạn thảo MATLAB cung cấp một cách làm tắt, lệnh {Increase Indent} dưới trình đơn {Text}.

Bản nháp đầu tiên của hàm trong đó không có biến đầu vào như sau:

function res = find_triples ()
    for a=1:3
        for b=a:4
            c = hypotenuse(a, b);
            flag = isintegral(c);
            if flag
                [a, b, c]
            end
        end
    end
end

Cặp ngoặc tròn trong đó không có gì ở dấu của hàm là không cần thiết, nhưng chúng làm rõ là không có biến đầu vào nào. Tương tự, khi tôi gọi một hàm mới, tôi cũng ưa dùng cặp ngoặc để tự nhủ rằng đó là một hàm chứ không phải nội dung tập tin lệnh:

>> find_triples()

Biến đầu ra cũng không nhất thiết phải có; nó không bao giờ được gán một giá trị. Nhưng tôi vẫn đặt nó ở đây như một thói quen, cũng là nhờ vậy mà các dấu hàm của tôi tất cả đều có chung một dạng.

Bước tiếp theo là khái quát hóa hàm này bằng cách thêm các biến đầu vào. Cách khái quát khá tự nhiên là thay thế các hằng số 3 và 4 bằng một biến để ta có thể tìm kiếm trên một khoảng lớn tùy ý.

function res = find_triples (n)
    for a=1:n
        for b=a:n
            c = hypotenuse(a, b);
            flag = isintegral(c);
            if flag
                [a, b, c]
            end
        end
    end
end

Sau đây là các kết quả thu được trong khoảng từ 1 đến 15:

>> find_triples(15)

ans = 3     4     5
ans = 5    12    13
ans = 6     8    10
ans = 8    15    17
ans = 9    12    15

Trong các kết quả này có cái hay, có cái không. Các cặp 5,12,138,15,17 thật sự “mới,” còn các cặp khác chỉ là các bội số của cặp 3,4,5 mà ta đã biết.

11. Một sai sót

Khi bạn thay đổi dấu của hàm, bạn cũng phải thay đổi tất cả mọi chỗ gọi đến hàm đó. Chẳng hạn, nếu tôi định thêm một biến thứ ba vào hypotenuse:

function res = hypotenuse(a, b, d)
    res = (a.^d + b.^d) ^ (1/d);
end

Khi d bằng 2, hàm này có tác dụng giống như cũ. Hiện không có lý do nào phù hợp để khái quát hàm này theo cách trên; đây chỉ là ví dụ. Bây giờ khi bạn chạy find_triples, bạn sẽ nhận được:

>> find_triples(20)
??? Input argument "d" is undefined.

Error in ==> hypotenuse at 2
    res = (a.^d + b.^d) ^ (1/d);

Error in ==> find_triples at 7
            c = hypotenuse(a, b);

Như vậy thật khó tìm ra lỗi. Đây là một ví dụ cho kĩ thuật phát triển mà đôi khi có ích: thay vì tìm kiếm mọi chỗ trong chương trình có dùng đến hypotenuse, bạn có thể chạy chương trình và theo các dòng thông báo để tìm ra lỗi.

Nhưng kĩ thuật này rất mạo hiểm, đặc biệt khi các thông báo lỗi đưa ra gợi ý phải sửa đổi những gì. Nếu bạn làm theo chúng, bạn có thể làm biến mất thông báo lỗi, nhưng điều đó không có nghĩa là chương trình đã làm đúng điều ta muốn. MATLAB không biết rằng chương trình cần phải làm gì, mà bạn phải có trách nhiệm về điều này.

Và từ đó dẫn đến Định lý thứ tám về gỡ lỗi:

Các lời thông báo lỗi đôi khi bảo cho bạn biết điều gì trục trặc, nhưng hiếm khi chúng bảo cho bạn cách làm (và nếu chúng cố gắng giúp đi nữa thì cũng thường nói sai).

12. continue

Ở khâu cải tiến cuối cùng, ta hãy sửa hàm này sao cho nó chỉ hiểu thị những bộ ba số Py-ta-go “thấp nhất”, chứ không kể tất cả các bội số của chúng.

Cách làm đơn giản nhất để loại bỏ các bội số là kiểm tra xem ab có thừa số chung hay không. Nếu có, thì việc chia các số này cho thừa số chung sẽ cho ra một bộ ba số nhỏ hơn mà ta đã kiểm tra.

MATLAB có một hàm gcd để tính ước số chung nhỏ nhất của hai số. Nếu kết quả lớn hơn 1, thì ab có một ước số chung và ta có thể dùng lệnh continue để nhảy sang cặp tiếp theo:

function res = find_triples (n)
    for a=1:n
        for b=a:n
            if gcd(a,b) > 1
                continue
            end
            c = hypotenuse(a, b);
            if isintegral(c)
                [a, b, c]
            end
        end
    end
end

continue làm cho chương trình dừng vòng lặp hiện tại (tức là không thực hiện phần còn lại của thân lệnh nữa), nhảy đến đầu vòng lặp, và “tiếp tục” với lượt lặp liền sau.

Trong trường hợp này, vì ta có hai vòng lặp nên sẽ không hiển nhiên là vòng lặp nào được nhảy đến, nhưng quy tắc là nhảy đến vòng lặp sâu bên trong nhất (đúng theo ý định của ta ở đây).

Tôi cũng làm đơn giản chương trình một chút bằng cách loại bỏ flag và dùng isintegral làm điều kiện cho lệnh if.

Sau đây là các kết quả với n=40:

>> find_triples(40)

ans =  3     4     5
ans =  5    12    13
ans =  7    24    25
ans =  8    15    17
ans =  9    40    41
ans = 12    35    37
ans = 20    21    29

Có một sự liên hệ thú vị giữa các số Fibonacci và cặp số Py-ta-go. Nếu F là một dãy số Fibonacci, thì

(FnFn+3 , 2Fn+1Fn+2 Fn+1Fn+22)

là một cặp số Py-ta-go với mọi n ≥ 1.

Hãy viết một hàm có tên fib_triple để nhận vào một biến n, dùng fibonacci2 để tính n số Fibonacci đầu tiên, rồi kiểm tra xem công thức nói trên có tạo thành bộ ba số Py-ta-go với mỗi số trong dãy không.

13. Khoa học và niềm tin

Ta hãy xem lại một loạt các bước xảy ra khi bạn gọi một hàm:

  1. Trước khi hàm bắt đầu chạy, MATLAB tạo ra một không gian làm việc mới cho nó.
  2. MATLAB lượng giá từng đối số và gán các giá trị tìm được lần lượt cho từng biến đầu vào (vốn tồn tại trong không gian làm việc mới).
  3. Mã lệnh ở phần thân của hàm được thực thi. Đâu đó trong phần thân (thường là ở dòng cuối cùng) một giá trị sẽ được gán cho biến đầu ra.
  4. Không gian làm việc của hàm bị xóa bỏ; thứ duy nhất còn lại là giá trị của biến đầu ra và mọi hiệu ứng phụ của hàm (như hiển thị các giá trị hoặc vẽ một hình).
  5. Chương trình tiếp tục tại điểm mà nó tạm dừng để thực hiện hàm. Giá trị của lời gọi hàm là giá trị của biến đầu ra.

Khi bạn đang đọc chương trình và gặp một lời gọi hàm, có hai cách diễn giải nó:

  • Bạn có thể nghĩ cách khoa học như tôi đã trình bày, và theo các bước thực hiện của chương trình, tiến vào trong hàm rồi sau đó trở lại, hoặc
  • Bạn có dựa vào “niềm tin”: giả sử rằng hàm hoạt động đúng, và đọc tiếp lệnh sau lời gọi hàm đó.

Khi bạn dùng những hàm lập sẵn, cách tự nhiên là dựa vào niềm tin, một phần là do bạn trông đợi rằng đại đa số các hàm MATLAB đều hoạt động đúng, và một phần là do bạn không xem được mã lệnh bên trong phần thân hàm.

Khi bắt đầu tự viết các hàm, bạn có thể sẽ tự thấy mình trong đi theo “luồng thực hiện” của chương trình. Điều này có thể giúp ích khi bạn đang học, nhưng khi đã có kinh nghiệm, bạn nên quen với ý tưởng viết một hàm, kiểm tra để đảm bảo nó chạy đúng, và sau đó quên đi những chi tiết về cách hoạt động của nó.

Việc quên đi các chi tiết được gọi là trừu tượng hóa; trong ngữ cảnh này, trừu tượng hóa có nghĩa là quên đi cách hoạt động của một hàm, và chỉ giả sử (sau khi kiểm tra hợp lý) rằng hàm chạy được.

14. Thuật ngữ

hiệu ứng phụ:
Hiệu ứng như thay đổi không gian làm việc, mà không phải là mục đích của chương trình.

xung đột về tên:
Hoàn cảnh trong đó hai tập tin chương trình dùng cùng một tên biến đã can thiệp nhau.

biến đầu vào:
Biến trong một hàm, được nhận giá trị từ một trong các đối số, khi ta gọi hàm.

biến đầu ra:
Biến trong một hàm, dùng để trả một giá trị từ hàm về chương trình gọi.

dấu của hàm:
Dòng đầu tiên của một lời định nghĩa hàm, trong đó có chỉ định tên hàm, các biến đầu vào và biến đầu ra.

hàm lặng:
Hàm không làm hiển thị giá trị nào, cũng không vẽ hình hay có bất kì hiệu ứng phụ gì.

hàm logic:
Hàm trả lại một giá trị logic (1 đóng vai trò “đúng” và 0 đóng vai trò “sai”).

bao bọc:
Quá trình gói một phần của chương trình vào trong một hàm nhằm hạn chế những tương tác (trong đó có xung đột về tên) giữa hàm và phần còn lại của chương trình.

khái quát hóa:
Làm cho hàm trở nên linh hoạt hơn bằng cách thay những giá trị cụ thể bằng các biến đầu vào.

trừu tượng hóa:
Sự bỏ qua những chi tiết hoạt động của một hàm nhằm tập trung vào một mô hình đơn giản hơn: hàm làm việc gì?

15. Bài tập

Chọn bất kì tập tin lệnh nào bạn đã viết, bao bọc nó vào trong một hàm có tên thích hợp, rồi khái quát hóa hàm này bằng cách bổ sung một hoặc nhiều biến đầu vào.

Làm cho hàm trở nên lặng bằng cách gọi nó từ Command Window và đảm bảo rằng bạn có thể hiển thị giá trị đầu ra.

5 phản hồi

Filed under Mô hình hóa, Sách

5 responses to “Chương 5: Hàm

  1. Pingback: Chương 1. Các biến và giá trị | Blog của Chiến

  2. Pingback: Chương 11: Tối ưu hóa và nội suy | Blog của Chiến

  3. Pingback: Chương 6: Tìm nghiệm | Blog của Chiến

  4. Pingback: Mô hình hóa hiện tượng vật lý bằng MATLAB | Blog của Chiến

  5. Thần tượng anh quá ^^

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s