#c# #linq #recursion #functional-programming
#c# #linq #рекурсия #функциональное программирование
Вопрос:
Учитывая список Func<string, string>
, возможно ли написать инструкцию, которая выполняет итерацию по списку и возвращает результат следующим образом:
string result = f1(f2(f..(input));
У меня есть следующий код (который работает), но меня не устраивает временная переменная.
public static string WrapEachElementWith<T>
( this IEnumerable<T> target,
params Func<string, string>[] func )
{
string result = string.Empty;
target.Each(s =>
{
var tmp = s.ToString();
func.Reverse().Each(x => tmp = x(tmp));
result = tmp;
});
return resu<
}
Как упростить / реорганизовать?
ОБНОВЛЕНИЕ: я должен был предоставить больше справочной информации. Я поигрался с функциональным программированием на c # после просмотра сеанса JavaScript более высокого порядка и оскорбительного сеанса c # Джона в Oredev.
Цель состоит в том, чтобы сгенерировать html.
var TABLE = WrapWith("TABLE");
var TR = WrapWith("TR");
var TD = WrapWith("TD");
const string expected = "<TABLE><TR><TD>1</TD></TR><TR><TD>2</TD></TR></TABLE>";
var result = TABLE(stringArray.WrapEachWith(TR, TD));
result.ShouldEqual(expected);
static Func<String, String> WrapWith(string element)
{
var startTag = '<' element '>';
var endTag = "</" element '>';
return s => startTag s endTag;
}
Комментарии:
1. Рекурсия? Что-то вроде
applyMany s [] = s
;applyMany s (f:fs) = applyMany (f s) fs
в C #?2. Просто из любопытства, каковы ваши доводы в пользу использования рекурсии для этого?
3. смотрите обновление в моем вопросе, я пытаюсь обернуть элементы в HTML, используя список элементов, с помощью которых я хочу его обернуть
4. Я допустил ошибку в обновлении вопроса, теперь у него есть допустимый код.
5. Вы не генерируете HTML таким образом. Точно так же, как вы не разбираете XML вручную, не говоря уже о регулярных выражениях. Проблема слишком сложна и уже решена, чтобы вы могли возиться с ней вручную.
Ответ №1:
Мне кажется, что вы делаете четыре вещи:
- Преобразование каждого элемента в строку
- Применение функций по очереди
- Применение этой составной функции к каждой строке в последовательности
- Объединение результатов вместе (неэффективно)
Я бы выделил эти четыре аспекта — в частности, string.Join
работает достаточно хорошо для четвертой части и Enumerable.Select
выполняет третью.
Я бы также избегал изменения порядка операций — я бы ожидал, что первая операция, которую я укажу, будет первой, примененной лично.
Итак, я бы переписал этот метод, чтобы возвращать a Func<string, string>
, который затем можно было бы использовать с Select
и Join
. Например:
public static Func<string, string> Compose(params Func<string, string> funcs)
{
return input => {
string current = input;
foreach (var func in funcs)
{
current = func(current);
}
return current;
};
}
Вы могли бы, конечно, сделать это само по себе универсальным, с подписью:
public static Func<T, T> Compose(params Func<T, T> funcs)
Затем вы могли бы вызвать его с помощью чего-то вроде:
var composite = Compose<string>(FirstFunction, SecondFunction, ThirdFunction);
var query = string.Join("", items.Select(x => x.ToString())
.Select(composite));
Ответ №2:
public static string WrapEachElementWith
( string input,
params Func<string, string>[] func )
{
foreach (var f in func.Reverse())
input = f(input);
return input;
}
Не уверен, зачем вам нужен параметр шаблона, все функции отображают строку в строку, верно?
Обратите внимание, что Each
расширения IEnumerable
нет, поэтому вам придется прибегнуть к foreach
или написать свое собственное Each
.
Редактировать:
ваш код фактически применяет эту функцию ко всем значениям из списка, поэтому фактический код будет выглядеть примерно так:
public static string F<T>
( this IEnumerable<T> target,
params Func<string, string>[] func )
{
target.Select(item => WrapEachElementWith(item.ToString(), func))
.Aggregate((sum, cur) => sum cur);
}
Как уже упоминал @Jon, суммирование таким образом довольно неэффективно, поэтому вы, возможно, хотели бы сформулировать это так:
string.Join("", target.Select(
item => WrapEachElementWith(item.ToString(), func)));
Комментарии:
1. Каждый входной сигнал равен T, и toString вызывается один раз для начала.
2. @Jon: Я предположил, что это вызывается по одному для каждого
target
элемента.
Ответ №3:
Этот парень написал целый трассировщик лучей, используя LINQ. Я не очень внимательно изучал его код, но он описывает использование техники, называемой «Y-combinator», для создания рекурсии в операторе LINQ. Он ссылается на это сообщение в блоге, в котором дается подробное описание этих рекурсивных лямбда-выражений.
Я не знаю, действительно ли это то, что вы ищете, но это может помочь вам встать на правильную почву.