Рекурсивные функции в haskell?

Я пытаюсь изучить Haskell и работал над проблемой книги по рекурсивным функциям.

> If   X_1 = 1 then X_2 = 1 + X_1 = 2, X_3 = 1 + X_1 + X_2
      or when it is 5,  X_5 = 1 + X_4 + X_3 + X_2 + X_1 = 16, and so forth. 

Я попытался сделать это на haskell:

test :: Int -> Int
test 1 = 1
test n = sum[test n .. test (n-1)]

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

0
Сумма списка с одним элементом выглядит странно.
добавлено автор ДМИТРИЙ МАЛИКОВ, источник
Стоит отметить, что [test 1 .. test n] не означает [тест 1, тест 2, тест 3 и т. Д. До теста n] ! Чтобы узнать, что происходит, попробуйте использовать нерекурсивную функцию, такую ​​как square x = x * x и посмотреть, что вы получаете за [square 1 .. square 5] , Затем попробуйте [square i | i <- [1..5]] и/или map square [1..5] .
добавлено автор yatima2975, источник
Стоит отметить, что [test 1 .. test n] не означает [тест 1, тест 2, тест 3 и т. Д. До теста n] ! Чтобы узнать, что происходит, попробуйте использовать нерекурсивную функцию, такую ​​как square x = x * x и посмотреть, что вы получаете за [square 1 .. square 5] , Затем попробуйте [square i | i <- [1..5]] и/или map square [1..5] .
добавлено автор yatima2975, источник
Стоит отметить, что [test 1 .. test n] не означает [тест 1, тест 2, тест 3 и т. Д. До теста n] ! Чтобы узнать, что происходит, попробуйте использовать нерекурсивную функцию, такую ​​как square x = x * x и посмотреть, что вы получаете за [square 1 .. square 5] , Затем попробуйте [square i | i <- [1..5]] и/или map square [1..5] .
добавлено автор yatima2975, источник
Не могли бы вы немного разобраться, я не понимаю, как x_5 = 16 ?
добавлено автор bereal, источник
@ChrisTaylor, тогда легко доказать, что x_i = 2 ^ (i-1) , рекурсия не требуется.
добавлено автор bereal, источник
@ChrisTaylor, тогда легко доказать, что x_i = 2 ^ (i-1) , рекурсия не требуется.
добавлено автор bereal, источник
@ChrisTaylor, тогда легко доказать, что x_i = 2 ^ (i-1) , рекурсия не требуется.
добавлено автор bereal, источник
Каково определение problem2 ?
добавлено автор Chris Taylor, источник
@bereal Я не думаю, что в этом суть упражнения.
добавлено автор Chris Taylor, источник
@bereal Я не думаю, что в этом суть упражнения.
добавлено автор Chris Taylor, источник
@bereal Схематически у вас есть x [i] = 1 + sum (x [1: i-1]) .
добавлено автор Chris Taylor, источник
@bereal Схематически у вас есть x [i] = 1 + sum (x [1: i-1]) .
добавлено автор Chris Taylor, источник
@bereal Схематически у вас есть x [i] = 1 + sum (x [1: i-1]) .
добавлено автор Chris Taylor, источник
@bereal Я не думаю, что в этом суть упражнения.
добавлено автор Chris Taylor, источник
Как реализован problem2 , вы показали нам только test ?
добавлено автор luqui, источник
Я повторно отредактировал вопрос
добавлено автор razshan, источник
Я повторно отредактировал вопрос
добавлено автор razshan, источник

5 ответы

Хорошее место для начала - со списком:

[ test i | i <- [1..5] ]

означает

[ test 1, test 2, test 3, test 4, test 5 ]

Посмотрите, можете ли вы решить это сейчас.

Не забудьте добавить 1!

5
добавлено
Сообщение rror не соответствует указанному вами коду.
добавлено автор Ingo, источник
@razshan, понимание списка дает вам список. Вам все равно нужно это суммировать ...
добавлено автор luqui, источник
Я получаю ту же ошибку, когда начинаю с списка: Происходит проверка: не может построить бесконечный тип t0 = [t0] в возвращаемом типе вызова «test». В выражении: test x. В выражении: [test x | x <- [1..5]]
добавлено автор razshan, источник

Хорошее место для начала - со списком:

[ test i | i <- [1..5] ]

означает

[ test 1, test 2, test 3, test 4, test 5 ]

Посмотрите, можете ли вы решить это сейчас.

Не забудьте добавить 1!

5
добавлено
Сообщение rror не соответствует указанному вами коду.
добавлено автор Ingo, источник
@razshan, понимание списка дает вам список. Вам все равно нужно это суммировать ...
добавлено автор luqui, источник
Я получаю ту же ошибку, когда начинаю с списка: Происходит проверка: не может построить бесконечный тип t0 = [t0] в возвращаемом типе вызова «test». В выражении: test x. В выражении: [test x | x <- [1..5]]
добавлено автор razshan, источник

Хорошее место для начала - со списком:

[ test i | i <- [1..5] ]

означает

[ test 1, test 2, test 3, test 4, test 5 ]

Посмотрите, можете ли вы решить это сейчас.

Не забудьте добавить 1!

5
добавлено
Сообщение rror не соответствует указанному вами коду.
добавлено автор Ingo, источник
@razshan, понимание списка дает вам список. Вам все равно нужно это суммировать ...
добавлено автор luqui, источник
Я получаю ту же ошибку, когда начинаю с списка: Происходит проверка: не может построить бесконечный тип t0 = [t0] в возвращаемом типе вызова «test». В выражении: test x. В выражении: [test x | x <- [1..5]]
добавлено автор razshan, источник

Эта часть вашего кода представляет собой диапазон Haskell

[test n .. test (n-1)]

Диапазоны работают, вычисляя левое число и правое число, а затем создавая список, содержащий все шаги от левого номера до нужного числа. Так:

[1 .. 6] --> [1,2,3,4,5,6]
[5 .. 9] --> [5,6,7,8,9]

Как вы можете видеть, шаг по умолчанию - 1, поэтому, если у вас есть левое число, превышающее право, вы получите пустой список:

[4 .. 3] --> []

В стороне, вы можете переопределить шаг по умолчанию, указав другой номер:

[1, 3 .. 6] --> [1,3,5] -- step is 2
[8, 6 .. 3] --> [8,6,4] -- step is -2

Как вы можете видеть, когда у вас есть другой шаг размером, чем 1, вы должны быть осторожны с тем, что входит в итоговый список. Это особенно важно для отрицательных шагов, и даже больше, если у вас есть нецелые шаги, такие как [1, 1.25, .. 2.1] . Вы почти никогда не должны генерировать список нецелых чисел, используя диапазон.

В вашем решении у вас есть линия

test n = sum[test n .. test (n-1)]

Согласно правилам диапазонов, это неизбежно пойдет не так. Когда программа пытается сделать список из диапазона, он пытается вычислить test n , так как это левый номер диапазона. Но это ни к чему не приводит, поскольку test n - это то, что эта целая строка пытается вычислить в первую очередь. Таким образом, у нас есть бесконечный цикл, и программа зависает.

Вы могли бы попытаться сделать

test n = sum[1 .. test (n-1)]

Это похоже на примеры, которые вы дали. Он начинается с 1 (который является test 1 ) и заканчивается test (n-1) . Но проблема в том, что эти значения находятся между ними. Поскольку диапазоны имеют один шаг, в итоге вы получаете:

[1 .. test (n-1)] --> [1,2,3, ......., test (n-1)]

это не то же самое, что

[test 1, test 2, test 3, .... , test (n-1)]

И поскольку диапазон может иметь только постоянный шаг, нет возможности получить эту последнюю строку с простым диапазоном, даже если вы переопределите шаг по умолчанию. Один намек на то, как решить это, - это отметить количество элементов в списке.

length [1 .. test (n-1)] --> test (n-1), 
  -- because [1,2,3] has 3 elements, [1,2,3,4] has 4 and so on

length [test 1, test 2, test 3, ....... , test (n-1)] --> n-1
  -- this is not quite Haskell syntax

Здесь путь Haskell состоит в том, чтобы составить список, который содержит правильное количество элементов, а затем преобразовать его, чтобы каждый элемент был правильным. Как создать список элементов (n-1) ? Просто:

[1..(n-1)]

Отсюда вы можете идти несколькими путями. Существует понимание списка из luqui:

[test x | x <- [1..(n-1)]]

Вы можете думать об этом как о том, как вывести каждый номер из диапазона, назначив его x , а затем применив функцию test к x , так что вы get [тест 1, тест 2, тест 3, ......., тест (n-1)] . Другой способ - использовать функцию map :

map test [1..(n-1)]

Я думаю об этом, применяя test к каждому элементу списка одновременно, но это точно так же, как и понимание списка, всего два способа взглянуть на него. Обратите внимание, что в обоих направлениях используется диапазон [1 .. (n-1)] .

Если вы используете любой из них вместо диапазона [test n .. test (n-1)] в вашем исходном коде, вы очень близки к решению. Единственное, чего не хватает, как напоминает luqui, заключается в том, чтобы не забыть добавить 1.

3
добавлено
@Razshan: Да, ошибка возникает, потому что в первой строке правая часть - это одно число, а во второй строке это список чего-то. Всегда полезно создавать объявления типа (например, test :: Int -> Int ) для ваших функций. Затем вы получите несколько более качественное сообщение об ошибке. Итак, вы знаете, что в последней строке вам нужно одно целое число. Что вам нужно сделать со списком, который у вас есть? Вам нужна сумма всех элементов, не так ли? Ну, у вас уже есть решение для этого в вашем исходном коде. Тогда не забудьте добавить 1 к этой сумме :) Надеюсь, это помогло.
добавлено автор Boris, источник
Благодарим вас за подробное объяснение. Я понимаю подход первого создания, а затем сопоставление диапазона с «тестом». Однако главной проблемой всегда были ошибки компилятора при создании списков/сопоставлений. Это код, который у меня есть с этой ошибкой: test 1 = 1 test n = map test [1 .. (n-1)] Ошибка: Происходит проверка: невозможно построить бесконечный тип: b0 = [b0]. Ожидаемый тип: a0 -> b0, Фактический тип: a0 -> [b0]. В первом аргументе «map», а именно «test». в выражении: map test [1 .. (n-1)] ^ Я понимаю, что я перехожу из целого числа в список целых чисел, но как это исправить?
добавлено автор razshan, источник

Эта часть вашего кода представляет собой диапазон Haskell

[test n .. test (n-1)]

Диапазоны работают, вычисляя левое число и правое число, а затем создавая список, содержащий все шаги от левого номера до нужного числа. Так:

[1 .. 6] --> [1,2,3,4,5,6]
[5 .. 9] --> [5,6,7,8,9]

Как вы можете видеть, шаг по умолчанию - 1, поэтому, если у вас есть левое число, превышающее право, вы получите пустой список:

[4 .. 3] --> []

В стороне, вы можете переопределить шаг по умолчанию, указав другой номер:

[1, 3 .. 6] --> [1,3,5] -- step is 2
[8, 6 .. 3] --> [8,6,4] -- step is -2

Как вы можете видеть, когда у вас есть другой шаг размером, чем 1, вы должны быть осторожны с тем, что входит в итоговый список. Это особенно важно для отрицательных шагов, и даже больше, если у вас есть нецелые шаги, такие как [1, 1.25, .. 2.1] . Вы почти никогда не должны генерировать список нецелых чисел, используя диапазон.

В вашем решении у вас есть линия

test n = sum[test n .. test (n-1)]

Согласно правилам диапазонов, это неизбежно пойдет не так. Когда программа пытается сделать список из диапазона, он пытается вычислить test n , так как это левый номер диапазона. Но это ни к чему не приводит, поскольку test n - это то, что эта целая строка пытается вычислить в первую очередь. Таким образом, у нас есть бесконечный цикл, и программа зависает.

Вы могли бы попытаться сделать

test n = sum[1 .. test (n-1)]

Это похоже на примеры, которые вы дали. Он начинается с 1 (который является test 1 ) и заканчивается test (n-1) . Но проблема в том, что эти значения находятся между ними. Поскольку диапазоны имеют один шаг, в итоге вы получаете:

[1 .. test (n-1)] --> [1,2,3, ......., test (n-1)]

это не то же самое, что

[test 1, test 2, test 3, .... , test (n-1)]

И поскольку диапазон может иметь только постоянный шаг, нет возможности получить эту последнюю строку с простым диапазоном, даже если вы переопределите шаг по умолчанию. Один намек на то, как решить это, - это отметить количество элементов в списке.

length [1 .. test (n-1)] --> test (n-1), 
  -- because [1,2,3] has 3 elements, [1,2,3,4] has 4 and so on

length [test 1, test 2, test 3, ....... , test (n-1)] --> n-1
  -- this is not quite Haskell syntax

Здесь путь Haskell состоит в том, чтобы составить список, который содержит правильное количество элементов, а затем преобразовать его, чтобы каждый элемент был правильным. Как создать список элементов (n-1) ? Просто:

[1..(n-1)]

Отсюда вы можете идти несколькими путями. Существует понимание списка из luqui:

[test x | x <- [1..(n-1)]]

Вы можете думать об этом как о том, как вывести каждый номер из диапазона, назначив его x , а затем применив функцию test к x , так что вы get [тест 1, тест 2, тест 3, ......., тест (n-1)] . Другой способ - использовать функцию map :

map test [1..(n-1)]

Я думаю об этом, применяя test к каждому элементу списка одновременно, но это точно так же, как и понимание списка, всего два способа взглянуть на него. Обратите внимание, что в обоих направлениях используется диапазон [1 .. (n-1)] .

Если вы используете любой из них вместо диапазона [test n .. test (n-1)] в вашем исходном коде, вы очень близки к решению. Единственное, чего не хватает, как напоминает luqui, заключается в том, чтобы не забыть добавить 1.

3
добавлено
@Razshan: Да, ошибка возникает, потому что в первой строке правая часть - это одно число, а во второй строке это список чего-то. Всегда полезно создавать объявления типа (например, test :: Int -> Int ) для ваших функций. Затем вы получите несколько более качественное сообщение об ошибке. Итак, вы знаете, что в последней строке вам нужно одно целое число. Что вам нужно сделать со списком, который у вас есть? Вам нужна сумма всех элементов, не так ли? Ну, у вас уже есть решение для этого в вашем исходном коде. Тогда не забудьте добавить 1 к этой сумме :) Надеюсь, это помогло.
добавлено автор Boris, источник
Благодарим вас за подробное объяснение. Я понимаю подход первого создания, а затем сопоставление диапазона с «тестом». Однако главной проблемой всегда были ошибки компилятора при создании списков/сопоставлений. Это код, который у меня есть с этой ошибкой: test 1 = 1 test n = map test [1 .. (n-1)] Ошибка: Происходит проверка: невозможно построить бесконечный тип: b0 = [b0]. Ожидаемый тип: a0 -> b0, Фактический тип: a0 -> [b0]. В первом аргументе «map», а именно «test». в выражении: map test [1 .. (n-1)] ^ Я понимаю, что я перехожу из целого числа в список целых чисел, но как это исправить?
добавлено автор razshan, источник
Haskell
Haskell
910 участник(ов)

https://combot.org/chat/-1001043143583 Ссылки на полезные ресурсы: https://ruhaskell.org/links.html ;