以下是使用普通递归函数的方法:
fun toTuple [] = ([], [])
| toTuple ((x,y)::pairs) =
case toTuple pairs of
(xs, ys) => (x::xs, y::ys)
它递归处理剩余的pairs,将结果解压缩为(xs, ys),然后将x 和y 添加到该结果中。您可以使用 let-binding 代替 case-of:
fun toTuple [] = ([], [])
| toTuple ((x,y)::pairs) =
let val (xs, ys) = toTuple pairs
in (x::xs, y::ys)
end
如果您没有直接在函数toTuple 中执行这种模式匹配,您可能必须将结果的解包和重新打包移动到一个单独的函数中:
fun add (x,y) (xs,ys) = (x::xs, y::ys)
fun toTuple [] = ([], [])
| toTuple (pair::pairs) = add pair (toTuple pairs)
这三种方法或多或少是等价的。
响应@pyon 的尾递归变体,
fun loop (xs, ys, nil) = (rev xs, rev ys)
| loop (xs, ys, (x, y) :: zs) = loop (x :: xs, y :: ys, zs)
fun toTuple xs = loop (nil, nil, xs)
由于我正在尝试更多地了解该语言,请问为什么要使用第二个函数?是为了让它更简单,并传入最终将成为返回元组的第一和第二部分的两个空值吗?此外,是否有任何方法可以在不使用辅助功能的情况下做到这一点?您的解决方案将有效,但我想了解更多有关此问题的信息。
如果您将我的三个解决方案与 pyon 的解决方案进行比较,他的解决方案在某些方面有所不同:他有一个递归的辅助函数 loop,而我使用辅助函数 add、add 编写的版本不是' t 递归,只管理元组的解包和重新打包。 toTuple 仍然只有一个参数,但loop 有两个参数,一个用于存储临时结果x :: xs,另一个用于y :: ys。
当您从左到右处理列表,但将结果累积到参数中时,输入中的第一个元素将成为第一个添加到累积结果中的元素,这意味着它最终成为结果的最后一个元素. (您可以将列表视为此处的堆栈。)
当累积的结果是一个应该与输入顺序相同的元素列表时,这就不是那么幸运了。您可以通过手动评估函数来最好地看到这一点:
首先,对于我的toTuple [(1,2),(3,4),(5,6)] 版本:
toTuple [(1,2),(3,4),(5,6)]
~> case toTuple [(3,4),(5,6)] of
(xs, ys1) => (1::xs1, 2::ys)
~> case (case toTuple [(5,6)] of
(xs', ys') => (3::xs', 4::ys')) of
(xs, ys) => (1::xs, 2::ys)
~> case (case (case toTuple [] of
(xs'', ys'') => (5::xs'', 6::ys'')) of
(xs', ys') => (3::xs', 4::ys')) of
(xs, ys) => (1::xs, 2::ys)
~> case (case (case ([], []) of
(xs'', ys'') => (5::xs'', 6::ys'')) of
(xs', ys') => (3::xs', 4::ys')) of
(xs, ys) => (1::xs, 2::ys)
~> case (case (5::[], 6::[]) of
(xs', ys') => (3::xs', 4::ys')) of
(xs, ys) => (1::xs, 2::ys)
~> case (3::5::[], 4::6::[]) of
(xs, ys) => (1::xs, 2::ys)
~> (1::3::5::[], 2::4::6::[]
~> ([1,3,5], [2,4,6])
第二,@pyon 的版本:
toTuple [(1,2),(3,4),(5,6)]
~> loop ([], [], [(1,2),(3,4),(5,6)])
~> loop (1::[], 2::[], [(3,4),(5,6)])
~> loop (3::1::[], 4::2::[], [(5,6)])
~> loop (5::3::1::[], 6::4::2::[], [])
~> (rev [5,3,1], rev [6,4,2])
~> ...
~> ([1,3,5], [2,4,6])
编辑:正如@pyon 在评论中指出的那样,它们使用相同数量的内存和时间。不同之处在于我的版本使用隐式(调用)堆栈,而他的版本使用显式(参数)堆栈。