2016-11-20 72 views
1

我是elixir的新手,很難更新變量。需要一些幫助。我有兩個地圖如何在elixir中更新函數內部的布爾變量

firstMsg = %{msg: "Hello", vt: %{"p1" => 1, "p2" => 1, "p3" => 1}, from: "p3"} 
state = %{ :name => "p2", 
       vector: %{"p1" => 0, "p2" => 0, "p3" => 0}, 
       participants: ["p1","p3","p2"] 
      } 

我傳遞這兩張地圖的功能,它應該返回我真或假,這取決於一些條件。

defmodule Testfunc do 
def keep_in_pending(firstMsg, state) do 
    if (firstMsg.vt[firstMsg.from] == state.vector[firstMsg.from] + 1) do 
    #IO.puts("Origin proc clock is 1 step ahead from rcvd process Origin clk") 
    checking = false #initially set this to false 
    for n <- state.participants do 
     if n != firstMsg.from do #filter the origin processes 
      IO.puts("#{n}: #{inspect firstMsg.vt[n]} <= #{n}: #{inspect state.vector[n]} ") 
      checking = cond do 
      (firstMsg.vt[n] <= state.vector[n]) -> false 
      (firstMsg.vt[n] > state.vector[n]) -> true 
      end 
     end 
     end 

    end 
    checking 
end 
end 

out = Testfunc.keep_in_pending(firstMsg, state) 
IO.puts("#{inspect out}") 

它總是給我虛假的(我最初分配給它的值),並沒有更新。我認爲變量的範圍僅限於內部「如果」。任何人都可以給我建議如何重新安排這段代碼,以便它返回適當的更新布爾值?

所以在這種情況下,它應該返回true,因爲firstMsg.vt [「p1」]> state.vector [「p1」]。

回答

2

所以這裏有一個想法:如果你試圖讓一個函數返回一個布爾值,只要讓它返回一個布爾值,不要將它分配給一個變量。在if/case/cond中指定將顯示警告。此外,您不重新分配checking,因爲理解範圍內的變量(for)僅限於該範圍。您在Elixir中的最佳工具將是第一次模式匹配,第二次是管道操作員,因此請始終嘗試使用它們。

這裏有一個想法,重構代碼:

defmodule Testfunc do 
    def keep_in_pending(firstMsg, state) do 
    if (firstMsg.vt[firstMsg.from] == state.vector[firstMsg.from] + 1) do 
     state.participants 
     |> Enum.filter(fn (n) -> n != firstMsg.from end) 
     |> Enum.reduce(fn (n, _) -> 
     cond do 
      (firstMsg.vt[n] <= state.vector[n]) -> false 
      (firstMsg.vt[n] > state.vector[n]) -> true 
     end 
     end) 
    end 
    end 
end 
+0

但它總是讓我在任何情況下都是虛假的。 –

+0

奇怪的是,我得到'true',使用你提供的所有信息。 –

4

歡迎藥劑。你是對的,這是一個範圍問題,但它比這更深入一點。 Elixir是一種數據不可變的語言。您不能將checked設置爲false,運行循環並將其設置爲該循環中的某處。這會改變checked。並不是有人設計惡魔範圍規則來防止這種情況發生,而是底層虛擬機不會改變狀態。

設置某種狀態的程序設計風格,然後運行改變該狀態的過程,依賴於可變狀態。當狀態是不可變的時候,循環的替代方法就是遞歸。您在每次遞歸調用中都帶有新狀態。

您正在學習一種函數式語言,我認爲將您的代碼拆分爲幾個函數會有幫助。這將解決您的直接問題,並使您的代碼更易於理解。

def keep_in_pending(%{from: from, vt: vt}, %{vector: vector, participants: ps}) do 
    if vt[from] == vector[from] + 1 do 
    ps 
    |> Enum.reject(& &1 == from) 
    |> check_participants(vector, vt, false) 
    end 
end 

def check_participants([], _, _, bool), do: bool 
def check_participants([h | t], vector, vt, bool) do 
    check_participants(t, vector, vt, vt[h] > vector[h]) 
end 

我會簡單地解釋一下。

首先,請注意我的模式與輸入相匹配,以抽出我們在函數體中使用的有趣部分。這擺脫了一些重複性的業務。 (順便說下,snake_case你的變量名稱。)

其次,我沒有觸及粗糙的外部if條件。我根本不知道這意味着什麼。你也許應該提取它並給它一個意圖揭示名字。

真實的行動開始於我們管道參與者。你正在篩選你的列表理解。我用Enum.reject/1來代替。然後我們將列表傳遞給遞歸函數。它將把布爾值傳遞到最後,從false開始。它需要檢查vtvector中的值,因此它們也被傳入。

遞歸的第一條規則是遞歸的第一條規則。不,等等。這是考慮如何終止遞歸。我們正在處理參與者列表,所以我們會在列表爲空時停止。在那個時候,我們有我們正在尋找的布爾值,所以只需要返回它。

遞歸步驟是從列表(h)摘下一個項目,用它來確定一個新的布爾(vt[h] > vector[h]),並與名單(check_participants(t, ...))的其餘部分再次調用該函數。

希望這會有所幫助!玩得開心學習函數式編程!

+0

很好解釋... :) –

相關問題