我想递归地获取一个2元组的列表(例如[[(1,2),(3,4),(5,6)]),然后将其变成两个int列表的元组(结果:( [1、3、5],[2、4、6]))。我了解相反的方法(将两个列表的元组转换为元组列表),但不了解如何对同一列表进行递归调用。
到目前为止,这是我的代码,我想我很接近:
fun toTuple [] = ([], [])
| toTuple [((x:int, y:int)::xs)] = (x::[], y::[]) toTuple (xs).
编译器给我错误:
Error: operator is not a function [tycon mismatch]
operator: int list * int list
in expression:
(x :: nil,y :: nil) unzip
我认为这意味着我需要在(x :: [],y :: [])和toTuple(xs)之间放置一个运算符。我希望递归将元组项放到我创建的相同列表中,并且我不知道执行类似操作的运算符。
谢谢。
答案 0 :(得分:1)
我将使用显式的累加器参数进行此操作:
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)
事后看来,以下做法会更有效率:
fun loop (xs, ys, nil) = (xs, ys)
| loop (xs, ys, (x, y) :: zs) = loop (x :: xs, y :: ys, zs)
fun toTuple xs = loop (nil, nil, rev xs)
答案 1 :(得分:1)
以下是使用简单的递归函数的方法:
fun toTuple [] = ([], [])
| toTuple ((x,y)::pairs) =
case toTuple pairs of
(xs, ys) => (x::xs, y::ys)
它将递归处理其余的pairs
,将结果解压缩为(xs, ys)
,然后将x
和y
添加到该结果中。您可以使用let绑定代替情况:
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
编写的版本,{ {1}}不是递归的,仅管理对元组的拆包和重新包装。 add
仍然只有一个参数,但是toTuple
还有两个参数,一个用于存储临时结果loop
,另一个用于x :: xs
。
当您从左至右处理列表,但将结果累加到一个参数中时,输入中的第一个元素将成为第一个添加到累加结果中的元素,这意味着它最终成为结果的最后一个元素。 (您可以在此处将列表视为堆栈。)
当累积结果是应该与输入顺序相同的元素列表时,这不是很幸运。您可以通过手动评估功能来最好地看到这一点:
首先,对于我的y :: ys
版本:
toTuple [(1,2),(3,4),(5,6)]
第二,对于@pyon版本:
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在评论中指出的那样,它们使用相同的内存和时间。区别在于我的版本使用隐式(调用)堆栈,而他的版本使用显式(参数)堆栈。