在 Haskell 中将任何类型的列表作为函数参数(使用 Replit)

Take list of any type as a function parameter in Haskell (using Replit)

提问人:Sophie 提问时间:4/19/2023 最后编辑:Sophie 更新时间:4/19/2023 访问量:67

问:

我正在尝试在 Haskell 中编写一个函数,该函数将列表作为参数并返回并删除最后一个元素 这是我拥有的代码:

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (hd:rest) =
  if null rest
    then return []
    else  hd:remLastElement rest

我已经使用它作为参数对其进行了测试,因此我知道它按预期工作,但是我想知道如何允许它获取不同类型的列表,例如我有一个列表或列表[Int]['apples','oranges','pears'][2.4,7.32,1.46,4.5]

我做了一些搜索,在我找到的许多示例和文档中,[a] 被用来表示任何类型的列表,但 Replit 不接受它作为有效的语法。我不确定它是否真的不是有效的语法,或者 Replit 是否出于某种原因很愚蠢。 我也偶然发现了所有的东西,但据我所知,这不是 Haskell 内置的东西,我需要一个扩展才能使用它。

列表 哈斯克尔 类型 语法

评论

1赞 Silvio Mayolo 4/19/2023
两个答案都已经指出你不需要这里。老实说,我的建议是:永远不要使用这个词。它只是令人困惑,因为它与 Python、Java 或任何其他语言中的关键字完全不同。当您需要将值提升为 monad 时,请使用 pure,它适用于更多类型,并且误导性要小得多。但在此示例中,您也不需要。returnreturnreturn

答:

3赞 willeM_ Van Onsem 4/19/2023 #1

但我想知道我如何允许它获取不同类型的列表,例如我有一个列表或列表。['apples','oranges','pears'][2.4,7.32,1.46,4.5]

它已经做到了,只要列表中的所有元素都具有相同的类型,那么 s、s 等列表就可以了。IntString

你只是犯了一个错误,你应该放弃.事实上,虽然列表是一个单子,但你在这里返回一个简单的列表,所以:return

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (hd:rest) =
  if null rest
    then []
    else hd:remLastElement rest

该功能还可以通过模式匹配轻松简化,甚至可以通过以下方式提高效率:

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (x:xs) = go x xs
    where go _ [] = []
          go z (y:ys) = z : go y ys

这将防止两次拆包相同的缺点。

我们可以用浮点数和字符串来测试这一点:

ghci> remLastElement ["apples","oranges","pears"]
["apples","oranges"]
ghci> remLastElement [2.4,7.32,1.46,4.5]
[2.4,7.32,1.46]
2赞 chi 4/19/2023 #2

让我们检查一下你的代码:

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (hd:rest) =
  if null rest
    then return []
    else  hd:remLastElement rest

在此上下文中,表示 ,仅包含作为其唯一元素的列表。因此,表示 ,包含空列表作为其唯一元素的列表列表。但是,这与类型签名所承诺的类型相符。return x[x]xreturn [][[]][a]

一般来说,除非您知道自己正在使用 monads,否则请避免使用。return

因此,固定代码是

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (hd:rest) =
  if null rest
    then []
    else hd : remLastElement rest

不过,我们仍然可以改进这一点。通常,您不必使用 ,它是一个次要的反模式。您已经使用模式匹配来检查输入列表是否为空,这不需要 .真棒!nullnull

我们可以使用相同的技术(模式)匹配来检查尾部(上面称为)是否为空。rest

remLastElement :: [a] -> [a]
remLastElement [] = []
remLastElement (hd:[]) = []
remLastElement (hd:(hd2:rest2)) = hd : remLastElement (hd2:rest2)

请注意,我们如何用两个新情况替换原始情况,并且将第二个方程拆分为两个新方程。现在不需要了。rest[]hd2:rest2if null

我们仍然可以改进。该模式可以写成 。此外,我们可以用原始变量替换整个模式——这是因为方程是按顺序尝试的,所以如果 和 大小写不匹配,我们知道它一定是非空的。hd:[][hd]hd2:rest2rest[][hd]rest

remLastElement :: [a] -> [a]
remLastElement []        = []
remLastElement [hd]      = []
remLastElement (hd:rest) = hd : remLastElement rest

最后的润色:如果您打开警告(我强烈建议),GHC 会抱怨第二个等式中没有使用。为了使警告静音,我们可以将该变量替换为通配符。-Wallhd_

remLastElement :: [a] -> [a]
remLastElement []        = []
remLastElement [_]       = []
remLastElement (hd:rest) = hd : remLastElement rest