我是haskell的新手,我正在尝试制作一个将int乘以列表的每个元素的函数。
例子:
mult 2 [2,4,6]
回报:
[4,8,12]
将问题分解为概念步骤:
你想对列表的每个元素做一些事情,这是map
函数提供的一般操作。使用它,您可以忽略列表,只考虑您需要对单个元素执行的操作。
您想将两个数字相乘。在函数的任何一次使用中,其中一个数字将是常数,因此我们可以给它一个名称作为函数的参数:mult x = ...
. 现在我们可以把x
它当作一个常数,只关心另一个数字。
另一个数字不是常数,所以你需要一个函数,而不仅仅是一个简单的表达式。Haskell 提供了“运算符部分”来使用中缀运算符来执行此操作,例如(*)
,因此使用x
我们 get (x *)
。
退出最后几个步骤,您现在要给出x
一个名称,并创建一个函数,然后将其传递给map
mult x = map (x *)
......如果你愿意的话,你实际上已经完成了。但是对于初学者来说,将列表作为明确的参数可能会更清楚:
mult x ys = map (x *) ys
不过,这两种形式都做同样的事情。
我最初以一种有点不屑一顾的高尔夫风格回答了这个问题,所以让我们再试一次。我的意图是双重的:赎回自己为新人写了一个令人讨厌的睡眠剥夺的答案,:-),并尝试暗示无意义的风格:
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 (*)
. 如果您考虑一下这个问题,您可能会对无点风格变得更加开明
由于没有人谈论列表推导,所以只是讲述另一种方式。
mult x ys = [i*x | i <- ys]