Recursively execute funcs in list
Given a list of Func<string, string>
, is it possible to write a statement that iterates through the list and returns the result like so:
string result = f1(f2(f..(input));
I have the following code (that works), but I'm not satisfied with the temporary variable.
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 result;
}
How to simplify / refactor?
UPDATE: I should have provided more background. I'm playing around with Functional programming in c# after seeing higher order JavaScript session and John's abusive c# session at Oredev.
The aim is to generate 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 en开发者_运维技巧dTag = "</" + element + '>';
return s => startTag + s + endTag;
}
It looks to me like you're doing four things:
- Converting each item to a string
- Applying the functions in turn
- Applying that composite function to each string in a sequence
- Joining the results together (inefficiently)
I would separate out those four aspects - in particular, string.Join
works well enough for the fourth part, and Enumerable.Select
does the third one.
I would also avoid reversing the order of the operations - I would expect the first operation I specify to be the first one applied, personally.
So, I would rewrite this method to return a Func<string, string>
which could then be used with Select
and Join
. For example:
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;
};
}
You could, of course, make this generic itself, with a signature of:
public static Func<T, T> Compose(params Func<T, T> funcs)
You would then call it with something like:
var composite = Compose<string>(FirstFunction, SecondFunction, ThirdFunction);
var query = string.Join("", items.Select(x => x.ToString())
.Select(composite));
public static string WrapEachElementWith
( string input,
params Func<string, string>[] func )
{
foreach (var f in func.Reverse())
input = f(input);
return input;
}
Not sure why you need template parameter, all the functions map string to string, right?
Note that there's no Each
extension of IEnumerable
, so you'll have to resort to foreach
or write your own Each
.
Edit:
your code actually applies this function to all the values from the list, so the actual code would be something like:
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);
}
As @Jon already mentioned, summing up this way is pretty inefficient, therefore you perhaps would like to put it this way:
string.Join("", target.Select(
item => WrapEachElementWith(item.ToString(), func)));
This guy wrote an entire ray tracer using LINQ. I haven't looked at his code that closely, but he describes using a technique called a "Y-combinator" to create recursion in a LINQ statement. He references this blog posting, which gives a detailed description of these recursive lambda expressions.
I don't know if that's quite what you're looking for, but it might get you off on the right footing.
精彩评论