Matlab'da fonksiyon parametreleri için varsayılan değerleri nasıl ayarlarım?


127

Matlab'da varsayılan argümanlara sahip olmak mümkün mü? Örneğin, burada:

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

Gerçek çözümün dalga fonksiyonuna isteğe bağlı bir argüman olmasını istiyorum. Mümkünse, herhangi biri bunu yapmanın doğru yolunu gösterebilir mi? Şu anda yukarıda yazdıklarımı deniyorum ve şunu elde ediyorum:

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

Yanıtlar:


151

Bunu denediğiniz gibi yapmanın doğrudan bir yolu yok.

Genel yaklaşım, "varargs" kullanmak ve argüman sayısıyla karşılaştırmaktır. Gibi bir şey:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

Yapabileceğiniz daha güzel birkaç şey var isemptyve bu tür şeyleri bir araya getiren bazı paketler için Matlab Central'a bakmak isteyebilirsiniz.

Sen bir göz olabilir varargin, nargchkbu tür bir şey için, vb Bunlar ediyoruz kullanışlı fonksiyonlar. varargs, değişken sayıda son argüman bırakmanıza izin verir, ancak bu sizi bazıları / tümü için varsayılan değerler sorununu çözmez.


58

inputParserVarsayılan seçenekleri ayarlamak için nesneyi kullandım . Matlab, soruda belirttiğiniz python benzeri biçimi kabul etmeyecektir, ancak işlevi şu şekilde çağırabilmelisiniz:

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

waveİşlevi şu şekilde tanımladıktan sonra :

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

Artık işleve aktarılan değerler aracılığıyla kullanılabilir i_p.Results. Ayrıca, için iletilen parametrenin ftrueaslında bir inlineişlev olduğunu nasıl doğrulayacağımı bilmiyordum, bu nedenle doğrulayıcıyı boş bıraktım.


7
En iyi olarak ben, söyleyebilirim bu , tercih edilen bir yöntemdir. Temiz, kendi kendini belgeleyen (daha çok bir grup if narginstatemens), bakımı kolay, kompakt ve esnektir.
JnBrymn

19

Biraz daha az karmaşık bir yol da

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

C kodu oluşturmak için MATLAB Coder'ı kullanacaksanız, Coder "var" işlevini desteklemediğinden, bu seçenek çalışmaz.
Dave Tillman

10

Evet, yazdığınızı yapma yeteneğine sahip olmak gerçekten güzel olabilir. Ancak MATLAB'da bu mümkün değildir. Argümanlar için varsayılanlara izin veren yardımcı programların çoğu, başlangıçta aşağıdaki gibi açık denetimlerle yazılma eğilimindedir:

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

Tamam, bu yüzden genellikle daha iyi, daha açıklayıcı bir hata mesajı uygularım. Boş bir değişkeni kontrol etmenin, kullanıcının, varsayılan değerini alacak bir değişken için yer tutucusu olarak boş bir parantez çifti [] geçirmesine izin verdiğine bakın. Yazar, bu boş bağımsız değişkeni varsayılan değeriyle değiştirmek için yine de kodu sağlamalıdır.

Hepsi varsayılan bağımsız değişkenlere sahip olan MANY parametreli daha karmaşık olan yardımcı programlarım, genellikle varsayılan bağımsız değişkenler için bir özellik / değer çifti arabirimi kullanır. Bu temel paradigma, matlab'daki tutamaç grafik araçlarında ve ayrıca optimset, odeset vb.

Bu özellik / değer çiftleriyle çalışmanın bir yolu olarak, bir işleve tamamen değişken sayıda argüman girmenin bir yolu olarak varargin hakkında bilgi edinmeniz gerekecektir. Bu tür özellik / değer çiftleriyle çalışmak için bir yardımcı program yazdım (ve gönderdim), parse_pv_pairs.m . Özellik / değer çiftlerini bir matlab yapısına dönüştürmenize yardımcı olur. Ayrıca, her parametre için varsayılan değerler sağlamanıza olanak tanır. Hantal bir parametre listesini bir yapıya dönüştürmek, bunları MATLAB'da geçirmenin ÇOK güzel bir yoludur.


7

Bu, varsayılan değerleri bir işleve "dene" yi kullanarak ayarlamanın basit yoludur:

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

Saygılarımızla!



3

Bir noktada matlab'den kaldırılabilmesine rağmen kullanılabilecek bir 'hack' de vardır: Function eval aslında iki argümanı kabul eder, eğer ilkinde bir hata meydana gelirse ikincisi çalıştırılır.

Böylece kullanabiliriz

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

bağımsız değişken için varsayılan olarak 1 değerini kullanmak


3

Bu sorunu çözmenin oldukça şık bir yolunu bulduğuma inanıyorum, sadece üç satırlık kod alıyorum (satır kaydırmaları hariç). Aşağıdakiler doğrudan yazdığım bir işlevden kaldırıldı ve istenildiği gibi çalışıyor gibi görünüyor:

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

Paylaşacağımı düşündüm.


3

Matlab'ın geliştiricilerinden biri olan Loren'in bu blog gönderisine kimsenin dikkat çekmediği kafam karıştı . Yaklaşım, varargintüm bu sonsuz ve zahmetli if-then-elseveya switchkarmaşık koşullara sahip vakalara dayanır ve bunlardan kaçınır . Olduğunda birkaç varsayılan değerler, etkisidir dramatik . İşte bağlantılı blogdan bir örnek:

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

Hala anlamadıysanız, Loren'in blog gönderisinin tamamını okumayı deneyin. Eksik konumsal varsayılan değerlerle ilgilenen bir takip blog yazısı yazdım . Demek istediğim, şöyle bir şey yazabilirsin:

somefun2Alt(a, b, '', 42)

ve hala varsayılan sahip epsdeğerini tolparametre (ve @magiciçin geri aramafunc tabii ki ). Loren'in kodu, küçük ama zor bir değişiklikle buna izin verir.

Son olarak, bu yaklaşımın sadece birkaç avantajı:

  1. Birçok if-then-elsevarsayılan değerde bile, standart kod çok büyük olmaz ( her yeni varsayılan değerle daha uzun süren yaklaşım ailesinin aksine )
  2. Tüm varsayılanlar tek bir yerdedir. Bunlardan herhangi birinin değişmesi gerekiyorsa, bakmanız gereken tek bir yer var.

Trooth'a söylemek gerekirse, bir dezavantaj da var. Fonksiyonu Matlab kabuğuna yazıp parametrelerini unuttuğunuzda, vararginipucu olarak yardımcı olmayan bir şey göreceksiniz . Bununla başa çıkmak için anlamlı bir kullanım maddesi yazmanız önerilir.


Takip eden blog yayınınızın bağlantısı bozuk; tamir edebilir misin?
equaeghe

2

Farkında olduktan sonra ASSIGNIN (sayesinde bu cevap ile b3 ve) EVALIN Sonunda çok basit çağıran yapı elde etmek için iki işlev yazdı:

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

İşte liste:

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

ve

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

Bu, Matlab kılavuzundan aşağı yukarı kaldırılmıştır ; Sadece geçici bir tecrübem var ...

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
Düzelttiğim kodda birkaç hata vardı. İlk olarak "optargin" tanımlanmalıdır. İkincisi, "varargin" sonraki tüm girdileri toplayan bir hücre dizisidir, bu nedenle ondan değerleri kaldırmak için hücre dizisi indekslemesini kullanmanız gerekir.
gnovice

Görüşümü kontrol ettirmem gerekiyor; Yemin ederim dün kılavuzda bunlardan hiçbirini görmedim :(
kyle

@kyle: Endişelenme, hepimiz hata yaparız. Bu yüzden SO'nun wiki-ish stilini seviyorum: Eğer biraz aptalca yazım hatası yaparsam, genellikle etrafta onu yakalayıp benim için çabucak düzeltebilecek başka biri olur. =)
gnovice

1

Matlab bunun için bir mekanizma sağlamaz, ancak kullanıcı alanı kodunda inputParser'dan veya "if nargin <1 ..." dizilerinden daha ters bir kod oluşturabilirsiniz.

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

Daha sonra onu aşağıdaki gibi işlevlerinizde çağırabilirsiniz:

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

Biçimlendirme, parametre adlarından varsayılan değerlerine kadar okumanızı sağlayan bir kuraldır. Getargs () değerinizi isteğe bağlı parametre türü belirtimleri (hata algılama veya örtük dönüştürme için) ve bağımsız değişken sayısı aralıklarıyla genişletebilirsiniz.

Bu yaklaşımın iki dezavantajı vardır. Birincisi, yavaştır, bu yüzden onu döngülerde çağrılan işlevler için kullanmak istemezsiniz. İkinci olarak, Matlab'ın işlev yardımı - komut satırındaki otomatik tamamlama ipuçları - varargin işlevleri için çalışmaz. Ama oldukça uygun.


0

parseparamsmatlab'daki komutu kullanmak isteyebilirsiniz ; kullanım şöyle görünür:

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;

ör f(2,4,'c',3). parametrenin c3 olmasına neden olur .


0

oktav kullanırsanız, bunu şu şekilde yapabilirsiniz - ama ne yazık ki matlab bu olasılığı desteklemiyor

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

(alınan doc )


0

Bunu biraz daha nesne odaklı bir şekilde yapmayı seviyorum. Wave () işlevini çağırmadan önce argümanlarınızdan bazılarını bir yapı içinde kaydedin, örn. biri parametreler:

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

Daha sonra, wave işlevi içinde, struct parametrelerinin 'bayrak' adlı bir alan içerip içermediğini ve eğer öyleyse, değerinin boş olmadığını kontrol edin. Ardından, daha önce tanımladığınız varsayılan değeri veya parameter struct'ta argüman olarak verilen değeri daha sekiz atayın:

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

Bu, verilen argümanların sırasına bağlı olmadığı için çok sayıda argümanı işlemeyi kolaylaştırır. Bununla birlikte, daha sonra daha fazla argüman eklemeniz gerektiğinde de yararlıdır, çünkü bunu yapmak için işlev imzasını değiştirmeniz gerekmez.


Neden ad-değer çiftlerinin MATLAB standardını takip etmiyorsunuz? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo

Bu kesinlikle bir seçenektir, özellikle de yalnızca birkaç parametresi olduğunda. Wave () 'i çok sayıda ad-değer çiftiyle çağırmak kodun okunabilirliğini azaltabilir. Bu nedenle genellikle belirli girdileri yapılar halinde gruplandırmayı tercih ederim.
CheshireCat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.