Bagaimana cara menetapkan nilai default untuk parameter fungsi di Matlab?

127

Apakah mungkin untuk memiliki argumen default di Matlab? Misalnya, di sini:

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

Saya ingin solusi yang sebenarnya menjadi argumen opsional untuk fungsi gelombang. Jika memungkinkan, adakah yang bisa menunjukkan cara yang tepat untuk melakukan ini? Saat ini, saya mencoba apa yang saya posting di atas dan saya dapatkan:

??? 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.
Scott
sumber

Jawaban:

151

Tidak ada cara langsung untuk melakukan ini seperti yang telah Anda coba.

Pendekatan yang biasa adalah dengan menggunakan "varargs" dan memeriksa jumlah argumen. Sesuatu seperti:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Ada beberapa hal yang lebih menarik yang dapat Anda lakukan isempty, dll., Dan Anda mungkin ingin melihat Matlab pusat untuk beberapa paket yang menggabungkan hal-hal semacam ini.

Anda mungkin telah melihat varargin, nargchkdll Mereka fungsi yang berguna untuk hal semacam ini. varargs memungkinkan Anda untuk meninggalkan sejumlah argumen final, tetapi ini tidak membantu Anda mengatasi masalah nilai default untuk sebagian / semuanya.

simon
sumber
58

Saya telah menggunakan inputParserobjek untuk menangani pengaturan opsi default. Matlab tidak akan menerima format seperti python yang Anda tentukan dalam pertanyaan, tetapi Anda harus dapat memanggil fungsi seperti ini:

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

Setelah Anda mendefinisikan wavefungsi seperti ini:

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{:});

Sekarang nilai yang diteruskan ke fungsi tersedia melalui i_p.Results. Juga, saya tidak yakin bagaimana memvalidasi bahwa parameter yang dilewatkan ftruesebenarnya adalah inlinefungsi sehingga membiarkan validator kosong.

Mat
sumber
7
Seperti yang bisa saya katakan, ini adalah metode yang disukai. Ini bersih, mendokumentasikan sendiri (lebih dari sekumpulan if narginstatemens), mudah dirawat, kompak, dan fleksibel.
JnBrymn
19

Cara lain yang sedikit kurang mencurigakan adalah

function output = fun(input)
   if ~exist('input','var'), input='BlahBlahBlah'; end
   ...
end
Peter
sumber
Opsi ini tidak berfungsi jika Anda akan menggunakan MATLAB Coder untuk menghasilkan kode C, karena Coder tidak mendukung fungsi "ada".
Dave Tillman
10

Ya, mungkin sangat menyenangkan memiliki kemampuan untuk melakukan seperti yang Anda tulis. Tetapi itu tidak mungkin di MATLAB. Banyak utilitas saya yang memungkinkan default untuk argumen cenderung ditulis dengan pemeriksaan eksplisit di awal seperti ini:

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

Oke, jadi saya biasanya akan menerapkan pesan kesalahan yang lebih baik dan lebih deskriptif. Lihat bahwa tanda centang untuk variabel kosong memungkinkan pengguna untuk mengirimkan tanda kurung kosong, [], sebagai pengganti untuk variabel yang akan menggunakan nilai defaultnya. Penulis tetap harus menyediakan kode untuk mengganti argumen kosong itu dengan nilai defaultnya.

Utilitas saya yang lebih canggih, dengan parameter BANYAK, yang semuanya memiliki argumen default, akan sering menggunakan antarmuka pasangan properti / nilai untuk argumen default. Paradigma dasar ini terlihat pada perangkat grafik pegangan di matlab, serta dalam optimset, odeset, dll.

Sebagai sarana untuk bekerja dengan pasangan properti / nilai ini, Anda perlu belajar tentang varargin, sebagai cara memasukkan jumlah argumen yang sepenuhnya variabel ke suatu fungsi. Saya menulis (dan memposting) utilitas untuk bekerja dengan pasangan properti / nilai tersebut, parse_pv_pairs.m . Ini membantu Anda untuk mengubah pasangan properti / nilai menjadi struktur matlab. Ini juga memungkinkan Anda untuk memberikan nilai default untuk setiap parameter. Mengubah daftar parameter yang sulit menjadi struktur adalah cara yang SANGAT bagus untuk membagikannya di MATLAB.


sumber
7

Ini adalah cara sederhana saya untuk menetapkan nilai default ke suatu fungsi, menggunakan "coba":

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

Salam!

Jonay Cruz
sumber
3

Saya telah menemukan bahwa fungsi parseArgs bisa sangat membantu.

Tuan Fooz
sumber
3

Ada juga 'hack' yang dapat digunakan walaupun mungkin dihapus dari matlab di beberapa titik: Function eval sebenarnya menerima dua argumen yang mana yang kedua dijalankan jika kesalahan terjadi dengan yang pertama.

Jadi bisa kita gunakan

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

untuk menggunakan nilai 1 sebagai standar untuk argumen

vakko
sumber
3

Saya percaya saya menemukan cara yang cukup bagus untuk menangani masalah ini, hanya mengambil tiga baris kode (garis pembatas membungkus). Berikut ini diangkat langsung dari fungsi yang saya tulis, dan sepertinya berfungsi seperti yang diinginkan:

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

Saya pikir saya akan membagikannya.

Bonnevie
sumber
3

Saya bingung tidak ada yang menunjukkan posting blog ini oleh Loren, salah satu pengembang Matlab. Pendekatan ini didasarkan pada varargindan menghindari semua kasus yang tak berujung dan menyakitkan if-then-elseatau switchdengan kondisi yang berbelit-belit. Ketika ada beberapa nilai default, efeknya dramatis . Berikut ini contoh dari blog yang tertaut:

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{:};

Jika Anda masih belum mengerti, cobalah membaca seluruh posting blog oleh Loren. Saya telah menulis posting blog tindak lanjut yang berkaitan dengan nilai default posisi yang hilang . Maksud saya Anda dapat menulis sesuatu seperti:

somefun2Alt(a, b, '', 42)

dan masih memiliki nilai default epsuntuk tolparameter (dan @magicpanggilan balik untukfunc tentu saja ). Kode Loren memungkinkan ini dengan sedikit modifikasi tetapi rumit.

Akhirnya, hanya beberapa keuntungan dari pendekatan ini:

  1. Bahkan dengan banyak standar kode boilerplate tidak menjadi besar (sebagai lawan keluarga if-then-else pendekatan, yang lebih lama dengan setiap nilai default baru)
  2. Semua default ada di satu tempat. Jika ada yang perlu diubah, Anda hanya memiliki satu tempat untuk dilihat.

Masalahnya, ada kerugian juga. Ketika Anda mengetik fungsi di Matlab shell dan melupakan parameternya, Anda akan melihat vararginpetunjuk yang tidak membantu . Untuk mengatasinya, Anda disarankan untuk menulis klausa penggunaan yang bermakna.

alisianoi
sumber
Tautan ke posting blog tindak lanjut Anda rusak; bisakah kamu memperbaikinya?
Equaeghe
2

Setelah mengetahui ASSIGNIN (berkat jawaban ini oleh b3 ) dan EVALIN saya menulis dua fungsi untuk akhirnya mendapatkan struktur panggilan yang sangat sederhana:

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

Berikut daftarnya:

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

dan

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 ')']) ;
Tobias Kienzler
sumber
1

Ini kurang lebih diangkat dari manual Matlab ; Saya hanya punya pengalaman lewat ...

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
kyle
sumber
1
Ada beberapa kesalahan dalam kode yang saya koreksi. Pertama, "optargin" perlu didefinisikan. Kedua, "varargin" adalah array sel yang mengumpulkan semua input berikutnya, jadi Anda harus menggunakan pengindeksan array sel untuk menghapus nilai dari itu.
gnovice
Saya perlu memeriksa visi saya; Aku bersumpah aku tidak melihat itu di manual kemarin :(
kyle
@kyle: Jangan khawatir, kita semua melakukan kesalahan. Itu sebabnya saya suka gaya wiki-ish SO: jika saya membuat kesalahan ketik konyol, biasanya ada orang lain di sekitar yang bisa menangkapnya dan memperbaikinya dengan cepat untuk saya. =)
gnovice
1

Matlab tidak menyediakan mekanisme untuk ini, tetapi Anda dapat membangun satu di kode userland yang lebih pendek daripada inputParser atau urutan "jika 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

Maka Anda dapat menyebutnya di fungsi Anda seperti ini:

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'});

Format adalah konvensi yang memungkinkan Anda membaca dari nama parameter ke nilai standarnya. Anda dapat memperpanjang getargs Anda () dengan spesifikasi tipe parameter opsional (untuk deteksi kesalahan atau konversi implisit) dan rentang jumlah argumen.

Ada dua kelemahan dari pendekatan ini. Pertama, lambat, jadi Anda tidak ingin menggunakannya untuk fungsi yang dipanggil dalam loop. Kedua, bantuan fungsi Matlab - petunjuk pelengkapan otomatis pada baris perintah - tidak berfungsi untuk fungsi varargin. Tapi ini cukup nyaman.

Andrew Janke
sumber
0

Anda mungkin ingin menggunakan parseparamsperintah di matlab; penggunaannya akan terlihat seperti:

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;
shabbychef
sumber
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;

misalnya f(2,4,'c',3)menyebabkan parameter cmenjadi 3.

Tobias Kienzler
sumber
0

jika Anda akan menggunakan oktaf Anda bisa melakukannya seperti ini - tetapi sayangnya matlab tidak mendukung kemungkinan ini

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

(diambil dari doc )

wuschLOR
sumber
0

Saya suka melakukan ini dengan cara yang lebih berorientasi objek. Sebelum memanggil wave () simpan beberapa argumen Anda dalam suatu struct, mis. satu parameter yang disebut:

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

Di dalam fungsi gelombang kemudian periksa, apakah parameter struct berisi bidang yang disebut 'flag' dan jika demikian, jika nilainya tidak kosong. Kemudian tetapkan dengan nilai default yang Anda tentukan sebelumnya, atau nilai yang diberikan sebagai argumen di struct parameter:

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

Ini membuatnya lebih mudah untuk menangani sejumlah besar argumen, karena tidak tergantung pada urutan argumen yang diberikan. Yang mengatakan, itu juga membantu jika Anda harus menambahkan lebih banyak argumen nanti, karena Anda tidak perlu mengubah fungsi tanda tangan untuk melakukannya.

CheshireCat
sumber
Mengapa tidak mengikuti standar MATLAB dari pasangan nama-nilai? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo
Ini tentunya merupakan opsi juga, terutama ketika seseorang hanya memiliki beberapa parameter. Gelombang panggilan () dengan sejumlah besar pasangan nama-nilai, bagaimanapun, dapat mengurangi keterbacaan kode. Karena itu saya sering lebih suka mengelompokkan input tertentu ke dalam struct.
CheshireCat