Если вы хотите сделать это с помощью функции по модулю, вы можете определить простой однострочный
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
имеет множество функций для работы со списками такого типа:
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)
времени на каждый элемент списка ввода.