2013-03-22 17 views
6

EDIT: Ho appena ricordato che appiattire ha lo stesso effetto come il mio filtro e mappareScala lista condizionale costruzione

sto usando Scala 2.9.2, e vorrebbe costruire una lista in base ad alcune condizioni.

Si consideri il seguente, dove cond è una funzione di prendere un predicato p ed un valore di tipo T (in questo caso t3):

t1 :: t2 :: cond(p, t3) :: t4 

Il comportamento che voglio è la seguente. Se p è vero, questo dovrebbe dare:

List[T](t1, t2, t3, t4) 

Se p restituisce false, questo dovrebbe dare:

List[T](t1, t2, t4) 

Probabilmente sto pensando a questo completamente il modo sbagliato, ma sto lottando per trovare una soluzione elegante. Potrei coinvolgere Opzioni dappertutto e poi filtrare, ma che lo rende il codice piuttosto difficile da leggere:

def cond[T](p : => Boolean, v : T) : Option[T] = 
{ 
    p match 
    { 
     case true => Some(v) 
     case false => None 
    } 
} 

In questo modo il seguente:

scala> (Some(1) :: Some(2) :: cond(true, 3) :: Some(4) :: Nil).flatten 
res4: List[Int] = List(1, 2, 3, 4) 

scala> (Some(1) :: Some(2) :: cond(false, 3) :: Some(4) :: Nil).flatten 
res5: List[Int] = List(1, 2, 4) 

Tuttavia, non è la soluzione più elegante, in quanto richiede l'utente per avvolgere tutti i loro elementi non condizionali in alcuni() e anche per ricordare di appiattire alla fine. Qualcuno può pensare ad una soluzione più elegante?

risposta

5

Come ottenere un elenco?

@inline def cond[T](p : => Boolean, v : T) : List[T] = if(p) v::Nil else Nil 

e poi usarli come questo:

List(1,2,3) ++ cond(false, 3) ++ List(4) 
+0

Questo funziona, ma non è l'ideale, in quanto non si adatta al metodo di costruzione 1 :: 2: 3: richiede di ricordare di usare ++ in alcuni casi invece di :: – paulmdavies

4

provare a creare e filtrando il nuovo elenco con la vostra condizione:

List[T](t1, t2, t3, t4).filter(p) 
+1

Che non funziona nel caso generale - la condizione p_i è specifica per l'elemento t_i - e il risultato della valutazione di p_i dovrebbe riguardare solo se t_i è nella lista, non t_j, dove j! = i – paulmdavies

0

Se avete bisogno di sapere l'indice per selezionare la corretta predicato, è possibile zipWithIndex abbinare gli indici con i valori quindi utilizzare collect anziché filter per consentire di eliminare l'indice dal risultato e codificare il pred selezione di ghiaccio e applicazione in una guardia. Ad esempio:

List(1, 4, 9, 16, 25).zipWithIndex.collect { case (n, i) if (n + i) % 3 <= 1 => n } 
res0: List[Int] = List(1, 16) 
3

Quindi, non c'è modo questo funzionerà con la lista standard, perché i tipi sono sbagliati: :: aspetta un elemento di tipo [A >: T] dove T è il tipo di lista, mentre si vuole dare qualcosa che potrebbe, o potrebbe non, produrre un elemento di quel tipo.

Tuttavia, non c'è motivo per cui non si debba essere in grado di definire un metodo che è abbastanza felice di prendere qualcosa che solo facoltativamente produce un elemento successivo. List stessa è sigillato, quindi non possiamo estendere direttamente, ma possiamo replicare il comportamento dobbiamo facilmente:

trait QList[+T] { 

    def hd : T 
    def tl : QList[T] 

    def ?::[A >: T](hd : A) : QList[A] = new ?::[A](hd, this) 
    def ?::[A >: T](hd : => Option[A]) : QList[A] = hd match { 
    case Some(a) => ?::(a) 
    case None => this 
    } 
} 

case object QNil extends QList[Nothing] { 
    def hd = throw new Exception("Head of empty list") 
    def tl = throw new Exception("Tail of empty list") 
} 
case class ?::[+T](val hd: T, val tl : QList[T]) extends QList[T] 

def cond[T](p : => Boolean, v : T) : Option[T] = 
{ 
    p match 
    { 
    case true => Some(v) 
    case false => None 
    } 
} 

val truelist = 1 ?:: 2 ?:: 3 ?:: cond(true, 4) ?:: 5 ?:: QNil 
val falselist = 1 ?:: 2 ?:: 3 ?:: cond(false, 4) ?:: 5 ?:: QNil 

Fondamentalmente ricreare l'elenco, ma permeare con un'operazione prepend overload che richiede una condizione .

È possibile aggiungere il metodo ?:: all'elenco standard utilizzando una conversione implicita in un'altra classe con il metodo corretto. Lei parla si sta utilizzando 2.9.2, che è un peccato perché altrimenti questo è il genere di cose che le classi di valore impliciti sono grandi per, ma non abbiamo bisogno di loro, hanno solo rendere le cose più facili:

class ConditionallyAppend[T](tl : List[T]) { 
    def ?::[A >: T](hd : A) : List[A] = hd :: tl 
    def ?::[A >: T](x : => Option[A]) : List[A] = x match { 
    case Some(a) => a :: tl 
    case None => tl 
    } 
} 

implicit def ListToConditionallyAppend[A](x : List[A]) = new ConditionallyAppend(x) 

E ora siamo in grado di fare questo:???

val truelist = 1 :: 2 :: 3 :: cond (vero, 4) :: 5 :: Nil

truelist:?? Lista [Qualsiasi] = Elenco (1, 2, 3, 4, 5)

E questo:???

val falselist = 1 :: 2 :: 3 :: cond (false, 4) :: 5 :: Nil

falselist:?? Lista [Qualsiasi] = Elenco (1, 2, 3, 5)

+0

I briefl Ho modificato questo per suggerire che potremmo invece sostituire l'operatore predefinito '::' invece di aggiungere '? ::'. Possiamo, ma non funziona. Dato che 'List' è covariante, il comando standard accetterà qualsiasi cosa che possa dedurre come super-tipo, e quindi ti darà un' List [Any] 'con un' None' in esso. – Impredicative