通过 Functor 实例将 Rose (Board, Int) 转换为 Rose Int

Converting Rose (Board, Int) to Rose Int through Functor instance

data Rose a = MkRose a [Rose a]
    deriving (Eq, Show)

-- Rose Int with two children
t1 = MkRose 3 [MkRose 9 [], MkRose 2 []]

data Field = X | O | B
    deriving (Eq, Ord)

type Row   = (Field, Field, Field)
type Board = (Row, Row, Row)

minimax :: Player -> Rose Board -> Rose Int 
-- MkRose -1 [MkRose 1 [..], MkRose -1 [..], MkRose 1 [..] ]
minimax P1 (MkRose b bs)
  = let bs' = minimax' (MkRose b bs) 
-- :t bs' = MkRose (Board, Int) 
--            [MkRose (Board,Int) [], MkRose (Board,Int) [] ]
    in  fmap snd bs'

如果我有 Rose (Board, Int),我希望能够将其转换为 Rose Int。我从尝试 Functor 实例切换到仅在列表中使用 fmapsnd,如上更新,这给了我这个错误:

error:
    • No instance for (Functor Rose) arising from a use of ‘fmap’
    • In the expression: fmap snd bs'
      In the expression: let bs' = minimax' (MkRose b bs) in fmap snd bs'
      In an equation for ‘minimax’:
          minimax P1 (MkRose b bs)
            = let bs' = minimax' (MkRose b bs) in fmap snd bs'
    |
259 |     in  fmap snd bs'

如何在 Haskell 中完成此操作?或者我可以只写一个函数,如果那样更容易的话。

你不需要fmap:

rmap :: (a -> b) -> Rose a -> Rose b
rmap f (MkRose a as) = MkRose (f a) (map (rmap f) as)

但是有

instance Functor Rose where fmap = rmap

让您可以像 (<$) 一样做一些整洁的事情,您不必一直记住 maprmapvmap

(另外,{-# LANGUAGE DeriveFunctor #-},你可以简单写成data Rose a = MkRose a [a] deriving Functor,实例给你计算。)

只需将 Functor 添加到 deriving 列表中:

data Rose a = MkRose a [Rose a]
    deriving (Eq, Show, Functor)

您可能必须启用扩展程序 DeriveFunctor

那你就可以用fmap snd.

对于任何类型 ab,它会将 Rose (a,b) 转换为 Rose b。特别是,对于 (Board, Int),它会将 Rose (Board, Int) 转换为 Rose Int,方法是简单地剥离树中每个值位置中元组的第一个组件。

fmap     :: Functor f =>            (a -> b) -> f a -> f b
fmap snd :: Functor f =>                   f (a, b) -> f b
fmap @ Rose ::                   (a -> b) -> Rose a -> Rose b
fmap @ Rose @ (Board, Int) snd :: Rose (Board, Int) -> Rose Int

这使用 TypeApplications 但您不需要它,类型将根据您提供的数据自动选择,即您将应用 fmap snd 的变量。例如:

Prelude> t1 = MkRose (0,3) [MkRose (0,9) []]
Prelude> fmap snd t1
MkRose 3 [MkRose 9 []]
Prelude> 

您在评论中所写的语法无效

bs' = MkRose (Board, Int) 
          [MkRose (Board,Int) [], MkRose (Board,Int) [] ]

如果你有这个作为代码,逐字记录。

你不能那样做。 Board 是一种类型的名称。 Int 是一种类型的名称。但是变量 bs' 应该引用一段 数据 。如果该数据是Rose (Bord, Int)类型,则意味着在每个出现类型变量a的地方,在数据类型定义中,

data Rose a = MkRose a [Rose a]
--                   ^       ^

应该会出现 一条 (Board, Int) 类型的数据 -- 而不是简单地键入 names。这是一个包含两个实际值的元组,一个是 Board 类型,另一个是 Int 类型。例如,Rose (String, Int):

t2 :: Rose (String, Int)
t2 = MkRose ("a", 1) [MkRose ("b", 2) [], MkRose ("c", 3) []]

fmap snd t2 
=> MkRose 1 [MkRose 2 [], MkRose 3 []]