Как мне создать полный набор комбинаций со строковыми манипуляциями? - PullRequest
1 голос
/ 22 марта 2011

У меня есть небольшой проект, в котором у меня есть входное предложение, в котором пользователь может указать варианты:

The {small|big} car is {red|blue}

Выше приведено примерное предложение, которое я хочу разбить на 4 предложения, например:

  • Маленькая машина красного цвета
  • Большая машина красная
  • Маленькая машина синего цвета
  • Большая машина синего цвета

Кажется, я не могу сосредоточиться на проблеме. Может быть, кто-то может помочь мне, пожалуйста.

Редактировать Вот мой начальный код

Regex regex = new Regex("{(.*?)}", RegexOptions.Singleline);
MatchCollection collection = regex.Matches(richTextBox1.Text);
string data = richTextBox1.Text;

//build amount of variations
foreach (Match match in collection)
{
    string[] alternatives = match.Value.Split(new char[] { '|', '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
    foreach (string alternative in alternatives)
    {
        //here i get problems                  
    }
}

Ответы [ 8 ]

3 голосов
/ 22 марта 2011

Похоже, вам нужна динамическая декартова функция для этого. Сообщение Эрика Липперта , написанное в ответ на Генерация всех возможных комбинаций .

Во-первых, нам нужно проанализировать входную строку:

Regex ex = new Regex(@"(?<=\{)(?<words>\w+(\|\w+)*)(?=\})");
var sentence = "The {small|big} car is {red|blue}";

тогда входная строка должна быть изменена для использования в string.Format -подобных функциях:

int matchCount = 0;
var pattern = ex.Replace(sentence, me => 
{
    return (matchCount++).ToString(); 
});
// pattern now contains "The {0} car is {1}"

тогда нам нужно найти все совпадения и применить превосходный метод расширения CartesianProduct Эрика:

var set = ex.Matches(sentence)
    .Cast<Match>()
    .Select(m => 
        m.Groups["words"].Value
            .Split('|')
    ).CartesianProduct();

foreach (var item in set)
{
    Console.WriteLine(pattern, item.ToArray());
}

это даст:

The small car is red
The small car is blue
The big car is red
The big car is blue

и, наконец, метод CartesianProduct (взят из здесь ):

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
    this IEnumerable<IEnumerable<T>> sequences) 
{ 
  IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
  return sequences.Aggregate( 
    emptyProduct, 
    (accumulator, sequence) =>  
      from accseq in accumulator  
      from item in sequence  
      select accseq.Concat(new[] {item}));                
}
3 голосов
/ 22 марта 2011
    private void ExpandString( List<string> result, string text )
    {
      var start = text.IndexOf('{');
      var end = text.IndexOf('}');
      if (start >= 0 && end > start)
      {
          var head = text.Substring(0, start);
          var list = text.Substring(start + 1, end - start - 1).Split('|');
          var tail = text.Substring(end + 1);
          foreach (var item in list)
              ExpandString(result, head + item + tail);
      }
      else
          result.Add(text);
    }

Используйте как:

    var result = new List<string>();
    ExpandString(result, "The {small|big} car is {red|blue}");
1 голос
/ 22 марта 2011

Если вы не знаете количество вариантов, рекурсия - ваш друг:

static public IEnumerable<string> permute(string template)
{
    List<string> options;
    string before;
    string after;
    if (FindFirstOptionList(template, out options, out before, out after))
    {
        foreach (string option in options)
        {
            foreach (string permutation in permute(before + option + after))
            {
                yield return permutation;
            }
        }
    }
    else
    {
        yield return template;
    }
}

static public bool FindFirstOptionList(string template, out List<string> options, out string before, out string after)
{
    before = string.Empty;
    after = string.Empty;
    options = new List<string>(0);
    if (template.IndexOf('{') == -1)
    {
        return false;
    }
    before = template.Substring(0, template.IndexOf('{'));
    template = template.Substring(template.IndexOf('{') + 1);
    if (template.IndexOf('}') == -1)
    {
        return false;
    }
    after = template.Substring(template.IndexOf('}') + 1);
    options = template.Substring(0, template.IndexOf('}')).Split('|').ToList();
    return true;
}

использование аналогично решению danbystrom, за исключением того, что это возвращает IEnumerable вместо манипулирования одним из параметров вызова.Остерегайтесь синтаксических ошибок и т. Д.

static void main()
{
    foreach(string permutation in permute("The {small|big} car is {red|blue}"))
    {
        Console.WriteLine(permutation);
    }
}
0 голосов
/ 22 марта 2011

Примерно так должно работать:

private void Do()
{
    string str = "The {small|big} car is {red|blue}";

    Regex regex = new Regex("{(.*?)}", RegexOptions.Singleline);

    int i = 0;
    var strWithPlaceHolders = regex.Replace(str, m => "{" + (i++).ToString() + "}");

    var collection = regex.Matches(str);

    var alternatives = collection.OfType<Match>().Select(m => m.Value.Split(new char[] { '|', '{', '}' }, StringSplitOptions.RemoveEmptyEntries));
    var replacers = GetReplacers(alternatives);

    var combinations = new List<string>();
    foreach (var replacer in replacers)
    {
        combinations.Add(string.Format(strWithPlaceHolders, replacer));
    }
}

private IEnumerable<object[]> GetReplacers(IEnumerable<string[]> alternatives)
{
    return GetAllPossibilities(0, alternatives.ToList());
}

private IEnumerable<object[]> GetAllPossibilities(int level, List<string[]> list)
{
    if (level == list.Count - 1)
    {
        foreach (var elem in list[level])
            yield return new[] { elem };
    }
    else
    {
        foreach (var elem in list[level])
        {
            var thisElemAsArray = new object[] { elem };
            foreach (var subPossibilities in GetAllPossibilities(level + 1, list))
                yield return thisElemAsArray.Concat(subPossibilities).ToArray();
        }
    }
    yield break;
}
0 голосов
/ 22 марта 2011

Возможно, вы ищете это: Отредактированная версия

static void Main(string[] args)
{
    var thisstring = "The {Small|Big} car is {Red|Blue}";           
    string FirstString = thisstring.Substring(thisstring.IndexOf("{"), (thisstring.IndexOf("}") - thisstring.IndexOf("{")) + 1);
    string[] FirstPossibility = FirstString.Replace("{", "").Replace("}", "").Split('|');
    thisstring = thisstring.Replace(FirstString, "[0]");
    string SecondString = thisstring.Substring(thisstring.IndexOf("{"), (thisstring.IndexOf("}") - thisstring.IndexOf("{")) + 1);
    string[] SecondPosibility = SecondString.Replace("{", "").Replace("}", "").Split('|');
    thisstring = thisstring.Replace(SecondString, "{1}").Replace("[0]", "{0}");
    foreach (string tempFirst in FirstPossibility)
    {
        foreach (string tempSecond in SecondPosibility)
        {
            Console.WriteLine(string.Format(thisstring, tempFirst, tempSecond));
        }
    }
    Console.Read();
}
0 голосов
/ 22 марта 2011

Рассмотрим вложение итерационных циклов. Что-то вроде ...

foreach(string s in someStringSet)
{
    foreach(string t in someOtherStringSet)
    {
        // do something with s and t
    }
}
0 голосов
/ 22 марта 2011

Я бы предложил разделить входной текст на упорядоченный список статических и динамических частей.Каждая динамическая часть содержит список, в котором хранятся ее значения, и индекс, представляющий текущее выбранное значение.Этот индекс изначально установлен на ноль.

Чтобы распечатать все возможные комбинации, вы сначала должны реализовать метод, который печатает полный список, используя текущие установленные индексы динамических деталей.Для первого вызова все индексы будут установлены на ноль.

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

0 голосов
/ 22 марта 2011
string color = SomeMethodToGetColor();
string size = SomeMethodToGetSize();

string sentence = string.Format("The {0} car is {1}", size, color);
...