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 andkinsert
instead of being a recursive function is the function given tofoldr
:This isn't strictly necessary;
kinsert
might as well be anonymous.You're using an inner helper function
kinsert
because you need a copy ofk
(i
) that you gradually decrement and reset tok
every time it reaches 0. So while the list thatkinsert
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 fori
:Now the result of the fold becomes
'a * 'a list
, which causes two problems: 1) We only really wanted to accumulatei
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 firsti
in place of?
.Since
kinsert
is a separate function declaration, you can use pattern matching and multiple function bodies:Your original
kinsert
deviates from the recursion pattern that a fold performs in one way: In the middle pattern, wheni
matches0
, you're not chopping an element offls
, which a fold would otherwise force you to. So your0
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 pointi
will have its initial value, where with the originalkinsert
, the initial value fori
will be when you're at the first element.Depending on whether you use
foldl
orfoldr
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 whenk
does not divide the length ofL
...At this point, consider using
foldl
and reverse the list instead: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]
.