10

短縮版:

の 4 番目の引数として渡された関数は、 の最初の引数をエンコードした仕様と一致しないaccumarray引数で呼び出されることがあります。accumarray

その結果、 への引数として使用される関数accumarrayは、事実上何が異常な状態であるかをテストする必要があります。

問題は、このような異常な状態を 1 式の無名関数でどのようにテストできるかということです。accumarrayそしてより一般的に:の文書化されていない動作に対して堅牢な無名関数をどのように書くことができますか?


完全版:

以下のコードは、今日の私の勤務時間のほとんどを占めていた問題を大幅に要約したものです。

最初のいくつかの定義:

idxs = [1:3 1:3 1:3]';

vals0 = [1   4 6   3 5 7   6 Inf 2]';
vals1 = [1 Inf 6   3 5 7   6   4 2]';

anon = @(x) max(x(~isinf(x)));

Notevals1は、要素 2 と 8 を交換することによって取得さvals0れます。「匿名」関数anonは、その入力の非無限要素の中で最大値を計算します。

これらの定義を考えると、以下の 2 つの呼び出し

accumarray(idxs, vals0, [], anon)
accumarray(idxs, vals1, [], anon)

2 番目の引数 ( vals0vs ) のみが異なる場合、とvals1の違いはへの呼び出しの 1 つに対する引数の値の順序にのみ影響し、この関数の結果はの順序に影響されないため、同じ結果が生成されます。その引数の要素。vals0vals1anon

結局のところ、これら 2 つの式の最初の式は正常に評価され、正しい結果1が生成されます。

>> accumarray(idxs, vals0, [], anon)
ans =
     6
     5
     7

ただし、2 つ目は次のように失敗します。

>> accumarray(idxs, vals1, [], anon)
Error using accumarray
The function '@(x)max(x(~isinf(x)))' returned a non-scalar value.

この問題をトラブルシューティングするために、私が思いつくことができたのは、別の関数を作成することだけでした (もちろん、「MATLAB の方法で」独自のファイルに)

function out = kluge(x)
    global ncalls;
    ncalls = ncalls + 1;
    y = ~isinf(x);
    if any(y)
        out = max(x(y));
    else
        {ncalls x}
        out = NaN;
    end
end

...そして以下を実行しました:

>> global ncalls;
>> ncalls = int8(0); accumarray(idxs, vals0, [], @kluge)
ans =
     6
     5
     7
>> ncalls = int8(0); accumarray(idxs, vals1, [], @kluge)
ans = 
    [2]    [Inf]

ans =
     6
     5
     7

上記の最後の呼び出しの出力からわかるaccumarrayように、コールバックへの 2 番目の呼び出しの引数klugeは array でした[Int]accumarrayこれは、文書化されている3のように動作していないことを疑う余地なく教えてくれます(idxs長さ 1 の配列がaccumarrayの関数引数に渡されないように指定されているため)。

実際、このテストや他のテストから、予想に反して、渡された関数が(= 3) 回accumarray以上呼び出されていることがわかりました。max(idxs)上記を含む式では、kluge5回呼び出されます。

ここでの問題は、 の関数引数が実際に呼び出される方法に依存できない場合accumarray、この関数引数を堅牢にする唯一の方法は、必要なチェックを実行するために多くの追加コードを含めることです。これにはほぼ確実に、関数に複数のステートメントが必要になるため、無名関数は除外されます。(たとえば、kluge上記の関数は よりも堅牢ですanonが、匿名関数に適合する方法がわかりません。) で匿名関数を使用できないとaccumarray、その有用性が大幅に低下します。

だから私の質問は:

への強力な引数となる無名関数を指定する方法は?accumarray


1この投稿に示されているすべての MATLAB 出力で、MATLAB の典型的なオーバー パディングから空白行を削除しました。
2その他のトラブルシューティングに関する提案があれば、コメントを歓迎します。この問題のトラブルシューティングは、本来よりもはるかに困難でした。
3特に、 「関数は次のように入力を処理します:」 という行の直後の項目番号 1 から 5 を参照してください。

4

2 に答える 2

7

簡潔な答え

この場合、の 4 番目の入力引数はaccumarrayanonすべての入力に対してスカラーを返さなければなりません。

長い答え (およびインデックスの並べ替えに関する議論)

インデックスがソートされたときの出力を考えてみましょう。

>> [idxsSorted,sortInds] = sort(idxs)
>> accumarray(idxsSorted, vals0(sortInds), [], anon)
ans =
     6
     5
     7
>> accumarray(idxsSorted, vals1(sortInds), [], anon)
ans =
     6
     5
     7

現在、これについてすべてのドキュメントに記載されているのは次のとおりです。

subs の添字がソートされていない場合、fun は入力データの値の順序に依存してはなりません。

これはトラブルとどのように関連していanonますか? ルイス・メンドが示唆したように、サブセット/サブアレイではなくanon、指定された値の完全なセットに対して呼び出されるように強制するため、これは手がかりです。idx


accumarrayインデックスと値のソートされていないリストに対してどのように機能するかを検討してください。

>> [idxs vals0 vals1]
ans =
     1     1     1
     2     4   Inf
     3     6     6
     1     3     3
     2     5     5
     3     7     7
     1     6     6
     2   Inf     4
     3     2     2

vals0との両方について、vals1は2 に等しいInf集合に属します。はソートされていないため、最初は のすべての値を一度に処理しません。実際のアルゴリズム (実装) は不透明ですが、最初の引数の各単一値ブロックを処理して、ソートされていると仮定することから始まるようです。これは、4 番目の入力引数による関数参照である にブレークポイントを設定することで確認できます。2回目の 1 inに遭遇すると、最初からやり直すように見えますが、その後の への呼び出しには、特定のインデックスのすべての値が含まれます。おそらく完全にセグメント化するための何らかの実装を呼び出しますidxsidxsidxs=2idxsfunidxsfunaccumarrayuniqueidxs(ちなみに、順序は保持されません)。kjo が示唆するように、これはドキュメントに記載されているようaccumarray に実際に入力を処理するポイントであり、ここの手順 1 ~ 5に従います(「一意のインデックスがいくつあるかを調べる...」)。その結果、 forが呼び出されvals1たときにクラッシュしますが、 for ではなく、最初の試行で呼び出されます。anon(Inf)vals0anon(4)

Infただし、最初にこれらの手順を正確に実行したとしても、値の完全な部分配列にs だけが含まれている場合 ( も空の行列を返すことを考慮してanon([Inf Inf Inf]ください) 、必ずしも堅牢であるとは限りません。控えめではありますが、スカラーを返さfun なければならないという要件があります。ドキュメントから明らかでないのは、アルゴリズムの高レベルの説明に基づいて期待されるものだけでなく、入力に対してスカラーを返さなければならないことです。


回避策:

anon = @(x) max([x(~isinf(x));-Inf]);
于 2014-02-10T23:13:40.653 に答える