LINQ:ありませんvsすべてありません


272

多くの場合、提供された値がリスト内の値と一致するかどうかを確認したい(たとえば、検証するとき):

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

最近、ReSharperがこれらのクエリを簡略化して次のことを要求することに気づきました。

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

明らかに、これは論理的に同一で、おそらく少し読みやすくなります(多くの数学を実行した場合)、私の質問は次のとおりです。これはパフォーマンスに影響を与えますか?

それはあるべきように感じます(すなわち.Any()、それは短絡.All()するように聞こえますが、そうではないように聞こえます)が、私はこれを実証するものは何もありません。クエリが同じように解決されるかどうか、またはReSharperが私を迷わせているかどうかについて、より深い知識を持っている人はいますか?


6
Linqコードを分解して何をしているのか確認しましたか?
RQDQ 2012年

9
この場合、実際にはif(!acceptedValues.Contains(someValue))を使用しますが、もちろんこれは問題ではありませんでした:)
csgero

2
@csgero同意する。上記は、実際のロジックを単純化したものです(多分単純化しすぎたかもしれません)。
マーク

1
「あるべきように感じます(つまり、.Any()は短絡しているように聞こえますが、.All()はそうではないように聞こえます)」-音の直感を持つ人にとってはそうではありません。あなたが注意する論理的等価性は、それらが等しく短絡可能であることを意味します。少し考えてみると、適格でないケースが発生するとすぐにAllが終了する可能性があります。
ジムバルター2014年

3
私はこれについてReSharperに普遍的に同意しません。賢明な思考のトレインを書いてください。必要なアイテムがない場合に例外をスローする場合:if (!sequence.Any(v => v == true))。:あなたはすべてが特定の仕様に準拠している場合にのみ継続をご希望の場合if (sequence.All(v => v < 10))
ティモ2015

回答:


344

AllILSpyによる実装(実際に行ったところ、「まあ、その方法は少し似ています...」ではなく、影響についてではなく理論について話していたのでした)。

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

AnyILSpyによる実装:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return true;
        }
    }
    return false;
}

もちろん、生成されたILには微妙な違いがある可能性があります。しかし、いいえ、ありません。ILはほとんど同じですが、述語の一致でtrueを返すのと、述語の不一致でfalseを返すという明らかな逆の違いがあります。

もちろんこれはlinq-for-objectsだけです。他の一部のlinqプロバイダーが他のlinqプロバイダーをより適切に処理することは可能ですが、そうである場合、どちらがより最適な実装を得たかはかなりランダムです。

ルールif(determineSomethingTrue)は、に比べてシンプルで読みやすいと感じている人にのみ適用されるようですif(!determineSomethingFalse)。公平にif(!someTest)言えば、私たちが行動したい条件に対してtrueを返す同等の冗長性と複雑さの代替テストがある場合、私はしばしば混乱*するという点で少しポイントがあると思います。しかし、実際には、私が個人的に、あなたが与える2つの選択肢のどちらかを優先することは何もないので、述語がより複雑である場合は、前者にわずかに傾くでしょう。

*私が理解していないように混乱しないが、私が理解していない決定にいくつかの微妙な理由があることを心配しているように混乱している。そうすれば、このコードをもう一度見て何を待っていたのか...」


8
行の背後で何が行われているのかはわかりませんが、私にとっては、if(すべてではない)よりも(すべてではない)の場合の方がはるかに読みやすくなっています。
VikciaR

49
列挙に値がない場合は、大きな違いがあります。「すべて」は常にFALSEを返し、「すべて」は常にTRUEを返します。つまり、一方が他方の論理的同等物であると言うことは完全に真実ではありません!
Arnaud

44
@Arnaud Anyはを返すfalseため、!Anyを返すtrueので、それらは同じです。
Jon Hanna

11
@Arnaud AnyとAllが論理的に同等であるとコメントした人はいません。または別の言い方をすると、コメントしたすべての人は、AnyとAllは論理的に同等であるとは言いませんでした。同値は!Any(述語)とAll(!述語)の間です。
ジムバルター

7
@MacsDickinsonこれは、反対の述語を比較していないため、まったく違いはありません。これ!test.Any(x => x.Key == 3 && x.Value == 1)を使用するのAllと同等ですtest.All(x => !(x.Key == 3 && x.Value == 1))(これは実際にと同等ですtest.All(x => x.Key != 3 || x.Value != 1))。
Jon Hanna

55

これらの拡張メソッドにより、コードが読みやすくなる場合があります。

public static bool None<TSource>(this IEnumerable<TSource> source)
{
    return !source.Any();
}

public static bool None<TSource>(this IEnumerable<TSource> source, 
                                 Func<TSource, bool> predicate)
{
    return !source.Any(predicate);
}

元の代わりに

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

あなたは言えた

if (acceptedValues.None(v => v == someValue))
{
    // exception logic
}

6
おかげで、私はすでにこれらをコモンズライブラリに実装することを考えていましたが、それが良いアイデアであるかどうかはまだ決めていません。コードを読みやすくすることに同意しますが、十分な付加価値がないことを懸念しています。
Mark

2
「なし」を探しましたが見つかりませんでした。それは非常に読みやすいです。
Rhyous、2018

nullチェックを追加する必要がありました:return source == null || !source.Any(述語);
Rhyous、2018

27

結果を決定することができる後の両方は、両方のストップ列挙ので、同じ性能を有するであろう- Any()最初のアイテムに渡される述語評価するのにtrue及びAll()述語評価する最初の項目にしますfalse


21

All 最初の不一致での短絡のため、問題ありません。

微妙な分野の1つは、

 bool allEven = Enumerable.Empty<int>().All(i => i % 2 == 0); 

本当です。シーケンス内のすべてのアイテムが偶数です。

このメソッドの詳細については、Enumerable.Allのドキュメントを参照してください。


12
はい、bool allEven = !Enumerable.Empty<int>().Any(i => i % 2 != 0)でもそうです。
Jon Hanna

1
@ジョンは意味的になし!=すべて。したがって、意味的には、何もないか、すべてを持っていますが、.All()の場合、どれもがすべてに対してtrueを返すすべてのコレクションのサブセットにすぎません。そのAnthonyの+1
Rune FS

フォローしていない@RuneFS。意味論的および論理的に「それが真実ではないところは...」は、「それが真実であるところはすべて」と同じです。たとえば、「私たちの会社から受け入れられたプロジェクトはどこにもないのですか?」「他の会社からのすべての承認されたプロジェクトはどこですか?」と常に同じ答えになります...
Jon Hanna

...さて、「すべてのアイテムが...」であると仮定してバグが発生する可能性があるのは事実です。「すべてのアイテム...」から、テストを満たす少なくとも1つのアイテムであるアイテムが少なくとも1つあることを意味します。 "は空のセットには常に当てはまりますが、それについては異議はありません。「どの項目も...」と想定しても同じ問題が発生する可能性があることを追加しましたが、「1つの項目も...」も空のセットに対して常に当てはまるため、少なくとも1つの項目がテストを満たしていません。 。私がAnthonyのポイントに同意しないということではありません。それは、議論中の2つの構成のもう一方にも当てはまると思います。
Jon Hanna

@ジョンあなたは論理を話している、そして私は言語学を話している。人間の脳はネガティブを処理することはできません(ポジティブを処理する前にそれを取り消すことができます)ので、その意味では2つの間にかなりの違いがあります。それはあなたが提案する論理を不正確にしない
Rune FS

8

All()シーケンスのすべての要素が条件を満たすかどうかを決定します。
Any()シーケンスの要素が条件を満たすかどうかを決定します。

var numbers = new[]{1,2,3};

numbers.All(n => n % 2 == 0); // returns false
numbers.Any(n => n % 2 == 0); // returns true

7

このリンクによると

任意–少なくとも1つの一致をチェックします

すべて–すべてが一致することを確認します


1
あなたは正しいですが、彼らは与えられたコレクションに対して同時に停止します。条件が失敗するとすべてが中断し、述語と一致するとすべてが中断します。したがって、技術的にはシナリオを除いて違いはありません
WPFKK

6

他の回答が十分にカバーしているので、これはパフォーマンスに関するものではなく、明確さに関するものです。

両方のオプションが幅広くサポートされています。

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

しかし、これにより幅広いサポートが得られると思います。

var isValueAccepted = acceptedValues.Any(v => v == someValue);
if (!isValueAccepted)
{
    // exception logic
}

何かを否定する前にブール値を計算する(そして名前を付ける)だけで、これは私の頭の中ではっきりとわかります。


3

あなたが見て取れば可算ソース、あなたはの実装がわかりますAnyし、All非常に近いです。

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (!predicate(element)) return false;
    }
    return true;
}

唯一の違いはブール値の否定であるため、1つの方法が他の方法よりも大幅に高速になる方法はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.