“varargin”函数在仅提供名称 - 值对时给出错误:MATLAB

时间:2017-08-14 08:07:59

标签: matlab function parsing

我正在尝试构建自己的MATLAB dir函数版本。我当前的代码(下面)几乎可以工作,但我在解析某个输入组合时遇到了问题。

我希望它能做到这一点:

  • 不要列出隐藏文件夹。
  • 有一个布尔参数(默认为false)只返回目录名而不是文件。
  • 为文件夹的路径指定一个可选字段(默认为当前目录)。

为了更清楚,我想创建可以处理这些组合的函数dir2

  1. dir2 这应该列出当前目录中的每个非隐藏文件或文件夹
  2. dir2('path_to_directory') 这应该列出指定目录中的每个未隐藏文件或文件夹
  3. dir2('OnlyDirectories', true) 这应该只列出当前目录中的非隐藏文件夹
  4. dir2('path_to_directory', 'OnlyDirectories', true) 这应该只列出指定目录中的非隐藏文件夹
  5. 我目前的版本是:

    function list = dir2(varargin)
        p = inputParser;
    
        addOptional(p, 'name', '.', @ischar);
        addParameter(p, 'OnlyDirectories', false, @islogical);
        parse(p, varargin{:});
    
        list = dir(p.Results.name);
        if p.Results.OnlyDirectories
            dirFlags = [list.isdir];
            list = list(dirFlags); % Keeping only directories
        end
        % Deleting hidden folders from the list
        list = list(arrayfun(@(x) ~strcmp(x.name(1),'.'), list)); 
    end
    

    这适用于 1 2 4 的情况,但对于 3 的情况不起作用。在这种情况下,它给了我错误:

      

    预期参数名称的字符串标量或字符向量,而输入类型为“逻辑”。

    我想我可能会遗漏一些关于MATLAB输入解析的小事,但我无法弄清楚是什么。

2 个答案:

答案 0 :(得分:2)

你是对的,解析器似乎给出了一些奇怪的结果,相关的问题可能是this one

对于您当前形式的函数,一种解决方法是添加检查是否给出了2个输入。如果有2个输入,则假设它是您的OnlyDirectories标志并使用默认的name值。代码看起来像这样,并传递了所有4个示例用例。

function list = dir2(varargin)
    p = inputParser;
    addOptional(p, 'name', '.', @ischar);
    addParameter(p, 'OnlyDirectories', false, @islogical);
    if numel(varargin) == 2 
        varargin = [{'.'}, varargin]; 
    end
    parse(p, varargin{:});
    list = dir(p.Results.name);
    if p.Results.OnlyDirectories
        dirFlags = [list.isdir];
        list = list(dirFlags); 
    end
    list = list(arrayfun(@(x) ~strcmp(x.name(1),'.'), list)); 
end

这个虽然有点hacky,并且有可能给出令人困惑的错误消息。将这两个输入作为名称 - 值对

会更好
function list = dir2(varargin)
    p = inputParser;
    addParameter(p, 'name', '.', @ischar);
    addParameter(p, 'OnlyDirectories', false, @islogical);
    % ... other code
end

使用:dir2('name', 'C:/Folder/MyStuff/', 'OnlyDirectories', true)

答案 1 :(得分:0)

您的问题是validation functionoptional argument不够具体。如你所知,它只是检查它是character array。当您只传递'OnlyDirectories', true作为第三种情况的参数时,字符串'OnlyDirectories'会传递您的验证函数,并用作参数'name'的值。然后您会收到错误,因为剩下的参数是逻辑值而不是参数名称字符串。

使用它作为更严格的验证功能对我有用:

addOptional(p, 'name', '.', @(d) (exist(d, 'dir') == 7));

这使用exist来检查可选参数是否是有效文件夹。对于第三种情况,字符串'OnlyDirectories'未传递可选参数'name'的验证函数,因此使用默认值并传递字符串以检查下一个参数。

修改

为了澄清,固有的问题是你想要包含一个字符串的可选参数,参数值对总是以一个参数名开头字符串也是如此。在这种情况下存在一些(可能不可避免的)歧义,inputParser可以清楚地区分可选字符串与后续参数值对的参数名称的唯一方法是通过您提供的验证功能。如果无法明确区分它们,则应使用required argumentsparameter-value pairs。请注意,如果您的可选参数不是character array or string,那么这不会有问题。

但是,如果你开始使用一个可选参数,我上面提供的一个更具体的验证函数的例子只是一种方法。还有其他选择可能更符合您的喜好。您可以检查可选参数字符串是否不是函数的任何参数名称:

addOptional(p, 'name', '.', @(d) ~ismember(d, {'OnlyDirectories', ...}));

这有一个限制,你永远不能搜索一个输入参数名称的文件夹(无论你选择哪种解决方案,你可能不得不接受歧义)。

还有一个解决方案允许你使用像'path_to_folder/*.csv'这样的可选参数(即通配符指示符和文件路径):

addOptional(p, 'name', '.', @name_check);
...

function isValid = name_check(d)
  try
    [dirPath, fileName, fileExt] = fileparts(d);
    isValid = ismember('*', d) ...  % A wildcard, not allowed in parameter names
              || (isempty(fileExt) && (exist(d, 'dir') == 7)) ...      % A folder path
              || (~isempty(fileExt) && (exist(dirPath, 'dir') == 7));  % A file path
  catch
    isValid = false;  % Any failure of the above indicates an invalid argument
  end
end