Reputation: 3434
I'd like to parse a string into a lambda expression in C#,
for example, parse "lt 5"
to x => x < 5
so I can use it as the argument of Enumerable.Where
:
static void Main(string[] args)
{
Enumerable
.Range(0,10)
.Select(x => (double)x)
.Where(Parse("lt 5"))
.ToList()
.ForEach(System.Console.WriteLine);
}
private const List<Tuple<List<string>, Func<double, Func<double, bool>>>> operationList =
new Dictionary<string, Func<double, Func<double, bool>>>()
{ { "lt <", val => ( x => x < val ) }
, { "le <=", val => ( x => x <= val ) }
, { "eq = ==" , val => ( x => x == val ) }
, { "ne != <>", val => ( x => x != val ) }
, { "ge >=", val => ( x => x >= val ) }
, { "gt >", val => ( x => x > val ) } }
.Select(kv =>
new Tuple<
List<string>,
Func<double, Func<double, bool>>>(
kv.Key.Split(' ').ToList(),
kv.Value))
.ToList();
public static Func<double, bool> Parse(string raw)
{
var fields = raw.Split(' ');
var predKey = fields[0].ToLower();
var predBuilder = operationList.FirstOrDefault(tp =>
tp.Item1.FirstOrDefault(key => key.Equals(predKey)) != null);
return predBuilder.Item2(double.Parse(fields[1]));
}
First I made a operationList
that stores a tuple where Item1
describes all aliases to the operator and Item2
stores a curried version of the operator.
Then Parse
will split string lt 5
into ["lt","5"]
and search the operation according to key lt
.
Finally, if I can find the operation, 5
will be partial applied to it and the result will be a lambda expression x => x < val
.
However, the compiler was mad at { "lt <", val => ( x => x < val ) }
and complained "Expression cannot contain anonymous methods or lambda expressions".
I have no idea about what it means and how to get things work.
Upvotes: 0
Views: 539
Reputation: 23636
change const
to readonly
and make it static
, like so:
private readonly static List<Tuple<List<string>, Func<double, Func<double, bool>>>> operationList =
new Dictionary<string, Func<double, Func<double, bool>>>()
{ { "lt <", val => ( x => x < val ) }
, { "le <=", val => ( x => x <= val ) }
, { "eq = ==" , val => ( x => x == val ) }
, { "ne != <>", val => ( x => x != val ) }
, { "ge >=", val => ( x => x >= val ) }
, { "gt >", val => ( x => x > val ) } }
.Select(kv =>
new Tuple<
List<string>,
Func<double, Func<double, bool>>>(
kv.Key.Split(' ').ToList(),
kv.Value))
.ToList();
In C# const keyword is used to denote compile time constants, but in your example list is calculated at runtime using LINQ.
In C# const
values are always static and making it readonly will change field's scope to instance.
Note, that contents of you list might change during execution and readonly
won't stop you from removing and adding Tuples into a list. Consider using ReadOnlyCollection (it's still won't stop you from modifying it, just will make it harder) or using Immutable Collections (which aren't part of .net BCL)
Upvotes: 1