På en tidligere version af dotninjas.dk kunne man finde en artikel om hvordan man kompilerer et udtryk til funktioner som kan bruges som plugin i egne beregninger - ved at benytte den indbyggede C# compiler. Jeg vil forsøge at genopfriske lidt for artiklen, men vil denne gang compile Lambda udtryk som f.eks.

   1:  x => x * 2;

Vores specialiserede interface erstattes nu af Expression<T> (i dette tilfælde Expression<Func<double, double>>). Dette er naturligvis intressant hvis man ønsker at arbejde videre med expression træet, f.eks. hvis man ønsker at differentiere udtrykket. Hvis man blot ønsker at beregne funktionsværdier anbefaler jeg at man returnerer Func<double, double> delegaten direkte, idet der skal kaldes Compile på et lambda udtryk for at få delegaten vi skal evaluere med - man kompiler altså i to omgange.

Grundlæggende er det dette stykke kode vi ønsker at compile runtime:

   1:  using System; 
   2:  using System.Linq.Expressions; 
   3:   
   4:  public static class LambdaParser 
   5:  { 
   6:    public static Expression<Func<double,double>> Parse() 
   7:    { 
   8:      return {expression}; 
   9:    } 
  10:  } 

Vi skal blot erstatte {expression} med det ønskede udtryk som skal være en funktion med et enkelt argument af type double og return værdi double, eller f:R->R som man siger på matematisk. Man kunne også lægge sig fast på at variablen hed x og erstatte {expression}; med x=>{expression};

Jeg har valgt C# men man kan også vælge VB eller et andet sprog hvis blot man har en passende CodeDomProvider.

   1:  CSharpCodeProvider provider = new CSharpCodeProvider(); 

Brug evt. en Dictionary med CompilerVersion="v3.5" hvis du har behov for en specific version, ellers brug config filen. Ellers skal vi blot definere vores referencer og kompile koden:

   1:  CompilerParameters cp = new CompilerParameters(); 
   2:  cp.GenerateInMemory = true; 
   3:  cp.ReferencedAssemblies.Add("System.Core.dll"); 
   4:   
   5:  CompilerResults cr = provider.CompileAssemblyFromSource(cp, code); 

Husk at checke for kompilefejl (se cr.Errors.HasErros), for der bliver ikke kastet en exception. Herfra er der 3 linjer kode til vi står med vores lambda udtryk:

   1:  Type parserType = cr.CompiledAssembly.GetType("LambdaParser"); 
   2:  MethodInfo mi = parserType.GetMethod("Parse"); 
   3:  var f = (Expression<Func<double,double>>)mi.Invoke(null, new object[] { })); 

Jeg lader det være op til læseren at indkapsle koden i en genbrugelig klasse og tilføje bells and whistles. Ekstra referencer vil være en oplagt mulighed for udvidelser. En anden oplagt mulighed er at indføre en potens funktion hvad man skal bruge matematiske formler. Det kræver dog at man parser udtrykket lidt mere end blot en søg og erstat, f.eks.

   1:  x+x^2 -> x + Math.Pow(x, 2) 

Her skal Math.Pow funktionen indsættes efter plus tegnet, mens den skal indsættes efter parentens i dette udtryk

   1:  (x+1)^2 -> Math.Pow(x+1,2) 

Heldigvis skal de øvrige operatorer behandles ens, så det kun er parenteser vis skal ta' højde for.

   1:  public static string ReplacePower(string s) 
   2:  { 
   3:    int powerIndex = s.IndexOf('^'); 
   4:    if (powerIndex > 0) 
   5:    { 
   6:      string right = s.Substring(powerIndex + 1); 
   7:      string left = s.Substring(0, powerIndex); 
   8:   
   9:      int leftStart; 
  10:      int rightEnd; 
  11:   
  12:      if (left[left.Length - 1] == ')') 
  13:      { 
  14:        leftStart = FindStartParenthesis(left) - 1; 
  15:      } 
  16:      else 
  17:      { 
  18:        leftStart = left.LastIndexOfAny(opChars); 
  19:      } 
  20:      if (right[0] == '(') 
  21:      { 
  22:        rightEnd = FindEndParenthesis(right); 
  23:      } 
  24:      else 
  25:      { 
  26:        rightEnd = right.LastIndexOfAny(opChars); 
  27:      } 
  28:   
  29:      string s0 = left.Substring(0, leftStart + 1); 
  30:      string s1 = left.Substring(leftStart + 1); 
  31:      string s2 = rightEnd >= 0 ? right.Substring(0, rightEnd) : right; 
  32:      string s3 = rightEnd >= 0 ? right.Substring(rightEnd) : string.Empty; 
  33:   
  34:      return ReplacePower(string.Format("{0}Math.Pow({1},{2}){3}", s0, s1, s2, s3)); 
  35:    } 
  36:    return s; 
  37:  } 
  38:   
  39:  private static char[] opChars = new char[] { '+', '-', '/', '*', ' ', ',' }; 
  40:   
  41:  private static int FindStartParenthesis(string s) 
  42:  { 
  43:    int index = s.Length - 1; 
  44:    int parCount = 0; 
  45:   
  46:    while (index >= 0) 
  47:    { 
  48:      if (s[index] == ')') parCount++; 
  49:      if (s[index] == '(') parCount--; 
  50:      if (parCount == 0) return index; 
  51:      index--; 
  52:    } 
  53:    return index; 
  54:  } 
  55:   
  56:  private static int FindEndParenthesis(string s) 
  57:  { 
  58:    int index = 0; 
  59:    int parCount = 0; 
  60:   
  61:    while (index < s.Length) 
  62:    { 
  63:      if (s[index] == ')') parCount--; 
  64:      if (s[index] == '(') parCount++; 
  65:      if (parCount == 0) return index; 
  66:      index++; 
  67:    } 
  68:    return index; 
  69:  } 

Med FindEndParenthesis metoden skulle det også være nemt at erstatte diverse matematisk funktioner
med deres System.Math ekvivalenter som cos -> Math.Cos og sin -> Math.Sin.

Det var lide kode til at komme igang med din egen runtime expression parser - uden at der er nogen garanti for at det virker. Husk i det mindste at indsætte diverse checks så det ikke brager ned midt i en vigtig beregning.

Her i sommervarmen var det måske på tide at lege lidt med lambda udtryk: 

Expression<Func<double,double>> f = x => x + 1;

Hvis vi ser på lambda udtryk med matematiske briller vil vi ofte gerne differentiere udtrykket for f.eks. at finde maksimum eller nulpunkter. Differentialregning er heldigvis noget med en masse regler og så hårdt arbejde for resten - hvilket jo er computerens speciale. Men hvor nemt er det egentligt at differentiere et lambda udtryk?

Ovenstående udtryk parses til en Expression-træstruktur, som er et standard abstrakt syntaks træ. De enkelte noder i træer repræsenterer operatorer (plus, minus, gange og dividere, men også større end og mindre end), tal (konstanter) og symboler (også kaldet parametre som x ovenfor). Der er en helt række af expression typer (se ExpressionType for komplet liste), men noget kortere liste at klasser som nedarver Expression.

Mange ExpressionType værdier bruger f.eks. BinaryExpression - det gælder bl.a. Add, Subtract, Multiply og Divide, men også diverse boolske operatorer som f.eks. LessThan og GreaterThan (bemærk dog at disse returner bool og ikke double).

Expression finder du i System.Linq.Expression men benyttes også af Dynamic Language Runtime (DLR). Det er specielt udtryk til at styre flow i et program og tildeling af værdier som benyttes af DLR, men det vil vi ignorere i et matematisk setup.

Vi vælger at implementere Derive som en extension metode:

public static Expression Derive(this Expression e, string parameterName)
{
  switch (e.NodeType)
  {
    case ExpressionType.Add:
      ...
      break;
    
    ...
  }
}

Vi skal naturligvis implementere regler for alle værdier ExpressionType som giver matematisk mening, men først lige en metode mere.

public static Expression Derive(this Expression e, string parameterName)
{
  return Expression.Lambda(e.Body.Derive(parameterName), e.Parameters);
}

Denne metode sikrer at vi kan benytte Derive på LambdaExpression og at resultat også bliver en LambdaExpression. Vi har altså vores afledte giver som:

Expression> df = f.Derive("x");

I vores eksempel indgår tre node typer: Parameter, Constant og Add

case ExpressionType.Parameter:
  {
    ParameterExpression pe = (ParameterExpression)e;
    return Expression.Constant(pe.Name == parametername ? 1.0 : 0.0)
  }
  
case ExpressionType.Constant:
  return Expression.Constant(0.0);
  
case ExpressionType.Add:
  {
    BinaryExpressiob be = (BinaryExpression)e;
    return Expression.Add(
      be.Left.Derive(parameterName),
      be.Right.Derive(parameterName)
    );
  }

Hvilket giver os

df = x => 1 + 0;

Bemærk, at vi arbejder med doubles hele vejen igennem, ellers fejler programmet runtime ved at der ikke findes en add-operator som tager int som det ene argument og double som det andet. Resultat fra vores Derive metode er korrekt, men ikke specielt pænt at se på og måske heller ikke optimalt rent beregningsmæssigt. Til det formål foreslår jeg endnu en ekstension metode:

public static Expression Simplify(this Expression e)
{
  ...
}

Men den øvelse gemmer vi til en anden god gang - der er endnu meget arbejde på Derive.

En anden ting man bør bemærke er at der oprettes nye Expression når man skal redigere et udtryk. Det er et underliggende princip at Expression instanserne ikke kan ændres (de er immutable), hvilket man udnytter til Multiply operatoren i Derive:

case ExpressionType.Multiply: // (fg)'=f'g+fg'
  {
    BinaryExpression be = (BinaryExpression)e;
    Expression dleft = be.Left.Derive(parameterName);
    Expression dright = be.Right.Derive(parameterName);
    return Expression.Add(
      Expression.Multiply(dleft, be.Right),
      Expression.Multiply(be.Left, dright)
    );
  }

Vi har altså genbrugt undernoderne i BinaryExpression til et nyt udtryk. Jeg vil overlade det til læseren at implementere Subtract, Divide og Negate (UnaryExpression).

For funktions kald findes typen Call og klassen MethodCallExpression som med Member (MethodInfo) giver os en reference til den funktion som skal kaldes. Det er op til os at implementere den afledte af forskellige kendte funktioner, f.eks.

(Math.Sin)':
  return Expression.Call(null, typeof(Math).GetMethod("Cos"), me.Arguments);
(Math.Cos)':
  return Expression.Negate(Expression.Call(null, typeof(Math).GetMethod("Sin"),   me.Arguments);

hvor me er vores originale MethodCallExpression og me.Arguments således er de originale argumenter til funktionskaldet - jeg lader det være op til læseren at implementere kædereglen ((f(g(x)))'=f'(g(x))*g'(x)).

Endnu en stor ting fra en matematisk vinkel er at implementere potensreglen. C# har ikke nogen potens operator (^ eller ** i andre sprog), så vi skal se på ExpressionType.Call hvor metoden er Math.Pow. Den generelle potens regel er (f^g)'=f^g*(g'ln f+(g/f)*f'), men hvis g er et tal så er det den noget simplere udgave (x^n)'=n*x^(n-1):

Expression e1 = Expression.Multiply(
  me.Arguments[1], // n
  Expression.Power(
    me.Arguments[0], // x
    Expression.Subtract(me.Arguments[1], Expression.Constant(1.0)) // -1
    )
  );

dertil kommer så kædereglen:

return Expression.Multiply(e1, me.Arguments[0].Derive(parameterName));

Bemærk, at Expression.Power(...) funktionen giver os ExpressionType.Power, så hvis resultat skal differentieres igen, så skal vi også tage højde for det. Alternativt kan man benytte Expression.Call med metoden me.Method.

Det skulle være lidt til at komme igang med noget differentialregning. Heldigvis skal man "bare" følge nogle matematiske regler for at få resultatet - som måske kræver en del simplifisering for at se pænt ud, men kompileren dømmer os ikke på skønhed.

Så er det igen tid til at komme ud af byen. Påsken er en god anledning til at finde dit kamera frem og ta' med dotninja'erne ud i Dyrehaven

Torsdag d. 1. april 2010 (Skærtorsdag)

Alle er (som altid) velkomne til at joine med deres ynglings kamera (eller bare et de har lånt), det er ikke en aprilsnar (Aprilsnar vil generelt være bandlyst under hele arrangementet). For de interesserede kan der blive tale om at returnere til Frederiksberg for et måltid mad efter arrangement.

Skriv en kommentar om du kommer og om du vil være interesseret i mad. Mødested/tid aftaler vi nærmere når vi nærmer os.

Indspireret af en artikel om Effect Sketches, og med hjælp fra Reflector og Graphviz, har jeg forsøgt at få et overblik over min kode. Spørgsmålet er så om det lykkedes, her er en klasse:

Kan man gætte hvad det skal forestillet? Evt. med inspiration i artiklen? Efter at jeg har set på det et stykke tid giver det god mening for mig. Jeg kan f.eks. se at yearFraction ikke er helt færdig implementeret -- men sourcekoden er også taget fra beta.

I forbindelse med opgraderingen af Dotninjas har kommentarerne brugt deres ninja-skill til at forsvinde. Grundet finanskrisen kan der gå lidt tid før end vi får dem tilbage… Men der arbejdes på sagen.

Update: Kommenater skulle nu være tilbage… go nuts!

Ninjaerne tager deres gadgets (i dette tilfælde deres kamera) med til Vallø slot

Søndag den 11. Oktober 2009

Alle er velkomne til at joine. Vi går en tur, ta’r nogle billeder og måske snupper vi en bid brød. Der ville være en præmie til dagens bedste billede, men desværre har ingen af os skrevet en bog. t4rzsan besøgte slottet sidste år og tog bl.a. dette billede:

DSC_0254

Så tag med og få gode råd om hvordan du ta’r de gode billeder, eller vis os at du er meget bedre, eller fordi du trænger til at komme ud af byen og få lidt frisk luft. Det giver også mening at ta’ med selvom ud ikke har et DSLR 1000++ med SuperZoom XT Q34 med overloader. Du kan sagtens klare dig med et kompakt kamera.

Smid en kommentar hvis du vil med, så ser vi på transporten.

Dette indlæg er et afsnit i serien om Continuous Integration.

Nu er der jo ikke meget sjov ved bare at check koden ud, så lad os få bygget noget. Vi starter med at tilføje en task:

<tasks>
  <nant>
    <baseDirectory>d:\dev\framework</baseDirectory>
    <executable>d:\dev\tools\nant\nant.exe</executable>
    <buildFile>framework.build</buildFile>
    <targetList>
      <target>clean</target>
      <target>compile-tests</target>
    </targetList>
  </nant>
</tasks> 

Task elementer kan indeholde mange forskellige opgaver hvor vi her har valgt at køre et NAnt script. Man kunne vælge at bruge MSBuild til at bygge VS solutions med, men jeg flytter nogle ektra ting ned i build scriptet som også kan køres på udvikler maskiner. Mit NAnt script kalder så MSBuild scripts, så du har egentlig tre niveauer for hvor du skal bygge:
CC, NAnt, MSBuild (Tilføj selv flere efter behov). Hvilket niveau du vælger afhænger af dine ønsker, men jeg fortrækker at man har mulighed for at kontrollere både build og unit-tests inden man checker kode ind i source kontrol systemet.

Dette script bygger to target: Et clean target (så vi slipper af med gammelt bras) og et compile target som bygger unit test, men som afhænger af at frameworket et bygget. Tilføj selv flere targets efter behov.

Hvis du har installere på en frisk maskine uden Visual Studio, så løber du nok ind i problemer med NAnt. Problemer udmønter sig i brok over at installDir for net-2.0 ikke er angivet og det er selvom du tvinger den til at bruge net-3.5. For at løse det finder du din NAnt.exe.config fil og søger efter readregistry elementer. Attributen sdkInstallRoot skal rettes. I mit tilfælde (jeg bruger Windows SDK 6.1) til: SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1\WinSDKNetFxTools\InstallationFolder. Det skal lige gøres et par steder, så kører det. Dette kan dog være løst i en nye version af NAnt når du læser dette. Jeg har taget udgangspunkt i TreeSurgeon som giver dig både et buildscript og de tilhørende filer.

Når vores projekter er bygget kan vi kopiere bygget til vores filserver med denne lille build publisher:

<buildpublisher>
  <sourceDir>d:\dev\framework\build</sourceDir>
  <publishDir>\\fileserver\framework</publishDir>
</buildpublisher>

Så er der altid adgang til det nyeste build.

Når nu det hele kører så mangler vi bare den automatiske del. Til det formål har CC en række triggers hvoraf ForceBuildTrigger er default. Det mest oplagte for CI er at indsætte en IntervalTrigger i triggers blokken:

<triggers>
  <intervalTrigger name="Continuous" seconds="60" />
</triggers> 

Med denne trigger vil CC checke subversion hvert minut. Hvis der er sket ændringer vil den sætte gang i et nyt build. Et natligt rebuild kan tilføjes med en ScheduleTrigger

<scheduleTrigger time="23:30" buildCondition="ForceBuild" name="Scheduled" /> 

Bemærk at buildCondition er sat til ForceBuild mod default at være IfModificationExists. Dette kan også bruges for intervalTrigger hvis man skulle få brug for det. Der er mange muligheder for at tilpasse triggers, så f.eks. der ikke bygges på bestemte tidspunkter med FilterTrigger eller at ScheduleTrigger kun bygger på hverdage. Se mere under http://confluence.public.thoughtworks.org/display/CCNET/Trigger+Blocks

Nu er det bare at checke noget nyt kode ind og vente på at de dit byg (hvis du altså ikke vil vente til kl 23.30).

Dette indlæg er et afsnit i serien om Continuous Integration.

For at få vores Continuous Integration til at spille skal vi bruge et source kontrol system. Hvis du allerede har et source control system kan du springe de næste to afsnit over.

Som source control system har jeg valgt subversion og til serveren er valgt VisualSVN Server. VisualSVN laver også et plugin til Visual Studio, men modsat serveren, så skal du betale for det (i skrivende stund er prisen $49). Alternativt kan du bruge AnhkSVN, eller hvis du vil klare ærterne udenfor VS, så TortoiseSVN. Bemærk dog at der er forskel på SVN 1.5 og 1.6. Jeg løb ind i nogle uløselige "tree conflicts", så pas på med versionerne - Specielt hvis du får flyttet eller kopieret SVN folderen i dit checkout. Dette kan dog være rettet når du læser dette.

Efer at ha' installeret Visual SVN Server vælger du 'VisualSVN Server Manager' fra startmenuen. Højreklik på roden i træstruktion, vælg options og vælge hvor du vil ha' dit repository til at ligge. Her kan du også vælge mellem Windows og subversion authentication og hvordan du vil tilgå dit repository. Opret dit repository (gerne med den anbefalede trunk/brances/tags struktur) og evt. brugere. Der er en kort beskrivelse (om end noget længere end min) af opsætningen proceduren på VisualSVNs hjemmeside. Lav et checkout på din udvikler maskine, fyld nogle filer i og commit ændringerne.

Nu skal vi så igang med CC. Programmet kører som default ikke efter det er blevet installeret. Det skal først konfigureres og det er meget nemmere at bruge CC console programmet som der ligger et link til på dit skrivebord. Du kan sagtens starte consolen og se programmet køre. Når du opdaterer konfigurationen indlæses den nye fil automatisk. Efter at konfigurationen er kommet på plads installeres services ved at køre installutil på ccservice.exe i server folderen.

Indtil videre starter vi blot CC ved at bruge linket på skrivebordet og finder ccnet.config i server folderen (der er også en ccnet.exe.config som kan bruges til at styre f.eks. logging). Det er en simple xml fil som indeholder alle de projekter din build server skal bygge. Lad os start med et enkelt projekt og lad os gi' vores projekt et navn:

<cruisecontrol>
  <project>
    <name>Framework</name>
    <workingDirectory>d:\dev\framework</workingDirectory>
    <artifactDirectory>d:\dev\framework\artifacts</artifactDirectory>
  </project>
</cruisecontrol>

Da jeg var igang valgte jeg også lige at fortælle hvor jeg ville ha' projektet liggende, men ellers sker der ikke så meget der er værd at se. Appropos se, så kan du holde øje med projektet via dashboardet på http://bs/ccnet. Den kan melde nogle fejl, men de forsvinder efterhånden som du får konfigureret og bygget projektet. Dashboardet har også et link til CCTray som du kan installere på din udvikler maskiner og som navnet antyder viser CC status som tray icon.

Hvis du vil ha’ flere projekter tilføjer du bare yderligere projekt elementer i konfigurationsfilen. Men først må det være tid til at checke noget kode ud fra vores Subversion repository ved at tilføje et sourcecontrol element under project noden:

<sourcecontrol type="svn">
  <trunkUrl>http://bs:8080/svn/framework/trunk</trunkUrl>
  <executable>d:\Program Files\VisualSVN Server\bin\svn.exe</executable>
  <workingDirectory>d:\dev\framework</workingDirectory>
</sourcecontrol>

TrunkUrl og executable afhænger af din Subversion installation. Jeg har valgt at køre på samme server og bruge subversion authentication, men du kan gøre andre valgt. Det er også muligt at angive username og password elementer. Det er også muligt at vælge andre source kontrol systemer, men så kan der være andre detaljer som skal udfyldes.

Hvis du går ind i dashboarded og ser dit projekt kan du vælge "Force Build", hvorefter du skulle kunne se dit checkout i d:\dev\framework folderen hvis builded afsluttede succesfuldt. Build rapport giver også en oversigt over hvilke filer der er checket ind siden sidste build og hvilke beskeder der er attachet til checkins. Så ikke noget med at skrive junk beskeder når du checker ind. Hvis buildet fejlede af den ene eller anden grund (compilefejl, unit test fejl, etc.) så vi man også her kunne se hvem synderen er.

I næste indlæg skal vi se hvordan vores build rent faktisk får bygget noget.

Dette er starten på en lille artikelserie om Continuous Integration med Cruise Control .Net (herefter blot CC). Continuous Integration (herefter blot CI) stammer egentlig fra Extreme Programming, men bruges også af folk som ikke kender til XP. Der er mest tale om en proces som giver en række fordele hvis man bider ind i konceptet. Med denne artikel serie vil vi bruge en række værktøjer til at få

  • Vores .net projekter under et source kontrol system
  • Bygget koden automatisk når vi checker ændringer ind
  • Kørt vores unit-tests automatisk
  • Nemt overblik over status på vores builds

Målet er at man vælger at checke ændringer ind tidligt og ofte, fordi man hurtigt kan se effekten på hele projektet. Det kan betale sig lige at se Martin Fowler's Artikel om Continuous Integration hvis man ikke har kendskab til emnet. Ikke noget med at ha' flere moduler liggende på en udvikler computer som ta' nogle dage at integrere i resten af projektet.

Det kan lyde af meget for en hobby programmør, men selvom du (endnu) ikke skal arbejde sammen med andre end dig selv, så er dele af processen stadig en fordel. Versions styring bør være nemt nok til at alle kan bruge det og man bør alligevel få sig et build script så snart projektet vokser til flere solutions. Brug CI som inspiration i din process og ta' de ting med dig du kan bruge.

Til denne artikelserie skal du bruge:

Jeg har installeret CC på min WHS og det kræver derfor jeg installerer et Windows SDK. Hvis du bruger en maskine som har installeret Visual Studio, så har du sikkert allerede det SDK du skal bruge. Hvis ikke, kan du løbe ind i nogle problemer lige som mig, men dem løser vi. Hvis du bruger din WHS så husk at installere på D drevet.

Du kan lige så godt komme igang med at downloade programmerne og få dem installeret på din build server (lad os kalde den BS til ære for B.S.) med det samme. Jeg vil ikke gå videre i dybden med hvordan man bruger SVN, NAnt eller NUnit. Der findes mange spændende tutorials på nettet om de emner, du kan læse mens jeg arbejder på flere artikler i serien.

Listen over planlagte artikler ser således ud:

Når der kommer nye indlæg vil jeg linke til dem fra denne side.

Denne sommer bliver fyldt med 5 nye episoder af klassikeren Monkey Island med titlen Tales of Monkey Island.

[youtube:EHojfwdLucI&hl=en&fs=1,width=560,height=340]

Disse episoder skal ikke forveksles med The Secret of Monkey Island Special Edition som er et remake af det første Monkey Island spil til bl.a. Xbox Arcade. Det bliver helt nye eventyr for Guybrush Threepwood og kommer til PC og Wii som downloads i løbet af sommeren. De bliver skabt af en gruppe af tidligere Lucas Arts medarbejdere, dog ikke Ron Gilbert som kører sit eget projekt som beskrives som Monkey Island meets Diablo.

Jeg glæder mig ihvertfald til at få lidt gang i adventure genren igen og hvorfor ikke starte med kongen?