2

我开始学习 Haskell 并找到了一个很好的练习。如下:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where ...

因此,练习希望我尝试填补其余部分。该函数应执行以下操作:示例:grouping 2 ['Mark','Hanna','Robert','Mike','Jimmy'] = [(1,'Mark'),(2,'Hanna'),(1,'Robert'),(2,'Mike'),(1,'Jimmy')]

所以,我们正在组建由两个学生组成的团队,最后一个学生“吉米”没有队友。

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

我的想法:1)我尝试构建两个函数“grab”和“infinite”。他们看起来如下:

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)

所以,他们所做的是:infinite我想创建一个无限列表。并且grap应该采取n其中的元素。例子grap 2 (infinite 1) = [1,2]

我在where -declaration 的第一行中使用这两个来实现上面的给定功能。所以我有:

grouping: Int -> [Student]->[(Team, Student)]
grouping teamNumber = zip ys
                      where 
                      xs = grap teamNumber (infinite 1)

所以,xs现在是我的第一个列表zip,尤其是整数列表。

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

4

3 回答 3

4

`分组teamNumber`的类型

仔细查看 的类型grouping :: Int -> [Student]->[(Team, Student)],以及为其声明而声明的参数

grouping :: Int        -> [Student]->[(Team, Student)]
grouping    teamNumber =  ...

grouping如果提供了等号左侧列出的所有参数,则返回类型(等号右侧的类型)是什么?

回答

等号右边的类型是[Student]->[(Team, Student)]。在 Haskell 中,接受两个参数并返回结果的函数可以等效地视为或定义为接受第一个参数并返回 a 的函数(接受第二个参数并返回结果的函数)。所以我们可以说,例如,表达式

grouping 3 :: [Student]->[(Team, Student)]

(grouping 3)是一个函数,它接受学生列表并返回这些学生的列表,标记为 3 个组。据推测,如果(grouping 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

zip ys例如,如果是什么类型ys :: [Bool]

这和你的问题有什么关系?

当您将其与 的类型一起考虑时grouping teamNumber,这如何告诉ys您练习中需要的类型是什么?

把它们放在一起

从练习代码(忽略类型和where子句)我们有:

grouping teamNumber = zip ys

=只有当它们的类型统一时,两件事才能在 Haskell 中。在这种情况下, 的类型grouping teamNumber必须与 的类型统一zip ys

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

从第二部分,我们知道zip ys有 type [b] -> [(a, b)],其中a有 typeys的 type [a]

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

[Student]->[(Team,Student)] ~ [b] -> [(a, b)]

如果我们将以下内容替换为类型变量ba

b ~ Student
a ~ Team

现在,我们知道了ysis的类型[a],如果我们做同样的替换,就是 is [Team]

因此,如果ys :: [Team].

结论

如果您可以提供 a ys :: [Team],您可以[Student]->[(Team,Student)]通过将ys作为第一个参数传递给zip. 这样的函数正是grouping应用到单个参数时需要返回的,teamNumber :: Int.

于 2014-05-12T17:22:33.373 回答
2

当你第一次遇到柯里化时,它可能会有点令人困惑。差不多就是这样(我将忽略一些技术细节)。

基本概念是这样的:在 Haskell 中,每个函数只接受一个参数。如果你想模拟一个带有两个参数的函数,有两种方法可以做到这一点:

元组

您可以编写一个接受元组的函数。这是标准 ML 中的传统方法,但通常仅在 Haskell 中使用它是最明智的做法:

distanceFromOrigin :: (Double, Double) -> Double
distanceFromOrigin (x, y) = sqrt (x^2 + y^2)

咖喱

您可以编写一个柯里化函数。柯里化背后的概念是,当您将一个函数应用于一个参数时,您可以获得另一个接受第二个参数的函数。首先,我将使用 lambda 表示法非常明确地写出来:

product :: Double -> (Double -> Double)
product x = \y -> x * y

假设我从(product 3) 4. 我可以(product 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而不是product :: Double -> (Double -> Double).

于 2014-05-13T06:34:00.850 回答
1

在 Haskell 中,以下是等价的:

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

盯着它看一会儿。

这就是关于柯里化的内容。函数调用在 Haskell 中仅通过并列表示,因此它关联到左侧(f x y是真的((f x) y))。而且因为 Haskell 定义是自动柯里化的,类型中的箭头关联到右边(a->b->c是真的a->(b->c))。

于 2014-05-13T09:28:06.933 回答