하나 이상의 부울이“진실”인지 우아하게 결정하십시오.
-
22-08-2019 - |
문제
5 개의 부울 값이 있습니다. 이 중 하나 이상이 사실이라면 특정 기능을 발굴하고 싶습니다. 단일 if () 문서 에서이 조건을 확인할 수있는 가장 우아한 방법은 무엇입니까? 대상 언어는 C#이지만 다른 언어의 솔루션에도 관심이 있습니다 (특정 내장 기능에 대해 이야기하지 않는 한).
흥미로운 옵션 중 하나는 부울을 바이트에 보관하고 올바른 교대를하고 원래 바이트와 비교하는 것입니다. 같은 것 if(myByte && (myByte >> 1))
그러나 이것은 별도의 부울을 바이트 (비트 array를 통해?)로 변환해야하며 약간 (말장난 의도) 서투른 것 같습니다 ... 편집] 죄송합니다 if(myByte & (myByte - 1))
/편집하다
참고 : 이것은 물론 고전적인 "모집단 수", "옆으로 첨가"또는 "무게"프로그래밍 문제에 매우 가깝습니다. 비트가 하나 이상인 경우에만 얼마나 많은 비트가 설정되어 있는지 알 필요가 없습니다. 내 희망은 이것을 달성하는 훨씬 간단한 방법이 있기를 희망합니다.
해결책
어때
if ((bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) +
(bool4? 1:0) + (bool5? 1:0) > 1)
// do something
또는 일반화 된 방법은 ...
public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
{
int trueCnt = 0;
foreach(bool b in bools)
if (b && (++trueCnt > threshold))
return true;
return false;
}
또는 다른 답변에서 제안한대로 LINQ 사용 :
public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
{ return bools.Count(b => b) > threshold; }
편집 (Joel Coehoorn 제안 추가 : (.NET 2.X 이상)
public void ExceedsThreshold<T>(int threshold,
Action<T> action, T parameter,
IEnumerable<bool> bools)
{ if (ExceedsThreshold(threshold, bools)) action(parameter); }
또는 .NET 3.5 이상에서 :
public void ExceedsThreshold(int threshold,
Action action, IEnumerable<bool> bools)
{ if (ExceedsThreshold(threshold, bools)) action(); }
또는 연장으로 IEnumerable<bool>
public static class IEnumerableExtensions
{
public static bool ExceedsThreshold<T>
(this IEnumerable<bool> bools, int threshold)
{ return bools.Count(b => b) > threshold; }
}
그러면 사용법은 다음과 같습니다.
var bools = new [] {true, true, false, false, false, false, true};
if (bools.ExceedsThreshold(3))
// code to execute ...
다른 팁
나는 LINQ 버전을 작성하려고했지만 5 명 정도의 사람들이 저를 이겼습니다. 그러나 나는 배열을 수동으로 새로 새로워지는 것을 피하기 위해 Params 접근법을 정말로 좋아합니다. 따라서 가장 좋은 하이브리드는 신체에 대한 RP의 답변을 기반으로 한 명백한 linqness로 대체된다고 생각합니다.
public static int Truth(params bool[] booleans)
{
return booleans.Count(b => b);
}
아름답게 명확하게 읽고 사용합니다.
if (Truth(m, n, o, p, q) > 2)
이 경우 실제로는 깔끔한 의무적 인 LINQ 답변을위한 시간입니다.
var bools = new[] { true, true, false, false, false };
return bools.Count(b => b == true) > 1;
나는 단지 그들을 INT와 합으로 던질 것입니다.
당신이 매우 타이트한 내부 루프에 있지 않으면 이해하기 쉬운 이점이 있습니다.
부울 값을 여러 번 받기 위해 함수를 작성했습니다. 사실 인 값의 수를 반환합니다. 무언가를하기 위해 긍정적이어야하는 값의 수를 결과를 확인하십시오.
영리하지 않고 명확하게하기 위해 더 열심히 노력하십시오!
private int CountTrues( params bool[] booleans )
{
int result = 0;
foreach ( bool b in booleans )
{
if ( b ) result++;
}
return result;
}
당신이 하나 이상의 부울 이상을 의미한다면, 진실과 동등하다면, 당신은 그렇게 할 수 있습니다.
if (bool1 || bool2 || bool3 || bool4 || bool5)
True와 동일한 2 개 이상의 부울이 필요한 경우 시도 할 수 있습니다.
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
단지 5 대신 수백만 명이 있으면 count ()를 피하고 대신 이것을 할 수 있습니다 ...
public static bool MoreThanOne (IEnumerable<bool> booleans)
{
return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
}
깃발이 한 단어로 포장된다면 마이클 버의 솔루션 작동합니다. 그러나 루프는 필요하지 않습니다.
int moreThanOneBitSet( unsigned int v)
{
return (v & (v - 1)) != 0;
}
예시
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
VILX-S 버전보다 짧고 추악합니다.
if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
내 머리 꼭대기 에서이 특정 예에 대한 빠른 접근 방식; bool을 int (0 또는 1)로 변환 할 수 있습니다. 그런 다음 열을 루프하고 추가하십시오. 결과가> = 2 인 경우 기능을 실행할 수 있습니다.
나는 LINQ를 좋아하지만이 문제와 같은 구멍이 있습니다.
계산하는 것은 일반적으로 괜찮지 만 계산 항목이 계산/검색하는 데 시간이 걸릴 때 문제가 될 수 있습니다.
어떤 () 확장 방법은 확인하고 싶다면 괜찮습니다. 그러나 적어도 확인하려면 내장 기능이없고 게으른 기능이 없습니다.
결국, 나는 목록에 적어도 특정 수의 항목이 있으면 True를 반환하는 기능을 작성했습니다.
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;
}
사용:
var query = bools.Where(b => b).AtLeast(2);
결과를 반환하기 전에 모든 항목을 평가할 필요가 없다는 이점이 있습니다.
플러그] 내 프로젝트, 발신 적어도, 최대 및 재정의를 포함하여 술어를 적어도/대부분의 수표와 혼합 할 수 있습니다. [/플러그
ints에 캐스팅하고 합산하는 것이 효과가 있어야하지만 약간 추악하고 일부 언어로는 불가능할 수 있습니다.
어떤 것 같아요
int count = (bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + (bool4? 1:0) + (bool5? 1:0);
또는 공간을 신경 쓰지 않으면 진실 테이블을 사전 컴퓨팅하고 Bools를 지수로 사용할 수 있습니다.
if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
... do something ...
}
나는 매개 변수 인수를 사용하여 이와 같은 일을 할 것입니다.
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;
}
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
// do stuff
}
int NumberOfTrue(IEnumerable<bool> bools)
{
return bools.Count(b => b);
}
정확히 예쁘지는 않지만 여기에 또 다른 방법이 있습니다.
if (
(a && (b || c || d || e)) ||
(b && (c || d || e)) ||
(c && (d || e)) ||
(d && e)
)
나는 지금 훨씬 더 나은 것을 가지고 있습니다.
bool[] bools = { b1, b2, b3, b4, b5 };
if (bools.Where(x => x).Count() > 1)
{
//do stuff
}
C ++ 11 Variadic 템플릿 답변을주고 싶었습니다.
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;
}
단순히 다음과 같이 부르는 것은 부울의 수를 세는 다소 우아한 방법을 만듭니다.
if ( countBool( bool1, bool2, bool3 ) > 1 )
{
....
}
대부분의 언어에서 True는 0이 아닌 값과 동일하고 False는 0입니다. 나는 당신을위한 정확한 구문이 없지만 의사 코드에서는 다음과 같습니다.
if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
{
//statements here
}
다섯 가지 값 만있는 경우 비트를 짧은 또는 int로 포장하여 쉽게 테스트를 수행하고 0 또는 1 비트 답변인지 확인할 수 있습니다. 당신이 얻을 수있는 유일한 유효하지 않은 숫자는 ..
0x 0000 0000 0x 0000 0001 0x 0000 0010 0x 0000 0100 0x 0000 1000 0x 0001 0000
이것은 당신에게 검색 할 6 가지 값을 제공하고, 조회 테이블에 넣고, 거기에 있지 않으면, 당신은 당신의 대답을 할 수 있습니다.
이것은 당신에게 간단한 대답을 제공합니다.
public static boolean moreThan1BitSet(int b) { final short 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) return false; return true; }
이것은 8 비트를 넘어서는 것이 좋지 않지만 5 개만 있습니다.
if ((b1.compareto (false) + b2.compareto (false) + b3.compareto (false) + ...)> 1)
// 그들 중 하나 이상이 사실입니다
...
또 다른
...
당신은 언급했습니다
흥미로운 옵션 중 하나는 부울을 바이트에 보관하고 올바른 교대를하고 원래 바이트와 비교하는 것입니다. 같은 것
if (myByte && (myByte >> 1))
표현식은 당신이 원하는 결과를 줄 것이라고 생각하지 않습니다 (표현이 유효하지 않기 때문에 C 시맨틱을 사용합니다) :
만약에 (myByte == 0x08)
, 비트 세트가 하나만 있더라도 표현식은 사실이 반환됩니다.
당신이 의미한다면 "if (myByte & (myByte >> 1))
"그럼 (myByte == 0x0a)
2 비트 세트가 있어도 표현식이 거짓으로 반환됩니다.
그러나 다음은 한 단어의 비트 수를 계산하는 몇 가지 기술입니다.
당신이 고려할 수있는 변형은 Kernighan의 계산 방법을 사용하는 것이지만, 비트 세트 이상이 하나 이상 있는지 알아야하므로 일찍 구제합니다.
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);
}
물론 조회 테이블을 사용하는 것은 나쁜 옵션도 아닙니다.
나는 최근에 같은 문제를 겪고 있었는데, 나는 세 가지 부울 값을 가지고 있었는데, 그중 하나만이 한 번에 사실인지 확인해야했습니다. 이를 위해 XOR 연산자를 다음과 같이 사용했습니다.
bool a = true;
bool b = true;
bool c = false;
if (a || b || c)
{
if (a ^ b ^ c){
//Throw Error
}
}
이 코드는 A와 B가 모두 사실이므로 오류가 발생합니다.
참조 : http://www.dotnetperls.com/xor
이 전략의 구덩이가 낙상을 알고 있다면 C#의 XOR 운영자 만 발견했습니다. 알려주세요.