ProgHelp

Une communauté intelligente et active

F# - Décoration

Message par Patak » 12 Décembre 2016, 21:52

Voilà mon premier code F#, il permet de générer une décoration (sous forme de string) pour une matrice de string ...

On l'appelle comme ceci :

let result = decorate [["Le F#"; "ca marche bien"; "y'a jamais d'erreur"];
["c'est beau"; "c'est fonctionnel"; "c'est rigolo"];
["c'est du .NET"; "blabla"; "et blabla"]]


Et on obtient :

+-------------+-----------------+-------------------+
| Le F#| ca marche bien|y'a jamais d'erreur|
+-------------+-----------------+-------------------+
| c'est beau|c'est fonctionnel| c'est rigolo|
+-------------+-----------------+-------------------+
|c'est du .NET| blabla| et blabla|
+-------------+-----------------+-------------------+


Sans plus attendre :

let decorate (list: (string list list)) =

//Encadre strings avec des séparateurs
let encadre sep strings = sep + String.concat sep strings + sep

let columnCount = list.[0].Length

let columnMaxLengths =
[for i in 0..columnCount-1 -> //Pour chaque column
list
|> List.map (fun ligne -> String.length ligne.[i])
|> List.max] //On recupere la largeur max

let decoration = encadre "+"
(columnMaxLengths |> List.map (fun maxLength -> String.replicate maxLength "-"))

encadre
(decoration + "\n")
(list |> List.mapi (fun x ligne ->
(encadre
"|"
(columnMaxLengths |> List.mapi (fun y maxLength -> //On genere les |valeur1| valeur2|
(String.replicate (maxLength - String.length list.[x].[y]) " ") + list.[x].[y]))
) + "\n"
))


J'accueille bras ouverts vos critiques :D
Image


SPOILER : AFFICHER


Avatar de l’utilisateur
Patak
Administrateur
 
Messages : 1144
Points d'honneur : -666 PH
Inscription : 30 Oct 2012

Message par Sehnsucht » 12 Décembre 2016, 23:49

Pour le fun :D (parce que je pense pas que la structure de donnée choisie soit la plus adaptée à la base)
let decorate table =
let inline flip f y x = f x y
let inline max (x, y) = max x y

let allLengths = List.map <| List.map String.length <| table
let columnCount = List.max << List.map List.length <| table
let folder maxLengths = List.zip maxLengths >> List.map max

let columnSizes = List.fold folder <| List.replicate columnCount 0 <| allLengths

let rowBuilder sep strs = sprintf "%s%s%s" sep <| String.concat sep strs <| sep
let rightAligner (length, str) = sprintf "%*s" length str

let rowSep = rowBuilder "+" << (List.map <| flip String.replicate "-") <| columnSizes
let dataRow = rowBuilder "|" << List.map rightAligner << List.zip columnSizes

let folder output rowList = sprintf "%s\n%s\n%s" output (dataRow rowList) rowSep

List.fold folder rowSep table
Censément, quelqu'un de sensé est censé s'exprimer sensément.

Image
Avatar de l’utilisateur
Sehnsucht
Membre VIP
 
Messages : 273
Points d'honneur : 210 PH
Inscription : 10 Fév 2013

Message par Patak » 13 Décembre 2016, 03:15

J'ai à peu près compris comment tu procèdes !

Bonne idée le flip !

C'est drôle que tu préfères utiliser <| plutôt que |>
Est-ce une préférence au niveau de la lisibilité de ton code ?

let decorateAha table =
let inline flip f y x = f x y
let inline max (x, y) = max x y

let allLengths = table |> List.map (List.map String.length)
let columnCount = table |> List.map List.length |> List.max

/////Début de mon incompréhension
let folder maxLengths = List.zip maxLengths >> List.map max

let columnSizes = allLengths |> List.fold folder (List.replicate columnCount 0)
/////Fin de mon incompréhension

let rowBuilder sep strs = sep |> (String.concat sep strs |> sprintf "%s%s%s" sep)
let rightAligner (length, str) = sprintf "%*s" length str

let rowSep = columnSizes |> (flip String.replicate "-" |> List.map >> rowBuilder "+")
let dataRow = columnSizes |> List.zip >> List.map rightAligner >> rowBuilder "|"

let folder output rowList = sprintf "%s\n%s\n%s" output (dataRow rowList) rowSep

List.fold folder rowSep table


Je ne comprends pas la partie entre les commentaires :?
Image


SPOILER : AFFICHER


Avatar de l’utilisateur
Patak
Administrateur
 
Messages : 1144
Points d'honneur : -666 PH
Inscription : 30 Oct 2012

Message par Sehnsucht » 13 Décembre 2016, 22:08

Entre <| et |> ça dépend ; faut se méfier de la priorité des opérateurs aussi qui peut jouer mais c'est variable chez moi la plupart du temps ça dépend de là où on veut mettre l'accent. après souvent c'est le |> qui est préféré parce que déjà pour ceux qui viennent du monde Objet

truc |> action1 |> action2 ça rappelle un truc.Action1().Action2() et on y voit bien la notion de séquentialité truc devient machin devient bidule.

Dans l'autre sens ça met plus l'accent sur l'objectif sur ce qu'on veut obtenir plutôt que sur comment on l'obtient. Si on prend ceci :

let columnCount = table |> List.map List.length |> List.max
let columnCount = List.max << List.map List.length <| table

Dnas le premier cas on voit bien le cheminement des étapes ; alors que dans le second on voit que le résultat c'est le truc maxi d'une liste (après faut avoir de bon choix de noms pour les choses) si je veux savoir plus du détail je regarde la suite, mais sinon j'en ai pas besoin

Mais bon tout ça c'est vachement subjectif ; là y'avait aussi une partie où je voulais limiter au max l'utilisation des parenthèses :hap:


Pour l'incompréhension, l'objectif de ces 2 lignes c'est d'obtenir la taille des colonnes, autrement dit la taille du plus grand élément de chaque colonne.
Pour ça je part de ma liste de liste allLengths qui contient la longueur de chaque chaine, et là en gros faudrait que je garde le plus grand entre chaque premier élément de chaque sous liste, puis le plus grand entre chaque second élément de chaque sous liste, etc et ça avec des listes chainées comme ça c'est pas le plus évident (sans "tricher" avec une boucle)

du coup ce que je fais c'est un fold (Aggregate) ; je pars d'une liste avec autant de "case" qu'il y a de colonne, et pour chaque liste de ma table (pour chaque ligne) je construis une nouvelle liste où chaque élément et le max entre la valeur d'un élément de la liste (= la taille de son texte) et le max jusque là (= le max gardé au tour d'avant).
ce qui fait qu'à la fin j'ai une liste contenant la taille de chaque colonne.

Pour construire cette nouvelle liste (folder) à chaque tour à partir des 2 listes (la ligne courante et le liste max du tour d'avant) je les zippe (passer de 2 listes à une liste de paires) et je garde le max de chaque paire
Censément, quelqu'un de sensé est censé s'exprimer sensément.

Image
Avatar de l’utilisateur
Sehnsucht
Membre VIP
 
Messages : 273
Points d'honneur : 210 PH
Inscription : 10 Fév 2013

Message par Patak » 14 Décembre 2016, 02:46

Super ! Tout est clair !

Dans l'autre sens ça met plus l'accent sur l'objectif


Ok !

je voulais limiter au max l'utilisation des parenthèses


C'était ma première hypothèse ...



Super ton explication sur les deux lignes (avec le fold ...) !
Image


SPOILER : AFFICHER


Avatar de l’utilisateur
Patak
Administrateur
 
Messages : 1144
Points d'honneur : -666 PH
Inscription : 30 Oct 2012


Retour vers F#

cron
  • Qui est en ligne ?
  • Consulter les nouveaux messages
  • Consulter les messages sans réponse
  • Au total, il y a 0 utilisateur en ligne :: 0 inscrit, 0 invisible et 0 invité (basé sur le nombre d’utilisateurs actifs des 5 dernières minutes)
  • Le nombre maximum d’utilisateurs en ligne simultanément a été de 272 le 12 Mars 2015, 03:11
  • Utilisateur(s) parcourant ce forum : Aucun utilisateur inscrit et 0 invité(s)