/ / Розуміння фібоначчі Хаскелла - хаскелл, розуміння списку

Розуміння Haskell у "Фібонс" - haskell, список-розуміння

fibs :: [Int]
fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)]

Це генерує послідовність Фібоначчі.

Я розумію поведінку охоронців :, zip і tail, але я не розумію <-. Що він тут робить?

Відповіді:

13 за відповідь № 1

Завдяки позитивним голосам я зробив свій коментар відповіддю.

Те, що ви бачите, не є охоронцем, але воно є усвідомлення списку. Для початку подумайте про це як про спосіб виразити aматематичні позначення набору, такі як A = {x | x елемент N}, що означає щось на зразок: Множина A - це множина всіх натуральних чисел. У розумінні списку це було б [x | x <- [1..] ].

Ви також можете використовувати обмеження на свої номери: [x | x <- [1..], x `mod` 2 == 0 ] та багато іншого.

Існує безліч хороших туроріалів Haskell, які охоплюють розуміння списку та навіть питання StackOverflow щодо ресурсів Haskell.


10 за відповідь № 2

Єдина хитра річ - zip fibs (tail fibs). zip просто робить попарний список з кожного з своїх аргументів. Отже, якщо у вас є два таких списки:

[ 1, 2, 3, 4 ]
[ "a", "b", "c", "d" ]

Застібнувши їх, ви отримаєте:

[ (1,"a"), (2,"b"), (3,"c"), (4,"d") ]

Стрілка вліво (присвоєння шаблону деструктуризації) просто витягує парні елементи, щоб їх можна було скласти. Два списки, які архівуються, є fibs і (tail fibs) - іншими словами, послідовність Фібоначчі тапослідовність Фібоначчі, зміщена на 1 елемент. Хаскелл оцінюється ліниво, тому він може обчислити список, скільки б не потрібно було багато елементів. Це стосується і поштового індексу.


3 для відповіді № 3

Давайте розширимо його.

zip створює пари із вмісту двох списків. Отже перша пара zip fibs (tail fibs) дає нам є (0, 1), що складає до 1. Отже, тепер список є [0,1,1]. Тепер ми знаємо три елементи у списку, тому розуміння списку можна продовжувати, захоплюючи наступний пункт зі списку та наступний елемент із хвоста, що дає (1,1) - складаються, складаючи 2. Тоді отримуємо наступну пару, яка є (1,2), роблячи наступне число в послідовності 3. Це може тривати нескінченно, оскільки розуміння завжди забезпечить достатню кількість елементів.


2 для відповіді № 4

Однією з переваг функціонального програмування є те, що ви можете оцінити вираз від руки, ніби це математична задача:

fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)]
= 0 : 1 : [ a + b | (a, b) <- zip [0, 1, ??] (tail [0, 1, ??])]

Ось тут ?? - частина, яка ще не оцінена. Ми будемо заповнювати його, як ми продовжуємо.

     = 0 : 1 : [ a + b | (a, b) <- zip [0, 1, ??] [1, ??])]
= 0 : 1 : [ a + b | (a, b) <- (0, 1) : zip [1, ??] [??]]

Зауважте, що я приховую оцінку zip оскільки його визначення тут не наводиться, а деталі насправді не є загальними для поточного питання. Це позначення, яке я буду використовувати, щоб показати кожну пару чисел, створену zip і поглинається розумінням списку.

     = 0 : 1 : 0+1 : [ a + b | (a, b) <- zip [1, ??] [??]]
= 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, ??] [??]]

Тепер ми знаємо, що наступний елемент у ?? це 1:

     = 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, 1, ??] [1, ??]]
= 0 : 1 : 1 : [ a + b | (a, b) <- (1, 1) : zip [1, ??] [??]]
= 0 : 1 : 1 : 1+1 : [ a + b | (a, b) <- zip [1, ??] [??]]
= 0 : 1 : 1 : 2 : [ a + b | (a, b) <- zip [1, ??] [??]]

І наступним елементом є 2:

     = 0 : 1 : 1 : 2 : [ a + b | (a, b) <- zip [1, 2, ??] [2, ??]]

Промити і повторити.


1 для відповіді № 5

Розуміння списку в дужках:

[ a + b | (a, b) <- zip fibs (tail fibs)]

повертає список, що містить вихідні дані (a + b), де змінні a і b походять з результату

zip fibs (tail fibs)

1 для відповіді № 6

Для чого це варто, мені здається, що наступну версію легше зрозуміти:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

0 для відповіді № 7

Він визначає цю діаграму потоку

           .----->>------->>----.
/                      
/                       /
                      /
<---- 0 <---- 1 ---<<--- (+)
/              
               
              /
*---->>------*

що витягує новий вхід із себе таким, яким він євиробляється, але завжди на одну і дві позиції перед виробничим пунктом, зберігаючи два "зворотні вказівники" в послідовності як би Це відображено у визначенні fibs = 0:1:[ a+b | a <- fibs | b <- tail fibs], з паралельним розумінням списку (:set -XParallelListComp тощо).

Оскільки він використовує лише свій останній два елементів, це еквівалентно

    map fst . iterate ((a, b) -> (b, a+b)) $ (0,1)

-1 для відповіді № 8

Як аналізатор міг би знати, що потрапляє в (а, б) інакше?

EDIT: Завдяки ViralShah я зроблю це трохи менш гномічним. Символ "<-" говорить парсеру призначити список пар з правого боку "zip fibs (tail fibs)" до лівого боку "(a, b)".


-1 для відповіді № 9

я все ще не розумію. мені подобається така відповідь: https://stackoverflow.com/a/42183415/246387 (від код-учень)

але я не розумію, як з цього рядка:

= 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, ??] [??]]

воно переходить до цього:

= 0 : 1 : 1 : [ a + b | (a, b) <- zip [1, 1, ??] [1, ??]]

і крім цього, у мене є ще щось, що мене турбує:

як я можу використовувати fib всередині списку-розуміння, якщо я цього не маю fib взагалі (так здається, але впевнений, що я "помиляюся), тому що fib ще не розраховано. він "чекає" (у лівій частині знака рівності) для обчислення в правій стороні (знака рівності).