I am really confused on how to make a function "Tail recursive".
Here is my function, but I don't know whether it is already tail recursive or not.
I am trying to merge two lists in Haskell.
merge2 :: Ord a =>[a]->[a]->[a]
merge2 xs [] = xs
merge2 [] ys = ys
merge2 (x:xs)(y:ys) = if y < x then y: merge2 (x:xs) ys else x :merge2 xs (y:ys)
Your function isn't tail-recursive; it's guarded recursive. However, guarded recursion is what you should be using in Haskell if you want to be memory efficient.
For a call to be a tail call, its result must be the result of the entire function. This definition applies to both recursive and non-recursive calls.
For example, in the code
f x y z = (x ++ y) ++ z
the call (x ++ y) ++ z is a tail call because its result is the result of the entire function. The call x ++ y is not a tail call.
For an example of tail-recursion, consider foldl:
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl _ acc [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs
The recursive call foldl f (f acc x) xs is a tail-recursive call because its result is the result of the entire function. Thus it's a tail call, and it is recursive being a call of foldl to itself.
The recursive calls in your code
merge2 (x:xs) (y:ys) = if y < x then y : merge2 (x:xs) ys
else x : merge2 xs (y:ys)
are not tail-recursive because they do not give the result of the entire function. The result of the call to merge2 is used as a part of the whole returned value, a new list. The (:) constructor, not the recursive call, gives the result of the entire function. And in fact, being lazy, (:) _ _ returns right away, and the holes _ are filled only later, if and when needed. That's why guarded recursion is space efficient.
However, tail-recursion doesn't guarantee space efficiency in a lazy language.
With lazy evaluation, Haskell builds up thunks, or structures in memory that represent code that is yet to be evaluated. Consider the evaluation of the following code:
foldl f 0 (1:2:3:[])
=> foldl f (f 0 1) (2:3:[])
=> foldl f (f (f 0 1) 2) (3:[])
=> foldl f (f (f (f 0 1) 2) 3) []
=> f (f (f 0 1) 2) 3
You can think of lazy evaluation as happening "outside-in." When the recursive calls to foldl are evaluated, thunks are built-up in the accumulator. So, tail recursion with accumulators is not space efficient in a lazy language because of the delayed evaluation (unless the accumulator is forced right away, before the next tail-recursive call is made, thus preventing the thunks build-up and instead presenting the already-calculated value, in the end).
Rather than tail recursion, you should try to use guarded recursion, where the recursive call is hidden inside a lazy data constructor. With lazy evaluation, expressions are evaluated until they are in weak head normal form (WHNF). An expression is in WHNF when it is either:
A lazy data constructor applied to arguments (e.g. Just (1 + 1))
A partially applied function (e.g. const 2)
A lambda expression (e.g. \x -> x)
Consider map:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
map (+1) (1:2:3:[])
=> (+1) 1 : map (+1) (2:3:[])
The expression (+1) 1 : map (+1) (2:3:[]) is in WHNF because of the (:) data constructor, and therefore evaluation stops at this point. Your merge2 function also uses guarded recursion, so it too is space-efficient in a lazy language.
TL;DR: In a lazy language, tail-recursion can still take up memory if it builds up thunks in the accumulator, while guarded recursion does not build up thunks.
Helpful links:
https://wiki.haskell.org/Tail_recursion
https://wiki.haskell.org/Stack_overflow
https://wiki.haskell.org/Thunk
https://wiki.haskell.org/Weak_head_normal_form
Does Haskell have tail-recursive optimization?
What is Weak Head Normal Form?
Related
I have implemented a function (!!=) that given a list and a tuple containing an index in the list and a
new value, updates the given list with the new value at the given
index.
(!!=) :: [a] -> (Int,a) -> [a]
(!!=) xs (0, a) = a : tail xs
(!!=) [] (i, a) = error "Index not in the list"
(!!=) (x:xs) (i, a) = x : xs !!= (i-1, a)
Being a beginner with the concept of folding I was wondering if there is a way to achieve the same result using foldl or foldr instead?
Thanks a lot in advance.
I'll give you the foldl version which is easier to understand I think and the easiest / most straight-forward version I can think of.
But please note that you should not use foldl (use foldl': https://wiki.haskell.org/Foldr_Foldl_Foldl') - nor should you use ++ like this (use : and reverse after) ;)
Anway this is the idea:
(!!=) xs (i, a) = snd $ foldl
(\(j, ys) x -> (j+1, if j == i then ys ++ [a] else ys ++ [x]))
(0, [])
xs
as the state/accumulator for the fold I take a tuple of the current index and the accumulated result list (therefore the snd because I only want this in the end)
then the folding function just have to look if we are at the index and exchange the element - returning the next index and the new accumulated list
as an exercise you can try to:
use : instead of ++ and a reverse
rewrite as foldr
look at zipWith and rewrite this using this (zipWith (...) [0..] xs) instead of the fold (this is similar to using a map with index
Neither foldl nor foldr can do this particular job efficiently (unless you "cheat" by pattern matching on the list as you fold over it), though foldr can do it a bit less badly. No, what you really need is a different style of fold, sometimes called para:
para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para _f n [] = n
para f n (a : as) = f a as (para f n as)
para is very similar to foldr. Each of them takes a combining function and, for each element, passes the combining function that element and the result of folding up the rest of the list. But para adds something extra: it also passes in the rest of the list! So there's no need to reconstruct the tail of the list once you've reached the replacement point.
But ... how do you count from the beginning with foldr or para? That brings in a classic trick, sometimes called a "higher-order fold". Instead of para go stop xs producing a list, it's going to produce a function that takes the insertion position as an argument.
(!!=) :: [a] -> (Int, a) -> [a]
xs0 !!= (i0, new) = para go stop xs0 i0
where
-- If the list is empty, then no matter what index
-- you seek, it's not there.
stop = \_ -> error "Index not in the list"
-- We produce a function that takes an index. If the
-- index is 0, we combine the new element with "the rest of the list".
-- Otherwise we apply the function we get from folding up the rest of
-- the list to the predecessor of the index, and tack on the current
-- element.
go x xs r = \i -> case i of
0 -> new : xs
_ -> x : r (i - 1)
Note that para is easily powerful enough to implement foldr:
foldr c = para (\a _ b -> c a b)
What's perhaps less obvious is that foldr can implement a (very inefficient version of) para:
para f n = snd . foldr go ([], n)
where
go x ~(xs, r) = (x : xs, f x xs r)
Lest you get the wrong idea and think that para is "better than" foldr, know that when its extra power isn't needed, foldr is simpler to use and will very often be compiled to more efficient code.
I have been working on a separate function that returns a list that inserts element x after each k elements of list l (counting from
the end of the list). For example, separate (1, 0, [1,2,3,4]) should return [1,0,2,0,3,0,4]. I finished the function and have it working as follows:
fun separate (k: int, x: 'a, l: 'a list) : 'a list =
let
fun kinsert [] _ = []
| kinsert ls 0 = x::(kinsert ls k)
| kinsert (l::ls) i = l::(kinsert ls (i-1))
in
List.rev (kinsert (List.rev l) k)
end
Im now trying to simplify the function using foldl/foldr without any recursion, but I cant seem to get it working right. Any tips/suggestions on how to approach this? Thank You!
These are more or less the thoughts I had when trying to write the function using foldl/foldr:
foldl/foldr abstracts away the list recursion from the logic that composes the end result.
Start by sketching out a function that has a much similar structure to your original program, but where foldr is used and kinsert instead of being a recursive function is the function given to foldr:
fun separate (k, x, L) =
let fun kinsert (y, ys) = ...
in foldr kinsert [] L
end
This isn't strictly necessary; kinsert might as well be anonymous.
You're using an inner helper function kinsert because you need a copy of k (i) that you gradually decrement and reset to k every time it reaches 0. So while the list that kinsert spits out is equivalent to the fold's accumulated variable, i is temporarily accumulated (and occasionally reset) in much the same way.
Change kinsert's accumulating variable to make room for i:
fun separate (k, x, L) =
let fun kinsert (y, (i, xs)) = ...
in foldr kinsert (?, []) L
end
Now the result of the fold becomes 'a * 'a list, which causes two problems: 1) We only really wanted to accumulate i temporarily, but it's part of the final result. This can be circumvented by discarding it using #2 (foldr ...). 2) If the result is now a tuple, I'm not sure what to put as the first i in place of ?.
Since kinsert is a separate function declaration, you can use pattern matching and multiple function bodies:
fun separate (k, x, L) =
let fun kinsert (y, (0, ys)) = ...
| kinsert (y, (i, ys)) = ...
in ... foldr kinsert ... L
end
Your original kinsert deviates from the recursion pattern that a fold performs in one way: In the middle pattern, when i matches 0, you're not chopping an element off ls, which a fold would otherwise force you to. So your 0 case will look slightly different from the original; you'll probably run into an off-by-one error.
Remember that foldr actually visits the last element in the list first, at which point i will have its initial value, where with the original kinsert, the initial value for i will be when you're at the first element.
Depending on whether you use foldl or foldr you'll run into different problems: foldl will reverse your list, but address items in the right order. foldr will keep the list order correct, but create a different result when k does not divide the length of L...
At this point, consider using foldl and reverse the list instead:
fun separate (k, x, L) =
let fun kinsert (y, (?, ys)) = ...
| kinsert (y, (i, ys)) = ...
in rev (... foldl kinsert ... L)
end
Otherwise you'll start to notice that separate (2, 0, [1,2,3,4,5]) should probably give [1,2,0,3,4,0,5] and not [1,0,2,3,0,5].
fun p(L) =
[L] # p( tl(L) # [hd(L)] );
If L is [1,2,3] then I want to have a [ [1,2,3], [2,3,1], [3,1,2] ].
Since every time I append the first num to the end, then if L = [] then [] doesn't work here.
How to stop the function once it has the three lists?
You can have a parameter x in the function to keep track of how many levels deep in the recursion you are.
fun p(L, x) =
if x < length(L) then [L] # p(tl(L) # [hd(L)], x+1)
else [];
Then call the function with x=0.
p([1, 2, 3], 0)
And if you don't like the extra parameter, then as you probably know you can define another function and make it equal to the p function with the parameter forced to 0.
fun p0(L) = p(L, 0);
p0([1, 2, 3]); (* same result as p([1, 2, 3], 0); *)
Let me show some more implementation variants.
First of all, let's define an auxiliary function, which rotates a list 1 position to the left:
(* operates on non-empty lists only *)
fun rot1_left (h :: tl) = tl # [h]
Then the p function could be defined as follows:
fun p xs =
let
(* returns reversed result *)
fun loop [] _ _ = []
| loop xs n res =
if n = 0
then res
else loop (rot1_left xs) (n-1) (xs :: res)
in
List.rev (loop xs (length xs) [])
end
It's usually better (performance-wise) to add new elements at the beginning of the list and then reverse the resulting list once, than to append to the end many times. Note: this version does one spurious rotate at the end and I could have optimized it out, but didn't, to make code more clear.
We have calculated the length of the given list to make its rotated "copies", but we don't have to traverse xs beforehand, we can do it as we rotate it. So, we can use xs as a kind of counter, recursively calling the loop helper function on the tail of the xs list.
fun p xs =
let
(* returns reversed result *)
fun loop [] _ _ = []
| loop xs [] res = res
| loop xs (_::tl) res =
loop (rot1_left xs) tl (xs :: res)
in
List.rev (loop xs xs [])
end
Having done that, we are now closer to implementing p as a foldl function:
fun p xs =
(List.rev o #1)
(List.foldl
(fn (_, (res, rot)) => (rot::res, rot1_left rot))
([], xs)
xs)
The second argument to the List.foldl function is our "accumulator", which is represented here as a pair of the current (partial) result as in the previous implementations and the current rotated list. That explains (List.rev o #1) part: we need to take the first component of the accumulator and reverse it. And as for the ([], xs) part -- the current result is empty at the beginning (hence []) and we start rotating the initial xs list. Also, the _ in (_, (res, rot)) means the current element of the given xs, which we don't care about, since it just serves as a counter (see the prev. variant).
Note: o stands for function composition in Standard ML.
As an exercise, I'm trying to define a ruler value
ruler :: (Num a, Enum a) => [a]
which corresponds to the ruler function
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2...
where the n'th element of the list (assuming the first element corresponds to n=1) is the largest power of 2 which evenly divides n. To make it more interesting, I'm trying to implement ruler without having to do any divisibility testing.
Using a helper function
interleave :: [a] -> [a] -> [a]
which simply alternates the elements from the two given lists, I came up with this - but alas it doesn't work:
interleave :: [a] -> [a] -> [a]
interleave (x:xs) (y:ys) = x : y : interleave xs ys
interleave _ _ = []
ruler :: (Num a, Enum a) => [a]
ruler = foldr1 interleave . map repeat $ [0..]
main :: IO ()
main = print (take 20 ruler)
The program eventually uses up all stack space.
Now, what's strange is that the program works just fine if I adjust the definition of interleave so that it reads
interleave (x:xs) ys = x : head ys : interleave xs (tail ys)
I.e. I no longer use pattern matching on the second argument. Why does using head and tail here make ruler terminate - after all, the pattern matching is rather defensive (I only evaluate the first element of the list spine, no?).
You are applying foldr with an strict combination function to an infinite list.
Boiled down to a minimal example, you can view this behaviour here:
*Main> :t const
const :: a -> b -> a
*Main> :t flip seq
flip seq :: c -> a -> c
*Main> foldr1 const [0..]
0
*Main> foldr1 (flip seq) [0..]
^CInterrupted.
The fix is, as explained in other answers, to make interleave lazy.
More concretely, here is what happens. First we resolve the foldr1, replacing every : of the outer list with interleave:
foldr1 interleave [[0..], [1...], ...]
= interleave [0...] (interleave [1...] (...))
In order to make progress, the first interleave wants to evaluate the second argument before producing the first value. But then the second wants to evaluate its second argument, and so on.
With the lazy definition of interleave, the first value is produced before evaluating the second argument. In particular, interleave [1...] (...) will evaluate to 1 : ... (which helps the first interleave to make progress) before evaluating stuff further down.
The difference is that pattern matching forces the first item in the spine, head/tail do not.
You could use lazy patterns to achieve the same goal:
interleave (x:xs) ~(y:ys) = x : y : interleave xs ys
Note the ~: this is equivalent to defining y and ys using head and tail.
For example: the list below is undefined.
fix (\ (x:xs) -> 1:x:xs)
where fix is the fixed point combinator (e.g. from Data.Function). By comparison, this other list repeats 1 forever:
fix (\ ~(x:xs) -> 1:x:xs)
This is because the 1 is produced before the list is split between x and xs.
Why forcing the first item in the spine triggers the problem?
When reasoning about a recursive equation such as
x = f x
it often helps to regard x as the value "approached" by the sequence of values
undefined
f undefined
f (f undefined)
f (f (f undefined))
...
(The above intuition can be made precise through a bit of denotational semantics and the Kleene's fixed point theorem.)
For instance, the equation
x = 1 : x
defines the "limit" of the sequence
undefined
1 : undefined
1 : 1 : undefined
...
which clearly converges to the repeated ones list.
When using pattern matching to define recursive values, the equation becomes, e.g.
(y:ys) = 1:y:ys
which, due to pattern matching, translates to
x = case x of (y:ys) -> 1:y:ys
Let us consider its approximating sequence
undefined
case undefined of (y:ys) -> .... = undefined
case undefined of (y:ys) -> .... = undefined
...
At the second step, the case diverges, making the result still undefined.
The sequence does not approach the intended "repeated ones" list, but is constantly undefined.
Using lazy patterns, instead
x = case x of ~(y:ys) -> 1:y:ys
we obtain the sequence
undefined
case undefined of ~(y:ys) -> 1:y:ys
= 1 : (case undefined of (y:_) -> y) : (case undefined of (_:ys) -> ys)
= 1 : undefined : undefined -- name this L1
case L1 of ~(y:ys) -> 1:y:ys
= 1 : (case L1 of (y:_) -> y) : (case L1 of (_:ys) -> ys)
= 1 : 1 : undefined : undefined -- name this L2
case L2 of ~(y:ys) -> 1:y:ys
= 1 : (case L2 of (y:_) -> y) : (case L2 of (_:ys) -> ys)
= 1 : 1 : 1 : undefined : undefined
which does converge to the intended list. Note how lazy patterns are "pushed forward" without evaluating the case argument early. This is what makes them lazy. In this way, the 1 is produced before the pattern matching is performed, making the result of the recursively defined entity non trivial.
The problem here is not so much about the pattern matching or using head and tail. The issue is how it's done, by defining your function as
interleave :: [a] -> [a] -> [a]
interleave (x:xs) (y:ys) = x : y : interleave xs ys
interleave _ _ = []
You're strictly pattern matching your arguments, that is, we need to know that they are lists of at least one element before we can choose the first branch. Since you're folding this function over an infinite list of lists, we can't really figure this out, and we run out of stack space.
To expand on this (to clarify things brought up in the comments), the first time we try to evaluate interleave (in ruler), we'd get something like
interleave (repeat 0) (foldr1 interleave (map repeat [1..]))
The first argument here of course matches the pattern, but to figure out if the second argument does, we have to try to evaluate it, so we get to
interleave (repeat 1) (foldr1 interleave (map repeat [2..]))
Now we can't evaluate this unless we know more about the second argument. Since the list [2..] never ends, this process can go on forever.
One solution to this is to do a lazy pattern binding on the second argument:
interleave (x:xs) ~(y:ys) = x : y : interleave xs ys
This acts like a promise that the second argument does match the pattern, so don't worry about it (of course this will fail if that isn't true). This means that that first evaluation of interleave can go on without looking deeper into the repeated fold, which in a domino-like effect solves the issue.
A sidenote is that your this version of interleave (as well as your head/tail version) will only work on lists where the second list is as long as or longer than the first.
So i'm new to sml and am trying to understand the ins/out out of it. Recently i tried creating a filter which takes two parameters: a function (that returns a boolean), and a list of values to run against the function. What the filter does is it returns the list of values which return true against the function.
Code:
fun filter f [] = [] |
filter f (x::xs) =
if (f x)
then x::(filter f xs)
else (filter f xs);
So that works. But what i'm trying to do now is just a return a tuple that contains the list of true values, and false. I'm stuck on my conditional and I can't really see another way. Any thoughts on how to solve this?
Code:
fun filter2 f [] = ([],[]) |
filter2 f (x::xs) =
if (f x)
then (x::(filter2 f xs), []) (* error *)
else ([], x::(filter2 f xs)); (* error *)
I think there are several ways to do this.
Reusing Filter
For instance, we could use a inductive approach based on the fact that your tuple would be formed by two elements, the first is the list of elements that satisfy the predicate and the second the list of elements that don't. So, you could reuse your filter function as:
fun partition f xs = (filter f xs, filter (not o f) xs)
This is not the best approach, though, because it evaluates the lists twice, but if the lists are small, this is quite evident and very readable.
Folding
Another way to think about this is in terms of fold. You could think that you are reducing your list to a tuple list, and as you go, you split your items depending on a predicate. Somwewhat like this:
fun parition f xs =
let
fun split x (xs,ys) =
if f x
then (x::xs,ys)
else (xs, x::ys)
val (trueList, falseList) = List.foldl (fn (x,y) => split x y)
([],[]) xs
in
(List.rev trueList, List.rev falseList)
end
Parition
You could also implement your own folding algorithm in the same way as the List.parition method of SML does:
fun partition f xs =
let
fun iter(xs, (trueList,falseList)) =
case xs of
[] => (List.rev trueList, List.rev falseList)
| (x::xs') => if f x
then iter(xs', (x::trueList,falseList))
else iter(xs', (trueList,x::falseList))
in
iter(xs,([],[]))
end
Use SML Basis Method
And ultimately, you can avoid all this and use SML method List.partition whose documentation says:
partition f l
applies f to each element x of l, from left to right, and returns a
pair (pos, neg) where pos is the list of those x for which f x
evaluated to true, and neg is the list of those for which f x
evaluated to false. The elements of pos and neg retain the same
relative order they possessed in l.
This method is implemented as the previous example.
So I will show a good way to do it, and a better way to do it (IMO). But the 'better way' is just for future reference when you learn:
fun filter2 f [] = ([], [])
| filter2 f (x::xs) = let
fun ftuple f (x::xs) trueList falseList =
if (f x)
then ftuple f xs (x::trueList) falseList
else ftuple f xs trueList (x::falseList)
| ftuple _ [] trueList falseList = (trueList, falseList)
in
ftuple f (x::xs) [] []
end;
The reason why yours does not work is because when you call x::(filter2 f xs), the compiler is naively assuming that you are building a single list, it doesn't assume that it is a tuple, it is stepping into the scope of your function call. So while you think to yourself result type is tuple of lists, the compiler gets tunnel vision and thinks result type is list. Here is the better version in my opinion, you should look up the function foldr if you are curious, it is much better to employ this technique since it is more readable, less verbose, and much more importantly ... more predictable and robust:
fun filter2 f l = foldr (fn(x,xs) => if (f x) then (x::(#1(xs)), #2(xs)) else (#1(xs), x::(#2(xs)))) ([],[]) l;
The reason why the first example works is because you are storing default empty lists that accumulate copies of the variables that either fit the condition, or do not fit the condition. However, you have to explicitly tell SML compiler to make sure that the type rules agree. You have to make absolutely sure that SML knows that your return type is a tuple of lists. Any mistake in this chain of command, and this will result in failure to execute. Hence, when working with SML, always study your type inferences. As for the second one, you can see that it is a one-liner, but I will leave you to research that one on your own, just google foldr and foldl.