2015-05-16 16 views
8

Mam listę Chcę kawałek się w oparciu o przejściu od typu struct B do A. Tak na przykład, mam następujące:listy wyrwy na podstawie typu struct zmieniającym

iex(1)> defmodule A, do: defstruct [] 
{:module, A ... 
iex(2)> defmodule B, do: defstruct [] 
{:module, B ... 
iex(3)> values = [ %A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{} ] 
[%A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{}] 

chcę mieć że dane pakietowego się na liście 2-elementu, który zawiera:

[ [ %A{}, %A{}, %B{}, %B{}, %B{} ], [ %A{}, %A{}, %B{} ] ] 

Jeżeli wejście miały być wszystkie a, B lub wszystkie początkowo wyjście że niezmieniony, ponieważ nie B-> wystąpił przejściowego.

Wyobrażam sobie, że jest to Enum.chunk_by/2, ale mam problem z ustaleniem, jak zachować kontekst poprzedniego elementu, aby wiedzieć, kiedy podzielić.

Jak wygląda idiomatyczne rozwiązanie takiego problemu?

Odpowiedz

4

Inną alternatywą jest chunk_by typ struct następnie zrobić kolejną przepustkę łączących list (z wyjątkiem, gdy lista zawiera %B{}):

def chunk(structs) do 
    structs 
    |> Enum.chunk_by(& &1.__struct__) 
    |> merge() 
end 

# Don't merge when current is %B 
defp merge([[%B{}|_]=h|t]), do: [h|merge(t)] 

# Merge all others 
defp merge([curr, next|t]), do: [curr ++ next|merge(t)] 

# We are done 
defp merge([]), do: [] 
+0

Musisz wybrać tę, ponieważ jest to jedyna, którą czuję się w stanie utrzymać, biorąc pod uwagę mój obecny poziom wiedzy o eliksiru! Dzięki! –

4

Enum.chunk_by/2 obecnie nie zapewnia dostępu do poprzedniego elementu, więc nie możemy w tym przypadku używać Enum.chunk_by/2. Będziemy musieli Przenieś się reduce/3

wszystkich funkcji Enum, reduce/3 jest najbardziej elastyczny i jest używany wewnętrznie przez większość, jeśli nie wszystkie, z funkcjami Enum.

Poniżej jest jednym ze sposobów, aby przejść o produkcję wyjście chcesz, podane wartości [ %A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{} ]:

values 
    |> Enum.reduce([[]], fn (elem, acc) -> 
    prev_list = List.first(acc)   
    prev_elem = List.first(prev_list) 
    b_changed_to_a? = fn -> prev_elem.__struct__ == B && elem.__struct__ == A end 

    if is_nil(prev_elem) || !b_changed_to_a?.() do 
     List.replace_at(acc, 0, [elem|prev_list]) 
    else 
     [[elem]|acc]  
    end 
    end) 
    |> Enum.map(&Enum.reverse/1) 
    |> Enum.reverse 

Zauważ, że ja zawsze poprzedzić element do listy. Dzieje się tak dlatego, że dołączenie do listy w Elixir jest kosztowną operacją.

Mam nadzieję, że to rozwiązanie pomoże!

5

Jeszcze innym sposobem jest użycie czystego rekurencji:

def collect_chunks([]), do: [] 
def collect_chunks(list) do 
    {chunk, post_chunk} = collect_chunk(list) 
    [chunk | collect_chunks(post_chunk)] 
end 

defp collect_chunk([]), do: {[], []} 
defp collect_chunk([%B{} = last_element | [%A{} | _] = post_chunk]), do: {[last_element], post_chunk} 
defp collect_chunk([el | rest]) do 
    {remaining_chunk, post_chunk} = collect_chunk(rest) 
    {[el | remaining_chunk], post_chunk} 
end 
Powiązane problemy