与其从试图以某种方式适应开始,不如map
考虑如何简化和概括当前的功能。从此开始:
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct [] _ = []
dotProduct [(x,y)] z = [(x*z,y)]
dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z
首先,我们将使用(:)
构造函数重写第二种情况:
dotProduct ((x,y):[]) z = (x*z,y):[]
[]
使用第一种情况扩展结果:
dotProduct ((x,y):[]) z = (x*z,y):dotProduct [] z
将其与第三种情况进行比较,我们可以看到它们是相同的,除了 this 专门用于 when xys
is []
。因此,我们可以简单地完全消除第二种情况:
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct [] _ = []
dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z
接下来,泛化功能。首先,我们重命名它,然后dotProduct
调用它:
generalized :: [(Float, Integer)] -> Float -> [(Float, Integer)]
generalized [] _ = []
generalized ((x,y):xys) z = (x*z,y):generalized (xys) z
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = generalized xs z
首先,我们通过操作对其进行参数化,专门用于乘法dotProduct
:
generalized :: (Float -> Float -> Float) -> [(Float, Integer)] -> Float -> [(Float, Integer)]
generalized _ [] _ = []
generalized f ((x,y):xys) z = (f x z,y):generalized f (xys) z
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = generalized (*) xs z
接下来,我们可以观察到两件事:generalized
不再直接依赖算术,因此它可以在任何类型上工作;并且唯一的时间z
被用作 的第二个参数f
,因此我们可以将它们组合成一个函数参数:
generalized :: (a -> b) -> [(a, c)] -> [(b, c)]
generalized _ [] = []
generalized f ((x,y):xys) = (f x, y):generalized f (xys)
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = generalized (* z) xs
现在,我们注意到它f
仅用于元组的第一个元素。这听起来很有用,所以我们将其提取为一个单独的函数:
generalized :: (a -> b) -> [(a, c)] -> [(b, c)]
generalized _ [] = []
generalized f (xy:xys) = onFirst f xy:generalized f (xys)
onFirst :: (a -> b) -> (a, c) -> (b, c)
onFirst f (x, y) = (f x, y)
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = generalized (* z) xs
现在我们再次观察到 in仅与 一起使用generalized
,因此我们再次将它们组合成一个函数参数:f
onFirst
generalized :: ((a, c) -> (b, c)) -> [(a, c)] -> [(b, c)]
generalized _ [] = []
generalized f (xy:xys) = f xy:generalized f (xys)
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = generalized (onFirst (* z)) xs
再一次,我们观察到它generalized
不再依赖于包含元组的列表,所以我们让它适用于任何类型:
generalized :: (a -> b) -> [a] -> [b]
generalized _ [] = []
generalized f (x:xs) = f x : generalized f xs
现在,将代码generalized
与此进行比较:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
事实证明,onFirst
还存在一个稍微更通用的版本,所以我们将generalized
用它们的标准库等价物替换它:
import Control.Arrow (first)
dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = map (first (* z)) xs