Aller au contenu

vl-sort et liste de chaîne de caractères


Luna

Messages recommandés

Bonjour à toutes et à tous !

 

Je n'ai pas su trouver une réponse à mon problème (bon c'est pas vraiment un problème mais plus une question d'esthétique ..) : j'ai remarqué que la fonction (vl-sort) utilisée de manière "basique" donc avec la fonction de comparaison égale à '<, ben les valeurs ne sont pas rangées correctement...ou du moins pas comme on s'attend à ce qu'elles le soient !

 

Un exemple (parce que c'est plus simple à comprendre ^^) :

Commande: (setq lst '("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000"))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000")
Commande: (vl-sort lst '<)
("P_10" "P_1000" "P_8" "P_89" "P_899" "P_9" "P_90" "P_900" "P_91" "P_989" "P_990" "P_991" "P_999")
Commande: (vl-sort lst '(lambda (e1 e2) (< e1 e2)))
("P_10" "P_1000" "P_8" "P_89" "P_899" "P_9" "P_90" "P_900" "P_91" "P_989" "P_990" "P_991" "P_999")

Ici la liste est simplifiée, c'est-à-dire que la structure des chaînes de caractères est identique (un préfixe "P_" suivi d'un nombre) et on s'attends donc à ce que la fonction (vl-sort) retourne :

("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000")

 

Donc pour ce cas "particulier" car il ne peut pas être pris comme une généralité on peut le traiter comme suit :

Commande: (vl-sort lst '(lambda (e1 e2) (and (< e1 e2) (< (strlen e1) (strlen e2)))))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000")

On a le résultat escompté, mais je suis pas satisfaite pour autant ...

 

Parce que même si cela peut fonctionner pour un cas particulier, soit, mais je souhaiterais plutôt avoir une fonction qui fonctionne dans tous les cas (de manière générale quoi) ce qui m'éviterai d'avoir à gérer les cas particuliers à chaque fois (ce qui n'est parfois pas possible car on ne connais pas le résultat)

 

Si on change la liste, et que l'on passe sur un cas plus général, ça marche plus (ce qui est évident vu la simplicité de son écriture... :

Commande: (setq lst '("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test"))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test")
Commande: (vl-sort lst '(lambda (e1 e2) (and (< e1 e2) (< (strlen e1) (strlen e2)))))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "est" "un" "test")

 

Bref, là pour le coup, c'est l'idée ou plutôt la logique de résolution que je ne trouve pas...

Je n'ai pas forcément besoin d'un code complet tout bien fait qui réponde à ma question (ça gâche le plaisir parfois ^^), se serait plutôt des pistes de recherches sur la logique...

Je pars sur l'hypothèse, que je ne sais rien de la liste retournée/étudiée, donc dans l'idée il faut je pense se baser sur le tri de chaînes de caractères (via la fonction (vl-princ-to-string) pour s'assurer que l'on compare pas un œuf avec une poule) et effectuer un premier tri "grossier" qui consiste à les trier dans l'ordre alphabétique puis identifier la succession de chaînes identiques mais incrémentées pour les trier au sein de ces "successions" dans l'ordre des incrémentations. Pour au final obtenir (si on conserve notre dernier exemple) :

("Ceci" "PT22" "PT44" "PT_1" "PT_3" "P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "est" "test" "un")

 

Du coup je me pose la question du fonctionnement interne de la fonction (vl-sort), car si je comprend bien son fonctionnement logique sur une chaîne de caractères, elle compare les valeurs ASCII (un peu comme (vl-string->list) qui retourne la liste des codes ASCII de chaque caractères de la chaîne) pour les trier selon ces valeurs (et ainsi trier des nombres, ce qui est la seule..."possibilité" du point de vue informatique)..

 

Donc je suppose que pour pouvoir faire cette fonction générale, je dois me rapprocher du fonctionnement natif de la fonction pour trier mes valeurs d'après leur équivalence en liste ASCII auxquel cas... une fonction récursive ? (je n'ai jamais mis les pattes encore dans ce domaine alors pardon si je dis des bêtises --')

Et ainsi trier la liste entrée en argument selon son premier code ASCII, retourne la liste, trie d'après le second code ASCII, retourne la liste, trie selon le troisième, etc

 

Mais du coup..On se retrouve finalement à devoir gérer les écarts de longueurs de string (strlen) différents et qui font "disparaître" les chaînes de la liste donc potentiellement qu'il faudrait équilibrer les longueurs de liste en ajoutant un caractère nul (ascii "") -> 0, mais du coup, conserver la liste originelle pour éviter de renvoyer des erreurs ?

Et une telle méthode risque d'être....longue, voire très longue car si j'ai une liste contenant 5 000 valeurs et chaque valeur à une longueur de 30 caractères, bah ça va pas trier de manière instantanée..

 

Bref, je suis un peu perdue dans mes réflexions et un peu d'aide pour y voir plus clair me ferait grand plaisir !

Merci,

Luna

Lien vers le commentaire
Partager sur d’autres sites

Salut, merci d'avoir été aussi rapide ;)

 

voici ce que j'obtiens avec l'exemple ci-dessus :

Commande: (setq lst '("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000"))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000")
Commande: (acad_strlsort lst)
("P_10" "P_1000" "P_8" "P_89" "P_899" "P_9" "P_90" "P_900" "P_91" "P_989" "P_990" "P_991" "P_999")

 

Commande: (setq lst '("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test"))
("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test")
Commande: (acad_strlsort lst)
("Ceci" "est" "P_10" "P_1000" "P_8" "P_89" "P_899" "P_9" "P_90" "P_900" "P_91" "P_989" "P_990" "P_991" "P_999" "PT_1" "PT_3" "PT22" "PT44" "test" "un")

 

Du coup ça ne correspond pas vraiment à mes attentes... mais merci, il est vrai que c'est encore un peu obscur les fonctions acad_*, acet-* et autres car je ne vois pas encore leur différenciation par rapport au LISP vanilla, ActiveX et Visual...^^'

 

Luna

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Tout est normal et c'est un classique vl-sort sur des chaînes comme cad_strlsort effectuent un tri alphabétique où "100" est toujours avant "2".

Pour avoir un tri comme tu le souhaite il faut trier la liste suivant la valeur numérique uniquement.

 

(setq lst '("P_9" "P_989" "P_89" "P_10" "P_90" "P_1000" "P_91" "P_990" "P_899" "P_8" "P_900" "P_991" "P_999"))
(vl-sort lst '(lambda (a B) (< (atoi (substr a 3)) (atoi (substr b 3)))))

renvoie :

("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_989" "P_990" "P_991" "P_999" "P_1000")

 

Sur des listes longues ceci devrait être plus efficient

(mapcar	'(lambda (i) (nth i lst))
(vl-sort-i
  (mapcar '(lambda (s) (atoi (substr s 3))) lst)
  '<
)
)

 

Une autre solution (souvent adoptée est de toujours écrire les chaînes représentant des nombres sur n caractères : "P_0001", "P_0253", ...

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Merci (gile) !

 

J'avais déjà exploré cette solution mais elle fonctionne uniquement sur des cas particuliers (ici l'argument "Start" de (substr) est défini en dur à 3)...

Une autre solution (souvent adoptée est de toujours écrire les chaînes représentant des nombres sur n caractères : "P_0001", "P_0253", ...

Mon but est un peu..chiant je l'avoue, mais ce n'est que de la lecture de données, donc je ne peux pas vraiment savoir à quelle genre de données je dois m'attendre... D'où la complexité de ma demande ^^'

 

En revanche je dois avoir moyen de récupérer un système de pattern sous forme de liste si je prend comme hypothèse qu'un pattern est défini par une chaîne de caractères dont j'exclue les valeurs numériques en fin de chaîne uniquement, par exemple :

 

Commande: (setq lst '("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test "00-Calque1" "02-Calque1""))

("P_8" "P_9" "P_10" "P_89" "P_90" "P_91" "P_899" "P_900" "P_990" "P_989" "P_991" "P_999" "P_1000" "PT_1" "PT_3" "PT44" "PT22" "Ceci" "est" "un" "test" "00-Calque1" "02-Calque1")

Commande: (get-pattern lst)

"P_" "PT_" "PT" "Ceci" "est" "un" "test" "00-Calque" "02-Calque")

 

Cela devrait être possible via (wcmatch) je pense, et ensuite trier cette liste de pattern pour pouvoir créer des sublist avec l'ensemble des valeurs ayant un pattern commun et ensuite, lui appliquer ta solution via un (substr) et (strlen) du pattern pour trier les valeurs numériques au sein des sous-listes...

Un petit coup de (append) des sublists et le tour est joué ! Bon bah, je vais commencer à potasser tout ça pour voir ce que ça me donne mais au moins j'ai une piste !!

 

Arigato !

Lien vers le commentaire
Partager sur d’autres sites

Pour décomposer la chaîne en préfixe alphanumérique / suffixe numérique, tu peux utiliser les expressions régulières.

La fonction RegexpExecute permet de faire du groupage.

$ (RegExpExecute "P_90" "(^.+\\D)(\\d+$)" nil nil)
(("P_90" 0 ("P_" "90")))
_$ (RegExpExecute "02-Calque1" "(^.+\\D)(\\d+$)" nil nil)
(("02-Calque1" 0 ("02-Calque" "1")))
_$ (RegExpExecute "Ceci" "(^.+\\D)(\\d+$)" nil nil)
nil

Malgré tout, exécuter RegExpExecute à chaque comparaison (et il en a beaucoup dans un algorithme de tri) peut s'avérer couteux, c'est pourquoi j'utiliserais plutôt vl-sort-i sur la liste 'mappé' une seule fois en paires telles que le caddar renvoyé par RegExpExecute.

 

(setq lst '("P_8" "P_1000" "PT_1" "PT22" "PT_3" "P_91" "PT44" "00-Calque1" "P_90" "P_991" "Ceci" "est" "un" "test" "02-Calque1" "P_9"))

 

(mapcar	'(lambda (i) (nth i lst))
(vl-sort-i
  (mapcar '(lambda (s / m)
	     (if (setq m (RegExpExecute s "(^.+\\D)(\\d+$)" nil nil))
	       (caddar m)
	       (list s s)
	     )
	   )
	  lst
  )
  '(lambda (a B)
     (if (= (car a) (car B))
       (< (atoi (cadr a)) (atoi (cadr B)))
       (< (car a) (car B))
     )
   )
)
)

renvoie :

("00-Calque1" "02-Calque1" "Ceci" "PT22" "PT44" "PT_1" "PT_3" "P_8" "P_9" "P_90" "P_91" "P_991" "P_1000" "est" "test" "un")

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Oh well...

 

Tes connaissances sont sans limites !!! J'étais arrivé à un résultat concluant (du moins uniquement pour la liste donnée en exemple, je n'ai pas vraiment eu le temps de la tester encore vraiment...) avec ceci pour ma part :

 

(setq lst '("P_1" "PT_5" "P_4" "P_10" "PT3" "TCPOINT_89" "P_89" "p_2" "Calque1" "Test" "Calque2" "00-Calque3" "00-Calque30" "00-Calque4" "02-Calque6" "P_1000" "PT25" "TCPOINT_889"))

(defun Sort-list (lst flag / pattern-list sort sort-lst)

(setq sort '()
      pattern-list (get-pattern lst)
)
(foreach pattern (cond ((null flag) (reverse pattern-list)) (flag pattern-list))
	(setq sort-lst (vl-remove-if-not '(lambda (x) (wcmatch (strcase x) (strcase (strcat pattern "#*," pattern)))) lst)
	      sort-lst (mapcar '(lambda (i) (nth i sort-lst))
			        (vl-sort-i (mapcar '(lambda (x) (atoi (substr x (1+ (strlen pattern)))))
					    sort-lst
					   )
					   (cond ((null flag) '>) (flag '<))
				)
			)
	)
	(setq sort (append sort sort-lst))
)
sort

)

(defun get-pattern (lst / str pattern-list)

(setq lst (mapcar 'vl-princ-to-string lst))
(while lst
	(setq str (strcase (car lst)))
	(while (wcmatch str "*#")
		(setq str (substr str 1 (1- (strlen str))))
	)
	(setq pattern-list (cons str pattern-list)
	      lst (vl-remove-if '(lambda (x) (wcmatch (strcase x) (strcase (strcat str "*")))) lst)
	)
)
(if pattern-list
	(setq pattern-list (vl-sort pattern-list '(lambda (e1 e2) (< (strcase e1) (strcase e2)))))
	(princ)
)

)

;;--- (Sort-list lst flag) Renvoie ainsi :
;;--- flag = t   -> ("00-Calque3" "00-Calque4" "00-Calque30" "02-Calque6" "Calque1" "Calque2" "PT3" "PT25" "PT_5" "P_1" "p_2" "P_4" "P_10" "P_89" "P_1000" "TCPOINT_89" "TCPOINT_889" "Test")
;;--- flag = nil -> ("Test" "TCPOINT_889" "TCPOINT_89" "P_1000" "P_89" "P_10" "P_4" "p_2" "P_1" "PT_5" "PT25" "PT3" "Calque2" "Calque1" "02-Calque6" "00-Calque30" "00-Calque4" "00-Calque3")

 

Mais comme je l'ai dit, je ne l'ai pas vraiment poussée dans ses retranchements (des listes de 500 atomes, voire plus) donc il m'est difficile de savoir jusqu'où elle fonctionne vraiment...

Ce qui est sûr, ce que comparé à ton code (bien trop propre à mes yeux !!! <3), j'ai encore du boulot avant de d'optimiser au mieux mes fonctions (et éviter de lancer 150 boucles avec des (while), (foreach) et (mapcar) imbriqués !! ce qui ralenti l'exécution)

 

En tout encore merci !! Et puis je ne connaissais pas non plus tes fonctions régulières, je regarderais tout ça plus en détail car cela risque de mettre très utile et une source d'inspiration !

Bisous,

Luna

Lien vers le commentaire
Partager sur d’autres sites

Bon du coup, comme prévu, les limites ont été très rapides à trouver car si l'on prend des listes de nombres cela ne fonctionne plus (pour les deux fonctions) mais de toutes façons je ne devrais pas avoir uniquement des nombres au sein de mes chaînes de caractères donc ça devrait le faire ^^"

 

Je me suis permise de modifier légèrement ton code pour faire correspondre le même résultat (il semblerait que les deux méthodes possèdent des limites identiques donc il suffit juste de contrôler la liste en entrée pour éviter d'arriver à une erreur !)

(defun sort-list (lst flag)

(mapcar '(lambda (i) (nth i lst))
        (vl-sort-i
          (mapcar '(lambda (s / m)
                     (if (setq m (RegExpExecute s "(^.+\\D)(\\d+$)" nil nil))
                       (caddar m)
                       (list s s)
                     )
                   )
                  (mapcar 'vl-princ-to-string lst)
          )
          '(lambda (a B)
             (if (= (strcase (car a)) (strcase (car B)))
               ((cond (flag <) ((null flag) >)) (atoi (cadr a)) (atoi (cadr B)))
               ((cond (flag <) ((null flag) >)) (strcase (car a)) (strcase (car B)))
             )
           )
        )
)

)

 

Donc merci encore !! Je ne pensais pas que mon problème serait réglé aussi vite donc c'est super, comme toujours !

Bisous,

Luna

Lien vers le commentaire
Partager sur d’autres sites

Même si je trouve curieux de trier une liste avec des formats différents, le challenge est intéressant.

 

Une version polyvalente :

(defun sort-list (lst fun ignoreCase)
 (mapcar '(lambda (i) (nth i lst))
  (vl-sort-i
    (mapcar '(lambda (s / m)
	       (if (setq m (RegExpExecute s "(^.+\\D)?(\\d+$)" nil nil))
		 (cond
		   ((caddar m))           ; "A1" -> ("A" "1")
		   (T (list "" (caar m))) ; "12" -> ("" "12")
		 )
		 (list s "")              ; "AB" -> ("AB" "")
	       )
	     )
	    (if	ignoreCase
	      (mapcar 'strcase lst)
	      lst
	    )
    )
    '(lambda (a B)
       (if (= (car a) (car B))
	 (apply fun (list (atoi (cadr a)) (atoi (cadr B))))
	 (apply fun (list (car a) (car B)))
       )
     )
  )
 )
)

 

Exemple :

_$ (setq lst '("0_M8" "12" "A" "0_M12" "bar" "85" "2" "0_M6" "foo" "P"))
("0_M8" "12" "A" "0_M12" "bar" "85" "2" "0_M6" "foo" "P")

_$ (sort-list lst '< T)
("2" "12" "85" "0_M6" "0_M8" "0_M12" "A" "bar" "foo" "P")

_$ (sort-list lst '> nil)
("foo" "bar" "P" "A" "0_M12" "0_M8" "0_M6" "85" "12" "2")

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Salut !

 

Encore merci à toi (gile), j'ai dû corriger mes erreurs sur mes fonctions car ma fonction permettant d'identifier les pattern était...loin d'être utile car j'avais des risques importants de perte d'informations.

Mais en suivant la logique (de manière simplifiée bien sûr car je n'ai pas tout bien compris encore sur tes fonctions régulières et leur fonctionnement) j'ai pu obtenir ceci pour trier une liste random (tout type confondu) :

; Permet de trier une liste en ignorant la la casse et en évitant les erreurs de numérotations induites par la fonction (vl-sort) :
;--- La fonction (sort-list) possède 2 arguments
;--- lst correspond à la liste devant être triée, pouvant contenir tout type d'élément (chaînes de caractères à conseiller)
;--- flag défini si l'on souhaite trier la liste dans l'ordre croissant, ou décroissant
;	flag = 0 -> Trie dans l'ordre croissant
;	flag = 1 -> Trie dans l'ordre décroissant

;--- Renvoie la liste une fois triée
(defun Sort-list (lst flag / a B)

(if (not (member flag '(0 1)))
	(setq flag 0)
)
(mapcar 'car
	(vl-sort
		(get-pattern-list lst)
		'(lambda (a B)
		 	(if (= (caadr a) (caadr B))
				((cond ((= flag 0) <) ((= flag 1) >)) (atoi (cdadr a)) (atoi (cdadr B)))
				((cond ((= flag 0) <) ((= flag 1) >)) (caadr a) (caadr B))
			)
		)
	)
)

)

; Permet de décomposer chaque membre d'une liste sous forme de sous-liste composée de l'élément original, puis de sa racine (ou pattern) et de son incrément :
;--- La fonction (get-pattern-list) possède 1 argument
;--- lst correspond à la liste que l'on souhaite étudier

;--- Renvoie une liste avec la décomposition des membres sous la forme (str . (pattern . num)) avec :
;	- str l'élément (avec conservation du type) qui sera décomposer
;	- pattern correspond à une chaîne de caractères majuscule correspondant à l'élément str dont on a retiré les chiffres situés en fin de chaîne uniquement (hypothèse)
;	- num correspond à une châine de caractères correspondant à l'incrément du pattern, soit l'ensemble des chiffres situés en fin de chaîne (hypothèse)
;		Ex. : Avec str = "Calque1", on a pattern = "CALQUE" et num = "1" ; Avec str = "Test", on a pattern = "TEST" et num = "" ; Avec str = 15, on a pattern = "" et num = "15" ; ...
(defun get-pattern-list (lst / str pattern)

(mapcar '(lambda (str / pattern)
		(if (distof (vl-princ-to-string str) 2)
			(cons str (list (cons "" (vl-princ-to-string str))))
			(progn
				(setq pattern (vl-princ-to-string str))
				(while (wcmatch pattern "*#")
					(setq pattern (substr pattern 1 (1- (strlen pattern))))
				)
				(cons str (list (cons (strcase pattern) (substr (vl-princ-to-string str) (1+ (strlen pattern))))))
			)
		)
	 )
	lst
)

)

 

En revanche j'aime beaucoup la possibilité d'appliquer une fonction au choix pour se permettre d'avoir un spectre plus large sur son utilisation !

Après pour la prise en compte des majuscules ou non je trouve cela étrange de trier et d'avoir les "a" après les "P" par exemple donc c'est pour cela que je me le suis imposé.

 

(setq lst '("Calque10" "Bloc" 8 "Calque1" "calque3" "Bloc5" 4 88 "90" "Bloc30" "Calque7"))
("Calque10" "Bloc" 8 "Calque1" "calque3" "Bloc5" 4 88 "90" "Bloc30" "Calque7")

(get-pattern-list lst)
(("Calque10" ("CALQUE" . "10")) ("Bloc" ("BLOC" . "")) (8 ("" . "8")) ("Calque1" ("CALQUE" . "1")) ("calque3" ("CALQUE" . "3")) ("Bloc5" ("BLOC" . "5")) (4 ("" . "4")) (88 ("" . "88")) ("90" ("" . "90")) ("Bloc30" ("BLOC" . "30")) ("Calque7" ("CALQUE" . "7")))

(sort-list lst 0)
(4 8 88 "90" "Bloc" "Bloc5" "Bloc30" "Calque1" "calque3" "Calque7" "Calque10")

(sort-list lst 1)
("Calque10" "Calque7" "calque3" "Calque1" "Bloc30" "Bloc5" "Bloc" "90" 88 8 4)

 

En tout cas merci beaucoup, cela m'a été très formateur, notamment sur l'utilisation réelle de (lambda). J'avais encore du mal à la voir comme une fonction (defun) non nommée..^^"

 

Bisous,

Luna

Lien vers le commentaire
Partager sur d’autres sites

  • 2 semaines après...

Bonjour,

Je vois que le problème a été solutionné avec brio, en parcourant ce post je vois que tu parles de pattern pour des expressions AlphaNumérique avec wcmatch

En revanche je dois avoir moyen de récupérer un système de pattern sous forme de liste si je prend comme hypothèse qu'un pattern est défini par une chaîne de caractères dont j'exclue les valeurs numériques en fin de chaîne uniquement…

Au cas ou, je souhaitais faire remarquer qu’en natif en AutoLisp il n'y a pas que wmatch qui permet cela, il y a aussi vl-string-right-trim et vl-string-left-trim qui supportent un jeu de charactères en argument.

 

En fouillant dans mes archives j’ai retrouvé la fonction sur laquelle je m’appuyais pour trié des nomenclatures d’éléments béton, éléments précédé d’une lettre produit suivie d’un numéro d’élément au format string.

;; AlphaNum2lst                                                  VDH-Bruno 
;; Modifie une chaine Alphanumérique en une liste au format (string number)
;; Exemple:  (AlphaNum2lst "abC_151")  --> ("abC_" 151)
;;           (AlphaNum2lst "abC_")     --> ("abC_" nil)
;;           (AlphaNum2lst "151")      --> ("" 151)
(defun AlphaNum2lst (alphanum / alpha)
 (list	(setq alpha (vl-string-right-trim "0123456789" alphanum))
(read (substr alphanum (1+ (strlen alpha))))
 )
)

Cela n'enlève rien aux excellentes et puissantes fonctions de (gile) sur les expressions régulières dont la polyvalence n’a pas d’égale, mais comme bien souvent la polyvalence à un coût.

 

Donc pour le fun sur la proposition du message n°9, une écriture possible de la fonction de tri :

(defun sortAlphaNum (lst fun Case)
 (mapcar '(lambda (i) (nth i lst))
  (vl-sort-i
    (mapcar 'AlphaNum2lst
	    (if	Case
	      (mapcar 'strcase lst)
	      lst
	    )
    )
    '(lambda (a B)
       (if (= (car a) (car B))
	 ((eval fun) (cadr a) (cadr B))
	 ((eval fun) (car a) (car B))
       )
     )
  )
 )
)

_$ (setq lst '("0_M8" "12" "A" "0_M12" "bar" "85" "2" "0_M6" "foo" "P"))
("0_M8" "12" "A" "0_M12" "bar" "85" "2" "0_M6" "foo" "P")
_$ (sortAlphaNum lst '> T)
("P" "foo" "bar" "A" "0_M12" "0_M8" "0_M6" "85" "12" "2")
_$ (sortAlphaNum lst > nil)
("foo" "bar" "P" "A" "0_M12" "0_M8" "0_M6" "85" "12" "2")
_$ (sortAlphaNum lst '< T)
("2" "12" "85" "0_M6" "0_M8" "0_M12" "A" "bar" "foo" "P")
_$ (sortAlphaNum lst < nil)
("2" "12" "85" "0_M6" "0_M8" "0_M12" "A" "P" "bar" "foo")

(Ps: Pour l’anecdote, comme la comparaison se fait 2 à 2, j’ai préféré eval à apply pour la fonction de comparaison, de fait si on oubli de quoté l’argument ça n’a pas d’incidence)

Salutations

Apprendre => Prendre => Rendre

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

En effet c'est une optimisation fort intéressante ! je connaissais déjà les fonctions vl-string-right-trim et vl-string-left-trim (que j'utilise dans une fonction récurrente) mais j'avais pour ainsi dire, complètement oublié la possibilité de spécifier l'ensemble des caractères numériques de la sorte..

 

Bravo à toi, ta fonction me semble parfaite ;)

 

En tout cas, je comprend mieux pourquoi j'aime tant la programmation, il n'existe pas un unique chemin pour parvenir au résultat escompté ! Certains sont plus sinueux que d'autres mais chaque chemin mérite d'être tracé car ils reflètent tous à leur manière la logique et l'ingéniosité de chacun donc merci à toi pour ce partage ! <3

 

Même si je trouve curieux de trier une liste avec des formats différents, le challenge est intéressant.

Pour info (gile) l'idée de cette fonction est d'être utilisable sur toute forme de liste (bien sûr sur les listes composées elles-mêmes de listes, vaut mieux passer par un (mapcar) + (lambda) adapté à la situation) et notamment : la liste des calques !

 

Car vouih la liste des calques et une zone de guerre, où chacun nomme comme il veut, par des chaînes de caractères en minuscule, majuscule, majuscule au début, avec des chiffres, ... bref on connaît le type de chaque élément (des strings) mais leur contenu est une inconnue (surtout en partant déjà avec par défaut le calque "0" hihi) du coup il me fallait une fonction un peu touche à tout ! ;)

Voili voilou !

 

Bisous,

Luna

Lien vers le commentaire
Partager sur d’autres sites

  • 1 an après...

Bonjour

 

j'ai une liste de présentation ( 150) que je voudrais trier  en premier sur les  (caractères 5 à 13) ( variable ) ( en rouge ) puis sur  les  derniers caractères en bleu  

est ce que ta fonction permet de faire ca ???

sachant que je peux transformé  le totozero-2-A en totozero-02-A pour avoir le meme nombre de caractere a prendre en compte

avant tri :

list =A4H totozero-13-A-I-4-k-9 1-10 PL009-,A4V totozero-13-A-I-4-k-9 1-10 PL019-,A4H totozero-7-A-I-4-k-9 1-10 PL009-,A4V totozero-7-A-I-4-k-9 1-10 PL010-,A4V totozero-2-B-I-4-k-9 1-7 PL007-,A4H totozero-2-A-I-4-k-9 1-20 PL020-

apres tri :

list =A4H totozero-2-A-I-4-k-9 1-20 PL020-,A4V totozero-2-B-I-4-k-9 1-7 PL007-,A4H totozero-7-A-I-4-k-9 1-9 PL009-,A4V totozero-7-A-I-4-k-9 1-10 PL010-, A4H totozero-13-A-I-4-k-9 1-9 PL009-,A4V totozero-13-A-I-4-k-9 1-19 PL019-

 

merci

 

Phil

 

Autodesk Architecture 2023 sous windows 11 64

24 pouces vertical + 30 pouces horizontal + 27 pouces horizontal

Lien vers le commentaire
Partager sur d’autres sites

hello

j'ai finalement écrit un bout de lisp

pas tres propre mais ca marche, si ca intéresse

 

ca trie les noms de présentations en fonction de la plage en rouge puis la plage en bleu

attention il ne faut pas d'autres noms de présentations  plus court que ceux que vous voulez trier

on choisit le debut et la longueur de plage rouge en fonction de :

A4H totozero-2-A-I-4-k-9 1-20 PL020-

(defun c:cnptrier4axet4a2fin (/ acdoc layouts layout layoutsname name)
  (setq cnpcdepuisdebut1 (atoi (getcfg "APPDATA/CNPCDEPUISDEBUT1")))
  (initget 4)
  (setq tmp (getint
              (strcat "\nENTRER UN NOMBRE POUR DEFINIR LE DEPART DE LA PLAGE DE TRIE DEPUIS LE DEBUT DU NOM [ MINIMUM : 1]<"
                      (rtos cnpcdepuisdebut1 2 0)
                      ">: "
              )
            )
  )
  (if tmp
    (setq cnpcdepuisdebut1 tmp)
  )
  (setcfg "APPDATA/CNPCDEPUISDEBUT1" (rtos cnpcdepuisdebut1 2 0))
  (setq cnpcsurx1 (atoi (getcfg "APPDATA/CNPCSURX1")))
  (initget 4)
  (setq tmp (getint (strcat "\nENTRER UN NOMBRE POUR DEFINIR LA PLAGE DE TRIE PRIMAIRE DU NOM <"
                            (rtos cnpcsurx1 2 0)
                            ">: "
                    )
            )
  )
  (if tmp
    (setq cnpcsurx1 tmp)
  )
  (setcfg "APPDATA/CNPCSURX1" (rtos cnpcsurx1 2 0))
  (setq acdoc (vla-get-activedocument (vlax-get-acad-object)))
  (setq layouts (vla-get-layouts acdoc))
  ;; récupérer la liste des noms de présentations
  (vlax-for layout layouts (setq layoutsname (cons (vla-get-name layout) layoutsname)))
  ;; supprimer la présentation "Model" de cette liste
  (setq layoutsname (vl-remove "Model" layoutsname))
  ;; nombre de presentations
  (setq nblayouts (length layoutsname)
        listelayoutsdecomp nil
        listelayoutrecomp nil
  )
  ;;décomposer le nom de la présentation en 5 morceaux et en faire une liste
  (foreach name layoutsname
    (setq nbc (strlen name))
    (setq listelayoutsdecomp (cons (list (substr name 1 (- cnpcdepuisdebut1 1))
                                         (substr name cnpcdepuisdebut1 cnpcsurx1)
                                         (substr name (+ cnpcdepuisdebut1 cnpcsurx1) (- nbc 3 (+ cnpcdepuisdebut1 cnpcsurx1)))
                                         (substr name (- nbc 3) 3)
                                         (substr name nbc)
                                   )
                                   listelayoutsdecomp
                             )
    )
  )
  ;; trier la liste sur 2 et 4 morceaux
  (setq listelayoutsdecomp1 (vl-sort listelayoutsdecomp
                                     '(lambda (a b)
                                        (if (eq (cadr a) (cadr b))
                                          (< (cadddr a) (cadddr b))
                                          (< (cadr a) (cadr b))
                                        )
                                      )
                            )
  )
  ;;reconstituer le noms des présentations et la lister
  (foreach decomp listelayoutsdecomp1
    (setq listelayoutrecomp (cons (strcat (nth 0 decomp) (nth 1 decomp) (nth 2 decomp) (nth 3 decomp) (nth 4 decomp))
                                  listelayoutrecomp
                            )
    )
  )
  ;inverser la liste
  (setq listelayoutrecomp (reverse listelayoutrecomp))
  ;; attribuer l'ordre à chaque présentation
  (setq i 1)
  ;; l'ordre 0 est réservé à la présentation "Model"
  (foreach name listelayoutrecomp
    (setq layout (vla-item layouts name))
    (vla-put-taborder layout i)
    (princ (strcat "\n" (itoa i) " SUR " (itoa nblayouts) " : " name))
    (setq i (1+ i))
  )
  (getlayouts "POUR VERIFICATION DES NOMS DE PRESENTATION" t)
  (princ)
)

a+

 

Phil

Autodesk Architecture 2023 sous windows 11 64

24 pouces vertical + 30 pouces horizontal + 27 pouces horizontal

Lien vers le commentaire
Partager sur d’autres sites

Il y a 9 heures, PHILPHIL a dit :

sachant que je peux transformé  le totozero-2-A en totozero-02-A pour avoir le meme nombre de caractere a prendre en compte

C'est la première chose à faire.

Ensuite, la fonction native vl-sort permet tout à fait de répondre à ta demande.

(vl-sort lst
	 '(lambda (s1 s2 / x1 x2)
	    (if	(= (setq x1 (substr s1 5 13))
		   (setq x2 (substr s2 5 13))
		)
	      (< (substr s1 (- (strlen s1) 5))
		 (substr s2 (- (strlen s2) 5))
	      )
	      (< x1 x2)
	    )
	  )
)

 

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité