以递归方式将项目添加到SML中的2元列表

时间:2018-10-08 03:06:38

标签: recursion sml smlnj

我想递归地获取一个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)之间放置一个运算符。我希望递归将元组项放到我创建的相同列表中,并且我不知道执行类似操作的运算符。

谢谢。

2 个答案:

答案 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),然后将xy添加到该结果中。您可以使用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在评论中指出的那样,它们使用相同的内存和时间。区别在于我的版本使用隐式(调用)堆栈,而他的版本使用显式(参数)堆栈。