2015-12-17 14 views
12

ile işlev yapmak Haskell'de bir görevim var (hayır, benim ödevim değil, sınava giriyorum).'if' point-free

iştir: Verilen listelerde elemanın oluşumlarını sayar

Yaz noktadan serbest fonksiyonu numocc. Örneğin: numocc 1 [[1, 2], [2, 3, 2, 1, 1], [3]] = [1, 2, 0]

Bu benim kodudur:

addif :: Eq a => a -> Int -> a -> Int 
addif x acc y = if x == y then acc+1 else acc 

count :: Eq a => a -> [a] -> Int 
count = flip foldl 0 . addif 

numocc :: Eq a => a -> [[a]] -> [Int] 
numocc = map . count 

numocc ve count 'nokta-ücretsiz', ancak değil işlevi addif kullanıyor.

addif noktasını serbest olarak nasıl çalıştırabilirim konusunda hiçbir fikrim yok. if ifadesi point-free yapmanın bir yolu var mı? Belki de if'u kullanan bir numara var mı?

+1

matematik-chenigans yapmak için izin verdiniz gibi izin fxy = 1 - tavan (fromIntegral (xy)/fromIntegral y) :: Int'? (tam olarak ihtiyaç duyduğunuz şey tam olarak değil;)) – Carsten

+0

ama '1 - tavan (absBenIntegral (xy)/fromIntegral (max xy)) :: Int' bir yerdeki bazı kötü sınır vakalarını kaçırmadım eğer yapmalıyım - belki Bu konuda akıl yürütmek isteyeceksiniz ya da bazı hızlı kontroller yapacaksınız;) (bazı negatifleri özledim, böylece daha çok "abs" olmak zorunda kalacaksınız ... ama prensip açık olmalı ... gerçek çözüm * önemsiz * okuyucunun soluna **: D **) – Carsten

+3

Genel olarak 'if' ifadesini' bool :: Bool -> a -> a -> a; bool False f _ = f; bool True _ t = t', bu durumda her zaman normal yöntemlerle noktaya getirilebilecek "normal" bir ifade oluşturabilirsin. – user2407038

cevap

10

ben kolayca dönüştürebilirsiniz gerçeğini kullanmak istiyorsunuz bir BoolfromEnum kullanılarak Int bir etmek:

addif x acc y = acc + fromEnum (x == y) 

Şimdi bunu işaret ücretsiz hale getirme zamanki hileler uygulayarak başlayabilirsiniz

-- Go prefix and use $ 
addif x acc y = (+) acc $ fromEnum $ (==) x y 
-- Swap $ for . when dropping the last argument 
addif x acc = (+) acc . fromEnum . (==) x 

Ve bunun gibi. Özellikle de sizin için yapabilecekleri araçlar olduğunda, bu noktaya dikkat etmeden tüm eğlenceyi almayacağım.

Alternatif olarak, neredeyse bedava işaret edilmektedir

count x = sum . map (fromEnum . (==) x) 

gibi bir fonksiyon yazabiliriz ve hızla oldukça kötü olsun her ne kadar daha yakın almak hileler vardır:

count = fmap fmap fmap sum map . fmap fmap fmap fromEnum (==) 

Burada Her fmap'u (.) ile değiştirebilmemize rağmen fmap yerine fmap'u kullanmanın daha güzel görüneceğini düşünün ve tam olarak aynı kod olacaktır.

> :t fmap fmap fmap sum map 
Num a => (a -> b) -> [a] -> b 

Yani bir işlev alır: Esasen, (fmap fmap fmap) yerine Eğer aşağı Broken

count = (sum .: map) . (fromEnum .: (==)) 

olarak bu yazabiliriz .: adını verirseniz, birlikte tek bir argüman ve iki argüman fonksiyonunu oluşturan b'dan1 nolu bir a nolu listeye ve a numaralı bir değere döner, çok kötü değil.

> :t fmap fmap fmap fromEnum (==) 
Eq a => a -> a -> Int 

Ve bu tür nota önemli bir şeydir, Eq a => a -> (a -> Int) olarak yazılabilir. Bu fonksiyonun dönüş türünüile fmap fmap fmap sum map girdisi ile eşleştirerek, Eq a => a -> [a] -> Int türünde bir işlev elde etmek için bunları oluşturabiliriz. Biz addIf farklı bir sürümünü tanımlayabilirsiniz

chk :: Bool -> (a,a) -> a 
chk = ([snd,fst]!!) . fromEnum 

chk Kullanılması: Bool için Enum örneğini kullanarak

6

Tek bir numara, birçok if functions, örn. Data.Bool.bool 1 0 (Data.Bool.Extras da bulunmaktadır).

Daha fazla bilgi, Foreign.Marshal.Utils.fromBool numaralı telefonu kullanmaktır. Veya aynı şey, daha az arcane: fromEnum (teşekkürler @bheklilr).

Ama bence en basit numara, sadece kendinizin sayılmasından kaçınmak ve sadece filter numaralı numaradan sonra standart length işlevini uygulamaktır.

+1

Neden 'Foreign.Marshal' işlevi yerine' fromEnum' kullanılmıyor? Aynı şeyi yapar ve "Prelude" da zaten var. – bheklilr

+0

@bheklilr: Uh, 'Bool'un' Enum'un bir örneği olduğunu farketmedin - şimdiden benim için aldığım bu cevabı aldım '' from the Bob '' aklımdan geçti, geçmişte bir yerlerde görmüştüm… – Bergi

8

neden olmasın

numocc x 
    = map (length . filter (== x)) 
    = map ((length .) (filter (== x))) 
    = map (((length .) . filter) (== x)) 
    = map (((length .) . filter) ((==) x)) 
    = map (((length .) . filter . (==)) x) 
    = (map . ((length .) . filter . (==))) x 
    = (map . (length .) . filter . (==)) x 

ve sonra önemsiz eta-kasılma

.

+1

Bunun en çok tanımlanan görevle uyumlu olduğunu düşünüyorum. –

+0

@ AndrásKovács geri adım yaklaşımı ... :) cf. [Bu yeni Lisp girişi] (http://stackoverflow.com/questions/34207933/print-adjacent-duplicates-of-a-list-scheme) bazı haritaların eşdeğer eşdeğerleri için. süzgeç (not.null.drop 1). grup adı'. –

1

, o daha genel durumlarda kullanılıp kullanılamayacağını bir pointfree değiştirme oluşturmak mümkündür

addIf' :: Eq a => a -> a -> Int -> Int 
addIf' = curry (flip chk ((+1),id) . uncurry (==)) 

Şimdi biz sadece addIf' içinde chk değiştirebilirsiniz:

0

0.’un bool’u (4.7.0.0 (2014–04–08) yıllarından beri aradığınızı düşünüyorum.

incif :: (Eq a, Enum counter) => a -> a -> counter -> counter 
incif = ((bool id succ) .) . (==) 

ek .

bool ifadeyi geçirmeden önce, iki parametre alması == verir. Parametrelerin sıralaması farklı olduğundan

, böyle incif kullanmak gerekir:

(flip . incif) 

(incif içine okuyucuya bir alıştırma olarak bırakılmıştır olduğunu entegre [Çeviri:. Bu önemsiz değil, ve henüz nasıl bilmiyorum;])