Variables globales ? Où ça ?
Version imprimable
Variables globales ? Où ça ?
Ben, je n'ai pas vu un seul var/let, pour déclarer tes variables...
Pendant ce temps-là, j'ai encore réussi à raccourcir ma solution à 38 chars, avec un var ^^
Rha j'ai un truc qui fait 40 caractères mais qui ne marche pas avec arguments…
39 :D
Code:f=m=>(...a)=>b=>m.bind(...a).apply(8,b)
Désolé je ne trouve pas mieux que 39, je vais faire une pause et y revenir peut-être ce soir.
Au moins, ce défi m'a aidé à mieux comprendre l'opérateur ... que je maîtrisais mal :)
Et ton f alors? ^^
Je pense que c'est la meilleure, jusqu'ici... concernant la mienne, j'sais pas trop ce que je testais mais j'crois que j'me suis emmêlé les pinceaux dans mes versions... car au reboot du pc, l'une marchait avec array/arguments, l'autre avec des params bindés... :koi:
Pour parfaire la tienne, mais qui ne peut, non-plus, respecter le "no globals", sans dépasser la limite de taille en même temps, je propose celle-ci :
f=m=>(i,...a)=>b=>m.apply(i,a.concat(b))
Parfaire ? Ta version est plus longue, *peut mieux faire* :mouarf2: et on s'en fiche du f, c'est juste le nom donné à la fonction pour tester les exemples. Ça devrait même pas compter dans le décompte :langue:
Pour aller plus loin dans la recette du curry :
si F=>F.bind.bind(F) est la recette du curry en 2 temps, c'est à dire curry2(func)(...args1)(...args2) === func.call(...args1, ...args2)
- Quelle est la solution la plus courte pour le curry en 3 temps, c'est-à-dire curry3(func)(...args1)(...args2)(..args3) === func.call(...args1, ...args2, ..args3) ?
curry3("".substring)("hello world!")(6)(11) === "world";
- Quelle est le solution la plus courte pour le curry à n temps, c'est-à-dire curryN(n)(func)(...args1)(...args2) (...) (...argsN) === func.call(...args1, ..., ...argsN)
curryN(1)("".substring)("hello world", 6, 11) === "world";
curryN(2)("".substring)("hello world", 6)(11) === "world";
curryN(2)("".substring)("hello world")(6, 11) === "world";
curryN(3)("".substring)("hello world!")(6)(11) === "world";
curryN(3)("".substring)("hello world!",6)()(11) === "world";
curry curry ! curryyy ! :lahola:
Hum, oui, il y avait visiblement un truc que j'avais pas saisi avec le spread... merci aussi et bien joué :D
Vous oubliez un détail : ni le spread ni concat ne marchent avec un objet arguments.
C'est à cause de ça que j'ai pas pu me débarrasser de mon apply ;)Code:
1
2
3
4
5 (function () { var result = substringFromHello(arguments); console.log(result); console.assert("world" === result, "failed"); }(6, 11));
Si on oublie cette contrainte du arguments on peut descendre à 33 en reprenant la solution de Sylvain :
En effet rien ne nous oblige à distinguer le premier argument (anciennement c) du reste des arguments (r).Code:f=F=>(...p)=>a=>F.call(...p,...a)
Avec le test case de mon précédent post, j'obtiens cette erreur sous Firefox :
Il semblerait que le spread appelle en interne le même mécanisme d'itération que for..of. Dans SpiderMonkey tout du moins.Citation:
TypeError: a[Symbol.iterator] is not a function
Avec Chrome… Pas d'erreur. Ok, si je suis le seul ici à utiliser Firefox, je comprends mieux pourquoi personne n'avait remarqué ce problème avec arguments avant moi. Il s'agit d'un bug de Firefox.
Note au passage, les erreurs dûes au spread sous V8 sont plus difficiles à comprendre :
Code:(function (nonIterable) { console.log(...nonIterable); }(42))
En attendant, on est sur un double score de 33 (V8) / 39 (SpiderMonkey).Citation:
Uncaught TypeError: undefined is not a function(…)
Voilà voilà. Désolé. Vraiment.
Là ça me fait réfléchir. Qu'est-ce qui se passerait si on ne donnait pas le nombre de temps (l'arité) en paramètre ? A priori on ne saurait pas à quel moment renvoyer une fonction ou la valeur finale. Mais en utilisant un Proxy, je pense qu'on peut renvoyer un objet qui apparaît comme une valeur mais qui peut également se comporter comme une fonction.
Mais bref ^^ Je vais déjà tâcher de répondre à tes deux questions.
Ah ben oui c'est évident comme optimisation, je ne sais pas comment j'ai pu louper ça... Parfois on ne voit pas les choses qui sautent aux yeux.
L'opérateur spread gère bien les arguments de fonction, c'est d'ailleurs l'exemple de démo le plus couramment utilisé. Mais son implémentation est encore balbutiante sur les navigateurs. Mieux vaut tester le code transpilé par Babel.
Ce qui serait à la fois très très cool et très très idiot :mrgreen: Pour un usage "pratique" d'une telle fonction, l'arité est toujours prédéfinie. Sinon on ne sait plus ce qu'on manipule.
J'ai choisi de passer l'arité en argument initial parce que ça simplifie de mon point de vue le code la fonction derrière. On supposera qu'il s'agit toujours d'un nombre entier supérieur ou égal à 1.
Voilà ma solution :
Non minifié
curryN minifié:Code:
1
2
3
4
5
6
7
8
9
10 var curry3= function(F){ var n = 3; var argsStack = []; return function R(...args){ argsStack.push(...args); return --n ? R : F.call(...argsStack) } }
Code:var curryN = n=>(F,s=[],R=(...a)=>(s.push(...a),--n?R:F.call(...s)))=>R
Et une version encore plus récursive, mais pas forcément plus courte:
Code:var curryN = n=>(f,...s)=>(...a)=>--n?curryN(n)(f,...s,...a):f.call(...s,...a)
J'ai trouvé des trucs pour curryN. Curieusement, j'ai deux solutions légèrement différentes mais qui font la même longueur :
Ce qui montre que ce n'est pas forcément gagnant de remplacer function par var.Code:
1
2 n=>f=>{var r=(n,...a)=>n?(...b)=>r(--n,...a,...b):f.call(...a);return r(n)} n=>f=>function r(n,...a){return n?(...b)=>r(--n,...a,...b):f.call(...a)}(n)
Je n'ai pas pensé à utiliser les valeurs d'arguments par défaut du coup on peut peut-être trouver plus court en mélangeant nos deux versions.
Au fait j'ai essayé Babel, c'est impressionnant :) Mais il doit y avoir des trucs qu'il ne peut pas faire, non ?
J'pense avoir mieux, les gars...
Qu'en pensez-vous?Code:var c,curryN=c=i=>(f,...a)=>i?c(--i).bind(i,f,...a):f.call(...a)
En non-minifié :
Code:
1
2
3
4
5
6
7
8
9 var curryN; curryN = function (arity) { return function (fn, ...args) { return arity ? curryN(--arity).bind(arity, fn, ...args) : fn.call(...args); }; };
EDIT:
En bonus, par rapport aux vôtres, la mienne marche aussi avec arity = 0
Vous aimez l'épicé? J'crois que ça va vous plaire... ^^
Cette version a la même longueur que celle de SylvainPV... mais elle est réutilisable et supporte des usages supplémentaires :
DémoCode:var c,curryN=c=(...a)=>((i,f,...b)=>i--?c(i,f,...b):f.call(...b)).bind(a,...a)
Curry thaï? :lol:
EDIT :
Même si, perso, je trouverais plus logique et plus élégant que le arity = 0 ressemble à ceci :
Ce qui donnerait, en version courte ne permettant pas d'aliaser curryN :Code:curryN(0, "".substring, "hello world!", 6, 11)
DémoCode:var c,curryN=c=(i,f,...a)=>i?c.bind(i,--i,...(f?[f,...a]:a)):f.call(...a)
Ou en version longue, permettant d'aliaser curryN :
DémoCode:var c,curryN=c=(i,f,...a)=>i<1?f.call(...a):c.bind(i,...(i?[--i]:[]),...(f?[f,...a]:a))
Ben si l'arité est nulle par définition c'est plus du Curry vu qu'on ne retourne plus de fonction. Et ça ne présente pas plus d'un intérêt qu'un f.call ^^
Pas bête le bind, j'oublie toujours que les arguments bound sont concaténés et non remplacés. Du coup je pense que ta solution curryN = i=>(f,...a)=>i?curryN(i-1).bind(f,f,...a):f.call(...a) est la meilleure.
Difficile de savoir quoi mettre comme premier argument du bind, le contexte des fonctions curry n'a en principe pas d'importance vu qu'on finit par un .call.
@Lcf: pas compris ce que tu voulais dire par "aliaser"
C'était un peu l'idée, que l'on puisse aussi s'en servir comme d'un .call... avec une légère différence tout de même, c'est que dans l'usage avec une méthode d'un objet, c'est qu'on peut éviter l'accès à la méthode (en tant que telle) et/ou de devoir connaître l'objet en question. ;)
La plus courte, oui... mais à mon sens, pas la meilleure, perso, je choisirais tout de même une des versions réutilisables...
En effet, si on ne peut se resservir d'une des fonctions résultantes, connaissant déjà les premiers arguments spécifiés, pour faire un second appel, on n'en utilise son potentiel qu'à moitié.
Pour ça que j'ai mis le arity, mais j'aurais pu mettre n'importe quoi, tant que ça fait sauter le param du contexte.
Bah, faire une copie de curryN, quoi...
Code:
1
2
3 var alias; alias = curryN();
Je ne comprends pas pourquoi tu dis que cette version n'est pas réutilisable. Toutes les fonctions sont locales donc regenérées.
Dans tes dernières versions tu as réduit de 1 le décompte des appels en ajoutant l'arité avec le reste des paramètres, ce qui n'a pas beaucoup de sens selon moi. En repartant des tests d'origine, ça fonctionne très bien et c'est réutilisable : démo