Stateful рекурсия для создания списка в кортеже - PullRequest
0 голосов
/ 15 апреля 2020

Я собрал пример кода ниже, чтобы продемонстрировать мой вопрос: что такое рекурсивный способ построения списка в кортеже?

Я хочу изменить recommendNPages так, чтобы он вызывал recommendPage рекурсивно возвращать ([Post], Blog) вместо использования конструктора данных Blog.


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) 

Будем благодарны за любые предложения или ссылки на документацию.

Ответы [ 2 ]

2 голосов
/ 16 апреля 2020

Есть несколько предостережений. Прежде всего, ваша recommendPage функция небезопасна, поскольку она не работает в пустых списках. Лучше избегать таких функций, как head и tail, которые не являются полными и не работают на некоторых входах. Во-вторых, логически кажется более естественным вместо этого иметь recommendNPages функцию в качестве «основной», а затем реализовать recommendPage как recommendNPages 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'')

Однако всякий раз, когда вы видите этот шаблон, вы должны подозревать, что задействовано состояние, что вы и сделали, судя по слову «stateful» в заголовке question:).

В самом деле, вы можете рассматривать вашу функцию recommendPage как вычисление с учетом состояния, которое «изменяет» блог, извлекая из него одну страницу, и тогда ваш recommendNPages становится просто повторением этого вычисления с учетом состояния n раз.

Инструмент, который мы используем в Haskell для такого рода вычислений, называется State монадой . В сочетании с функцией replicateM, которая повторяет вычисление монади c 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
1 голос
/ 15 апреля 2020

Из комментариев я получил частичный ответ, который не решает проблему с recommendPage:

recommendNPages :: Int -> Blog -> ([Post], Blog)
recommendNPages 0 b = ([], b)
recommendNPages n b = 
    let (x, b') = recommendPage b in 
        first (x:) $ recommendNPages (n - 1) b'

При использовании Data.Bifunctor.first для сопоставления [Post] в возвращаемом значении recommendNPages (n - 1) b'.

...