Suite

Copier la classe d'entités dans la classe d'entités en boucle et une liste de champs dans le générateur de modèles

Copier la classe d'entités dans la classe d'entités en boucle et une liste de champs dans le générateur de modèles


J'ai une géodatabase à plusieurs fichiers contenant sept entités (polygone). Je souhaite les copier dans une nouvelle entité dans une autre géodatabase fichier vide (au lieu de supprimer les champs… j'ai eu les mêmes problèmes avec cet outil)

Ce que je veux : J'ai juste besoin de 20 champs sur 60 et de la nouvelle fonctionnalité dans une nouvelle commande. J'ai essayé de configurer cela dans Modelbuilder, mais il y a plusieurs problèmes que je ne reçois pas. J'utiliserai l'itérateur pour les fonctionnalités qui sont claires. Aucun problème. Puis fonctionnalité à fonctionnalité outil. Jusqu'à ce point est clair pour moi. Je crée une variable avec mes noms de champs nécessaires dans l'ordre où je les veux. clair pour moi. Mais ce qui n'est pas clair :

  1. Comment dire prendre cette liste à chaque fois pour chaque caractéristique de la carte de terrain ?
  2. Comment les réorganiser ? comme variable-list l'a-t-il?

Est-ce possible avec model builder ou ai-je besoin d'un script pour cela ? J'ai environ trois millions de polygones dans chaque entité…


Je vois que mon expression anglaise n'était pas la meilleure. Je suis allemand ;-) Je vais simplifier un peu plus facilement.

Un contenu Filegeodatabase : 7 entités (polygones) et environ 3 millions d'attributs et 60 champs. Et j'ai 4 de ces bases de données de fichiers… Chaque champ de chaque fonctionnalité a le même nom. C'est le résultat de nombreuses étapes d'intersection. Mais pour le produit final, j'ai juste besoin de 20 de ces champs dans l'ordre souhaité. Oui, je suis d'accord avec vous, je peux utiliser l'outil, mais je dois donc cliquer 40 fois sur chacun pour avoir ma nouvelle fonctionnalité mais je ne peux pas déplacer l'ordre dans la fenêtre "fieldmap".

L'autre option que je pourrais faire est de copier d'abord la fonctionnalité dans la nouvelle base de données de fichiers et de supprimer les champs indésirables, mais à la fin, c'est le même temps de clic que je n'ai pas, donc cela doit fonctionner de manière automatisée ou? Dans l'outil de suppression de champ, il apporte des cases à cocher, je peux tout sélectionner ou tout désélectionner et rechercher dans les champs où sont mes recherchés et ainsi je pourrais l'obtenir. Je peux le faire pour un. Et je ne peux pas rappeler l'outil à partir de la liste des résultats, car ensuite il lit à nouveau la liste de la fonctionnalité, et je peux recommencer à choisir… est-ce que c'est clair expliqué ? En cherchant sur le net, je n'ai trouvé aucune solution utile. L'exemple de code de delete-tool dans la ressource arcgis-help que je n'ai pas pu exécuter…


J'ai eu peu de succès dans la réorganisation des champs dans le générateur de modèles, vous ne savez pas si l'on peut même le faire ? J'avais écrit un peu de python qui pourrait créer une nouvelle table qui créerait une table de sortie avec les champs dans un ordre spécifié par l'utilisateur, le code est ci-dessous, vous pouvez l'adapter à vos besoins.

Dans cet exemple, une table avec 4 champs est réordonnée et stockée IN_MEMORY.

inputTable = "ABC" # Ceci est une table chargée dans ArcMap outputTable = "XYZ" # Obtenir les mappages de champs de la table d'entrée fieldMappings = arcpy.FieldMappings() fieldMappings.addTable(inputTable) # Créer un objet FieldMappings vide newFieldMappings = arcpy.FieldMappings( ) # Ajoutez les champs dans l'ordre souhaité. Remarque : l'index de champ doit être connu newFieldMappings.addFieldMap(fieldMappings.getFieldMap(3)) newFieldMappings.addFieldMap(fieldMappings.getFieldMap(0)) newFieldMappings.addFieldMap(fieldMappings.getFieldMap(2)) newFieldMappings.addFieldMap(fieldMappings.getFieldMap(1)) newFieldMapping(0) # Créer une table arcpy.TableToTable_conversion(inputTable, "IN_MEMORY", outputTable, None, newFieldMappings)

Itérer la sélection d'entités

Cet outil est destiné à être utilisé dans ModelBuilder et non dans les scripts Python.

Iterate Feature Selection appelle Make Feature Layer pour effectuer une nouvelle sélection et crée deux sorties : Selected Features et Value. La sortie Entités sélectionnées est une couche d'entités et peut être utilisée dans d'autres outils de géotraitement qui acceptent une couche d'entités dans ModelBuilder.

Les classes d'entités complexes, telles que les annotations et les cotes, ne sont pas prises en charge par cet outil.

Plusieurs groupes par champ peuvent être utilisés pour la sélection. Si un ou plusieurs champs de groupe par sont sélectionnés, le nombre d'itérations est déterminé par le nombre de combinaisons uniques des champs de groupe. Pour chaque itération, la sélection de la couche d'entités en sortie est déterminée par le nombre d'enregistrements qui correspondent à la combinaison donnée du groupe par champs. Si plusieurs groupes par champ sont choisis, les valeurs sont concaténées <field value1>_<field value2> dans le nom de la variable en ligne de sortie. Voir un exemple ci-dessous où deux champs de groupe : Nom de l'ouragan et Catégorie de l'ouragan sont utilisés. La valeur de sortie est 'Katrina_H5' et ainsi de suite pour les autres valeurs.

Si aucun groupe par champ n'est choisi, la valeur de sortie est le groupe par champ ObjectID et la sélection est un enregistrement par sélection. Si le tableau n'a pas de champ ObjectID (OID), tel qu'un tableau Excel, la valeur est vide.

L'outil a deux sorties : les entités sélectionnées de sortie et la valeur de groupe du champ pour les entités sélectionnées, qui pourraient être utilisées comme variable en ligne %Value% dans d'autres outils.

La couche d'entités temporaire peut être enregistrée en tant que fichier de couche à l'aide de l'outil Enregistrer dans un fichier de couche ou enregistrée en tant que nouvelle classe d'entités à l'aide de l'outil Copier les entités.

Si un itérateur est ajouté à un modèle, tous les outils du modèle itèrent pour chaque valeur de l'itérateur. Si vous ne souhaitez pas exécuter chaque outil du modèle pour chaque valeur itérée, créez un sous-modèle/modèle dans un modèle/modèle imbriqué qui contient uniquement l'itérateur et ajoutez-le en tant qu'outil de modèle dans le modèle principal.


Vous pouvez utiliser document.querySelectorAll('.label') pour sélectionner toutes les div avec une étiquette de classe et boucler dessus pour obtenir son contenu :

Vous pouvez utiliser un sélecteur de requête et forEach pour obtenir le textContent de chacun des div avec une certaine classe.

Vous pouvez utiliser le sélecteur de classe comme suit :

Une simple itération de boucle à travers le tableau webelement et un concat de chaîne devraient faire l'affaire !

Je parcourrais comme ça. Initialement, obtenez tous les divs .user-field et ensuite juste pourChaque les nœuds que vous avez reçus.

Vous pouvez utiliser document.getElementsByClassName('label') pour sélectionner tous les div avec l'étiquette de classe


Cela ajoutera un champ personnalisé à l'édition de l'élément de menu. Cependant, vous ne verrez pas le champ personnalisé tant que vous n'aurez pas enregistré le menu pour l'élément de menu nouvellement ajouté. Lorsque vous ajoutez un élément pour la première fois, vous ne verrez que les champs standard. Après l'enregistrement, le nouveau champ sera également disponible pour modification. Si vous ajoutez des éléments de menu supplémentaires, vous devrez à nouveau enregistrer le menu pour voir et modifier le champ personnalisé pour les éléments nouvellement ajoutés. J'espère que cette explication a du sens.

J'ai pu créer et utiliser un marcheur personnalisé pour créer les champs d'édition des éléments de menu. Lorsque vous ajoutez pour la première fois un élément de menu au menu, il semble le créer entièrement avec jQuery au lieu du marcheur, c'est pourquoi vous ne verrez le champ personnalisé qu'après l'enregistrement.

J'ai appelé de manière créative le champ personnalisé que j'ai ajouté "personnalisé". Vous pouvez donc modifier/éditer/dupliquer les valeurs dont vous avez besoin. Il y a des commentaires dans le marcheur pour montrer où j'ai ajouté le nouveau champ.


Choisir les bons identifiants

Quand je vois un nom comme des options, je pense à quelque chose qu'un joueur peut choisir comme "un joueur" (c'est-à-dire jouer contre l'ordinateur) ou "multijoueur". Un meilleur nom serait conseil parce que c'est ce que c'est.

tordu ? Cela signifie-t-il "vrai ou faux" ? Chaque booléen est vrai ou faux. Cela ne reflète pas le sens qu'il a dans ce contexte. Mieux : isValidInt . Mais j'inline cette variable (voir plus loin).

Conseil() . Un tableau est une chose, mais ici il représente une action. Mieux PrintBoard() . Idem pour PrintIntro() .

Le champ Jouer commence par une majuscule et est donc dit PascalCase. Ceci est réservé aux noms de type, aux noms de méthode et aux noms de propriété. Utilisez camelCase pour les champs, les paramètres et les variables. Voir C# Coding Standards and Naming Conventions pour une liste complète.

La logique et la structure peuvent être simplifiées et clarifiées.

À de nombreux endroits, vous calculez le tour % 2 == 0 . Au lieu de cela, je suggère de calculer directement le nombre de joueurs. Nous déclarerions static int player = 0 et une méthode obtenant le joueur suivant ainsi qu'une méthode de commutation entre les joueurs

Cela conduit à une autre simplification. La première instruction if-else peut être remplacée par (j'utilise l'interpolation de chaîne ici)

Je voudrais insérer la variable que vous avez appelée torf et déclarer playerInput dans une déclaration de variable de sortie comme celle-ci.

J'ai utilisé la correspondance de modèle pour tester si la valeur est dans une plage valide. Mais vous pouvez la remplacer par une expression booléenne classique si vous préférez ou si vous utilisez une version antérieure à C# 9.0.

Vous différenciez deux cas en faisant de même avec une petite exception (affichée avec les nouveaux identifiants) :

Cela peut facilement être simplifié en déclarant un nouveau champ playerMark en tant que tableau. Puisque nous y sommes, nous pouvons faire la même chose pour les noms de joueurs. Cela nous évite la conversion base-0 en base-1 des numéros de joueurs pour l'affichage et pourrait facilement être étendu pour stocker de vrais noms.

Même sans ce nouveau champ statique, vous pouvez déplacer tout sauf la première ligne de l'instruction if-else, car ils sont exactement les mêmes dans les deux cas :

La méthode WinCondition teste les conditions et imprime sur la console. Et il le fait en répétant les mêmes instructions d'impression encore et encore. Mieux vaut séparer les deux préoccupations.

et appelez-le avec (encore une autre simplification ici)

Même problème que ci-dessus avec Tie. De plus, il ne teste pas si nous avons une égalité mais seulement si le tableau est complet. Nous n'avons une égalité que si nous n'avons pas de situation de victoire en même temps. Par conséquent, j'ai changé la méthode en (nécessite l'utilisation de System.Linq avant l'espace de noms)

Pour que cela fonctionne, j'ai changé le type de tableau et playerMark en char[] . char est considéré comme un type numérique en C# et peut être comparé comme des nombres. (Vous devez remplacer les guillemets doubles correspondants par des guillemets simples.)

Les conditions deviennent alors

Maintenant, la lecture peut devenir une variable locale car elle n'est pas utilisée ou définie dans d'autres méthodes

La solution complète (sans l'espace de noms et la classe englobants) :

Pensée supplémentaire

Vous stockez le plateau de jeu dans un tableau à une dimension. Il existe des variantes de Tic-tac-toe ayant plus de lignes et de colonnes. L'utilisation d'un tableau 2D rendrait le jeu plus extensible, car cela permettrait de tester les conditions de victoire et d'imprimer le tableau en itérant simplement les lignes et les colonnes. Nous utilisons maintenant une condition de gain composée manuellement. Ce n'est pas grave car le tableau est très petit. Mais faire cela sur des planches à bière blonde serait à la fois fastidieux et sujet aux erreurs.

Dans cette revue, permettez-moi d'essayer de me concentrer sur la partie la plus longue de votre code : WinCondition .

  • Essayez de séparer les vérifications des conditions de l'interaction avec l'utilisateur, ne les mélangez pas
  • Essayez d'utiliser un meilleur nom, comme : CheckVictory , HasAWinner , HasGameEnded , etc.
  • Essayez de séparer les données et la logique lors des contrôles de condition

Quand ils ne sont pas séparés

Si vous le souhaitez, vous pouvez introduire une classe d'assistance au lieu de vous fier à ValueTuples.

Olivier Jacot-Descombes a déjà donné de très bons retours. Je veux continuer à partir de sa solution. Vous n'utilisez que des méthodes statiques, qui sont essentiellement de la programmation procédurale. C# est fortement pris en charge par la programmation orientée objet, et je veux vous montrer comment vous pouvez l'introduire dans votre programme.

Dans la POO, vous stockez des données qui appartiennent ensemble dans un objet. Vous ne donnez pas un accès direct à ces données, mais avez plutôt des méthodes qui vous aident à manipuler l'état des objets.

Dans votre cas, il est tout à fait naturel d'introduire un objet qui représente le plateau de jeu. J'introduis également une énumération pour marquer l'état de chaque champ au lieu de le stocker dans des chaînes. La classe de votre objet peut ressembler à ceci :

Vous pouvez utiliser cette classe en ajoutant

dans votre classe Program, appelez sa méthode. Par exemple, la méthode PrintBoard() deviendrait :

Tout cela se traduit par une séparation des préoccupations : vous avez votre classe Programme, qui lit les entrées de l'utilisateur, affiche les informations à l'écran et gère les tours du joueur. Vous avez également la classe GameBoard, qui stocke les données, et fournit des informations sur l'état du jeu : Un joueur a-t-il gagné ? Un autre virage est-il possible ? Vous pouvez même continuer et mettre la logique, quel joueur a le prochain tour dans une classe séparée. Il est également très important que cette classe n'ait aucune référence à la classe Console. Cela signifie que vous avez une meilleure séparation entre la logique et l'interface utilisateur : si vous décidez à un moment donné, vous ne voulez pas avoir cette classe dans une console, mais une application winforms ou WPF, vous n'avez pas à toucher à cette classe.

Notez également que de l'extérieur de la classe, on ne sait pas que les informations sont stockées dans un tableau à une dimension. De l'extérieur, nous ne voyons que les méthodes publiques, mais pas les champs, qui sont marqués comme privés. Si vous souhaitez changer cela en un tableau à deux dimensions, vous n'avez qu'à modifier votre classe GameBoard et pouvez laisser toutes les autres classes inchangées. Je viens de vous montrer certaines de vos méthodes à quoi elles pourraient ressembler avec un tableau à deux dimensions :


1 réponse 1

  1. Il n'y a pas besoin de crochets dans la classe Matrix() : . . Il peut s'agir simplement de la classe Matrix : . . Voir la documentation pour la syntaxe de définition de classe.
  2. Dans self.rows = [[0]*width for i in range(height)], vous devez remplacer i par _ que nous utilisons habituellement pour les variables jetables.

Dans la même ligne 0 est un nombre magique. Vous pouvez en faire un paramètre par défaut :

Avoir ces " " aux deux extrémités semble inutile. Il suffit de retourner la partie centrale et de laisser l'utilisateur décider s'il veut plus de lignes vides entre les sorties.

[rows for rows in self.rows] est identique à self.rows . Cela nous laisse :

je suis un mauvais nom. la rangée est meilleure.

Et pour être plus concis :

Les parenthèses sont inutiles ici, et l'espacement est un peu bizarre. Ça devrait être comme ça :

Comme vous le savez probablement déjà, avec __repr__, vous devriez pouvoir passer la chaîne renvoyée à l'interpréteur Python afin qu'il puisse recréer l'objet. Mais dans votre cas, les informations sur les valeurs de votre matrice seraient perdues. Ce que vous retournez est juste quelque chose comme Matrix(5, 5) . Serait-ce OK? Il serait probablement préférable que les valeurs soient également renvoyées, mais la logique actuelle de la classe ne vous permettra pas de recréer la matrice car vous avez séparé le remplissage ( fill_matrix ) de l'initialisation. Peut-être serait-il préférable de pouvoir faire les deux en même temps ?

la méthode len doit être appelée __len__ .

Comme avec l'implémentation actuelle, sa valeur est constante, vous pouvez utiliser lru_cache(1) pour ne la calculer qu'une seule fois, puis réutiliser la valeur mise en cache :

Vous avez des méthodes non encore implémentées. Au lieu de ne rien renvoyer :

Ajoutez une docstring. Il n'est pas clair à première vue pourquoi cette méthode devrait retourner quoi que ce soit. Quelque chose comme:

Vous attrapez IndexError mais ce cas semble impossible car les indices sont toujours tirés des plages définies par la forme de la matrice. Vous pouvez le supprimer.

Pourquoi recalculer len(self.rows) et len(self.rows[i]) si vous avez déjà self.height et self.width ?

L'idée d'itérer l'index ne semble pas Pythonic. Que diriez-vous de quelque chose comme ça à la place :

Cela peut accepter des itérateurs ou des séquences et retournera un itérateur avec "overflow". Donc, si vous avez besoin d'utiliser ces restes, vous les obtenez comme ceci :

Il serait préférable de renommer fill_matrix en fill et son argument fill_list en valeurs .

Concernant add_value . Je pense qu'il vaut mieux le nommer remplacer à la place.

Cette méthode est trop compliquée et ne suit pas le principe de responsabilité unique. Attrapez les erreurs à l'extérieur et utilisez cette méthode uniquement pour remplacer une valeur. Si vous devez remplacer plusieurs valeurs, placez l'appel de fonction dans la boucle. Voir ce post pour une meilleure explication. Au final, ça devrait être aussi simple que ça :

Et vous pouvez l'appeler, récupérer et enregistrer les erreurs comme ceci :

Vous pourriez également être intéressé par le module de journalisation Python. Cependant, je trouve étrange que vous ayez décidé de prêter autant d'attention à la détection des erreurs.


3 réponses 3

Bienvenue dans CodeReview StackExchange. Je suis nouveau ici aussi, donc je ferais de mon mieux pour revoir votre code et vous donner une explication à ce sujet. Prenez en considération que tout ceci est mon opinion.

Tout d'abord, votre code doit s'expliquer. Vous n'avez pas besoin de mettre des commentaires sur tout ce que vous faites dans votre code. C'est une bonne pratique de commenter votre code. Mais, vous devriez commenter des choses qui peuvent être difficiles à comprendre à première vue, cela peut-être pour vous ou pour toute autre personne lisant votre code. Essayez de vous mettre en étranger. Vous sauriez ce que fait un morceau de code juste en le regardant ou si vous revenez dans un an, vous sauriez ce qu'il fait ?

Deuxièmement, vous ne devez pas mettre de commentaire sur des blocs de code séparés (dans votre cas, plusieurs barres obliques). Cela rendrait votre code un peu mais illisible et étendu. Lorsque vous lisez du code, vous souhaitez faire défiler le moins possible pour comprendre le code en question. Et la plupart des IDE et Editor incluent désormais une fonctionnalité permettant de réduire les régions de code.

Troisièmement, vous devez utiliser les bons littéraux C ++ pour votre code. Toute votre déclaration double était déclarée comme int double i = 20 . Vous devez les déclarer comme double i = 20,0 .

Quatrièmement, essayez d'utiliser la STL C ++ dans la mesure du possible. Il offre la meilleure fonctionnalité C ++ et la mise en œuvre de choses triviales presque sans bogues. Ainsi, vous n'avez pas à vous soucier de problèmes triviaux et non triviaux.

Je ne sais pas Si j'ai raté quelque chose, peut-être que quelqu'un d'autre le signalera. Si vous avez des questions, posez-les simplement. Je serais heureux de vous répondre du mieux que je peux. Ceci est le code source avec la même fonctionnalité et quelques commentaires expliquant les changements

L'utilisation de « utiliser l'espace de noms std » est considérée comme une mauvaise pratique, voir https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice. Si une autre unité déclare une fonction ou une variable portant le même nom, le compilateur peut choisir la mauvaise et créer des bogues difficiles à déboguer.

Vous pouvez envisager de valider l'entrée de calcFinalLetterGrade(), une valeur supérieure à 100 ou inférieure à 0 devrait probablement générer une erreur/un avertissement. Bien que vous soyez le seul à appeler cette fonction.

  • Vous n'avez pas besoin de #include "stdafx.h" . Il est généralement utilisé pour consolider les inclusions dans les projets Windows. Ce n'est pas applicable ici.
  • N'utilisez pas l'espace de noms std . Référez-vous plutôt aux choses dans std avec le préfixe std ::. C++ (résolution de noms) est inutilement compliqué et, par conséquent, vous pouvez être piqué par d'étranges bugs si les noms entrent en collision. Vous évitez tout cela en n'utilisant pas l'espace de noms std .
  • Au lieu de double quiz[2] , utilisez std::array ( std::array<double, 2> quiz ). Ceci est préférable au fait de passer des pointeurs pour de nombreuses raisons, la principale étant que la longueur du tableau est codée dans le type. Avec un pointeur ( *double ), faire des quiz[3] est un moyen très simple d'introduire un comportement indéfini. C'est mauvais. (Un autre avantage est que si vous modifiez la taille, vous n'avez pas à modifier tous les sites d'itération d'utilisation avec votre code existant, vous devrez modifier les boucles dans inputQuizzes() , main() , etc.) Si vous voulez accepter un nombre variable de quiz, utilisez plutôt std::vector.
  • Nous préfixons généralement les membres avec m_ pour différencier les variables locales. Ex. finalGrade doit être m_finalGrade
  • Implémentation séparée de l'interface. Toutes les déclarations préalables doivent être dans un fichier student.h (qui commence par #pragma once ) et l'implémentation doit être dans un fichier student.cpp (qui a #include "student.h" )
  • Habituellement, nous écrivons des commentaires // commenter au lieu de // commenter // . De plus, des diviseurs comme /////////// sont rarement utilisés. Votre utilisation d'entre eux indique que vous souhaitez probablement diviser les choses en différents fichiers.
  • Vous avez beaucoup d'informations en double (ex. Student et StudentRecord a un finalLetterGrade avec différentes méthodes d'accès (ex. directement par student.finalLetterGrade et via un setter par studentRecord.setFinalLetterGrade('A') ). modèle setter. Vous voulez probablement choisir une seule approche. Mais aussi, cette information ne devrait vivre qu'à un seul endroit. Toute duplication est une opportunité pour les choses de se désynchroniser accidentellement (pensez si ailleurs vous avez changé la note finale de la lettre pour le Student mais pas le StudentRecord . Maintenant, comment savez-vous quel finalLetterGrade est correct ?).
  • Certaines de vos méthodes sur StudentRecord n'ont pas vraiment leur place. Par exemple, calcFinalLetterGrade (et calcPercent ) n'effectue aucune action sur un étudiant spécifique. Il n'utilise aucune des variables de membre privé. Cela pourrait juste (et devrait probablement) être une fonction statique.
  • getQuizzes() a un corps étrange. Vous pouvez simplement renvoyer le quiz lorsque les tableaux se décomposent en pointeurs (bien que, comme je l'ai expliqué ci-dessus, s'appuyer sur cela soit généralement mauvais et vous devriez utiliser std::array ).
  • Attention au double. Il y a des nombres qui ne peuvent pas être représentés avec. En particulier, vous ne devez pas utiliser de nombres à virgule flottante pour coder des devises (car de petites imprécisions peuvent entraîner une perte d'argent). Je doute que vous puissiez faire la même chose pour les notes. Il est possible qu'avec la bonne série de divisions, vous puissiez produire un nombre qui, dans une classe de mathématiques pures, serait > 90 mais ne serait pas représentable et serait plutôt représenté comme plus proche d'un 89. Comme il est plus facile d'utiliser double , je Je m'en tiendrai à cela, mais gardez cela à l'esprit.
  • cin peut échouer. Vous ne gérez pas non plus le cas d'une note invalide. Que se passe-t-il si l'utilisateur saisit une note négative à mi-parcours ?
  • La représentation dépend de vous, bien sûr, mais souvent nous représenterons 0-100 % comme la plage 0-1 (comme vous le feriez en mathématiques) au lieu de 1-100 (je m'en tiendrai à votre convention, mais gardez cela dans écouter).
  • Gardez votre indentation cohérente. Indenter les blocs entourés de <> . Cela rend le code beaucoup plus facile à suivre.

Dans l'ensemble, c'est une bonne première tentative, mais cela pourrait nécessiter un peu de travail. Mon observation principale est qu'il y a beaucoup de code (avec beaucoup de méthodes et plusieurs objets) pour relativement peu de logique. C'est souvent un signe que vous pouvez refactoriser pour simplifier les choses. Un code plus court et plus simple sera presque toujours plus facile à comprendre.

La première chose que nous allons essayer est d'éliminer toutes les indirections et de tout faire à l'intérieur de main . Vous êtes déjà presque en train de le faire. calcPercent et calcFinalLetterGrade est la seule vraie logique effectuée en dehors de celle-ci (sans l'entrée). La seule chose que vous avez vraiment extraite est l'endroit où les notes et les informations sur les étudiants sont stockées (encapsulées dans un objet, au lieu de variables locales).

Quelques points à noter avec cette approche :

  • L'utilisation de std::accumulate pour additionner une collection de longueur arbitraire.
  • Je suis un peu dubitatif sur le ternaire (car on abuse un peu de l'antériorité ici), mais ça me parait assez clair
  • Il doit illustrer à quel point la logique métier de votre programme est simple. Il n'y a pas besoin de compliquer les choses au-delà de cela.
  • Il ne gère pas les entrées non valides (échec cin, notes négatives). Nous y viendrons dans une seconde

À l'exception du dernier point, je plaiderais pour ce scénario, il n'y a pas de bonne raison de compliquer le code plus loin que ce qui précède. Mais il semble que vous vouliez apprendre comment correctement OOP-ifier un problème, nous allons donc aborder cela à titre d'exemple. Mais d'abord, la validation.

Nous notons que pour la première moitié de notre programme, nous répétons deux actions pour chaque variable que nous devons remplir :

  1. Imprimer un message informant l'utilisateur que les données que nous collectons
  2. Lire la note de stdin

Idéalement, (2) devrait également demander à l'utilisateur s'il a saisi une note invalide (disons, une note négative).

On pourrait simplifier la logique en extraire en une fonction.

Notez comment cela gère les erreurs avec élégance. Il permet également aux notes > 100 pour permettre des points de crédit supplémentaires. Nous pouvons utiliser cette fonction comme ceci :

Maintenant, vous remarquerez que cela ne nous fait pas économiser beaucoup de lignes dans main() à première vue, mais pour gérer les erreurs, readGradeInteractive doit être un peu plus compliqué. C'est donc un bon refactoring.

Je dirais à ce stade, c'est une bonne solution à votre problème. Vous n'avez pas vraiment besoin de présenter la POO ici. Mais disons que nous voulions permettre à d'autres programmes d'utiliser cette logique (le calcul de la note) dans leurs programmes. Ensuite, il peut être judicieux d'introduire un objet Student, qui encapsule ces notes et est capable de calculer sa note finale (sous forme de nombre et de lettre). Pourquoi pas de StudentRecord ? Pensez à ce qui se passe ici. Un étudiant a des notes. Et une action que vous pouvez effectuer sur l'élève est de lui demander quelle est sa note finale. Il n'y a pas besoin d'objets intermédiaires. Commençons par reconcevoir notre main pour imaginer ce dont nous avons besoin que Student soit capable de faire.

Remarquez combien c'est plus simple ! main n'a plus de logique. Il délègue la lecture du nom et des notes de l'étudiant de stdin à une méthode statique sur Student . Et il délègue le calcul de la note finale (et lettre) à l'objet étudiant.

Commençons par écrire student.h . Tout ce que nous avons à faire est de regarder l'API que nous voulons que l'étudiant expose (à savoir la statique Student readInteractive() , char finalGrade() , std::string name() ) et de consulter notre code d'origine pour rappeler les variables membres que l'étudiant doit encapsuler:

Maintenant, il ne reste plus qu'à l'implémenter dans student.cpp :

Notez comment nous avons en grande partie copié la logique de notre programme d'origine (en changeant les variables locales en variables membres le cas échéant).

Je parierais que c'est une application assez raisonnable de la POO. Vous pouvez aller plus loin en examinant la note. Souvent, nous aimons que nos signatures de type soient très informatives sur les choses qu'elles prennent et retournent. Dans le cas de char finalGrade() , il peut ne pas être immédiatement clair que char est une note alphabétique. De plus, le type char ne restreint pas les éléments pouvant être renvoyés à ABCDEF . Un H pourrait être retourné. Qu'est-ce que cela signifierait? De plus, il existe d'autres états similaires à des notes tels que Incomplet, Retiré, etc. que vous souhaiterez peut-être encoder ici. Mais nous ne voudrions pas simplement utiliser des lettres magiques pour eux (de peur que nous ne leur retournions accidentellement une note en raison d'un bug). Un autre problème avec l'approche ci-dessus est qu'elle combine deux préoccupations dans finalGrade : (1) calculer la note finale sous forme de nombre et (2) convertir cette note numérique en une note alphabétique.

Ainsi, nous pourrions résoudre ces deux problèmes en utilisant une classe enum pour représenter les notes et rendre cette enum responsable de la conversion d'une note numérique en une lettre. Je vous laisse ça en exercice :)


Fonctionnalités de classe alternative

Explorateur de la ville

Fonctionnalité de rôdeur de niveau 1 (remplace l'explorateur naturel)

Vous êtes un maître de la navigation dans les rues et les ruelles sinueuses des villes et des villages, et vous réagissez avec une action rapide et décisive en cas d'attaque. Cela vous confère les avantages suivants lorsque vous êtes à l'intérieur d'une ville ou d'une ville :

  • Vous ignorez les terrains difficiles.
  • Vous avez l'avantage sur les jets d'initiative.
  • Lors de votre premier tour de combat, vous avez un avantage aux jets d'attaque contre les créatures qui n'ont pas encore agi.

De plus, vous maîtrisez la navigation urbaine. Vous bénéficiez des avantages suivants lorsque vous voyagez pendant une heure ou plus :

  • Un terrain difficile ne ralentit pas le déplacement de votre groupe.
  • Votre groupe ne peut se perdre que par des moyens magiques.
  • Même lorsque vous êtes engagé dans une autre activité en voyage (telle que la recherche de nourriture, la navigation ou le suivi), vous restez attentif au danger.
  • Si vous voyagez seul, vous pouvez vous déplacer furtivement à un rythme normal.
  • Lorsque vous fouillez, fourrez ou plongez dans une benne à ordures, vous trouvez deux fois plus de nourriture que vous le feriez normalement.
  • En traquant d'autres créatures, vous apprenez également leur nombre exact, leur taille et depuis combien de temps elles ont traversé la zone.

Ennemi favori, révisé (urbain)

Fonctionnalité de rôdeur de niveau 1 (remplace l'ennemi favori)

À partir du niveau 1, vous avez une expérience significative de l'étude, du pistage, de la chasse et même de la conversation avec un certain type d'ennemi couramment rencontré dans les villes et les villages.

Choisissez un type d'ennemi favori : aberrations, bêtes, célestes, constructions, dragons, élémentaux, fées, démons, géants, monstruosités, limons, plantes ou morts-vivants. Alternativement, vous pouvez sélectionner deux races d'humanoïdes (comme les gnolls et les orcs) comme ennemis privilégiés.

Une autre alternative, avec l'approbation du MD, un rôdeur urbain peut sélectionner une organisation au lieu d'un type de créature comme son ennemi préféré. Par exemple, un personnage peut sélectionner une guilde de voleurs, une maison de marchand ou même le garde de la ville. Les bonus d'ennemis favoris s'appliqueraient à tous les membres de l'organisation choisie, quel que soit leur type ou sous-type de créature.

Vous gagnez un bonus de +2 aux jets de dégâts avec les attaques d'armes contre les créatures du type choisi. De plus, vous avez un avantage sur les tests d'Intelligence (Enquête) pour suivre vos ennemis préférés, ainsi que sur les tests d'Intelligence pour rappeler des informations à leur sujet. De plus, si la règle de variante des compétences alternatives est utilisée, vous avez un avantage sur le charisme (enquête) pour recueillir des informations sur l'ennemi que vous avez choisi.

Lorsque vous obtenez cette fonctionnalité, vous apprenez également une langue de votre choix, généralement parlée par votre ennemi préféré ou les créatures qui lui sont associées. Cependant, vous êtes libre de choisir la langue que vous souhaitez apprendre.

Sensibilisation urbaine

Fonction de rôdeur de niveau 3 (remplace la conscience primitive)

À partir du niveau 3, votre maîtrise du savoir des rôdeurs vous permet d'établir un lien puissant avec les bêtes et la ville qui vous entoure.

Vous avez une capacité innée à communiquer avec les bêtes, et elles vous reconnaissent comme une âme sœur. À travers des sons et des gestes, vous pouvez communiquer des idées simples à une bête en tant qu'action, et pouvez lire son humeur et son intention de base. Vous apprenez son état émotionnel, s'il est affecté par la magie de quelque sorte que ce soit, ses besoins à court terme (comme la nourriture ou la sécurité) et les actions que vous pouvez prendre (le cas échéant) pour le persuader de ne pas attaquer.

Vous ne pouvez pas utiliser cette capacité contre une créature que vous avez attaquée au cours des 10 dernières minutes.

De plus, vous pouvez affiner vos sens pour déterminer si l'un de vos ennemis préférés se cache à proximité. En passant 1 minute ininterrompue en concentration (comme si vous vous concentriez sur un sort), vous pouvez sentir si l'un de vos ennemis préférés est présent à moins de 8 km de vous, ou dans les limites de la ville, selon la plus petite des deux. Cette fonctionnalité révèle lesquels de vos ennemis préférés sont présents, leur nombre, ainsi que la direction générale et la distance (en miles) des créatures par rapport à vous.

S'il y a plusieurs groupes de vos ennemis préférés à portée, vous apprenez cette information pour chaque groupe.

Ennemi privilégié supérieur, révisé (urbain)

Fonctionnalité de rôdeur de niveau 6 (remplace l'amélioration de l'ennemi favori)

Au niveau 6, vous êtes prêt à chasser un gibier encore plus meurtrier. Choisissez un deuxième type d'ennemi privilégié : aberrations, bêtes, célestes, constructions, dragons, élémentaux, fées, démons, géants, monstruosités, limons, plantes ou morts-vivants. Alternativement, vous pouvez sélectionner deux autres races d'humanoïdes (comme les gnolls et les orcs) comme ennemis privilégiés, ou une organisation avec l'approbation du SM. Vous gagnez tous les avantages contre cet ennemi choisi que vous gagnez normalement contre votre ennemi préféré, y compris une langue supplémentaire. Votre bonus aux jets de dégâts contre tous vos ennemis préférés passe à +4.

De plus, vous avez un avantage sur les jets de sauvegarde contre les sorts et les capacités utilisés par un ennemi plus favorisé.

Communauté favorisée

Réature de rôdeur de niveau 6 (remplace l'amélioration de l'explorateur naturel)

Au niveau 6, le ranger urbain tisse un lien avec une communauté. Cela vous confère les avantages suivants lorsque vous êtes à l'intérieur de cette communauté privilégiée :

  • Vous gagnez un +2 aux jets d'initiative
  • Vous avez un avantage aux tests de Perception, de Discrétion et de Survie.

Un garde forestier urbain qui traverse sa communauté préférée ne laisse aucune trace et ne peut pas être suivi (bien qu'il puisse laisser une trace s'il le souhaite).

Aux fins de cette capacité, une communauté est tout établissement composé de 100 personnes ou plus. La communauté peut être plus grande que ce minimum. Les fermes, les champs et les maisons périphériques ne sont pas considérés comme faisant partie d'une communauté.

Cette fonctionnalité peut être reprise au niveau 10, remplaçant l'amélioration Natural Explorer, pour sélectionner une autre communauté privilégiée.

Flotte de Foot

Fonction de rôdeur de niveau 8 (remplace Land's Stride)

À partir du niveau 8, vous pouvez utiliser l'action Course comme une action bonus à votre tour.

Faire passer

Fonction de rôdeur de niveau 8 (remplace Land's Stride)

À partir du niveau 8, vous déplacer sur un terrain difficile non magique ne vous coûte aucun déplacement supplémentaire. De plus, vous pouvez vous déplacer dans l'espace occupé par les citoyens locaux comme s'ils étaient des alliés. This does not apply to creatures intent on harming you. Areas that are enchanted or magically manipulated to impede motion, however, still affect you.

In addition, you have advantage on saving throws against plants that are magically created or manipulated to impede movement, such those created by the entangle spell.

Blend In

10th-level ranger feature (Replaces Natural Explorer improvement)

At 10th level, you can cast Disguise Self as a bonus action, when inside your favored community. In addition to this, when a creature uses its action to discern that you are disguised, it must succeed on an Intelligence (Investigation) against your Dexterity (Stealth) roll instead of your spell save DC.

This does not expend a spell slot, but you cannot use this feature again until you finish a short or long rest.


Joomla custom fields in Category List

With Joomla 3.7.x article custom fields have finally gotten part of Joomla content.

However, I would like to ask how I can render these Custom Fields in the Joomla Category List?

Adding custom fields to the Joomla Category List is still not part of Joomla (hopefully it will come one day too).

So in order to add the custom fields to the Joomla Category List, I've made an template override of 'com_content/category/default_articles.php' to add two columns that I want to use to display Two Custom fields:

  • Date field with Title: 'Entry into force' and name 'gildiskoma'
  • Text field with Title 'Type' and name 'slag'

My current override of default_articles.php looks like this:

We don't use the article vote feature, so I've chosen to use the Custom Fields in its place as you might notice from the code above, which gives me two custom columns.

The documentation has a section explaining how to add individual fields with override. But my code above doesn't display the custom fields value.

So I'm not sure what I'm missing to get the fields value to show in the list?

Below also a screenshot which illustrates what I'm trying to do.


2 réponses 2

I have a few thoughts to share about dimension reduction in unsupervised learning problems. In answering, I've assumed that your interest is in "high-touch," human involvement wrt cluster interpretation as opposed to an automated, turnkey, black box and "low-touch" machine learning approach in which interpretation is deliberately de-emphasized. If it were the latter, why would you even be asking the question? Also, note that I've had a ton of experience running cluster solutions across a wide range of business environments over the years including strategic B2C marketing, B2B tech arenas and education policy (clustering students and schools).

First though, I do have a question about your comment concerning "grouping different datasets." I didn't know what you meant by that or how it might impact the approach and was hoping you could elaborate.

I would like to challenge your assumption in #1 above that solutions based on PCAs are "hard to interpret." The reasons for even running a PCA as a preliminary step in clustering have mostly to do with the hygiene of the resulting solution insofar as many clustering algorithms are sensitive to feature redundancy. PCA collapses this redundancy into a manageable handful of components, thereby minimizing the challenges and difficulties that you note regarding feature selection. While it is true that the components output from a PCA blur the granularity and specificity of the individual features, this is a problem iff you solely rely on those components in analyzing the results. In other words, you are not in any way locked into using only the components for cluster interpretation. Not only that, you don't necessarily even need to care what the factor dimensions "mean." They are only an intermediate and (ultimately) disposable means to an end facilitating an actionable solution. But in making this point I differ from many practitioners since teams can, will and do spend weeks carefully building a "meaningful" factor solution. To me, this is an inefficient waste of client time and money.

At this point there will be a boatload of technical considerations to address. For one, if your PCA algorithm is not scale invariant (e.g., is OLS vs ML), then any resulting PCA solution will be distorted, loading more heavily on the high variance features. In these cases your features need to be preprocessed or transformed in some way to flatten this variance out. There are a huge number of possibilities here including mean standardizing, range or IQR standardizing, ipsative scaling, and so on. Leverage that transformation delivering the best, most interpretable solution.

Once a cluster solution is generated, interpretation is best motivated (in my experience) by ignoring the components and folding back in the original features along with any additional descriptive information not directly used in the solution. At this point a few heuristics are the best guides to qualitative insight. This can be as easy as generating a spreadsheet that profiles your clusters based on averages or medians for each feature (the rows of the sheet), for each cluster (the columns) as well as an additional column representing the grand mean for your total sample. Then, by indexing the cluster averages for each feature against the grand mean (and multiplying by 100), a heuristic is created that is like an IQ score insofar as around "100" is "normal" IQ or average behavior, indexes of 120+ are suggestive of high likelihoods for a feature to be "true" about the behavior of a cluster and indexes of 80 or less are indicative of features that are "not true" of a cluster. These indexes of 120+ and 80 or less are like proxy t-tests for significance of a given feature in driving the solution. Of course, you can run between group tests of significance and, depending on sample sizes, will get answers that vary around these quick and dirty rules of thumb.

D'accord. after all of that, suppose you're still opposed to using PCA as direct input into a clustering algorithm, the problem remains regarding how to select a reduced set of features. PCA can still be useful here since PCAs are like running a regression without a dependent variable. The top loading features on each component can become the inputs into the cluster algorithm.

To your point about the large number of features and relatively small sample size of your data, the typical rule of thumb in many "full information" multivariate analyses is a minimum of about 10 observations per feature. There are some specialized methods that can be leveraged to work around this challenge. For instance, partial least squares (PLS) was first developed by Herman Wold in his 1990 book Theoretical Empiricism for use in fields such as chemometrics which face this precise issue. It is factor-analytic in nature but is much less stringent in requiring a large n to generate the dimensions. Other solutions include the random forest-like, "divide and conquer," machine learning approaches used with massive amounts of information. These methods are reviewed in this pdf http://www.wisdom.weizmann.ac.il/

But suppose you've decided that you still want nothing to do with factor analysis and are dead set on running some kind of supervised, "sequential" selection process. In my view, the most important issue is less about finding a post-hoc performance metric (Dunn Index) and more about identifying a suitable proxy -- a dependent variable -- to even make this approach possible. This decision is entirely a function of your judgement and SME status wrt your data. There are no "best practices," much less easy answers for this and given how you've described your data, no small challenge.

Once that decision is made, then there are literally hundreds of possible variable selection solutions to choose from. Variable selection is a topic area on which every statistician and their brother has published a paper. Your preferred approach seems to be "sequential forward selection" is fine.

It's worth noting that supervised learning models exist which fold in a cluster solution as part of the algorithm. Examples of this include the large and highly flexible approaches known as latent class models. The essence of LC models is that they are two stage: in stage one a DV is defined and a regression model is built. In the second stage, any heterogeneity in the residual output from the model -- a single latent vector -- is partitioned into latent "classes." There's an overview of LC modeling in this CV discussion here . Latent class multinomial logit model doubt


2 réponses 2

I know this approach is strange because you are still working with a List<SObject> , but when you assign it you can make it more specific (e.g. List<Account> ) by using Type.forName and Type.newInstance methods.

The getSObjectType call may not be completely reliable, especially since some of these records are being inserted and hence won't have ids. For that reason, it is probably better to accept sObjectType as an additional parameter instead of trying to determine it on the fly.

Some bad news, as discovered here (actually earlier than your post), the above methodology does not always work. Specifically, if I want to perform a partial upsert and also specify an external Id field, I get a compile fail.

Additional Update

I have a case open with support and it has been escalated to Tier 3. Currently the responses I'm getting are all along the lines of "will update you tomorrow," but I will post here if I get any resolution.

Additional Update

Support claims the three parameter signature failure is WAD. They said if I want them to actually fix it, I need to post on an Idea, so here it is, vote for it!

Tier 3 . performed some testing and it looks like that we cannot do upsert with List regardless of which of the 3 method signatures you use.

The only difference in behavior seems to be that we block using the 3-arg one at compile time and the 2-arg and 0-arg ones fail at run- time.

Basically for the 3-arg we validate at compile time that you are not passing in a list parameterized with SObject. But for the other 2 we compile it and we allow it to run and check in the call whether the list is generic.

We could make the 3-arg work but it seems like a feature request and based on the behavior of the 0-arg and 2-arg versions we would presumably still enforce that the underlying list isn't generic.


Voir la vidéo: Combiner les classes dentités arcgis