我正在尝试为具有任意数量参数的函数创建一个记忆接口,但我失败了,我觉得我的解决方案不是很灵活。我试图为一个函数定义一个接口,该接口在执行时会自动记忆,每个函数都必须实现这个接口。这是一个带有两个参数的指数移动平均函数的示例:
class EMAFunction:IFunction
{
Dictionary<List<object>, List<object>> map;
class EMAComparer : IEqualityComparer<List<object>>
{
private int _multiplier = 97;
public bool Equals(List<object> a, List<object> b)
{
List<object> aVals = (List<object>)a[0];
int aPeriod = (int)a[1];
List<object> bVals = (List<object>)b[0];
int bPeriod = (int)b[1];
return (aVals.Count == bVals.Count) && (aPeriod == bPeriod);
}
public int GetHashCode(List<object> obj)
{
// Don't compute hash code on null object.
if (obj == null)
{
return 0;
}
List<object> vals = (List<object>) obj[0];
int period = (int) obj[1];
return (_multiplier * period.GetHashCode()) + vals.Count;
}
}
public EMAFunction()
{
NumParams = 2;
Name = "EMA";
map = new Dictionary<List<object>, List<object>>(new EMAComparer());
}
#region IFunction Members
public int NumParams
{
get;
set;
}
public string Name
{
get;
set;
}
public object Execute(List<object> parameters)
{
if (parameters.Count != NumParams)
throw new ArgumentException("The num params doesn't match!");
if (!map.ContainsKey(parameters))
{
//map.Add(parameters,
List<double> values = new List<double>();
List<object> asObj = (List<object>)parameters[0];
foreach (object val in asObj)
{
values.Add((double)val);
}
int period = (int)parameters[1];
asObj.Clear();
List<double> ema = TechFunctions.ExponentialMovingAverage(values, period);
foreach (double val in ema)
{
asObj.Add(val);
}
map.Add(parameters, asObj);
}
return map[parameters];
}
public void ClearMap()
{
map.Clear();
}
#endregion
}
这是我对该功能的测试:
private void MemoizeTest()
{
DataSet dataSet = DataLoader.LoadData(DataLoader.DataSource.FROM_WEB, 1024);
List<String> labels = dataSet.DataLabels;
Stopwatch sw = new Stopwatch();
IFunction emaFunc = new EMAFunction();
List<object> parameters = new List<object>();
int numRuns = 1000;
long sumTicks = 0;
parameters.Add(dataSet.GetValues("open"));
parameters.Add(12);
// First call
for(int i = 0; i < numRuns; ++i)
{
emaFunc.ClearMap();// remove any memoization mappings
sw.Start();
emaFunc.Execute(parameters);
sw.Stop();
sumTicks += sw.ElapsedTicks;
sw.Reset();
}
Console.WriteLine("Average ticks not-memoized " + (sumTicks/numRuns));
sumTicks = 0;
// Repeat call
for (int i = 0; i < numRuns; ++i)
{
sw.Start();
emaFunc.Execute(parameters);
sw.Stop();
sumTicks += sw.ElapsedTicks;
sw.Reset();
}
Console.WriteLine("Average ticks memoized " + (sumTicks/numRuns));
}
更新:
感谢您指出我的 n00bish 错误...我总是忘记在秒表上调用 Reset!
我也看到了另一种记忆方法......它不提供 n 参数记忆,但我使用接口的方法并没有多大优势,因为我必须为每个函数编写一个类。有没有一种合理的方法可以将这些想法合并成更强大的东西?我想让记住一个函数更容易,而不是让用户为他们打算使用的每个函数编写一个类。