2016-12-24 21 views
5

System.Directory.getFileSize path IO, dizin içeriğinin boyutlarını ararken sorun mu yaşıyor? Bugün Haskell ve amacıma öğreniyorum

  • path ise, mantık

    • path bir dosya ise birlikte bir işlevi sizeOf :: FilePath -> IO Integer (Bir dosya veya klasörün boyutunu hesaplamak) yazmaktır bir dizin bir dosya veya dizin başka bir şey varsa, sonuçlar
    • yinelemeli, içeriğinin bir listesini almak onlara bu fonksiyonu çalıştırmak ve sum olduğunu return 0
    İşte

    Ben Ruby bunu uygulamak istiyorum göstermek için nasıl (Yakut notları: reduce :+foldl (+) 0 olduğunu map 'ın argümanı \d -> size_of d eşdeğerdir, ? döner bool biten herhangi bir işlev, döner örtülü olan):

    def size_of path 
        if File.file? path 
        File.size path 
        elsif File.directory? path 
        Dir.glob(path + '/*').map { |d| size_of d }.reduce :+ 
        end 
    end 
    
    benim problem nerede olduğunu biliyorum

    sizeOf :: FilePath -> IO Integer 
        sizeOf path = 
        do 
         isFile <- doesFileExist path 
         if isFile then 
         getFileSize path 
         else do 
         isDir <- doesDirectoryExist path 
         if isDir then 
          sum $ map sizeOf $ listDirectory path 
         else 
          return 0 
    

    :

    İşte Haskell ona benim çatlak var. listDirectory path bir IO [FilePath] ve bir FilePath döndüren sum $ map sizeOf $ listDirectory path. Ama ... Bunu çözerken herhangi bir çözüm hayal bile edemiyorum. yerine $ ait <$> Ben a -> b bir fonksiyonu bir Context a -> Context b haline gelmesine izin şey olduğu anlaşılan <$> beri aklıma gelen ilk şeydi. Ama ... sanırım öyle değil mi?

    Orada mantık çözmeye yaklaşık iki saat geçirdim. Diğer örneklerde denedim. İşte beni attı ilgili bir keşif: if sonra double = (*) 2, map double [1,2,3] == [2,4,6] ama map double <$> [return 1, return 2, return 3] == [[2],[4],[6]] ... Bu liste halinde sarar. Sanırım bu bana oluyor ama ben derinlikten çıkıyorum.

  • +1

    'Ama ... GÇ böyle değil?' Tam olarak böyle yani Eh, 'IO' bir monad ve bu nedenle funktoru olduğunu ama aradığınız şey muhtemelen (>> =) '- monadik hesaplamaları zincirlemenin bir yoludur. Sadece manga öğreticileri okumaya gitme! Bazı haskell kitaplarına gir. – user2847643

    +1

    'harita çift <$> [1,2,3]' çok garip bir şey yapmadıkça bir tür hatadır. –

    +0

    Oh iyi yakalamak @DanielWagner. Ben harita çift <$> [dönüş 1, dönüş 2, dönüş 3] 'yazmak istedim. 'IO' bağlamında 'return' değerinin sarıldığını duymuştum, bu yüzden 'IO Integer' üzerindeki işlemleri denemek ve test etmek için kullanıyordum. Ama sanırım kafam karıştı, 'x dönüşü bir' Monad x' yapmak gibi görünüyor, ve eğer bir blokta iseniz sadece bir 'IO x' gibi görünüyor? Belki de monad'lara girmeden öğrenebildiğim sınırın üstündeyim (öğretmenim son noktaya kadar gitmeyi tavsiye etti) ... – GreenTriangle

    cevap

    5

    Sen

    sum <$> (listDirectory path >>= mapM sizeOf) 
    

    Açıklama gerekir:

    • bir IO [Integer] üzerinde sum kullanmak fikri Tamam, bu yüzden böyle bir şeyi almak gerekir.
    • listDirectory path
    • bize IO [FilePath] verir, bu yüzden sizeOf her dosya yolu geçmesi gerekiyor. Bu mapM birlikte >>= yaptığı iştir. Tek başına map bize [IO Integer] verecekti
    • Not biz neden ihtiyaç olduğunu nasıl (Control.Monad.Extra kullanarak) hakkında
    1

    mapM: Sana yolunu eklemem gerekiyor inanıyoruz

    du :: FilePath -> IO Integer 
    du path = ifM (doesFileExist path) 
           (getFileSize path) $ 
           ifM (doesDirectoryExist path) 
            (sum <$> (listDirectory path >>= mapM (du . 
              (addTrailingPathSeparator path ++)))) 
            (return 0) 
    

    listDirectory başarılı özyinelemeli iniş için listDirectory çıkışı sadece du sonraki çağrılar için gerekli olan yolun, olmadan dosya adlarını döndürür.

    ifM tipi muhtemelen açıktır, ama

    ifM :: Monad m => m Bool -> m a -> m a -> m a