2013-04-13 3 views
2

Я пытаюсь реализовать функцию reverse с Maybe. Я не знаю, как вернуть Just в соответствие шаблонов с помощью рекурсии. Например, ghci> myReverse [1,2,3] необходимо вернуть Just [3,2,1]. Вот мой код:Возможно и рекурсия в списке

myReverse :: [a] -> Maybe [a] 
myReverse [] = Nothing 
myReverse [x] = Just [x] 
myReverse (x:xs) = myReverse xs ++ [x] -- here's my problem. 

Я думал, что myReverse (x:xs) = Just $ myReverse xs ++ [x] работа, но нет, и я не знаю, как это сделать. Что я хотел бы знать, так это, как это сделать и почему.

Спасибо за помощь!

+1

Просто из любопытства, почему вы не реализующий такое же поведение, что и оригинальный 'reverse' функции? Почему пустым списком не может быть пустой список? –

+2

Да, это может быть, но цель состоит в том, чтобы узнать больше о Haskell (чтобы противостоять этой проблеме, вы должны попробовать множество решений и искать ответы, и поэтому вы узнаете новые вещи). – vildric

ответ

4

myReverse возвращает Maybe [a], который не может быть непосредственно добавлен к чему-то, потому что это не список. IOW значение myReverse xs будет либо Nothing, либо Just <some list>. Результат должен совпадать с результатом.

myReverse (x:xs) = 
    case myReverse xs of 
     Just list -> ... 
     Nothing -> ... 

И, конечно же, вы должны решить, что должно быть сделано в каждом из этих случаев, в зависимости от того, что вы хотите myReverse делать.

Также имейте в виду, что не каждый функция должна быть рекурсивной, так что вы можете вызвать регулярные reverse из myReverse, если вам это нужно.

+0

Я пробовал какое-то время и не могу окунуться в голову о том, как реализовать рекурсию, предложенную в вашем закодированном разделе (я предполагаю, что она должна вытеснить только последнюю строку кода vildric). Можете ли вы дать мне/нам подсказку? –

+0

Единственный способ, которым я мог бы применить ваш кодированный раздел, - это mappend, как упоминал zurgl, но тогда синтаксис case казался бы лишним, так как вы могли бы написать 'myReverse (x: xs) = mappend (myReverse xs) (Just [ х]) '. Использование синтаксиса case: 'myReverse [] = Nothing; case myReverse xs of Nothing -> Just [x]; в противном случае -> mappend (myReverse xs) (Just [x]) '.. Есть ли способ реализовать ваш кодированный раздел без mappend? –

+0

Вы даже не используете значение, связанное с корпусом! В 'Just list ->' case, 'list' является значением типа' [a] ', к которому вы можете сделать, как вам угодно ... например' Just (list ++ [x]) '. В любом случае, этот ответ «низкий уровень», и я считаю его запахом, если он произошел в моем коде; Обычно я предпочитаю использовать 'mappend', как вы предложили. Я просто хотел сохранить его красивым и конкретным для новичков. – luqui

1

я думал что-то вдоль линий luqui годов, за исключением того, применяя Может быть в конце:

myReverse :: [a] -> Maybe [a] 
myReverse ys 
    | null (myReverse' ys) = Nothing 
    | otherwise   = Just (myReverse' ys) 
where 
    myReverse' []  = [] 
    myReverse' (x:xs) = myReverse' xs ++ [x] 

Или, если хотите,

myReverse ys | null (reverse ys) = Nothing 
      | otherwise   = Just (reverse ys) 
+0

Это всегда будет возвращать 'Nothing' или вызывать ошибку, потому что' в противном случае = True'. Вы имели в виду что-то более похожее на 'let reversed = myReverse 'ys in, если значение null отменено, тогда Nothing else Just reversed'? –

+0

@JonPurdy, похоже, отлично работает для меня. Вы попробовали? –

+0

О, я неправильно понял образец как выражение. Я никогда не думал, что вы вызываете групповой случай 'иначе', потому что он будет тень' Prelude.otherwise'. –

4

Как [a] является Monoid определить по,

instance Monoid [a] where 
     mempty = [] 
     mappend = (++) 

Затем Maybe [a] также Monoid,

instance Monoid a => Monoid (Maybe a) where 
    mempty = Nothing 
    Nothing `mappend` m = m 
    m `mappend` Nothing = m 
    Just m1 `mappend` Just m2 = Just (m1 `mappend` m2) 

Обратите внимание на тип ограничения в объявлении экземпляра, которые налагают a быть Monoid или иначе Maybe a не будет.

Затем мы можем использовать mappend, (<>), чтобы связать наш рекурсивный вызов при условии, чтобы преобразовать головку списка в одноэлементный.

import Data.Monoid ((<>)) 

myReverse :: [a] -> Maybe [a] 
myReverse []  = Nothing 
myReverse (x:xs) = myReverse xs <> Just [x] 

Последнее замечание, предыдущее решение сложения также может быть улучшено.

>>> let mrev = foldl' (\x y -> Just [y] <> x) Nothing 
>>> mrev [] 
Nothing 
>>> mrev "hello" 
Just "olleh" 

Предыдущий ответ складка

Зная, что обратный можно определить с помощью складка, как следовать,

>>> foldl' (flip (:)) [] [1..5] 
[5,4,3,2,1] 

Это можно переписать в виде,

>>> foldl' (\x y -> y:x) [] [1..5] 
[5,4,3,2,1] 

Чтобы адаптироваться к Maybe типа, мы делаем следующее преобразование,

  • Семя [] стать (Just [])
  • Анонимная функция должна теперь быть применимы внутри Just, мы используем БПМЖ, чтобы сделать это.

Это приводит нас к,

>>> foldl' (\x y -> fmap (y:) x) (Just []) [1..5] 
Just [5,4,3,2,1] 

Наконец,

mreverse xs | null xs = Nothing 
      | foldl' (\x y -> fmap (y:) x) (Just []) xs 
+1

Прохладная интерпретация ...как насчет «ничего» для 'myReverse []'? –

+0

мы можем управлять этим футляром с помощью шаблона guard, 'mreverse xs | null xs = Nothing | в противном случае = foldl '(\ x y -> fmap (y :) x) (Just []) xs' – zurgl

Смежные вопросы