Maak, maar vooral houd je code leesbaar

Graag wil ik mij even voorstellen. Mijn naam is Patrick Smit en ik ben een FileMaker enthousiastelling. Ooit begonnen met MS Access en daar een leuk systeem mee gemaakt voor een toenmalige werkgever. Vervolgens door omstandigheden een tijd geen database werkzaamheden, maar wel altijd de kracht die daarvan uitging geuit tegenover een ieder die dat maar wilde horen en ook aan hen die dat niet inzagen c.q. begrepen. Vervolgens ben ik op een bepaald moment in 2010 met FMPro in aanraking gekomen en dat heeft mij sindsdien niet meer losgelaten.

Het onderwerp wat ik graag zou willen aansnijden betreft calculaties. En dan met name de leesbaarheid daarvan. Persoonlijk heb ik er altijd moeite mee om code ( calculaties ) te lezen waar geen of nauwelijks spaties in voorkomen. Dat wordt nog eens versterkt als haken direct tegen tekst aan getypt staan. Wat ik dan als eerst doe is daar waar nodig spatiëringen toepassen om de boel leesbaarder te krijgen. Noem het een tik of van mijn part zonde van de tijd, maar mij helpt het. En ik stel me zo voor dat er meerdere mensen zijn die graag een leesbare code voorgeschoteld krijgen.

Zo kwam ik laatst in een database de volgende code tegen:

Middle(GetAsText(T06_DOCUMENTS::Document_Container) ; Position(GetAsText(T06_DOCUMENTS::Document_Container) ; “/” ; 1; PatternCount(GetAsText(T06_DOCUMENTS::Document_Container) ; “/” )) + 1;1000)

Let wel, het lijkt dat dit allemaal netjes onder elkaar geschreven is. Niets is minder waar. Kopieer deze code in het calculatieveld en je zal zien dat dit allemaal achter elkaar geschreven zonder de bedoeling te hebben om het op meerdere regels te laten uitkomen.

Op het moment dat ik deze code las moest ik direct denken aan een artikel van Kevin Frank van www.filemakerhacks.com. Dat artikel is al wat gedateerd en stamt uit januari 2011. Maar is zo goed en treffend dat ik de techniek hier graag onder de aandacht wil brengen.

Als je de code bekijkt is het geen slechte code, want het doet wat het moet doen. En dat is dat ‘t het laatste stukje tekst uit een tekst-string vangt. Wanneer je de code verder ontleed dan herken een Middle functie waarbinnen een nesting plaatsvindt met een Position functie en een PatternCount. De PatternCount zit binnenin de Position. Verder valt op dat tot driemaal toe de functie GetAsText wordt toegepast en dat telkens op een en hetzelfde veld. Dan begint het bij al te kriebelen van “Waarom niet gewoon één keer en het resultaat vangen in een variabele?”.

Echter bedenk dat zodra je met variabelen binnen een calculatie wilt gaan werken dat je daar de Let functie bij nodig hebt.

Let ( [

~string = GetAsText ( T06_DOCUMENTS::Document_Container ) ] ;

Deze variabele ingevoerd in de Middle functie levert het volgende resultaat.

Middle ( ~String ; Position ( ~String ; “/” ; 1 ; PatternCount ( ~String ; “/” ) ) + 1 ; 1000 )

Kijk, dat past alvast op 1 regel van en A4tje. Maar het kan nog verder.

Let ( [
     ~string = GetAsText ( T06_DOCUMENTS::Document_Container ) ;
     ~count = Patterncount ( ~string ; “/” )
] ;
     // Total of all “/”
     Middle ( ~string ; Position ( ~string ; “/” ; 1 ; ~count ) + 1 ; 1000 )
)

Nu is veel beter herkenbaar wat Position moet doen en wat Middle doet, maar toch ga ik nog een stap verder.

Let ( [
     ~string = GetAsText ( T06_DOCUMENTS::Document_Container ) ;
     ~count = Patterncount ( ~string ; “/” ) ; // Total of all “/”
     ~pos = Position ( ~string ; “/” ; 1 ; ~count )
    // Determine the position of the last “/”
] ;
     Middle ( ~string ; ~pos + 1 ; 1000 )
)

Nu is het leesbaar, lekker compact en dus zie je direct wat de bedoeling is. Ik zit alleen met 1 heel klein punt. Aan het einde van de Middle functie staat het getal 1000. Nu, bedenk dat we hier een pad-verwijzing uit een container vangen. En in de praktijk zullen er geen pad-verwijzingen zijn van wel 1000 karakters ( dat is tekst en leestekens totaal ). Maar toch, we willen wel nette code produceren. Vandaar de laatste poging.

Let ( [
     ~string = GetAsText ( T06_DOCUMENTS::Document_Container ) ;
     ~count = Patterncount ( ~string ; “/” ) ; // Total of all “/”
     ~pos = Position ( ~string ; “/” ; 1 ; ~count ) ;
                 // Determine the position of the last “/”
     ~string_len = Length ( ~string ) // Total length of the string
] ;
     Middle ( ~string ; ~pos + 1 ; ~string_len – ~pos )
)

Het verhaal is hiermee niet ten einde. Van Kevin, een van de vele die ik zeer bewonder, leerde ik dat je minimaal moet streven om tenminste 3 oplossingen voor een probleemstelling paraat moet hebben. En binnen FileMaker zijn er legio, dat is het mooie van dit pakket. Ik zal er 1 geven en dat is een volledig andere kijk op de ~string.

Even het geheugen opfrissen. De string is een pad-verwijzing en begint met een stukje tekst, wat vervolgens wordt onderbroken door een “/”. Waarna het volgende stukje tekst volgt. Hoeveel “/” er in zitten weten we van te voren niet en dat kan zelfs per pad verschillen. Echter waar het de maker van de allereerste code om te doen was, was uiteindelijk alleen maar het laatste stukje tekst uit de hele string halen. En in zijn geval was dat de bestandsnaam.

Concentreer je nou alleen op dat laatste stukje tekst. En bekommeren je verder totaal niet om al hetgeen wat daarvoor staat. We zouden daarvoor vanuit de rechterkant naar een tekst-string kunnen kijken. Maar dan zijn we nog steeds bezig met tekst manipuleren en dus FM vertellen waar het precies de tekst vandaan moet halen.

We gaan de ~string nu heel anders bekijken en als een lijst beschouwen. Een lijst waarbij de waarden gescheiden worden door “¶” ( het teken van de carriage return ) i.p.v. “/”.

Let ( [
     ~string = GetAsText ( T06_DOCUMENTS::Document_Container ) ; 
                 // Deze is vertrouwd
     ~list = Substitute ( ~string ; “/” ; “¶” ) // Dit is je lijst
] ;
     RightValues ( ~list ; 1 ) // Vang alleen de onderste en dus de laatste waarde
)

Compact, krachtig. Niks geen vaste waardes om lengtes van teksten te bepalen. En direct zichtbaar wat de bedoeling is.

Het stukje broncode waar ik dit stuk mee begon komt, zoals eerder gezegd, uit een bestaande en zelfs gratis verkrijgbare database genaamd FM StartingPoint van RCC. Inc.

Nou vooruit, nog eentje om het af te maken. Het betreft hier een veld waarop gesorteerd moet kunnen worden, waarbij records zonder datum gegroepeerd worden onder maand 13.

Orginele code:

Case (
     Month ( Date ) = 1 ; 1 ;
     
Month ( Date ) = 2 ; 2 ;
     
Month ( Date ) = 3 ; 3 ;
     
Month ( Date ) = 4 ; 4 ;
     
Month ( Date ) = 5 ; 5 ;
     Month ( Date ) = 6 ; 6 ;
     Month ( Date ) = 7 ; 7 ;

     Month ( Date ) = 8 ; 8 ;
     
Month ( Date ) = 9 ; 9 ;
     
Month ( Date ) = 10 ; 10 ;
     
Month ( Date ) = 11 ; 11 ;
     
Month ( Date ) = 12 ; 12 ; 13
)

De code is duidelijk, absoluut. Maar is het ook nodig om de code zo uitgebreid te schrijven?

Vernieuwde versie:

Let ( [
     ~Month = Month ( Date )
] ;
     
If ( IsEmpty ( Date ) or ~Month < 1 or ~Month > 12 ; 13 ; ~Month )
)

Voor degene die zich afvragen waarom ik elke variabele vooraf laat gaan met een “~”. Ik pas een naming convention toe. Niet zelf bedacht door mij overigens, maar op deze manier zijn variabelen binnen een calculatie ( door mijn calculatievariabelen genoemd ) beter te herkennen. Dit omdat FileMaker toestaat om ook local script variabelen en global script variabelen toe te passen in calculaties. Maar dit is wellicht voer voor een ander onderwerp.

Voor alle beginnende FM enthousiastellingen zeg ik, lees zoveel mogelijk. Struin diverse fora af. Clarify.net is er daar 1 van. Probeer logisch te blijven denken. Schrijf desnoods je probleemstelling eerst eens uit op papier ( ja echt, ik heb zelf gemerkt dat dat werkt ). En pas als de basis ingrediënten lijken te kloppen, pas dan uitwerken en de mogelijke uitzonderingen beschrijven.

 

Voor nu beëindig dit stuk en wens ik eenieder een goede gezondheid.

En wellicht tot een volgende keer.