如何在haskell中的函数参数中传递列表?

时间:2017-07-29 10:16:30

标签: list function haskell polymorphism

我知道这个问题以前已被问过很多次了,我已经仔细阅读了它们,但它并没有帮助我回答我的问题。我对Haskell很新,

假设我们有以下内容:

filter p [] = []
filter p (h:l) = if (p h) then (h:(filter p l)) else (filter p l)

我有两个问题

  1. 如何调用过滤器?我所知道的是你通过了p列表

  2. 老实说,我一般都不知道什么是多态类型,我无法弄清楚过滤函数的多态类型。

  3. 我没有事件理解函数过滤器在if语句中的作用。

    如果你能帮我解决这两个问题,我将非常感激。

    有足够的资源来解释多态性,但我不了解它们。

2 个答案:

答案 0 :(得分:6)

p不是列表。 p是谓词的缩写 - 通常用于获取值并返回Bool的函数的术语。列表是filter第二个参数。

你怎么称呼过滤器?你需要阅读那里的许多haskell书籍之一。马上。一些例子:

filter (> 5) [1, 6, 2, 8, 9, 3] -- [6, 8, 9]
filter (== 'z') "bazzz" -- "zzz" (String === [Char])

此处(> 5) :: Int -> Bool(== 'z') :: Char -> Bool是谓词。

多态非常松散意味着它具有不同类型的相同形式:

filter :: (a -> Bool) -> [a] -> [a]

filter必须适用于任何类型a。因此,实现者不知道特定的a,并且函数不能假设aa由呼叫站点的功能用户选择。

顺便说一下。这是一个有趣的小练习,可以确定允许执行以下功能:

:: a -> a

(提示:它只能做一件事,名字就把它拿走了,所以我把它留了出来)

您还可以将filter视为一系列功能完全相同且仅在a中有所不同。其中一些可能是:

:: (Int -> Bool) -> [Int] -> [Int]
:: (Char -> Bool) -> [Char] -> [Char]
:: (Foo -> Bool) -> [Foo] -> [Foo]

在学习新概念时,开始并不是一个好的开始。你应该抓一本好书。

答案 1 :(得分:4)

在了解有关实施的任何细节之前,我们应该确定filter应该是什么类型。实际上,您通常应该设计函数的类型签名,而不必编写任何实际代码......但损坏已在此处完成。 (正如Chi所言,你可以在这一点上实际问GHCi 你的实现类型是什么......但是,这又是向后的,所以我不会深入研究它。 )

那么你想要filter完成什么?确实应该列出一个清单。您希望根据每个元素可能具有的某些属性来提取该列表的某些元素;但是filter不应该有任何难以理解的假设,使用什么标准,即它应该是任何类型的元素列表的类型。在Haskell中,我们写了[a](这实际上是∀ a . [a]的简写,将其读作“对于您可能考虑的所有元素类型 - 例如,A - 它是一个列表类型[A]“)。

然后应该通过一个额外的参数来确定实际标准:过滤谓词。例如,您可能希望从整数列表中筛选小于5的所有数字 - 您使用谓词(<5) :: Int -> Bool。通常,对于[a]列表,您需要一个类型为a -> Bool的谓词。最终结果将与您传入的列表元素相同,因此filter将具有签名

filter :: [a] -> (a -> Bool) -> [a]

...除了惯例之外,我们首先把谓词,即

filter :: (a -> Bool) -> [a] -> [a]

让我们检查一下是否有意义......例如,我们想要

> filter ((<5) :: Int -> Bool) ([4,9,3] :: [Int])

在这种情况下a ~ Int如此

filter :: (Int -> Bool) -> [Int] -> [Int]

...是的,这很有意义。

现在你开始真的担心实施。有两种通用方法:

  • 使用库中预先存在的一些组合器来定义函数。这非常适合开始涉及手动递归等,但是现在让我们手工完成所有事情。
  • 解构列表。基本上,列表只有两种方式:它可以包含某些内容,也可以为空。空是很容易的,因为在这种情况下你不可能再返回一个空列表。您甚至无法使用谓词,因为 没有您可以检查的元素,因此只需通过匹配_将其丢弃:

    filter _ [] = []
    

    (或者你也可以这样,你也可以将谓词与p匹配,但人们会想知道: the mouse p发生了什么? )

    如果列表为空,我们可以直接弹出一个元素:

    filter p (h:l) = …
    

    此处,h是头元素,l是列表的其余部分。太棒了,我们现在有一个a类型的元素,让我们看看谓词告诉我们的内容!

    filter p (h:l) = if p h then … else …
    

    因此,如果谓词已经完成,我们希望在最终结果中再次看到h,我们不会?实际上,最终结果应该h开始,因此

    filter p (h:l) = if p h then h : … else …
    

    最终结果的其余部分应该与输入列表的其余部分有关。我们可以传递then h : l else …,但这意味着我们只能控制头元素的条件。不,我们仍然需要过滤列表的其余部分:

    filter p (h:l) = if p h then h : filter p l else …
    

    事实上,即使h没有达到谓词,我们也希望这样做,除非我们不加前缀:

    filter p (h:l) = if p h then h : filter p l else filter p l
    

然后你去了:

filter _ [] = []
filter p (h:l) = if p h then h : filter p l else filter p l

这个if看起来有点笨拙,首选的语法实际上是 guards (它做同样的事情)

filter _ [] = []
filter p (h:l)
 | p h        = h : filter p l
 | otherwise  = filter p l