Search code examples
recursionerlangtail-recursion

Erlang Understanding recursion: Find if substring is prefix of string recursively


I want to understand this implementation of finding a prefix in a string, which is implemented without using any built-in list functions, but using recursion to iterate through the string.

attempt:

checkStringPrefix([C|StringTail],[C|LookupTail]) ->
checkStringPrefix(StringTail,LookupTail);

checkStringPrefix(_,[]) ->                            
    "***";                                    

checkStringPrefix(_String,_Lookup) ->                    
    "".                                         

It works, the function calls itself recursively separating the first Character from the Tail.

Example calls:

1> stringUtil:checkStringPrefix("test xxxxx", "test").
"***"
2> stringUtil:checkStringPrefix("test xxxxx", "testtt"). 
[]

In the case of two non equal characters, the last function variant gets called. I dont fully grasp this concept, I would appreciate an explanation. I understand the process of recursive iteration, what I dont understand is why the second variants gets called in the correct moment.


Solution

  • Consider what happens when passing either single character strings or empty strings as arguments:

    • Passing "a" and "a": this calls the first clause of checkStringPrefix/2 because explicitly matches the first elements of both arguments in its function head, which also enforces that neither argument can be the empty list. This clause calls the function recursively, and since neither argument has a tail, the second function clause gets called because the second argument matches the empty list, so the result is "***".
    • Passing "a" and "b": this won't call the first clause because the first elements do not match, and it won't call the second clause because the second argument isn't the empty list. It therefore calls the third clause, so the result is "".
    • Passing "" and "": this won't call the first clause because both arguments are empty lists; it calls the second clause because the second argument matches the empty list, so the result is "***". In fact, since the second clause treats its first argument as a don't-care, this same analysis applies even if the first argument is non-empty.
    • Passing "" and "a": this won't call the first clause because the first argument is empty, and it won't call the second clause because the second argument is not empty, so it calls the third clause and the result is "".

    No matter the lengths of the two argument strings, these are the choices for each invocation.

    To answer your specific question about when the second function clause gets called, it happens any time the second argument is the empty list because of the specific match for that case in the function head, and that function head also ignores the first argument. Matching occurs in order of declaration; the first function clause requires both arguments to have at least one element, so when the second argument is the empty list, it can't match the first clause and so matches the second clause.