Matlab tips: viết functions chuyên nghiệp hơn với inputParser

Tạo một hàm có quá nhiều input dễ dẫn đến nhầm lẫn không đáng có. Sẽ ra sao nếu bạn có thể quyết định thêm hay không các arguments chỉ bằng việc gọi tên chúng và gán giá trị kiểu func('ip1',val1,'ip2',val2)? inputParser trong matlab sẽ giúp bạn làm điềm đó.

inputParser là gì?

Đây là một object trong matlab giúp bạn quản lý được các tham số đầu vào của một function tự tạo. Bạn có thể tự định nghĩa các tham số cần thiết cho hàm kèm theo tên gọi của chúng để mỗi khi cần sử dụng thì chỉ việc điền tên chúng vào trong hàm là được. Bạn không cần phải liệt kê tất cả các tham số cho dù cần chúng hay không.

Thông thường một hàm tự định nghĩa sẽ có cấu trúc như sau:

func(arg0,arg1,arg2,arg3,...);

Khi sử dụng:

func(arg0,arg1,agr2,arg3,...); % phải đầy đủ tất cả tham số

Với inputParser, bạn có thể sử dụng như sau

func(arg0,'arg1',val1,'agr5',val5); % nếu bạn chỉ muốn dùng arg1 và arg5

inputParser giúp được gì?

  • Tạo được các arguments do mình tự đặt tên kèm theo giá trị mặc định của chúng khi chúng không được gọi tên trong hàm. Ví dụ trong trường hợp trên, các arguments ngoài arg1 và arg5 sẽ nhận giá trị mặc định của chúng.
  • Không quan tâm đến thứ tự nhập các arguments.
  • Làm cho hàm khi sử dụng trở nên gọn nhẹ hơn. Thay vì phải nhập n arguments thì chỉ cần nhập đúng 2 argument mình cần như ví dụ ở trên.

Cách tạo và sử dụng

Ví dụ để hiểu

Để cho dễ dàng, chúng ta hãy cùng nhau xây dựng một hàm ve_hinh với các tham số sau (bạn muốn vẽ hàm số $y=f(x)$ và có thể cả hàm $y=g(x)$ trên trục $Oxy$).

  • x (bắt buộc, theo thứ tự): giá trị của các điểm $x$ trên trục $Ox$.
  • f (bắt buộc, theo thứ tự): giá trị của các điểm $y=f(x)$ trên trục $Oy$.
  • g (tùy chọn, theo thứ tự): giá trị của các điểm $y=g(x)$ trên trục $Oy$ (giả sử màu của đường này luôn là xanh dương và nét liền).
  • duong (tùy chọn, không theo thứ tự): loại đường nối các điểm của đồ thị lại:
    • net_lien (mặc định): đường nét liền
    • net_dut: đường nét đứt dài
    • khong: không vẽ đường nối, chỉ vẽ điểm
  • mau (tùy chọn, không theo thứ tự): màu sắc của đường $y=f(x)$
    • den (mặc định): màu đen
    • xanh_la: xanh lá
    • do: đỏ

Ví dụ, dưới đây là một số cách khai báo và kết quả:

x = 0:pi/100:2*pi;
f = sin(x);
g = cos(x);
ve_hinh(x,f,'duong','khong');
x = 0:pi/100:2*pi;
f = sin(x);
g = cos(x);
ve_hinh(x,f,g,'mau','do');

Nếu dùng đầy đủ tất cả các tham số:

ve_hinh(x,f,g,'duong','dut_net','mau','xanh_la');

Như bạn có thể thấy trong hàm số, sẽ có 3 đối tượng khác biệt:

  • bắt buộc + theo thứ tự (xf): cái này là tham số buộc bạn phải điền vào trong ve_hinh và buộc phải đúng thứ tự như vậy. Tương ứng với từ khóa addRequired (sẽ giải thích bên dưới).
  • tùy chọn + theo thứ tự (g): cái này là tham số không bắt buộc phải điền vào ve_hinh, nếu điền thì nó sẽ xử lý, không điền thì thôi (khi ấy giá trị mặc định của nó sẽ được sử dụng). Tuy nhiên tham số này phải đảm bảo đúng thứ tự như trong định nghĩa hàm ve_hinh (trong trường hợp này, nó phải đứng thứ 3 ngay sau x và f). Tương ứng với từ khóa addOptional (sẽ giải thích bên dưới).
  • tùy chọn + không theo thứ tự (duongmau): bạn có thể không cần điền khi sử dụng hàm, khi ấy các giá trị mặc định ứng với từng tham số sẽ được chọn. Đặc biệt hơn, bạn không nhất thiết phải điền đúng theo thứ tự như lúc khai báo hàm ve_hinh. Ví dụ bạn có thể khai báo mau trước duong hoặc ngược lại điều được. Tương ứng với từ khóa addParameter (sẽ giải thích bên dưới).

Đoạn code đầy đủ của hàm ve_hinh là (chứa trong file ve_hinh.m).

function ve_hinh(x,f,varargin)
p = inputParser;
addRequired(p,'x'); % giá trị x
addRequired(p,'f'); % giá trị y của f
addOptional(p,'g',[]); % giá trị y của g
addParameter(p,'duong','net_lien',@(s) ismember(s,{'khong','net_lien','net_dut'})); % loại đường
addParameter(p,'mau','den',@(s) ismember(s,{'den','do','xanh_la'})); % màu của đường
parse(p,x,f,varargin{:});
kq = p.Results;

switch kq.duong
    case 'khong'
        duong = '.';
    case 'net_lien'
        duong = '-';
    case 'net_dut'
        duong = '--';
end
switch kq.mau
    case 'den'
        mau = strcat('k',duong);
    case 'do'
        mau = strcat('r',duong);
    case 'xanh_la'
        mau = strcat('g',duong);
end

plot(kq.x,kq.f,mau,'DisplayName','f(x)'); % vẽ đường y=f(x)
if ~isempty(kq.g) % nếu g có trong hàm
   hold on
   plot(kq.x,kq.g,'b-','DisplayName','g(x)'); % vẽ đường y=g(x)
end

legend('show'); % hiển thị chú thích cho từng đường

end

Cấu trúc

Như trong ví dụ trên, để có thể tạo một inputParser, ta tuân thủ theo cấu trúc sau:

function <output> = <tên-hàm>(<các-tham-số-cố-định>,varargin)
  p = inputParser; % khởi tạo inputParser (vào biến p)

  % Các tham số BẮT BUỘC phải có, THEO THỨ TỰ, mỗi tham số một dòng
  addRequired(p,'<tham-số>',<điều-kiện-cho-tham-số>);

  % Các tham số TÙY CHỌN nhưng THEO THỨ TỰ, mỗi tham số một dòng
  addOptional(p,'<tham-số>',<giá-trị-mặc-định>,<điều-kiện-cho-tham-số>);

  % Các tham số TÙY CHỌN và KHÔNG THEO THỨ TỰ, mỗi tham số một dòng
  addParameter(p,'<tham-số>',<giá-trị-mặc-định>,<điều-kiện-cho-tham-số>);

  % phân tích
  parse(p,<các-tham-số-bắt-buộc>,varargin{:});

  % Làm việc với các tham số
  p.Results.<tham-số>
end

Giải thích và lưu ý

  • varargin: cái này là để matlab biết là bạn đang sử dụng inputParser và tất cả các tham số trước nó đều là bắt buộc.
  • <điều-kiện-cho-tham-số>: cái này là điều kiện dùng để check xem tham số người dùng nhập vào có đúng hay không. Bạn phải dùng function handle để làm điều này. Có rất nhiều cách để làm cái này, bên dưới là một số gợi ý cơ bản.
    • Kiểm tra xem một số có là số dương hay không.
      <điều-kiện> = @(x) isnumeric(x) && x>0;
          
      % ví dụ khi đưa vào inputParser
      addOptional(p,'x',@(x) isnumeric(x) && x>0);
          
      % hoặc
      dieu_kien = @(x) isnumeric(x) && x>0;
      addOptional(p,'x',dieu_kien);
    • Kiểm tra có phải là ký tự hay không: ischarisstring (chuỗi ký tự hay không).
    • Kiểm tra số người dùng nhập vào có nằm trong các số mà mình cho phép hay không.
      @(s) ismember(s,[1,5,7,9]); % dấu ngoặc vuông []
    • Kiểm tra xem chữ người dùng nhập có nằm trong các chữ cho phép hay không.
      @(s) ismember(s,{'den','do','xanh_la'}); % dấu ngoặc nhọn {}
  • Ngoại trừ các tham số bắt buộc (addRequired), các tham số tùy chọn đều phải cần có <giá-trị-mặc-định> để khi người dùng không gọi đến chúng, chúng sẽ tự động sử dụng những giá trị mặc định này.

Nâng cao hơn

Trên đây là những điều cơ bản về inputParser tôi giúp bạn quen và tìm hiểu. Dưới đây là một số tùy chọn nâng cao mà bạn có thể tham khảo thêm

  • Cho phép các tham số phân biệt hoa/thường hay không (xem [1])
  • “Chơi” với các tham số khi bạn có thể để chúng gần nhau (thậm chí không cần dấu phẩy, khoảng trắng để phân biệt), hoặc dùng chung dấu '' cho tất cả các tham số,… (xem [4])

Tài liệu tham khảo

[1] MathWorks. Matlab Documentation: inputParser
[2] Will Hope. Better MATLAB functions with the inputParser class
[3] Mathworks. Parse Function Inputs
[4] Tibor Simon. Simple Input Parser.