stu*_*ith 6 monads haskell list
考虑我写的以下代码:
import Control.Monad
increasing :: Integer -> [Integer]
increasing n
| n == 1 = [1..9]
| otherwise = do let ps = increasing (n - 1)
let last = liftM2 mod ps [10]
let next = liftM2 (*) ps [10]
alternateEndings next last
where alternateEndings xs ys = concat $ zipWith alts xs ys
alts x y = liftM2 (+) [x] [y..9]
Run Code Online (Sandbox Code Playgroud)
' increasing n'应返回一个n位数字列表,其数字从左到右增加(或保持不变).
有没有办法简化这个?使用' let'和' liftM2'到处都看起来很难看.我想我错过了关于列表monad的重要内容,但我似乎无法摆脱它们.
好吧,就liftM功能而言,我使用它们的首选方式是定义的组合器Control.Applicative.使用这些,你就可以写last = mod <$> ps <*> [10].在ap从功能Control.Monad做同样的事情,但我更喜欢缀版本.
什么(<$>)以及(<*>)是这样的:liftM2把一个函数a -> b -> c变成一个功能m a -> m b -> m c.平原liftM是公正的(a -> b) -> (m a -> m b),fmap也是如此(<$>).
如果您对多参数函数执行此操作会发生什么?它转变a -> b -> c -> d成了m a -> m (b -> c -> d).这就是在哪里ap或(<*>)进来的:他们所做的就是把事情m (a -> b)变成现实m a -> m b.因此,您可以根据需要为这么多参数保持字符串.
也就是说,特拉维斯·布朗是正确的,在这种情况下,你似乎并不真正需要上述任何一种.事实上,您可以简化功能的极大:例如,两者last并next可以写成映射在同一个列表单参数的功能,ps和zipWith相同的zip和map.所有这些地图都可以组合并下推到alts函数中.这使得alts单参数函数也消失了zip.最后,concat可以与mapas concatMap或如果优选的话组合(>>=).这是最终结果:
increasing' :: Integer -> [Integer]
increasing' 1 = [1..9]
increasing' n = increasing' (n - 1) >>= alts
where alts x = map ((x * 10) +) [mod x 10..9]
Run Code Online (Sandbox Code Playgroud)
请注意,所有重构我没有去从你的那个版本是纯粹的语法,只应用应该对函数的结果没有影响转换.均衡推理和参考透明度很好!
我想你要做的是这样的:
increasing :: Integer -> [Integer]
increasing 1 = [1..9]
increasing n = do p <- increasing (n - 1)
let last = p `mod` 10
next = p * 10
alt <- [last .. 9]
return $ next + alt
Run Code Online (Sandbox Code Playgroud)
或者,使用"list comprehension",这只是列表的特殊monad语法:
increasing2 :: Integer -> [Integer]
increasing2 1 = [1..9]
increasing2 n = [next + alt | p <- increasing (n - 1),
let last = p `mod` 10
next = p * 10,
alt <- [last .. 9]
]
Run Code Online (Sandbox Code Playgroud)
列表monad中的想法是使用"bind"(<-)迭代值列表,并let根据当前迭代中的内容计算单个值.当您第二次使用bind时,迭代将从该点开始嵌套.