Je lis la documentation ramda
const madd3 = R.lift((a, b, c) => a + b + c); madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
Cela semble être une fonction vraiment utile. Je ne vois pas quel serait un cas d'utilisation pour cela.
Merci
4 Réponses :
Cette fonction ne peut accepter que des nombres:
const results = add3Lifted([1, 10], [2], [3]);
Mais que se passerait-il si ces nombres étaient chacun contenus dans un foncteur? (c'est-à-dire une chose qui contient une valeur; un tableau dans l'exemple ci-dessous)
const results = [add3(1, 2, 3), add3(10, 2, 3)];
Ce n'est évidemment pas ce que nous voulons. Vous pouvez "soulever" la fonction pour qu'elle puisse "extraire" la valeur de chaque paramètre / foncteur:
add3Lifted([1, 10], [2], [3]); //=> [6, 15]
Les tableaux peuvent évidemment contenir plus d'une valeur et être combinés avec un fonction qui sait extraire les valeurs de chaque foncteur, vous pouvez maintenant faire ceci:
[ add3(1, 2, 3), // 6 add3(1, 2, 30), // 33 add3(1, 20, 3), // 24 add3(1, 20, 30), // 51 add3(10, 2, 3), // 15 add3(10, 2, 30), // 42 add3(10, 20, 3), // 33 add3(10, 20, 30) // 60 ]
Ce qui est essentiellement ce que vous auriez obtenu si vous aviez fait ceci: p>
add3Lifted([1, 10], [2, 20], [3, 30]); //=> [6, 33, 24, 51, 15, 42, 33, 60]
Notez que chaque tableau n'a pas besoin d'être de la même longueur:
const add3Lifted = lift(add3); add3Lifted([1], [2], [3]); //=> [6]
Donc, pour répondre à votre question: si vous avez l'intention d'exécuter une fonction avec différents ensembles de valeurs, la levée de cette fonction peut être une chose utile à considérer:
add3([1], [2], [3]); //=> "123"
équivaut à:
const add3 = (a, b, c) => a + b + c; add3(1, 2, 3); //=> 6
Fondamentalement, il prend un produit cartésien et applique une fonction à chaque tableau.
const cartesian = (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []), fn = ([a, b, c]) => a + b + c, result = [[1, 2, 3], [1, 2, 3], [1]] .reduce(cartesian) .map(fn); console.log(result); // [3, 4, 5, 4, 5, 6, 5, 6, 7]
Ce qui n'a pas été mentionné dans les réponses actuelles, c'est que des fonctions telles que R.lift
ne fonctionneront pas seulement avec des tableaux, mais aussi avec tout Apply 1 type de données.
Par exemple, nous pouvons réutiliser la même fonction produite par R. lift
:
const Asynchronous = fn => ({ run: fn, map: f => Asynchronous(g => fn(a => g(f(a)))), ap: other => Asynchronous(fb => fn(f => other.run(a => fb(f(a))))) }) const delay = (n, x) => Asynchronous(then => void(setTimeout(then, n, x))) lifted(delay(2000, 4), delay(1000, 6), delay(500, 8)).run(console.log)
Avec des fonctions comme type Apply:
const Just = val => ({ map: f => Just(f(val)), ap: other => other.map(otherVal => val(otherVal)), getOr: _ => val }) const Nothing = { map: f => Nothing, ap: other => Nothing, getOr: x => x } lifted(Just(4), Just(6), Just(8)).getOr(NaN) //=> 2 lifted(Just(4), Nothing, Just(8)).getOr(NaN) //=> NaN
Types facultatifs (distribution à .ap
):
lifted(a => a * a, b => b + 5, c => c * 3)(4) //=> 13
Types asynchrones (envoi vers .ap
):
const lifted = lift((a, b, c) => a + b - c)
La programmation fonctionnelle est un sujet long et mathématique, en particulier la partie traitant des monades et théorie de la cathegorie en général . Mais il vaut la peine d'y jeter un œil, voici un introduction amusante avec des images .
En bref, lift est une fonction qui prendra une fonction n-arguments et produira une fonction qui prend n wrapped-values et produit une autre valeur wrapped-value résultante. Un ascenseur qui accepte une fonction à un argument est défini par la signature de type suivante
<script src="https://cdn.jsdelivr.net/npm/ramda@0.26.1/dist/ramda.min.js"></script>
Wait ... Wrapped-value?
Je présenterai brièvement Haskell, uniquement pour l'expliquer. Dans haskell, un exemple simple de valeur encapsulée est Maybe , Maybe peut être une valeur encapsulée ou rien, c'est aussi une valeur encapsulée. L'exemple suivant applique une fonction à un Maybe contenant une valeur et un Maybe vide.
const add2 = (a, b) => a + b; const madd2 = R.lift(add2); const res = madd2([1,2,3], [2,3,4]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7] console.log(res); // Equivalent to lift using ap const result2 = R.ap(R.ap( [R.curry(add2)], [1, 2, 3]), [2, 3, 4] ); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7] console.log(result2);
La liste est également une valeur encapsulée, et nous pouvons lui appliquer des fonctions. Dans le second cas, liftA2 applique des fonctions à deux arguments à deux listes.
[a â b] â [a] â [b] Apply f => f (a â b) â f a â f b
Cette valeur encapsulée est une application Functor , donc à partir de maintenant je l'appellerai Applicative.
Peut-être que vous commencez se désintéresser de ce point ... Mais quelqu'un avant nous s'est perdu sur ce sujet, enfin il a survécu et l'a publié comme réponse à cette question .
Regardons ce qu'il a vu ...
...
Il a vu Fantasy Land
Dans fantasy-land, un objet implémente
Apply
a > spec quand il a une méthodeap
définie (cet objet doit également implémenterFunctor
spécification en définissant unecarte code> méthode).
Alors, attendez ... le Array en javascript a une carte ...
<script src="https://cdn.jsdelivr.net/npm/ramda@0.26.1/dist/ramda.min.js"></script>
Alors le Array est un Functor, et map applique une fonction à toutes ses valeurs, en renvoyant un autre Functor avec le même nombre de valeurs.
Mais que fait le ap faire?
ap applique une liste de fonctions à une liste de valeurs.
Envoie à la méthode ap du deuxième argument, s'il est présent. Également traite les fonctions curry comme des applicatifs.
Essayons de faire quelque chose avec.
const res = R.ap( [ (a)=>(-1*a), (a)=>((a>1?'greater than 1':'a low value')) ], [1,2,3]); //=> [ -1, -2, -3, "a low value", "greater than 1", "greater than 1" ] console.log(res);
[1,2,3].map((a)=>a+1) \\=> [ 2, 3, 4 ]
La méthode ap prend un tableau (ou un autre applicatif) de fonctions et l'applique à un applicatif de valeurs pour produire un autre applicatif aplati.
La signature de la méthode explique cela
> liftA (+ 8) [1,2,3] [9,10,11] > liftA2 (*) [1..3] [1..3] [1,2,3,2,4,6,3,6,9]
Enfin, que fait le lift?
Lift prend une fonction avec n arguments, et produit une autre fonction qui prend n Aplicatives et produit une Aplicative aplatie des résultats.
Dans ce cas, notre Applicative est le tableau.
> liftA (+ 8) (Just 8) Just 16 > liftA (+ 8) Nothing Nothing
// name :: f is a wrp-value => function -> wrp-value -> wrp-value liftA :: Applicative f => (a -> b) -> f a -> f b
Ces wrappers (Applicatifs, Functeurs, Monades) sont intéressants car ils peuvent être tout ce qui implémente ces méthodes. Dans haskell, ceci est utilisé pour encapsuler les opérations non sécurisées, telles que les entrées / sorties. Il peut également s'agir d'un wrapper d'erreur ou d'un arbre, voire de toute structure de données.
En général, lift
, ou plus précisément liftA2
équivaut à ap (map (x))
, et non à ap (ap (x) ))
. Ceci est donc spécifique à Ramda et s'écarte de la norme.
@reify: Ramda met en œuvre liftN code>
(et donc lift
) avec une map
initiale, réduisant les appels ap
sur le résultat.
Hmm, je pensais que nous avions nettoyé cette documentation. Un exemple plus clair serait
madd3 ([100, 200], [30, 40], [5, 6, 7]) // => [135, 136, 137, 145, 146, 147, 235, 236 , 237, 245, 246, 247]
. Les réponses données sont excellentes, mais consultez également stackoverflow.com/q/36558598/1243641 .