2016-01-02 12 views
8

Um Länge einer Liste zu berechnen, eine foldr verwenden, würde man etwas tun:Haskell-Funktion für Listenlänge

foldr (\_ acc -> acc + 1) 0 

baut auf der Idee, dass die Klappfunktion das zweite Argument erhöhen muss, kam ich up mit diesem (und es ist falsch):

foldr ((+1) . (flip const)) 0` 

Eine weitere Untersuchung des Typs zeigt dies:

(+1) . (flip const) :: Num (c -> c) => a -> c -> c 

Haskell higher order function to calculate length Es ist ein interessanter Kommentar auf dieser Seite, die ich wirklich nicht verstehen kann

foldr (((+1).).(flip const)) 0 

Kann mir jemand erklären, wie funktioniert eigentlich, dass Zusammensetzung arbeiten?

+0

Dies sieht so aus, als wäre es mit einem Tool erstellt worden, wie [pointfree] (https://hackage.haskell.org/package/pointfree) .... '(+1). (flip const) 'bedeutet im Grunde, dass man für jede Anwendung dieser Funktion eins addiert, unabhängig von dem ersten Parameter (dafür steht der Flip const). Es ist wie '(\ a b -> (b + 1))' – Arnon

Antwort

7

Zunächst konzentrieren wir uns, warum foldr ((+1) . (flip const)) 0 falsch ist. Sie möchten nur das zweite Argument erhöhen und das erste Argument vergessen. Semantisch ist das

\_ a -> a + 1 

Sie jedoch folgendes geschrieben:

(+1) . flip const 
= (+1) . (\_ a -> a) 
= \x -> (+1) . (\_ a -> a) $ x 
= \x -> (+1) $ (\_ a -> a) $ x 
= \x -> (+1) $ \a -> a 
= \_ -> (+1) (\a -> a) 
= const ((+1) (\a -> a)) 

Welches ist, warum Sie plötzlich Num (c -> c) benötigen, da Sie (+1) auf id anzuwenden versuchen.

Aber du eigentlich gemeint:

\_ a -> a + 1 
= \_ a -> (+1) a 
= \_ -> (+1) 
= const (+1) 

Schließlich wollen Sie das erste Argument vergessen und eine Funktion f auf dem zweiten verwenden. Alles, was Sie tun müssen, ist const f zu verwenden.

Die Zusammensetzung ((+1).).(flip const) ist allzu ausführlich und wahrscheinlich erzeugt durch pointfree:

((+1).).(flip const) 
= ((\x -> x + 1).) . (\a _ -> a) 
= \c -> ((\x -> x + 1).) . (\a _ -> a) $ c 
= \c -> ((\x -> x + 1).) $ \_ -> c 
= \c -> \f -> (\x -> x + 1) . f $ \_ -> c 
= \c -> (\x -> x + 1) . \_ -> c 
= \_ c -> (\x -> x + 1) $ c 
= \_ c -> c + 1 
+0

vielen dank @Zeta –

3

Das ist wirklich ein Kommentar, aber für eine zu lange viel.

Wenn Sie nicht mit seltsamen Zahlen wie faul Nat s es zu tun, die Sie wirklich wollen

length xs = foldl' (\acc _ -> 1 + acc) 0 xs 

machen diese sinnlos,

Wenn Sie möchten, können Sie die ursprüngliche foldl' Ausdruck verwandeln sich in eine foldr Form etwa so:

length xs = foldr go id xs 0 where 
    go _ r acc = r $! 1 + acc 

Kauen auf go,

go _ r acc = ($!) r $ (+) 1 acc 
go _ r = ($!) r . (+1) 
go _ r = (. (+1)) (($!) r) 
go _ = (. (+1)) . ($!) 
go = const ((. (+1)) . ($!)) 

Kauen auf length,

length = flip (foldr go id) 0 

setzen sie alle zusammen,

length = flip (foldr (const ((. (+1)) . ($!))) id) 0 

ich, für einen, finden diesen Punkt freier Form völlig undurchsichtig.

+2

'foldl '(flip ((+). Const 1)) 0' funktioniert auch. 'flip' geht nach' foldl' umgekehrte Reihenfolge von Argumenten zur Kombinationsfunktion, und '((+). f)' ist falten + map fusion; also ist es halblesbar. --- eine andere ist 'foldl '((. const 1). (+)) 0'. :) –

+0

Ich mag dich wirklich antworten! 'foltl '(const. (1+)) 0' ist definitiv das beste. Hier ist eine andere Version, die das Argument in const: 'foldl (on const (+1)) 0' inkrementiert – Alvin

Verwandte Themen