Какой элегантный способ проверить, равны ли 3 переменные, когда любая из них может быть подстановочным знаком? - PullRequest
17 голосов
/ 10 августа 2011

Скажем, у меня есть 3 char переменные, a, b и c.
Каждая из них может быть '0', что является особым случаем и означает, что оно соответствует каждому символу.

Так что если a равно '0', мне нужно только проверить, если b == c.
Я хочу проверить, если a == b == c, но обнаружил, что реализация в C # идет хаотично и долго.

Есть ли какое-нибудь креативное или симпатичное решение, которое вы можете предложить?

обновление

для повышения производительности, воспользуйтесь подходом Эрика А. Брандштадмоена.для простоты используйте apprach M4N, также я сделал некоторые изменения: !(query.Any() && query.Distinct().Skip(1).Any())

Ответы [ 9 ]

35 голосов
/ 10 августа 2011

Примерно так:

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
18 голосов
/ 10 августа 2011

Элегантное решение

Это требует базового понимания 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'.
Вторая перегрузка просто вызываетпервый для каждой пары.

(Обратите внимание, что из-за подстановочных знаков мы не можем использовать переходное свойство и сравнивать только две пары.)

2 голосов
/ 11 августа 2011

Если вы ограничиваете символы в ASCII, а не в юникоде, тогда мне нравится: 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) * $

2 голосов
/ 11 августа 2011

Примерно так должно работать для любого количества значений символов:

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 голосов
/ 11 августа 2011

Вы можете написать структуру "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 ();
    }
}
2 голосов
/ 10 августа 2011
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);
}

Не уверен, что я назвал бы это изящным, но это не ужасно (и даже может быть правильным ...) (заметьте, это более или менее уточнение ответа Падди выше).

2 голосов
/ 10 августа 2011

Это считается хаотичным и продолжительным?

Кажется, хорошо для меня, при условии, что вы можете иметь только три из них ...

return ((a == "0" || b == "0" || a == b) && (b =="0" || c =="0" || b == c) && (a =="0" || c =="0" || a == c));
1 голос
/ 16 января 2014

Как насчет:

if ((a==b) && (b==c) && (a==c)) 
....
....

Моя логика неверна?

0 голосов
/ 12 августа 2011

Это не очень отличается от принятого ответа, но в любом случае

var list = new List<Char> {'1', '1', '0'};

var check = list.Where(ch => ch != '0')
                .Distinct()
                .Count() < 2;
...