【问题标题】:Why does the shuffle' function require an Int parameter?为什么 shuffle 函数需要一个 Int 参数?
【发布时间】:2019-03-11 18:08:56
【问题描述】:

System.Random.Shuffle

shuffle' :: RandomGen gen => [a] -> Int -> gen -> [a]

hackage page 将此 Int 参数称为

...,它的长度,...

但是,看起来像一个简单的包装函数

shuffle'' x = shuffle' x (length x)

应该够了。

【问题讨论】:

    标签: list haskell random shuffle


    【解决方案1】:

    shuffle 通过构建其输入列表的树形形式进行操作,包括树形大小buildTree 函数使用Data.Function.fix 以一种我还没有完全理解的方式执行此任务。不知何故(我认为由于inner 的递归,而不是fix 魔法),它产生了一个平衡树,然后具有对数查找。然后它消耗这棵树,为每个提取的项目重建它。数据结构的优点是它只以不可变的形式保存剩余的项目;懒惰的更新适用于它。但是树的大小是索引期间需要的数据,因此无需单独传递它来生成用于构建排列的索引。 System.Random.Shuffle.shuffle 确实没有随机元素 - 它只是一个置换函数。 shuffle' 的存在是为了给它一个随机序列,使用它的内部帮助器 rseq。所以shuffle' 采用长度参数的原因似乎是因为他们根本不希望它触及列表参数;它只传递给shuffle

    首先,该任务似乎不太适合单链表。我可能会考虑改用VectorShuffling。我很困惑为什么rseq 不在导出函数之列,因为它是使用随机数生成器来构建排列的函数......这反过来可能使用Data.Permute 处理得更好。原因可能与历史有关,例如 Data.Permute 是稍后编写的,而 System.Random.Shuffle 是基于一篇关于不可变随机访问队列的论文。

    Data.Random.Extras 似乎有一个更直接的基于 Seq 的随机播放功能。

    【讨论】:

    • inner 获取节点列表,joins 将它们分成一半的节点。也就是说,[n1, n2, n3, n4 ...] 变为 [join n1 n2, join n3 n4 ...](末尾带有 slop)。 fix growLevel 只是构建这个计算,重复 inner 直到一切都归结为一个节点。这就是为什么它被称为growLevel。我们从n高度1节点(map Leaf)开始,然后移动到n/2高度2节点,然后n/4高度3节点,直到我们到达1 = n/(2^(h - 1))高度@节点987654351@(忽略前面提到的废话;如果我错了,请纠正我)。
    • 我理解这一点,并且fix 应该达到这个固定点,其中不超过一个条目,但我还没有弄清楚它是如何工作的。就像head . head . dropWhile (\x -> length x > 1) $ iterate inner es
    • 我认为您误解了fix 的工作原理。 fix growLevelgrowLevel (growLevel (growLevel ...)) = \case { [node] -> node; l -> (\case { [node] -> node; l -> (...) (inner l)}) (inner l)。 “包含自身的数据结构”不是列表,而是函数。 leting 无限远/内联fix 产生:fix growLevel = let { function [node] = [node]; function l = function (inner l) } in function。老实说,我不知道为什么使用fix。它对于内联递归定义很有用,或者允许自己被替换,但定义既不是内联也不是公共的。
    • 我明白它的作用; dropWhile 方法只是在这种情况下完成相同结果的一种方法。嵌套调用是 iterate 所做的。 fix 如何执行测试的细节让我感到困惑,如果我仔细考虑一下,你的描述可能会有所帮助。
    【解决方案2】:

    这可能是给定列表的长度已知且不需要再次计算的情况。因此,它可能被视为一种优化。

    此外,一般来说,结果列表不需要与原始列表具有相同的大小。因此,这个参数可以用来设置这个长度。

    这对于 Oleg 的最初想法是正确的(来源 - http://okmij.org/ftp/Haskell/perfect-shuffle.txt):

    -- examples
    
    t1 = shuffle1 ['a','b','c','d','e'] [0,0,0,0]
    -- "abcde"
    -- Note, that rseq of all zeros leaves the sequence unperturbed.
    
    t2 = shuffle1 ['a','b','c','d','e'] [4,3,2,1]
    -- "edcba"
    -- The rseq of (n-i | i<-[1..n-1]) reverses the original sequence of elements
    

    但是,'random-shuffle' 包的实现并不相同:

    > shuffle [0..10] [0,0,0,0]
    [0,1,2,3random-shuffle.hs: [shuffle] called with lists of different lengths
    

    我认为值得跟进包维护者以了解此功能的合同。

    【讨论】:

    • 它还允许只洗牌列表的前缀,这对于无限列表可能很重要。
    • 它不适用于无限列表,因为列表按原样传递给buildTree。如果它使用take len elements,它可能会有。
    猜你喜欢
    • 2020-01-25
    • 2016-03-31
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多