1

给定一个列表lst和一个数字n,以下代码将输出一个列表,其中n包含来自给定列表的不同元素lst

rndsel :: (Eq a, RandomGen g) => [a] -> g -> Int -> [a]
rndsel lst _ 0 = []
rndsel lst g n = schar:(rndsel rem g (n-1))
  where schar = lst !! index
        index = head $ randomRs (0, ll-1) g
        ll = length lst
        rem = delete schar lst

我想知道是否有任何方法可以在可读性、口才方面改进此代码,以及是否可以使其更符合惯用的 haskell 代码?

4

1 回答 1

5
  1. 为了更好地与列表中的其他函数组合,最好将输入列表参数移动到最后一个位置,即:

    g -> Int -> [a] -> [a]

    这样你就可以像这样使用你的函数:

    nub . rndsel g n . sort $ someList
    
  2. 使用“n”参数更改“生成器”的顺序也感觉更自然,因为“n”似乎是最动态的:

    整数 -> g -> [a] -> [a]

  3. !!函数是部分函数,​​如果您为其提供大于列表大小的索引,则会向您显示运行时错误。最近,使用偏函数被认为是一种不好的做法。

  4. head功能也是局部的。

  5. List 不是基于索引的结构,通过索引(!!函数)访问它的元素具有O(n)复杂性,这是非常无效的。

  6. 您的函数的问题域很容易分为另外两个更简单的问题域:改组和获取改组结果的一部分。那里有很多好的洗牌算法实现,Fisher Yates似乎最适合你的情况。它具有以下签名:

    shuffle :: RandomGen g => g -> [a] -> ([a], g)
    

    现在使用它,我们可以轻松地重新实现您的功能,如下所示:

    rndsel :: (Eq a, RandomGen g) => Int -> g -> [a] -> [a]
    rndsel n g = take n . fst . shuffle g
    
于 2013-06-13T15:36:07.643 回答