Bu karmaşık Haskell kodu parçası nasıl çalışır?


91

Https://en.uncyclopedia.co/wiki/Haskell'i okurken (ve tüm "saldırgan" şeyleri görmezden gelirken ), aşağıdaki karmaşık kod parçasına rastladım:

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

Bu kod parçasını içinde çalıştırdığımda ghci( Data.Functionve aldıktan sonra Control.Applicative), ghci2'nin tüm üslerinin listesini yazdırır.

Bu kod parçası nasıl çalışır?


3
Acaba cevap kibirli bir şekilde saldırgan olabilir mi ... eğer doğruysa, kabalıktan kaçınma çabalarınız düşünüldüğünde ironik.
Meredith

31
Ne denedin Denenmesi gereken bariz şeyler şunlardır: (a) yorumu kaldırmak, (b) kodu yeniden biçimlendirmek / yeniden girmek, (c) hangi Functor / Applicative / Monad örneklerinin kullanıldığını bulmak (muhtemelen tüm liste, ancak varsaymayın ..) Yeterince çılgın bir programcının tek bir kod satırında beş farklı Monad örneğini kullanmasını hiçbir şey engelleyemez), (d) olabildiğince basitleştirin. Sonra neyle bırakıldığına bir bak.
dave4420

10
Haskell, açık ara en sevdiğim programlama dilidir, ancak yine de uncyclopedia.wikia.com/wiki/Haskell beni çok güldürdü!
AndrewC


5
Birinin XYZ dilinde bulabileceği en gereksiz şifreli kod parçasını bulması ve ardından "XYZ dilinde okunabilir kod yazmanın neredeyse imkansız" olduğunu iddia etmesi beni gerçekten rahatsız ediyor. Ama bu sadece benim ...
MathematicalOrchid

Yanıtlar:


219

Başlangıç ​​olarak, güzel tanımımız var

x = 1 : map (2*) x

eğer daha önce hiç görmediyseniz, kendi başına biraz kafa karıştırıcıdır. Her neyse, bu oldukça standart bir tembellik ve özyineleme numarasıdır. Şimdi, fixve point-free-ify kullanarak açık özyinelemeden kurtulacağız .

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

Sonraki yapacağımız şey :bölümü genişletmek ve mapgereksiz yere karmaşık hale getirmek .

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

Şimdi bu sabitin iki kopyasına sahibiz 1. Bu asla işe yaramayacak, bu yüzden bunu kopyalamak için okuyucu uygulamalarını kullanacağız. Ayrıca, işlev bileşimi biraz saçmadır, bu yüzden (<$>)onu elimizden geldiğince değiştirelim .

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

Sıradaki: bu çağrı mapçok fazla okunabilir. Ancak korkulacak bir şey yok: Monad yasalarını biraz genişletmek için kullanabiliriz. Özellikle fmap f x = x >>= return . f, bu yüzden

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

Biz, serbest-ify işaret yerine (.)birlikte (<$>), ve sonra bazı sahte bölümler eklemek:

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

Bu denklemi önceki adımımızda değiştirerek:

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

Sonunda, boşluk çubuğunuzu kırarsınız ve harika son denklemi oluşturursunuz

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

4
Sen çıktın {- thor's mother -}!
Simon Shine

13

Nihai koda götüren deneylerin IRC günlüklerimin tam olarak gözden geçirilmesiyle uzun bir cevap yazıyordum (bu 2008'in başındaydı), ama yanlışlıkla tüm metni :) O kadar da büyük bir kayıp değil - için Daniel'in analizinin çoğu yerinde.

İşte başladığım şey:

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 *))

Farklılıklar çoğunlukla yeniden düzenlemelerin gerçekleştiği sırayla ilgilidir.

  • Bunun yerine, x = 1 : map (2*) xile başladım 2 : map ...ve bu ilk 2'yi son versiyona kadar tuttum, burada a (*2)ile sıkıp $2sonunda $1. "Haritayı gereksiz şekilde karmaşık hale getirin" adımı (o kadar erken) gerçekleşmedi.
  • LiftA2 yerine liftM2 kullandım
  • Karartılmış mapfonksiyon Uygulamalı bağdaştırıcılarla ile liftM2 değiştirmeden önce konuldu. Aynı zamanda tüm boşluklar kayboldu.
  • Benim "son" sürümümde bile .işlev kompozisyonu için çok fazla şey kalmıştı. Bunların hepsini, <$>görünüşe göre bununla ansiklopedi arasındaki aylarda bir süre oldu.

BTW, işte artık numaradan bahsetmeyen güncellenmiş bir sürüm 2:

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

10
İlk paragrafta "silinmiş" kelimesinin çıkarılması kasıtlı mı? Eğer öyleyse şapkam çıkar.
Jake Brownson

3
@JakeBrownson Bu yaygın bir internet deyimidir , ancak bunun kasıtlı olup olmadığından da emin değilim.
Lambda Fairy

1

Her iki cevap da karmaşıklaştırılmış kod parçacığını maviden verilen kısa orijinalden türetir, ancak soru aslında uzun gizlenmiş kodun işini nasıl yaptığını sorar.

Bunu nasıl yapacağınız aşağıda açıklanmıştır:

fix$(<$>)<$>(:)<*>((<$>((:[{- thor's mother -}])<$>))(=<<)<$>(*)<$>(*2))$1 
= {- add spaces, remove comment -}
fix $ (<$>) <$> (:) <*> ( (<$> ((:[]) <$>) ) (=<<)  <$>  (*)  <$>  (*2) ) $ 1 
--                      \__\______________/_____________________________/
= {-    A   <$> B   <*> C                          $ x   =   A (B x) (C x) -}
fix $ (<$>) (1 :)     ( ( (<$> ((:[]) <$>) ) (=<<)  <$>  (*)  <$>  (*2) ) 1 )
--                      \__\______________/____________________________/
= {- op f g = (f `op` g) ; (`op` g) f = (f `op` g) -}
fix $ (1 :) <$>  ( (((=<<) <$> ((:[]) <$>) )        <$>  (*)  <$>  (*2) ) 1 )
--                  \\____________________/____________________________/
= {- <$> is left associative anyway -}
fix $ (1 :) <$>  ( ( (=<<) <$> ((:[]) <$>)          <$>  (*)  <$>  (*2) ) 1 )
--                  \__________________________________________________/
= {- A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$>  ( ( (=<<) <$> ((:[]) <$>)           .   (*)   .   (*2) ) 1 )
--                  \__________________________________________________/
= {- ((:[]) <$>) = (<$>) (:[]) = fmap (:[])  is a function -}
fix $ (1 :) <$>  ( ( (=<<)  .  ((:[]) <$>)           .   (*)   .   (*2) ) 1 )
--                  \__________________________________________________/
= {- (a . b . c . d) x = a (b (c (d x))) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)           (   (*)   (   (*2)   1 )))
= {- (`op` y) x = (x `op` y) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)           (   (*)   2             ))
= {- op x = (x `op`) -}
fix $ (1 :) <$>      (=<<)  (  ((:[]) <$>)              (2*)                  )
= {-  (f `op`) g = (f `op` g) -}
fix $ (1 :) <$>      (=<<)  (   (:[]) <$>               (2*)                  )
= {-  A <$> foo = A . foo when foo is a function -}
fix $ (1 :) <$>      (=<<)  (   (:[])  .                (2*)                  )
= {-  (f . g) = (\ x -> f (g x)) -}
fix $ (1 :) <$>      (=<<)  (\ x -> [2*x]  )
= {- op f = (f `op`)  -}
fix $ (1 :) <$>           ( (\ x -> [2*x]  )  =<<)

İşte ( (\ x -> [2*x]) =<<) = (>>= (\ x -> [2*x])) = concatMap (\ x -> [2*x]) = map (2*)bir işlev, yani yine <$> = .:

= 
fix $ (1 :)  .  map (2*)
= {- substitute the definition of fix -}
let xs = (1 :) . map (2*) $ xs in xs
=
let xs = 1 : [ 2*x | x <- xs] in xs
= {- xs = 1 : ys -}
let ys =     [ 2*x | x <- 1:ys] in 1:ys
= {- ys = 2 : zs -}
let zs =     [ 2*x | x <- 2:zs] in 1:2:zs
= {- zs = 4 : ws -}
let ws =     [ 2*x | x <- 4:ws] in 1:2:4:ws
=
iterate (2*) 1
= 
[2^n | n <- [0..]]

artan sırayla 2'nin tüm güçleridir .


Bu kullanır

  • A <$> B <*> C $ x = liftA2 A B C xve ona liftA2 A B Cuygulandığı için xbir işlev, bir işlev olarak anlam ifade ediyor liftA2 A B C x = A (B x) (C x).
  • (f `op` g) = op f g = (f `op`) g = (`op` g) f operatör bölümlerinin üç kuralı
  • >>=monadic bağdır ve o zamandan beri (`op` g) f = op f gve türleri

    (>>=)                :: Monad m => m a -> (a -> m b ) -> m b
    (\ x -> [2*x])       :: Num t   =>         t -> [ t]
    (>>= (\ x -> [2*x])) :: Num t   => [ t]               -> [ t]
    

    tip uygulaması ve ikamesi ile söz konusu monadın []hangisi için olduğunu görüyoruz (>>= g) = concatMap g.

  • concatMap (\ x -> [2*x]) xs olarak basitleştirilmiştir

    concat $ map (\ x -> [2*x]) 
    =
    concat $ [ [2*x] | x <- xs]
    =
             [  2*x  | x <- xs]
    =
             map (\ x ->  2*x )
    
  • ve tanım gereği,

    (f . g) x  =  f (g x)
    
    fix f  =  let x = f x in x
    
    iterate f x  =  x : iterate f (f x)
                 =  x : let y = f x in 
                        y : iterate f (f y)
                 =  x : let y = f x in 
                        y : let z = f y in 
                            z : iterate f (f z)
                 = ...
                 = [ (f^n) x | n <- [0..]]
    

    nerede

            f^n  =  f  .  f  .  ...  . f
            --     \_____n_times _______/
    

    Böylece

    ((2*)^n) 1  =  ((2*) . (2*) .  ...  . (2*)) 1
                =    2*  (  2*  (  ...  (  2*   1 )...)) 
                =    2^n   ,  for n in [0..]
    
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.