【问题标题】:How do I perform these basic operations for linked lists in a functional language?如何在函数式语言中对链表执行这些基本操作?
【发布时间】:2018-01-09 02:10:18
【问题描述】:

列表到链接列表的转换

我正在尝试在标准 ML 中将列表转换为单链表(不确定是否可以使用双链表)。但是,我不确定这是如何完成的,但会展示我目前的尝试。

datatype 'a mlist = Nil
        | Cons of 'a * 'a mlist ref

fun listToLL [] = Nil
    | listToLL (x::xs) = Cons(x, (ref (listToLL xs)))

我首先在数据类型上创建了一个链表,但我对“ref”函数感到困惑。我想避免将链接列表变成惰性列表,因此我可以使用 ref 显式地将下一个序列设为仅对前一个序列的引用。但问题是这个函数根本没有使用链表数据类型,即使我不使用这个链表定义也能工作。那么我的实现真的正确吗?

作为一个附带问题,将链接列表与惰性列表区分开来的唯一因素是“ref”函数吗?从他们的数据类型定义来看,好像是这样的。

将两个链表连接在一起

fun joining (mlpx, mlpy) = 
    case !mlpx of
        Nil => mlpx := mlpy
    |   Cons(_, mlp) => joining(mlp, mlpy)          


fun join (mlpx, mlpy) = 
    let
        val mlp = ref mlpx
    in
        joining(mlp, mlpy);
        !mlp
    end;    

对于第二个操作,我对第二个连接函数中需要ref 感到非常困惑。当我处理一个链表时,我希望我处理一个引用还是我处理包含内部引用的整个 Cons 结构?我不明白为什么我们不能单独使用 joining 函数,因为它似乎真的足够了。为什么需要在join函数中创建ref

【问题讨论】:

    标签: linked-list sml


    【解决方案1】:

    我首先在数据类型上创建了一个链表,但我对“ref”函数感到困惑。我想避免将链接列表变成惰性列表 [...]

    [...]

    作为一个附带问题,将链接列表与惰性列表区分开来的唯一因素是 'ref' 函数吗?

    惰性列表完全不同,因为它们需要“暂停”计算,通常是通过创建闭包(例如,请参阅 this answer 了解 SML 中惰性列表的实现)。您的实现并不懒惰。 ref 的使用只允许可变性。

    但问题是这个函数根本没有使用链表数据类型,即使我不使用这个链表定义也能工作。那么我的实现真的正确吗?

    您对listToLL 的实现是正确的。我不确定我是否理解您对这个问题的其余部分的意思。您的函数确实使用链表数据类型,因为它使用构造函数NilCons

    我对第二个连接函数中需要 ref 感​​到非常困惑。当我处理一个链表时,我希望我处理一个引用还是我处理包含来自内部的引用的整个 Cons 结构?

    你在这里的困惑是很自然的。我记得当我第一次尝试在 SML 中实现可变链表时,我对完全相同的问题感到非常困惑。

    您提供的join 实现创建了一个额外的引用来干净地处理第一个列表为空的情况,因为辅助函数joining'a mlist ref 类型的对象而不是'a mlist 进行操作。但是,可以避免创建这个额外的 ref。以下是我的做法:

    (* find the last ref of a linked list *)
    fun getLast mlp =
      case !mlp of
        Nil => mlp
      | Cons (_, mlp') => getLast mlp'
    
    fun join (mlpx, mlpy) =
      case mlpx of
        Nil => mlpy
      | Cons (_, mlp) => (getLast mlp := mlpy; mlpx)
    

    【讨论】:

      【解决方案2】:

      将列表转换为单链表

      SML 中的列表已经实现为链表。

      当你定义一个数据类型时

      datatype 'a mylist = Nil | Cons of 'a * 'a mylist
      

      您正在创建一个与带有[] (nil) 和:: (缺点) 的内置'a list 类型同构的类型)。

      您的尝试似乎是如何使用引用制作一个可变链表。

      我想避免将链表变成惰性列表

      正如 Sam Westrick 所指出的,惰性列表通常使用闭包来制作:

      datatype 'a lazylist = Nil | Cons of 'a * (unit -> 'a lazylist)
      

      编辑:查看他的example of suspensions [stackoverflow.com] 了解更多详情。

      不确定双向链表是否可行

      可变链表和双向链表都可以使用引用,但我不建议在 SML 中使用这种方法。如果您需要向前和向后遍历列表,或者如果您需要对其进行更新,您可以使用 zippers 纯粹在功能上这样做:

      原始文章有 OCaml 中的示例,LYAH 章节中有 Haskell 中的示例。

      我想在标准 ML 中看到一些好的示例,但我没有找到任何示例。

      【讨论】:

      • 只是关于惰性的注释,对于那些好奇的人(Simon,我猜你可能只是为了简洁而省略了这个):通常,惰性求值保证表达式最多被求值一次。因此,虽然用函数(即记忆函数)实现惰性是可能,但函数类型本身并不提供这种保证。例如,在惰性列表的这个定义中,简单地将列表尾部包裹在 fn () => ... 中不会是“惰性的”。
      猜你喜欢
      • 2016-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-05
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多