Как можно отрефакторить данный участок кода
Постановка задачи (что я хочу получить в ответах - указано в конце)
Задача - предоставить пользователю список всех возможных последовательностей элементов, в котором элементы (тип которых - мой перечислитель (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;
}
Что ожидаю получить в ответах
Как отрефакторить крайний метод (GetMyEnumProgressions), чтобы он работал для входных значений от 2 до 100 (или больше), при этом был по размерам меньше моего (думал насчёт выделение куска кода в отдельный метод и рекурсивно его вызывать, но не смог додумать как это сделать). Либо как изменить весь подход целиком для получения более лакончиного (визуально менее объёмного) и оптимального с точки зрения производительности кода
Ускорение \ оптимизация достижения задачи (получения списка возможных последовательностей)
Литература про рефакторинг подобных примеров и чего-то посложнее
Ответы (1 шт):
Здесь напрашивается рекурсивный подход. До конца в суть задания у меня вникнуть не получилось, поэтому пытался просто при оптимизации сохранить поведение кода, упрощая сам код.
Получилось вот так
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);
}
}
Вам суть задачи видна яснее, поэтому сможете дальше дорабатывать, если я где-то промахнулся или не увидел новых оптимизаций.