给定一个列表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代码?
答案 0 :(得分:5)
为了与列表中的其他函数更好地组合,最好将输入列表参数移动到最后位置,即:
g - > Int - > [a] - > [α]
通过这种方式,您可以像这样使用您的功能:
nub . rndsel g n . sort $ someList
使用“n”参数更改“generator”的顺序也更自然,因为“n”似乎是最具动态性的:
Int - > g - > [a] - > [α]
!!
函数是部分函数,如果您为其提供大于列表大小的索引,则会向您显示运行时错误。最近,使用部分功能被认为是一种不好的做法。
head
功能也是部分功能。
List不是基于索引的结构,并且按索引访问它的元素(!!
函数)具有O(n)
复杂度,这是非常无效的。
你的函数的问题域很容易分成另外两个更简单的问题:改组和取一部分洗牌结果。那里有很多很好的改组算法实现,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