Làm cách nào để đặt giá trị mặc định cho tham số hàm trong Matlab?


126

Có thể có các đối số mặc định trong Matlab? Ví dụ, ở đây:

function wave(a, b, n, k, T, f, flag, fTrue=inline('0'))

Tôi muốn có giải pháp thực sự là một đối số tùy chọn cho hàm sóng. Nếu có thể, bất cứ ai cũng có thể chứng minh cách thích hợp để làm điều này? Hiện tại, tôi đang thử những gì tôi đã đăng ở trên và tôi nhận được:

??? Error: File: wave.m Line: 1 Column: 37
The expression to the left of the equals sign is not a valid target for an assignment.

Câu trả lời:


151

Không có cách nào trực tiếp để làm điều này như bạn đã cố gắng.

Cách tiếp cận thông thường là sử dụng "varargs" và kiểm tra số lượng đối số. Cái gì đó như:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Có một vài điều thú vị hơn bạn có thể làm isempty, v.v., và bạn có thể muốn xem trung tâm Matlab để biết một số gói có các loại này.

Bạn có thể có một cái nhìn tại varargin, nargchk, vv Họ chức năng hữu ích đang cho các loại điều này. varargs cho phép bạn để lại một số lượng đối số cuối cùng khác nhau, nhưng điều này không giúp bạn giải quyết được vấn đề về giá trị mặc định cho một số / tất cả chúng.


58

Tôi đã sử dụng inputParserđối tượng để xử lý các thiết lập tùy chọn mặc định. Matlab sẽ không chấp nhận định dạng giống python mà bạn đã chỉ định trong câu hỏi, nhưng bạn sẽ có thể gọi hàm như thế này:

wave(a,b,n,k,T,f,flag,'fTrue',inline('0'))

Sau khi bạn xác định wavehàm như thế này:

function wave(a,b,n,k,T,f,flag,varargin)

i_p = inputParser;
i_p.FunctionName = 'WAVE';

i_p.addRequired('a',@isnumeric);
i_p.addRequired('b',@isnumeric);
i_p.addRequired('n',@isnumeric);
i_p.addRequired('k',@isnumeric);
i_p.addRequired('T',@isnumeric);
i_p.addRequired('f',@isnumeric);
i_p.addRequired('flag',@isnumeric); 
i_p.addOptional('ftrue',inline('0'),1);    

i_p.parse(a,b,n,k,T,f,flag,varargin{:});

Bây giờ các giá trị được truyền vào hàm có sẵn thông qua i_p.Results. Ngoài ra, tôi không chắc chắn làm thế nào để xác thực rằng tham số được truyền vào ftruethực sự là một inlinehàm nên để trống trình xác nhận.


7
Như tốt nhất tôi có thể nói, đây , là phương pháp ưa thích. Nó sạch sẽ, tự ghi lại tài liệu (hơn nữa là một loạt các if narginstHRens), dễ bảo trì, nhỏ gọn và linh hoạt.
JnBrymn

19

Một cách khác ít hacky hơn là

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end

Tùy chọn này không hoạt động nếu bạn định sử dụng MATLAB Coder để tạo mã C, vì Coder không hỗ trợ chức năng "tồn tại".
Dave Tillman

10

Vâng, nó có thể thực sự tốt để có khả năng làm như bạn đã viết. Nhưng điều đó là không thể trong MATLAB. Nhiều tiện ích của tôi cho phép mặc định cho các đối số có xu hướng được viết bằng các kiểm tra rõ ràng ngay từ đầu như sau:

if (nargin<3) or isempty(myParameterName)
  MyParameterName = defaultValue;
elseif (.... tests for non-validity of the value actually provided ...)
  error('The sky is falling!')
end

Ok, vì vậy tôi thường sẽ áp dụng một thông báo lỗi mô tả tốt hơn. Xem rằng kiểm tra một biến trống cho phép người dùng chuyển vào một cặp dấu ngoặc rỗng, [], như một trình giữ chỗ cho một biến sẽ lấy giá trị mặc định của nó. Tác giả vẫn phải cung cấp mã để thay thế đối số trống đó bằng giá trị mặc định của nó.

Các tiện ích của tôi tinh vi hơn, với NHIỀU tham số, tất cả đều có đối số mặc định, thường sẽ sử dụng giao diện cặp thuộc tính / giá trị cho các đối số mặc định. Mô hình cơ bản này được nhìn thấy trong các công cụ đồ họa xử lý trong MATLAB, cũng như trong Optimset, odeset, v.v.

Là một phương tiện để làm việc với các cặp thuộc tính / giá trị này, bạn sẽ cần tìm hiểu về varargin, như một cách nhập số lượng đối số biến đổi đầy đủ cho một hàm. Tôi đã viết (và đăng) một tiện ích để làm việc với các cặp thuộc tính / giá trị như vậy, parse_pv_pairs.m . Nó giúp bạn chuyển đổi các cặp thuộc tính / giá trị thành cấu trúc MATLAB. Nó cũng cho phép bạn cung cấp các giá trị mặc định cho từng tham số. Chuyển đổi một danh sách các tham số khó sử dụng thành một cấu trúc là một cách RẤT hay để vượt qua chúng trong MATLAB.


6

Đây là cách đơn giản của tôi để đặt giá trị mặc định cho hàm, sử dụng "thử":

function z = myfun (a,varargin)

%% Default values
b = 1;
c = 1;
d = 1;
e = 1;

try 
    b = varargin{1};
    c = varargin{2};
    d = varargin{3};
    e = varargin{4};
end

%% Calculation
z = a * b * c * d * e ;
end

Trân trọng!



3

Ngoài ra còn có một 'hack' có thể được sử dụng mặc dù có thể xóa nó khỏi matlab tại một số điểm: Hàm eval thực sự chấp nhận hai đối số trong đó đối số thứ hai được chạy nếu xảy ra lỗi với lần đầu tiên.

Do đó chúng ta có thể sử dụng

function output = fun(input)
   eval('input;', 'input = 1;');
   ...
end

sử dụng giá trị 1 làm mặc định cho đối số


3

Tôi tin rằng tôi đã tìm thấy một cách khá tiện lợi để giải quyết vấn đề này, chỉ chiếm ba dòng mã (chặn dòng kết thúc). Sau đây được nâng trực tiếp từ một chức năng tôi đang viết và có vẻ như nó hoạt động như mong muốn:

defaults = {50/6,3,true,false,[375,20,50,0]}; %set all defaults
defaults(1:nargin-numberForcedParameters) = varargin; %overload with function input
[sigma,shifts,applyDifference,loop,weights] = ...
     defaults{:}; %unfold the cell struct

Chỉ cần nghĩ rằng tôi sẽ chia sẻ nó.


3

Tôi bối rối không ai chỉ ra bài đăng trên blog này của Loren, một trong những nhà phát triển của Matlab. Cách tiếp cận dựa trênvarargin và tránh tất cả những trường hợp vô tận và đau đớn if-then-elsehoặc những switchtrường hợp có điều kiện phức tạp. Khi có một vài giá trị mặc định, hiệu ứng sẽ rất ấn tượng . Đây là một ví dụ từ blog được liên kết:

function y = somefun2Alt(a,b,varargin)
% Some function that requires 2 inputs and has some optional inputs.

% only want 3 optional inputs at most
numvarargs = length(varargin);
if numvarargs > 3
    error('myfuns:somefun2Alt:TooManyInputs', ...
        'requires at most 3 optional inputs');
end

% set defaults for optional inputs
optargs = {eps 17 @magic};

% now put these defaults into the valuesToUse cell array, 
% and overwrite the ones specified in varargin.
optargs(1:numvarargs) = varargin;
% or ...
% [optargs{1:numvarargs}] = varargin{:};

% Place optional args in memorable variable names
[tol, mynum, func] = optargs{:};

Nếu bạn vẫn không nhận được nó, thì hãy thử đọc toàn bộ bài đăng trên blog của Loren. Tôi đã viết một bài đăng blog tiếp theo liên quan đến các giá trị mặc định vị trí bị thiếu . Ý tôi là bạn có thể viết một cái gì đó như:

somefun2Alt(a, b, '', 42)

và vẫn có epsgiá trị mặc định cho toltham số (và tất nhiên là @magicgọi lại func). Mã của Loren cho phép điều này với một sửa đổi nhỏ nhưng khó khăn.

Cuối cùng, chỉ một vài ưu điểm của phương pháp này:

  1. Ngay cả với rất nhiều giá trị mặc định, mã soạn sẵn cũng không lớn (trái ngược với họ của các if-then-elsephương pháp tiếp cận, sẽ dài hơn với mỗi giá trị mặc định mới)
  2. Tất cả các mặc định là ở một nơi. Nếu bất kỳ ai trong số những người cần thay đổi, bạn chỉ có một nơi để xem xét.

Trooth được nói, có một bất lợi quá. Khi bạn gõ hàm trong vỏ Matlab và quên các tham số của nó, bạn sẽ thấy vararginmột gợi ý không hữu ích như một gợi ý. Để giải quyết vấn đề đó, bạn nên viết một điều khoản sử dụng có ý nghĩa.


Liên kết đến bài đăng blog tiếp theo của bạn bị hỏng; bạn có thể sửa nó không?
equaeghe

2

Sau khi nhận ra ASSIGNIN (nhờ câu trả lời này của b3 ) và EVALIN tôi đã viết hai hàm để cuối cùng có được cấu trúc gọi rất đơn giản:

setParameterDefault('fTrue', inline('0'));

Đây là danh sách:

function setParameterDefault(pname, defval)
% setParameterDefault(pname, defval)
% Author: Tobias Kienzler (https://stackoverflow.com/users/321973)
% sets the parameter NAMED pname to the value defval if it is undefined or
% empty

if ~isParameterDefined('pname')
    error('paramDef:noPname', 'No parameter name defined!');
elseif ~isvarname(pname)
    error('paramDef:pnameNotChar', 'pname is not a valid varname!');
elseif ~isParameterDefined('defval')
    error('paramDef:noDefval', ['No default value for ' pname ' defined!']);
end;

% isParameterNotDefined copy&pasted since evalin can't handle caller's
% caller...
if ~evalin('caller',  ['exist(''' pname ''', ''var'') && ~isempty(' pname ')'])
    callername = evalin('caller', 'mfilename');
    warnMsg = ['Setting ' pname ' to default value'];
    if isscalar(defval) || ischar(defval) || isvector(defval)
        warnMsg = [warnMsg ' (' num2str(defval) ')'];
    end;
    warnMsg = [warnMsg '!'];
    warning([callername ':paramDef:assigning'], warnMsg);
    assignin('caller', pname, defval);
end

function b = isParameterDefined(pname)
% b = isParameterDefined(pname)
% Author: Tobias Kienzler (https://stackoverflow.com/users/321973)
% returns true if a parameter NAMED pname exists in the caller's workspace
% and if it is not empty

b = evalin('caller',  ['exist(''' pname ''', ''var'') && ~isempty(' pname ')']) ;

1

Điều này ít nhiều được nâng lên từ hướng dẫn Matlab ; Tôi chỉ có kinh nghiệm vượt qua ...

function my_output = wave ( a, b, n, k, T, f, flag, varargin )
  optargin = numel(varargin);
  fTrue = inline('0');
  if optargin > 0
    fTrue = varargin{1};
  end
  % code ...
end

1
Có một vài lỗi trong mã mà tôi đã sửa. Đầu tiên, "optargin" cần được xác định. Thứ hai, "varargin" là một mảng ô thu thập tất cả các đầu vào tiếp theo, vì vậy bạn phải sử dụng lập chỉ mục mảng ô để loại bỏ các giá trị khỏi nó.
gnovice

Tôi cần phải kiểm tra tầm nhìn của tôi; Tôi thề tôi đã không thấy điều đó trong hướng dẫn sử dụng ngày hôm qua :(
kyle

@kyle: Đừng lo lắng, tất cả chúng ta đều mắc lỗi. Đó là lý do tại sao tôi thích phong cách wiki-ish của SO: nếu tôi mắc một lỗi đánh máy ngớ ngẩn, thường có người khác có thể bắt nó và sửa nó nhanh chóng cho tôi. =)
gnovice

1

Matlab không cung cấp một cơ chế cho việc này, nhưng bạn có thể xây dựng một cơ chế trong mã người dùng chặt chẽ hơn trình tự inputParser hoặc "if nargin <1 ...".

function varargout = getargs(args, defaults)
%GETARGS Parse function arguments, with defaults
%
% args is varargin from the caller. By convention, a [] means "use default".
% defaults (optional) is a cell vector of corresponding default values

if nargin < 2;  defaults = {}; end

varargout = cell(1, nargout);
for i = 1:nargout
    if numel(args) >= i && ~isequal(args{i}, [])
        varargout{i} = args{i};
    elseif numel(defaults) >= i
        varargout{i} = defaults{i};
    end
end

Sau đó, bạn có thể gọi nó trong các chức năng của bạn như thế này:

function y = foo(varargin)
%FOO 
%
% y = foo(a, b, c, d, e, f, g)

[a, b,  c,       d, e, f, g] = getargs(varargin,...
{1, 14, 'dfltc'});

Định dạng là một quy ước cho phép bạn đọc từ tên tham số đến giá trị mặc định của chúng. Bạn có thể mở rộng getargs () của mình bằng các đặc tả loại tham số tùy chọn (để phát hiện lỗi hoặc chuyển đổi ngầm định) và phạm vi đếm đối số.

Có hai nhược điểm của phương pháp này. Đầu tiên, nó chậm, vì vậy bạn không muốn sử dụng nó cho các chức năng được gọi là vòng lặp. Thứ hai, trợ giúp chức năng của Matlab - gợi ý tự động hoàn thành trên dòng lệnh - không hoạt động đối với các hàm varargin. Nhưng nó khá thuận tiện.


0

bạn có thể muốn sử dụng parseparamslệnh trong MATLAB; việc sử dụng sẽ như thế nào:

function output = wave(varargin);
% comments, etc
[reg, props] = parseparams(varargin);
ctrls = cell2struct(props(2:2:end),props(1:2:end),2);  %yes this is ugly!
a = reg{1};
b = reg{2};
%etc
fTrue = ctrl.fTrue;

0
function f(arg1, arg2, varargin)

arg3 = default3;
arg4 = default4;
% etc.

for ii = 1:length(varargin)/2
  if ~exist(varargin{2*ii-1})
    error(['unknown parameter: ' varargin{2*ii-1}]);
  end;
  eval([varargin{2*ii-1} '=' varargin{2*ii}]);
end;

ví dụ f(2,4,'c',3)làm cho tham số clà 3.


0

nếu bạn sử dụng quãng tám, bạn có thể làm như thế này - nhưng thật đáng buồn là MATLAB không hỗ trợ khả năng này

function hello (who = "World")
  printf ("Hello, %s!\n", who);
endfunction

(lấy từ tài liệu )


0

Tôi thích làm điều này theo một cách hướng đối tượng hơn. Trước khi gọi wave () lưu một số đối số của bạn trong một cấu trúc, ví dụ: một tham số được gọi là:

parameters.flag =42;
parameters.fTrue =1;
wave(a,b,n,k,T,f,parameters);

Sau đó, trong hàm sóng, kiểm tra xem các tham số cấu trúc có chứa trường gọi là 'cờ' hay không và nếu có, nếu giá trị của nó không trống. Sau đó gán cho nó rõ hơn một giá trị mặc định mà bạn xác định trước hoặc giá trị được cung cấp làm đối số trong tham số struct:

function output = wave(a,b,n,k,T,f,parameters)
  flagDefault=18;
  fTrueDefault=0;
  if (isfield(parameters,'flag') == 0 || isempty(parameters.flag)),flag=flagDefault;else flag=parameters.flag; end
  if (isfield(parameter,'fTrue') == 0 || isempty(parameters.fTrue)),fTrue=fTrueDefault;else fTrue=parameters.fTrue; end
  ...
end

Điều này giúp dễ dàng xử lý số lượng lớn các đối số, bởi vì nó không phụ thuộc vào thứ tự của các đối số đã cho. Điều đó nói rằng, nó cũng hữu ích nếu bạn phải thêm nhiều đối số sau này, vì bạn không phải thay đổi chữ ký hàm để làm như vậy.


Tại sao không tuân theo tiêu chuẩn MATLAB của các cặp tên-giá trị? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo

Đây chắc chắn là một tùy chọn là tốt, đặc biệt là khi người ta chỉ có một vài tham số. Tuy nhiên, gọi sóng () với số lượng lớn các cặp giá trị tên có thể làm giảm khả năng đọc của mã. Do đó tôi thường thích nhóm các đầu vào nhất định thành các cấu trúc.
CheshireCat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.