【问题标题】:Haskell using foldl like recursionHaskell 使用类似折叠的递归
【发布时间】:2014-02-13 06:00:08
【问题描述】:

我正在尝试让一个使用 foldl 的函数工作 遍历元组列表并从中创建一个字符串。 我正在尝试创建一个已经使用递归的类似函数。

这是我要编译的代码:

citeBook :: (String, String, Integer) -> String
citeBook (name, titl, date) = (titl ++ " (" ++ name ++ ", " ++ show date ++ ")\n")

--Recursion function
-- function must be called with putStr in order for newlines to work
bibliography_rec :: [(String, String, Integer)] -> String
bibliography_rec [] = ""
bibliography_rec xs = (citeBook(head xs) ++ bibliography_rec (tail xs))

--foldl function 
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) citeBook(x) xs   --ERROR HERE

所以在提供的代码的最后一行,我试图让 foldl 使用 (++) 作为运算符以组合列表中的字符串。 我使用 citeBook(x) 作为我的基本情况,因为 x 将是第一个元组 从列表中取出。注意 citeBook(x) 返回一个字符串。然后继续 与列表 xs 折叠。

这是我遇到的错误。我认为我的 foldl 参数类型不是 符合预期,但对我来说一切似乎都很好..

hw1.hs:28:34:
    Couldn't match type `[a0]'
                  with `(String, String, Integer) -> String'
    Expected type: ((String, String, Integer) -> String)
                   -> [a0] -> (String, String, Integer) -> String
      Actual type: [a0] -> [a0] -> [a0]
    In the first argument of `foldl', namely `(++)'
    In the expression: foldl (++) citeBook (x) xs
    In an equation for `bibliography_fold':
        bibliography_fold (x : xs) = foldl (++) citeBook (x) xs

hw1.hs:28:48:
    Couldn't match expected type `[[a0]]'
                with actual type `(String, String, Integer)'
    In the third argument of `foldl', namely `(x)'
    In the expression: foldl (++) citeBook (x) xs
    In an equation for `bibliography_fold':
        bibliography_fold (x : xs) = foldl (++) citeBook (x) xs

hw1.hs:28:51:
    Couldn't match expected type `(String, String, Integer)'
                with actual type `[(String, String, Integer)]'
    In the fourth argument of `foldl', namely `xs'
    In the expression: foldl (++) citeBook (x) xs
    In an equation for `bibliography_fold':
        bibliography_fold (x : xs) = foldl (++) citeBook (x) xs

感谢任何和所有反馈。谢谢!

【问题讨论】:

  • bibliography_rec 不只是concatMap citeBook
  • 是的,将其发布为答案。
  • bheklilr,我刚刚尝试过,它确实有效,但我明确地试图创建一个递归函数。我的问题是我试图像 bibliography_rec 一样打印出一个列表,但是使用 foldl 而不是递归
  • 一些小注意事项:首先, bibliography_rec 将串联关联到右侧,因此您应该使用 foldr insread 的 foldl 来获得等效函数(结果是相同的,因为 ++ 是关联的,但是右关联版本的效率更高)。此外,最好在 bibliography_head 上使用模式匹配而不是 head 和 tail。

标签: haskell recursion types fold


【解决方案1】:

你给foldl 提供了(++) 类型为String -> String -> String 的函数。但是,您要折叠的集合 xs 的类型为 [(String, String, Integer)],而不是类型 [String]

您可以将bibliography_fold 更改为

bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) (citeBook x) (map citeBook xs)

或者只是为了

bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold xs = foldl (++) "" (map citeBook xs)

但我自己是 Haskell 的一个相对菜鸟,所以对我的编码风格持保留态度。

另外,你需要写(citeBook x) 而不是citeBook(x),否则编译器会假设citeBook(x) 都是foldl 的参数(如果我错了,请纠正我)。这有助于解释为什么您收到的错误消息看起来如此奇怪。

【讨论】:

  • 我要强调的是,foldl (++) "" 版本更可取,因为它不会对空列表进行特殊处理。
  • 感谢 jcarpenter!结果非常好。我自己是 Haskell 的菜鸟。我想我对地图功能不是很熟悉。看来我会做一些研究。
  • 你也可以不使用 map 函数来编写它,像这样:bibliography_fold xs = foldl (\s t -> s ++ citeBook t) "" xs。现在累加器函数的类型为String -> (String, String, Integer) -> String,而您又要折叠[(String, String, Integer)]
  • 我肯定会避免 foldl (++) 因为讨厌的二次开销。在此处使用foldr
【解决方案2】:

你已经得到了答案,所以我将提供另一种使用折叠的方法来解决这个问题:

bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold = foldr ((++) . citeBook) ""

没有地图,没有特殊情况,它可以写成无点风格。我鼓励你在 GHCi 中解构这个表达式并使用:info 检查每个组件,以探索它的实际工作原理。查看foldr++citeBook(++) . citeBook 的类型,看看你能不能弄清楚为什么会这样。您可能还想查找foldr 的源代码。

【讨论】:

  • +1 建议在这里使用 foldr 而不是 foldl
猜你喜欢
  • 2023-04-09
  • 2011-03-03
  • 1970-01-01
  • 2012-04-13
  • 2019-04-27
  • 2016-10-29
  • 2022-02-07
  • 2015-08-30
  • 1970-01-01
相关资源
最近更新 更多