Haskell:带有元组的映射函数

发布于 2024-12-01 04:35:08 字数 543 浏览 1 评论 0原文

我必须编写一个 Haskell 程序来执行以下操作:

Main> dotProduct [(1,3),(2,5),(3,3)]  2
[(2,3),(4,5),(6,3)]

我必须在使用和不使用 map 函数的情况下执行此操作。 我已经在没有 map 的情况下做到了这一点,但我不知道如何使用 map 做到这一点。

我的 dotProduct 没有 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

所以我真的需要 map 版本的帮助。

I have to write a Haskell program that does the following:

Main> dotProduct [(1,3),(2,5),(3,3)]  2
[(2,3),(4,5),(6,3)]

I have to do it both with and without map function.
I already did it without map, but I have no clue to do it with map.

My dotProduct without map function:

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

So I really need help with the map version.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风筝有风,海豚有海 2024-12-08 04:35:08

不要从尝试以某种方式适应 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

将其与第三种情况,我们可以看到它们是相同的,除了它专门用于当 xys[] 时。因此,我们可以简单地完全消除第二种情况:

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

接下来,我们可以观察到两件事:< code>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< /code> 仅用于元组的第一个元素。这听起来很有用,所以我们将其提取为一个单独的函数:

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

现在我们再次观察到,在 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

Rather than starting by trying to fit map in somehow, consider how you might simplify and generalize your current function. Starting from this:

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

First, we'll rewrite the second case using the (:) constructor:

dotProduct ((x,y):[]) z = (x*z,y):[]

Expanding the [] in the result using the first case:

dotProduct ((x,y):[]) z = (x*z,y):dotProduct [] z

Comparing this to the third case, we can see that they're identical except for this being specialized for when xys is []. So, we can simply eliminate the second case entirely:

dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct [] _ = []
dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z

Next, generalizing the function. First, we rename it, and let dotProduct call it:

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

First, we parameterize it by the operation, specializing to multiplication for 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

Next, we can observe two things: generalized doesn't depend on arithmetic directly anymore, so it can work on any type; and the only time z is used is as the second argument to f, so we can combine them into a single function argument:

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

Now, we note that f is only used on the first element of a tuple. This sounds useful, so we'll extract that as a separate function:

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

Now we again observe that, in generalized, f is only used with onFirst, so we again combine them into a single function argument:

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

And once again, we observe that generalized no longer depends on the list containing tuples, so we let it work on any type:

generalized :: (a -> b) -> [a] -> [b]
generalized _ [] = []
generalized f (x:xs) = f x : generalized f xs

Now, compare the code for generalized to this:

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

It also turns out that a slightly more general version of onFirst also exists, so we'll replace both that and generalized with their standard library equivalents:

import Control.Arrow (first)

dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)]
dotProduct xs z = map (first (* z)) xs
不乱于心 2024-12-08 04:35:08
dotProduct xs z = map (\(x,y) -> (x*z,y)) xs

(\(x,y) -> (x*z,y)) 部分是一个函数,它接受一对并返回一个新对,该新对与旧对类似,只是它的第一个组件是乘以zmap 函数接受一个函数并将其应用于列表中的每个元素。因此,如果我们将 (\(x,y) -> (x*z,y)) 函数传递给 map,它将将该函数应用于中的每个元素xs

虽然你确定你的第一个是正确的吗?点积运算通常被定义为将两个向量相乘,将相应的分量相乘,然后将它们全部相加。像这样:

dotProduct xs ys = sum $ zipWith (*) xs ys
dotProduct xs z = map (\(x,y) -> (x*z,y)) xs

The (\(x,y) -> (x*z,y)) part is a function which takes a pair and returns a new pair that's like the old one, except its first component is multiplied by z. The map function takes a function and applies it to each element in a list. So if we pass the (\(x,y) -> (x*z,y)) function to map, it will apply that function to every element in xs.

Although are you sure your first one is correct? The dot product operation is usually defined so that it takes two vectors, multiplies corresponding component and then sums it all together. Like this:

dotProduct xs ys = sum $ zipWith (*) xs ys
何其悲哀 2024-12-08 04:35:08

EEVIAC 已经发布了答案,所以我将解释如何自己想出答案。您可能知道,map 具有类型签名 (a -> b) ->; [一]-> [b]。现在,dotProduct 的类型为 [(Float, Integer)] ->浮动-> [(Float, Integer)] 并且您将在其中的某处调用 map,因此它必须看起来像这样:

dotProduct theList z = map (??? z) theList

其中 ???Float 类型的函数 -> (浮点数、整数)-> (Float, Integer) - 这直接来自于 map 的类型签名以及我们将 z 传递给函数的事实,我们必须这样做,只是因为没有其他地方可以使用它。

map 和高阶函数的一般情况是,您必须记住高阶函数的作用并“简单地”为其提供正确的功能。由于 map 将给定函数应用于列表中的所有元素,因此您的函数只需要处理一个元素,并且您可以忘记列表的所有内容 - map 会照顾到它的。

EEVIAC already posted the answer, so I'll just explain how to come up with it yourself. As you probably know, map has the type signature (a -> b) -> [a] -> [b]. Now, dotProduct has the type [(Float, Integer)] -> Float -> [(Float, Integer)] and you'll call map somewhere in there, so it has to look something like this:

dotProduct theList z = map (??? z) theList

where ??? is a function of type Float -> (Float, Integer) -> (Float, Integer) - this follows immediately from the type signature of map and from the fact that we pass z to the function, which we have to do, simply because there's no other place to use it in.

The thing with map and higher order functions in general is that you have to keep in mind what the higher order function does and "simply" supply it with the correct function. As map applies a given function to all elements in the list, your function only needs to work with one element, and you can forget all about the list - map will take care of it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文