我在处理Haskell类的作业时遇到麻烦。我已经解决了此任务的部分问题:我必须编写一个函数,该函数接受一个Int并使用该Int的倍数创建一个无限列表。
function :: Int -> [Int]
function d = [d*x | x <- [1..]]
控制台:
ghci> take 10 (function 3)
给予
[3,6,9,12,15,18,21,24,27,30]
在第二个任务中,我必须扩展该函数,以便它接受一个数字列表,并将该列表的每个值用作因子(以前是d)。例如:
ghci> take 10 (function [3, 5])
应提供
[3,5,6,9,10,12,15,18,20,21]
已经尝试过类似列表理解
function d = [y*x | y <- [1..], x <- d]
但是该函数以未排序的形式返回列表:
[3,5,6,10,9,15,12,20,15,25]
我们得到了应该使用Haskell的模函数的提示,但是我不知道如何准确地进行操作。你对我有个好建议吗?
答案 0 :(得分:3)
如果您认为d
不是一个因素
y = x * d
但是
y `mod` d == 0,
然后,您可以从列表[1..]
中获取列表推导,并添加谓词函数,例如:
function ds
| null ds = [1..]
| otherwise = [ x | x <- [1..], qualifies x ]
where
qualifies x = any (==0) $ (flip mod) <$> ds <*> [x]
更具表现力的版本,一开始可能更容易理解:
function' ds
| null ds = [1..]
| otherwise = [ x | x <- [1..], divByAnyIn ds x ]
where
divByAnyIn ds x =
case ds of
(d:ds') -> if x `mod` d == 0 then True
else divByAnyIn ds' x
_ -> False
答案 1 :(得分:2)
我只有一支班轮。
import Data.List (nub)
f xs = nub [x|x<-[1..], d<-xs, x `mod` d == 0]
take 10 $ f [3,5] -- [3,5,6,9,10,12,15,18,20,21]
从结果列表中,运行时应为O(n²+ n * d)。 nub 以O(n²)运行。摆脱它会很好。
g xs = [x |x<-[1..], let ys = map (mod x) xs in 0 `elem` ys]
这很好。它应该以O(n * d)运行。我也有这个版本,我认为它的性能至少和 g 一样好,但是显然它的性能比 f 好,但比 g 差。 / p>
h xs = [x |x<-[1..], or [x `mod` d == 0 |d<-xs] ]
我不确定为什么或是懒惰的,据我所知,它运行速度较慢的任何原因都没有。当您增加输入列表的长度时,它尤其不能很好地缩放。
i xs = foldr1 combine [[x, x+x ..] |x<- sort xs]
where
combine l [] = l
combine [] r = r
combine l@(x:xs) r@(y:ys)
| x < y = (x: combine xs r)
| x > y = (y: combine l ys)
| otherwise = (x: combine xs ys)
不再是一个班轮,但我能想到的最快的。我不能百分百地确定,如果您左右折叠并且预先对输入列表进行排序,为什么它对运行时有如此大的影响?但是,由于以下原因,它不会对结果产生影响:
commutative a b = combine [a] [b] == combine [b] [a]
我认为在将递归函数折叠到输入系数的倍数的无穷列表上时,完全想到这个问题是很疯狂的。
在我的系统上,它仍然比这里使用Data.List.Ordered提供的另一种解决方案慢大约10倍。
答案 2 :(得分:1)
如果要使用模函数,可以定义一个简单的单线
foo ds = filter (\x -> any (== 0) [mod x d | d <- ds]) [1..]
或更容易阅读的形式
foo ds = filter p [1..]
where
p x = any id [ mod x d == 0 | d <- ds]
= any (== 0) [ mod x d | d <- ds]
= not $ null [ () | d <- ds, mod x d == 0]
= null [ () | d <- ds, mod x d /= 0]
= null [ () | d <- ds, rem x d > 0]
有了这个,我们得到
> take 20 $ foo [3,5]
[3,5,6,9,10,12,15,18,20,21,24,25,27,30,33,35,36,39,40,42]
但是,它效率低下:last $ take 20 $ foo [300,500] == 4200
,因此要生成这20个数字,此代码将测试4200。数字越大,情况就越糟。
相反,我们应该及时产生n
个与n
大致成比例的数字。
为此,首先将每个数字的倍数写在自己的列表中:
[ [d*x | x <- [1..]] | d <- ds ] ==
[ [d, d+d ..] | d <- ds ]
然后合并这些有序的递增数字列表,以一种有序的方式生成一个有序的非递减数字列表。软件包data-ordlist
具有许多functions来处理这种列表:
import qualified Data.List.Ordered as O
import Data.List (sort)
bar :: (Ord a, Num a, Enum a) => [a] -> [a]
bar ds = foldr O.merge [] [ [d, d+d ..] | d <- ds ]
= O.foldt' O.merge [] [ [d, d+d ..] | d <- ds ] -- more efficient,
= O.mergeAll [ [d, d+d ..] | d <- sort ds ] -- tree-shaped folding
如果我们希望生成的列表不包含任何重复项,即创建一个递增列表,则可以将其更改为
baz ds = O.nub $ foldr O.merge [] [ [d, d+d ..] | d <- ds ]
= foldr O.union [] [ [d, d+d ..] | d <- ds ]
= O.foldt' O.union [] [ [d, d+d ..] | d <- ds ]
= O.unionAll [ [d, d+d ..] | d <- sort ds ]
= (O.unionAll . map (iterate =<< (+)) . sort) ds
哦,与二次Data.List.nub
不同,Data.List.Ordered.nub
是线性的,在输入列表的每个元素上花费了O(1)
时间。
答案 3 :(得分:1)
这里的答案仅显示了这个想法,它不是一个优化的解决方案,可能存在许多实现它的方法。
首先,从输入的列表中计算每个因子的所有值:
map (\d->[d*x|x<-[1..]]) xs
例如:xs = [3, 5]
给出
[[3, 6, 9, ...], [5, 10, 15, ...]]
然后,找到每个列表的第一个元素的最小值:
findMinValueIndex::[(Int, [Int])]->Int
findMinValueIndex xss = minimum $
map fst $
filter (\p-> (head $ snd p) == minValue) xss
where minValue = minimum $ map (head . snd) xss
一旦发现列表包含最小值,则将其返回并从列表中删除最小值,如下所示:
sortMulti xss =
let idx = findMinValueIndex $ zip [0..] xss
in head (xss!!idx):sortMulti (updateList idx (tail $ xss!!idx) xss
例如,在找到结果的第一个值(即3
)之后,查找下一个值的列表为:
[[6, 9, ...], [5, 10, 15, ...]]
重复以上步骤,我们可以构建所需的列表。最后,删除重复的值。这是完整的编码:
import Data.Sequence (update, fromList)
import Data.Foldable (toList)
function :: [Int] -> [Int]
function xs = removeDup $ sortMulti $ map (\d->[d*x|x<-[1..]]) xs
where sortMulti xss =
let idx = findMinValueIndex $ zip [0..] xss
in head (xss!!idx):sortMulti (updateList idx (tail $ xss!!idx) xss)
removeDup::[Int]->[Int]
removeDup [] = []
removeDup [a] = [a]
removeDup (x:xs) | x == head xs = removeDup xs
| otherwise = x:removeDup xs
findMinValueIndex::[(Int, [Int])]->Int
findMinValueIndex xss = minimum $
map fst $
filter (\p-> (head $ snd p) == minValue) xss
where minValue = minimum $ map (head . snd) xss
updateList::Int->[Int]->[[Int]]->[[Int]]
updateList n xs xss = toList $ update n xs $ fromList xss
答案 4 :(得分:1)
有一个很好的递归解决方案
function' :: Int -> [Int]
function' d = [d * x | x <- [1..]]
braid :: [Int] -> [Int] -> [Int]
braid [] bs = bs
braid as [] = as
braid aa@(a:as) bb@(b:bs)
| a < b = a:braid as bb
| a == b = a:braid as bs # avoid duplicates
| otherwise = b:braid aa bs
function :: [Int] -> [Int]
function ds = foldr braid [] (map function' ds)
braid
函数仅使用输入的标题和惰性即可“即时”构建所需列表