Как можно отрефакторить данный участок кода

Постановка задачи (что я хочу получить в ответах - указано в конце)

Задача - предоставить пользователю список всех возможных последовательностей элементов, в котором элементы (тип которых - мой перечислитель (MyEnum)) расположены по определенному обязательному условию - чтобы поставить элемент на текущую позицию в последовательности, предыдущие 2 элемента должны быть с подходящими значениями (для этого я использую указанную ниже myEnumAvailabilityDictionary)

Последовательность элементов для пользователя в итоге будет отображаться на форме или консоли как строка, состоящая из всех элементов последовательности, разделённых знаком "-"

Описание существующего рабочего кода

Существует перечисление:

public enum MyEnum 
{
  nan,
  I,
  II,
  III,
  V,
  X
}

Существует утилитарный класс (ProgressionsUtil.cs) для генерации списка последовательностей, и различной работы с ними. Он хранит в себе хранит статичную библиотеку, заполненную списками доступных мест для каждого значения MyEnum (доступное место - массив из 2 элементов (также типа MyEnum) после которых может находится новое MyEnum для которого список этих массивов):

Dictionary<MyEnum, List<MyEnum[]>> myEnumAvailabilityDictionary = new Dictionary<MyEnum, List<MyEnum[]>(){
  {MyEnum.I, new List<MyEnum[]>() {new MyEnum[] {MyEnum.nan,MyEnum.nan}, new MyEnum[] {MyEnum.nan,MyEnum.I}, new MyEnum[] {MyEnum.I,MyEnum.II}, new MyEnum[] {MyEnum.II,MyEnum.III}}},
  {MyEnum.II, new List<MyEnum[]>() {new MyEnum[] {MyEnum.nan,MyEnum.nan},new MyEnum[] {MyEnum.I,MyEnum.III},new MyEnum[] {MyEnum.I,MyEnum.I}, new MyEnum[] {MyEnum.I,MyEnum.II}}},
  {MyEnum.III, new List<MyEnum[]>() {new MyEnum[] {MyEnum.nan,MyEnum.nan}, new MyEnum[] {MyEnum.II,MyEnum.II}, new MyEnum[] {MyEnum.V,MyEnum.X}, new MyEnum[] {MyEnum.V,MyEnum.X}}},
  {MyEnum.V, new List<MyEnum[]>() {new MyEnum[] {MyEnum.nan,MyEnum.nan}, new MyEnum[] {MyEnum.nan,MyEnum.V}, new MyEnum[] {MyEnum.I,MyEnum.X}, new MyEnum[] {MyEnum.I,MyEnum.X}}},
  {MyEnum.X, new List<MyEnum[]>() {new MyEnum[] {MyEnum.nan,MyEnum.nan}, new MyEnum[] {MyEnum.I,MyEnum.nan}, new MyEnum[] {MyEnum.nan,MyEnum.I}, new MyEnum[] {MyEnum.I,MyEnum.I}}}
}

(на самом деле библиотека значительно больше, не могу полную выложить, здесь привёл примеры заполнения)

В этом классе есть методы:

Для получения списка доступных элементов (типа MyEnum) на основании 2-х уже существующих (т.е. они перед ним)

private List<MyEnum> GetAvailableMyEnumsOnPoint(MyEnum prevEl1, MyEnum prevEl2) 
{
  List<MyEnum> availableMyEnumsOnPoint = new List<MyEnum>();
  foreach (var pair in myEnumAvailabilityDictionary) 
  {
    if (pair.Value.Any(x => x[0] == prevEl1 && x[1] == prevEl2)) 
    {
      availableMyEnumsOnPoint.Add(pair.Key);
    }
  }
  return availableMyEnumsOnPoint;
}

Для проверки возможности быть следующим элементом на основании 2-х уже существующих (т.е. они перед ним)

private bool AvailableLastMyEnum(MyEnum el, MyEnum prevEl1, MyEnum prevEl2) 
{
  List<MyEnum[]> availableLastMyEnums;
  myEnumAvailabilityDictionary.TryGetValue(el, out availableLastMyEnums);
  return availableLastMyEnums.Any(x => x[0] == prevEl1 && x[1] == prevEl2);
}

Для получения списка возможных последовательностей (сейчас он работает только для входного значения равного 2-4 включительно)

public List<MyEnum[]> GetMyEnumProgressions(int elementInProgressionsCount)
{
  List<MyEnum[]> definedMyEnumProgressions = new List<MyEnum[]>();
  List<MyEnum> myEnums1 = GetAvailableMyEnumsOnPoint(MyEnum.nan, MyEnum.nan);
  foreach (MyEnum el1 in myEnums1)
  {
    List<MyEnum> myEnums2 = GetAvailableMyEnumsOnPoint(MyEnum.nan, el1);
    foreach (MyEnum el2 in myEnums2)
    {
      if (elementInProgressionsCount == 2)
      {
        if (AvailableLastMyEnum(el1, el1, el2))
        {
          definedMyEnumProgressions.Add(new MyEnum[2] { el1, el2 });
        }
      }
      else
      {
        List<MyEnum> myEnums3 = GetAvailableMyEnumsOnPoint(el1, el2);
        foreach (MyEnum el3 in myEnums3)
        {
          if (elementInProgressionsCount == 3)
          {
            if (AvailableLastMyEnum(el1, el2, el3))
            {
              definedMyEnumProgressions.Add(new MyEnum[3] { el1, el2, el3 });
            }
          }
          else
          {
            List<MyEnum> myEnums4 = GetAvailableMyEnumsOnPoint(el2, el3);
            foreach (MyEnum el4 in myEnums4)
            {
              if (elementInProgressionsCount == 4)
              {
                if (AvailableLastMyEnum(el1, el3, el4))
                {
                  definedMyEnumProgressions.Add(new MyEnum[4] { el1, el2, el3, el4 });
                }
              }
            }
          }
        }
      }
    }
  }
  return definedMyEnumProgressions;
}

Что ожидаю получить в ответах

  1. Как отрефакторить крайний метод (GetMyEnumProgressions), чтобы он работал для входных значений от 2 до 100 (или больше), при этом был по размерам меньше моего (думал насчёт выделение куска кода в отдельный метод и рекурсивно его вызывать, но не смог додумать как это сделать). Либо как изменить весь подход целиком для получения более лакончиного (визуально менее объёмного) и оптимального с точки зрения производительности кода

  2. Ускорение \ оптимизация достижения задачи (получения списка возможных последовательностей)

  3. Литература про рефакторинг подобных примеров и чего-то посложнее


Ответы (1 шт):

Автор решения: aepot

Здесь напрашивается рекурсивный подход. До конца в суть задания у меня вникнуть не получилось, поэтому пытался просто при оптимизации сохранить поведение кода, упрощая сам код.

Получилось вот так

private List<MyEnum> GetAvailableMyEnumsOnPoint(MyEnum prevEl1, MyEnum prevEl2)
{
    List<MyEnum> availableMyEnumsOnPoint = new();
    foreach (var pair in myEnumAvailabilityDictionary)
    {
        if (pair.Value.Any(x => x[0] == prevEl1 && x[1] == prevEl2))
            availableMyEnumsOnPoint.Add(pair.Key);
    }
    return availableMyEnumsOnPoint;
}

private bool AvailableLastMyEnum(MyEnum el, MyEnum prevEl1, MyEnum prevEl2)
{
    return myEnumAvailabilityDictionary.TryGetValue(el, out List<MyEnum[]> availableLastMyEnums) && availableLastMyEnums.Any(x => x[0] == prevEl1 && x[1] == prevEl2);
}

public List<MyEnum[]> GetMyEnumProgressions(int elementInProgressionsCount)
{
    List<MyEnum[]> definedMyEnumProgressions = new();
    if (elementInProgressionsCount < 2)
        return definedMyEnumProgressions;
    Dive(definedMyEnumProgressions, elementInProgressionsCount, new List<MyEnum>(), 1);
    return definedMyEnumProgressions;
}

private void Dive(List<MyEnum[]> definedMyEnumProgressions, int elementInProgressionsCount, List<MyEnum> list, int level)
{
    foreach (MyEnum el in GetAvailableMyEnumsOnPoint(list.Count < 2 ? MyEnum.nan : list[^2], list.Count < 1 ? MyEnum.nan : list[^1]))
    {
        list.Add(el);
        if (elementInProgressionsCount == level)
        {
            if (AvailableLastMyEnum(list[0], list[^2], list[^1]))
                definedMyEnumProgressions.Add(list.ToArray());
        }
        else
            Dive(definedMyEnumProgressions, elementInProgressionsCount, list, level + 1);
        list.RemoveAt(list.Count - 1);
    }
}

Вам суть задачи видна яснее, поэтому сможете дальше дорабатывать, если я где-то промахнулся или не увидел новых оптимизаций.

→ Ссылка