如何将所有其他元素的列表元素相乘?

时间:2012-01-23 05:18:04

标签: haskell

例如,list = [1,2,3,4]listProduct list返回[1,2,3,4,6,8,9,12,16],即[(1*1),(1*2),(1*3),(1*4),(2*3),(2*4),(3*3),(3*4),(4*4)]

我记得看到有些东西可以做到这一点,但我再也找不到那个资源了。

4 个答案:

答案 0 :(得分:9)

您可以使用列表解析以简单的方式编写此代码:

listProduct xs = [x * y | x <- xs, y <- xs]

但是,使用list monad更加惯用:

import Control.Monad

listProduct = join $ liftM2 (*)

(相当于listProduct xs = liftM2 (*) xs xs

要理解此版本,您可以将liftM2视为一种广义Cartesian productliftM2 (,)是笛卡尔积本身)。如果将liftM2的定义专门化为列表monad,则更容易看出它是如何工作的:

liftM2 f mx my = do { x <- mx; y <- my; return (f x y) }
-- expand the do notation
liftM2 f mx my = mx >>= (\x -> my >>= (\y -> return (f x y)))
-- substitute the list monad's definition of (>>=)
liftM2 f mx my = concatMap (\x -> concatMap (\y -> [f x y]) my) mx
-- simplify
liftM2 f mx my = concatMap (\x -> map (\y -> f x y) my) mx
-- simplify again
liftM2 f mx my = concatMap (\x -> map (f x) my) mx

因此listProduct的monadic定义扩展为:

listProduct xs = concatMap (\x -> map (x *) xs) xs

(请注意,从技术上讲,您不需要这里的完整列表monad;所需的只是列表的Applicative实例,而listProduct = join $ liftA2 (*)也可以正常工作。但是,它更容易显示如何使用monadic定义,因为列表的Applicative实例是根据Monad实例定义的。)

答案 1 :(得分:5)

为什么您的示例不在结果中包含2*2

如果是因为它与1*4相同---也就是说,你不需要重复 - 那么

listProduct xs = nub [x * y | x <- xs, y <- xs]

另一方面,如果你想要重复 - 如果你想将每个数字乘以列表中的每个后续数字,并在结果中包含重复数据---那么

listProduct' xs = triangularAutoZipWith (*)

triangularAutoZipWith op = concatMap f . tails
  where f [] = []
        f xs @ (x : _) = map (op x) xs

您可以在第一个解决方案的更高效版本中使用triangularAutoZipWith

listProduct = nub . triangularAutoZipWith (*)

答案 2 :(得分:4)

使用...

import Control.Applicative

......有重复...

listProduct list = (*) <$> list <*> list

......没有......

listProduct list = concat (mul <$> list <*> list) where
    mul a b | a <= b = [a*b]
            | otherwise = []

如果你处于rube-goldberg心情,你可以使用......

listProduct list = concat $ zipWith (map.(*)) list (map ((`filter` list).(<=)) list) 

......或者只是......

import Data.List

listProduct list = concat $ zipWith (map.(*)) list $ tails list 

<强> [编辑]

另一种方法是使用sequence。有重复:

listProduct = map product . sequence . replicate 2

listProduct = map product . filter(\[a,b] -> a <= b) . sequence . replicate 2   

答案 3 :(得分:1)

嗯,你已经得到了一些答案,但我会打算扔进去,因为我认为早期的答案虽然准确,但可能没有多大帮助。

初学者理解的最简单的解决方案是列表理解:

example1 = [ x*y | x <- list, y <- list ]

这种语法存在于Python等一些流行语言中,在任何情况下都应该易于理解:“其元素是x*y结果的列表,其中x是{{1}的元素}和listy的元素。“您还可以在列表推导中添加条件以过滤掉某些组合,例如,如果您不希望产品位于list

x == y

更复杂的答案与列表推导等同于 List Monad 的事实有关;列表类型的标准example2 = [ x*y | x <- list, y <- list, x /= y ] 类型类的实现。这意味着Monad也可以通过以下方式实现:

example1

example1' = do x <- list y <- list return (x*y) - 符号只是语法糖:

do

Landei的答案是基于以下事实:如果你没有在列表理解中使用任何条件,只有Cartesian产品,你就可以使用弱于{{1的example1'' = list >>= (\x -> list >>= (\y -> return (x*y))) 类型类。 }}