如何映射带有两个参数(而不是一个)的函数?
我有一个 Haskell 问题,我想出了一个解决方案,但我就是不能把笔写在纸上。
基本上我有一个函数calc,它接受一个Int并返回一个Float.
calc :: Int -> Int -> Float
calc :: Int -> Int -> Float
在我的函数中,g我想将此函数应用于数组
g :: [Int] -> [Float]
由于calc需要两个Int参数,我想消耗两个Ints(
x作为数组的第一个索引和x+1第二个索引)。这将继续在(数组索引)[1][2], [2][3], [3][4]等上重复该函数。
我试过在网上研究如何使用map,它基本上是这样说的?知道为什么代码不想编译,x+1甚至是可行的吗?
回答
一元映射是map但二元映射是zipWith。那么什么是拉链?同一列表的两个副本,一个位置相对另一个位置偏移:
calc :: Int -> Int -> Float
g :: [Int] -> [Float]
g xs = zipWith calc xs (drop 1 xs)
这就是全部。当较短的序列用完时,压缩会自动停止。
zipWith foo适用foo于每一对参数,每个参数来自相应的列表,一个接一个的元素。它可以使用zip作为实现
zipWith foo xs ys = map ( (x,y) -> foo x y) $ zip xs ys
= [ foo x y | (x,y) <- zip xs ys ]
但它自己已经做到了。
作为一个例子,drop 1 [1,2,3] == [2,3]这意味着
zipWith calc [1, 2, 3] (drop 1 [1, 2, 3]) ==
zipWith calc [1, 2, 3]
[2, 3 ] ==
[calc 1
2,
calc 2
3]
您想使用索引。索引实际上从未在 Haskell 中与列表一起使用。我们改为通过结构抖动实现相同的效果,然后当我们沿着两个列表一次一个元素前进时,每次操作都是O(1),因为下一个元素已经在高级列表的顶部。而对于索引,我们每次都必须从顶部重新遍历相同的列表,导致每个( th ) 元素的O(k)时间,即整体二次行为。k
更新:采用压缩方法导致代码融合的可能性,
g2 xs = zipWith calc xs (drop 1 xs)
= [calc x y | (x,y) <- zip xs (drop 1 xs)]
= [calc x y | (x:y:_) <- tails xs] -- tails ~= iterate (drop 1)
因为当它们已经出现在结构中时,为什么要构建一个新结构来容纳两个连续元素......原始列表结构。
有趣的是,Common Lisp 有这种特殊的映射,尾部映射,作为语言的一部分。虽然常规地图在那里被称为mapcar,但尾巴上的地图在那里被称为maplist(准确地说,它是非空尾巴上的地图)。