2010-12-27 6 views
5

Próbuję zmniejszyć zakres, w jakim piszę Scala (2.8) jak Java. Oto uproszczenie problemu, z którym się zetknąłem. Czy możesz zaproponować ulepszenia moich rozwiązań, które są "bardziej funkcjonalne"?Scala, spraw by moja pętla była bardziej funkcjonalna

Transform mapę

val inputMap = mutable.LinkedHashMap(1->'a',2->'a',3->'b',4->'z',5->'c') 

odrzucając wszelkie wpisy o wartości 'Z' i indeksowania znaki są one spotykane

Najpierw spróbuj

var outputMap = new mutable.HashMap[Char,Int]() 
var counter = 0 
for(kvp <- inputMap){ 
    val character = kvp._2 
    if(character !='z' && !outputMap.contains(character)){ 
    outputMap += (character -> counter) 
    counter += 1 
    } 
} 

drugą szansę (nie wiele lepsze , ale używa niezmiennej mapy i "foreach")

var outputMap = new immutable.HashMap[Char,Int]() 
var counter = 0 
inputMap.foreach{ 
    case(number,character) => { 
    if(character !='z' && !outputMap.contains(character)){ 
     outputMap2 += (character -> counter) 
     counter += 1 
    } 
    } 
} 

Odpowiedz

11

Nicer rozwiązanie:

inputMap.toList.filter(_._2 != 'z').map(_._2).distinct.zipWithIndex.toMap 
+1

ładniejszych rozwiązanie jest bardzo miłe! Spełnia wszystkie wymagania w zwarty i zrozumiały sposób. (Osobiście mapowałbym przed filtrowaniem, ale jest to niemal banalny szczegół.) –

5

Po pierwsze, jeśli robisz to funkcjonalnie, należy użyć niezmiennej mapę.

Następnie, aby pozbyć się czegoś, należy użyć metody filter:

inputMap.filter(_._2 != 'z') 

i wreszcie, aby wykonać ponowne mapowanie, można po prostu użyć wartości (ale jako zestaw) z zipWithIndex, który będzie liczy się od zera, a następnie przekształcić z powrotem do mapy:

inputMap.filter(_._2 != 'z').values.toSet.zipWithIndex.toMap 

Ponieważ kolejność wartości nie zostanie zachowany w każdym razie *, prawdopodobnie nie ma znaczenia, że ​​zamówienie może być jeszcze raz tasuje z ustawioną transformacją.

Edytuj: Istnieje lepsze rozwiązanie w podobnym duchu; patrz Arjan's. Założenie (*) jest błędne, ponieważ było to LinkedHashMap. A więc musisz zachować porządek, który rozwiązuje Arjan.

3

Stworzyłbym taki "rurociąg", ale ma to wiele operacji i prawdopodobnie można go skrócić. Te dwa List.map można umieścić w jednym, ale myślę, że masz ogólny pomysł.

inputMap 
.toList // List((5,c), (1,a), (2,a), (3,b), (4,z)) 
.sorted // List((1,a), (2,a), (3,b), (4,z), (5,c)) 
.filterNot((x) => {x._2 == 'z'}) // List((1,a), (2,a), (3,b), (5,c)) 
.map(_._2) // List(a, a, b, c) 
.zipWithIndex // List((a,0), (a,1), (b,2), (c,3)) 
.map((x)=>{(x._2+1 -> x._1)}) // List((1,a), (2,a), (3,b), (4,c)) 
.toMap // Map((1,a), (2,a), (3,b), (4,c))

wykonanie tej operacji na listach powoduje porządkowanie elementów.

2

EDYCJA: błędnie przeczytałem pytanie OP - myślałem, że chcesz kodowanie długości przebiegu. Oto moje zdanie na temat aktualnej pytanie:

val values = inputMap.values.filterNot(_ == 'z').toSet.zipWithIndex.toMap 

EDIT 2: Jak zauważył w komentarzach, użyj toSeq.distinct lub podobnych, jeżeli zamówienie zachowania jest bardzo ważne.

val values = inputMap.values.filterNot(_ == 'z').toSeq.distinct.zipWithIndex.toMap 
+0

Program operacyjny nie prosi o kodowanie początkowe. Usuń problem (lub kod). –

+0

To prawda - przeczytaj to za szybko - edytuj. Nowe rozwiązanie wygląda podobnie jak wszystkie inne, czego można się było spodziewać. Twoje zdrowie! – Janx

+0

Jak zaznaczył Rex we własnej odpowiedzi, 'toSet' nie spełnia wymagań, ponieważ nie zachowuje porządku. –

-2

Z mojego doświadczenia wynika, że ​​mapy i języki funkcjonalne nie grają przyjemnie. Zauważysz, że wszystkie dotychczasowe odpowiedzi w taki czy inny sposób obejmują przekształcenie mapy w listę, filtrowanie listy, a następnie zamianę listy z powrotem na mapę.

Myślę, że wynika to z faktu, że mapy są zmiennymi strukturami danych z natury. Należy wziąć pod uwagę, że podczas budowania listy struktura podstawowa listy nie zmienia się po dołączeniu nowego elementu, a jeśli lista jest prawdziwa, to dołączenie jest stałą operacją O (1).Podczas gdy dla mapy wewnętrzna struktura mapy może się znacznie zmienić po dodaniu nowego elementu, tj. gdy współczynnik obciążenia staje się zbyt wysoki, a algorytm dodawania zmienia rozmiar mapy. W ten sposób język funkcyjny nie może po prostu utworzyć serii wartości i poprowadzić je do mapy w miarę jej przemieszczania się ze względu na możliwe skutki uboczne wprowadzenia nowej pary klucz/wartość.

Mimo to, nadal uważam, że powinno być lepsze wsparcie dla filtrowania, mapowania i składania/zmniejszania map. Ponieważ zaczynamy od mapy, znamy maksymalny rozmiar mapy i powinno być łatwo stworzyć nową.

Jeśli chcesz opanować programowanie funkcjonalne, zalecam, aby od początku unikać map. Trzymaj się rzeczy, dla których zaprojektowano języki funkcyjne - manipuluj listami.

+0

Scala zapewnia bardzo ładne niezmienne mapy. Fakt, że wiele rozwiązań przekształca się w listę (zauważ, że Rex nie jest) jest bardziej konsekwencją problemu niż jakiekolwiek ograniczenia związane z mapami. OP wydaje się nie interesować oryginalnymi kluczami, więc struktura danych wejściowych mogłaby również być listą. –

+1

Ten komentarz nie odpowiada na pytanie i jest niezgodny z rzeczywistością (np. Drzewo oprócz drzewa O (log (n)) pozostaje nienaruszone po aktualizacji). Poza tym, pytanie OP polega na zastosowaniu algorytmu, który nie jest z natury operacją mapowania_, ponieważ mapa jest tylko do indeksu (który można wywnioskować z kolejności pojawiania się). Więc, wiele rozwiązań nie wykorzystuje map. –

+0

Ugh, mój błąd. Myślałem tylko o hashmapach, gdy patrzę na to pytanie. Myślę też, że nie wiedziałem o zachowaniu połączonej mapy (iteracja według kolejności wstawiania). Myślę o moim zdrowiu psychicznym. Chciałbym odpowiedzieć na to pytanie. Jest pożądaną funkcjonalnością tworzenia mapy z odwzorowaniami klucz/wartość, w której każdy klucz nie jest "z" i którego wartość jest ostatnim razem/indeksem, który został wstawiony do mapy (z wyjątkiem wstawień znaków "z"). – Dunes

9

znajdę rozwiązanie to nieco prostsze niż arjan's:

inputMap.values.filter(_ != 'z').toSeq.distinct.zipWithIndex.toMap 

Poszczególne etapy:

inputMap.values  // Iterable[Char] = MapLike(a, a, b, z, c) 
    .filter(_ != 'z') // Iterable[Char] = List(a, a, b, c) 
    .toSeq.distinct // Seq[Char]  = List(a, b, c) 
    .zipWithIndex  // Seq[(Char, Int)] = List((a,0), (b,1), (c,2)) 
    .toMap    // Map[Char, Int] = Map((a,0), (b,1), (c,2)) 

Pamiętaj, że Twój problem nie nieodłącznie wiążą się z mapy jako dane wejściowe, ponieważ jesteś tylko odrzucanie kluczy. Gdybym kodowania to, pewnie bym napisać funkcję jak

def buildIndex[T](s: Seq[T]): Map[T, Int] = s.distinct.zipWithIndex.toMap 

i powołać go jako

buildIndex(inputMap.values.filter(_ != 'z').toSeq) 
+0

Bardzo ładnie. Szczególnie doceniamy komentarze w drugim fragmencie. – Pengin