Как работает этот кусок запутанного кода Хаскелла?
во время чтения http://uncyclopedia.wikia.com/wiki/Haskell (и игнорируя все "оскорбительные" вещи), я наткнулся на следующий кусок запутанного кода:
fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))
когда я запускаю этот кусок кода ghci (после импорта Data.Function и Control.Applicative),ghci выводит список всех степеней 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