Haskell 中的递归

Recursion in Haskell

我仍在学习 Haskell 并且正在做一些练习,但我遇到了麻烦。所以我有一个名为 "novel" 的函数,它接受 2 个字符串和一个 Int (novel :: (String, String, Int) -> String) 的论据。小说的 input/output 必须如下所示:

> novel ("Rowling", "Harry Potter", 1998)
"Harry Potter (Rowling, 1998)"

这是我的新功能代码,其工作原理如上所述:

novel :: (String, String, Int) -> String
novel (author, book, year) = book ++ " (" ++ author ++ ", " ++ (show year) ++ ")" 

我正在尝试编写一个名为 "cite" (cite :: [(String, String, Int)] -> String) 的新函数。 Cite 的 input/output 应该如下所示:

> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
"book1 (author1, year1)
book2 (author2, year2)
book3 (author3, year3)"

我正在尝试递归地使用 "novel," 以获得所需的输出,但我不确定该怎么做。

我尝试过的:

cite :: [(String, String, Int)] -> String                -- | Listed arguments
cite [] = ""                                             -- | Base Case
cite x:xs = [(novel (author, book, year)), (novel (author, book, year)), (novel (author, book, year))]

这是老实说的,据我所知。显然,它不起作用,但我不确定从这里开始做什么。

也许这会给你一个良好的开端:

cite :: [(String, String, Int)] -> String
cite [] = ""
cite (x:xs) = undefined -- put your code that recursively calls cite in here, hint: use ++ and "\n\"

模式匹配 (x:xs) 说的是,给我列表中的第一项 x 和列表的尾部 xs。这将与编写此内容相同:

cite xs' = let x = head xs'
               xs = tail xs'
           in  undefined -- your code here

甚至

cite xs' = undefined -- your code here
    where
        x = head xs'
        xs = tail xs'

希望能帮助您朝着正确的方向前进。

编辑:OP 询问如何递归执行此操作,以下是我的原始答案:

您可能应该重写您的基本案例以说 cite [] = ""。它并没有真正的区别,但它有助于提高代码的可读性。

让我们先把“:t map novel”放入ghci看看你得到了什么:

> :t map novel
map novel :: [([Char], [Char], Int)] -> [[Char]]

我们可以重写为:map novel :: [(String, String, Int)] -> [String]

怎么样?因为 map 将一种类型 a 转换为另一种类型 b 并将其应用于列表中的每个项目。 map 的第一个参数是任何接受一个参数的函数。正是 novel 的作用。

但这并没有给我们您所需要的,我们最终会得到一个字符串列表而不是一个字符串:

> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
["book1 (author1, year1)","book2 (author2, year2)","book3 (author3, year3)"]

并且您希望它是一个由换行符“\n”分隔的字符串。有没有一个函数可以接受一个字符串列表并将它们连接成一个字符串,但在它们之间插入一个分隔符?

首先我们来描述这样一个函数:String -> [String] -> String。接下来我们把它放入 Hoogle 看看我们得到了什么:https://www.haskell.org/hoogle/?hoogle=String+-%3E+%5BString%5D+-%3E+String

啊,第二个函数 intercalate 听起来正是我们需要的。它不仅适用于字符串,它适用于任何列表。它是如何工作的?像这样:

> import Data.List (intercalate)
> intercalate "\n" ["List","Of","Strings"]
"List\nOf\nStrings"

所以现在您可以结合插入和映射来获得您想要的东西。 cite 的定义由您决定。

编辑:完全忘记了,实际上有一个专门的功能。如果您只是在 Hoogle 中搜索 [String] -> String,您会发现 unlines

有一种相当简单的方法可以做到这一点。

首先,将 novel 映射到给定列表的每个元素,然后使用 Data.List.intersperse 用换行符填充空白。这是我的实现:

import Data.List (intersperse)

cite :: [(String, String, Int)] -> String
cite bs = intersperse '\n' (map novel bs)

或者,更优雅的无积分风格:

cite = intersperse '\n' . map novel

也可以编写一个漂亮、高效的递归函数:

cite []     = ""
cite [x]    = novel x
cite (x:xs) = novel x ++ '\n' : cite xs

在以后的此类问题中,请记住 mapfoldr 等函数 - 它们是 Haskell 和函数式编程中最不可或缺的两个部分。此外,您的模式匹配需要括在括号中。