Stellen Sie elegant fest, ob mehr als ein Boolescher Wert „wahr“ ist.

77

Ich habe einen Satz von fünf booleschen Werten. Wenn mehr als eine davon wahr ist, möchte ich eine bestimmte Funktion ausführen. Was ist die eleganteste Art, wie Sie sich das vorstellen können, um diese Bedingung in einer einzigen if () - Anweisung zu überprüfen? Die Zielsprache ist C #, aber ich bin auch an Lösungen in anderen Sprachen interessiert (solange es sich nicht um bestimmte integrierte Funktionen handelt).

Eine interessante Option besteht darin, die Booleschen Werte in einem Byte zu speichern, eine Rechtsverschiebung durchzuführen und mit dem ursprünglichen Byte zu vergleichen. So etwas wie if(myByte && (myByte >> 1))Aber dies würde die Konvertierung der separaten Booleschen Werte in ein Byte erfordern (über ein BitArray?) Und das scheint ein bisschen (Wortspiel beabsichtigt) ungeschickt zu sein ... [Bearbeiten] Entschuldigung, das hätte sein sollen if(myByte & (myByte - 1)) [/ Bearbeiten]

Hinweis: Dies kommt natürlich dem klassischen Programmierproblem "Bevölkerungszahl", "Seitwärtsaddition" oder "Hamming-Gewicht" sehr nahe - ist aber nicht ganz dasselbe. Ich muss nicht wissen, wie viele der Bits gesetzt sind, nur wenn es mehr als eins sind. Ich hoffe, dass es einen viel einfacheren Weg gibt, dies zu erreichen.

Ola Tuvesson
quelle
Wie würde (myByte && (myByte >> 1)) helfen? Wenn myByte == 0x08 ist, wird nur ein Bit gesetzt, aber der Ausdruck wird als wahr ausgewertet. Wenn Sie (myByte & (myByte >> 1)) gemeint haben, schlägt dies fehl, wenn myByte == 0x0a zum Beispiel
Michael Burr
Stimmt etwas nicht mit Booleschen oder?
Martin York
1
Boolean oder gibt True zurück, wenn mindestens ein Wert True ist. Das OP möchte etwas, das True zurückgibt, wenn mehr als ein Wert True ist.
RobH
@ Michael Burr - Entschuldigung, das hätte sein sollen (myByte & (myByte - 1)). Löschen Sie das LSB und führen Sie ein bitweises UND aus, um festzustellen, ob mehr als ein Bit gesetzt wurde (false, wenn> 1). Wiederholen Sie diesen Vorgang für die Gesamtzahl der Bits, und Sie können die Anzahl der gesetzten Bits ermitteln. Nur eine Möglichkeit, einen Pop-Count durchzuführen.
Ola Tuvesson
Bitanzahl ist ein interessantes Rätsel: public static int BitCount (int x) {return ((x == 0)? 0: ((x <0)? 1: 0) + BitCount (x << = 1)); }
Charles Bretana

Antworten:

90

Wie wäre es mit

  if ((bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + 
      (bool4? 1:0) + (bool5? 1:0) > 1)
      // do something

oder eine verallgemeinerte Methode wäre ...

   public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    {
       int trueCnt = 0;
       foreach(bool b in bools)
          if (b && (++trueCnt > threshold)) 
              return true;
       return false;          
    } 

oder Verwenden von LINQ, wie in anderen Antworten vorgeschlagen:

    public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    { return bools.Count(b => b) > threshold; }

BEARBEITEN (um Joel Coehoorns Vorschlag hinzuzufügen: (in .Net 2.x und höher)

    public void ExceedsThreshold<T>(int threshold, 
                      Action<T> action, T parameter, 
                      IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(parameter); }

oder in .Net 3.5 und höher:

    public void ExceedsThreshold(int threshold, 
            Action action, IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(); }

oder als Erweiterung zu IEnumerable<bool>

  public static class IEnumerableExtensions
  {
      public static bool ExceedsThreshold<T> 
         (this IEnumerable<bool> bools, int threshold)
      { return bools.Count(b => b) > threshold; }
  }

Verwendung wäre dann:

  var bools = new [] {true, true, false, false, false, false, true};
  if (bools.ExceedsThreshold(3))
      // code to execute  ...
Charles Bretana
quelle
Um wirklich schlau zu sein, haben Sie einen Override, der auch eine Aktion akzeptiert.
Joel Coehoorn
2
Vorschlag - Unterbrechen Sie die Schleife, sobald der Schwellenwert erreicht ist.
Vilx
1
Sixletter-Variablen, was ist los mit dem ersten Beispiel. Ich denke, es ist einfach und erledigt den Job.
Kevin
@ Joel, was meinst du mit einer "Aktion", einem Delegaten der auszuführenden Methode, wenn die Bedingung wahr ist?
Charles Bretana
parms-> params. Sie können diese Funktion auch ändern, um frühzeitig zurückzukehren.
Mackenir
117

Ich wollte die Linq-Version schreiben, aber ungefähr fünf Leute haben mich geschlagen. Aber ich mag den params-Ansatz sehr, um zu vermeiden, dass ein Array manuell neu erstellt werden muss. Ich denke also, der beste Hybrid ist, basierend auf der Antwort von rp, wobei der Körper durch die offensichtliche Linqness ersetzt wird:

public static int Truth(params bool[] booleans)
{
    return booleans.Count(b => b);
}

Schön klar zu lesen und zu verwenden:

if (Truth(m, n, o, p, q) > 2)
Daniel Earwicker
quelle
1
Einverstanden, brillante Antwort. Für den Fall, dass jemand nach einer Lösung sucht, die zu einem allgemeinen Szenario passt, hat mich die Antwort von @DanielEarwicker dazu inspiriert : static int MatchCount<T>(Predicate<T> match, params T[] objs) { return objs.Count(obj => match(obj)); }. Um die Anzahl der positiven Werte zu zählen: double a, b, c, d; ... MatchCount(x => x > 0.0, a, b, c, d);Und so weiter ...
Anders Gustafsson
1
@AndersGustafsson Oder einfachnew[] { a, b, c, d }.Count(x => x > 0.0)
Daniel Earwicker
19

Es ist Zeit für die obligatorische LINQ-Antwort, die in diesem Fall eigentlich ganz ordentlich ist.

var bools = new[] { true, true, false, false, false };

return bools.Count(b => b == true) > 1;
Garry Shutler
quelle
7
Oder einfach zählen (b => b)
Daniel Earwicker
16

Ich würde sie nur in Ints und Summen umwandeln.

Wenn Sie sich nicht in einer sehr engen inneren Schleife befinden, hat dies den Vorteil, dass Sie leicht zu verstehen sind.

rekursiv
quelle
2
Jemand muss die Linq-Version hinzufügen: myBools.Cast <int> () .Sum ()!
Jennifer
2
@ Jennifer etwas spät dazu (!), Cast<int>().Sum()Wird aber leider eine Ausnahme auf eine Folge von Bools werfen. Obwohl Sie besetzen können bool-> int, können Sie nicht besetzen bool-> object-> int, was hier hinter den Kulissen passiert.
Daniel Earwicker
6

Ich würde eine Funktion schreiben, um eine beliebige Anzahl von Booleschen Werten zu erhalten. Es würde die Anzahl der Werte zurückgeben, die wahr sind. Überprüfen Sie das Ergebnis auf die Anzahl der Werte, die Sie benötigen, um etwas zu tun.

Arbeite härter, um es klar zu machen, nicht klug!

private int CountTrues( params bool[] booleans )
{
    int result = 0;
    foreach ( bool b in booleans )
    {
        if ( b ) result++;
    }

    return result;
}
rp.
quelle
5

Wenn Sie meinen, dass mehr als oder gleich einem Booleschen Wert gleich wahr ist, können Sie es so machen

if (bool1 || bool2 || bool3 || bool4 || bool5)

Wenn Sie mehr als einen (2 und mehr) Booleschen Wert benötigen, der gleich true ist, können Sie es versuchen

int counter = 0;
if (bool1) counter++;
if (bool2) counter++;
if (bool3) counter++;
if (bool4) counter++;
if (bool5) counter++;
if (counter >= 2) //More than 1 boolean is true
fehlerhaft
quelle
5

Wenn es Millionen statt nur 5 gäbe, könnten Sie Count () vermeiden und dies stattdessen tun ...

public static bool MoreThanOne (IEnumerable<bool> booleans)
{
    return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
}
Ian Mercer
quelle
Vorgeschlagene, präzisere Version davon: return booleans.Where(b => b).Skip(1).Any()Dies gilt auch für alle Fälle, in denen wir wissen möchten, ob mehr als N Mitglieder eine bestimmte Bedingung erfüllen.
Steve
5

Wenn Ihre Flaggen in einem Wort zusammengefasst sind, funktioniert die Lösung von Michael Burr . Die Schleife ist jedoch nicht erforderlich:

int moreThanOneBitSet( unsigned int v)
{
    return (v & (v - 1)) != 0;
}

Beispiel

 v (binary) | v - 1 | v&(v-1) | result
------------+-------+---------+--------
       0000 |  1111 |    0000 |  false
       0001 |  0000 |    0000 |  false
       0010 |  0001 |    0000 |  false
       0011 |  0010 |    0010 |   true
       .... |  .... |    .... |   ....
       1000 |  0111 |    0000 |  false
       1001 |  1000 |    1000 |   true
       1010 |  1001 |    1000 |   true
       1011 |  1010 |    1010 |   true
       1100 |  1011 |    1000 |   true
       1101 |  1100 |    1100 |   true
       1110 |  1101 |    1100 |   true
       1111 |  1110 |    1110 |   true
finnw
quelle
4

Kürzer und hässlicher als die Vilx-Version:

if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
etwas
quelle
1
Ja, es ist schrecklich, aber es funktioniert (ich habe alle Kombinationen überprüft).
einige
2

von oben ein kurzer Ansatz für dieses spezielle Beispiel; Sie können den Bool in ein int (0 oder 1) konvertieren. dann durch therm schleifen und addieren. Wenn das Ergebnis> = 2 ist, können Sie Ihre Funktion ausführen.

Sieger
quelle
2

Während ich LINQ mag, gibt es einige Löcher, wie dieses Problem.

Das Zählen ist im Allgemeinen in Ordnung, kann jedoch zu einem Problem werden, wenn das Berechnen / Abrufen der zu zählenden Elemente eine Weile dauert.

Die Any () - Erweiterungsmethode ist in Ordnung, wenn Sie nur nach einer suchen möchten, aber wenn Sie nach mindestens suchen möchten, gibt es keine eingebaute Funktion, die dies tut und faul ist.

Am Ende habe ich eine Funktion geschrieben, die true zurückgibt, wenn mindestens eine bestimmte Anzahl von Elementen in der Liste enthalten ist.

public static bool AtLeast<T>(this IEnumerable<T> source, int number)
{
    if (source == null)
        throw new ArgumentNullException("source");

    int count = 0;
    using (IEnumerator<T> data = source.GetEnumerator())
        while (count < number && data.MoveNext())
        {
            count++;
        }
    return count == number;
}

Benutzen:

var query = bools.Where(b => b).AtLeast(2);

Dies hat den Vorteil, dass nicht alle Elemente ausgewertet werden müssen, bevor ein Ergebnis zurückgegeben wird.

[Plug] Mein Projekt, NExtension, enthält AtLeast, AtMost und Überschreibungen, mit denen Sie das Prädikat mit dem AtLeast / Most-Check mischen können. [/Stecker]

Cameron MacFarland
quelle
1

Das Casting in Ints und Summieren sollte funktionieren, ist aber etwas hässlich und in einigen Sprachen möglicherweise nicht möglich.

Wie wäre es mit so etwas

int count = (bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + (bool4? 1:0) + (bool5? 1:0);

Oder wenn Sie sich nicht für Platz interessieren, können Sie einfach die Wahrheitstabelle vorberechnen und die Bools als Indizes verwenden:

if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
 ... do something ...
}
frankodwyer
quelle
1
ja, ich auch. lol. In einigen Fällen hilft Precomputing der Leistung - obwohl ich nicht denke, dass dies einer von ihnen ist :-)
Frankodwyer
1

Ich würde so etwas mit dem Argument params machen.

        public void YourFunction()
        {
            if(AtLeast2AreTrue(b1, b2, b3, b4, b5))
            {
                // do stuff
            }
        }

        private bool AtLeast2AreTrue(params bool[] values)
        {
            int trueCount = 0;
            for(int index = 0; index < values.Length || trueCount >= 2; index++)
            {
                if(values[index])
                    trueCount++;
            }

            return trueCount > 2;

        }
John Sonmez
quelle
Sie können diese if-Anweisung reduzieren auf: return trueCount> = 2
frankodwyer
Ich bin mir auch nicht sicher, ob dies besonders der Definition von "elegant" entspricht
stephenbayer
@frankodwyer Das stimmt, ich habe es geändert. Ich dachte, es könnte mit dem Wahren, Falschen besser lesbar sein, aber wenn man es noch einmal betrachtet, ist das andere definitiv besser.
John Sonmez
1
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

int NumberOfTrue(IEnumerable<bool> bools)
{
    return bools.Count(b => b);
}
AndreasN
quelle
1

Nicht gerade hübsch ... aber hier ist eine andere Möglichkeit:

if (
    (a && (b || c || d || e)) ||
    (b && (c || d || e)) ||
    (c && (d || e)) ||
    (d && e)
)
Vilx-
quelle
Was ist mit (a && (b || c || d || e)) || (b & (c || d || 3)) || (c && (d || e)) || (d && e)?
Stephenbayer
1
Schön ... ich könnte dies als Interviewfrage verwenden ... "Was macht das?" reinen Intellekt zu messen und "Was halten Sie davon als Lösung?" jeden auszusortieren, der es mochte.
ChrisA
Beeindruckend! Sprechen Sie über rohe Gewalt und blutige Unwissenheit! :-)
RobH
1

Ich habe jetzt eine viel viel bessere und sehr kurze!

bool[] bools = { b1, b2, b3, b4, b5 };
if (bools.Where(x => x).Count() > 1)
{
   //do stuff
}
John Sonmez
quelle
2
Einige Leute haben das bereits geschrieben, obwohl das Prädikat an Count übergeben wurde, anstatt das Wo zu benötigen.
Daniel Earwicker
1

Ich wollte eine Antwort auf eine variable C ++ 11-Vorlage geben.

template< typename T>
T countBool(T v)
{
    return v;
}

template< typename T, typename... Args>
int countBool(T first, Args... args)
{
    int boolCount = 0;
    if ( first )
        boolCount++;
    boolCount += countBool( args... );
    return boolCount;
}

Wenn Sie es einfach wie folgt aufrufen, erhalten Sie eine ziemlich elegante Methode zum Zählen der Anzahl der Bools.

if ( countBool( bool1, bool2, bool3 ) > 1 )
{
  ....
}
Scott Aron Bloom
quelle
0

In den meisten Sprachen entspricht true einem Wert ungleich Null, während false Null ist. Ich habe keine genaue Syntax für Sie, aber was ist mit Pseudocode?

if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
{
    //statements here
}
Bork Blatt
quelle
Nicht gültig in einer Sprache, in der true einem Wert ungleich Null entspricht. (Das Beispiel funktioniert nur, wenn 1 immer für true verwendet wird.)
RobH
0

Wenn Sie nur fünf verschiedene Werte haben, können Sie den Test einfach durchführen, indem Sie die Bits in ein kurzes oder ein int packen und prüfen, ob es sich um eine der Null- oder Ein-Bit-Antworten handelt. Die einzigen ungültigen Zahlen, die Sie bekommen könnten, wären ..

0x 0000 0000 
0x 0000 0001
0x 0000 0010
0x 0000 0100
0x 0000 1000
0x 0001 0000

Auf diese Weise können Sie nach sechs Werten suchen, diese in eine Nachschlagetabelle einfügen und wenn sie nicht vorhanden sind, haben Sie Ihre Antwort.

Dies gibt Ihnen eine einfache Antwort.

   public static boolean moreThan1BitSet (int b)
   {
      letzte kurze multiBitLookup [] = { 
            1, 1, 1, 0, 1, 0, 0, 0,
            1, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0,
            1, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0
      };
      if (multiBitLookup [b] == 1)
         falsch zurückgeben;
      return true;
   }}

Dies skaliert nicht weit über 8 Bit hinaus, aber Sie haben nur fünf.


quelle
0

if ((b1.CompareTo (false) + b2.CompareTo (false) + b3.CompareTo (false) + ...)> 1)

// Mehr als einer von ihnen ist wahr

...

sonst

...

Partha Choudhury
quelle
0

Du erwähntest

Eine interessante Option besteht darin, die Booleschen Werte in einem Byte zu speichern, eine Rechtsverschiebung durchzuführen und mit dem ursprünglichen Byte zu vergleichen. Etwas wieif (myByte && (myByte >> 1))

Ich glaube nicht, dass dieser Ausdruck das gewünschte Ergebnis liefert (zumindest unter Verwendung der C-Semantik, da der Ausdruck in C # nicht gültig ist):

Wenn (myByte == 0x08), dann gibt der Ausdruck true zurück, obwohl nur ein Bit gesetzt ist.

Wenn Sie " if (myByte & (myByte >> 1))" gemeint haben, dann, wenn (myByte == 0x0a)der Ausdruck false zurückgibt, obwohl 2 Bits gesetzt sind.

Aber hier sind einige Techniken zum Zählen der Anzahl von Bits in einem Wort:

Bit Twiddling Hacks - Bits zählen

Eine Variante, die Sie in Betracht ziehen könnten, ist die Verwendung der Kernighan-Zählmethode. Sie können jedoch frühzeitig aussteigen, da Sie nur wissen müssen, ob mehr als ein Bit gesetzt ist:

int moreThanOneBitSet( unsigned int v)
{
    unsigned int c; // c accumulates the total bits set in v

    for (c = 0; v && (c <= 1); c++)
    {
      v &= v - 1; // clear the least significant bit set
    }

    return (c > 1);
}

Natürlich ist die Verwendung einer Nachschlagetabelle auch keine schlechte Option.

Michael Burr
quelle
-1

Ich hatte kürzlich das gleiche Problem, bei dem ich drei boolesche Werte hatte, mit denen ich überprüfen musste, ob jeweils nur einer wahr war. Dafür habe ich den xor-Operator wie folgt verwendet:

bool a = true;
bool b = true;
bool c = false;

if (a || b || c)
{
    if (a ^ b ^ c){
        //Throw Error
    }
}

Dieser Code gibt einen Fehler aus, da a und b beide wahr sind.

Als Referenz: http://www.dotnetperls.com/xor

Ich habe den xor-Operator gerade erst in C # gefunden. Wenn jemand von Grubenstürzen dieser Strategie weiß, lassen Sie es mich bitte wissen.

dmoore1181
quelle