Problemi fondamentali di programmazione [chiuso]

-1

Ho solo lasciato correre il cervello e i risultati sono di nuovo caotici. Mi preoccupa il modo in cui, nelle basi, la programmazione stessa può essere piuttosto ridondante.

Diamo un'occhiata a questo frammento di codice e analizziamo cosa fa

public function __construct($data = array(), $strict = false, $prefix = null) {
    if (!$data) {
        return;
    }

    foreach ($data as $key => $val) {
        if ($prefix) {
            if (($pos = strpos($key, $prefix)) === 0) {
                $key = substr($key, 0, $pos);
            } else {
                continue;
            }
        }

        if ($strict === false || property_exists($this, $key)) {
            $this->$key = $val;
        }
    }
}

Quando costruisci questo oggetto possiamo fornire una matrice per riempire le sue proprietà, tuttavia possiamo fare un po 'di filtraggio. Se abilitiamo il parametro strict, riempiremo solo le chiavi che ha già, e se forniamo un parametro prefisso esso riempirà solo proprietà che corrispondono a chiavi che iniziano con un certo prefisso.

Vedi, tuttavia, che abbiamo n * 2 istruzioni if valutate nel ciclo dato come minimo. Se avremo 100 elementi nell'array $data ci saranno almeno 200 if-statements valutate. Non sarebbe meglio se potessimo ridurlo?

public function __construct($data = array(), $strict = false, $prefix = null) {
    if (!$data) {
        return;
    }

    if ($strict === false) {
        if ($prefix) {
            foreach ($data as $key => $val) {
                if (($pos = strpos($key, $prefix)) === 0) {
                    $this->{substr($key, 0, $pos)} = $val;
                }
            }
        } else {
            foreach ($data as $key => $val) {
                $this->$key = $val;
            } 
        }
    } else { 
        if ($prefix) {
            foreach ($data as $key => $val) {
                if (property_exists($this, $key) && ($pos = strpos($key, $prefix)) === 0) { 
                    $this->{substr($key, 0, $pos)} = $val;
                } 
            }
        } else { 
            foreach ($data as $key => $val) {
                if (property_exists($this, $key)) {
                    $this->$key = $val;
                } 
            }
        }
    }
}

Come possiamo vedere chiaramente, la leggibilità è andata a rotoli ma tuttavia abbiamo ridotto la quantità di espressioni valutate da 200 a 2 nel migliore dei casi quando $strict === false e prefix === null - che è un aumento del 99% in termini di efficienza, in teoria.

Per lo più tutto ciò che ho detto fino ad ora è teorico, quindi la domanda è - C'è qualche beneficio effettivo nell'esempio 2 contrario all'esempio 1 riguardante l'esecuzione del programma?

Dato che non mi sono laureato in informatica non ho familiarità con gli interni di programmazione (e php) e come il codice viene valutato ed eseguito ad un livello più profondo, ma ho letto questo e quello su come i processori possono ridimensionare i collegamenti dichiarazioni if, ma come ho detto, non sono così familiare a rispondere alla domanda da solo.

    
posta php_nub_qq 30.10.2015 - 12:54
fonte

4 risposte

4

C'è un ottimo libro chiamato Clean Coder . Gestisce un sacco di domande come questa e contiene alcuni fantastici suggerimenti per le questioni fondamentali di cui stai parlando.

Per la tua domanda, esiste una regola chiamata Attenzione alle ottimizzazioni!

La spiegazione inizia con: Concentrarsi sulla scrittura di codice comprensibile. Il codice ottimizzato è spesso tutt'altro che leggibile ... e io personalmente sono d'accordo.

Se non hai bisogno di ottimizzare, non farlo. Se si verificano problemi di prestazioni o se è probabile che si verifichino problemi di prestazioni nel prossimo futuro, non si ha altra scelta che ottimizzare il proprio codice per prestazioni (anche se ciò significa rendere il codice meno leggibile).

Clean coder: link

    
risposta data 30.10.2015 - 15:50
fonte
3

Questo non è il codice ideale perché ci sono due operazioni distinte eseguite in questa unica funzione. C'è copia di una chiave solo se esiste sul target (filtro), chiamata "$ strict". Poi c'è la corrispondenza della chiave con un valore prefisso e la trasformazione della chiave in quel valore (filtro + mappa). (Si noti che quest'ultimo sembra in particolare che abbia una logica imperfetta, ma ho comunque usato il vostro codice nella mia risposta.)

Ci dovrebbero essere 2 funzioni separate che eseguono le loro operazioni specifiche su UN articolo. Quindi puoi semplificare il codice costruttore e persino aumentare la manutenibilità. Non ho usato PHP da un po 'di tempo, quindi non sono sicuro che funzionerà, ma qui va.

private function choosePrefix($prefix, $key) {
    if (($pos = strpos($key, $prefix)) === 0) {
        return substr($key, 0, $pos);
    } else {
        return false;
    }
}

private function strictCopy($target, $key, $value) {
    if (property_exists($target, $key)) {
        $target->$key = $value;
    }
}

Nota che separare le cose in funzioni aggiunge un sacco di spese generali (beh, non in tutte le lingue, ma in questo caso). Tuttavia, ha il vantaggio di descrivere l'intenzione di ciò che stai facendo! Vedrai di seguito.

In secondo luogo, stai trattando l'oggetto come se fosse una raccolta (dizionario). Questo è perfettamente naturale in PHP, ma nella maggior parte degli altri linguaggi, è comune rappresentare i dati della raccolta come una proprietà della classe invece che la chiave / i valori che vengono mescolati con le proprietà concrete.

$data = $this->data;
$data[$key] = $value;

In terzo luogo, ciò che risolve il tuo problema specifico è scegliere le tue funzioni di elaborazione prima di eseguire il ciclo e quindi eseguire le funzioni scelte sui dati all'interno del ciclo. Questo è abbastanza comune nei linguaggi funzionali e credo ora disponibile in PHP , se un po 'goffamente . Ecco un esempio ... ma ancora una volta, è da un po 'che non uso PHP.

private function passThru($dummy, $key) { return $key; }
private function choosePrefix($prefix, $key) {
    if (($pos = strpos($key, $prefix)) === 0) {
        return substr($key, 0, $pos);
    } else {
        return false;
    }
}

private function strictFilter($target, $key) {
    if ($key !== false && property_exists($target, $key)) {
        return $key;
    } else {
        return false;
    }
}

public function __construct($data = array(), $strict = false, $prefix = null) {
    if (!$data) {
        return;
    }
    // ternary form of if/then/else
    $chooseFn = ($prefix ? 'choosePrefix' : 'passThru');
    $filterFn = ($strict ? 'strictFilter' : 'passThru');

    foreach ($data as $key => $val) {
        $key = $this->$chooseFn($prefix, $key);
        $key = $this->$filterFn($this, $key);
        if ($key !== false) {
            $this->$key = $value;
        }
    }
}

Ora, se guardiamo il costruttore, senza dover effettivamente lavorare mentalmente attraverso il codice, posso vedere che potrei saltare alcuni tasti, altrimenti copierò la chiave / valore su questo oggetto. Fornire i nomi delle operazioni inserendoli in funzioni separate aiuta la leggibilità qui.

    
risposta data 30.10.2015 - 16:10
fonte
1

In risposta a questa domanda dai commenti:

We say that we sacrifice performance for maintainability and readability but why is this necessary, why is there no way to have the best out of both.

Il codice è un costrutto che viene utilizzato per esprimere una sequenza di passaggi che rappresenta un'attività da eseguire o per esprimere un insieme di espressioni per calcolare un valore. Affinché questa logica sia di qualche utilità, deve essere eseguita ad un certo punto, quindi la realtà di questa esecuzione deve essere presa in considerazione. È qui che arriva l'ottimizzazione.

Ottimizzazione significa aggiungere o modificare la logica del tuo compito per tenere conto dei dettagli del meccanismo di esecuzione, al fine di accelerare le cose. Spesso questo complica il codice, ad es. rompendo le astrazioni utilizzate dal codice per ottenere e modificare i dettagli di implementazione.

Il codice mantenibile ha le proprietà di (tra gli altri) chiarezza e semplicità; rimuovendo questi nel tuo codice, ad es. rompendo le astrazioni che li comprano, causerà la perdita di una certa manutenibilità.

Questo non significa necessariamente che tutte le ottimizzazioni interrompano la manutenibilità. Può essere possibile scrivere codice gestibile tenendo conto delle particolarità del meccanismo di esecuzione o applicare diverse astrazioni per farlo. (O usare correttamente le astrazioni in primo luogo.)

    
risposta data 30.10.2015 - 15:51
fonte
1

Non è necessario alcun compromesso. Sfortunatamente non conosco il php abbastanza bene da scrivere in esso, quindi utilizzerò invece pseudo-codice. Il tuo algoritmo può essere simile a:

if($prefix) {
    $data = $data filtered to only include items prefixed with $prefix;
    $data = $data mapped to remove $prefix;
}

if($strict) {
    $data = $data filtered to only include items where $item.$key already exists;
}

foreach($item in $data) {
    add $item to $this;
}

Ciò ti consente di non ripetere i tuoi o logici controlli, e in un linguaggio che fornisce manipolazione di raccolta dichiarativa (come LINQ di C # o Java Stream API di Java 8), sarebbe molto pulito e chiaro .

Si noti che, realisticamente perché il controllo dei booleani è così a buon mercato, probabilmente sarebbe ancora meno performante della tua versione, ma è solo perché hai scelto un'operazione così economica da essere inutile da ottimizzare. Si spera che questo affronti un punto di vista più generale rispetto alla pulizia: spesso è illusorio.

    
risposta data 30.10.2015 - 17:24
fonte

Leggi altre domande sui tag