Problemi di gestione dei vincoli per l'input di un metodo

0

Sto cercando consigli su come gestire i vincoli sull'input di un metodo. Ho un metodo che funziona solo se gli argomenti di input soddisfano determinati vincoli. Se il metodo viene chiamato su input che non soddisfa i vincoli, il metodo fa qualcosa di dannoso.

Il problema concreto è questo. Ho una classe Interval che rappresenta gli intervalli 1-dimensionale chiusi:

// code in C# or pseudo-code
class Interval
{
    private double lb, ub; // lower bound and upper bound of the interval

    public Interval(double lb, double ub)
    {
        if (lb > ub)
            throw new ArgumentException("Lower bound cannot be larger than upper bound!");

        this.lb = lb; this.ub = ub;
    }

    public bool Disjoint(Interval i)
    {
        return (ub < i.lb || i.ub < lb);
    }

    public void UniteWith(collection of Intervals)
    {
        ...
    }
}

Il metodo UniteWith dovrebbe prendere una raccolta di% disgiunto Interval come input e modificarlo in modo tale che, quando si pensa alla collezione come unione di insiemi, l'attuale Interval sia unita a quell'insieme. Inoltre, la raccolta deve essere modificata in modo tale che, una volta unito, sia di nuovo disgiunto Interval s.

Esempio:

collection is: [-3, 0], [2, 4], [5, 18], [21, 22]
current interval is: [3, 6]
resulting modified collection is: [-3, 0], [2, 18], [21, 22]

I metodi che ho assolutamente bisogno per la raccolta sono Add e Remove , quindi ICollection sembra essere una scelta ragionevole per il tipo di input (ripensandoci in C #, ma probabilmente si applica anche a Java e ad altri linguaggi OOP) ).

Perché l'algoritmo funzioni correttamente, ho bisogno che l'input sia ordinato. Se non è ordinato, l'input verrà modificato in modo imprevedibile. E questo è il punto in cui non so cosa fare:

  • Potrei prendere ICollection come argomento, ma poi avrei bisogno di verificare da solo se l'input è ordinato. Se non è ordinato, lancio un'eccezione per dire al programmatore che usa il mio codice che il suo input è cattivo. Credo di poterlo fare utilizzando l'enumeratore che ogni ICollection ha.

    public void UniteWith(ICollection<Interval> coll)
    {
        IEnumerator enumerator = coll.GetEnumerator();
        enumerator.MoveNext();
        Interval fst = enumerator.Current;
    
        while (enumerator.MoveNext())
        {
            Interval snd = enumerator.Current;
            if (fst.ub >= snd.lb)
                throw new ArgumentException("coll must be sorted ascendingly!");
            fst = snd;
        }
    
        // if we get here, we can start the actual algorithm
        ...
    }
    
  • Potrei rendere coll a SortedList o SortedSet nella firma del metodo anziché ICollection . Quindi sarei sicuro che sia ordinato, ma ora un utente non può inserire il suo IList anche se lo ha già ordinato, e dato che il suo IList fornisce tutti i metodi di cui avrà bisogno l'algoritmo ( Add e Remove e iterando su di esso), potrebbe essere deluso di non poterlo usare. Inoltre, non conosco un modo per dire al compilatore "questo input può essere SortedSet o SortedList ", e se non esiste un modo simile, è ancora più restrittivo. Ok, potrei scrivere due metodi per questo. Poi ho dovuto occuparmi di due commenti di documentazione (sto commentando OGNI metodo che ho) e le modifiche di copia e incolla sono sempre. È possibile, ma non mi sembra molto attraente.

  • Potrei mantenere il tipo di input ICollection , non eseguire alcun controllo sulla selezione dell'ingresso e consigliare nel commento della documentazione di non inserire alcuna raccolta non ordinata e spiegare le conseguenze di ciò. Quindi consegno la responsabilità all'utente del mio codice. Anche questo non mi sembra molto pulito, perché questo è esattamente ciò che fa C / C ++ (e ciò che non mi piace): ti lasciano accendere il computer; dicono che ti hanno lasciato perché non possono essere sicuri di non volerlo, ma in realtà non lo volevi - hai fatto un errore.

In attesa di suggerimenti, non solo per questo scenario concreto, ma anche per il caso generale - se esiste una cosa come un "caso generale". (Potrebbe anche essere che dipende sempre.)

EDIT:

Ho appena notato che per la prima e la terza opzione potrebbe essere meglio consentire solo input con un concetto di ordine. Anche se HashSet ha un enumeratore e come tale un ordine, è più una caratteristica interna. Dal suo concetto, non ha ordine (può cambiare quando aggiungi / rimuovi elementi). Quindi quale interfaccia posso inserire? C'è qualcosa come IOrderable ?

    
posta Kjara 27.07.2016 - 16:08
fonte

2 risposte

2
  1. ICollection con errori: penso che ICollection sia l'opzione migliore. Tuttavia, è responsabilità dell'utente utilizzare correttamente l'input, non l'utente responsabile del proprio codice per fornire un input corretto. Evita di lanciare errori a meno che non ci sia un modo ragionevole per usare l'input dato.

  2. SortedSet o SortedList : come hai detto, questo è ancora più restrittivo. Se, per qualsiasi ragione, l'input della funzione è in una forma diversa, tu, di nuovo, dovresti essere responsabile di utilizzare correttamente l'input.

  3. ICollection senza assegni: mentre la documentazione è buona, ci sono molte persone che non sempre la leggono. Questo approccio potrebbe portare a errori che richiederebbero molto tempo per identificare e risolvere.

Fondamentalmente, dovrebbe essere responsabilità del programmatore che il suo codice funzioni, preferibilmente con restrizioni minime. Non ho molta familiarità con C #, ma quando sto scrivendo Java, preferisco rendere i miei parametri Collection s o List s piuttosto che SortedSet s o Stack s. Se ho bisogno di un SortedSet , chiedo comunque un Collection e poi copio e ordinamento convertendolo in SortedSet o utilizzando metodi di ordinamento integrati. In C # la classe List<T> ha un metodo Sort e la conversione tra i tipi di raccolta doesn sembra troppo difficile , anche se è necessario utilizzare un ciclo for-each. Se ti preoccupi molto del rendimento, e facendo questa conversione è troppo dispendioso in termini di risorse, utilizzerei la tua seconda opzione (a SortedList o SortedSet ). Se scrivere due volte è troppo lavoro per te, puoi sceglierne uno e far convertire l'altro e passarlo nel metodo, avvertendo gli utenti che sarebbe meno efficiente.

Spero che questo aiuti!

    
risposta data 27.07.2016 - 17:06
fonte
2

La riga if (fst.ub >= snd.lb) , in cui si controlla se sono in ordine, fornisce un indizio per la soluzione. Sai se sono in ordine, quindi sai come ordinarli. Quindi hai Interval implementa IComparable<Interval> e aggiungi il seguente codice:

public void UniteWith(IEnumerable<Interval> intervals)
{
    var sortedIntervals = intervals.OrderBy(i => i);
    ...
    // do stuff with your ordered set of intervals
}

public int CompareTo(Interval other)
{
    if (ub < other.lb) return -1;
    if (lb > other.ub) return 1;
    return 0;
}

Inoltre, metterei in dubbio il tuo utilizzo di void qui. Non sarebbe meglio creare un nuovo Interval e restituirlo, lasciando quello attuale invariato?

    
risposta data 27.07.2016 - 17:26
fonte

Leggi altre domande sui tag