2014-03-19 1 views
2

Предположим 2 списка строк неизвестной, возможно, разной длины. товары во втором списке должны быть согласованы с элементами в первом списке, но согласование может зависеть от содержания следующих элементов:Loop - управляющий поток для работы с 2 списками неизвестной длины

Список lst_1: [0] Apple, [1] Orange [2] Peach [3] ...

Список lst_2: [0] соль [1] текила [2] мед [3] водка [4] виски [5] ...

Элементы в lst_1 должны быть сопоставлены с элементами в lst_2 на основе нескольких условий. Например, для каждого плода должен быть хотя бы один алкоголь; виски нельзя сочетать с персиком; должно быть менее 5 спиртов в любом данном коктейле и т. д.

Если я согласен с яблоком с солью и текилой, апельсин с медом и водкой, я получаю персик с виски, что нарушает одно из условий ... но если Я положил яблоко, текилу и мед вместе, я получаю апельсино-водки-виски, которые являются истинным напитком.

Подводя итог, мне нужно пройти через оба списка, каждый раз проверяя ряд условий для текущего элемента, а также то, что он означает для следующего элемента в списке и для предыдущего элемента, и мне может понадобиться назад несколько раз и исправить вещи, пока все не будет соответствовать правильно.

Я думал о большой while(bNotReady){...} петли так:

int i = 0; 
int j = 0; 
string fruit, additive; 
bool bContainsAlcohol = false; 
dictionary<string, string> dic = new dictionary<string,string>(); 
while(bNotReady){ 
    fruit = lst_1[i]; 
    additive = lst_2[j]; 
    if (is_valid_match(fruit,additive) && bContainsAlcohol) 
    { 
    dic.Add(fruit,additive); 
    i++; j++; 
    continue; 
    } 
    else if(...) 

} 

И работаю мой путь через списки, но я могу видеть это быстро стало бы один огромным, нечитаемым циклом.

Есть ли лучший способ выработать поток управления для этой задачи?

+1

Это работа для программирования ограничений! –

+0

Так как это не звучит, например. «Яблоко» можно использовать только один раз, и у вас, вероятно, нет мести по каждому из них, я бы просто сделал что-то вроде: for (int fruit = 0; fruit

+0

@presiuslitelsnoflek Я не знаком с программированием ограничений. Я быстро посмотрел на Google, и это кажется интересным, но я нашел очень мало практических примеров. Как бы вы решили решить небольшую проблему выше, используя программирование ограничений? – Sylverdrag

ответ

0

Если вы решите пойти по пути программирования ограничений, я думаю, что это будет лучшая идея, но вы можете сделать это с помощью грубой силы и коллекций .Net. Это будет не очень хорошо, но это должно сработать.

  • Во-первых, вам нужен способ перечисления всех возможных разделов ваших добавок в комплекты с мощностью, равной количеству ингредиентов. [a, b, c], разделенные на три набора, могут дать [abc,,], затем [ab,c,], затем [a,bc,] и т. д.
  • Перечисляя все эти множества, вам нужно будет перечислить все перестановки этих множеств
  • Наконец, для каждой перестановки набора вам нужно будет проверить, удовлетворяются ли все правила текущим совпадением между ингредиентом и набором добавок

у меня не было времени, чтобы создать метод для разделения, но я имел удлинительный перестановку доступны, поэтому я создал второй этап программы

private static IList<string> Ingredients { get; set; } 
private static IList<string> Additives { get; set; } 
private static IList<Func<string, string, bool>> Rules { get; set; } 

private static void Main(string[] args) 
{ 
    Ingredients = new List<string>() { "Apple", "Orange", "Peach" }; 
    Additives = new List<string>() { "Vodka", "Rum", "Whiskey" }; 
    Rules = new List<Func<string, string, bool>>() { (ingredient1, ingredient2) => { return (ingredient1 != "Peach" && ingredient2 != "Whiskey"); } }; 
    var additivesOrganisationMatchingAllTheRules = FindMatch(); 
} 

private static IList<string> FindMatch() 
{ 
    // here we should enumerate all sets and then enumerate permutation of all the sets 
    // instead for the example we just enumerate the permutations 
    foreach (var additivesPermutation in Additives.GetCombinations()) 
    { 
     for (int i = 0; i < additivesPermutation.Count; i++) 
     { 
      var thisSituationIsOk = Rules.All(r => r(Ingredients[i], Additives[i])); 
      if (thisSituationIsOk) return additivesPermutation; 
     } 
    } 
    return null; 
} 

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

public static class CombinatorialExtension 
{ 
    public static IEnumerable<IList<TSource>> GetCombinations<TSource>(
     this IList<TSource> source) 
    { 
     if (source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 
     return GetCombinationsImpl<TSource>(source); 
    } 

    private static IEnumerable<IList<TSource>> GetCombinationsImpl<TSource>(
     this IList<TSource> list) 
    { 
     return Permutations(list, list.Count); 
    } 

    private static void ShiftRight<TSource>(IList<TSource> list, int cardinality) 
    { 
     var lastElement = list[cardinality - 1]; 
     list.RemoveAt(cardinality - 1); 
     list.Insert(0, lastElement); 
    } 

    private static IEnumerable<IList<TSource>> Permutations<TSource>(IList<TSource> list, int cardinality) 
    { 
     if (cardinality == 1) 
     { 
      yield return list; 
     } 
     else 
     { 
      for (int i = 0; i < cardinality; i++) 
      { 
       foreach (var perm in Permutations(list, cardinality - 1)) 
        yield return perm; 
       ShiftRight(list, cardinality); 
      } 
     } 
    } 
} 

Этот алгоритм позволил бы найти решение, которое соответствует ингредиенты и правила, которые вы имеете, но это не очень красиво, а на самом деле действительно не эффективно: много комбинаций было бы вычислено несколько раз. Вы также должны настроить правила, чтобы получить результаты, которые были бы приятным (то есть минимальное и максимальное количество добавок и т.д.)


EDIT

Это даже возможно, чтобы избежать необходимости создавать все наборы, вы можете просто добавить столько добавок в ваши добавки, как у вас есть ингредиенты минус 1. Затем просто выполните перестановку и разделите свои добавки в соответствии с разделителями на списки добавок. Тогда ваши правила могут принять ингредиент и список добавок в качестве основы для проверки того, соблюдается ли оно. Вот пример кода

private static IList<string> Ingredients { get; set; } 
private static IList<string> Additives { get; set; } 
private static IList<Func<string, IList<string>, bool>> Rules { get; set; } 

private static void Main(string[] args) 
{ 
    Ingredients = new List<string>() { "Apple", "Orange", "Peach" }; 
    Additives = new List<string>() { "Vodka", "Rum", "Whiskey" }; 
    Additives.Add("Separator"); 
    Additives.Add("Separator"); // add as many separators as the number of ingredients - 1 
    Rules = new List<Func<string, IList<string>, bool>>() { 
     (ingredient1, ingredient2) => { return (ingredient1 != "Peach" && ingredient2.All(s => s != "Whiskey")); } 
     , 
     (ingredient1, ingredient2) => { return ingredient2.Count > 0; } 
    }; 
    var additivesOrganisationMatchingAllTheRules = FindMatch(); 
} 

private static IList<IList<string>> FindMatch() 
{ 
    // separators will create the sets 
    foreach (var additivesPermutation in Additives.GetCombinations()) 
    { 
     var Sets = Split(additivesPermutation); 
     var thisSituationIsOk = true; 
     for (int i = 0; i < Sets.Count && thisSituationIsOk ; i++) 
     { 
      thisSituationIsOk = thisSituationIsOk && Rules.All(r => r(Ingredients[i], Sets[i])); 
     } 
     if (thisSituationIsOk) return Sets; 
    } 
    return null; 
} 

private static IList<IList<string>> Split(IList<string> values) 
{ 
    var splitValues = new List<IList<String>>(); 
    var currentList = new List<string>(); 
    foreach (var value in values) 
    { 
     if (value == "Separator") 
     { 
      splitValues.Add(currentList); 
      currentList = new List<string>(); 
     } 
     else 
     { 
      currentList.Add(value); 
     } 
    } 
    splitValues.Add(currentList); 
    return splitValues; 
} 
Смежные вопросы