Задача понимания Хаскелла

Я пытаюсь изучить 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 - длина выходного списка.

Другие вопросы по тегам