背景: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而不是[]。这些类型没有成功,我无法理解!
谢谢!
答案 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)