【问题标题】:Recursively add items to 2-tuple of lists in SML递归地将项目添加到 SML 中的 2 元组列表中
【发布时间】:2019-03-12 16:30:24
【问题描述】:

我想递归地获取一个 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) 之间放置一个运算符。我希望递归将元组项放入我创建的同一个列表中,但我不知道操作员会做这样的事情。

谢谢。

【问题讨论】:

    标签: recursion sml smlnj


    【解决方案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)
    

    【讨论】:

    • 感谢您的回复!当我试图了解更多关于一般语言的信息时,我可以问一下为什么要使用第二个函数吗?是为了让它更简单并传入两个空值,它们最终将成为返回元组的第一部分和第二部分?此外,是否有任何方法可以在不使用辅助功能的情况下做到这一点?您的解决方案将有效,但我想了解更多有关此问题的信息。
    • @Scaldiron:我使用辅助函数的主要原因是为了明确状态管理。当无限量的程序状态是隐式的时,我碰巧发现递归代码不太容易理解。除此之外,对于 Simon Shine 已经说过的话,我没有什么要补充的。
    【解决方案2】:

    以下是使用普通递归函数的方法:

    fun toTuple [] = ([], [])
      | toTuple ((x,y)::pairs) =
          case toTuple pairs of
            (xs, ys) => (x::xs, y::ys)
    

    它递归处理剩余的pairs,将结果解压缩为(xs, ys),然后将xy 添加到该结果中。您可以使用 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,而我使用辅助函数 addadd 编写的版本不是' 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 在评论中指出的那样,它们使用相同数量的内存和时间。不同之处在于我的版本使用隐式(调用)堆栈,而他的版本使用显式(参数)堆栈。

    【讨论】:

    • 我在我的解决方案中发现的唯一优势是我的状态管理是完全明确的,因此它需要更少的精力(至少对我而言)来弄清楚它是如何工作的。
    • 我的解决方案使用的空间不比你少(我没有使用所谓的“调用堆栈”,但我使用两个辅助列表用于完全相同的目的),而你的解决方案需要的空间也不少比我的时间(你仍然需要反转列表,当你基本上将它们从“调用堆栈”转移到最终答案时)。
    猜你喜欢
    • 2020-01-30
    • 2017-09-19
    • 2015-04-17
    • 2016-08-12
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多