首页 > 解决方案 > 用于在元组中构建列表的有状态递归

问题描述

我将下面的示例代码放在一起来演示我的问题:什么是在元组中构建列表的递归方式?

我想改变recommendNPagesrecommendPage以递归方式调用返回([Post], Blog),而不是利用Blog's 数据构造函数。


data Tag = Food | Tech | Business | Travel | None
          deriving (Show, Read, Eq, Ord, Bounded, Enum)

data Author = Joe | Bob | Kayla | Jade
          deriving (Show, Read, Eq, Ord, Bounded, Enum)

data Post = Post{getRank::Tag,getAuthor::Author}
        deriving (Eq,Ord)

instance Show Post where
    show (Post t a) = "Tag: " ++ (show t) ++ " - " ++ "Author: "++ (show a)


newtype Blog = Blog [Post]

instance Show Blog where
    show (Blog posts) = "This blog has " ++ (show (length posts)) ++" posts"


fullBlog :: Blog
fullBlog = Blog [Post t a | a <- [Joe .. Jade],
                            t <- [Food .. None]]

recommendPage :: Blog -> (Post,Blog)
recommendPage (Blog posts) = (head posts,Blog (tail posts))


recommendNPages :: Int -> Blog -> ([Post], Blog)
recommendNPages n (Blog posts) = (take n posts, Blog $ drop n posts) 

任何建议或文档链接将不胜感激。

标签: haskellfunctional-programmingtuples

解决方案


有几点需要注意。首先,您的recommendPage函数是不安全的,因为它在空列表上失败。最好避免使用 和 之类的函数headtail这些函数不是全部并且在某些输入上失败。其次,从逻辑上讲,将您的recommendNPages功能作为“主要”功能似乎更自然,然后实现recommendPagerecommendNPages 1.

但我会假设你有充分的理由去做你想做的事情。然后你可以使用这个简单的实现:

recommendNPages :: Int -> Blog -> ([Post], Blog)
recommendNPages 0 blog = ([], blog)
recommendNPages n blog =
  case recommendPage blog of
    (post, blog') -> case recommendNPages (n - 1) blog' of
      (posts, blog'') -> (post : posts, blog'')

但是,每当您看到这种模式时,您都应该怀疑涉及到一个状态,您确实这样做了,从问题标题中的“有状态”一词来判断:)。

事实上,您可以将您的recommendPage函数视为一个有状态计算,通过从中提取一个页面来“修改”博客,然后您recommendNPages就变成了重复n多次的这种状态计算。

我们在 Haskell 中用于这种计算的工具称为Statemonad。结合replicateM迭代一元计算n时间的函数,我们得到:

recommendNPages :: Int -> Blog -> ([Post], Blog)
recommendNPages n = runState $ replicateM n step
  where
    -- | One step of our computation. (Just wrapping it into `State`.)
    step :: State Blog Post
    step = state recommendPage

推荐阅读