我是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]