60

Beurk! Pourquoi ne pas faire déclarer le type systématiquement alors?

Parce que trouver le meilleur type possible est un problème NP, me semble t il.
Mais il faut le faire exprès (véritablement) pour que caml ne type pas correctement (en général, il répond correctement mais donne un type juste un peu trop spécifique).
En tout cas, dans la pratique, ça ne m'est jamais arrivé.
Même dans une évaluation d'expressions, on peut vouloir sauter à une autre étape de l'évaluation.

D'abord, si on veut faire un goto, on a l'esprit mal tourné.
Ensuite, dans une évaluation de *fonctions* (caml est un langage fonctionnel), ça n'a pas de sens.
Pour l'assignement, il suffit d'utiliser un opérateur = différent (comme le C ou le Pascal, et comme le fait aussi le CAML si j'ai bien compris vos exemples), ou alors de faire comme le BASIC moderne (les premières versions utilisaient LET): selon l'endroit où on se trouve, = veut dire soit comparaison, soit assignement.

Il n'y a pas d'assignement : Une fois déclarée, une variable est une constante.
Une variable modifiable doit être une référence (= un pointeur)
écrire x:= !x+2 ne modifie pas la variable x, := est une instruction d'affectation à un pointeur.
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

61

msvc, et pour les archi Intel (pas AMD) icc, le compilo officiel d'intel produit actuellement un code meilleur que celui de gcc et j'en passe

Il n'existe (et heureusement d'ailleur) pas que gcc comme compilo "portable" il en existe un qui a été assé décrié, mais qui par contre est assé cher sad c'est "CodeWarrior" tres connu dans le monde mac et qui fait une persé timide sur les pc windows et sous linux.
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

62

Intel est évidemment le mieux renseigné pour faire un compilateur efficace sur les pentium....
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

63

HIPPOPOTAME a écrit :
On mesure les écarts de performance en nombre de microversions qui sont releasées?hum

Ça donne quand-même une idée de l'activité du développement. Et il y a eu des améliorations importantes de l'optimisation entre 3.0 et 3.1/3.2, et il y en aura entre 3.1/3.2 et 3.3 (qui va sortir dans très peu de temps).
Rien à voir. Caml est en notation algébrique classique.

1. f x, ou carrément f x y, c'est de la notation polonaise préfixée.
2. Il faut être habitué de la notation polonaise pour aimer l'absence de parenthèses!
En tout cas, en caml, le problème ne se pose pas. pour des raisons d'astuce d'implémentation, les variables doivent être initialisées (car en interne, les entiers doivent obligatoirement être impairs)

Très intelligent, on gaspille un bit pour strictement rien! Si j'ai bien compris, il est déjà impossible de par le langage d'utiliser une variable non initialisée, alors pourquoi gaspiller un bit (et les performances en même temps) pour ça?
Parce que c'est porc.

Ce n'est pas porc si on l'utilise intelligemment. Évidemment, on peut aussi faire du "code spaghetti" avec goto (par exemple en codant toutes ses boucles avec), mais c'est un abus, pas une caractéristique de l'instruction.
Différence de type.

Ce n'est pas ce que j'appelle une différence fondamentale.
Il n'y a pas de long ni de short en caml. Il n'y a que des int, qui sur x86 font 31 bits.

Au revoir, efficacité!
Et si on veut du 64 bits (ou même du 32 bits complet), on fait quoi? En C (GNU C ou ISO C99), on peut mettre long long qui est garanti faire au moins 64 bits.
Les processeurs actuels ne justifient plus tellement les subtilités de taille d'entier.

C'est une excuse bidon.
Le typage caml est de loin le plus propre, le plus précis et le plus rigoureux que j'ai pu tester. L'attitude de caml face au typage est très différente de celle du C, qui est un langage faiblement typé.

L'attitude du C est qu'il faut typer pour pouvoir travailler efficacement, pas pour alourdir la tâche du programmeur. Donc on convertit automatiquement là où ça a un sens (entre différents types numériques). Je trouve ça très pratique.
Surcharger des opérateurs, c'est le top de la propreté?

Ce n'est pas une vraie surcharge vu que ça fait la même chose (multiplier en l'occurrence).
Quand on voit ce qui traîne parfois dans le code source de linux ....

Si Linus ou un autre mainteneur relit ce code, ce genre de saletés ne restera pas longtemps...
D'ailleurs, si tu en remarques une, pourquoi ne pas envoyer un patch pour la nettoyer? smile
godzil
a écrit : hum franchement je trouve vraiment pas que GCC soit une référence. Oui il est surement l'un des plus mutiplateforme, mais non il est loin d'etre le meilleur compilateur au monde et il existe de tres bon compilo qui ne sont pas mi a jour tout les 3 jours et qui fonctionnent ters tres bien (voir mieux que gcc dans pas mal de cas)

Il est non seulement très portable, il est aussi très fonctionnel en termes de constructions de langage acceptées: il est le seul compilateur C à ma connaissance à supporter presque entièrement le nouveau standard ISO C99 (je rappelle que le standard ISO C90 a été officiellement déclaré comme dépassé en 1999, le C99 le remplaçant), et il apporte aussi des tas d'extensions pratiques.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

64

Pour se qu'apporte le C99 a pars des méthodes de faire des truc encore mieux pour le IOCCC... le C prend trop de truc appartenant au C++ et se n'est pas bon... le C++ a bcp fait de mal au C (les commentaires tant utilisé "//" sont enorme source d'érreur lors de copier coller de code par ex)

Déclarer une variable en plein millieu du code est loin d'etre propre... et j'en passe

D'ailleur je tiens a préciser que les "extension" GNU (qui si utilisé rendent le code C hyper importable) non pas été inclu tel quel dans le C99 (pour celles qui ressembles en extentions GNU !) d'ailleur une grosse majorité n'a pas été inclu dans se standard.
Aussi je vois pas se que ses *** d'ANSI on voulu prover en changer les fondement d'un langage qui a fait ses preuves tel quel depuis plus de 10ans sans avoir besoin de le changer

A se propos d'ailleur quand on parle de code C portable, c pas en utilisant le meme compilo sur 2 machine totalement différentes, mais de pouvoir compiler le source sur 2 compilo différents !

Toutes les extentions ajouté par le C99 ne sont que des moyen de rendre le C encore plus illisible qu'il ne peut dja l"etre entre de mauvaise main, et cette evolution ne fait qu'accentuer ma vision du programmeur d'aujourd'hui : Il est fénéant au possible, car incapable d'avoir une simple rigeur au point d'ajouter des extention qui alourdissent le code généré pour des trucs quasi inutiles...

Vive le C89 !


D'ailleur, et pour la majorité des cas, porter le meme compilo, sur une machine totalement différente pour le meme programme ne suffira JAMAIS a compiler un programme sans lui appliquer de modifications.
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

65

godzil
a écrit : Pour se qu'apporte le C99 a pars des méthodes de faire des truc encore mieux pour le IOCCC...

Pas du tout. Il y a plein de trucs utiles. Par exemple les tableaux de taille variable (que GCC supporte déjà depuis pas mal de temps tongue).
le C prend trop de truc appartenant au C++ et se n'est pas bon...

Il prend les trucs intéressants du C++ sans les trucs lourds (surcharge de méthodes et d'opérateurs, classes, templates, ...), donc c'est très bien!
le C++ a bcp fait de mal au C (les commentaires tant utilisé "//" sont enorme source d'érreur lors de copier coller de code par ex)

what
Déclarer une variable en plein millieu du code est loin d'etre propre...

Pourquoi? C'est très pratique.
D'ailleur je tiens a préciser que les "extension" GNU (qui si utilisé rendent le code C hyper importable) non pas été inclu tel quel dans le C99 (pour celles qui ressembles en extentions GNU !) d'ailleur une grosse majorité n'a pas été inclu dans se standard.

Ce n'est pas vrai du tout. Ce qui a été inclus dans le standard:
* long long
* commentaires en //
* litéraux de structures et tableaux avec &(struct foo){a,b}
* nombres complexes (le mot-clé est écrit différemment, mais c'est histoire d'un #define)
* tableaux de taille variable
* inline
et j'en oublie certainement. Il y a quelques extensions très intéressantes qui n'ont malheureusement pas été intégrées, par exemple les expressions à instructions - ({...;...;...)} -, mais ce n'est pas grave si on utilise GCC, parce que ces extensions sont évidemment gardées.
Aussi je vois pas se que ses *** d'ANSI on voulu prover en changer les fondement d'un langage qui a fait ses preuves tel quel depuis plus de 10ans sans avoir besoin de le changer

Les compilateurs sont devenus plus intelligents entretemps (cf. GCC), donc autant en profiter.
A se propos d'ailleur quand on parle de code C portable, c pas en utilisant le meme compilo sur 2 machine totalement différentes, mais de pouvoir compiler le source sur 2 compilo différents !

Je ne suis pas d'accord. Une source portable est une source qui tourne sur plusieurs plateformes différentes. Comme GCC est portable, une source en GNU C l'est aussi. Je ne vois pas l'intérêt d'utiliser un autre compilateur. GCC est gratuit, donc l'argument du prix ne passe pas.
Toutes les extentions ajouté par le C99 ne sont que des moyen de rendre le C encore plus illisible qu'il ne peut dja l"etre entre de mauvaise main, et cette evolution ne fait qu'accentuer ma vision du programmeur d'aujourd'hui : Il est fénéant au possible, car incapable d'avoir une simple rigeur au point d'ajouter des extention qui alourdissent le code généré pour des trucs quasi inutiles...

Je ne suis pas d'accord, pour les raisons citées ci-dessus, que je ne répèterai pas.
Vive le C89 !

Vive le B tant qu'on y est. grin
D'ailleur, et pour la majorité des cas, porter le meme compilo, sur une machine totalement différente pour le meme programme ne suffira JAMAIS a compiler un programme sans lui appliquer de modifications.

Pour la majorité des cas, le système suit la norme POSIX (Windows est l'exception la plus notable), donc le portage est immédiat. Pour les plateformes spéciales comme AMS, il faut de toute façon travailler pour porter un programme, si portable qu'il soit.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

66

Kevin Kofler a écrit :
2. Il faut être habitué de la notation polonaise pour aimer l'absence de parenthèses!

Ou il faut aimer la curryfication. C'est très pratique. Par exemple en C si tu as une fonction int f(int a,int b); si tu veux faire la fonction fa(x)=f(a,x), c'est pas pratique, tu vas devoir définir fa. Par exemple en faisant int fa(int x) { f(a,x) }; ou en utilisant une macro. En CAML avec la curryfication ça le fait tout seul: let fa= f a in ...
Le fait que les parenthèses soient obligatoires en C empêche ce genre de raisonnement.
Ce n'est pas porc si on l'utilise intelligemment. Évidemment, on peut aussi faire du "code spaghetti" avec goto (par exemple en codant toutes ses boucles avec), mais c'est un abus, pas une caractéristique de l'instruction.

C'est parce que tu n'as pas la même vision de l'informatique et de la programmation. Pour toi pour avoir de la performance il faut un langage proche de la machine. CAML a montré le contraire, en faisant du très bon code (très efficace), malgré son aspect fonctionnel très éloigné de la machine mais proche de la rigueur mathématique. Les goto ont une toute petite raison d'exister dans les langages impératifs, mais ils n'ont aucune raison d'exister en maths ni dans les langages fonctionnels.
Au revoir, efficacité!
Et si on veut du 64 bits (ou même du 32 bits complet), on fait quoi? En C (GNU C ou ISO C99), on peut mettre long long qui est garanti faire au moins 64 bits.

L'INRIA a dévelloppé CAML pour Windows, Linux, etc, mais aussi pour des machines à processeur 64 bits. (Les entiers seront codés sur 63 bits je pense)
Ce n'est pas une vraie surcharge vu que ça fait la même chose (multiplier en l'occurrence).

Non, ça multiplie des entiers pour l'un, des flottants pour l'autre. C'est comme élever à la puissance des entiers codés sur une longueur fixe et des entiers à précision arbitraire, on peut penser que c'est la même chose, pourtant dans le premier cas il est utile d'utiliser un algo d'exponentiation rapide, dans le second cas moins.


Enfin ces 2 types de langage n'ont rien à voir, c'est dur de les comparer comme ça. Peut-être que dans quelques années les langages fonctionnels remplaçeront les vieux langages impératifs qui sont trop proches de la machine pour faire du code portable et compréhensible.

[Edit: Enlevé des trucs qui n'avaient rien à voir grin]
avatar
;)

67

Ça donne quand-même une idée de l'activité du développement. Et il y a eu des améliorations importantes de l'optimisation entre 3.0 et 3.1/3.2, et il y en aura entre 3.1/3.2 et 3.3 (qui va sortir dans très peu de temps).

Mais non ça n'indique rien du tout. Ce n'est pas un mystère, par exemple, que gcc est très souvent mis à jour pour des points de détails (ce qui n'est pas une critique).
1. f x, ou carrément f x y, c'est de la notation polonaise préfixée.

N'importe quoi.
1+2, c'est de la polonaise préfixée?
En notation algébrique classique, une application de fonction est préfixée : fonction, puis argument. C'est comme ça.
Caml est en algébrique classique.
C'est Lisp, par exemple, qui est en polonais préfixé. ça n'a rien à voir.
2. Il faut être habitué de la notation polonaise pour aimer l'absence de parenthèses!

Mais non mais non.
Il suffit de ne pas aimer les caractères inutiles, voilà tout.
Très intelligent, on gaspille un bit pour strictement rien! Si j'ai bien compris, il est déjà impossible de par le langage d'utiliser une variable non initialisée, alors pourquoi gaspiller un bit (et les performances en même temps) pour ça?

Mais non, on ne gaspille pas un bit pour strictement rien tongue
Et d'autre part, ça ne nuit pas aux performances (sur un 68000, ça nuirait, pas sur un Mips) tongue

Bon je vais tâcher d'expliquer ce point. Ca a trait à la gestion de la mémoire en caml.
Contrairement au C, on ne gère pas soit même les allocations, c'est le langage qui s'en occupe. D'autre part, on peut montrer que dans un langage fonctionnel d'ordre supérieur comme caml, on ne peut pas prévoir en temps polynomial, à la compilation, quand un bloc de mémoire pourra être libéré. Caml a donc recours aux Garbage Collector : les blocs inutiles sont libérés d'un coup, lorsque la mémoire est pleine.

[ Ici je me dois d'anticiper ton prochain post :
- Non ce n'est pas sale! Au contraire c'est souple et puissant. C'est l'avenir. Le programmeur n'a plus à s'occuper des trivialités de mémoire.
- Non ça ne nuit pas du tout aux performances. ]

Poursuivons.
Lors d'un garbage, toutes les structures de données sont parcourues récursivement pour déterminer les blocs pointés, donc utilisés. Le problème qui se pose est alors de déterminer rapidement quand est ce qu'un champ d'une structure de donnée est un pointeur (auquel cas on continue la plongée récursive) et quand est ce qu'il s'agit d'un entier (on s'arrête).
(Je laisse volontairement de côté les float et autres complications).
L'astuce magique utilisée par caml est évidente : comme les adresses sont tout le temps paires sur les procs actuels, les entiers seront tout le temps impairs (N sera codé en interne 2N+1).
C'est simple et efficace (le test de bit de poids faible est automatique sur x86, au moment de l'accès aux données). En contrepartie, on a des entiers de 31 bits => pas gênant du tout.
Ce n'est pas porc si on l'utilise intelligemment. Évidemment, on peut aussi faire du "code spaghetti" avec goto (par exemple en codant toutes ses boucles avec), mais c'est un abus, pas une caractéristique de l'instruction.

Bon j'ai pas envie de recopier texto les posts de Boogerman, alors je m'arrête là.
mais je n'en pense pas moins. d'ailleurs, si tout le monde dit que c'est porc, c'est peut être parce qu'il y a le début d'un soupçon de commencement d'une bonne raison.
>>> Différence de type Ce n'est pas ce que j'appelle une différence fondamentale.

C'est ce que j'appelle une différence fondamentale.
Au revoir, efficacité!

Les benchs montrent le contraire.
Bienvenue dans le monde réel, Neo.
Et si on veut du 64 bits (ou même du 32 bits complet), on fait quoi? En C (GNU C ou ISO C99), on peut mettre long long qui est garanti faire au moins 64 bits.

On utilise par exemple le module int32 ou le module int64.
C'est une excuse bidon.

Non.
Aujourd'hui, les entiers sont de toute façon alignés sur les adresses multiples de 4 pour une plus grande efficacité, et il n'y a plus de différence de vitesse entre short et long.
L'attitude du C est qu'il faut typer pour pouvoir travailler efficacement, pas pour alourdir la tâche du programmeur. Donc on convertit automatiquement là où ça a un sens (entre différents types numériques). Je trouve ça très pratique.

C'est le point de vue du C, il a sa cohérence et je le respecte.
Le point de vue de caml est différent, et je le trouve plus pratique, mais peu importe, c'est une question de gout.
Caml est fortement typé : les types sont deux à deux incompatibles et il n'y a aucune tolérance dans les types, comme en C. Si nécessaire, il faut transtyper explicitement.

Celà n'alourdit pas du tout la tâche du programmeur, puisque c'est le compilateur qui type tout seul. Et celà a l'avantage énorme (outre le fait d'être remarquablement propre) de supprimer directement à la compilation les erreurs de programmation stupides que C aurait laissé passé.
Et le typage est fortement lié à la programmation fonctionnelle d'une part, et au filtrage d'autre part, qui est l'un des grands grands points forts de caml.
Ce n'est pas une vraie surcharge vu que ça fait la même chose (multiplier en l'occurrence).

Non, c'est fondamentalement différent.
Pour le processeur, convertir un short en float est autrement plus complexe que de le convertir en long. Idem pour multiplier deux int et deux float.
La différence de type traduit la différence *fondamentale* des données.

D'ailleurs, je comprends très bien qu'en C, les types entiers soient compatibles entre eux, mais j'ai du mal à accepter qu'ils soient compatibles avec les float...
Si Linus ou un autre mainteneur relit ce code, ce genre de saletés ne restera pas longtemps...

Au moment où j'écrivais ça, j'avais en tête ce programmeur qui avait écrit une routine en "simulant" un autre langage que le C (et en employant massivement des macros)...
pour le fun...
Il est non seulement très portable, il est aussi très fonctionnel en termes de constructions de langage acceptées: il est le seul compilateur C à ma connaissance à supporter presque entièrement le nouveau standard ISO C99 (je rappelle que le standard ISO C90 a été officiellement déclaré comme dépassé en 1999, le C99 le remplaçant), et il apporte aussi des tas d'extensions pratiques.

S'il est le seul à supporter à peu près le standard, j'ai du mal à appeler ça un standard.
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

68

C'est encore lisible si on regarde attentivement. Rien à voir avec du C poussé à bout.


Bon évidemment, si tu pousses le C vraiment à bout il éclate tout ; rien qu'en abusant des #define...
let x = x*2;;
C'était une recopie des codes en C.


Je ne suis pas d'accord. Ça n'est PAS DU TOUT l'équivalent de l'expression C "x *= 2;"
L'équivalent le plus proche en C serait ceci :
const int _x = x * 2;
#define x _x

Notamment, ça signifie que si tu as utilisé x dans une déclaration de fonction avant ta re-déclaration de x, elle continuera à utiliser l'ancien x même si tu l'appelles après cette re-déclaration. C'est pour ça que je pense que c'est un bon moyen d'obtenir du code illisible.
D'ailleurs c'est en faisant le même genre d'opération (déclarer plusieurs fois le même identifiant) avec des types qu'on peut obtenir le célèbre message d'erreur incompréhensible « This expression has type t but is here used with type t »...
Un typage plus fort en ne typant pas ses variables???
Et ce n'est pas efficace d'avoir des variables qui peuvent contenir n'importe quoi!


en ne typant pas EXPLICITEMENT ses variables. Mais le compilateur fait l'inférence de types et donne un type bien déterminé à la variable. Quant aux variables qui peuvent contenir n'importe quoi, la situation est différente d'en C parce que ce ne sont pas vraiment des variables (pas d'assignement possible). Les références, elles, n'ont pas le droit de contenir n'importe quoi.
x:= !x+2 et non pas x:=x+2
:= est du type 'a -> 'a ref -> unit.
x:= !x+2 est l'équivalent de *x = *x +2

Je trouve quand même que *x += 2; est nettement plus clair et plus court.


Rien n'empêche de définir un opérateur += si l'on veut... comme ceci :
[nosmile]let (+happy = fun x y -> x := !x + y
ou en plus court mais moins lisible :
[nosmile]let (+happy x y = x := !x + y
(je ne mets pas de parenthèses pour faire plaisir à hippopotame)

et après on peut écrire des trucs comme x += 2.
parfois il faut l'aider malgré tout, dans des cas compliqués)

Beurk! Pourquoi ne pas faire déclarer le type systématiquement alors?


Je me suis peut-être mal exprimé : le compilateur arrive toujours à donner un type à une expression, seulement dans certains cas compliqués (avec des objets) il arrive qu'il lui donne un type trop restrictif, ce qui le conduit à rejeter du code valide (c'est rare).
Les expressions peuvent avoir des types très compliqués, donc forcer le programmeur à le déclarer lui-même serait extrêmement lourd ; en plus c'est inutile dans presque tous les cas.
Remarque : dans les fichiers d'interfaces (en gros l'équivalent des headers), on déclare les types de tout ce qu'on exporte. On peut demander au compilateur de générer une interface automatiquement, auquel cas il exporte tout avec les types les plus généraux possibles, mais on peut ensuite restreindre certains des types et virer les déclarations qui ne sont pas censées être accessibles de l'extérieur.
Différence de type.

Ce n'est pas ce que j'appelle une différence fondamentale.


C'est parce que tu penses C. En caml ça l'est.
Mais rien n'empêche de définir un type union regroupant ints et floats et de n'utiliser que celui-là (en fait y a déjà des trucs de ce genre dans la bibliothèque standard, module Num si je me souviens bien). Un petit exemple rapide :

type int_or_float = Int of int | Float of float
let (*) a b = match (a, b) with
  | Float a, Float b -> Float (a *. b)
  | Int a, Int b -> Int (a * b)
  | Float a, Int b -> Float (a *. (float_of_int b))
  | Int a, Float b -> Float ((float_of_int a) *. b)


Et voilà (désolé hippopotame, je n'ai pas pu m'empêcher de mettre plus de parenthèses qu'il n'est probablement nécessaire...)
Tout a commencé quand j'étais garde du cardinal...

69

Et voilà (désolé hippopotame, je n'ai pas pu m'empêcher de mettre plus de parenthèses qu'il n'est probablement nécessaire...)

J'aurais aussi écrit ça comme ça.
Je ne suis pas non plus un fanatique du déparenthésage!
Je pense simplement que Kevin aurait préféré un
Float (a *. (int_to_float(b)))
au lieu de
Float (a *. (int_to_float b))smile
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

70

BiHi a écrit :
Ou il faut aimer la curryfication. C'est très pratique. Par exemple en C si tu as une fonction int f(int a,int b); si tu veux faire la fonction fa(x)=f(a,x), c'est pas pratique, tu vas devoir définir fa. Par exemple en faisant int fa(int x) { f(a,x) }; ou en utilisant une macro. En CAML avec la curryfication ça le fait tout seul: let fa= f a in ... Le fait que les parenthèses soient obligatoires en C empêche ce genre de raisonnement.

Qu'on mette let fa = f a ou #define fa(x) f(a,x), ça revient au même!
Les goto ont une toute petite raison d'exister dans les langages impératifs, mais ils n'ont aucune raison d'exister en maths ni dans les langages fonctionnels.

Regarde bien une démonstration de mathématiques! Il y a plein d'endroits où on saute à une autre étape! Par exemple, à chaque fois que tu lis "de même", c'est un goto!
L'INRIA a dévelloppé CAML pour Windows, Linux, etc, mais aussi pour des machines à processeur 64 bits. (Les entiers seront codés sur 63 bits je pense)

Et si on veut un nombre 64 bits sur une machine 32 bits? Le C99 le permet (long long est garanti faire au moins 64 bits)!
Non, ça multiplie des entiers pour l'un, des flottants pour l'autre. C'est comme élever à la puissance des entiers codés sur une longueur fixe et des entiers à précision arbitraire, on peut penser que c'est la même chose, pourtant dans le premier cas il est utile d'utiliser un algo d'exponentiation rapide, dans le second cas moins.

C'est un détail d'implémentation, cela. L'opération mathématique à la base est la même. C'est juste la représentation interne qui est différente.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

71

HIPPOPOTAME
a écrit : Mais non ça n'indique rien du tout. Ce n'est pas un mystère, par exemple, que gcc est très souvent mis à jour pour des points de détails (ce qui n'est pas une critique).

Ben, eux au moins, ils corrigent leurs bogues!
N'importe quoi.
1+2, c'est de la polonaise préfixée? En notation algébrique classique, une application de fonction est préfixée : fonction, puis argument. C'est comme ça.

Oui, mais avec des parenthèses! La notation classique d'une fonction est f(x,y), pas f x y!
Mais non mais non. Il suffit de ne pas aimer les caractères inutiles, voilà tout.

Les parenthèses ne sont pas inutiles parce que leur absence nuit gravement à la lisibilité!
Mais non, on ne gaspille pas un bit pour strictement rien tongue
Et d'autre part, ça ne nuit pas aux performances (sur un 68000, ça nuirait, pas sur un Mips) tongue

1. Merci de penser aux 68k. roll Ça existe encore!
2. Quel que soit le processeur (même sur un MIPS), si tous les nombres sont codés sous la forme 2x+1, ça complique tous les calculs, d'où perte de temps et d'octets!
Bon je vais tâcher d'expliquer ce point. Ca a trait à la gestion de la mémoire en caml. Contrairement au C, on ne gère pas soit même les allocations, c'est le langage qui s'en occupe. D'autre part, on peut montrer que dans un langage fonctionnel d'ordre supérieur comme caml, on ne peut pas prévoir en temps polynomial, à la compilation, quand un bloc de mémoire pourra être libéré. Caml a donc recours aux Garbage Collector : les blocs inutiles sont libérés d'un coup, lorsque la mémoire est pleine.

Comme en Java. Et pourtant, le Java s'arrange pour ne pas perdre un bit à chaque entier pour ça!
[ Ici je me dois d'anticiper ton prochain post :
- Non ce n'est pas sale! Au contraire c'est souple et puissant. C'est l'avenir. Le programmeur n'a plus à s'occuper des trivialités de mémoire. - Non ça ne nuit pas du tout aux performances. ]

Ce n'est certes pas sale, mais ça encourage la paresse des programmeurs au dépens de la performance! Et il n'est pas vrai du tout que "ça ne nuit pas du tout aux performances":
1. Le programme sera interrompu à des moments imprévisibles à des fins de "garbage collection", donc au revoir programmes temps réel. Donc le langage est inutilisable pour pas mal d'environnements: multimédia (sauf pour ceux qui aiment les sauts d'image et le son intermittent grin), contrôle de pas mal de types de matériel, ...
2. On gaspille de la mémoire, parce que la mémoire n'est plus libérée dès qu'on n'en a plus besoin, mais quand la prochaine "garbage collection" arrive. Et ne me dis pas qu'on n'a pas besoin de libérer de la mémoire avant que la mémoire ne soit pleine, parce que ce n'est pas vrai sous n'importe quel environnement multitâches!
3. Un exemple concret: GCC 3 (depuis la version 3.0) utilise une "garbage collection" pour le compilateur lui-même. La version précédente, GCC 2.95(.x), utilisait autre chose. Tout le monde se plaint de la lenteur de GCC 3, et tous les tests ont montré que le ralentissement le plus important était exactement quand la "garbage collection" a été introduite.
L'astuce magique utilisée par caml est évidente : comme les adresses sont tout le temps paires sur les procs actuels,

Au contraire, les processeurs modernes ont plus tendance à tolérer l'accès non-aligné que les vieux processeurs (68000 par exemple). Mais passons sur ce point-là.
les entiers seront tout le temps impairs (N sera codé en interne 2N+1). C'est simple et efficace (le test de bit de poids faible est automatique sur x86, au moment de l'accès aux données).

Ce n'est pas efficace du tout! Par exemple, tu ne peux plus calculer x+y directement, parce que tu te retrouves avec 2x+1+2y+1=2(x+y)+2. Tu es donc obligé de soustraire 1 à chaque fois. Donc une instruction de plus, donc perte de temps et de place. Et c'est pire pour les multiplications, divisions, ...
En contrepartie, on a des entiers de 31 bits => pas gênant du tout.

Si, c'est gênant! On se retrouve avec une rangée de nombres divisée par 2, et tu appelles ça "pas gênant du tout"???
>> Différence de type
>Ce n'est pas ce que j'appelle une différence fondamentale. C'est ce que j'appelle une différence fondamentale.

Pas moi, parce que l'opération mathématique à la base est la même!
>>Il n'y a pas de long ni de short en caml. Il n'y a que des int, qui sur x86 font 31 bits.
>Au revoir, efficacité!
Les benchs montrent le contraire. Bienvenue dans le monde réel, Neo.

Tes benchs montrent peut-être qu'il n'y a pas de différence de vitesse (ce qui est vrai si (et seulement si) on considère un processeur 32 bits et si on néglige les ralentissements dus aux calculs supplémentaires nécessités par le bit pris à part), mais il n'y a pas que la vitesse qui compte, il y a aussi la taille des données (en mémoire et dans les fichiers). Et puis, le seul bench que tu m'as montré est un bench dont l'auteur lui-même dit qu'il n'est probablement pas fiable...
On utilise par exemple le module int32 ou le module int64.

Ben, dans ce cas, c'est de l'émulé, donc là, ça sera indéniablement lent et gros!
Non. Aujourd'hui, les entiers sont de toute façon alignés sur les adresses multiples de 4 pour une plus grande efficacité, et il n'y a plus de différence de vitesse entre short et long.

Pense à la mémoire consommée! Quant à l'alignement, ça dépend du processeur, mais il n'est presque jamais nécessaire d'aligner un objet à plus de sa taille. Donc un entier 16 bits peut très bien être sur une adresse multiple impair de 2, et un entier 8 bits peut très bien être sur une adresse impaire, sans que ça nuise à la performance.
Caml est fortement typé : les types sont deux à deux incompatibles et il n'y a aucune tolérance dans les types, comme en C. Si nécessaire, il faut transtyper explicitement.

C'est lourd!
Celà n'alourdit pas du tout la tâche du programmeur, puisque c'est le compilateur qui type tout seul.

Et on y gagne quoi?
Et celà a l'avantage énorme (outre le fait d'être remarquablement propre) de supprimer directement à la compilation les erreurs de programmation stupides que C aurait laissé passé.

Exemple?
Et le typage est fortement lié à la programmation fonctionnelle d'une part, et au filtrage d'autre part, qui est l'un des grands grands points forts de caml.

Filtrage? confus (Je sais, ça doit être une question stupide, désolé. smile)
Non, c'est fondamentalement différent.
Pour le processeur, convertir un short en float est autrement plus complexe que de le convertir en long. Idem pour multiplier deux int et deux float. La différence de type traduit la différence *fondamentale* des données.

Mais c'est un détail d'implémentation.
D'ailleurs, je comprends très bien qu'en C, les types entiers soient compatibles entre eux, mais j'ai du mal à accepter qu'ils soient compatibles avec les float...

Un nombre est un nombre! Et ça ne dérange personne: tant que tu ne calcules qu'avec des entiers, jamais le C n'utilisera-t'il un float (ou un double ou long double) pour faire tes calculs. Un float (ou un double ou long double) ne sera utilisé pour le résultat que si au moins une des opérandes en était un.
Au moment où j'écrivais ça, j'avais en tête ce programmeur qui avait écrit une routine en "simulant" un autre langage que le C (et en employant massivement des macros)... pour le fun...

Ce n'est pas dans Linux, ça, c'est la source du "Bourne shell" d'origine (pas du "Bourne again shell" (bash) du projet GNU qui est le plus utilisé maintenant). Il y a un paquet de macros pour transformer le C en un dialecte de l'ALGOL. smile
S'il est le seul à supporter à peu près le standard, j'ai du mal à appeler ça un standard.

C'est un standard ISO!
Et vu que l'ancien standard a été retiré en faveur du nouveau, les compilateurs C89 ne sont plus des compilateurs C!
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

72

[nosmile]
Filtrage ?


cf. mon exemple de multiplication sur les int_or_float :
| Float a Int b -> ...
| Int a Int b -> ...

C'est ça le filtrage (matching)

Remarque en passant : quand je parle de type union, ce n'est pas une union "à la C", c'est à dire que si une valeur de type int_or_float est un entier elle ne prendra pas autant de place qu'un flottant.

Kevin : la multiplication sur les entiers peut être considérée comme une restriction de celle sur les réels, OK, mais un flottant n'est pas un réel... ce n'est pas *vraiment* la même opération mathématique.
Et quand on parle de « module » ça ne signifie pas que c'est de l'émulation. Les modules dont parle hippopotame sont dans la stdlib ("module" signifie simplement que pour avoir accès aux identifiants déclarés dans le module il faut soit utiliser la directive #open "Nom_module" soit utiliser la notation préfixée Nom_module.<ident>). On peut même définir plusieurs modules dans un même fichier si l'on veut (même si je ne vois pas très bien l'utilité... mais je ne connais rien à toutes les histoires de foncteurs et de modules paramétrés, ça sert sûrement dans certains cas...)
Un nombre est un nombre !

Oui, type num défini dans le module Num je crois bien. Y a toutes les conversions automatiques qu'il faut, même passer en précision arbitraire quand on dépasse max_int (je n'ai pas vérifié mais il me semble bien).
Tout a commencé quand j'étais garde du cardinal...

73

Ca sert à rien de comparer un langage impératif et un langage fonctionnel. On ne peut pas dire qu'ils sont "biens" ou "pas biens", ils ont chacun des utilisations différentes adaptés à chaque type de problème c'est tout.

74

timoleon> Oui num est en précision arbitraire quand il le faut.
Nerick> C'est clair. Mais je pense que l'avenir est aux langages fonctionnels.
avatar
;)

75

Qu'on mette let fa = f a ou #define fa(x) f(a,x), ça revient au même!

non non non...
Les fonctions curryfiées ont des avantages indéniables par rapport aux fonctions à deux variables.

Un exemple bateau :

Imaginons que L est une liste de listes, et qu'on veuille ajouter l'élément 1 en tête de tous les éléments de L.
Par exemple, [ [3] ; [] ; [8;9] ] => [ [1;3] ; [1] ; [1;8;9] ]
Grâce à la curryfication de la fonction append, la solution tient en seulement :
map (append [1]) L;;

(enfin bon, dans mon exemple improvisé j'ai mis append pour des raisons de clarté mais j'utiliserais personellement plutôt map (prefix :: 1) L;;)
Regarde bien une démonstration de mathématiques! Il y a plein d'endroits où on saute à une autre étape! Par exemple, à chaque fois que tu lis "de même", c'est un goto!

Non, c'est un Gosub.
Ou un appel de fonction, si tu veux.smile
Dans une démonstration mathématique, on ne trouve pas de « la démonstration se poursuit à la page 5 » et de « le fil de la démonstration retourne à la page 2 ».
Et si on veut un nombre 64 bits sur une machine 32 bits? Le C99 le permet (long long est garanti faire au moins 64 bits)!

module int64
C'est un détail d'implémentation, cela. L'opération mathématique à la base est la même. C'est juste la représentation interne qui est différente.

Non, l'opération n'est pas la même. Les propriétés algébriques de Z et de R sont fondamentalement différentes. Et les propriétés des pseudo-entiers et des pseudo-réels représentés sur machine sont encore plus fondamentalement différentes.
Ben, eux au moins, ils corrigent leurs bogues!

Parce qu'eux, ils ont des bogues.smile
Oui, mais avec des parenthèses! La notation classique d'une fonction est f(x,y), pas f x y!

ça ne change rien, met les parenthèses si tu veux.
Les parenthèses ne sont pas inutiles parce que leur absence nuit gravement à la lisibilité!

Leur absence complète nuit à la lisibilité.
mais leur abus nuit tout autant.
1. Merci de penser aux 68k. Ça existe encore!

Et effectivement Caml est bcp moins adapté que le C pour les petites machines. Il est fait pour les processeurs modernes (bien que ce serait un bien beau projet d'implémenter une version très light de cml sur une calculatrice, il y a un hpuser qui a vaguement cette idée en tête)
2. Quel que soit le processeur (même sur un MIPS), si tous les nombres sont codés sous la forme 2x+1, ça complique tous les calculs, d'où perte de temps et d'octets!

Bah, il faut calculer a+b-1 au lieu de a+b....
Si je parlais de Mips, c'est parce que sur un Mips on fait ça en une seule instruction : add $sp,$sp, -1, par exemple.
Je ne connais pas assez le x86 pour savoir si ce genre de chose existe sur la dernière génération de microprocesseur, mais même si ce n'est pas le cas, je gage que les mécanismes de pipeline & cie font que ça ne nuit pas du tout aux performances.
Comme en Java. Et pourtant, le Java s'arrange pour ne pas perdre un bit à chaque entier pour ça!

Niveau performances, le Java est à la rue par rapport au caml et au C.
Question implémentation, caml se veut un langage très "pointu", comme le C.

Et évidemment, perdre un bit ne gêne absolument pas!
Ce n'est certes pas sale, mais ça encourage la paresse des programmeurs au dépens de la performance!

Certainement pas. Ca permet de se concentrer sur l'algorithme plutôt que sur des futilités.
2. On gaspille de la mémoire, parce que la mémoire n'est plus libérée dès qu'on n'en a plus besoin, mais quand la prochaine "garbage collection" arrive. Et ne me dis pas qu'on n'a pas besoin de libérer de la mémoire avant que la mémoire ne soit pleine, parce que ce n'est pas vrai sous n'importe quel environnement multitâches!

Hum....
Sais tu comment est gérée la mémoire dans un environnement multitache par le C?

Le gestionnaire de programme réserve un ou deux très gros blocs à l'OS. Ensuite, il y a un minimum de transactions avec l'OS, et c'est un sous-gestionnaire qui gère l'intérieur de ces gros blocs.
Quand on réserve de la mémoire dans le programme, le sous-bloc qui est alloué n'a pas d'existence du point de vue de l'OS.
De même, quand on libère un bloc, ça ne fait pas du tout augmenter la mémoire disponible pour les autres programmes!
(Tout celà étant bien sûr une simplification, le gestionnaire de mémoire ayant parfois à décider de réserver ou de rendre un gros bloc)

Pour caml, c'est pareil. Ca ne pose pas de problème.
3. Un exemple concret: GCC 3 (depuis la version 3.0) utilise une "garbage collection" pour le compilateur lui-même. La version précédente, GCC 2.95(.x), utilisait autre chose. Tout le monde se plaint de la lenteur de GCC 3, et tous les tests ont montré que le ralentissement le plus important était exactement quand la "garbage collection" a été introduite.

Je préfère ne pas faire de commentaire sur la façon dont gcc est programmé.
Ce n'est pas efficace du tout! Par exemple, tu ne peux plus calculer x+y directement, parce que tu te retrouves avec 2x+1+2y+1=2(x+y)+2. Tu es donc obligé de soustraire 1 à chaque fois. Donc une instruction de plus, donc perte de temps et de place. Et c'est pire pour les multiplications, divisions, ...

Cf réponse plus haut.
Sur un 68000 ce serait pénalisant, pas sur un Pc tout beau tout neuf.
Il suffit de voir les benchs, de toute façon.
Si, c'est gênant! On se retrouve avec une rangée de nombres divisée par 2, et tu appelles ça "pas gênant du tout"???

Non.
D'ailleurs, si on ne me l'avait pas dit, je ne m'en serais même pas rendu compte!smile
Pas moi, parce que l'opération mathématique à la base est la même!

(Z,+,.) et (R,+,.) sont des anneaux très, très, très différents.
Tes benchs montrent peut-être qu'il n'y a pas de différence de vitesse

En effet.
(ce qui est vrai si (et seulement si) on considère un processeur 32 bits et si on néglige les ralentissements dus aux calculs supplémentaires nécessités par le bit pris à part),

Non.
Et puis, le seul bench que tu m'as montré est un bench dont l'auteur lui-même dit qu'il n'est probablement pas fiable...

Aucun bench n'est fiable. Celui ci est donc honnête.
Ben, dans ce cas, c'est de l'émulé, donc là, ça sera indéniablement lent et gros!

On a si peu besoin de 32 et 64 bits, de toute façon, que ça n'a pas d'importance.
C'est effectivement plus gros, mais pas tellement plus lent.
Pense à la mémoire consommée!

Ce ne sont pas en tant que telles les données basiques des programmes qui peuvent saturer les >64Mo des ordinateurs récents. Ce sont les données graphiques, multimédias, etc...
Quant à l'alignement, ça dépend du processeur, mais il n'est presque jamais nécessaire d'aligner un objet à plus de sa taille. Donc un entier 16 bits peut très bien être sur une adresse multiple impair de 2, et un entier 8 bits peut très bien être sur une adresse impaire, sans que ça nuise à la performance.

En caml ils sont en adresse paire.
Et de toute façon, par conservatisme et souci de compatibilité, les données restent presque toujours alignées.
C'est lourd!

Au contraire.
Et on y gagne quoi?

La simplicité.
L'efficacité.
La beauté du langage.
Exemple?

bah, tout ce qui est mauvais type. Par exemple, quand on se trompe sur l'ordre des arguments d'une fonction compliquée. Le C, qui est tolérant, fait un transtypage transparent, alors que caml pointe l'erreur.

De plus, l'analyse des erreurs de type permet souvent de démasquer les erreurs dans le code.
Filtrage?

Un autre grand pilier de caml.
Le filtrage, c'est un peu comme une structure switch/case généralisée : on recherche un motif (pattern matching) dans les données et on exécute du code différent selon le motif détecté.

Exemple basique :

let nombre_vers_lettres = function
   0 -> "zéro"
 | 1 -> "un"
 | 2 -> "deux"
 | 3 | 4 -> "trois ou quatre"
 | _ -> "beaucoup";;


Des variables locales peuvent aussi être déclarées au cours d'un filtrage :

let nombre_vers_lettres = function
   0 -> "nul"
 | x -> "le nombre " ^ (string_of_int x);;


Bien sûr le filtrage peut porter sur des types de données aussi élaborés qu'on veut :

let truc x = match x with
  ( _ , [a::q, x] as w) -> .....


Le filtrage a une puissance assez fabuleuse quand il s'agit de manipuler des structures de données élaborées. Par exemple, si on définit un type arbre binaire sans étiquettes par :
type arbre = Feuille | Noeud of arbre*arbre
La fonction permettant de compter les feuilles d'un arbre s'écrit grâce au filtrage :
let rec nb_feuilles = function
   Feuille -> 1
 | Noeud(a,b) -> (nb_feuilles a)+(nb_feuilles b);;


Je ne crois pas que le C puisse rivaliser dans ce domaine...
Mais c'est un détail d'implémentation.

et de mathématiques!
D'ailleurs la différence d'implémentation est elle même issue de la différence mathématique...
Ce n'est pas dans Linux, ça, c'est la source du "Bourne shell" d'origine (pas du "Bourne again shell" (bash) du projet GNU qui est le plus utilisé maintenant). Il y a un paquet de macros pour transformer le C en un dialecte de l'ALGOL.

Je ne savais pas, je croyais que c'était dans le code linux.
Amusantsmile
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

76

je pense que l'avenir est aux langages fonctionnels.

Clairement, les langages fonctionnels devraient être plus utilisés.
Je ne pense pas qu'ils doivent s'imposer partout : le C reste indispensable. Mais le C est *trop* utilisé, il y a des tas de domaines où d'autres langages sont plus appropriés.
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

77

Voilà c'est ce que je me disais. Je pense que toute la partie interface aura du mal à être gérée par des langages fonctionnels, c'est clairement impératif de dire "Fais ceci si l'utilisateur fais cela", mais pour le noyau de calcul, et pour pas mal de fonctions le CAML et les langages fonctionnels devraient être préférés.
avatar
;)

78

Pour tout ce qui est parseurs aussi !!
Donc compilation, interprétation de l'html, etc...
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

79

Comment on execute du CAML ?

80

Comment on execute du CAML ?

Ben on compile le source en natif (avec ocamlopt) et ça fait un exécutable...
L'autre possibilité est de compiler en "bytecode" (avec ocamlc) auquel cas ça se lance avec l'interpréteur de bytecode (ocamlrun).
Tout a commencé quand j'étais garde du cardinal...

81

Oh, oki, je savais pas que ca faisait un exec ... je pensais du style script python etc.

82

J'ai entendu dire que certaines parties de Doom3 ont été codées en caml.

83

Je n'ai toujours pas compris pourquoi les nombres sont forcément impairs en interne, en CAML.
Quel(s) avantage(s) ça apporte ?

84

Pour pouvoir les distinguer rapidement des poniteurs (qui sont pairs).
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

85

ah oui, tu l'avais déjà dit ici, désolé.
HIPPOPOTAME
a écrit : L'astuce magique utilisée par caml est évidente : comme les adresses sont tout le temps paires sur les procs actuels, les entiers seront tout le temps impairs (N sera codé en interne 2N+1).

86

timoleon a écrit :
cf. mon exemple de multiplication sur les int_or_float :
| Float a Int b -> ...
| Int a Int b -> ...
C'est ça le filtrage (matching)

Cet exemple concret, ça s'appelle du surchargement en C++ (il n'y a pas ça en C, en effet).
HIPPOPOTAME a écrit :
non non non...
Les fonctions curryfiées ont des avantages indéniables par rapport aux fonctions à deux variables.

Un exemple bateau :

Imaginons que L est une liste de listes, et qu'on veuille ajouter l'élément 1 en tête de tous les éléments de L.
Par exemple, [ [3] ; [] ; [8;9] ] => [ [1;3] ; [1] ; [1;8;9] ]
Grâce à la curryfication de la fonction append, la solution tient en seulement :
map (append [1]) L;;

[i]#define map(f,x,l) for(int i=0;i<sizeof(l)/sizeof(l[0]);i++) f(x,l)
Et maintenant, une ligne:
map(append,1,l);
smile
Reste à écrire append.
Non, c'est un Gosub.
Ou un appel de fonction, si tu veux.smile

Si le "de même" est la conclusion, pas vraiment. smile
Non, l'opération n'est pas la même. Les propriétés algébriques de Z et de R sont fondamentalement différentes. Et les propriétés des pseudo-entiers et des pseudo-réels représentés sur machine sont encore plus fondamentalement différentes.

Z est quand-même un sous-ensemble de |R! (Mais je t'accorde que cela n'est vrai pour leurs équivalents machine seulement sous certaines conditions de taille de réprésentation. Par exemple, il faut un double pour stocker LONG_MAX sur la plupart des plateformes, un float ne suffit pas.)
Parce qu'eux, ils ont des bogues.smile

rotfl
(En pratique,) tout logiciel a des bogues!
Leur absence complète nuit à la lisibilité. mais leur abus nuit tout autant.

Mais notre définition d'"abus" est différente. grin
Évidemment, si je lis du (((a)+(b))+(c)), je commence à me poser des questions. grin (Sauf si on est dans une macro, où il faut mettre les plus de parenthèses possible pour éviter des effets non voulus.)
Bah, il faut calculer a+b-1 au lieu de a+b....
Si je parlais de Mips, c'est parce que sur un Mips on fait ça en une seule instruction : add $sp,$sp, -1, par exemple.

Mais tu viens de tomber par hasard sur un calcul comme ça. Maintenant, va voir les divisions. smile
Je ne connais pas assez le x86 pour savoir si ce genre de chose existe sur la dernière génération de microprocesseur, mais même si ce n'est pas le cas, je gage que les mécanismes de pipeline & cie font que ça ne nuit pas du tout aux performances.

Une instruction de plus dans la pipeline prend la place d'une autre... roll
Et évidemment, perdre un bit ne gêne absolument pas!

Si!
Certainement pas. Ca permet de se concentrer sur l'algorithme plutôt que sur des futilités.

Toi, tu appelles ça comme ça. Moi, j'appelle ça de la paresse. smile
Hum....
Sais tu comment est gérée la mémoire dans un environnement multitache par le C?

Le gestionnaire de programme réserve un ou deux très gros blocs à l'OS. Ensuite, il y a un minimum de transactions avec l'OS, et c'est un sous-gestionnaire qui gère l'intérieur de ces gros blocs.
Quand on réserve de la mémoire dans le programme, le sous-bloc qui est alloué n'a pas d'existence du point de vue de l'OS.
De même, quand on libère un bloc, ça ne fait pas du tout augmenter la mémoire disponible pour les autres programmes!
(Tout celà étant bien sûr une simplification, le gestionnaire de mémoire ayant parfois à décider de réserver ou de rendre un gros bloc)
Pour caml, c'est pareil. Ca ne pose pas de problème.

Mais si on alloue, on alloue, ..., sans libérer (à temps) ce qu'on alloue, on est obligé de réclamer des pages supplémentaires à l'OS, qui auraient pu servir à d'autres applications!
Cf réponse plus haut. Sur un 68000 ce serait pénalisant, pas sur un Pc tout beau tout neuf.

Au fait, je viens de me rendre compte que le 68000 ne serait même pas le processeur qui souffrirait le plus: lea -1(a0,d0.l),a0. smile Mais ça oblige à mettre l'entier dans un registre d'adresses, ce qui empêche d'y mettre une vraie adresse. sad Et c'est plus gros et plus lent qu'un add ou adda. sad
Il suffit de voir les benchs, de toute façon.

Le seul bench que tu m'as indiqué montre le C gagnant! Pour moi, chaque point compte!
Non.
D'ailleurs, si on ne me l'avait pas dit, je ne m'en serais même pas rendu compte!smile

Tu aurais dû te poser la question dès le départ! C'est dangereux de coder sans connaître la taille des nombres avec lesquels on travaille!
(Z,+,.) et (R,+,.) sont des anneaux très, très, très différents.

Mais Z est un sous-anneau de |R!
>(ce qui est vrai si (et seulement si) on considère un processeur 32 [ou plus, j'aurais dû le préciser] bits et si on néglige les ralentissements dus aux calculs supplémentaires nécessités par le bit pris à part), Non.

Si!
1. Sur un processeur 16 bits, une opération sur 32 bits est plus lente (2 fois en théorie, un peu moins, mais quand-même d'un facteur remarquable, en pratique) qu'une opération sur 16 bits! À titre d'exemple, regarde les cycles pris par les instructions 68k.
2. Il faut des calculs supplémentaires pour le bit pris à part, et ils se retrouveront d'une manière ou d'une autre dans le temps d'exécution, même si le processeur est très sophistiqué.
On a si peu besoin de 32 et 64 bits, de toute façon, que ça n'a pas d'importance.

Je pense que tu sous-estimes le nombre de calculs nécessitant un nombre de chiffres important.
C'est effectivement plus gros, mais pas tellement plus lent.

1. "pas tellement plus lent", mais plus lent quand-même! J'en ai marre de cette attitude "pas tellement"! C'est valable quand la perte de vitesse s'accompagne d'un gain d'efficacité ailleurs (la taille par exemple), mais pas dans ce cas-ci.
2. Je déteste le bloatware! Tout le monde à l'air de se fichier de la taille prise par ses programmes. Solution proposée: acheter un disque dur plus grand chaque année. sick J'ai marre des gens qui sacrifient la taille pour la vitesse, mais j'ai encore plus marre des gens qui font des programmes qui sont non seulement gros, mais aussi lents!
Ce ne sont pas en tant que telles les données basiques des programmes qui peuvent saturer les >64Mo des ordinateurs récents. Ce sont les données graphiques, multimédias, etc...

Ce n'est pas une raison de gaspiller de la place pour rien.
En caml ils sont en adresse paire.

C'est bien ce que je critique. Un octet n'a pas besoin d'être à une adresse paire, et il n'a pas besoin non plus d'être codé sur 31 bits (dont 23 inutilisés).
Et de toute façon, par conservatisme et souci de compatibilité, les données restent presque toujours alignées.

Déclare un tableau de chars ou de shorts en C et tu verras ce qui se passe. roll. Il se passera exactement ce que j'ai décrit (alignement à 1 pour les chars et à 2 pour les shorts, pas à 4).
>>Si nécessaire, il faut transtyper explicitement.
>C'est lourd! Au contraire.

Arrête de raconter n'importe quoi! C'est très lourd de devoir transtyper explicitement à chaque fois.
>>Celà n'alourdit pas du tout la tâche du programmeur, puisque c'est le compilateur qui type tout seul.
>Et on y gagne quoi?
La simplicité.
L'efficacité. La beauté du langage.

* En quoi est-ce plus simple de devoir réfléchir à chaque fois "qu'est-ce que le compilateur va donner comme type à ma variable" plutôt que de lui dire clairement ce qu'on veut?
* Pour l'efficacité, je ne vois pas du tout, désolé.
* Personnellement, je ne trouve pas un langage où on doit deviner les types en lisant une source "beau".
bah, tout ce qui est mauvais type. Par exemple, quand on se trompe sur l'ordre des arguments d'une fonction compliquée. Le C, qui est tolérant, fait un transtypage transparent, alors que caml pointe l'erreur.

Le cas où il y a une différence de type est quand-même rare. D'habitude, presque tous les paramètres sont soit des int, soit des pointeurs. Et si on passe un entier à la place d'un pointeur ou vice-versa, GCC donnera un warning.
De plus, l'analyse des erreurs de type permet souvent de démasquer les erreurs dans le code.

C'est à ça que servent les warnings de conversion implicite int<->pointeur! Les erreurs de types les plus courants sont les * ou & oubliés, et on aura un warning pour ça. Je me vois mal déclarer une variable en int et y mettre un flottant.
Un autre grand pilier de caml.
let nombre_vers_lettres = function
   0 -> "zéro"
 | 1 -> "un"
 | 2 -> "deux"
 | 3 | 4 -> "trois ou quatre"
 | _ -> "beaucoup";;

switch(x) {
  case 0: return "zéro";
  case 1: return "un";
  case 2: return "deux";
  case 3: case 4: return "trois ou quatre";
  default: return "beaucoup";
}

Des variables locales peuvent aussi être déclarées au cours d'un filtrage :

let nombre_vers_lettres = function
   0 -> "nul"
 | x -> "le nombre " ^ (string_of_int x);;

Il suffit de déclarer la variable dans le prototype, puis d'utiliser un switch en C.
Bien sûr le filtrage peut porter sur des types de données aussi élaborés qu'on veut :

let truc x = match x with
  ( _ , [a::q, x] as w) -> .....

Là, ça devient un peu plus intéressant.
Le filtrage a une puissance assez fabuleuse quand il s'agit de manipuler des structures de données élaborées. Par exemple, si on définit un type arbre binaire sans étiquettes par :
type arbre = Feuille | Noeud of arbre*arbre
La fonction permettant de compter les feuilles d'un arbre s'écrit grâce au filtrage :
let rec nb_feuilles = function
   Feuille -> 1
 | Noeud(a,b) -> (nb_feuilles a)+(nb_feuilles b);;

Là aussi. Ça, c'est de la polymorphie. En C++, on peut faire ça aussi: on travaillerait ici avec de l'héritage et du RTTI. En C pur, il faudra utiliser une structure type_id + union.
et de mathématiques! D'ailleurs la différence d'implémentation est elle même issue de la différence mathématique...

En mathématiques, tu écris 2 × 3 de la même manière que 2 × 3,1 !
HIPPOPOTAME a écrit :
Clairement, les langages fonctionnels devraient être plus utilisés. Je ne pense pas qu'ils doivent s'imposer partout : le C reste indispensable. Mais le C est *trop* utilisé, il y a des tas de domaines où d'autres langages sont plus appropriés.

Ça a le grand avantage qu'on n'a pas besoin de connaître 10000 langages, un seul suffit!
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

87

Et j'ai mieux pour les arbres en C pur:
typedef struct arbre {
  union {
    struct {
      struct arbre *a;
      struct arbre *b;
    } noeud;
    struct {
      void *zero;
      void *data;
    } feuille;
  };
} arbre;

Et après, il suffit de comparer x.feuille.zero ou x.noeud.a avec 0 pour savoir si on a un nœud ou une feuille.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

88

Cet exemple concret, ça s'appelle du surchargement en C++ (il n'y a pas ça en C, en effet).

attention En caml, ce n'est pas une surcharge (ça n'existe pas).
#define map(f,x,l) for(int i=0;i<sizeof(l)/sizeof(l[0]);i++) f(x,l[i])
Et maintenant, une ligne:
map(append,1,l);
Reste à écrire append.

Bien compliqué tout ça, et également beaucoup moins clairsmile
Si le "de même" est la conclusion, pas vraiment.

Mais si, c'est encore un gosub / appel de fonction!
L'exécution du programme (c'est à dire la lecture de la démonstration) termine quand on arrive physiquement à la fin de la démo.
Donc le "de même" est un gosub vers une partie précédente, puis la lecture reprend à la fin et termine.
Bien sur, le compilateur (le lecteur) optimisera cet appel en un JMP, mais c'est une basse question d'implémentation smile
(En pratique,) tout logiciel a des bogues!

Je n'étais pas sérieux en disant ça.
Mais il faut arrêter de mesurer l'évolution concrète d'un logiciel au nombre de releases. Ca dépend de la stratégie de l'équipe de développement : sortir publiquement une nouvelle version à la moindre correction, ou regrouper les modifications.
>Et évidemment, perdre un bit ne gêne absolument pas!
Si!

Non.
Toi, tu appelles ça comme ça. Moi, j'appelle ça de la paresse.

Tu dois avoir raisontriso
D'ailleurs on devrait aussi supprimer les langages compilés (quelle paresse que de ne pas s'occuper des registres soi même!) et recommencer à programmer en assembleur!

L'informatique évolue vers plus d'abstraction. Ce n'est pas de la paresse, c'est du progrès.
Mais si on alloue, on alloue, ..., sans libérer (à temps) ce qu'on alloue, on est obligé de réclamer des pages supplémentaires à l'OS, qui auraient pu servir à d'autres applications!

Peu importe tes allégations de mauvaise foi, en pratique ce n'est pas ce qui se passe. Caml (comme Java d'ailleurs) n'aspire pas petit à petit toute la mémoire, comme tu semble le croire. Du point de vue de l'OS il utilise la mémoire peu ou prou comme le C.
Au fait, je viens de me rendre compte que le 68000 ne serait même pas le processeur qui souffrirait le plus: lea -1(a0,d0.l),a0. Mais ça oblige à mettre l'entier dans un registre d'adresses, ce qui empêche d'y mettre une vraie adresse. Et c'est plus gros et plus lent qu'un add ou adda.

Si même sur un 68k d'il y a 30 ans ça ne pose que des problèmes mineurs, imagine sur un P IV ou un athlon.
Le seul bench que tu m'as indiqué montre le C gagnant! Pour moi, chaque point compte!

Bon, très bien.
Le C est 0.13% plus efficace que le Caml, si ça peut te faire plaisirsick
Mais Z est un sous-anneau de |R!

mais oui mais oui.
C'est aussi un sous anneau du corps des quaternions, tu en déduis que les entiers et les quaternions ont les mêmes propriétés?
Si! 1. Sur un processeur 16 bits, une opération sur 32 bits est plus lente (2 fois en théorie, un peu moins, mais quand-même d'un facteur remarquable, en pratique) qu'une opération sur 16 bits! À titre d'exemple, regarde les cycles pris par les instructions 68k.

Ah pardon. Je pensais aux processeurs 32 bits vs 64 bits.
Bon je vais répéter clairement ce que j'ai déjà dit de façon un peu allusive : on ne peut décemment avoir une implémentation efficace de caml que sur des processeurs récents.
Une implémentation 16 bits de caml devrait pour être raisonnablement efficace utiliser des entiers 15 bits, ce qui est un peu juste, ou 31 bits, ce qui est un peu lent. (Par rapport au C, s'entend. Caml resterait un langage intéressant, mais il ne serait plus concurrenciel).
Je pense que tu sous-estimes le nombre de calculs nécessitant un nombre de chiffres important.

Donne des exemples courants...
Moi je n'en ai jamais besoin. Ou alors il s'agit de calculs en précision infinie.
1. "pas tellement plus lent", mais plus lent quand-même! J'en ai marre de cette attitude "pas tellement"! C'est valable quand la perte de vitesse s'accompagne d'un gain d'efficacité ailleurs (la taille par exemple), mais pas dans ce cas-ci. 2. Je déteste le bloatware! Tout le monde à l'air de se fichier de la taille prise par ses programmes. Solution proposée: acheter un disque dur plus grand chaque année. J'ai marre des gens qui sacrifient la taille pour la vitesse, mais j'ai encore plus marre des gens qui font des programmes qui sont non seulement gros, mais aussi lents!

Puisque de toute façon on se sert à peu près jamais de int32 et int64, ces objections n'ont pas beaucoup d'intérêt.
C'est bien ce que je critique. Un octet n'a pas besoin d'être à une adresse paire, et il n'a pas besoin non plus d'être codé sur 31 bits (dont 23 inutilisés).

Un octet c'est un char en caml, et c'est codé sur 8 bits (dans une chaîne de caractères en tout cas).
Déclare un tableau de chars ou de shorts en C et tu verras ce qui se passe. . Il se passera exactement ce que j'ai décrit (alignement à 1 pour les chars et à 2 pour les shorts, pas à 4).

Idem pour les string en caml...
Arrête de raconter n'importe quoi! C'est très lourd de devoir transtyper explicitement à chaque fois.

L'erreur est dans le "à chaque fois".
On transtype très peu.
* En quoi est-ce plus simple de devoir réfléchir à chaque fois "qu'est-ce que le compilateur va donner comme type à ma variable" plutôt que de lui dire clairement ce qu'on veut?

C'est plus simple justement parce qu'il n'y a pas besoin d'y réfléchir.
Soyons sérieux. Si x=3, alors x est un entier. On ne s'embête jamais à choisir véritablement les types, ils viennent tout seul.

switch(x) {

case 0: return "zéro";

case 1: return "un";

case 2: return "deux";

case 3: case 4: return "trois ou quatre";

default: return "beaucoup";
}

Oui, il est possible de vaguement émuler en C une version extrêmement simplifiée du filtrage...
Il suffit de déclarer la variable dans le prototype, puis d'utiliser un switch en C.

Le filtrage à la caml est incomparablement plus léger, dans des exemples un peu consistants.
let truc x = match x with

( _ , [a::q, x] as w) -> .....
Là, ça devient un peu plus intéressant.

c'est tout l'intérêt : il n'y a pas de limite à la complexité du motif.
Là aussi. Ça, c'est de la polymorphie. En C++, on peut faire ça aussi: on travaillerait ici avec de l'héritage et du RTTI. En C pur, il faudra utiliser une structure type_id + union.

Le filtrage est à un tout autre niveau de simplicité.
En mathématiques, tu écris 2 × 3 de la même manière que 2 × 3,1 !

Si j'étais martien et si j'avais à ma disposition un ensemble plus grand de symboles, je l'écrirais différemment.
Les microprocesseurs l'écrivent différemment.
Ça a le grand avantage qu'on n'a pas besoin de connaître 10000 langages, un seul suffit!

Windows a l'avantage qu'on a pas besoin de connaître 10000 OS, un seul suffit!

Il existe d'autres langages, d'autres façons de penser.
Un programmeur qui connaît un peu d'assembleur, de C, de Forth, de Caml, de Lisp, aura une vision des problèmes bien plus riche que celui qui reste coincé dans son langage impératif bien bourrin.
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

89

Kevin Kofler a écrit :
[i]#define map(f,x,l) for(int i=0;i<sizeof(l)/sizeof(l[0]);i++) f(x,l)
Et maintenant, une ligne:
map(append,1,l);
smile
Reste à écrire append.

C'est du bricolage, et tu dois le refaire dès que f s'applique à trois éléments, ou tout autre changement...
Le seul bench que tu m'as indiqué montre le C gagnant! Pour moi, chaque point compte!

Met le coefficient du nombre de lignes de code à 1 au lieu de 0, et ocaml *écrase* tout le monde.
Arrête de raconter n'importe quoi! C'est très lourd de devoir transtyper explicitement à chaque fois.

Aussi lourd que de devoir déclarer les types de chaque variable. Et plus rare.
* En quoi est-ce plus simple de devoir réfléchir à chaque fois "qu'est-ce que le compilateur va donner comme type à ma variable" plutôt que de lui dire clairement ce qu'on veut?

Tu écris ton code comme si le type des variables était déclaré, et c'est le compilo qui se charge de le faire pour toi. Y a pas besoin de réfléchir à quel type choisir, justement.
Le cas où il y a une différence de type est quand-même rare. D'habitude, presque tous les paramètres sont soit des int, soit des pointeurs. Et si on passe un entier à la place d'un pointeur ou vice-versa, GCC donnera un warning.

C'est le "d'habitude", qui fait tache. L'intérêt de Caml, c'est qu'on n'a plus *jamais* besoin de se soucier des erreurs de type indécelables.
C'est à ça que servent les warnings de conversion implicite int<->pointeur! Les erreurs de types les plus courants sont les * ou & oubliés, et on aura un warning pour ça. Je me vois mal déclarer une variable en int et y mettre un flottant.

GCC rattrape donc uniquement les erreurs les plus courantes, là où Caml les rattrape toutes.

En mathématiques, tu écris 2 × 3 de la même manière que 2 × 3,1 !

Oui, mais tu peux dire que 2 * 3 = 3 + 3 = 2 + 2 + 2. Encore, pour 2 * 3,1, tu peux dire que c'est 3,1 + 3,1. Mais que dire de 3,1 * 3,1 ? Ou pire, de Pi * Sqrt(2) ? La multiplication réelle est définie par extension de la multiplication entière pour être une loi de corps, mais on perd des propriétés au passage ! De même pour l'exponentiation : 3^4 = 3 * 3 * 3 * 3, 3 ^ (1/2), on peut encore admettre que c'est sqrt(3), mais pour passer à 3^Pi, c'est une autre paire de manches... Même pour l'addition entière, tu peux dire que 4 + 5 = 4 + 1 + 1 + 1 + 1 + 1, alors que tu ne peux faire de même avec 2,3 + 6,8.

Les lois sur |R sont définies par extension des lois sur Z, mais les différences sont fondamentales ! Bon je sais bien que dans le proc, les réels sont finalement des rationnels, mais la différence, même si elle est moins marquée entre Q et Z, reste. Tu n'utilises d'ailleurs surement pas le même algo pour calculer 2^3 ou racine(3,5). Alors pourquoi considérer que 2 * 3 et 3,5 * 0,5 sont les mêmes opérations ?
Ça a le grand avantage qu'on n'a pas besoin de connaître 10000 langages, un seul suffit!

Toi par exemple, tu ne connais qu'un langage ?
avatar
I'm on a boat motherfucker, don't you ever forget

90

Et j'ai mieux pour les arbres en C pur:

typedef struct arbre {
union {
struct {
struct arbre *a;
struct arbre *b;
} noeud;
struct {
void *zero;
void *data;
} feuille;
};
} arbre;
Et après, il suffit de comparer x.feuille.zero ou x.noeud.a avec 0 pour savoir si on a un nœud ou une feuille.

Mes habitudes caméliennes me font vraiment trouver ça terriblement lourd.
EnCaml, une ligne suffit, ou deux si on veut avoir une jolie présentation
type 'a arbre = feuille of 'a | noeud of ('a arbre)*('a arbre);;

Et un filtrage trivial (noeud/feuille) permet de discriminer les deux cas possibles.
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou