4

我被要求实现一个使用以下配置文件的功能:

maybe_divide :: Maybe Integer -> Maybe Integer -> Maybe Integer

并以下列方式响应:

> maybe_divide (Just 5) (Just 2) 
Just 2
> maybe_divide (Just (-5)) (Just 2) 
Just (-3)
> maybe_divide (Just (-5)) (Just 0) 
Nothing
> maybe_divide Nothing (Just 1) 
Nothing
> maybe_divide (Just 1) Nothing
Nothing

我已经写了以下内容,但它不会编译。你们有什么建议吗?

maybe_divide :: Maybe Integer -> Maybe Integer -> Maybe Integer
maybe_divide x y = case x of
    Just x' -> case y of 
        Just y'
            | y' == 0 -> Nothing  
            | otherwise -> x' `div` y'
    Nothing -> Nothing 
4

4 回答 4

12

除了输出类型之外,我想指出一些您可能更喜欢编写此类代码的替代样式:

首先,您可以case通过将它们放在一个元组中来处理多个表达式:

case (x,y) of
    (_      , Just 0) -> Nothing
    (Just x', Just y') -> Just (x' `div` y')
    _ -> Nothing

有几种不同的方法可以使用守卫甚至函数来编写它Control.Monad.guard

case (x,y) of
  (Just x', Just y') -> (x' `div` y') <$ guard (y' /= 0)
  _ -> Nothing

第二种方法将从一个函数开始:

safeDiv :: Integer -> Integer -> Maybe Integer
safeDiv x 0 = Nothing
safeDiv x y = Just (x `div` y)

现在您有了safeDiv,您可以将其提升为 Maybe 包装的参数。它非常接近Applicative样式代码,除了输出中的额外层Maybe。幸运的是嵌套的单子(例如Maybe (Maybe t))很容易崩溃:

maybe_divide x y = join $ safeDiv <$> x <*> y

甚至

maybe_divide = (join .) . liftM2 safeDiv

如果你精通无点。

就个人而言,我会使用两个元组大小写变体之一。但是已经有一个类似的函数是相当普遍的safeDiv,在这种情况下,第二种形式可能很有用。

于 2013-04-25T05:42:15.670 回答
7

You should post the error you are getting, but

x' `div` y'

has type Integer and not Maybe Integer. Perhaps you need to wrap this in a Just.

于 2013-04-25T05:24:29.323 回答
6

You need to wrap the successful result in Just here:

... | otherwise -> Just (x' `div` y')
于 2013-04-25T05:24:20.603 回答
4

我认为这样做的一个好方法是将 Monad 实例用于Maybe

maybe_divide x y = do
   a <- x
   b <- y
   if b == 0 
     then fail "Division by zero" 
     else return (a/b)

这摆脱了大多数繁琐的Nothings 检查,并开始检查除以零的主要业务。

在 Maybe monad 中,fail "error message"实际上结果是Nothing; 错误消息被丢弃,所以我不必说"Division by zero"""也会这样做。

在 Maybe monad 中,return意思是“包裹在 Just 中”。


如果你不需要做任何错误检查,你可以使用应用实例:

import Control.Applicative -- at the top of your code

maybe_multiply x y = (*) <$> x <*> y

我认为这在美学上是令人愉悦的。

这是因为对于匹配 Monad 的 Applicative 实例,f <$> x <*> y <*> z相当于

do 
  a <- x
  b <- y
  c <- z
  return $ f a b c
于 2013-04-25T13:44:23.400 回答