【问题标题】:How do you pass list in a function parameter in haskell?你如何在haskell的函数参数中传递列表?
【发布时间】:2017-07-29 10:16:29
【问题描述】:

我知道这个问题之前已经被问过很多次,并且我已经仔细阅读过它们,但这并不能帮助我回答我的问题类型。我对 Haskell 很陌生,

假设我们有以下内容:

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

我有两个问题

  1. 如何调用过滤器?我只知道你通过p 这是一个列表

  2. 老实说我一般不知道什么是多态类型,也搞不清过滤函数的多态类型。

我不明白函数过滤器在 if 语句中的作用。

如果您能帮助我解决这两个问题,我将不胜感激。

有很多资源可以解释多态性,但我不明白。

【问题讨论】:

  • p 不是一个列表,它是一个谓词,即一个函数a->Boolfilter 的第二个参数是一个列表。可以通过加载您的代码并使用:t filter 向 GHCi 询问类型。我认为您应该阅读一些 Haskell 教程,并先学习语言基础知识:您似乎对上面的代码感到很困惑,所以您应该先尝试熟悉更基本的代码。

标签: list function haskell polymorphism


【解决方案1】:

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 对实现者来说是未知的,并且该函数不能假设任何关于a 的内容。 a 由调用站点的功能用户选择。

顺便说一句。这是一个有趣的小练习,可以弄清楚以下函数可以做什么:

:: a -> a

(提示:它只能做一件事,而且名字泄露了它,所以我把它省略了)

您也可以将filter 视为实现完全相同且仅在a 中不同的一系列函数。其中一些可能是:

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

在学习新概念时,SO 并不是一个很好的起点。你真的应该拿一本好书。

【讨论】:

    【解决方案2】:

    在了解有关实现的任何细节之前,我们应该确定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]
    

    ...是的,这是有道理的。

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

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

      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
    

    【讨论】:

    • 谢谢。你不仅回答了我的问题,而且建议很好:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-26
    • 2014-12-03
    相关资源
    最近更新 更多