ฉันจะตั้งค่าเริ่มต้นสำหรับพารามิเตอร์ฟังก์ชันใน Matlab ได้อย่างไร


127

เป็นไปได้ไหมที่จะมีอาร์กิวเมนต์เริ่มต้นใน Matlab ตัวอย่างเช่นที่นี่:

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

ฉันต้องการให้คำตอบที่แท้จริงเป็นอาร์กิวเมนต์เสริมสำหรับฟังก์ชัน wave ถ้าเป็นไปได้ใครสามารถอธิบายวิธีการที่เหมาะสมในการทำเช่นนี้ได้หรือไม่? ขณะนี้ฉันกำลังลองสิ่งที่ฉันโพสต์ไว้ด้านบนและฉันได้รับ:

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

คำตอบ:


152

ไม่มีวิธีโดยตรงในการทำเช่นนี้เหมือนที่คุณเคยลอง

วิธีการปกติคือการใช้ "varargs" และตรวจสอบกับจำนวนอาร์กิวเมนต์ สิ่งที่ต้องการ:

function f(arg1, arg2, arg3)

  if nargin < 3
    arg3 =   'some default'
  end

end

มีบางสิ่งที่น่าสนใจกว่านี้ที่คุณสามารถทำได้isemptyฯลฯ และคุณอาจต้องการดู Matlab central สำหรับแพ็คเกจบางอย่างที่รวมสิ่งเหล่านี้ไว้

คุณอาจมีลักษณะที่varargin, nargchkฯลฯ พวกเขากำลังทำงานที่เป็นประโยชน์สำหรับการจัดเรียงของสิ่งนี้ varargs อนุญาตให้คุณทิ้งอาร์กิวเมนต์สุดท้ายไว้เป็นจำนวนตัวแปร แต่สิ่งนี้ไม่ได้ช่วยคุณแก้ปัญหาค่าเริ่มต้นสำหรับบางส่วน / ทั้งหมด


58

ฉันใช้inputParserวัตถุเพื่อจัดการกับการตั้งค่าตัวเลือกเริ่มต้น Matlab จะไม่ยอมรับรูปแบบที่เหมือน python ที่คุณระบุในคำถาม แต่คุณควรจะเรียกใช้ฟังก์ชันนี้ได้:

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

หลังจากคุณกำหนดwaveฟังก์ชันดังนี้:

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

i_p.Resultsตอนนี้ค่าผ่านเข้าสู่ฟังก์ชั่นที่มีอยู่ผ่าน นอกจากนี้ฉันไม่แน่ใจว่าจะตรวจสอบได้อย่างไรว่าพารามิเตอร์ที่ส่งผ่านมาftrueนั้นเป็นinlineฟังก์ชันจริง ๆดังนั้นจึงปล่อยให้ตัวตรวจสอบว่างเปล่า


7
อย่างที่ดีที่สุดฉันสามารถบอกได้ว่านี่เป็นวิธีที่ต้องการ มันสะอาดจัดทำเอกสารด้วยตัวเอง (ยิ่งไปกว่านั้นคือกลุ่มของif narginstatemens) ดูแลรักษาง่ายกะทัดรัดและยืดหยุ่น
JnBrymn

19

อีกวิธีที่แฮ็กน้อยกว่าเล็กน้อยคือ

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

ตัวเลือกนี้ใช้ไม่ได้หากคุณจะใช้ MATLAB Coder เพื่อสร้างโค้ด C เนื่องจาก Coder ไม่รองรับฟังก์ชัน "มีอยู่"
Dave Tillman

10

ใช่มันอาจจะดีมากที่มีความสามารถในการทำตามที่คุณเขียนไว้ แต่ไม่สามารถทำได้ใน MATLAB ยูทิลิตี้จำนวนมากของฉันที่อนุญาตให้มีค่าเริ่มต้นสำหรับอาร์กิวเมนต์มักจะเขียนด้วยการตรวจสอบอย่างชัดเจนในตอนต้นเช่นนี้:

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

ตกลงดังนั้นโดยทั่วไปฉันจะใช้ข้อความแสดงข้อผิดพลาดที่ดีกว่าและอธิบายได้ดีกว่า ดูว่าการตรวจสอบตัวแปรว่างช่วยให้ผู้ใช้ส่งผ่านในวงเล็บคู่ว่าง [] เป็นตัวยึดสำหรับตัวแปรที่จะใช้กับค่าเริ่มต้น ผู้เขียนยังคงต้องจัดหารหัสเพื่อแทนที่อาร์กิวเมนต์ว่างนั้นด้วยค่าเริ่มต้น

โปรแกรมอรรถประโยชน์ของฉันที่ซับซ้อนกว่าด้วยพารามิเตอร์ MANY ซึ่งทั้งหมดนี้มีอาร์กิวเมนต์เริ่มต้นมักจะใช้อินเตอร์เฟสคู่คุณสมบัติ / ค่าสำหรับอาร์กิวเมนต์เริ่มต้น กระบวนทัศน์พื้นฐานนี้มีให้เห็นในเครื่องมือจับกราฟิกใน matlab เช่นเดียวกับใน optimset, odeset เป็นต้น

ในการทำงานกับคู่คุณสมบัติ / ค่าเหล่านี้คุณจะต้องเรียนรู้เกี่ยวกับ varargin ซึ่งเป็นวิธีการป้อนอาร์กิวเมนต์ที่แปรผันเต็มจำนวนให้กับฟังก์ชัน ผมเขียน (และโพสต์) ยูทิลิตี้ที่จะทำงานร่วมกับคุณสมบัติ / คู่ค่าดังกล่าวparse_pv_pairs.m ช่วยให้คุณสามารถแปลงคู่คุณสมบัติ / ค่าเป็นโครงสร้าง matlab นอกจากนี้ยังช่วยให้คุณสามารถระบุค่าเริ่มต้นสำหรับแต่ละพารามิเตอร์ การแปลงรายการพารามิเตอร์ที่ไม่สะดวกให้เป็นโครงสร้างเป็นวิธีที่ดีมากในการส่งผ่านพารามิเตอร์เหล่านี้ใน MATLAB


7

นี่เป็นวิธีง่ายๆของฉันในการตั้งค่าเริ่มต้นให้กับฟังก์ชันโดยใช้ "ลอง":

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

ความนับถือ!



3

นอกจากนี้ยังมี 'แฮ็ก' ที่สามารถใช้ได้แม้ว่าอาจจะถูกลบออกจาก matlab ในบางจุด: Function eval ยอมรับอาร์กิวเมนต์สองอาร์กิวเมนต์ซึ่งอันที่สองจะถูกเรียกใช้หากเกิดข้อผิดพลาดกับครั้งแรก

ดังนั้นเราจึงสามารถใช้

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

เพื่อใช้ค่า 1 เป็นค่าเริ่มต้นสำหรับอาร์กิวเมนต์


3

ฉันเชื่อว่าฉันพบวิธีที่ดีในการจัดการกับปัญหานี้โดยใช้รหัสเพียงสามบรรทัด (จำกัด การตัดบรรทัด) สิ่งต่อไปนี้ยกขึ้นโดยตรงจากฟังก์ชั่นที่ฉันกำลังเขียนและดูเหมือนว่าจะได้ผลตามที่ต้องการ:

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

แค่คิดว่าฉันจะแบ่งปัน


3

ฉันรู้สึกสับสนว่าไม่มีใครชี้โพสต์บล็อกนี้โดย Loren หนึ่งในผู้พัฒนาของ Matlab แนวทางนี้มีพื้นฐานมาจากvararginและหลีกเลี่ยงสิ่งที่ไม่มีที่สิ้นสุดและเจ็บปวดif-then-elseหรือswitchกรณีที่มีเงื่อนไขที่ซับซ้อน เมื่อมีไม่กี่ค่าเริ่มต้นผลเป็นอย่างมาก นี่คือตัวอย่างจากบล็อกที่เชื่อมโยง:

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

หากคุณยังไม่เข้าใจลองอ่านบล็อกโพสต์ทั้งหมดของลอเรน ฉันได้เขียนบล็อกโพสต์ติดตามซึ่งเกี่ยวข้องกับค่าเริ่มต้นของตำแหน่งที่ขาดหายไป ฉันหมายความว่าคุณสามารถเขียนสิ่งที่ชอบ:

somefun2Alt(a, b, '', 42)

และยังคงมีค่าเริ่มต้นepsสำหรับtolพารามิเตอร์ (และ@magicเรียกกลับสำหรับfuncแน่นอนว่าจะ ) รหัสของลอเรนอนุญาตให้มีการปรับเปลี่ยนเล็กน้อย แต่ยุ่งยาก

ในที่สุดข้อดีบางประการของแนวทางนี้:

  1. แม้จะมีค่าเริ่มต้นจำนวนมาก แต่รหัสสำเร็จรูปก็ไม่ได้ใหญ่โต (ตรงข้ามกับตระกูล if-then-elseวิธีการซึ่งใช้เวลานานขึ้นกับค่าเริ่มต้นใหม่แต่ละค่า)
  2. ค่าเริ่มต้นทั้งหมดอยู่ในที่เดียว หากจำเป็นต้องเปลี่ยนแปลงคุณมีเพียงที่เดียวที่จะดู

บอกเลยว่ามีข้อเสียเหมือนกัน เมื่อคุณพิมพ์ฟังก์ชันใน Matlab shell และลืมพารามิเตอร์คุณจะเห็นvararginคำใบ้ที่ไม่มีประโยชน์ ในการจัดการกับสิ่งนั้นคุณควรเขียนประโยคการใช้งานที่มีความหมาย


ลิงก์ไปยังบล็อกโพสต์ติดตามของคุณเสีย คุณซ่อมได้หรือไม่?
equaeghe

2

หลังจากรับรู้ASSIGNIN (ขอบคุณคำตอบนี้โดยb3 ) และEVALINฉันได้เขียนสองฟังก์ชันเพื่อให้ได้โครงสร้างการโทรที่ง่ายมากในที่สุด:

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

นี่คือรายชื่อ:

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

นี้จะมากหรือน้อยยกจากคู่มือ Matlab ; ฉันเพิ่งผ่านประสบการณ์มา ...

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
มีข้อผิดพลาดสองสามข้อในโค้ดที่ฉันแก้ไข ขั้นแรกต้องกำหนด "optargin" ประการที่สอง "varargin" คืออาร์เรย์ของเซลล์ที่รวบรวมอินพุตที่ตามมาทั้งหมดดังนั้นคุณต้องใช้การสร้างดัชนีอาร์เรย์ของเซลล์เพื่อลบค่าออกจากมัน
gnovice

ฉันจำเป็นต้องตรวจสายตา ฉันสาบานว่าฉันไม่เห็นสิ่งนั้นในคู่มือเมื่อวานนี้ :(
kyle

@kyle: ไม่ต้องกังวลเราทุกคนทำผิดพลาด นั่นเป็นเหตุผลที่ฉันชอบสไตล์ wiki-ish ของ SO: ถ้าฉันพิมพ์ผิด ๆ โง่ ๆ มักจะมีคนอื่นจับได้และแก้ไขให้ฉันได้อย่างรวดเร็ว =)
gnovice

1

Matlab ไม่ได้จัดเตรียมกลไกสำหรับสิ่งนี้ แต่คุณสามารถสร้างได้ในโค้ด userland ที่สั้นกว่า inputParser หรือลำดับ "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

จากนั้นคุณสามารถเรียกใช้ในฟังก์ชันของคุณดังนี้:

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

การจัดรูปแบบเป็นหลักการที่ช่วยให้คุณสามารถอ่านจากชื่อพารามิเตอร์ไปเป็นค่าเริ่มต้นได้ คุณสามารถขยาย getargs () ของคุณด้วยข้อกำหนดประเภทพารามิเตอร์ที่เป็นทางเลือก (สำหรับการตรวจจับข้อผิดพลาดหรือการแปลงโดยนัย) และช่วงจำนวนอาร์กิวเมนต์

แนวทางนี้มีข้อเสียอยู่สองประการ ประการแรกมันช้าดังนั้นคุณจึงไม่ต้องการใช้กับฟังก์ชันที่เรียกในลูป ประการที่สองความช่วยเหลือเกี่ยวกับฟังก์ชันของ Matlab - คำแนะนำการเติมข้อความอัตโนมัติในบรรทัดคำสั่ง - ไม่สามารถใช้ได้กับฟังก์ชัน varargin แต่มันค่อนข้างสะดวก


0

คุณอาจต้องการใช้parseparamsคำสั่งใน matlab การใช้งานจะมีลักษณะดังนี้:

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;

เช่นf(2,4,'c',3)ทำให้พารามิเตอร์cเป็น 3


0

หากคุณจะใช้อ็อกเทฟคุณสามารถทำได้เช่นนี้ แต่น่าเสียดายที่ matlab ไม่รองรับความเป็นไปได้นี้

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

(นำมาจากเอกสาร )


0

ฉันชอบทำแบบนี้ในเชิงวัตถุมากกว่า ก่อนที่จะเรียก wave () ให้บันทึกอาร์กิวเมนต์ของคุณไว้ในโครงสร้างเช่น พารามิเตอร์หนึ่งที่เรียกว่า:

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

ภายในฟังก์ชัน wave ให้ตรวจสอบว่าพารามิเตอร์ struct มีฟิลด์ที่เรียกว่า 'flag' หรือไม่หากเป็นเช่นนั้นหากค่าไม่ว่างเปล่า จากนั้นกำหนดให้เป็นค่าเริ่มต้นที่คุณกำหนดไว้ก่อนหน้านี้หรือค่าที่กำหนดเป็นอาร์กิวเมนต์ในโครงสร้างพารามิเตอร์:

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

ทำให้ง่ายต่อการจัดการอาร์กิวเมนต์จำนวนมากเนื่องจากไม่ได้ขึ้นอยู่กับลำดับของอาร์กิวเมนต์ที่กำหนด ที่กล่าวว่าจะมีประโยชน์เช่นกันหากคุณต้องเพิ่มอาร์กิวเมนต์เพิ่มเติมในภายหลังเนื่องจากคุณไม่จำเป็นต้องเปลี่ยนลายเซ็นฟังก์ชันเพื่อทำเช่นนั้น


ทำไมไม่ปฏิบัติตามมาตรฐาน MATLAB ของคู่ชื่อ - ค่า? wave(a,b,'flag',42,'fTrue',1)
Cris Luengo

นี่เป็นตัวเลือกอย่างแน่นอนเช่นกันโดยเฉพาะอย่างยิ่งเมื่อมีพารามิเตอร์เพียงไม่กี่ตัว อย่างไรก็ตามการเรียก wave () ที่มีคู่ชื่อ - ค่าจำนวนมากอาจลดความสามารถในการอ่านรหัส ฉันจึงมักชอบจัดกลุ่มปัจจัยการผลิตบางอย่างเป็นโครงสร้าง
CheshireCat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.