Hai la tua libreria "misc utils"? Di quale parte sei più orgoglioso? [chiuso]


32

So che molti di noi mantengono la nostra piccola biblioteca personale con strumenti e utilità che usiamo spesso.

Ho avuto il mio da quando avevo 16 anni, quindi è cresciuto fino a dimensioni considerevoli. Alcune delle cose che ho scritto sono state aggiunte al framework. Ho scritto la mia piccola implementazione di alberi delle espressioni da utilizzare con algoritmi genetici molto prima di LINQ, che mi piace molto e di cui ero orgoglioso all'epoca - ovviamente ora è piuttosto inutile. Ma recentemente l'ho passato e aggiornato a .NET 4.0 e riacceso un interesse.

Quindi sono curioso di sapere per cosa usi la tua libreria. Forse potremmo avere alcune idee interessanti per piccoli frammenti utili e condividerli tra di noi.

Quindi le mie domande sono:

  • Hai una libreria di utilità varie?
  • Di quale parte sei più orgoglioso e perché?

Fai un esempio di codice, se lo desideri :-)


Nessuno sembra dare risposte positive ...
Joey Adams,

@Joey Adams giusto? Attualmente 17 voti di domanda e 6 voti di risposta totali .
Nicole,

Non vedo davvero risposte degne di essere votate. Cosa significa un voto per loro? La natura della domanda è tale che le risposte ottengono semplicemente un "oh. Bello". tipo di reazione, e quindi o vota tutto o niente. (E non mi piace votare ogni risposta solo perché è lì. Se non altro, sono senza voti.: P)
Adam Lear

@Anna Lear, ok, sei scusata :)
Nicole,

3
Qualsiasi utilità decente dovrebbe essere messa su github e condivisa con il mondo. Non ha senso tenerlo nascosto se è veramente buono.
Giobbe

Risposte:


27

No.

Ho visto alcuni effetti da incubo di una dozzina di sviluppatori che aggiungono tutti i loro piccoli libretti di stile "util.h" ai progetti e li ho trasformati in un enorme casino di nomi e comportamenti incoerenti. Proprio come PHP. Quindi per questo motivo evito di farlo.

Evito di doverlo fare usando ambienti di programmazione che mi danno quasi tutti gli strumenti e le librerie di cui ho bisogno in anticipo quando possibile, come C # e Python.


7
Riscrivo costantemente la mia biblioteca per scopi organizzativi.
Maxpm

3
I casi in cui il pacchetto utils è diventato da incubo non significa che tutti siano cattivi. Non riesco a vedere come puoi evitarlo e non avere più duplicazioni di codice a causa di ciò. E quindi, test peggiori e meno efficienza.
Nicole,

2
@Renesis: i pacchetti utils sono disastrosi quanto le dichiarazioni goto. Certo, di per sé non è poi così male, ma sembra che finisca sempre per essere un disastro prima o poi. Per quanto riguarda la duplicazione del codice, se ti ritrovi a svolgere un compito simile praticamente in tutti i tuoi progetti, quindi per qualcosa come Python o C #, probabilmente lo hanno fatto anche altre persone ed è probabilmente nelle librerie standard da allora.
whatsisname

6
Nella mia esperienza, gli ingegneri con la propria biblioteca preferiranno usarlo prima di quello fornito dal sistema, quindi non è una buona pratica avere librerie personali. Una volta ho avuto un ragazzo che era assolutamente convinto che la sua funzione "strlen" fosse più veloce di quella fornita dal compilatore, perché l'ha scritta . Ci è voluta una semplice dimostrazione di come strlen sia un paio di istruzioni di assemblaggio per lui concesse che forse altre persone possono fare meglio.
JBR Wilkinson,

4
@JBRWilkinson Il tuo punto è ben preso. Non tutti i programmatori sono in grado di sviluppare codice comune.
Nicole,

15

SmartFormat

La mia utility preferita è quella che ho scritto: un semplice generatore / formatter di stringhe che rende davvero semplice trasformare i dati in stringhe con grammatica corretta.

Ad esempio, la maggior parte dei programmatori di costruire il testo da un modello: "There are {0} items remaining" ma questo porta ad errori grammaticali: "There are 1 items remaining".

Così, SmartFormat permette di scrivere: "There {0:is|are} {0} item{0:|s} remaining".

Ti basta sostituire String.Format(...)con Smart.Format(...)e basta!

Il codice SmartFormat è open source: http://github.com/scottrippey/SmartFormat/wiki


Questo mi ricorda il formato utilizzato da java.text.MessageFormat.
Barjak,

@barjak Interessante! Ho passato molto tempo a ricercare la formattazione "condizionale" e finora non ho mai trovato nulla di simile! MessageFormatha la ChoiceFormatclasse che consente una sintassi sorprendentemente simile! Un esempio dalla documentazione: "There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.". Grazie per aver menzionato questo riferimento.
Scott Rippey,

@barjak Solo per confermare il mio punto, SmartFormat ha molte altre funzionalità! La formattazione condizionale funziona per qualsiasi tipo di dati, come bool, date, timepan e object; supporta anche operatori avanzati, come "{Count:<0?negative|=5?five|>50&<100?large|other}". Ha una riflessione (vale a dire "There are {items.Length} items", può formattare elementi di array e intervalli di tempo. Inoltre, ha un modello di plug-in per supportare ancora più funzioni.
Scott Rippey,

Sembra essere potente, anzi. La formattazione degli array è interessante.
Barjak,

@barjak: Sì, la formattazione dell'array è davvero utile! Dai un'occhiata a questo esempio: Smart.Format("There are {0.Count} files: {0:'{}'|, |, and }.", files);si tradurrebbe in "There are 3 files: 'a.txt', 'b.txt', and 'c.txt'.". Non riesco a immaginare la "localizzazione" senza di essa.
Scott Rippey,

7

K Combinator (C #, Scala)

Uso il combinatore K in Ruby abbastanza spesso, principalmente nelle pieghe quando l'operazione di piegatura viene eseguita attraverso un effetto collaterale anziché un valore di ritorno, come in questo esempio:

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

Questo conta la frequenza con cui ogni elemento si presenta some_collection. Sfortunatamente, in realtà non funziona, poiché il blocco deve restituire il nuovo valore dell'accumulatore ad ogni iterazione, ma nelle assegnazioni di Ruby si valuta il valore assegnato.

Quindi, devi restituire esplicitamente il nuovo valore dell'accumulatore in questo modo:

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

Ma trovo brutto questo sequenziamento esplicito in questo stile funzionale usando le pieghe. Il combinatore K (chiamato Object#tapin Ruby) in soccorso:

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

L'ho già perso un paio di volte in C # (principalmente perché per qualche ragione mutatori di raccolta come List.Addreturn voidinvece di this) e Scala, quindi mi porto dietro questo:

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;
        }
    }
}

e in 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) }

Funzione identità (Ruby)

Qualcosa che mi manca in Ruby, è un modo ben chiamato per accedere alla funzione identità. Haskell fornisce la funzione di identità con il nome di id, Scala con il nome di identity. Ciò consente di scrivere codice come:

someCollection.groupBy(identity)

L'equivalente in Ruby è

some_collection.group_by {|x| x }

Non esce esattamente dalla lingua, vero?

La soluzione è

IDENTITY = -> x { x }

some_collection.group_by(&IDENTITY)

ForEach (.NET)

Un altro metodo gravemente mancante in 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);
        }
    }
}

3
Penso che il tuo ultimo esempio sia stato una decisione di progettazione calcolata. Il concetto di un Actionimplica effetti collaterali che vanno contro i principi di progettazione di LINQ.
ChaosPandion,

1
@ChaosPandion: cosa c'entra questo con LINQ?
Jörg W Mittag,

@ Jörg W Mittag - Le IEnumerableestensioni sono state aggiunte per LINQ.
ChaosPandion,

2
@ChaosPandion: ancora non capisco. ForEachnon è un operatore LINQ. Perché dovrebbero essere applicate le restrizioni che si applicano solo agli operatori LINQ ForEach, che non sono operatori LINQ? E perché gli effetti collaterali sono vietati IEnumerable.ForEachma consentiti List.ForEach? Inoltre, perché gli effetti collaterali sono vietati IEnumerable.ForEachma consentiti foreach?
Jörg W Mittag,

@ Jörg W Mittag - Quello che sto dicendo è il fatto che manca dalle estensioni è stata una decisione di progettazione. Il fatto che List<T>abbia un ForEachè ragionevole considerando che è un tipo mutabile.
ChaosPandion,

6

Ho un convertitore di tipo Java. Ha una firma pubblica

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

e fa del suo meglio per convertire il valore di origine nel tipo di destinazione. In sostanza ti consente di eseguire la digitazione dinamica all'interno di un linguaggio tipicamente statico :-)

In realtà è utile con tipi numerici inscatolati. Quanto è irritante non riuscire a capire Integerdove Longci si aspetta? Nessun problema, convertilo e basta. O cosa succede se la tua funzione prevede un double, ma hai un nullda mettere lì? Kaboom, un NPE. Ma mettilo attraverso converte ottieni a NaN.


Soluzione interessante. Ho sempre pensato che Long dovrebbe estendere Integer. Ma anche allora avresti ancora il problema dell'autoboxing (per quanto ne so non c'è modo che l'autoboxing funzioni con l'ereditarietà). Inoltre, +1 per il NaNsupporto.
Nicole,

NaNè eccellente. Peccato che non ci sia nulla di simile per gli interi. Ho usato Integer.MIN_VALUEcome una convenzione. Di solito è "abbastanza strano" da notare, a differenza del valore predefinito 0. Non so perché il boxing automatico (un) non tratti (Double) nullcome NaN. È l'ovvia soluzione giusta, IMHO.
Joonas Pulakka,

6

Del codice misc che ho scritto, la maggior parte delle cose buone è dentro CCAN ora, mentre il resto tendo a trovare versioni migliori di progetti open source esistenti. Oggi mi ritrovo a scrivere codice "misc" sempre meno generico, a favore della scrittura di varianti specifiche di tale codice o della scrittura di moduli per scopi generali che posso rilasciare da soli.

C

Ecco una funzione e un typedef che ho usato più di una volta. Per le applicazioni che richiedono un tempismo, è difficile battere i millisecondi in termini di semplicità:

#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;
}

E altre funzioni C varie che tendo a usare più e più volte (e più):

/* 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]funzione di Haskell è O (n²) perché, con la sua firma di tipo, è consentito verificare solo se due elementi sono uguali. Una semplice alternativa O (n log n) è map head . group . sort, ma richiede forzare l'intero elenco di input prima di produrre output, mentre nubpuò iniziare a produrre output immediatamente. Quella che segue è un'alternativa O (n log n) a nubquella che raccoglie oggetti già visti in un 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)

In Haskell, io uso alternative a sequence, mapM, forM, replicateM, e filterM. Ciascuna di queste azioni genera un elenco, ma l'elenco non può essere utilizzato fino a quando l'azione non viene completata nella sua interezza (se si utilizza una monade rigorosa come IO). Le alternative costruiscono la lista al contrario piuttosto che formare una torre di thunk, che ho trovato attraverso il benchmarking per essere più veloce, almeno con 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')

Nota: sequence_, mapM_, forM_, e replicateM_le funzioni sono ancora una scelta migliore se non siete interessati nella lista dei risultati.


+1 per CCAN, anche se potrei essere considerato un po 'di parte :)
Tim Post

4

Finisco con l'implementazione di split / join ala Perl in lingue che non ce l'hanno.

Ho anche reimplementato atoi e itoa in C più volte di quanto non voglia pensare (junk di sistemi integrati).


4

No.

Faccio la maggior parte del mio codice in Java e la migliore pratica è quella di riutilizzare "utils" dalle librerie di Apache Commons e progetti simili.

Se sei obiettivo a riguardo, ci sono pochi casi in cui la tua raccolta "utils" sarà un miglioramento significativo di ciò che altre persone hanno già fatto. E se non si tratta di un miglioramento, allora la tua libreria utils è probabilmente una perdita di tempo di sviluppo e un fastidio / onere per i futuri manutentori.


3

Ho avuto alcune manipolazioni della data che ho eseguito utilizzando Java, quindi ho iniziato a utilizzare JodaTime poiché avevo sentito parlare di cose positive e che doveva essere incluso in Java 7 (non sono sicuro che sia ancora così, ma anche se non lo è è ancora vale la pena usarlo imho).

Ha trasformato una classe di oltre 50 in una linea con circa tre chiamate di metodo concatenate.

Per i curiosi si trattava di ottenere la data per ogni giorno di n settimane passate: ad es. Il valore delle vendite di lunedì 10 settimane fa ecc. Ecc.).

E qui ne fa parte

public static DateTime getDayPreviousWeek(DateTime dt, DayOfWeek dayOfWeek, int n_weeks) {
       return dt.minusWeeks(n_weeks).dayOfWeek().setCopy(dayOfWeek.getDayAsString());
}

java ha metodi di estensione?
Kugel,

no, ma penso che potrebbero averli nella versione 7
NimChimpsky

2

Ho sempre un utilspacchetto di qualche tipo, anche in Java, ma la mia raccolta di programmi di utilità PHP è la più riutilizzata. Ci sono così tante buone librerie in Java, che o ho già una libreria inclusa nel progetto o ho bisogno di progettare solo alcuni programmi di utilità mancanti per conto mio. Le biblioteche PHP tendono a fare troppo per me per includerle nei miei progetti.

Mi piace questa funzione per PHP, perfezionata con l'aiuto di 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;
}

È simile a BeanUtils per Java di Apache e lo uso per uno scopo simile, dando agli elementi del modulo in un linguaggio modello una singola chiave che può ottenere / impostare un valore nidificato in un array di origine:

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

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

Ovviamente, essendo PHP, volevo mantenere il metodo il più leggero possibile, quindi non è abbastanza caratteristico come BeanUtils;)


2

La libreria standard Scala non dispone di alcune funzioni di ordine superiore utilizzate più comunemente.

Due funzioni di cui ho bisogno più spesso:

// #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)
}

1

Attualmente no. Ne avevo uno quando stavo facendo C, ma ora che faccio Java, non ha più senso, considerando tutte le librerie standard disponibili, oltre a tutte le chicche provenienti dal progetto Apache.

Una delle cose utili nella mia libreria era un'implementazione della macchina a stati finiti veloce e sporca, che permetteva la definizione di una macchina a stati finiti con solo due stringhe e una serie di stringhe. Potrebbe essere usato per controllare le stringhe rispetto alle regole (es. "Deve essere lungo 4..6 caratteri, prima una lettera, cifre di riposo"), ma la disponibilità di regex ha reso quella cosa completamente inutile.



1

Ho scoperto che stavo scrivendo molto dello stesso codice in Django, fai questa cosa comune, poi questa cosa comune, e infine quella cosa comune. Praticamente ottenere uno o più elementi dal database o salvare i risultati di un modulo.

Se ognuna di queste cose si verifica solo una volta in una vista, allora posso usare le viste generiche di django. Sfortunatamente, quelli non sono veramente compostabili e ho dovuto fare diverse cose in sequenza.

Così sono andato e ho scritto una libreria di viste ancora più generica, una che ha funzionato prima costruendo un elenco di azioni dai relativi queryset (o qualsiasi altra cosa), quindi ho avvolto l'elenco in una vista.

Devo ancora scrivere alcune visualizzazioni a mano, ma di solito sono abbastanza complesse da non poter essere riutilizzate. Tutto il boilerplate atterra solo altrove, sia come vista generica, sia come decoratore di vista (spesso una vista generica decorata). Questo in genere finisce per essere circa il 10% dei gestori che scrivo, poiché alcuni gestori generici possono fare tutto il resto.


1

Sì, ma solo per strutture di idiomi specifici del dominio (come contenitori specifici degli oggetti di gioco).

Trattandosi di semplici strumenti di utilità piuttosto che complessi, non sono orgoglioso di nulla. Sono l'utente unico al momento, quindi non c'è niente di cui essere orgogliosi.


1

Ordinamento indiretto C ++, basato su STL sorte un modello di funzione.

La necessità di un ordinamento indiretto (in cui l'output desiderato erano gli indici di permutazione che sarebbero risultati dall'ordinamento dei dati, ma non dai dati ordinati stessi) è apparsa molte volte in numerosi progetti. Mi sono sempre chiesto perché STL non ha fornito un'implementazione per questo.

Un altro era un vettore ciclico C ++, in cui gli indici positivi e negativi sono modulo con le dimensioni del vettore (in modo che tutti i valori interi siano indici validi per il vettore).


-4

Ho scritto un piccolo pacchetto utils mentre stavo facendo lo sviluppo Java nel mio Comp. Lezione di sci al liceo. Sono molto orgoglioso del mio generatore di numeri casuali.

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

Puntelli alla mia ispirazione.


12
dai, xkcd ....
Darknight

2
Dai, non importa.
Josh K,

1
Con i tuoi voti attuali a -2,
suppongo che

8
Il plagio è la più alta forma di adulazione, tranne quando è ovvio.
Maxpm

5
Bene, il pulsante downvote dice: "Questa risposta non è utile". Immagino che sia necessario un pulsante adizionale: "... ma sicuramente è divertente"
skajfes
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.