2012-07-29 9 views
25

Ich war eines Tages gelangweilt und wollte mein Gehirn trainieren, so entschied ich mich, die 99 Haskell Problems tun, sondern mich darauf beschränkt, sie in Punkt-freien Stil zu tun. Ein Problem, das bei punktlosen Arbeiten häufig auftaucht, ist folgendes: Wie wenden Sie mehrere Funktionen auf denselben Wert an, während Sie jedes Ergebnis als unabhängige Einheit beibehalten? Spitz Notation:Anwenden mehrerer Funktionen auf den gleichen Wert Punkt-freien Stil in Haskell

foobar x = [id x, reverse x] 

Und was ich mit so weit in Punkt freie Notation kommen:

foobar' = `map` [id, reverse] ($ x) 

kann ich nicht, dass x ab Ende dorthin zu kommen scheinen.

Antwort

25

Andere haben bereits gebucht, wie Sie dies den Reader Monade mit tun kann, aber das ist nicht der einzige Weg. Es stellt sich heraus, dass deine zweite Funktion ziemlich nah ist. Ich glaube, Sie

foobar' x = (`map` [id, reverse]) ($ x) 

schreiben bedeutete Da die x bereits in der Nähe einer Position am weitesten rechts ist, Sie sind fast da.Zuerst verwandelt den Abschnitt ($ x) in eine Funktion, weil es ein bisschen einfacher, mit zu arbeiten:

-- by the definition of a right operator section 
foobar'2 x = (`map` [id, reverse]) (\y -> ($) y x) 

Nächstes entfernen Sie die x aus dem Lambda-Körper durch eine neue Variable in Rahmen zu bringen und Anwendung die Funktion auf x

-- lambda abstraction I think... 
foobar'2 x = (`map` [id, reverse]) $ (\z y -> ($) y z) x 

Rewrite diese Anwendung als Funktion Zusammensetzung, und dann können Sie eta reduzieren:

-- by definition of '.' 
foobar'3 x = (`map` [id, reverse]) . (\z y -> ($) y z) $ x 

-- eta reduction 
foobar'4 = (`map` [id, reverse]) . (\z y -> ($) y z) 

Beachten Sie, dass wir das Lambda durch eine Funktion

-- by definition of `flip` 
foobar'5 = (`map` [id,reverse]) . flip ($) 

ersetzen können, und Sie haben eine Point-freie Form.

8

Sie werden in der Applicative Instanz des Lesers Monade interessieren:

instance Applicative (e ->) 

es Verwenden Sie einfach ein Argument verteilen können:

liftA2 (+) sin cos 3 

Hier sin und cos Funktionen sind, die beide erhalten der Wert 3. Die einzelnen Ergebnisse werden dann unter Verwendung von (+) kombiniert. Sie können dies weiter mit der Category Instanz von (->) kombinieren, aber von spezialisierten Versionen von (.) und id sind bereits in der Prelude definiert.

Hintergrund: Die Applicative Instanz für (e ->) stellt wirklich die SKI Kalkül, wo (<*>) die S combinator und pure ist die K combinator. S genau ein Argument zu verteilen, um zwei Funktionen verwendet:

S f g x = f x (g x) 

es eine Funktion Anwendung nimmt (fg) und macht sowohl abhängig von dem Wert x ((fx) (Gx)).

9

Verwendung sequence:

> let foobar' = sequence [id, reverse] 
> foobar' "abcde" 
["abcde","edcba"] 
+0

Nur wenn Sie mit den Einschränkungen in Ordnung sind. Dies wird nicht für alle Anwendungen funktionieren. –

+0

@ ThomasM.DuBuisson: welche Einschränkungen? –

+0

@BenMillwood Ich beziehe mich auf die Einschränkungen der Typklasse. JohnLs Antwort ist vom Typ 'a -> [a]'. Diese Antwort ist, obwohl sie schön und sauber ist, vom Typ 'Monade ((-> a) => a -> [a]'. –

5

Es gibt einige grundlegende idiomatische Kombinatoren, die wiederholt auftauchen und mit verschiedenen höheren Konzepten und Bibliotheken neu implementiert werden, die aber im Wesentlichen sehr einfach sind. Namen können variieren, und einige sind implementierbar in Bezug auf die anderen:

fork (f,g) x = (f x, g x)    -- == (f &&& g) 
prod (f,g) x = (f $ fst x, g $ snd x) -- == (f *** g) 
pmap f (x,y) = (f x, f y)    -- == (f *** f) 
dup  x = (x,x) 

etc. Natürlich uncurry f (x,y) == f x y viel mit diesen verwendet wird, auch.

&&& und *** sind in Control.Arrow definiert, sowie first und second. Dann prod (f,id) == first f, prod(id,g) == second g etc. etc.

So Ihre foobar wird

foobar = (\(a,b)->[a,b]) . fork (id,reverse) 
     = (\(a,b)->[a,b]) . (id &&& reverse) 
     = (\(a,b)->[a,b]) . (id *** reverse) . dup 
     = join $ curry ((\(a,b)->[a,b]) . second reverse) 

Für die letzten Sie müssen auch Control.Monad und Control.Monad.Instances importieren. Siehe auch this question.


spät edit: auch mit Control.Applicative als in Antwort von ertes angedeutet,

 = (:) <*> ((:[]) . reverse) 
+0

auch,' (:) <*> (pure. reverse) '(das' ((->) r), [ ] 'Applicatives),' ([id, reverse] <*>) .pure' (das '[]' Applicative), 'sequence [id, reverse]' (das '((->) r)' Monad). –

Verwandte Themen