Perché non c'è una prossima operazione sull'enumerazione?

6

Nei linguaggi di programmazione più popolari come Java e C # esiste un modo per definire enums , che sono essenzialmente tipi di dati con un insieme fisso di valori, ad es. DayOfWeek .

Il problema è, dato un valore, ad es. DayOfWeek.Monday , come faccio a ottenere il valore successivo di enum , in questo caso particolare DayOfWeek.Tuesday ?

Comprendo che non tutti gli enums sono ordinati e potrebbero esserci diversi tipi di ordini (ciclici, parziali, ecc.), ma nella maggior parte dei casi una semplice operazione next sarebbe sufficiente. Infatti, poiché l'insieme di valori in enum è fisso e limitato, ciò potrebbe essere fatto in modo completamente dichiarativo, assumendo che ci sia un mezzo nella lingua per farlo. Tuttavia, nella maggior parte dei linguaggi di programmazione è semplicemente impossibile, almeno non così facilmente come potrebbe essere in teoria.

Quindi, la mia domanda è: perché è così? Quali sono i motivi per cui non viene fornita una sintassi semplice per la dichiarazione del valore next per enum ? Suggerisco, in C # o Java, che questo potrebbe essere fatto anche con uno speciale attributo o annotazione , risp. Ma non ce ne sono, per quanto ne so.

Sono esplicitamente che non sto chiedendo per soluzioni alternative; So che ci sono soluzioni alternative. Voglio solo sapere perché devo impiegare una soluzione alternativa in primo luogo.

    
posta proskor 08.07.2014 - 10:55
fonte

9 risposte

10

Why isn't there a next operation on enums?

È difficile generalizzare.

È una decisione presa da ciascun progettista / team di linguaggio di programmazione. Tuttavia, alcuni linguaggi di programmazione fanno forniscono un'operazione "successiva" per i tipi enumerati.

In Pascal, la funzione succ restituisce il valore successivo di un tipo enumerato e la funzione pred restituisce il valore precedente. Ma questo funziona solo per i tipi enumerati classici. I tipi enumerati in stile C hanno "buchi" nel dominio del tipo e le funzioni succ e pred non sono consentite.

Riferimento: link

E, in effetti, ciò fornisce un indizio sul perché molte lingue con enumerazione / tipi enumerati non hanno un'operazione "successiva". È proibitivamente costoso quando non puoi implementare "next" usando l'aggiunta di interi sotto il cofano.

Naturalmente, nelle lingue che non supportano direttamente next , è sempre possibile implementarlo per te ... se ne hai bisogno ... come una sorta di metodo / funzione di supporto.

    
risposta data 08.07.2014 - 13:37
fonte
10

In Java, l'enum è un tipo di riferimento (non lasciarti ingannare da quella piccola "e"). Puoi fare cose pulite con esso come definire i metodi. Un classico esempio di questo è il Operation enum definito come:

public enum Operation {
  PLUS   { double eval(double x, double y) { return x + y; } },
  MINUS  { double eval(double x, double y) { return x - y; } },
  TIMES  { double eval(double x, double y) { return x * y; } },
  DIVIDE { double eval(double x, double y) { return x / y; } };

  // Do arithmetic op represented by this constant
  abstract double eval(double x, double y);
}
//Elsewhere:
Operation op = Operation.PLUS;
double two = op.eval(1, 1);

E puoi vedere ... sì, ha dei metodi e quindi non è un primitivo. Loro sono Oggetti.

C'è anche qualche magia che accade nel compilatore come descritto nel tutorial di Java Enum :

The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared. This method is commonly used in combination with the for-each construct to iterate over the values of an enum type. For example, this code from the Planet class example below iterates over all the planets in the solar system.

E sarò certamente d'accordo che è fastidioso che questo non sia menzionato nel javadocs.

I valori restituiti dalla chiamata values() sono un array su cui è possibile eseguire l'iterazione.

class Demo {
    enum DayOfWeek {
        U, M, T, W, R, F, S
    }

    public static void main (String[] args) {
        for (DayOfWeek day : DayOfWeek.values()) {
            System.out.println(day.toString());
        }
    }
}

Stampa:

U
M
T
W
R
F
S

Esegui questo codice su ideone

Perché no? Bene, data la possibilità di ottenere l'array values() , riempie tutti i casi d'uso per il metodo next() ed è un po 'più potente.

Se davvero vuoi che il metodo next() richieda un po 'di un cerchio per ottenere la matrice in un oggetto Iterator (che è esso stesso una sostituzione per il tipo Enumeration - vedi javadoc ):

import java.util.*;

class Demo {
    enum DayOfWeek {
        U, M, T, W, R, F, S
    }

    public static void main (String[] args) {
        Iterator days = Arrays.asList(DayOfWeek.values()).iterator();
        while(days.hasNext()) {
            System.out.println(days.next());
        }
    }
}

Esegui questo codice in ideone

Quindi, di nuovo, il motivo per cui non è lì è che non è necessario. Puoi recuperare un array dall'enumerazione in modo semplice e iterare o convertire tale array in Iterator che ti consente di chiamare next() .

C'è anche forse la confusione che si sarebbe provata a fare in modo che enum e Enumeration sembrassero troppo simili (hanno già un aspetto troppo simile) e cercano di rendere il enum più simile a Enumeration con le stesse chiamate di metodo avrebbero ulteriormente confuso le cose. Detto questo, questa è solo speculazione.

Fondamentalmente, Java enum è molto simile al C enum con la possibilità di avere alcuni metodi sul lato. È un po 'più limitato non è possibile avere numeri specifici dietro le quinte che puoi in C:

/* This is C */
enum cardsuit {
    CLUBS    = 1,
    DIAMONDS = 2,
    HEARTS   = 4,
    SPADES   = 8
};

Le persone a volte puntano su Pascal e dicono che succ può essere chiamato su un enum lì, perché non in Java? Le enunciati Pascal sono tipi ordinali mentre i enumeri C non lo sono. Java prende in prestito da C piuttosto che da Pascal.

Non c'è niente che dice che non puoi definire la tua funzione next() o prev() . È piuttosto facile da fare dopo aver spostato un fastidio di riferimenti in avanti .

class Demo {
    enum DayOfWeek {
        U, M, T, W, R, F, S;

        private DayOfWeek n;
        private DayOfWeek p;

        static {
            U.n = M; U.p = S;
            M.n = T; M.p = U;
            T.n = W; T.p = M;
            W.n = R; W.p = T;
            R.n = F; R.p = W;
            F.n = S; F.p = R;
            S.n = U; S.p = F;
        }

        public DayOfWeek next() { return this.n; }
        public DayOfWeek prev() { return this.p; }

    }

    public static void main (String[] args) {
        System.out.println(DayOfWeek.M.next());
        System.out.println(DayOfWeek.U.prev());
    }
}
T
S

...

  1. L'enum è stato preso in prestito dalla tradizione C di un enum, non dalla tradizione Pascal dei tipi ordinali
  2. Non volevano confondere enum con Enumeration
  3. Hai la possibilità di recuperare facilmente tutti i valori con un tipo iterabile
  4. È possibile definire un metodo next() che funzioni in qualsiasi modo si desideri.
risposta data 09.07.2014 - 01:15
fonte
2

È a causa della natura delle enumerazioni (enum in Java) , < a href="http://msdn.microsoft.com/en-us/library/sbbt4032.aspx"> (enum in C #) . Sono solo costanti fisse nella lingua, contenenti dati essenziali (a volte solo i loro nomi o solo identità), non un tipo di elenco. Quindi se hai bisogno di questo tipo di logica (che potrebbe essere applicabile a molti casi), allora devi effettivamente creare la logica.

Oltre ad essere una sorta di elenco collegato, può essere anche singleton (vedi: Esempio di implementazione: utilizzo di enum ). È più questione di uso. Tristemente enum (almeno in Java) in questo modo più complesso perché non puoi estenderli o definire un'interfaccia direttamente per loro.

È un dibattito aperto se è una buona pratica o no usare le enumerazioni per una sorta di elenco costante o singleton, il fatto è che puoi farlo. Voterò di non usarlo a meno che non sia un modello ampiamente utilizzato nel progetto. Puzza di hacking per me se viene usato localmente.

    
risposta data 08.07.2014 - 13:17
fonte
1

le enumerazioni vengono sostituite con i loro valori costanti nell'eseguibile. non possono essere attraversati in fase di esecuzione.

in C enum fanno parte del codice sorgente e il compilatore sostituisce l'enum con la costante int.

     // C source - enum gets replaced with constant
enum _tenum { A, B, C};
_tenum ft;
ft = B;
     // assembler enum is gone. only constant value 1 remains
mov DWORD PTR _ft$[ebp], 1

if (ft == B) {

cmp DWORD PTR _ft$[ebp], 1
jne SHORT $LN1@main

in enum Java fanno anche parte del file sorgente e il generatore del codice byte sostituisce l'enum con il valore statico finale pubblico.

Ecco una spiegazione    link

    
risposta data 08.07.2014 - 20:49
fonte
1

Tecnicamente parlando, c'è un modo per eseguire una prossima operazione in entrambe le lingue, anche se varia tra Java e C #.

C #

In C #, le enumerazioni sono tipi di numeri nel loro nucleo. Puoi semplicemente eseguire l'aggiunta con numeri interi:

var day = DayOfWeek.Sunday;
foreach(var n in Enumerable.Range(0, 7))
    Console.WriteLine("Day of week: {0}", day + n);

nota: quanto sopra funziona allo stesso modo con day ++

Output:

Day of week: Sunday
Day of week: Monday
Day of week: Tuesday
Day of week: Wednesday
Day of week: Thursday
Day of week: Friday
Day of week: Saturday

Java

Java consente di definire enumerazioni come tipi di oggetti completi. Di conseguenza, non puoi semplicemente incrementarlo. Tuttavia, StackOverflow mostra diverse alternative .

TL; DR: c'è un modo un po 'hacker di incrementare un enum, ma il modo suggerito è di implementare una funzione next() nell'enumerazione.

    
risposta data 08.07.2014 - 19:42
fonte
0

@ dan-lyons answer for C # va bene, ma funziona solo quando:

  • l'enum viene definito utilizzando valori interi consecutivi
  • sai quanti valori ci sono
  • sai quali sono i loro valori interi.

Invece, suggerirei di usare Enum.GetValues() e iterare la collezione che restituisce.

enum MyEnum
{
    Value1 = -4,
    Value2 = 0,
    Value3,       // = 1
    Value4,       // = 2
    Value5 = 100,
}

...

// We need to use Cast<>() because GetValues() only returns
// a non-generic Array, which can hold any type of object.
var values = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
foreach(var value in values)
{
    Console.WriteLine($"{value} = {(int)value}");
}

Questo stamperà tutti i valori esplicitamente definiti dal loro nome in codice e dai loro equivalenti interi.

Value1 = -4
Value2 = 0
Value3 = 1
Value4 = 2
Value5 = 100

Non è necessario conoscere i valori o quanti ce ne sono.

    
risposta data 25.05.2017 - 23:43
fonte
0

Fornire questo tipo di comportamento sulle enumerate potrebbe solo facilitare un attacco.

Le enumerazioni non sono matrici. Possono tecnicamente essere implementati come array, ma il loro significato logico è un numero limitato di costanti raggruppate che possono essere utilizzate in un modo sicuro per il tipo. Non esiste una relazione logica sequenziale tra i membri del gruppo.

Una definizione di tipo enum non è una lista, non è nemmeno una collezione, quindi i singoli membri non hanno un indice, l'ordine in cui appaiono nel codice sorgente è logicamente privo di significato.

Con l'implementazione di DayOfWeek, Microsoft è pragmatico e causa confusione su cosa sia e dovrebbe essere usato per enum:

If cast to an integer, its value ranges from zero (which indicates DayOfWeek.Sunday) to six (which indicates DayOfWeek.Saturday)

Comodo ... e sbagliato. Il modo corretto sarebbe stato quello di offrire una classe Week con un metodo statico:

static DayOfWeek Day(DayOfWeek pivot, int offset)

Quindi non ci sarebbe un problema di roll-over e non si dovrebbe abusare dell'enumerazione.

    
risposta data 26.05.2017 - 00:58
fonte
-1

Prima di Java c'erano C, C ++ e Pascal. Le enumerazioni in C e C ++ potevano avere valori molto diversi: non avevano bisogno di essere interi contigui. Questo ha avuto il vantaggio di poter definire un enum per cose come i codici dei messaggi di Windows, che erano un insieme sparse di numeri interi non contigui.

(Borland) Pascal ha adottato l'altro approccio, in cui tutte le enumerazioni avevano valori ordinali contigui a partire da zero. Ciò significava che l'aritmetica degli insiemi era facile (ad es. Definire Jan..Dec come enum, poi SummerMonths come un insieme di mesi (luglio, agosto, settembre). Era quindi legale scrivere if (month in SummerMonths) ... Era anche efficiente in termini di memoria, dal momento che le enumerazioni potevano essere mappate in bit.

Nel bene o nel male, Java è andato giù per il percorso C / C ++. Per questo motivo, un'operazione next () non ha senso.

    
risposta data 11.07.2014 - 12:11
fonte
-5

Parlando per Java, ciò che stai effettivamente cercando è Enumerazione (che è più simile a iteratore) che ha il supporto nextElement.

enum è un tipo di dati che consente alla variabile di avere un insieme di valori predefiniti.

    
risposta data 08.07.2014 - 11:26
fonte