Reputation:
For instance, the following function merge
can be called to merge two given integer lists:
fun
merge
{m,n:nat}
(
xs: list(int, m)
,
ys: list(int, n)
) : list(int, m+n) =
(
case+ xs of
| list_nil() => ys
| list_cons(x, xs2) =>
(
case+ ys of
| list_nil() => xs
| list_cons(y, ys2) =>
if x <= y
then list_cons(x, merge(xs2, ys))
else list_cons(y, merge(xs, ys2))
// end of [if]
)
)
Clearly, merge
is not tail-recursive. If applied to two very long list, merge
is likely to overflow the call stack. Is there a way to implement merge
tail-recursively?
Upvotes: 1
Views: 143
Reputation: 935
Note that stream2list_vt is tail-recusive (and very memory-frugal):
fun
merge
{m,n:nat}
(
xs: list(int, m)
,
ys: list(int, n)
) : List0_vt(int) = stream2list_vt(merge2(xs, ys))
and
merge2
{m,n:nat}
(
xs0: list(int, m)
,
ys0: list(int, n)
) : stream_vt(int) = $ldelay
(
case+ xs0 of
| list_nil() =>
!(streamize_list_elt(ys0))
| list_cons(x0, xs1) =>
(
case+ ys0 of
| list_nil() =>
!(streamize_list_elt(xs0))
| list_cons(y0, ys1) =>
if x0 <= y0
then stream_vt_cons(x0, merge2(xs1, ys0))
else stream_vt_cons(y0, merge2(xs0, ys1))
// end of [if]
)
)
There are certainly more efficient ways to implement merge
tail-recursively than the one given above. However, this example of using linear streams shows a simple and systematic way to build lists tail-recursively.
Upvotes: 1