使用ListPair.foldr在SML中实现zipWith

时间:2013-01-27 19:07:07

标签: list functional-programming sml currying

背景:SML初级水平

我的作业要求我使用ListPair.foldr并且只使用此函数来实现zipWith函数。

ListPair.foldr : ('a * 'b * 'c -> 'c) -> 'c -> 'a list * 'b list -> 'c
zipWith : ('a * 'b -> 'c) -> 'a list -> 'b list -> 'c list

ListPair.foldr返回一个'c元素,而zipWith返回一个'c列表,所以我的方法很自然地会反复使用ListPair.foldr来溢出单个'c元素,然后我将这些元素放入我的'c列表中。 ListPair.foldr根据提供的函数获取一对列表并将它们相互折叠,因此获得所需效果的唯一方法是一次从每个列表中获取一个元素,将其作为一个列表提供给ListPair.foldr对列表,获取结果,并将其连接到下一轮。 我还必须将函数从('a *'b->'c)转换为('a *'b *'c->'c)。 像这样:

fun zipWith f [] l2 = []
| zipWith f l1 l2 = 
let val f2 = fn (a,b,c)=> f(a,b)+c   (* converting the function *)
    val xh = [hd(l1)]      (*first element of 'a list, as a list itself *)
    val yh = [hd(l2)]      (*first element of 'b list, as a list itself *)
    val xt = tl(l1)        (*taking the tail of 'a list*)
    val yt = tl(l2)        (*taking the tail of 'b list*)
in
    ListPair.foldr f2 0 (xh, yh)::    (*perform the operation with the two heads*)
    zipWith f xt yt                   (*recursively call zipWith with the remainder*)
end;

这很有效。

- zipWith (fn (x,y)=>x+y) [1,2,3] [10,20,30];
val it = [11,22,33] : int list

但现在是棘手的部分:我不应该递归地执行此操作,也就是说,我无法在我的zipWith函数中调用zipWith。 这甚至可能吗?从我读到的,Haskell中的实际zipWith函数是递归定义的;我如何非递归地执行此操作?

我无法想象教授敦促我们以面向对象的方式用while循环等方式做到这一点(无论如何,我尝试过,即便如此,我的水平还不够。)

我是否完全错误的方向?我该如何处理这个问题?

-----------------回答----------------

我最初尝试过pad的解决方案:

fun zipWith f l1 l2 = 
let val f2 = fn (a,b,c)=> f(a,b)::c
in
    ListPair.foldr f2 0 l1 l2
end;

但它不起作用,因为我将它追加到0而不是[]。这些类型没有成功,我无法理解!

谢谢!

1 个答案:

答案 0 :(得分:4)

您的方法是正确的但不必要的复杂。函数zipWith是递归的,但您可以非递归地定义它,因为ListPair.foldr已经具有递归性质。

要靠近zipWith,您需要具有以下签名的ListPair.foldr的专用版本

ListPair.foldr : 
   ('a * 'b * 'c list -> 'c list) -> 'c list -> 'a list * 'b list -> 'c list

这意味着您将空列表作为累加器传递,并在此过程中构建更大的列表。在zipWith f xs ys中,f具有'a * 'b -> 'c签名,因此很容易适应:

fun zipWith f xs ys =
    ListPair.foldr (fn (a, b, cs) => f(a, b)::cs) [] (xs, ys)