3

我是haskell的新手,我正在尝试制作一个将int乘以列表的每个元素的函数。

例子:

mult 2 [2,4,6]

回报:

[4,8,12]
4

3 回答 3

18

将问题分解为概念步骤:

  • 你想对列表的每个元素做一些事情,这是map函数提供的一般操作。使用它,您可以忽略列表,只考虑您需要对单个元素执行的操作。

  • 您想将两个数字相乘。在函数的任何一次使用中,其中一个数字将是常数,因此我们可以给它一个名称作为函数的参数:mult x = .... 现在我们可以把x它当作一个常数,只关心另一个数字。

  • 另一个数字不是常数,所以你需要一个函数,而不仅仅是一个简单的表达式。Haskell 提供了“运算符部分”来使用中缀运算符来执行此操作,例如(*),因此使用x我们 get (x *)

  • 退出最后几个步骤,您现在要给出x一个名称,并创建一个函数,然后将其传递给map

mult x = map (x *)

......如果你愿意的话,你实际上已经完成了。但是对于初学者来说,将列表作为明确的参数可能会更清楚:

mult x ys = map (x *) ys

不过,这两种形式都做同样的事情。

于 2012-09-27T23:37:05.750 回答
9

我最初以一种有点不屑一顾的高尔夫风格回答了这个问题,所以让我们再试一次。我的意图是双重的:赎回自己为新人写了一个令人讨厌的睡眠剥夺的答案,:-),并尝试暗示无意义的风格:

mult_l = map . (*)

那个代码有什么作用?

以流水线的方式考虑它是有帮助的:

  • 该代码接受(*)并将其输入map.

  • 是什么类型的(*)?嗯,是的Num a => a -> a -> a。这意味着它做什么?接受一个数字(调用它x),并给你另一个函数,这个函数将——如果给定另一个数字(调用它y)——计算x“次” y。(我把“次”放在括号中,因为Num它是一个类型类......)

  • 现在您将使用 (*). map做什么map?好吧,让我们看看它的类型:(a -> b) -> [a] -> [b]. 所以现在,map将一个函数作为参数并将该函数应用于列表的每个成员。

现在想想组合是如何工作的:(f . g) x = f (g x).

如果给定一个论点,说 2:

mult_l 2

是真的

(map . (*)) 2

真的只是:

map (* 2)

现在,map接受两个参数,一个函数(表示“你想让我做什么?”)和一个列表(表示“你想让我做什么?”)。 map然后获取该列表中的每个项目,并将函数应用于元素,逐点进行。

这意味着,如果您采取以下措施:

map (* 2) [1,2,3]

然后 map 将获取列表,并查看第一个元素,然后将其乘以 2,查看第二个元素,等等...

所以 的类型map . (*)Num a => a -> [a] -> [a]因为它接受一个数字x,然后将高阶函数\x -> (* x)提供给map

现在你面临的挑战问题(因为你是 Haskell 的新手),是如何写map?我给你一个提示:

  • 如果要在空列表上映射函数,则返回空列表。
  • 如果你想将一个函数映射到一个列表的头上,连同它的尾,你取头,将函数应用到头上,然后对列表的其余部分做同样的事情。

由此你得到 的递归定义map,以及相关的归纳原则。

你可能会疑惑的另一件事是:为什么我必须写map . (*)而不是map (*). 如果您考虑一下这个问题,您可能会对无点风格变得更加开明

于 2012-09-27T23:32:17.740 回答
8

由于没有人谈论列表推导,所以只是讲述另一种方式。

mult x ys = [i*x | i <- ys]
于 2012-09-28T02:34:55.527 回答