Ошибка понимания вложенного списка в коде Haskell

Я пытаюсь написать следующее понимание списка в Haskell, и он не проверяет тип. Я новичок в этом и не могу понять, почему.

something :: Int -> [Int]
something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]

Вот что я вижу:

Couldn't match expected type `Int' with actual type `[t0]'
    In the expression: [2 * x + 1 | x <- xs]

NB: В этом фрагменте кода может быть гораздо больше ошибок.

Вот чему я действительно пытаюсь научиться. Из списка всех натуральных чисел от 3 до n (который является входом Int для функции) я хочу извлечь только подмножество чисел, которое можно записать как i+j+2*i*j, где i, j - целые числа и i‹=j и i>=1. К этому списку подмножеств я хочу применить функцию 2*x+1 к каждому элементу x и вывести окончательный список.
Надеюсь, это имеет смысл.


person atlantis    schedule 13.10.2011    source источник


Ответы (3)


Прежде всего, у вас есть понимание вложенного списка, поэтому вы создаете список списков, поэтому тип возвращаемого значения должен быть [[Int]] (список списков целых чисел), а не [Int] (список целых чисел).

Во-вторых, xs — это число (потому что вы берете его из списка чисел), но его название предполагает, что это список, и когда вы делаете x <- xs, вы на самом деле обращаетесь с ним так, как если бы это был список.

В ответ на ваше редактирование: я действительно не понимаю, почему вы думали, что для этого вам нужны вложенные списки. Если мы просто удалим вложенность из вашего кода, мы получим что-то, что довольно близко к рабочему (я также переименовал xs в x, потому что вызов числа xs просто сбивает с толку - я также удалил условие, что i по крайней мере 1 потому что это уже данность, так как вы берете i из списка [1..]):

[ 2 * x + 1 | x <- [3..n], i <- [1..],j <-[1..] ,x == i+j+2*i*j,i<=j]

Теперь это компилируется, но зациклится навсегда. Почему он зацикливается навсегда? Потому что вы берете i и j из бесконечных списков. Это означает, что он начнет с x=3, i=1, j=1, а затем перепробует все значения j от 1 до бесконечности, прежде чем попытается найти следующее значение i (другими словами, он никогда не будет пробовать следующее значение j). я).

Итак, что нам нужно сделать, так это задать верхние границы i и j. Простая верхняя граница для выбора — x (если i или j больше x (и ни одно из них не меньше 1), i+j+2*i*j не может быть равно x), поэтому вы получаете:

[ 2 * x + 1 | x <- [3..n], i <- [1..x],j <-[1..x], x == i+j+2*i*j, i<=j]

Это работает, но его все же можно несколько упростить: если мы возьмем j из списка [i..n] вместо [1..n], мы гарантируем, что j равно как минимум i, и нам больше не нужно условие i<=j, поэтому мы можем написать:

[ 2 * x + 1 | x <- [3..n], i <- [1..x], j <-[i..x], x == i+j+2*i*j]

PS: делать это таким образом (перебирать все x, а затем перебирать все возможные значения i и j для каждого x) немного неэффективно, поэтому вы можете пересмотреть свой подход. С другой стороны, если все, что вам нужно, это то, что работает, это нормально.

person sepp2k    schedule 13.10.2011
comment
Как я и подозревал, я облажался более чем одним способом. Я действительно хочу, чтобы [Int] наконец. Поэтому я думаю, что мне нужно сделать что-то помимо понимания. Я также вижу проблему с xs, являющимся числом. Пожалуйста, смотрите редактирование. - person atlantis; 14.10.2011

Во-первых, имя функции не должно быть в верхнем регистре.

xs <- [3..n] означает, что xs является Int, но x <- xs использует его как список.

Остальное понимание тоже выглядит несколько странно. Если вы хотите объяснить, что именно вы хотите сделать, мы могли бы помочь немного больше. :-)

[Редактировать]

Вы получаете бесконечный список ваших чисел, используя [i+j+2*i*j| j <-[2..], i<-[1..(j-1)]], но он не отсортирован. [x| x <-[3..(2*n*n)], j <-[2..n], i<-[1..(j-1)], x==i+j+2*i*j] дает отсортированный список всех таких чисел, меньших 2n².

person Landei    schedule 13.10.2011

Начнем с того, что у вас есть.

something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]

Основная проблема здесь в том, что вам не нужно понимание вложенного списка.

something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j, i>=1]

Это скомпилируется. Но, как вы подозреваете, в этом фрагменте кода гораздо больше неправильного.

Начнем с условий. Тестирование на i>=1 излишне, учитывая, что i <- [1..].

something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j]

Точно так же мы можем избавиться от состояния i<=j, если начнем j с i, а не с 1.

something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i..], x == i+j+2*i*j]

Должно быть ясно, что значения j больше (n - i) `div` (1 + 2 * i) не могут привести к xn.

something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]

Точно так же значения i из n `div` 3 или выше не могут привести к xn.

something n = [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1], j <- [i .. (n - i) `div` (1 + 2 * i)],
                            x == i+j+2*i*j]

На данный момент мы сделали достаточно, чтобы something действительно дал результаты. Но есть повторяющиеся значения (например, когда (i,j) равно (1,7) или (2,4), вы получаете x = 22), что, как я предполагаю, вам не нужно.

Мы отфильтровываем их, используя nub из Data.List.

something n = nub [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1],
                                j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]

Нет необходимости проверять, что x удовлетворяет условию, когда мы могли бы построить x для удовлетворения этого условия в первую очередь. (Вы все равно захотите убедиться, что 3 ≤ x ≤ n.) Это более эффективно.

something n = nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
                                let x = i+j+2*i*j]

Результаты больше не выводятся в порядке возрастания, так что давайте удостоверимся, что это так.

something n = sort $ nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
                                       let x = i+j+2*i*j]

С точки зрения стиля, удвоение и добавление единицы — это отдельный расчет от гарантии того, что x может быть выражен как i+j+2*i*j, поэтому давайте разделим их.

something n = sort $ map f $ nub [ x | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
                                   let x = i+j+2*i*j]
  where f x = 2 * x + 1

Это позволяет нам избавиться от x из понимания списка.

something n = sort $ map f $ nub [ i+j+2*i*j | i <- [1 .. (n `div` 3) - 1],
                                               j <- [1 .. (n - i) `div` (1 + 2 * i)]]
  where f x = 2 * x + 1

Сделанный.

person dave4420    schedule 13.10.2011