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
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)
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 )
~remainingChars = Length ( input ) - Start;
~output = Case (
~remainingChars >= 0;
Middle ( Input; Start; 1) &
Case ( ~remainingChars > 0; ":"; "") &
AddColonsBetweenChars ( Input; Output; Start + 1 );
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.
/* AddSepChar(text; sep) */
/* text: the text you want transformed */
/* sep: the character (or characters) that you want added between every character of text */
tL = 0 ; "";
tL = 1; text;
Left(text; 1) & sep & AddSepChar(Right(text; tL-1); sep)
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
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 ) ;
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:
- The LET statement just reads the input and creates the new string for the recursive call of the function and a variable (L) for
- 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:
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 ;-)
That's truly awesome guys...
thanks so much for everyone's help
In FM, "tail recursion" need function call without additional calculation outside of the function, so yours are not.
This is it.
AddColonsBetweenChars19752 ( input ; start )
Case ( Length ( input ) < start ; input ;
AddColonsBetweenChars19752 ( Replace ( input ; start ; 0 ; ":" ) ; start + 2 )