2013-12-08 34 views
5

Sayıların bir listesi verilen bir işlev yazmaya çalışıyorum, son öğeden başlayarak her 2. sayının değer olarak ikiye katlandığı bir liste döndürür. Yani, eğer liste elemanları 1.n'dir, n-inci, olduğu gibi bırakılacak, (n-1) -th değerinden iki katına çıkacak, (n-2) -th bırakılacak gibi kullanıldı Yani burada vbHaskell - liste öğelerini nasıl ters sırayla zarif bir şekilde yinelemeli?

ben onu çözdüm nasıl:

MyFunc :: [Integer] -> [Integer] 
MyFunc xs = reverse (MyFuncHelper (reverse xs)) 

MyFuncHelper :: [Integer] -> [Integer] 
MyFuncHelper []  = [] 
MyFuncHelper (x:[]) = [x] 
MyFuncHelper (x:y:zs) = [x,y*2] ++ MyFuncHelper zs 

Ve çalışır:

MyFunc [1,1,1,1] = [2,1,2,1] 
MyFunc [1,1,1] = [1,2,1] 

ancak, elimde olmadan daha basit olmalı düşünemiyorum listeyi tersine çevirme, işleme koyma ve tekrar tersine çevirme işlemidir. Listeyi geriye doğru itebilir miyim? Evet ise nasıl?

+2

Sen elemanları saymak ve sonra her tek sayılı veya her çift sayılı eleman ya katına çıkarabilir. –

cevap

9

lens kütüphanesinden under reversed f xs deyim ters sırada xs f geçerli olacaktır:

under reversed (take 5) [1..100] => [96,97,98,99,100] 
+5

'Ters ters f xs', burada aynı sonucu verir ve' over' daha yaygın olarak kullanılan bir lens birleştirici olduğunu unutmayın. –

+0

Bu kesinlikle sorunu çözmek için en özlü yol gibi görünüyor. Burada kullanılan yöntemin Prelude'ye dahil edilmemesi utanç verici. – Zaroth

3

oldukça iyi genellikle ucundan foldr eser listeyi işlemek için ihtiyacım olduğunda. multiplyCond işlevi bayrak ve akümülatör listesini içeren bir tuple alır

doubleOdd :: Num a => [a] -> [a] 
doubleOdd = fst . foldr multiplyCond ([], False) 
    where multiplyCond x (rest, flag) = ((if flag then (x * 2) else x) : rest, not flag) 

: Burada iki kez tüm listeyi ters olmadan sizin için bir çözümdür. Bayrak, elemanı çarpıp çoğaltmamamız gerekip gerekmediğini izlemek için sürekli olarak açılıp kapanır. Akümülatör listesi sonuçta ortaya çıkan sayıları toplar. Bu çözüm çok özlü olmayabilir, ancak fazladan çalışmayı önler ve başlangıç ​​işlevlerinden başka hiçbir şey kullanmaz.

+0

Listenin ilk elemanını bu işlevde üretmek için, 'kat' listenin sonuna kadar tüm yolu tekrarlamalı ve her çiftin ikinci (boolean) öğesini zorlamalıdır. Bu yüzden orjinal listeyi tersine çevirmenin daha iyi olduğuna şüphem var. –

+0

@LuisCasillas Listenin sonuna gitmek kaçınılmazdır çünkü son maddeye ulaşmadan önce tüm öğelere bakılmaksızın erişilemez. Bununla birlikte, ortaya çıkan liste, ikinci geri alma operasyonu olmaksızın, halihazırda geri alınan elemanlardan hemen üretilmektedir. Belki iki tersine dönme tembelliği ile aynı sonucu elde eder, ama bundan tamamen emin değilim. – Malcolm

1
myFunc = reverse 
     . map (\(b,x) -> if b then x*2 else x) 
     . zip (cycle [False,True]) 
     . reverse 

Ancak bu daha iyi değil. Uygulamanız yeterince zariftir.

+5

Bunu kod-golf '' myFunc = tersine çevirebilir. zipWith (*) (döngü [1, 2]). reverse'' – bisserlis

0

Listeyi geriye doğru yinelemenin en kolay yolu listeyi geri çevirmektir. Bundan daha iyisini yapabileceğinizi sanmıyorum; Sonunu bulmak için bütün listeyi geçmek zorunda kalırsanız ve nasıl geri döneceğinizi hatırlarsanız, tersine çevirdiğinizden de emin olabilirsiniz. Bu büyük bir anlaşma ise, belki de listelerin yerine başka bir veri yapısı kullanıyor olmalısınız - Vector veya Seq iyi seçenekler olabilir.

  1. Kullanımı: Biz Foldable ve Traversable ile çılgın biraz giderseniz

    import Control.Monad.State 
    import Data.Traversable (Traversable, traverse) 
    
    toggle :: (Bool -> a -> b) -> a -> State Bool b 
    toggle f a = 
        do active <- get 
         put (not active) 
         return (f active a) 
    
    doubleEvens :: (Num a, Traversable t) => t a -> t a 
    doubleEvens xs = evalState (traverse (toggle step) xs) False 
        where step True x = 2*x 
          step False x = x 
    
    yourFunc :: Num a => [a] -> [a] 
    yourFunc = reverse . doubleEvens 
    

    Ya da, bu deneyebilirsiniz:

    yardımcın fonksiyonunu yazmak için başka bir yolu da Traversable kullanmaktır Foldablefoldl, örneklerinden herhangi birinden ters sıralı bir liste çıkarmak için. Bazı türler için bu liste tersine çevrilmekten daha verimli olacaktır.

  2. Daha sonra, orijinal yapının her bir öğesini ters sırayla eşine eşlemek için traverse ve State öğelerini kullanabiliriz.

İşte bunu nasıl açıklanmıştır:

import Control.Monad.State 
import Data.Foldable (Foldable) 
import qualified Data.Foldable as F 
import Data.Traversable (Traversable, traverse) 
import Data.Map (Map) 
import qualified Data.Map as Map 


toReversedList :: Foldable t => t a -> [a] 
toReversedList = F.foldl (flip (:)) [] 

reverse' :: Traversable t => t a -> t a 
reverse' ta = evalState (traverse step ta) (toReversedList ta) 
    where step _ = do (h:t) <- get 
         put t 
         return h 

yourFunc' :: (Traversable t, Num a) => t a -> t a 
yourFunc' = reverse' . doubleEvens 

-- >>> yourFunc' $ Map.fromList [(1, 1), (2, 1), (3, 1), (4, 1)] 
-- fromList [(1,2),(2,1),(3,2),(4,1)] 

-- >>> yourFunc' $ Map.fromList [(1, 1), (2, 1), (3, 1)] 
-- fromList [(1,1),(2,2),(3,1)] 

olsa muhtemelen bunu yapmak için daha iyi bir yolu var ...

0

func xs = zipWith (*) xs $ reverse . (take $ length xs) $ cycle [1,2]

İlgili konular