Suggerimento per la codifica: prova a codificare senza istruzioni If

Potresti scoprire soluzioni migliori

Codice da un progetto Facebook. Per favore, non denunciarli.
Aggiornamento: questo articolo fa ora parte del mio libro "Impara la programmazione con JavaScript moderno".
Leggi la versione aggiornata di esso su jscomplete.com/no-ifs.

Quando insegno ai principianti a programmare e presentare loro sfide con il codice, una delle mie sfide di follow-up preferite è: ora risolvi lo stesso problema senza usare istruzioni if ​​(o operatori ternari o istruzioni switch).

Potresti chiedere perché sarebbe utile?

Bene, penso che questa sfida costringa il tuo cervello a pensare in modo diverso e in alcuni casi, la diversa soluzione potrebbe essere migliore.

Non c'è niente di sbagliato nell'usare le istruzioni if, ma evitarle a volte può rendere il codice un po 'più leggibile per gli umani. Questa non è sicuramente una regola generale, poiché a volte evitare le istruzioni if ​​renderà il codice molto meno leggibile. Sii il giudice.

Evitare le dichiarazioni if ​​non riguarda solo la leggibilità. C'è un po 'di scienza dietro il concetto. Come mostreranno le sfide che seguono, non usare if-statement ti avvicina al concetto di codice come dati, che apre le porte a capacità uniche come la modifica del codice mentre viene eseguito!

In tutti i casi, è sempre divertente provare a risolvere una sfida di codifica senza l'uso di alcun condizionale.

Ecco alcuni esempi di sfide con le loro soluzioni if-based e if-less. Tutte le soluzioni sono scritte in JavaScript.

Dimmi quali soluzioni ritieni siano più leggibili.

Sfida n. 1: contare gli interi dispari in un array

Diciamo che abbiamo una matrice di numeri interi e vogliamo contare quanti di questi numeri sono dispari.

Ecco un esempio con cui testare:

const arrayOfIntegers = [1, 4, 5, 9, 0, -1, 5];

Ecco una soluzione che utilizza un'istruzione if:

lascia counter = 0;
arrayOfIntegers.forEach ((intero) => {
  const restinder = Math.abs (numero intero% 2);
  if (resto === 1) {
    contatore ++;
  }
});
console.log (contatore);

Ecco una soluzione senza istruzioni if:

lascia counter = 0;
arrayOfIntegers.forEach ((intero) => {
  const restinder = Math.abs (numero intero% 2);
  contatore + = resto;
});
console.log (contatore);

Nota: gli esempi precedenti usano forOach e mutano la variabile counter. È invece più pulito e sicuro utilizzare metodi immutabili. Tuttavia, questo articolo non tratta questo argomento. Resta sintonizzato per ulteriori articoli sui suggerimenti per la codifica che copriranno l'immutabilità e la programmazione funzionale. Si noti inoltre, come sottolineato da David Nagli, che la soluzione if-less funzionerebbe solo per numeri interi mentre la soluzione if-based ha il vantaggio di gestire qualsiasi numero compresi quelli decimali.

Nella soluzione if-less, stiamo sfruttando il fatto che il risultato dell'operazione del modulo 2 è sempre 1 (per dispari) o 0 (per pari). Questo risultato sono dati. Abbiamo appena usato quei dati direttamente.

Che ne dici di contare anche numeri interi? Riesci a pensare a un modo per farlo senza usare un'istruzione if?

Sfida n. 2: la funzione weekendOrWeekday

Scrivi una funzione che accetta un argomento oggetto date (come new Date ()) e restituisce la stringa "weekend" o "giorno della settimana".

Ecco una soluzione che utilizza un'istruzione if:

const weekendOrWeekday = (inputDate) => {
  const day = inputDate.getDay ();
  if (giorno === 0 || giorno === 6) {
    ritorno 'weekend';
  }
  
  ritorno 'giorno della settimana';
  // Oppure, per i fan del ternario:
  // ritorno (giorno === 0 || giorno === 6)? 'weekend': 'giorno della settimana';
};
console.log (weekendOrWeekday (nuova data ()));

Ecco una soluzione senza istruzioni if:

const weekendOrWeekday = (inputDate) => {
  const day = inputDate.getDay ();
  return weekendOrWeekday.labels [giorno] ||
         weekendOrWeekday.labels [ 'default'];
};
weekendOrWeekday.labels = {
  0: "weekend",
  6: "weekend",
  impostazione predefinita: "giorno della settimana"
};
console.log (weekendOrWeekday (nuova data ()));

Hai notato che la condizione dell'istruzione if contiene alcuni dati? Ci dice quali giorni sono i fine settimana. Quello che abbiamo fatto per evitare l'istruzione if è estrarre quei dati in un oggetto e utilizzarlo direttamente. Ciò ci consente anche di archiviare tali dati a livelli generici più elevati.

Aggiornamento: Harald Niesche ha sottolineato correttamente che il || il trucco sopra è tecnicamente una cosa condizionale. Ho scelto di usarlo in modo da non ripetere "giorni feriali" cinque volte. Possiamo facilmente liberarcene se mettiamo i 7 giorni interi nell'oggetto etichette.

Sfida n. 3: la funzione duplicatore (ecco i draghi)

Scrivi la funzione duplicatore che, in base al tipo di input, eseguirà le seguenti operazioni:

  • Quando l'ingresso è un numero, lo raddoppia (ovvero 5 => 10, -10 => -20).
  • Quando l'input è una stringa, ripete ogni lettera (ovvero 'hello' => 'hheelloo').
  • Quando l'ingresso è una funzione, lo chiama due volte.
  • Quando l'input è un array, si chiama su tutti gli elementi di quell'array.
  • Quando l'input è un oggetto, si chiama su tutti i valori di quell'oggetto.

Ecco una soluzione che utilizza un'istruzione switch:

const doubler = (input) => {
  switch (input di tipo) {
    numero del caso':
      ritorno input + input;
    caso 'stringa':
      input di ritorno
        .Diviso('')
        .map ((letter) => letter + letter)
        .aderire('');
    caso 'oggetto':
      Object.keys (ingresso)
            .map ((chiave) => (input [chiave] = duplicatore (input [chiave])));
      input di ritorno;
    caso 'funzione':
      ingresso();
      ingresso();
  }
};
console.log (duplicatore (-10));
console.log (duplicatore ( 'hey'));
console.log (doubler ([5, 'hello']));
console.log (duplicatore ({a: 5, b: 'hello'}));
console.log (
  doubler (function () {
    console.log ( 'call-me');
  }),
);

Ecco una soluzione senza un'istruzione switch:

const doubler = (input) => {
  return doubler.operationsByType [tipo di input] (input);
};
doubler.operationsByType = {
  numero: (input) => input + input,
  stringa: (input) =>
    ingresso
      .Diviso('')
      .map ((letter) => letter + letter)
      .aderire(''),
  funzione: (input) => {
    ingresso();
    ingresso();
  },
  oggetto: (input) => {
    Object.keys (ingresso)
          .map ((chiave) => (input [chiave] = duplicatore (input [chiave])));
    input di ritorno;
  },
};
console.log (duplicatore (-10));
console.log (duplicatore ( 'hey'));
console.log (doubler ([5, 'hello']));
console.log (duplicatore ({a: 5, b: 'hello'}));
console.log (
  doubler (function () {
    console.log ( 'call-me');
  }),
);

Ancora una volta, nota come i dati (quali operazioni dovrebbero essere eseguite per quale tipo di input) sono stati tutti estratti dall'istruzione switch in un oggetto. Quindi l'oggetto è stato utilizzato per selezionare l'operazione corretta e invocarla con l'input originale.

Ora che hai visto alcuni esempi, quali sono i tuoi pensieri? Hai qualche argomento a favore o contro uno di questi due stili di codifica?

Grazie per aver letto.