List zip函数还需要第二个列表,如何仅使用一个参数列表

List zip函数还需要第二个列表,如何仅使用一个参数列表,list,haskell,currying,partial-application,List,Haskell,Currying,Partial Application,我开始学习Haskell,并找到了一个很好的锻炼方法。以下是: grouping: Int -> [Student]->[(Team, Student)] grouping teamNumber = zip ys where ... grap :: Int -> [a] -> [a] grab _ [] = [] grab n (x:xs) = if n <= 0 then [] else x : grab (n-1)

我开始学习Haskell,并找到了一个很好的锻炼方法。以下是:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where ...
grap :: Int -> [a] -> [a]
grab _ [] = []
grab n (x:xs) = if n <= 0 then [] else x : grab (n-1) xs  

infinite :: Num a => a -> [a]
infinite x = x : infinite(x+1)
所以,这个练习想让我试着填满剩下的部分。该函数应执行以下操作: 示例:
分组2['Mark'、'Hanna'、'Robert'、'Mike'、'Jimmy']=[(1,'Mark')、(2,'Hanna')、(1,'Robert')、(2,'Mike')、(1,'Jimmy')]

因此,我们正在组建由两名学生组成的团队,最后一名学生“吉米”没有队友

然后,我还查找预定义函数
zip
的作用。它获取两个列表参数,并将列表的每个元素连接到一个元组以构建元组列表

我的想法是: 1) 我尝试构建两个函数“抓取”和“无限”。他们的情况如下:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where ...
grap :: Int -> [a] -> [a]
grab _ [] = []
grab n (x:xs) = if n <= 0 then [] else x : grab (n-1) xs  

infinite :: Num a => a -> [a]
infinite x = x : infinite(x+1)
因此,
xs
现在是我的第一个
zip
列表,尤其是整数列表

但是现在我的问题是:
zip
作为预定义函数还需要第二个列表,特别是学生姓名的列表,但是在给定的函数中,他们只给zip一个参数,即作为列表的
ys
。我怎么能理解呢?

分组组号的类型` 仔细查看
分组::Int->[Student]->[(团队,学生)]
的类型,以及为其声明而声明的参数

grouping :: Int        -> [Student]->[(Team, Student)]
grouping    teamNumber =  ...
如果在等号左侧列出的所有参数中都提供了
grouping
则返回类型是什么(等号右侧的类型)

答复
等号右侧的类型是
[Student]->[(团队,学生)]
。在Haskell中,接受两个参数并返回结果的函数可以等价地看作或定义为接受第一个参数并返回结果的函数(接受第二个参数并返回结果的函数)。例如,我们可以说,表达式

grouping 3 :: [Student]->[(Team, Student)]
(分组3)
是一个函数,它获取学生列表并返回这些学生的列表,标记为3个组。假设将
(第3组)
应用于您示例中的学生列表,我们将

(grouping 3) [   'Mark' ,   'Hanna' ,   'Robert' ,   'Mike' ,   'Jimmy' ] =
             [(1,'Mark'),(2,'Hanna'),(3,'Robert'),(1,'Mike'),(2,'Jimmy')]
zip-ys的类型` 咖喱与以下类型和表达式有什么关系

zip :: [a] -> [b] -> [(a, b)]
zip    ys
例如,如果
ys:[Bool]
,zip ys的类型会是什么

这和你的问题有什么关系

当你考虑这与“代码”类型分组“团队编号< /代码>”时,这是如何告诉你在练习中需要使用<代码> ys>代码>的类型?< /p> 把它们放在一起 从练习代码(忽略类型和

where
子句)中,我们有:

只有在Haskell中将两种类型统一起来的情况下,才能将它们定义为
=
。在这种情况下,
分组组号
的类型必须与
zip ys
的类型统一

从第一部分,我们知道分组组号的类型是
[Student]->[(Team,Student)]

从第二部分,我们知道
zip ys
具有类型
[b]->[(a,b)]
,其中
a
是一种类型,使得
ys
具有类型
[a]

因此,我们知道(
~
在Haskell中是类型相等的)

如果我们用以下变量替换类型变量
b
a

b ~ Student
a ~ Team
现在,我们知道
ys
的类型是
[a]
,如果我们进行相同的替换,它就是
[Team]

因此,如果
ys::[Team]
,则类型将是正确的

结论
如果您可以提供一个
ys::[Team]
,您可以通过将
ys
作为第一个参数传递给
zip
,从学生到被标记为其团队的学生(
[Student]->[(Team,Student)]
)生成一个函数。这样的函数正是
分组
应用于单个参数时需要返回的函数,
teamNumber::Int

当您第一次遇到它时,Currying可能会有点混乱。基本上是这样的(我将忽略一些技术细节)

基本概念是:在Haskell中,每个函数只接受一个参数。如果要模拟具有两个参数的函数,有两种方法:

多元组 您可以编写一个接受元组的函数。这是标准ML中的常规方法,但通常仅在Haskell中使用,尤其是在最明智的情况下:

distanceFromOrigin :: (Double, Double) -> Double
distanceFromOrigin (x, y) = sqrt (x^2 + y^2)
咖喱 您可以编写一个curried函数。curry背后的概念是,当你将一个函数应用于一个参数时,你可以得到另一个函数,它接受第二个参数。首先,我将使用lambda表示法非常明确地写出:

product :: Double -> (Double -> Double)
product x = \y -> x * y
假设我从
(产品3)4
开始。我可以先减少
(产品3)
以获得

(\y -> 3 * y) 4
然后我可以完成这项工作,得到12分

Haskell提供了一些语法来帮助解决这类问题。首先,它让我写作

product x y = x * y
意指

product x = \y -> x * y
其次,它使函数应用程序左关联,因此我可以编写
product 3 4
,意思是
(product 3)4


最后,它使
->
类型构造函数具有右关联性,因此我可以编写
product::Double->Double->Double
,而不是在Haskell中编写
product::Double->(Double->Double)
,以下内容是等效的:

f = (\x      y -> ..x..y..  )
f = (\x -> (\y -> ..x..y.. ))  -- this equivalence is known as "currying"
f     x =  (\y -> ..x..y.. )   -- partially applying f with x gives (\y->...)
f     x      y =  ..x..y..
(\x->…)
当然是Haskell对匿名的表示法,即所谓的“lambda”函数(
\
是希腊字母
λ的提醒)

在Haskell中,函数与其他值一样,因此函数调用或“函数指针”等没有特殊的语法。对于类型,上述自然需要

f ::  a ->   b ->   t
f     x ::   b ->   t  -- the result of calling f w/ x (of type a) has type b->t
f     x      y ::   t  -- when f :: a->b->t, x :: a, y :: b, then f x y :: t
盯着它看一会儿

这就是咖喱。函数调用仅由并置表示
f ::  a ->   b ->   t
f     x ::   b ->   t  -- the result of calling f w/ x (of type a) has type b->t
f     x      y ::   t  -- when f :: a->b->t, x :: a, y :: b, then f x y :: t