This project has moved and is read-only. For the latest updates, please go here.

List gets evaluated too early

Jul 25, 2014 at 10:50 AM
Edited Jul 25, 2014 at 2:32 PM
Hello!

First of all your Library is great and helps me a lot with my bachelor thesis.

While using your Expression Evaluator, I found a strange problem:
var values = new List<IImportedValue>();

for (DateTime date = endDate; date <= startDate; date = date.AddDays(1))
{
  var value = GetByDate(istWerte, date) ?? GetByDate(planWerte, date);
  
  if (value != null)
  {
    values.Add(value);
  }
}

var trend = Trend(values, ForecastDatum);

Result = (double)values.Count;
When executing this Expression my Trend() function gets passed an empty values-list, but my Result in the end is 27, so my values-list is not empty at all. How is this possible? Is the list evaluated to early?
Jul 25, 2014 at 2:05 PM
Hi!
You are using the (non-specified) variable "werte" to get the Result/Count value instead of "values.Count".
Jul 25, 2014 at 2:33 PM
Edited Jul 25, 2014 at 2:33 PM
Hey!

Thanks for the fast replay. Thats actually not the problem, I translated my vaiables for you into English and forgot this one. I edited my first post and corrected the mistake.
Jul 25, 2014 at 2:36 PM
Ok, that is strange, could it be that you are modifying the contents of the list inside of the Trend() method?
That would mean that "values" would change as well since it is a reference type, i.e. the data passed into Trend() is not a copy, it is the same list.
Jul 25, 2014 at 2:49 PM
This was a nice idea, but unfortunately I do not modify the list in my Trend() function. The Trend function just uses the method of least squares:
public double Trend(IList<IImportedValue> werte, DateTime dateToTrend)
        {
            double xAvg = werte.Sum(wert => wert.Datum.Ticks >> 23) / Convert.ToDouble(werte.Count);
            double yAvg = werte.Sum(wert => wert.Menge) / Convert.ToDouble(werte.Count);

            double v1 = 0;
            double v2 = 0;

            foreach (var wert in werte)
            {
                v1 += ((wert.Datum.Ticks >> 23) - xAvg) * (wert.Menge - yAvg);
                v2 += Math.Pow((wert.Datum.Ticks >> 23) - xAvg, 2);
            }

            double a = v1 / v2;
            double b = yAvg - (a * xAvg);

            return (a * (dateToTrend.Ticks >> 23)) + b;
        }
Another strange thing: even if i change the last to lines form my first post, the results stays the same. Could there be a problem with the automatic optimization of the code?
Jul 25, 2014 at 3:04 PM
Strange indeed. I cannot see the problem, however a small side note; you are iterating from EndDate to StartDate but increment by 1, not -1.

Anyway, I tried pretty much the same code here and everything works as expected, no empty list.
Unfortunately I cannot think of any solution, or a reason your code would behave like that.
Jul 25, 2014 at 3:15 PM
Thank you for that hint, but my startDate lies in the future and my endDate in the past, so everything is fine here.

I worked around that problem by extracting the code for generating the values-list into a hard code scope function. This works for me, but does not solve this strange behaviour.

Thanks JakobMollas for your help. Maybe someone else has another idea, where the source of this problem is hiding.
Jul 29, 2014 at 12:38 PM
Hi,

If values is empty when Trend() is called, would it mean the results of Trend are therefore incorrect? It's strange behavior indeed. Perhaps something to do with how I implemented the for-loop. I will try to replicate your problem.
Jul 29, 2014 at 5:28 PM
Edited Jul 29, 2014 at 5:29 PM
Hey!

Yes, the results are incorrect. As I wrote earlier I could work around the problem by hard-coding the list creation and filling into a scope method, which I call in my expression.

Another thought I got during implementation: The variables istWerte and planWerte are ILists created by Entity Framework. This could be the source of this behaviour as well.
Jul 30, 2014 at 6:40 AM
Edited Jul 30, 2014 at 6:42 AM
Hi,

I was able to reproduce your problem, and find the cause and a temporary solution. I am probably not handling variable instantiation using functions properly.

This will fail:
var trend = Trend(values, ForecastDatum);
However, this will work:
double trend; 
trend = Trend(values, ForecastDatum);
Jul 30, 2014 at 6:58 AM
Edited Jul 30, 2014 at 6:59 AM
For those interested, this is the Expression Tree generated if the instantiation of the variable using a function occurs on one line:

Actual Expression
var x = new List<IImportedValue>();

for(int i = 0; i < 27; i++)
{
    x.Add(new ImportedValue());
}

Console.WriteLine(x.Count);

int z = Trend(x);

x.Count;
Expression Tree
.Block(
    System.Collections.Generic.List`1[Tests.IImportedValue] $x,
    System.Int32 $z) {
    $x = .New System.Collections.Generic.List`1[Tests.IImportedValue]();
    $z = .Call $scope.Trend($x);
    .Block(System.Int32 $i) {
        $i = 0;
        .Loop  {
            .Block() {
                .If (
                    !($i < 27)
                ) {
                    .Goto #Label1 { }
                } .Else {
                    .Default(System.Void)
                };
                .Block() {
                    .Call $x.Add(.New Tests.ImportedValue())
                };
                .Label
                .LabelTarget #Label2:;
                $i = .Increment($i)
            }
        };
        .Label
        .LabelTarget #Label1:
    };
    .Call System.Console.WriteLine($x.Count);
    $x.Count
}
As you can see the following line is called even before the for loop code block is executed:
$z = .Call $scope.Trend($x);
I am pulling all variables to the top of the code block and instantiating them there, so in fact the list was being evaluated too early.

Thanks for bringing this up!
Jul 30, 2014 at 7:16 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Jul 30, 2014 at 10:31 AM
This issue has been fixed with the latest commit (3d4a6e258c24662316dea21fb7a44ac71a14e00b)