8 Replies Latest reply on Nov 2, 2015 3:43 PM by user19752

    Recursive custom function... a task I'm trying to get my head around it

    PeterWindle

      I have given myself a task.

       

      I am attempting to create a recursive custom function to do the following:

       

      Take a word like this:

       

      Peter Windle

       

      and change it to something like this :

       

      P:e:t:e:r: :W:i:n:d:l:e

      where the ":" is separating each character within the text, so in affect, I want to grab each character in the field, one at a time, until the end of the text. I have a reason for this, but I will reveal that later ! ;-)

       

      I've looked at many samples of recursive functions, but I have not really found a solution, partly because I am still trying to get my head around the recursive functionality and partly because other examples are not quite the same as what I'm aiming for.

       

      Any hints, suggestions, solutions?

        • 1. Re: Recursive custom function... a task I'm trying to get my head around it
          Vincent_L

          I've been there, and I'm not quite a recursive Jedi, but here's my sample. I use a temp parameter to store the result, you can do without, but it helps to understand.

           

          PeterWindle ( text ; sep ; temp )

           

          // temp should be empty when invoking the function

          If(not IsEmpty(text);

          Let(

          [

          letter=Left(text;1);

          new_text=Right(text;Length(text)-1);

          temp=List(temp;letter)

          ];

           

           

          PeterWindle(new_text;sep;temp)

          )

          ;

          Substitute(temp;"¶";sep)

          )

           

          PeterWindle ( "Peter Windle" ; ":" ; "" )

           

          Here's the reasoning, you always need a way to end the recursion, and a way to make it process.

          In that case that's both done via the transformation of the text, into a shorter text called new_text. That's the one we'll pass to the next call of the function. So each tile we iterate, we make a call with a one character less text the the one from the start.

           

          We chez if text (i.e. new_text) is not empty, if it is, that means we're done and that we can display the result.

           

          You'll also remark that rather than make the result (i.e. temp) with temp&sep&char, I prefer to use the list function and do it in the end, that's because the list function, will not insert a separator at the end or the start. So in the end I just need to substitute (If the text is huge it's faster to avoid the list)

          • 2. Re: Recursive custom function... a task I'm trying to get my head around it
            karimhanafi

            Hi Peter,

             

            You have a very good explanation and a neat function from Vincent. But I was working on my own function to answer your post. I am posting it anyway.

             

            It is exactly the same principle as vincent, minus the cleverness of using the list. The only upside of using this one rather than Vincent's is that you can begin the text transformation at any position. Example : AddColonsToText ( "Test"; ""; 1 ) gives "T:e:s:t" and  AddColonsToText ( "Test"; ""; 2 ) gives you "Te:s:t".

             

            Function : AddColonsBetweenChars ( Input; Output; Start )

             

            Let (

              [

             

              ~remainingChars = Length ( input ) - Start;

             

              ~output = Case (

                                  ~remainingChars >= 0;

             

                                              Output &

                                              Middle ( Input; Start; 1)  &

                                              Case ( ~remainingChars > 0; ":"; "")  &

                                              AddColonsBetweenChars ( Input; Output; Start + 1 );

             

                                  Output

                            )

                  ];

                            ~output

            )

             

            Take care.

             

            Karim

            • 3. Re: Recursive custom function... a task I'm trying to get my head around it
              TomHays

              Here's my solution.

               

              I think unlike the others submitted so far, mine uses "tail recursion" which for FileMaker recursive functions should allow it to handle longer more depth of recursion and thus longer character strings (i.e. 30,000 in length instead of just 10,000).  Tail recursion is implemented when the function is called recursively only at the very end of the calculation (the tail).  (Karim's solution may be tail recursive.)

               

              This function allows the caller to provide a separator and it makes no restriction on the separator nor the text it operates on.  It might be a single character (including ¶), a long string, or the empty string.

               

              -Tom

               

              /* AddSepChar(text; sep) */

              /* */

              /* text: the text you want transformed */

              /* sep: the character (or characters) that you want added between every character of text */

              Let(

              [

                tL=Length(text)

              ];

              Case(

                 tL = 0 ; "";

                 tL = 1; text;

                 Left(text; 1) & sep & AddSepChar(Right(text; tL-1); sep)

              )

              )

              • 4. Re: Recursive custom function... a task I'm trying to get my head around it
                TomHays

                Like Vincent, I'll provide some conceptual information about how to approach

                and solve the problem using a recursive custom function.  Peter, the following discussion

                might be to basic for your level, but it might be helpful for others getting started in it.

                 

                While this specific task is fairly simple and the recursive function I provided is pretty

                short to pick apart and decipher, sometimes it helps to have some guidance on where

                to start when trying to solve a new problem.  It also helps to have a sense of when

                recursion is the right tool for the job.

                 

                A task that lends itself to recursion generally follows the structure of

                "I know how to do a little bit, and the task that remains is essentially the

                same problem I started with just a little smaller."

                 

                A recursive function needs:

                (1) A very reliable way to know when to stop.

                (2) A way to solve the task a little and generate a smaller task that looks just like the first.

                 

                Let's apply this to the task you presented: take a string and insert a colon between every character

                in the string. "apple" becomes "a:p:p:l:e".

                 

                Study the problem in the boundary conditions or the hard cases.

                What do you do if there is no input?  What about only one character?

                "" becomes "".  Nothing to do.

                "e" becomes "e". Nothing special to do.

                 

                If I make the problem smaller, then I've got my stopping conditions from those above.

                 

                How do I make the task smaller?

                If I were solving the problem by hand, I'd start at the beginning and write the first letter followed

                by the colon. "a:".  Phew.  All that is left ahead of me is to tackle "pple".  Again continuing

                by hand, I know that when I solve it, I'll need to write the remainder of the solution immediately

                following the part I just solved.

                "a:" & Solution to ("pple")

                I have just found the heart of the recursion.

                 

                Implementing in FileMaker usually involves a Case() statement.

                Tackle your stop conditions first.

                 

                Make sure you have accommodated every situation that your function arguments might encounter even

                if the input value makes no sense for the problem you are solving.

                What happens if an input string of text is empty? What about an input number being negative or zero?

                 

                If you can manage it, make the recursive function call (the function's call to itself),

                the very last operation in your function so that you can take advantage of the greater stack depth

                available for tail recursion, i.e. your function can call itself more times to handle larger depth

                problems.

                 

                -Tom

                • 5. Re: Recursive custom function... a task I'm trying to get my head around it
                  Menno

                  Hi Peter,

                   

                  although others have made excellent suggestions here I would like to give you my solution as well:

                  Custom Function: Colonize ( text )

                   

                  Let ( [

                    L = Length ( text ) ;

                    x = Middle ( text ; 2 ; L - 1 )

                  ] ;

                    Case ( L > 1 ;

                    Left ( text ; 1 ) & ":" & Colonize ( x ) ;

                    text

                    )

                  )

                  I have stripped down the function as much as possible, so no settable separator and I chose head-recursion. It uses more memory, but when applied to shorter texts, it's also faster. Names probably won't exceed 10^4 (or even 10^2) so it should do.

                   

                  Then how to create such a function:

                  It is just a matter of definition and your requirement happens to be really straight forward and easy:

                  Add a colon after each character as long as it is not the last character

                  and that's what happens in this function in (more or less) 2 steps:

                  1. The LET statement just reads the input and creates the new string for the recursive call of the function and a variable (L) for
                  2. a check which is done in the CASE statement and there you see the requirement from the quoted text:

                  As long as the length is more than 1 character, take the first character from the given text, place a colon after it and call the same function with the rest of the text.

                  So your requirement:

                  PeterWindle wrote:

                  I want to grab each character in the field, one at a time, until the end of the text.

                  is met exactly as you've written it yourself ;-)

                  • 6. Re: Recursive custom function... a task I'm trying to get my head around it
                    PeterWindle

                    That's truly awesome guys...

                    thanks so much for everyone's help

                    • 7. Re: Recursive custom function... a task I'm trying to get my head around it
                      user19752

                      In FM, "tail recursion" need function call without additional calculation outside of the function, so yours are not.

                      This is it.

                       

                      /*

                      AddColonsBetweenChars19752 ( input ; start )

                      input:text

                      start:always 2

                      */

                       

                      Case ( Length ( input ) < start ; input ;

                      AddColonsBetweenChars19752 ( Replace ( input ; start ; 0 ; ":" ) ; start + 2 )

                      )

                      • 8. Re: Recursive custom function... a task I'm trying to get my head around it
                        user19752

                        I wondered that too many recursion makes function result random value, not "?"recursive.JPG

                        Is this bug of my machine? (Windows7, FM12)