18

関数ファイルを解析する以外に、matlab の関数の入力引数と出力引数の名前を取得する方法はありますか?

たとえば、次の関数ファイルがあるとします。

分割.m

function [value, remain] = divide(left, right)
     value = floor(left / right);
     remain = left / right - value;
end

関数の外側から、ここでは出力引数の配列を取得したいと思います。['value', 'remain']入力引数についても同様です['left', 'right']

matlabでこれを行う簡単な方法はありますか? 通常、Matlab はリフレクションをかなりうまくサポートしているようです。

背景を編集:

これの目的は、ユーザーが入力できるウィンドウに関数パラメーターを表示することです。私は一種の信号処理プログラムを書いており、これらの信号に対して操作を実行する関数はサブフォルダーに格納されています。ユーザーが選択できる各関数のリストと名前は既にありますが、一部の関数には追加の引数が必要です (たとえば、smooth 関数はウィンドウ サイズをパラメーターとして受け取る場合があります)。

現時点では、プログラムが見つけるサブフォルダーに新しい機能を追加でき、ユーザーはそれを選択して操作を実行できます。私が見逃しているのは、ユーザーが入力パラメーターと出力パラメーターを指定することです。ここで、関数の名前が見つからないというハードルにぶつかりました。

4

6 に答える 6

11

MATLAB は (metaパッケージを使用して) クラス メタデータに関する情報を取得する方法を提供しますが、これは通常の関数ではなく OOP クラスでのみ使用できます。

1 つの秘訣は、処理したい関数のソースを含むクラス定義をオンザフライで作成し、MATLAB にソース コードの解析を任せることです (ご想像のとおり、これはトリッキーになる可能性があります: 関数定義行複数行にわたる、実際の定義の前のコメントなど...)

したがって、あなたの場合に作成される一時ファイルは次のようになります。

classdef SomeTempClassName
    methods
        function [value, remain] = divide(left, right)
            %# ...
        end
    end
end

meta.class.fromNameその後、メタデータを解析するために渡すことができます...


これは、このハックの簡単な実装です。

function [inputNames,outputNames] = getArgNames(functionFile)
    %# get some random file name
    fname = tempname;
    [~,fname] = fileparts(fname);

    %# read input function content as string
    str = fileread(which(functionFile));

    %# build a class containing that function source, and write it to file
    fid = fopen([fname '.m'], 'w');
    fprintf(fid, 'classdef %s; methods;\n %s\n end; end', fname, str);
    fclose(fid);

    %# terminating function definition with an end statement is not
    %# always required, but now becomes required with classdef
    missingEndErrMsg = 'An END might be missing, possibly matching CLASSDEF.';
    c = checkcode([fname '.m']);     %# run mlint code analyzer on file
    if ismember(missingEndErrMsg,{c.message})
        % append "end" keyword to class file
        str = fileread([fname '.m']);
        fid = fopen([fname '.m'], 'w');
        fprintf(fid, '%s \n end', str);
        fclose(fid);
    end

    %# refresh path to force MATLAB to detect new class
    rehash

    %# introspection (deal with cases of nested/sub-function)
    m = meta.class.fromName(fname);
    idx = find(ismember({m.MethodList.Name},functionFile));
    inputNames = m.MethodList(idx).InputNames;
    outputNames = m.MethodList(idx).OutputNames;

    %# delete temp file when done
    delete([fname '.m'])
end

そして単に次のように実行します:

>> [in,out] = getArgNames('divide')
in = 
    'left'
    'right'
out = 
    'value'
    'remain'
于 2012-05-25T00:09:53.687 に答える
11

問題が、ファイル内のプライマリ関数の関数宣言行を解析する単純なケースに限定されている場合(つまり、ローカル関数ネストされた関数、または無名関数を処理しない場合)、抽出できます。いくつかの標準的な文字列操作と正規表現を使用して、ファイルに表示される入力引数と出力引数の名前。関数宣言行には標準の形式がありますが、次の理由により、いくつかのバリエーションを考慮する必要があります。

(ブロック コメントの説明が最も難しい部分であることがわかりました...)

get_arg_names上記のすべてを処理する関数をまとめました。関数ファイルへのパスを指定すると、入力パラメーター文字列と出力パラメーター文字列を含む 2 つの cell 配列 (存在しない場合は空の cell 配列) が返されます。変数の入力リストまたは出力リストを持つ関数は、変数名に対して、それぞれ単純に'varargin'またはをリストすることに注意してください。'varargout'関数は次のとおりです。

function [inputNames, outputNames] = get_arg_names(filePath)

    % Open the file:
    fid = fopen(filePath);

    % Skip leading comments and empty lines:
    defLine = '';
    while all(isspace(defLine))
        defLine = strip_comments(fgets(fid));
    end

    % Collect all lines if the definition is on multiple lines:
    index = strfind(defLine, '...');
    while ~isempty(index)
        defLine = [defLine(1:index-1) strip_comments(fgets(fid))];
        index = strfind(defLine, '...');
    end

    % Close the file:
    fclose(fid);

    % Create the regular expression to match:
    matchStr = '\s*function\s+';
    if any(defLine == '=')
        matchStr = strcat(matchStr, '\[?(?<outArgs>[\w, ]*)\]?\s*=\s*');
    end
    matchStr = strcat(matchStr, '\w+\s*\(?(?<inArgs>[\w, ]*)\)?');

    % Parse the definition line (case insensitive):
    argStruct = regexpi(defLine, matchStr, 'names');

    % Format the input argument names:
    if isfield(argStruct, 'inArgs') && ~isempty(argStruct.inArgs)
        inputNames = strtrim(textscan(argStruct.inArgs, '%s', ...
                                      'Delimiter', ','));
    else
        inputNames = {};
    end

    % Format the output argument names:
    if isfield(argStruct, 'outArgs') && ~isempty(argStruct.outArgs)
        outputNames = strtrim(textscan(argStruct.outArgs, '%s', ...
                                       'Delimiter', ','));
    else
        outputNames = {};
    end

% Nested functions:

    function str = strip_comments(str)
        if strcmp(strtrim(str), '%{')
            strip_comment_block;
            str = strip_comments(fgets(fid));
        else
            str = strtok([' ' str], '%');
        end
    end

    function strip_comment_block
        str = strtrim(fgets(fid));
        while ~strcmp(str, '%}')
            if strcmp(str, '%{')
                strip_comment_block;
            end
            str = strtrim(fgets(fid));
        end
    end

end
于 2012-05-24T21:43:01.263 に答える
3

これは、一般的な関数 (varargin などを考えてください) に対して行うのは非常に困難です (読む: 不可能です)。また、一般に、ドキュメントの形式として変数名に依存することは...あなたが望むものではないかもしれません。別のアプローチを提案します。

プログラムを制御するのはあなたなので、m ファイルだけでなく、追加情報を含むテーブル エントリを使用して各モジュールを指定するのはどうでしょうか。追加のパラメーター、関数自体を文書化し、オプションがブール値である場合に注釈を付け、それらをチェックボックスとして提示することができます。

さて、これをどこに置くか。メインの m-file 関数が構造体を返すようにすることをお勧めします。これは、モジュールの読み込みステップのようなもので、実際の作業を行うサブ関数 (またはネストされた関数) を指す関数ハンドルを使用します。これにより、保持したいと確信している単一ファイルの設定が保持され、モジュールの設定がより柔軟になります。

function module = divide_load()
    module.fn = @my_divide;
    module.name = 'Divide';
    module.description = 'Divide two signals';
    module.param(1).name = 'left';
    module.param(1).description = 'left signal';
    module.param(1).required_shape = 'columnvector';
    % Etc, etc.

    function [value, remain] = my_divide(left, right)
         value = floor(left / right);
         remain = left / right - value;
    end
end
于 2012-05-29T20:14:33.107 に答える
1

プログラミング言語からその内容に関する情報を取得できない場合 (「リフレクション」など)、その言語の外に出る必要があります。

別の投稿者は「正規表現」を提案しましたが、正規表現は文脈自由言語を解析できないため、実際のプログラムの解析に適用すると常に失敗します。

これを確実に行うには、解析ツリーにアクセスできる実際の M 言語パーサーが必要です。次に、これはかなり簡単です。

当社のDMS ソフトウェア リエンジニアリング ツールキットには、M 言語パーサーが用意されており、これを行うことができます。

于 2012-05-27T04:37:29.667 に答える
0

マップ コンテナーの使用を検討しましたか?

これらの線に沿って関数を書くことができます。. .

function [outMAP] = divide(inMAP)
     outMAP = containers.Map();
     outMAP('value') = floor(inMAP('left') / inMAP('right'));
     outMAP('remain') = inMAP('left') / inMAP('right') - outMAP('value');
end

...そして、このように呼びます...

inMAP  = containers.Map({'left', 'right'}, {4, 5});
outMAP = divide(inMAP);

...そして、次の構文を使用して変数名を調べるだけです...

>> keys(inMAP)

ans = 

    'left'    'right'
于 2012-05-24T22:11:27.650 に答える
-3

inputname(argnum) http://www.mathworks.com/help/techdoc/ref/inputname.html .

于 2012-05-03T13:49:37.090 に答える