web-dev-qa-db-ja.com

フラグ変数を使用して配列内のMAX要素を検索するのは悪い考えですか?

Msftがlinq Max()拡​​張メソッドの実装で行っているように、プログラミングのキャリアで、最初の比較が行われたことを示すフラグ変数を導入する癖をつけました

_public static int Max(this IEnumerable<int> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    int num = 0;
    bool flag = false;
    foreach (int num2 in source)
    {
        if (flag)
        {
            if (num2 > num)
            {
                num = num2;
            }
        }
        else
        {
            num = num2;
            flag = true;
        }
    }
    if (!flag)
    {
        throw Error.NoElements();
    }
    return num;
}
_

しかし、最近、最初の要素から始めて結果に割り当てることでこれを実装する異端者に会いました。STLとJavaの作成者は、後者の方法を好むことがわかりました。 。

Java:

_public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
Iterator<? extends T> i = coll.iterator();
T candidate = i.next();

    while (i.hasNext()) {
    T next = i.next();
    if (next.compareTo(candidate) > 0)
    candidate = next;
}
return candidate;
}
_

STL:

_template<class _FwdIt> inline
_FwdIt _Max_element(_FwdIt _First, _FwdIt _Last)
{   // find largest element, using operator<
_FwdIt _Found = _First;
if (_First != _Last)
    for (; ++_First != _Last; )
        if (_DEBUG_LT(*_Found, *_First))
            _Found = _First;
return (_Found);
}
_

ある方法と別の方法の間に好みはありますか?これには歴史的な理由がありますか?ある方法は別の方法よりも危険ですか?

[〜#〜] update [〜#〜]:JavaバージョンはT candidate = i.next(); andで例外をスローできることに注意してください@ratchetフリークの回答の2番目の部分に示されているように、guard句を使用してC#で同じ動作を得ることができます。

6
Boris Treukhov

完全に過激になるには、次のことができます。

public static int Max(this IEnumerable<int> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }

    int num = int.MinValue;
    foreach (int num2 in source)
    {
        if (num2 > num)
        {
            num = num2;
        }
    }
    return num;
}

空のセットの最大値をint.Minvalueに定義することによって

しかし、c#でJava/stlアプローチを行うことは可能です

public static int Max(this IEnumerable<int> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if(!source.MoveNext()){
        throw throw Error.NoElements();
    }
    int num = source.Current;
    foreach (int num2 in source)
    {
        if (num2 > num)
        {
            num = num2;
        }
    }
    return num;
}

yMMVがより明確かどうかにかかわらず

2
ratchet freak

どちらか一方の好みがあるかもしれません。ただし、ここでの重要な違いの1つは、セマンティクスにおいてメソッドが同等ではないことです。特に空のリストの処理で。

LINQバージョンは値を返す必要があるため、リストが空の場合は例外がスローされます。

一方、C++バージョンはイテレータを返します。そのイテレータはコレクションのend()を指している可能性があるため、結果を使用する前に、呼び出しが非終了かどうかを確認する必要があります。

したがって、ここでの答えは、実装ではなく、関数の使用方法に関するものです。 危険見る人の目にあります。例外とリターンおよび期待は、それぞれの実装哲学に適切である可能性があります。

5
sdg

個人的には、常にある方法と別の方法で行うパターンを開発したことがありません。私にとっては、他のすべてが一定であるとすると、コードの行数とローカル変数が増えると、コードの複雑さが増すという事実に常に要約されます。少しずつではなく、少しずつ、ウォータークーラーの隣で議論される機能に行き着きます。

したがって、明確さを犠牲にすることなくコードを縮小できることを考えると、ローカル変数の使用と条件文の使用を可能な限り最小限に抑えるアプローチを常に選択します。しかし、明快さは常に追加のローカル変数の使用に勝ちます。

あなたが提供した例では、おそらくフラグを使用せずにコーディングしますが、結局それは個人的なスタイルになり、どちらかといえば間違っているとは言えません。

1
DXM