2016-11-20 47 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

很好解释... :) –

相关问题