我有一个数据类型为SFeld的表(列表列表)。 SFeld是Int或Ints列表
data SFeld = E Int | M [Int]
type STable = [[SFeld]]
我想定义一个函数expandM :: STable -> [STable]
,它接受一个STable和#34;扩展"其中的每个M,以便为M列表中的每个值创建一个新的STable列表。听起来很混乱所以这是一个例子:
>> expandM [[E 1,M [1,2]],[M [2,3],E 2,E 4],[E 5]] =
[ [[E 1,E 1],[E 2,E 2,E 4],[E 5]]
, [[E 1,E 2],[E 2,E 2,E 4],[E 5]]
, [[E 1,E 1],[E 3,E 2,E 4],[E 5]]
, [[E 1,E 2],[E 3,E 2,E 4],[E 5]]
]
我如何实现这样的功能?
答案 0 :(得分:3)
将列表中项目的所有可能组合放在一起是>>=
实例的绑定运算符Monad []
。专门为列表
(>>=) :: [a] -> (a -> [b]) -> [b]
对于列表中的每个项a
,它运行函数a -> [b]
以找出所有可能性,并在结果列表[b]
中收集这些可能性。我们想要使用的基本功能与此签名相匹配;它从初始SFeld
计算所有可能的SFeld
。
expandSFeld :: SFeld -> [SFeld]
expandSFeld (E x) = [E x]
expandSFeld (M xs) = map E xs
SFeld
被保存在现有结构中,STable
是SFeld
列表。我们想在Monad []
上使用STable
实例的绑定,而不更改表的结构。通常,Traverable
类描述了可以保留的结构,以便在结构内部运行的Monad
†可以移动到结构的外部。
-- v-------------v flips Traverable from outside to inside
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
-- flips Monad from inside to outside ^-------^
Traversable
还包含一个函数mapM
,用于将函数a -> m b
映射到t a
,从而生成m (t b)
。生成的t b
具有与初始t a
相同的结构。
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
mapM k = sequence . fmap k
列表中有Traversable
个实例。这意味着我们可以在列表中每个项目的某些Monad
上执行操作时保留列表的结构。我们可以使用它来对行中的每个项执行expandSFeld
操作,同时保留行的结构。
import Data.Traversable
import Prelude hiding (mapM)
expandSRow :: [SFeld] -> [[SFeld]]
expandSRow = mapM expandSFeld
我们可以对expandSRow
中的每一行执行STable
操作,同时保留STable
的结构。
expandM :: STable -> [STable]
expandM = mapM expandSRow
生成的expandM
函数会返回通过展开所有STable
获得的每个M
。
expandM [[E 1,M [1,2]],[M [2,3],E 2]]
[
[[E 1,E 1],[E 2,E 2]],
[[E 1,E 1],[E 3,E 2]],
[[E 1,E 2],[E 2,E 2]],
[[E 1,E 2],[E 3,E 2]]
]
† Traverable
类的功能可以更普遍地用于任何Applicative
而不是任何Monad
。