将值过滤为两个列表

时间:2013-04-09 03:24:03

标签: sml smlnj

所以我是sml的新手,并试图理解它的输入/输出。最近我尝试创建一个带有两个参数的过滤器:一个函数(返回一个布尔值),以及一个针对该函数运行的值列表。过滤器的作用是返回对函数返回true的值列表。

代码:

fun filter f [] = []  |
   filter f (x::xs) =
      if (f x)
      then x::(filter f xs)
      else (filter f xs);

这样有效。但是我现在要做的只是返回包含真值列表的元组,并且返回false。我坚持我的条件,我真的看不到另一种方式。有关如何解决这个问题的想法吗?

代码:

fun filter2 f [] = ([],[])  |
   filter2 f (x::xs) =
      if (f x)
      then (x::(filter2 f xs), []) (* error *)
      else ([], x::(filter2 f xs)); (* error *)

2 个答案:

答案 0 :(得分:5)

我认为有几种方法可以做到这一点。

重新使用过滤器

例如,我们可以使用归纳方法,因为你的元组将由两个元素组成,第一个是满足谓词的元素列表,第二个是不满足谓词的元素列表。因此,您可以重复使用过滤器功能:

fun partition f xs = (filter f xs, filter (not o f) xs)

这不是最好的方法,因为它会对列表进行两次评估,但如果列表很小,这很明显且非常易读。

<强>折叠

考虑到这一点的另一种方式是折叠。您可以认为您将列表缩减为元组列表,并且随着时间的推移,您可以根据谓词拆分项目。 Somwe是这样的:

fun parition f xs = 
    let
        fun split x (xs,ys) =
            if f x
            then (x::xs,ys)
            else (xs, x::ys)

        val (trueList, falseList) = List.foldl (fn (x,y) => split x y) 
                                                   ([],[]) xs
    in
        (List.rev trueList, List.rev falseList)
    end

<强>分区之前

你也可以用与SML的List.parition方法相同的方式实现你自己的折叠算法:

fun partition f xs = 
    let
        fun iter(xs, (trueList,falseList)) = 
            case xs of
                 [] => (List.rev trueList, List.rev falseList)
               | (x::xs') => if f x
                             then iter(xs', (x::trueList,falseList))
                             else iter(xs', (trueList,x::falseList))
    in
        iter(xs,([],[]))
    end

使用SML基础方法

最终,您可以避免所有这些并使用其文档说明的SML方法List.partition

  

分区f l

     

从左到右将f应用于l的每个元素x,并返回a   pair(pos,neg)其中pos是f x的列表   评估为真,而neg是f x的列表   评价为假。 pos和neg的元素保持不变   他们在l。中拥有的相对顺序。

此方法与前面的示例一样实现。

答案 1 :(得分:2)

所以我会展示一个很好的方法来做到这一点,以及一个更好的方法(IMO)。但是,当您了解到时,“更好的方式”仅供将来参考:

fun filter2 f [] = ([], [])
  | filter2 f (x::xs) = let

  fun ftuple f (x::xs) trueList falseList =  
    if (f x) 
      then ftuple f xs (x::trueList) falseList 
    else ftuple f xs trueList (x::falseList)

    | ftuple _ [] trueList falseList = (trueList, falseList)

in
  ftuple f (x::xs) [] []
end;

你的工作不起作用的原因是因为当你调用x::(filter2 f xs)时,编译器天真地假设你正在构建一个列表,它并不认为它是一个元组,它是进入函数调用的范围。因此,当您认为自己结果类型是列表元组时,编译器会获得隧道视觉并认为结果类型是列表。在我看来这是更好的版本,你应该查看函数foldr如果你很好奇,使用这种技术要好得多,因为它更具可读性,更简洁,更重要的是...... 更可预测和更强大

fun filter2 f l = foldr (fn(x,xs) => if (f x) then (x::(#1(xs)), #2(xs)) else (#1(xs), x::(#2(xs)))) ([],[]) l;

第一个示例的工作原因是因为您存储的默认空列表会累积符合条件的变量副本,或者不符合条件。但是,您必须明确告诉SML编译器确保类型规则一致。您必须确保SML知道您的返回类型是列表元组。此命令链中的任何错误,这将导致无法执行。因此,在使用SML时,请始终研究您的类型推断。至于第二个,你可以看到它是一个单行,但我会让你自己研究那个,只是google foldrfoldl