Каков элегантный способ проверить, являются ли 3 переменных равными, если любой из них может быть подстановочным знаком?
Скажем, у меня есть 3 char
переменные, a
, b
и c
.
Каждый из них может быть '0'
, что является особым случаем и означает, что он соответствует каждому char.
Итак, если a есть '0'
, мне нужно только проверить, есть ли b == c
.
Я хочу проверить, если a == b == c
, но обнаружил, что реализация в С# идет хаотично и длительно.
Есть ли какое-либо творческое или привлекательное решение, которое вы можете предложить?
Обновление
для достижения эффективности, возьмите подход Эрика А. Брандстадмуна.
для простоты используйте оценку M4N, также я сделал некоторые изменения: !(query.Any() && query.Distinct().Skip(1).Any())
Ответы
Ответ 1
Что-то вроде этого должно работать для любого количества значений char:
public class Comparer
{
public static bool AreEqualOrZero(params char[] values)
{
var firstNonZero = values.FirstOrDefault(x => x != '0');
return values.All(x => x == firstNonZero || x == '0');
}
}
Пропускает следующие модульные тесты:
[TestClass()]
public class ComparerTest
{
[TestMethod()]
public void Matches_With_Wildcard()
{
char[] values = {'0', '1', '1', '1'};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_No_Wildcard()
{
char[] values = {'1', '1', '1', '1'};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_Only_Wildcards()
{
char[] values = {'0', '0', '0'};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_Zero_Length()
{
char[] values = {};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_One_Element()
{
char[] values = {'9'};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_One_Wildcard_And_Nothing_Else()
{
char[] values = {'0'};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Does_Not_Match_On_NonEqual_Sequence_No_Wildcard()
{
char[] values = {'1', '2', '1', '1'};
Assert.IsFalse(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Does_Not_Match_On_NonEqual_Sequence_With_Wildcard()
{
char[] values = {'1', '2', '1', '0'};
Assert.IsFalse(Comparer.AreEqualOrZero(values));
}
}
Ответ 2
Что-то вроде этого:
var a = '1';
var b = '0';
var c = '1';
var chars = new List<char> { a, b, c };
var filtered = chars.Where(ch => ch != '0');
var allEqual = filtered.Count() == 0 || filtered.Distinct().Count() == 1;
Чтобы объяснить решение:
- сначала вставьте все символы в список
- исключить все символы, которые являются "0":
.Where(ch => ch != '0')
- все остальные символы равны, если:
- в оставшейся коллекции нет элементов:
chars.Count() == 0
- или количество уникальных оставшихся элементов: 1:
chars.Distinct().Count() == 1
Обновить: здесь другая версия, которая не использует LINQ, но остается и читаема (IMO). Он реализуется как метод и может быть вызван любым количеством символов для тестирования:
public bool AllEqualOrZero(params char[] chars)
{
if (chars.Length <= 1) return true;
char? firstNonZero = null;
foreach (var c in chars)
{
if (c != '0')
{
firstNonZero = firstNonZero ?? c;
if (c != firstNonZero) return false;
}
}
}
// Usage:
AllEqualOrZero('0', '0', '0'); // -> true
AllEqualOrZero('0', '1', '1'); // -> true
AllEqualOrZero('2', '1', '0'); // -> false
AllEqualOrZero(); // -> true
AllEqualOrZero('1'); // -> true
Ответ 3
Элегантное решение
Это требует базового понимания LINQ и основано на решении M4N:
static bool IsMatch(params char[] chars)
{
return chars.Where(c => c != '0')
.Distinct().Count() <= 1;
}
Изменить
Первоначально мое решение отличалось от решения M4N, но после некоторых упрощений я пришел к чему-то (почти) точно так же. Хотя кредиты идут на него полностью, я просто оставлю это для справки.
Он возвращает true
, когда в коллекции есть не более одного отдельного символа без подстановочных знаков.
Я заставил его принять переменное количество параметров, чтобы вы могли называть его 2, 3 или более символов:
bool match = IsMatch('3', '3', '4', '0');
Простое решение
Это чистый перевод вашей логики в ваш код, никаких причудливых вещей.
static bool IsMatch(char x, char y)
{
return x == y || x == '0' || y == '0';
}
static bool IsMatch(char a, char b, char c)
{
return IsMatch(a, b) && IsMatch(b, c) && IsMatch(a, c);
}
Первая IsMatch
перегрузка возвращает true
, когда ее аргумент равен или один из них '0'
.
Вторая перегрузка просто вызывает первую для каждой пары.
(Обратите внимание, что из-за подстановочных знаков мы не можем использовать транзитивное свойство и сравнивать только две пары.)
Ответ 4
Это считается хаотичным и продолжительным?
Кажется, для меня это нормально, потому что вы можете иметь только три из них...
return ((a == "0" || b == "0" || a == b) && (b =="0" || c =="0" || b == c) && (a =="0" || c =="0" || a == c));
Ответ 5
bool MatchTwo(char a, char b)
{
return a == '0' || b == '0' || a == b;
}
bool MatchThree(char a, char b, char c)
{
return MatchTwo(a, b) && MatchTwo(a, c) && MatchTwo(b, c);
}
Не уверен, что я бы назвал это изящным, но это не ужасно (и может даже быть правильным...) (заметьте, это более или менее уточнение ответа Пэдди выше).
Ответ 6
Вы можете написать структуру "MYChar", которая реализует char
и переопределяет Equals
, операторы равенства и неявное преобразование, чтобы вы могли:
MyChar a = 'a';
MyChar b = '0';
bool eq = a == b; //true
Изменить
Оказывается, вы не можете наследовать от char
, потому что он запечатан, но я попробовал следующий код. Он компилируется, но я не уверен, что он работает. Я скомпилировал его из http://compilr.com/IDE/34853, но в это время мне нечего тестировать.
вот он:
public struct MyChar
{
private static char _wild = '0';
private char _theChar;
public MyChar(char c)
{
_theChar = c;
}
public MyChar ()
:this (_wild)
{}
private bool IsWildCard ()
{
return _theChar.Equals (_wild);
}
public static implicit operator char (MyChar c)
{
return c._theChar;
}
public static implicit operator MyChar (char c)
{
return new MyChar (c);
}
public override bool Equals (object obj)
{
if (!(obj is MyChar))
{
return base.Equals (obj);
}
else
{
if (IsWildCard ())
{
return true;
}
else
{
MyChar theChar = (MyChar) obj;
return theChar.IsWildCard () || base.Equals ((char) theChar);
}
}
}
public override int GetHashCode ()
{
return _theChar.GetHashCode ();
}
}
Ответ 7
Если вы ограничиваете символы ASCII, а не unicode, тогда мне нравится:
http://ideone.com/khacx. (редактирование в ответ на комментарий, указывающий, что я не совсем понял спецификации, но мне все же нравится основная идея. Добавлен дополнительный тест в качестве проверки).
using System;
class example {
static void elegant(char a, char b, char c) {
int y = ((int) a - 48) + ((int) b - 48) + ((int) c - 48);
int z = ((int) a - 48) * ((int) b - 48) * ((int) c - 48);
bool result = y == ((int) a-48)*3 || (z ==0 && (a==b || b==c || a==c));
Console.WriteLine(result);
}
static void Main() {
elegant('0', 'b', 'c'); // false
elegant('a', '0', 'c'); // false
elegant('a', 'b', '0'); // false
elegant('a', 'b', 'c'); // false
elegant('0', '0', '0'); // true
elegant('a', 'a', 'a'); // true
elegant('0', 'a', 'a'); // true
elegant('a', '0', 'a'); // true
elegant('a', 'a', '0'); // true
elegant('0', '0', 'a'); // true
elegant('0', 'a', '0'); // true
elegant('a', '0', '0'); // true
}
}
Для более общего решения, которое охватывает неограниченное количество символов, это то, что регулярные выражения для: ^ (.) (\ 1 | 0) * $
Ответ 8
Как насчет:
if ((a==b) && (b==c) && (a==c))
....
....
Я ошибаюсь в своей логике?
Ответ 9
Это не очень отличается от принятого ответа, но в любом случае
var list = new List<Char> {'1', '1', '0'};
var check = list.Where(ch => ch != '0')
.Distinct()
.Count() < 2;