我有二维字符地图的类型:
type Row = [Char]
type Mappy = [Row]
我想编写一个类似Mappy
的函数:
[['o','o'],['o','o']]
并生成所有Mappy
的列表,其中单个'o'元素替换为'i':
[ [['i','o'],['o','o']]
, [['o','i'],['o','o']]
, [['o','o'],['i','o']]
, [['o','o'],['o','i']]
]
这是我尝试过的:我认为我需要使用map函数,因为我需要遍历每个元素,但我不知道如何,因为map函数不跟踪它的位置正在努力。
type Row = [Char]
type Mappy = [Row]
func :: Mappy -> [Mappy]
func a = map (map someFunc a) a
someFunc :: Mappy -> Char -> Mappy
someFunc a b = if b == "o"
then undefined
else a
显然,我应该改变undefined
,但我不知道怎么做。提前谢谢。
答案 0 :(得分:3)
拉链很棒,而且有一篇有趣的博客文章 实施Conway's Game of Life using zippers and comonads in Haskell。在另一 如果这仍然是你学习Haskell的第一周,你可能会这样做 想要在星期四保存Comonads,对吧?
这是另一种使用简单递归和列表的方法 理解并没有复杂的Haskell特征。
首先,假设我们有一个很棒的功能:
varyOne :: (a -> [a]) -> [a] -> [[a]]
varyOne = undefined
的工作原理如下。给定函数f
产生零或
元素a
的更多变体,函数调用varyOne f xs
生成由于获取而导致的列表xs
的所有变体
正好是xs
中的一个元素,在列表中间说x
,并将其替换为全部
由f x
给出的变体。
此功能非常灵活。它可以生成所有变体的列表,这些变体是通过用常量强制替换元素得到的:
> varyOne (\x -> [3]) [1,2,3,4]
[[3,2,3,4],[1,3,3,4],[1,2,3,4],[1,2,3,3]]
通过为特定值返回单例变体和为其他值返回变量的空列表,它可以生成用'o'
替换'i'
的所有变体,同时抑制"变体& #34;无法替换的地方:
> let varyRow = varyOne (\c -> if c == 'o' then ['i'] else [])
> varyRow "ooxo"
["ioxo","oixo","ooxi"]
并且,因为varyRow
本身会生成行的变体,所以它可以与varyOne
一起使用来生成表格的变体,其中特定的行已被其替换变体:
> varyOne varyRow ["ooo","oox","ooo"]
[["ioo","oox","ooo"],["oio","oox","ooo"],["ooi","oox","ooo"],
["ooo","iox","ooo"],["ooo","oix","ooo"],
["ooo","oox","ioo"],["ooo","oox","oio"],["ooo","oox","ooi"]]
事实证明,这个令人敬畏的功能非常容易编写:
varyOne :: (a -> [a]) -> [a] -> [[a]]
varyOne f (x:xs)
= [y:xs | y <- f x] ++ [x:ys | ys <- varyOne f xs]
varyOne _ [] = []
第一个列表推导生成当前元素的所有变体。第二个列表推导使用递归varyOne
调用生成涉及更改当前元素右侧的变体。
鉴于varyOne
,我们可以定义:
replaceOne :: Char -> Char -> Mappy -> [Mappy]
replaceOne old new = varyOne (varyOne rep1)
where rep1 x = if x == old then [new] else []
和
> replaceOne 'o' 'i' ["ooo","oox","ooo"]
[["ioo","oox","ooo"],["oio","oox","ooo"],["ooi","oox","ooo"]
,["ooo","iox","ooo"],["ooo","oix","ooo"]
,["ooo","oox","ioo"],["ooo","oox","oio"],["ooo","oox","ooi"]]
可能是您正在寻找的功能。
如果您希望无条件地用i
替换单个元素,无论旧元素是什么,那么这将起作用:
> varyOne (varyOne (const ['i'])) ["ooo","oox","ooo"]
[["ioo","oox","ooo"],["oio","oox","ooo"],["ooi","oox","ooo"]
,["ooo","iox","ooo"],["ooo","oix","ooo"],["ooo","ooi","ooo"]
,["ooo","oox","ioo"],["ooo","oox","oio"],["ooo","oox","ooi"]]
答案 1 :(得分:2)
你想要的,年轻的BaasBartMans,是拉链。
data Zipper a = Zipper [a] a [a]
ofList :: [a] -> Maybe (Zipper a)
ofList [] = Nothing
ofList (a:as) = Just (Zipper [] a as)
拉链为您提供列表中某个位置的上下文,所以您 可以轻松地一次修改一个,前进和后退等。
我们可以从拉链中恢复列表:
instance Foldable Zipper where
foldr f c (Zipper ls a rs) = foldl' (flip f) (f a (foldr f c rs)) ls
我们可以同时修改Zipper中的每个位置:
instance Functor Zipper where
fmap f (Zipper ls a rs) = Zipper (fmap f ls) (f a) (fmap f rs)
或者只是关注的元素:
here :: Functor f => (a -> f a) -> Zipper a -> f (Zipper a)
here f (Zipper ls a rs) = fmap (\a' -> Zipper ls a' rs) (f a)
由于Zipper
是Comonad
,我们可以修改上下文中的每个元素:
instance Comonad Zipper where
extract (Zipper _ a _) = a
extend f z@(Zipper ls a rs) = Zipper ls' a' rs' where
a' = f z
ls' = unfoldr (fmap (\z' -> (f z', z')) . goLeft) z
rs' = unfoldr (fmap (\z' -> (f z', z')) . goRight) z
使用它,我们可以构建一个在上下文中修改列表的每个元素的函数:
everywhere :: Alternative f => (a -> f a) -> [a] -> f [a]
everywhere f as = case ofList as of
Nothing -> pure []
Just z -> asum $ extend (fmap toList . here f) z
适用于简单列表:
λ everywhere (\a -> [a+1]) [10,20,30]
[[11,20,30]
,[10,21,30]
,[10,20,31]]
嵌套列表:
λ everywhere (everywhere (\a -> [a+1])) [[10], [20,20], [30,30,30]]
[[[11],[20,20],[30,30,30]]
,[[10],[21,20],[30,30,30]]
,[[10],[20,21],[30,30,30]]
,[[10],[20,20],[31,30,30]]
,[[10],[20,20],[30,31,30]]
,[[10],[20,20],[30,30,31]]]