2013-03-21 27 views
15

Örneğin aşağıdaki düşünün: safeMapM (-O2 ile GHC 7.6.1 kullanarak) iyi iş gibi görünüyor ederken büyük listelerindeMapM/dizisi iyi uygulama örneklerini kullanıyor mu?

safeMapM f xs = safeMapM' xs [] 
    where safeMapM' []  acc = return $ reverse acc 
      safeMapM' (x:xs) acc = do y <- f x 
            safeMapM' xs (y:acc) 

mapM return largelist  -- Causes stack space overflow on large lists 
safeMapM return largelist -- Seems to work fine 

mapM kullanma yığın boşluk taşmasına neden olur. Ancak Haskell standart kütüphanelerinde safeMapM'a benzer bir işlev bulamadım.

mapM (veya bu konu için sequence) kullanmak hala iyi bir uygulama mı?
Eğer öyleyse, neden yığın boşluk taşması tehlikesine rağmen iyi uygulama olarak kabul edilir?
Eğer hangi alternatifi kullanmamanız öneriliyor?

+0

Belki 'mapM' taşma yapmazsa daha hızlıdır çünkü tersine çevirmek zorunda değilsiniz? Ölçdün mü? –

+0

Test etmek için kullandığınız 'Ana' modülünü gönderebilir misiniz? – jberryman

+4

Ayrıca, '' control.Monad.State.Lazy' gibi '' gibi bir şey '100 <$> mapM id [1 ..]' ı sonlandırır. '100 <$> güvenliMapM id [1 ..]', mononun –

cevap

9

Niklas B. olarak, mapM'un semantikleri, etkili bir sağdaki katlamalardır ve ters çevrilmiş bir sürümden daha fazla durumda başarılı bir şekilde sona erer. Genel olarak, mapM daha mantıklıdır, çünkü muazzam bir veri listesi üzerinde sonuç veren bir harita yapmak isteyeceğimiz nadirdir. Daha yaygın olarak, efektleri için böyle bir listeyi değerlendirmek isteriz ve bu durumda sonuçları ortadan kaldıran mapM_ ve sequence_ genellikle tavsiye edilenlerdir.

Düzenleme: başka bir deyişle, söz konusu yükseltilmiş meselesine rağmen evet, mapM ve sequence yaygın olarak kullanılan ve genellikle iyi uygulama olarak kabul edilir.

+1

Kodumun yalnızca "daha fazla durumda" değil, tüm durumlarda çalıştırılmasını istiyorum. Ve çok fazla etki, dönüş değerleri ile birlikte gelir. kullanıcı girişi. Geliştirirken yakalayabildiğim Niklas B. örnek, dağıtımdan sonra büyük listelere sahip yığın taşmaları olabilir. Imho – jonnydee

+2

'u bulmak çok daha zor farklı anlamlar ve farklı tradeoffs vardır. bu yüzden durumunuza uyan birini seçmeniz gerekiyor. cevap bu! tek cevap! Sağladığınız diğer kod da yeterince büyük listelerde başarısız olacaktır - sadece daha büyük listeler alacaktır. Ayrıca programa yığın alanı da ekleyebilirsiniz. Sadece, biliyorsun, anlaşmaları anla ve sonra bir seçim yap. – sclv

6

Eğer öyleyse, neden yığın boşluğu taşması tehlikesine rağmen iyi uygulama olarak kabul edilir? Eğer hangi alternatifi kullanmamanız öneriliyor? Eğer onlar üretilir olarak liste elemanlarını işlemek istiyorsanız

, pipes veya conduit birini kullanın. İkisi de asla bir ara liste oluşturmaz.

Bu, kitaplığım olduğu için pipes yolunu göstereceğim. Ben ilk kullanıcı girişi dan IO monad üretilen sayıların sonsuz liste ile başlayacağız:

import Control.Proxy 

infiniteInts :: (Proxy p) =>() -> Producer p Int IO r 
infiniteInts() = runIdentityP $ forever $ do 
    n <- lift readLn 
    respond n 

Şimdi, oluşturuldukları olarak yazdırmak istiyorum.

printer :: (Proxy p) =>() -> Consumer p Int IO r 
printer() = runIdentityP $ forever $ do 
    n <- request() 
    lift $ print n 

Şimdi Producer ve Consumer(>->) kullanarak bağlayın ve runProxy kullanarak sonuca çalıştırabilirsiniz:: o zaman ve kullanıcıdan Int s okuyacak

>>> runProxy $ infiniteInts >-> printer 
4<Enter> 
4 
7<Enter> 
7 
... 

yankı Bu bir mansap işleyici tanımlayan gerektirir belleğe tek bir öğeden daha fazla kaydetmeden oluşturuldukları için konsola geri dönerler.

Bu nedenle, genellikle bir öğe akışı oluşturan ve bunları hemen tüketen etkili bir hesaplama istiyorsanız, mapM'u istemezsiniz. Uygun bir akış kitaplığı kullanın.

pipes hakkında daha fazla bilgi edinmek isterseniz, reading the tutorial'u öneririm.

+0

Bu cevap biraz modası geçmiş. Güncellemek isteyebilirsiniz. – dfeuer

-1

Tembel kampta kalmak istiyorsanız, lazyio paketi giriş listesini tembel bir şekilde işlemenizi sağlar. Bunun yerine

mapM f 

arasında artık

import qualified System.IO.Lazy as LazyIO 

LazyIO.run . mapM (LazyIO.interleave . f) 

yok yığın taşması yazın.

İlgili konular