如何提高多参数的类似函数的可读性?

时间:2014-08-25 13:36:03

标签: haskell functional-programming readability

我做了这个功能:

-- Generates multiple random values
randoms :: (Random a) => StdGen -> Int -> a -> a -> ([a], StdGen)
randoms rndGen nbrsCount min max = randomNbrs' nbrsCount min max ([], rndGen) where
    randomNbrs' rndGen 0 min max cumul = cumul
    randomNbrs' rndGen count min max cumul = randomNbrs' rndGen (count-1) min max (values, snd rndGen') where
        rndGen' = randomR (min, max) rndGen
        values = fst rndGen' : values

并且难以阅读。因为我是Haskell的新手,所以我无法找到如何提高其可读性。如何使这更容易阅读和更简洁?

2 个答案:

答案 0 :(得分:7)

我真的很想在这些情况下使用MonadRandom

import Control.Monad.Random

randomsR :: (Random a, RandomGen b) => b -> Int -> a -> a -> ([a], b)
randomsR gen nb min max = flip runRand gen        -- run the random monad
                          . sequence              -- get the random monad out of the list
                          . replicate nb          -- generate nb values
                          $ getRandomR (min, max) -- the generator for one value

你也可以缩短它(感谢Ørjan):

randomsR :: (Random a, RandomGen b) => b -> Int -> a -> a -> ([a], b)
randomsR gen nb min max = flip runRand gen
                          . replicateM nb
                          $ getRandomR (min, max)

randomsR :: (Random a, RandomGen b) => b -> Int -> a -> a -> ([a], b)
randomsR gen nb min max = flip runRand gen
                          . fmap (take nb)
                          $ getRandomRs (min, max)

答案 1 :(得分:3)

你可以做一些简单的事情。首先,您可以使用模式匹配来分解没有多个表达式的元组。您也不需要传递随机生成器的实例,因为您已经在元组中使用它,再次使用模式匹配将允许您访问它。

randoml :: (Random a, RandomGen b)=> b -> Int -> a -> a -> ([a], b)
randoml rndGen nbrsCount minVal maxVal = randomNbrs' nbrsCount ([], rndGen)
  where
    randomNbrs' 0 cumul = cumul
    randomNbrs' count (values, gen) = randomNbrs' (count-1) (newVal:values, newGen)
      where
        (newVal, newGen) = randomR (minVal, maxVal) gen

我重命名了一些变量,因为它们与PreludeSystem.Random中的其他名称冲突。

现在这已经看起来更清洁了。您可以采用的下一步是添加foldr而不是显式递归。它有点像惯用的Haskell。如果您还没有使用折叠,请不要担心您会很快到达。折叠实际上只是表达一种常见递归形式的一种方式,您可以在一系列项目上操作,随时累积最终结果。

randoml :: (Random a, RandomGen b)=> b -> Int -> a -> a -> ([a], b)
randoml rndGen nbrsCount minVal maxVal = foldr (\ _ (vals, gen) ->
                                                 let
                                                   (val, newGen) = randomR (minVal, maxVal) gen
                                                 in
                                                  (val:vals, newGen))
                                          ([], rndGen) [0..nbrsCount-1]

或者如果您不喜欢lambda表达式

randoml :: (Random a, RandomGen b)=> b -> Int -> a -> a -> ([a], b)
randoml rndGen nbrsCount minVal maxVal = foldr func ([], rndGen) [0..nbrsCount-1]
  where
    func _ (vals, gen) = (val:vals, newGen)
      where
        (val, newGen) = randomR (minVal, maxVal) gen
相关问题