2015-02-24 15 views
7

Sono nuovo di Scala e la programmazione funzionale in generale. Quindi ecco il mio dubbio.Qual è il valore di ritorno appropriato quando il modello corrisponde a zero e vogliamo restituire Nil?

In una funzione con abbinamento di modelli, quando case Nil corrisponde, e si desidera restituire Nil, dovremmo restituire Nil o il tipo di dati stesso? Ad esempio,

def drop[A](l: List[A], n: Int): List[A] = { 
    if (n <= 0) l 
    else l match { 
     case Nil => Nil 
     case Cons(_, t) => drop(t, n - 1) 
    } 
} 

Questa è una funzione che scende primi n elementi di testa da un elenco concatenata. Qui, per il primo caso, dovrei restituire Nil (forse come buona pratica) o dovrei restituire l (perché quindi non dovremo costruire l'oggetto Nil)?

risposta

8

C'è solo un'istanza singleton dell'oggetto Nil. Quando scrivi Nil non ne crei uno nuovo ogni volta, usi solo l'unico esistente.

In genere è preferibile scrivere Nil perché è più leggibile. Almeno questo è quello che ho sempre letto e scritto.

+0

Grazie! Ciò ha senso. – aa8y

1

Poiché esiste una sola istanza di Nil, quindi non importa poiché è lo stesso oggetto reale.


Ora la vera domanda è: ciò che è più leggibile?

  • Se si vuole mettere in chiaro che quando si arriva Nil, quindi Nil viene restituito, quindi scrivere Nil => Nil
  • Se sembra più logico tornare l, anche se in realtà è Nil, quindi scrivere Nil => l

IMHO, nel tuo caso, Nil => Nil è più chiaro per me.

+1

Puoi dare un esempio per il caso in cui il valore restituito è 'Nil', ma la scrittura di' l' rende il codice più leggibile? Ho visto casi in cui ho un accumulatore inizializzato su 'Nil' che viene restituito perché potrebbe accumulare più valori. Ma non ho ancora visto il caso che hai menzionato. – aa8y

0

Se questo Nil => Nil è davvero offendere il vostro sensibilità all'interno del vostro flusso logico principale, si potrebbe considerare la possibilità di arricchire il-mio-biblioteca in stile classe implicita di nasconderlo all'interno di un nuovo metodo:

implicit class ListWithIfNonEmpty[A](list: List[A]) { 
    /** 
    * Replaces a list with a new one in the case that it is 
    * not empty. newList is probably a conversion of the original 
    * list, but the original list is available in the caller's 
    * scope, so no need to pass it as a parameter. 
    */ 
    def ifNonEmpty[B](newList: => List[B]): List[B] = list match { 
    case Nil => Nil 
    case _ => newList 
    } 
} 

E poi il tuo drop metodo diventa un po 'più pulito:

def drop[A](l: List[A], n: Int): List[A] = l.ifNonEmpty { 
    if (n <= 0) l else drop(l.tail, n - 1) 
} 

Forse c'è un nome meno scomodo per il nuovo metodo. Questo genere di cose è solo una grande vittoria per il tuo flusso logico principale in quei casi in cui il nome del metodo rende la semantica davvero ovvia. Ma ottieni la deriva.

Problemi correlati