以下Python中的示例可能有助于说明“哨兵值”的含义:
SENTINEL = object()
def foo(opt=SENTINEL):
if opt is SENTINEL:
opt = make_me_fresh_default_opt_please()
# etc
在上面的代码片段中,变量SENTINEL
被初始化为一个全新的“nonce”对象,该对象在其定义的文件之外的任何其他位置都没有内容,也没有任何可想象的含义。 foo
是一个带有可选参数opt
的函数。 SENTINEL
常量foo
用于一件事,只有一件事:来测试没有参数调用的条件。这是通过if opt is SENTINEL
完成的。
要了解为什么人们会想要像SENTINEL
这样便宜的“随机数”常量,将上面的代码段与使用标准Python值(None
)的代码段进行对比,以测试无争论条件:
def foo2(opt=None):
if opt is None:
opt = make_me_fresh_default_opt_please()
# etc
此函数foo2
无法区分 1 下的两个调用:
foo2()
foo2(None)
所以,总而言之,“sentinel”在这里我的意思是一个代码的其余部分未知的常量值,其唯一目的是作为某些条件的布尔测试的基础。
在MATLAB中是否有一种类似的简单方法来生成这样的标记常量?
编辑:我想强调一点,我的问题严格关于如何创建哨兵,留下使用这种哨兵的目的问题。我提供了一个具体而合理的例子,说明为什么人们可能想要这样一个哨兵不变,部分是为了确保帖子的读者能够知道“哨兵”一词的含义。在这个例子中,sentinel 刚刚发生用于测试“无参数传递”条件,但是有许多其他可能的条件,人们可能想要使用sentinel值进行测试。所以,这个问题,重点是,不是关于测试“无参数通过”条件,而是关于生成标记常数的问题,一般来说。
EDIT2:基于Notlikethat和Bas Swinckels的回答:
function y = foo(varargin)
p = inputParser;
p.addParameter('Opt', @DEFAULT);
p.parse(varargin{:});
opt = p.Results.Opt;
if DEFAULT(opt)
opt = rand();
end
y = opt;
% etc
end
function its_me = DEFAULT(opt)
its_me = isequal(opt, @DEFAULT);
end
%{
>> foo('Opt', 3)
ans =
3
>> foo()
ans =
0.9134
>> foo
ans =
0.6324
%}
好的,这可能太可爱了。如果它更接近Notlikethat的原始建议(注意下面的make_sentinel
是一个独立的功能),代码既简单又清晰:
% foo.m
function y = foo(varargin)
DEFAULT = make_sentinel();
p = inputParser;
p.addParameter('Opt', DEFAULT);
p.parse(varargin{:});
opt = p.Results.Opt;
if isequal(opt, DEFAULT)
opt = rand();
end
y = opt;
% etc
end
% make_sentinel.m
function sentinel = make_sentinel()
sentinel = ...
@() 'qqqxyz_lalala_the_user_would_be_a_moron_to_use_this_function';
% or, more conventionally,
% sentinel = @() 0;
end
>> foo('Opt', 6)
ans =
6
>> foo()
ans =
0.9575
>> foo
ans =
0.9649
并且表明由make_sentinel
制作的哨兵确实是独一无二的(而且这种堕落不会付出代价):
>> foo('Opt', make_sentinel())
ans =
@()'qqqxyz_lalala_the_user_would_be_a_moron_to_use_this_function'
1 当然,之前定义的foo
无法区分foo()
和foo(SENTINEL)
,但是,AFAIC,后者的调用会降低在“反向编程”的范畴内(因为它要求调用代码“走出去”以颠覆被调用代码的意图),我不担心这种反常。相比之下,前面显示的foo2(None)
示例说明了在常规编程过程中可能很容易发生的情况,特别是当它的实际源代码类似于foo(some_variable)
和some_variable
时,无意中最终持有标准Python值None
。
答案 0 :(得分:3)
一个解决方案几乎就是直接的等价物:
function y = foo(opt)
%default argument
if nargin<1
opt = @SENTINEL;
end
%actual check
if isequal(opt, @SENTINEL)
opt = make_me_fresh_default_opt_please()
end
% etc
end
function SENTINEL % private function in the same file
end
不可否认,相当这么简单,但与其他任何内容一样,这与Matlab语法一样。
您可能也会作弊并将其基于完全采用相同的方法:
SENTINEL = java.lang.Object;
% etc...
为了更加简单,牺牲保证唯一性,tempname
是生成不太可能与有效数据冲突的字符串的简单方法(特别是如果有效数据不是字符串... )
答案 1 :(得分:1)
如果你想在函数调用中确定没有传递参数的“万无一失”的方法,那么使用nargin
是习惯性的MATLAB方法。
在函数体中,调用nargin
将返回传递给函数的参数个数。所以你可以写代码......
function foo(a, b)
switch nargin
case 0
% Handle the no-argument case...
case 1
% Handle the one-argument case...
otherwise
% All arguments supplied...
end
% Other code here...
end
有关详细信息,请参阅the MATLAB documentation for nargin。
答案 2 :(得分:1)
如果你真的想拥有一个独特的哨兵值,你可以创建自己的哨兵类。但我想在任何现实的上下文中,找到一些不能由任何函数或用户输入产生的标准数字/单元格/结构/字符串应该总是微不足道的。这可以像
一样简单SENTINEL = 'qqqxyz_lalala_the_user_would_be_a_moron_to_use_this_string';
使用此值的用户显然属于“反常编程”的定义。使用此函数可以测试变量是否是哨兵:
issentinel = @(var) ischar(var) && strcmp(var, SENTINEL)
还注意inputParser类,它允许您为缺少的参数定义默认值,这可能会减少对标记值的需求。