Задача понимания Хаскелла
Я пытаюсь изучить Haskell и списки понимания, но не могу найти решение по этому вопросу:
mylist = [x*y | x <- [1..], y <- [1..]]
После моих испытаний результат примерно такой
mylist = [1,2,3,4,5,...]
потому что в списке понимания, x
принимает значение 1
,а потом y
изменяет значение повторно.
Но моя цель - добиться другого назначения, чтобы получить следующий результат:
mylist = [1,2,2,4,3,3,6.....]
Я хочу, чтобы комбинации были смешанными, а не каждая отдельно, потому что у меня есть серьезная проблема, чтобы получить подходящий результат.
Я приведу более конкретный пример.
Я хочу список, который будет иметь все номера этой формы:
num = 2^x * 3^y
x
а также y
должен принимать все значения >= 0
,
Мой подход заключается в следующем:
powers = [2^x * 3^y | x <- [0..], y <- [0..]]
Но таким образом я беру полномочия только 3, потому что x
постоянно 0.
Я попробовал это
multiples = nub (merge (<=) powers2 powers3)
powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]
чтобы объединить разные, но опять же значения 6,12 и т. д. отсутствуют - результат таков:
mylist = [1,2,3,4,8,9,16,27,32,64,81...]
2 ответа
Код, который вы показываете,
multiples = nub (merge (<=) powers2 powers3)
powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]
эквивалентно
powers3 = [2^x * 3^y | x <- [0], y <- [0..]]
= [2^0 * 3^y | y <- [0..]]
= [3^y | y <- [0..]]
powers2 = [2^x * 3^y | y <- [0], x <- [0..]]
= [2^x * 3^0 | x <- [0..]]
= [2^x | x <- [0..]]
Таким образом, вы производите только силы 2 и 3, без смешанных кратных. Таким образом, в потоке гарантированно не будет дубликатов, а nub
не было необходимости. И конечно это неполно.
Но давайте посмотрим на это под другим углом. В комментариях было предложено создать двумерную сетку из этих чисел:
mults23_2D = [[2^x * 3^y | y <- [0..]] | x <- [0..]]
{-
1 3 9 27 81 ...
2 6 18 54 ...
4 12 36 108 ...
8 24 72 ...
16 ...
.......
-}
Теперь мы куда-то добираемся. По крайней мере, теперь ни один из них не пропущен. Нам просто нужно понять, как объединить их в один, увеличивающийся поток чисел. просто concat
конечно не подойдет. Нам нужно объединить их по порядку. Хорошо известная функция merge
делает это, если аргументы уже упорядочены, увеличивая списки.
Каждый произведенный ряд уже в порядке возрастания, но их бесконечно много. Не бойся, foldr
может сделать это. Мы определяем
mults23 = foldr g [] [[2^x * 3^y | y <- [0..]] | x <- [0..]]
-- foldr g [] [a,b,c,...] == a `g` (b `g` (c `g` (....)))
where
g (x:xs) ys =
Здесь это немного сложно. Если мы определим g = merge
у нас будет беглая рекурсия, потому что каждый merge
захочет узнать элемент head своего "правого" (второго) потока аргументов.
Чтобы предотвратить это, мы сразу создаем самый левый элемент.
x : merge xs ys
И это все.
Использование инструмента
Мне нужна была бесконечная декартова функция произведения. Бесконечная функция должна занимать диагонали таблицы. Пара шаблон диагонального обхода имеет вид
0 0 - 0 1, 1 0 - 0 2, 1 1, 2 0 - 0 3, 1 2, 2 1, 3 0
Мне нравятся симметрии, но паттерн подсчитывает вперед первой цифрой и назад второй, что при выражении в бесконечной функции
diag2 xs ys = [ (m,n) | i<- [1..], (m,n) <- zip (take i xs) (reverse.take i $ ys) ]
Бесконечное поколение просто возьмет любое количество, даже большое, чтобы работать с ним. Что может быть важно, так это выбор диагонального или треугольного числа для полного набора.
revt n
делает из вводимых вами треугольное число. Если вы хотите, чтобы 25 элементов вернули 7. вернет 28 параметр для
take
.
revt
и
tri
находятся
tri n = foldl (+) 1 [2..n]
revt n = floor (sqrt (n*2))
Изготовление и использование
taket
хорошо, пока вы не выучите первые 10 или около того треугольных чисел.
taket n xs = take (tri $ revt n) xs
Теперь, имея в наличии некоторые инструменты, мы применяем их (в основном 1) к проблеме.
[ 2^a * 3^b | (a,b) <- sort.taket 25 $ diag2 [0..] [0..]]
[1,3,9,27,81,243,729, 2,6,18,54,162,486, 4,12,36,108,324, 8,24,72,216, 16,48,144, 32,96, 64]
И это диагональ. Первая группа - 7 длинных, вторая - 6 длинных, предпоследняя - 2 длинных и последняя - 1 длинная.
revt 25
7.
tri 7
28 - длина выходного списка.