简体   繁体   English

在列表中应用函数的“排列”

[英]Apply “permutations” of a function over a list

Creating the permutations of a list or set is simple enough. 创建列表或集合的排列很简单。 I need to apply a function to each element of all subsets of all elements in a list, in the order in which they occur. 我需要按照它们出现的顺序将一个函数应用于列表中所有元素的所有子集的每个元素。 For instance: 例如:

apply f [x,y] = { [x,y], [f x, y], [x, f y], [f x, f y] }

The code I have is a monstrous pipeline or expensive computations, and I'm not sure how to proceed, or if it's correct. 我的代码是一个可怕的管道或昂贵的计算,我不知道如何继续,或者它是否正确。 I'm sure there must be a better way to accomplish this task - perhaps in the list monad - but I'm not sure. 我确信必须有更好的方法来完成这项任务 - 也许在monad列表中 - 但我不确定。 This is my code: 这是我的代码:

apply :: Ord a => (a -> Maybe a) -> [a] -> Set [a]
apply p xs = let box = take (length xs + 1) . map (take $ length xs) in
  (Set.fromList . map (catMaybes . zipWith (flip ($)) xs) . concatMap permutations
   . box . map (flip (++) (repeat Just)) . flip iterate []) ((:) p)

The general idea was: 一般的想法是:

(1) make the list 
      [[], [f], [f,f], [f,f,f], ... ]
(2) map (++ repeat Just) over the list to obtain
      [[Just, Just, Just, Just, ... ],
       [f   , Just, Just, Just, ... ],
       [f   , f   , Just, Just, ... ],
                                ... ]
(3) find all permutations of each list in (2) shaved to the length of the input list        
(4) apply the permuted lists to the original list, garnering all possible applications
    of the function f to each (possibly empty) subset of the original list, preserving
    the original order.

I'm sure there's a better way to do it, though. 不过,我确信有更好的方法。 I just don't know it. 我只是不知道。 This way is expensive, messy, and rather prone to error. 这种方式昂贵,杂乱,而且容易出错。 The Justs are there because of the intended application. 由于预期的应用,Justs在那里。

To do this, you can leverage the fact that lists represent non-deterministic values when using applicatives and monads. 为此,您可以利用列表在使用applicatives和monad时表示非确定性值的事实。 It then becomes as simple as: 然后变得如此简单:

apply f = mapM (\x -> [x, f x])

It basically reads as follows: "Map each item in a list to itself and the result of applying f to it. Finally, return a list of all the possible combinations of these two values across the whole list." 它基本上如下所示:“将列表中的每个项目映射到自身以及将f应用于它的结果。最后,返回整个列表中这两个值的所有可能组合的列表。”

If I understand your problem correctly, it's best not to describe it in terms of permutations. 如果我正确理解你的问题,最好不要用排列来描述它。 Rather, it's closer to generating powersets. 相反,它更接近于生成powersets。

powerset (x:xs) = let pxs = powerset xs in pxs ++ map (x :) pxs
powerset []     = [[]]

Each time you add another member to the head of the list, the powerset doubles in size. 每次将另一个成员添加到列表的头部时,powerset的大小都会翻倍。 The second half of the powerset is exactly like the first, but with x included. powerset的后半部分与第一部分完全相同,但包括x。

For your problem, the choice is not whether to include or exclude x, but whether to apply or not apply f. 对于您的问题,选择不是包括或排除x,而是选择是否应用f。

powersetapp f (x:xs) = let pxs = powersetapp f xs in map (x:) pxs ++ map (f x:) pxs
powersetapp f []     = [[]]

This does what your "apply" function does, modulo making a Set out of the result. 这就是你的“应用”功能所做的事情,模拟结果的Set。

Paul's and Heatsink's answers are good, but error out when you try to run them on infinite lists. Paul和Heatsink的答案很好,但是当你尝试在无限列表上运行它们时会出错。

Here's a different method that works on both infinite and finite lists: 这是一个适用于无限和有限列表的不同方法:

apply _ [] = [ [] ]
apply f (x:xs) = (x:ys):(x':ys):(double yss)
  where x' = f x
        (ys:yss) = apply f xs
        double [] = []
        double (ys:yss) = (x:ys):(x':ys):(double yss)

This works as expected - though you'll note it produces a different order to the permutations than Paul's and Heatsink's 这可以按预期工作 - 虽然你会注意到它产生的排列顺序与Paul和Heatsink不同

ghci> -- on an infinite list
ghci> map (take 4) $ take 16 $ apply (+1) [0,0..]
[[0,0,0,0],[1,0,0,0],[0,1,0,0],[1,1,0,0],[0,0,1,0],...,[1,1,1,1]]
ghci> -- on a finite list
ghci> apply (+1) [0,0,0,0]
[[0,0,0,0],[1,0,0,0],[0,1,0,0],[1,1,0,0],[0,0,1,0],...,[1,1,1,1]]

Here is an alternative phrasing of rampion's infinite-input-handling solution: 以下是rampion的无限输入处理解决方案的另一种措辞:

-- sequence a list of nonempty lists
sequenceList :: [[a]] -> [[a]]
sequenceList [] = [[]]
sequenceList (m:ms) = do
    xs <- nonempty (sequenceList ms)
    x  <- nonempty m
    return (x:xs)
  where
    nonempty ~(x:xs) = x:xs

Then we can define apply in Paul's idiomatic style: 然后我们可以用保罗的惯用风格定义apply

apply f = sequenceList . map (\x -> [x, f x])

Contrast sequenceList with the usual definition of sequence : 对比sequenceList与通常的sequence定义:

sequence :: (Monad m) => [m a] -> m [a]
sequence [] = [[]]
sequence (m:ms) = do
    x <- m
    xs <- sequence ms
    return (x:xs)

The order of binding is reversed in sequenceList so that the variations of the first element are the "inner loop", ie we vary the head faster than the tail. sequenceList反转绑定的sequenceList使得第一个元素的变化是“内环”,即我们比尾部更快地改变头部。 Varying the end of an infinite list is a waste of time. 改变无限列表的结尾是浪费时间。

The other key change is nonempty , the promise that we won't bind an empty list. 另一个关键变化nonempty的,我们不会绑定空列表的承诺。 If any of the inputs were empty, or if the result of the recursive call to sequenceList were ever empty, then we would be forced to return an empty list. 如果任何输入为空,或者对sequenceList的递归调用结果为空,那么我们将被迫返回一个空列表。 We can't tell in advance whether any of inputs is empty (because there are infinitely many of them to check), so the only way for this function to output anything at all is to promise that they won't be. 我们无法预先判断是否有任何输入是空的(因为有无数的输入要检查),所以这个函数输出任何东西的唯一方法就是保证它们不会。

Anyway, this is fun subtle stuff. 无论如何,这是有趣的微妙的东西。 Don't stress about it on your first day :-) 不要在第一天对此施加压力:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM