为什么需要`fmap` 在`Maybe Integer` 上调用`succ`?

ars*_*anQ 4 haskell types functor collatz map-function

我是 Haskell 的新手,正在研究 Collat​​z 猜想问题。所需的输出是从给定整数变为 1 所需的步数。这是我第一次不得不使用该Maybe类型,这可能会导致我的困惑。

我有这个工作解决方案基于另一个我发现的相同问题的解决方案:

collatz :: Integer -> Maybe Integer
collatz n
    | n <= 0 = Nothing
    | n == 1 = Just 0
    | even n = fmap succ . collatz $ n `div` 2
    | otherwise = fmap succ . collatz $ 3 * n + 1
Run Code Online (Sandbox Code Playgroud)

我不清楚的是为什么有必要fmap succ在这种情况下使用。根据我目前的理解,我希望能够调用succ递归调用的输出collatz以增加它;但是,这会引发错误:

> No instance for (Enum (Maybe Integer))
        arising from a use of `succ'
Run Code Online (Sandbox Code Playgroud)

它看起来像错误有事情做与调用succ一个Maybe Integer类型,而不是一个Integer。错误是因为Maybe Integer在 Haskell 中a不被认为是可枚举的吗?如果是这样,为什么调用可以fmap succ解决这个问题?

Wil*_*ess 6

如果您刚开始学习 Haskell,那么使用.$不必要地为您带来额外的认知负担。你所拥有的更简单写为

collatz :: Integer -> Maybe Integer
collatz n
    | n <= 0    = Nothing
    | n == 1    = Just 0
    | even n    = fmap succ (collatz (n `div` 2))
    | otherwise = fmap succ (collatz (3 * n + 1))
Run Code Online (Sandbox Code Playgroud)

现在,什么是succ?如果我们看它的类型,

> :t succ
succ :: Enum a => a -> a
Run Code Online (Sandbox Code Playgroud)

需要注意的主要事情是输入和输出类型是相同的。它也是Enum类型类的一个实例,也就是说这个类型实现了它特定版本的succ函数(这样有点循环)。

由于我们正在处理Integers,它们确实实现了它们的succas版本

succ :: Integer -> Integer
succ i = i + 1
Run Code Online (Sandbox Code Playgroud)

一切都很好并且得到了照顾。

除了collatz :: Integer -> Maybe Integer需要一个Integer并返回一个Maybe Integer

-- pseudocode
Maybe Integer = Nothing
              | Just      Integer
             -- ^ tags    ^ types of contained data
Run Code Online (Sandbox Code Playgroud)

所以我们需要应用succ包含的 Integer. 这就是工作fmap

-- pseudocode
> :t fmap
fmap :: Functor f => (a -> b) -> f     a -> f     b
> :t fmap @ Maybe
fmap ::              (a -> b) -> Maybe a -> Maybe b
> :t fmap @ Maybe succ @ Integer
fmap ::                    Maybe Integer -> Maybe Integer
Run Code Online (Sandbox Code Playgroud)

这是由一类类型定义的泛型函数,每个类型都定义了它们的专用版本。至于Maybe确实的作用:

-- pseudocode:
fmap f Nothing  = Nothing
fmap f (Just i) = Just (f i)
                --      ^^ f applied on the "inside"
                --      ^^ when there is something in there
Run Code Online (Sandbox Code Playgroud)

  • 你可能认为 `succ Nothing` 可以被定义为返回 `Nothing`,但 `Enum` 也提供(实际上,主要基于)方法 `toEnum :: Int -&gt; a` 和 `fromEnum :: a - &gt; 国际`。对于 `fromEnum Nothing` 应该返回的内容,没有好的选择。 (2认同)
  • 您可以根据辅助函数 `collat​​z' :: Integer -&gt; Integer` 定义 `collat​​z`,其中 `collat​​z` 负责确保永远不会在非正整数上调用 `collat​​z'。`collat​​z n | n &lt;=0 = 没有;| 否则 = Just (collat​​z' n)`。然后 `collat​​z' 将永远不需要使用 `fmap`,因为它不再接收类型为 `Maybe Integer` 的值,只有定义了 `succ` 的 `Integer` 值。 (2认同)