2013-03-22 19 views
6

EDIT: Właśnie pamiętać, że spłaszczyć ma taki sam efekt jak moim filtrze i mapScala lista warunkowa budowa

Używam Scala 2.9.2, i chciałby skonstruować listę opartą na pewnych warunkach.

Rozważmy następujące, gdzie cond jest jakaś funkcja robienia predykat P i wartość typu T (w tym przypadku T3):

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

Zachowanie Chcę się następująco. Jeśli p jest prawdziwe, powinno to dać:

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

jeżeli p ma wartość false, to powinien dać:

List[T](t1, t2, t4) 

ja prawdopodobnie myśli o tym całkowicie niewłaściwy sposób, ale mam zmaga wymyślić eleganckie rozwiązanie. Mógłbym zaangażować Opcje wszędzie, a następnie filtrować, ale to sprawia, że ​​kod raczej trudniejsze do odczytania:

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

Pozwala następujące:

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) 

Jednakże, nie jest to najbardziej eleganckie rozwiązanie, ponieważ wymaga użytkownik, aby zawinąć wszystkie ich elementy nie-warunkowe w Some(), a także pamiętać o spłaszczeniu na końcu. Czy ktoś może pomyśleć o bardziej eleganckim rozwiązaniu?

Odpowiedz

5

Co powiesz na wystawienie list?

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

a następnie wykorzystując je jako to:

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

Działa, ale nie jest idealny, ponieważ nie pasuje do metody konstrukcji 1 :: 2 :: 3: wymaga zapamiętania, aby użyć ++ w niektórych przypadkach zamiast :: – paulmdavies

4

Spróbuj tworzenia i filtrowanie nową listę ze stanu:

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

To nie działa w ogólnym przypadku - warunek p_i jest specyficzny dla elementu t_i - a wynik oceny p_i powinien wpływać tylko na to, czy t_i znajduje się na liście, a nie t_j, gdzie j! = i – paulmdavies

0

Jeśli trzeba wiedzieć indeksu, aby wybrać właściwy predykat, możesz sparować indeksy z wartościami, a następnie użyć collect zamiast filter, aby pozwolić ci upuścić indeks z wyniku i zakodować pred wybór i zastosowanie lodowca w straży. Np:

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

Tak więc, nie ma mowy, to będzie działać ze standardowej listy, ponieważ typy są złe: :: spodziewa element typu [A >: T] gdzie T jest typ listy, podczas gdy chcesz dać mu coś, może, lub nie, wytworzyć element tego typu.

Jednak nie ma powodu, aby nie można było zdefiniować metody, która jest całkiem szczęśliwa, aby wziąć coś, co tylko opcjonalnie tworzy następny element. Sam List jest zamknięty, więc nie możemy przedłużyć go bezpośrednio, ale możemy replikować zachowanie musimy dość łatwo:

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 

My w zasadzie odtworzyć listę, ale nadać jej przeciążonej pracy prepend że trwa stan .

Istnieje możliwość dodania metody ?:: do standardowej listy przy użyciu niejawnej konwersji na inną klasę, która ma poprawną metodę. Wspomnieć używasz 2.9.2, który jest wstyd, bo inaczej to jest coś takiego, że klasy wartości ukryte świetnie nadają, ale my ich nie potrzebują, oni po prostu ułatwić:

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) 

A teraz możemy to zrobić:?

val truelist = 1 :: 2 :: 3 :: dyr (true, 4) :: 5 :: Nil

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

I:?

Val falselist = 1 :: 2 :: 3 :: Warunek (fałsz, 4) :: 5 :: Nil

falselist:? Lista [Dowolny] = List (1, 2, 3, 5)

+0

I brief y zredagowaliśmy to, aby zasugerować, że zamiast tego możemy zastąpić domyślny operator '::' zamiast dodawać '? ::'. Możemy, ale to nie działa. Ponieważ 'List' jest kowariantnym, standardowy prepend przyjmie wszystko, co może wywnioskować jako supertyp, a więc po prostu da ci' List [Any] 'z' None' w nim. – Impredicative