我真的很喜欢编程和Haskell(这是新的,我实际上不知道这是不是一个愚蠢的问题)。但我正在观看Eric Meijer(http://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-Functional-Programming-Fundamentals)所做的演讲,我很着迷Graham Hutton博士在第11讲中所写的课程;倒计时问题。
我的问题是:
有没有办法按长度(元素数量)“过滤”解决方案列表,以便解决方案列表仅限于使用(例如)三个源数字的解决方案?换句话说,我想更改问题“从给定数字[1,2,3,4,5,6,8,9]
构造18
使用运算符...”到“给出数字[..]
可以使用三个数字”建造......“
在我的徒劳尝试中,我一直试图对他的函数subbags
(它返回列表的所有排列和子序列)进行一种限制。
subbags :: [a] -> [[a]]
subbags xs = [zs | ys <- subs xs, zs <- perms ys]
这样我就得到了只包含三个源数的所有排列和子序列。这可能吗?如果是这样,怎么样?
就像我说的那样,我不知道这是否是一个合法的问题 - 但我已经从好奇变为痴迷,所以任何形式的帮助或提示都会受到高度赞赏!
答案 0 :(得分:3)
最简单的方法是从候选人中选择三次
[ (x, y, z) | x <- xs, y <- xs, z <- xs ]
虽然这假设重复使用单个数字是可以的。
如果不是,我们必须变得更聪明。在一个更简单的场景中,我们只想挑选两个候选人:
[ (x, y) | x <- xs, y <- ys, aboveDiagonal (x, y) ]
换句话说,如果我们将此视为笛卡尔积,将列表转化为可能性网格,我们只想考虑“在对角线上方”的值,其中重复不会发生。我们可以通过压缩坐标和值来表达这一点
[ (x, y) | (i, x) <- zip [1..] xs
, (j, y) <- zip [1..] xs
, i < j
]
可以延伸回n=3
场景
[ (x, y, z) | (i, x) <- zip [1..] xs
, (j, y) <- zip [1..] xs
, (k, z) <- zip [1..] xs
, i < j
, j < k
]
然而,最终,这种方法效率很低,因为它仍然必须扫描所有可能的对,然后修剪重复。通过仅列举上述对角线值,我们可以更聪明一些。返回n=2
我们将其写为
choose2 :: [a] -> [(a, a)]
choose2 [] = []
choose2 (a:as) = map (a,) as ++ choose2 as
换句话说,我们首先选择列表头部首先出现的所有对,列表尾部的值为秒 - 这将捕获上三角形的一条边 - 然后我们通过添加全部来递归候选人名单中对角线的上对角值没有头。
使用n=3
案例作为构建块,可以直接将此方法扩展到n=2
案例
choose3 :: [a] -> [(a, a, a)]
choose3 [] = []
choose3 (a:as) = map (\(y, z) -> (a, y, z)) (choose2 as) ++ choose3 as
还提供了对完全通用n
维解决方案
choose :: Int -> [a] -> [[a]]
choose 0 as = [[]] -- there's one way to choose 0 elements
choose _ [] = [] -- there are 0 ways to choose (n>0) elements of none
choose 1 as = map (:[]) as -- there are n ways to choose 1 element of n
choose n (a:as) = map (a:) (choose (n-1) as) ++ choose n as
答案 1 :(得分:0)
我喜欢这个解决方案,它不需要列表元素是Eq
的实例:
import Data.List (tails)
triples ls = [[x,y,z] | (x:xs) <- tails ls,
(y:ys) <- tails xs,
z <- ys]
但这只返回子序列,而不是排列。