Quale comportamento si aspettano la maggior parte degli utenti da "Iteratore di riga" e "Iteratore di colonna"?

2

Diciamo che ho una classe Matrix che ho già implementato.

Matrix<float> mat(30, 30);
for(size_t row = 0; row < mat.rows(); row++) {//Assume Row-Major ordering for performance reasons
    for(size_t column = 0; column < mat.columns(); column++) {
        mat(row, column) = float(row+1) / float(column+1);
    }
}

Vorrei aggiungere a questa implementazione alcuni concetti di "Row Iterator" e "Column Iterator". Una sorta di interfaccia come la seguente:

//public:
    row_iterator begin_row(size_t row); //Is this intuitive?
    const_row_iterator begin_row(size_t row) const;
    const_row_iterator cbegin_row(size_t row) const;
    row_iterator end_row(size_t row); 
    const_row_iterator end_row(size_t row) const;
    const_row_iterator cend_row(size_t row) const;
    //Same for column iterators

Il problema è che non mi è chiaro quale comportamento sia intuitivo per la maggior parte degli utenti. Il mio istinto è che un "Row Iterator" itera "all'interno" della riga, cioè,

auto begin = mat.begin_row(1);
auto end = mat.end_row(1);
for(; begin < end; ++begin) *begin = 5.f;//Fills the second row with the value 5.

Tuttavia, mi viene in mente che alcuni utenti potrebbero aspettarsi un "Row Iterator" per iterare "attraverso" le righe, cioè

auto begin = mat.begin_row(1);
auto end = mat.end_row(1);
for(; begin < end; ++begin) *begin = 5.f;//Fills the second column with the value 5.

L'implementazione di entrambe le versioni di questo codice è (teoricamente) banale, ma quale comportamento sembra più intuitivo per un utente medio? C'è un design migliore che eviti questa ambiguità?

    
posta Xirema 13.10.2017 - 22:31
fonte

3 risposte

7

Personalmente mi aspetto che questo iteratore possa scorrere "su" righe diverse e non "all'interno" della stessa riga. Il motivo per cui penso che questo sia dovuto al fatto che quando leggo "row_iterator" penso a un contenitore (vettore, elenco, mappa, ecc.) Che contiene diverse "righe" che ho ripetuto. Quindi, incrementando l'iteratore, ora indico una riga diversa. Al contrario, se leggo "row :: iterator" penso di iterare "all'interno" degli elementi della stessa riga.

Tuttavia, è difficile dire quale utente penserà in un modo o nell'altro. Anche se potesse essere determinato ciò che l'utente medio vorrebbe interpretare nel senso, ci sarebbero degli anomali occasionali che potrebbero fraintendere. Nella mia esperienza, anche le interfacce meglio progettate possono essere fraintese almeno da qualcuno. Non penso che ci sia davvero una risposta "migliore" qui.

Qualunque implementazione tu scelga e qualunque sia il nome che dai a questo iteratore, ti suggerirei di aggiungere una breve documentazione nel file di intestazione che contiene la sua definizione che chiarisce cos'è un row_iterator. Se lo impacchettate in una specie di libreria, assicuratevi che la stessa documentazione sia inserita nel manuale utente da qualche parte dove l'utente della classe Matrix lo vedrà probabilmente.

La chiave, almeno nella mia testa, è di documentare chiaramente ciò che fa e rendere la documentazione visibile all'utente finale.

    
risposta data 13.10.2017 - 22:48
fonte
4

Bene, suggerisco di ripensare il tuo design.
Perché? Semplicemente perché non può essere utilizzato nei cicli for-range.

Considera qualcosa di più sulla falsariga di:

public:
    // Access by row
    row_view<const T> rows() const;
    row_view<T> rows();
    // Access by col
    col_view<const T> cols() const;
    col_view<T> cols();
    // Access by cell
    iterator<T> begin();
    iterator<const T> begin() const;
    iterator<T> end();
    iterator<const T> end() const;
    
risposta data 14.10.2017 - 11:13
fonte
1

Come disse una volta Leon Bambrick:

There are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors.

Quando dai un nome a qualcosa row_iterator e poi nominerai qualcos'altro column_iterator , è logico che uno supporterà che il primo iteri attraverso la raccolta di righe, e il quest'ultimo attraverso la raccolta di colonne. Pertanto, il risultato della singola iterazione sarebbe una riga (nel primo caso) o una colonna (nel secondo caso).

Considera di dare nomi più intuitivi agli iteratori e / o documentare in più dettagli (e alcuni esempi) come ognuno di questi dovrebbe essere usato.

    
risposta data 14.10.2017 - 10:37
fonte

Leggi altre domande sui tag