Recursieve Custom Functions maken

Custom Functions of Eigen functies (CF) zijn heel handig wanneer je getallen of tekst volgens een vast patroon wilt bewerken. Graden celsius omrekenen naar graden Fahrenheit en vice versa is daar een voorbeeld van: F = C * 9 / 5 + 32 en andersom C = 5 * ( F – 32 ) / 9 zijn gemakkelijk in CF te vangen, want het zijn enkelvoudige bewerkingen van een stuk data.

Recursie debuggen

Het wordt een ander verhaal wanneer data in stukken moet worden bewerkt door dezelfde functie. De gegevens worden dan als het ware in een lus bewerkt, door de functie zichzelf met de resterende gegevens als parameter te laten aanroepen (recursiviteit). Filemaker biedt niet echt de mogelijkheid om een recursieve functie te debuggen en dan moet je teruggrijpen op iets anders.

Scripten

Een manier waarop dat kan is door een script te schrijven dat hetzelfde doet als de beoogde CF. Voordat je dat kan doen moet natuurlijk eerst worden bepaald wat de CF moet gaan doen. Het voorbeeld dat ik hier wil gebruiken is een decimaal getal omzetten naar een binaire waarde: 21 <=> 10101

Werkwijze vastleggen

De werkwijze die ik heb bedacht is als volgt:

  1. Starten met een getal
  2. Bepalen welk grootste getal uit een macht van 2 van dat getal kan worden afgetrokken. Het bepalen met welke macht 2 moet worden verheven om een bepaald resulaat te verkrijgen kan je doen met logartimen:
    x = Ln ( getal ) / Ln ( 2 )
    (het maakt niet uit of je Log/Lg of Ln gebruikt, het resulaat is het zelfde)
  3. De restwaarde bepalen van het getal minus de grootste integer-macht van 2. Voorbeeld:
    getal = 21, dus 16 = 2ˆ4, dus de rest van 21 – 16 = 5
  4. De binaire weergave bepalen van de integer-waarde van die macht van 2.
    Dus de binaire weergave is: 10ˆ4 = 10000

Recursieve stappen

  1. Met de restwaarde van stap 3 wordt dan stap 1 tot 4 weer doorlopen
    Rest van 21 – 16 = 5, 4 = 2ˆ2, dus de binaire weergave daarvan is 10ˆ2 = 100
  2. Met de restwaarde van stap R1 wordt dan stap 1 tot 3 weer doorlopen.
    Rest van 5 – 4 = 1, 1 = 2ˆ0, dus de binaire weergave daarvan is 10ˆ0 = 1
  3. De restwaarde van R2 = 0

Deze werkwijze moet doorgaan totdat er geen rest meer over is (zie R3, daar is de rest nu). Aan het einde worden alle resultaten bij elkaar opgeteld, omdat iedere diepere recursie zijn resultaat (van stap4) terugstuurt aan het aanroepende script. Het resultaat is dan: 10000 + 100 + 1 = 10101.

In een script [Dec_2_Bin] ziet dat er als volgt uit:
# Converteer ScriptParameter naar Integer (positief getal zonder fractie)
# Stap #1
Set Variable [$n; Value:Int ( Get ( ScriptParameter ) )]
# Stap #2
# Bereken macht van 2 voor $n
Set Variable [$x; Value:Ln ( $n ) / Ln ( 2 )]
Set Variable [$p; Value:Int( $x )]
# Bereken resultaat van macht van 2 met Int ( $x )
Set Variable [$t; Value:2^$p]
# Stap #3 (rest bepalen)
Set Variable [$r; Value:$n – $t]
# Stap #4 (binaire weergave)
Set Variable [$Z; Value:10^$p]
# Zolang $n>0
If [$n > 0]
            # Recursief aanroepen met restwaarde
            Perform Script [“Dec_2_Bin”; Parameter: $r]
            # Scriptresultaat optellen bij resultaat van stap #4
            Exit Script [$Z + Get ( ScriptResult )]
End If
#

Dit script is al gedebugged, maar je kan iedere stap doorlopen en in de debugger en in de Data Viewer (Gegevens Weergave)

Script omzetten naar een functie

De CF die aan de hand van het voorgaande script kan worden gemaakt ziet er zo uit:

Let ( [
    n = Int ( dec ) ;
    x = Ln ( n ) / Ln ( 2 ) ;
    p = Int( x ) ;
    t = 2^p ;
    r = n – t ;
    z = 10^p
] ;
    Case ( n > 0 ; z + dec2bin ( r ) )
)

en uiteindelijk wordt deze functie een stuk korter dan het script. Waar je in een script losse stappen moet maken, kan je in de formule een aantal stappen samenvoegen:

Let ( [
    n = Int ( dec ) ;
    x = Ln ( n ) / Ln ( 2 ) ;
    p = Int( x )
] ;
    Case ( n > 0 ; 10^p + dec2bin ( n – 2^p ) )
)

Met name de stappen #3 en #4 zijn allebei opgegaan in de case-statement. De recursieve script-aanroep is vervangen voor dec2bin, de parameter kwam in het script uit stap #3, maar wordt nu hier direct berekend. Stap #4 maakt ook deel uit van de case-statement ipv los te worden uitgevoerd.

Staart-recursie niet besproken

Ik hoop dat ik je op weg heb kunnen helpen om zelf een recursieve functie te maken. De beschreven methode laat slechts één vorm van recursie zien en dat is kop-recursie. Het verschil tussen kop- en staart-recursie heb ik hier niet besproken. Daar kan een apart hoofdstuk aan worden gewijd en dus heb ik dat achterwege gelaten.

Uiteraard is er weer een download die je kan bekijken: Recursieve_Custom_Functions veel plezier met het bekijken en om dit keer met Matt Petrowski te spreken: Happy Filemaking!