使用折叠/贴图比较SML中的两个列表?

时间:2016-02-02 03:46:56

标签: list functional-programming sml fold

说我有两个列表,[1,2,3][9,2,3]。说我给了第三个值2。如果我想知道这个值是否在两个列表中,但我只能使用foldl / foldr / map来执行此操作(不允许在map / foldr / foldl之外的环境或自定义递归),我该怎么办?这是一个编程课的作业问题,我已经坚持了一个星期了。

2 个答案:

答案 0 :(得分:1)

您可以采取一些措施来完成此任务:

  1. 为函数编写存根,以便知道您正在处理的是什么:

    fun isInBoth (xs, ys, z) = ...
    

    此函数返回 bool 类型的内容。

  2. 如果它只是 one 列表中的成员资格,请考虑如何解决此问题:

    (* Using built-in functions *)
    fun isInList (xs, z) = List.exists (fn x => x = z) xs
    
    (* Using recursion directly *)
    fun isInList ([], z) = false
      | isInList (x::xs, z) = x = z orelse isInList (xs, z)
    
  3. 排除map的使用,因为这会产生'b列表,而不是 bool
  4. 继续使用折叠只为一个列表来解决这个问题:

    fun isInList (xs, z) = foldl (fn (x, acc) => ...) ... xs
    

    其中accfoldl在每次递归调用时累积的值。

    第一个...必须反映列表中值x的存在是否会对函数的结果产生影响,或者是否有任何先前认为的元素产生差异(使用{{1}作为代理)。

    第二个acc是一个 bool ,表示...为空时的默认值,是xs执行的递归的基本案例

    请注意,标准ML中的折叠是一个急切的过程:它会遍历整个列表,超出了元素存在的结论。因此,平均而言,foldl是搜索单个列表的更好组合。在懒惰评估的语言中,折叠可能是等效的。

  5. 继续为两个列表解决这个问题:

    List.exists
  6. (可选)考虑如何将这两个递归调用交织在一起并创建成对折叠。实际上,有一个名为ListPair.foldl的函数可以这样工作:

    fun isInBoth (xs, ys, z) = isInList (xs, z) andalso isInList(ys, z)
    

    但它带来了恼人的副作用:

    (* Finds the largest positive integer in two lists, or 0 *)
    fun max3 (x, y, z) = Int.max (x, Int.max(y, z))
    fun maxTwoLists (xs, ys) = ListPair.foldl max3 0 (xs, ys)
    

    因此,如果你想迭代两个列表并成对地看待它们的元素,并在另一个列表中继续查看另一个列表,并在符合条件或不再满足条件时停止折叠,那么你正在处理一种递归方案,val huh = maxTwoLists ([1,2,3], [1,2,3,4]) (* gives 3 *) List.foldl都不支持开箱即用。如果这不是要求折叠的学校练习,这将是一个解决方案:

    ListPair.foldl

    将递归模式抽象为类似于fun isInList (xs, z) = List.exists (fn x => x = z) xs fun isInBoth (x::xs, y::ys, z) = x = z andalso isInList (y::ys, z) orelse (* no needs to look at more xs *) y = z andalso isInList (x::xs, z) orelse (* no needs to look at more ys *) isInBoth (xs, ys, z) (* keep looking in both *) | isInBoth ([], ys, z) = false (* not found in xs *) | isInBoth (xs, [], z) = false (* not found in ys *) 的函数可能没那么有用。

答案 1 :(得分:0)

您可以使用foldl函数中的某种形式的保证,并与and运算符结合使用:

fun intersection l1 l2 v = (#1 (foldl (fn (x,y) => if (x = (#2 y)) then (true, (#2 y)) else y) (false, v) l1)) andalso (#1 (foldl (fn (x,y) => if (x = (#2 y)) then (true, (#2 y)) else y) (false, v) l2))

这是优雅,简单和单行(但可以说,出于风格目的,你可能希望它不止一行)。