【问题标题】:Comparing Two Lists in SML using Fold/Map?使用折叠/映射比较 SML 中的两个列表?
【发布时间】:2016-05-10 21:47:57
【问题描述】:

假设我有两个列表,[1,2,3][9,2,3]。假设我得到了第三个值,2。如果我想知道这个值是否在两个列表中,但我只能使用 foldl/foldr/map 这样做(在 map/foldr/foldl 之外没有 let 环境或自定义递归),我该怎么做?这是一个编程课的家庭作业问题,我已经坚持了一个星期了。

【问题讨论】:

  • 到目前为止你尝试过什么?您对转换这些列表有什么想法?
  • 如果您不需要 聪明,您可以采用显而易见的解决方案,并考虑如何用您所拥有的来表示 filter,然后如何决定是否2 是一个列表的元素(使用filter) - 最后它只是一个列表平等

标签: list functional-programming sml fold


【解决方案1】:

你可以做很多事情来完成这个任务:

  1. 为函数写一个存根,这样你就知道你在处理什么:

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

    这个函数返回bool类型的东西。

  2. 想想如果只是一个列表中的成员,你会如何解决这个问题:

    (* 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 的存在是否对函数的结果产生影响,或者是否任何先前考虑的元素产生影响(使用acc 作为代理) .

    第二个... 是一个bool,表示xs 为空时的默认值,是foldl 执行的递归的基本情况。

    请注意,标准 ML 中的折叠是一个急切的过程:它会遍历整个列表,直到得出元素存在的结论。因此,平均而言,List.exists 是搜索单个列表的更好组合子。在惰性求值语言中,折叠可能是等价的。

  5. 继续为两个列表解决这个问题,很简单:

    fun isInBoth (xs, ys, z) = isInList (xs, z) andalso isInList(ys, z)
    
  6. (可选) 考虑如何将这两个递归调用交织在一起并创建成对折叠。实际上,有一个名为 ListPair.foldl 的函数是这样工作的:

    (* 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.foldlListPair.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 *)
    

    将递归模式抽象为类似于ListPair.foldl 的函数可能没那么有用。

【讨论】:

    【解决方案2】:

    您可以简单地使用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))
    

    这是优雅、简单且单行的(但可以说,出于样式目的,您可能希望它多行)。

    【讨论】:

      猜你喜欢
      • 2012-03-26
      • 2023-03-21
      • 2018-12-05
      • 2012-11-10
      • 2019-07-12
      • 2021-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多