2015-12-17 7 views
12

Ich habe eine Aufgabe in Haskell (nein, es ist nicht meine Hausaufgaben, ich lerne für die Prüfung).make-Funktion mit 'if' point-free

Die Aufgabe ist:

schreiben punktfreie Funktion numocc, die in bestimmten Listen Vorkommen von Element zählt. Zum Beispiel: numocc 1 [[1, 2], [2, 3, 2, 1, 1], [3]] = [1, 2, 0]

Dies ist mein Code:

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 und count sind 'Punkt frei', aber sie sind mit der Funktion addif, die nicht ist.

Ich habe keine Ahnung, wie ich die Funktion addif Punkt-frei machen kann. Gibt es eine Möglichkeit if Aussage Punkt-frei zu tun? Vielleicht gibt es einen Trick, der keine if verwendet?

+1

darf man Mathe-chenigans wie 'let fxy = 1 - ceiling (fromIntegral (xy)/fromIntegral y) :: Int'? (was nicht genau das ist, was du brauchst;)) – Carsten

+0

aber '1 - ceiling (abs $ fromIntegral (xy)/fromIntegral (max xy)) :: Int' sollte es tun, wenn ich irgendwo einen fiesen Grenzfall nicht vermisse - vielleicht Du wirst darüber nachdenken oder ein paar Quickchecks machen;) (Nun, ich habe ein paar Negative vermisst, also musst du doch mehr "abs" haben ... aber das Prinzip sollte offensichtlich sein ... die wirkliche Lösung ist * trivial * und links für den Leser **: D **) – Carsten

+3

Im Allgemeinen können Sie eine 'if'-Anweisung durch die Funktion' bool :: Bool -> a -> a -> a; bool Falsch f _ = f; bool True _ t = t', in diesem Fall können Sie immer einen "normalen" Ausdruck bilden, der mit den regulären Methoden point-free gemacht werden kann. – user2407038

Antwort

10

würde ich die Tatsache nutzen, dass man leicht ein Bool auf ein Int mit fromEnum umwandeln kann:

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

Nun können Sie die üblichen Tricks anwenden, um es frei Punkt-

-- 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 

Und so weiter. Ich werde nicht den ganzen Spaß davon nehmen, es frei zu machen, besonders wenn es Werkzeuge gibt, die es für dich tun.

Alternativ könnten Sie eine Funktion wie

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

schreiben, die fast frei ist Punkt, und es gibt Tricks, die Sie näher kommen, obwohl sie schnell ziemlich böse bekommen:

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

Here I denke, es sieht tatsächlich schöner aus, fmap anstelle von (.) zu verwenden, obwohl Sie alle fmap durch (.) ersetzen könnten und es genau derselbe Code wäre.Im Wesentlichen komponiert die (fmap fmap fmap) ein einziges Argument und zwei zusammen Argument Funktion, wenn Sie den Namen stattdessen geben .: Sie dies als

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

Gegliedert schreiben konnte:

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

So dauert es eine Funktion von b zu einem numerischen a, eine Liste von b s, und gibt eine a zurück, nicht so schlecht.

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

Und diese Art kann als Eq a => a -> (a -> Int) geschrieben werden, was eine wichtige Sache zu beachten ist. Das macht den Rückgabetyp dieser Funktion mit der Eingabe fmap fmap fmap sum map mit b ~ Int übereinstimmt, so dass wir sie zusammensetzen können, um eine Funktion des Typs Eq a => a -> [a] -> Int zu erhalten.

6

Ein Trick wäre, einen der vielen if functions, z. Data.Bool.bool 1 0 (auch gefunden in Data.Bool.Extras).

Ein arkaner Trick wäre die Verwendung Foreign.Marshal.Utils.fromBool, die genau das tut, was Sie hier brauchen. Oder das Gleiche, weniger geheimnisvoll: fromEnum (Danke @bheklilr).

Aber ich denke, der einfachste Trick wäre, einfach zu vermeiden, selbst zu zählen, und wenden Sie einfach die Standard length Funktion nach filter ing für die Nummer an.

+1

Warum nicht einfach 'fromEnum' anstelle der' Foreign.Marshal' Funktion verwenden? Es macht das gleiche und existiert bereits in 'Prelude'. – bheklilr

+0

@bheklilr: Uh, habe nicht gemerkt, dass 'Bool' eine Instanz von' Enum' ist - du hast dafür schon meinen upvote :-) 'fromBool' ist mir gerade in den Sinn gekommen, ich habe es irgendwo in der Vergangenheit gesehen ... – Bergi

8

warum nicht

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 

und dann die triviale eta-Kontraktion.

+1

Ich denke, das entspricht am meisten der beschriebenen Aufgabe. –

+0

@ AndrásKovács der Step-Back-Ansatz ... :) vgl. [dieser neue Lisp-Eintrag] (http://stackoverflow.com/questions/34207933/print-adjacent-duplicates-of-a-list-scheme) für einige komplizierte Äquivalente von map head. Filter (nicht.null.drop 1). Gruppe ". –

1

Mit der Enum Instanz für Bool, ist es möglich, eine pointfree Ersatz für zu bauen, wenn das kann in allgemeineren Fällen verwendet werden:

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

chk Mit wir eine andere Version von addIf definieren:

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

Jetzt können wir einfach ersetzen chk in addIf':

addIf :: Eq a => a -> a -> Int -> Int 
addIf = curry (flip (([snd,fst]!!) . fromEnum) ((+1),id) . uncurry (==)) 
0

Ich glaube, Sie suchen nach Data.Boolbool, die seit 4.7.0.0 (2014-04-08) existiert.

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

Die zusätzliche . ermöglicht == zwei Parameter zu nehmen, bevor der Ausdruck bool geben.

Da die Reihenfolge der Parameter unterschiedlich ist, müssen Sie incif wie folgt verwenden:

(flip . incif) 

(Integration dass inincif als Übung dem Leser überlassen wird [Übersetzung:. Es ist nicht trivial ist, und ich weiß noch nicht wie.;)