Как работает этот кусок запутанного кода Хаскелла?



во время чтения http://uncyclopedia.wikia.com/wiki/Haskell (и игнорируя все "оскорбительные" вещи), я наткнулся на следующий кусок запутанного кода:



fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))


когда я запускаю этот кусок кода ghci (после импорта Data.Function и Control.Applicative),ghci выводит список всех степеней 2.



как работает этот кусок кода?

428   2  

2 ответов:

для начала, у нас есть прекрасное определение

x = 1 : map (2*) x

что само по себе немного галлюциногенный, если вы никогда не видели его раньше. Во всяком случае, это довольно стандартный трюк лени и рекурсии. Теперь мы избавимся от явной рекурсии с помощью fix, и точка-бесплатно-ify.

x = fix (\vs -> 1 : map (2*) vs)
x = fix ((1:) . map (2*))

следующее, что мы собираемся сделать, это расширить : раздел и сделать map излишне сложной.

x = fix ((:) 1 . (map . (*) . (*2)) 1)

Ну, теперь у нас есть две копии эта константа 1. Это никогда не будет делать, поэтому мы будем использовать приложение для чтения, чтобы де-дублировать это. Кроме того, композиция функций-это немного мусор, поэтому давайте заменим это на (<$>) везде, где мы можем.

x = fix (liftA2 (.) (:) (map . (*) . (*2)) 1)
x = fix (((.) <$> (:) <*> (map . (*) . (*2))) 1)
x = fix (((<$>) <$> (:) <*> (map <$> (*) <$> (*2))) 1)

далее: этот звонок map слишком читаемым. Но бояться нечего: мы можем использовать законы монады, чтобы немного расширить его. В частности, fmap f x = x >>= return . f, так что

map f x = x >>= return . f
map f x = ((:[]) <$> f) =<< x

мы можем указать-free-ify, заменить (.) С (<$>), а затем добавить некоторые паразитные разделы:

map = (=<<) . ((:[]) <$>)
map = (=<<) <$> ((:[]) <$>)
map = (<$> ((:[]) <$>)) (=<<)

подставляя это уравнение в нашем предыдущем шаге:

x = fix (((<$>) <$> (:) <*> ((<$> ((:[]) <$>)) (=<<) <$> (*) <$> (*2))) 1)

наконец, вы нарушаете пробел и создаете замечательное окончательное уравнение

x=fix(((<$>)<$>(:)<*>((<$>((:[])<$>))(=<<)<$>(*)<$>(*2)))1)

писал длинный ответ с полным прогоном моих IRC-журналов экспериментов, ведущих к окончательному коду (это было в начале 2008 года), но я случайно весь текст :) не так много потерь, хотя - по большей части анализ Даниэля находится на месте.

вот с чего я начал:

Jan 25 23:47:23 <olsner>        @pl let q = 2 : map (2*) q in q
Jan 25 23:47:23 <lambdabot>     fix ((2 :) . map (2 *))

различия в основном сводятся к порядку, в котором происходили рефакторинги.

  • вместо x = 1 : map (2*) x Я начал с 2 : map ..., и я сохранил этот начальный 2 вплоть до самой последней версии, где я сжал в (*2) и изменил в конце . Шаг" сделать карту ненужно сложной " не произошел (так рано).
  • я использовал liftM2 вместо liftA2
  • скрытый map функция была введена перед заменой liftM2 с аппликативными комбинаторами. Вот тогда-то и исчезли все пробелы.
  • даже в моей "окончательной" версии было много . для функциональная композиция осталась. Замена всех этих с <$> по-видимому, произошло некоторое время между этим и uncyclopedia.

кстати, вот обновленная версия, которая больше не упоминает номером 2:

fix$(<$>)<$>(:)<*>((<$>((:[{- Jörð -}])<$>))(=<<)<$>(*)<$>(>>=)(+)($))

Comments

    Ничего не найдено.