Avez-vous votre propre bibliothèque 'misc utils'? De quelle partie êtes-vous le plus fier? [fermé]

32

Je sais que beaucoup d'entre nous ont notre propre petite bibliothèque personnelle avec des outils et des utilitaires que nous utilisons souvent.

J'ai le mien depuis que j'ai 16 ans, il a donc atteint une taille assez considérable. Certains éléments que j'ai écrits ont depuis été ajoutés au cadre. J'ai écrit ma propre petite implémentation d'arbres d'expression pour une utilisation avec des algorithmes génétiques bien avant LINQ, ce que j'aime bien et dont j'étais fier à l'époque - bien sûr, c'est assez inutile maintenant. Mais récemment, je l'ai parcouru et mis à niveau vers .NET 4.0 et ravivé un intérêt.

Je suis donc curieux de savoir à quoi vous utilisez votre bibliothèque. Peut-être pourrions-nous obtenir des idées intéressantes pour de petits extraits utiles et les partager entre nous.

Donc mes questions sont:

  • Avez-vous une bibliothèque d'utilitaires divers?
  • De quelle partie êtes-vous le plus fier et pourquoi?

Donnez un exemple de code si vous le souhaitez :-)

Personne
la source
Personne ne semble voter pour des réponses ...
Joey Adams
@Joey Adams N'est-ce pas? Actuellement 17 votes pour les questions et 6 votes pour les réponses au total .
Nicole
Je ne vois pas vraiment de réponses qui méritent d'être votées. Qu'est-ce que cela signifie pour eux? La nature de la question est telle que les réponses obtiennent juste un "oh. Bien." sorte de réaction, et puis c'est soit voter contre tout, soit rien du tout. (Et je n'aime pas voter pour chaque réponse juste parce qu'elle est là. Si rien d'autre, je n'ai plus de votes.: P)
Adam Lear
@Anna Lear, ok, vous êtes excusée :)
Nicole
3
Tout utilitaire décent devrait être installé sur github et partagé avec le monde. Cela n'a pas de sens de le garder caché s'il est vraiment bon.
Job

Réponses:

27

Non.

J'ai vu des effets cauchemardesques d'une douzaine de développeurs ajoutant tous leurs propres petites bibliothèques de style "util.h" aux projets, et je les ai transformés en un gâchis géant de dénominations et de comportements de fonctions incohérents. Tout comme PHP. C'est pourquoi j'évite de le faire.

J'évite d'avoir à le faire en utilisant des environnements de programmation qui me fournissent presque tous les outils et bibliothèques dont j'ai besoin dès que possible, comme C # et python.

comment s'appelle-t-il
la source
7
Je réécris constamment ma bibliothèque à des fins d'organisation.
Maxpm
3
Les cas où le paquet d'utils est devenu cauchemardesque ne signifie pas que tous sont mauvais. Je ne vois pas comment vous pouvez l'éviter et ne pas avoir plus de duplication de code à cause de cela. Et donc, pire test et moins d'efficacité.
Nicole
2
@Renesis: Les paquets utils sont à peu près aussi désastreux que les instructions goto. Bien sûr, en soi, ce n'est pas si mal, mais cela finit toujours par être un désastre tôt ou tard. En ce qui concerne la duplication de code, si vous vous trouvez à faire une tâche similaire dans pratiquement tous vos projets, alors pour quelque chose comme python ou C #, d'autres personnes l'ont probablement fait aussi et c'est probablement dans les bibliothèques standard d'ici là.
whatsisname
6
D'après mon expérience, les ingénieurs disposant de leur propre bibliothèque préféreront l'utiliser avant celle fournie par le système, il n'est donc pas recommandé d'avoir des bibliothèques personnelles. J'ai eu une fois un gars qui était absolument convaincu que sa fonction "strlen" était plus rapide que celle fournie par le compilateur, car il l'a écrite . Il lui a fallu une simple démonstration de la façon dont strlen est un couple d'instructions de montage intégrées pour qu'il admette que peut-être d'autres personnes peuvent faire mieux.
JBRWilkinson
4
@JBRWilkinson Votre point est bien compris. Tous les programmeurs ne sont pas aptes à développer un code commun.
Nicole
15

SmartFormat

Mon utilitaire préféré est celui que j'ai écrit - un simple générateur / formateur de chaînes qui facilite vraiment la transformation des données en chaînes avec une grammaire correcte.

Par exemple, la plupart des programmeurs construire texte à partir d' un modèle: "There are {0} items remaining" mais cela conduit à des erreurs grammaticales: "There are 1 items remaining".

Alors, SmartFormat vous permet d' écrire: "There {0:is|are} {0} item{0:|s} remaining".

Vous venez de remplacer String.Format(...)par Smart.Format(...)et c'est tout!

Le code SmartFormat est open source: http://github.com/scottrippey/SmartFormat/wiki

Scott Rippey
la source
Cela me rappelle le format utilisé par java.text.MessageFormat.
barjak
@barjak Intéressant! J'ai passé beaucoup de temps à rechercher la mise en forme "conditionnelle", et je n'ai jamais rien trouvé de semblable jusqu'à présent! MessageFormata la ChoiceFormatclasse qui permet une syntaxe étonnamment similaire! Un exemple de la documentation: "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.". Merci d'avoir mentionné cette référence.
Scott Rippey
@barjak Juste pour valider mon point, cependant, SmartFormat a beaucoup plus de fonctionnalités! La mise en forme conditionnelle fonctionne pour tout type de données, comme booléen, date, intervalle de temps et objet; il prend également en charge les opérateurs avancés, tels que "{Count:<0?negative|=5?five|>50&<100?large|other}". Il a une réflexion (c.-à "There are {items.Length} items"- d . Peut formater des éléments de tableau et des plages horaires. De plus, il a un modèle de plugin pour prendre en charge encore plus de fonctionnalités.
Scott Rippey
Il semble en effet être puissant. Le formatage des tableaux est intéressant.
barjak
@barjak: Oui, le formatage des tableaux est vraiment utile! Découvrez cet exemple: Smart.Format("There are {0.Count} files: {0:'{}'|, |, and }.", files);entraînerait "There are 3 files: 'a.txt', 'b.txt', and 'c.txt'.". Je ne peux pas imaginer une "localisation" sans cela.
Scott Rippey
7

Combinateur K (C #, Scala)

J'utilise le combinateur K dans Ruby assez souvent, principalement dans les plis lorsque l'opération de pliage est effectuée via un effet secondaire plutôt qu'une valeur de retour, comme dans cet exemple:

some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1 }

Cela compte la fréquence à laquelle chaque élément se produit some_collection. Malheureusement, cela ne fonctionne pas réellement, car le bloc doit renvoyer la nouvelle valeur de l'accumulateur à chaque itération, mais dans Ruby, les affectations sont évaluées selon la valeur affectée.

Donc, vous devez renvoyer explicitement la nouvelle valeur de l'accumulateur comme ceci:

some_collection.reduce(Hash.new(0)) {|acc, el| acc[el] += 1; acc }

Mais je trouve ce séquencement explicite laid dans ce style fonctionnel utilisant des plis. Le combinateur K (appelé Object#tapen Ruby) à la rescousse:

some_collection.reduce(Hash.new(0)) {|acc, el| acc.tap { acc[el] += 1 }}

Je l'ai déjà manqué plusieurs fois en C # (principalement parce que pour une raison quelconque, les mutateurs de collection tels que List.Addreturn voidau lieu de this) et Scala, je porte donc ceci:

namespace GenericExtensions
{
    public static class GenericExtensions
    {
        public static T Tap<T>(this T o, Action<T> f)
        {
            Contract.Requires(o != null);
            Contract.Requires(f != null);

            f(o);
            return o;
        }

        public static T Tap<T>(this T o, Action f)
        {
            Contract.Requires(o != null);
            Contract.Requires(f != null);

            f();
            return o;
        }
    }
}

et à Scala:

class Tap[T](o: T) {
  def tap(f: T => Unit) = { f(o); o }
  def tap(f: => Unit) = { f; o }
}

object Implicits { implicit def any2Tap[T](o: T) = new Tap(o) }

Fonction d'identité (Ruby)

Quelque chose qui me manque dans Ruby, est un moyen bien nommé d'accéder à la fonction d'identité. Haskell fournit la fonction d'identité sous le nom de id, Scala sous le nom de identity. Cela permet d'écrire du code comme:

someCollection.groupBy(identity)

L'équivalent en Ruby est

some_collection.group_by {|x| x }

Ne déroule pas exactement la langue, n'est-ce pas?

Le correctif est

IDENTITY = -> x { x }

some_collection.group_by(&IDENTITY)

ForEach (.NET)

Une autre méthode cruellement manquante en C #:

namespace IEnumerableExtensions
{
    public static class IEnumerableExtensions
    {
        public static void ForEach<T>(this IEnumerable<T> xs, Action<T> f)
        {
            Contract.Requires(xs != null);
            Contract.Requires(f != null);

           foreach (var x in xs) f(x);
        }
    }
}
Jörg W Mittag
la source
3
Je pense que votre dernier exemple était une décision de conception calculée. Le concept d'un Actionimplique des effets secondaires qui vont à l'encontre des principes de conception de LINQ.
ChaosPandion
1
@ChaosPandion: Qu'est-ce que cela a à voir avec LINQ?
Jörg W Mittag
@ Jörg W Mittag - Les IEnumerableextensions ont été ajoutées pour LINQ.
ChaosPandion
2
@ChaosPandion: Je ne comprends toujours pas. ForEachn'est pas un opérateur LINQ. Pourquoi les restrictions qui ne s'appliquent qu'aux opérateurs LINQ s'appliquent-elles ForEach, qui n'est pas un opérateur LINQ? Et pourquoi les effets secondaires sont-ils interdits IEnumerable.ForEachmais autorisés List.ForEach? Aussi, pourquoi les effets secondaires sont-ils interdits IEnumerable.ForEachmais autorisés foreach?
Jörg W Mittag
@ Jörg W Mittag - Ce que je dis, c'est le fait qu'il manque dans les extensions était une décision de conception. Le fait d' List<T>avoir un ForEachest raisonnable étant donné qu'il s'agit d'un type mutable.
ChaosPandion
6

J'ai un convertisseur de type Java. Il a une signature publique

public static <T> T convert(Object sourceValue, Class<T> destinationType)

et il fait de son mieux pour convertir la valeur source en type de destination. Il vous permet essentiellement de taper dynamiquement dans une langue typée statiquement :-)

Il est en fait utile avec les types numériques encadrés. À quel point est-ce irritant de ne pas pouvoir mettre un Integerà l'endroit Longprévu? Pas de problème, il suffit de le convertir. Ou que se passe-t-il si votre fonction attend un double, mais que vous avez un nullà y mettre? Kaboom, un NPE. Mais faites-le passer convertet vous obtenez un NaN.

Joonas Pulakka
la source
Solution intéressante. J'ai toujours pensé que Long devrait étendre Integer. Mais même dans ce cas, vous auriez toujours le problème de l'autoboxing (pour autant que je sache, il n'y a aucun moyen que l'autoboxing fonctionne avec l'héritage). En outre, +1 pour le NaNsupport.
Nicole
NaNest excellente. Dommage qu'il n'y ait rien de tel pour les entiers. J'ai utilisé Integer.MIN_VALUEcomme convention. C'est généralement "assez bizarre" pour être remarqué, contrairement à la valeur par défaut 0. Je ne sais pas pourquoi la boxe (dé) automatique ne se traite pas (Double) nullcomme NaN. C'est la bonne solution évidente, à mon humble avis.
Joonas Pulakka
6

Du code divers que j'ai écrit, la plupart des bonnes choses se trouvent maintenant dans CCAN , tandis que le reste, j'ai tendance à trouver de meilleures versions de dans les projets open source existants. Je me retrouve à écrire de moins en moins de code "divers" à usage général ces jours-ci, en faveur de l'écriture de variantes spécifiques à l'application de ce code, ou d'écrire des modules à usage général que je peux publier par eux-mêmes.

C

Voici une fonction et un typedef que j'ai utilisés plus d'une fois. Pour les applications qui nécessitent un timing, il est difficile de battre les millisecondes en termes de simplicité:

#include <stdint.h>
#include <sys/time.h>

typedef int64_t msec_t;

static msec_t time_ms(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (msec_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

Et plus de fonctions C diverses que j'ai tendance à utiliser encore et encore (et plus):

/* Remove a trailing newline, if present. */
void chomp(char *buffer)
{
    if (!*buffer)
        return;

    while (*buffer)
        buffer++;

    if (buffer[-1] == '\n')
        buffer[-1] = 0;
}

/*
 * Skip whitespace, update the pointer, and return it.
 * Example:
 *
 * switch (*skipSpace(&s)) {
 *     case '\0':
 *         ...
 *     case '(':
 *         ...
 */
const char *skipSpace(const char **sptr)
{
    const char *s = *sptr;
    while (isspace(*s))
        s++;
    *sptr = s;
    return s;
}

/* Scramble an array of items uniformly. */
void scramble(void *base, size_t nmemb, size_t size)
{
    char *i = base;
    char *o;
    size_t sd;
    for (;nmemb>1;nmemb--) {
        o = i + size*(rand()%nmemb);
        for (sd=size;sd--;) {
            char tmp = *o;
            *o++ = *i;
            *i++ = tmp;
        }
    }
}

Haskell

La nub :: (Eq a) => [a] -> [a]fonction de Haskell est O (n²) car, par sa signature de type, il est seulement permis de tester si deux éléments sont égaux. Une alternative simple à O (n log n) est map head . group . sort, mais elle nécessite de forcer toute la liste d'entrées avant de produire la sortie, alors qu'elle nubpeut commencer à produire la sortie tout de suite. Ce qui suit est une alternative à O (n log n) nubqui collecte les éléments déjà vus dans a Data.Set:

module Nub (nub') where

import Prelude
import Data.Set (empty, member, insert)

nub' :: Ord a => [a] -> [a]
nub' xs = loop xs empty where
    loop [] _ = []
    loop (x:xs) set =
        if x `member` set
            then loop xs set
            else x : loop xs (insert x set)

Dans Haskell, j'utilise des alternatives à sequence, mapM, forM, replicateMet filterM. Ces actions génèrent chacune une liste, mais la liste ne peut pas être utilisée tant que l'action n'est pas terminée dans son intégralité (si vous utilisez une monade stricte comme IO). Les alternatives construisent la liste à l'envers plutôt que de former une tour de thunks, ce que j'ai trouvé grâce à l'analyse comparative pour être plus rapide, au moins avec GHC.

sequence' :: Monad m => [m a] -> m [a]
sequence' ms = loop ms [] >>= return . reverse where
    loop []     xs = return xs
    loop (m:ms) xs = do
        x <- m
        loop ms (x:xs)

mapM' :: Monad m => (a -> m b) -> [a] -> m [b]
mapM' f xs = sequence' $ map f xs

forM' :: Monad m => [a] -> (a -> m b) -> m [b]
forM' = flip mapM'

replicateM' :: Monad m => Int -> m a -> m [a]
replicateM' n x = sequence' (replicate n x)

filterM' :: Monad m => (a -> m Bool) -> [a] -> m [a]
filterM' pred xs = loop xs [] >>= return . reverse where
    loop []     xs' = return xs'
    loop (x:xs) xs' = do
        keep <- pred x
        loop xs (if keep then (x:xs') else xs')

Note: sequence_, mapM_, forM_, et les replicateM_fonctions sont toujours un meilleur choix si vous n'êtes pas intéressé par la liste des résultats.

Joey Adams
la source
+1 pour CCAN, bien que je puisse être considéré comme un peu biaisé :)
Tim Post
4

Je finis par implémenter split / join ala Perl dans des langues qui ne l'ont pas.

J'ai également réimplémenté atoi et itoa en C plus de fois que je ne le pense (junk de systèmes embarqués).

Paul Nathan
la source
4

Non.

Je fais la plupart de mon codage en Java, et la meilleure pratique est de réutiliser des "utils" des bibliothèques Apache Commons et des projets similaires.

Si vous êtes objectif à ce sujet, il y a peu de cas où votre propre collection "utils" sera une amélioration significative par rapport à ce que d'autres personnes ont déjà fait. Et si ce n'est pas une amélioration, votre bibliothèque d'utilitaires est probablement une perte de temps de développement et une nuisance / fardeau pour les futurs responsables.

Stephen C
la source
3

J'ai eu quelques manipulations de date que j'ai effectuées en utilisant Java, puis j'ai commencé à utiliser JodaTime car j'avais entendu de bonnes choses à ce sujet et à inclure dans Java 7 (je ne sais pas si c'est toujours le cas, mais même si ce n'est pas le cas, il l'est toujours vaut bien l'utiliser à mon humble avis).

Il a transformé une classe de 50+ lignes en une seule ligne avec environ trois appels de méthode en chaîne.

Pour les curieux, cela impliquait d'obtenir la date de chaque jour des n semaines passées: par exemple, le chiffre des ventes pour un lundi il y a 10 semaines, etc.).

Et en fait partie

public static DateTime getDayPreviousWeek(DateTime dt, DayOfWeek dayOfWeek, int n_weeks) {
       return dt.minusWeeks(n_weeks).dayOfWeek().setCopy(dayOfWeek.getDayAsString());
}
NimChimpsky
la source
java a des méthodes d'extension?
Kugel
non, mais je pense que cela pourrait les avoir dans la version 7
NimChimpsky
2

J'ai toujours un utilspaquet quelconque, même en Java, mais ma collection d'utilitaires PHP est la plus réutilisée. Il y a tellement de bonnes bibliothèques en Java, que j'ai déjà une bibliothèque incluse dans le projet ou que je dois concevoir moi-même quelques utilitaires manquants. Les bibliothèques PHP ont tendance à faire trop pour que je veuille les inclure dans mes projets.

J'aime un peu cette fonction pour PHP, affinée avec l'aide de StackOverflow ...

function getValueFromDotKey(&$context, $name) {
    $pieces = explode('.', $name);
    foreach ($pieces as $piece) {
        if (!is_array($context) || !array_key_exists($piece, $context)) {
            // error occurred
            return null;
        }
        $context = &$context[$piece];
    }
    return $context;
}

Il est similaire aux BeanUtils d'Apache pour Java, et je l'utilise dans un but similaire, donnant aux éléments de formulaire dans un langage de modèle une seule clé qui peut obtenir / définir une valeur imbriquée dans un tableau source:

$source = array('a' => array('b' => 5));

$val = getValueFromDotKey($source, 'a.b');

Bien sûr, le PHP, je voulais garder la méthode aussi légère que possible de sorte qu'il n'est pas tout à fait aussi featureful que BeanUtils;)

Nicole
la source
2

La bibliothèque standard de Scala ne possède pas certaines fonctions d'ordre supérieur les plus couramment utilisées.

Deux de ces fonctions dont j'ai le plus souvent besoin:

// #1: unfold
def unfold[T, R](init: T)(f: T => Option[(R, T)]): List[R] = f(init) match {
  case None => Nil
  case Some(r, v) => r :: unfold(v)(f)
}

// #2: zipWith
def zipWith[A, B, C](xs: List[A], ys: List[B])(f: (A, B) => C): List[C] = {
  (xs, ys).zipped.map(f)
}
manquantfaktor
la source
1

Actuellement non. J'en avais un quand je faisais du C, mais maintenant que je fais Java, cela n'a plus de sens, compte tenu de toutes les bibliothèques standard disponibles, ainsi que de tous les goodies provenant du projet Apache.

Une des choses utiles dans mon C lib était une implémentation de machine à états finis rapide et sale, qui a permis la définition d'une machine à états finis avec seulement deux chaînes et un tableau de chaînes. Il pourrait être utilisé pour vérifier les chaînes par rapport aux règles (par exemple, "doit avoir 4 à 6 caractères, le premier une lettre, les chiffres de reste"), mais la disponibilité des expressions régulières rendait cette chose complètement inutile.

utilisateur281377
la source
1

Je ne peux pas écrire d'interfaces de bureau maintenant sans dialogues dynamiques , basés sur une exécution différentielle . C'est un hack sur lequel je suis tombé vers 1985, et je l'ai réimplémenté dans plusieurs langues plus de fois que je me souvienne.

Mike Dunlavey
la source
1

J'ai trouvé que j'écrivais beaucoup du même code dans django, Faites cette chose courante, puis cette chose courante, et enfin cette chose courante. Obtenez essentiellement un ou plusieurs éléments de la base de données ou enregistrez les résultats d'un formulaire.

Si chacune de ces choses se produit une seule fois dans une vue, je peux utiliser les vues génériques django. Malheureusement, ceux-ci ne sont pas vraiment composables, et j'ai dû faire plusieurs choses en séquence.

J'ai donc écrit une bibliothèque de vues encore plus générique, qui fonctionnait en créant d'abord une liste d'actions à partir d'ensembles de requêtes pertinents (ou autre), puis en enveloppant la liste dans une vue.

Je dois encore écrire quelques vues à la main, mais elles sont généralement suffisamment complexes pour qu'elles ne soient pas réutilisables. Tout le passe-partout atterrit juste ailleurs, soit en vue générique, soit en tant que décorateur de vue (souvent une vue générique décorée). Cela finit généralement par représenter environ 10% des gestionnaires que j'écris, car certains gestionnaires génériques peuvent faire tout le reste.

SingleNegationElimination
la source
1

Oui, mais uniquement pour les structures d'idiomes spécifiques au domaine (comme les conteneurs spécifiques aux objets de jeu).

Comme ce sont des outils utilitaires simples et complexes, je ne suis pas fier de quoi que ce soit. De toute façon, je suis l'utilisateur unique en ce moment, il n'y a donc pas de quoi être fier.

Klaim
la source
1

Tri indirect C ++, basé sur la STL sortet un modèle de foncteur.

Le besoin d'un tri indirect (dans lequel la sortie souhaitée était les indices de permutation qui résulteraient du tri des données, mais pas des données triées elles- mêmes) est apparu à plusieurs reprises dans un certain nombre de projets. Je me suis toujours demandé pourquoi STL n'avait pas fourni d'implémentation pour cela.

Un autre était un vecteur cyclique C ++, où les indices positifs et négatifs sont modulo avec la taille du vecteur (de sorte que toutes les valeurs entières sont des indices valides pour le vecteur).

rwong
la source
-4

J'ai écrit un petit paquet d'utilitaires quand je faisais du développement Java dans mon Comp. Classe Sci au lycée. Je suis le plus fier de mon générateur de nombres aléatoires.

/**
* Returns a random integer.
*
* @returns    int    Random integer
*/
public static int getRandom()
{
    return 4; // Chosen by a fair dice roll.
              // Guaranteed to be random.
}

Props à mon inspiration.

Josh K
la source
12
allez, xkcd ....
Darknight
2
Allez, ça n'a pas d'importance.
Josh K
1
Avec vos votes actuels à -2, je suppose que cela compte vraiment ...
user7676
8
Le plagiat est la forme la plus élevée de flatterie, sauf lorsqu'elle est évidente.
Maxpm
5
Et bien le bouton downvote dit: "Cette réponse n'est pas utile". Je suppose qu'il y a un besoin d'un bouton supplémentaire: "... mais bien sûr c'est drôle"
skajfes