2017-04-17 2 views
1

Ruby에서 순수하게 OO 방식으로 이진 트리 미로 생성을 구현했습니다. 나는 이것을 Elixir에서 학습 연습으로 다시 쓰려고 노력하고 있지만, OO 대 FP 패러다임에 대한 몇 가지 문제가있다.Elixir의 이진 트리 미로 생성

셀을 포함하는 표를 렌더링합니다. 이진 트리 알고리즘을 사용하여 격자를 통과 할 때 모든 셀에 대해 북쪽 또는 동쪽 셀과 연결하기로 결정합니다. Ruby 구현에서이 연결은 양방향입니다.

def link(cell, bidirectional=true) 
    @links[cell] = true 
    cell.link(self, false) if bidirectional 
    self 
end 

def unlink(cell, bidirectional=true) 
    @links.delete cell 
    cell.unlink(self, false) if bidirectional 
    self 
end 

그래서이 링크는 셀을 인접 항목에 연결하고 인접 항목을 셀에 연결합니다. 나는 엘릭서에서 이것을 어떻게하는지 알아낼 수 없다. 함수의 첫 번째 부분이 아래로 있습니다.

def link(cell, neighbour, bidirectional) do 
    %{ cell | links: cell.links ++ [neighbour]} 
end 



test "it links cells in a bidirectional way" do 
    cell = Cell.create(1, 1) 
    neighbour = Cell.create(2, 2) 

    %{ row: _, column: _, links: cell_links } = Cell.link(cell, neighbour, true) 
    assert Enum.member? cell_links, neighbour 
    # ?? check if neighbour links includes cell, but cannot get a reference to "new" neighbour 
end 

그러나 양방향 호출로 인해 문제가 발생합니다. 문제없이 호출 할 수 있지만 불변의 데이터를 다루고 있기 때문에 정확한 링크 배열을 가진 "새로운"이웃 셀에 대한 참조를 얻을 수는 없습니다.

모든 셀에 대해 GenServer를 구현하는 것은 나에게 반 패턴과 비슷합니다. 순전히 기능적인 방법으로이 동작을 구현하는 방법이 반드시 있어야합니다. 나는 FP에 익숙하지만 도움을 많이 원합니다.

답변

1

OO를 순차적 인 Elixir (일반적으로 함수 언어)에 매핑 할 때 사용할 수있는 패턴에서 데이터 객체 (OO 객체가 아닌)를 만들고 함수의 첫 번째 인수로 전달할 수 있습니다. 이렇게하면 각 호출에서 해당 데이터를 변환합니다.

따라서 api의 모양은 def link(maze, cell, bidirectional \\ true)입니다. 맵을 사용하여 {x,y} 튜플을 키로 사용하고지도를 값으로 사용하여 개별 셀에 액세스하고 업데이트 할 수 있습니다.

예제로 테스트되지 않은 코드가 있습니다.

def Maze do 
    def new, do: %{cells: %{], links: %{}, start: {0,0}}} 

    def link(maze, cell1, cell2, bidirectional \\ true) do 
    maze 
    |> put_in([:links, cell2], true) 
    |> link_bidirectional(cell1, bidirectional) 
    end 

    defp link_bidirectional(maze, _, _, false), do: maze 
    defp link_bidirectional(maze, cell1, cell2, _) do 
    link(maze, cell2, cell1, false) 
    end 
end 

EDIT : 여기

defmodule Maze do 
    def new do 
    %{cells: %{{0, 0} => Cell.create(0,0)}, tree: {{0, 0}, nil, nil}} 
    end 

    def new_cell(maze, row, column) do 
    # ignoring the tree for now 
    put_in(maze, [:cells, {row, column}], Cell.create(row, column)) 
    end 

    def link(maze, cell1, cell2, bidirectional \\ true) 
    def link(maze, %{} = cell1, %{} = cell2, bidirectional) do 
    maze 
    |> update_in([:cells, cell1[:origin]], &(Cell.link(&1, cell2))) 
    |> do_bidirectional(cell1, cell2, bidirectional, &link/4) 
    end 
    def link(maze, {_, _} = pt1, {_, _} = pt2, bidirectional) do 
    link(maze, maze[:cells][pt1], maze[:cells][pt2], bidirectional) 
    end 

    def unlink(maze, %{} = cell1, %{} = cell2, bidirectional \\ true) do 
    maze 
    |> update_in([:cells, cell1[:origin]], &(Cell.unlink(&1, cell2))) 
    |> do_bidirectional(cell1, cell2, bidirectional, &unlink/4) 
    end 

    defp do_bidirectional(maze, _, _, false, _), do: maze 
    defp do_bidirectional(maze, cell1, cell2, _, fun) do 
    fun.(maze, cell2, cell1, false) 
    end 
end 

defmodule Cell do 
    def create(row,column), do: %{origin: {row, column}, links: %{}} 
    def link(self, cell) do 
    update_in(self, [:links, cell[:origin]], fn _ -> true end) 
    end 
    def unlink(self, cell) do 
    update_in(self, [:links], &Map.delete(&1, cell[:origin])) 
    end 
end 

iex(26)> Maze.new() |> 
...(26)> Maze.new_cell(0,1) |> 
...(26)> Maze.new_cell(1,0) |> 
...(26)> Maze.link({0,0}, {0,1}) |> 
...(26)> Maze.link({0,0}, {1,0}) 
%{cells: %{{0, 
    0} => %{links: %{{0, 1} => true, {1, 0} => true}, origin: {0, 0}}, 
    {0, 1} => %{links: %{{0, 0} => true}, origin: {0, 1}}, 
    {1, 0} => %{links: %{{0, 0} => true}, origin: {1, 0}}}, 
    tree: {{0, 0}, nil, nil}} 
iex(27)> 
연결하는 기능 솔루션