Ich kann mich meistens durch meine Haskell-Fragen stolpern, aber ich habe keine bessere Lösung für mein Problem gefunden.
Angenommen, ich habe eine Funktion f
, die fünf Parameter benötigt, und ich möchte eine Liste von teilweise angewendeten Funktionen erstellen, bei denen die ersten drei Parameter angewendet werden, die jedoch in jedem Element der Liste unterschiedlich sind.
Nehmen wir zum Beispiel an, dass f :: Num a => a -> a -> a -> b -> b -> c
und ich möchte mit [b -> b -> c]
als Typ des Ergebnisses enden. Eine der Funktionen könnte f 1 3 5
und eine andere möglicherweise f 6 4 2
sein.
Mit 1 Argument könnte ich einfach etwas wie
machen %Vor%, um f 1
, f 2
usw. zu erhalten, und mit 2 Argumenten konnte ich
Jetzt für 3 Argumente könnte ich tun
%Vor% aber das wird schrecklich schnell hässlich. Gibt es einen eleganteren (oder idiomatischen) Weg, dies zu tun (abgesehen davon, dass ich meine eigene "uncurry3" -Funktion zur Paarung mit zip3
mache)? Ich habe immer eine elegante Lösung mit Haskell gefunden, und das scheint sehr ungeschickt.
Es tut uns leid, wenn dies eine neue Frage ist oder bereits beantwortet wurde. Danke.
Sie können Ihren 2-Argument-Code mit zipWith
verkürzen:
Und praktisch gibt es tatsächlich eine zipWith3
(und so weiter bis zu 7), die in der Standardbibliothek definiert sind.
Es gibt auch parallele Listen-Comprehensions mit -XParallelListComp
, die auf eine beliebige Zahl ansteigen:
Dies ist eigentlich eine Möglichkeit, eine anwendungsspezifische Instanz für Listen zu definieren.
Erinnern Sie sich daran, dass die Definition von Applicative sich auf die Definition von (<*>)
bezieht:
Und wenn Sie sich auf []
spezialisieren, erhalten Sie:
Vielleicht sieht das so aus, als könntest du dies erreichen? Sie haben eine Liste von Funktionen, und Sie können sie auf eine Liste von Werten anwenden. Vielleicht können wir (<*>)
auf eine Art arbeiten lassen, so dass es die Liste der Funktionen auf die Werteliste wie eine Zip-Datei anwendet:
Rufen Sie ($)
, die Funktion Anwendungsoperator, zurück:
So zipWith
"zippt" eine Liste von Funktionen und eine Liste von Werten und gibt das Ergebnis der Anwendung jeder Funktion auf den entsprechenden Wert zurück.
Ich denke, Sie sollten es wahrscheinlich von hier aus tun können. Lassen Sie uns zwei Listen hinzufügen:
%Vor%was zu
wird %Vor%was zu
wird %Vor%und
%Vor%Wie wäre es mit einer Drei-Argumente-Funktion?
%Vor%Ordentlich!
Es ist nicht schwer zu sehen, dass Sie dies auf beliebige Funktionen erweitern können, indem Sie einfach ein anderes (<*>)
übernehmen.
Außerdem können wir einen passenden Alias für fmap
mit der richtigen Fixität definieren und ihn (<$>)
nennen, und auch (<*>)
definieren, um die korrekte Fixität zu haben, um keine Klammern zu benötigen, und wir können etwas wie
Was ist ordentlich, oder? Jetzt können Sie im Grunde eine zipWithN
... zipWith
mit so vielen Argumenten wie Sie wollen!
Leider hat die standardmäßige Applicative-Instanz für []
dieses Verhalten nicht; es verhält sich in einer Weise, die mit seiner Monad-Instanz übereinstimmt. Um dies zu umgehen, verwenden wir normalerweise einen Wrapper vom Typ newtype, um verschiedene Instanzen für denselben Typ definieren zu können. In den Standardbibliotheken ist in Control.Applicative
der Wrapper des neuen Typs ZipList
:
So können wir das obige in echtem Haskell wie folgt machen:
%Vor%Was etwas unglücklicher ist als die ursprüngliche Version, leider - und ein bisschen mehr als
%Vor%Aber der "Vorteil" ist, dass Sie grundsätzlich willkürlich fixieren können "heben":)
Das eigentliche Ding, das hier weggenommen werden muss, ist, dass dies "genau die Art von Muster" ist, die Applicative erfunden hat, um es zu lösen; Es ist ein sehr häufiges Muster / eine Domäne, in der / dem Applicative besonders gut gedeiht, und es wäre vielleicht schön, mit der Entwicklung einer Intuition zu beginnen, um die verräterischen Anzeichen für ein Problem zu erkennen, das sich gut eignet eine anwendungsbezogene Lösung.