【问题标题】:zip function requires also a second list, how can it work with only one argument listzip 函数还需要第二个列表,它如何仅使用一个参数列表
【发布时间】:2014-05-12 17:11:41
【问题描述】:

我开始学习 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')]。

所以,我们正在组建由两名学生组成的团队,而最后一名学生“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 声明的第一行中使用这两个来实现上面的给定功能。 所以,我有:

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

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

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

【问题讨论】:

标签: list haskell currying partial-application


【解决方案1】:

`grouping teamNumber`的类型

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

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

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

回答

等号右边的类型是[Student]-&gt;[(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]-&gt;[(Team,Student)]。

从第二部分,我们知道zip ys 具有[b] -&gt; [(a, b)] 类型,其中a 是ys 具有[a] 类型的类型。

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

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

如果我们将以下内容替换为类型变量b 和a,这些将统一

b ~ Student
a ~ Team

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

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

结论

如果您可以提供ys :: [Team],您可以通过将ys 作为第一个参数传递给zip,生成一个从学生到学生的函数,并带有他们的团队([Student]-&gt;[(Team,Student)]) 的标签。这样的函数正是 grouping 在应用于单个参数 teamNumber :: Int 时需要返回的函数。

【讨论】:

  • 等号右边的类型一定是([Student], [(Team, Student)])吧?
  • 不,它不是一个既包含学生列表又包含团队和学生的元组列表的元组。这是一个接受学生列表并返回团队和学生元组列表的函数。 [Student]-&gt;[(Team, Student)]。在 Haskell 中,接受两个参数并返回结果的函数可以等效地视为或定义为接受第一个参数并返回 a 的函数(接受第二个参数并返回结果的函数)。顺便说一句:这被称为“currying”,以 Haskell Curry 命名。
  • 要回答你的第一个问题,我可以说 zip ys 的类型必须是 [b] -> [(Bool, b)]。但对于其他人,我必须考虑一下
  • [b] -&gt; [(Bool, b)] 看起来像您需要的其他东西吗?也许如果您尝试使用其他方法代替 Bool?
  • 我想,在我继续之前,我应该阅读一些关于柯里化的进一步信息,因为目前我非常困惑。
【解决方案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。

最后,它使-&gt; 类型构造函数具有右关联性,所以我可以写product :: Double -&gt; Double -&gt; Double 而不是product :: Double -&gt; (Double -&gt; Double)。

【讨论】:

    【解决方案3】:

    在 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 -&gt; ...) 当然是 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 定义是自动curried,类型中的箭头关联到右侧(a-&gt;b-&gt;c 实际上是a-&gt;(b-&gt;c))。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      • 2021-12-19
      • 1970-01-01
      相关资源
      最近更新 更多